1.0 shell: one window with a party strip; protocol 9 profile + avatars by hash
The shell (lib/src/shell/) replaces the Home and session screens:
- Party strip: profile, Library / Schedule / Settings tabs, Join and
Host cards (hosting no longer takes media), status-ring avatar stack
with the member list, chat & activity panel, Leave.
- The real session player is mounted once and shown full, as a
draggable corner-snapping mini player, or fullscreen (window_manager,
party and toasts overlaid); the Video is never mounted twice.
- Profile popover with inline rename, colour and a crop picker (256 px
PNG), pushed live to the session; avatar toasts; waiting pill.
- Motion kit (fastEaseInToSlowEaseOut, fade-through, popovers,
mouse-drag/wheel horizontal scrolling).
- Library = the existing picker for now (restyled next round); Schedule
= the new SchedulePage; Settings = left nav over the existing panels.
Protocol 9 (packages/core, packages/server, sync client):
- {"t":"profile"} renames / changes the picture mid-session ("rename",
"avatar" feed actions); names are cleaned the same way at hello.
- state.members carry avatarHash; pictures are served once from
/a/<sid>/<token>/<hash> (PNG/JPEG/WebP/GIF by magic bytes only) and
cached by hash in the app (AvatarCache).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f790470935
commit
d29c12e4f0
62 files changed
+5777
-3572
No files matched your search
@@ -7,9 +7,8 @@ const repoUrl = 'https://git.senkochan.moe/Foxyz/sync_replay_project';
|
||||
|
||||
const kRadius = 12.0;
|
||||
const kGap = 16.0;
|
||||
const kPagePadding = EdgeInsets.fromLTRB(28, 20, 28, 28);
|
||||
|
||||
/// Rounded panel used by every Home/Settings section.
|
||||
/// Rounded panel used by every Settings section.
|
||||
class Panel extends StatelessWidget {
|
||||
final Widget child;
|
||||
final EdgeInsetsGeometry padding;
|
||||
@@ -38,33 +37,6 @@ class Panel extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Page heading + optional subtitle, same spacing everywhere.
|
||||
class PageTitle extends StatelessWidget {
|
||||
final String title;
|
||||
final String? subtitle;
|
||||
const PageTitle(this.title, {super.key, this.subtitle});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final t = Theme.of(context).textTheme;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: kGap),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(title, style: t.headlineSmall),
|
||||
if (subtitle != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: Text(subtitle!,
|
||||
style: t.bodyMedium?.copyWith(color: Colors.grey)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Small grey heading inside a panel.
|
||||
class SectionLabel extends StatelessWidget {
|
||||
final String text;
|
||||
@@ -87,7 +59,7 @@ class SectionLabel extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// Text field style shared by the connect cards.
|
||||
/// Outlined text field style (settings, Jellyfin sign-in).
|
||||
InputDecoration fieldDecoration(String label, {String? hint, Widget? suffix}) =>
|
||||
InputDecoration(
|
||||
labelText: label,
|
||||
@@ -97,31 +69,6 @@ InputDecoration fieldDecoration(String label, {String? hint, Widget? suffix}) =>
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
|
||||
);
|
||||
|
||||
/// Single-item scrollable page body, width-capped so it doesn't sprawl
|
||||
/// on wide windows.
|
||||
class PageScroll extends StatelessWidget {
|
||||
final List<Widget> children;
|
||||
final double maxWidth;
|
||||
const PageScroll({super.key, required this.children, this.maxWidth = 920});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
padding: kPagePadding,
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: maxWidth),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: children,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Widest a single text field gets, however wide the window.
|
||||
const kFieldMaxWidth = 720.0;
|
||||
|
||||
|
||||
Reference in new issue
Block a user