release: seal Focus Seed 1.0.0
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import '../core/json_utils.dart';
|
||||
import '../models/app_state.dart';
|
||||
|
||||
Future<void> showHabitDialog(
|
||||
BuildContext context,
|
||||
ValueChanged<String> onSubmit, {
|
||||
String initialValue = '',
|
||||
}) async {
|
||||
final controller = TextEditingController(text: initialValue);
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder:
|
||||
(context) => AlertDialog(
|
||||
title: Text(initialValue.isEmpty ? 'Add habit' : 'Rename habit'),
|
||||
content: TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
textCapitalization: TextCapitalization.sentences,
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
hintText: 'Example: Read 10 pages',
|
||||
),
|
||||
onSubmitted: (value) {
|
||||
onSubmit(value);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () {
|
||||
onSubmit(controller.text);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Save'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
controller.dispose();
|
||||
}
|
||||
|
||||
Future<void> confirmDeleteHabit(
|
||||
BuildContext context,
|
||||
VoidCallback onConfirm,
|
||||
) async {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder:
|
||||
(context) => AlertDialog(
|
||||
title: const Text('Delete habit?'),
|
||||
content: const Text(
|
||||
'Past completion records for this habit will be removed.',
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () {
|
||||
onConfirm();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Delete'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> showExportDialog(BuildContext context, AppState state) async {
|
||||
final controller = TextEditingController(text: prettyJson(state.toJson()));
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder:
|
||||
(context) => AlertDialog(
|
||||
title: const Text('Export JSON'),
|
||||
content: SizedBox(
|
||||
width: double.maxFinite,
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
minLines: 8,
|
||||
maxLines: 12,
|
||||
readOnly: true,
|
||||
decoration: const InputDecoration(border: OutlineInputBorder()),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Close'),
|
||||
),
|
||||
FilledButton.icon(
|
||||
onPressed: () async {
|
||||
await Clipboard.setData(ClipboardData(text: controller.text));
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
showSnack(context, 'Export copied');
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.copy),
|
||||
label: const Text('Copy'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
controller.dispose();
|
||||
}
|
||||
|
||||
Future<void> showImportDialog(
|
||||
BuildContext context,
|
||||
ValueChanged<String> onImport,
|
||||
) async {
|
||||
final controller = TextEditingController();
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder:
|
||||
(context) => AlertDialog(
|
||||
title: const Text('Import JSON'),
|
||||
content: TextField(
|
||||
controller: controller,
|
||||
minLines: 8,
|
||||
maxLines: 12,
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
hintText: 'Paste Focus Seed JSON export',
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () {
|
||||
onImport(controller.text);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Import'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
controller.dispose();
|
||||
}
|
||||
|
||||
Future<void> confirmClearData(
|
||||
BuildContext context,
|
||||
VoidCallback onConfirm,
|
||||
) async {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder:
|
||||
(context) => AlertDialog(
|
||||
title: const Text('Clear local data?'),
|
||||
content: const Text(
|
||||
'This resets habits, focus sessions, and reflections on this device.',
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () {
|
||||
onConfirm();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Clear'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void showPrivacySheet(BuildContext context) {
|
||||
showModalBottomSheet<void>(
|
||||
context: context,
|
||||
showDragHandle: true,
|
||||
builder:
|
||||
(context) => Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 8, 20, 24),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: const [
|
||||
Text(
|
||||
'Privacy',
|
||||
style: TextStyle(fontSize: 22, fontWeight: FontWeight.w800),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
Text(
|
||||
'Focus Seed stores your anchor task, habits, focus sessions, '
|
||||
'reflections, and settings locally on this device. V1 does not '
|
||||
'include account sign-in, cloud sync, ads, analytics SDKs, or '
|
||||
'billing SDKs.',
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
Text(
|
||||
'You can export your local data as JSON, import a previous '
|
||||
'export, or clear local data from Settings.',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void showSnack(BuildContext context, String message) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
|
||||
}
|
||||
Reference in New Issue
Block a user