checkpoint: focus seed 1.0.1 localized dev switch

This commit is contained in:
2026-06-11 23:35:49 +08:00
parent c51abdfb73
commit d06dc35ee6
72 changed files with 6155 additions and 1390 deletions
+103 -63
View File
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../core/json_utils.dart';
import '../l10n/l10n.dart';
import '../models/app_state.dart';
Future<void> showHabitDialog(
@@ -9,21 +10,26 @@ Future<void> showHabitDialog(
ValueChanged<String> onSubmit, {
String initialValue = '',
}) async {
final controller = TextEditingController(text: initialValue);
var value = initialValue;
await showDialog<void>(
context: context,
builder:
(context) => AlertDialog(
title: Text(initialValue.isEmpty ? 'Add habit' : 'Rename habit'),
content: TextField(
controller: controller,
title: Text(
initialValue.isEmpty
? context.l10n.addHabitTitle
: context.l10n.renameHabitTitle,
),
content: TextFormField(
initialValue: initialValue,
autofocus: true,
textCapitalization: TextCapitalization.sentences,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Example: Read 10 pages',
decoration: InputDecoration(
border: const OutlineInputBorder(),
hintText: context.l10n.habitHint,
),
onSubmitted: (value) {
onChanged: (nextValue) => value = nextValue,
onFieldSubmitted: (value) {
onSubmit(value);
Navigator.pop(context);
},
@@ -31,19 +37,63 @@ Future<void> showHabitDialog(
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
child: Text(context.l10n.cancel),
),
FilledButton(
onPressed: () {
onSubmit(controller.text);
onSubmit(value);
Navigator.pop(context);
},
child: const Text('Save'),
child: Text(context.l10n.save),
),
],
),
);
}
Future<void> showIntentionDialog(
BuildContext context,
ValueChanged<String> onSubmit, {
String initialValue = '',
}) async {
var value = initialValue;
await showDialog<void>(
context: context,
builder:
(context) => AlertDialog(
title: Text(
initialValue.isEmpty
? context.l10n.setIntentionTitle
: context.l10n.editIntentionTitle,
),
content: TextFormField(
initialValue: initialValue,
autofocus: true,
maxLength: 90,
maxLines: 3,
textCapitalization: TextCapitalization.sentences,
decoration: InputDecoration(hintText: context.l10n.intentionHint),
onChanged: (nextValue) => value = nextValue,
onFieldSubmitted: (value) {
onSubmit(value);
Navigator.pop(context);
},
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(context.l10n.cancel),
),
FilledButton(
onPressed: () {
onSubmit(value);
Navigator.pop(context);
},
child: Text(context.l10n.save),
),
],
),
);
controller.dispose();
}
Future<void> confirmDeleteHabit(
@@ -54,21 +104,19 @@ Future<void> confirmDeleteHabit(
context: context,
builder:
(context) => AlertDialog(
title: const Text('Delete habit?'),
content: const Text(
'Past completion records for this habit will be removed.',
),
title: Text(context.l10n.deleteHabitTitle),
content: Text(context.l10n.deleteHabitBody),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
child: Text(context.l10n.cancel),
),
FilledButton(
onPressed: () {
onConfirm();
Navigator.pop(context);
},
child: const Text('Delete'),
child: Text(context.l10n.delete),
),
],
),
@@ -76,79 +124,78 @@ Future<void> confirmDeleteHabit(
}
Future<void> showExportDialog(BuildContext context, AppState state) async {
final controller = TextEditingController(text: prettyJson(state.toJson()));
final scaffoldContext = context;
final export = prettyJson(state.toJson());
await showDialog<void>(
context: context,
builder:
(context) => AlertDialog(
title: const Text('Export JSON'),
title: Text(context.l10n.exportJson),
content: SizedBox(
width: double.maxFinite,
child: TextField(
controller: controller,
minLines: 8,
maxLines: 12,
readOnly: true,
decoration: const InputDecoration(border: OutlineInputBorder()),
child: SingleChildScrollView(
child: SelectableText(
export,
style: const TextStyle(fontFamily: 'monospace'),
),
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Close'),
child: Text(context.l10n.close),
),
FilledButton.icon(
onPressed: () async {
await Clipboard.setData(ClipboardData(text: controller.text));
final message = context.l10n.exportCopied;
await Clipboard.setData(ClipboardData(text: export));
if (context.mounted) {
Navigator.pop(context);
showSnack(context, 'Export copied');
showSnack(scaffoldContext, message);
}
},
icon: const Icon(Icons.copy),
label: const Text('Copy'),
label: Text(context.l10n.copy),
),
],
),
);
controller.dispose();
}
Future<void> showImportDialog(
BuildContext context,
ValueChanged<String> onImport,
) async {
final controller = TextEditingController();
var value = '';
await showDialog<void>(
context: context,
builder:
(context) => AlertDialog(
title: const Text('Import JSON'),
content: TextField(
controller: controller,
title: Text(context.l10n.importJson),
content: TextFormField(
minLines: 8,
maxLines: 12,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Paste Focus Seed JSON export',
onChanged: (nextValue) => value = nextValue,
decoration: InputDecoration(
border: const OutlineInputBorder(),
hintText: context.l10n.importHint,
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
child: Text(context.l10n.cancel),
),
FilledButton(
onPressed: () {
onImport(controller.text);
onImport(value);
Navigator.pop(context);
},
child: const Text('Import'),
child: Text(context.l10n.import),
),
],
),
);
controller.dispose();
}
Future<void> confirmClearData(
@@ -159,21 +206,19 @@ Future<void> confirmClearData(
context: context,
builder:
(context) => AlertDialog(
title: const Text('Clear local data?'),
content: const Text(
'This resets habits, focus sessions, and reflections on this device.',
),
title: Text(context.l10n.clearDataTitle),
content: Text(context.l10n.clearDataBody),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
child: Text(context.l10n.cancel),
),
FilledButton(
onPressed: () {
onConfirm();
Navigator.pop(context);
},
child: const Text('Clear'),
child: Text(context.l10n.clear),
),
],
),
@@ -189,23 +234,18 @@ void showPrivacySheet(BuildContext context) {
padding: const EdgeInsets.fromLTRB(20, 8, 20, 24),
child: ListView(
shrinkWrap: true,
children: const [
children: [
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.',
context.l10n.privacyTitle,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.w800,
),
),
const SizedBox(height: 12),
Text(context.l10n.privacyBodyOne),
const SizedBox(height: 12),
Text(context.l10n.privacyBodyTwo),
],
),
),