merge: branch 'main' into reports
This commit is contained in:
commit
244c13cd84
30 changed files with 207 additions and 435 deletions
|
@ -1,3 +0,0 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
11
frontend/src/lib/api/markdown.ts
Normal file
11
frontend/src/lib/api/markdown.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import MarkdownIt from "markdown-it";
|
||||
import sanitize from "sanitize-html";
|
||||
|
||||
const md = new MarkdownIt({
|
||||
html: false,
|
||||
breaks: true,
|
||||
}).disable(["heading", "link", "table"]);
|
||||
|
||||
export default function renderMarkdown(src: string | null) {
|
||||
return src ? sanitize(md.render(src)) : null;
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
<script lang="ts">
|
||||
import { Card, CardHeader, CardTitle, ListGroup, ListGroupItem } from "sveltestrap";
|
||||
|
||||
import type { Field } from "$lib/api/entities";
|
||||
|
||||
import StatusIcon from "./StatusIcon.svelte";
|
||||
|
|
|
@ -31,7 +31,9 @@
|
|||
</script>
|
||||
|
||||
<div>
|
||||
<FallbackImage urls={memberAvatars(member)} width={200} alt="Avatar for {member.name}" />
|
||||
<a href="/@{user.name}/{member.name}">
|
||||
<FallbackImage urls={memberAvatars(member)} width={200} alt="Avatar for {member.name}" />
|
||||
</a>
|
||||
<p class="m-2">
|
||||
<a class="text-reset fs-5" href="/@{user.name}/{member.name}">
|
||||
{member.display_name ?? member.name}
|
||||
|
|
|
@ -4,18 +4,25 @@
|
|||
export let pronouns: Pronoun;
|
||||
|
||||
let pronounText: string;
|
||||
if (pronouns.display_text) {
|
||||
pronounText = pronouns.display_text;
|
||||
} else {
|
||||
const split = pronouns.pronouns.split("/");
|
||||
if (split.length < 2) pronounText = split.join("/");
|
||||
else pronounText = split.slice(0, 2).join("/");
|
||||
}
|
||||
$: pronounText = updatePronouns(pronouns);
|
||||
|
||||
const link = pronouns.display_text
|
||||
const updatePronouns = (pronouns: Pronoun) => {
|
||||
if (pronouns.display_text) {
|
||||
return pronouns.display_text;
|
||||
} else {
|
||||
const split = pronouns.pronouns.split("/");
|
||||
if (split.length < 2) return split.join("/");
|
||||
else return split.slice(0, 2).join("/");
|
||||
}
|
||||
};
|
||||
|
||||
let link: string;
|
||||
let shouldLink: boolean;
|
||||
|
||||
$: link = pronouns.display_text
|
||||
? `${pronouns.pronouns},${pronouns.display_text}`
|
||||
: pronouns.pronouns;
|
||||
const shouldLink = pronouns.pronouns.split("/").length === 5;
|
||||
$: shouldLink = pronouns.pronouns.split("/").length === 5;
|
||||
</script>
|
||||
|
||||
{#if shouldLink}
|
||||
|
|
|
@ -4,7 +4,7 @@ import type { APIError } from "$lib/api/entities";
|
|||
import { apiFetch } from "$lib/api/fetch";
|
||||
import type { MetaResponse } from "$lib/api/responses";
|
||||
|
||||
export const load = (async (event) => {
|
||||
export const load = (async () => {
|
||||
try {
|
||||
return await apiFetch<MetaResponse>("/meta", {});
|
||||
} catch (e) {
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
<script lang="ts">
|
||||
import { marked } from "marked";
|
||||
import sanitizeHtml from "sanitize-html";
|
||||
|
||||
import type { PageData } from "./$types";
|
||||
|
||||
import {
|
||||
|
@ -33,11 +30,12 @@
|
|||
import { apiFetchClient } from "$lib/api/fetch";
|
||||
import ErrorAlert from "$lib/components/ErrorAlert.svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
import renderMarkdown from "$lib/api/markdown";
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
let bio: string | null;
|
||||
$: bio = data.bio ? sanitizeHtml(marked.parse(data.bio, { breaks: true })) : null;
|
||||
$: bio = renderMarkdown(data.bio);
|
||||
|
||||
let memberPage: number = 0;
|
||||
let memberSlice: PartialMember[] = [];
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
<script lang="ts">
|
||||
import { marked } from "marked";
|
||||
import sanitizeHtml from "sanitize-html";
|
||||
|
||||
import FieldCard from "$lib/components/FieldCard.svelte";
|
||||
|
||||
import type { PageData } from "./$types";
|
||||
|
@ -12,11 +9,12 @@
|
|||
import { memberAvatars, pronounDisplay, WordStatus } from "$lib/api/entities";
|
||||
import { PUBLIC_BASE_URL } from "$env/static/public";
|
||||
import { userStore } from "$lib/store";
|
||||
import renderMarkdown from "$lib/api/markdown";
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
let bio: string | null;
|
||||
$: bio = data.bio ? sanitizeHtml(marked.parse(data.bio, { breaks: true })) : null;
|
||||
$: bio = renderMarkdown(data.bio);
|
||||
|
||||
const favNames = data.names.filter((entry) => entry.status === WordStatus.Favourite);
|
||||
const favPronouns = data.pronouns.filter((entry) => entry.status === WordStatus.Favourite);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { APIError, MeUser } from "$lib/api/entities";
|
||||
import { apiFetch } from "$lib/api/fetch";
|
||||
import type { PageServerLoad, Actions } from "./$types";
|
||||
import type { PageServerLoad } from "./$types";
|
||||
import { PUBLIC_BASE_URL } from "$env/static/public";
|
||||
|
||||
export const load = (async ({ url }) => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { WordStatus, type Field } from "$lib/api/entities";
|
||||
import IconButton from "$lib/components/IconButton.svelte";
|
||||
import { Button, Input, InputGroup, InputGroupText } from "sveltestrap";
|
||||
import { Button, Input, InputGroup } from "sveltestrap";
|
||||
import FieldEntry from "./FieldEntry.svelte";
|
||||
|
||||
export let field: Field;
|
||||
|
|
|
@ -88,6 +88,7 @@
|
|||
|
||||
const fieldsEqual = (arr1: Field[], arr2: Field[]) => {
|
||||
if (arr1?.length !== arr2?.length) return false;
|
||||
if (!arr1.every((_, i) => arr1[i].entries.length === arr2[i].entries.length)) return false;
|
||||
if (!arr1.every((_, i) => arr1[i].name === arr2[i].name)) return false;
|
||||
|
||||
return arr1.every((_, i) =>
|
||||
|
@ -334,8 +335,8 @@
|
|||
accept="image/png, image/jpeg, image/gif, image/webp"
|
||||
/>
|
||||
<p class="text-muted mt-3">
|
||||
<Icon name="info-circle-fill" /> Only PNG, JPEG, GIF, and WebP can be used as avatars.
|
||||
Avatars cannot be larger than 1 MB, and animated avatars will be made static.
|
||||
<Icon name="info-circle-fill" aria-hidden /> Only PNG, JPEG, GIF, and WebP can be used
|
||||
as avatars. Avatars cannot be larger than 1 MB, and animated avatars will be made static.
|
||||
</p>
|
||||
<a href="" on:click={() => (avatar = "")}>Remove avatar</a>
|
||||
</div>
|
||||
|
@ -356,6 +357,15 @@
|
|||
<FormGroup floating label="Bio ({bio.length}/{MAX_DESCRIPTION_LENGTH})">
|
||||
<textarea style="min-height: 100px;" class="form-control" bind:value={bio} />
|
||||
</FormGroup>
|
||||
<p class="text-muted mt-3">
|
||||
<Icon name="info-circle-fill" aria-hidden /> Your bio supports limited
|
||||
<a
|
||||
class="text-reset"
|
||||
href="https://commonmark.org/help/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">Markdown</a
|
||||
>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -75,6 +75,7 @@
|
|||
|
||||
const fieldsEqual = (arr1: Field[], arr2: Field[]) => {
|
||||
if (arr1?.length !== arr2?.length) return false;
|
||||
if (!arr1.every((_, i) => arr1[i].entries.length === arr2[i].entries.length)) return false;
|
||||
if (!arr1.every((_, i) => arr1[i].name === arr2[i].name)) return false;
|
||||
|
||||
return arr1.every((_, i) =>
|
||||
|
@ -281,8 +282,9 @@
|
|||
accept="image/png, image/jpeg, image/gif, image/webp"
|
||||
/>
|
||||
<p class="text-muted mt-3">
|
||||
<Icon name="info-circle-fill" /> Only PNG, JPEG, GIF, and WebP images can be used as avatars.
|
||||
Avatars cannot be larger than 1 MB, and animated avatars will be made static.
|
||||
<Icon name="info-circle-fill" aria-hidden /> Only PNG, JPEG, GIF, and WebP images can be
|
||||
used as avatars. Avatars cannot be larger than 1 MB, and animated avatars will be made
|
||||
static.
|
||||
</p>
|
||||
<p>
|
||||
<a href="" on:click={() => (avatar = "")}>Remove avatar</a>
|
||||
|
@ -298,6 +300,15 @@
|
|||
<FormGroup floating label="Bio ({bio.length}/{MAX_DESCRIPTION_LENGTH})">
|
||||
<textarea style="min-height: 100px;" class="form-control" bind:value={bio} />
|
||||
</FormGroup>
|
||||
<p class="text-muted mt-3">
|
||||
<Icon name="info-circle-fill" aria-hidden /> Your bio supports limited
|
||||
<a
|
||||
class="text-reset"
|
||||
href="https://commonmark.org/help/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">Markdown</a
|
||||
>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
import {
|
||||
ErrorCode,
|
||||
type APIError,
|
||||
type Invite,
|
||||
type MeUser,
|
||||
type PartialMember,
|
||||
} from "$lib/api/entities";
|
||||
import { ErrorCode, type APIError, type Invite, type MeUser } from "$lib/api/entities";
|
||||
import { apiFetchClient } from "$lib/api/fetch";
|
||||
import type { LayoutLoad } from "./$types";
|
||||
|
||||
|
@ -12,7 +6,6 @@ export const ssr = false;
|
|||
|
||||
export const load = (async ({ parent }) => {
|
||||
const user = await apiFetchClient<MeUser>("/users/@me");
|
||||
const members = await apiFetchClient<PartialMember[]>("/users/@me/members");
|
||||
|
||||
let invites: Invite[] = [];
|
||||
let invitesEnabled = true;
|
||||
|
@ -29,7 +22,6 @@ export const load = (async ({ parent }) => {
|
|||
return {
|
||||
...data,
|
||||
user,
|
||||
members,
|
||||
invites,
|
||||
invitesEnabled,
|
||||
};
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Members</th>
|
||||
<td>{data.members.length}/{MAX_MEMBERS}</td>
|
||||
<td>{data.user.members.length}/{MAX_MEMBERS}</td>
|
||||
</tr>
|
||||
{#if data.invitesEnabled}
|
||||
<tr>
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
|
||||
export let data: PageData;
|
||||
|
||||
import * as jose from "jose";
|
||||
const claims = jose.decodeJwt(localStorage.getItem("pronouns-token")!);
|
||||
import { decodeJwt } from "jose";
|
||||
const claims = decodeJwt(localStorage.getItem("pronouns-token")!);
|
||||
</script>
|
||||
|
||||
<h1>Tokens ({data.tokens.length})</h1>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue