Restructure step 2: Status as one computed level, the Notifier's real policy, system notifications
ci / core (push) Successful in 28s
ci / server (push) Successful in 1m11s
ci / updater-windows (push) Failing after 6m16s

- Status: computed from connection, presence, hold, relay, player facts;
  no setters (health writes and HealthStatus gone); pill, strip and
  source line read it; transitions go to the Notifier.
- Notifier: noticeFor(event, context) with self presence, alone, window
  activity, playing and prefs; toasts for joins/leaves/returns/swaps/
  Up next adds (with picture)/problems/chat, the rest logged; while
  sitting out only chat, nudges, starting, Up next (+ joins if chosen);
  alone no social notices; replayed lines logged; owns its toast timer.
- System notifications (flutter_local_notifications: D-Bus on Linux,
  WinRT on Windows) when the window isn't focused; a click focuses it.
- Toasts sit above the control bar and move with it.
- Versions: each member's in the members card and avatar tooltip,
  the server's in the card header; the strip no longer shows it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
foxyzandClaude Opus 5.5 committed 2026-10-15 22:11:08 +03:00
1 parent 130c934b9b
commit 629be0d9ed
41 files changed
+2114 -741

No files matched your search

+2 -4
View File
@@ -1,6 +1,3 @@
// restructure: rewrite in step 2 (Status + Notifier): "feed and chat toasts
// name their actor exactly".
//
// The session through drops, takeovers and goodbyes: real coordinators
// (real mpv), a real server, and FlakyProxy between a member and it.
import 'dart:async';
@@ -242,7 +239,7 @@ void main() {
final second = await member(server.boundPort, 'me', identity: install);
expect(second.connection.memberId, id, reason: 'the newer connection took the member over');
await _poll(() => first.connection.state.peek() is ConnReplaced, 'the first one replaced');
expect(first.health.peek().message, replacedText);
expect(first.status.peek().line?.text, replacedText);
// It does not fight back.
await Future.delayed(const Duration(seconds: 4));
expect(first.connection.state.peek(), isA<ConnReplaced>());
@@ -412,6 +409,7 @@ void main() {
final toasts = coord.toasts.peek();
expect(toasts.firstWhere((t) => t.text == 'Bob Jr joined').actor, 'Bob Jr');
final chat = toasts.firstWhere((t) => t.text == 'Bob Jr: hi there');
expect(chat.chat, isTrue);
expect(chat.actor, 'Bob Jr');
expect(chat.actorId, friend.memberId, reason: 'matched by member id');
// Others' chat makes a sound, your own does not.