1.0 library + player settings: Jellyfin/Link/Files library with details panel, player gear, settings cards, host flag
Library: source switch, selectable cards with a details panel, one merged Continue row, Latest grouped per series without re-releases, Link page with previews and recent links, Files embedded. Player: gear popover in both designs, progress line while controls hide, live hwdec. Settings rebuilt as cards. State marks the host member (proto stays 9, unreleased); hosting shows LAN invite addresses; window buttons in fullscreen. fadeThrough keeps the outgoing view's state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
99cda7ed04
commit
d092ea371a
54 files changed
+4963
-1825
No files matched your search
@@ -516,7 +516,9 @@ void mountSyncRoute(
|
||||
}
|
||||
memberId = hub.engine.applyJoin(token, session.tokens[token]!.name, hello.user.avatar);
|
||||
}
|
||||
session.tokens[token]!.isHost = hub.isHostKey(hello.hostKey);
|
||||
final isHost = hub.isHostKey(hello.hostKey);
|
||||
session.tokens[token]!.isHost = isHost;
|
||||
hub.engine.members[token]?.host = isHost;
|
||||
final activeToken = token;
|
||||
hub.sockets[activeToken] = ws;
|
||||
session.tokens[activeToken]!.onKick = (reason) async {
|
||||
|
||||
@@ -148,10 +148,27 @@ void main() {
|
||||
await host.ws.close();
|
||||
});
|
||||
|
||||
test('state marks the host member for everyone', () async {
|
||||
await server.stop();
|
||||
server = SyncServer(AppConfig(port: 0, secret: '', name: 't', hostKey: 'hk'));
|
||||
await server.start();
|
||||
final host = await _Peer.join('127.0.0.1', server.boundPort, 'host', hostKey: 'hk');
|
||||
final friend = await _Peer.join('127.0.0.1', server.boundPort, 'friend');
|
||||
final state = StateBroadcast.fromJson(await friend.next('state'));
|
||||
final byName = {for (final m in state.members) m.name: m};
|
||||
expect(byName['host']!.host, isTrue);
|
||||
expect(byName['friend']!.host, isFalse);
|
||||
final raw = server.hub.stateBroadcast().toJson()['members'] as List;
|
||||
expect([for (final m in raw) (m as Map)['host']], containsAll([true, null]));
|
||||
await host.ws.close();
|
||||
await friend.ws.close();
|
||||
});
|
||||
|
||||
test('no hostKey configured: nobody is the host', () async {
|
||||
final a = await _Peer.join('127.0.0.1', server.boundPort, 'a', hostKey: '');
|
||||
final b = await _Peer.join('127.0.0.1', server.boundPort, 'b', hostKey: 'x');
|
||||
expect(server.session.tokens.values.where((t) => t.isHost), isEmpty);
|
||||
expect(server.hub.stateBroadcast().members.where((m) => m.host), isEmpty);
|
||||
await a.ws.close();
|
||||
await b.ws.close();
|
||||
});
|
||||
|
||||
@@ -88,6 +88,12 @@ void main() {
|
||||
const h = MemberView(
|
||||
id: 'm1', name: 'a', avatarHash: 'abc', ready: false, inSet: false, buffered: 0, stale: false);
|
||||
expect(MemberView.fromJson(Map<String, Object?>.from(h.toJson())).avatarHash, 'abc');
|
||||
// host: only on the hosting app's member, absent otherwise.
|
||||
expect(v.toJson().containsKey('host'), isFalse);
|
||||
const host = MemberView(id: 'm2', name: 'h', ready: false, inSet: false, buffered: 0, stale: false, host: true);
|
||||
expect(host.toJson()['host'], isTrue);
|
||||
expect(MemberView.fromJson(Map<String, Object?>.from(host.toJson())).host, isTrue);
|
||||
expect(MemberView.fromJson(Map<String, Object?>.from(v.toJson())).host, isFalse);
|
||||
expect(const ProfileIntent(name: 'b').toJson(), {'t': 'profile', 'name': 'b'});
|
||||
final p = ProfileIntent.fromJson({'t': 'profile', 'avatar': ''});
|
||||
expect(p.name, isNull);
|
||||
|
||||
Reference in new issue
Block a user