From 6372d262cbcee5b7835a9a8f96021c1beb9cc915 Mon Sep 17 00:00:00 2001 From: Pico2x Date: Fri, 26 Aug 2022 18:36:04 +0100 Subject: [PATCH] Dashboards in Group about page --- common/group.ts | 1 + firestore.rules | 2 +- web/components/groups/group-dashboard.tsx | 143 ++++++++++++++++++++++ web/lib/firebase/groups.ts | 7 ++ web/pages/group/[...slugs]/index.tsx | 22 +++- 5 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 web/components/groups/group-dashboard.tsx diff --git a/common/group.ts b/common/group.ts index 7d3215ae..c449f451 100644 --- a/common/group.ts +++ b/common/group.ts @@ -10,6 +10,7 @@ export type Group = { anyoneCanJoin: boolean contractIds: string[] + dashboardId?: string chatDisabled?: boolean mostRecentChatActivityTime?: number mostRecentContractAddedTime?: number diff --git a/firestore.rules b/firestore.rules index fe45071b..122ceed8 100644 --- a/firestore.rules +++ b/firestore.rules @@ -160,7 +160,7 @@ service cloud.firestore { allow update: if request.auth.uid == resource.data.creatorId && request.resource.data.diff(resource.data) .affectedKeys() - .hasOnly(['name', 'about', 'contractIds', 'memberIds', 'anyoneCanJoin' ]); + .hasOnly(['name', 'about', 'contractIds', 'memberIds', 'anyoneCanJoin', 'dashboardId' ]); allow update: if (request.auth.uid in resource.data.memberIds || resource.data.anyoneCanJoin) && request.resource.data.diff(resource.data) .affectedKeys() diff --git a/web/components/groups/group-dashboard.tsx b/web/components/groups/group-dashboard.tsx new file mode 100644 index 00000000..3d18140a --- /dev/null +++ b/web/components/groups/group-dashboard.tsx @@ -0,0 +1,143 @@ +import { useAdmin } from 'web/hooks/use-admin' +import { Row } from '../layout/row' +import { Content } from '../editor' +import { TextEditor, useTextEditor } from 'web/components/editor' +import { Button } from '../button' +import { Spacer } from '../layout/spacer' +import { Group } from 'common/group' +import { deleteFieldFromGroup, updateGroup } from 'web/lib/firebase/groups' +import PencilIcon from '@heroicons/react/solid/PencilIcon' +import { DocumentRemoveIcon } from '@heroicons/react/solid' +import { createDashboard } from 'web/lib/firebase/api' +import { Dashboard } from 'common/dashboard' +import { deleteDashboard, updateDashboard } from 'web/lib/firebase/dashboards' +import { useRouter } from 'next/router' +import { useState } from 'react' + +export function GroupDashboard(props: { + group: Group + isCreator: boolean + dashboard: Dashboard +}) { + const { group, isCreator, dashboard } = props + const isAdmin = useAdmin() + + if (group.dashboardId == null && !isCreator) { + return

No dashboard has been created

+ } + + return ( +
+ {isCreator || isAdmin ? ( + + ) : ( + + )} +
+ ) +} + +function RichEditGroupDashboard(props: { group: Group; dashboard: Dashboard }) { + const { group, dashboard } = props + const [editing, setEditing] = useState(false) + const [isSubmitting, setIsSubmitting] = useState(false) + const router = useRouter() + + const { editor, upload } = useTextEditor({ + defaultValue: dashboard.content, + disabled: isSubmitting, + }) + + async function saveDashboard() { + if (!editor) return + const newDashboard = { + name: group.name, + content: editor.getJSON(), + } + + if (group.dashboardId == null) { + const result = await createDashboard(newDashboard).catch((e) => { + console.error(e) + return e + }) + await updateGroup(group, { + dashboardId: result.dashboard.id, + }) + } else { + await updateDashboard(dashboard, { + content: newDashboard.content, + }) + } + await router.replace(router.asPath) + } + + async function deleteGroupDashboard() { + await deleteDashboard(dashboard) + await deleteFieldFromGroup(group, 'dashboardId') + await router.replace(router.asPath) + } + + return editing ? ( + <> + + + + + + + + ) : ( + <> + {group.dashboardId == null ? ( +
+

+ No dashboard has been created yet. + + +

+
+ ) : ( +
+
+ + + +
+ + + +
+ )} + + ) +} diff --git a/web/lib/firebase/groups.ts b/web/lib/firebase/groups.ts index 3f5d18af..4815e430 100644 --- a/web/lib/firebase/groups.ts +++ b/web/lib/firebase/groups.ts @@ -1,5 +1,6 @@ import { deleteDoc, + deleteField, doc, getDocs, query, @@ -17,6 +18,7 @@ import { } from './utils' import { Contract } from 'common/contract' import { updateContract } from 'web/lib/firebase/contracts' +import * as admin from 'firebase-admin' export const groups = coll('groups') @@ -28,6 +30,7 @@ export function groupPath( | 'about' | typeof GROUP_CHAT_SLUG | 'leaderboards' + | 'dashboard' ) { return `/group/${groupSlug}${subpath ? `/${subpath}` : ''}` } @@ -36,6 +39,10 @@ export function updateGroup(group: Group, updates: Partial) { return updateDoc(doc(groups, group.id), updates) } +export function deleteFieldFromGroup(group: Group, field: string) { + return updateDoc(doc(groups, group.id), { [field]: deleteField() }) +} + export function deleteGroup(group: Group) { return deleteDoc(doc(groups, group.id)) } diff --git a/web/pages/group/[...slugs]/index.tsx b/web/pages/group/[...slugs]/index.tsx index 28658a16..05e14c47 100644 --- a/web/pages/group/[...slugs]/index.tsx +++ b/web/pages/group/[...slugs]/index.tsx @@ -45,6 +45,10 @@ import { Button } from 'web/components/button' import { listAllCommentsOnGroup } from 'web/lib/firebase/comments' import { GroupComment } from 'common/comment' import { REFERRAL_AMOUNT } from 'common/economy' +import { GroupDashboard } from 'web/components/groups/group-dashboard' +import { getDashboard } from 'web/lib/firebase/dashboards' +import { Dashboard } from 'common/dashboard' +import { Spacer } from 'web/components/layout/spacer' export const getStaticProps = fromPropz(getStaticPropz) export async function getStaticPropz(props: { params: { slugs: string[] } }) { @@ -57,6 +61,10 @@ export async function getStaticPropz(props: { params: { slugs: string[] } }) { const contracts = (group && (await listContractsByGroupSlug(group.slug))) ?? [] + const dashboard = + group && + group.dashboardId != null && + (await getDashboard(group.dashboardId)) const bets = await Promise.all( contracts.map((contract: Contract) => listAllBets(contract.id)) ) @@ -83,6 +91,7 @@ export async function getStaticPropz(props: { params: { slugs: string[] } }) { creatorScores, topCreators, messages, + dashboard, }, revalidate: 60, // regenerate after a minute @@ -121,6 +130,7 @@ export default function GroupPage(props: { creatorScores: { [userId: string]: number } topCreators: User[] messages: GroupComment[] + dashboard: Dashboard }) { props = usePropz(props, getStaticPropz) ?? { group: null, @@ -139,6 +149,7 @@ export default function GroupPage(props: { topTraders, creatorScores, topCreators, + dashboard, } = props const router = useRouter() @@ -176,6 +187,16 @@ export default function GroupPage(props: { const aboutTab = ( + {group.dashboardId != null || isCreator ? ( + + ) : ( +
+ )} +