Compare commits
	
		
			2 Commits
		
	
	
		
			d7dcde898c
			...
			abf395ff9a
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| abf395ff9a | |||
| 4fdc8eb1d0 | 
| @@ -907,5 +907,12 @@ | |||||||
|   "copyKeyHint": "Please copy this key and store it somewhere safe. You will not be able to see it again.", |   "copyKeyHint": "Please copy this key and store it somewhere safe. You will not be able to see it again.", | ||||||
|   "rotateKey": "Rotate Key", |   "rotateKey": "Rotate Key", | ||||||
|   "rotateBotKey": "Rotate Bot Key", |   "rotateBotKey": "Rotate Bot Key", | ||||||
|   "rotateBotKeyHint": "Are you sure you want to rotate this key? The old key will become invalid immediately. This action cannot be undone." |   "rotateBotKeyHint": "Are you sure you want to rotate this key? The old key will become invalid immediately. This action cannot be undone.", | ||||||
|  |   "webFeedArticleCount": { | ||||||
|  |     "zero": "No articles", | ||||||
|  |     "one": "{} article", | ||||||
|  |     "other": "{} articles" | ||||||
|  |   }, | ||||||
|  |   "webFeedSubscribed": "The feed has been subscribed", | ||||||
|  |   "webFeedUnsubscribed": "The feed has been unsubscribed" | ||||||
| } | } | ||||||
| @@ -297,16 +297,24 @@ class EditAppScreen extends HookConsumerWidget { | |||||||
|                 } |                 } | ||||||
|                 : null, |                 : null, | ||||||
|       }; |       }; | ||||||
|       if (isNew) { |       try { | ||||||
|         await client.post( |         showLoadingModal(context); | ||||||
|           '/develop/developers/$publisherName/projects/$projectId/apps', |         if (isNew) { | ||||||
|           data: data, |           await client.post( | ||||||
|         ); |             '/develop/developers/$publisherName/projects/$projectId/apps', | ||||||
|       } else { |             data: data, | ||||||
|         await client.patch( |           ); | ||||||
|           '/develop/developers/$publisherName/projects/$projectId/apps/$id', |         } else { | ||||||
|           data: data, |           await client.patch( | ||||||
|         ); |             '/develop/developers/$publisherName/projects/$projectId/apps/$id', | ||||||
|  |             data: data, | ||||||
|  |           ); | ||||||
|  |         } | ||||||
|  |       } catch (err) { | ||||||
|  |         showErrorAlert(err); | ||||||
|  |         return; | ||||||
|  |       } finally { | ||||||
|  |         if (context.mounted) hideLoadingModal(context); | ||||||
|       } |       } | ||||||
|       ref.invalidate(customAppsProvider(publisherName, projectId)); |       ref.invalidate(customAppsProvider(publisherName, projectId)); | ||||||
|       if (context.mounted) { |       if (context.mounted) { | ||||||
|   | |||||||
| @@ -116,33 +116,89 @@ class SliverArticlesList extends ConsumerWidget { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| class ArticlesScreen extends ConsumerWidget { | @riverpod | ||||||
|   final String? feedId; | Future<List<SnWebFeed>> subscribedFeeds(Ref ref) async { | ||||||
|   final String? publisherId; |   final client = ref.watch(apiClientProvider); | ||||||
|   final String? title; |   final response = await client.get('/sphere/feeds/subscribed'); | ||||||
|  |   final data = response.data as List<dynamic>; | ||||||
|  |   return data.map((json) => SnWebFeed.fromJson(json)).toList(); | ||||||
|  | } | ||||||
|  |  | ||||||
|   const ArticlesScreen({super.key, this.feedId, this.publisherId, this.title}); | class ArticlesScreen extends ConsumerWidget { | ||||||
|  |   const ArticlesScreen({super.key}); | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context, WidgetRef ref) { |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
|     return AppScaffold( |     final subscribedFeedsAsync = ref.watch(subscribedFeedsProvider); | ||||||
|       appBar: AppBar(title: Text(title ?? 'Articles')), |  | ||||||
|       body: Center( |     return subscribedFeedsAsync.when( | ||||||
|         child: ConstrainedBox( |       data: (feeds) { | ||||||
|           constraints: const BoxConstraints(maxWidth: 560), |         return DefaultTabController( | ||||||
|           child: CustomScrollView( |           length: feeds.length + 1, | ||||||
|             slivers: [ |           child: AppScaffold( | ||||||
|               SliverPadding( |             appBar: AppBar( | ||||||
|                 padding: const EdgeInsets.only(top: 8, left: 8, right: 8), |               title: const Text('Articles'), | ||||||
|                 sliver: SliverArticlesList( |               bottom: TabBar( | ||||||
|                   feedId: feedId, |                 isScrollable: true, | ||||||
|                   publisherId: publisherId, |                 tabs: [ | ||||||
|                 ), |                   const Tab(text: 'All'), | ||||||
|  |                   ...feeds.map((feed) => Tab(text: feed.title)), | ||||||
|  |                 ], | ||||||
|               ), |               ), | ||||||
|             ], |             ), | ||||||
|  |             body: TabBarView( | ||||||
|  |               children: [ | ||||||
|  |                 Center( | ||||||
|  |                   child: ConstrainedBox( | ||||||
|  |                     constraints: const BoxConstraints(maxWidth: 560), | ||||||
|  |                     child: CustomScrollView( | ||||||
|  |                       slivers: [ | ||||||
|  |                         SliverPadding( | ||||||
|  |                           padding: const EdgeInsets.only( | ||||||
|  |                             top: 8, | ||||||
|  |                             left: 8, | ||||||
|  |                             right: 8, | ||||||
|  |                           ), | ||||||
|  |                           sliver: SliverArticlesList(), | ||||||
|  |                         ), | ||||||
|  |                       ], | ||||||
|  |                     ), | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |                 ...feeds.map((feed) { | ||||||
|  |                   return Center( | ||||||
|  |                     child: ConstrainedBox( | ||||||
|  |                       constraints: const BoxConstraints(maxWidth: 560), | ||||||
|  |                       child: CustomScrollView( | ||||||
|  |                         slivers: [ | ||||||
|  |                           SliverPadding( | ||||||
|  |                             padding: const EdgeInsets.only( | ||||||
|  |                               top: 8, | ||||||
|  |                               left: 8, | ||||||
|  |                               right: 8, | ||||||
|  |                             ), | ||||||
|  |                             sliver: SliverArticlesList(feedId: feed.id), | ||||||
|  |                           ), | ||||||
|  |                         ], | ||||||
|  |                       ), | ||||||
|  |                     ), | ||||||
|  |                   ); | ||||||
|  |                 }).toList(), | ||||||
|  |               ], | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |       loading: | ||||||
|  |           () => AppScaffold( | ||||||
|  |             appBar: AppBar(title: const Text('Articles')), | ||||||
|  |             body: const Center(child: CircularProgressIndicator()), | ||||||
|  |           ), | ||||||
|  |       error: | ||||||
|  |           (err, stack) => AppScaffold( | ||||||
|  |             appBar: AppBar(title: const Text('Articles')), | ||||||
|  |             body: Center(child: Text('Error: $err')), | ||||||
|           ), |           ), | ||||||
|         ), |  | ||||||
|       ), |  | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,6 +6,25 @@ part of 'articles.dart'; | |||||||
| // RiverpodGenerator | // RiverpodGenerator | ||||||
| // ************************************************************************** | // ************************************************************************** | ||||||
|  |  | ||||||
|  | String _$subscribedFeedsHash() => r'cd2f5d7d4ea49ad00dc731f8fc2ed65450a3f0e4'; | ||||||
|  |  | ||||||
|  | /// See also [subscribedFeeds]. | ||||||
|  | @ProviderFor(subscribedFeeds) | ||||||
|  | final subscribedFeedsProvider = | ||||||
|  |     AutoDisposeFutureProvider<List<SnWebFeed>>.internal( | ||||||
|  |       subscribedFeeds, | ||||||
|  |       name: r'subscribedFeedsProvider', | ||||||
|  |       debugGetCreateSourceHash: | ||||||
|  |           const bool.fromEnvironment('dart.vm.product') | ||||||
|  |               ? null | ||||||
|  |               : _$subscribedFeedsHash, | ||||||
|  |       dependencies: null, | ||||||
|  |       allTransitiveDependencies: null, | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  | @Deprecated('Will be removed in 3.0. Use Ref instead') | ||||||
|  | // ignore: unused_element | ||||||
|  | typedef SubscribedFeedsRef = AutoDisposeFutureProviderRef<List<SnWebFeed>>; | ||||||
| String _$articlesListNotifierHash() => | String _$articlesListNotifierHash() => | ||||||
|     r'579741af4d90c7c81f2e2697e57c4895b7a9dabc'; |     r'579741af4d90c7c81f2e2697e57c4895b7a9dabc'; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,27 +1,75 @@ | |||||||
| import 'package:easy_localization/easy_localization.dart'; | import 'package:easy_localization/easy_localization.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:flutter/services.dart'; | import 'package:flutter/services.dart'; | ||||||
|  | import 'package:flutter_hooks/flutter_hooks.dart'; | ||||||
| import 'package:gap/gap.dart'; | import 'package:gap/gap.dart'; | ||||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||||
| import 'package:island/models/webfeed.dart'; | import 'package:island/models/webfeed.dart'; | ||||||
| import 'package:island/pods/network.dart'; | import 'package:island/pods/network.dart'; | ||||||
| import 'package:island/widgets/alert.dart'; | import 'package:island/widgets/alert.dart'; | ||||||
| import 'package:island/widgets/app_scaffold.dart'; | import 'package:island/widgets/app_scaffold.dart'; | ||||||
|  | import 'package:island/widgets/web_article_card.dart'; | ||||||
| import 'package:material_symbols_icons/symbols.dart'; | import 'package:material_symbols_icons/symbols.dart'; | ||||||
| import 'package:riverpod_annotation/riverpod_annotation.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:styled_widget/styled_widget.dart'; | ||||||
|  |  | ||||||
| part 'feed_detail.g.dart'; | part 'feed_detail.g.dart'; | ||||||
|  |  | ||||||
|  | @riverpod | ||||||
|  | Future<SnWebFeed> marketplaceWebFeed(Ref ref, String feedId) async { | ||||||
|  |   final apiClient = ref.watch(apiClientProvider); | ||||||
|  |   final resp = await apiClient.get('/sphere/feeds/$feedId'); | ||||||
|  |   return SnWebFeed.fromJson(resp.data); | ||||||
|  | } | ||||||
|  |  | ||||||
| /// Provider for web feed articles content | /// Provider for web feed articles content | ||||||
| @riverpod | @riverpod | ||||||
| Future<List<SnWebArticle>> marketplaceWebFeedContent( | class MarketplaceWebFeedContentNotifier | ||||||
|   Ref ref, { |     extends _$MarketplaceWebFeedContentNotifier | ||||||
|   required String feedId, |     with CursorPagingNotifierMixin<SnWebArticle> { | ||||||
| }) async { |   static const int _pageSize = 20; | ||||||
|   final apiClient = ref.watch(apiClientProvider); |  | ||||||
|   final resp = await apiClient.get('/sphere/feeds/$feedId/articles'); |   @override | ||||||
|   return (resp.data as List).map((e) => SnWebArticle.fromJson(e)).toList(); |   Future<CursorPagingData<SnWebArticle>> build(String feedId) async { | ||||||
|  |     _feedId = feedId; | ||||||
|  |     return fetch(cursor: null); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   late final String _feedId; | ||||||
|  |   ValueNotifier<int> totalCount = ValueNotifier(0); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Future<CursorPagingData<SnWebArticle>> 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( | ||||||
|  |       '/sphere/feeds/$_feedId/articles', | ||||||
|  |       queryParameters: queryParams, | ||||||
|  |     ); | ||||||
|  |     final total = int.parse(response.headers.value('X-Total') ?? '0'); | ||||||
|  |     totalCount.value = total; | ||||||
|  |     final List<dynamic> data = response.data; | ||||||
|  |     final articles = data.map((json) => SnWebArticle.fromJson(json)).toList(); | ||||||
|  |  | ||||||
|  |     final hasMore = offset + articles.length < total; | ||||||
|  |     final nextCursor = hasMore ? (offset + articles.length).toString() : null; | ||||||
|  |  | ||||||
|  |     return CursorPagingData( | ||||||
|  |       items: articles, | ||||||
|  |       hasMore: hasMore, | ||||||
|  |       nextCursor: nextCursor, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void dispose() { | ||||||
|  |     totalCount.dispose(); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Provider for web feed subscription status | /// Provider for web feed subscription status | ||||||
| @@ -49,11 +97,7 @@ class MarketplaceWebFeedDetailScreen extends HookConsumerWidget { | |||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context, WidgetRef ref) { |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
|     // TODO: Need to create a web feed provider similar to stickerPackProvider |     final feed = ref.watch(marketplaceWebFeedProvider(id)); | ||||||
|     // For now, we'll fetch the feed directly |  | ||||||
|     final feedContent = ref.watch( |  | ||||||
|       marketplaceWebFeedContentProvider(feedId: id), |  | ||||||
|     ); |  | ||||||
|     final subscribed = ref.watch( |     final subscribed = ref.watch( | ||||||
|       marketplaceWebFeedSubscriptionProvider(feedId: id), |       marketplaceWebFeedSubscriptionProvider(feedId: id), | ||||||
|     ); |     ); | ||||||
| @@ -65,7 +109,7 @@ class MarketplaceWebFeedDetailScreen extends HookConsumerWidget { | |||||||
|       HapticFeedback.selectionClick(); |       HapticFeedback.selectionClick(); | ||||||
|       ref.invalidate(marketplaceWebFeedSubscriptionProvider(feedId: id)); |       ref.invalidate(marketplaceWebFeedSubscriptionProvider(feedId: id)); | ||||||
|       if (!context.mounted) return; |       if (!context.mounted) return; | ||||||
|       showSnackBar('feedSubscribed'.tr()); |       showSnackBar('webFeedSubscribed'.tr()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Unsubscribe from web feed |     // Unsubscribe from web feed | ||||||
| @@ -75,86 +119,94 @@ class MarketplaceWebFeedDetailScreen extends HookConsumerWidget { | |||||||
|       HapticFeedback.selectionClick(); |       HapticFeedback.selectionClick(); | ||||||
|       ref.invalidate(marketplaceWebFeedSubscriptionProvider(feedId: id)); |       ref.invalidate(marketplaceWebFeedSubscriptionProvider(feedId: id)); | ||||||
|       if (!context.mounted) return; |       if (!context.mounted) return; | ||||||
|       showSnackBar('feedUnsubscribed'.tr()); |       showSnackBar('webFeedUnsubscribed'.tr()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // TODO: Replace with actual feed data provider once created |     final feedNotifier = ref.watch( | ||||||
|     final dummyFeed = SnWebFeed( |       marketplaceWebFeedContentNotifierProvider(id).notifier, | ||||||
|       id: id, |  | ||||||
|       url: 'https://example.com', |  | ||||||
|       title: 'Loading...', |  | ||||||
|       publisherId: 'publisher-id', |  | ||||||
|       createdAt: DateTime.now(), |  | ||||||
|       updatedAt: DateTime.now(), |  | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|  |     useEffect(() { | ||||||
|  |       return feedNotifier.dispose; | ||||||
|  |     }, []); | ||||||
|  |  | ||||||
|     return AppScaffold( |     return AppScaffold( | ||||||
|       appBar: AppBar(title: Text(dummyFeed.title)), |       appBar: AppBar(title: Text(feed.value?.title ?? 'loading'.tr())), | ||||||
|       body: Column( |       body: Column( | ||||||
|         crossAxisAlignment: CrossAxisAlignment.stretch, |         crossAxisAlignment: CrossAxisAlignment.stretch, | ||||||
|         children: [ |         children: [ | ||||||
|           // Feed meta |           // Feed meta | ||||||
|           Column( |           feed | ||||||
|             crossAxisAlignment: CrossAxisAlignment.stretch, |               .when( | ||||||
|             children: [ |                 data: | ||||||
|               Text(dummyFeed.description ?? ''), |                     (data) => Column( | ||||||
|               Row( |                       crossAxisAlignment: CrossAxisAlignment.stretch, | ||||||
|                 spacing: 4, |                       children: [ | ||||||
|                 children: [ |                         Text(data.description ?? 'descriptionNone'.tr()), | ||||||
|                   const Icon(Symbols.rss_feed, size: 16), |                         Row( | ||||||
|                   Text('${feedContent.value?.length ?? 0} articles'), |                           spacing: 4, | ||||||
|                 ], |                           children: [ | ||||||
|               ).opacity(0.85), |                             const Icon(Symbols.rss_feed, size: 16), | ||||||
|               Row( |                             ListenableBuilder( | ||||||
|                 spacing: 4, |                               listenable: feedNotifier.totalCount, | ||||||
|                 children: [ |                               builder: | ||||||
|                   const Icon(Symbols.link, size: 16), |                                   (context, _) => Text( | ||||||
|                   SelectableText(dummyFeed.url), |                                     'webFeedArticleCount'.plural( | ||||||
|                 ], |                                       feedNotifier.totalCount.value, | ||||||
|               ).opacity(0.85), |                                     ), | ||||||
|             ], |                                   ), | ||||||
|           ).padding(horizontal: 24, vertical: 24), |                             ), | ||||||
|  |                           ], | ||||||
|  |                         ).opacity(0.85), | ||||||
|  |                         Row( | ||||||
|  |                           spacing: 4, | ||||||
|  |                           children: [ | ||||||
|  |                             const Icon(Symbols.link, size: 16), | ||||||
|  |                             SelectableText(data.url), | ||||||
|  |                           ], | ||||||
|  |                         ).opacity(0.85), | ||||||
|  |                       ], | ||||||
|  |                     ), | ||||||
|  |                 error: (err, _) => Text(err.toString()), | ||||||
|  |                 loading: () => CircularProgressIndicator().center(), | ||||||
|  |               ) | ||||||
|  |               .padding(horizontal: 24, vertical: 24), | ||||||
|           const Divider(height: 1), |           const Divider(height: 1), | ||||||
|           // Articles list |           // Articles list | ||||||
|           Expanded( |           Expanded( | ||||||
|             child: feedContent.when( |             child: PagingHelperView( | ||||||
|               data: |               provider: marketplaceWebFeedContentNotifierProvider(id), | ||||||
|                   (articles) => RefreshIndicator( |               futureRefreshable: | ||||||
|                     onRefresh: |                   marketplaceWebFeedContentNotifierProvider(id).future, | ||||||
|                         () => ref.refresh( |               notifierRefreshable: | ||||||
|                           marketplaceWebFeedContentProvider(feedId: id).future, |                   marketplaceWebFeedContentNotifierProvider(id).notifier, | ||||||
|                         ), |               contentBuilder: | ||||||
|                     child: ListView.builder( |                   (data, widgetCount, endItemView) => ListView.separated( | ||||||
|                       padding: const EdgeInsets.symmetric( |                     padding: const EdgeInsets.symmetric( | ||||||
|                         horizontal: 24, |                       horizontal: 24, | ||||||
|                         vertical: 20, |                       vertical: 20, | ||||||
|                       ), |  | ||||||
|                       itemCount: articles.length, |  | ||||||
|                       itemBuilder: (context, index) { |  | ||||||
|                         final article = articles[index]; |  | ||||||
|                         return Card( |  | ||||||
|                           child: ListTile( |  | ||||||
|                             title: Text(article.title), |  | ||||||
|                             subtitle: Text(article.author ?? ''), |  | ||||||
|                             trailing: const Icon(Symbols.open_in_new), |  | ||||||
|                             onTap: () { |  | ||||||
|                               // TODO: Navigate to article detail or open URL |  | ||||||
|                             }, |  | ||||||
|                           ), |  | ||||||
|                         ); |  | ||||||
|                       }, |  | ||||||
|                     ), |                     ), | ||||||
|  |                     itemCount: widgetCount, | ||||||
|  |                     itemBuilder: (context, index) { | ||||||
|  |                       if (index == widgetCount - 1) { | ||||||
|  |                         return endItemView; | ||||||
|  |                       } | ||||||
|  |  | ||||||
|  |                       final article = data.items[index]; | ||||||
|  |                       return WebArticleCard(article: article); | ||||||
|  |                     }, | ||||||
|  |                     separatorBuilder: (context, index) => const Gap(12), | ||||||
|                   ), |                   ), | ||||||
|               error: |  | ||||||
|                   (err, _) => |  | ||||||
|                       Text( |  | ||||||
|                         'Error: $err', |  | ||||||
|                       ).textAlignment(TextAlign.center).center(), |  | ||||||
|               loading: () => const CircularProgressIndicator().center(), |  | ||||||
|             ), |             ), | ||||||
|           ), |           ), | ||||||
|           Padding( |           Container( | ||||||
|             padding: EdgeInsets.symmetric(horizontal: 24, vertical: 8), |             padding: EdgeInsets.only( | ||||||
|  |               bottom: 16 + MediaQuery.of(context).padding.bottom, | ||||||
|  |               left: 24, | ||||||
|  |               right: 24, | ||||||
|  |               top: 16, | ||||||
|  |             ), | ||||||
|  |             color: Theme.of(context).colorScheme.surfaceContainer, | ||||||
|             child: subscribed.when( |             child: subscribed.when( | ||||||
|               data: |               data: | ||||||
|                   (isSubscribed) => FilledButton.icon( |                   (isSubscribed) => FilledButton.icon( | ||||||
| @@ -181,7 +233,6 @@ class MarketplaceWebFeedDetailScreen extends HookConsumerWidget { | |||||||
|                   ), |                   ), | ||||||
|             ), |             ), | ||||||
|           ), |           ), | ||||||
|           Gap(MediaQuery.of(context).padding.bottom), |  | ||||||
|         ], |         ], | ||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
|   | |||||||
| @@ -6,8 +6,8 @@ part of 'feed_detail.dart'; | |||||||
| // RiverpodGenerator | // RiverpodGenerator | ||||||
| // ************************************************************************** | // ************************************************************************** | ||||||
|  |  | ||||||
| String _$marketplaceWebFeedContentHash() => | String _$marketplaceWebFeedHash() => | ||||||
|     r'4e65350bff4055302e15ec14266cdebb1cd89bbe'; |     r'8383f94f1bc272b903c341b8d95000313b69d14c'; | ||||||
|  |  | ||||||
| /// Copied from Dart SDK | /// Copied from Dart SDK | ||||||
| class _SystemHash { | class _SystemHash { | ||||||
| @@ -30,34 +30,25 @@ class _SystemHash { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Provider for web feed articles content | /// See also [marketplaceWebFeed]. | ||||||
| /// | @ProviderFor(marketplaceWebFeed) | ||||||
| /// Copied from [marketplaceWebFeedContent]. | const marketplaceWebFeedProvider = MarketplaceWebFeedFamily(); | ||||||
| @ProviderFor(marketplaceWebFeedContent) |  | ||||||
| const marketplaceWebFeedContentProvider = MarketplaceWebFeedContentFamily(); |  | ||||||
|  |  | ||||||
| /// Provider for web feed articles content | /// See also [marketplaceWebFeed]. | ||||||
| /// | class MarketplaceWebFeedFamily extends Family<AsyncValue<SnWebFeed>> { | ||||||
| /// Copied from [marketplaceWebFeedContent]. |   /// See also [marketplaceWebFeed]. | ||||||
| class MarketplaceWebFeedContentFamily |   const MarketplaceWebFeedFamily(); | ||||||
|     extends Family<AsyncValue<List<SnWebArticle>>> { |  | ||||||
|   /// Provider for web feed articles content |  | ||||||
|   /// |  | ||||||
|   /// Copied from [marketplaceWebFeedContent]. |  | ||||||
|   const MarketplaceWebFeedContentFamily(); |  | ||||||
|  |  | ||||||
|   /// Provider for web feed articles content |   /// See also [marketplaceWebFeed]. | ||||||
|   /// |   MarketplaceWebFeedProvider call(String feedId) { | ||||||
|   /// Copied from [marketplaceWebFeedContent]. |     return MarketplaceWebFeedProvider(feedId); | ||||||
|   MarketplaceWebFeedContentProvider call({required String feedId}) { |  | ||||||
|     return MarketplaceWebFeedContentProvider(feedId: feedId); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   MarketplaceWebFeedContentProvider getProviderOverride( |   MarketplaceWebFeedProvider getProviderOverride( | ||||||
|     covariant MarketplaceWebFeedContentProvider provider, |     covariant MarketplaceWebFeedProvider provider, | ||||||
|   ) { |   ) { | ||||||
|     return call(feedId: provider.feedId); |     return call(provider.feedId); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   static const Iterable<ProviderOrFamily>? _dependencies = null; |   static const Iterable<ProviderOrFamily>? _dependencies = null; | ||||||
| @@ -72,36 +63,28 @@ class MarketplaceWebFeedContentFamily | |||||||
|       _allTransitiveDependencies; |       _allTransitiveDependencies; | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   String? get name => r'marketplaceWebFeedContentProvider'; |   String? get name => r'marketplaceWebFeedProvider'; | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Provider for web feed articles content | /// See also [marketplaceWebFeed]. | ||||||
| /// | class MarketplaceWebFeedProvider extends AutoDisposeFutureProvider<SnWebFeed> { | ||||||
| /// Copied from [marketplaceWebFeedContent]. |   /// See also [marketplaceWebFeed]. | ||||||
| class MarketplaceWebFeedContentProvider |   MarketplaceWebFeedProvider(String feedId) | ||||||
|     extends AutoDisposeFutureProvider<List<SnWebArticle>> { |  | ||||||
|   /// Provider for web feed articles content |  | ||||||
|   /// |  | ||||||
|   /// Copied from [marketplaceWebFeedContent]. |  | ||||||
|   MarketplaceWebFeedContentProvider({required String feedId}) |  | ||||||
|     : this._internal( |     : this._internal( | ||||||
|         (ref) => marketplaceWebFeedContent( |         (ref) => marketplaceWebFeed(ref as MarketplaceWebFeedRef, feedId), | ||||||
|           ref as MarketplaceWebFeedContentRef, |         from: marketplaceWebFeedProvider, | ||||||
|           feedId: feedId, |         name: r'marketplaceWebFeedProvider', | ||||||
|         ), |  | ||||||
|         from: marketplaceWebFeedContentProvider, |  | ||||||
|         name: r'marketplaceWebFeedContentProvider', |  | ||||||
|         debugGetCreateSourceHash: |         debugGetCreateSourceHash: | ||||||
|             const bool.fromEnvironment('dart.vm.product') |             const bool.fromEnvironment('dart.vm.product') | ||||||
|                 ? null |                 ? null | ||||||
|                 : _$marketplaceWebFeedContentHash, |                 : _$marketplaceWebFeedHash, | ||||||
|         dependencies: MarketplaceWebFeedContentFamily._dependencies, |         dependencies: MarketplaceWebFeedFamily._dependencies, | ||||||
|         allTransitiveDependencies: |         allTransitiveDependencies: | ||||||
|             MarketplaceWebFeedContentFamily._allTransitiveDependencies, |             MarketplaceWebFeedFamily._allTransitiveDependencies, | ||||||
|         feedId: feedId, |         feedId: feedId, | ||||||
|       ); |       ); | ||||||
|  |  | ||||||
|   MarketplaceWebFeedContentProvider._internal( |   MarketplaceWebFeedProvider._internal( | ||||||
|     super._createNotifier, { |     super._createNotifier, { | ||||||
|     required super.name, |     required super.name, | ||||||
|     required super.dependencies, |     required super.dependencies, | ||||||
| @@ -115,13 +98,12 @@ class MarketplaceWebFeedContentProvider | |||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Override overrideWith( |   Override overrideWith( | ||||||
|     FutureOr<List<SnWebArticle>> Function(MarketplaceWebFeedContentRef provider) |     FutureOr<SnWebFeed> Function(MarketplaceWebFeedRef provider) create, | ||||||
|     create, |  | ||||||
|   ) { |   ) { | ||||||
|     return ProviderOverride( |     return ProviderOverride( | ||||||
|       origin: this, |       origin: this, | ||||||
|       override: MarketplaceWebFeedContentProvider._internal( |       override: MarketplaceWebFeedProvider._internal( | ||||||
|         (ref) => create(ref as MarketplaceWebFeedContentRef), |         (ref) => create(ref as MarketplaceWebFeedRef), | ||||||
|         from: from, |         from: from, | ||||||
|         name: null, |         name: null, | ||||||
|         dependencies: null, |         dependencies: null, | ||||||
| @@ -133,13 +115,13 @@ class MarketplaceWebFeedContentProvider | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   AutoDisposeFutureProviderElement<List<SnWebArticle>> createElement() { |   AutoDisposeFutureProviderElement<SnWebFeed> createElement() { | ||||||
|     return _MarketplaceWebFeedContentProviderElement(this); |     return _MarketplaceWebFeedProviderElement(this); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   bool operator ==(Object other) { |   bool operator ==(Object other) { | ||||||
|     return other is MarketplaceWebFeedContentProvider && other.feedId == feedId; |     return other is MarketplaceWebFeedProvider && other.feedId == feedId; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
| @@ -153,19 +135,18 @@ class MarketplaceWebFeedContentProvider | |||||||
|  |  | ||||||
| @Deprecated('Will be removed in 3.0. Use Ref instead') | @Deprecated('Will be removed in 3.0. Use Ref instead') | ||||||
| // ignore: unused_element | // ignore: unused_element | ||||||
| mixin MarketplaceWebFeedContentRef | mixin MarketplaceWebFeedRef on AutoDisposeFutureProviderRef<SnWebFeed> { | ||||||
|     on AutoDisposeFutureProviderRef<List<SnWebArticle>> { |  | ||||||
|   /// The parameter `feedId` of this provider. |   /// The parameter `feedId` of this provider. | ||||||
|   String get feedId; |   String get feedId; | ||||||
| } | } | ||||||
|  |  | ||||||
| class _MarketplaceWebFeedContentProviderElement | class _MarketplaceWebFeedProviderElement | ||||||
|     extends AutoDisposeFutureProviderElement<List<SnWebArticle>> |     extends AutoDisposeFutureProviderElement<SnWebFeed> | ||||||
|     with MarketplaceWebFeedContentRef { |     with MarketplaceWebFeedRef { | ||||||
|   _MarketplaceWebFeedContentProviderElement(super.provider); |   _MarketplaceWebFeedProviderElement(super.provider); | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   String get feedId => (origin as MarketplaceWebFeedContentProvider).feedId; |   String get feedId => (origin as MarketplaceWebFeedProvider).feedId; | ||||||
| } | } | ||||||
|  |  | ||||||
| String _$marketplaceWebFeedSubscriptionHash() => | String _$marketplaceWebFeedSubscriptionHash() => | ||||||
| @@ -309,5 +290,169 @@ class _MarketplaceWebFeedSubscriptionProviderElement | |||||||
|       (origin as MarketplaceWebFeedSubscriptionProvider).feedId; |       (origin as MarketplaceWebFeedSubscriptionProvider).feedId; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | String _$marketplaceWebFeedContentNotifierHash() => | ||||||
|  |     r'25688082884cb824eeff300888ba38c9748295dc'; | ||||||
|  |  | ||||||
|  | abstract class _$MarketplaceWebFeedContentNotifier | ||||||
|  |     extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnWebArticle>> { | ||||||
|  |   late final String feedId; | ||||||
|  |  | ||||||
|  |   FutureOr<CursorPagingData<SnWebArticle>> build(String feedId); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Provider for web feed articles content | ||||||
|  | /// | ||||||
|  | /// Copied from [MarketplaceWebFeedContentNotifier]. | ||||||
|  | @ProviderFor(MarketplaceWebFeedContentNotifier) | ||||||
|  | const marketplaceWebFeedContentNotifierProvider = | ||||||
|  |     MarketplaceWebFeedContentNotifierFamily(); | ||||||
|  |  | ||||||
|  | /// Provider for web feed articles content | ||||||
|  | /// | ||||||
|  | /// Copied from [MarketplaceWebFeedContentNotifier]. | ||||||
|  | class MarketplaceWebFeedContentNotifierFamily | ||||||
|  |     extends Family<AsyncValue<CursorPagingData<SnWebArticle>>> { | ||||||
|  |   /// Provider for web feed articles content | ||||||
|  |   /// | ||||||
|  |   /// Copied from [MarketplaceWebFeedContentNotifier]. | ||||||
|  |   const MarketplaceWebFeedContentNotifierFamily(); | ||||||
|  |  | ||||||
|  |   /// Provider for web feed articles content | ||||||
|  |   /// | ||||||
|  |   /// Copied from [MarketplaceWebFeedContentNotifier]. | ||||||
|  |   MarketplaceWebFeedContentNotifierProvider call(String feedId) { | ||||||
|  |     return MarketplaceWebFeedContentNotifierProvider(feedId); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   MarketplaceWebFeedContentNotifierProvider getProviderOverride( | ||||||
|  |     covariant MarketplaceWebFeedContentNotifierProvider provider, | ||||||
|  |   ) { | ||||||
|  |     return call(provider.feedId); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static const Iterable<ProviderOrFamily>? _dependencies = null; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Iterable<ProviderOrFamily>? get dependencies => _dependencies; | ||||||
|  |  | ||||||
|  |   static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Iterable<ProviderOrFamily>? get allTransitiveDependencies => | ||||||
|  |       _allTransitiveDependencies; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String? get name => r'marketplaceWebFeedContentNotifierProvider'; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Provider for web feed articles content | ||||||
|  | /// | ||||||
|  | /// Copied from [MarketplaceWebFeedContentNotifier]. | ||||||
|  | class MarketplaceWebFeedContentNotifierProvider | ||||||
|  |     extends | ||||||
|  |         AutoDisposeAsyncNotifierProviderImpl< | ||||||
|  |           MarketplaceWebFeedContentNotifier, | ||||||
|  |           CursorPagingData<SnWebArticle> | ||||||
|  |         > { | ||||||
|  |   /// Provider for web feed articles content | ||||||
|  |   /// | ||||||
|  |   /// Copied from [MarketplaceWebFeedContentNotifier]. | ||||||
|  |   MarketplaceWebFeedContentNotifierProvider(String feedId) | ||||||
|  |     : this._internal( | ||||||
|  |         () => MarketplaceWebFeedContentNotifier()..feedId = feedId, | ||||||
|  |         from: marketplaceWebFeedContentNotifierProvider, | ||||||
|  |         name: r'marketplaceWebFeedContentNotifierProvider', | ||||||
|  |         debugGetCreateSourceHash: | ||||||
|  |             const bool.fromEnvironment('dart.vm.product') | ||||||
|  |                 ? null | ||||||
|  |                 : _$marketplaceWebFeedContentNotifierHash, | ||||||
|  |         dependencies: MarketplaceWebFeedContentNotifierFamily._dependencies, | ||||||
|  |         allTransitiveDependencies: | ||||||
|  |             MarketplaceWebFeedContentNotifierFamily._allTransitiveDependencies, | ||||||
|  |         feedId: feedId, | ||||||
|  |       ); | ||||||
|  |  | ||||||
|  |   MarketplaceWebFeedContentNotifierProvider._internal( | ||||||
|  |     super._createNotifier, { | ||||||
|  |     required super.name, | ||||||
|  |     required super.dependencies, | ||||||
|  |     required super.allTransitiveDependencies, | ||||||
|  |     required super.debugGetCreateSourceHash, | ||||||
|  |     required super.from, | ||||||
|  |     required this.feedId, | ||||||
|  |   }) : super.internal(); | ||||||
|  |  | ||||||
|  |   final String feedId; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   FutureOr<CursorPagingData<SnWebArticle>> runNotifierBuild( | ||||||
|  |     covariant MarketplaceWebFeedContentNotifier notifier, | ||||||
|  |   ) { | ||||||
|  |     return notifier.build(feedId); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Override overrideWith(MarketplaceWebFeedContentNotifier Function() create) { | ||||||
|  |     return ProviderOverride( | ||||||
|  |       origin: this, | ||||||
|  |       override: MarketplaceWebFeedContentNotifierProvider._internal( | ||||||
|  |         () => create()..feedId = feedId, | ||||||
|  |         from: from, | ||||||
|  |         name: null, | ||||||
|  |         dependencies: null, | ||||||
|  |         allTransitiveDependencies: null, | ||||||
|  |         debugGetCreateSourceHash: null, | ||||||
|  |         feedId: feedId, | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   AutoDisposeAsyncNotifierProviderElement< | ||||||
|  |     MarketplaceWebFeedContentNotifier, | ||||||
|  |     CursorPagingData<SnWebArticle> | ||||||
|  |   > | ||||||
|  |   createElement() { | ||||||
|  |     return _MarketplaceWebFeedContentNotifierProviderElement(this); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   bool operator ==(Object other) { | ||||||
|  |     return other is MarketplaceWebFeedContentNotifierProvider && | ||||||
|  |         other.feedId == feedId; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   int get hashCode { | ||||||
|  |     var hash = _SystemHash.combine(0, runtimeType.hashCode); | ||||||
|  |     hash = _SystemHash.combine(hash, feedId.hashCode); | ||||||
|  |  | ||||||
|  |     return _SystemHash.finish(hash); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @Deprecated('Will be removed in 3.0. Use Ref instead') | ||||||
|  | // ignore: unused_element | ||||||
|  | mixin MarketplaceWebFeedContentNotifierRef | ||||||
|  |     on AutoDisposeAsyncNotifierProviderRef<CursorPagingData<SnWebArticle>> { | ||||||
|  |   /// The parameter `feedId` of this provider. | ||||||
|  |   String get feedId; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class _MarketplaceWebFeedContentNotifierProviderElement | ||||||
|  |     extends | ||||||
|  |         AutoDisposeAsyncNotifierProviderElement< | ||||||
|  |           MarketplaceWebFeedContentNotifier, | ||||||
|  |           CursorPagingData<SnWebArticle> | ||||||
|  |         > | ||||||
|  |     with MarketplaceWebFeedContentNotifierRef { | ||||||
|  |   _MarketplaceWebFeedContentNotifierProviderElement(super.provider); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String get feedId => | ||||||
|  |       (origin as MarketplaceWebFeedContentNotifierProvider).feedId; | ||||||
|  | } | ||||||
|  |  | ||||||
| // ignore_for_file: type=lint | // 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 | // 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 | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ part of 'feed_marketplace.dart'; | |||||||
| // ************************************************************************** | // ************************************************************************** | ||||||
|  |  | ||||||
| String _$marketplaceWebFeedsNotifierHash() => | String _$marketplaceWebFeedsNotifierHash() => | ||||||
|     r'dbf885d95570ca9c2259a58998975db813b18cbb'; |     r'774b2985f2f7d61fe958f534f84e39f814327c4e'; | ||||||
|  |  | ||||||
| /// Copied from Dart SDK | /// Copied from Dart SDK | ||||||
| class _SystemHash { | class _SystemHash { | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| import 'package:dio/dio.dart'; | import 'package:dio/dio.dart'; | ||||||
| import 'package:easy_localization/easy_localization.dart'; | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | import 'package:flutter/foundation.dart'; | ||||||
| import 'package:island/screens/chat/chat.dart'; | import 'package:island/screens/chat/chat.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:island/models/chat.dart'; | import 'package:island/models/chat.dart'; | ||||||
| @@ -520,9 +521,11 @@ class _RealmActionMenu extends HookConsumerWidget { | |||||||
| class RealmMemberListNotifier extends _$RealmMemberListNotifier | class RealmMemberListNotifier extends _$RealmMemberListNotifier | ||||||
|     with CursorPagingNotifierMixin<SnRealmMember> { |     with CursorPagingNotifierMixin<SnRealmMember> { | ||||||
|   static const int _pageSize = 20; |   static const int _pageSize = 20; | ||||||
|  |   ValueNotifier<int> totalCount = ValueNotifier(0); | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Future<CursorPagingData<SnRealmMember>> build(String realmSlug) async { |   Future<CursorPagingData<SnRealmMember>> build(String realmSlug) async { | ||||||
|  |     totalCount.value = 0; | ||||||
|     return fetch(); |     return fetch(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -541,6 +544,7 @@ class RealmMemberListNotifier extends _$RealmMemberListNotifier | |||||||
|     ); |     ); | ||||||
|  |  | ||||||
|     final total = int.parse(response.headers.value('X-Total') ?? '0'); |     final total = int.parse(response.headers.value('X-Total') ?? '0'); | ||||||
|  |     totalCount.value = total; | ||||||
|     final List<dynamic> data = response.data; |     final List<dynamic> data = response.data; | ||||||
|     final members = data.map((e) => SnRealmMember.fromJson(e)).toList(); |     final members = data.map((e) => SnRealmMember.fromJson(e)).toList(); | ||||||
|  |  | ||||||
| @@ -553,52 +557,9 @@ class RealmMemberListNotifier extends _$RealmMemberListNotifier | |||||||
|       nextCursor: nextCursor, |       nextCursor: nextCursor, | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } |  | ||||||
|  |  | ||||||
| // Keep the old provider for backward compatibility |   void dispose() { | ||||||
| final realmMemberStateProvider = |     totalCount.dispose(); | ||||||
|     StateNotifierProvider.family<RealmMemberNotifier, RealmMemberState, String>( |  | ||||||
|       (ref, realmSlug) { |  | ||||||
|         final apiClient = ref.watch(apiClientProvider); |  | ||||||
|         return RealmMemberNotifier(apiClient, realmSlug); |  | ||||||
|       }, |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
| class RealmMemberNotifier extends StateNotifier<RealmMemberState> { |  | ||||||
|   final String realmSlug; |  | ||||||
|   final Dio _apiClient; |  | ||||||
|  |  | ||||||
|   RealmMemberNotifier(this._apiClient, this.realmSlug) |  | ||||||
|     : super(const RealmMemberState(members: [], isLoading: false, total: 0)); |  | ||||||
|  |  | ||||||
|   Future<void> loadMore({int offset = 0, int take = 20}) async { |  | ||||||
|     if (state.isLoading) return; |  | ||||||
|     if (state.total > 0 && state.members.length >= state.total) return; |  | ||||||
|  |  | ||||||
|     state = state.copyWith(isLoading: true, error: null); |  | ||||||
|  |  | ||||||
|     try { |  | ||||||
|       final response = await _apiClient.get( |  | ||||||
|         '/sphere/realms/$realmSlug/members', |  | ||||||
|         queryParameters: {'offset': offset, 'take': take, 'withStatus': true}, |  | ||||||
|       ); |  | ||||||
|  |  | ||||||
|       final total = int.parse(response.headers.value('X-Total') ?? '0'); |  | ||||||
|       final List<dynamic> data = response.data; |  | ||||||
|       final members = data.map((e) => SnRealmMember.fromJson(e)).toList(); |  | ||||||
|  |  | ||||||
|       state = state.copyWith( |  | ||||||
|         members: [...state.members, ...members], |  | ||||||
|         total: total, |  | ||||||
|         isLoading: false, |  | ||||||
|       ); |  | ||||||
|     } catch (e) { |  | ||||||
|       state = state.copyWith(error: e.toString(), isLoading: false); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void reset() { |  | ||||||
|     state = const RealmMemberState(members: [], isLoading: false, total: 0); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -610,18 +571,10 @@ class _RealmMemberListSheet extends HookConsumerWidget { | |||||||
|   Widget build(BuildContext context, WidgetRef ref) { |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
|     final realmIdentity = ref.watch(realmIdentityProvider(realmSlug)); |     final realmIdentity = ref.watch(realmIdentityProvider(realmSlug)); | ||||||
|     final memberListProvider = realmMemberListNotifierProvider(realmSlug); |     final memberListProvider = realmMemberListNotifierProvider(realmSlug); | ||||||
|  |     final memberListNotifier = ref.watch(memberListProvider.notifier); | ||||||
|     // For backward compatibility and to show total count in the header |  | ||||||
|     final memberState = ref.watch(realmMemberStateProvider(realmSlug)); |  | ||||||
|     final memberNotifier = ref.read( |  | ||||||
|       realmMemberStateProvider(realmSlug).notifier, |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     useEffect(() { |     useEffect(() { | ||||||
|       Future(() { |       return memberListNotifier.dispose; | ||||||
|         memberNotifier.loadMore(); |  | ||||||
|       }); |  | ||||||
|       return null; |  | ||||||
|     }, []); |     }, []); | ||||||
|  |  | ||||||
|     Future<void> invitePerson() async { |     Future<void> invitePerson() async { | ||||||
| @@ -638,9 +591,7 @@ class _RealmMemberListSheet extends HookConsumerWidget { | |||||||
|           '/sphere/realms/invites/$realmSlug', |           '/sphere/realms/invites/$realmSlug', | ||||||
|           data: {'related_user_id': result.id, 'role': 0}, |           data: {'related_user_id': result.id, 'role': 0}, | ||||||
|         ); |         ); | ||||||
|         // Refresh both providers |         // Refresh the provider | ||||||
|         memberNotifier.reset(); |  | ||||||
|         await memberNotifier.loadMore(); |  | ||||||
|         ref.invalidate(memberListProvider); |         ref.invalidate(memberListProvider); | ||||||
|       } catch (err) { |       } catch (err) { | ||||||
|         showErrorAlert(err); |         showErrorAlert(err); | ||||||
| @@ -652,12 +603,17 @@ class _RealmMemberListSheet extends HookConsumerWidget { | |||||||
|         padding: EdgeInsets.only(top: 16, left: 20, right: 16, bottom: 12), |         padding: EdgeInsets.only(top: 16, left: 20, right: 16, bottom: 12), | ||||||
|         child: Row( |         child: Row( | ||||||
|           children: [ |           children: [ | ||||||
|             Text( |             ListenableBuilder( | ||||||
|               'members'.plural(memberState.total), |               listenable: memberListNotifier.totalCount, | ||||||
|               style: Theme.of(context).textTheme.headlineSmall?.copyWith( |               builder: | ||||||
|                 fontWeight: FontWeight.w600, |                   (context, _) => Text( | ||||||
|                 letterSpacing: -0.5, |                     'members'.plural(memberListNotifier.totalCount.value), | ||||||
|               ), |                     key: ValueKey(memberListNotifier), | ||||||
|  |                     style: Theme.of(context).textTheme.headlineSmall?.copyWith( | ||||||
|  |                       fontWeight: FontWeight.w600, | ||||||
|  |                       letterSpacing: -0.5, | ||||||
|  |                     ), | ||||||
|  |                   ), | ||||||
|             ), |             ), | ||||||
|             const Spacer(), |             const Spacer(), | ||||||
|             IconButton( |             IconButton( | ||||||
| @@ -668,9 +624,7 @@ class _RealmMemberListSheet extends HookConsumerWidget { | |||||||
|             IconButton( |             IconButton( | ||||||
|               icon: const Icon(Symbols.refresh), |               icon: const Icon(Symbols.refresh), | ||||||
|               onPressed: () { |               onPressed: () { | ||||||
|                 // Refresh both providers |                 // Refresh the provider | ||||||
|                 memberNotifier.reset(); |  | ||||||
|                 memberNotifier.loadMore(); |  | ||||||
|                 ref.invalidate(memberListProvider); |                 ref.invalidate(memberListProvider); | ||||||
|               }, |               }, | ||||||
|             ), |             ), | ||||||
| @@ -744,9 +698,7 @@ class _RealmMemberListSheet extends HookConsumerWidget { | |||||||
|                                   ), |                                   ), | ||||||
|                             ).then((value) { |                             ).then((value) { | ||||||
|                               if (value != null) { |                               if (value != null) { | ||||||
|                                 // Refresh both providers |                                 // Refresh the provider | ||||||
|                                 memberNotifier.reset(); |  | ||||||
|                                 memberNotifier.loadMore(); |  | ||||||
|                                 ref.invalidate(memberListProvider); |                                 ref.invalidate(memberListProvider); | ||||||
|                               } |                               } | ||||||
|                             }); |                             }); | ||||||
| @@ -766,9 +718,7 @@ class _RealmMemberListSheet extends HookConsumerWidget { | |||||||
|                                 await apiClient.delete( |                                 await apiClient.delete( | ||||||
|                                   '/sphere/realms/$realmSlug/members/${member.accountId}', |                                   '/sphere/realms/$realmSlug/members/${member.accountId}', | ||||||
|                                 ); |                                 ); | ||||||
|                                 // Refresh both providers |                                 // Refresh the provider | ||||||
|                                 memberNotifier.reset(); |  | ||||||
|                                 memberNotifier.loadMore(); |  | ||||||
|                                 ref.invalidate(memberListProvider); |                                 ref.invalidate(memberListProvider); | ||||||
|                               } catch (err) { |                               } catch (err) { | ||||||
|                                 showErrorAlert(err); |                                 showErrorAlert(err); | ||||||
| @@ -801,34 +751,6 @@ class _RealmMemberListSheet extends HookConsumerWidget { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| class RealmMemberState { |  | ||||||
|   final List<SnRealmMember> members; |  | ||||||
|   final bool isLoading; |  | ||||||
|   final int total; |  | ||||||
|   final String? error; |  | ||||||
|  |  | ||||||
|   const RealmMemberState({ |  | ||||||
|     required this.members, |  | ||||||
|     required this.isLoading, |  | ||||||
|     required this.total, |  | ||||||
|     this.error, |  | ||||||
|   }); |  | ||||||
|  |  | ||||||
|   RealmMemberState copyWith({ |  | ||||||
|     List<SnRealmMember>? members, |  | ||||||
|     bool? isLoading, |  | ||||||
|     int? total, |  | ||||||
|     String? error, |  | ||||||
|   }) { |  | ||||||
|     return RealmMemberState( |  | ||||||
|       members: members ?? this.members, |  | ||||||
|       isLoading: isLoading ?? this.isLoading, |  | ||||||
|       total: total ?? this.total, |  | ||||||
|       error: error ?? this.error, |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| class _RealmMemberRoleSheet extends HookConsumerWidget { | class _RealmMemberRoleSheet extends HookConsumerWidget { | ||||||
|   final String realmSlug; |   final String realmSlug; | ||||||
|   final SnRealmMember member; |   final SnRealmMember member; | ||||||
|   | |||||||
| @@ -399,7 +399,7 @@ class _RealmChatRoomsProviderElement | |||||||
| } | } | ||||||
|  |  | ||||||
| String _$realmMemberListNotifierHash() => | String _$realmMemberListNotifierHash() => | ||||||
|     r'2f88f803b2e61e7287ed8a43025173e56ff6ca3b'; |     r'db1fd8a6741dfb3d5bb921d5d965f0cfdc0e7bcc'; | ||||||
|  |  | ||||||
| abstract class _$RealmMemberListNotifier | abstract class _$RealmMemberListNotifier | ||||||
|     extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnRealmMember>> { |     extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnRealmMember>> { | ||||||
|   | |||||||
							
								
								
									
										1169
									
								
								swagger.json
									
									
									
									
									
								
							
							
						
						
									
										1169
									
								
								swagger.json
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user