feat(direct-call): call history, recent contacts, deregister button
Some checks failed
Mirror to GitHub / mirror (push) Failing after 39s
Build Release Binaries / build-amd64 (push) Failing after 3m41s

Persistent JSON-backed call history for the direct-call screen so users
can see what they've placed / received / missed and dial back with one
click. Also fixes two small latent UX issues reported alongside.

Backend (Rust)
- new crate/module desktop/src-tauri/src/history.rs: thread-safe in-
  process store (OnceLock<RwLock<Vec<CallHistoryEntry>>>) backed by
  <APP_DATA_DIR>/call_history.json. Atomic writes via temp+rename. Max
  200 entries, FIFO pruning. CallDirection { Placed, Received, Missed }.
- Log hooks in the signal loop + commands:
    * place_call     → Placed entry (with target fingerprint)
    * DirectCallOffer → Missed entry up front; upgraded to Received
                        inside answer_call when accept_mode != Reject
                        via history::mark_received_if_pending(call_id).
                        If user rejects or never answers, it stays Missed.
- New Tauri commands:
    * get_call_history()     → all entries, newest first
    * get_recent_contacts()  → unique peers by fp, newest interaction first
    * clear_call_history()   → wipes JSON + in-memory
    * deregister()           → tears down signal transport + endpoint
  Backend emits `history-changed` events so the UI can live-refresh
  without polling.

Frontend (main.ts + index.html + style.css)
- Direct-call panel now has:
    * Recent contacts chip row (top 6 unique peers). Click a chip → dial.
    * Call history list (up to 50 rows). Direction icon (↗ placed, ↙
      received, ✗ missed), peer alias/fp, relative timestamp, callback
      button. Both click handlers populate target-fp and fire place_call.
    * Deregister button in the "registered" header — calls the new
      deregister command, tears down the signal transport, returns the
      UI to the pre-register state.
    * Clear-history link in the history header.
- Subscribes to `history-changed` events so the list updates the moment
  the backend logs a new entry. Also refreshed on register + after a
  clear.
- Nothing is rendered until there is data — empty sections stay hidden.

Tasks #20 + #21 (small UX items bundled in)
- Default room "general" for new installations: the html input value
  attribute is now "general" and loadSettings() defaults match. Existing
  users' localStorage still wins.
- Random alias on desktop: already latent but confirmed working — the
  startup IIFE at main.ts:374 calls get_app_info() and prefills the
  alias input from derive_alias(seed) when the input is empty. No code
  change needed, just verified it flows through the same path as the
  Android client.

Known follow-ups (deferred to step 6 polish)
- Call duration tracking (currently all entries have no duration field)
- Hangup signal from an unanswered incoming should emit history-changed
  so the missed state is visible even when the user never tapped accept
- Android UI layout fit-check on the smaller Nothing screen
This commit is contained in:
Siavash Sameni
2026-04-10 11:03:36 +04:00
parent 76a4c53e21
commit 510eae2089
5 changed files with 525 additions and 7 deletions

View File

@@ -21,7 +21,7 @@
</button>
</label>
<label>Room
<input id="room" type="text" value="android" />
<input id="room" type="text" value="general" />
</label>
<label>Alias
<input id="alias" type="text" placeholder="your name" />
@@ -48,7 +48,10 @@
<div id="direct-mode" class="hidden">
<button id="register-btn" class="primary" style="background:#2196F3">Register on Relay</button>
<div id="direct-registered" class="hidden" style="margin-top:12px">
<p style="color:var(--green);font-size:13px">&#x2705; Registered — waiting for calls</p>
<div class="direct-registered-header">
<p style="color:var(--green);font-size:13px;margin:0">&#x2705; Registered — waiting for calls</p>
<button id="deregister-btn" class="secondary-btn small">Deregister</button>
</div>
<div id="incoming-call-panel" class="hidden" style="background:#1B5E20;padding:12px;border-radius:8px;margin:8px 0">
<p style="font-weight:bold;margin:0 0 4px 0">Incoming Call</p>
<p id="incoming-caller" style="font-size:12px;opacity:0.8;margin:0 0 8px 0">From: unknown</p>
@@ -57,6 +60,22 @@
<button id="reject-call-btn" style="flex:1;background:var(--red);color:white;border:none;padding:8px;border-radius:6px;cursor:pointer">Reject</button>
</div>
</div>
<!-- Recent contacts -->
<div id="recent-contacts-section" class="hidden">
<div class="history-header">Recent contacts</div>
<div id="recent-contacts-list" class="history-list"></div>
</div>
<!-- Call history -->
<div id="call-history-section" class="hidden">
<div class="history-header">
History
<button id="clear-history-btn" class="link-btn">clear</button>
</div>
<div id="call-history-list" class="history-list"></div>
</div>
<label style="margin-top:8px">Call by fingerprint
<input id="target-fp" type="text" placeholder="xxxx:xxxx:xxxx:..." />
</label>