Skip to content

Commit

Permalink
chore: create a draft hook
Browse files Browse the repository at this point in the history
  • Loading branch information
DenisVorop committed Aug 7, 2023
1 parent 3f6b705 commit f701535
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 49 deletions.
63 changes: 31 additions & 32 deletions src/components/CommentCreateForm/CommentCreateForm.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React, { useState, useCallback, useEffect } from 'react';
import React, { useState, useCallback, useEffect, useMemo } from 'react';
import { ArrowDownSmallIcon, ArrowUpSmallIcon, Button, Dropdown, UserPic } from '@taskany/bricks';
import { State } from '@prisma/client';
import styled from 'styled-components';
import { debounce } from 'throttle-debounce';

import { usePageContext } from '../../hooks/usePageContext';
import { useCommentResource } from '../../hooks/useCommentResource';
Expand All @@ -11,70 +10,72 @@ import { CommentForm } from '../CommentForm/CommentForm';
import { ActivityFeedItem } from '../ActivityFeed';
import { ColorizedMenuItem } from '../ColorizedMenuItem';
import { StateDot } from '../StateDot';
import { DraftComment } from '../../types/draftComment';

import { tr } from './CommentCreateForm.i18n';

interface CommentCreateFormProps {
goalId: string;
states?: State[];
draftComment?: DraftComment;
description?: string;
stateId?: string;

onSubmit?: (id?: string) => void;
onFocus?: () => void;
onCancel?: () => void;
onDraftComment?: (comment: DraftComment | null) => void;
onChange?: (comment?: { description?: string; stateId?: string }) => void;
}

const StyledStateUpdate = styled.div`
display: flex;
align-items: center;
`;

const findStateHelper = (draftComment?: DraftComment) => (state?: State) => state?.id === draftComment?.stateId;

const CommentCreateForm: React.FC<CommentCreateFormProps> = ({
goalId,
states,
draftComment,
description: currentDescription,
stateId = '',
onSubmit,
onFocus,
onCancel,
onDraftComment,
onChange,
}) => {
const statesMap = useMemo(() => {
if (!states) return {};

return states.reduce((acc, cur) => {
acc[cur.id] = cur;
return acc;
}, {} as Record<string, State>);
}, [states]);

const { user, themeId } = usePageContext();
const { create } = useCommentResource();
const [pushState, setPushState] = useState<State | undefined>();
const [description, setDescription] = useState<string | undefined>('');
const [focused, setFocused] = useState(false);
const [pushState, setPushState] = useState<State | undefined>(statesMap[stateId]);
const [description, setDescription] = useState<string | undefined>(currentDescription);
const [focused, setFocused] = useState(Boolean(currentDescription));
const [busy, setBusy] = useState(false);
const [currentGoal, setCurrentGoal] = useState(goalId);
const [prevGoal, setPrevGoal] = useState('');

// eslint-disable-next-line react-hooks/exhaustive-deps
const debouncedOnDraftComment = useCallback(
debounce(500, (comment: DraftComment) => {
onDraftComment?.(comment);
}),
[onDraftComment],
);

useEffect(() => {
if (!description) return;
if (!description) {
onChange?.();
return;
}

debouncedOnDraftComment({ description, stateId: pushState?.id });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pushState?.id, description]);
onChange?.({ description, stateId: pushState?.id });
}, [pushState?.id, description, onChange]);

useEffect(() => {
if (goalId === prevGoal) return;

setCurrentGoal(goalId);
setPrevGoal(goalId);
setDescription(draftComment?.description ?? '');
setFocused(Boolean(draftComment?.description));
setPushState(states?.find(findStateHelper(draftComment)));
}, [draftComment, goalId, prevGoal, states]);
setDescription(currentDescription);
setFocused(Boolean(currentDescription));
setPushState(statesMap[stateId]);
}, [currentDescription, goalId, prevGoal, stateId, statesMap]);

const onCommentFocus = useCallback(() => {
setFocused(true);
Expand All @@ -90,13 +91,12 @@ const CommentCreateForm: React.FC<CommentCreateFormProps> = ({
onSubmit?.(id);
setDescription('');
setPushState(undefined);
onDraftComment?.(null);
})(form);

setBusy(false);
setFocused(true);
},
[create, onSubmit, onDraftComment],
[create, onSubmit],
);

const onCancelCreate = useCallback(() => {
Expand All @@ -105,8 +105,7 @@ const CommentCreateForm: React.FC<CommentCreateFormProps> = ({
setPushState(undefined);
setDescription('');
onCancel?.();
onDraftComment?.(null);
}, [onCancel, onDraftComment]);
}, [onCancel]);

const onStateSelect = useCallback((state: State) => {
setPushState((prev) => (state.id === prev?.id ? undefined : state));
Expand Down
33 changes: 19 additions & 14 deletions src/components/GoalActivity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import { Priority } from '../types/priority';
import { useHighlightedComment } from '../hooks/useHighlightedComment';
import { GoalByIdReturnType } from '../../trpc/inferredTypes';
import { HistoryAction } from '../types/history';
import { useLocalStorage } from '../hooks/useLocalStorage';
import { DraftComment } from '../types/draftComment';
import { useLSDraft } from '../hooks/useLSDraft';

import { ActivityFeed } from './ActivityFeed';
import { CommentView } from './CommentView/CommentView';
Expand Down Expand Up @@ -45,23 +44,27 @@ function excludeString<T>(val: T): Exclude<T, string> {

export const GoalActivity = forwardRef<HTMLDivElement, GoalActivityProps>(
({ feed, onCommentReaction, onCommentPublish, userId, goalId, goalStates, onCommentDelete, children }, ref) => {
const [draftComment, setDraftComment] = useLocalStorage('draftGoalComment', {});
const { saveDraft, resolveDraft, removeDraft } = useLSDraft('draftGoalComment', {});
const { highlightCommentId, setHighlightCommentId } = useHighlightedComment();

const draft = resolveDraft(goalId);

const onPublish = (id?: string) => {
onCommentPublish(id);
setHighlightCommentId(id);
removeDraft(goalId);
};

const onCancel = () => {
removeDraft(goalId);
};

const onDraftComment = (comment: DraftComment | null) => {
setDraftComment((prev) => {
if (comment) {
prev[goalId] = comment;
} else {
delete prev[goalId];
}
return prev;
});
const onChange = (comment?: { stateId?: string; description?: string }) => {
if (!comment) {
removeDraft(goalId);
return;
}
saveDraft(goalId, comment);
};

return (
Expand Down Expand Up @@ -163,9 +166,11 @@ export const GoalActivity = forwardRef<HTMLDivElement, GoalActivityProps>(
<CommentCreateForm
goalId={goalId}
states={goalStates}
description={draft?.description}
stateId={draft?.stateId}
onSubmit={onPublish}
onDraftComment={onDraftComment}
draftComment={draftComment?.[goalId]}
onCancel={onCancel}
onChange={onChange}
/>
</ActivityFeed>
);
Expand Down
64 changes: 64 additions & 0 deletions src/hooks/useLSDraft.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { useCallback } from 'react';

import { DraftGoalComment, useLocalStorage } from './useLocalStorage';

type LSParams = Parameters<typeof useLocalStorage>;

type DraftComment = DraftGoalComment[keyof DraftGoalComment];
type DraftGoalCommentKey = 'draftGoalComment';
type DraftGoalCommentKeyReturn = {
saveDraft: (id: string, draft: DraftComment) => void;
resolveDraft: (id: string) => DraftComment;
removeDraft: (id: string) => void;
};

function useLSDraft(
storageKey: DraftGoalCommentKey,
initialValue: Record<string, DraftComment>,
): DraftGoalCommentKeyReturn;

/**
* before using, you need to set up an overload
*/
function useLSDraft<KDG extends DraftGoalCommentKey>(
storageKey: KDG,
initialValue: Record<string, LSParams[1]>,
): DraftGoalCommentKeyReturn {
const [draft, setDraft] = useLocalStorage(storageKey, initialValue);

const saveDraft = useCallback(
(id: string, draft: DraftComment) => {
setDraft((prev) => {
// @ts-expect-error
prev[id] = draft;
return prev;
});
},
[setDraft],
);

const resolveDraft = useCallback(
(id: string) => {
return draft[id];
},
[draft],
);

const removeDraft = useCallback(
(id: string) => {
setDraft((prev) => {
delete prev[id];
return prev;
});
},
[setDraft],
);

return {
saveDraft,
resolveDraft,
removeDraft,
};
}

export { useLSDraft };
3 changes: 0 additions & 3 deletions src/types/draftComment.ts

This file was deleted.

0 comments on commit f701535

Please sign in to comment.