mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-08 23:07:54 +00:00
refactor(dashboard): use svelte context for lists
This commit is contained in:
parent
f2e160c526
commit
a48f2b40c4
23 changed files with 598 additions and 433 deletions
|
|
@ -142,9 +142,9 @@
|
|||
<Link to={getGroupPageUrl(true)}><button class="link-button button-right btn btn-secondary" style={isPublic ? "float: none !important; margin-left: 0;" : ""} tabindex={-1} aria-label="randomize group members">Randomize group</button></Link>
|
||||
|
||||
{:else if editMode}
|
||||
<Edit on:update on:deletion bind:group bind:editMode />
|
||||
<Edit {group} bind:editMode />
|
||||
{:else if memberMode}
|
||||
<MemberEdit on:updateGroupMembers bind:group bind:memberMode bind:members />
|
||||
<MemberEdit {group} bind:memberMode />
|
||||
{/if}
|
||||
</CardBody>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { Card, CardHeader, CardTitle, Modal, Button, ListGroup, ListGroupItem, Label, Input, Alert, Tooltip, Row, Col } from 'sveltestrap';
|
||||
import { getContext } from 'svelte';
|
||||
import { CardHeader, CardTitle, Modal, Button, ListGroup, ListGroupItem, Label, Input, Alert, Tooltip, Row, Col } from 'sveltestrap';
|
||||
import parseMarkdown from '../../api/parse-markdown';
|
||||
import twemoji from 'twemoji';
|
||||
import { Link } from 'svelte-navigator';
|
||||
|
|
@ -12,16 +12,16 @@
|
|||
import FaTimes from 'svelte-icons/fa/FaTimes.svelte'
|
||||
import FaCheck from 'svelte-icons/fa/FaCheck.svelte'
|
||||
|
||||
import type { Group, Member} from '../../api/types';
|
||||
import type { Group, Member } from '../../api/types';
|
||||
import api from '../../api';
|
||||
import default_avatar from '../../assets/default_avatar.png';
|
||||
import resizeMedia from '../../api/resize-media';
|
||||
import AwaitHtml from '../common/AwaitHtml.svelte';
|
||||
import type { Writable } from 'svelte/store';
|
||||
|
||||
export let group: Group;
|
||||
export let searchBy: string;
|
||||
export let sortBy: string;
|
||||
export let members: Member[];
|
||||
export let isPublic = false;
|
||||
export let isDash = false;
|
||||
|
||||
|
|
@ -60,17 +60,14 @@
|
|||
|
||||
let altText = `member ${group.name} avatar`;
|
||||
|
||||
$: memberList = members && members.filter(m => group.members && group.members.includes(m.uuid) && true).sort((a, b) => a.name.localeCompare(b.name)) || [];
|
||||
$: members = getContext<Writable<Member[]>>("members")
|
||||
$: memberList = $members && $members.filter(m => group.members && group.members.includes(m.uuid) && true).sort((a, b) => a.name.localeCompare(b.name)) || [];
|
||||
|
||||
$: groups = getContext<Writable<Member[]>>("groups")
|
||||
let listGroupElements = [];
|
||||
|
||||
let pageLink = isPublic ? `/profile/g/${group.id}` : `/dash/g/${group.id}`;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
function update(group: Group) {
|
||||
dispatch('update', group);
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
let data = input;
|
||||
err = [];
|
||||
|
|
@ -93,9 +90,17 @@
|
|||
loading = true;
|
||||
try {
|
||||
let res = await api().groups(group.id).patch({data});
|
||||
update({...group, ...res});
|
||||
group = {...group, ...res};
|
||||
const newgroup = {...group, ...res};
|
||||
const newList = $groups.map((g: Group) => {
|
||||
if (group.uuid === g.uuid) {
|
||||
g = newgroup
|
||||
}
|
||||
return g
|
||||
})
|
||||
groups.set(newList)
|
||||
|
||||
err = [];
|
||||
loading = false;
|
||||
view = 'card';
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
|
@ -179,7 +184,7 @@
|
|||
<Input bind:value={input.icon} maxlength={256} type="url" placeholder={group.icon} aria-label="group avatar url"/>
|
||||
<hr style="min-height: 1px" />
|
||||
<Label>Display name:</Label>
|
||||
<textarea class="form-control mb-2" style="resize: none; height: 1em" bind:value={input.display_name} maxlength={100} type="text" placeholder={group.display_name} aria-label="group display name" />
|
||||
<textarea class="form-control mb-2" style="resize: none; height: 1em" bind:value={input.display_name} maxlength={100} placeholder={group.display_name} aria-label="group display name" />
|
||||
<hr style="min-height: 1px" />
|
||||
<Label>Description:</Label>
|
||||
<textarea class="form-control" style="resize: none; overflow: hidden;" bind:value={input.description} maxlength={1000} use:autoresize placeholder={group.description} aria-label="group description"/>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<script lang="ts">
|
||||
import type { Writable } from 'svelte/store';
|
||||
import { Row, Col, Input, Button, Label, Alert, Spinner, Modal, ModalHeader, ModalBody } from 'sveltestrap';
|
||||
import { createEventDispatcher, tick } from 'svelte';
|
||||
import { getContext, tick } from 'svelte';
|
||||
import type { Group } from '../../api/types';
|
||||
import api from '../../api';
|
||||
import { autoresize } from 'svelte-textarea-autoresize';
|
||||
|
|
@ -13,19 +14,11 @@
|
|||
export let group: Group;
|
||||
export let editMode: boolean;
|
||||
|
||||
$: groups = getContext<Writable<Group[]>>("groups")
|
||||
|
||||
let err: string[] = [];
|
||||
|
||||
let input: Group = group;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
function deletion() {
|
||||
dispatch('deletion', group.id);
|
||||
}
|
||||
|
||||
function update(group: Group) {
|
||||
dispatch('update', group);
|
||||
}
|
||||
let input: Group = JSON.parse(JSON.stringify(group));
|
||||
|
||||
async function submit() {
|
||||
let data = input;
|
||||
|
|
@ -49,7 +42,15 @@
|
|||
loading = true;
|
||||
try {
|
||||
let res = await api().groups(group.id).patch({data});
|
||||
update({...group, ...res});
|
||||
const newgroup = {...group, ...res};
|
||||
const newList = $groups.map((g: Group) => {
|
||||
if (group.uuid === g.uuid) {
|
||||
g = newgroup
|
||||
}
|
||||
return g
|
||||
})
|
||||
groups.set(newList);
|
||||
|
||||
err = [];
|
||||
editMode = false;
|
||||
return;
|
||||
|
|
@ -85,7 +86,10 @@
|
|||
deleteErr = null;
|
||||
toggleDeleteModal();
|
||||
loading = false;
|
||||
deletion();
|
||||
|
||||
const newList = $groups.filter((g: Group) => group.uuid !== g.uuid)
|
||||
groups.set(newList);
|
||||
|
||||
} catch (error) {
|
||||
loading = false;
|
||||
}
|
||||
|
|
@ -110,7 +114,7 @@
|
|||
</Col>
|
||||
<Col xs={12} lg={4} class="mb-2">
|
||||
<Label>Display name:</Label>
|
||||
<textarea class="form-control" style="resize: none; height: 1em" bind:value={input.display_name} maxlength={100} type="text" placeholder={group.display_name} aria-label="group display name"/>
|
||||
<textarea class="form-control" style="resize: none; height: 1em" bind:value={input.display_name} maxlength={100} placeholder={group.display_name} aria-label="group display name"/>
|
||||
</Col>
|
||||
<Col xs={12} lg={4} class="mb-2">
|
||||
<Label>Color:</Label>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
import type { Writable } from 'svelte/store';
|
||||
import { Row, Col, Button, Alert, ListGroup, ListGroupItem, Spinner } from 'sveltestrap';
|
||||
import ListPagination from "../common/ListPagination.svelte";
|
||||
import twemoji from "twemoji";
|
||||
|
|
@ -14,7 +16,9 @@
|
|||
let err: string;
|
||||
export let group: Group;
|
||||
export let memberMode: boolean = true;
|
||||
export let members: Member[];
|
||||
|
||||
$: groups = getContext<Writable<Group[]>>("groups")
|
||||
$: members = getContext<Writable<Member[]>>("members")
|
||||
|
||||
let membersInGroup: Member[];
|
||||
let membersNotInGroup: Member[];
|
||||
|
|
@ -28,18 +32,13 @@
|
|||
|
||||
let smallPages = true;
|
||||
|
||||
updateMemberList();
|
||||
|
||||
function updateMemberList() {
|
||||
membersInGroup = members.filter(member => group.members.includes(member.uuid));
|
||||
membersInGroup = membersInGroup.sort((a, b) => a.name.localeCompare(b.name));
|
||||
$: membersInGroup = $members.filter(member => group.members.includes(member.uuid)).sort((a, b) => a.name.localeCompare(b.name));;
|
||||
|
||||
membersNotInGroup = members.filter(member => !group.members.includes(member.uuid));
|
||||
membersNotInGroup = membersNotInGroup.sort((a, b) => a.name.localeCompare(b.name));
|
||||
$: membersNotInGroup = $members.filter(member => !group.members.includes(member.uuid)).sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
membersInGroupSelection = membersInGroup.map(function(member) { return {name: member.name, shortid: member.id, id: member.uuid, display_name: member.display_name}; }).sort((a, b) => a.name.localeCompare(b.name));
|
||||
membersNotInGroupSelection = membersNotInGroup.map(function(member) { return {name: member.name, shortid: member.id, id: member.uuid, display_name: member.display_name}; }).sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
$: membersInGroupSelection = membersInGroup.map(function(member) { return {name: member.name, shortid: member.id, id: member.uuid, display_name: member.display_name}; }).sort((a, b) => a.name.localeCompare(b.name));
|
||||
$: membersNotInGroupSelection = membersNotInGroup.map(function(member) { return {name: member.name, shortid: member.id, id: member.uuid, display_name: member.display_name}; }).sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
$: indexOfLastItem = currentPage * 10;
|
||||
$: indexOfFirstItem = indexOfLastItem - 10;
|
||||
|
|
@ -69,7 +68,13 @@ function memberListRenderer(item: any) {
|
|||
loading = true;
|
||||
await api().groups(group.id).members.add.post({data});
|
||||
data.forEach(member => group.members.push(member));
|
||||
updateMemberList();
|
||||
$groups.forEach(g => {
|
||||
if (g.uuid === group.uuid) {
|
||||
g.members = group.members
|
||||
}
|
||||
})
|
||||
groups.set($groups)
|
||||
|
||||
err = null;
|
||||
membersToBeAdded = [];
|
||||
loading = false;
|
||||
|
|
@ -86,7 +91,13 @@ function memberListRenderer(item: any) {
|
|||
loading = true;
|
||||
await api().groups(group.id).members.remove.post({data});
|
||||
group.members = group.members.filter(m => !data.includes(m));
|
||||
updateMemberList();
|
||||
$groups.forEach(g => {
|
||||
if (g.uuid === group.uuid) {
|
||||
g.members = group.members
|
||||
}
|
||||
})
|
||||
groups.set($groups)
|
||||
|
||||
err = null;
|
||||
membersToBeRemoved = [];
|
||||
loading = false;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
import type { Group } from '../../api/types';
|
||||
import api from '../../api';
|
||||
import { autoresize } from 'svelte-textarea-autoresize';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { getContext } from 'svelte';
|
||||
import FaPlus from 'svelte-icons/fa/FaPlus.svelte';
|
||||
import type { Writable } from 'svelte/store';
|
||||
|
||||
const descriptions: string[] = JSON.parse(localStorage.getItem("pk-config"))?.description_templates;
|
||||
|
||||
|
|
@ -24,11 +25,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
function create(data: Group) {
|
||||
dispatch('create', data);
|
||||
}
|
||||
$: groups = getContext<Writable<Group[]>>("groups");
|
||||
|
||||
let input: Group = JSON.parse(JSON.stringify(defaultGroup));
|
||||
|
||||
|
|
@ -55,7 +52,10 @@
|
|||
try {
|
||||
let res: Group = await api().groups().post({data});
|
||||
res.members = [];
|
||||
create(res);
|
||||
|
||||
$groups.push(res)
|
||||
groups.set($groups)
|
||||
|
||||
input = JSON.parse(JSON.stringify(defaultGroup));
|
||||
message = `Group ${data.name} successfully created!`
|
||||
err = [];
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
<script lang="ts">
|
||||
import { tick, createEventDispatcher } from "svelte";
|
||||
import { tick, getContext } from "svelte";
|
||||
import { ModalBody, ModalHeader, Col, Row, Input, Label, ModalFooter, Button, Spinner, Alert } from "sveltestrap";
|
||||
|
||||
import type { Group, GroupPrivacy } from '../../api/types';
|
||||
import api from '../../api';
|
||||
import type { Writable } from "svelte/store";
|
||||
|
||||
export let privacyOpen: boolean;
|
||||
export let group: Group;
|
||||
const togglePrivacyModal = () => (privacyOpen = !privacyOpen);
|
||||
|
||||
$: groups = getContext<Writable<Group[]>>("groups")
|
||||
|
||||
let err: string;
|
||||
let loading = false;
|
||||
let success = false;
|
||||
|
|
@ -18,12 +21,6 @@
|
|||
Object.keys(privacy).forEach(x => privacy[x] = target.value);
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
function update(group) {
|
||||
dispatch('update', group);
|
||||
}
|
||||
|
||||
// I can't use the hacked together Required<T> type from the bulk privacy here
|
||||
// that breaks updating the displayed privacy after submitting
|
||||
// but there's not really any way for any privacy fields here to be missing
|
||||
|
|
@ -43,7 +40,15 @@
|
|||
const data: Group = {privacy: privacy};
|
||||
try {
|
||||
let res = await api().groups(group.id).patch({data});
|
||||
group = {...group, ...res};
|
||||
const newgroup = {...group, ...res};
|
||||
const newList = $groups.map((g: Group) => {
|
||||
if (group.uuid === g.uuid) {
|
||||
g = newgroup
|
||||
}
|
||||
return g
|
||||
})
|
||||
groups.set(newList);
|
||||
|
||||
success = true;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue