aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/photo-sync-settings.js
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/photo-sync-settings.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/photo-sync-settings.js97
1 files changed, 27 insertions, 70 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/photo-sync-settings.js b/packages/meshbay-hub/src/meshbay_hub/static/photo-sync-settings.js
index b5e9a30..7809efa 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/photo-sync-settings.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/photo-sync-settings.js
@@ -1,14 +1,14 @@
// The "Photo backup" section of the Android Sync page, on the Android
// application only (docs/MESHBAY_DESIGN.md §9.12). Loaded on demand: no other
-// client has it.
+// client has it. Where the photos go is the page's destination section;
+// this one chooses which.
import { html, useState, useEffect, useCallback } from './vendor/htm-preact.js';
import { t } from './i18n.js';
import * as platform from './platform.js';
-import { hubFetch } from './hub-client.js';
import { formatSize } from './file-utils.js';
import { ask } from './ask.js';
-import { subscribe, runPhotoSync, probeGroup } from './photo-sync.js';
+import { subscribe, runPhotoSync } from './photo-sync.js';
function stateLine(st, status) {
switch (st.phase) {
@@ -30,10 +30,11 @@ const FAILURES = {
no_such_directory: 'photosync.fail_no_folder',
root_unavailable: 'photosync.fail_unavailable',
not_a_member: 'photosync.fail_not_member',
+ not_private: 'photosync.fail_not_private',
permission: 'photosync.fail_permission',
};
-export function PhotoSyncSection({ user, groups }) {
+export function PhotoSyncSection({ destination }) {
const [status, setStatus] = useState(null);
const [run, setRun] = useState({ phase: 'idle' });
const [editing, setEditing] = useState(false);
@@ -51,6 +52,16 @@ export function PhotoSyncSection({ user, groups }) {
if (!status) return null;
const cfg = status.config;
+ if (!destination) {
+ return html`
+ <div class="settings-section">
+ <h3 class="settings-heading">${t('photosync.heading')}</h3>
+ <p class="settings-hint">${t('photosync.intro')}</p>
+ <p class="settings-hint">${t('androidsync.dest_needed')}</p>
+ </div>
+ `;
+ }
+
const backUpNow = async () => {
setError('');
try {
@@ -82,11 +93,9 @@ export function PhotoSyncSection({ user, groups }) {
${cfg && !editing && html`
<div class="settings-row">
<span class="settings-label settings-name">
- ${t('photosync.summary', { group: cfg.groupName, owner: cfg.owner, folder: cfg.folder })}
+ ${t('phonesync.summary', { group: destination.groupName, dir: status.dir })}
</span>
</div>
- ${cfg.account !== user.username && html`
- <p class="settings-hint">${t('photosync.other_account', { account: cfg.account })}</p>`}
<p class="settings-hint">
${t('photosync.sent_total', { n: status.sent })}
${' · '}
@@ -107,7 +116,7 @@ export function PhotoSyncSection({ user, groups }) {
</div>
`}
${editing && html`
- <${Setup} user=${user} groups=${groups} status=${status}
+ <${Setup} status=${status}
onPermission=${setStatus}
onDone=${(next) => { setEditing(false); if (next) { setStatus(next); runPhotoSync(); } }} />`}
${error && html`<p class="error-msg">${error}</p>`}
@@ -115,12 +124,8 @@ export function PhotoSyncSection({ user, groups }) {
`;
}
-function Setup({ user, groups, status, onPermission, onDone }) {
- const cfg = status.config && status.config.account === user.username ? status.config : null;
- const [groupId, setGroupId] = useState(cfg ? cfg.groupId : '');
- const [folders, setFolders] = useState(null); // null while unknown
- const [probe, setProbe] = useState(''); // '', 'checking', 'offline'
- const [folder, setFolder] = useState(cfg ? cfg.folder : '');
+function Setup({ status, onPermission, onDone }) {
+ const cfg = status.config;
const [albums, setAlbums] = useState(null);
const [chosen, setChosen] = useState(() => new Set(cfg ? cfg.albums : []));
const [existing, setExisting] = useState(cfg ? cfg.includeExisting : true);
@@ -138,20 +143,6 @@ function Setup({ user, groups, status, onPermission, onDone }) {
}).catch((e) => setError(e.message));
}, [permitted]);
- useEffect(() => {
- setFolders(null);
- if (!groupId) return;
- let gone = false;
- setProbe('checking');
- probeGroup(groupId).then((r) => {
- if (gone) return;
- setProbe('');
- setFolders(r.folders);
- setFolder((f) => (r.folders.includes(f) ? f : (r.folders[0] || '')));
- }).catch(() => { if (!gone) setProbe('offline'); });
- return () => { gone = true; };
- }, [groupId]);
-
const allow = async () => {
setError('');
try { onPermission(await platform.photoSync.permit()); } catch (e) { setError(e.message); }
@@ -164,30 +155,16 @@ function Setup({ user, groups, status, onPermission, onDone }) {
});
const start = async () => {
- const group = groups.find((g) => g.id === groupId);
- if (!group || !folder || !chosen.size) return;
+ if (!chosen.size) return;
setBusy(true);
setError('');
try {
- const settings = {
- account: user.username, groupId, groupName: group.name,
- owner: group.owner_username || '', folder, albums: [...chosen], includeExisting: existing,
- };
- // Asked when the photos would go somewhere new, or from somewhere new in
- // time — never for a change of albums alone, which the person just made.
- const same = cfg && cfg.groupId === groupId && cfg.folder === folder
- && cfg.includeExisting === existing;
- if (!same) {
- const [est, members] = await Promise.all([
- platform.photoSync.estimate(settings),
- hubFetch(`/v1/groups/${groupId}/members`, { token: user.token })
- .then((r) => (r.members || []).length).catch(() => null),
- ]);
- const params = {
- group: group.name, owner: group.owner_username || '', folder,
- members: members == null ? '?' : members,
- count: est.count, size: formatSize(est.bytes),
- };
+ const settings = { albums: [...chosen], includeExisting: existing };
+ // Asked when the photos would come from somewhere new in time; never
+ // for a change of albums alone, which the person just made.
+ if (!cfg || cfg.includeExisting !== existing) {
+ const est = await platform.photoSync.estimate(settings);
+ const params = { count: est.count, size: formatSize(est.bytes), dir: status.dir };
const yes = await ask(t(existing && est.count > 0 ? 'photosync.confirm'
: 'photosync.confirm_new', params));
if (!yes) { setBusy(false); return; }
@@ -210,26 +187,6 @@ function Setup({ user, groups, status, onPermission, onDone }) {
<button class="btn-secondary" onClick=${allow}>${t('photosync.permission_allow')}</button>
`}
${permitted && html`
- <div class="settings-row">
- <span class="settings-label">${t('photosync.group')}</span>
- <select class="settings-select" value=${groupId}
- onChange=${(e) => setGroupId(e.target.value)}>
- <option value="">${t('photosync.choose_group')}</option>
- ${groups.map((g) => html`<option key=${g.id} value=${g.id}>
- ${g.owner_username ? `${g.name} (@${g.owner_username})` : g.name}</option>`)}
- </select>
- </div>
- ${probe === 'checking' && html`<p class="settings-hint">${t('photosync.checking_group')}</p>`}
- ${probe === 'offline' && html`<p class="settings-hint">${t('photosync.group_offline')}</p>`}
- ${folders && !folders.length && html`<p class="settings-hint">${t('photosync.no_folder')}</p>`}
- ${folders && folders.length > 0 && html`
- <div class="settings-row">
- <span class="settings-label">${t('photosync.folder')}</span>
- <select class="settings-select" value=${folder} onChange=${(e) => setFolder(e.target.value)}>
- ${folders.map((f) => html`<option key=${f} value=${f}>${f}</option>`)}
- </select>
- </div>
- `}
<p class="settings-label" style="margin-top:10px">${t('photosync.albums')}</p>
${(albums || []).map((a) => html`
<label class="settings-choice" key=${a.id}>
@@ -253,7 +210,7 @@ function Setup({ user, groups, status, onPermission, onDone }) {
</label>
<div class="settings-row" style="gap:8px; justify-content:flex-start">
<button class="admin-btn" onClick=${start}
- disabled=${busy || !groupId || !folder || !chosen.size}>${t('photosync.start')}</button>
+ disabled=${busy || !chosen.size}>${t('photosync.start')}</button>
<button class="btn-secondary" onClick=${() => onDone(null)}>${t('dialog.cancel')}</button>
</div>
`}