2023-02-17 09:21:02 +00:00
|
|
|
import type { JSX } from 'solid-js'
|
2023-11-14 15:10:00 +00:00
|
|
|
|
2024-07-30 19:06:17 +00:00
|
|
|
import { createContext, createMemo, onCleanup, useContext } from 'solid-js'
|
2023-11-14 15:10:00 +00:00
|
|
|
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
|
2024-07-04 07:51:15 +00:00
|
|
|
} 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>
|
|
|
|
}
|