forked from Foxyz/sync_replay_project
Restructure step 1: lasting identity, presence and protocol 16
Server and core: version ranges on both sides with a coded refusal; a client id maps to the same member and token for the server's life; a drop is `gone` for a 10-minute grace keeping ready, presence, queue items, relay and avatar; `bye` leaves at once; ordered takeover (seq, replaced/stale); presence joining/in/away/gone with one PresencePolicy; joiners never pause playback and hold a held gate at most ~15 s; state.hold; feed ids, seq and replay; the relay lives through its source's grace and continues on re-dial; queue `by` lasting, auto items and cancel; MediaRef carried; client ids never logged. App: persisted client id and seq; Connection with unstable/lost/ replaced states, backoff with jitter, no lost/back pair per flap; a drop keeps the session (state, player, media, relay source); presence in the strip and members card; sit out = away intent; feed matched by id; auto sit-out (idle when not playing, minimised for a while; never when alone) replaces auto-unready; bye on close and Leave, not on update restart; relay source re-dials through its grace. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
51c1c5ab78
commit
9f2586b7ff
85 files changed
+5198
-1902
No files matched your search
@@ -6,6 +6,7 @@ import 'package:sync_relay_core/core.dart';
|
||||
|
||||
import '../screens/home/ui.dart' show appName, displayVersion, shortVersion;
|
||||
import '../session/delivery.dart';
|
||||
import '../session/party.dart';
|
||||
import '../state/session_state.dart';
|
||||
import '../widgets/app_keys.dart';
|
||||
import '../widgets/app_menu.dart';
|
||||
@@ -282,8 +283,11 @@ class _ServerLabel extends StatelessWidget {
|
||||
builder: (context) {
|
||||
final coord = SessionState.instance.coordinator;
|
||||
final r = coord.serverState.value;
|
||||
final members = r?.members ?? const <MemberView>[];
|
||||
final ready = members.where((m) => m.ready).length;
|
||||
// The ready counter counts who the presence policy counts (not away,
|
||||
// not gone, able to play it).
|
||||
final counted = (r?.members ?? const <MemberView>[]).where(countsForReady).toList();
|
||||
final ready = counted.where((m) => m.ready).length;
|
||||
final stale = coord.stateStale.value;
|
||||
return Column(
|
||||
key: const ValueKey('server-label'),
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -294,7 +298,11 @@ class _ServerLabel extends StatelessWidget {
|
||||
style: const TextStyle(fontWeight: FontWeight.w600, height: 1.1),
|
||||
),
|
||||
Text(
|
||||
r == null ? 'connecting…' : 'ready $ready/${members.length}',
|
||||
r == null
|
||||
? 'connecting…'
|
||||
: stale
|
||||
? 'reconnecting…'
|
||||
: 'ready $ready/${counted.length}',
|
||||
style: TextStyle(
|
||||
color: scheme.onSurfaceVariant,
|
||||
fontSize: 12,
|
||||
@@ -586,10 +594,13 @@ class _MemberTile extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final coord = SessionState.instance.coordinator;
|
||||
final m = member;
|
||||
final status = m.stale
|
||||
? 'reconnecting…'
|
||||
: m.out
|
||||
final since = m.goneSince;
|
||||
final status = m.presence == Presence.gone
|
||||
? 'reconnecting…${since == null ? '' : ' (${sinceText(since, DateTime.now().toUtc())})'}'
|
||||
: m.presence == Presence.away
|
||||
? 'sitting out'
|
||||
: m.presence == Presence.joining
|
||||
? 'joining…'
|
||||
: m.error.isNotEmpty
|
||||
? "can't play this: ${m.error}"
|
||||
: info.buffering
|
||||
@@ -628,11 +639,11 @@ class _MemberTile extends StatelessWidget {
|
||||
}
|
||||
},
|
||||
items: () => [
|
||||
if (!m.out) AppMenuItem(value: 'force', label: m.ready ? 'Force unready' : 'Force ready'),
|
||||
if (m.presence != Presence.away) AppMenuItem(value: 'force', label: m.ready ? 'Force unready' : 'Force ready'),
|
||||
if (!info.self) const AppMenuItem(value: 'nudge', label: 'Nudge'),
|
||||
if (info.self && m.out)
|
||||
if (info.self && m.presence == Presence.away)
|
||||
const AppMenuItem(value: 'back', label: 'Join back')
|
||||
else if (!m.out)
|
||||
else if (m.presence != Presence.away)
|
||||
const AppMenuItem(value: 'out', label: 'Sit out'),
|
||||
],
|
||||
),
|
||||
|
||||
Reference in new issue
Block a user