♻️ Optimize the creator hub

This commit is contained in:
2025-10-12 00:06:48 +08:00
parent c3f61467c8
commit c660a419e2
10 changed files with 437 additions and 337 deletions

View File

@@ -150,19 +150,15 @@ final routerProvider = Provider<GoRouter>((ref) {
return EventCalanderScreen(name: name); return EventCalanderScreen(name: name);
}, },
), ),
ShellRoute(
builder:
(context, state, child) => CreatorHubShellScreen(child: child),
routes: [
GoRoute( GoRoute(
name: 'creatorHub', name: 'creatorHub',
path: '/creators', path: '/creators',
builder: (context, state) => const CreatorHubScreen(), builder: (context, state) => const CreatorHubScreen(),
), routes: [
// Web Feed Routes // Web Feed Routes
GoRoute( GoRoute(
name: 'creatorFeeds', name: 'creatorFeeds',
path: '/creators/:name/feeds', path: ':name/feeds',
builder: (context, state) { builder: (context, state) {
final name = state.pathParameters['name']!; final name = state.pathParameters['name']!;
return WebFeedListScreen(pubName: name); return WebFeedListScreen(pubName: name);
@@ -191,7 +187,7 @@ final routerProvider = Provider<GoRouter>((ref) {
), ),
GoRoute( GoRoute(
name: 'creatorPosts', name: 'creatorPosts',
path: '/creators/:name/posts', path: ':name/posts',
builder: (context, state) { builder: (context, state) {
final name = state.pathParameters['name']!; final name = state.pathParameters['name']!;
return CreatorPostListScreen(pubName: name); return CreatorPostListScreen(pubName: name);
@@ -200,7 +196,7 @@ final routerProvider = Provider<GoRouter>((ref) {
// Poll list route // Poll list route
GoRoute( GoRoute(
name: 'creatorPolls', name: 'creatorPolls',
path: '/creators/:name/polls', path: ':name/polls',
builder: (context, state) { builder: (context, state) {
final name = state.pathParameters['name']!; final name = state.pathParameters['name']!;
return CreatorPollListScreen(pubName: name); return CreatorPollListScreen(pubName: name);
@@ -209,7 +205,7 @@ final routerProvider = Provider<GoRouter>((ref) {
// Poll routes // Poll routes
GoRoute( GoRoute(
name: 'creatorPollNew', name: 'creatorPollNew',
path: '/creators/:name/polls/new', path: ':name/polls/new',
builder: (context, state) { builder: (context, state) {
final name = state.pathParameters['name']!; final name = state.pathParameters['name']!;
// initialPollId left null for create; initialPublisher prefilled // initialPollId left null for create; initialPublisher prefilled
@@ -218,7 +214,7 @@ final routerProvider = Provider<GoRouter>((ref) {
), ),
GoRoute( GoRoute(
name: 'creatorPollEdit', name: 'creatorPollEdit',
path: '/creators/:name/polls/:id/edit', path: ':name/polls/:id/edit',
builder: (context, state) { builder: (context, state) {
final name = state.pathParameters['name']!; final name = state.pathParameters['name']!;
final id = state.pathParameters['id']!; final id = state.pathParameters['id']!;
@@ -230,7 +226,7 @@ final routerProvider = Provider<GoRouter>((ref) {
), ),
GoRoute( GoRoute(
name: 'creatorStickers', name: 'creatorStickers',
path: '/creators/:name/stickers', path: ':name/stickers',
builder: (context, state) { builder: (context, state) {
final name = state.pathParameters['name']!; final name = state.pathParameters['name']!;
return StickersScreen(pubName: name); return StickersScreen(pubName: name);
@@ -238,7 +234,7 @@ final routerProvider = Provider<GoRouter>((ref) {
), ),
GoRoute( GoRoute(
name: 'creatorStickerPackNew', name: 'creatorStickerPackNew',
path: '/creators/:name/stickers/new', path: ':name/stickers/new',
builder: (context, state) { builder: (context, state) {
final name = state.pathParameters['name']!; final name = state.pathParameters['name']!;
return NewStickerPacksScreen(pubName: name); return NewStickerPacksScreen(pubName: name);
@@ -246,7 +242,7 @@ final routerProvider = Provider<GoRouter>((ref) {
), ),
GoRoute( GoRoute(
name: 'creatorStickerPackEdit', name: 'creatorStickerPackEdit',
path: '/creators/:name/stickers/:packId/edit', path: ':name/stickers/:packId/edit',
builder: (context, state) { builder: (context, state) {
final name = state.pathParameters['name']!; final name = state.pathParameters['name']!;
final packId = state.pathParameters['packId']!; final packId = state.pathParameters['packId']!;
@@ -255,7 +251,7 @@ final routerProvider = Provider<GoRouter>((ref) {
), ),
GoRoute( GoRoute(
name: 'creatorStickerPackDetail', name: 'creatorStickerPackDetail',
path: '/creators/:name/stickers/:packId', path: ':name/stickers/:packId',
builder: (context, state) { builder: (context, state) {
final name = state.pathParameters['name']!; final name = state.pathParameters['name']!;
final packId = state.pathParameters['packId']!; final packId = state.pathParameters['packId']!;
@@ -264,7 +260,7 @@ final routerProvider = Provider<GoRouter>((ref) {
), ),
GoRoute( GoRoute(
name: 'creatorStickerNew', name: 'creatorStickerNew',
path: '/creators/:name/stickers/:packId/new', path: ':name/stickers/:packId/new',
builder: (context, state) { builder: (context, state) {
final packId = state.pathParameters['packId']!; final packId = state.pathParameters['packId']!;
return NewStickersScreen(packId: packId); return NewStickersScreen(packId: packId);
@@ -272,7 +268,7 @@ final routerProvider = Provider<GoRouter>((ref) {
), ),
GoRoute( GoRoute(
name: 'creatorStickerEdit', name: 'creatorStickerEdit',
path: '/creators/:name/stickers/:packId/:id/edit', path: ':name/stickers/:packId/:id/edit',
builder: (context, state) { builder: (context, state) {
final packId = state.pathParameters['packId']!; final packId = state.pathParameters['packId']!;
final id = state.pathParameters['id']!; final id = state.pathParameters['id']!;
@@ -281,12 +277,12 @@ final routerProvider = Provider<GoRouter>((ref) {
), ),
GoRoute( GoRoute(
name: 'creatorNew', name: 'creatorNew',
path: '/creators/new', path: 'new',
builder: (context, state) => const NewPublisherScreen(), builder: (context, state) => const NewPublisherScreen(),
), ),
GoRoute( GoRoute(
name: 'creatorEdit', name: 'creatorEdit',
path: '/creators/:name/edit', path: ':name/edit',
builder: (context, state) { builder: (context, state) {
final name = state.pathParameters['name']!; final name = state.pathParameters['name']!;
return EditPublisherScreen(name: name); return EditPublisherScreen(name: name);

View File

@@ -102,42 +102,167 @@ class PublisherMemberListNotifier extends _$PublisherMemberListNotifier
} }
} }
class CreatorHubShellScreen extends StatelessWidget { class PublisherSelector extends StatelessWidget {
final Widget child; final SnPublisher? currentPublisher;
const CreatorHubShellScreen({super.key, required this.child}); final List<DropdownMenuItem<SnPublisher>> publishersMenu;
final ValueChanged<SnPublisher?>? onChanged;
final bool isReadOnly;
const PublisherSelector({
required this.currentPublisher,
required this.publishersMenu,
this.onChanged,
this.isReadOnly = false,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isWide = isWideScreen(context); if (isReadOnly || currentPublisher == null) {
if (isWide) { return ProfilePictureWidget(
return AppBackground( radius: 16,
isRoot: true, fileId: currentPublisher?.picture?.id,
child: Row( ).center().padding(right: 8);
}
return DropdownButtonHideUnderline(
child: DropdownButton2<SnPublisher>(
value: currentPublisher,
hint: CircleAvatar(
radius: 16,
child: Icon(
Symbols.person,
color: Theme.of(
context,
).colorScheme.onSecondaryContainer.withOpacity(0.9),
fill: 1,
),
).center().padding(right: 8),
items: publishersMenu,
onChanged: onChanged,
selectedItemBuilder: (context) {
return publishersMenu
.map(
(e) => ProfilePictureWidget(
radius: 16,
fileId: e.value?.picture?.id,
).center().padding(right: 8),
)
.toList();
},
buttonStyleData: ButtonStyleData(
height: 40,
padding: const EdgeInsets.only(left: 14, right: 8),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(20)),
),
dropdownStyleData: DropdownStyleData(
width: 320,
padding: const EdgeInsets.symmetric(vertical: 6),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(4)),
),
menuItemStyleData: const MenuItemStyleData(
height: 64,
padding: EdgeInsets.only(left: 14, right: 14),
),
iconStyleData: IconStyleData(
icon: Icon(Icons.arrow_drop_down),
iconSize: 19,
iconEnabledColor: Theme.of(context).appBarTheme.foregroundColor!,
iconDisabledColor: Theme.of(context).appBarTheme.foregroundColor!,
),
),
);
}
}
class _PublisherUnselectedWidget extends HookConsumerWidget {
final ValueChanged<SnPublisher> onPublisherSelected;
const _PublisherUnselectedWidget({required this.onPublisherSelected});
@override
Widget build(BuildContext context, WidgetRef ref) {
final publishers = ref.watch(publishersManagedProvider);
final publisherInvites = ref.watch(publisherInvitesProvider);
final hasPublishers = publishers.value?.isNotEmpty ?? false;
return Card(
margin: const EdgeInsets.all(16),
child: Column(
children: [ children: [
Flexible(flex: 2, child: const CreatorHubScreen(isAside: true)), if (!hasPublishers) ...[
const VerticalDivider(width: 1), const Icon(
Flexible(flex: 3, child: child), Symbols.info,
fill: 1,
size: 32,
).padding(bottom: 6, top: 24),
Text(
'creatorHubUnselectedHint',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyLarge,
).tr(),
const Gap(24),
],
if (hasPublishers)
...(publishers.value?.map(
(publisher) => ListTile(
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
leading: ProfilePictureWidget(file: publisher.picture),
title: Text(publisher.nick),
subtitle: Text('@${publisher.name}'),
onTap: () => onPublisherSelected(publisher),
),
) ??
[]),
const Divider(height: 1),
ListTile(
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
leading: const CircleAvatar(child: Icon(Symbols.mail)),
title: Text('publisherCollabInvitation').tr(),
subtitle: Text(
'publisherCollabInvitationCount',
).plural(publisherInvites.value?.length ?? 0),
trailing: const Icon(Symbols.chevron_right),
onTap: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (_) => const _PublisherInviteSheet(),
);
},
),
ListTile(
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
leading: const CircleAvatar(child: Icon(Symbols.add)),
title: Text('createPublisher').tr(),
subtitle: Text('createPublisherHint').tr(),
trailing: const Icon(Symbols.chevron_right),
onTap: () {
context.pushNamed('creatorNew').then((value) {
if (value != null) {
ref.invalidate(publishersManagedProvider);
}
});
},
),
], ],
), ),
); );
} }
return AppBackground(isRoot: true, child: child);
}
} }
class CreatorHubScreen extends HookConsumerWidget { class CreatorHubScreen extends HookConsumerWidget {
final bool isAside; const CreatorHubScreen({super.key});
const CreatorHubScreen({super.key, this.isAside = false});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final isWide = isWideScreen(context);
if (isWide && !isAside) {
return Container(color: Theme.of(context).colorScheme.surface);
}
final publishers = ref.watch(publishersManagedProvider); final publishers = ref.watch(publishersManagedProvider);
final publisherInvites = ref.watch(publisherInvitesProvider);
final currentPublisher = useState<SnPublisher?>( final currentPublisher = useState<SnPublisher?>(
publishers.value?.firstOrNull, publishers.value?.firstOrNull,
); );
@@ -207,227 +332,94 @@ class CreatorHubScreen extends HookConsumerWidget {
publisherFeaturesProvider(currentPublisher.value?.name), publisherFeaturesProvider(currentPublisher.value?.name),
); );
return AppScaffold( Widget buildNavigationWidget(bool isWide) {
appBar: AppBar( final leftItems = [
leading: !isWide ? const PageBackButton() : null,
title: Text('creatorHub').tr(),
actions: [
DropdownButtonHideUnderline(
child: DropdownButton2<SnPublisher>(
alignment: Alignment.centerRight,
value: currentPublisher.value,
hint: CircleAvatar(
radius: 16,
child: Icon(
Symbols.person,
color: Theme.of(
context,
).colorScheme.onSecondaryContainer.withOpacity(0.9),
fill: 1,
),
).center().padding(right: 8),
items: [...publishersMenu],
onChanged: (value) {
currentPublisher.value = value;
},
selectedItemBuilder: (context) {
return [
...publishersMenu.map(
(e) => ProfilePictureWidget(
radius: 16,
fileId: e.value?.picture?.id,
).center().padding(right: 8),
),
];
},
buttonStyleData: ButtonStyleData(
height: 40,
padding: const EdgeInsets.only(left: 14, right: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
),
),
dropdownStyleData: DropdownStyleData(
width: 320,
padding: const EdgeInsets.symmetric(vertical: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
),
),
menuItemStyleData: const MenuItemStyleData(
height: 64,
padding: EdgeInsets.only(left: 14, right: 14),
),
iconStyleData: IconStyleData(
icon: Icon(Icons.arrow_drop_down),
iconSize: 19,
iconEnabledColor:
Theme.of(context).appBarTheme.foregroundColor!,
iconDisabledColor:
Theme.of(context).appBarTheme.foregroundColor!,
),
),
),
const Gap(8),
],
),
body: publisherStats.when(
data:
(stats) => SingleChildScrollView(
child:
currentPublisher.value == null
? Column(
children: [
const Gap(24),
const Icon(Symbols.info, size: 32).padding(bottom: 4),
Text(
'creatorHubUnselectedHint',
textAlign: TextAlign.center,
).tr(),
const Gap(24),
const Divider(height: 1),
...(publishers.value?.map(
(publisher) => ListTile(
leading: ProfilePictureWidget(
file: publisher.picture,
),
title: Text(publisher.nick),
subtitle: Text('@${publisher.name}'),
onTap: () {
currentPublisher.value = publisher;
},
),
) ??
[]),
ListTile( ListTile(
leading: const CircleAvatar( shape: RoundedRectangleBorder(
child: Icon(Symbols.mail), borderRadius: const BorderRadius.all(Radius.circular(8)),
), ),
title: Text('publisherCollabInvitation').tr(),
subtitle: Text(
'publisherCollabInvitationCount',
).plural(publisherInvites.value?.length ?? 0),
trailing: const Icon(Symbols.chevron_right),
onTap: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (_) => const _PublisherInviteSheet(),
);
},
),
ListTile(
leading: const CircleAvatar(
child: Icon(Symbols.add),
),
title: Text('createPublisher').tr(),
subtitle: Text('createPublisherHint').tr(),
trailing: const Icon(Symbols.chevron_right),
onTap: () {
context.pushNamed('creatorNew').then((value) {
if (value != null) {
ref.invalidate(publishersManagedProvider);
}
});
},
),
],
)
: Column(
children: [
if (stats != null)
_PublisherStatsWidget(
stats: stats,
).padding(vertical: 12, horizontal: 12),
ListTile(
minTileHeight: 48, minTileHeight: 48,
title: Text('stickers').tr(), title: Text('stickers').tr(),
trailing: Icon(Symbols.chevron_right), trailing: Icon(Symbols.chevron_right),
leading: const Icon(Symbols.ar_stickers), leading: const Icon(Symbols.ar_stickers),
contentPadding: EdgeInsets.symmetric(
horizontal: 24,
),
onTap: () { onTap: () {
context.pushNamed( context.pushNamed(
'creatorStickers', 'creatorStickers',
pathParameters: { pathParameters: {'name': currentPublisher.value!.name},
'name': currentPublisher.value!.name,
},
); );
}, },
), ),
ListTile( ListTile(
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
minTileHeight: 48, minTileHeight: 48,
title: Text('posts').tr(), title: Text('posts').tr(),
trailing: Icon(Symbols.chevron_right), trailing: Icon(Symbols.chevron_right),
leading: const Icon(Symbols.sticky_note_2), leading: const Icon(Symbols.sticky_note_2),
contentPadding: EdgeInsets.symmetric(
horizontal: 24,
),
onTap: () { onTap: () {
context.pushNamed( context.pushNamed(
'creatorPosts', 'creatorPosts',
pathParameters: { pathParameters: {'name': currentPublisher.value!.name},
'name': currentPublisher.value!.name,
},
); );
}, },
), ),
ListTile( ListTile(
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
minTileHeight: 48, minTileHeight: 48,
title: Text('polls').tr(), title: Text('polls').tr(),
trailing: const Icon(Symbols.chevron_right), trailing: const Icon(Symbols.chevron_right),
leading: const Icon(Symbols.poll), leading: const Icon(Symbols.poll),
contentPadding: const EdgeInsets.symmetric(
horizontal: 24,
),
onTap: () { onTap: () {
context.pushNamed( context.pushNamed(
'creatorPolls', 'creatorPolls',
pathParameters: { pathParameters: {'name': currentPublisher.value!.name},
'name': currentPublisher.value!.name,
},
); );
}, },
), ),
ListTile( ListTile(
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
minTileHeight: 48, minTileHeight: 48,
title: Text('publisherMembers').tr(), title: Text('publisherMembers').tr(),
trailing: const Icon(Symbols.chevron_right), trailing: const Icon(Symbols.chevron_right),
leading: const Icon(Symbols.group), leading: const Icon(Symbols.group),
contentPadding: const EdgeInsets.symmetric(
horizontal: 24,
),
onTap: () { onTap: () {
showModalBottomSheet( showModalBottomSheet(
isScrollControlled: true, isScrollControlled: true,
context: context, context: context,
builder: builder:
(context) => _PublisherMemberListSheet( (context) => _PublisherMemberListSheet(
publisherUname: publisherUname: currentPublisher.value!.name,
currentPublisher.value!.name,
), ),
); );
}, },
), ),
];
final rightItems = [
ListTile( ListTile(
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
minTileHeight: 48, minTileHeight: 48,
title: const Text('webFeeds').tr(), title: const Text('webFeeds').tr(),
trailing: const Icon(Symbols.chevron_right), trailing: const Icon(Symbols.chevron_right),
leading: const Icon(Symbols.rss_feed), leading: const Icon(Symbols.rss_feed),
contentPadding: const EdgeInsets.symmetric(
horizontal: 24,
),
onTap: () { onTap: () {
context.push( context.push('/creators/${currentPublisher.value!.name}/feeds');
'/creators/${currentPublisher.value!.name}/feeds',
);
}, },
), ),
ExpansionTile( ExpansionTile(
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
title: Text('publisherFeatures').tr(), title: Text('publisherFeatures').tr(),
leading: const Icon(Symbols.flag), leading: const Icon(Symbols.flag),
tilePadding: EdgeInsets.symmetric(horizontal: 24), tilePadding: const EdgeInsets.only(left: 16, right: 24),
minTileHeight: 48, minTileHeight: 48,
children: [ children: [
...publisherFeatures.when( ...publisherFeatures.when(
@@ -437,28 +429,19 @@ class CreatorHubScreen extends HookConsumerWidget {
'publisherFeature${entry.key.capitalizeEachWord()}'; 'publisherFeature${entry.key.capitalizeEachWord()}';
return ListTile( return ListTile(
minTileHeight: 48, minTileHeight: 48,
contentPadding: EdgeInsets.symmetric( contentPadding: const EdgeInsets.symmetric(horizontal: 16),
horizontal: 24,
),
leading: Icon( leading: Icon(
Symbols.circle, Symbols.circle,
color: color: entry.value ? Colors.green : Colors.red,
entry.value
? Colors.green
: Colors.red,
fill: 1, fill: 1,
size: 16, size: 16,
).padding(left: 2, top: 4), ).padding(left: 2, top: 4),
title: Text(keyPrefix).tr(), title: Text(keyPrefix).tr(),
subtitle: Column( subtitle: Column(
crossAxisAlignment: crossAxisAlignment: CrossAxisAlignment.start,
CrossAxisAlignment.start,
children: [ children: [
Text('${keyPrefix}Description').tr(), Text('${keyPrefix}Description').tr(),
if (!entry.value) if (!entry.value) Text('${keyPrefix}Hint').tr().bold(),
Text(
'${keyPrefix}Hint',
).tr().bold(),
], ],
), ),
isThreeLine: true, isThreeLine: true,
@@ -470,37 +453,131 @@ class CreatorHubScreen extends HookConsumerWidget {
), ),
], ],
), ),
Divider(height: 1).padding(vertical: 8),
ListTile( ListTile(
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
minTileHeight: 48, minTileHeight: 48,
title: Text('editPublisher').tr(), title: Text('editPublisher').tr(),
trailing: Icon(Symbols.chevron_right), trailing: Icon(Symbols.chevron_right),
leading: const Icon(Symbols.edit), leading: const Icon(Symbols.edit),
contentPadding: EdgeInsets.symmetric( onTap: updatePublisher,
horizontal: 24,
),
onTap: () {
updatePublisher();
},
), ),
ListTile( ListTile(
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
minTileHeight: 48, minTileHeight: 48,
title: Text('deletePublisher').tr(), title: Text('deletePublisher').tr(),
trailing: Icon(Symbols.chevron_right), trailing: Icon(Symbols.chevron_right),
leading: const Icon(Symbols.delete), leading: const Icon(Symbols.delete),
contentPadding: EdgeInsets.symmetric( onTap: deletePublisher,
horizontal: 24,
), ),
onTap: () { ];
deletePublisher();
if (isWide) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8,
children: [
Expanded(
child: Card(
margin: EdgeInsets.zero,
child: Column(children: leftItems),
),
),
Expanded(
child: Card(
margin: EdgeInsets.zero,
child: Column(children: rightItems),
),
),
],
).padding(horizontal: 12);
} else {
return Card(
margin: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [...leftItems, const Divider(height: 8), ...rightItems],
),
);
}
}
return AppScaffold(
isNoBackground: false,
appBar: AppBar(
leading: const PageBackButton(),
title: Text('creatorHub').tr(),
actions: [
if (!isWideScreen(context))
PublisherSelector(
currentPublisher: currentPublisher.value,
publishersMenu: publishersMenu,
onChanged: (value) {
currentPublisher.value = value;
}, },
), ),
const Gap(8),
],
),
body: LayoutBuilder(
builder: (context, constraints) {
final isWide = isWideScreen(context);
final maxWidth = isWide ? 800.0 : double.infinity;
return Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: maxWidth),
child: publisherStats.when(
data:
(stats) => SingleChildScrollView(
child:
currentPublisher.value == null
? ConstrainedBox(
constraints: BoxConstraints(maxWidth: 640),
child: _PublisherUnselectedWidget(
onPublisherSelected: (publisher) {
currentPublisher.value = publisher;
},
),
).center()
: isWide
? Column(
spacing: 8,
children: [
PublisherSelector(
currentPublisher: currentPublisher.value,
publishersMenu: publishersMenu,
onChanged: (value) {
currentPublisher.value = value;
},
),
if (stats != null)
_PublisherStatsWidget(
stats: stats,
).padding(horizontal: 12),
buildNavigationWidget(true),
],
)
: Column(
spacing: 12,
children: [
if (stats != null)
_PublisherStatsWidget(
stats: stats,
).padding(horizontal: 16),
buildNavigationWidget(false),
], ],
), ),
), ),
loading: () => const Center(child: CircularProgressIndicator()), loading: () => const Center(child: CircularProgressIndicator()),
error: (_, _) => const SizedBox.shrink(), error: (_, _) => const SizedBox.shrink(),
), ),
),
);
},
),
); );
} }
} }
@@ -576,6 +653,18 @@ class _PublisherStatsWidget extends StatelessWidget {
height: 100, height: 100,
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8), padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
child: Tooltip(
richMessage: TextSpan(
text: statLabel.tr(),
style: TextStyle(fontWeight: FontWeight.bold),
children: [
TextSpan(text: ' '),
TextSpan(
text: statValue,
style: TextStyle(fontWeight: FontWeight.normal),
),
],
),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@@ -583,6 +672,8 @@ class _PublisherStatsWidget extends StatelessWidget {
Text( Text(
statValue, statValue,
style: Theme.of(context).textTheme.headlineMedium, style: Theme.of(context).textTheme.headlineMedium,
maxLines: 1,
overflow: TextOverflow.ellipsis,
), ),
const Gap(4), const Gap(4),
Text( Text(
@@ -594,6 +685,7 @@ class _PublisherStatsWidget extends StatelessWidget {
), ),
), ),
), ),
),
); );
} }
} }

View File

@@ -84,6 +84,7 @@ class CreatorPollListScreen extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
return AppScaffold( return AppScaffold(
isNoBackground: false,
appBar: AppBar(title: const Text('Polls')), appBar: AppBar(title: const Text('Polls')),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
onPressed: () => _createPoll(context), onPressed: () => _createPoll(context),

View File

@@ -61,6 +61,7 @@ class CreatorPostListScreen extends HookConsumerWidget {
} }
return AppScaffold( return AppScaffold(
isNoBackground: false,
appBar: AppBar(title: Text('posts').tr()), appBar: AppBar(title: Text('posts').tr()),
body: CustomScrollView( body: CustomScrollView(
key: ValueKey(refreshKey.value), key: ValueKey(refreshKey.value),

View File

@@ -178,6 +178,7 @@ class EditPublisherScreen extends HookConsumerWidget {
} }
return AppScaffold( return AppScaffold(
isNoBackground: false,
appBar: AppBar( appBar: AppBar(
title: Text(name == null ? 'createPublisher' : 'editPublisher').tr(), title: Text(name == null ? 'createPublisher' : 'editPublisher').tr(),
leading: const PageBackButton(), leading: const PageBackButton(),

View File

@@ -68,6 +68,7 @@ class StickerPackDetailScreen extends HookConsumerWidget {
} }
return AppScaffold( return AppScaffold(
isNoBackground: false,
appBar: AppBar( appBar: AppBar(
title: Text(pack.value?.name ?? 'loading'.tr()), title: Text(pack.value?.name ?? 'loading'.tr()),
actions: [ actions: [
@@ -396,6 +397,7 @@ class EditStickersScreen extends HookConsumerWidget {
} }
return AppScaffold( return AppScaffold(
isNoBackground: false,
appBar: AppBar( appBar: AppBar(
title: Text(id == null ? 'createSticker' : 'editSticker').tr(), title: Text(id == null ? 'createSticker' : 'editSticker').tr(),
), ),

View File

@@ -23,6 +23,7 @@ class StickersScreen extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
return AppScaffold( return AppScaffold(
isNoBackground: false,
appBar: AppBar( appBar: AppBar(
title: const Text('stickers').tr(), title: const Text('stickers').tr(),
actions: [ actions: [
@@ -196,6 +197,7 @@ class EditStickerPacksScreen extends HookConsumerWidget {
} }
return AppScaffold( return AppScaffold(
isNoBackground: false,
appBar: AppBar( appBar: AppBar(
title: title:
Text(packId == null ? 'createStickerPack' : 'editStickerPack').tr(), Text(packId == null ? 'createStickerPack' : 'editStickerPack').tr(),

View File

@@ -115,10 +115,12 @@ class WebFeedEditScreen extends HookConsumerWidget {
return feedAsync.when( return feedAsync.when(
loading: loading:
() => const AppScaffold( () => const AppScaffold(
isNoBackground: false,
body: Center(child: CircularProgressIndicator()), body: Center(child: CircularProgressIndicator()),
), ),
error: error:
(error, stack) => AppScaffold( (error, stack) => AppScaffold(
isNoBackground: false,
appBar: AppBar(title: const Text('Error')), appBar: AppBar(title: const Text('Error')),
body: Center(child: Text('Error: $error')), body: Center(child: Text('Error: $error')),
), ),
@@ -186,6 +188,7 @@ class WebFeedEditScreen extends HookConsumerWidget {
}, [pubName, feedId, ref, context]); }, [pubName, feedId, ref, context]);
return AppScaffold( return AppScaffold(
isNoBackground: false,
appBar: AppBar( appBar: AppBar(
title: Text(hasFeedId ? 'Edit Web Feed' : 'New Web Feed'), title: Text(hasFeedId ? 'Edit Web Feed' : 'New Web Feed'),
actions: [ actions: [

View File

@@ -17,6 +17,7 @@ class WebFeedListScreen extends ConsumerWidget {
final feedsAsync = ref.watch(webFeedListProvider(pubName)); final feedsAsync = ref.watch(webFeedListProvider(pubName));
return AppScaffold( return AppScaffold(
isNoBackground: false,
appBar: AppBar(title: const Text('Web Feeds')), appBar: AppBar(title: const Text('Web Feeds')),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
child: const Icon(Symbols.add), child: const Icon(Symbols.add),

View File

@@ -416,6 +416,7 @@ class PollEditorScreen extends ConsumerWidget {
} }
return AppScaffold( return AppScaffold(
isNoBackground: false,
appBar: AppBar( appBar: AppBar(
title: Text(model.id == null ? 'pollCreate'.tr() : 'pollEdit'.tr()), title: Text(model.id == null ? 'pollCreate'.tr() : 'pollEdit'.tr()),
actions: [ actions: [