Restore standalone mobile editor
This commit is contained in:
1 parent
e753dfdaa5
commit
3379c8b019
4 files changed
+113
-12
No files matched your search
@@ -50,6 +50,7 @@ import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "@
|
||||
import { EditorToolbar } from "./EditorToolbar";
|
||||
import { RevisionHistoryDialog } from "./dialogs/RevisionHistoryDialog";
|
||||
import { api } from "@/lib/api";
|
||||
import { consumeStandaloneMobileEditorReturn, openStandaloneMobileEditor } from "@/lib/mobile-editor";
|
||||
import { cn, formatDateTime, parseTagsText } from "@/lib/utils";
|
||||
import { docToMarkdown, markdownToDoc, type Notebook, type MemoDetail, type TiptapDoc } from "@edgeever/shared";
|
||||
import { compressImageForUpload } from "@/lib/image-compression";
|
||||
@@ -930,7 +931,96 @@ const MobileNativeEditorPane = ({
|
||||
};
|
||||
|
||||
export const EditorPane = (props: EditorPaneProps) => {
|
||||
return <RichEditorPane {...props} />;
|
||||
const [isMobileViewport, setIsMobileViewport] = useState(() =>
|
||||
typeof window === "undefined" ? false : window.matchMedia(MOBILE_EDITOR_QUERY).matches
|
||||
);
|
||||
const [mobileNativeEditMemoId, setMobileNativeEditMemoId] = useState<string | null>(null);
|
||||
const standaloneOpenMemoIdRef = useRef<string | null>(null);
|
||||
const readOnly = props.isTrashView || Boolean(props.memo?.isDeleted);
|
||||
const mobileDefaultEditRequested = Boolean(
|
||||
props.memo?.id && props.memo.id === props.mobileDefaultEditMemoId && !readOnly
|
||||
);
|
||||
const mobileNativeEditingActive = Boolean(
|
||||
isMobileViewport &&
|
||||
props.memo &&
|
||||
!readOnly &&
|
||||
(mobileDefaultEditRequested || mobileNativeEditMemoId === props.memo.id)
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isMobileViewport && mobileDefaultEditRequested && props.memo?.id) {
|
||||
if (consumeStandaloneMobileEditorReturn(props.memo.id)) {
|
||||
props.onMobileDefaultEditConsumed();
|
||||
setMobileNativeEditMemoId(null);
|
||||
props.onBackToList();
|
||||
return;
|
||||
}
|
||||
|
||||
if (standaloneOpenMemoIdRef.current === props.memo.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
standaloneOpenMemoIdRef.current = props.memo.id;
|
||||
props.onMobileDefaultEditConsumed();
|
||||
openStandaloneMobileEditor(props.memo.id);
|
||||
}
|
||||
}, [isMobileViewport, mobileDefaultEditRequested, props.memo?.id, props.onBackToList, props.onMobileDefaultEditConsumed]);
|
||||
|
||||
useEffect(() => {
|
||||
const clearReturnedStandaloneEditor = () => {
|
||||
if (!consumeStandaloneMobileEditorReturn(props.memo?.id ?? null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
props.onMobileDefaultEditConsumed();
|
||||
setMobileNativeEditMemoId(null);
|
||||
props.onBackToList();
|
||||
};
|
||||
|
||||
clearReturnedStandaloneEditor();
|
||||
window.addEventListener("pageshow", clearReturnedStandaloneEditor);
|
||||
document.addEventListener("visibilitychange", clearReturnedStandaloneEditor);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("pageshow", clearReturnedStandaloneEditor);
|
||||
document.removeEventListener("visibilitychange", clearReturnedStandaloneEditor);
|
||||
};
|
||||
}, [props.memo?.id, props.onBackToList, props.onMobileDefaultEditConsumed]);
|
||||
|
||||
useEffect(() => {
|
||||
const mediaQuery = window.matchMedia(MOBILE_EDITOR_QUERY);
|
||||
const updateMobileViewport = () => setIsMobileViewport(mediaQuery.matches);
|
||||
|
||||
updateMobileViewport();
|
||||
mediaQuery.addEventListener("change", updateMobileViewport);
|
||||
|
||||
return () => mediaQuery.removeEventListener("change", updateMobileViewport);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setMobileNativeEditMemoId(null);
|
||||
}, [props.memo?.id]);
|
||||
|
||||
if (mobileNativeEditingActive) {
|
||||
return (
|
||||
<div className="flex h-full min-h-0 items-center justify-center bg-white text-sm font-medium text-slate-400">
|
||||
打开编辑器
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<RichEditorPane
|
||||
{...props}
|
||||
mobileDefaultEditMemoId={null}
|
||||
onRequestMobileNativeEdit={() => {
|
||||
if (props.memo?.id && !readOnly) {
|
||||
setMobileNativeEditMemoId(props.memo.id);
|
||||
openStandaloneMobileEditor(props.memo.id);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const RichEditorPane = ({
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
import { getNotebookMoveOptions } from "@/lib/app-helpers";
|
||||
import { compressImageForUpload } from "@/lib/image-compression";
|
||||
import { localDb, type LocalDraft } from "@/lib/local-db";
|
||||
import { markStandaloneMobileEditorReturning } from "@/lib/mobile-editor";
|
||||
import { getStandaloneMobileEditorReturnPath, markStandaloneMobileEditorReturning } from "@/lib/mobile-editor";
|
||||
import {
|
||||
DEFAULT_MOBILE_EDITOR_MEMO_TITLE,
|
||||
MOBILE_EDITOR_AUTO_SAVE_DELAY_MS,
|
||||
@@ -27,7 +27,6 @@ import {
|
||||
normalizeMobileEditorDoc,
|
||||
parseMobileEditorTags,
|
||||
requestMobileEditorJson,
|
||||
safeMobileEditorReturnPath,
|
||||
uploadMobileEditorResource,
|
||||
type MobileEditorDraft,
|
||||
type MobileEditorMemoResponse,
|
||||
@@ -41,18 +40,15 @@ type ListNotebooksResponse = {
|
||||
|
||||
type MobileStandaloneTiptapEditorProps = {
|
||||
memoId?: string | null;
|
||||
returnTo?: string;
|
||||
onLeave?: () => void;
|
||||
};
|
||||
|
||||
export const MobileStandaloneTiptapEditor = ({
|
||||
memoId: memoIdProp,
|
||||
returnTo: returnToProp,
|
||||
onLeave,
|
||||
}: MobileStandaloneTiptapEditorProps = {}) => {
|
||||
const params = useMemo(() => getMobileEditorParams(), []);
|
||||
const memoId = memoIdProp ?? params.get("memoId");
|
||||
const returnTo = safeMobileEditorReturnPath(returnToProp ?? params.get("returnTo"));
|
||||
const draftKey = getMobileEditorDraftKey(memoId);
|
||||
const [memo, setMemo] = useState<MemoDetail | null>(null);
|
||||
const memoRef = useRef<MemoDetail | null>(null);
|
||||
@@ -440,8 +436,8 @@ export const MobileStandaloneTiptapEditor = ({
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.replace(returnTo);
|
||||
}, [memoId, onLeave, returnTo]);
|
||||
window.location.replace(memoId ? getStandaloneMobileEditorReturnPath(memoId) : "/");
|
||||
}, [memoId, onLeave]);
|
||||
|
||||
const leavePage = useCallback(async () => {
|
||||
if (leavingRef.current) {
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
import { MemoListPane, MemoSelectionActionBar } from "./MemoListPane";
|
||||
import { AppConfirmDialog, MemoDeleteConfirmDialog, NotebookNameDialog } from "./dialogs/ConfirmDialogs";
|
||||
import { api } from "@/lib/api";
|
||||
import { MOBILE_EDITOR_RETURN_PARAM } from "@/lib/mobile-editor";
|
||||
import { MOBILE_EDITOR_RETURN_PARAM, openStandaloneMobileEditor } from "@/lib/mobile-editor";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { createExcerpt, docToText, type Notebook, type AuthUser, type MemoSummary, type MemoDetail } from "@edgeever/shared";
|
||||
import type {
|
||||
@@ -1296,6 +1296,9 @@ export const WorkspaceApp = ({
|
||||
setSelectedMemoId(data.memo.id);
|
||||
setActivePane("editor");
|
||||
|
||||
if (!isDesktopViewport()) {
|
||||
openStandaloneMobileEditor(data.memo.id);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
const params = new URLSearchParams(window.location.hash ? window.location.hash.slice(1) : window.location.search);
|
||||
const returnTo = params.get("returnTo");
|
||||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { MobileStandaloneTiptapEditor } from "@/components/MobileStandaloneTiptapEditor";
|
||||
import "./styles/mobile-markdown-editor.css";
|
||||
|
||||
window.location.replace(returnTo?.startsWith("/") ? returnTo : "/");
|
||||
const root = document.getElementById("mobile-editor-root");
|
||||
|
||||
if (!root) {
|
||||
throw new Error("Mobile editor root not found");
|
||||
}
|
||||
|
||||
createRoot(root).render(
|
||||
<React.StrictMode>
|
||||
<MobileStandaloneTiptapEditor />
|
||||
</React.StrictMode>
|
||||
);
|
||||
Reference in new issue
Block a user