diff --git a/mobile/lib/features/channels/channels_page.dart b/mobile/lib/features/channels/channels_page.dart index 3c23b91071..bd62d62117 100644 --- a/mobile/lib/features/channels/channels_page.dart +++ b/mobile/lib/features/channels/channels_page.dart @@ -25,6 +25,7 @@ import '../../shared/community/paired_community_landing.dart'; import '../../shared/community/community_membership_provider.dart'; import '../../shared/widgets/app_list_card_item.dart'; import '../../shared/widgets/app_list.dart'; +import '../../shared/widgets/buzz_sheet_header.dart'; import '../../shared/widgets/app_list_card.dart'; import '../../shared/relay/relay.dart'; import '../../shared/theme/theme.dart'; diff --git a/mobile/lib/features/channels/channels_page/filters.dart b/mobile/lib/features/channels/channels_page/filters.dart index 09525c3983..a5f8162604 100644 --- a/mobile/lib/features/channels/channels_page/filters.dart +++ b/mobile/lib/features/channels/channels_page/filters.dart @@ -132,57 +132,36 @@ class _SectionViewSheet extends HookConsumerWidget { Map values, T selected, ValueChanged? change, - ) => Column( - crossAxisAlignment: CrossAxisAlignment.start, + ) => AppListCard( + label: label, + dividerIndent: Grid.xs, children: [ - Text(label, style: context.textTheme.titleSmall), - const SizedBox(height: Grid.xxs), - SegmentedButton( - segments: [ - for (final entry in values.entries) - ButtonSegment(value: entry.key, label: Text(entry.value)), - ], - selected: {selected}, - showSelectedIcon: false, - direction: MediaQuery.textScalerOf(context).scale(14) > 20 - ? Axis.vertical - : Axis.horizontal, - expandedInsets: MediaQuery.textScalerOf(context).scale(14) > 20 - ? null - : EdgeInsets.zero, - style: ButtonStyle( - foregroundColor: WidgetStateProperty.resolveWith( - (states) => states.contains(WidgetState.disabled) - ? context.colors.onSurface.withValues(alpha: 0.5) - : states.contains(WidgetState.selected) - ? context.colors.onSurface - : context.colors.onSurfaceVariant, - ), - backgroundColor: WidgetStateProperty.resolveWith( - (states) => states.contains(WidgetState.selected) - ? context.colors.surface - : context.colors.onSurface.withValues(alpha: 0.06), - ), - side: WidgetStatePropertyAll( - BorderSide(color: context.colors.outline.withValues(alpha: 0.25)), - ), - shape: WidgetStatePropertyAll( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(Radii.sm), - ), - ), - padding: const WidgetStatePropertyAll( - EdgeInsets.symmetric(horizontal: Grid.xxs, vertical: Grid.twelve), + for (final entry in values.entries) + Semantics( + checked: entry.key == selected, + inMutuallyExclusiveGroup: true, + enabled: change != null, + child: AppListRow( + key: ValueKey('section-choice-$label-${entry.value}'), + title: entry.value, + trailing: entry.key == selected + ? Icon( + BuzzIcons.check, + size: 18, + color: context.colors.primary, + ) + : null, + titleColor: change == null + ? context.colors.onSurfaceVariant + : null, + onTap: change == null + ? null + : () { + dirty.value = true; + change(entry.key); + }, ), ), - onSelectionChanged: change == null - ? null - : (selection) { - dirty.value = true; - change(selection.single); - }, - ), - const SizedBox(height: Grid.xs), ], ); return PopScope( @@ -193,33 +172,20 @@ class _SectionViewSheet extends HookConsumerWidget { maxHeight: MediaQuery.sizeOf(context).height * 0.85, ), child: Padding( - padding: const EdgeInsets.fromLTRB( - Grid.gutter, - Grid.xxs, - Grid.gutter, - Grid.twelve, - ), + padding: const EdgeInsets.only(bottom: Grid.twelve), child: Column( mainAxisSize: MainAxisSize.min, children: [ - Row( - children: [ - Expanded( - child: Text( - title, - style: context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w600, - ), - ), - ), - IconButton( - tooltip: 'Close', - onPressed: saving.value ? null : () => close(), - icon: const Icon(Icons.close), - ), - ], + BuzzSheetHeader( + title: title, + trailing: saving.value + ? const IconButton( + tooltip: 'Saving…', + onPressed: null, + icon: Icon(BuzzIcons.x), + ) + : null, ), - const SizedBox(height: Grid.xxs), Flexible( child: SingleChildScrollView( child: Column( @@ -297,34 +263,37 @@ class _SectionViewSheet extends HookConsumerWidget { ), ), const SizedBox(height: Grid.xxs), - Align( - alignment: Alignment.centerRight, - child: Wrap( - alignment: WrapAlignment.end, - spacing: Grid.xxs, - runSpacing: Grid.xxs, - children: [ - TextButton( - onPressed: saving.value ? null : () => close(), - child: const Text('Cancel'), - ), - FilledButton( - style: FilledButton.styleFrom( - backgroundColor: context.colors.onSurface, - foregroundColor: context.colors.surface, + Padding( + padding: const EdgeInsets.symmetric(horizontal: Grid.gutter), + child: Align( + alignment: Alignment.centerRight, + child: Wrap( + alignment: WrapAlignment.end, + spacing: Grid.xxs, + runSpacing: Grid.xxs, + children: [ + TextButton( + onPressed: saving.value ? null : () => close(), + child: const Text('Cancel'), ), - onPressed: saving.value || manager?.busy == true - ? null - : apply, - child: Text( - saving.value - ? 'Saving…' - : failed.value - ? 'Retry' - : 'Apply', + FilledButton( + style: FilledButton.styleFrom( + backgroundColor: context.colors.onSurface, + foregroundColor: context.colors.surface, + ), + onPressed: saving.value || manager?.busy == true + ? null + : apply, + child: Text( + saving.value + ? 'Saving…' + : failed.value + ? 'Retry' + : 'Apply', + ), ), - ), - ], + ], + ), ), ), ], diff --git a/mobile/test/features/channels/channels_page_test.dart b/mobile/test/features/channels/channels_page_test.dart index a4fe444452..ebe4353985 100644 --- a/mobile/test/features/channels/channels_page_test.dart +++ b/mobile/test/features/channels/channels_page_test.dart @@ -1,4 +1,4 @@ -import 'package:buzz/features/channels/channel_sort/channel_sort_storage.dart'; +import 'package:buzz/shared/widgets/app_list.dart'; import 'package:nostr/nostr.dart' as nostr; import 'package:buzz/features/channels/channel_filter/channel_filter_provider.dart'; import 'package:buzz/features/channels/channel_filter/channel_filter_manager.dart'; @@ -226,13 +226,10 @@ void main() { await tester.tap(find.byTooltip('Channels options')); await tester.pumpAndSettle(); expect(find.text('general'), findsNothing); - final all = tester.widget>( - find.ancestor( - of: find.text('All'), - matching: find.byType(SegmentedButton), - ), + final all = tester.widget( + find.ancestor(of: find.text('All'), matching: find.byType(AppListRow)), ); - expect(all.onSelectionChanged, isNull); + expect(all.onTap, isNull); await tester.tap(find.text('Cancel')); await tester.pumpAndSettle(); await tester.tap(find.text('Show hidden channels · 1')); @@ -272,13 +269,13 @@ void main() { expect(manager.store.windowFor('channels'), 7 * 86400); await tester.tap(find.byTooltip('Channels options')); await tester.pumpAndSettle(); - final days = tester.widget>( + final days = tester.widget( find.ancestor( of: find.text('7 days'), - matching: find.byType(SegmentedButton), + matching: find.byType(AppListRow), ), ); - expect(days.selected, {7}); + expect(days.trailing, isA()); await tester.tap(find.text('All')); await tester.tap(find.text('Cancel')); await tester.pumpAndSettle(); @@ -323,13 +320,13 @@ void main() { ), findsOneWidget, ); - final selected = tester.widget>( + final selected = tester.widget( find.ancestor( of: find.text('7 days'), - matching: find.byType(SegmentedButton), + matching: find.byType(AppListRow), ), ); - expect(selected.selected, {7}); + expect(selected.trailing, isA()); await tester.tap(find.text('Cancel')); await tester.pumpAndSettle(); expect(find.byType(AlertDialog), findsNothing); @@ -475,13 +472,10 @@ void main() { expect(find.text('A–Z'), findsOneWidget); expect(find.text('Show'), findsOneWidget); expect(find.byType(AlertDialog), findsNothing); - final alpha = tester.widget>( - find.ancestor( - of: find.text('A–Z'), - matching: find.byType(SegmentedButton), - ), + final alpha = tester.widget( + find.ancestor(of: find.text('A–Z'), matching: find.byType(AppListRow)), ); - expect(alpha.selected, {ChannelSortMode.alpha}); + expect(alpha.trailing, isA()); await tester.tap(find.text('Cancel')); await tester.pumpAndSettle();