/** * Sync page — device linking via autopass; create invite, pair with invite, show status. * Depends: core/utils.js ($), core/messaging.js (sendToNative), ui/toast.js (showToast) */ /** * Update the sync status UI from the native host. */ function updateSyncStatus() { chrome.runtime.sendMessage( { target: 'holesail-native', action: 'send', payload: { type: 'getSyncStatus' } }, (response) => { if (chrome.runtime.lastError) return; const dot = $('syncStatusDot'); const text = $('syncStatusText'); const lastSynced = $('syncLastSynced'); const btnCopy = $('btnCopyInvite'); const inviteDisplay = $('syncInviteDisplay'); if (!text) return; if (!response || response.error) { if (dot) dot.style.background = 'var(--text4)'; text.textContent = 'Not linked'; if (lastSynced) lastSynced.textContent = ''; if (btnCopy) btnCopy.style.display = 'none'; if (inviteDisplay) inviteDisplay.style.display = 'none'; return; } if (response.linked) { if (dot) dot.style.background = 'var(--green)'; text.textContent = 'Linked'; if (response.lastSyncedAt) { if (lastSynced) lastSynced.textContent = 'Last synced ' + timeAgo(response.lastSyncedAt); } else if (lastSynced) lastSynced.textContent = ''; if (response.invite) { if (btnCopy) btnCopy.style.display = ''; if (inviteDisplay) { inviteDisplay.textContent = response.invite; inviteDisplay.style.display = 'block'; } } else { if (btnCopy) btnCopy.style.display = 'none'; if (inviteDisplay) inviteDisplay.style.display = 'none'; } } else { if (dot) dot.style.background = 'var(--text4)'; text.textContent = 'Not linked'; if (lastSynced) lastSynced.textContent = ''; if (btnCopy) btnCopy.style.display = 'none'; if (inviteDisplay) inviteDisplay.style.display = 'none'; } } ); } /** * Attach event listeners for the Sync page. */ function setupSyncEvents() { const btnCreate = $('btnCreateSyncInvite'); const btnCopy = $('btnCopyInvite'); const btnPair = $('btnPairWithInvite'); const inviteInput = $('syncInviteInput'); const inviteDisplay = $('syncInviteDisplay'); btnCreate?.addEventListener('click', () => { if (btnCreate.disabled) return; btnCreate.disabled = true; btnCreate.textContent = 'Creating…'; chrome.runtime.sendMessage( { target: 'holesail-native', action: 'send', payload: { type: 'createSyncInvite' } }, (response) => { btnCreate.disabled = false; btnCreate.textContent = 'Create invite'; if (chrome.runtime.lastError) { showToast('Failed: ' + (chrome.runtime.lastError.message || 'unknown'), 'error'); return; } if (response && response.ok && response.invite) { inviteDisplay.style.display = 'block'; inviteDisplay.textContent = response.invite; btnCopy.style.display = ''; showToast('Invite created. Share it with the other device.', 'success'); updateSyncStatus(); } else { showToast(response && response.error ? response.error : 'Failed to create invite', 'error'); } } ); }); btnCopy?.addEventListener('click', () => { if (!inviteDisplay || !inviteDisplay.textContent) return; navigator.clipboard.writeText(inviteDisplay.textContent).then(() => { showToast('Invite copied to clipboard', 'success'); }).catch(() => { showToast('Copy failed', 'error'); }); }); btnPair?.addEventListener('click', () => { const invite = inviteInput?.value?.trim(); if (!invite) { showToast('Paste an invite first', 'error'); return; } if (btnPair.disabled) return; btnPair.disabled = true; btnPair.textContent = 'Linking…'; chrome.runtime.sendMessage( { target: 'holesail-native', action: 'send', payload: { type: 'pairWithInvite', payload: { invite } } }, (response) => { btnPair.disabled = false; btnPair.textContent = 'Link device'; if (chrome.runtime.lastError) { showToast('Failed: ' + (chrome.runtime.lastError.message || 'unknown'), 'error'); return; } if (response && response.ok) { showToast('Device linked. State has been synced.', 'success'); if (inviteInput) inviteInput.value = ''; updateSyncStatus(); if (typeof refresh === 'function') refresh(); } else { showToast(response && response.error ? response.error : 'Linking failed', 'error'); } } ); }); }