webapp/src/context/reactions.tsx

95 lines
2.9 KiB
TypeScript

import type { JSX } from 'solid-js'
import { createContext, onCleanup, useContext } from 'solid-js'
import type { Reaction, ReactionBy, ReactionInput } from '../graphql/types.gen'
import { ReactionKind } from '../graphql/types.gen'
import { apiClient } from '../utils/apiClient'
import { createStore, reconcile } from 'solid-js/store'
type ReactionsContextType = {
reactionEntities: Record<number, Reaction>
actions: {
loadReactionsBy: ({ by, limit }: { by: ReactionBy; limit?: number }) => Promise<Reaction[]>
createReaction: (reaction: ReactionInput) => Promise<void>
updateReaction: (id: number, reaction: ReactionInput) => Promise<void>
deleteReaction: (id: number) => Promise<void>
}
}
const ReactionsContext = createContext<ReactionsContextType>()
export function useReactions() {
return useContext(ReactionsContext)
}
export const ReactionsProvider = (props: { children: JSX.Element }) => {
const [reactionEntities, setReactionEntities] = createStore<Record<number, Reaction>>({})
const loadReactionsBy = async ({
by,
limit
}: {
by: ReactionBy
limit?: number
}): Promise<Reaction[]> => {
const reactions = await apiClient.getReactionsBy({ by, limit })
const newReactionEntities = reactions.reduce((acc, reaction) => {
acc[reaction.id] = reaction
return acc
}, {})
setReactionEntities(newReactionEntities)
return reactions
}
const createReaction = async (input: ReactionInput): Promise<void> => {
const reaction = await apiClient.createReaction(input)
const changes = {
[reaction.id]: reaction
}
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 &&
r.createdBy.slug === reaction.createdBy.slug &&
r.shout.id === reaction.shout.id &&
r.replyTo === reaction.replyTo
)
if (oppositeReaction) {
changes[oppositeReaction.id] = undefined
}
}
setReactionEntities(changes)
}
const deleteReaction = async (id: number): Promise<void> => {
const reaction = await apiClient.destroyReaction(id)
setReactionEntities({
[reaction.id]: undefined
})
}
const updateReaction = async (id: number, input: ReactionInput): Promise<void> => {
const reaction = await apiClient.updateReaction(id, input)
setReactionEntities(reaction.id, reaction)
}
onCleanup(() => setReactionEntities(reconcile({})))
const actions = {
loadReactionsBy,
createReaction,
updateReaction,
deleteReaction
}
const value: ReactionsContextType = { reactionEntities, actions }
return <ReactionsContext.Provider value={value}>{props.children}</ReactionsContext.Provider>
}