diff --git a/web/src/components/Editor/Editor.tsx b/web/src/components/Editor/Editor.tsx index dd544373..e33c4df6 100644 --- a/web/src/components/Editor/Editor.tsx +++ b/web/src/components/Editor/Editor.tsx @@ -18,12 +18,13 @@ interface Props { fullscreen: boolean; tools?: ReactNode; onPaste: (event: React.ClipboardEvent) => void; + onFocus: () => void; onContentChange: (content: string) => void; } // eslint-disable-next-line react/display-name const Editor = forwardRef((props: Props, ref: React.ForwardedRef) => { - const { className, initialContent, placeholder, fullscreen, onPaste, onContentChange: handleContentChangeCallback } = props; + const { className, initialContent, placeholder, fullscreen, onPaste, onFocus, onContentChange: handleContentChangeCallback } = props; const editorRef = useRef(null); const refresh = useRefresh(); @@ -105,6 +106,7 @@ const Editor = forwardRef((props: Props, ref: React.ForwardedRef ); diff --git a/web/src/components/MemoEditor.tsx b/web/src/components/MemoEditor.tsx index 23638869..71bbbb45 100644 --- a/web/src/components/MemoEditor.tsx +++ b/web/src/components/MemoEditor.tsx @@ -2,14 +2,16 @@ import { IEmojiData } from "emoji-picker-react"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { deleteMemoResource, upsertMemoResource } from "../helpers/api"; -import { TAB_SPACE_WIDTH, UNKNOWN_ID } from "../helpers/consts"; +import { TAB_SPACE_WIDTH, UNKNOWN_ID, VISIBILITY_SELECTOR_ITEMS } from "../helpers/consts"; import { editorStateService, locationService, memoService, resourceService } from "../services"; import { useAppSelector } from "../store"; import * as storage from "../helpers/storage"; import Icon from "./Icon"; import toastHelper from "./Toast"; +import Selector from "./common/Selector"; import Editor, { EditorRefActions } from "./Editor/Editor"; import EmojiPicker from "./Editor/EmojiPicker"; +import { toLower } from "lodash"; import "../less/memo-editor.less"; const getEditorContentCache = (): string => { @@ -22,16 +24,24 @@ const setEditorContentCache = (content: string) => { }); }; +const setEditingMemoVisibilityCache = (visibility: Visibility) => { + storage.set({ + editingMemoVisibilityCache: visibility, + }); +}; + interface State { fullscreen: boolean; isUploadingResource: boolean; shouldShowEmojiPicker: boolean; resourceList: Resource[]; + hasFocused: boolean; } const MemoEditor: React.FC = () => { const { t, i18n } = useTranslation(); const user = useAppSelector((state) => state.user.user); + const { setting } = useAppSelector((state) => state.user.user as User); const editorState = useAppSelector((state) => state.editor); const tags = useAppSelector((state) => state.memo.tags); const [state, setState] = useState({ @@ -39,6 +49,7 @@ const MemoEditor: React.FC = () => { fullscreen: false, shouldShowEmojiPicker: false, resourceList: [], + hasFocused: false, }); const [allowSave, setAllowSave] = useState(false); const prevGlobalStateRef = useRef(editorState); @@ -48,10 +59,15 @@ const MemoEditor: React.FC = () => { const mobileEditorStyle = user?.setting.mobileEditorStyle || "normal"; useEffect(() => { - const { editingMemoIdCache } = storage.get(["editingMemoIdCache"]); + const { editingMemoIdCache, editingMemoVisibilityCache } = storage.get(["editingMemoIdCache", "editingMemoVisibilityCache"]); if (editingMemoIdCache) { editorStateService.setEditMemoWithId(editingMemoIdCache); } + if (editingMemoVisibilityCache) { + editorStateService.setMemoVisibility(editingMemoVisibilityCache as "PUBLIC" | "PROTECTED" | "PRIVATE"); + } else { + editorStateService.setMemoVisibility(setting.memoVisibility); + } }, []); useEffect(() => { @@ -75,6 +91,7 @@ const MemoEditor: React.FC = () => { ...state, resourceList: memo.resourceList, }); + editorStateService.setMemoVisibility(memo.visibility); editorRef.current?.setContent(memo.content ?? ""); editorRef.current?.focus(); } @@ -196,6 +213,7 @@ const MemoEditor: React.FC = () => { await memoService.patchMemo({ id: prevMemo.id, content, + visibility: editorState.memoVisibility, resourceIdList: state.resourceList.map((resource) => resource.id), }); } @@ -203,6 +221,7 @@ const MemoEditor: React.FC = () => { } else { await memoService.createMemo({ content, + visibility: editorState.memoVisibility, resourceIdList: state.resourceList.map((resource) => resource.id), }); locationService.clearQuery(); @@ -217,7 +236,9 @@ const MemoEditor: React.FC = () => { fullscreen: false, resourceList: [], }); + editorStateService.setMemoVisibility(setting.memoVisibility); setEditorContentCache(""); + storage.remove(["editingMemoVisibilityCache"]); editorRef.current?.setContent(""); }; @@ -229,6 +250,7 @@ const MemoEditor: React.FC = () => { editorStateService.clearEditMemo(); editorRef.current?.setContent(""); setEditorContentCache(""); + storage.remove(["editingMemoVisibilityCache"]); }; const handleContentChange = (content: string) => { @@ -355,6 +377,26 @@ const MemoEditor: React.FC = () => { [state.fullscreen, i18n.language, editorFontStyle] ); + const memoVisibilityOptionSelectorItems = VISIBILITY_SELECTOR_ITEMS.map((item) => { + return { + value: item.value, + text: t(`memo.visibility.${toLower(item.value)}`), + }; + }); + + const handleMemoVisibilityOptionChanged = async (value: string) => { + const visibilityValue = value as Visibility; + editorStateService.setMemoVisibility(visibilityValue); + setEditingMemoVisibilityCache(visibilityValue); + }; + + const handleEditorFocus = () => { + setState({ + ...state, + hasFocused: true, + }); + }; + return (
{ {t("common.cancel")}
- + +
+
+ +
+
diff --git a/web/src/helpers/storage.ts b/web/src/helpers/storage.ts index d6b704f9..b603107b 100644 --- a/web/src/helpers/storage.ts +++ b/web/src/helpers/storage.ts @@ -6,6 +6,8 @@ interface StorageData { editorContentCache: string; // Editing memo id cache editingMemoIdCache: MemoId; + // Editing memo visibility + editingMemoVisibilityCache: Visibility; // locale locale: Locale; // skipped version diff --git a/web/src/less/memo-editor.less b/web/src/less/memo-editor.less index 9ba1e8a8..7d541306 100644 --- a/web/src/less/memo-editor.less +++ b/web/src/less/memo-editor.less @@ -62,12 +62,16 @@ } } + > .visibility-selector-container{ + @apply w-full border-b py-2; + } + > .memo-editor { @apply flex flex-col justify-start items-start relative w-full h-auto bg-white; } > .common-tools-wrapper { - @apply relative w-full flex flex-row justify-between items-center; + @apply relative w-full flex flex-row justify-between items-center pt-2; > .common-tools-container { @apply flex flex-row justify-start items-center; diff --git a/web/src/services/editorStateService.ts b/web/src/services/editorStateService.ts index 0e0b0716..110010fd 100644 --- a/web/src/services/editorStateService.ts +++ b/web/src/services/editorStateService.ts @@ -1,5 +1,5 @@ import store from "../store"; -import { setEditMemoId, setMarkMemoId } from "../store/modules/editor"; +import { setEditMemoId, setMarkMemoId, setMemoVisibility } from "../store/modules/editor"; const editorStateService = { getState: () => { @@ -21,6 +21,10 @@ const editorStateService = { clearMarkMemo: () => { store.dispatch(setMarkMemoId()); }, + + setMemoVisibility: (memoVisibility: Visibility) => { + store.dispatch(setMemoVisibility(memoVisibility)); + }, }; export default editorStateService; diff --git a/web/src/store/modules/editor.ts b/web/src/store/modules/editor.ts index 2c989f8d..15f8654b 100644 --- a/web/src/store/modules/editor.ts +++ b/web/src/store/modules/editor.ts @@ -3,6 +3,7 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; interface State { markMemoId?: MemoId; editMemoId?: MemoId; + memoVisibility: Visibility; } const editorSlice = createSlice({ @@ -21,9 +22,15 @@ const editorSlice = createSlice({ editMemoId: action.payload, }; }, + setMemoVisibility: (state, action: PayloadAction) => { + return { + ...state, + memoVisibility: action.payload, + }; + }, }, }); -export const { setEditMemoId, setMarkMemoId } = editorSlice.actions; +export const { setEditMemoId, setMarkMemoId, setMemoVisibility } = editorSlice.actions; export default editorSlice.reducer;