From 07a1c124601997e5158d128f972acf1b519d8fd2 Mon Sep 17 00:00:00 2001 From: Ian Philips Date: Mon, 1 Aug 2022 21:04:46 -0600 Subject: [PATCH] Remove grouplinks on frontend --- functions/src/create-contract.ts | 58 ++++++++-------- functions/src/on-update-group.ts | 68 ++++++++----------- .../groups/contract-groups-list.tsx | 6 +- web/components/groups/group-selector.tsx | 14 ++-- web/lib/firebase/groups.ts | 40 +++++++++-- web/pages/create.tsx | 2 +- web/pages/group/[...slugs]/index.tsx | 2 +- 7 files changed, 104 insertions(+), 86 deletions(-) diff --git a/functions/src/create-contract.ts b/functions/src/create-contract.ts index 77d9f25a..17feb127 100644 --- a/functions/src/create-contract.ts +++ b/functions/src/create-contract.ts @@ -32,8 +32,8 @@ import { Group, MAX_ID_LENGTH } from '../../common/group' import { getPseudoProbability } from '../../common/pseudo-numeric' import { JSONContent } from '@tiptap/core' import { uniq, zip } from 'lodash' -import { Bet } from 'common/bet' -import { createGroupLinks } from 'functions/src/on-update-group' +import { Bet } from '../../common/bet' +import { createGroupLinks } from './on-update-group' const descScehma: z.ZodType = z.lazy(() => z.intersection( @@ -137,33 +137,6 @@ export const createmarket = newEndpoint({}, async (req, auth) => { const slug = await getSlug(question) const contractRef = firestore.collection('contracts').doc() - let group = null - if (groupId) { - const groupDocRef = firestore.collection('groups').doc(groupId) - const groupDoc = await groupDocRef.get() - if (!groupDoc.exists) { - throw new APIError(400, 'No group exists with the given group ID.') - } - - group = groupDoc.data() as Group - if ( - !group.memberIds.includes(user.id) && - !group.anyoneCanJoin && - group.creatorId !== user.id - ) { - throw new APIError( - 400, - 'User must be a member/creator of the group or group must be open to add markets to it.' - ) - } - if (!group.contractIds.includes(contractRef.id)) - await groupDocRef.update({ - contractIds: uniq([...group.contractIds, contractRef.id]), - }) - // We'll update the group links manually here bc we have the user's id and won't in the trigger - await createGroupLinks(group, [contractRef.id], auth.uid) - } - console.log( 'creating contract for', user.username, @@ -195,6 +168,33 @@ export const createmarket = newEndpoint({}, async (req, auth) => { await contractRef.create(contract) + let group = null + if (groupId) { + const groupDocRef = firestore.collection('groups').doc(groupId) + const groupDoc = await groupDocRef.get() + if (!groupDoc.exists) { + throw new APIError(400, 'No group exists with the given group ID.') + } + + group = groupDoc.data() as Group + if ( + !group.memberIds.includes(user.id) && + !group.anyoneCanJoin && + group.creatorId !== user.id + ) { + throw new APIError( + 400, + 'User must be a member/creator of the group or group must be open to add markets to it.' + ) + } + if (!group.contractIds.includes(contractRef.id)) { + await createGroupLinks(group, [contractRef.id], auth.uid) + await groupDocRef.update({ + contractIds: uniq([...group.contractIds, contractRef.id]), + }) + } + } + const providerId = user.id if (outcomeType === 'BINARY' || outcomeType === 'PSEUDO_NUMERIC') { diff --git a/functions/src/on-update-group.ts b/functions/src/on-update-group.ts index 58da0af1..bd289245 100644 --- a/functions/src/on-update-group.ts +++ b/functions/src/on-update-group.ts @@ -1,18 +1,18 @@ import * as functions from 'firebase-functions' import * as admin from 'firebase-admin' import { Group, GroupLink } from '../../common/group' -import { getContract } from './utils' +import { getContract, log } from './utils' import { uniq } from 'lodash' +import { removeUndefinedProps } from '../../common/util/object' const firestore = admin.firestore() export const onUpdateGroup = functions.firestore .document('groups/{groupId}') - .onUpdate(async (change, context) => { + .onUpdate(async (change) => { const prevGroup = change.before.data() as Group const group = change.after.data() as Group - const userId = context.auth?.uid - // ignore the update we just made + // Ignore the activity update we just made if (prevGroup.mostRecentActivityTime !== group.mostRecentActivityTime) return @@ -24,19 +24,6 @@ export const onUpdateGroup = functions.firestore //TODO: create notification with isSeeOnHref set to the group's /group/slug/questions url // but first, let the new /group/slug/chat notification permeate so that we can differentiate between the two } - if (prevGroup.contractIds.length != group.contractIds.length) { - if (prevGroup.contractIds.length < group.contractIds.length) - await createGroupLinks( - group, - group.contractIds.slice(prevGroup.contractIds.length), - userId - ) - else - await removeGroupLinks( - group, - group.contractIds.slice(prevGroup.contractIds.length) - ) - } await firestore .collection('groups') @@ -51,28 +38,31 @@ export async function createGroupLinks( ) { for (const contractId of contractIds) { const contract = await getContract(contractId) - if ( - contract?.groupSlugs?.includes(group.slug) && - contract?.groupLinks?.map((gl) => gl.groupId).includes(group.id) - ) - continue - await firestore - .collection('contracts') - .doc(contractId) - .update({ - groupSlugs: uniq([group.slug, ...(contract?.groupSlugs ?? [])]), - groupLinks: [ - { - groupId: group.id, - name: group.name, - slug: group.slug, - userId, - createdTime: Date.now(), - } as GroupLink, - contract?.groupLinks?.filter((link) => link.groupId !== group.id) ?? - [], - ], - }) + if (!contract?.groupSlugs?.includes(group.slug)) { + await firestore + .collection('contracts') + .doc(contractId) + .update({ + groupSlugs: uniq([group.slug, ...(contract?.groupSlugs ?? [])]), + }) + } + if (!contract?.groupLinks?.map((gl) => gl.groupId).includes(group.id)) { + await firestore + .collection('contracts') + .doc(contractId) + .update({ + groupLinks: [ + removeUndefinedProps({ + groupId: group.id, + name: group.name, + slug: group.slug, + userId, + createdTime: Date.now(), + }) as GroupLink, + ...(contract?.groupLinks ?? []), + ], + }) + } } } diff --git a/web/components/groups/contract-groups-list.tsx b/web/components/groups/contract-groups-list.tsx index edafef48..79f2390f 100644 --- a/web/components/groups/contract-groups-list.tsx +++ b/web/components/groups/contract-groups-list.tsx @@ -38,7 +38,7 @@ export function ContractGroupsList(props: { ignoreGroupIds: groupLinks.map((g) => g.groupId), }} setSelectedGroup={(group) => - group && addContractToGroup(group, contract.id, user.id) + group && addContractToGroup(group, contract, user.id) } selectedGroup={undefined} creator={user} @@ -62,9 +62,7 @@ export function ContractGroupsList(props: { diff --git a/web/components/groups/group-selector.tsx b/web/components/groups/group-selector.tsx index f5cf3542..d48256a6 100644 --- a/web/components/groups/group-selector.tsx +++ b/web/components/groups/group-selector.tsx @@ -12,7 +12,6 @@ import { useState } from 'react' import { useMemberGroups, useOpenGroups } from 'web/hooks/use-group' import { User } from 'common/user' import { searchInAny } from 'common/util/parse' -import { uniq } from 'lodash' export function GroupSelector(props: { selectedGroup: Group | undefined @@ -28,11 +27,14 @@ export function GroupSelector(props: { const [isCreatingNewGroup, setIsCreatingNewGroup] = useState(false) const { showSelector, showLabel, ignoreGroupIds } = options const [query, setQuery] = useState('') - const availableGroups = uniq( - useOpenGroups() - .concat(useMemberGroups(creator?.id) ?? []) - .filter((group) => !ignoreGroupIds?.includes(group.id)) - ) + const openGroups = useOpenGroups() + const availableGroups = openGroups + .concat( + (useMemberGroups(creator?.id) ?? []).filter( + (g) => !openGroups.map((og) => og.id).includes(g.id) + ) + ) + .filter((group) => !ignoreGroupIds?.includes(group.id)) const filteredGroups = availableGroups.filter((group) => searchInAny(query, group.name) ) diff --git a/web/lib/firebase/groups.ts b/web/lib/firebase/groups.ts index 4aa5368a..3f5d18af 100644 --- a/web/lib/firebase/groups.ts +++ b/web/lib/firebase/groups.ts @@ -15,6 +15,8 @@ import { listenForValue, listenForValues, } from './utils' +import { Contract } from 'common/contract' +import { updateContract } from 'web/lib/firebase/contracts' export const groups = coll('groups') @@ -131,14 +133,29 @@ export async function leaveGroup(group: Group, userId: string): Promise { export async function addContractToGroup( group: Group, - contractId: string, + contract: Contract, userId: string ) { if (!canModifyGroupContracts(group, userId)) return + const newGroupLinks = [ + ...(contract.groupLinks ?? []), + { + groupId: group.id, + createdTime: Date.now(), + slug: group.slug, + userId, + name: group.name, + } as GroupLink, + ] + // It's good to update the contract first, so the on-update-group trigger doesn't re-add them + await updateContract(contract.id, { + groupSlugs: uniq([...(contract.groupSlugs ?? []), group.slug]), + groupLinks: newGroupLinks, + }) - if (!group.contractIds.includes(contractId)) { + if (!group.contractIds.includes(contract.id)) { return await updateGroup(group, { - contractIds: uniq([...group.contractIds, contractId]), + contractIds: uniq([...group.contractIds, contract.id]), }) .then(() => group) .catch((err) => { @@ -150,13 +167,24 @@ export async function addContractToGroup( export async function removeContractFromGroup( group: Group, - contractId: string, + contract: Contract, userId: string ) { if (!canModifyGroupContracts(group, userId)) return - if (group.contractIds.includes(contractId)) { - const newContractIds = group.contractIds.filter((id) => id !== contractId) + if (contract.groupLinks?.map((l) => l.groupId).includes(group.id)) { + const newGroupLinks = contract.groupLinks?.filter( + (link) => link.slug !== group.slug + ) + await updateContract(contract.id, { + groupSlugs: + contract.groupSlugs?.filter((slug) => slug !== group.slug) ?? [], + groupLinks: newGroupLinks ?? [], + }) + } + + if (group.contractIds.includes(contract.id)) { + const newContractIds = group.contractIds.filter((id) => id !== contract.id) return await updateGroup(group, { contractIds: uniq(newContractIds), }) diff --git a/web/pages/create.tsx b/web/pages/create.tsx index a9ca83da..3f9f4995 100644 --- a/web/pages/create.tsx +++ b/web/pages/create.tsx @@ -19,7 +19,7 @@ import { import { formatMoney } from 'common/util/format' import { removeUndefinedProps } from 'common/util/object' import { ChoicesToggleGroup } from 'web/components/choices-toggle-group' -import { addContractToGroup, getGroup } from 'web/lib/firebase/groups' +import { getGroup } from 'web/lib/firebase/groups' import { Group } from 'common/group' import { useTracking } from 'web/hooks/use-tracking' import { useWarnUnsavedChanges } from 'web/hooks/use-warn-unsaved-changes' diff --git a/web/pages/group/[...slugs]/index.tsx b/web/pages/group/[...slugs]/index.tsx index bd5fc4d9..dd712a36 100644 --- a/web/pages/group/[...slugs]/index.tsx +++ b/web/pages/group/[...slugs]/index.tsx @@ -555,7 +555,7 @@ function AddContractButton(props: { group: Group; user: User }) { Promise.all( contracts.map(async (contract) => { setLoading(true) - await addContractToGroup(group, contract.id, user.id) + await addContractToGroup(group, contract, user.id) }) ).then(() => { setLoading(false)