refactor(share): make saveFile content nullable for path-based shares (#4424)

The book "Send" flow had to pass `new ArrayBuffer(0)` to saveFile purely to
satisfy the type-checker: the content arg is ignored on the native share
path when `options.filePath` points at an already-on-disk file. Widen
saveFile's content parameter to `string | ArrayBuffer | null` across the
AppService contract so callers can hand off a file by path without buffering
it into memory, and pass `null` from the Send flow instead of a throwaway
empty buffer.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Huang Xin
2026-06-02 22:49:47 +08:00
committed by GitHub
parent df66c63a07
commit e4bb9fc4b7
7 changed files with 40 additions and 14 deletions
+1 -1
View File
@@ -77,7 +77,7 @@ export abstract class BaseAppService implements AppService {
abstract selectFiles(name: string, extensions: string[]): Promise<string[]>;
abstract saveFile(
filename: string,
content: string | ArrayBuffer,
content: string | ArrayBuffer | null,
options?: {
filePath?: string;
mimeType?: string;
@@ -633,7 +633,7 @@ export class NativeAppService extends BaseAppService {
async saveFile(
filename: string,
content: string | ArrayBuffer,
content: string | ArrayBuffer | null,
options?: {
filePath?: string;
mimeType?: string;
@@ -655,7 +655,7 @@ export class NativeAppService extends BaseAppService {
shareablePath = await this.resolveFilePath(filename, 'Temp');
if (typeof content === 'string') {
await writeTextFile(shareablePath, content);
} else {
} else if (content) {
await writeFile(shareablePath, new Uint8Array(content));
}
}
@@ -688,7 +688,7 @@ export class NativeAppService extends BaseAppService {
if (typeof content === 'string') {
await writeTextFile(filePath, content);
} else {
} else if (content) {
await writeFile(filePath, new Uint8Array(content));
}
return true;
@@ -386,7 +386,7 @@ export class NodeAppService extends BaseAppService {
async saveFile(
_filename: string,
content: string | ArrayBuffer,
content: string | ArrayBuffer | null,
options?: {
filePath?: string;
mimeType?: string;
@@ -399,7 +399,7 @@ export class NodeAppService extends BaseAppService {
await fsp.mkdir(nodePath.dirname(filepath), { recursive: true });
if (typeof content === 'string') {
await fsp.writeFile(filepath, content, 'utf-8');
} else {
} else if (content) {
await fsp.writeFile(filepath, Buffer.from(content));
}
return true;
@@ -333,7 +333,7 @@ export class WebAppService extends BaseAppService {
async saveFile(
filename: string,
content: string | ArrayBuffer,
content: string | ArrayBuffer | null,
options?: {
filePath?: string;
mimeType?: string;
@@ -344,6 +344,9 @@ export class WebAppService extends BaseAppService {
},
): Promise<boolean> {
const mimeType = options?.mimeType || 'application/octet-stream';
// Web has no filesystem path to read from, so `null` content (only the
// native-only "Send" flow passes it) degrades to an empty body.
const body = content ?? '';
if (
options?.share &&
typeof navigator !== 'undefined' &&
@@ -351,7 +354,7 @@ export class WebAppService extends BaseAppService {
) {
let shareData: ShareData | null = null;
try {
const file = new File([content], filename, { type: mimeType });
const file = new File([body], filename, { type: mimeType });
const candidate: ShareData = { files: [file], title: filename };
if (typeof navigator.canShare !== 'function' || navigator.canShare(candidate)) {
shareData = candidate;
@@ -377,7 +380,7 @@ export class WebAppService extends BaseAppService {
}
}
try {
const blob = new Blob([content], { type: mimeType });
const blob = new Blob([body], { type: mimeType });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;