Skip to content

Commit 881b06c

Browse files
author
wyf in c2
committed
[feat] 公告删除编辑
1 parent 51f7238 commit 881b06c

10 files changed

Lines changed: 247 additions & 30 deletions

lib/l10n/app_en.arb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,11 @@
858858
"announcementCreateEmpty": "Content cannot be empty",
859859
"announcementCreateSuccess": "Announcement created",
860860
"announcementCreateFailed": "Failed to create announcement",
861+
"announcementEdit": "Edit Announcement",
862+
"announcementEditHint": "Edit announcement content...",
863+
"announcementEditSuccess": "Announcement updated",
864+
"announcementEditFailed": "Failed to update announcement",
865+
"announcementEditEmpty": "Content cannot be empty",
861866
"announcementDeleteConfirm": "Delete this announcement?",
862867
"announcementDeleteSuccess": "Announcement deleted",
863868
"announcementDeleteFailed": "Failed to delete announcement",

lib/l10n/app_localizations.dart

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4252,6 +4252,36 @@ abstract class AppLocalizations {
42524252
/// **'Failed to create announcement'**
42534253
String get announcementCreateFailed;
42544254

4255+
/// No description provided for @announcementEdit.
4256+
///
4257+
/// In en, this message translates to:
4258+
/// **'Edit Announcement'**
4259+
String get announcementEdit;
4260+
4261+
/// No description provided for @announcementEditHint.
4262+
///
4263+
/// In en, this message translates to:
4264+
/// **'Edit announcement content...'**
4265+
String get announcementEditHint;
4266+
4267+
/// No description provided for @announcementEditSuccess.
4268+
///
4269+
/// In en, this message translates to:
4270+
/// **'Announcement updated'**
4271+
String get announcementEditSuccess;
4272+
4273+
/// No description provided for @announcementEditFailed.
4274+
///
4275+
/// In en, this message translates to:
4276+
/// **'Failed to update announcement'**
4277+
String get announcementEditFailed;
4278+
4279+
/// No description provided for @announcementEditEmpty.
4280+
///
4281+
/// In en, this message translates to:
4282+
/// **'Content cannot be empty'**
4283+
String get announcementEditEmpty;
4284+
42554285
/// No description provided for @announcementDeleteConfirm.
42564286
///
42574287
/// In en, this message translates to:

lib/l10n/app_localizations_en.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2253,6 +2253,21 @@ class AppLocalizationsEn extends AppLocalizations {
22532253
@override
22542254
String get announcementCreateFailed => 'Failed to create announcement';
22552255

2256+
@override
2257+
String get announcementEdit => 'Edit Announcement';
2258+
2259+
@override
2260+
String get announcementEditHint => 'Edit announcement content...';
2261+
2262+
@override
2263+
String get announcementEditSuccess => 'Announcement updated';
2264+
2265+
@override
2266+
String get announcementEditFailed => 'Failed to update announcement';
2267+
2268+
@override
2269+
String get announcementEditEmpty => 'Content cannot be empty';
2270+
22562271
@override
22572272
String get announcementDeleteConfirm => 'Delete this announcement?';
22582273

lib/l10n/app_localizations_och.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2155,6 +2155,21 @@ class AppLocalizationsOch extends AppLocalizations {
21552155
@override
21562156
String get announcementCreateFailed => '發公告未成';
21572157

2158+
@override
2159+
String get announcementEdit => '改公告';
2160+
2161+
@override
2162+
String get announcementEditHint => '改公告之內容…';
2163+
2164+
@override
2165+
String get announcementEditSuccess => '公告已更';
2166+
2167+
@override
2168+
String get announcementEditFailed => '改公告未成';
2169+
2170+
@override
2171+
String get announcementEditEmpty => '內容不可空';
2172+
21582173
@override
21592174
String get announcementDeleteConfirm => '確欲刪除此公告乎?';
21602175

lib/l10n/app_localizations_zh.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2157,6 +2157,21 @@ class AppLocalizationsZh extends AppLocalizations {
21572157
@override
21582158
String get announcementCreateFailed => '发布公告失败';
21592159

2160+
@override
2161+
String get announcementEdit => '编辑公告';
2162+
2163+
@override
2164+
String get announcementEditHint => '编辑公告内容...';
2165+
2166+
@override
2167+
String get announcementEditSuccess => '公告已更新';
2168+
2169+
@override
2170+
String get announcementEditFailed => '更新公告失败';
2171+
2172+
@override
2173+
String get announcementEditEmpty => '内容不能为空';
2174+
21602175
@override
21612176
String get announcementDeleteConfirm => '确认删除此公告?';
21622177

lib/l10n/app_och.arb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,11 @@
825825
"announcementCreateEmpty": "內容不可空",
826826
"announcementCreateSuccess": "公告已發",
827827
"announcementCreateFailed": "發公告未成",
828+
"announcementEdit": "改公告",
829+
"announcementEditHint": "改公告之內容…",
830+
"announcementEditSuccess": "公告已更",
831+
"announcementEditFailed": "改公告未成",
832+
"announcementEditEmpty": "內容不可空",
828833
"announcementDeleteConfirm": "確欲刪除此公告乎?",
829834
"announcementDeleteSuccess": "公告已刪",
830835
"announcementDeleteFailed": "刪公告未成",

lib/l10n/app_zh.arb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,11 @@
826826
"announcementCreateEmpty": "内容不能为空",
827827
"announcementCreateSuccess": "公告已发布",
828828
"announcementCreateFailed": "发布公告失败",
829+
"announcementEdit": "编辑公告",
830+
"announcementEditHint": "编辑公告内容...",
831+
"announcementEditSuccess": "公告已更新",
832+
"announcementEditFailed": "更新公告失败",
833+
"announcementEditEmpty": "内容不能为空",
829834
"announcementDeleteConfirm": "确认删除此公告?",
830835
"announcementDeleteSuccess": "公告已删除",
831836
"announcementDeleteFailed": "删除公告失败",

lib/screens/announcement_screen.dart

Lines changed: 109 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import '../services/api/tf_api_client.dart';
66
import '../services/auth_state.dart';
77
import '../services/notification_service.dart';
88
import '../utils/talker.dart';
9+
import '../widgets/markdown_renderer.dart';
910

1011
class AnnouncementScreen extends StatefulWidget {
1112
const AnnouncementScreen({super.key});
@@ -143,6 +144,68 @@ class _AnnouncementScreenState extends State<AnnouncementScreen> {
143144
}
144145
}
145146

147+
Future<void> _showEditDialog(Announcement a) async {
148+
final l10n = AppLocalizations.of(context)!;
149+
final controller = TextEditingController(text: a.content);
150+
final ok = await showDialog<bool>(
151+
context: context,
152+
builder: (ctx) => AlertDialog(
153+
title: Text(l10n.announcementEdit),
154+
content: TextField(
155+
controller: controller,
156+
maxLines: 5,
157+
decoration: InputDecoration(
158+
hintText: l10n.announcementEditHint,
159+
border: const OutlineInputBorder(),
160+
),
161+
),
162+
actions: [
163+
TextButton(
164+
onPressed: () => Navigator.pop(ctx, false),
165+
child: Text(l10n.cancel),
166+
),
167+
FilledButton(
168+
onPressed: () => Navigator.pop(ctx, true),
169+
child: Text(l10n.confirm),
170+
),
171+
],
172+
),
173+
);
174+
if (ok != true) return;
175+
176+
final content = controller.text.trim();
177+
if (mounted) {
178+
if (content.isEmpty) {
179+
ScaffoldMessenger.of(
180+
context,
181+
).showSnackBar(SnackBar(content: Text(l10n.announcementEditEmpty)));
182+
return;
183+
}
184+
final uid = AuthState.instance.uid;
185+
final password = AuthState.instance.password;
186+
if (uid == null || password == null) return;
187+
final success = await TfApiClient.instance.editAnnouncement(
188+
uid,
189+
password,
190+
a.timeStamp,
191+
content,
192+
);
193+
if (mounted) {
194+
final l10n = AppLocalizations.of(context)!;
195+
ScaffoldMessenger.of(context).showSnackBar(
196+
SnackBar(
197+
content: Text(
198+
success
199+
? l10n.announcementEditSuccess
200+
: l10n.announcementEditFailed,
201+
),
202+
),
203+
);
204+
if (success) _load();
205+
}
206+
}
207+
}
208+
146209
Future<void> _confirmDelete(Announcement a) async {
147210
final l10n = AppLocalizations.of(context)!;
148211
final ok = await showDialog<bool>(
@@ -289,6 +352,7 @@ class _AnnouncementScreenState extends State<AnnouncementScreen> {
289352
itemBuilder: (context, index) => _AnnouncementCard(
290353
announcement: list[index],
291354
isAdmin: _isAdmin,
355+
onEdit: () => _showEditDialog(list[index]),
292356
onDelete: () => _confirmDelete(list[index]),
293357
),
294358
),
@@ -387,11 +451,13 @@ class _AnnouncementNotificationSheet extends StatelessWidget {
387451
class _AnnouncementCard extends StatelessWidget {
388452
final Announcement announcement;
389453
final bool isAdmin;
454+
final VoidCallback onEdit;
390455
final VoidCallback onDelete;
391456

392457
const _AnnouncementCard({
393458
required this.announcement,
394459
required this.isAdmin,
460+
required this.onEdit,
395461
required this.onDelete,
396462
});
397463

@@ -404,42 +470,55 @@ class _AnnouncementCard extends StatelessWidget {
404470

405471
return Card(
406472
margin: const EdgeInsets.symmetric(vertical: 6),
407-
child: InkWell(
408-
borderRadius: BorderRadius.circular(12),
409-
onLongPress: isAdmin ? onDelete : null,
410-
child: Padding(
411-
padding: const EdgeInsets.all(20),
412-
child: Column(
413-
crossAxisAlignment: CrossAxisAlignment.start,
414-
children: [
415-
Row(
416-
children: [
417-
Icon(
418-
Icons.campaign_outlined,
419-
size: 24,
473+
child: Padding(
474+
padding: const EdgeInsets.all(20),
475+
child: Column(
476+
crossAxisAlignment: CrossAxisAlignment.start,
477+
children: [
478+
Row(
479+
children: [
480+
Icon(
481+
Icons.campaign_outlined,
482+
size: 24,
483+
color: theme.colorScheme.primary,
484+
),
485+
const SizedBox(width: 10),
486+
Text(
487+
senderLabel,
488+
style: theme.textTheme.titleSmall?.copyWith(
420489
color: theme.colorScheme.primary,
490+
fontWeight: FontWeight.w600,
421491
),
422-
const SizedBox(width: 10),
423-
Text(
424-
senderLabel,
425-
style: theme.textTheme.titleSmall?.copyWith(
426-
color: theme.colorScheme.primary,
427-
fontWeight: FontWeight.w600,
428-
),
492+
),
493+
const Spacer(),
494+
if (isAdmin) ...[
495+
IconButton(
496+
icon: const Icon(Icons.edit_outlined),
497+
tooltip: '编辑公告',
498+
iconSize: 18,
499+
onPressed: onEdit,
429500
),
430-
const Spacer(),
431-
Text(
432-
timeLabel,
433-
style: theme.textTheme.bodyMedium?.copyWith(
434-
color: theme.colorScheme.onSurfaceVariant,
501+
IconButton(
502+
icon: Icon(
503+
Icons.delete_outline,
504+
color: theme.colorScheme.error,
435505
),
506+
tooltip: '删除公告',
507+
iconSize: 18,
508+
onPressed: onDelete,
436509
),
437510
],
438-
),
439-
const SizedBox(height: 12),
440-
Text(announcement.content, style: theme.textTheme.bodyLarge),
441-
],
442-
),
511+
Text(
512+
timeLabel,
513+
style: theme.textTheme.bodyMedium?.copyWith(
514+
color: theme.colorScheme.onSurfaceVariant,
515+
),
516+
),
517+
],
518+
),
519+
const SizedBox(height: 12),
520+
MarkdownRenderer(data: announcement.content),
521+
],
443522
),
444523
),
445524
);

lib/services/api/tf_api_client.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,6 +1624,21 @@ class TfApiClient {
16241624
return _parseBool(result);
16251625
}
16261626

1627+
Future<bool> editAnnouncement(
1628+
int uid,
1629+
String password,
1630+
String timeStamp,
1631+
String content,
1632+
) async {
1633+
final result = await secretPost(
1634+
'/announcement/edit_announcement',
1635+
{'time_stamp': timeStamp, 'content': content},
1636+
uid: uid,
1637+
password: password,
1638+
);
1639+
return _parseBool(result);
1640+
}
1641+
16271642
Future<bool> deleteAnnouncement(
16281643
int uid,
16291644
String password,

lib/widgets/markdown_renderer.dart

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,39 @@ class MarkdownRenderer extends HookWidget {
150150
fontSize: 14,
151151
),
152152
decoration: codeBlockDecoration,
153+
builder: (code, language) => Container(
154+
decoration: codeBlockDecoration,
155+
margin: const EdgeInsets.symmetric(vertical: 8),
156+
padding: const EdgeInsets.all(16),
157+
width: double.infinity,
158+
child: SingleChildScrollView(
159+
scrollDirection: Axis.horizontal,
160+
child: RichText(
161+
text: TextSpan(
162+
style: TextStyle(
163+
fontFamily: 'monospace',
164+
fontSize: 14,
165+
color: isDark ? const Color(0xffd6d6d6) : const Color(0xff24292f),
166+
),
167+
children: highLightSpans(
168+
code,
169+
language: language.isEmpty ? null : language,
170+
theme: isDark ? a11yDarkTheme : a11yLightTheme,
171+
textStyle: TextStyle(
172+
fontFamily: 'monospace',
173+
fontSize: 14,
174+
color: isDark ? const Color(0xffd6d6d6) : const Color(0xff24292f),
175+
),
176+
styleNotMatched: TextStyle(
177+
fontFamily: 'monospace',
178+
fontSize: 14,
179+
color: isDark ? const Color(0xffd6d6d6) : const Color(0xff24292f),
180+
),
181+
),
182+
),
183+
),
184+
),
185+
),
153186
),
154187
TableConfig(
155188
wrapper: (child) => SingleChildScrollView(

0 commit comments

Comments
 (0)