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 actions: { loadReactionsBy: ({ by, limit }: { by: ReactionBy; limit?: number }) => Promise createReaction: (reaction: ReactionInput) => Promise updateReaction: (id: number, reaction: ReactionInput) => Promise deleteReaction: (id: number) => Promise } } const ReactionsContext = createContext() export function useReactions() { return useContext(ReactionsContext) } export const ReactionsProvider = (props: { children: JSX.Element }) => { const [reactionEntities, setReactionEntities] = createStore>({}) const loadReactionsBy = async ({ by, limit }: { by: ReactionBy limit?: number }): Promise => { 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 => { 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 => { const reaction = await apiClient.destroyReaction(id) setReactionEntities((oldState) => ({ ...oldState, [reaction.id]: undefined })) } const updateReaction = async (id: number, input: ReactionInput): Promise => { 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 {props.children} }