aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/platform.js
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/platform.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/platform.js83
1 files changed, 74 insertions, 9 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/platform.js b/packages/meshbay-hub/src/meshbay_hub/static/platform.js
index 4b507dc..47e3a72 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/platform.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/platform.js
@@ -238,21 +238,84 @@ export const push = {
export const photoSync = {
available: Boolean(bridge && bridge.photoSync),
status() { return bridge.photoSync.status(); },
- permit() { return bridge.photoSync.permit(); },
- albums() { return bridge.photoSync.albums(); },
+ permit(withVideos) { return bridge.photoSync.permit(withVideos); },
+ albums(withVideos) { return bridge.photoSync.albums(withVideos); },
configure(settings) { return bridge.photoSync.configure(settings); },
estimate(settings) { return bridge.photoSync.estimate(settings); },
plan() { return bridge.photoSync.plan(); },
sent(token, dir, name) { return bridge.photoSync.sent(token, dir, name); },
+ manifest() { return bridge.photoSync.manifest(); },
+ manifestSent(token) { return bridge.photoSync.manifestSent(token); },
completed() { return bridge.photoSync.completed(); },
failed(code, text) { return bridge.photoSync.failed(code, text); },
keepAlive(on, text) { return bridge.photoSync.keepAlive(on, text); },
- /** The bytes of a photo the last plan issued, as a File named for the node. */
- async file(token, name) {
- const r = await fetch(`/photosync/${encodeURIComponent(token)}`, { cache: 'no-store' });
- if (!r.ok) throw new Error('This photo is no longer on the phone');
- return new File([await r.blob()], name);
- },
+ file: (token, name, size) => rangedFile('/photosync/', token, name, size),
+};
+
+/**
+ * A file the phone issued under `token`, as what an upload reads (`name`,
+ * `size`, `slice(a, b).arrayBuffer()`): each slice is fetched from the phone
+ * when it is read, so a video of gigabytes is never whole in this page. The
+ * slice is in the address (`?range=a-b`), not a Range header: the WebView
+ * does not hand that header to the application that answers.
+ */
+function rangedFile(prefix, token, name, size) {
+ const url = `${prefix}${encodeURIComponent(token)}`;
+ const part = (start, end) => ({
+ async arrayBuffer() {
+ if (end <= start) return new ArrayBuffer(0);
+ const r = await fetch(`${url}?range=${start}-${end - 1}`, { cache: 'no-store' });
+ if (!r.ok) throw new Error('This file is no longer on the phone');
+ return r.arrayBuffer();
+ },
+ });
+ return {
+ name, size,
+ slice: (a = 0, b = size) => part(Math.max(0, a), Math.min(b, size)),
+ };
+}
+
+/**
+ * Backing up folders the person chose through the system's picker
+ * (docs/MESHBAY_DESIGN.md §9.13), to the destination every kind shares.
+ * Android only, like `photoSync`. See drive-sync.js.
+ */
+const driveBridge = bridge && bridge.driveSync;
+export const driveSync = {
+ available: Boolean(driveBridge),
+ status() { return driveBridge.status(); },
+ addFolder() { return driveBridge.addFolder(); },
+ removeFolder(id) { return driveBridge.removeFolder(id); },
+ configure(settings) { return driveBridge.configure(settings); },
+ plan() { return driveBridge.plan(); },
+ sent(token, dir, name) { return driveBridge.sent(token, dir, name); },
+ manifest() { return driveBridge.manifest(); },
+ manifestSent(token) { return driveBridge.manifestSent(token); },
+ completed() { return driveBridge.completed(); },
+ failed(code, text) { return driveBridge.failed(code, text); },
+ keepAlive(on, text) { return driveBridge.keepAlive(on, text); },
+ file: (token, name, size) => rangedFile('/drivesync/', token, name, size),
+};
+
+/**
+ * Backing up WhatsApp's own folder, chosen once through the picker (its
+ * encrypted chat backups, and its media if asked), like `driveSync`.
+ */
+const waBridge = bridge && bridge.whatsappSync;
+export const whatsappSync = {
+ available: Boolean(waBridge),
+ status() { return waBridge.status(); },
+ chooseFolder() { return waBridge.chooseFolder(); },
+ forgetFolder(id) { return waBridge.forgetFolder(id); },
+ configure(settings) { return waBridge.configure(settings); },
+ plan() { return waBridge.plan(); },
+ sent(token, dir, name) { return waBridge.sent(token, dir, name); },
+ manifest() { return waBridge.manifest(); },
+ manifestSent(token) { return waBridge.manifestSent(token); },
+ completed() { return waBridge.completed(); },
+ failed(code, text) { return waBridge.failed(code, text); },
+ keepAlive(on, text) { return waBridge.keepAlive(on, text); },
+ file: (token, name, size) => rangedFile('/wasync/', token, name, size),
};
/**
@@ -267,7 +330,7 @@ export const phoneSync = {
};
/**
- * Backing up one kind of this phone's data (contacts, messages) to the destination
+ * Backing up one kind of this phone's data (contacts, calendar, messages) to the destination
* every kind shares (docs/MESHBAY_DESIGN.md §9.13). Android only, like
* `photoSync`. See phone-sync.js.
*/
@@ -282,6 +345,7 @@ function docSync(name) {
sent(token, dir, n) { return b.sent(token, dir, n); },
completed() { return b.completed(); },
failed(code, text) { return b.failed(code, text); },
+ openSettings() { return b.openSettings(); },
/** The file the last plan wrote, as a File named for the node. */
async file(token, fileName) {
const r = await fetch(`/phonesync/${encodeURIComponent(token)}`, { cache: 'no-store' });
@@ -292,6 +356,7 @@ function docSync(name) {
}
export const contactSync = docSync('contactSync');
+export const calendarSync = docSync('calendarSync');
/** Absent from the Play build, whose policy keeps text messages from it. */
export const messageSync = docSync('messageSync');