Skip to content

fix(mobile): show ephemeral (TTL) channels in the channel list#571

Merged
wesbillman merged 2 commits into
mainfrom
fix/mobile-show-ephemeral-channels
May 13, 2026
Merged

fix(mobile): show ephemeral (TTL) channels in the channel list#571
wesbillman merged 2 commits into
mainfrom
fix/mobile-show-ephemeral-channels

Conversation

@tlongwell-block
Copy link
Copy Markdown
Collaborator

@tlongwell-block tlongwell-block commented May 13, 2026

What

The iOS channel list was unconditionally dropping any channel whose
kind:39000 metadata had a ttl tag, so TTL ("ephemeral") channels were
invisible — even for members, even for the owner.

// mobile/lib/features/channels/channels_provider.dart  (before)
if (!channel.isEphemeral) {
  channels.add(channel);
}

Desktop has no equivalent filter and shows TTL channels with a badge.
Reported when Wes couldn't see #agent-creation-deep-dive on his iOS
app while everyone else saw it fine on desktop.

Why two changes

1) Drop the !isEphemeral filter. channels_page.dart already has
an _EphemeralBadge wired up:

if (channel.isEphemeral) ...[
  const SizedBox(width: Grid.xxs),
  _EphemeralBadge(channel: channel),
],

That branch was dead code because the filter one layer up was
preventing TTL channels from ever reaching it. Someone meant for TTL
channels to show with a badge — the filter was just left in.

2) Parse the archived tag in ChannelData.fromEvent. The relay's
TTL reaper auto-archives expired ephemeral channels and republishes
kind:39000 with ["archived", "true"]. The mobile parser ignored that
tag, leaving Channel.archivedAt permanently null on the Nostr path.
Without this, removing the !isEphemeral guard would let expired TTL
channels stay visible after the reaper hid them. As a bonus, this also
makes user-initiated archive state propagate correctly — the existing
!channel.isArchived filters in channels_page.dart:275 and
channels_provider.dart:207 (_subscribeLive) now work for the Nostr
path the same way they already worked for the JSON path.

The filter has been there since PR #507 introduced the file; no commit
comment explains it. Other channelsProvider consumers (search,
channel management, activity) all handle generic channels — verified
none assume "no ephemeral channels in the list."

Test

Added two regression tests in channels_provider_test.dart:

  1. ephemeral (TTL) channels appear in the list — kind:39000 with
    ["ttl", "86400"] → channel surfaces with isEphemeral=true.
  2. archived kind:39000 metadata sets Channel.isArchived (covers TTL auto-archive) — kind:39000 with both ttl and ["archived","true"]
    → channel surfaces with isArchived=true and isEphemeral=true, so
    the existing _SliverChannelsList filter hides it. Sibling active
    channel stays unarchived.
flutter test test/features/channels/channels_provider_test.dart
00:00 +5: All tests passed!
flutter test            # full mobile suite
00:06 +332 ~1: All tests passed!
flutter analyze
No issues found!

Risk

Tiny. Two narrow changes to mobile parsing, no relay-side behavior
changed, regression tests pin both the active-TTL and archived-TTL
cases. Existing test for "subscribes only to joined, non-archived"
still passes — proves _subscribeLive's archive filter continues to
work.

Credit

The archived-tag oversight was caught by a codex review pass on
the first revision of this PR — without it, this would have shipped a
visible regression once the first TTL channel expired.

@tlongwell-block tlongwell-block force-pushed the fix/mobile-show-ephemeral-channels branch 2 times, most recently from 135cb1e to 542b9ed Compare May 13, 2026 20:45
The iOS channel list provider was unconditionally dropping any channel
with a 'ttl' tag, which made TTL channels invisible to members even
though they appeared correctly on desktop. The filter was added when the
file was introduced (PR #507) and no comment explained why.

Meanwhile, channels_page.dart already has an _EphemeralBadge widget
ready to render TTL channels with a remaining-time indicator — that
path was dead code for the main list because the channels never made
it there.

Repro: a member of an open, kind:39000-with-ttl-tag channel could not
see it on iOS at all, while desktop showed it with the existing badge.

Fix:
1) Drop the !channel.isEphemeral guard in channels_provider.dart so
   ttl-tagged channels reach the list.
2) Parse the kind:39000 'archived' tag in ChannelData.fromEvent and
   propagate it to Channel.archivedAt.

The second change is necessary because the relay's TTL reaper auto-
archives expired ephemeral channels and republishes kind:39000 with
['archived', 'true']. Without parsing that tag, removing the
!isEphemeral guard would let expired TTL channels stay visible after
the reaper hid them. The same fix also makes user-initiated archive
state propagate correctly via the Nostr path — previously the mobile
parser ignored 'archived' entirely, so the existing
!channel.isArchived filters in channels_page.dart:275 and
channels_provider.dart:207 only worked for the JSON path.

Tests: added two regression tests in channels_provider_test.dart that
construct kind:39000 metadata events with the ttl / archived tags and
assert the resulting Channel surfaces them correctly.

Signed-off-by: Dawn <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co>
@tlongwell-block tlongwell-block force-pushed the fix/mobile-show-ephemeral-channels branch from 542b9ed to 74687fd Compare May 13, 2026 20:56
Codex v4 review caught: the assertion compared the second
channelDetailsProvider read count against a baseline taken before
channelsProvider.refresh(), so the refresh's own kinds:[39000]/#d
metadata query was being attributed to invalidation. If
`ref.invalidate(channelDetailsProvider)` were removed, the refresh
fetch alone would still push the count above the prime baseline and
the assertion would falsely pass.

Take a new baseline immediately after refresh and assert that the
second channelDetailsProvider read triggers at least one additional
kinds:[39000]/#d fetch on top of that. Verified by temporarily
commenting out the invalidation in channels_provider.dart and
re-running: test fails (Expected: > 0, Actual: 0). Restored.

Signed-off-by: npub1cc3ha7z055mu0rwwu7806t2wt8mj3pvu0uv5mfp2c50dahaqhczshdalg6 <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co>
@wesbillman wesbillman merged commit b176b52 into main May 13, 2026
15 checks passed
@wesbillman wesbillman deleted the fix/mobile-show-ephemeral-channels branch May 13, 2026 22:15
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