Skip to content

Show when users are live - #127

Merged
simonhamp merged 6 commits into
NativePHP:mainfrom
jubianchi:feat/live-avatar
Oct 29, 2026
Merged

simonhamp merged 6 commits into
NativePHP:mainfrom
jubianchi:feat/live-avatar

Conversation

@jubianchi

@jubianchi jubianchi commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

On the web, someone who is streaming wears a red ring and a LIVE badge on their avatar, and on a phone a tap on it opens a sheet with their stream. The app now does both: the badge on the log's rail and on their profile, and the stream sheet from either.

It needs the live field the API adds to every user, which is in dev-null-sh/dev-null.sh#1274 (it has the field's shape and the endpoints that carry it). This stays a draft until that one is merged.

How it works:

  • User reads live into a new Live value: the stream's URL, when it ends, the platform (twitch or youtube, or none for one the app doesn't know yet) and the thumbnail. It also writes it back, so a kept user (the account, cached rows and profiles) keeps it.
  • User::isLive() goes by ends_at, not by live being there. devnull stops sending live once the stream ends, but a user the app kept from earlier still has it, and the badge should go away on time anyway. For the same reason, whether a commit's author is live is now one of the things RowInputs keeps a row against. Without it, a row the log kept showed the badge after the stream ended, for as long as the commit's time read the same.
  • A live-badge partial draws the web's badge: a dot and LIVE in white mono on red, cut out of the surface by a 2pt edge. The dot pulses between full and half opacity, as the web's animate-pulse does, with EDGE's animate-loop like the loading skeletons.
  • rail-avatar swaps the lane ring for a red one and puts the badge 6pt below the avatar, as the web does. Rail avatars of 24pt and less (replies) only get the red ring, since the badge would be wider than the avatar.
  • The profile header does the same with a larger badge (12pt instead of the web's 10px), which read too small under the avatar on a phone. The rail keeps the web's smaller one, which reads fine on a real iPhone.
  • The red is a new live / on-live theme pair, red-600 and white in both appearances like on the web.

The stream sheet:

  • A tap on a live user's avatar opens the web's touch sheet (OpensLiveStream and partials/live-stream-sheet), on the log, a profile, a commit page and Starred: the thumbnail in 16:9 with the badge, "{name} is live" and where, Watch now, then who it is with Open profile. Their own profile's avatar opens it without Open profile, as on the web.
  • Watch now opens the stream with Browser::open, so the YouTube or Twitch app takes it when it's installed, rather than an in-app browser.
  • The thumbnail isn't kept on the device by ImageCache, since it changes while they stream.
  • A user whose stream has ended opens their profile from their avatar, as before. VoiceOver reads a live avatar as "{name} is live" rather than "{name}'s profile".

Not in this PR: notifications, who to follow and remotes aren't marked, as on the web. The web's "Live now" aside (people you follow who are live) would need an endpoint listing them.

Screenshots

iOS only: there was no Android emulator to run on. Taken on an iPhone 18 Pro simulator against a local devnull running the API branch, with @jubianchi and @glab-ranker live. Also checked on a real iPhone 16.

log

iOS light iOS dark
Before
After

profile

iOS light iOS dark
Before
After

The profile's before shots already show the rail badge at the bottom, as that change was in place when they were taken.

Stream sheet, from the log (new, so no before shots)

iOS light iOS dark
After

Recordings of the sheet opening and the pulsing dot: iOS light, iOS dark

Stream sheet, from their own profile, without Open profile

iOS light iOS dark
After
  • No visible change

🤖 Generated with Claude Code

devnull now sends `live` on every user: the platform, the stream's URL, its
thumbnail and when it ends. Kept users (rows, profiles, the account) still
carry it after the stream ends, so whether someone is live goes by `ends_at`
rather than by the field being there.

🤖 Generated with Claude Code
A user who is streaming wears a red ring instead of their lane, with the
web's LIVE badge centred under the avatar, on the log's rail and on their
profile. The badge is cut out of the surface by a 2pt edge, and the rail
uses the web's smaller one. Rail avatars of 24pt and less (replies) only
get the red ring, as the badge would be wider than the avatar.

The red is a new `live` / `on-live` theme pair, red-600 and white in both
appearances like on the web.

Tapping the avatar still opens the profile; the stream sheet comes next.

🤖 Generated with Claude Code
The web's 10px badge reads too small under the profile's avatar on a
phone, so the profile now gets a larger one: 12pt text, a 7pt dot and
more padding. The rail keeps the web's smaller badge, which reads fine at
that size on a real iPhone.

🤖 Generated with Claude Code
The log keeps the rows it drew against what they show, and a row only
changed with its commit, its time or its poll. A row drawn while its
author streamed kept the badge after the stream ended, for as long as the
commit's time read the same. Whether the author is live is now one of the
things a row is kept against.

🤖 Generated with Claude Code
On a phone, the web opens a sheet when you tap a live user's avatar, as
there's no hover to show the stream card. The app now does the same on
the log, a profile, a commit page and Starred: the stream's thumbnail in
16:9 with the LIVE badge, "{name} is live" and where, Watch now, then
who it is with Open profile. Their own profile's avatar opens it without
Open profile, as on the web.

Watch now opens the stream in the system browser, so the YouTube or
Twitch app takes it when it's installed. The thumbnail isn't kept on the
device, since it changes while they stream. A user whose stream has
ended opens their profile as before.

🤖 Generated with Claude Code
The web's badge dot uses animate-pulse. The dot now loops between full and
half opacity over a second each way, the way the app's loading skeletons
already pulse.

🤖 Generated with Claude Code
@jubianchi jubianchi changed the title Mark live users' avatars as the web does Show when users are live, as the web does Oct 28, 2026
@jubianchi jubianchi changed the title Show when users are live, as the web does Show when users are live Oct 28, 2026
@jubianchi

jubianchi commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor Author

The stream sheet opens at medium, which leaves empty space under "Watch now" on a user's own profile where there's no "Open profile" row. <native:bottom-sheet> can't size itself to its content yet, so NativePHP/mobile-ui#186 adds a fit detent for it.

Once that's in, the sheet here switches to detents="fit" (and the app needs a native rebuild to pick up the renderers).

@jubianchi
jubianchi marked this pull request as ready for review October 28, 2026 22:57
@simonhamp

Copy link
Copy Markdown
Member

thanks this is awesome!

@simonhamp
simonhamp merged commit f1cf86b into NativePHP:main Oct 29, 2026
3 checks passed
simonhamp pushed a commit that referenced this pull request Oct 30, 2026
* docs(readme): drop livestream styling from the roadmap

Live users' avatars, their LIVE badge and the stream sheet shipped in
#127, so styling for livestreams is no longer something left to do.

🤖 Generated with Claude Code

* docs(readme): catch up with what has shipped

- The API: dev-null-sh/dev-null.sh#130 was merged, and the app talks to
  production by default, so it no longer needs an instance running that
  branch.
- Setup: since #75 the app tracks main of nativephp/mobile and
  nativephp/mobile-ui instead of the combined/dev-null-app branches. The
  ToastKit fork it does track wasn't mentioned.
- Roadmap: Remotes already has voice notes (#128) and photos (#117).

🤖 Generated with Claude Code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants