// The contacts and messages sections of the Android Sync page, on the Android // application only (docs/MESHBAY_DESIGN.md §9.13). Loaded on demand: no other // client has it. Where the copies go is the page's destination section; these // only turn each backup on or off. import { html, useState, useEffect, useCallback } from './vendor/htm-preact.js'; import { t } from './i18n.js'; import { KINDS, subscribe, runDocSync, failureText } from './phone-sync.js'; // What each kind says in its own words; everything else is shared. const TEXT = { contacts: { intro: 'contactsync.intro', permission: 'contactsync.permission_needed', }, messages: { intro: 'messagesync.intro', permission: 'messagesync.permission_needed', }, }; function stateLine(st, status) { switch (st.phase) { case 'connecting': return t('photosync.state_connecting'); case 'sending': return t('phonesync.state_sending'); case 'offline': return t('photosync.state_offline'); case 'interrupted': return t('photosync.state_interrupted'); case 'up_to_date': return t('photosync.state_up_to_date'); default: return status && status.lastCompleted ? t('photosync.state_up_to_date') : ''; } } export function DocSyncSection({ kind, destination }) { const { sync, heading } = KINDS[kind]; const [status, setStatus] = useState(null); const [run, setRun] = useState({ phase: 'idle' }); const [busy, setBusy] = useState(false); const [error, setError] = useState(''); const refresh = useCallback(() => sync.status().then(setStatus).catch(() => {}), [sync]); useEffect(() => { refresh(); }, [refresh]); useEffect(() => subscribe(kind, (st) => { setRun(st); if (st.phase !== 'sending' && st.phase !== 'connecting') refresh(); }), [kind, refresh]); if (!status) return null; const running = run.phase === 'sending' || run.phase === 'connecting'; const turnOn = async () => { setBusy(true); setError(''); try { let st = status; if (st.permission !== 'granted') st = await sync.permit(); if (st.permission !== 'granted') { setStatus(st); return; } setStatus(await sync.configure({})); runDocSync(kind); } catch (e) { setError(e.message); } finally { setBusy(false); } }; const turnOff = async () => { setError(''); try { setStatus(await sync.configure(null)); } catch (e) { setError(e.message); } }; const backUpNow = async () => { setError(''); try { await runDocSync(kind, { force: true }); } catch (e) { setError(e.message); } }; return html`
${t(TEXT[kind].intro)}
${!destination && html`${t('androidsync.dest_needed')}
`} ${destination && status.permission !== 'granted' && html`${t(TEXT[kind].permission)}
`} ${destination && html` `} `} ${status.on && html` ${destination && html`${t('androidsync.dest_needed')}
`}${t('phonesync.copies_sent', { n: status.sent })} ${' · '} ${status.lastCompleted ? t('photosync.last_run', { when: new Date(status.lastCompleted).toLocaleString() }) : t('photosync.never')}
${status.lastSent && html`${t('phonesync.last_file', { name: status.lastSent })}
`} ${status.failure ? html`${failureText(status.failure)}
` : stateLine(run, status) && html`${stateLine(run, status)}
`}${error}
`}