feat(extension): update devices UI for new auth model
- Show revoked devices in collapsible section with strikethrough styling - Fetch revoked.json via new list_revoked message + router case - Registration flow uses register_device WASM API (private keys internal) - Display revoked_by and timestamp for each revoked entry - Update setup wizard to use new register_device API Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,13 @@
|
||||
import { setState, sendMessage, navigate, escapeHtml } from '../../shared/state';
|
||||
import type { Device } from '../../shared/types';
|
||||
|
||||
interface RevokedEntry {
|
||||
name: string;
|
||||
public_key: string;
|
||||
revoked_at: number;
|
||||
revoked_by: string;
|
||||
}
|
||||
|
||||
function relativeTime(unixSec: number): string {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const diff = now - unixSec;
|
||||
@@ -36,16 +43,62 @@ export async function renderDevices(app: HTMLElement): Promise<void> {
|
||||
const stored = await chrome.storage.local.get(['device_name']);
|
||||
const currentDeviceName: string | undefined = stored.device_name as string | undefined;
|
||||
|
||||
// Fetch device list
|
||||
const resp = await sendMessage({ type: 'list_devices' });
|
||||
if (!resp.ok) {
|
||||
// Fetch active device list and revoked list in parallel
|
||||
const [devicesResp, revokedResp] = await Promise.all([
|
||||
sendMessage({ type: 'list_devices' }),
|
||||
sendMessage({ type: 'list_revoked' }),
|
||||
]);
|
||||
|
||||
if (!devicesResp.ok) {
|
||||
app.innerHTML = `<div class="pad"><p class="error">Failed to load devices</p></div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
const devices = (resp.data as { devices: Device[] }).devices;
|
||||
const devices = (devicesResp.data as { devices: Device[] }).devices;
|
||||
const revokedDevices: RevokedEntry[] = revokedResp.ok
|
||||
? (revokedResp.data as { revoked: RevokedEntry[] }).revoked
|
||||
: [];
|
||||
|
||||
const isRegistered = currentDeviceName && devices.some((d) => d.name === currentDeviceName);
|
||||
|
||||
const activeDevicesHtml = devices.length === 0
|
||||
? `<p class="muted" style="text-align:center;margin-top:32px;">No devices registered</p>`
|
||||
: devices.map((d) => {
|
||||
const isCurrentDevice = d.name === currentDeviceName;
|
||||
return `
|
||||
<div class="device-row">
|
||||
<div class="device-row__info">
|
||||
<span class="device-row__name">${escapeHtml(d.name)}${isCurrentDevice ? ' <span class="device-row__you">← you</span>' : ''}</span>
|
||||
<span class="device-row__meta">added ${relativeTime(d.added_at)}</span>
|
||||
</div>
|
||||
${isCurrentDevice ? '' : `<button class="device-row__revoke" data-revoke="${escapeHtml(d.name)}">revoke</button>`}
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
const revokedSectionHtml = revokedDevices.length === 0 ? '' : `
|
||||
<details class="revoked-section" style="margin-top:16px;">
|
||||
<summary class="muted" style="cursor:pointer;font-size:0.85em;">
|
||||
${revokedDevices.length} revoked device${revokedDevices.length !== 1 ? 's' : ''}
|
||||
</summary>
|
||||
<div style="margin-top:8px;">
|
||||
${revokedDevices.map((r) => `
|
||||
<div class="device-row device-row--revoked">
|
||||
<div class="device-row__info">
|
||||
<span class="device-row__name" style="text-decoration:line-through;opacity:0.5;">
|
||||
${escapeHtml(r.name)}
|
||||
</span>
|
||||
<span class="device-row__meta">
|
||||
revoked ${relativeTime(r.revoked_at)}
|
||||
${r.revoked_by !== 'unknown' ? ` by ${escapeHtml(r.revoked_by)}` : ''}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</details>
|
||||
`;
|
||||
|
||||
app.innerHTML = `
|
||||
<div class="pad">
|
||||
<div class="devices-header">
|
||||
@@ -58,20 +111,8 @@ export async function renderDevices(app: HTMLElement): Promise<void> {
|
||||
<button class="btn btn-primary" id="register-btn">Register this device</button>
|
||||
</div>
|
||||
` : ''}
|
||||
${devices.length === 0
|
||||
? `<p class="muted" style="text-align:center;margin-top:32px;">No devices registered</p>`
|
||||
: devices.map((d) => {
|
||||
const isCurrentDevice = d.name === currentDeviceName;
|
||||
return `
|
||||
<div class="device-row">
|
||||
<div class="device-row__info">
|
||||
<span class="device-row__name">${escapeHtml(d.name)}${isCurrentDevice ? ' <span class="device-row__you">← you</span>' : ''}</span>
|
||||
<span class="device-row__meta">added ${relativeTime(d.added_at)}</span>
|
||||
</div>
|
||||
${isCurrentDevice ? '' : `<button class="device-row__revoke" data-revoke="${escapeHtml(d.name)}">revoke</button>`}
|
||||
</div>
|
||||
`;
|
||||
}).join('')}
|
||||
${activeDevicesHtml}
|
||||
${revokedSectionHtml}
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user