webapp/src/context/reactions.tsx

139 lines
5.1 KiB
TypeScript
Raw Normal View History

2023-02-17 09:21:02 +00:00
import type { JSX } from 'solid-js'
2024-07-30 19:06:17 +00:00
import { createContext, createMemo, onCleanup, useContext } from 'solid-js'
import { createStore, reconcile } from 'solid-js/store'
2024-07-30 19:06:17 +00:00
import { coreApiUrl } from '~/config'
2024-07-26 15:49:15 +00:00
import { loadReactions } from '~/graphql/api/public'
2024-06-24 17:50:27 +00:00
import createReactionMutation from '~/graphql/mutation/core/reaction-create'
import destroyReactionMutation from '~/graphql/mutation/core/reaction-destroy'
import updateReactionMutation from '~/graphql/mutation/core/reaction-update'
import {
MutationCreate_ReactionArgs,
MutationUpdate_ReactionArgs,
QueryLoad_Reactions_ByArgs,
Reaction,
2024-06-26 08:22:05 +00:00
ReactionKind
} from '~/graphql/schema/core.gen'
2024-07-30 19:06:17 +00:00
import { graphqlClientCreate } from '../graphql/client'
2024-03-06 09:04:33 +00:00
import { useLocalize } from './localize'
2024-07-30 19:06:17 +00:00
import { useSession } from './session'
2024-06-24 17:50:27 +00:00
import { useSnackbar } from './ui'
2023-02-17 09:21:02 +00:00
type ReactionsContextType = {
reactionEntities: Record<number, Reaction>
2024-07-03 17:38:43 +00:00
reactionsByShout: Record<string, Reaction[]>
2024-06-24 17:50:27 +00:00
loadReactionsBy: (args: QueryLoad_Reactions_ByArgs) => Promise<Reaction[]>
createReaction: (reaction: MutationCreate_ReactionArgs) => Promise<void>
updateReaction: (reaction: MutationUpdate_ReactionArgs) => Promise<Reaction>
deleteReaction: (id: number) => Promise<{ error: string } | null>
2024-07-16 00:14:08 +00:00
addReactions: (rrr: Reaction[]) => void
2023-02-17 09:21:02 +00:00
}
2024-06-24 17:50:27 +00:00
const ReactionsContext = createContext<ReactionsContextType>({} as ReactionsContextType)
2023-02-17 09:21:02 +00:00
export function useReactions() {
return useContext(ReactionsContext)
}
export const ReactionsProvider = (props: { children: JSX.Element }) => {
const [reactionEntities, setReactionEntities] = createStore<Record<number, Reaction>>({})
2024-07-03 17:38:43 +00:00
const [reactionsByShout, setReactionsByShout] = createStore<Record<number, Reaction[]>>({})
2024-03-06 09:04:33 +00:00
const { t } = useLocalize()
const { showSnackbar } = useSnackbar()
2024-07-30 19:06:17 +00:00
const { session } = useSession()
const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
2024-07-16 00:14:08 +00:00
const addReactions = (rrr: Reaction[]) => {
const newReactionsByShout: Record<string, Reaction[]> = { ...reactionsByShout }
const newReactionEntities = rrr.reduce(
2024-01-23 16:32:57 +00:00
(acc: { [reaction_id: number]: Reaction }, reaction: Reaction) => {
acc[reaction.id] = reaction
2024-07-03 17:38:43 +00:00
if (!newReactionsByShout[reaction.shout.slug]) newReactionsByShout[reaction.shout.slug] = []
newReactionsByShout[reaction.shout.slug].push(reaction)
2024-01-23 16:32:57 +00:00
return acc
},
2024-07-16 00:14:08 +00:00
{ ...reactionEntities }
2024-01-23 16:32:57 +00:00
)
2023-02-17 09:21:02 +00:00
setReactionEntities(newReactionEntities)
2024-07-16 00:14:08 +00:00
setReactionsByShout(newReactionsByShout)
}
const loadReactionsBy = async (opts: QueryLoad_Reactions_ByArgs): Promise<Reaction[]> => {
!opts.by && console.warn('reactions provider got wrong opts')
const fetcher = await loadReactions(opts)
const result = (await fetcher()) || []
console.debug('[context.reactions] loaded', result)
result && addReactions(result)
2024-06-24 17:50:27 +00:00
return result
2023-02-17 09:21:02 +00:00
}
2024-06-24 17:50:27 +00:00
const createReaction = async (input: MutationCreate_ReactionArgs): Promise<void> => {
2024-07-30 19:06:17 +00:00
const resp = await client()?.mutation(createReactionMutation, input).toPromise()
2024-06-24 17:50:27 +00:00
const { error, reaction } = resp?.data?.create_reaction || {}
2024-03-06 09:56:00 +00:00
if (error) await showSnackbar({ type: 'error', body: t(error) })
2024-01-31 12:34:15 +00:00
if (!reaction) return
2023-02-28 17:13:14 +00:00
const changes = {
2024-06-26 08:22:05 +00:00
[reaction.id]: reaction
2023-02-28 17:13:14 +00:00
}
if ([ReactionKind.Like, ReactionKind.Dislike].includes(reaction.kind)) {
const oppositeReactionKind =
reaction.kind === ReactionKind.Like ? ReactionKind.Dislike : ReactionKind.Like
const oppositeReaction = Object.values(reactionEntities).find(
(r) =>
r.kind === oppositeReactionKind &&
2023-11-28 13:18:25 +00:00
r.created_by.slug === reaction.created_by.slug &&
2023-02-28 17:13:14 +00:00
r.shout.id === reaction.shout.id &&
2024-06-26 08:22:05 +00:00
r.reply_to === reaction.reply_to
2023-02-28 17:13:14 +00:00
)
if (oppositeReaction) {
changes[oppositeReaction.id] = undefined
}
}
setReactionEntities(changes)
2023-02-17 09:21:02 +00:00
}
2024-06-24 17:50:27 +00:00
const deleteReaction = async (
2024-06-26 08:22:05 +00:00
reaction_id: number
2024-06-24 17:50:27 +00:00
): Promise<{ error: string; reaction?: string } | null> => {
2024-01-25 18:19:59 +00:00
if (reaction_id) {
2024-07-30 19:06:17 +00:00
const resp = await client()?.mutation(destroyReactionMutation, { reaction_id }).toPromise()
2024-06-24 17:50:27 +00:00
const result = resp?.data?.destroy_reaction
2024-03-07 07:20:50 +00:00
if (!result.error) {
setReactionEntities({
2024-06-26 08:22:05 +00:00
[reaction_id]: undefined
2024-03-07 07:20:50 +00:00
})
2024-03-06 09:04:33 +00:00
}
2024-03-07 07:20:50 +00:00
return result
2024-01-25 18:19:59 +00:00
}
2024-06-24 17:50:27 +00:00
return null
2023-02-17 09:21:02 +00:00
}
2024-06-24 17:50:27 +00:00
const updateReaction = async (input: MutationUpdate_ReactionArgs): Promise<Reaction> => {
2024-07-30 19:06:17 +00:00
const resp = await client()?.mutation(updateReactionMutation, input).toPromise()
2024-06-24 17:50:27 +00:00
const result = resp?.data?.update_reaction
2024-07-09 10:35:57 +00:00
if (!result) throw new Error('cannot update reaction')
2024-06-24 17:50:27 +00:00
const { error, reaction } = result
2024-03-06 09:56:00 +00:00
if (error) await showSnackbar({ type: 'error', body: t(error) })
2024-03-06 09:49:06 +00:00
if (reaction) setReactionEntities(reaction.id, reaction)
2024-03-04 10:47:11 +00:00
return reaction
2023-02-17 09:21:02 +00:00
}
2023-03-02 18:48:39 +00:00
onCleanup(() => setReactionEntities(reconcile({})))
2023-02-17 09:21:02 +00:00
const actions = {
loadReactionsBy,
createReaction,
updateReaction,
2024-07-16 00:14:08 +00:00
deleteReaction,
addReactions
2023-02-17 09:21:02 +00:00
}
2024-07-03 17:38:43 +00:00
const value: ReactionsContextType = { reactionEntities, reactionsByShout, ...actions }
2023-02-17 09:21:02 +00:00
return <ReactionsContext.Provider value={value}>{props.children}</ReactionsContext.Provider>
}