aboutsummaryrefslogtreecommitdiffstats
path: root/src/features
Commit message (Collapse)AuthorAgeFilesLines
...
* Ark: the renewal notices and the Spending coins screen speak in sats, ↵Adam Soltys2026-09-211-8/+8
| | | | | | | whatever the display unit. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Point of sale: the sats sit under a fiat total, so a payer's wallet and the ↵Adam Soltys2026-09-211-1/+6
| | | | | | | counter agree by eye. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Point of sale: a sale the till never saw settle is adopted from the wallet's ↵Adam Soltys2026-09-211-0/+18
| | | | | | | own record. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Point of sale: a paid sale opens its payment detail, which now says bill and ↵Adam Soltys2026-09-212-1/+29
| | | | | | | | | | | | tip. The row finds the ln-receive movement by the invoice it recorded and opens the same detail screen the history page shows; the detail asks features for more (arkMoveDetailExtra) and the till adds Bills, Tips (with the percentage) and the note. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Point of sale: in a currency the pad works like a till — digits shift in ↵Adam Soltys2026-09-211-2/+12
| | | | | | | | | | from the right. 5 is 0.05, 277 is 2.77, 5588 is 55.88; backspace shifts out; the "." key becomes "00". Sats are whole numbers and type plainly. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Point of sale: /pos, a per-device till mode, an on-screen pad — and off ↵Adam Soltys2026-09-213-15/+42
| | | | | | | | | | | | | | | the Receive tab. v3.coinos.io/pos opens the point of sale once a wallet is up (claimed at boot before any feature reads the path), so the till can be bookmarked or put on a home screen. Settings → Payments gains "Start in point of sale on this device" (localStorage, not synced: the till, not every phone the owner carries). The amount and the custom tip get a 3×4 pad that edits the same field a keyboard or a paste would. The button under the payment address on Receive is gone. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Point of sale: the total on the tip screen reads at a glance.Adam Soltys2026-09-211-1/+1
| | | | | Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
* Point of sale: the tip screen shows an invoice error, and both screens say ↵Adam Soltys2026-09-211-3/+7
| | | | | | when the invoice is being made. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Point of sale: a bill, an optional tip prompt, one invoice, and a ledger ↵Adam Soltys2026-09-214-3/+279
| | | | | | | | | | | | | | | | | | that keeps tips apart. A new deferred feature (src/features/pos.js): type the amount in the display unit, optionally a note, Charge. With the tip prompt on, the customer sees the bill and picks a percentage (configurable, default 10/15/20), their own figure, or no tip; one Lightning invoice is made for the total through the Ark feature's receive (new hooks arkLnInvoice/arkLnWatch/arkLnUnwatch/ arkLnCancel wrap createLnInvoice and pollArkLn). Paid, expired and cancelled sales are recorded with the bill and the tip as separate figures, and the fiat rate at the time; the amount screen shows today's bills, tips and total. Doors: Settings → Payments card, and a button under the payment address on Receive (a receiveExtras hook the names pane renders). Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Profile: Messages opens the DM even when the profile sits over a thread.Adam Soltys2026-09-211-1/+5
| | | | Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Deep links: /note1… and /nevent1… open the thread; nostr:note intents ↵Adam Soltys2026-09-201-3/+17
| | | | | | | | | | route there; posts name the NIP-89 handler. A NIP-89 handler event (kind 31990, d=coinos) under the coinos key points njump and other pickers at /<npub> for people and /<note> for notes. The client tag on public posts now carries that coordinate. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Feeds: a following.space link is read before any naddr in the pasted text; ↵Adam Soltys2026-09-201-6/+4
| | | | | | the test clears its field between searches. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Feeds: packs are found by a few letters of their title, and by ↵Adam Soltys2026-09-201-13/+38
| | | | | | | | | | | | following.space's real link form. NIP-50 search only knew the packs the search relay carried — 'Niche Accounts' lives on damus and nos.lol, not relay.nostr.band. So the editor now indexes the wide relays' packs once per session and matches titles and descriptions by substring. A following.space link is /d/<id>?p=<pubkey>, not an naddr; both are accepted. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Feeds: follow packs from following.space (NIP-51 kind 39089) as a feed's people.Adam Soltys2026-09-201-7/+133
| | | | | | | | | | | A feed can name packs as well as people: the pack's current members are its authors, read from the pack event itself and cached on disk, refreshed hourly when a feed using it is opened — so a curator's change reaches the feed by itself. The editor takes a pasted following.space link or naddr, or searches packs by title (NIP-50 on relay.nostr.band). parseNostrRef learned naddr. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Messages: every saved feed is a row on the home list, under Following.Adam Soltys2026-09-201-1/+19
| | | | Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Feeds: Following is one of many; topics from search and #tags; the bolt is ↵Adam Soltys2026-09-201-53/+257
| | | | | | | | | | | | | | | | | | | | | | | | amber only for your own zap. Custom feeds live in the synced messages state — a name, everyone you follow and/or hand-picked people, and topics (hashtags, any of them; with people set as well, only their posts on those topics — the way a nostr filter composes authors and #t). Each feed keeps its own notes, clock and disk cache; a chips row under the feed title switches between them and + opens the editor (people are added through the same search the Send form uses). A word in the header search is a topic too: 'Posts on this topic' opens a session feed for it, as does a #tag in any post body — Save feed keeps it. Topic-only feeds read the big public relays as well as ours, since a hashtag has no author whose outbox we could follow. The zap icon used to go amber once anyone had zapped a post; now only when you have, like the heart. tools/feed-custom-test.js drives the editor, the relay filter, a tagged post landing, the #tag link, reload persistence and delete. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Chat: list sync stops rewriting a refused document every two seconds.Adam Soltys2026-09-201-2/+30
| | | | | | | | | | | | | | | | A relay refusing the community-list write (rate limit, ban) left the remote copy stale, and every resync asked for the same write again, each write echoing back on the live fragment subscription and triggering the next — five events every two seconds, forever. That filled relay.coinos.io's per-IP budget for the whole household, and the phone behind the same IP could no longer reach its remote signer: every NIP-46 request refused as rate-limited, every incoming DM stuck undecrypted (2026-09-20). An unchanged document is re-sent at most once per five minutes; a real change goes out at once. Our own echoes are ignored, and another device's write triggers a resync no more than once every ten seconds. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Notifications: a reaction, boost or zap opens its note with the who-panel ↵Adam Soltys2026-09-201-3/+8
| | | | | | unfolded. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Messages: the Notifications row's unread dot sits like a group row's.Adam Soltys2026-09-191-4/+4
| | | | Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Zaps: the sound can be switched off — from the held bolt or Settings → ↵Adam Soltys2026-09-192-5/+14
| | | | | | | | | | Nostr. A prefs flag (zapSound, absent = on) synced with the zap amount. The strike and the tally are unchanged; only the clip stays quiet, and the clip chunk is not even fetched while it's off. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Zaps: tap again while one flies, hold the bolt to change the amount.Adam Soltys2026-09-192-51/+138
| | | | | | | | | | | | | | | | A pending zap no longer refuses the next tap: the optimistic tally keeps one entry per zap and matches each report and receipt to the oldest it can describe, so two default-amount zaps stack, settle and hand over to their receipts one at a time. Instant post zaps run off the send screen's zap slot, so a second tap can't evict the one still resolving, and send action ids are unique within a millisecond. Holding ⚡ (500ms) opens the amount screen in an edit mode — Save alone, or Save and zap this note — from the action bar and the chat chips alike; the press's timer lives on the element so a background repaint mid-press can't turn a lifted finger into a hold. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Feed: live posts go straight in; the pill is a notice, not a gate.Adam Soltys2026-09-191-32/+30
| | | | | | | | | | | | | "Nothing new ever arrives" was four posts waiting behind the pill on the phone and two on the laptop. Now a post that arrives on its own goes into the feed at once, wherever you are: at the top it opens itself under your eyes; further down it goes in above the fold with the page held still (holdScroll), and the pill only says how many are up there. Its tap scrolls to the top; reaching the top by yourself clears it, since only the pill repaints and the keyed rows stay put. The small catch-up case gets the same notice. The feed-live test says so. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Feed: coming back settles once — a few new posts above you, a lot means ↵Adam Soltys2026-09-191-4/+73
| | | | | | | | | | | | | | | | | | | | | | | | the top. The catch-up on arriving at the feed asked every relay for its newest posts with no lower bound, and everything not already in the fifty-post cache went behind the pill, chunk by chunk: the count climbed in steps and stopped at the pending list's cap of 200, whether there were 12 new posts or 500. Now the pass is buffered and settled once, after every relay has answered. Posts older than the newest we had are gap fills and merge silently. Up to twenty genuinely new posts go in above what you're looking at with the page held still (holdScroll: the row at the top of the viewport stays put, measured after the paint so a browser that anchored on its own isn't corrected twice), so scrolling up reveals them in order, newest at the top. More than twenty means you've been away: the feed goes to the top, at the newest post, as on a fresh open. The window grows only by posts landing inside it, so a catch-up doesn't paint two hundred rows. The pill is now only for posts that arrive live while you read further down. The feed-live test covers the three arrivals, and its stale checks (auto-flush on scrolling up was removed on purpose; a fresh <img> reports complete a task late) now say what the app does. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Zaps: the sound, the sender, and one action bar for every post.Adam Soltys2026-09-183-102/+156
| | | | | | | | | | | | | | | | | | Tap feedback: a bolt strike, the amount popping off the button, the post trembling, and the zap.mp3 clip. The clip is 62KB of base64 and messages.js is boot code, so it is its own chunk, fetched once a zappable post is on screen and played inside the tap once loaded; the standalone build inlines it. Ark zap receipts are signed by the wallet key, which has no profile: they now carry the login identity as the sender (NIP-57's P tag), readers prefer it, and our own older receipts read as us. The action bar is the same six slots on every post: the zap button and the who-reacted chevron are always there (inert until they have something to do), so the first zap no longer reflows the row, and you can zap your own posts and messages. Community rows centre their unread dot off the edge. Also in here: reactions with custom emoji, reply-composer focus under the tapped post, profile fetch retry, and Lightning zap failures that say so. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Accounts from the header, snapshot-first Spending restore, name ownership ↵Adam Soltys2026-09-182-45/+88
| | | | | | | | | | | | | | | | grants. Header buttons always leave the current feature screen; an account with no remembered nostr identity opens Accounts instead of nothing. Logout confirms in place. A scan finishing after logout or an account switch no longer drops the new session into offline mode (stateGeneration guards in sync + ark). Spending restores from the snapshot first. A migration grant names the identity that owns an address; its manager may register it without becoming that identity (names/ownership.js). Browser tests for account access, profile retry, spending restore, thread reply focus, custom-emoji reactions and registry ownership. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Feed: the zap tally rides on the action-bar bolt, not the header.Adam Soltys2026-09-171-6/+21
| | | | | | | | | The sats total used to sit up by the timestamp as its own chip; now it is the count on the zap button, like likes and boosts have theirs. Amber once anyone has zapped, breathing while ours is in flight, and shown read-only (no zap handler) on posts you can't zap so the number still reads. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* A thread reply is a growing textarea: Enter sends, Shift+Enter breaks the line.Adam Soltys2026-09-171-5/+21
| | | | | Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* Pay from a profile opens the Send form, not a lingering payment detail.Adam Soltys2026-09-162-3/+3
| | | | | | | | | | | A tapped history row claims the whole tab area whichever tab is picked, so jumping to ui.tab = 'send' from a profile's Pay button (or a zap's form fallback) with a renewal's detail still open showed that detail instead of the form. showSend() closes any open detail first; the three feature paths that jump to Send use it. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* A note that wasn't found is asked for again, with the reactor's relay hint.Adam Soltys2026-09-161-13/+22
| | | | | | | | | | | | A reaction's target was fetched once from our relays, and a miss was final for the session — so with our own relay down for an evening, a reply that sat on four relays showed as "no longer on your relays" until a reload. A missing note is now retried when next wanted (half a minute on), the reaction's e-tag relay hint rides along, and tapping the row asks again instead of doing nothing. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* Feed: what arrives waits behind the pill; the count bumps; no auto-flush.Adam Soltys2026-09-161-13/+26
| | | | | | | | | | | | | | Posts a refresh found slid in a beat after the cached page painted, moving what you had begun reading, and reaching the top of the page let waiting posts in with a re-render under a finger on its way to the pill — the tap landed on the post beneath. Now everything that arrives after the first paint, the boot catch-up included, waits behind the pill wherever you are; the pill is a keyed node whose count bumps on each change; and a tap scrolls to the top before painting so the new posts open under your eyes. A rebuilt follow list still rebuilds the feed in place. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* A bare @npub in a message resolves to the person's name.Adam Soltys2026-09-151-4/+8
| | | | | | | | | | Vector writes a mention as @npub1… with no nostr: prefix; ours only resolved the prefixed form, so the raw key sat in the bubble. Bare npubs and nprofiles, with or without the @, now render as @name and open the profile, like the prefixed ones. Keys inside URLs are left alone. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* A changed reaction stays changed: the fold keeps the newest, not the last to ↵Adam Soltys2026-09-151-3/+20
| | | | | | | | | | | | | arrive. One reaction per author was folded as "last one in wins". A reload's backfill hands reactions back in relay order, so a flame swapped for confetti came back as the flame. Rooms, DMs and the public page now keep the newest by the rumor's own clock, and an optimistic send stamps its time so an older one arriving afterwards stays out. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* Community reactions carry the target author's p tag, as Concord specifies.Adam Soltys2026-09-151-1/+4
| | | | | | | | | A reaction sent from v3 never showed in Vector: its parser requires the NIP-25 p tag naming the reacted-to message's author (CORD examples §2.3 has e, p and k), and ours sent only e and k. Added. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* Gift detail links the coin behind the gift, and the claim once claimed.Adam Soltys2026-09-151-2/+17
| | | | | | | | | | | An unclaimed on-chain gift has no transaction of its own yet — its funding transaction is presigned and only broadcast when someone claims. The coin it's locked against is on-chain from the start, so the detail page now links that outpoint's transaction on the block explorer, and the claim transaction once it exists. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* Coins page: renewal is automatic, and the copy says so first.Adam Soltys2026-09-151-4/+6
| | | | | | | | | | | | | | The renewal card opened with how to renew and mentioned only at the end that the wallet does it by itself, which read as an invitation to renew by hand. It now leads with the automatic, free renewal before expiry, then says renewing early is only for someone who would rather not count on the coinos server, with the round cadence taken from the server's own info (two minutes now, not the hour the old text claimed). The "past its expiry" footnote under the table is gone. Stale translations of the rewritten strings fall back to English. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* Cached faces warm from the wallet's own namespace, once its keys are in.Adam Soltys2026-09-151-0/+11
| | | | | | | | | | | | | On the phone the profile cache was intact — 150 faces, our own with a thumbnail — yet an offline boot drew a punk in the header. The header asks for a face before a silent sign-in has finished resuming, and the warm-up ran then: it read an empty slot under a keyless namespace and counted itself done. Online the relays refilled the map and nobody saw it; offline nothing did. The warm-up now waits for the wallet's keys and runs again at feature init, from the right namespace. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* Offline, the chat shows what it read: messages cache on arrival.Adam Soltys2026-09-151-2/+16
| | | | | | | | | | | | | | | | The room cache was written only on our own sends, deletes and attachments, so a member who only reads had nothing on an offline boot — "No messages yet" over a room read the day before, and no faces or names with it. Messages now persist as they arrive (debounced past the backfill burst), so the last fifty per channel are there without a network. Our own face never waits its turn for a thumbnail and never gives it up to the budget, so the header avatar paints offline too. tools/offline-cache-test.js boots the PWA build, caches a room, reloads fully offline and checks messages, names and thumbnails are all there. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* Spending coins as a headed table instead of chip rows.Adam Soltys2026-09-151-30/+29
| | | | | | | | | | | Every row repeated "expiry", "exit cost", "renewal cost" as pills. The labels now sit once in a header row over right-aligned tabular figures; the table's wrapper scrolls sideways on a screen too narrow for it (the page never does), and it fits a 390px phone without scrolling. A tap anywhere on a row toggles it. A sub-satoshi renewal share reads "< 1 sat". Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* A tiny coin's renewal chip says "under 1 sat", not "1 sat".Adam Soltys2026-09-151-2/+10
| | | | | | | | | | | | | The server prices a renewal on all the coins together: ppm accumulates sub-satoshi across every input and rounds up once. The per-coin chip rounded each coin's share up on its own, so nine 1-sat coins each showed "renewal cost 1 sat" under a total of 4 sats — as if the page were skipping them. The chip now shows the coin's real share: "under 1 sat" for a sub-satoshi one, rounded otherwise, and its tooltip says how the fee is priced. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* Chat keeps its relay subscriptions alive while the app is on screen.Adam Soltys2026-09-151-16/+75
| | | | | | | | | | | | | | | | | | | | | New messages rang in Vector but sat unseen in v3 until a reload. The resume hook only rebuilds subscriptions after an absence it was told about; a socket that errors while the page stays visible (a wifi hop, a laptop lid, a phone freezing the tab without the away timer noticing, another tab clearing that timer first) is dropped from the nostr-tools pool with every subscription it carried, and nothing dials it again. A watchdog now runs whenever the app is on screen: on focus, on becoming visible, and every ten seconds between. It asks the pool whether each relay the inbox and the rooms listen on still holds a live socket (a dial hung past 20s counts as dead, and a relay that closed a subscription on us counts too) and rebuilds the lot when one is missing, dropping a stuck relay first so the rebuild dials afresh. Rebuilds retry on a growing backoff while a relay keeps refusing. tools/chat-watchdog-test.js errors every socket under an open room and checks the recovery. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* A pack link in chat adds the pack in place; coinos has its own share link.Adam Soltys2026-09-151-14/+39
| | | | | | | | | | | | | | | | | | Tapping "Emoji pack" on a phone opened Vector: the chip was an outbound link to vectorapp.io, a domain the Vector app owns. The chip is now a button — one tap adds the pack right here, nothing leaves coinos. Packs also get a coinos share link, v3.coinos.io/emojis/pack/<naddr> (copy it from the picker's pack row). The Android app already claims every v3.coinos.io path, so on a phone that link opens coinos, which adds the pack once the wallet is open. Pack links from any host render as the chip; the naddr's kind is what's checked. Pack changes now save at once (learned shortcodes stay coalesced) — a remove followed straight away by a navigation was lost to the debounce. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* A reply's quoted line shows its custom emoji as pictures.Adam Soltys2026-09-151-6/+30
| | | | | | | | | | The quote above a reply bubble, the composer's reply bar, and the same on the public /chat page rendered the raw :shortcode:. They now run the one-line snippet through the same NIP-30 tokenizer, cutting on text so a shortcode is never sliced in half. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* Custom emoji in chat: :shortcodes: render as pictures, packs sync with Vector.Adam Soltys2026-09-151-26/+353
| | | | | | | | | | | | | | | | | | | | | | | A message from Vector says :pika_wave: and carries a NIP-30 ["emoji", code, url] tag for it; coinos showed the bare shortcode. Now the bubble shows the picture (in rooms, DMs, the feed and the public /chat page), a bubble that is only one to three of them shows them big, and a :code: reaction chip is a picture too. Sending: every :code: you type that resolves travels with its tag, so the other side renders it whether or not they hold the pack. Shortcodes come from packs you hold (NIP-51 kind 30030 sets) and from what you've seen in chat, so anyone can answer a :pika_wave: with one. The composer autocompletes on ":pi" (Tab/Enter takes the first), the reaction picker lists custom emoji first, and Vector's pack share link renders with an Add button; a pasted naddr works too. Packs live on your kind 10030 list — the one Vector reads and writes — so a pack added in either app appears in both. tools/emoji-test.js drives the real coinos room headlessly and checks all of the above. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
* Opening a reply scrolls the thread to that reply.Adam Soltys2026-09-151-1/+18
| | | | | | | | | | A thread opened from a reply highlighted the reply but never scrolled to it, so in a long thread it sat below the fold and the page looked like it had missed. The thread now scrolls the focused note into view after its paint — and if the replies are still loading, after the render that brings them. Opening the root itself means the top. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* An @handle in a bio or post opens the people search for that name.Adam Soltys2026-09-151-1/+21
| | | | | | | | A bare @name carries no key, so nothing can resolve it for certain — but it can be a tap that opens the search with the name filled in. The @ inside an email or nip05 is left alone, as is anything inside a URL. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* A gift locked from Savings shows under Savings, not Spending.Adam Soltys2026-09-142-2/+3
| | | | | | | | | Every feature balance line landed on the Spending face, but a gift's locked reservation is on-chain money still sitting in Savings. Lines now say which side they belong to and draw on that card: gifts on Savings beside pending incoming, ark's Moving… on Spending as before. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Join suggests communities from the Vector Hub, searchable, beside the paste box.Adam Soltys2026-09-141-7/+60
| | | | | | | | | | | The Join panel used to be one empty box asking for a link nobody had. It now lists the Vector Hub's public communities under it — icon, name, member count, category, description, sorted by size, marked when you're already in one — fetched on demand and kept for the session. The same box takes a link or filters the list; a tapped suggestion previews exactly like a pasted invite, since every listing is a CORD-05 link. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Invite links open in Vector, communities sync across Concord clients, and ↵Adam Soltys2026-09-141-35/+213
| | | | | | | | | | | | | | | | | | | | | | | | | | | any emoji can be a reaction. Vector's bundle parser has no default for a channel's key and epoch, so a coinos invite — which sent only id and name — failed its whole JSON parse and the link did nothing there. Every channel entry now carries key and epoch (a public channel's key is the community_root at the base epoch, CORD-05 §1), and bundles minted before this are re-signed at the same coordinate once per device, so links already shared start working. Concord clients now keep their Community List as kind 33302 fragments (CORD-02 §8): addressable per index, NIP-44 to self, 32-byte values as unpadded base64url, embedded join material without its community_id, seed omitted when it equals current. coinos read and wrote only the retired single-event 13302 on its own two relays, so a community joined in Vector under the same key never appeared here. The fragmented list is now read from our relays, the stock CORD set and every held community's relays, merged, and written back in the same shape; the home view re-syncs after a minute and a live subscription brings a fresh write straight in. 13302 stays for older coinos devices. The reaction row keeps its quick emojis, recents first, and a + opens a searchable picker of a few hundred more — eyes included. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* A picture can be sent to a channel or a DM, encrypted, and read by Vector.Adam Soltys2026-09-141-7/+154
| | | | | | | | | | | | | | | The composer grows a paper clip. A picked file is encrypted under a fresh AES-GCM key and 16-byte nonce, the ciphertext uploaded to a public Blossom host that takes anonymous blobs (blossom.ditto.pub, then nostr.download as a fallback copy), and the pointer posted the way Vector posts it: a kind-9 with empty content and a NIP-92 imeta tag carrying url, mime, key, nonce, both hashes, size and dimensions. In a DM it is a NIP-17 kind-15 file message with the same fields as flat tags and the url as content, wrapped to the peer and to ourselves. Kind-15 file messages are now accepted on receive too, rendered like any attachment, and previewed as a photo in the thread list. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* A picture sent to a channel shows up as a picture, not an empty bubble.Adam Soltys2026-09-141-1/+86
| | | | | | | | | | | | | Vector attaches files as NIP-92 imeta tags: the blob is encrypted with a one-off AES-GCM key before it reaches Blossom, and the tag carries the url, mime, key, nonce, plaintext sha256 and dimensions. Such a message has no text, so the bubble rendered blank. The blob is now fetched (fallback hosts too), decrypted in the browser, checked against the hash, and shown from an object URL — a grey box its shape while it loads, tap to view full size, other file types as a download by name. The same tags render in DMs. Co-Authored-By: Claude Fable 5.1 <[email protected]>