Update group links in trigger and api

This commit is contained in:
Ian Philips 2022-07-29 15:02:22 -06:00
parent d3da6de5dd
commit 75de3662f3
8 changed files with 133 additions and 76 deletions

View File

@ -31,8 +31,9 @@ import { User } from '../../common/user'
import { Group, MAX_ID_LENGTH } from '../../common/group'
import { getPseudoProbability } from '../../common/pseudo-numeric'
import { JSONContent } from '@tiptap/core'
import { zip } from 'lodash'
import { uniq, zip } from 'lodash'
import { Bet } from 'common/bet'
import { createGroupLinks } from 'functions/src/on-update-group'
const descScehma: z.ZodType<JSONContent> = z.lazy(() =>
z.intersection(
@ -145,16 +146,22 @@ export const createmarket = newEndpoint({}, async (req, auth) => {
}
group = groupDoc.data() as Group
if (!group.memberIds.includes(user.id)) {
if (
!group.memberIds.includes(user.id) &&
!group.anyoneCanJoin &&
group.creatorId !== user.id
) {
throw new APIError(
400,
'User must be a member of the group to add markets to it.'
'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: [...group.contractIds, contractRef.id],
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(

View File

@ -1,13 +1,16 @@
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import { Group } from '../../common/group'
import { Group, GroupLink } from '../../common/group'
import { getContract } from './utils'
import { uniq } from 'lodash'
const firestore = admin.firestore()
export const onUpdateGroup = functions.firestore
.document('groups/{groupId}')
.onUpdate(async (change) => {
.onUpdate(async (change, context) => {
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
if (prevGroup.mostRecentActivityTime !== group.mostRecentActivityTime)
@ -21,9 +24,73 @@ 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')
.doc(group.id)
.update({ mostRecentActivityTime: Date.now() })
})
export async function createGroupLinks(
group: Group,
contractIds: string[],
userId?: string
) {
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) ??
[],
],
})
}
}
export async function removeGroupLinks(group: Group, contractIds: string[]) {
for (const contractId of contractIds) {
const contract = await getContract(contractId)
await firestore
.collection('contracts')
.doc(contractId)
.update({
groupSlugs: uniq([
...(contract?.groupSlugs?.filter((slug) => slug !== group.slug) ??
[]),
]),
groupLinks: [
contract?.groupLinks?.filter((link) => link.groupId !== group.id) ??
[],
],
})
}
}

View File

@ -7,6 +7,7 @@ import { Button } from 'web/components/button'
import { GroupSelector } from 'web/components/groups/group-selector'
import {
addContractToGroup,
canModifyGroupContracts,
removeContractFromGroup,
} from 'web/lib/firebase/groups'
import { User } from 'common/user'
@ -37,7 +38,7 @@ export function ContractGroupsList(props: {
ignoreGroupIds: groupLinks.map((g) => g.groupId),
}}
setSelectedGroup={(group) =>
group && addContractToGroup(group, contract, user.id)
group && addContractToGroup(group, contract.id, user.id)
}
selectedGroup={undefined}
creator={user}
@ -57,11 +58,13 @@ export function ContractGroupsList(props: {
<Row className="line-clamp-1 items-center gap-2">
<GroupLinkItem group={group} />
</Row>
{user && group.memberIds.includes(user.id) && (
{user && canModifyGroupContracts(group, user.id) && (
<Button
color={'gray-white'}
size={'xs'}
onClick={() => removeContractFromGroup(group, contract)}
onClick={() =>
removeContractFromGroup(group, contract.id, user.id)
}
>
<XIcon className="h-4 w-4 text-gray-500" />
</Button>

View File

@ -9,9 +9,10 @@ import {
import clsx from 'clsx'
import { CreateGroupButton } from 'web/components/groups/create-group-button'
import { useState } from 'react'
import { useMemberGroups } from 'web/hooks/use-group'
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
@ -27,10 +28,12 @@ export function GroupSelector(props: {
const [isCreatingNewGroup, setIsCreatingNewGroup] = useState(false)
const { showSelector, showLabel, ignoreGroupIds } = options
const [query, setQuery] = useState('')
const memberGroups = (useMemberGroups(creator?.id) ?? []).filter(
(group) => !ignoreGroupIds?.includes(group.id)
const availableGroups = uniq(
useOpenGroups()
.concat(useMemberGroups(creator?.id) ?? [])
.filter((group) => !ignoreGroupIds?.includes(group.id))
)
const filteredGroups = memberGroups.filter((group) =>
const filteredGroups = availableGroups.filter((group) =>
searchInAny(query, group.name)
)

View File

@ -5,6 +5,7 @@ import {
listenForGroup,
listenForGroups,
listenForMemberGroups,
listenForOpenGroups,
listGroups,
} from 'web/lib/firebase/groups'
import { getUser, getUsers } from 'web/lib/firebase/users'
@ -32,6 +33,16 @@ export const useGroups = () => {
return groups
}
export const useOpenGroups = () => {
const [groups, setGroups] = useState<Group[]>([])
useEffect(() => {
return listenForOpenGroups(setGroups)
}, [])
return groups
}
export const useMemberGroups = (
userId: string | null | undefined,
options?: { withChatEnabled: boolean },

View File

@ -8,7 +8,6 @@ import {
} from 'firebase/firestore'
import { sortBy, uniq } from 'lodash'
import { Group, GROUP_CHAT_SLUG, GroupLink } from 'common/group'
import { updateContract } from './contracts'
import {
coll,
getValue,
@ -16,7 +15,6 @@ import {
listenForValue,
listenForValues,
} from './utils'
import { Contract } from 'common/contract'
export const groups = coll<Group>('groups')
@ -52,6 +50,13 @@ export function listenForGroups(setGroups: (groups: Group[]) => void) {
return listenForValues(groups, setGroups)
}
export function listenForOpenGroups(setGroups: (groups: Group[]) => void) {
return listenForValues(
query(groups, where('anyoneCanJoin', '==', true)),
setGroups
)
}
export function getGroup(groupId: string) {
return getValue<Group>(doc(groups, groupId))
}
@ -126,29 +131,14 @@ export async function leaveGroup(group: Group, userId: string): Promise<void> {
export async function addContractToGroup(
group: Group,
contract: Contract,
contractId: string,
userId: string
) {
if (!contract.groupLinks?.map((l) => l.groupId).includes(group.id)) {
const newGroupLinks = [
...(contract.groupLinks ?? []),
{
groupId: group.id,
createdTime: Date.now(),
slug: group.slug,
userId,
name: group.name,
} as GroupLink,
]
if (!canModifyGroupContracts(group, userId)) return
await updateContract(contract.id, {
groupSlugs: uniq([...(contract.groupSlugs ?? []), group.slug]),
groupLinks: newGroupLinks,
})
}
if (!group.contractIds.includes(contract.id)) {
if (!group.contractIds.includes(contractId)) {
return await updateGroup(group, {
contractIds: uniq([...group.contractIds, contract.id]),
contractIds: uniq([...group.contractIds, contractId]),
})
.then(() => group)
.catch((err) => {
@ -160,21 +150,13 @@ export async function addContractToGroup(
export async function removeContractFromGroup(
group: Group,
contract: Contract
contractId: string,
userId: string
) {
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 (!canModifyGroupContracts(group, userId)) return
if (group.contractIds.includes(contract.id)) {
const newContractIds = group.contractIds.filter((id) => id !== contract.id)
if (group.contractIds.includes(contractId)) {
const newContractIds = group.contractIds.filter((id) => id !== contractId)
return await updateGroup(group, {
contractIds: uniq(newContractIds),
})
@ -186,29 +168,10 @@ export async function removeContractFromGroup(
}
}
export async function setContractGroupLinks(
group: Group,
contractId: string,
userId: string
) {
await updateContract(contractId, {
groupSlugs: [group.slug],
groupLinks: [
{
groupId: group.id,
name: group.name,
slug: group.slug,
userId,
createdTime: Date.now(),
} as GroupLink,
],
})
return await updateGroup(group, {
contractIds: uniq([...group.contractIds, contractId]),
})
.then(() => group)
.catch((err) => {
console.error('error adding contract to group', err)
return err
})
export function canModifyGroupContracts(group: Group, userId: string) {
return (
group.creatorId === userId ||
group.memberIds.includes(userId) ||
group.anyoneCanJoin
)
}

View File

@ -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 { getGroup, setContractGroupLinks } from 'web/lib/firebase/groups'
import { addContractToGroup, 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'
@ -122,7 +122,10 @@ export function NewContract(props: {
useEffect(() => {
if (groupId && creator)
getGroup(groupId).then((group) => {
if (group && group.memberIds.includes(creator.id)) {
if (
group &&
(group.memberIds.includes(creator.id) || group.anyoneCanJoin)
) {
setSelectedGroup(group)
setShowGroupSelector(false)
}
@ -240,7 +243,7 @@ export function NewContract(props: {
isFree: false,
})
if (result && selectedGroup) {
await setContractGroupLinks(selectedGroup, result.id, creator.id)
await addContractToGroup(selectedGroup, result.id, creator.id)
}
await router.push(contractPath(result as Contract))

View File

@ -555,7 +555,7 @@ function AddContractButton(props: { group: Group; user: User }) {
Promise.all(
contracts.map(async (contract) => {
setLoading(true)
await addContractToGroup(group, contract, user.id)
await addContractToGroup(group, contract.id, user.id)
})
).then(() => {
setLoading(false)