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 4, 2023
1 parent 47085ff commit f3cb524
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 42 deletions.
50 changes: 21 additions & 29 deletions src/components/CommentCreateForm/CommentCreateForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { useState, useCallback, useEffect } 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 @@ -18,63 +17,56 @@ 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;
onDraft?: (comment: DraftComment | null) => void;
}

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

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

const CommentCreateForm: React.FC<CommentCreateFormProps> = ({
goalId,
states,
draftComment,
description: currentDescription,
stateId,
onSubmit,
onFocus,
onCancel,
onDraftComment,
onDraft,
}) => {
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>(states?.find(findStateHelper(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 || !onDraft) return;

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

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(states?.find(findStateHelper(stateId)));
}, [currentDescription, goalId, prevGoal, stateId, states]);

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

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

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

const onStateSelect = useCallback((state: State) => {
setPushState((prev) => (state.id === prev?.id ? undefined : state));
Expand Down
27 changes: 14 additions & 13 deletions src/components/GoalActivity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ 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 +45,23 @@ 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, dropDraft } = useLSDraft('draftGoalComment', {});
const { highlightCommentId, setHighlightCommentId } = useHighlightedComment();

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

const onDraftComment = (comment: DraftComment | null) => {
setDraftComment((prev) => {
if (comment) {
prev[goalId] = comment;
} else {
delete prev[goalId];
}
return prev;
});
const draft = resolveDraft(goalId);

const onDraft = (comment: DraftComment | null) => {
if (comment) {
saveDraft(goalId, comment);
return;
}

dropDraft(goalId);
};

return (
Expand Down Expand Up @@ -164,8 +164,9 @@ export const GoalActivity = forwardRef<HTMLDivElement, GoalActivityProps>(
goalId={goalId}
states={goalStates}
onSubmit={onPublish}
onDraftComment={onDraftComment}
draftComment={draftComment?.[goalId]}
onDraft={onDraft}
description={draft?.description}
stateId={draft?.stateId}
/>
</ActivityFeed>
);
Expand Down
54 changes: 54 additions & 0 deletions src/hooks/useLSDraft.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { DraftComment } from '../types/draftComment';

import { useLocalStorage } from './useLocalStorage';

type LSParams = Parameters<typeof useLocalStorage>;

type DraftGoalCommentKey = 'draftGoalComment';
type DraftGoalCommentKeyReturn = {
saveDraft: (id: string, draft: DraftComment) => void;
resolveDraft: (id: string) => DraftComment;
dropDraft: (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 = (id: string, draft: DraftComment) => {
setDraft((prev) => {
// @ts-expect-error
prev[id] = draft;
return prev;
});
};

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

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

return {
saveDraft,
resolveDraft,
dropDraft,
};
}

export { useLSDraft };

0 comments on commit f3cb524

Please sign in to comment.