9dfa06ffee
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
Signed-off-by: cls_宁波本机 <908705107@qq.com>
143 lines
5.0 KiB
Dart
143 lines
5.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
|
|
|
import '../l10n/l10n.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(
|
|
SnackBar(content: Text(context.l10n.signInSetReminders)),
|
|
);
|
|
return;
|
|
}
|
|
|
|
Future<void> submit(int notBefore) async {
|
|
try {
|
|
await service.createReminder(target: target, notBefore: notBefore);
|
|
messenger.showSnackBar(SnackBar(content: Text(context.l10n.reminderSet)));
|
|
} catch (error) {
|
|
// Stable user-facing copy; the relay/crypto detail goes to the log.
|
|
debugPrint('[RemindMeLater] createReminder failed: $error');
|
|
messenger.showSnackBar(
|
|
SnackBar(content: Text(context.l10n.reminderSetFailed)),
|
|
);
|
|
}
|
|
}
|
|
|
|
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(
|
|
sheetContext.l10n.remindAboutMessage,
|
|
style: Theme.of(sheetContext).textTheme.titleSmall,
|
|
),
|
|
),
|
|
for (final preset in reminderTimePresets)
|
|
ListTile(
|
|
leading: const Icon(LucideIcons.clock),
|
|
title: Text(_presetLabel(sheetContext, preset)),
|
|
onTap: () {
|
|
Navigator.of(sheetContext).pop();
|
|
submit(preset.getTimestamp());
|
|
},
|
|
),
|
|
const SheetDivider(),
|
|
ListTile(
|
|
leading: const Icon(LucideIcons.calendarClock),
|
|
title: Text(sheetContext.l10n.pickDateTime),
|
|
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(SnackBar(content: Text(context.l10n.pickFutureTime)));
|
|
}
|
|
return timestamp;
|
|
}
|
|
|
|
String _presetLabel(BuildContext context, ReminderTimePreset preset) {
|
|
final l10n = context.l10n;
|
|
return switch (preset.id) {
|
|
ReminderTimePresetId.in30Minutes => l10n.reminderIn30Minutes,
|
|
ReminderTimePresetId.in1Hour => l10n.reminderIn1Hour,
|
|
ReminderTimePresetId.in3Hours => l10n.reminderIn3Hours,
|
|
ReminderTimePresetId.tomorrow9am => l10n.reminderTomorrow9am,
|
|
ReminderTimePresetId.nextMonday9am => l10n.reminderNextMonday9am,
|
|
ReminderTimePresetId.legacy => preset.label,
|
|
};
|
|
}
|