-
-
Save undrcrxwn/5f1830ed7ead47828750cc5313ede8c9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {createEvent, createStore, sample} from 'effector'; | |
import {createEffect} from 'effector/compat'; | |
import {toast} from 'sonner'; | |
import {apiV1CommentsDiscussionIdReactionsPostFx, apiV1CommentsGetFx} from '~/shared/api'; | |
// Словарь [айди коммента] -> реакции | |
export const $viewerReactions = createStore<Record<string, {draft: string[]; committed: string[]}>>( | |
{}, | |
); | |
// Ивент который вызывает вьюшка при нажатии на кнопку реакции | |
export const reactionToggled = createEvent<{commentId: string; toggledReaction: string}>(); | |
// Попытка отправить набор реакций на бек | |
// toggledReaction тут нужен чтобы commitReactionsFx.fail какую именно реакцию не удалось тогглнуть | |
const commitReactionsFx = createEffect( | |
async (params: {commentId: string; toggledReaction: string; reactions: string[]}) => { | |
await apiV1CommentsDiscussionIdReactionsPostFx({ | |
path: {discussionId: params.commentId}, | |
body: params.reactions, | |
}); | |
}, | |
); | |
// При получении с бека комментов заполняем для них draft и committed начальными значениями | |
$viewerReactions.on(apiV1CommentsGetFx.doneData, (state, {answer: {items}}) => ({ | |
...state, | |
...Object.fromEntries( | |
items.map((comment) => [ | |
comment.id, | |
{ | |
draft: comment.viewer_reactions as string[], | |
committed: comment.viewer_reactions as string[], | |
}, | |
]), | |
), | |
})); | |
// Если не получилось отправить на бек реакции | |
$viewerReactions.on(commitReactionsFx.fail, (state, {params: {commentId, toggledReaction}}) => { | |
// Отображаем тост (это я наверное попытаюсь как нибудь вынести во вью, чтобы было model.ts а не tsx) | |
toast('Oops...', { | |
description: ( | |
<p> | |
Failed to toggle <span className="font-semibold"> {toggledReaction} </span> | |
</p> | |
), | |
action: { | |
label: 'Shit happens', | |
onClick: () => {}, | |
}, | |
}); | |
// Заменяем draft реакции этого коммента на его committed реакции | |
return { | |
...state, | |
[commentId]: { | |
...state[commentId], | |
draft: state[commentId].committed, | |
}, | |
}; | |
}); | |
// Если получилось отправить на бек набор реакций, то записываем его в committed | |
$viewerReactions.on(commitReactionsFx.done, (state, {params: {commentId, reactions}}) => ({ | |
...state, | |
[commentId]: { | |
...state[commentId], | |
committed: reactions, | |
}, | |
})); | |
// При reactionToggled изменяем draft реакции | |
const draftReactionsUpdate = sample({ | |
clock: reactionToggled, | |
source: $viewerReactions, | |
fn: (viewerReactions, {commentId, toggledReaction}) => { | |
// Берём текущие draft реакции | |
const draftReactions = viewerReactions[commentId].draft; | |
// Тогглим нужную реакцию | |
const newDraftReactions = draftReactions.includes(toggledReaction) | |
? draftReactions.filter((draftReaction) => draftReaction !== toggledReaction) | |
: [...draftReactions, toggledReaction].slice(-3); | |
return { | |
commentId, | |
toggledReaction, | |
newDraftReactions, | |
newReactions: { | |
...viewerReactions, | |
[commentId]: { | |
...viewerReactions[commentId], | |
draft: newDraftReactions, | |
}, | |
}, | |
}; | |
}, | |
}); | |
sample({ | |
clock: draftReactionsUpdate, | |
fn: ({newReactions}) => newReactions, | |
target: $viewerReactions, | |
}); | |
sample({ | |
clock: draftReactionsUpdate, | |
fn: ({commentId, toggledReaction, newDraftReactions}) => ({ | |
commentId, | |
toggledReaction, | |
reactions: newDraftReactions, | |
}), | |
target: commitReactionsFx, | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment