mirror of
https://github.com/usememos/memos.git
synced 2025-02-21 21:57:47 +01:00
feat: select visibility in editor (#421)
* feat: editing visibility selection * update * update * update variable name * update Co-authored-by: boojack <stevenlgtm@gmail.com>
This commit is contained in:
parent
8f119c4265
commit
421f4dbf60
@ -18,12 +18,13 @@ interface Props {
|
|||||||
fullscreen: boolean;
|
fullscreen: boolean;
|
||||||
tools?: ReactNode;
|
tools?: ReactNode;
|
||||||
onPaste: (event: React.ClipboardEvent) => void;
|
onPaste: (event: React.ClipboardEvent) => void;
|
||||||
|
onFocus: () => void;
|
||||||
onContentChange: (content: string) => void;
|
onContentChange: (content: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
// eslint-disable-next-line react/display-name
|
||||||
const Editor = forwardRef((props: Props, ref: React.ForwardedRef<EditorRefActions>) => {
|
const Editor = forwardRef((props: Props, ref: React.ForwardedRef<EditorRefActions>) => {
|
||||||
const { className, initialContent, placeholder, fullscreen, onPaste, onContentChange: handleContentChangeCallback } = props;
|
const { className, initialContent, placeholder, fullscreen, onPaste, onFocus, onContentChange: handleContentChangeCallback } = props;
|
||||||
const editorRef = useRef<HTMLTextAreaElement>(null);
|
const editorRef = useRef<HTMLTextAreaElement>(null);
|
||||||
const refresh = useRefresh();
|
const refresh = useRefresh();
|
||||||
|
|
||||||
@ -105,6 +106,7 @@ const Editor = forwardRef((props: Props, ref: React.ForwardedRef<EditorRefAction
|
|||||||
ref={editorRef}
|
ref={editorRef}
|
||||||
onPaste={onPaste}
|
onPaste={onPaste}
|
||||||
onInput={handleEditorInput}
|
onInput={handleEditorInput}
|
||||||
|
onFocus={onFocus}
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -2,14 +2,16 @@ import { IEmojiData } from "emoji-picker-react";
|
|||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { deleteMemoResource, upsertMemoResource } from "../helpers/api";
|
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 { editorStateService, locationService, memoService, resourceService } from "../services";
|
||||||
import { useAppSelector } from "../store";
|
import { useAppSelector } from "../store";
|
||||||
import * as storage from "../helpers/storage";
|
import * as storage from "../helpers/storage";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import toastHelper from "./Toast";
|
import toastHelper from "./Toast";
|
||||||
|
import Selector from "./common/Selector";
|
||||||
import Editor, { EditorRefActions } from "./Editor/Editor";
|
import Editor, { EditorRefActions } from "./Editor/Editor";
|
||||||
import EmojiPicker from "./Editor/EmojiPicker";
|
import EmojiPicker from "./Editor/EmojiPicker";
|
||||||
|
import { toLower } from "lodash";
|
||||||
import "../less/memo-editor.less";
|
import "../less/memo-editor.less";
|
||||||
|
|
||||||
const getEditorContentCache = (): string => {
|
const getEditorContentCache = (): string => {
|
||||||
@ -22,16 +24,24 @@ const setEditorContentCache = (content: string) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setEditingMemoVisibilityCache = (visibility: Visibility) => {
|
||||||
|
storage.set({
|
||||||
|
editingMemoVisibilityCache: visibility,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
fullscreen: boolean;
|
fullscreen: boolean;
|
||||||
isUploadingResource: boolean;
|
isUploadingResource: boolean;
|
||||||
shouldShowEmojiPicker: boolean;
|
shouldShowEmojiPicker: boolean;
|
||||||
resourceList: Resource[];
|
resourceList: Resource[];
|
||||||
|
hasFocused: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MemoEditor: React.FC = () => {
|
const MemoEditor: React.FC = () => {
|
||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
const user = useAppSelector((state) => state.user.user);
|
const user = useAppSelector((state) => state.user.user);
|
||||||
|
const { setting } = useAppSelector((state) => state.user.user as User);
|
||||||
const editorState = useAppSelector((state) => state.editor);
|
const editorState = useAppSelector((state) => state.editor);
|
||||||
const tags = useAppSelector((state) => state.memo.tags);
|
const tags = useAppSelector((state) => state.memo.tags);
|
||||||
const [state, setState] = useState<State>({
|
const [state, setState] = useState<State>({
|
||||||
@ -39,6 +49,7 @@ const MemoEditor: React.FC = () => {
|
|||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
shouldShowEmojiPicker: false,
|
shouldShowEmojiPicker: false,
|
||||||
resourceList: [],
|
resourceList: [],
|
||||||
|
hasFocused: false,
|
||||||
});
|
});
|
||||||
const [allowSave, setAllowSave] = useState<boolean>(false);
|
const [allowSave, setAllowSave] = useState<boolean>(false);
|
||||||
const prevGlobalStateRef = useRef(editorState);
|
const prevGlobalStateRef = useRef(editorState);
|
||||||
@ -48,10 +59,15 @@ const MemoEditor: React.FC = () => {
|
|||||||
const mobileEditorStyle = user?.setting.mobileEditorStyle || "normal";
|
const mobileEditorStyle = user?.setting.mobileEditorStyle || "normal";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { editingMemoIdCache } = storage.get(["editingMemoIdCache"]);
|
const { editingMemoIdCache, editingMemoVisibilityCache } = storage.get(["editingMemoIdCache", "editingMemoVisibilityCache"]);
|
||||||
if (editingMemoIdCache) {
|
if (editingMemoIdCache) {
|
||||||
editorStateService.setEditMemoWithId(editingMemoIdCache);
|
editorStateService.setEditMemoWithId(editingMemoIdCache);
|
||||||
}
|
}
|
||||||
|
if (editingMemoVisibilityCache) {
|
||||||
|
editorStateService.setMemoVisibility(editingMemoVisibilityCache as "PUBLIC" | "PROTECTED" | "PRIVATE");
|
||||||
|
} else {
|
||||||
|
editorStateService.setMemoVisibility(setting.memoVisibility);
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -75,6 +91,7 @@ const MemoEditor: React.FC = () => {
|
|||||||
...state,
|
...state,
|
||||||
resourceList: memo.resourceList,
|
resourceList: memo.resourceList,
|
||||||
});
|
});
|
||||||
|
editorStateService.setMemoVisibility(memo.visibility);
|
||||||
editorRef.current?.setContent(memo.content ?? "");
|
editorRef.current?.setContent(memo.content ?? "");
|
||||||
editorRef.current?.focus();
|
editorRef.current?.focus();
|
||||||
}
|
}
|
||||||
@ -196,6 +213,7 @@ const MemoEditor: React.FC = () => {
|
|||||||
await memoService.patchMemo({
|
await memoService.patchMemo({
|
||||||
id: prevMemo.id,
|
id: prevMemo.id,
|
||||||
content,
|
content,
|
||||||
|
visibility: editorState.memoVisibility,
|
||||||
resourceIdList: state.resourceList.map((resource) => resource.id),
|
resourceIdList: state.resourceList.map((resource) => resource.id),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -203,6 +221,7 @@ const MemoEditor: React.FC = () => {
|
|||||||
} else {
|
} else {
|
||||||
await memoService.createMemo({
|
await memoService.createMemo({
|
||||||
content,
|
content,
|
||||||
|
visibility: editorState.memoVisibility,
|
||||||
resourceIdList: state.resourceList.map((resource) => resource.id),
|
resourceIdList: state.resourceList.map((resource) => resource.id),
|
||||||
});
|
});
|
||||||
locationService.clearQuery();
|
locationService.clearQuery();
|
||||||
@ -217,7 +236,9 @@ const MemoEditor: React.FC = () => {
|
|||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
resourceList: [],
|
resourceList: [],
|
||||||
});
|
});
|
||||||
|
editorStateService.setMemoVisibility(setting.memoVisibility);
|
||||||
setEditorContentCache("");
|
setEditorContentCache("");
|
||||||
|
storage.remove(["editingMemoVisibilityCache"]);
|
||||||
editorRef.current?.setContent("");
|
editorRef.current?.setContent("");
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -229,6 +250,7 @@ const MemoEditor: React.FC = () => {
|
|||||||
editorStateService.clearEditMemo();
|
editorStateService.clearEditMemo();
|
||||||
editorRef.current?.setContent("");
|
editorRef.current?.setContent("");
|
||||||
setEditorContentCache("");
|
setEditorContentCache("");
|
||||||
|
storage.remove(["editingMemoVisibilityCache"]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleContentChange = (content: string) => {
|
const handleContentChange = (content: string) => {
|
||||||
@ -355,6 +377,26 @@ const MemoEditor: React.FC = () => {
|
|||||||
[state.fullscreen, i18n.language, editorFontStyle]
|
[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 (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`memo-editor-container ${mobileEditorStyle} ${isEditing ? "edit-ing" : ""} ${state.fullscreen ? "fullscreen" : ""}`}
|
className={`memo-editor-container ${mobileEditorStyle} ${isEditing ? "edit-ing" : ""} ${state.fullscreen ? "fullscreen" : ""}`}
|
||||||
@ -367,7 +409,19 @@ const MemoEditor: React.FC = () => {
|
|||||||
{t("common.cancel")}
|
{t("common.cancel")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<Editor ref={editorRef} {...editorConfig} onPaste={handlePasteEvent} />
|
<Editor ref={editorRef} {...editorConfig} onPaste={handlePasteEvent} onFocus={handleEditorFocus} />
|
||||||
|
<div className={`visibility-selector-container ${state.hasFocused || allowSave ? "" : "!hidden"}`}>
|
||||||
|
<div className="memo-visibility-selector">
|
||||||
|
<label className="form-label selector">
|
||||||
|
<Selector
|
||||||
|
className="w-36"
|
||||||
|
value={editorState.memoVisibility}
|
||||||
|
dataSource={memoVisibilityOptionSelectorItems}
|
||||||
|
handleValueChanged={handleMemoVisibilityOptionChanged}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="common-tools-wrapper">
|
<div className="common-tools-wrapper">
|
||||||
<div className="common-tools-container">
|
<div className="common-tools-container">
|
||||||
<div className="action-btn tag-action">
|
<div className="action-btn tag-action">
|
||||||
|
@ -6,6 +6,8 @@ interface StorageData {
|
|||||||
editorContentCache: string;
|
editorContentCache: string;
|
||||||
// Editing memo id cache
|
// Editing memo id cache
|
||||||
editingMemoIdCache: MemoId;
|
editingMemoIdCache: MemoId;
|
||||||
|
// Editing memo visibility
|
||||||
|
editingMemoVisibilityCache: Visibility;
|
||||||
// locale
|
// locale
|
||||||
locale: Locale;
|
locale: Locale;
|
||||||
// skipped version
|
// skipped version
|
||||||
|
@ -62,12 +62,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> .visibility-selector-container{
|
||||||
|
@apply w-full border-b py-2;
|
||||||
|
}
|
||||||
|
|
||||||
> .memo-editor {
|
> .memo-editor {
|
||||||
@apply flex flex-col justify-start items-start relative w-full h-auto bg-white;
|
@apply flex flex-col justify-start items-start relative w-full h-auto bg-white;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .common-tools-wrapper {
|
> .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 {
|
> .common-tools-container {
|
||||||
@apply flex flex-row justify-start items-center;
|
@apply flex flex-row justify-start items-center;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import store from "../store";
|
import store from "../store";
|
||||||
import { setEditMemoId, setMarkMemoId } from "../store/modules/editor";
|
import { setEditMemoId, setMarkMemoId, setMemoVisibility } from "../store/modules/editor";
|
||||||
|
|
||||||
const editorStateService = {
|
const editorStateService = {
|
||||||
getState: () => {
|
getState: () => {
|
||||||
@ -21,6 +21,10 @@ const editorStateService = {
|
|||||||
clearMarkMemo: () => {
|
clearMarkMemo: () => {
|
||||||
store.dispatch(setMarkMemoId());
|
store.dispatch(setMarkMemoId());
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setMemoVisibility: (memoVisibility: Visibility) => {
|
||||||
|
store.dispatch(setMemoVisibility(memoVisibility));
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default editorStateService;
|
export default editorStateService;
|
||||||
|
@ -3,6 +3,7 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
|||||||
interface State {
|
interface State {
|
||||||
markMemoId?: MemoId;
|
markMemoId?: MemoId;
|
||||||
editMemoId?: MemoId;
|
editMemoId?: MemoId;
|
||||||
|
memoVisibility: Visibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
const editorSlice = createSlice({
|
const editorSlice = createSlice({
|
||||||
@ -21,9 +22,15 @@ const editorSlice = createSlice({
|
|||||||
editMemoId: action.payload,
|
editMemoId: action.payload,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
setMemoVisibility: (state, action: PayloadAction<Visibility>) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
memoVisibility: action.payload,
|
||||||
|
};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { setEditMemoId, setMarkMemoId } = editorSlice.actions;
|
export const { setEditMemoId, setMarkMemoId, setMemoVisibility } = editorSlice.actions;
|
||||||
|
|
||||||
export default editorSlice.reducer;
|
export default editorSlice.reducer;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user