Files
buzz/mobile/lib/shared/reminders/remind_me_later_sheet.dart
klopez4212 27b51144f3 Polish mobile bottom sheets and profile cards (#4911)
## Summary

- standardize mobile sheets with shared spacing, close controls, action
tiles, haptics, and motion
- add uniform native concentric corners on iOS 26+ while preserving the
Android sheet shape
- refresh profile actions/status and normalize membership and huddle
timeline spacing

## Validation

- `just mobile-check`
- `just mobile-test` (1,165 tests)
- signed iPhone Release build and device install
- Android debug build and Pixel 10 install

## Snapshots

### Channel actions

![Channel action sheet on
Pixel](https://raw.githubusercontent.com/block/buzz/3babe5d8e339a1e7ad69de3b07e17eab17fe3f9d/pr-4911--pixel-channel-actions.png)

### Profile card

![Profile card sheet on
Pixel](https://raw.githubusercontent.com/block/buzz/3babe5d8e339a1e7ad69de3b07e17eab17fe3f9d/pr-4911--pixel-profile-card.png)

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
2026-08-05 20:22:37 +01:00

130 lines
4.4 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import '../theme/theme.dart';
import '../widgets/sheet_divider.dart';
import '../widgets/modal_presentation.dart';
import 'reminder_service.dart';
import 'reminder_time_presets.dart';
/// Bottom sheet for "Remind me later": desktop-parity presets plus a native
/// date/time picker, publishing the same author-private kind-30300 reminder
/// events desktop reads.
void showRemindMeLaterSheet({
required BuildContext context,
required WidgetRef ref,
required ReminderTarget target,
}) {
final service = ref.read(reminderServiceProvider);
final messenger = ScaffoldMessenger.of(context);
if (service == null) {
messenger.showSnackBar(
const SnackBar(content: Text('Sign in to set reminders')),
);
return;
}
Future<void> submit(int notBefore) async {
try {
await service.createReminder(target: target, notBefore: notBefore);
messenger.showSnackBar(const SnackBar(content: Text('Reminder set')));
} catch (error) {
// Stable user-facing copy; the relay/crypto detail goes to the log.
debugPrint('[RemindMeLater] createReminder failed: $error');
messenger.showSnackBar(
const SnackBar(content: Text('Couldn’t set reminder. Try again.')),
);
}
}
showBuzzModalBottomSheet<void>(
context: context,
showDragHandle: true,
builder: (sheetContext) => SafeArea(
child: IconTheme.merge(
data: const IconThemeData(size: 22),
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.fromLTRB(
Grid.gutter,
0,
Grid.gutter,
Grid.xs,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(
left: Grid.half,
bottom: Grid.xxs,
),
child: Text(
'Remind me about this message',
style: Theme.of(sheetContext).textTheme.titleSmall,
),
),
for (final preset in reminderTimePresets)
ListTile(
leading: const Icon(LucideIcons.clock),
title: Text(preset.label),
onTap: () {
Navigator.of(sheetContext).pop();
submit(preset.getTimestamp());
},
),
const SheetDivider(),
ListTile(
leading: const Icon(LucideIcons.calendarClock),
title: const Text('Pick a date & time'),
onTap: () async {
final navigator = Navigator.of(sheetContext);
final timestamp = await _pickCustomDateTime(context);
// Cancelled or not-in-the-future: keep the preset sheet
// open so retrying doesn't mean long-pressing again.
if (timestamp == null) return;
if (navigator.mounted) navigator.pop();
await submit(timestamp);
},
),
],
),
),
),
),
),
);
}
/// Native date + time pickers chained together. Returns a future Unix
/// timestamp in seconds, or null when cancelled or not in the future.
Future<int?> _pickCustomDateTime(BuildContext context) async {
final now = DateTime.now();
final date = await showBuzzDialog<DateTime>(
context: context,
builder: (_) => DatePickerDialog(
initialDate: now,
firstDate: DateTime(now.year, now.month, now.day),
lastDate: now.add(const Duration(days: 365 * 2)),
),
);
if (date == null || !context.mounted) return null;
final time = await showBuzzDialog<TimeOfDay>(
context: context,
builder: (_) =>
const TimePickerDialog(initialTime: TimeOfDay(hour: 9, minute: 0)),
);
if (time == null) return null;
final timestamp = combineCustomDateTime(date, time.hour, time.minute);
if (timestamp == null && context.mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('Pick a time in the future')));
}
return timestamp;
}