Skip to content

Instantly share code, notes, and snippets.

@undrcrxwn
Created November 17, 2024 11:35
Show Gist options
  • Save undrcrxwn/5f1830ed7ead47828750cc5313ede8c9 to your computer and use it in GitHub Desktop.
Save undrcrxwn/5f1830ed7ead47828750cc5313ede8c9 to your computer and use it in GitHub Desktop.
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