Compare commits
	
		
			8 Commits
		
	
	
		
			3.2.0+125
			...
			001897bbcd
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 001897bbcd | |||
| 76b39f2df3 | |||
| 509b3e145b | |||
| 2b80ebc2d0 | |||
| 0ab908dd2a | |||
| 6007467e7a | |||
| 3745157c42 | |||
| 94481ec7bd | 
| @@ -334,6 +334,7 @@ | ||||
|   "walletCreate": "Create a Wallet", | ||||
|   "settingsServerUrl": "Server URL", | ||||
|   "settingsApplied": "The settings has been applied.", | ||||
|   "settingsCustomFontsHelper": "Use comma to seprate.", | ||||
|   "notifications": "Notifications", | ||||
|   "posts": "Posts", | ||||
|   "settingsBackgroundImage": "Background Image", | ||||
|   | ||||
| @@ -300,6 +300,7 @@ | ||||
|   "walletCreate": "创建钱包", | ||||
|   "settingsServerUrl": "服务器 URL", | ||||
|   "settingsApplied": "设置已应用。", | ||||
|   "settingsCustomFontsHelper": "用逗号分隔。", | ||||
|   "notifications": "通知", | ||||
|   "posts": "帖子", | ||||
|   "settingsBackgroundImage": "背景图片", | ||||
|   | ||||
| @@ -245,7 +245,7 @@ PODS: | ||||
|     - PromisesObjC (= 2.4.0) | ||||
|   - receive_sharing_intent (1.8.1): | ||||
|     - Flutter | ||||
|   - record_ios (1.0.0): | ||||
|   - record_ios (1.1.0): | ||||
|     - Flutter | ||||
|   - SAMKeychain (1.5.3) | ||||
|   - SDWebImage (5.21.1): | ||||
| @@ -510,7 +510,7 @@ SPEC CHECKSUMS: | ||||
|   PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 | ||||
|   PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 | ||||
|   receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00 | ||||
|   record_ios: fee1c924aa4879b882ebca2b4bce6011bcfc3d8b | ||||
|   record_ios: f75fa1d57f840012775c0e93a38a7f3ceea1a374 | ||||
|   SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c | ||||
|   SDWebImage: f29024626962457f3470184232766516dee8dfea | ||||
|   share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a | ||||
|   | ||||
| @@ -11,8 +11,8 @@ sealed class SnScrappedLink with _$SnScrappedLink { | ||||
|     required String title, | ||||
|     required String? description, | ||||
|     required String? imageUrl, | ||||
|     required String faviconUrl, | ||||
|     required String siteName, | ||||
|     required String? faviconUrl, | ||||
|     required String? siteName, | ||||
|     required String? contentType, | ||||
|     required String? author, | ||||
|     required DateTime? publishedDate, | ||||
|   | ||||
| @@ -15,7 +15,7 @@ T _$identity<T>(T value) => value; | ||||
| /// @nodoc | ||||
| mixin _$SnScrappedLink { | ||||
|  | ||||
|  String get type; String get url; String get title; String? get description; String? get imageUrl; String get faviconUrl; String get siteName; String? get contentType; String? get author; DateTime? get publishedDate; | ||||
|  String get type; String get url; String get title; String? get description; String? get imageUrl; String? get faviconUrl; String? get siteName; String? get contentType; String? get author; DateTime? get publishedDate; | ||||
| /// Create a copy of SnScrappedLink | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @JsonKey(includeFromJson: false, includeToJson: false) | ||||
| @@ -48,7 +48,7 @@ abstract mixin class $SnScrappedLinkCopyWith<$Res>  { | ||||
|   factory $SnScrappedLinkCopyWith(SnScrappedLink value, $Res Function(SnScrappedLink) _then) = _$SnScrappedLinkCopyWithImpl; | ||||
| @useResult | ||||
| $Res call({ | ||||
|  String type, String url, String title, String? description, String? imageUrl, String faviconUrl, String siteName, String? contentType, String? author, DateTime? publishedDate | ||||
|  String type, String url, String title, String? description, String? imageUrl, String? faviconUrl, String? siteName, String? contentType, String? author, DateTime? publishedDate | ||||
| }); | ||||
|  | ||||
|  | ||||
| @@ -65,16 +65,16 @@ class _$SnScrappedLinkCopyWithImpl<$Res> | ||||
|  | ||||
| /// Create a copy of SnScrappedLink | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? url = null,Object? title = null,Object? description = freezed,Object? imageUrl = freezed,Object? faviconUrl = null,Object? siteName = null,Object? contentType = freezed,Object? author = freezed,Object? publishedDate = freezed,}) { | ||||
| @pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? url = null,Object? title = null,Object? description = freezed,Object? imageUrl = freezed,Object? faviconUrl = freezed,Object? siteName = freezed,Object? contentType = freezed,Object? author = freezed,Object? publishedDate = freezed,}) { | ||||
|   return _then(_self.copyWith( | ||||
| type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable | ||||
| as String,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable | ||||
| as String,title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable | ||||
| as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable | ||||
| as String?,imageUrl: freezed == imageUrl ? _self.imageUrl : imageUrl // ignore: cast_nullable_to_non_nullable | ||||
| as String?,faviconUrl: null == faviconUrl ? _self.faviconUrl : faviconUrl // ignore: cast_nullable_to_non_nullable | ||||
| as String,siteName: null == siteName ? _self.siteName : siteName // ignore: cast_nullable_to_non_nullable | ||||
| as String,contentType: freezed == contentType ? _self.contentType : contentType // ignore: cast_nullable_to_non_nullable | ||||
| as String?,faviconUrl: freezed == faviconUrl ? _self.faviconUrl : faviconUrl // ignore: cast_nullable_to_non_nullable | ||||
| as String?,siteName: freezed == siteName ? _self.siteName : siteName // ignore: cast_nullable_to_non_nullable | ||||
| as String?,contentType: freezed == contentType ? _self.contentType : contentType // ignore: cast_nullable_to_non_nullable | ||||
| as String?,author: freezed == author ? _self.author : author // ignore: cast_nullable_to_non_nullable | ||||
| as String?,publishedDate: freezed == publishedDate ? _self.publishedDate : publishedDate // ignore: cast_nullable_to_non_nullable | ||||
| as DateTime?, | ||||
| @@ -159,7 +159,7 @@ return $default(_that);case _: | ||||
| /// } | ||||
| /// ``` | ||||
|  | ||||
| @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String type,  String url,  String title,  String? description,  String? imageUrl,  String faviconUrl,  String siteName,  String? contentType,  String? author,  DateTime? publishedDate)?  $default,{required TResult orElse(),}) {final _that = this; | ||||
| @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String type,  String url,  String title,  String? description,  String? imageUrl,  String? faviconUrl,  String? siteName,  String? contentType,  String? author,  DateTime? publishedDate)?  $default,{required TResult orElse(),}) {final _that = this; | ||||
| switch (_that) { | ||||
| case _SnScrappedLink() when $default != null: | ||||
| return $default(_that.type,_that.url,_that.title,_that.description,_that.imageUrl,_that.faviconUrl,_that.siteName,_that.contentType,_that.author,_that.publishedDate);case _: | ||||
| @@ -180,7 +180,7 @@ return $default(_that.type,_that.url,_that.title,_that.description,_that.imageUr | ||||
| /// } | ||||
| /// ``` | ||||
|  | ||||
| @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String type,  String url,  String title,  String? description,  String? imageUrl,  String faviconUrl,  String siteName,  String? contentType,  String? author,  DateTime? publishedDate)  $default,) {final _that = this; | ||||
| @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String type,  String url,  String title,  String? description,  String? imageUrl,  String? faviconUrl,  String? siteName,  String? contentType,  String? author,  DateTime? publishedDate)  $default,) {final _that = this; | ||||
| switch (_that) { | ||||
| case _SnScrappedLink(): | ||||
| return $default(_that.type,_that.url,_that.title,_that.description,_that.imageUrl,_that.faviconUrl,_that.siteName,_that.contentType,_that.author,_that.publishedDate);} | ||||
| @@ -197,7 +197,7 @@ return $default(_that.type,_that.url,_that.title,_that.description,_that.imageUr | ||||
| /// } | ||||
| /// ``` | ||||
|  | ||||
| @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String type,  String url,  String title,  String? description,  String? imageUrl,  String faviconUrl,  String siteName,  String? contentType,  String? author,  DateTime? publishedDate)?  $default,) {final _that = this; | ||||
| @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String type,  String url,  String title,  String? description,  String? imageUrl,  String? faviconUrl,  String? siteName,  String? contentType,  String? author,  DateTime? publishedDate)?  $default,) {final _that = this; | ||||
| switch (_that) { | ||||
| case _SnScrappedLink() when $default != null: | ||||
| return $default(_that.type,_that.url,_that.title,_that.description,_that.imageUrl,_that.faviconUrl,_that.siteName,_that.contentType,_that.author,_that.publishedDate);case _: | ||||
| @@ -220,8 +220,8 @@ class _SnScrappedLink implements SnScrappedLink { | ||||
| @override final  String title; | ||||
| @override final  String? description; | ||||
| @override final  String? imageUrl; | ||||
| @override final  String faviconUrl; | ||||
| @override final  String siteName; | ||||
| @override final  String? faviconUrl; | ||||
| @override final  String? siteName; | ||||
| @override final  String? contentType; | ||||
| @override final  String? author; | ||||
| @override final  DateTime? publishedDate; | ||||
| @@ -259,7 +259,7 @@ abstract mixin class _$SnScrappedLinkCopyWith<$Res> implements $SnScrappedLinkCo | ||||
|   factory _$SnScrappedLinkCopyWith(_SnScrappedLink value, $Res Function(_SnScrappedLink) _then) = __$SnScrappedLinkCopyWithImpl; | ||||
| @override @useResult | ||||
| $Res call({ | ||||
|  String type, String url, String title, String? description, String? imageUrl, String faviconUrl, String siteName, String? contentType, String? author, DateTime? publishedDate | ||||
|  String type, String url, String title, String? description, String? imageUrl, String? faviconUrl, String? siteName, String? contentType, String? author, DateTime? publishedDate | ||||
| }); | ||||
|  | ||||
|  | ||||
| @@ -276,16 +276,16 @@ class __$SnScrappedLinkCopyWithImpl<$Res> | ||||
|  | ||||
| /// Create a copy of SnScrappedLink | ||||
| /// with the given fields replaced by the non-null parameter values. | ||||
| @override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? url = null,Object? title = null,Object? description = freezed,Object? imageUrl = freezed,Object? faviconUrl = null,Object? siteName = null,Object? contentType = freezed,Object? author = freezed,Object? publishedDate = freezed,}) { | ||||
| @override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? url = null,Object? title = null,Object? description = freezed,Object? imageUrl = freezed,Object? faviconUrl = freezed,Object? siteName = freezed,Object? contentType = freezed,Object? author = freezed,Object? publishedDate = freezed,}) { | ||||
|   return _then(_SnScrappedLink( | ||||
| type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable | ||||
| as String,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable | ||||
| as String,title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable | ||||
| as String,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable | ||||
| as String?,imageUrl: freezed == imageUrl ? _self.imageUrl : imageUrl // ignore: cast_nullable_to_non_nullable | ||||
| as String?,faviconUrl: null == faviconUrl ? _self.faviconUrl : faviconUrl // ignore: cast_nullable_to_non_nullable | ||||
| as String,siteName: null == siteName ? _self.siteName : siteName // ignore: cast_nullable_to_non_nullable | ||||
| as String,contentType: freezed == contentType ? _self.contentType : contentType // ignore: cast_nullable_to_non_nullable | ||||
| as String?,faviconUrl: freezed == faviconUrl ? _self.faviconUrl : faviconUrl // ignore: cast_nullable_to_non_nullable | ||||
| as String?,siteName: freezed == siteName ? _self.siteName : siteName // ignore: cast_nullable_to_non_nullable | ||||
| as String?,contentType: freezed == contentType ? _self.contentType : contentType // ignore: cast_nullable_to_non_nullable | ||||
| as String?,author: freezed == author ? _self.author : author // ignore: cast_nullable_to_non_nullable | ||||
| as String?,publishedDate: freezed == publishedDate ? _self.publishedDate : publishedDate // ignore: cast_nullable_to_non_nullable | ||||
| as DateTime?, | ||||
|   | ||||
| @@ -13,8 +13,8 @@ _SnScrappedLink _$SnScrappedLinkFromJson(Map<String, dynamic> json) => | ||||
|       title: json['title'] as String, | ||||
|       description: json['description'] as String?, | ||||
|       imageUrl: json['image_url'] as String?, | ||||
|       faviconUrl: json['favicon_url'] as String, | ||||
|       siteName: json['site_name'] as String, | ||||
|       faviconUrl: json['favicon_url'] as String?, | ||||
|       siteName: json['site_name'] as String?, | ||||
|       contentType: json['content_type'] as String?, | ||||
|       author: json['author'] as String?, | ||||
|       publishedDate: | ||||
|   | ||||
| @@ -23,6 +23,8 @@ const kAppSoundEffects = 'app_sound_effects'; | ||||
| const kAppAprilFoolFeatures = 'app_april_fool_features'; | ||||
| const kAppWindowSize = 'app_window_size'; | ||||
| const kAppEnterToSend = 'app_enter_to_send'; | ||||
| const kFeaturedPostsCollapsedId = | ||||
|     'featured_posts_collapsed_id'; // Key for storing the ID of the collapsed featured post | ||||
|  | ||||
| const Map<String, FilterQuality> kImageQualityLevel = { | ||||
|   'settingsImageQualityLowest': FilterQuality.none, | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import 'package:animations/animations.dart'; | ||||
| import 'package:firebase_analytics/firebase_analytics.dart'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:go_router/go_router.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| @@ -56,6 +56,20 @@ final rootNavigatorKey = GlobalKey<NavigatorState>(); | ||||
| final _shellNavigatorKey = GlobalKey<NavigatorState>(); | ||||
| final _tabsShellKey = GlobalKey<NavigatorState>(); | ||||
|  | ||||
| Widget _tabPagesTransitionBuilder( | ||||
|   BuildContext context, | ||||
|   Animation<double> animation, | ||||
|   Animation<double> secondaryAnimation, | ||||
|   Widget child, | ||||
| ) { | ||||
|   return FadeThroughTransition( | ||||
|     animation: animation, | ||||
|     secondaryAnimation: secondaryAnimation, | ||||
|     fillColor: Theme.of(context).colorScheme.surface, | ||||
|     child: child, | ||||
|   ); | ||||
| } | ||||
|  | ||||
| // Provider for the router | ||||
| final routerProvider = Provider<GoRouter>((ref) { | ||||
|   return GoRouter( | ||||
| @@ -339,7 +353,12 @@ final routerProvider = Provider<GoRouter>((ref) { | ||||
|               GoRoute( | ||||
|                 name: 'explore', | ||||
|                 path: '/', | ||||
|                 builder: (context, state) => const ExploreScreen(), | ||||
|                 pageBuilder: | ||||
|                     (context, state) => CustomTransitionPage( | ||||
|                       key: const ValueKey('explore'), | ||||
|                       child: const ExploreScreen(), | ||||
|                       transitionsBuilder: _tabPagesTransitionBuilder, | ||||
|                     ), | ||||
|               ), | ||||
|               GoRoute( | ||||
|                 name: 'postSearch', | ||||
| @@ -389,8 +408,12 @@ final routerProvider = Provider<GoRouter>((ref) { | ||||
|  | ||||
|               // Chat tab | ||||
|               ShellRoute( | ||||
|                 builder: | ||||
|                     (context, state, child) => ChatShellScreen(child: child), | ||||
|                 pageBuilder: | ||||
|                     (context, state, child) => CustomTransitionPage( | ||||
|                       key: const ValueKey('chat'), | ||||
|                       child: ChatShellScreen(child: child), | ||||
|                       transitionsBuilder: _tabPagesTransitionBuilder, | ||||
|                     ), | ||||
|                 routes: [ | ||||
|                   GoRoute( | ||||
|                     name: 'chatList', | ||||
| @@ -433,7 +456,12 @@ final routerProvider = Provider<GoRouter>((ref) { | ||||
|               GoRoute( | ||||
|                 name: 'realmList', | ||||
|                 path: '/realms', | ||||
|                 builder: (context, state) => const RealmListScreen(), | ||||
|                 pageBuilder: | ||||
|                     (context, state) => CustomTransitionPage( | ||||
|                       key: const ValueKey('realms'), | ||||
|                       child: const RealmListScreen(), | ||||
|                       transitionsBuilder: _tabPagesTransitionBuilder, | ||||
|                     ), | ||||
|                 routes: [ | ||||
|                   GoRoute( | ||||
|                     name: 'realmNew', | ||||
| @@ -461,8 +489,12 @@ final routerProvider = Provider<GoRouter>((ref) { | ||||
|  | ||||
|               // Account tab | ||||
|               ShellRoute( | ||||
|                 builder: | ||||
|                     (context, state, child) => AccountShellScreen(child: child), | ||||
|                 pageBuilder: | ||||
|                     (context, state, child) => CustomTransitionPage( | ||||
|                       key: const ValueKey('account'), | ||||
|                       child: AccountShellScreen(child: child), | ||||
|                       transitionsBuilder: _tabPagesTransitionBuilder, | ||||
|                     ), | ||||
|                 routes: [ | ||||
|                   GoRoute( | ||||
|                     name: 'account', | ||||
|   | ||||
| @@ -178,7 +178,8 @@ class _AboutScreenState extends ConsumerState<AboutScreen> { | ||||
|                                 context, | ||||
|                                 icon: Symbols.label, | ||||
|                                 label: 'aboutDeviceName'.tr(), | ||||
|                                 value: _deviceInfo?.data['name'], | ||||
|                                 value: | ||||
|                                     _deviceInfo?.data['name'] ?? 'unknown'.tr(), | ||||
|                               ), | ||||
|                               _buildInfoItem( | ||||
|                                 context, | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import 'package:island/models/realm.dart'; | ||||
| import 'package:island/models/webfeed.dart'; | ||||
| import 'package:island/pods/event_calendar.dart'; | ||||
| import 'package:island/pods/userinfo.dart'; | ||||
| import 'package:island/screens/notification.dart'; | ||||
| import 'package:island/services/responsive.dart'; | ||||
| import 'package:island/widgets/account/fortune_graph.dart'; | ||||
| import 'package:island/widgets/app_scaffold.dart'; | ||||
| @@ -30,6 +31,33 @@ import 'package:styled_widget/styled_widget.dart'; | ||||
|  | ||||
| part 'explore.g.dart'; | ||||
|  | ||||
| Widget notificationIndicatorWidget( | ||||
|   BuildContext context, { | ||||
|   required int count, | ||||
|   EdgeInsets? margin, | ||||
| }) => Card( | ||||
|   margin: margin, | ||||
|   child: ListTile( | ||||
|     shape: const RoundedRectangleBorder( | ||||
|       borderRadius: BorderRadius.all(Radius.circular(8)), | ||||
|     ), | ||||
|     leading: const Icon(Symbols.notifications), | ||||
|     title: Row( | ||||
|       children: [ | ||||
|         Text('notifications').tr().fontSize(14), | ||||
|         const Gap(8), | ||||
|         Badge(label: Text(count.toString())), | ||||
|       ], | ||||
|     ), | ||||
|     trailing: const Icon(Symbols.chevron_right), | ||||
|     minTileHeight: 40, | ||||
|     contentPadding: EdgeInsets.only(left: 16, right: 15), | ||||
|     onTap: () { | ||||
|       GoRouter.of(context).pushNamed('notifications'); | ||||
|     }, | ||||
|   ), | ||||
| ); | ||||
|  | ||||
| class ExploreScreen extends HookConsumerWidget { | ||||
|   const ExploreScreen({super.key}); | ||||
|  | ||||
| @@ -77,6 +105,10 @@ class ExploreScreen extends HookConsumerWidget { | ||||
|  | ||||
|     final user = ref.watch(userInfoProvider); | ||||
|  | ||||
|     final notificationCount = ref.watch( | ||||
|       notificationUnreadCountNotifierProvider, | ||||
|     ); | ||||
|  | ||||
|     return AppScaffold( | ||||
|       isNoBackground: false, | ||||
|       appBar: AppBar( | ||||
| @@ -200,6 +232,8 @@ class ExploreScreen extends HookConsumerWidget { | ||||
|                 if (user.value != null) | ||||
|                   Flexible( | ||||
|                     flex: 2, | ||||
|                     child: Align( | ||||
|                       alignment: Alignment.topCenter, | ||||
|                       child: SingleChildScrollView( | ||||
|                         child: Column( | ||||
|                           children: [ | ||||
| @@ -215,13 +249,28 @@ class ExploreScreen extends HookConsumerWidget { | ||||
|                                 ); | ||||
|                               }, | ||||
|                             ), | ||||
|                             if (notificationCount.value != null && | ||||
|                                 notificationCount.value! > 0) | ||||
|                               notificationIndicatorWidget( | ||||
|                                 context, | ||||
|                                 count: notificationCount.value ?? 0, | ||||
|                                 margin: EdgeInsets.only( | ||||
|                                   left: 8, | ||||
|                                   right: 12, | ||||
|                                   top: 8, | ||||
|                                 ), | ||||
|                               ), | ||||
|                             PostFeaturedList().padding( | ||||
|                               left: 8, | ||||
|                               right: 12, | ||||
|                               top: 8, | ||||
|                             ), | ||||
|                             FortuneGraphWidget( | ||||
|                             margin: EdgeInsets.only(left: 8, right: 12, top: 8), | ||||
|                               margin: EdgeInsets.only( | ||||
|                                 left: 8, | ||||
|                                 right: 12, | ||||
|                                 top: 8, | ||||
|                               ), | ||||
|                               events: events, | ||||
|                               constrainWidth: true, | ||||
|                               onPointSelected: onDaySelected, | ||||
| @@ -229,6 +278,7 @@ class ExploreScreen extends HookConsumerWidget { | ||||
|                           ], | ||||
|                         ), | ||||
|                       ), | ||||
|                     ), | ||||
|                   ) | ||||
|                 else | ||||
|                   Flexible( | ||||
| @@ -380,6 +430,10 @@ class _ActivityListView extends HookConsumerWidget { | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final user = ref.watch(userInfoProvider); | ||||
|  | ||||
|     final notificationCount = ref.watch( | ||||
|       notificationUnreadCountNotifierProvider, | ||||
|     ); | ||||
|  | ||||
|     return CustomScrollView( | ||||
|       slivers: [ | ||||
|         SliverGap(12), | ||||
| @@ -393,6 +447,14 @@ class _ActivityListView extends HookConsumerWidget { | ||||
|           SliverToBoxAdapter( | ||||
|             child: PostFeaturedList().padding(horizontal: 8, bottom: 4, top: 4), | ||||
|           ), | ||||
|         if (!contentOnly) | ||||
|           SliverToBoxAdapter( | ||||
|             child: notificationIndicatorWidget( | ||||
|               context, | ||||
|               count: notificationCount.value ?? 0, | ||||
|               margin: EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4), | ||||
|             ), | ||||
|           ), | ||||
|         SliverList.builder( | ||||
|           itemCount: widgetCount, | ||||
|           itemBuilder: (context, index) { | ||||
|   | ||||
| @@ -3,14 +3,17 @@ import 'dart:math' as math; | ||||
|  | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:gap/gap.dart'; | ||||
| import 'package:go_router/go_router.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:island/models/account.dart'; | ||||
| import 'package:island/pods/network.dart'; | ||||
| import 'package:island/pods/websocket.dart'; | ||||
| import 'package:island/route.dart'; | ||||
| import 'package:island/widgets/alert.dart'; | ||||
| import 'package:island/widgets/app_scaffold.dart'; | ||||
| import 'package:island/widgets/content/markdown.dart'; | ||||
| import 'package:material_symbols_icons/material_symbols_icons.dart'; | ||||
| import 'package:relative_time/relative_time.dart'; | ||||
| import 'package:riverpod_annotation/riverpod_annotation.dart'; | ||||
| import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; | ||||
| @@ -62,6 +65,10 @@ class NotificationUnreadCountNotifier | ||||
|     final current = await future; | ||||
|     state = AsyncData(math.max(current - count, 0)); | ||||
|   } | ||||
|  | ||||
|   void clear() async { | ||||
|     state = AsyncData(0); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @riverpod | ||||
| @@ -111,8 +118,27 @@ class NotificationScreen extends HookConsumerWidget { | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     Future<void> markAllRead() async { | ||||
|       showLoadingModal(context); | ||||
|       final apiClient = ref.watch(apiClientProvider); | ||||
|       await apiClient.post('/pusher/notifications/all/read'); | ||||
|       if (!context.mounted) return; | ||||
|       hideLoadingModal(context); | ||||
|       ref.invalidate(notificationListNotifierProvider); | ||||
|       ref.watch(notificationUnreadCountNotifierProvider.notifier).clear(); | ||||
|     } | ||||
|  | ||||
|     return AppScaffold( | ||||
|       appBar: AppBar(title: const Text('notifications').tr()), | ||||
|       appBar: AppBar( | ||||
|         title: const Text('notifications').tr(), | ||||
|         actions: [ | ||||
|           IconButton( | ||||
|             onPressed: markAllRead, | ||||
|             icon: const Icon(Symbols.mark_as_unread), | ||||
|           ), | ||||
|           const Gap(8), | ||||
|         ], | ||||
|       ), | ||||
|       body: PagingHelperView( | ||||
|         provider: notificationListNotifierProvider, | ||||
|         futureRefreshable: notificationListNotifierProvider.future, | ||||
|   | ||||
| @@ -51,12 +51,12 @@ class PostSearchNotifier | ||||
|       final offset = cursor == null ? 0 : int.parse(cursor); | ||||
|  | ||||
|       final response = await client.get( | ||||
|         '/sphere/posts/search', | ||||
|         '/sphere/posts', | ||||
|         queryParameters: { | ||||
|           'query': _currentQuery, | ||||
|           'offset': offset, | ||||
|           'take': _pageSize, | ||||
|           'useVector': false, | ||||
|           'vector': false, | ||||
|         }, | ||||
|       ); | ||||
|  | ||||
|   | ||||
| @@ -26,7 +26,12 @@ StreamSubscription<WebSocketPacket> setupNotificationListener( | ||||
|       final notification = SnNotification.fromJson(pkt.data!); | ||||
|       showTopSnackBar( | ||||
|         globalOverlay.currentState!, | ||||
|         NotificationCard(notification: notification), | ||||
|         Center( | ||||
|           child: ConstrainedBox( | ||||
|             constraints: const BoxConstraints(maxWidth: 480), | ||||
|             child: NotificationCard(notification: notification), | ||||
|           ), | ||||
|         ), | ||||
|         onTap: () { | ||||
|           if (notification.meta['action_uri'] != null) { | ||||
|             var uri = notification.meta['action_uri'] as String; | ||||
| @@ -53,9 +58,9 @@ StreamSubscription<WebSocketPacket> setupNotificationListener( | ||||
|                       (Platform.isMacOS || | ||||
|                           Platform.isWindows || | ||||
|                           Platform.isLinux)) | ||||
|                   ? 24 | ||||
|                   ? 28 | ||||
|                   // ignore: use_build_context_synchronously | ||||
|                   : MediaQuery.of(context).padding.top + 8, | ||||
|                   : MediaQuery.of(context).padding.top + 16, | ||||
|           bottom: 16, | ||||
|         ), | ||||
|       ); | ||||
|   | ||||
| @@ -162,7 +162,7 @@ class AccountSessionSheet extends HookConsumerWidget { | ||||
|       try { | ||||
|         final apiClient = ref.watch(apiClientProvider); | ||||
|         await apiClient.patch( | ||||
|           '/accounts/me/devices/$sessionId/label', | ||||
|           '/id/accounts/me/devices/$sessionId/label', | ||||
|           data: jsonEncode(label), | ||||
|         ); | ||||
|         ref.invalidate(authDevicesProvider); | ||||
|   | ||||
| @@ -11,7 +11,12 @@ export 'content/alert.native.dart' | ||||
| void showSnackBar(String message, {SnackBarAction? action}) { | ||||
|   showTopSnackBar( | ||||
|     globalOverlay.currentState!, | ||||
|     Card(child: Text(message).padding(horizontal: 20, vertical: 16)), | ||||
|     ConstrainedBox( | ||||
|       constraints: const BoxConstraints(maxWidth: 480), | ||||
|       child: Center( | ||||
|         child: Card(child: Text(message).padding(horizontal: 20, vertical: 16)), | ||||
|       ), | ||||
|     ), | ||||
|     snackBarPosition: SnackBarPosition.bottom, | ||||
|   ); | ||||
| } | ||||
|   | ||||
| @@ -57,11 +57,11 @@ class EmbedLinkWidget extends StatelessWidget { | ||||
|                     Row( | ||||
|                       children: [ | ||||
|                         // Favicon | ||||
|                         if (link.faviconUrl.isNotEmpty) ...[ | ||||
|                         if (link.faviconUrl?.isNotEmpty ?? false) ...[ | ||||
|                           ClipRRect( | ||||
|                             borderRadius: BorderRadius.circular(4), | ||||
|                             child: UniversalImage( | ||||
|                               uri: link.faviconUrl, | ||||
|                               uri: link.faviconUrl!, | ||||
|                               width: 16, | ||||
|                               height: 16, | ||||
|                               fit: BoxFit.cover, | ||||
| @@ -80,8 +80,8 @@ class EmbedLinkWidget extends StatelessWidget { | ||||
|                         // Site name | ||||
|                         Expanded( | ||||
|                           child: Text( | ||||
|                             link.siteName.isNotEmpty | ||||
|                                 ? link.siteName | ||||
|                             (link.siteName?.isNotEmpty ?? false) | ||||
|                                 ? link.siteName! | ||||
|                                 : Uri.parse(link.url).host, | ||||
|                             style: theme.textTheme.bodySmall?.copyWith( | ||||
|                               color: colorScheme.onSurfaceVariant, | ||||
|   | ||||
| @@ -183,9 +183,15 @@ class MarkdownTextContent extends HookConsumerWidget { | ||||
|                     ); | ||||
|                 } | ||||
|               } | ||||
|               final content = ConstrainedBox( | ||||
|               final content = ClipRRect( | ||||
|                 borderRadius: const BorderRadius.all(Radius.circular(8)), | ||||
|                 child: ConstrainedBox( | ||||
|                   constraints: BoxConstraints(maxHeight: 360), | ||||
|                 child: UniversalImage(uri: uri.toString(), fit: BoxFit.contain), | ||||
|                   child: UniversalImage( | ||||
|                     uri: uri.toString(), | ||||
|                     fit: BoxFit.contain, | ||||
|                   ), | ||||
|                 ), | ||||
|               ); | ||||
|               return content; | ||||
|             }, | ||||
|   | ||||
| @@ -244,7 +244,6 @@ class ComposeSettingsSheet extends HookConsumerWidget { | ||||
|             ), | ||||
|  | ||||
|             // Categories field | ||||
|             // FIXME: Sometimes the entire dropdown crashes: 'package:flutter/src/rendering/stack.dart': Failed assertion: line 799 pos 12: 'firstChild == null || child != null': is not true. | ||||
|             DropdownButtonFormField2<SnPostCategory>( | ||||
|               isExpanded: true, | ||||
|               decoration: InputDecoration( | ||||
| @@ -306,7 +305,7 @@ class ComposeSettingsSheet extends HookConsumerWidget { | ||||
|               value: currentCategories.isEmpty ? null : currentCategories.last, | ||||
|               onChanged: (_) {}, | ||||
|               selectedItemBuilder: (context) { | ||||
|                 return currentCategories.map((item) { | ||||
|                 return (postCategories.value ?? []).map((item) { | ||||
|                   return SingleChildScrollView( | ||||
|                     scrollDirection: Axis.horizontal, | ||||
|                     child: Row( | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import 'package:material_symbols_icons/symbols.dart'; | ||||
| import 'package:riverpod_annotation/riverpod_annotation.dart'; | ||||
| import 'package:island/widgets/post/post_item.dart'; | ||||
| import 'package:styled_widget/styled_widget.dart'; | ||||
| import 'package:island/pods/config.dart'; // Import config.dart for shared preferences keys and provider | ||||
|  | ||||
| part 'post_featured.g.dart'; | ||||
|  | ||||
| @@ -25,7 +26,13 @@ class PostFeaturedList extends HookConsumerWidget { | ||||
|     final featuredPostsAsync = ref.watch(featuredPostsProvider); | ||||
|  | ||||
|     final pageViewController = usePageController(); | ||||
|     final prefs = ref.watch(sharedPreferencesProvider); | ||||
|     final pageViewCurrent = useState(0); | ||||
|     final previousFirstPostId = useState<String?>(null); | ||||
|     final storedCollapsedId = useState<String?>( | ||||
|       prefs.getString(kFeaturedPostsCollapsedId), | ||||
|     ); | ||||
|     final isCollapsed = useState(false); | ||||
|  | ||||
|     useEffect(() { | ||||
|       pageViewController.addListener(() { | ||||
| @@ -34,6 +41,59 @@ class PostFeaturedList extends HookConsumerWidget { | ||||
|       return null; | ||||
|     }, [pageViewController]); | ||||
|  | ||||
|     // Log isCollapsed state changes | ||||
|     useEffect(() { | ||||
|       debugPrint( | ||||
|         'PostFeaturedList: isCollapsed changed to ${isCollapsed.value}', | ||||
|       ); | ||||
|       return null; | ||||
|     }, [isCollapsed.value]); | ||||
|  | ||||
|     useEffect(() { | ||||
|       if (featuredPostsAsync.hasValue && featuredPostsAsync.value!.isNotEmpty) { | ||||
|         final currentFirstPostId = featuredPostsAsync.value!.first.id; | ||||
|         debugPrint( | ||||
|           'PostFeaturedList: Current first post ID: $currentFirstPostId', | ||||
|         ); | ||||
|         debugPrint( | ||||
|           'PostFeaturedList: Previous first post ID: ${previousFirstPostId.value}', | ||||
|         ); | ||||
|         debugPrint( | ||||
|           'PostFeaturedList: Stored collapsed ID: ${storedCollapsedId.value}', | ||||
|         ); | ||||
|  | ||||
|         if (previousFirstPostId.value == null) { | ||||
|           // Initial load | ||||
|           previousFirstPostId.value = currentFirstPostId; | ||||
|           isCollapsed.value = (storedCollapsedId.value == currentFirstPostId); | ||||
|           debugPrint( | ||||
|             'PostFeaturedList: Initial load. isCollapsed set to ${isCollapsed.value}', | ||||
|           ); | ||||
|         } else if (previousFirstPostId.value != currentFirstPostId) { | ||||
|           // First post changed, expand by default | ||||
|           previousFirstPostId.value = currentFirstPostId; | ||||
|           isCollapsed.value = false; | ||||
|           prefs.remove( | ||||
|             kFeaturedPostsCollapsedId, | ||||
|           ); // Clear stored ID if post changes | ||||
|           debugPrint( | ||||
|             'PostFeaturedList: First post changed. isCollapsed set to false.', | ||||
|           ); | ||||
|         } else { | ||||
|           // Same first post, maintain current collapse state | ||||
|           // No change needed for isCollapsed.value unless manually toggled | ||||
|           debugPrint( | ||||
|             'PostFeaturedList: Same first post. Maintaining current collapse state.', | ||||
|           ); | ||||
|         } | ||||
|       } else { | ||||
|         debugPrint( | ||||
|           'PostFeaturedList: featuredPostsAsync has no value or is empty.', | ||||
|         ); | ||||
|       } | ||||
|       return null; | ||||
|     }, [featuredPostsAsync.value]); | ||||
|  | ||||
|     return ClipRRect( | ||||
|       borderRadius: const BorderRadius.all(Radius.circular(8)), | ||||
|       child: Card( | ||||
| @@ -73,10 +133,48 @@ class PostFeaturedList extends HookConsumerWidget { | ||||
|                   }, | ||||
|                   icon: const Icon(Symbols.arrow_right), | ||||
|                 ), | ||||
|                 IconButton( | ||||
|                   padding: EdgeInsets.zero, | ||||
|                   visualDensity: VisualDensity.compact, | ||||
|                   constraints: const BoxConstraints(), | ||||
|                   onPressed: () { | ||||
|                     isCollapsed.value = !isCollapsed.value; | ||||
|                     debugPrint( | ||||
|                       'PostFeaturedList: Manual toggle. isCollapsed set to ${isCollapsed.value}', | ||||
|                     ); | ||||
|                     if (isCollapsed.value && | ||||
|                         featuredPostsAsync.hasValue && | ||||
|                         featuredPostsAsync.value!.isNotEmpty) { | ||||
|                       prefs.setString( | ||||
|                         kFeaturedPostsCollapsedId, | ||||
|                         featuredPostsAsync.value!.first.id, | ||||
|                       ); | ||||
|                       debugPrint( | ||||
|                         'PostFeaturedList: Stored collapsed ID: ${featuredPostsAsync.value!.first.id}', | ||||
|                       ); | ||||
|                     } else { | ||||
|                       prefs.remove(kFeaturedPostsCollapsedId); | ||||
|                       debugPrint( | ||||
|                         'PostFeaturedList: Removed stored collapsed ID.', | ||||
|                       ); | ||||
|                     } | ||||
|                   }, | ||||
|                   icon: Icon( | ||||
|                     isCollapsed.value | ||||
|                         ? Symbols.expand_more | ||||
|                         : Symbols.expand_less, | ||||
|                   ), | ||||
|                 ), | ||||
|               ], | ||||
|             ).padding(horizontal: 16, vertical: 8), | ||||
|             featuredPostsAsync.when( | ||||
|               loading: () => const Center(child: CircularProgressIndicator()), | ||||
|             AnimatedSize( | ||||
|               duration: const Duration(milliseconds: 300), | ||||
|               curve: Curves.easeInOut, | ||||
|               child: Visibility( | ||||
|                 visible: !isCollapsed.value, | ||||
|                 child: featuredPostsAsync.when( | ||||
|                   loading: | ||||
|                       () => const Center(child: CircularProgressIndicator()), | ||||
|                   error: (error, stack) => Center(child: Text('Error: $error')), | ||||
|                   data: (posts) { | ||||
|                     return SizedBox( | ||||
| @@ -97,6 +195,8 @@ class PostFeaturedList extends HookConsumerWidget { | ||||
|                     ); | ||||
|                   }, | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|   | ||||
| @@ -879,7 +879,8 @@ class _LinkPreview extends ConsumerWidget { | ||||
|             crossAxisAlignment: CrossAxisAlignment.start, | ||||
|             children: [ | ||||
|               // Favicon and image | ||||
|               if (embed.imageUrl != null || embed.faviconUrl.isNotEmpty) | ||||
|               if (embed.imageUrl != null || | ||||
|                   (embed.faviconUrl?.isNotEmpty ?? false)) | ||||
|                 Container( | ||||
|                   width: 60, | ||||
|                   height: 60, | ||||
| @@ -899,11 +900,14 @@ class _LinkPreview extends ConsumerWidget { | ||||
|                               errorBuilder: (context, error, stackTrace) { | ||||
|                                 return _buildFaviconFallback( | ||||
|                                   context, | ||||
|                                   embed.faviconUrl, | ||||
|                                   embed.faviconUrl ?? '', | ||||
|                                 ); | ||||
|                               }, | ||||
|                             ) | ||||
|                             : _buildFaviconFallback(context, embed.faviconUrl), | ||||
|                             : _buildFaviconFallback( | ||||
|                               context, | ||||
|                               embed.faviconUrl ?? '', | ||||
|                             ), | ||||
|                   ), | ||||
|                 ), | ||||
|               // Content | ||||
| @@ -912,9 +916,9 @@ class _LinkPreview extends ConsumerWidget { | ||||
|                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                   children: [ | ||||
|                     // Site name | ||||
|                     if (embed.siteName.isNotEmpty) | ||||
|                     if (embed.siteName?.isNotEmpty ?? false) | ||||
|                       Text( | ||||
|                         embed.siteName, | ||||
|                         embed.siteName!, | ||||
|                         style: Theme.of(context).textTheme.labelSmall?.copyWith( | ||||
|                           color: Theme.of(context).colorScheme.primary, | ||||
|                         ), | ||||
|   | ||||
| @@ -195,7 +195,7 @@ PODS: | ||||
|   - PromisesObjC (2.4.0) | ||||
|   - PromisesSwift (2.4.0): | ||||
|     - PromisesObjC (= 2.4.0) | ||||
|   - record_macos (1.0.0): | ||||
|   - record_macos (1.1.0): | ||||
|     - FlutterMacOS | ||||
|   - SAMKeychain (1.5.3) | ||||
|   - share_plus (0.0.1): | ||||
| @@ -422,7 +422,7 @@ SPEC CHECKSUMS: | ||||
|   path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 | ||||
|   PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 | ||||
|   PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 | ||||
|   record_macos: 295d70bd5fb47145df78df7b80e6697cd18403c0 | ||||
|   record_macos: 43194b6c06ca6f8fa132e2acea72b202b92a0f5b | ||||
|   SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c | ||||
|   share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc | ||||
|   shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 | ||||
|   | ||||
							
								
								
									
										36
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								pubspec.lock
									
									
									
									
									
								
							| @@ -1281,10 +1281,10 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: image_picker_platform_interface | ||||
|       sha256: "886d57f0be73c4b140004e78b9f28a8914a09e50c2d816bdd0520051a71236a0" | ||||
|       sha256: "9f143b0dba3e459553209e20cc425c9801af48e6dfa4f01a0fcf927be3f41665" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.10.1" | ||||
|     version: "2.11.0" | ||||
|   image_picker_windows: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -1897,58 +1897,58 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: record | ||||
|       sha256: daeb3f9b3fea9797094433fe6e49a879d8e4ca4207740bc6dc7e4a58764f0817 | ||||
|       sha256: "3d08502b77edf2a864aa6e4cd7874b983d42a80f3689431da053cc5e85c1ad21" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "6.0.0" | ||||
|     version: "6.1.0" | ||||
|   record_android: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: record_android | ||||
|       sha256: "97d7122455f30de89a01c6c244c839085be6b12abca251fc0e78f67fed73628b" | ||||
|       sha256: "8b170e33d9866f9b51e01a767d7e1ecb97b9ecd629950bd87a47c79359ec57f8" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.3.3" | ||||
|     version: "1.4.0" | ||||
|   record_ios: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: record_ios | ||||
|       sha256: "73706ebbece6150654c9d6f57897cf9b622c581148304132ba85dba15df0fdfb" | ||||
|       sha256: ad97d0a75933c44bcf5aff648e86e32fc05eb61f8fbef190f14968c8eaf86692 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.0.0" | ||||
|     version: "1.1.0" | ||||
|   record_linux: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: record_linux | ||||
|       sha256: "0626678a092c75ce6af1e32fe7fd1dea709b92d308bc8e3b6d6348e2430beb95" | ||||
|       sha256: "785e8e8d6db109aa606d0669d95aaae416458aaa39782bb0abe0bee74eee17d7" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.1.1" | ||||
|     version: "1.2.0" | ||||
|   record_macos: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: record_macos | ||||
|       sha256: "02240833fde16c33fcf2c589f3e08d4394b704761b4a3bb609d872ff3043fbbd" | ||||
|       sha256: f1399bca76a1634da109e5b0cba764ed8332a2b4da49c704c66d2c553405ed81 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.0.0" | ||||
|     version: "1.1.0" | ||||
|   record_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: record_platform_interface | ||||
|       sha256: c1ad38f51e4af88a085b3e792a22c685cb3e7c23fc37aa7ce44c4cf18f25fe89 | ||||
|       sha256: b0065fdf1ec28f5a634d676724d388a77e43ce7646fb049949f58c69f3fcb4ed | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.3.0" | ||||
|     version: "1.4.0" | ||||
|   record_web: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: record_web | ||||
|       sha256: a12856d0b3dd03d336b4b10d7520a8b3e21649a06a8f95815318feaa8f07adbb | ||||
|       sha256: "4f0adf20c9ccafcc02d71111fd91fba1ca7b17a7453902593e5a9b25b74a5c56" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.1.9" | ||||
|     version: "1.2.0" | ||||
|   record_windows: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -2568,10 +2568,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: vector_graphics_compiler | ||||
|       sha256: "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331" | ||||
|       sha256: ca81fdfaf62a5ab45d7296614aea108d2c7d0efca8393e96174bf4d51e6725b0 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.1.17" | ||||
|     version: "1.1.18" | ||||
|   vector_math: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|   | ||||
| @@ -75,7 +75,7 @@ dependencies: | ||||
|   image_picker: ^1.1.2 | ||||
|   file_picker: ^10.3.1 | ||||
|   riverpod_annotation: ^2.6.1 | ||||
|   image_picker_platform_interface: ^2.10.1 | ||||
|   image_picker_platform_interface: ^2.11.0 | ||||
|   image_picker_android: ^0.8.12+25 | ||||
|   super_context_menu: ^0.9.1 | ||||
|   modal_bottom_sheet: ^3.0.0 | ||||
| @@ -107,7 +107,7 @@ dependencies: | ||||
|   livekit_client: ^2.5.0+hotfix.1 | ||||
|   pasteboard: ^0.4.0 | ||||
|   flutter_colorpicker: ^1.1.0 | ||||
|   record: ^6.0.0 | ||||
|   record: ^6.1.0 | ||||
|   qr_flutter: ^4.1.0 | ||||
|   flutter_otp_text_field: ^1.5.1+1 | ||||
|   palette_generator: ^0.3.3+7 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user