Jason Tudisco b5528b0ecf Add Nostr dual-sync variant (folder + relay)
Fifth library variant that keeps folder sync for offline/local use
AND adds Nostr relay sync for cross-device reach via WebSocket.
Both transports run simultaneously - writes go to folder AND Nostr,
sync imports from both and bridges events between them.

Works from file:// since Nostr uses WebSocket (not fetch/WebRTC).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 22:44:39 -06:00

71 lines
2.2 KiB
TypeScript

import { FolderSyncDB } from '../../nostr/src/index.ts';
import { initPasteApp } from '../shared.ts';
document.addEventListener('DOMContentLoaded', async () => {
const db = await FolderSyncDB.open({ autoSyncIntervalMs: 3000 });
await initPasteApp(db as any, 'nostr');
// ── Nostr room UI ──────────────────────────────────────────
const roomInput = document.querySelector('#room-input') as HTMLInputElement;
const joinBtn = document.querySelector('#join-room') as HTMLButtonElement;
const nostrStatus = document.querySelector('#nostr-status')!;
function updateNostrUI(connected: boolean, room?: string) {
if (connected && room) {
nostrStatus.textContent = `Connected: ${room}`;
nostrStatus.className = 'status ok';
joinBtn.textContent = 'Leave';
roomInput.disabled = true;
} else {
nostrStatus.textContent = 'Not connected';
nostrStatus.className = 'status';
joinBtn.textContent = 'Join room';
roomInput.disabled = false;
}
}
joinBtn.addEventListener('click', async () => {
if (db.isConnected()) {
db.leaveRoom();
updateNostrUI(false);
} else {
const key = roomInput.value.trim();
if (!key) {
nostrStatus.textContent = 'Enter a room key';
nostrStatus.className = 'status err';
return;
}
try {
nostrStatus.textContent = 'Connecting...';
nostrStatus.className = 'status';
await db.joinRoom(key);
updateNostrUI(true, key);
} catch (e: unknown) {
nostrStatus.textContent = 'Error: ' + (e as Error).message;
nostrStatus.className = 'status err';
}
}
});
roomInput.addEventListener('keydown', (e: KeyboardEvent) => {
if (e.key === 'Enter') joinBtn.click();
});
// Restore saved room
if (db.isConnected()) {
updateNostrUI(true, db.currentRoom ?? undefined);
roomInput.value = db.currentRoom ?? '';
} else {
updateNostrUI(false);
}
db.on('nostr:connected', (data: any) => {
updateNostrUI(true, data?.roomKey);
});
db.on('nostr:disconnected', () => {
updateNostrUI(false);
});
});