From 2acdca681a15a82996e16c34dc103350d2922c65 Mon Sep 17 00:00:00 2001 From: Michael Fatemi Date: Wed, 11 Aug 2021 16:09:11 -0400 Subject: [PATCH] add admin stuff --- src/components/Group/Group.tsx | 20 ++++- ...eCodeGenerator.tsx => GroupInviteCode.tsx} | 81 +++++++++++-------- src/components/Group/GroupMembersLink.tsx | 56 +++++++++++-- src/components/api.ts | 16 ++++ src/components/types.ts | 4 + 5 files changed, 136 insertions(+), 41 deletions(-) rename src/components/Group/{GroupInviteCodeGenerator.tsx => GroupInviteCode.tsx} (56%) diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 642984c..9ff5aff 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -1,7 +1,9 @@ +import { useMemo } from 'react'; import { createContext, useEffect, useState } from 'react'; import { getGroup } from '../api'; import EventCreatorLink from '../EventCreator/EventCreatorLink'; import EventStream from '../EventStream'; +import { useMe } from '../hooks'; import { IGroup } from '../types'; import UILink from '../UI/UILink'; import useImmutable from '../useImmutable'; @@ -13,6 +15,7 @@ const DEFAULT_GROUP = (): IGroup => ({ name: '', users: [], events: [], + admins: [], joinCode: null, }); @@ -21,6 +24,7 @@ export const GroupContext = createContext({ group: DEFAULT_GROUP() }); export default function Group({ id }: { id: number }) { const [group, setGroup] = useImmutable(null); const [loading, setLoading] = useState(false); + const me = useMe(); useEffect(() => { setLoading(true); @@ -29,6 +33,14 @@ export default function Group({ id }: { id: number }) { .finally(() => setLoading(false)); }, [id, setGroup]); + const isAdmin = useMemo(() => { + if (!group) { + return false; + } + + return group.admins.some((a) => a.id === me?.id); + }, [group, me?.id]); + if (loading) { return

Loading...

; } @@ -48,8 +60,12 @@ export default function Group({ id }: { id: number }) {

- -
+ {isAdmin && ( + <> + +
+ + )}
diff --git a/src/components/Group/GroupInviteCodeGenerator.tsx b/src/components/Group/GroupInviteCode.tsx similarity index 56% rename from src/components/Group/GroupInviteCodeGenerator.tsx rename to src/components/Group/GroupInviteCode.tsx index 31a8a40..fda0e3f 100644 --- a/src/components/Group/GroupInviteCodeGenerator.tsx +++ b/src/components/Group/GroupInviteCode.tsx @@ -1,12 +1,21 @@ +import { useMemo } from 'react'; import { useCallback, useContext, useState } from 'react'; import { lightgrey } from '../../lib/colors'; import { generateCode, resetCode } from '../api'; +import { useMe } from '../hooks'; import UIButton from '../UI/UIButton'; import { GroupContext } from './Group'; -export default function GroupInviteCodeGenerator() { +export default function GroupInviteCode() { const { group } = useContext(GroupContext); + const me = useMe(); + + const isAdmin = useMemo( + () => group.admins.some((a) => a.id === me?.id), + [group.admins, me?.id] + ); + const [shown, setShown] = useState(false); const generateJoinCode = useCallback(() => { @@ -34,44 +43,50 @@ export default function GroupInviteCodeGenerator() { {' '} (click to show/hide) -
- - Reset - - - Regenerate - -
+ {isAdmin && ( +
+ + Reset + + + Regenerate + +
+ )} ); } else { return ( <> This group has no way for new members to join. - - Generate a code - + {isAdmin ? ( + + Generate a code + + ) : ( + 'Contact an admin to create a code' + )} ); } diff --git a/src/components/Group/GroupMembersLink.tsx b/src/components/Group/GroupMembersLink.tsx index 136b7e8..fc135b9 100644 --- a/src/components/Group/GroupMembersLink.tsx +++ b/src/components/Group/GroupMembersLink.tsx @@ -1,15 +1,47 @@ -import { useContext, useState } from 'react'; +import { useCallback, useContext, useMemo, useState } from 'react'; +import { addGroupAdmin, removeGroupAdmin } from '../api'; +import { useMe } from '../hooks'; import UIPressable from '../UI/UIPressable'; import UISecondaryBox from '../UI/UISecondaryBox'; import { GroupContext } from './Group'; -import GroupInviteCodeGenerator from './GroupInviteCodeGenerator'; +import GroupInviteCode from './GroupInviteCode'; export default function GroupMembersLink() { const [open, setOpen] = useState(false); const { group } = useContext(GroupContext); + const me = useMe(); - const handleClick = () => setOpen(!open); + const handleClick = useCallback(() => setOpen((o) => !o), []); + + const adminIds = useMemo( + () => new Set(group.admins.map((a) => a.id)), + [group.admins] + ); + + const addAdmin = useCallback( + (adminId: number, adminName: string) => { + addGroupAdmin(group.id, adminId).then(({ status }) => { + if (status === 'success') { + group.admins.push({ id: adminId, name: adminName }); + } + }); + }, + [group.admins, group.id] + ); + + const amIAdmin = me?.id ? adminIds.has(me.id) : false; + + const removeAdmin = useCallback( + (adminId: number) => { + removeGroupAdmin(group.id, adminId).then((res) => { + if (res.status === 'success') { + group.admins = group.admins.filter((admin) => admin.id !== adminId); + } + }); + }, + [group] + ); return ( <> @@ -20,12 +52,24 @@ export default function GroupMembersLink() {

Members

- {group.users.map(({ name }) => ( - {name} + {group.users.map(({ name, id }) => ( + + {name} {adminIds.has(id) && ' (admin)'}{' '} + {amIAdmin && + (adminIds.has(id) ? ( + + ) : ( + + ))} + ))}
- +
)} diff --git a/src/components/api.ts b/src/components/api.ts index 8ede23c..53faf81 100644 --- a/src/components/api.ts +++ b/src/components/api.ts @@ -145,6 +145,22 @@ export async function createGroup(name: string): Promise<{ id: number }> { return { id }; } +export async function addGroupAdmin( + id: number, + userId: number +): Promise<{ status: 'success' | 'error' }> { + const { status } = await post(`/groups/${id}/add_admin`, { userId }); + return { status }; +} + +export async function removeGroupAdmin( + id: number, + userId: number +): Promise<{ status: 'success' | 'error' }> { + const { status } = await post(`/groups/${id}/remove_admin`, { userId }); + return { status }; +} + export async function getNotifications() { return await get('/users/@me/received_requests_and_invites'); } diff --git a/src/components/types.ts b/src/components/types.ts index aa0d2bb..106ddb5 100644 --- a/src/components/types.ts +++ b/src/components/types.ts @@ -54,6 +54,10 @@ export type IGroup = { id: number; name: string; }[]; + admins: { + id: number; + name: string; + }[]; joinCode: string | null; };