aboutsummaryrefslogtreecommitdiffstats
path: root/src/features/messages.js
Commit message (Collapse)AuthorAgeFilesLines
* Public kind-1 publishes (profile posts and thread replies) carry a NIP-89 ↵Adam Soltys2026-09-041-1/+8
| | | | | | | client tag — ['client', 'coinos'], name-only since no kind-31990 handler coordinate is published yet — so other clients show "via coinos". Deliberately absent from everything encrypted (DMs, community wraps): private traffic needs no billboard Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
* Hats no longer pop in a beat after page load. The hats feature is a deferred ↵Adam Soltys2026-09-041-1/+17
| | | | | | | chunk, so every boot painted bare heads until it arrived — but the core bundle can't carry the hat art. Instead, wrapAvatar now leaves a STAMP per head in localStorage (art + percent geometry, size-independent, capped at 40, cleared when a head goes bare so removals can't ghost), and the core avatar factory replays stamps until hatsReady answers — from then on a null from wrapAvatar genuinely means bare. Verified headless: a stamped hat is on the header avatar 15ms after DOM-ready, and once the chunk lands with a bare-head answer the stamp is cleared with no double-wrap Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
* Three profile/thread refinements from field use. (1) The thread reply box ↵Adam Soltys2026-09-041-62/+114
| | | | | | | sits INLINE, directly under the note it answers, instead of at the bottom of the thread where nobody scrolled to find it — slotted after the focused note (the root by default), appended at the end only until the focused note loads, with a refocus handshake so the cursor survives the box relocating when the thread finishes loading. (2) Posting is OPTIMISTIC: a provisional dimmed copy tops the feed before any signing or relay round-trip (a remote signer alone can take seconds), swapped for the signed event on success and withdrawn on failure — the composer closes immediately and reopens with the text intact if publishing fails; pending rows can't be opened/replied/zapped until real. (3) The full header nav (search, chat, settings, lock, avatar) now shows on the profile, thread, and search screens whenever a wallet is open — losing the search button on a profile made finding the next person a trek back home; the public no-wallet surface keeps the plain brand. All verified headless: box renders mid-card under the focused note with focus landing after load, a post appears 250ms after tapping Post (pre-publish) and survives settle, and the search button works from a profile Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
* Every nostr post row carries a ↩ Reply button beside the ⚡ zap: it opens ↵Adam Soltys2026-09-041-1/+14
| | | | | | | the post's thread focused on that note and puts the cursor straight in the reply box (inside an already-open thread it just re-targets the reply to that message). Verified headless on /adam: 51 rows all carry the button, tapping one lands with the thread reply input focused Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
* Profile notes become a real feed: INFINITE SCROLL (nearing the page bottom ↵Adam Soltys2026-09-041-6/+53
| | | | | | | pulls the next page of older kind-1s — until = oldest seen, one fetch in flight, a short answer marks the end; the listener registers at feature construction so the public no-wallet /username pages scroll too), VIDEO rendering in noteBody (mp4/webm/mov/m4v as <video controls preload=metadata playsinline> beside the existing inline images), and FULL-BLEED on phones — the notes card drops its walls and runs edge to edge under 520px; the inner 440px scrollbox is gone everywhere, the page itself scrolls. Verified headless at phone width on /adam: feed spans 0px-to-edge both sides, images render inline, and scrolling grew the list 51 → 82 posts Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
* The tap-a-bubble action sheet works in DMs too: reactions + Reply + Copy (no ↵Adam Soltys2026-09-041-6/+126
| | | | | | | Delete — a gift-wrapped DM can't be retracted from the peer's relays). DM reactions ride the kind-7-in-a-gift-wrap shape 0xchat uses (makeDMReaction in dm.js), sent to the peer and echoed to our own inbox so other devices fold them in; inbound kind-7 rumors land in dmReacts keyed by the reacted message's id — no thread lookup, so a reaction arriving before its message still sticks. Replies e-tag the quoted kind-14 (makeDMRumor grew extraTags) and render the same accent-bar quote as community chat; the composer gets the same replying-to bar. Chips are joinable and your own reaction is highlighted/replaceable, all optimistic with rollback. Round-trip verified: reply and reaction rumors wrap/unwrap with tags intact Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
* An uploaded (or pasted) profile picture or cover previews at the top of the ↵Adam Soltys2026-09-041-6/+17
| | | | | | | profile page immediately, not after Save: while the editor is open the DRAFT is the truth for the banner, the avatar, and the display name shown up top — clearing a field previews the removal too. The editor's text fields render per keystroke so pasted URLs track live (the morph never rewrites a focused field, so it can't fight the typing); uploads already rendered after filling the field. Verified headless: typing picture/cover URLs into the edit form updates the page-top avatar and banner backgrounds before any save Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
* Your own profile page opens as a PROFILE now, not a form: npub (tap to copy ↵Adam Soltys2026-09-041-13/+58
| | | | | | | — it was hidden on your own page), bio, and latest posts, exactly like anyone else's, with the editor behind an explicit Edit profile button (Save or the new Cancel returns to the view) and a New post button that unfolds a composer — publishPost signs a plain kind-1 with the login/wallet identity, publishes to the identity's own relays, and prepends it to the notes list so it shows immediately. The header avatar's tooltip says "Your profile" instead of "Edit profile" to match. Compose/edit state clears on open/back so drafts never leak across profiles. Verified headless end-to-end: onboard → avatar → view mode (buttons + npub, no form) → Edit opens form → Cancel → New post opens composer Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
* Telegram-style message action sheet + replies in community chat. Tapping any ↵Adam Soltys2026-09-041-14/+92
| | | | | | | bubble opens a centered sheet: a quick row of the six reaction emojis (your current one highlighted; picking fires immediately) above Reply / Copy / Delete (delete on own messages only) — replacing the hover-only 🙂 picker, which was invisible on the phones that need it most. Links, images and the hover × keep their own clicks, and a desktop text-selection drag never pops the sheet over the copy. REPLIES are new: Reply arms a composer bar showing who/what you're answering (× to cancel, cleared on channel/room switch), the sent kind-9 e-tags the quoted message, and rows carrying an e-tag render the quoted context above their text — author + one-line snippet, silent when the original is deleted or not held. The public /chat page renders the same quotes. Verified against the built stylesheet: 300px sheet, six even emoji buttons, quote ellipsizes, circular cancel button Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
* Chat renders links and sends emoji reactions. Links: community bubbles and ↵Adam Soltys2026-09-041-3/+51
| | | | | | | DM bubbles now run through noteBody (URLs → anchors, image URLs inline, npub mentions clickable, all content kept as text nodes — nothing from a relay reaches innerHTML); the public /chat page gets its own linkedBody (same splitter, nostr refs stay dim stubs since there are no profile screens there). Reactions: a 🙂 opener on every bubble (rides the edge like the delete ×, steps left on own rows) unfolds a six-emoji picker; tapping an existing chip joins that vote, your own reaction is highlighted, and picking a different emoji replaces it (kind-7 rumor on the channel stream, one per author, fold keeps the latest — inbound rendering already existed, this adds the sending half). Optimistic like sendMessage: the chip lands instantly and is rolled back to whatever stood before if signing/wrapping fails Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
* Public read-only coinos community chat at /chat — a share-anywhere link ↵Adam Soltys2026-09-041-15/+7
| | | | | | | that shows the live chat history to anyone, logged in or not, with a "Sign in to chat" button ("Open coinos" when the device already has an account). No server-side decoder needed: the community's join material was already public by construction (baked into the client bundle — every coinos user is meant to be a member), so the page derives the stream keys and decrypts wraps client-side exactly like the app's Chat tab, worker-offloaded with the same sliced fallback. Renders messages + edits + deletes + reactions and honors the folded control state's ban list; presence/typing/guestbook deliberately omitted. Profiles come from ONE batched kind-0 query per burst (a REQ per author tripped relay concurrency limits). Plumbing: the join material moved to src/community.js (shared with messages.js so they can never drift), /chat is excluded from the single-segment profile deep-link (which was eating the path and rewriting the URL before the route ran), and app renders stand down in public-chat mode so a feature-init render can't paint onboarding over the page. Verified headless against the live relays: 59 messages decrypt, ban-fold applies, names resolve Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
* Move nip44 decryption off the main thread — the remaining boot secp256k1 ↵Adam Soltys2026-09-041-9/+39
| | | | | | | after verification moved off (chunk-60dfb4nt in profiles). The verify worker grows two ops: 'openWraps' opens community (Concord) wraps given the symmetric stream conversation key — nip44 decrypt + the seal's schnorr verify + hash checks per wrap, no secret crossing the thread boundary since stream keys are group material; 'unwrapDMs' runs the full NIP-17 unwrap (two ECDH ops + seal verify) when the wallet holds the raw identity key. Remote signers can't export their key, so their DM path stays on the main thread, exactly as before. The time-sliced main-thread queues survive as fallbacks — a dead or stalled worker only changes WHERE the crypto runs (workerCrypto resolves null and callers re-enqueue), never whether chat catches up. Bonus: subChannel derives its stream key once per channel instead of once per wrap (groupKey does hkdf + ECDH per call — real curve work on a 200-wrap backfill) Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
* Communities no longer decrypt on the home screen: init built and subscribed ↵Adam Soltys2026-09-031-17/+37
| | | | | | | every community room up front (500 control + 500 guestbook + channels each — the bulk of the ~560ms of boot secp256k1 behind the carousel stutter). Now boot builds rooms from the decrypted local cache ONLY (subscribe:false), so the unread dot still reads last-known state, and the live backfill's verify/decrypt waits until the user actually opens Chat (homeView/room view subscribe then). ensureRoom split into cache-build + subscribeRoom Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The post-refresh carousel stutter, root-caused and fixed: a CPU profile of ↵Adam Soltys2026-09-031-11/+41
| | | | | | | the exact cold first-drag window showed ~700ms of secp256k1, and it was the COMMUNITY room's gift-wrap decryption — a relay backlog of 500+ wraps decrypted inline in the subscription callbacks, one synchronous EC burst on boot (the DM inbox already yielded; community rooms didn't). Now their decrypt work is time-sliced through the same yield-every-8ms queue, so it spreads over a few frames and the wallet stays responsive. Measured: first cold flick dropped 6 frames before, and this was the dominant cost Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The DM backfill yields to the main thread: decrypting a gift-wrap backlog ↵Adam Soltys2026-09-031-0/+10
| | | | | | | (NIP-44 ECDH, ~500ms of secp256k1 in a boot profile — the last of the carousel jank) ran as one uninterrupted loop. Each synchronous run is now capped to ~8ms, one frame's budget, then yields so input and paint get a turn; the backfill just spreads over a few extra frames instead of freezing them Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* A cold boot can no longer dress a real account in a punk for a day: an empty ↵Adam Soltys2026-09-031-2/+17
| | | | | | | profile answer (the 5s query racing still-dialing relays) is retried in minutes instead of being trusted for the 24h TTL, faceless rows are never persisted (they were evicting real faces from the 150-cap), and the logged-in identity's own profile is pinned against eviction entirely Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* An offboard's price stops hiding in one number: the miners' cut and the ↵Adam Soltys2026-09-021-3/+13
| | | | | | | ASP's service charge (base + lifetime ppm) record separately and the history detail shows them on their own lines — settling the mystery of a ~350-sat chain fee inside a ~650-sat total. The community row's member count anchors to last session's settled figure instead of ticking upward through the guestbook replay, and a profile's Pay button reached from search actually reaches Send — the lingering search screen was winning the router Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* History becomes home: the tab bar slims to Receive | Send (tapping the lit ↵Adam Soltys2026-09-021-2/+14
| | | | | | | one puts it away), every landing — login, account creation, an account snapped into the carousel — is the account's own timeline, and that timeline is now SCOPED: Savings shows the on-chain ledger and gift reservations, Spending shows the ark side. The carousel meanwhile learns mouse and mind-reading: desktop gets a real grab-and-pull drag (snap lifted mid-drag, restored on the glide), the peeking neighbor switches on a single click, and the strip wears grab cursors. And a thread's avatars and author names finally go somewhere: profiles stack on top of the conversation and back returns to it Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* nevent and nprofile references come alive: nprofile mentions render as ↵Adam Soltys2026-09-021-5/+24
| | | | | | | clickable @names beside their npub siblings, and note/nevent references become 'view note' links that fetch the event by id (relay hints first) and open its thread right in the app — the dim truncated stubs survive only for what genuinely can't resolve Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* A raw npub in a bio stops punching through the profile card: the about text ↵Adam Soltys2026-09-021-1/+8
| | | | | | | renders through noteBody like any note — npub mentions become clickable @names, other nostr: refs truncate to stubs, URLs linkify — with overflow-wrap catching whatever unbreakable string remains Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* Recipient search warms its pipes on focus, not first keystroke: the Primal ↵Adam Soltys2026-09-021-2/+3
| | | | | | | cache socket, the registrar's TLS session, and the NIP-50 relay connections each cost a handshake that used to land on the first query — the cold second of dead air. Touching the send recipient field, opening user search, or unfolding New message now pre-opens all three (throttled; the idle reaper still closes the socket a minute after real use) Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* Three render hogs put on a leash for the sluggish-tabs hunt: qrSvg is ↵Adam Soltys2026-09-021-2/+20
| | | | | | | memoized (make() runs eight mask trials over a version-20 grid — 30ms+ per call on a phone, paid on EVERY render of the receive pane), the header's unread count reuses its answer behind a revision counter bumped only when messages or read-marks actually change, and ark's history list rebuilds at most every 1.5s instead of per repaint Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* Cold boots stop lying for a few seconds: an empty profile answer (relays ↵Adam Soltys2026-08-311-2/+10
| | | | | | | still dialing) no longer clobbers a remembered avatar with a punk — stale fields hold while the refresh does its rounds — and the ark merge marks coins spent the moment a synced movement or done action names them as consumed inputs, instead of double-counting a sleeping device's view (output merged in, inputs still 'spendable') until the reconcile caught up and the balance visibly ticked Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The migrate link stands under the profile editor's username field too — ↵Adam Soltys2026-08-311-0/+8
| | | | | | | right where a coinos.io veteran is looking at the name they want to bring over Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* A migrated coinos.io account keeps its face and its cover: the claim fetches ↵Adam Soltys2026-08-301-1/+55
| | | | | | | the legacy avatar and banner and fills the kind 0's empty slots (fill, never overwrite — an existing nostr identity keeps its own look), the wizard's punk picker steps aside for a profile that already has a picture, and v3 finally wears cover photos — nostr's standard kind-0 banner renders full-bleed across the top of every profile card with the avatar overlapping it, settable by URL or upload in the profile editor Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The header dot stops arriving fashionably late on a brand-new account: the ↵Adam Soltys2026-08-301-4/+9
| | | | | | | first-frame optimism keyed on 'no rooms yet', but init() creates the coinos room before the first paint, so the guard never fired and the dot waited for real messages to decrypt — it now keys on no messages having landed, which is what it meant all along Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The migration round trip stops eating its own homework: the ?migrated= ↵Adam Soltys2026-08-301-7/+9
| | | | | | | return is stashed synchronously at boot and claimed whenever the wallet actually opens (the old 1.5s one-shot silently dropped it), the wizard's 'I'm done, continue' state survives the trip to coinos.io as a persisted step, the legacy page shows the claim in progress instead of re-asking, and a finished claim advances the wizard by itself. Fresh-seed polish riding along: the spending-setup ask stops waiting out registrar round trips that only exist for restored wallets, and the header punk paints synchronously instead of after an async identity fetch Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The DM drain wakes the signer itself: boot's single resume attempt losing to ↵Adam Soltys2026-08-301-3/+10
| | | | | | | cold-start churn left the phone holding every wrap with nothing ever asking the bunker again — now a drain that finds its decryptor set incomplete calls resume (single-flight, rate-limited) before deciding anything, and the bunker resume deadline stretches to 15s Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* A signer dying mid-drain stops burning retry budget: the pass breaks off and ↵Adam Soltys2026-08-301-0/+4
| | | | | | | leaves the untried wraps pending for the next full-strength attempt, instead of counting them as failures against a decryptor set that was no longer there Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* The Amethyst signer stops dying every time a stranger's wrap fails to ↵Adam Soltys2026-08-301-1/+4
| | | | | | | decrypt: only connection-shaped failures (with a real per-op deadline now — a backgrounded signer app used to just hang the call) drop the live bunker, the reconnect surfaces offer only the door the account actually came through (a recorded 'via', so an Amethyst-linked npub no longer stares at Google and passkey buttons), the settings card quietly reattaches from the stored session instead of waiting for a tap, and the DM retry drain eases to three full-strength passes per wrap Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* DMs stop losing the race with the signer, and your own address stops paying ↵Adam Soltys2026-08-301-8/+83
| | | | | | | yourself: undecrypted wraps are kept and retried when a signer comes live (the backfill used to be consumed and dropped whenever the login signer hadn't resumed yet — each device kept a different random subset of history), the inbox also reads the relays our kind-10050 actually advertises, and pasting your own ark address or coinos name into Send now opens the board panel from Savings instead of a silent Spending→Spending self-send; a foreign ark address while Savings is selected says to switch instead of quietly spending the other balance Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
* Read is read everywhere: the messages home gains a Mark-all-read link (every ↵Adam Soltys2026-08-291-1/+53
| | | | | | | DM and channel watermark jumps to its newest foreign message), and read state now rides its own sync domain — watermarks only ever grow, so a per-key max merges cleanly across devices and a thread read on the phone stops glowing on the desk Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019WboeoetMF3MTmgT7Ew8yi
* The composer learns the second dimension: a textarea that grows with the ↵Adam Soltys2026-08-291-4/+22
| | | | | | | text (capped at five lines), Enter still sends, and a newline is one Shift+Enter or Ctrl+J away — bubbles already knew how to show them Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019WboeoetMF3MTmgT7Ew8yi
* A half-typed message waits where you left it: drafts live per conversation ↵Adam Soltys2026-08-291-12/+34
| | | | | | | (each DM and each channel its own), survive navigation and reloads through feature state on a typing debounce, and clear only when the message actually sends Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019WboeoetMF3MTmgT7Ew8yi
* A round's output can no longer strand: every refresh asks the server to drop ↵Adam Soltys2026-08-291-5/+27
| | | | | | | its unlock hash in the mailbox, and any device that finds such a breadcrumb with no matching action adopts the claim itself (input ids now read from the status response — the rescue needs no operator). History learns to tell the whole truth and nothing more: coins the server saw spent elsewhere get a folded row (skipping coins a known renewal consumed), renewals fold across devices on their unlock hash and appear only when they cost something, and a rescue records the fee the round actually took. The account page keeps its header icons when reached from an open wallet; the messages page folds the control/guestbook replay once per burst instead of painting every intermediate state — no more blinking messages from superseded ban lists or member counts crawling to their total, which now paints from last session's settled figure Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019WboeoetMF3MTmgT7Ew8yi
* The profile editor learns to take a photo, not just a URL: an Upload button ↵Adam Soltys2026-08-281-0/+18
| | | | | | | feeds the same signed nostr.build path the onboarding picker uses and fills the picture field with the result Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
* Forget-all keeps its word: the delete-everything warning lives inside the ↵Adam Soltys2026-08-281-13/+27
| | | | | | | logout popup now, and confirming it wipes on the spot and lands on the front door — no detour through the Accounts page that looked like the deed was done when only a confirm screen had opened Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
* The red dot stops being fashionably late: a device with no read watermarks ↵Adam Soltys2026-08-281-1/+10
| | | | | | | paints it from the first frame — the community is never empty and read state is local, so the fetch was only ever going to confirm it Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
* The face arrives with the key: sign-in warms the identity's profile and ↵Adam Soltys2026-08-271-0/+11
| | | | | | | preloads the avatar bytes in parallel with the seed work, so the home screen's first paint already wears the picture instead of a blank circle Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
* The address stops waiting for the mail: your own claimed name prints ↵Adam Soltys2026-08-261-5/+14
| | | | | | | instantly, a deep link's URL already spells the address so it paints with the first frame, and the light cache now remembers nip05 and lightning address between sessions instead of shedding them at the door Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
* The hat pitch moves next door to the head it would sit on: a small "Support ↵Adam Soltys2026-08-261-2/+3
| | | | | | | Coinos — buy a hat for your avatar" link beside the profile avatar replaces the block button buried in the editor Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
* No borrowed faces: the seeded profile entry is marked provisional, so the ↵Adam Soltys2026-08-261-5/+8
| | | | | | | avatar holds its quiet circle until the real kind 0 answers — punk art means "has no picture", never "picture not here yet" Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
* The landing settles as one motion: the resolving shell now holds the frame ↵Adam Soltys2026-08-261-9/+16
| | | | | | | over every screen — an auto-restored wallet no longer flashes its home page — and the animation clock stands still until the profile has replaced the shell in place, so nothing slides twice Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
* The door opens on the right room: a profile link claims the very first paint ↵Adam Soltys2026-08-261-1/+26
| | | | | | | — a shell with the asked-for name holds the frame while the registrar answers, the username seeds the avatar's fallback art so no npub prefix or empty circle shows, and an unknown name falls quietly through to the front door Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
* The profile page steps out front: /username resolves at page load — not ↵Adam Soltys2026-08-261-25/+42
| | | | | | | wallet open — and for a visitor with no account it renders as a truly public page over the front door, threads included, with wallet-only actions held back and Back leading into the app Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
* A doormat note is read once: the profile deep link is consumed by the first ↵Adam Soltys2026-08-261-4/+9
| | | | | | | wallet to see it, so logging out and starting fresh in the same tab lands on the new home screen, not the last visitor's profile Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
* The old address book habit carries over: v3.coinos.io/<username> opens that ↵Adam Soltys2026-08-261-0/+29
| | | | | | | user's profile — the server already hands every stray path to the app, which now reads a lone name (or npub, or bare key) off the doormat, asks the registrar who lives there, and shows you in Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
* Every face is a door: note-row avatars open the author's profile again — ↵Adam Soltys2026-08-241-1/+3
| | | | | | | the thread view had them inert, and even in the feed a tap on the picture now means the person, not the post Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
* Delivery moves at the speed of the best relay, not the worst: publish ↵Adam Soltys2026-08-181-13/+17
| | | | | | | resolves on the first acceptance, DMs stop idling on the peer's inbox lookup, and the dimming departs — a sent message simply appears Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_012u4oe1KPx9faCsDBLfZYqi
* The tick bows out: delivery speaks through the dimming alone — faint in ↵Adam Soltys2026-08-181-7/+5
| | | | | | | flight, full strength once a relay has it Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_012u4oe1KPx9faCsDBLfZYqi