Pills only with the controls, web links in their final layout at once, chat pop-ups when paused by default

- Next episode (known ending, else the last two minutes) and Skip ED
  show only while the controls are up, like Skip 1:30 (PillGate).
- A web link's name and wide cover slot show from the first state, so
  nothing flips when its thumbnail arrives.
- Chat pop-ups default to Only when paused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
foxyzandClaude Opus 5.5 committed 2026-10-11 05:13:50 +03:00
1 parent 27b4ec2c96
commit 2a7b553597
14 files changed
+149 -78

No files matched your search

+3 -3
View File
@@ -301,9 +301,9 @@ void main() {
a.toggleReady();
await _poll(() => all.every((x) => x.playback.player.state.playing), 'playing again');
// No known ending (no Outro segment, no ED chapter): no floating
// "Next episode" prompt mid-episode, only the card at the end.
expect(a.nextPromptVisible.peek(), isFalse);
// Account holders see the "Next episode" prompt (this item is
// shorter than its 2 min lead); the guest cannot put it up.
await _poll(() => a.nextPromptVisible.peek(), 'next prompt');
expect(b.nextPromptVisible.peek(), isFalse);
// Alice and the guest pick English; it carries to the next episode.
+3 -3
View File
@@ -321,9 +321,9 @@ void main() {
expect(media.jellyfin!.subtitle, 'Black Summoner · S1E7');
});
test('no floating prompt without a known ending', () {
expect(nextPromptStart(null, const Duration(minutes: 24)), isNull);
expect(nextPromptStart(null, const Duration(seconds: 90)), isNull);
test('the floating prompt starts two minutes before the end', () {
expect(nextPromptStart(null, const Duration(minutes: 24)), const Duration(minutes: 22));
expect(nextPromptStart(null, const Duration(seconds: 90)), Duration.zero);
});
test('one auto-advance swapper: the lowest id among members with their own account', () {
+7 -7
View File
@@ -154,14 +154,14 @@ void main() {
});
group('next episode prompt', () {
test('at the ED start when known, else none (never a guessed time)', () {
test('at the ED start when known, else the last two minutes', () {
expect(nextPromptStart(null, m(24), endingStart: m(20)), m(20));
expect(nextPromptStart(null, m(24)), isNull);
expect(nextPromptStart(null, m(24), endingStart: null), isNull);
// Nonsense ending starts count as unknown.
expect(nextPromptStart(null, m(24), endingStart: m(25)), isNull);
expect(nextPromptStart(null, m(24), endingStart: Duration.zero), isNull);
expect(nextPromptStart(null, s(90)), isNull);
expect(nextPromptStart(null, m(24)), m(22));
expect(nextPromptStart(null, m(24), endingStart: null), m(22));
// Nonsense ending starts fall back.
expect(nextPromptStart(null, m(24), endingStart: m(25)), m(22));
expect(nextPromptStart(null, m(24), endingStart: Duration.zero), m(22));
expect(nextPromptStart(null, s(90)), Duration.zero);
});
});
@@ -42,4 +42,25 @@ void main() {
const NowPlaying(title: 'Show', episode: 'S1 E2 · The Pilot', posterHash: 'aaaa', bannerHash: 'bbbb'));
expect(nowPlayingOf({'name': 'Show - 12.mkv'}), const NowPlaying(title: 'Show', episode: 'E12'));
});
test('web videos: name as is and wide from the start, before the thumbnail', () {
const title = 'Frieren - 05 | reaction';
// Everyone opens it, passed on by a member, fetched by the server.
for (final media in [
{'name': title, 'url': 'https://www.youtube.com/watch?v=x'},
{'name': title, 'relay': {'source': 'm2', 'live': true}},
{'name': title, 'relay': {'fetch': true}},
]) {
expect(nowPlayingOf(media), const NowPlaying(title: title, thumbnail: true));
expect(nowPlayingOf({...media, 'art': {'poster': 'aa', 'banner': 'aa'}}),
const NowPlaying(title: title, posterHash: 'aa', bannerHash: 'aa', thumbnail: true));
}
// Files (a link to one, a relayed one) and Jellyfin keep their parsing.
expect(nowPlayingOf({'name': 'Show - 12.mkv', 'url': 'https://cdn.example/Show%20-%2012.mkv'}),
const NowPlaying(title: 'Show', episode: 'E12'));
expect(nowPlayingOf({'name': 'Show - 12.mkv', 'relay': {'source': 'm2'}}),
const NowPlaying(title: 'Show', episode: 'E12'));
expect(nowPlayingOf({'name': 'Show - 12', 'relay': {}, 'art': {'poster': 'p', 'banner': 'b'}}),
const NowPlaying(title: 'Show', episode: 'E12', posterHash: 'p', bannerHash: 'b'));
});
}