Optimistically join groups

This commit is contained in:
Ian Philips 2022-07-14 11:09:28 -06:00
parent 8daf1b2ba8
commit 27a544205f
3 changed files with 36 additions and 32 deletions

View File

@ -1,6 +1,6 @@
import clsx from 'clsx' import clsx from 'clsx'
import { User } from 'common/user' import { User } from 'common/user'
import { useState } from 'react' import { useEffect, useState } from 'react'
import { useUser } from 'web/hooks/use-user' import { useUser } from 'web/hooks/use-user'
import { withTracking } from 'web/lib/service/analytics' import { withTracking } from 'web/lib/service/analytics'
import { Row } from 'web/components/layout/row' import { Row } from 'web/components/layout/row'
@ -9,9 +9,10 @@ import { TextButton } from 'web/components/text-button'
import { Group } from 'common/group' import { Group } from 'common/group'
import { Modal } from 'web/components/layout/modal' import { Modal } from 'web/components/layout/modal'
import { Col } from 'web/components/layout/col' import { Col } from 'web/components/layout/col'
import { addUserToGroup, leaveGroup } from 'web/lib/firebase/groups' import { joinGroup, leaveGroup } from 'web/lib/firebase/groups'
import { firebaseLogin } from 'web/lib/firebase/users' import { firebaseLogin } from 'web/lib/firebase/users'
import { GroupLink } from 'web/pages/groups' import { GroupLink } from 'web/pages/groups'
import toast from 'react-hot-toast'
export function GroupsButton(props: { user: User }) { export function GroupsButton(props: { user: User }) {
const { user } = props const { user } = props
@ -88,22 +89,34 @@ export function JoinOrLeaveGroupButton(props: {
}) { }) {
const { group, small, className } = props const { group, small, className } = props
const currentUser = useUser() const currentUser = useUser()
const isFollowing = currentUser const [isMember, setIsMember] = useState<boolean>(false)
? group.memberIds.includes(currentUser.id) useEffect(() => {
: false if (currentUser && group.memberIds.includes(currentUser.id)) {
setIsMember(group.memberIds.includes(currentUser.id))
}
}, [currentUser, group])
const onJoinGroup = () => { const onJoinGroup = () => {
if (!currentUser) return if (!currentUser) return
addUserToGroup(group, currentUser.id) setIsMember(true)
joinGroup(group, currentUser.id).catch(() => {
setIsMember(false)
toast.error('Failed to join group')
})
} }
const onLeaveGroup = () => { const onLeaveGroup = () => {
if (!currentUser) return if (!currentUser) return
leaveGroup(group, currentUser.id) setIsMember(false)
leaveGroup(group, currentUser.id).catch(() => {
setIsMember(true)
toast.error('Failed to leave group')
})
} }
const smallStyle = const smallStyle =
'btn !btn-xs border-2 border-gray-500 bg-white normal-case text-gray-500 hover:border-gray-500 hover:bg-white hover:text-gray-500' 'btn !btn-xs border-2 border-gray-500 bg-white normal-case text-gray-500 hover:border-gray-500 hover:bg-white hover:text-gray-500'
if (!currentUser || isFollowing === undefined) { if (!currentUser || isMember === undefined) {
if (!group.anyoneCanJoin) if (!group.anyoneCanJoin)
return <div className={clsx(className, 'text-gray-500')}>Closed</div> return <div className={clsx(className, 'text-gray-500')}>Closed</div>
return ( return (
@ -116,7 +129,7 @@ export function JoinOrLeaveGroupButton(props: {
) )
} }
if (isFollowing) { if (isMember) {
return ( return (
<button <button
className={clsx( className={clsx(

View File

@ -87,32 +87,23 @@ export async function addUserToGroupViaSlug(groupSlug: string, userId: string) {
console.error(`Group not found: ${groupSlug}`) console.error(`Group not found: ${groupSlug}`)
return return
} }
return await addUserToGroup(group, userId) return await joinGroup(group, userId)
} }
export async function addUserToGroup( export async function joinGroup(group: Group, userId: string): Promise<void> {
group: Group,
userId: string
): Promise<Group> {
const { memberIds } = group const { memberIds } = group
if (memberIds.includes(userId)) { if (memberIds.includes(userId)) return // already a member
return group
}
const newMemberIds = [...memberIds, userId] const newMemberIds = [...memberIds, userId]
const newGroup = { ...group, memberIds: newMemberIds } return await updateGroup(group, { memberIds: uniq(newMemberIds) })
await updateGroup(newGroup, { memberIds: uniq(newMemberIds) })
return newGroup
} }
export async function leaveGroup(group: Group, userId: string): Promise<Group> { export async function leaveGroup(group: Group, userId: string): Promise<void> {
const { memberIds } = group const { memberIds } = group
if (!memberIds.includes(userId)) { if (!memberIds.includes(userId)) return // not a member
return group
}
const newMemberIds = memberIds.filter((id) => id !== userId) const newMemberIds = memberIds.filter((id) => id !== userId)
const newGroup = { ...group, memberIds: newMemberIds } return await updateGroup(group, { memberIds: uniq(newMemberIds) })
await updateGroup(newGroup, { memberIds: uniq(newMemberIds) })
return newGroup
} }
export async function addContractToGroup(group: Group, contract: Contract) { export async function addContractToGroup(group: Group, contract: Contract) {

View File

@ -8,7 +8,7 @@ import {
groupPath, groupPath,
getGroupBySlug, getGroupBySlug,
updateGroup, updateGroup,
addUserToGroup, joinGroup,
addContractToGroup, addContractToGroup,
} from 'web/lib/firebase/groups' } from 'web/lib/firebase/groups'
import { Row } from 'web/components/layout/row' import { Row } from 'web/components/layout/row'
@ -604,19 +604,19 @@ function JoinGroupButton(props: {
user: User | null | undefined user: User | null | undefined
}) { }) {
const { group, user } = props const { group, user } = props
function joinGroup() { function addUserToGroup() {
if (user && !group.memberIds.includes(user.id)) { if (user && !group.memberIds.includes(user.id)) {
toast.promise(addUserToGroup(group, user.id), { toast.promise(joinGroup(group, user.id), {
loading: 'Joining group...', loading: 'Joining group...',
success: 'Joined group!', success: 'Joined group!',
error: "Couldn't join group", error: "Couldn't join group, try again?",
}) })
} }
} }
return ( return (
<div> <div>
<button <button
onClick={user ? joinGroup : firebaseLogin} onClick={user ? addUserToGroup : firebaseLogin}
className={'btn-md btn-outline btn whitespace-nowrap normal-case'} className={'btn-md btn-outline btn whitespace-nowrap normal-case'}
> >
{user ? 'Join group' : 'Login to join group'} {user ? 'Join group' : 'Login to join group'}