diff --git a/CHANGELOG.md b/CHANGELOG.md index e67cc9d8..c5248b5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Changed - 不再显示被设置为不显示的html节点。 +- 尝试在首页和我的页面内提醒有未读的消息。 ## [0.5.1] - 2024-01-31 diff --git a/lib/features/homepage/bloc/homepage_bloc.dart b/lib/features/homepage/bloc/homepage_bloc.dart index 8e8c5cd9..52849858 100644 --- a/lib/features/homepage/bloc/homepage_bloc.dart +++ b/lib/features/homepage/bloc/homepage_bloc.dart @@ -185,6 +185,12 @@ class HomepageBloc extends Bloc { // Style 2: With welcome text document.querySelector('div#chart > script'); + final hasUnreadNotice = + document.querySelector('a#myprompt')?.classes.contains('new') ?? false; + + final hasUnreadMessage = + document.querySelector('a#pm_ntc')?.classes.contains('new') ?? false; + final picUrlList = _buildKahrpbaPicUrlList(styleNode).whereType().toList(); final picHrefList = @@ -282,6 +288,8 @@ class HomepageBloc extends Bloc { loggedUserInfo: loggedUserInfo, pinnedThreadGroupList: pinnedThreadGroupList, swiperUrlList: swiperUrlList, + hasUnreadNotice: hasUnreadNotice, + hasUnreadMessage: hasUnreadMessage, ); } diff --git a/lib/features/homepage/bloc/homepage_state.dart b/lib/features/homepage/bloc/homepage_state.dart index cf28be91..58ad741f 100644 --- a/lib/features/homepage/bloc/homepage_state.dart +++ b/lib/features/homepage/bloc/homepage_state.dart @@ -44,6 +44,8 @@ final class HomepageState extends Equatable { this.loggedUserInfo, this.pinnedThreadGroupList = const [], this.swiperUrlList = const [], + this.hasUnreadNotice = false, + this.hasUnreadMessage = false, }); /// Loading status. @@ -63,6 +65,12 @@ final class HomepageState extends Equatable { /// Swiper urls in the homepage. final List swiperUrlList; + /// Flag indicating has unread notices or not. + final bool hasUnreadNotice; + + /// Flag indicating has unread messages or not. + final bool hasUnreadMessage; + /// Copy with HomepageState copyWith({ HomepageStatus? status, @@ -71,6 +79,8 @@ final class HomepageState extends Equatable { List? pinnedThreadGroupList, List? swiperUrlList, int? documentHashCode, + bool? hasUnreadNotice, + bool? hasUnreadMessage, }) { return HomepageState( status: status ?? this.status, @@ -79,6 +89,8 @@ final class HomepageState extends Equatable { pinnedThreadGroupList: pinnedThreadGroupList ?? this.pinnedThreadGroupList, swiperUrlList: swiperUrlList ?? this.swiperUrlList, + hasUnreadNotice: hasUnreadNotice ?? this.hasUnreadNotice, + hasUnreadMessage: hasUnreadMessage ?? this.hasUnreadMessage, ); } @@ -89,5 +101,6 @@ final class HomepageState extends Equatable { loggedUserInfo, pinnedThreadGroupList, swiperUrlList, + hasUnreadNotice, ]; } diff --git a/lib/features/homepage/widgets/welcome_section.dart b/lib/features/homepage/widgets/welcome_section.dart index 5be87c09..baaf0f1b 100644 --- a/lib/features/homepage/widgets/welcome_section.dart +++ b/lib/features/homepage/widgets/welcome_section.dart @@ -8,6 +8,7 @@ import 'package:tsdm_client/constants/url.dart'; import 'package:tsdm_client/extensions/string.dart'; import 'package:tsdm_client/features/authentication/repository/authentication_repository.dart'; import 'package:tsdm_client/features/home/cubit/home_cubit.dart'; +import 'package:tsdm_client/features/homepage/bloc/homepage_bloc.dart'; import 'package:tsdm_client/features/homepage/models/models.dart'; import 'package:tsdm_client/routes/screen_paths.dart'; import 'package:tsdm_client/utils/debug.dart'; @@ -154,6 +155,10 @@ class WelcomeSection extends StatelessWidget { final needExpand = ResponsiveBreakpoints.of(context) .largerOrEqualTo('homepage_welcome_expand'); + final homePageState = context.read().state; + final hasUnreadInfo = + homePageState.hasUnreadMessage || homePageState.hasUnreadNotice; + return ConstrainedBox( constraints: BoxConstraints( maxHeight: needExpand ? _kahrpbaPicHeight : _kahrpbaPicHeight * 2 + 20, @@ -203,7 +208,11 @@ class WelcomeSection extends StatelessWidget { ), Expanded(child: Container()), IconButton( - icon: const Icon(Icons.notifications_outlined), + icon: hasUnreadInfo + ? const Badge( + child: Icon(Icons.notifications_outlined), + ) + : const Icon(Icons.notifications_outlined), onPressed: () async { await context.pushNamed(ScreenPaths.notice); }, diff --git a/lib/features/profile/bloc/profile_bloc.dart b/lib/features/profile/bloc/profile_bloc.dart index fcfe279c..b1489d18 100644 --- a/lib/features/profile/bloc/profile_bloc.dart +++ b/lib/features/profile/bloc/profile_bloc.dart @@ -45,10 +45,14 @@ class ProfileBloc extends Bloc { event.uid == null && _profileRepository.hasCache()) { final userProfile = _buildProfile(_profileRepository.getCache()!); + final (hasUnreadNotice, hasUnreadMessage) = + _buildUnreadInfoStatus(_profileRepository.getCache()!); emit( state.copyWith( status: ProfileStatus.success, userProfile: userProfile, + hasUnreadNotice: hasUnreadNotice, + hasUnreadMessage: hasUnreadMessage, ), ); return; @@ -69,10 +73,14 @@ class ProfileBloc extends Bloc { emit(state.copyWith(status: ProfileStatus.failed)); return; } + final (hasUnreadNotice, hasUnreadMessage) = + _buildUnreadInfoStatus(document); emit( state.copyWith( status: ProfileStatus.success, userProfile: userProfile, + hasUnreadNotice: hasUnreadNotice, + hasUnreadMessage: hasUnreadMessage, ), ); } on HttpRequestFailedException catch (e) { @@ -98,10 +106,14 @@ class ProfileBloc extends Bloc { emit(state.copyWith(status: ProfileStatus.failed)); return; } + final (hasUnreadNotice, hasUnreadMessage) = + _buildUnreadInfoStatus(document); emit( state.copyWith( status: ProfileStatus.success, userProfile: userProfile, + hasUnreadNotice: hasUnreadNotice, + hasUnreadMessage: hasUnreadMessage, ), ); } on HttpRequestFailedException catch (e) { @@ -222,4 +234,16 @@ class ProfileBloc extends Bloc { activityInfoList: activityInfoList, ); } + + (bool hasUnreadNotice, bool hasUnreadMessage) _buildUnreadInfoStatus( + uh.Document document, + ) { + // Check notice status. + final hasUnreadNotice = + document.querySelector('a#myprompt')?.classes.contains('new') ?? false; + final hasUnreadMessage = + document.querySelector('a#pm_ntc')?.classes.contains('new') ?? false; + + return (hasUnreadNotice, hasUnreadMessage); + } } diff --git a/lib/features/profile/bloc/profile_state.dart b/lib/features/profile/bloc/profile_state.dart index dcbcfebb..8b8f14a1 100644 --- a/lib/features/profile/bloc/profile_state.dart +++ b/lib/features/profile/bloc/profile_state.dart @@ -30,6 +30,8 @@ class ProfileState extends Equatable { this.uid, this.userProfile, this.failedToLogoutReason, + this.hasUnreadNotice = false, + this.hasUnreadMessage = false, }); /// Status. @@ -50,6 +52,12 @@ class ProfileState extends Equatable { /// page content is as same as [ProfileStatus.success]. final Exception? failedToLogoutReason; + /// Flag indicating has unread notices or not. + final bool hasUnreadNotice; + + /// Flag indicating has unread messages or not. + final bool hasUnreadMessage; + /// Copy with. ProfileState copyWith({ ProfileStatus? status, @@ -57,14 +65,18 @@ class ProfileState extends Equatable { String? uid, UserProfile? userProfile, Exception? failedToLogoutReason, + bool? hasUnreadNotice, + bool? hasUnreadMessage, }) { return ProfileState( status: status ?? this.status, username: username ?? this.username, uid: uid ?? this.uid, userProfile: userProfile ?? this.userProfile, - failedToLogoutReason: - failedToLogoutReason, // This argument should be cleaned if not set. + // This argument should be cleaned if not set. + failedToLogoutReason: failedToLogoutReason, + hasUnreadNotice: hasUnreadNotice ?? this.hasUnreadNotice, + hasUnreadMessage: hasUnreadMessage ?? this.hasUnreadMessage, ); } @@ -75,5 +87,7 @@ class ProfileState extends Equatable { uid, userProfile, failedToLogoutReason, + hasUnreadNotice, + hasUnreadMessage, ]; } diff --git a/lib/features/profile/view/profile_page.dart b/lib/features/profile/view/profile_page.dart index 73c01201..c66c7753 100644 --- a/lib/features/profile/view/profile_page.dart +++ b/lib/features/profile/view/profile_page.dart @@ -121,12 +121,20 @@ class _ProfilePageState extends State { ); } + final profileState = context.read().state; + final hasUnreadInfo = + profileState.hasUnreadNotice || profileState.hasUnreadMessage; + late final List actions; if (widget.username == null && widget.uid == null) { // Current is current logged user's profile page. actions = [ IconButton( - icon: const Icon(Icons.notifications_outlined), + icon: hasUnreadInfo + ? const Badge( + child: Icon(Icons.notifications_outlined), + ) + : const Icon(Icons.notifications_outlined), onPressed: () async { await context.pushNamed(ScreenPaths.notice); },