From 9e609b8fe46551f0c38cbb6fb26454242da0bda0 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Tue, 20 May 2025 01:14:13 +0800 Subject: [PATCH] :sparkles: Notification UI --- assets/i18n/en-US.json | 6 +- lib/models/user.dart | 21 ++ lib/models/user.freezed.dart | 172 +++++++++++++ lib/models/user.g.dart | 38 +++ lib/pods/websocket.dart | 47 ++-- lib/route.dart | 9 +- lib/route.gr.dart | 397 ++++++++++++++++-------------- lib/screens/account.dart | 18 +- lib/screens/auth/tabs.dart | 14 +- lib/screens/notification.dart | 194 +++++++++++++++ lib/screens/notification.g.dart | 52 ++++ lib/screens/settings.dart | 3 + lib/widgets/content/markdown.dart | 8 +- 13 files changed, 741 insertions(+), 238 deletions(-) create mode 100644 lib/screens/notification.dart create mode 100644 lib/screens/notification.g.dart diff --git a/assets/i18n/en-US.json b/assets/i18n/en-US.json index 976b3f9..f278769 100644 --- a/assets/i18n/en-US.json +++ b/assets/i18n/en-US.json @@ -152,7 +152,7 @@ "status": "Status", "statusActivityTitle": "{} is {} {}", "statusActivityEndedTitle": "{} is {} {} until {}", - "appSettings": "App Settings", + "appSettings": "App settings", "accountSettings": "Account Settings", "settings": "Settings", "language": "Language", @@ -258,5 +258,7 @@ "walletNotFound": "Wallet not found", "walletCreateHint": "You don't have a wallet yet. Create one to start using the Solar Network eWallet.", "walletCreate": "Create a Wallet", - "settingsServerUrl": "Server URL" + "settingsServerUrl": "Server URL", + "settingsApplied": "The settings has been applied.", + "notifications": "Notifications" } diff --git a/lib/models/user.dart b/lib/models/user.dart index 99e7022..8de652a 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -86,3 +86,24 @@ abstract class SnAccountBadge with _$SnAccountBadge { factory SnAccountBadge.fromJson(Map json) => _$SnAccountBadgeFromJson(json); } + +@freezed +abstract class SnNotification with _$SnNotification { + const factory SnNotification({ + required DateTime createdAt, + required DateTime updatedAt, + required DateTime? deletedAt, + required String id, + required String topic, + required String title, + @Default('') String subtitle, + required String content, + @Default({}) Map meta, + required int priority, + required DateTime? viewedAt, + required String accountId, + }) = _SnNotification; + + factory SnNotification.fromJson(Map json) => + _$SnNotificationFromJson(json); +} diff --git a/lib/models/user.freezed.dart b/lib/models/user.freezed.dart index a3b5e5c..3c241d5 100644 --- a/lib/models/user.freezed.dart +++ b/lib/models/user.freezed.dart @@ -749,6 +749,178 @@ as DateTime?, } +} + + +/// @nodoc +mixin _$SnNotification { + + DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; String get id; String get topic; String get title; String get subtitle; String get content; Map get meta; int get priority; DateTime? get viewedAt; String get accountId; +/// Create a copy of SnNotification +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$SnNotificationCopyWith get copyWith => _$SnNotificationCopyWithImpl(this as SnNotification, _$identity); + + /// Serializes this SnNotification to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is SnNotification&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.id, id) || other.id == id)&&(identical(other.topic, topic) || other.topic == topic)&&(identical(other.title, title) || other.title == title)&&(identical(other.subtitle, subtitle) || other.subtitle == subtitle)&&(identical(other.content, content) || other.content == content)&&const DeepCollectionEquality().equals(other.meta, meta)&&(identical(other.priority, priority) || other.priority == priority)&&(identical(other.viewedAt, viewedAt) || other.viewedAt == viewedAt)&&(identical(other.accountId, accountId) || other.accountId == accountId)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,createdAt,updatedAt,deletedAt,id,topic,title,subtitle,content,const DeepCollectionEquality().hash(meta),priority,viewedAt,accountId); + +@override +String toString() { + return 'SnNotification(createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, id: $id, topic: $topic, title: $title, subtitle: $subtitle, content: $content, meta: $meta, priority: $priority, viewedAt: $viewedAt, accountId: $accountId)'; +} + + +} + +/// @nodoc +abstract mixin class $SnNotificationCopyWith<$Res> { + factory $SnNotificationCopyWith(SnNotification value, $Res Function(SnNotification) _then) = _$SnNotificationCopyWithImpl; +@useResult +$Res call({ + DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String topic, String title, String subtitle, String content, Map meta, int priority, DateTime? viewedAt, String accountId +}); + + + + +} +/// @nodoc +class _$SnNotificationCopyWithImpl<$Res> + implements $SnNotificationCopyWith<$Res> { + _$SnNotificationCopyWithImpl(this._self, this._then); + + final SnNotification _self; + final $Res Function(SnNotification) _then; + +/// Create a copy of SnNotification +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? id = null,Object? topic = null,Object? title = null,Object? subtitle = null,Object? content = null,Object? meta = null,Object? priority = null,Object? viewedAt = freezed,Object? accountId = null,}) { + return _then(_self.copyWith( +createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable +as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable +as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable +as DateTime?,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,topic: null == topic ? _self.topic : topic // ignore: cast_nullable_to_non_nullable +as String,title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable +as String,subtitle: null == subtitle ? _self.subtitle : subtitle // ignore: cast_nullable_to_non_nullable +as String,content: null == content ? _self.content : content // ignore: cast_nullable_to_non_nullable +as String,meta: null == meta ? _self.meta : meta // ignore: cast_nullable_to_non_nullable +as Map,priority: null == priority ? _self.priority : priority // ignore: cast_nullable_to_non_nullable +as int,viewedAt: freezed == viewedAt ? _self.viewedAt : viewedAt // ignore: cast_nullable_to_non_nullable +as DateTime?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable +as String, + )); +} + +} + + +/// @nodoc +@JsonSerializable() + +class _SnNotification implements SnNotification { + const _SnNotification({required this.createdAt, required this.updatedAt, required this.deletedAt, required this.id, required this.topic, required this.title, this.subtitle = '', required this.content, final Map meta = const {}, required this.priority, required this.viewedAt, required this.accountId}): _meta = meta; + factory _SnNotification.fromJson(Map json) => _$SnNotificationFromJson(json); + +@override final DateTime createdAt; +@override final DateTime updatedAt; +@override final DateTime? deletedAt; +@override final String id; +@override final String topic; +@override final String title; +@override@JsonKey() final String subtitle; +@override final String content; + final Map _meta; +@override@JsonKey() Map get meta { + if (_meta is EqualUnmodifiableMapView) return _meta; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(_meta); +} + +@override final int priority; +@override final DateTime? viewedAt; +@override final String accountId; + +/// Create a copy of SnNotification +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$SnNotificationCopyWith<_SnNotification> get copyWith => __$SnNotificationCopyWithImpl<_SnNotification>(this, _$identity); + +@override +Map toJson() { + return _$SnNotificationToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnNotification&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.id, id) || other.id == id)&&(identical(other.topic, topic) || other.topic == topic)&&(identical(other.title, title) || other.title == title)&&(identical(other.subtitle, subtitle) || other.subtitle == subtitle)&&(identical(other.content, content) || other.content == content)&&const DeepCollectionEquality().equals(other._meta, _meta)&&(identical(other.priority, priority) || other.priority == priority)&&(identical(other.viewedAt, viewedAt) || other.viewedAt == viewedAt)&&(identical(other.accountId, accountId) || other.accountId == accountId)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,createdAt,updatedAt,deletedAt,id,topic,title,subtitle,content,const DeepCollectionEquality().hash(_meta),priority,viewedAt,accountId); + +@override +String toString() { + return 'SnNotification(createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, id: $id, topic: $topic, title: $title, subtitle: $subtitle, content: $content, meta: $meta, priority: $priority, viewedAt: $viewedAt, accountId: $accountId)'; +} + + +} + +/// @nodoc +abstract mixin class _$SnNotificationCopyWith<$Res> implements $SnNotificationCopyWith<$Res> { + factory _$SnNotificationCopyWith(_SnNotification value, $Res Function(_SnNotification) _then) = __$SnNotificationCopyWithImpl; +@override @useResult +$Res call({ + DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String topic, String title, String subtitle, String content, Map meta, int priority, DateTime? viewedAt, String accountId +}); + + + + +} +/// @nodoc +class __$SnNotificationCopyWithImpl<$Res> + implements _$SnNotificationCopyWith<$Res> { + __$SnNotificationCopyWithImpl(this._self, this._then); + + final _SnNotification _self; + final $Res Function(_SnNotification) _then; + +/// Create a copy of SnNotification +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? id = null,Object? topic = null,Object? title = null,Object? subtitle = null,Object? content = null,Object? meta = null,Object? priority = null,Object? viewedAt = freezed,Object? accountId = null,}) { + return _then(_SnNotification( +createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable +as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable +as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable +as DateTime?,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,topic: null == topic ? _self.topic : topic // ignore: cast_nullable_to_non_nullable +as String,title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable +as String,subtitle: null == subtitle ? _self.subtitle : subtitle // ignore: cast_nullable_to_non_nullable +as String,content: null == content ? _self.content : content // ignore: cast_nullable_to_non_nullable +as String,meta: null == meta ? _self._meta : meta // ignore: cast_nullable_to_non_nullable +as Map,priority: null == priority ? _self.priority : priority // ignore: cast_nullable_to_non_nullable +as int,viewedAt: freezed == viewedAt ? _self.viewedAt : viewedAt // ignore: cast_nullable_to_non_nullable +as DateTime?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable +as String, + )); +} + + } // dart format on diff --git a/lib/models/user.g.dart b/lib/models/user.g.dart index 3e8fc85..7d5faa2 100644 --- a/lib/models/user.g.dart +++ b/lib/models/user.g.dart @@ -160,3 +160,41 @@ Map _$SnAccountBadgeToJson(_SnAccountBadge instance) => 'updated_at': instance.updatedAt.toIso8601String(), 'deleted_at': instance.deletedAt?.toIso8601String(), }; + +_SnNotification _$SnNotificationFromJson(Map json) => + _SnNotification( + createdAt: DateTime.parse(json['created_at'] as String), + updatedAt: DateTime.parse(json['updated_at'] as String), + deletedAt: + json['deleted_at'] == null + ? null + : DateTime.parse(json['deleted_at'] as String), + id: json['id'] as String, + topic: json['topic'] as String, + title: json['title'] as String, + subtitle: json['subtitle'] as String? ?? '', + content: json['content'] as String, + meta: json['meta'] as Map? ?? const {}, + priority: (json['priority'] as num).toInt(), + viewedAt: + json['viewed_at'] == null + ? null + : DateTime.parse(json['viewed_at'] as String), + accountId: json['account_id'] as String, + ); + +Map _$SnNotificationToJson(_SnNotification instance) => + { + 'created_at': instance.createdAt.toIso8601String(), + 'updated_at': instance.updatedAt.toIso8601String(), + 'deleted_at': instance.deletedAt?.toIso8601String(), + 'id': instance.id, + 'topic': instance.topic, + 'title': instance.title, + 'subtitle': instance.subtitle, + 'content': instance.content, + 'meta': instance.meta, + 'priority': instance.priority, + 'viewed_at': instance.viewedAt?.toIso8601String(), + 'account_id': instance.accountId, + }; diff --git a/lib/pods/websocket.dart b/lib/pods/websocket.dart index 14380fa..2670229 100644 --- a/lib/pods/websocket.dart +++ b/lib/pods/websocket.dart @@ -38,36 +38,31 @@ final websocketProvider = Provider((ref) { }); class WebSocketService { + late Ref _ref; WebSocketChannel? _channel; final StreamController _streamController = StreamController.broadcast(); final StreamController _statusStreamController = StreamController.broadcast(); - String? _lastUrl; - String? _lastAtk; Timer? _reconnectTimer; Stream get dataStream => _streamController.stream; Stream get statusStream => _statusStreamController.stream; - Future connect(String url, String atk, {Ref? ref}) async { - _lastUrl = url; - _lastAtk = atk; + Future connect(Ref ref) async { + _ref = ref; - if (ref != null) { - final freshAtk = await getFreshAtk( - ref.watch(tokenPairProvider), - url.replaceFirst('ws', 'http').replaceFirst('/ws', ''), - onRefreshed: (atk, rtk) { - setTokenPair(ref.watch(sharedPreferencesProvider), atk, rtk); - ref.invalidate(tokenPairProvider); - }, - ); - if (freshAtk != null) { - atk = freshAtk; - _lastAtk = freshAtk; - } - } + final baseUrl = ref.watch(serverUrlProvider); + final atk = await getFreshAtk( + ref.watch(tokenPairProvider), + baseUrl, + onRefreshed: (atk, rtk) { + setTokenPair(ref.watch(sharedPreferencesProvider), atk, rtk); + ref.invalidate(tokenPairProvider); + }, + ); + + final url = '$baseUrl/ws'.replaceFirst('http', 'ws'); log('[WebSocket] Trying connecting to $url'); try { @@ -113,10 +108,8 @@ class WebSocketService { void _scheduleReconnect() { _reconnectTimer?.cancel(); _reconnectTimer = Timer(const Duration(milliseconds: 500), () { - if (_lastUrl != null && _lastAtk != null) { - _statusStreamController.sink.add(WebSocketState.connecting()); - connect(_lastUrl!, _lastAtk!); - } + _statusStreamController.sink.add(WebSocketState.connecting()); + connect(_ref); }); } @@ -128,8 +121,6 @@ class WebSocketService { void close() { _reconnectTimer?.cancel(); - _lastUrl = null; - _lastAtk = null; _channel?.sink.close(); } } @@ -162,11 +153,7 @@ class WebSocketStateNotifier extends StateNotifier { state = const WebSocketState.error('Unauthorized'); return; } - await service.connect( - '$baseUrl/ws'.replaceFirst('http', 'ws'), - atk, - ref: ref, - ); + await service.connect(ref); state = const WebSocketState.connected(); service.statusStream.listen((event) { state = event; diff --git a/lib/route.dart b/lib/route.dart index 7fac723..5430d95 100644 --- a/lib/route.dart +++ b/lib/route.dart @@ -14,15 +14,15 @@ class AppRouter extends RootStackRouter { page: AccountShellRoute.page, path: '/account', children: [ + AutoRoute(page: AccountRoute.page, path: ''), + AutoRoute(page: NotificationRoute.page, path: 'notifications'), AutoRoute(page: WalletRoute.page, path: 'wallet'), AutoRoute(page: RelationshipRoute.page, path: 'relationships'), - AutoRoute(page: AccountRoute.page, path: ''), - AutoRoute(page: AccountSettingsRoute.page, path: 'settings'), AutoRoute(page: AccountProfileRoute.page, path: ':name'), AutoRoute(page: PublisherProfileRoute.page, path: ':name/calendar'), AutoRoute(page: MyselfEventCalendarRoute.page, path: 'me/calendar'), AutoRoute(page: UpdateProfileRoute.page, path: 'me/update'), - AutoRoute(page: ManagedPublisherRoute.page, path: 'me/publishers'), + AutoRoute(page: AccountSettingsRoute.page, path: 'settings'), ], ), AutoRoute(page: RealmListRoute.page, path: '/realms'), @@ -37,10 +37,9 @@ class AppRouter extends RootStackRouter { AutoRoute(page: ChatDetailRoute.page, path: ':id/detail'), ], ), - AutoRoute(page: SettingsRoute.page, path: '/settings'), AutoRoute(page: LoginRoute.page, path: '/auth/login'), AutoRoute(page: CreateAccountRoute.page, path: '/auth/create-account'), - AutoRoute(page: AccountSettingsRoute.page, path: '/account/settings'), + AutoRoute(page: SettingsRoute.page, path: '/settings'), AutoRoute(page: PostComposeRoute.page, path: '/posts/compose'), AutoRoute(page: PostDetailRoute.page, path: '/posts/:id'), AutoRoute(page: PostEditRoute.page, path: '/posts/:id/edit'), diff --git a/lib/route.gr.dart b/lib/route.gr.dart index ca9c99f..8015773 100644 --- a/lib/route.gr.dart +++ b/lib/route.gr.dart @@ -9,21 +9,21 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:auto_route/auto_route.dart' as _i25; -import 'package:flutter/foundation.dart' as _i27; -import 'package:flutter/material.dart' as _i26; -import 'package:island/models/post.dart' as _i28; -import 'package:island/route.dart' as _i29; +import 'package:auto_route/auto_route.dart' as _i26; +import 'package:flutter/foundation.dart' as _i28; +import 'package:flutter/material.dart' as _i27; +import 'package:island/models/post.dart' as _i29; +import 'package:island/route.dart' as _i30; import 'package:island/screens/account.dart' as _i2; import 'package:island/screens/account/me/event_calendar.dart' as _i15; import 'package:island/screens/account/me/publishers.dart' as _i9; import 'package:island/screens/account/me/settings.dart' as _i3; -import 'package:island/screens/account/me/update.dart' as _i23; +import 'package:island/screens/account/me/update.dart' as _i24; import 'package:island/screens/account/profile.dart' as _i1; -import 'package:island/screens/account/relationship.dart' as _i20; +import 'package:island/screens/account/relationship.dart' as _i21; import 'package:island/screens/auth/create_account.dart' as _i7; import 'package:island/screens/auth/login.dart' as _i14; -import 'package:island/screens/auth/tabs.dart' as _i22; +import 'package:island/screens/auth/tabs.dart' as _i23; import 'package:island/screens/chat/chat.dart' as _i5; import 'package:island/screens/chat/room.dart' as _i6; import 'package:island/screens/chat/room_detail.dart' as _i4; @@ -31,21 +31,22 @@ import 'package:island/screens/creators/hub.dart' as _i8; import 'package:island/screens/creators/stickers/pack_detail.dart' as _i12; import 'package:island/screens/creators/stickers/stickers.dart' as _i11; import 'package:island/screens/explore.dart' as _i13; -import 'package:island/screens/posts/compose.dart' as _i16; -import 'package:island/screens/posts/detail.dart' as _i17; -import 'package:island/screens/posts/pub_profile.dart' as _i18; -import 'package:island/screens/realm/detail.dart' as _i19; +import 'package:island/screens/notification.dart' as _i16; +import 'package:island/screens/posts/compose.dart' as _i17; +import 'package:island/screens/posts/detail.dart' as _i18; +import 'package:island/screens/posts/pub_profile.dart' as _i19; +import 'package:island/screens/realm/detail.dart' as _i20; import 'package:island/screens/realm/realms.dart' as _i10; -import 'package:island/screens/settings.dart' as _i21; -import 'package:island/screens/wallet.dart' as _i24; +import 'package:island/screens/settings.dart' as _i22; +import 'package:island/screens/wallet.dart' as _i25; /// generated route for /// [_i1.AccountProfileScreen] -class AccountProfileRoute extends _i25.PageRouteInfo { +class AccountProfileRoute extends _i26.PageRouteInfo { AccountProfileRoute({ - _i26.Key? key, + _i27.Key? key, required String name, - List<_i25.PageRouteInfo>? children, + List<_i26.PageRouteInfo>? children, }) : super( AccountProfileRoute.name, args: AccountProfileRouteArgs(key: key, name: name), @@ -55,7 +56,7 @@ class AccountProfileRoute extends _i25.PageRouteInfo { static const String name = 'AccountProfileRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final pathParams = data.inheritedPathParams; @@ -71,7 +72,7 @@ class AccountProfileRoute extends _i25.PageRouteInfo { class AccountProfileRouteArgs { const AccountProfileRouteArgs({this.key, required this.name}); - final _i26.Key? key; + final _i27.Key? key; final String name; @@ -83,11 +84,11 @@ class AccountProfileRouteArgs { /// generated route for /// [_i2.AccountScreen] -class AccountRoute extends _i25.PageRouteInfo { +class AccountRoute extends _i26.PageRouteInfo { AccountRoute({ - _i27.Key? key, + _i28.Key? key, bool isAside = false, - List<_i25.PageRouteInfo>? children, + List<_i26.PageRouteInfo>? children, }) : super( AccountRoute.name, args: AccountRouteArgs(key: key, isAside: isAside), @@ -96,7 +97,7 @@ class AccountRoute extends _i25.PageRouteInfo { static const String name = 'AccountRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final args = data.argsAs( @@ -110,7 +111,7 @@ class AccountRoute extends _i25.PageRouteInfo { class AccountRouteArgs { const AccountRouteArgs({this.key, this.isAside = false}); - final _i27.Key? key; + final _i28.Key? key; final bool isAside; @@ -122,13 +123,13 @@ class AccountRouteArgs { /// generated route for /// [_i3.AccountSettingsScreen] -class AccountSettingsRoute extends _i25.PageRouteInfo { - const AccountSettingsRoute({List<_i25.PageRouteInfo>? children}) +class AccountSettingsRoute extends _i26.PageRouteInfo { + const AccountSettingsRoute({List<_i26.PageRouteInfo>? children}) : super(AccountSettingsRoute.name, initialChildren: children); static const String name = 'AccountSettingsRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { return const _i3.AccountSettingsScreen(); @@ -138,13 +139,13 @@ class AccountSettingsRoute extends _i25.PageRouteInfo { /// generated route for /// [_i2.AccountShellScreen] -class AccountShellRoute extends _i25.PageRouteInfo { - const AccountShellRoute({List<_i25.PageRouteInfo>? children}) +class AccountShellRoute extends _i26.PageRouteInfo { + const AccountShellRoute({List<_i26.PageRouteInfo>? children}) : super(AccountShellRoute.name, initialChildren: children); static const String name = 'AccountShellRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { return const _i2.AccountShellScreen(); @@ -154,11 +155,11 @@ class AccountShellRoute extends _i25.PageRouteInfo { /// generated route for /// [_i4.ChatDetailScreen] -class ChatDetailRoute extends _i25.PageRouteInfo { +class ChatDetailRoute extends _i26.PageRouteInfo { ChatDetailRoute({ - _i26.Key? key, + _i27.Key? key, required String id, - List<_i25.PageRouteInfo>? children, + List<_i26.PageRouteInfo>? children, }) : super( ChatDetailRoute.name, args: ChatDetailRouteArgs(key: key, id: id), @@ -168,7 +169,7 @@ class ChatDetailRoute extends _i25.PageRouteInfo { static const String name = 'ChatDetailRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final pathParams = data.inheritedPathParams; @@ -183,7 +184,7 @@ class ChatDetailRoute extends _i25.PageRouteInfo { class ChatDetailRouteArgs { const ChatDetailRouteArgs({this.key, required this.id}); - final _i26.Key? key; + final _i27.Key? key; final String id; @@ -195,11 +196,11 @@ class ChatDetailRouteArgs { /// generated route for /// [_i5.ChatListScreen] -class ChatListRoute extends _i25.PageRouteInfo { +class ChatListRoute extends _i26.PageRouteInfo { ChatListRoute({ - _i26.Key? key, + _i27.Key? key, bool isAside = false, - List<_i25.PageRouteInfo>? children, + List<_i26.PageRouteInfo>? children, }) : super( ChatListRoute.name, args: ChatListRouteArgs(key: key, isAside: isAside), @@ -208,7 +209,7 @@ class ChatListRoute extends _i25.PageRouteInfo { static const String name = 'ChatListRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final args = data.argsAs( @@ -222,7 +223,7 @@ class ChatListRoute extends _i25.PageRouteInfo { class ChatListRouteArgs { const ChatListRouteArgs({this.key, this.isAside = false}); - final _i26.Key? key; + final _i27.Key? key; final bool isAside; @@ -234,11 +235,11 @@ class ChatListRouteArgs { /// generated route for /// [_i6.ChatRoomScreen] -class ChatRoomRoute extends _i25.PageRouteInfo { +class ChatRoomRoute extends _i26.PageRouteInfo { ChatRoomRoute({ - _i26.Key? key, + _i27.Key? key, required String id, - List<_i25.PageRouteInfo>? children, + List<_i26.PageRouteInfo>? children, }) : super( ChatRoomRoute.name, args: ChatRoomRouteArgs(key: key, id: id), @@ -248,7 +249,7 @@ class ChatRoomRoute extends _i25.PageRouteInfo { static const String name = 'ChatRoomRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final pathParams = data.inheritedPathParams; @@ -263,7 +264,7 @@ class ChatRoomRoute extends _i25.PageRouteInfo { class ChatRoomRouteArgs { const ChatRoomRouteArgs({this.key, required this.id}); - final _i26.Key? key; + final _i27.Key? key; final String id; @@ -275,13 +276,13 @@ class ChatRoomRouteArgs { /// generated route for /// [_i5.ChatShellScreen] -class ChatShellRoute extends _i25.PageRouteInfo { - const ChatShellRoute({List<_i25.PageRouteInfo>? children}) +class ChatShellRoute extends _i26.PageRouteInfo { + const ChatShellRoute({List<_i26.PageRouteInfo>? children}) : super(ChatShellRoute.name, initialChildren: children); static const String name = 'ChatShellRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { return const _i5.ChatShellScreen(); @@ -291,13 +292,13 @@ class ChatShellRoute extends _i25.PageRouteInfo { /// generated route for /// [_i7.CreateAccountScreen] -class CreateAccountRoute extends _i25.PageRouteInfo { - const CreateAccountRoute({List<_i25.PageRouteInfo>? children}) +class CreateAccountRoute extends _i26.PageRouteInfo { + const CreateAccountRoute({List<_i26.PageRouteInfo>? children}) : super(CreateAccountRoute.name, initialChildren: children); static const String name = 'CreateAccountRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { return const _i7.CreateAccountScreen(); @@ -307,13 +308,13 @@ class CreateAccountRoute extends _i25.PageRouteInfo { /// generated route for /// [_i8.CreatorHubScreen] -class CreatorHubRoute extends _i25.PageRouteInfo { - const CreatorHubRoute({List<_i25.PageRouteInfo>? children}) +class CreatorHubRoute extends _i26.PageRouteInfo { + const CreatorHubRoute({List<_i26.PageRouteInfo>? children}) : super(CreatorHubRoute.name, initialChildren: children); static const String name = 'CreatorHubRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { return const _i8.CreatorHubScreen(); @@ -323,8 +324,8 @@ class CreatorHubRoute extends _i25.PageRouteInfo { /// generated route for /// [_i5.EditChatScreen] -class EditChatRoute extends _i25.PageRouteInfo { - EditChatRoute({_i26.Key? key, String? id, List<_i25.PageRouteInfo>? children}) +class EditChatRoute extends _i26.PageRouteInfo { + EditChatRoute({_i27.Key? key, String? id, List<_i26.PageRouteInfo>? children}) : super( EditChatRoute.name, args: EditChatRouteArgs(key: key, id: id), @@ -334,7 +335,7 @@ class EditChatRoute extends _i25.PageRouteInfo { static const String name = 'EditChatRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final pathParams = data.inheritedPathParams; @@ -349,7 +350,7 @@ class EditChatRoute extends _i25.PageRouteInfo { class EditChatRouteArgs { const EditChatRouteArgs({this.key, this.id}); - final _i26.Key? key; + final _i27.Key? key; final String? id; @@ -361,11 +362,11 @@ class EditChatRouteArgs { /// generated route for /// [_i9.EditPublisherScreen] -class EditPublisherRoute extends _i25.PageRouteInfo { +class EditPublisherRoute extends _i26.PageRouteInfo { EditPublisherRoute({ - _i26.Key? key, + _i27.Key? key, String? name, - List<_i25.PageRouteInfo>? children, + List<_i26.PageRouteInfo>? children, }) : super( EditPublisherRoute.name, args: EditPublisherRouteArgs(key: key, name: name), @@ -375,7 +376,7 @@ class EditPublisherRoute extends _i25.PageRouteInfo { static const String name = 'EditPublisherRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final pathParams = data.inheritedPathParams; @@ -390,7 +391,7 @@ class EditPublisherRoute extends _i25.PageRouteInfo { class EditPublisherRouteArgs { const EditPublisherRouteArgs({this.key, this.name}); - final _i26.Key? key; + final _i27.Key? key; final String? name; @@ -402,11 +403,11 @@ class EditPublisherRouteArgs { /// generated route for /// [_i10.EditRealmScreen] -class EditRealmRoute extends _i25.PageRouteInfo { +class EditRealmRoute extends _i26.PageRouteInfo { EditRealmRoute({ - _i26.Key? key, + _i27.Key? key, String? slug, - List<_i25.PageRouteInfo>? children, + List<_i26.PageRouteInfo>? children, }) : super( EditRealmRoute.name, args: EditRealmRouteArgs(key: key, slug: slug), @@ -416,7 +417,7 @@ class EditRealmRoute extends _i25.PageRouteInfo { static const String name = 'EditRealmRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final pathParams = data.inheritedPathParams; @@ -431,7 +432,7 @@ class EditRealmRoute extends _i25.PageRouteInfo { class EditRealmRouteArgs { const EditRealmRouteArgs({this.key, this.slug}); - final _i26.Key? key; + final _i27.Key? key; final String? slug; @@ -444,12 +445,12 @@ class EditRealmRouteArgs { /// generated route for /// [_i11.EditStickerPacksScreen] class EditStickerPacksRoute - extends _i25.PageRouteInfo { + extends _i26.PageRouteInfo { EditStickerPacksRoute({ - _i26.Key? key, + _i27.Key? key, required String pubName, String? packId, - List<_i25.PageRouteInfo>? children, + List<_i26.PageRouteInfo>? children, }) : super( EditStickerPacksRoute.name, args: EditStickerPacksRouteArgs( @@ -463,7 +464,7 @@ class EditStickerPacksRoute static const String name = 'EditStickerPacksRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final pathParams = data.inheritedPathParams; @@ -490,7 +491,7 @@ class EditStickerPacksRouteArgs { this.packId, }); - final _i26.Key? key; + final _i27.Key? key; final String pubName; @@ -504,12 +505,12 @@ class EditStickerPacksRouteArgs { /// generated route for /// [_i12.EditStickersScreen] -class EditStickersRoute extends _i25.PageRouteInfo { +class EditStickersRoute extends _i26.PageRouteInfo { EditStickersRoute({ - _i26.Key? key, + _i27.Key? key, required String packId, required String? id, - List<_i25.PageRouteInfo>? children, + List<_i26.PageRouteInfo>? children, }) : super( EditStickersRoute.name, args: EditStickersRouteArgs(key: key, packId: packId, id: id), @@ -519,7 +520,7 @@ class EditStickersRoute extends _i25.PageRouteInfo { static const String name = 'EditStickersRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final pathParams = data.inheritedPathParams; @@ -546,7 +547,7 @@ class EditStickersRouteArgs { required this.id, }); - final _i26.Key? key; + final _i27.Key? key; final String packId; @@ -560,13 +561,13 @@ class EditStickersRouteArgs { /// generated route for /// [_i13.ExploreScreen] -class ExploreRoute extends _i25.PageRouteInfo { - const ExploreRoute({List<_i25.PageRouteInfo>? children}) +class ExploreRoute extends _i26.PageRouteInfo { + const ExploreRoute({List<_i26.PageRouteInfo>? children}) : super(ExploreRoute.name, initialChildren: children); static const String name = 'ExploreRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { return const _i13.ExploreScreen(); @@ -576,13 +577,13 @@ class ExploreRoute extends _i25.PageRouteInfo { /// generated route for /// [_i14.LoginScreen] -class LoginRoute extends _i25.PageRouteInfo { - const LoginRoute({List<_i25.PageRouteInfo>? children}) +class LoginRoute extends _i26.PageRouteInfo { + const LoginRoute({List<_i26.PageRouteInfo>? children}) : super(LoginRoute.name, initialChildren: children); static const String name = 'LoginRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { return const _i14.LoginScreen(); @@ -592,13 +593,13 @@ class LoginRoute extends _i25.PageRouteInfo { /// generated route for /// [_i9.ManagedPublisherScreen] -class ManagedPublisherRoute extends _i25.PageRouteInfo { - const ManagedPublisherRoute({List<_i25.PageRouteInfo>? children}) +class ManagedPublisherRoute extends _i26.PageRouteInfo { + const ManagedPublisherRoute({List<_i26.PageRouteInfo>? children}) : super(ManagedPublisherRoute.name, initialChildren: children); static const String name = 'ManagedPublisherRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { return const _i9.ManagedPublisherScreen(); @@ -608,13 +609,13 @@ class ManagedPublisherRoute extends _i25.PageRouteInfo { /// generated route for /// [_i15.MyselfEventCalendarScreen] -class MyselfEventCalendarRoute extends _i25.PageRouteInfo { - const MyselfEventCalendarRoute({List<_i25.PageRouteInfo>? children}) +class MyselfEventCalendarRoute extends _i26.PageRouteInfo { + const MyselfEventCalendarRoute({List<_i26.PageRouteInfo>? children}) : super(MyselfEventCalendarRoute.name, initialChildren: children); static const String name = 'MyselfEventCalendarRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { return const _i15.MyselfEventCalendarScreen(); @@ -624,13 +625,13 @@ class MyselfEventCalendarRoute extends _i25.PageRouteInfo { /// generated route for /// [_i5.NewChatScreen] -class NewChatRoute extends _i25.PageRouteInfo { - const NewChatRoute({List<_i25.PageRouteInfo>? children}) +class NewChatRoute extends _i26.PageRouteInfo { + const NewChatRoute({List<_i26.PageRouteInfo>? children}) : super(NewChatRoute.name, initialChildren: children); static const String name = 'NewChatRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { return const _i5.NewChatScreen(); @@ -640,13 +641,13 @@ class NewChatRoute extends _i25.PageRouteInfo { /// generated route for /// [_i9.NewPublisherScreen] -class NewPublisherRoute extends _i25.PageRouteInfo { - const NewPublisherRoute({List<_i25.PageRouteInfo>? children}) +class NewPublisherRoute extends _i26.PageRouteInfo { + const NewPublisherRoute({List<_i26.PageRouteInfo>? children}) : super(NewPublisherRoute.name, initialChildren: children); static const String name = 'NewPublisherRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { return const _i9.NewPublisherScreen(); @@ -656,13 +657,13 @@ class NewPublisherRoute extends _i25.PageRouteInfo { /// generated route for /// [_i10.NewRealmScreen] -class NewRealmRoute extends _i25.PageRouteInfo { - const NewRealmRoute({List<_i25.PageRouteInfo>? children}) +class NewRealmRoute extends _i26.PageRouteInfo { + const NewRealmRoute({List<_i26.PageRouteInfo>? children}) : super(NewRealmRoute.name, initialChildren: children); static const String name = 'NewRealmRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { return const _i10.NewRealmScreen(); @@ -673,11 +674,11 @@ class NewRealmRoute extends _i25.PageRouteInfo { /// generated route for /// [_i11.NewStickerPacksScreen] class NewStickerPacksRoute - extends _i25.PageRouteInfo { + extends _i26.PageRouteInfo { NewStickerPacksRoute({ - _i26.Key? key, + _i27.Key? key, required String pubName, - List<_i25.PageRouteInfo>? children, + List<_i26.PageRouteInfo>? children, }) : super( NewStickerPacksRoute.name, args: NewStickerPacksRouteArgs(key: key, pubName: pubName), @@ -687,7 +688,7 @@ class NewStickerPacksRoute static const String name = 'NewStickerPacksRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final pathParams = data.inheritedPathParams; @@ -704,7 +705,7 @@ class NewStickerPacksRoute class NewStickerPacksRouteArgs { const NewStickerPacksRouteArgs({this.key, required this.pubName}); - final _i26.Key? key; + final _i27.Key? key; final String pubName; @@ -716,11 +717,11 @@ class NewStickerPacksRouteArgs { /// generated route for /// [_i12.NewStickersScreen] -class NewStickersRoute extends _i25.PageRouteInfo { +class NewStickersRoute extends _i26.PageRouteInfo { NewStickersRoute({ - _i26.Key? key, + _i27.Key? key, required String packId, - List<_i25.PageRouteInfo>? children, + List<_i26.PageRouteInfo>? children, }) : super( NewStickersRoute.name, args: NewStickersRouteArgs(key: key, packId: packId), @@ -730,7 +731,7 @@ class NewStickersRoute extends _i25.PageRouteInfo { static const String name = 'NewStickersRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final pathParams = data.inheritedPathParams; @@ -746,7 +747,7 @@ class NewStickersRoute extends _i25.PageRouteInfo { class NewStickersRouteArgs { const NewStickersRouteArgs({this.key, required this.packId}); - final _i26.Key? key; + final _i27.Key? key; final String packId; @@ -757,12 +758,28 @@ class NewStickersRouteArgs { } /// generated route for -/// [_i16.PostComposeScreen] -class PostComposeRoute extends _i25.PageRouteInfo { +/// [_i16.NotificationScreen] +class NotificationRoute extends _i26.PageRouteInfo { + const NotificationRoute({List<_i26.PageRouteInfo>? children}) + : super(NotificationRoute.name, initialChildren: children); + + static const String name = 'NotificationRoute'; + + static _i26.PageInfo page = _i26.PageInfo( + name, + builder: (data) { + return const _i16.NotificationScreen(); + }, + ); +} + +/// generated route for +/// [_i17.PostComposeScreen] +class PostComposeRoute extends _i26.PageRouteInfo { PostComposeRoute({ - _i26.Key? key, - _i28.SnPost? originalPost, - List<_i25.PageRouteInfo>? children, + _i27.Key? key, + _i29.SnPost? originalPost, + List<_i26.PageRouteInfo>? children, }) : super( PostComposeRoute.name, args: PostComposeRouteArgs(key: key, originalPost: originalPost), @@ -771,13 +788,13 @@ class PostComposeRoute extends _i25.PageRouteInfo { static const String name = 'PostComposeRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final args = data.argsAs( orElse: () => const PostComposeRouteArgs(), ); - return _i16.PostComposeScreen( + return _i17.PostComposeScreen( key: args.key, originalPost: args.originalPost, ); @@ -788,9 +805,9 @@ class PostComposeRoute extends _i25.PageRouteInfo { class PostComposeRouteArgs { const PostComposeRouteArgs({this.key, this.originalPost}); - final _i26.Key? key; + final _i27.Key? key; - final _i28.SnPost? originalPost; + final _i29.SnPost? originalPost; @override String toString() { @@ -799,12 +816,12 @@ class PostComposeRouteArgs { } /// generated route for -/// [_i17.PostDetailScreen] -class PostDetailRoute extends _i25.PageRouteInfo { +/// [_i18.PostDetailScreen] +class PostDetailRoute extends _i26.PageRouteInfo { PostDetailRoute({ - _i26.Key? key, + _i27.Key? key, required String id, - List<_i25.PageRouteInfo>? children, + List<_i26.PageRouteInfo>? children, }) : super( PostDetailRoute.name, args: PostDetailRouteArgs(key: key, id: id), @@ -814,14 +831,14 @@ class PostDetailRoute extends _i25.PageRouteInfo { static const String name = 'PostDetailRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final pathParams = data.inheritedPathParams; final args = data.argsAs( orElse: () => PostDetailRouteArgs(id: pathParams.getString('id')), ); - return _i17.PostDetailScreen(key: args.key, id: args.id); + return _i18.PostDetailScreen(key: args.key, id: args.id); }, ); } @@ -829,7 +846,7 @@ class PostDetailRoute extends _i25.PageRouteInfo { class PostDetailRouteArgs { const PostDetailRouteArgs({this.key, required this.id}); - final _i26.Key? key; + final _i27.Key? key; final String id; @@ -840,12 +857,12 @@ class PostDetailRouteArgs { } /// generated route for -/// [_i16.PostEditScreen] -class PostEditRoute extends _i25.PageRouteInfo { +/// [_i17.PostEditScreen] +class PostEditRoute extends _i26.PageRouteInfo { PostEditRoute({ - _i26.Key? key, + _i27.Key? key, required String id, - List<_i25.PageRouteInfo>? children, + List<_i26.PageRouteInfo>? children, }) : super( PostEditRoute.name, args: PostEditRouteArgs(key: key, id: id), @@ -855,14 +872,14 @@ class PostEditRoute extends _i25.PageRouteInfo { static const String name = 'PostEditRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final pathParams = data.inheritedPathParams; final args = data.argsAs( orElse: () => PostEditRouteArgs(id: pathParams.getString('id')), ); - return _i16.PostEditScreen(key: args.key, id: args.id); + return _i17.PostEditScreen(key: args.key, id: args.id); }, ); } @@ -870,7 +887,7 @@ class PostEditRoute extends _i25.PageRouteInfo { class PostEditRouteArgs { const PostEditRouteArgs({this.key, required this.id}); - final _i26.Key? key; + final _i27.Key? key; final String id; @@ -881,13 +898,13 @@ class PostEditRouteArgs { } /// generated route for -/// [_i18.PublisherProfileScreen] +/// [_i19.PublisherProfileScreen] class PublisherProfileRoute - extends _i25.PageRouteInfo { + extends _i26.PageRouteInfo { PublisherProfileRoute({ - _i26.Key? key, + _i27.Key? key, required String name, - List<_i25.PageRouteInfo>? children, + List<_i26.PageRouteInfo>? children, }) : super( PublisherProfileRoute.name, args: PublisherProfileRouteArgs(key: key, name: name), @@ -897,7 +914,7 @@ class PublisherProfileRoute static const String name = 'PublisherProfileRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final pathParams = data.inheritedPathParams; @@ -905,7 +922,7 @@ class PublisherProfileRoute orElse: () => PublisherProfileRouteArgs(name: pathParams.getString('name')), ); - return _i18.PublisherProfileScreen(key: args.key, name: args.name); + return _i19.PublisherProfileScreen(key: args.key, name: args.name); }, ); } @@ -913,7 +930,7 @@ class PublisherProfileRoute class PublisherProfileRouteArgs { const PublisherProfileRouteArgs({this.key, required this.name}); - final _i26.Key? key; + final _i27.Key? key; final String name; @@ -924,12 +941,12 @@ class PublisherProfileRouteArgs { } /// generated route for -/// [_i19.RealmDetailScreen] -class RealmDetailRoute extends _i25.PageRouteInfo { +/// [_i20.RealmDetailScreen] +class RealmDetailRoute extends _i26.PageRouteInfo { RealmDetailRoute({ - _i26.Key? key, + _i27.Key? key, required String slug, - List<_i25.PageRouteInfo>? children, + List<_i26.PageRouteInfo>? children, }) : super( RealmDetailRoute.name, args: RealmDetailRouteArgs(key: key, slug: slug), @@ -939,14 +956,14 @@ class RealmDetailRoute extends _i25.PageRouteInfo { static const String name = 'RealmDetailRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final pathParams = data.inheritedPathParams; final args = data.argsAs( orElse: () => RealmDetailRouteArgs(slug: pathParams.getString('slug')), ); - return _i19.RealmDetailScreen(key: args.key, slug: args.slug); + return _i20.RealmDetailScreen(key: args.key, slug: args.slug); }, ); } @@ -954,7 +971,7 @@ class RealmDetailRoute extends _i25.PageRouteInfo { class RealmDetailRouteArgs { const RealmDetailRouteArgs({this.key, required this.slug}); - final _i26.Key? key; + final _i27.Key? key; final String slug; @@ -966,13 +983,13 @@ class RealmDetailRouteArgs { /// generated route for /// [_i10.RealmListScreen] -class RealmListRoute extends _i25.PageRouteInfo { - const RealmListRoute({List<_i25.PageRouteInfo>? children}) +class RealmListRoute extends _i26.PageRouteInfo { + const RealmListRoute({List<_i26.PageRouteInfo>? children}) : super(RealmListRoute.name, initialChildren: children); static const String name = 'RealmListRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { return const _i10.RealmListScreen(); @@ -981,33 +998,33 @@ class RealmListRoute extends _i25.PageRouteInfo { } /// generated route for -/// [_i20.RelationshipScreen] -class RelationshipRoute extends _i25.PageRouteInfo { - const RelationshipRoute({List<_i25.PageRouteInfo>? children}) +/// [_i21.RelationshipScreen] +class RelationshipRoute extends _i26.PageRouteInfo { + const RelationshipRoute({List<_i26.PageRouteInfo>? children}) : super(RelationshipRoute.name, initialChildren: children); static const String name = 'RelationshipRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { - return const _i20.RelationshipScreen(); + return const _i21.RelationshipScreen(); }, ); } /// generated route for -/// [_i21.SettingsScreen] -class SettingsRoute extends _i25.PageRouteInfo { - const SettingsRoute({List<_i25.PageRouteInfo>? children}) +/// [_i22.SettingsScreen] +class SettingsRoute extends _i26.PageRouteInfo { + const SettingsRoute({List<_i26.PageRouteInfo>? children}) : super(SettingsRoute.name, initialChildren: children); static const String name = 'SettingsRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { - return const _i21.SettingsScreen(); + return const _i22.SettingsScreen(); }, ); } @@ -1015,12 +1032,12 @@ class SettingsRoute extends _i25.PageRouteInfo { /// generated route for /// [_i12.StickerPackDetailScreen] class StickerPackDetailRoute - extends _i25.PageRouteInfo { + extends _i26.PageRouteInfo { StickerPackDetailRoute({ - _i26.Key? key, + _i27.Key? key, required String pubName, required String id, - List<_i25.PageRouteInfo>? children, + List<_i26.PageRouteInfo>? children, }) : super( StickerPackDetailRoute.name, args: StickerPackDetailRouteArgs(key: key, pubName: pubName, id: id), @@ -1030,7 +1047,7 @@ class StickerPackDetailRoute static const String name = 'StickerPackDetailRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final pathParams = data.inheritedPathParams; @@ -1057,7 +1074,7 @@ class StickerPackDetailRouteArgs { required this.id, }); - final _i26.Key? key; + final _i27.Key? key; final String pubName; @@ -1071,11 +1088,11 @@ class StickerPackDetailRouteArgs { /// generated route for /// [_i11.StickersScreen] -class StickersRoute extends _i25.PageRouteInfo { +class StickersRoute extends _i26.PageRouteInfo { StickersRoute({ - _i26.Key? key, + _i27.Key? key, required String pubName, - List<_i25.PageRouteInfo>? children, + List<_i26.PageRouteInfo>? children, }) : super( StickersRoute.name, args: StickersRouteArgs(key: key, pubName: pubName), @@ -1085,7 +1102,7 @@ class StickersRoute extends _i25.PageRouteInfo { static const String name = 'StickersRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final pathParams = data.inheritedPathParams; @@ -1100,7 +1117,7 @@ class StickersRoute extends _i25.PageRouteInfo { class StickersRouteArgs { const StickersRouteArgs({this.key, required this.pubName}); - final _i26.Key? key; + final _i27.Key? key; final String pubName; @@ -1111,14 +1128,14 @@ class StickersRouteArgs { } /// generated route for -/// [_i22.TabsNavigationWidget] +/// [_i23.TabsNavigationWidget] class TabsNavigationWidget - extends _i25.PageRouteInfo { + extends _i26.PageRouteInfo { TabsNavigationWidget({ - _i26.Key? key, - required _i26.Widget child, - required _i29.AppRouter router, - List<_i25.PageRouteInfo>? children, + _i27.Key? key, + required _i27.Widget child, + required _i30.AppRouter router, + List<_i26.PageRouteInfo>? children, }) : super( TabsNavigationWidget.name, args: TabsNavigationWidgetArgs(key: key, child: child, router: router), @@ -1127,11 +1144,11 @@ class TabsNavigationWidget static const String name = 'TabsNavigationWidget'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { final args = data.argsAs(); - return _i22.TabsNavigationWidget( + return _i23.TabsNavigationWidget( key: args.key, child: args.child, router: args.router, @@ -1147,11 +1164,11 @@ class TabsNavigationWidgetArgs { required this.router, }); - final _i26.Key? key; + final _i27.Key? key; - final _i26.Widget child; + final _i27.Widget child; - final _i29.AppRouter router; + final _i30.AppRouter router; @override String toString() { @@ -1160,33 +1177,33 @@ class TabsNavigationWidgetArgs { } /// generated route for -/// [_i23.UpdateProfileScreen] -class UpdateProfileRoute extends _i25.PageRouteInfo { - const UpdateProfileRoute({List<_i25.PageRouteInfo>? children}) +/// [_i24.UpdateProfileScreen] +class UpdateProfileRoute extends _i26.PageRouteInfo { + const UpdateProfileRoute({List<_i26.PageRouteInfo>? children}) : super(UpdateProfileRoute.name, initialChildren: children); static const String name = 'UpdateProfileRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { - return const _i23.UpdateProfileScreen(); + return const _i24.UpdateProfileScreen(); }, ); } /// generated route for -/// [_i24.WalletScreen] -class WalletRoute extends _i25.PageRouteInfo { - const WalletRoute({List<_i25.PageRouteInfo>? children}) +/// [_i25.WalletScreen] +class WalletRoute extends _i26.PageRouteInfo { + const WalletRoute({List<_i26.PageRouteInfo>? children}) : super(WalletRoute.name, initialChildren: children); static const String name = 'WalletRoute'; - static _i25.PageInfo page = _i25.PageInfo( + static _i26.PageInfo page = _i26.PageInfo( name, builder: (data) { - return const _i24.WalletScreen(); + return const _i25.WalletScreen(); }, ); } diff --git a/lib/screens/account.dart b/lib/screens/account.dart index 297f963..27dd321 100644 --- a/lib/screens/account.dart +++ b/lib/screens/account.dart @@ -9,6 +9,7 @@ import 'package:island/pods/message.dart'; import 'package:island/pods/network.dart'; import 'package:island/pods/userinfo.dart'; import 'package:island/route.gr.dart'; +import 'package:island/screens/notification.dart'; import 'package:island/services/responsive.dart'; import 'package:island/widgets/account/status.dart'; import 'package:island/widgets/account/leveling_progress.dart'; @@ -52,6 +53,9 @@ class AccountScreen extends HookConsumerWidget { } final user = ref.watch(userInfoProvider); + final notificationUnreadCount = ref.watch( + notificationUnreadCountNotifierProvider, + ); if (!user.hasValue || user.value == null) { return _UnauthorizedAccountScreen(); @@ -168,12 +172,20 @@ class AccountScreen extends HookConsumerWidget { const Gap(8), ListTile( minTileHeight: 48, - leading: const Icon(Symbols.public), + leading: const Icon(Symbols.notifications), trailing: const Icon(Symbols.chevron_right), contentPadding: EdgeInsets.symmetric(horizontal: 24), - title: Text('publishers').tr(), + title: Row( + children: [ + Expanded(child: Text('notifications').tr()), + Badge.count( + count: notificationUnreadCount.value ?? 0, + isLabelVisible: (notificationUnreadCount.value ?? 0) > 0, + ), + ], + ), onTap: () { - context.router.push(ManagedPublisherRoute()); + context.router.push(NotificationRoute()); }, ), ListTile( diff --git a/lib/screens/auth/tabs.dart b/lib/screens/auth/tabs.dart index d7f9c2c..a3b7cf7 100644 --- a/lib/screens/auth/tabs.dart +++ b/lib/screens/auth/tabs.dart @@ -5,6 +5,7 @@ import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/route.dart'; import 'package:island/route.gr.dart'; +import 'package:island/screens/notification.dart'; import 'package:island/services/responsive.dart'; import 'package:material_symbols_icons/symbols.dart'; @@ -17,7 +18,6 @@ class TabNavigationObserver extends AutoRouterObserver { @override void didPush(Route route, Route? previousRoute) { Future(() { - print('didPush: ${route.settings.name}'); onChange(route.settings.name); }); } @@ -25,7 +25,6 @@ class TabNavigationObserver extends AutoRouterObserver { @override void didPop(Route route, Route? previousRoute) { Future(() { - print('didPop: ${previousRoute?.settings.name}'); onChange(previousRoute?.settings.name); }); } @@ -46,7 +45,10 @@ class TabsNavigationWidget extends HookConsumerWidget { final useHorizontalLayout = isWideScreen(context); final useExpandableLayout = isWidestScreen(context); final currentRoute = ref.watch(currentRouteProvider); - print('currentRoute: $currentRoute'); + + final notificationUnreadCount = ref.watch( + notificationUnreadCountNotifierProvider, + ); int activeIndex = 0; @@ -62,7 +64,11 @@ class TabsNavigationWidget extends HookConsumerWidget { ), NavigationDestination( label: 'account'.tr(), - icon: const Icon(Symbols.account_circle), + icon: Badge.count( + count: notificationUnreadCount.value ?? 0, + isLabelVisible: (notificationUnreadCount.value ?? 0) > 0, + child: const Icon(Symbols.account_circle), + ), ), ]; diff --git a/lib/screens/notification.dart b/lib/screens/notification.dart new file mode 100644 index 0000000..88930d9 --- /dev/null +++ b/lib/screens/notification.dart @@ -0,0 +1,194 @@ +import 'dart:math' as math; + +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:island/models/user.dart'; +import 'package:island/pods/network.dart'; +import 'package:island/widgets/alert.dart'; +import 'package:island/widgets/content/markdown.dart'; +import 'package:relative_time/relative_time.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; +import 'package:styled_widget/styled_widget.dart'; +import 'package:url_launcher/url_launcher.dart'; + +part 'notification.g.dart'; + +@riverpod +class NotificationUnreadCountNotifier + extends _$NotificationUnreadCountNotifier { + @override + Future build() async { + try { + final client = ref.read(apiClientProvider); + final response = await client.get('/notifications/count'); + return (response.data as num).toInt(); + } catch (_) { + return 0; + } + } + + Future decrement(int count) async { + final current = await future; + state = AsyncData(math.min(current - count, 0)); + } +} + +@riverpod +class NotificationListNotifier extends _$NotificationListNotifier + with CursorPagingNotifierMixin { + static const int _pageSize = 5; + + @override + Future> build() => fetch(cursor: null); + + @override + Future> fetch({ + required String? cursor, + }) async { + final client = ref.read(apiClientProvider); + final offset = cursor == null ? 0 : int.parse(cursor); + + final queryParams = {'offset': offset, 'take': _pageSize}; + + final response = await client.get( + '/notifications', + queryParameters: queryParams, + ); + final total = int.parse(response.headers.value('X-Total') ?? '0'); + final List data = response.data; + final notifications = + data.map((json) => SnNotification.fromJson(json)).toList(); + + final hasMore = offset + notifications.length < total; + final nextCursor = + hasMore ? (offset + notifications.length).toString() : null; + final unreadCount = notifications.where((n) => n.viewedAt == null).length; + ref + .read(notificationUnreadCountNotifierProvider.notifier) + .decrement(unreadCount); + + return CursorPagingData( + items: notifications, + hasMore: hasMore, + nextCursor: nextCursor, + ); + } +} + +@RoutePage() +class NotificationScreen extends HookConsumerWidget { + const NotificationScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Scaffold( + appBar: AppBar(title: const Text('notifications').tr()), + body: PagingHelperView( + provider: notificationListNotifierProvider, + futureRefreshable: notificationListNotifierProvider.future, + notifierRefreshable: notificationListNotifierProvider.notifier, + contentBuilder: + (data, widgetCount, endItemView) => ListView.builder( + itemCount: widgetCount, + itemBuilder: (context, index) { + if (index == widgetCount - 1) { + return endItemView; + } + + final notification = data.items[index]; + return ListTile( + isThreeLine: true, + contentPadding: EdgeInsets.symmetric( + horizontal: 16, + vertical: 8, + ), + title: Text(notification.title), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + if (notification.subtitle.isNotEmpty) + Text(notification.subtitle).bold(), + Row( + spacing: 6, + children: [ + Text( + DateFormat().format( + notification.createdAt.toLocal(), + ), + ).fontSize(11), + Text('ยท').fontSize(11).bold(), + Text( + RelativeTime( + context, + ).format(notification.createdAt.toLocal()), + ).fontSize(11), + ], + ).opacity(0.75).padding(bottom: 4), + MarkdownTextContent( + content: notification.content, + textStyle: Theme.of( + context, + ).textTheme.bodyMedium?.copyWith( + color: Theme.of( + context, + ).colorScheme.onSurface.withOpacity(0.8), + ), + ), + ], + ), + trailing: + notification.viewedAt == null + ? null + : Container( + width: 12, + height: 12, + decoration: const BoxDecoration( + color: Colors.blue, + shape: BoxShape.circle, + ), + ), + onTap: () { + if (notification.meta['link'] is String) { + final href = notification.meta['link']; + final uri = Uri.tryParse(href); + if (uri == null) { + showSnackBar( + context, + 'brokenLink'.tr(args: []), + action: SnackBarAction( + label: 'copyToClipboard'.tr(), + onPressed: () { + Clipboard.setData(ClipboardData(text: href)); + clearSnackBar(context); + }, + ), + ); + return; + } + if (uri.scheme == 'solian') { + context.router.pushPath( + ['', uri.host, ...uri.pathSegments].join('/'), + ); + return; + } + showConfirmAlert( + 'openLinkConfirmDescription'.tr(args: [href]), + 'openLinkConfirm'.tr(), + ).then((value) { + if (value) { + launchUrl(uri, mode: LaunchMode.externalApplication); + } + }); + } + }, + ); + }, + ), + ), + ); + } +} diff --git a/lib/screens/notification.g.dart b/lib/screens/notification.g.dart new file mode 100644 index 0000000..2026f43 --- /dev/null +++ b/lib/screens/notification.g.dart @@ -0,0 +1,52 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'notification.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$notificationUnreadCountNotifierHash() => + r'ddec25e8e693b8feb800c085ef87d65f0d172341'; + +/// See also [NotificationUnreadCountNotifier]. +@ProviderFor(NotificationUnreadCountNotifier) +final notificationUnreadCountNotifierProvider = + AutoDisposeAsyncNotifierProvider< + NotificationUnreadCountNotifier, + int + >.internal( + NotificationUnreadCountNotifier.new, + name: r'notificationUnreadCountNotifierProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : _$notificationUnreadCountNotifierHash, + dependencies: null, + allTransitiveDependencies: null, + ); + +typedef _$NotificationUnreadCountNotifier = AutoDisposeAsyncNotifier; +String _$notificationListNotifierHash() => + r'934a47bc2ce9e75699a4f53e2169470fd0c04a53'; + +/// See also [NotificationListNotifier]. +@ProviderFor(NotificationListNotifier) +final notificationListNotifierProvider = AutoDisposeAsyncNotifierProvider< + NotificationListNotifier, + CursorPagingData +>.internal( + NotificationListNotifier.new, + name: r'notificationListNotifierProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : _$notificationListNotifierHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$NotificationListNotifier = + AutoDisposeAsyncNotifier>; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart index 1798f07..ab0983f 100644 --- a/lib/screens/settings.dart +++ b/lib/screens/settings.dart @@ -4,6 +4,7 @@ import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:island/widgets/alert.dart'; import 'package:island/widgets/app_scaffold.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:styled_widget/styled_widget.dart'; @@ -87,6 +88,7 @@ class SettingsScreen extends HookConsumerWidget { kNetworkServerDefault, ); ref.invalidate(serverUrlProvider); + showSnackBar(context, 'settingsApplied'.tr()); }, ), border: OutlineInputBorder( @@ -98,6 +100,7 @@ class SettingsScreen extends HookConsumerWidget { if (value.isNotEmpty) { prefs.setString(kNetworkServerStoreKey, value); ref.invalidate(serverUrlProvider); + showSnackBar(context, 'settingsApplied'.tr()); } }, ), diff --git a/lib/widgets/content/markdown.dart b/lib/widgets/content/markdown.dart index b4abdad..47ab0c1 100644 --- a/lib/widgets/content/markdown.dart +++ b/lib/widgets/content/markdown.dart @@ -71,8 +71,8 @@ class MarkdownTextContent extends HookConsumerWidget { style: linkStyle ?? TextStyle(color: Theme.of(context).colorScheme.primary), - onTap: (herf) { - final url = Uri.tryParse(herf); + onTap: (href) { + final url = Uri.tryParse(href); if (url != null) { if (url.scheme == 'solian') { context.router.pushPath( @@ -96,11 +96,11 @@ class MarkdownTextContent extends HookConsumerWidget { } else { showSnackBar( context, - 'brokenLink'.tr(args: [herf]), + 'brokenLink'.tr(args: [href]), action: SnackBarAction( label: 'copyToClipboard'.tr(), onPressed: () { - Clipboard.setData(ClipboardData(text: herf)); + Clipboard.setData(ClipboardData(text: href)); clearSnackBar(context); }, ),