✨ Notification card
🐛 Fix post item truncate hint from overflowing
			
			
This commit is contained in:
		| @@ -274,6 +274,12 @@ | |||||||
|   "attachmentUnsetAsPostThumbnail": "Unset as post thumbnail", |   "attachmentUnsetAsPostThumbnail": "Unset as post thumbnail", | ||||||
|   "attachmentSetThumbnail": "Set thumbnail", |   "attachmentSetThumbnail": "Set thumbnail", | ||||||
|   "attachmentUpload": "Upload", |   "attachmentUpload": "Upload", | ||||||
|  |   "notification": "Notification", | ||||||
|  |   "notificationUnreadCount": { | ||||||
|  |     "zero": "All notifications read", | ||||||
|  |     "one": "{} unread notification", | ||||||
|  |     "other": "{} unread notifications" | ||||||
|  |   }, | ||||||
|   "notificationUnread": "Unread", |   "notificationUnread": "Unread", | ||||||
|   "notificationRead": "Read", |   "notificationRead": "Read", | ||||||
|   "notificationMarkAllRead": "Mark all notifications as read", |   "notificationMarkAllRead": "Mark all notifications as read", | ||||||
|   | |||||||
| @@ -272,6 +272,12 @@ | |||||||
|   "attachmentUnsetAsPostThumbnail": "取消设置为帖子缩略图", |   "attachmentUnsetAsPostThumbnail": "取消设置为帖子缩略图", | ||||||
|   "attachmentSetThumbnail": "设置缩略图", |   "attachmentSetThumbnail": "设置缩略图", | ||||||
|   "attachmentUpload": "上传", |   "attachmentUpload": "上传", | ||||||
|  |   "notification": "通知", | ||||||
|  |   "notificationUnreadCount": { | ||||||
|  |     "zero": "无未读通知", | ||||||
|  |     "one": "有 {} 个未读通知", | ||||||
|  |     "other": "有 {} 个未读通知" | ||||||
|  |   }, | ||||||
|   "notificationUnread": "未读", |   "notificationUnread": "未读", | ||||||
|   "notificationRead": "已读", |   "notificationRead": "已读", | ||||||
|   "notificationMarkAllRead": "已读所有通知", |   "notificationMarkAllRead": "已读所有通知", | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ import 'dart:math' as math; | |||||||
| import 'package:easy_localization/easy_localization.dart'; | import 'package:easy_localization/easy_localization.dart'; | ||||||
| import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; | import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; | ||||||
| import 'package:gap/gap.dart'; | import 'package:gap/gap.dart'; | ||||||
|  | import 'package:go_router/go_router.dart'; | ||||||
| import 'package:google_fonts/google_fonts.dart'; | import 'package:google_fonts/google_fonts.dart'; | ||||||
| import 'package:material_symbols_icons/symbols.dart'; | import 'package:material_symbols_icons/symbols.dart'; | ||||||
| import 'package:provider/provider.dart'; | import 'package:provider/provider.dart'; | ||||||
| @@ -40,6 +41,10 @@ class _HomeScreenState extends State<HomeScreen> { | |||||||
|       name: 'dashEntryCheckIn', |       name: 'dashEntryCheckIn', | ||||||
|       child: _HomeDashCheckInWidget(), |       child: _HomeDashCheckInWidget(), | ||||||
|     ), |     ), | ||||||
|  |     HomeScreenDashEntry( | ||||||
|  |       name: 'dashEntryNotification', | ||||||
|  |       child: _HomeDashNotificationWidget(), | ||||||
|  |     ), | ||||||
|   ]; |   ]; | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
| @@ -52,16 +57,12 @@ class _HomeScreenState extends State<HomeScreen> { | |||||||
|       body: LayoutBuilder( |       body: LayoutBuilder( | ||||||
|         builder: (context, constraints) { |         builder: (context, constraints) { | ||||||
|           return Align( |           return Align( | ||||||
|             alignment: constraints.maxWidth > 640 |             alignment: constraints.maxWidth > 640 ? Alignment.center : Alignment.topCenter, | ||||||
|                 ? Alignment.center |  | ||||||
|                 : Alignment.topCenter, |  | ||||||
|             child: Container( |             child: Container( | ||||||
|               constraints: const BoxConstraints(maxWidth: 640), |               constraints: const BoxConstraints(maxWidth: 640), | ||||||
|               child: SingleChildScrollView( |               child: SingleChildScrollView( | ||||||
|                 child: Column( |                 child: Column( | ||||||
|                   mainAxisAlignment: constraints.maxWidth > 640 |                   mainAxisAlignment: constraints.maxWidth > 640 ? MainAxisAlignment.center : MainAxisAlignment.start, | ||||||
|                       ? MainAxisAlignment.center |  | ||||||
|                       : MainAxisAlignment.start, |  | ||||||
|                   children: [ |                   children: [ | ||||||
|                     if (constraints.maxWidth <= 640) const Gap(8), |                     if (constraints.maxWidth <= 640) const Gap(8), | ||||||
|                     _HomeDashSpecialDayWidget().padding(top: 8, horizontal: 8), |                     _HomeDashSpecialDayWidget().padding(top: 8, horizontal: 8), | ||||||
| @@ -96,9 +97,7 @@ class _HomeDashSpecialDayWidget extends StatelessWidget { | |||||||
|     final ua = context.watch<UserProvider>(); |     final ua = context.watch<UserProvider>(); | ||||||
|     final today = DateTime.now(); |     final today = DateTime.now(); | ||||||
|     final birthday = ua.user?.profile?.birthday?.toLocal(); |     final birthday = ua.user?.profile?.birthday?.toLocal(); | ||||||
|     final isBirthday = birthday != null && |     final isBirthday = birthday != null && birthday.day == today.day && birthday.month == today.month; | ||||||
|         birthday.day == today.day && |  | ||||||
|         birthday.month == today.month; |  | ||||||
|     return Column( |     return Column( | ||||||
|       children: [ |       children: [ | ||||||
|         if (isBirthday) |         if (isBirthday) | ||||||
| @@ -154,20 +153,15 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   Widget _buildDetailChunk(int index, bool positive) { |   Widget _buildDetailChunk(int index, bool positive) { | ||||||
|     final prefix = |     final prefix = positive ? 'dailyCheckPositiveHint' : 'dailyCheckNegativeHint'; | ||||||
|         positive ? 'dailyCheckPositiveHint' : 'dailyCheckNegativeHint'; |     final mod = positive ? kSuggestionPositiveHintCount : kSuggestionNegativeHintCount; | ||||||
|     final mod = |  | ||||||
|         positive ? kSuggestionPositiveHintCount : kSuggestionNegativeHintCount; |  | ||||||
|     final pos = math.max(1, _todayRecord!.resultModifiers[index] % mod); |     final pos = math.max(1, _todayRecord!.resultModifiers[index] % mod); | ||||||
|     return Column( |     return Column( | ||||||
|       crossAxisAlignment: CrossAxisAlignment.start, |       crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|       children: [ |       children: [ | ||||||
|         Text( |         Text( | ||||||
|           prefix.tr(args: ['$prefix$pos'.tr()]), |           prefix.tr(args: ['$prefix$pos'.tr()]), | ||||||
|           style: Theme.of(context) |           style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold), | ||||||
|               .textTheme |  | ||||||
|               .titleMedium! |  | ||||||
|               .copyWith(fontWeight: FontWeight.bold), |  | ||||||
|         ).tr(), |         ).tr(), | ||||||
|         Text( |         Text( | ||||||
|           '$prefix${pos}Description', |           '$prefix${pos}Description', | ||||||
| @@ -202,10 +196,7 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> { | |||||||
|               else |               else | ||||||
|                 Text( |                 Text( | ||||||
|                   'dailyCheckEverythingIsNegative', |                   'dailyCheckEverythingIsNegative', | ||||||
|                   style: Theme.of(context) |                   style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold), | ||||||
|                       .textTheme |  | ||||||
|                       .titleMedium! |  | ||||||
|                       .copyWith(fontWeight: FontWeight.bold), |  | ||||||
|                 ).tr(), |                 ).tr(), | ||||||
|               const Gap(8), |               const Gap(8), | ||||||
|               if (_todayRecord?.resultTier != 4) |               if (_todayRecord?.resultTier != 4) | ||||||
| @@ -221,10 +212,7 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> { | |||||||
|               else |               else | ||||||
|                 Text( |                 Text( | ||||||
|                   'dailyCheckEverythingIsPositive', |                   'dailyCheckEverythingIsPositive', | ||||||
|                   style: Theme.of(context) |                   style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold), | ||||||
|                       .textTheme |  | ||||||
|                       .titleMedium! |  | ||||||
|                       .copyWith(fontWeight: FontWeight.bold), |  | ||||||
|                 ).tr(), |                 ).tr(), | ||||||
|             ], |             ], | ||||||
|           ), |           ), | ||||||
| @@ -338,14 +326,28 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| class _HomeDashLinkWidget extends StatelessWidget { | class _HomeDashNotificationWidget extends StatefulWidget { | ||||||
|   final String title; |   const _HomeDashNotificationWidget({super.key}); | ||||||
|   final String subtitle; |  | ||||||
|   const _HomeDashLinkWidget({ |   @override | ||||||
|     super.key, |   State<_HomeDashNotificationWidget> createState() => _HomeDashNotificationWidgetState(); | ||||||
|     required this.title, | } | ||||||
|     required this.subtitle, |  | ||||||
|   }); | class _HomeDashNotificationWidgetState extends State<_HomeDashNotificationWidget> { | ||||||
|  |   int? _count; | ||||||
|  |  | ||||||
|  |   Future<void> _fetchNotificationCount() async { | ||||||
|  |     final sn = context.read<SnNetworkProvider>(); | ||||||
|  |     final resp = await sn.client.get('/cgi/id/notifications/count'); | ||||||
|  |     _count = resp.data['count']; | ||||||
|  |     setState(() {}); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   void initState() { | ||||||
|  |     super.initState(); | ||||||
|  |     _fetchNotificationCount(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
| @@ -358,11 +360,11 @@ class _HomeDashLinkWidget extends StatelessWidget { | |||||||
|               crossAxisAlignment: CrossAxisAlignment.start, |               crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|               children: [ |               children: [ | ||||||
|                 Text( |                 Text( | ||||||
|                   title, |                   'notification', | ||||||
|                   style: Theme.of(context).textTheme.titleLarge, |                   style: Theme.of(context).textTheme.titleLarge, | ||||||
|                 ), |                 ).tr(), | ||||||
|                 Text( |                 Text( | ||||||
|                   subtitle, |                   _count == null ? 'loading'.tr() : 'notificationUnreadCount'.plural(_count ?? 0), | ||||||
|                   style: Theme.of(context).textTheme.bodyLarge, |                   style: Theme.of(context).textTheme.bodyLarge, | ||||||
|                 ), |                 ), | ||||||
|               ], |               ], | ||||||
| @@ -377,7 +379,9 @@ class _HomeDashLinkWidget extends StatelessWidget { | |||||||
|               ), |               ), | ||||||
|               child: IconButton( |               child: IconButton( | ||||||
|                 icon: const Icon(Symbols.arrow_right_alt), |                 icon: const Icon(Symbols.arrow_right_alt), | ||||||
|                 onPressed: () {}, |                 onPressed: () { | ||||||
|  |                   GoRouter.of(context).goNamed('notification'); | ||||||
|  |                 }, | ||||||
|               ), |               ), | ||||||
|             ), |             ), | ||||||
|           ) |           ) | ||||||
|   | |||||||
| @@ -1,8 +1,4 @@ | |||||||
| import 'dart:io'; |  | ||||||
|  |  | ||||||
| import 'package:flutter/foundation.dart'; |  | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:path_provider/path_provider.dart'; |  | ||||||
| import 'package:shared_preferences/shared_preferences.dart'; | import 'package:shared_preferences/shared_preferences.dart'; | ||||||
|  |  | ||||||
| const kMaterialYouToggleStoreKey = 'app_theme_material_you'; | const kMaterialYouToggleStoreKey = 'app_theme_material_you'; | ||||||
|   | |||||||
| @@ -60,8 +60,7 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> { | |||||||
|               ], |               ], | ||||||
|             ).padding( |             ).padding( | ||||||
|               horizontal: 32, |               horizontal: 32, | ||||||
|               top: MediaQuery.of(context).padding.top + 12, |               vertical: 12, | ||||||
|               bottom: 12, |  | ||||||
|             ), |             ), | ||||||
|             ...destinations.where((ele) => ele.isPinned).map((ele) { |             ...destinations.where((ele) => ele.isPinned).map((ele) { | ||||||
|               return NavigationDrawerDestination( |               return NavigationDrawerDestination( | ||||||
|   | |||||||
| @@ -720,7 +720,9 @@ class _PostTruncatedHint extends StatelessWidget { | |||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     return Row( |     return SingleChildScrollView( | ||||||
|  |       scrollDirection: Axis.horizontal, | ||||||
|  |       child: Row( | ||||||
|         children: [ |         children: [ | ||||||
|           if (data.body['content_length'] != null) |           if (data.body['content_length'] != null) | ||||||
|             Row( |             Row( | ||||||
| @@ -745,7 +747,8 @@ class _PostTruncatedHint extends StatelessWidget { | |||||||
|               ], |               ], | ||||||
|             ), |             ), | ||||||
|         ], |         ], | ||||||
|     ).opacity(0.75); |       ).opacity(0.75), | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user