forked from mirrors/pronouns.cc
move member edit page to /@user/member/edit
This commit is contained in:
parent
56c9270fdb
commit
03311d7004
9 changed files with 480 additions and 29 deletions
|
@ -10,6 +10,9 @@
|
||||||
>log in again</a
|
>log in again</a
|
||||||
>.
|
>.
|
||||||
</p>
|
</p>
|
||||||
|
{:else if $page.error?.code === ErrorCode.NotOwnMember}
|
||||||
|
<h1>Not your member</h1>
|
||||||
|
<p>You can only edit your own members.</p>
|
||||||
{:else}
|
{:else}
|
||||||
<h1>An error occurred ({$page.status})</h1>
|
<h1>An error occurred ({$page.status})</h1>
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,9 @@
|
||||||
href="/@{$member.user.name}/{data.member.name}"
|
href="/@{$member.user.name}/{data.member.name}"
|
||||||
tooltip="Back to member"
|
tooltip="Back to member"
|
||||||
/>
|
/>
|
||||||
<Button color="success" on:click={() => updateMember()} disabled={!memberNameValid}>Save changes</Button>
|
<Button color="success" on:click={() => updateMember()} disabled={!memberNameValid}>
|
||||||
|
Save changes
|
||||||
|
</Button>
|
||||||
<Button color="danger" on:click={toggleDeleteOpen}
|
<Button color="danger" on:click={toggleDeleteOpen}
|
||||||
>Delete {data.member.display_name ?? data.member.name}</Button
|
>Delete {data.member.display_name ?? data.member.name}</Button
|
||||||
>
|
>
|
||||||
|
@ -119,30 +121,27 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<Nav tabs>
|
<Nav tabs>
|
||||||
<NavItem
|
<NavItem>
|
||||||
><ActiveLink href="/@{$member.user.name}/{$member.name}/edit">Names and avatar</ActiveLink
|
<ActiveLink href="/@{$member.user.name}/{$member.name}/edit">Names and avatar</ActiveLink>
|
||||||
></NavItem
|
</NavItem>
|
||||||
>
|
<NavItem>
|
||||||
<NavItem
|
<ActiveLink href="/@{$member.user.name}/{$member.name}/edit/bio">Bio</ActiveLink>
|
||||||
><ActiveLink href="/@{$member.user.name}/{$member.name}/edit/bio">Bio</ActiveLink></NavItem
|
</NavItem>
|
||||||
>
|
<NavItem>
|
||||||
<NavItem
|
<ActiveLink href="/@{$member.user.name}/{$member.name}/edit/pronouns">Pronouns</ActiveLink>
|
||||||
><ActiveLink href="/@{$member.user.name}/{$member.name}/edit/pronouns">Pronouns</ActiveLink
|
</NavItem>
|
||||||
></NavItem
|
<NavItem>
|
||||||
>
|
<ActiveLink href="/@{$member.user.name}/{$member.name}/edit/fields">Fields</ActiveLink>
|
||||||
<NavItem
|
</NavItem>
|
||||||
><ActiveLink href="/@{$member.user.name}/{$member.name}/edit/fields">Fields</ActiveLink
|
<NavItem>
|
||||||
></NavItem
|
<ActiveLink href="/@{$member.user.name}/{$member.name}/edit/flags">Flags</ActiveLink>
|
||||||
>
|
</NavItem>
|
||||||
<NavItem
|
<NavItem>
|
||||||
><ActiveLink href="/@{$member.user.name}/{$member.name}/edit/flags">Flags</ActiveLink></NavItem
|
<ActiveLink href="/@{$member.user.name}/{$member.name}/edit/links">Links</ActiveLink>
|
||||||
>
|
</NavItem>
|
||||||
<NavItem
|
<NavItem>
|
||||||
><ActiveLink href="/@{$member.user.name}/{$member.name}/edit/links">Links</ActiveLink></NavItem
|
<ActiveLink href="/@{$member.user.name}/{$member.name}/edit/other">Other</ActiveLink>
|
||||||
>
|
</NavItem>
|
||||||
<NavItem
|
|
||||||
><ActiveLink href="/@{$member.user.name}/{$member.name}/edit/other">Other</ActiveLink></NavItem
|
|
||||||
>
|
|
||||||
</Nav>
|
</Nav>
|
||||||
|
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
import type { PrideFlag, MeUser, APIError, Member, PronounsJson } from "$lib/api/entities";
|
import {
|
||||||
|
type PrideFlag,
|
||||||
|
type MeUser,
|
||||||
|
type APIError,
|
||||||
|
type Member,
|
||||||
|
type PronounsJson,
|
||||||
|
ErrorCode,
|
||||||
|
} from "$lib/api/entities";
|
||||||
import { apiFetchClient } from "$lib/api/fetch";
|
import { apiFetchClient } from "$lib/api/fetch";
|
||||||
import { error, redirect } from "@sveltejs/kit";
|
import { error, redirect } from "@sveltejs/kit";
|
||||||
|
|
||||||
|
@ -11,11 +18,21 @@ export const ssr = false;
|
||||||
export const load = (async ({ params }) => {
|
export const load = (async ({ params }) => {
|
||||||
try {
|
try {
|
||||||
const user = await apiFetchClient<MeUser>(`/users/@me`);
|
const user = await apiFetchClient<MeUser>(`/users/@me`);
|
||||||
const member = await apiFetchClient<Member>(`/users/@me/members/${params.memberName}`);
|
const member = await apiFetchClient<Member>(
|
||||||
|
`/users/${params.username}/members/${params.memberName}`,
|
||||||
|
);
|
||||||
const flags = await apiFetchClient<PrideFlag[]>("/users/@me/flags");
|
const flags = await apiFetchClient<PrideFlag[]>("/users/@me/flags");
|
||||||
|
|
||||||
if (user.name !== params.username || member.user.name !== params.username || member.name !== params.memberName) {
|
if (user.id !== member.user.id) {
|
||||||
throw redirect(303, `/@${user.name}/${member.name}`);
|
throw { code: ErrorCode.NotOwnMember, message: "Can only edit your own members" } as APIError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
user.name !== params.username ||
|
||||||
|
member.user.name !== params.username ||
|
||||||
|
member.name !== params.memberName
|
||||||
|
) {
|
||||||
|
throw redirect(303, `/@${user.name}/${member.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { getContext } from "svelte";
|
||||||
|
import type { Writable } from "svelte/store";
|
||||||
|
import { MAX_DESCRIPTION_LENGTH, type Member } from "$lib/api/entities";
|
||||||
|
import { charCount, renderMarkdown } from "$lib/utils";
|
||||||
|
import MarkdownHelp from "$lib/components/edit/MarkdownHelp.svelte";
|
||||||
|
import { Card, CardBody, CardHeader } from "sveltestrap";
|
||||||
|
|
||||||
|
const member = getContext<Writable<Member>>("member");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="form">
|
||||||
|
<textarea class="form-control" style="height: 200px;" bind:value={$member.bio} />
|
||||||
|
</div>
|
||||||
|
<p class="text-muted mt-1">
|
||||||
|
Using {charCount($member.bio || "")}/{MAX_DESCRIPTION_LENGTH} characters
|
||||||
|
</p>
|
||||||
|
<p class="text-muted my-2">
|
||||||
|
<MarkdownHelp />
|
||||||
|
</p>
|
||||||
|
{#if $member.bio}
|
||||||
|
<hr />
|
||||||
|
<Card>
|
||||||
|
<CardHeader>Preview</CardHeader>
|
||||||
|
<CardBody>
|
||||||
|
{@html renderMarkdown($member.bio)}
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
{/if}
|
|
@ -0,0 +1,53 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { getContext } from "svelte";
|
||||||
|
import type { Writable } from "svelte/store";
|
||||||
|
import { Alert, Button, Icon } from "sveltestrap";
|
||||||
|
|
||||||
|
import type { Member } from "$lib/api/entities";
|
||||||
|
import EditableField from "$lib/components/edit/EditableField.svelte";
|
||||||
|
|
||||||
|
const member = getContext<Writable<Member>>("member");
|
||||||
|
|
||||||
|
const moveField = (index: number, up: boolean) => {
|
||||||
|
if (up && index == 0) return;
|
||||||
|
if (!up && index == $member.fields.length - 1) return;
|
||||||
|
|
||||||
|
const newIndex = up ? index - 1 : index + 1;
|
||||||
|
|
||||||
|
const temp = $member.fields[index];
|
||||||
|
$member.fields[index] = $member.fields[newIndex];
|
||||||
|
$member.fields[newIndex] = temp;
|
||||||
|
$member.fields = [...$member.fields];
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeField = (index: number) => {
|
||||||
|
$member.fields.splice(index, 1);
|
||||||
|
$member.fields = [...$member.fields];
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if $member.fields.length === 0}
|
||||||
|
<Alert class="mt-3" color="secondary" fade={false}>
|
||||||
|
Fields are extra categories you can add separate from names and pronouns.<br />
|
||||||
|
For example, you could use them for gender terms, honorifics, or compliments.
|
||||||
|
</Alert>
|
||||||
|
{/if}
|
||||||
|
<div class="grid gap-3">
|
||||||
|
<div class="row row-cols-1 row-cols-md-2">
|
||||||
|
{#each $member.fields as _, index}
|
||||||
|
<EditableField
|
||||||
|
bind:field={$member.fields[index]}
|
||||||
|
preferences={$member.user.custom_preferences}
|
||||||
|
deleteField={() => removeField(index)}
|
||||||
|
moveField={(up) => moveField(index, up)}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
on:click={() => ($member.fields = [...$member.fields, { name: "New field", entries: [] }])}
|
||||||
|
>
|
||||||
|
<Icon name="plus" aria-hidden /> Add new field
|
||||||
|
</Button>
|
||||||
|
</div>
|
|
@ -0,0 +1,104 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { getContext } from "svelte";
|
||||||
|
import type { Writable } from "svelte/store";
|
||||||
|
import { Alert, ButtonGroup, Input } from "sveltestrap";
|
||||||
|
import type { PageData } from "./$types";
|
||||||
|
|
||||||
|
import type { Member, PrideFlag } from "$lib/api/entities";
|
||||||
|
import IconButton from "$lib/components/IconButton.svelte";
|
||||||
|
import FlagButton from "$lib/components/edit/FlagButton.svelte";
|
||||||
|
|
||||||
|
export let data: PageData;
|
||||||
|
|
||||||
|
const member = getContext<Writable<Member>>("member");
|
||||||
|
|
||||||
|
let flagSearch = "";
|
||||||
|
let filteredFlags: PrideFlag[];
|
||||||
|
$: filteredFlags = filterFlags(flagSearch, data.flags);
|
||||||
|
|
||||||
|
const filterFlags = (search: string, flags: PrideFlag[]) => {
|
||||||
|
return (
|
||||||
|
search
|
||||||
|
? flags.filter((flag) => flag.name.toLocaleLowerCase().includes(search.toLocaleLowerCase()))
|
||||||
|
: flags
|
||||||
|
).slice(0, 25);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addFlag = (flag: PrideFlag) => {
|
||||||
|
$member.flags = [...$member.flags, flag];
|
||||||
|
};
|
||||||
|
|
||||||
|
const moveFlag = (index: number, up: boolean) => {
|
||||||
|
if (up && index == 0) return;
|
||||||
|
if (!up && index == $member.flags.length - 1) return;
|
||||||
|
|
||||||
|
const newIndex = up ? index - 1 : index + 1;
|
||||||
|
|
||||||
|
const temp = $member.flags[index];
|
||||||
|
$member.flags[index] = $member.flags[newIndex];
|
||||||
|
$member.flags[newIndex] = temp;
|
||||||
|
$member.flags = [...$member.flags];
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeFlag = (index: number) => {
|
||||||
|
$member.flags.splice(index, 1);
|
||||||
|
$member.flags = [...$member.flags];
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{#each $member.flags as _, index}
|
||||||
|
<ButtonGroup class="m-1">
|
||||||
|
<IconButton
|
||||||
|
icon="chevron-left"
|
||||||
|
color="secondary"
|
||||||
|
tooltip="Move flag to the left"
|
||||||
|
click={() => moveFlag(index, true)}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
icon="chevron-right"
|
||||||
|
color="secondary"
|
||||||
|
tooltip="Move flag to the right"
|
||||||
|
click={() => moveFlag(index, false)}
|
||||||
|
/>
|
||||||
|
<FlagButton
|
||||||
|
flag={$member.flags[index]}
|
||||||
|
tooltip="Remove this flag from your profile"
|
||||||
|
on:click={() => removeFlag(index)}
|
||||||
|
/>
|
||||||
|
</ButtonGroup>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md">
|
||||||
|
<Input placeholder="Filter flags" bind:value={flagSearch} disabled={data.flags.length === 0} />
|
||||||
|
<div class="p-2">
|
||||||
|
{#each filteredFlags as flag (flag.id)}
|
||||||
|
<FlagButton {flag} tooltip="Add this flag to your profile" on:click={() => addFlag(flag)} />
|
||||||
|
{:else}
|
||||||
|
{#if data.flags.length === 0}
|
||||||
|
You haven't uploaded any flags yet.
|
||||||
|
{:else}
|
||||||
|
There are no flags matching your search <strong>{flagSearch}</strong>.
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md">
|
||||||
|
<Alert color="secondary" fade={false}>
|
||||||
|
{#if data.flags.length === 0}
|
||||||
|
<p><strong>Why can't I see any flags?</strong></p>
|
||||||
|
<p>
|
||||||
|
There are thousands of pride flags, and it would be impossible to bundle all of them by
|
||||||
|
default. Many labels also have multiple different flags that are favoured by different
|
||||||
|
people. Because of this, there are no flags available by default--instead, you can upload
|
||||||
|
flags in your <a href="/settings/flags">settings</a>. Your main profile and your member
|
||||||
|
profiles can all have different flags.
|
||||||
|
</p>
|
||||||
|
{:else}
|
||||||
|
To upload and delete flags, go to your <a href="/settings/flags">settings</a>.
|
||||||
|
{/if}
|
||||||
|
</Alert>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,63 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { getContext } from "svelte";
|
||||||
|
import type { Writable } from "svelte/store";
|
||||||
|
|
||||||
|
import type { Member } from "$lib/api/entities";
|
||||||
|
import IconButton from "$lib/components/IconButton.svelte";
|
||||||
|
|
||||||
|
const member = getContext<Writable<Member>>("member");
|
||||||
|
|
||||||
|
let newLink = "";
|
||||||
|
|
||||||
|
const addLink = (event: Event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
$member.links = [...$member.links, newLink];
|
||||||
|
newLink = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeLink = (index: number) => {
|
||||||
|
$member.links.splice(index, 1);
|
||||||
|
$member.links = [...$member.links];
|
||||||
|
};
|
||||||
|
|
||||||
|
const moveLink = (index: number, up: boolean) => {
|
||||||
|
if (up && index == 0) return;
|
||||||
|
if (!up && index == $member.links.length - 1) return;
|
||||||
|
|
||||||
|
const newIndex = up ? index - 1 : index + 1;
|
||||||
|
|
||||||
|
const temp = $member.links[index];
|
||||||
|
$member.links[index] = $member.links[newIndex];
|
||||||
|
$member.links[newIndex] = temp;
|
||||||
|
$member.links = [...$member.links];
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#each $member.links as _, index}
|
||||||
|
<div class="input-group m-1">
|
||||||
|
<IconButton
|
||||||
|
icon="chevron-up"
|
||||||
|
color="secondary"
|
||||||
|
tooltip="Move link up"
|
||||||
|
click={() => moveLink(index, true)}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
icon="chevron-down"
|
||||||
|
color="secondary"
|
||||||
|
tooltip="Move link down"
|
||||||
|
click={() => moveLink(index, false)}
|
||||||
|
/>
|
||||||
|
<input type="text" class="form-control" bind:value={$member.links[index]} />
|
||||||
|
<IconButton
|
||||||
|
color="danger"
|
||||||
|
icon="trash3"
|
||||||
|
tooltip="Remove link"
|
||||||
|
click={() => removeLink(index)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
<form class="input-group m-1" on:submit={addLink}>
|
||||||
|
<input type="text" class="form-control" bind:value={newLink} />
|
||||||
|
<IconButton type="submit" color="success" icon="plus" tooltip="Add link" />
|
||||||
|
</form>
|
|
@ -0,0 +1,99 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { getContext } from "svelte";
|
||||||
|
import type { Writable } from "svelte/store";
|
||||||
|
import { DateTime } from "luxon";
|
||||||
|
import { Button, ButtonGroup, Icon } from "sveltestrap";
|
||||||
|
|
||||||
|
import type { APIError, Member } from "$lib/api/entities";
|
||||||
|
import { PUBLIC_SHORT_BASE } from "$env/static/public";
|
||||||
|
import IconButton from "$lib/components/IconButton.svelte";
|
||||||
|
import { apiFetchClient } from "$lib/api/fetch";
|
||||||
|
import { addToast } from "$lib/toast";
|
||||||
|
import type { PageData } from "./$types";
|
||||||
|
import ErrorAlert from "$lib/components/ErrorAlert.svelte";
|
||||||
|
|
||||||
|
export let data: PageData;
|
||||||
|
|
||||||
|
const member = getContext<Writable<Member>>("member");
|
||||||
|
|
||||||
|
let error: APIError | null = null;
|
||||||
|
|
||||||
|
const now = DateTime.now().toLocal();
|
||||||
|
let canRerollSid: boolean;
|
||||||
|
$: canRerollSid =
|
||||||
|
now.diff(DateTime.fromISO(data.user.last_sid_reroll).toLocal(), "hours").hours >= 1;
|
||||||
|
|
||||||
|
const rerollSid = async () => {
|
||||||
|
try {
|
||||||
|
const resp = await apiFetchClient<Member>(`/members/${data.member.id}/reroll`);
|
||||||
|
addToast({ header: "Success", body: "Rerolled short ID!" });
|
||||||
|
error = null;
|
||||||
|
$member.sid = resp.sid;
|
||||||
|
} catch (e) {
|
||||||
|
error = e as APIError;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const copyShortURL = async () => {
|
||||||
|
const url = `${PUBLIC_SHORT_BASE}/${data.member.sid}`;
|
||||||
|
await navigator.clipboard.writeText(url);
|
||||||
|
addToast({ body: "Copied the short link to your clipboard!", duration: 2000 });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if error}
|
||||||
|
<ErrorAlert {error} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md">
|
||||||
|
<div class="form-check">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked={$member.unlisted}
|
||||||
|
id="unlisted"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" for="unlisted">Hide from member list</label>
|
||||||
|
</div>
|
||||||
|
<p class="text-muted mt-1">
|
||||||
|
{#if data.user.list_private}
|
||||||
|
<Icon name="exclamation-triangle-fill" aria-hidden />
|
||||||
|
Your member list is currently hidden, so <strong>this setting has no effect</strong>. If you
|
||||||
|
want to make your member list visible again,
|
||||||
|
<a href="/@{$member.user.name}/other">edit your user profile</a>.
|
||||||
|
<br />
|
||||||
|
{/if}
|
||||||
|
<Icon name="info-circle-fill" aria-hidden />
|
||||||
|
This <em>only</em> hides this member from your member list.
|
||||||
|
<strong>
|
||||||
|
This member will still be visible to anyone at
|
||||||
|
<code class="text-nowrap">pronouns.cc/@{$member.user.name}/{$member.name}</code>.
|
||||||
|
</strong>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{#if PUBLIC_SHORT_BASE}
|
||||||
|
<div class="col-md">
|
||||||
|
<p>
|
||||||
|
Current short ID: <code>{$member.sid}</code>
|
||||||
|
<ButtonGroup class="mb-1">
|
||||||
|
<Button color="secondary" disabled={!canRerollSid} on:click={() => rerollSid()}
|
||||||
|
>Reroll short ID</Button
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
icon="link-45deg"
|
||||||
|
tooltip="Copy short link"
|
||||||
|
color="secondary"
|
||||||
|
click={copyShortURL}
|
||||||
|
/>
|
||||||
|
</ButtonGroup>
|
||||||
|
<br />
|
||||||
|
<span class="text-muted">
|
||||||
|
<Icon name="info-circle-fill" aria-hidden />
|
||||||
|
This ID is used in <code>prns.cc</code> links. You can reroll one short ID every hour (shared
|
||||||
|
between your main profile and all members) by pressing the button above.
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
|
@ -0,0 +1,84 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { getContext } from "svelte";
|
||||||
|
import type { Writable } from "svelte/store";
|
||||||
|
import type { Member } from "$lib/api/entities";
|
||||||
|
import { Button, Icon, Popover } from "sveltestrap";
|
||||||
|
import EditablePronouns from "$lib/components/edit/EditablePronouns.svelte";
|
||||||
|
import IconButton from "$lib/components/IconButton.svelte";
|
||||||
|
import type { PageData } from "./$types";
|
||||||
|
|
||||||
|
export let data: PageData;
|
||||||
|
|
||||||
|
const member = getContext<Writable<Member>>("member");
|
||||||
|
let newPronouns = "";
|
||||||
|
|
||||||
|
const movePronoun = (index: number, up: boolean) => {
|
||||||
|
if (up && index == 0) return;
|
||||||
|
if (!up && index == $member.pronouns.length - 1) return;
|
||||||
|
|
||||||
|
const newIndex = up ? index - 1 : index + 1;
|
||||||
|
|
||||||
|
const temp = $member.pronouns[index];
|
||||||
|
$member.pronouns[index] = $member.pronouns[newIndex];
|
||||||
|
$member.pronouns[newIndex] = temp;
|
||||||
|
$member.pronouns = [...$member.pronouns];
|
||||||
|
};
|
||||||
|
|
||||||
|
const addPronouns = (event: Event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (newPronouns in data.pronouns) {
|
||||||
|
const fullSet = data.pronouns[newPronouns];
|
||||||
|
$member.pronouns = [
|
||||||
|
...$member.pronouns,
|
||||||
|
{
|
||||||
|
pronouns: fullSet.pronouns.join("/"),
|
||||||
|
display_text: fullSet.display || null,
|
||||||
|
status: "okay",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$member.pronouns = [
|
||||||
|
...$member.pronouns,
|
||||||
|
{ pronouns: newPronouns, display_text: null, status: "okay" },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
newPronouns = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const removePronoun = (index: number) => {
|
||||||
|
$member.pronouns.splice(index, 1);
|
||||||
|
$member.pronouns = [...$member.pronouns];
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#each $member.pronouns as _, index}
|
||||||
|
<EditablePronouns
|
||||||
|
bind:pronoun={$member.pronouns[index]}
|
||||||
|
preferences={$member.user.custom_preferences}
|
||||||
|
moveUp={() => movePronoun(index, true)}
|
||||||
|
moveDown={() => movePronoun(index, false)}
|
||||||
|
remove={() => removePronoun(index)}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
<form class="input-group m-1" on:submit={addPronouns}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="New pronouns"
|
||||||
|
bind:value={newPronouns}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
type="submit"
|
||||||
|
color="success"
|
||||||
|
icon="plus"
|
||||||
|
tooltip="Add pronouns"
|
||||||
|
disabled={newPronouns === ""}
|
||||||
|
/>
|
||||||
|
<Button id="pronouns-help" color="secondary"><Icon name="question" /></Button>
|
||||||
|
<Popover target="pronouns-help" placement="bottom">
|
||||||
|
For common pronouns, the short form (e.g. "she/her" or "he/him") is enough; for less common
|
||||||
|
pronouns, you will have to use all five forms (e.g. "ce/cir/cir/cirs/cirself").
|
||||||
|
</Popover>
|
||||||
|
</form>
|
Loading…
Reference in a new issue