diff --git a/src/components/node/CommentForm/index.tsx b/src/components/node/CommentForm/index.tsx index b9bc991d..3d143d70 100644 --- a/src/components/node/CommentForm/index.tsx +++ b/src/components/node/CommentForm/index.tsx @@ -22,6 +22,7 @@ import { moveArrItem } from '~/utils/fn'; import { SortEnd } from 'react-sortable-hoc'; import { SortableAudioGrid } from '~/components/editors/SortableAudioGrid'; import { getRandomPhrase } from '~/constants/phrases'; +import { ERROR_LITERAL } from '~/constants/errors'; const mapStateToProps = (state: IState) => ({ node: selectNode(state), @@ -237,6 +238,15 @@ const CommentFormUnconnected: FC = memo( const hasAudioAttaches = audios.length > 0 || locked_audios.length > 0; const hasAttaches = hasImageAttaches || hasAudioAttaches; + const clearError = useCallback(() => nodeSetCommentData(id, { error: '' }), [ + id, + nodeSetCommentData, + ]); + + useEffect(() => { + if (comment.error) clearError(); + }, [comment.files, comment.text]); + return (
@@ -248,6 +258,12 @@ const CommentFormUnconnected: FC = memo( placeholder={placeholder} minRows={2} /> + + {comment.error && ( +
+ {ERROR_LITERAL[comment.error] || comment.error} +
+ )}
{hasAttaches && ( diff --git a/src/components/node/CommentForm/styles.scss b/src/components/node/CommentForm/styles.scss index 910b24d5..75248544 100644 --- a/src/components/node/CommentForm/styles.scss +++ b/src/components/node/CommentForm/styles.scss @@ -9,7 +9,7 @@ .input { @include outer_shadow(); - + position: relative; flex: 1; padding: ($gap / 2) ($gap / 2 + 1px); } @@ -36,3 +36,17 @@ .attaches { @include outer_shadow(); } + +.error { + position: absolute; + bottom: 0; + left: 50%; + background: $red; + z-index: 10; + font: $font_12_regular; + box-sizing: border-box; + padding: 0 $gap; + border-radius: 4px 4px 0 0; + transform: translate(-50%, 0); + cursor: pointer; +} diff --git a/src/redux/node/actions.ts b/src/redux/node/actions.ts index 90fe809c..74489781 100644 --- a/src/redux/node/actions.ts +++ b/src/redux/node/actions.ts @@ -70,7 +70,7 @@ export const nodeSetRelated = (related: INodeState['related']) => ({ type: NODE_ACTIONS.SET_RELATED, }); -export const nodeSetCommentData = (id: number, comment: IComment) => ({ +export const nodeSetCommentData = (id: number, comment: Partial) => ({ id, comment, type: NODE_ACTIONS.SET_COMMENT_DATA, diff --git a/src/redux/node/constants.ts b/src/redux/node/constants.ts index 5912780c..76d421b0 100644 --- a/src/redux/node/constants.ts +++ b/src/redux/node/constants.ts @@ -110,6 +110,7 @@ export const EMPTY_COMMENT: IComment = { temp_ids: [], is_private: false, user: null, + error: '', }; export const NODE_EDITORS = { diff --git a/src/redux/node/handlers.ts b/src/redux/node/handlers.ts index 273e885f..5c542b56 100644 --- a/src/redux/node/handlers.ts +++ b/src/redux/node/handlers.ts @@ -49,7 +49,16 @@ const setRelated = (state: INodeState, { related }: ReturnType -) => assocPath(['comment_data', id], comment, state); +) => ({ + ...state, + comment_data: { + ...state.comment_data, + [id]: { + ...(state.comment_data[id] || {}), + ...comment, + }, + }, +}) const setTags = (state: INodeState, { tags }: ReturnType) => assocPath(['current', 'tags'], tags, state); diff --git a/src/redux/node/sagas.ts b/src/redux/node/sagas.ts index d6800a17..b2e0e4a2 100644 --- a/src/redux/node/sagas.ts +++ b/src/redux/node/sagas.ts @@ -207,7 +207,7 @@ function* onPostComment({ id }: ReturnType) { yield put(nodeSetSendingComment(false)); if (error || !comment) { - return yield put(nodeSetSaveErrors({ error: error || ERRORS.EMPTY_RESPONSE })); + return yield put(nodeSetCommentData(id, { error })); } const { current: current_node } = yield select(selectNode); diff --git a/src/redux/types.ts b/src/redux/types.ts index 24d8d684..dd5b95b4 100644 --- a/src/redux/types.ts +++ b/src/redux/types.ts @@ -146,6 +146,7 @@ export interface IComment { files: IFile[]; is_private: boolean; user: IUser; + error?: string; created_at?: string; update_at?: string;