diff --git a/src/components/EventCreator/EventCreatorLink.tsx b/src/components/EventCreator/EventCreatorLink.tsx index 9c73eda..ce0b10c 100644 --- a/src/components/EventCreator/EventCreatorLink.tsx +++ b/src/components/EventCreator/EventCreatorLink.tsx @@ -1,9 +1,11 @@ -import EventCreator from './EventCreator'; -import { IGroup } from '../types'; +import { useContext } from 'react'; +import { GroupContext } from '../Group/Group'; import useToggle from '../useToggle'; +import EventCreator from './EventCreator'; -export default function EventCreatorLink({ group }: { group: IGroup }) { +export default function EventCreatorLink() { const [open, toggle] = useToggle(false); + const { group } = useContext(GroupContext); return ( <div> diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index f0ef167..beb2a75 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -1,38 +1,59 @@ +import { createContext, useEffect } from 'react'; +import { getGroup } from '../api'; import EventCreatorLink from '../EventCreator/EventCreatorLink'; import EventStream from '../EventStream'; -import GroupSettingsLink from './GroupSettingsLink'; import { IGroup } from '../types'; import UILink from '../UI/UILink'; +import useImmutable from '../useImmutable'; import GroupMembersLink from './GroupMembersLink'; +import GroupSettingsLink from './GroupSettingsLink'; + +const DEFAULT_GROUP = (): IGroup => ({ + id: 0, + name: '', + users: [], + events: [], + joinCode: null, +}); + +export const GroupContext = createContext({ group: DEFAULT_GROUP() }); + +export default function Group({ id }: { id: number }) { + const [group, setGroup] = useImmutable<IGroup>(DEFAULT_GROUP()); + + useEffect(() => { + getGroup(id).then(setGroup); + }, [id, setGroup]); -export default function Group({ group }: { group: IGroup }) { return ( - <div - style={{ - textAlign: 'center', - maxWidth: '30rem', - marginLeft: 'auto', - marginRight: 'auto', - }} - > - <h1>{group.name}</h1> - <UILink href="/">Home</UILink> - <br /> - <br /> - <GroupMembersLink group={group} /> - <br /> - <GroupSettingsLink group={group} /> - <br /> - <EventCreatorLink group={group} /> - <br /> + <GroupContext.Provider value={{ group }}> + <div + style={{ + textAlign: 'center', + maxWidth: '30rem', + marginLeft: 'auto', + marginRight: 'auto', + }} + > + <h1>{group.name}</h1> + <UILink href="/">Home</UILink> + <br /> + <br /> + <GroupMembersLink /> + <br /> + <GroupSettingsLink /> + <br /> + <EventCreatorLink /> + <br /> - {group.events.length > 0 ? ( - <EventStream events={group.events} /> - ) : ( - <span> - There are no events yet. Click 'create event' above to add one! - </span> - )} - </div> + {group.events.length > 0 ? ( + <EventStream events={group.events} /> + ) : ( + <span> + There are no events yet. Click 'create event' above to add one! + </span> + )} + </div> + </GroupContext.Provider> ); } diff --git a/src/components/Group/GroupInviteCodeGenerator.tsx b/src/components/Group/GroupInviteCodeGenerator.tsx new file mode 100644 index 0000000..caecffe --- /dev/null +++ b/src/components/Group/GroupInviteCodeGenerator.tsx @@ -0,0 +1,72 @@ +import { useCallback, useContext } from 'react'; +import { lightgrey } from '../../lib/colors'; +import { generateCode, resetCode } from '../api'; +import UIButton from '../UI/UIButton'; +import { GroupContext } from './Group'; + +export default function GroupInviteCodeGenerator() { + const { group } = useContext(GroupContext); + + const generateJoinCode = useCallback(() => { + generateCode(group.id).then((code) => { + group.joinCode = code; + }); + }, [group]); + + const resetJoinCode = useCallback(() => { + resetCode(group.id).then((code) => { + group.joinCode = null; + }); + }, [group]); + + if (group.joinCode) { + return ( + <> + <span> + Join this group with the code{' '} + <b> + <code>{group.joinCode}</code> + </b> + </span> + <div style={{ display: 'flex', justifyContent: 'space-between' }}> + <UIButton + onClick={resetJoinCode} + style={{ + backgroundColor: lightgrey, + margin: '0.5rem', + flex: 1, + }} + > + Reset + </UIButton> + <UIButton + onClick={generateJoinCode} + style={{ + backgroundColor: lightgrey, + margin: '0.5rem', + flex: 1, + }} + > + Regenerate + </UIButton> + </div> + </> + ); + } else { + return ( + <> + This group has no way for new members to join. + <UIButton + onClick={generateJoinCode} + style={{ + backgroundColor: lightgrey, + marginTop: '0.5rem', + marginBottom: '0.5rem', + }} + > + Generate a code + </UIButton> + </> + ); + } +} diff --git a/src/components/Group/GroupMembersLink.tsx b/src/components/Group/GroupMembersLink.tsx index 4cd2b6e..9c030a2 100644 --- a/src/components/Group/GroupMembersLink.tsx +++ b/src/components/Group/GroupMembersLink.tsx @@ -1,11 +1,14 @@ -import { useState } from 'react'; -import { IGroup } from '../types'; +import { useContext, useState } from 'react'; import UIPressable from '../UI/UIPressable'; import UISecondaryBox from '../UI/UISecondaryBox'; +import { GroupContext } from './Group'; +import GroupInviteCodeGenerator from './GroupInviteCodeGenerator'; -export default function GroupMembersLink({ group }: { group: IGroup }) { +export default function GroupMembersLink() { const [open, setOpen] = useState(false); + const { group } = useContext(GroupContext); + const handleClick = () => setOpen(!open); return ( @@ -16,6 +19,9 @@ export default function GroupMembersLink({ group }: { group: IGroup }) { <br /> <UISecondaryBox> <h1>Members</h1> + + <GroupInviteCodeGenerator /> + {group.users.map(({ name }) => ( <span key={name}>{name}</span> ))} diff --git a/src/components/Group/GroupPage.tsx b/src/components/Group/GroupPage.tsx index bf69432..b7caf0e 100644 --- a/src/components/Group/GroupPage.tsx +++ b/src/components/Group/GroupPage.tsx @@ -1,43 +1,8 @@ -import { useEffect, useState } from 'react'; import { useParams } from 'react-router'; -import { Link } from 'react-router-dom'; -import { getGroup } from '../api'; -import { IGroup } from '../types'; import Group from './Group'; export default function GroupPage() { const { id } = useParams<{ id: string }>(); - const [loading, setLoading] = useState(true); - const [group, setGroup] = useState<IGroup | null>(null); - useEffect(() => { - if (isNaN(+id)) { - setLoading(false); - return; - } - - async function load() { - setLoading(true); - getGroup(+id) - .then(setGroup) - .finally(() => setLoading(false)); - } - - load(); - }, [id]); - - if (!group && !loading) { - return ( - <div style={{ textAlign: 'center' }}> - <h1>Group Not Found</h1> - <Link to="/">Home</Link> - </div> - ); - } - - if (!group) { - return null; - } - - return <Group group={group} />; + return <Group id={+id} />; } diff --git a/src/components/Group/GroupSettingsLink.tsx b/src/components/Group/GroupSettingsLink.tsx index 7b97911..a3ad286 100644 --- a/src/components/Group/GroupSettingsLink.tsx +++ b/src/components/Group/GroupSettingsLink.tsx @@ -1,9 +1,11 @@ -import { IGroup } from '../types'; +import { useContext } from 'react'; import useToggle from '../useToggle'; +import { GroupContext } from './Group'; import GroupSettings from './GroupSettings'; -export default function GroupSettingsLink({ group }: { group: IGroup }) { +export default function GroupSettingsLink() { const [open, toggle] = useToggle(false); + const { group } = useContext(GroupContext); return ( <div> diff --git a/src/components/api.ts b/src/components/api.ts index d4c98a5..b8c9de0 100644 --- a/src/components/api.ts +++ b/src/components/api.ts @@ -166,7 +166,9 @@ export async function joinGroup(id: number, code: string) { } export async function generateCode(groupId: number) { - return await post('/groups/' + groupId + '/generate_code', {}); + const { code } = await post(`/groups/${groupId}/generate_code`, {}); + + return code; } export async function resetCode(groupId: number) { diff --git a/src/components/types.ts b/src/components/types.ts index afa9c89..e6a05eb 100644 --- a/src/components/types.ts +++ b/src/components/types.ts @@ -54,6 +54,7 @@ export type IGroup = { id: number; name: string; }[]; + joinCode: string | null; }; /**