// The WhatsApp section of the Android Sync page, on the Android application // only (docs/MESHBAY_DESIGN.md §9.13). Loaded on demand: no other client has // it. WhatsApp's chats are only ever in its own encrypted backups, which this // copies for a restore onto a new phone; its media too, if asked. import { html, useState, useEffect, useCallback } from './vendor/htm-preact.js'; import { t } from './i18n.js'; import { ask } from './ask.js'; import * as platform from './platform.js'; import { subscribeWhatsApp, runWhatsAppSync } from './drive-sync.js'; import { failureText } from './phone-sync.js'; function stateLine(st, status) { switch (st.phase) { case 'waiting_wifi': return t('photosync.state_waiting_wifi'); case 'connecting': return t('photosync.state_connecting'); case 'sending': return t('photosync.state_sending', { done: st.done, total: st.total }); 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 WhatsAppSyncSection({ destination }) { const sync = platform.whatsappSync; 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(() => subscribeWhatsApp((st) => { setRun(st); if (st.phase !== 'sending' && st.phase !== 'connecting') refresh(); }), [refresh]); if (!status) return null; const chosen = (status.folders || []).length > 0; const media = Boolean(status.options && status.options.media); const running = run.phase === 'sending' || run.phase === 'connecting'; const act = async (fn) => { setBusy(true); setError(''); try { const next = await fn(); if (next) setStatus(next); } catch (e) { setError(/Refused: not_whatsapp/.test(e.message || '') ? t('whatsappsync.refused') : e.message); } finally { setBusy(false); } }; const turnOn = (withMedia) => act(async () => { const next = await sync.configure({ media: withMedia }); runWhatsAppSync(); return next; }); const backUpNow = () => act(async () => { if (!status.unmetered && !await ask(t('drivesync.metered_confirm'))) return null; await runWhatsAppSync({ force: true, metered: !status.unmetered }); return null; }); return html`
${t('whatsappsync.intro')}
`} ${!destination && html`${t('androidsync.dest_needed')}
`} ${destination && html`${t('whatsappsync.folder_hint')}
`} `} ${destination && chosen && !status.on && html` `} ${destination && status.on && html`${t('drivesync.sent_total', { n: status.sent })} ${' · '} ${status.lastCompleted ? t('photosync.last_run', { when: new Date(status.lastCompleted).toLocaleString() }) : t('photosync.never')}
${status.failure ? html`${failureText(status.failure)}
` : stateLine(run, status) && html`${stateLine(run, status)}
`}${t('whatsappsync.restore_hint')}
${error}
`}