aboutsummaryrefslogtreecommitdiffstats
path: root/src
Commit message (Collapse)AuthorAgeFilesLines
* Spending settings: ask for the chain tip right after init, so expiries and ↵Adam Soltys16 hours1-0/+8
| | | | | | exit costs are judged at once and the next refresh starts from the last tip seen Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Spending settings paint their real numbers at once after a refresh: the ↵Adam Soltys16 hours1-10/+42
| | | | | | | | | | manager is readable the moment its own init is done, and the server's fee table and tip are cached per server Why: the coins were on screen at 350 ms (from the store), but the renewal price read "free", expiries "—" and exit costs the fallback for eleven seconds, because the pages read everything from `ark`, which is only set once the Nostr snapshot restore has also finished (Promise.all with mgr.init) — a gate that exists so the mailbox isn't replayed before the snapshot's cursor arrives, not for display. GetArkInfo itself answers in 0.4 s. `arkInited` is the same manager as soon as mgr.init() resolves (info, server key, tip, persisted coins); arkView() = ark || arkInited is what the custody card and the coins page read from. Renew (the one action the page runs on the manager directly) still waits for `ark`; exits go through connectArk, which waits anyway. For the first half second before init, btc-wallet-arkinfo:<server> in localStorage carries the last info and tip seen (tip advanced a block per ten minutes), so the first paint prices renewals right and shows days-left; a card with no info at all shows "…", never "free". Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Custody card: drop "Coinos holds it for you"Adam Soltys16 hours1-1/+1
| | | | Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Custody card: a full stop where the colon wasAdam Soltys16 hours1-1/+1
| | | | Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Custody card: "paying still works" (paying and withdrawing are one thing; ↵Adam Soltys16 hours1-1/+1
| | | | | | receiving goes without saying) Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Co-operative exit takes the ticked coins: a subset on the Manage page goes ↵Adam Soltys16 hours2-22/+40
| | | | | | | | on-chain as exactly those coins Adam, 2026-10-23: the checkboxes only fed Renew; the exit button always prefilled the whole balance. The manager's offboard always took a coin list, and the fee quote prices each coin by its own expiry, so the page now passes the selection through: the card says which coins will move and prices their total, the exit form shows "N selected coins · amount" instead of an amount field, the quote is for those coins, and no fresh coin is split off first. Nothing ticked (or everything) still means the whole balance, with the editable amount as before. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Custody card: plain words — "Allow coins to expire", "Minimize renewal ↵Adam Soltys16 hours1-10/+10
| | | | | | | | cost", "Minimize exit cost"; no timelocks, rounds or "the old Coinos" Adam, 2026-10-23. Coins "expire"; the exit is "yours until then"; what a renewal buys is said without naming the mechanism. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Custody card copy: "coin", not "Spending coin"; Coinos capitalized; the ↵Adam Soltys16 hours1-7/+7
| | | | | | | | default tier says the exit stays until a coin's timelock runs out Adam, 2026-10-23: "Coinos holds my coins" read as if the holder never had a unilateral exit. They do, until the timelock runs out; only then does Coinos hold the coin. The option is now "Coinos holds my coins once they expire", its description says so, and the intro says what the timelock buys and what renewing keeps. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Zap sound: shorter and deeper — the strike alone, 0.35 s, 20% lower, ↵Adam Soltys16 hours1-1/+1
| | | | | | | | | | low-passed at 2.6 kHz Adam auditioned twelve takes on the Jacob's ladder clip (freesound 536741) and chose this one: the first 0.45 s of the arc with its opening spike cut (0.10–0.45 s), pitched down 20%, the fizz above 2.6 kHz removed, a 130 ms fade. Same loudness as before (loudnorm −16 LUFS). The clip chunk shrinks from 12 KB to 4.7 KB of base64. tools/zap-feedback-test.js pins the new length (0.3–0.5 s) and dispatches the held-screen's two clicks in the page: the sound toggle re-renders that screen, and puppeteer's own click found its node detached mid-morph (failed on HEAD with the old clip too). Co-Authored-By: Claude Fable 5.1 <[email protected]>
* The third custody tier keeps exits cheap by depth, not by date: "Keep my ↵Adam Soltys17 hours3-19/+51
| | | | | | | | | | exit cheap" renews a coin once its history is four hops deep Why: a unilateral exit publishes one on-chain transaction per off-chain hop, so depth — not age — is what makes an exit expensive, and a renewal a week before expiry flattens exactly the same history a renewal in the last hours does. The week-ahead "early" tier only guarded against the server being unreachable at the very end, which is not the case worth selling. (Adam, 2026-10-23.) exit = self (last-hours renewal, free bracket + base, the same grid-snapped backstop) plus: any coin whose genesis chain is 4+ hops deep is renewed in the next round the app runs, paying the top bracket + base — but only when that fee is under a tenth of the coin (flattening a 21-sat coin for 300 sats helps nobody; its exit is uneconomic anyway). Deep coins ride along with an expiry renewal when one is due, or get a round of their own. The server-side backstop stays near-expiry only: depth changes only while the wallet transacts, i.e. while the app is open. A stored 'early' reads as 'exit'. Card copy says what the hop count means and what the two prices are for. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Custody is a choice, and coinos holding the coins is the default: nothing ↵Adam Soltys18 hours7-119/+340
| | | | | | | | | | | | | | | | | | | | renews on its own and no renewal fee is paid unless the user opts in Why: renewal fees were "money passively draining" to people who thought they had a custodial wallet (Stacker News, 2026-10-22). Measured over Sep–Oct: 84% of what users paid came from the client's own consolidation round at 20 coins (0.2% + 300 on every fresh coin), the rest from 300-sat rounds that each cohort of Lightning-received coins (30-day pool lifetime) triggered on its own schedule. The ASP itself honors a coin past its timelock — rounds and offboards never checked expiry, and captaind's allow_expired_arkoor (now on at ark.coinos.io) lets arkoor spends through too — so expiry only ever meant "the exit is gone", which most people coming from v2 never had. Settings → Spending gets a Custody card with three tiers: - coinos holds my coins (default): no renewals, no fees; a coin past its timelock shows as "held by coinos", stays spendable and receivable, and is left out of a unilateral exit (there is nothing on-chain for it to take). - Keep my own exit: renew in the last hours (free bracket + base), the server backstop registered 36 blocks out and snapped to a 48-block grid so every wallet's orders share one round. - Renew a week early: 1008 blocks out on a 144-block grid (0.1% + base). renewPolicy() in ark/manager.js is the one table; the app, the service worker (the mirror carries the tier) and the Manage page all read it. Only shown on servers that honor expired coins (presets carry honorsExpired); elsewhere renewal stays automatic. Manager: honorsExpired makes a past-timelock coin ordinary money (balance, selection, receive validation via acceptExpired) while a server refusal (expiryRejected) still excludes it; such coins spend LAST so an older build on the receiving side almost never meets one. A once-per-wallet rescan re-reads the mailbox and adopts coins older builds refused as expired, un-forgets expired dust, and clears stale refusals. The refresh gate that retired a renewal of expired inputs is gone (the server accepts them; the Manage page's Renew of exactly those coins was failing silently). Automatic consolidation is gone; the send cap goes 24 → 48 coins; small change (<1,000 sats) folds into a renewal that happens anyway. Server (cs:~/ark/captaind-main.toml, restarted): allow_expired_arkoor = true; pool vtxo_lifetime 4320 → 8640 so Lightning-received coins live as long as the rest. Tests: tools/ark/custody-test.js (policies, honoring, ordering, grid snap, validator), tools/ark/custody-ui-test.js (the card, held coins, exit count, switching), nwc-sw-test updated for the tier the worker reads. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Media grids: one tile for a clip uploaded twice, and a picture that won't ↵Adam Soltys24 hours2-10/+36
| | | | | | | | | | | | | | | | | | | | | | | | load stays out - Videos: one person's clips of one size to the byte are one clip. Beatbox Serenade posted the same video as a note and as a video event, each upload a fresh Blossom hash, so neither the file nor the words matched. Only a size the post or the host states counts (a bitrate x duration guess is marked sizeGuess and never matched). - Images: a picture whose load failed was hidden by a class the next repaint rewrote, so the broken-picture glyph came back (a deleted nostr.media file in #artstr). Failed pictures are now remembered for the session and left out of the grid. - A tile fetching its own frame froze the whole tile against repaints, so one built for the one-to-a-row layout never got its --ar in justified rows (zero width). Only the player and its stand-ins are frozen now; the render draws the cost badge itself. Tile pictures no longer size their tile (a frame-grab <video>'s 320px stretched its row). - Test fixtures that served one file under several names (or one imeta size for every clip) now differ per clip. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01K5xtiFrkFVsrkWx6JDQjiT
* Videos grid: a clip the player can't load leaves the grid instead of sitting ↵Adam Soltys27 hours1-0/+12
| | | | | | | | | | | | | there as a blank tile A deleted file on Primal's blossom answers 404 without CORS headers, so the probe saw only a quiet host (not a 404), the server had no still for it, and the tile's fallback player failed with nothing listening: a blank box (Adam's iPhone). The player's error now hides the tile and marks the clip gone for a day, as a 404 the probe could read already did. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01K5xtiFrkFVsrkWx6JDQjiT
* iPhone: a push subscription iOS dropped is made again when the app opens or ↵Adam Soltys28 hours1-3/+21
| | | | | | | | | | | | | | | comes back WebKit removes a subscription after three pushes that showed no notification (743011c stops new strikes, but a wallet already struck out stayed deaf): background stayed on, getSubscription() came back empty and refreshRegistration gave up, so NWC requests stopped waking the closed app until the switch was toggled (reported by PullCap). It now subscribes again (also when the VAPID key changed) and re-registers with the notifier, on start and whenever the app returns to the screen. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01K5xtiFrkFVsrkWx6JDQjiT
* Videos: a desktop-sized window defaults to every shape (a phone keeps to its ↵Adam Soltys28 hours1-5/+19
| | | | | | way round; the choice is stored only when it differs from the device's default). A clip's shape falls back to its still's proportions, so leaving All shapes finds the clips known. The grid's pager keeps a clip of unknown shape out until measured, as the grid does (d898efb let an unmeasured landscape clip into a phone's vertical pager). Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Images grid in justified rows too: tiles in each picture's own shape (the ↵Adam Soltys29 hours3-15/+37
| | | | | | tile thumbnail is no longer a square crop: 360px tall, its middle cut to 1:2–2:1; vmedia pic.webp?s=row for hosts without CORS; tile cache v2) Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Videos grid, justified rows: a tile whose clip's size isn't known yet takes ↵Adam Soltys29 hours1-2/+16
| | | | | | its still's proportions Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Videos grid, "All shapes": justified rows (each tile its clip's own ↵Adam Soltys29 hours2-7/+20
| | | | | | proportions, every row one height filling the width) instead of squares Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Videos grid: "All shapes" lays the clips out three across in squares (tall ↵Adam Soltys29 hours2-4/+8
| | | | | | tiles stay for a phone held upright showing tall clips only) Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Videos grid on a wide screen (wide clips only): one clip to a row with its ↵Adam Soltys29 hours2-1/+7
| | | | | | 16:9 poster whole, instead of portrait slivers three across Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Videos: "All shapes" in a pager opened from the grid brings in the clips the ↵Adam Soltys29 hours1-3/+6
| | | | | | other way round (the pager now holds every clip, pagerFit keeping the wrong-shaped ones out of sight until asked) Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Videos grid: a clip whose host says it's deleted (404/410) is left out for a ↵Adam Soltys29 hours1-5/+12
| | | | | | day instead of sitting there as a blank tile Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Media grid shows a clip or picture once however many posts carry it (same ↵Adam Soltys29 hours1-5/+34
| | | | | | file or Blossom hash, or the same words from the same person re-uploaded); on a big window a small clip in the pager is drawn at most 1.5x its pixels instead of blown up across the monitor Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Videos on a desktop: moving the mouse over a playing clip brings the seek ↵Adam Soltys29 hours1-2/+13
| | | | | | bar up, and it fades about 2 s after the mouse goes still (it stays while the pointer rests on the bar) Co-Authored-By: Claude Opus 5.5 <[email protected]>
* iPhone: every push ends in a notification, so iOS stops dropping the ↵Adam Soltys31 hours1-14/+56
| | | | | | | | | | | | | | | | wallet's push subscription WebKit allows no silent pushes: a push that shows no notification is a strike (even with the app open), and after three it removes the subscription, so a closed wallet stopped waking for NWC requests until it was opened again (reported by PullCap). On an Apple endpoint, the paths that used to end quietly (lookups answered, another device took the request, DMs/reactions we don't announce, an open window, unknown pushes) now show a silent self-replacing "Coinos" line, closed at once when the app is on screen. A backgrounded iOS app is suspended, so an NWC request is no longer handed to it: the worker answers itself. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Replies whose post can't be found leave the Conversations view, and fewer go ↵Adam Soltys3 days2-7/+42
| | | | | | | | | | | | | | | missing A reply under "Couldn't find that note on the relays" told nobody anything (Adam, 2026-10-20), so once every lookup comes back without its post it isn't shown in a feed's Conversations view (its thread, notifications and the replier's own Replies tab keep it; a lookup that later finds the post brings it back). The lookups miss less: - A NIP-22 comment (kind 1111) names its parent's author at e-tag [3], not NIP-10's [4], so every comment's parent lost its author and the author's outbox went unasked. fiatjaf's comment on Kieran's routstr post lives on relay.snort.social (its hint), Kieran's pyramid.fiatjaf.com and ditto: none of ours. - A reply that names no author asks the outboxes of the people it's addressed to (its p tags, up to three). - The relay the reference itself names is asked even when this device has marked it sick. - A post read in Following (or the feed in use) is found in that feed's disk cache too, so a Conversations view opened right after a reload doesn't take its replies for orphans. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Timed reactions sit on the seek bar at their moment, and the usual buttons ↵Adam Soltys3 days4-72/+145
| | | | | | | | | carry the time The "at this time" reply/like/zap buttons are gone, and with them the panel they made over the pager. A reply, like or zap made from a post's own buttons (the feed's, or the pager's ♥ ⚡ and post) while its clip has played is about that moment; the reply box says so and its × makes it about the whole post. What was said, reacted or zapped at a moment sits over the bar at that moment (a custom :emoji: as its picture, from the reaction's own tag; it was showing as :cb-rofl: text), a tap goes there and says who and what, and in the pager it floats up off the bar as the clip plays past. Your reaction no longer turns the count red, only the heart. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Videos: the seek bar is out of sight (and out of reach) while a clip plays. ↵Adam Soltys4 days2-4/+19
| | | | | | | A tap near the foot of the screen brings it up for a few seconds without pausing (longer while it's dragged), and a paused clip shows it. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Videos grid: a clip opens sooner. Its host is connected to ahead (a ↵Adam Soltys4 days1-2/+52
| | | | | | | preconnect for the grid's clip hosts: the first clip from a host took 1.8 s on Adam's phone, 0.65 s once connected), and the player starts loading when the finger comes down on the tile; the pager adopts that very element, so the load overlaps the tap and the pager's opening. A press with no tap after it lets its player go. (feed-media-views test: the Live chip, and the warm next/kept previous players.) Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Videos: a warm player gets its sound going before the swipe lands. On an ↵Adam Soltys4 days1-2/+38
| | | | | | | Android phone a player's first play with sound held a still picture ~250 ms while the audio output started (muted: 60 ms; a player that had played resumes in 20-80 ms). So the next clip (and the one behind) plays at a volume nobody hears until its picture moves, a frame or two in, and pauses there (a seek back to 0 undoes it). Only with the pager's sound on, and for clips up to 25 MB: once played, a player reads the whole file. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Videos: the clip just left keeps its player, so a swipe back starts at once ↵Adam Soltys4 days1-10/+16
| | | | | | | too (on Adam's phone it was the slow one: 0.66-0.78 s, built anew, against 0.04-0.08 s onto the warm next clip). The swipe clock records the player's readyState and how far it had buffered. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Videos start the moment a swipe lands, and streams say who's watching.Adam Soltys4 days2-3/+101
| | | | | | | | - The pager's next clip gets a warm player: once its opening is in the cache it reads to its first frame (preload metadata: Chrome stops ~2-4 MB in at real link speeds) and waits paused, so landing on it is only play and sound. coinosPagerStarts() in the console times every landing (swipe to moving picture, warm or cold, host, size). - A stream's "N watching" names who on hover, and a tap shows their faces: NIP-53 presence (kind 10312, as Shosho and zap.stream say it), fresh for half an hour. coinos says it too while a stream is open (every 5 min, with a key held here only, never for your own stream) and takes it back on leaving. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Zaps are signed as the person zapping: the login identity (extension, ↵Adam Soltys4 days2-5/+17
| | | | | | | bunker, passkey, Google) when there is one, else the wallet's key. They were always signed by the wallet's own key, so a signed-in person's zaps showed in stream chats and streamers' overlays under a stranger npub. Zap requests also carry created_at now (a real signer refused them without it). Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Posts: a relay named in a post (wss://…) is a link to what's on it — the ↵Adam Soltys4 days2-1/+10
| | | | | | | raw firehose of that relay alone (/feed/all?r=…). Trailing punctuation stays outside the link. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Profiles: past streams move off the top into a Streams tab (beside Posts and ↵Adam Soltys4 days3-14/+10
| | | | | | | Replies, shown once they've streamed), as the Live chip's grid of tiles. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Streams: Delete recording is a trash-can button and Share is the share icon ↵Adam Soltys4 days1-3/+4
| | | | | | | (three joined circles), so the stream's buttons fit on one row on a phone. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Add timestamp comments, reactions and zaps with audio waveform seekingAdam Soltys4 days7-26/+263
|
* Streams: Delete recording, and Past streams that scroll on.Adam Soltys4 days2-14/+86
| | | | | | | | | - **Delete recording:** your own ended stream's page has the button. The live server lets the recording go, and the stream is announced again without it, so other apps drop the link too. A deleted one leaves Past streams and says the streamer deleted it. - **Admin:** the Take down button no longer shows on the admin's own streams. - **Past streams** scroll on: 24 at a time as the grid's foot comes near, then older ones from the relays (before the oldest held), until a page brings nothing. They're no longer cut off at a month. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Live streams over WebRTC. Going live also sends the camera to Cloudflare's ↵Adam Soltys4 days1-3/+107
| | | | | | | | | | | | SFU, which the live server brokers, and a viewer in the app watches from there: 0.1 s behind the broadcaster in go-live-test LATENCY=1, against 3.5 s on HLS. - Switching to the screen and back swaps the track in place. - A reconnect that makes a new stream publishes again, and its viewers follow. - A viewer falls back to HLS if there's no WebRTC for the stream or no connection in 8 s. HLS is still sent, for the recording and the other nostr apps. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Notifications: tapping a zap with no post behind it does something. A zap on ↵Adam Soltys4 days1-1/+7
| | | | | | | your profile opens the zapper's profile, and a zap on a stream opens the stream. The tap used to do nothing, because the row only knew how to open a post. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Watching a stream: a stream that's on plays through hls.js, held near the ↵Adam Soltys5 days2-4/+15
| | | | | | | live edge (1.5 s back, up to 1.1× when it drifts past 5 s) — Chrome's own HLS player starts three segments back, never catches up, and fails outright (DEMUXER_ERROR_COULD_NOT_PARSE) if a page changes its speed. With the live server's 1 s segments a viewer is 3.5 s behind the broadcaster, from 8.6 s (go-live-test LATENCY=1 measures it: the broadcaster's camera is a clock in black and white blocks, read off the viewer's frame). Recordings, and streams where hls.js can't run (iOS), stay on the browser's player. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Screen sharing. In a call: Share screen in the call's bar (where the browser ↵Adam Soltys5 days4-10/+85
| | | | | | | can — desktops) puts your screen in your camera's place, or into the call if your camera's off; Stop sharing, or the browser's own, brings the camera back or stops the picture, and the other side's view follows (a picture that stops coming is hidden). Live: Share screen / Back to camera on the broadcast bar switches without stopping the stream; the browser's Stop sharing goes back to the camera instead of ending it, and a cancelled screen picker leaves the camera going. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Video calls show the whole of the other person's picture (letterboxed), not ↵Adam Soltys5 days1-1/+5
| | | | | | | cropped to fill the window — a phone's portrait camera on a laptop's wide window was a forehead filling the screen. The page under a full-screen call no longer scrolls behind it (the scrollbar did nothing visible). Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* A stream's chat is the app's chat: each comment is a bubble like a DM's or a ↵Adam Soltys5 days3-43/+172
| | | | | | | group's — tap for the emoji row, reply, zap or copy; reactions (kind 7, tagged with the stream and the comment, one per person, deletable) and zap totals show on the comment for every viewer; the zap goes to the commenter, for that comment, on that stream. The composer is the one the other chats use (drafts kept per stream). A reaction no relay took is taken back. (Begun by Codex; finished here.) Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Leaving a stream: Feed (and the rest of the nav) leaves it on the first tap ↵Adam Soltys5 days1-3/+7
| | | | | | | — with the feed under the stream, Feed thought it was already there and only scrolled the hidden feed, and the nav's page-leaving didn't close the stream or Go live. On the stream's own page, the host's ringed face opens their profile instead of the same stream again. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* DMs and calls go to relay.coinos.io and relay.primal.net (both take gift ↵Adam Soltys5 days2-4/+28
| | | | | | | wraps and pass call messages on live), not nos.lol, which refused or ignored the household IP's publishes. A relay that turns a call message away is left out of the next ones on this device: a day for one that won't take the kind (nip17.com: only kind 1059), ten minutes for a rate limit or ban. publishOn tells its caller which relay refused and why (onrefused). Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Calls: the routes found before the offer or answer ride inside it (until ↵Adam Soltys5 days2-6/+22
| | | | | | | there's a direct and a relayed one, 1.5 s at most) and only later ones go one by one — each route was its own signed message, dozens per call; a household's two devices calling each other got its IP banned by damus and rate-limited by relay.coinos.io, and the answer was refused with them: the callee stuck on Connecting…, the caller ringing on. An offer, answer, hang-up or decline no relay took is tried again (1.5, 3, 6 s) before the call gives up. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* A voice call can turn video: Turn on camera in the call's bar puts your ↵Adam Soltys5 days3-4/+37
| | | | | | | camera in (a renegotiation, NIP-AC's kind 25055); the other side sees you at once and gets the same button for theirs. The call's sound carries on throughout. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Streams on coinos's server: a ⚑ Report button (what's wrong, a note) for ↵Adam Soltys5 days2-5/+89
| | | | | | | anyone signed in, and Take down for the admin (asks whether to bar the account too); a taken-down stream says so and plays nothing. Go live asks the server first: a name claimed less than a day ago is told when it can, and a barred account that it can't, Start off. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Streams: one our live server has ended reads ended, whatever its last ↵Adam Soltys5 days3-6/+75
| | | | | | | announcement says (an app closed mid-stream never says so, and it read live for an hour with no way to stop it); its host gets End — on the stream and on their profile — which publishes the ended announcement with the recording. Each announcement is now newer than the last to the second (an ended stamped the same second as the live before it lost to it). A recording that is a web page (zap.stream's Rumble links) gets a Watch the recording link instead of a player that can't play it. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg