Skip to content

Commit fd1ef75

Browse files
committed
Update Telegram settings form with validation and improved UI elements for better user experience
1 parent 314531d commit fd1ef75

File tree

2 files changed

+216
-71
lines changed

2 files changed

+216
-71
lines changed

lib/app_config.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class AppConfig {
5656

5757
static String get fontFamily {
5858
final fontFamily = UserSession().settings(AppConstants.fontFamily);
59-
return fontFamily ?? 'Varela';
59+
return fontFamily ?? 'Noto Sans';
6060
}
6161

6262
static String get version {

lib/screens/settings/add_telegram_setting.dart

Lines changed: 215 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ class AddTelegramSettingState extends State<AddTelegramSetting> {
1717
final TextEditingController _tokenController = TextEditingController();
1818
final TextEditingController _remarkController = TextEditingController();
1919
final TextEditingController _tagController = TextEditingController();
20+
final _formKey = GlobalKey<FormState>();
2021
int _syncType = 3;
22+
bool _isLoading = false;
2123
final TelegramSyncSettingsController _settingsController = locator<TelegramSyncSettingsController>();
2224

2325
@override
@@ -29,87 +31,230 @@ class AddTelegramSettingState extends State<AddTelegramSetting> {
2931
}
3032
}
3133

34+
@override
35+
void dispose() {
36+
_channelIdController.dispose();
37+
_channelNameController.dispose();
38+
_tokenController.dispose();
39+
_remarkController.dispose();
40+
_tagController.dispose();
41+
super.dispose();
42+
}
43+
44+
Widget _buildInputField({
45+
required TextEditingController controller,
46+
required String label,
47+
String? hint,
48+
String? Function(String?)? validator,
49+
bool obscureText = false,
50+
TextInputType? keyboardType,
51+
int maxLines = 1,
52+
}) {
53+
return Padding(
54+
padding: const EdgeInsets.only(bottom: 16),
55+
child: TextFormField(
56+
controller: controller,
57+
obscureText: obscureText,
58+
maxLines: maxLines,
59+
keyboardType: keyboardType,
60+
decoration: InputDecoration(
61+
labelText: label,
62+
hintText: hint,
63+
border: const OutlineInputBorder(),
64+
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
65+
errorMaxLines: 2,
66+
),
67+
validator: validator ?? (value) {
68+
if (value == null || value.trim().isEmpty) {
69+
return '$label is required';
70+
}
71+
return null;
72+
},
73+
),
74+
);
75+
}
76+
77+
Future<void> _saveSetting() async {
78+
if (!_formKey.currentState!.validate()) return;
79+
80+
setState(() => _isLoading = true);
81+
82+
try {
83+
await _settingsController.addTelegramSetting(
84+
TelegramSettings(
85+
syncType: _syncType,
86+
syncValue: _syncType == 4 ? _tagController.text : '',
87+
channelId: _channelIdController.text.trim(),
88+
channelName: _channelNameController.text.trim(),
89+
tokenRemark: _remarkController.text.trim(),
90+
encryptedToken: _tokenController.text.trim(),
91+
),
92+
);
93+
if (mounted) {
94+
Navigator.of(context).pop();
95+
}
96+
} catch (e) {
97+
if (mounted) {
98+
ScaffoldMessenger.of(context).showSnackBar(
99+
SnackBar(
100+
content: Text('Failed to save settings: ${e.toString()}'),
101+
backgroundColor: Colors.red,
102+
),
103+
);
104+
}
105+
} finally {
106+
if (mounted) {
107+
setState(() => _isLoading = false);
108+
}
109+
}
110+
}
111+
32112
@override
33113
Widget build(BuildContext context) {
34114
return Scaffold(
35115
appBar: AppBar(
36116
title: const Text('Add Sync Setting - Telegram'),
37-
),
38-
body: Padding(
39-
padding: const EdgeInsets.all(16.0),
40-
child: Column(
41-
crossAxisAlignment: CrossAxisAlignment.start,
42-
children: [
43-
const Padding(
44-
padding: EdgeInsets.symmetric(vertical: 8.0),
45-
child: Text(
46-
'Source Note',
47-
style: TextStyle(fontSize: 16, color: Colors.black54),
117+
actions: [
118+
if (_isLoading)
119+
const Center(
120+
child: Padding(
121+
padding: EdgeInsets.symmetric(horizontal: 16.0),
122+
child: SizedBox(
123+
width: 20,
124+
height: 20,
125+
child: CircularProgressIndicator(
126+
strokeWidth: 2,
127+
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
128+
),
129+
),
48130
),
49131
),
50-
DropdownButton<int>(
51-
value: _syncType,
52-
onChanged: (int? newValue) {
53-
setState(() {
54-
_syncType = newValue!;
55-
});
56-
},
57-
items: const [
58-
DropdownMenuItem<int>(
59-
value: 1,
60-
child: Text('Public'),
61-
),
62-
DropdownMenuItem<int>(
63-
value: 2,
64-
child: Text('Private'),
65-
),
66-
DropdownMenuItem<int>(
67-
value: 3,
68-
child: Text('All'),
132+
],
133+
),
134+
body: Form(
135+
key: _formKey,
136+
child: SingleChildScrollView(
137+
padding: const EdgeInsets.all(16),
138+
child: Column(
139+
crossAxisAlignment: CrossAxisAlignment.start,
140+
children: [
141+
Card(
142+
child: Padding(
143+
padding: const EdgeInsets.all(16),
144+
child: Column(
145+
crossAxisAlignment: CrossAxisAlignment.start,
146+
children: [
147+
const Text(
148+
'Sync Settings',
149+
style: TextStyle(
150+
fontSize: 18,
151+
fontWeight: FontWeight.bold,
152+
),
153+
),
154+
const SizedBox(height: 16),
155+
DropdownButtonFormField<int>(
156+
decoration: const InputDecoration(
157+
labelText: 'Source Note',
158+
border: OutlineInputBorder(),
159+
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
160+
),
161+
value: _syncType,
162+
items: const [
163+
DropdownMenuItem<int>(
164+
value: 1,
165+
child: Text('Public Notes'),
166+
),
167+
DropdownMenuItem<int>(
168+
value: 2,
169+
child: Text('Private Notes'),
170+
),
171+
DropdownMenuItem<int>(
172+
value: 3,
173+
child: Text('All Notes (Public + Private)'),
174+
),
175+
DropdownMenuItem<int>(
176+
value: 4,
177+
child: Text('Notes with Specific Tag'),
178+
),
179+
],
180+
onChanged: (value) => setState(() => _syncType = value!),
181+
validator: (value) {
182+
if (value == null) {
183+
return 'Please select a source note type';
184+
}
185+
return null;
186+
},
187+
),
188+
const SizedBox(height: 16),
189+
if (_syncType == 4)
190+
_buildInputField(
191+
controller: _tagController,
192+
label: 'Tag',
193+
hint: 'Enter tag text, such as "memories"',
194+
validator: (value) {
195+
if (_syncType == 4 && (value == null || value.trim().isEmpty)) {
196+
return 'Tag is required for tag-based sync';
197+
}
198+
return null;
199+
},
200+
),
201+
_buildInputField(
202+
controller: _channelIdController,
203+
label: 'Channel ID',
204+
hint: 'Telegram channel ID',
205+
keyboardType: TextInputType.text,
206+
),
207+
_buildInputField(
208+
controller: _channelNameController,
209+
label: 'Channel Name',
210+
hint: 'Channel name that helps you remember',
211+
keyboardType: TextInputType.text,
212+
),
213+
_buildInputField(
214+
controller: _tokenController,
215+
label: 'Telegram Bot Token',
216+
hint: 'Your bot token',
217+
obscureText: false,
218+
),
219+
_buildInputField(
220+
controller: _remarkController,
221+
label: 'Token Remark',
222+
hint: 'Add a description for this token',
223+
maxLines: 2,
224+
),
225+
],
226+
),
69227
),
70-
DropdownMenuItem<int>(
71-
value: 4,
72-
child: Text('Tag'),
228+
),
229+
const SizedBox(height: 24),
230+
SizedBox(
231+
width: double.infinity,
232+
child: ElevatedButton(
233+
style: ElevatedButton.styleFrom(
234+
padding: const EdgeInsets.symmetric(vertical: 16),
235+
backgroundColor: Theme.of(context).primaryColor,
236+
foregroundColor: Colors.white,
237+
),
238+
onPressed: _isLoading ? null : _saveSetting,
239+
child: _isLoading
240+
? const SizedBox(
241+
height: 20,
242+
width: 20,
243+
child: CircularProgressIndicator(
244+
strokeWidth: 2,
245+
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
246+
),
247+
)
248+
: const Text(
249+
'Save Settings',
250+
style: TextStyle(fontSize: 16),
251+
),
73252
),
74-
],
75-
),
76-
if (_syncType == 4)
77-
TextField(
78-
controller: _tagController,
79-
decoration: const InputDecoration(labelText: 'Tag'),
80253
),
81-
TextField(
82-
controller: _channelIdController,
83-
decoration: const InputDecoration(labelText: 'Channel ID'),
84-
),
85-
TextField(
86-
controller: _channelNameController,
87-
decoration: const InputDecoration(labelText: 'Channel Name'),
88-
),
89-
TextField(
90-
controller: _tokenController,
91-
decoration: const InputDecoration(labelText: 'Telegram Bot Token'),
92-
),
93-
TextField(
94-
controller: _remarkController,
95-
decoration: const InputDecoration(labelText: 'Token Remark'),
96-
),
97-
const SizedBox(height: 20),
98-
ElevatedButton(
99-
onPressed: () {
100-
_settingsController.addTelegramSetting(TelegramSettings(
101-
syncType: _syncType,
102-
syncValue: _syncType == 4 ? _tagController.text : '',
103-
channelId: _channelIdController.text,
104-
channelName: _channelNameController.text,
105-
tokenRemark: _remarkController.text,
106-
encryptedToken: _tokenController.text)).then(Navigator.of(context).pop);
107-
},
108-
child: const Text('Save'),
109-
),
110-
],
254+
],
255+
),
111256
),
112257
),
113258
);
114259
}
115-
}
260+
}

0 commit comments

Comments
 (0)