Restore standalone mobile editor

This commit is contained in:
yoma committed 2026-07-09 03:44:20 +08:00
1 parent e753dfdaa5
commit 3379c8b019
4 files changed
+113 -12

No files matched your search

+91 -1
View File
@@ -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) {
+4 -1
View File
@@ -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);
}
},
});
+15 -3
View File
@@ -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>
);