import type { JSX } from 'solid-js' import { createContext, onCleanup, useContext } from 'solid-js' import { createStore, reconcile } from 'solid-js/store' import { apiClient } from '../graphql/client/core' import { Reaction, ReactionBy, ReactionInput, ReactionKind } from '../graphql/schema/core.gen' type ReactionsContextType = { reactionEntities: Record actions: { loadReactionsBy: ({ by, limit, offset, }: { by: ReactionBy limit?: number offset?: 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, offset, }: { by: ReactionBy limit?: number offset?: number }): Promise => { const reactions = await apiClient.getReactionsBy({ by, limit, offset }) const newReactionEntities = reactions.reduce( (acc: { [reaction_id: number]: Reaction }, reaction: Reaction) => { acc[reaction.id] = reaction return acc }, {}, ) setReactionEntities(newReactionEntities) return reactions } const createReaction = async (input: ReactionInput): Promise => { const reaction = await apiClient.createReaction(input) if (!reaction) return 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.created_by.slug === reaction.created_by.slug && r.shout.id === reaction.shout.id && r.reply_to === reaction.reply_to, ) if (oppositeReaction) { changes[oppositeReaction.id] = undefined } } setReactionEntities(changes) } const deleteReaction = async (reaction_id: number): Promise => { if (reaction_id) { await apiClient.destroyReaction(reaction_id) setReactionEntities({ [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} }