mirror of
https://github.com/block/buzz.git
synced 2026-10-28 04:51:40 +00:00
fix(mobile): match section options to existing choice sheets
Reuse the shared sheet header and checked list-card rows for sort, filter and duration. Preserve the single-sheet draft flow and disable the shared close control while saving. Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
@@ -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';
|
||||
|
||||
@@ -132,57 +132,36 @@ class _SectionViewSheet extends HookConsumerWidget {
|
||||
Map<T, String> values,
|
||||
T selected,
|
||||
ValueChanged<T>? change,
|
||||
) => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
) => AppListCard(
|
||||
label: label,
|
||||
dividerIndent: Grid.xs,
|
||||
children: [
|
||||
Text(label, style: context.textTheme.titleSmall),
|
||||
const SizedBox(height: Grid.xxs),
|
||||
SegmentedButton<T>(
|
||||
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',
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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<SegmentedButton<ChannelFilterMode>>(
|
||||
find.ancestor(
|
||||
of: find.text('All'),
|
||||
matching: find.byType(SegmentedButton<ChannelFilterMode>),
|
||||
),
|
||||
final all = tester.widget<AppListRow>(
|
||||
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<SegmentedButton<int>>(
|
||||
final days = tester.widget<AppListRow>(
|
||||
find.ancestor(
|
||||
of: find.text('7 days'),
|
||||
matching: find.byType(SegmentedButton<int>),
|
||||
matching: find.byType(AppListRow),
|
||||
),
|
||||
);
|
||||
expect(days.selected, {7});
|
||||
expect(days.trailing, isA<Icon>());
|
||||
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<SegmentedButton<int>>(
|
||||
final selected = tester.widget<AppListRow>(
|
||||
find.ancestor(
|
||||
of: find.text('7 days'),
|
||||
matching: find.byType(SegmentedButton<int>),
|
||||
matching: find.byType(AppListRow),
|
||||
),
|
||||
);
|
||||
expect(selected.selected, {7});
|
||||
expect(selected.trailing, isA<Icon>());
|
||||
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<SegmentedButton<ChannelSortMode>>(
|
||||
find.ancestor(
|
||||
of: find.text('A–Z'),
|
||||
matching: find.byType(SegmentedButton<ChannelSortMode>),
|
||||
),
|
||||
final alpha = tester.widget<AppListRow>(
|
||||
find.ancestor(of: find.text('A–Z'), matching: find.byType(AppListRow)),
|
||||
);
|
||||
expect(alpha.selected, {ChannelSortMode.alpha});
|
||||
expect(alpha.trailing, isA<Icon>());
|
||||
await tester.tap(find.text('Cancel'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user