From cbedd512a41522f2b6e12db18d08271f2404a7a8 Mon Sep 17 00:00:00 2001 From: realth000 Date: Sat, 28 Dec 2024 06:43:45 +0800 Subject: [PATCH] fix(html): recognize user credit and permission page url --- lib/exceptions/exceptions.dart | 5 ++++ lib/extensions/string.dart | 30 +++++++++++++++++++ .../notification/bloc/notification_bloc.dart | 5 ++++ lib/utils/html/html_muncher.dart | 2 +- pubspec.lock | 2 +- 5 files changed, 42 insertions(+), 2 deletions(-) diff --git a/lib/exceptions/exceptions.dart b/lib/exceptions/exceptions.dart index 752c8c38..c762e342 100644 --- a/lib/exceptions/exceptions.dart +++ b/lib/exceptions/exceptions.dart @@ -372,6 +372,11 @@ final class ThreadPublishLocationNotFoundException extends AppException final class NotificationUserNotFound extends AppException with NotificationUserNotFoundMappable {} +/// Notification not found in bloc. +@MappableClass() +final class NotificationNotFound extends AppException + with NotificationNotFoundMappable {} + /// Cookie not found in storage when doing auto checkin for user [userInfo]. /// /// Means a checkin failure. diff --git a/lib/extensions/string.dart b/lib/extensions/string.dart index 887d9837..ce565c1e 100644 --- a/lib/extensions/string.dart +++ b/lib/extensions/string.dart @@ -204,6 +204,36 @@ extension ParseUrl on String { String? uriQueryParameter(String name) { return Uri.parse(this).queryParameters[name]; } + + /// Check a string is pattern of user space url. + /// + /// A string is a valid user space url ONLY if + /// + /// 1. Is a valid url. + /// 2. In query parameters, 'mod' == 'space'. + /// 3. In query parameters, contains key 'uid' or 'username'. (email ignored). + /// 4. In query parameters, value of 'ac' is neither 'usergroup' nor 'credit'. + bool get isUserSpaceUrl { + final args = Uri.tryParse(this)?.queryParameters; + if (args == null) { + return false; + } + + if (args['mod'] != 'space') { + return false; + } + + if (args['uid'] == null && args['username'] == null) { + return false; + } + + final ac = args['ac']; + if (ac == 'usergroup' || ac == 'credit') { + return false; + } + + return true; + } } /// Extension on [String] that enhances modification. diff --git a/lib/features/notification/bloc/notification_bloc.dart b/lib/features/notification/bloc/notification_bloc.dart index 40b529a6..5e52a548 100644 --- a/lib/features/notification/bloc/notification_bloc.dart +++ b/lib/features/notification/bloc/notification_bloc.dart @@ -1,6 +1,7 @@ import 'package:bloc/bloc.dart'; import 'package:dart_mappable/dart_mappable.dart'; import 'package:fpdart/fpdart.dart'; +import 'package:tsdm_client/exceptions/exceptions.dart'; import 'package:tsdm_client/extensions/date_time.dart'; import 'package:tsdm_client/extensions/fp.dart'; import 'package:tsdm_client/extensions/string.dart'; @@ -400,6 +401,10 @@ class NotificationBloc extends Bloc final task = switch (recordMark) { RecordMarkNotice(:final uid, :final nid, alreadyRead: final read) => () { final targetIndex = state.noticeList.indexWhere((e) => e.id == nid); + if (targetIndex < 0) { + // target not found. + return AsyncVoidEither(() async => left(NotificationNotFound())); + } final target = state.noticeList[targetIndex]; final list = state.noticeList.toList(); list[targetIndex] = target.copyWith(alreadyRead: read); diff --git a/lib/utils/html/html_muncher.dart b/lib/utils/html/html_muncher.dart index dd0ef57f..e0b7dca3 100644 --- a/lib/utils/html/html_muncher.dart +++ b/lib/utils/html/html_muncher.dart @@ -846,7 +846,7 @@ final class _Muncher with LoggerMixin { return ret; } final Widget content; - if (url.contains('mod=space') && !element.innerText.contains('@')) { + if (url.isUserSpaceUrl && !element.innerText.contains('@')) { content = Text( '@', style: TextStyle( diff --git a/pubspec.lock b/pubspec.lock index 842190b3..a6322d5e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -2078,4 +2078,4 @@ packages: version: "3.1.3" sdks: dart: ">=3.6.0 <4.0.0" - flutter: ">=3.27.0" + flutter: ">=3.27.1"