main
dart 118 lines 3.42 KB
Raw
1 import 'package:flutter/material.dart';
2 import 'package:localmind/l10n/app_localizations.dart';
3
4 /// Generic helpers used by both chat-history folders and saved-message folders
5 /// to rename or delete a folder. The caller is responsible for invoking the
6 /// underlying repository method on confirmation.
7
8 /// Shows a dialog asking the user to enter a new folder name. Returns the
9 /// trimmed new name, or `null` if cancelled. Rejects empty names.
10 ///
11 /// The controller lives inside [_RenameFolderDialog]'s state so it is only
12 /// disposed once the dialog element is actually unmounted — disposing it as
13 /// soon as `showDialog` resolves is unsafe because the exit animation keeps
14 /// the TextField alive (and rebuilding) for a short while.
15 Future<String?> showRenameFolderDialog(
16 BuildContext context, {
17 required String currentName,
18 }) {
19 return showDialog<String>(
20 context: context,
21 builder: (ctx) => _RenameFolderDialog(currentName: currentName),
22 );
23 }
24
25 class _RenameFolderDialog extends StatefulWidget {
26 const _RenameFolderDialog({required this.currentName});
27
28 final String currentName;
29
30 @override
31 State<_RenameFolderDialog> createState() => _RenameFolderDialogState();
32 }
33
34 class _RenameFolderDialogState extends State<_RenameFolderDialog> {
35 late final TextEditingController _controller;
36 String? _errorText;
37
38 @override
39 void initState() {
40 super.initState();
41 _controller = TextEditingController(text: widget.currentName);
42 _controller.selection = TextSelection(
43 baseOffset: 0,
44 extentOffset: widget.currentName.length,
45 );
46 }
47
48 @override
49 void dispose() {
50 _controller.dispose();
51 super.dispose();
52 }
53
54 void _submit() {
55 final value = _controller.text.trim();
56 if (value.isEmpty) {
57 setState(() {
58 _errorText = AppLocalizations.of(context)!.folder_name_required;
59 });
60 return;
61 }
62 Navigator.pop(context, value);
63 }
64
65 @override
66 Widget build(BuildContext context) {
67 final l10n = AppLocalizations.of(context)!;
68 return AlertDialog(
69 title: Text(l10n.rename_folder),
70 content: TextField(
71 controller: _controller,
72 autofocus: true,
73 decoration: InputDecoration(
74 hintText: l10n.folder_name_hint,
75 errorText: _errorText,
76 ),
77 onSubmitted: (_) => _submit(),
78 ),
79 actions: [
80 TextButton(
81 onPressed: () => Navigator.pop(context),
82 child: Text(l10n.cancel),
83 ),
84 TextButton(onPressed: _submit, child: Text(l10n.save)),
85 ],
86 );
87 }
88 }
89
90 /// Shows a confirmation dialog asking the user whether they really want to
91 /// delete the folder. Returns `true` if confirmed, `false` (or `null`) otherwise.
92 Future<bool> showDeleteFolderConfirmation(
93 BuildContext context, {
94 required String folderName,
95 }) async {
96 final l10n = AppLocalizations.of(context)!;
97 final result = await showDialog<bool>(
98 context: context,
99 builder: (ctx) {
100 return AlertDialog(
101 title: Text(l10n.delete_folder_title),
102 content: Text(l10n.delete_folder_body(folderName)),
103 actions: [
104 TextButton(
105 onPressed: () => Navigator.pop(ctx, false),
106 child: Text(l10n.cancel),
107 ),
108 TextButton(
109 onPressed: () => Navigator.pop(ctx, true),
110 style: TextButton.styleFrom(foregroundColor: Colors.red),
111 child: Text(l10n.delete),
112 ),
113 ],
114 );
115 },
116 );
117 return result ?? false;
118 }