@multiplatform.one/platform 7.87.1 → 7.88.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@multiplatform.one/platform",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.88.0",
|
|
4
4
|
"description": "Platform detection flags and utilities for multiplatform.one",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"detection",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"@tamagui/constants": "2.7.6"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@multiplatform.one/config": "7.
|
|
59
|
+
"@multiplatform.one/config": "7.88.0",
|
|
60
60
|
"@tamagui/build": "2.7.6",
|
|
61
61
|
"@types/chrome": "^0.3.5",
|
|
62
62
|
"@types/firefox-webext-browser": "^143.0.1",
|
|
@@ -96,6 +96,50 @@ describe("native byte save (native boundary modeled)", () => {
|
|
|
96
96
|
expect(file.delete).not.toHaveBeenCalled();
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
+
it("names the iOS file from its URI when expo's File.name comes back empty (FZ-9198)", async () => {
|
|
100
|
+
native.platform.isIos = true;
|
|
101
|
+
native.platform.isAndroid = false;
|
|
102
|
+
directory.uri = "file:///Shared/File%20Provider%20Storage/";
|
|
103
|
+
file.uri = "file:///Shared/File%20Provider%20Storage/Pokemon_report.csv";
|
|
104
|
+
file.name = "";
|
|
105
|
+
const { saveFile } = await api();
|
|
106
|
+
await expect(saveFile({ ...options(), filename: "Pokemon_report.csv" })).resolves.toEqual({
|
|
107
|
+
status: "written",
|
|
108
|
+
uri: file.uri,
|
|
109
|
+
requestedFilename: "Pokemon_report.csv",
|
|
110
|
+
filename: "Pokemon_report.csv",
|
|
111
|
+
byteLength: 4,
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("keeps both on iOS: a taken name gets the next free copy number instead of failing (FZ-9198)", async () => {
|
|
116
|
+
native.platform.isIos = true;
|
|
117
|
+
native.platform.isAndroid = false;
|
|
118
|
+
directory.uri = "file:///picked/";
|
|
119
|
+
const taken = ["file:///picked/Pokemon_report.csv", "file:///picked/Pokemon_report%202.csv"];
|
|
120
|
+
Object.assign(directory, { list: vi.fn(() => taken.map((uri) => ({ uri }))) });
|
|
121
|
+
directory.createFile.mockImplementation((name: string) => {
|
|
122
|
+
if (taken.includes(`file:///picked/${encodeURIComponent(name)}`)) {
|
|
123
|
+
throw new Error(`File '${name}' already exists`);
|
|
124
|
+
}
|
|
125
|
+
file.uri = `file:///picked/${encodeURIComponent(name)}`;
|
|
126
|
+
file.name = "";
|
|
127
|
+
return file;
|
|
128
|
+
});
|
|
129
|
+
const { saveFile } = await api();
|
|
130
|
+
await expect(saveFile({ ...options(), filename: "Pokemon_report.csv" })).resolves.toEqual({
|
|
131
|
+
status: "written",
|
|
132
|
+
uri: "file:///picked/Pokemon_report%203.csv",
|
|
133
|
+
requestedFilename: "Pokemon_report.csv",
|
|
134
|
+
filename: "Pokemon_report 3.csv",
|
|
135
|
+
byteLength: 4,
|
|
136
|
+
});
|
|
137
|
+
expect(directory.createFile).toHaveBeenCalledExactlyOnceWith(
|
|
138
|
+
"Pokemon_report 3.csv",
|
|
139
|
+
"application/octet-stream",
|
|
140
|
+
);
|
|
141
|
+
});
|
|
142
|
+
|
|
99
143
|
it("omits an opaque Android document ID without reading it as a filename", async () => {
|
|
100
144
|
file.uri = "content://example.documents/tree/root/document/42";
|
|
101
145
|
const readName = vi.fn(() => "42");
|
|
@@ -53,10 +53,14 @@ export async function saveFile(options: FileSaveOptions): Promise<FileSaveResult
|
|
|
53
53
|
|
|
54
54
|
let file: File | undefined;
|
|
55
55
|
try {
|
|
56
|
-
|
|
56
|
+
const name = platform.isIos
|
|
57
|
+
? freeName(input.filename, takenNames(directory))
|
|
58
|
+
: input.filename;
|
|
59
|
+
file = directory.createFile(name, input.nativeMimeType);
|
|
57
60
|
const uri = file.uri;
|
|
58
61
|
// Android content URIs can contain opaque IDs instead of provider display names.
|
|
59
|
-
const filename =
|
|
62
|
+
const filename =
|
|
63
|
+
platform.isIos && uri.startsWith("file://") ? { filename: lastSegment(uri) || name } : {};
|
|
60
64
|
file.write(input.bytes);
|
|
61
65
|
return {
|
|
62
66
|
status: "written",
|
|
@@ -81,3 +85,32 @@ export async function saveFile(options: FileSaveOptions): Promise<FileSaveResult
|
|
|
81
85
|
busy = false;
|
|
82
86
|
}
|
|
83
87
|
}
|
|
88
|
+
|
|
89
|
+
function lastSegment(uri: string): string {
|
|
90
|
+
const segment = uri.replace(/\/+$/, "").split("/").pop() ?? "";
|
|
91
|
+
try {
|
|
92
|
+
return decodeURIComponent(segment);
|
|
93
|
+
} catch {
|
|
94
|
+
return segment;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function takenNames(directory: Directory): Set<string> {
|
|
99
|
+
try {
|
|
100
|
+
return new Set(directory.list().map((entry) => lastSegment(entry.uri)));
|
|
101
|
+
} catch {
|
|
102
|
+
return new Set();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function freeName(requested: string, taken: Set<string>): string {
|
|
107
|
+
if (!taken.has(requested)) return requested;
|
|
108
|
+
const dot = requested.lastIndexOf(".");
|
|
109
|
+
const stem = dot > 0 ? requested.slice(0, dot) : requested;
|
|
110
|
+
const extension = dot > 0 ? requested.slice(dot) : "";
|
|
111
|
+
for (let copy = 2; copy < 1000; copy++) {
|
|
112
|
+
const candidate = `${stem} ${copy}${extension}`;
|
|
113
|
+
if (!taken.has(candidate)) return candidate;
|
|
114
|
+
}
|
|
115
|
+
return requested;
|
|
116
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"saveFile.native.d.ts","sourceRoot":"","sources":["../../src/file/saveFile.native.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAmB,MAAM,SAAS,CAAC;AAIpG,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,
|
|
1
|
+
{"version":3,"file":"saveFile.native.d.ts","sourceRoot":"","sources":["../../src/file/saveFile.native.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAmB,MAAM,SAAS,CAAC;AAIpG,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAgFhF"}
|