Files
dchain/desktop
vsecoder 96b347076e feat(desktop): Contacts + Settings→Devices + expanded Profile + QR + keybinds (v2.2.0-rc1)
Completes the desktop feature surface ahead of the v2.2.0 tag. Only
auto-update + packaging remain.

Settings — now two-paned (nav on the left, pages on the right):
  * NodePage — URL ping-on-commit + API token field.
  * IdentityPage — pub key / X25519 pub, Export (safe-save dialog) /
    Import (open dialog + wipe + replace) / Delete identity.
  * DevicesPage — full multi-device UI: list every active device with
    a THIS DEVICE badge; Unlink button on every other row submits
    UNLINK_DEVICE + optimistic local remove; Link new device modal
    takes {code, device key, name}, submits LINK_DEVICE, then ships
    the handshake envelope (master Ed25519 priv encrypted for the
    new X25519) — same protocol as mobile's primary-device modal.
  * AboutPage — version, platform, Gitea links.
  * store.settingsPage discriminated union keeps selection across
    section switches.

Contacts section (now real):
  * ContactsList — alphabetical, filter-as-you-type; each row shows
    avatar letter + name + short address.
  * ContactsDetail — profile card (username/alias/pub) + Open chat /
    View posts / Copy address actions + stats grid
    (Balance, Devices, Encryption, Added) + Identity card with
    DC address, username, published X25519, device_count.
  * store.selectedContact persists across navigation.

Profile section (expanded):
  * ProfileList — big avatar + pub key + contacts count.
  * ProfileDetail — balance hero, quick actions (My posts →
    feed author wall, Manage devices → Settings→Devices, Copy
    address), Identity card, inline Linked devices list with a
    THIS DEVICE badge matching the Settings page.

Receive modal — canvas QR via `qrcode` (new dep, ~5 KB gzipped),
white-on-transparent so it sits inside the same black modal chrome.

Global keybinds (useGlobalKeybinds hook mounted in Shell):
  * Ctrl/Cmd+W — close the current conversation (drops activeChat,
    keeps section). Does NOT close the window.
  * Ctrl/Cmd+K — jump to Contacts.
  * Ctrl/Cmd+, — Settings.
  Each guards against being in a text field so typing `k,` in a
  composer / search doesn't hijack.

docs/ROADMAP.md — rc1 row flipped to done; v2.2.0 narrows to
auto-update + packaging + optional attachments in Compose.
2026-04-22 18:39:39 +03:00
..

DChain Desktop

Electron shell for the DChain messenger and social feed.

Same functionality as the mobile client-app, re-imagined with a keyboard-first, 3-panel desktop layout:

┌──────────────────────────────────────────────────────────┐
│  DChain                                                   │ titlebar (drag)
├──────┬───────────────────┬────────────────────────────────┤
│ nav  │      list         │             detail             │
│ 72px │   340px fixed     │            flex 1              │
├──────┴───────────────────┴────────────────────────────────┤
│  ● online  ·  node.example:8080  ·  height 10942          │ status bar
└──────────────────────────────────────────────────────────┘

Sections (left rail): Messages · Feed · Wallet · Contacts · Settings · Profile.

Quick start

cd desktop
npm install
npm run dev          # concurrently: Vite dev server + Electron

The first boot will show the Welcome screen. Pick Create to generate fresh keys, or Import a node.json exported from the mobile client.

Build

npm run build        # produces dist/ (renderer) + dist-electron/ (main) + installers

Default installers are built with electron-builder: .dmg on macOS, NSIS .exe on Windows, AppImage + .deb on Linux. Adjust build.* in package.json for signing / notarisation.

Layout

  • electron/ — main + preload. TypeScript, compiled to dist-electron/ by tsc -p electron/tsconfig.json.
  • src/ — renderer. React + Vite. @/ aliases to src/.
  • src/shell/ — 3-panel chrome.
  • src/sections/ — one folder per nav section, each exports { List, Detail }.
  • src/auth/Welcome.tsx — shown when no key is loaded.
  • src/lib/ — api, storage, store, types. Mirrors (without React-Native deps) the relevant pieces of ../client-app/lib/.

Security model

Master Ed25519 priv lives in the OS keychain via Electron safeStorage (macOS Keychain / Windows DPAPI / libsecret). A renderer compromise cannot read or exfiltrate the key — it always travels through window.dchain.keyfile.* IPC, which main.ts validates and mediates.

contextIsolation: true, nodeIntegration: false. CSP in index.html pins script sources to 'self' while allowing connect-src * so the renderer can hit any node the user configures.

Pairing (v2.2.0-alpha5+)

Desktop will reuse the same 6-digit-code + relay-envelope handshake as the mobile client. The scaffold in src/auth/Welcome.tsx stubs the button until the polling loop lands.

Multi-device fan-out

When the node is at v2.2.0-alpha1+, lib/api.ts:fetchDevices returns every linked X25519 pub for a given identity; the sender then encrypts one envelope per device. Legacy nodes return an empty array and the client falls back to IdentityInfo.x25519_pub, preserving the pre-multi-device behaviour.