fix(android): encode brackets in note image export URIs

This commit is contained in:
tianma-if committed 2026-10-19 18:48:37 +08:00
1 parent aa1efec65a
commit 20ae6ddf2b
3 files changed
+29 -2

No files matched your search

@@ -1,5 +1,25 @@
import { describe, expect, test } from "bun:test";
import { planMobileNoteImageRender } from "./mobile-note-image-export";
import { PathUtilities } from "../../node_modules/expo-file-system/src/pathUtilities";
import { buildImageExportBasename } from "@edgeever/shared/note-image-card";
import { encodeMobileNoteImageFileUri, planMobileNoteImageRender } from "./mobile-note-image-export";
describe("mobile note image file URI", () => {
test.each(["png", "jpg"])("preserves bracketed titles for %s exports through Expo path joining", (extension) => {
const filename = `${buildImageExportBasename("[Serv00.com] Account activation tuuki", "Untitled")}.${extension}`;
const uri = encodeMobileNoteImageFileUri(PathUtilities.join("file:///cache/exports/request-1/", filename));
expect(uri).toBe(`file:///cache/exports/request-1/%5BServ00.com%5D%20Account%20activation%20tuuki.${extension}`);
// File's constructor joins the URI once more before handing it to Android.
expect(PathUtilities.join(uri)).toBe(uri);
expect(decodeURIComponent(new URL(uri).pathname.split("/").at(-1)!)).toBe(filename);
});
test.each(["中文 [标题] 100% #1.png", "literal %5Bname%5D.png", "普通笔记.jpg"])("preserves the filename %s without double encoding", (filename) => {
const uri = encodeMobileNoteImageFileUri(PathUtilities.join("file:///cache/exports/", filename));
expect(uri).not.toMatch(/[\[\]]/);
expect(decodeURIComponent(new URL(uri).pathname.split("/").at(-1)!)).toBe(filename);
expect(encodeMobileNoteImageFileUri(uri)).toBe(uri);
});
});
describe("planMobileNoteImageRender", () => {
test("keeps a short card at full quality", () => {
@@ -1 +1,7 @@
export { planNativeNoteImageRender as planMobileNoteImageRender } from "@edgeever/shared/note-image-card";
// Expo's path joiner escapes spaces and percent signs, but leaves brackets raw.
// Android's java.net.URI rejects those in a path. Escape only brackets here so
// existing percent escapes stay intact and the actual filename is preserved.
export const encodeMobileNoteImageFileUri = (uri: string): string =>
uri.replace(/\[/g, "%5B").replace(/\]/g, "%5D");
@@ -26,6 +26,7 @@ import { getMobileNoteImageFontLabel } from "../lib/note-image-font-options";
import { MobileResourceActions } from "../components/MobileResourceActions";
import { ZoomableImagePreview } from "../components/ZoomableImagePreview";
import type { ImageSize } from "../lib/mobile-image-zoom";
import { encodeMobileNoteImageFileUri } from "../lib/mobile-note-image-export";
import { SAFE_DOM_WEBVIEW_PROPS } from "../lib/mobile-dom";
import { getNextMobileNoteSearchIndex } from "../lib/mobile-note-search";
import { hasMobilePoster, hasMobileInfographic, hasMobileStructuredTable, hasMobileVisualDiagram, resolveMobileMemoViewerContent } from "../lib/mobile-diagram";
@@ -972,7 +973,7 @@ export const MemoDetailModal = ({
// earlier export when different notes share the same download filename.
const directory = new Directory(Paths.cache, "edgeever-note-exports", event.requestId);
if (!directory.exists) directory.create({ idempotent: true, intermediates: true });
const file = new File(directory, event.filename);
const file = new File(encodeMobileNoteImageFileUri(Paths.join(directory, event.filename)));
if (file.exists) file.delete();
file.create({ overwrite: true, intermediates: true });
file.write(decodeBase64Chunks(imageExportChunksRef.current));