Compare commits
	
		
			3 Commits
		
	
	
		
			1fe4889460
			...
			1232318a5d
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 1232318a5d | |||
|  | 56f41b6c0e | ||
|  | 3ea717d25a | 
| @@ -1,5 +1,6 @@ | |||||||
| import 'dart:convert'; | import 'dart:convert'; | ||||||
| import 'dart:developer'; | import 'dart:developer'; | ||||||
|  | import 'dart:io' show Platform; | ||||||
|  |  | ||||||
| import 'package:dio/dio.dart'; | import 'package:dio/dio.dart'; | ||||||
| import 'package:easy_localization/easy_localization.dart'; | import 'package:easy_localization/easy_localization.dart'; | ||||||
| @@ -28,7 +29,10 @@ class UserInfoNotifier extends StateNotifier<AsyncValue<SnAccount?>> { | |||||||
|       final response = await client.get('/id/accounts/me'); |       final response = await client.get('/id/accounts/me'); | ||||||
|       final user = SnAccount.fromJson(response.data); |       final user = SnAccount.fromJson(response.data); | ||||||
|       state = AsyncValue.data(user); |       state = AsyncValue.data(user); | ||||||
|  |  | ||||||
|  |       if (kIsWeb || !Platform.isLinux) { | ||||||
|         FirebaseAnalytics.instance.setUserId(id: user.id); |         FirebaseAnalytics.instance.setUserId(id: user.id); | ||||||
|  |       } | ||||||
|     } catch (error, stackTrace) { |     } catch (error, stackTrace) { | ||||||
|       if (!kIsWeb) { |       if (!kIsWeb) { | ||||||
|         if (error is DioException) { |         if (error is DioException) { | ||||||
| @@ -83,9 +87,11 @@ class UserInfoNotifier extends StateNotifier<AsyncValue<SnAccount?>> { | |||||||
|     final prefs = _ref.read(sharedPreferencesProvider); |     final prefs = _ref.read(sharedPreferencesProvider); | ||||||
|     await prefs.remove(kTokenPairStoreKey); |     await prefs.remove(kTokenPairStoreKey); | ||||||
|     _ref.invalidate(tokenProvider); |     _ref.invalidate(tokenProvider); | ||||||
|  |     if (kIsWeb || !Platform.isLinux) { | ||||||
|       FirebaseAnalytics.instance.setUserId(id: null); |       FirebaseAnalytics.instance.setUserId(id: null); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| final userInfoProvider = | final userInfoProvider = | ||||||
|     StateNotifierProvider<UserInfoNotifier, AsyncValue<SnAccount?>>( |     StateNotifierProvider<UserInfoNotifier, AsyncValue<SnAccount?>>( | ||||||
|   | |||||||
| @@ -37,6 +37,8 @@ import 'package:island/screens/creators/stickers/stickers.dart'; | |||||||
| import 'package:island/screens/creators/stickers/pack_detail.dart'; | import 'package:island/screens/creators/stickers/pack_detail.dart'; | ||||||
| import 'package:island/screens/stickers/sticker_marketplace.dart'; | import 'package:island/screens/stickers/sticker_marketplace.dart'; | ||||||
| import 'package:island/screens/stickers/pack_detail.dart'; | import 'package:island/screens/stickers/pack_detail.dart'; | ||||||
|  | import 'package:island/screens/discovery/feeds/feed_marketplace.dart'; | ||||||
|  | import 'package:island/screens/discovery/feeds/feed_detail.dart'; | ||||||
| import 'package:island/screens/creators/poll/poll_list.dart'; | import 'package:island/screens/creators/poll/poll_list.dart'; | ||||||
| import 'package:island/screens/creators/publishers.dart'; | import 'package:island/screens/creators/publishers.dart'; | ||||||
| import 'package:island/screens/creators/webfeed/webfeed_list.dart'; | import 'package:island/screens/creators/webfeed/webfeed_list.dart'; | ||||||
| @@ -546,6 +548,22 @@ final routerProvider = Provider<GoRouter>((ref) { | |||||||
|                       ), |                       ), | ||||||
|                     ], |                     ], | ||||||
|                   ), |                   ), | ||||||
|  |                   GoRoute( | ||||||
|  |                     name: 'webFeedMarketplace', | ||||||
|  |                     path: '/feeds', | ||||||
|  |                     builder: | ||||||
|  |                         (context, state) => const MarketplaceWebFeedsScreen(), | ||||||
|  |                     routes: [ | ||||||
|  |                       GoRoute( | ||||||
|  |                         name: 'webFeedDetail', | ||||||
|  |                         path: ':feedId', | ||||||
|  |                         builder: (context, state) { | ||||||
|  |                           final feedId = state.pathParameters['feedId']!; | ||||||
|  |                           return MarketplaceWebFeedDetailScreen(id: feedId); | ||||||
|  |                         }, | ||||||
|  |                       ), | ||||||
|  |                     ], | ||||||
|  |                   ), | ||||||
|                   GoRoute( |                   GoRoute( | ||||||
|                     name: 'notifications', |                     name: 'notifications', | ||||||
|                     path: '/account/notifications', |                     path: '/account/notifications', | ||||||
|   | |||||||
| @@ -236,6 +236,16 @@ class AccountScreen extends HookConsumerWidget { | |||||||
|                 context.pushNamed('stickerMarketplace'); |                 context.pushNamed('stickerMarketplace'); | ||||||
|               }, |               }, | ||||||
|             ), |             ), | ||||||
|  |             ListTile( | ||||||
|  |               minTileHeight: 48, | ||||||
|  |               leading: const Icon(Symbols.rss_feed), | ||||||
|  |               trailing: const Icon(Symbols.chevron_right), | ||||||
|  |               contentPadding: EdgeInsets.symmetric(horizontal: 24), | ||||||
|  |               title: Text('webFeeds').tr(), | ||||||
|  |               onTap: () { | ||||||
|  |                 context.pushNamed('webFeedMarketplace'); | ||||||
|  |               }, | ||||||
|  |             ), | ||||||
|             ListTile( |             ListTile( | ||||||
|               minTileHeight: 48, |               minTileHeight: 48, | ||||||
|               leading: const Icon(Symbols.star), |               leading: const Icon(Symbols.star), | ||||||
|   | |||||||
							
								
								
									
										189
									
								
								lib/screens/discovery/feeds/feed_detail.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								lib/screens/discovery/feeds/feed_detail.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,189 @@ | |||||||
|  | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter/services.dart'; | ||||||
|  | import 'package:gap/gap.dart'; | ||||||
|  | import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||||
|  | import 'package:island/models/webfeed.dart'; | ||||||
|  | import 'package:island/pods/network.dart'; | ||||||
|  | import 'package:island/widgets/alert.dart'; | ||||||
|  | import 'package:island/widgets/app_scaffold.dart'; | ||||||
|  | import 'package:material_symbols_icons/symbols.dart'; | ||||||
|  | import 'package:riverpod_annotation/riverpod_annotation.dart'; | ||||||
|  | import 'package:styled_widget/styled_widget.dart'; | ||||||
|  |  | ||||||
|  | part 'feed_detail.g.dart'; | ||||||
|  |  | ||||||
|  | /// Provider for web feed articles content | ||||||
|  | @riverpod | ||||||
|  | Future<List<SnWebArticle>> marketplaceWebFeedContent( | ||||||
|  |   Ref ref, { | ||||||
|  |   required String feedId, | ||||||
|  | }) async { | ||||||
|  |   final apiClient = ref.watch(apiClientProvider); | ||||||
|  |   final resp = await apiClient.get('/sphere/feeds/$feedId/articles'); | ||||||
|  |   return (resp.data as List).map((e) => SnWebArticle.fromJson(e)).toList(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Provider for web feed subscription status | ||||||
|  | @riverpod | ||||||
|  | Future<bool> marketplaceWebFeedSubscription( | ||||||
|  |   Ref ref, { | ||||||
|  |   required String feedId, | ||||||
|  | }) async { | ||||||
|  |   final api = ref.watch(apiClientProvider); | ||||||
|  |   try { | ||||||
|  |     await api.get('/sphere/feeds/$feedId/subscription'); | ||||||
|  |     // If not 404, consider subscribed | ||||||
|  |     return true; | ||||||
|  |   } on Object catch (e) { | ||||||
|  |     // Dio error handling agnostic: treat 404 as not-subscribed, rethrow others | ||||||
|  |     final msg = e.toString(); | ||||||
|  |     if (msg.contains('404')) return false; | ||||||
|  |     rethrow; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class MarketplaceWebFeedDetailScreen extends HookConsumerWidget { | ||||||
|  |   final String id; | ||||||
|  |   const MarketplaceWebFeedDetailScreen({super.key, required this.id}); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
|  |     // TODO: Need to create a web feed provider similar to stickerPackProvider | ||||||
|  |     // For now, we'll fetch the feed directly | ||||||
|  |     final feedContent = ref.watch( | ||||||
|  |       marketplaceWebFeedContentProvider(feedId: id), | ||||||
|  |     ); | ||||||
|  |     final subscribed = ref.watch( | ||||||
|  |       marketplaceWebFeedSubscriptionProvider(feedId: id), | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     // Subscribe to web feed | ||||||
|  |     Future<void> subscribeToFeed() async { | ||||||
|  |       final apiClient = ref.watch(apiClientProvider); | ||||||
|  |       await apiClient.post('/sphere/feeds/$id/subscribe'); | ||||||
|  |       HapticFeedback.selectionClick(); | ||||||
|  |       ref.invalidate(marketplaceWebFeedSubscriptionProvider(feedId: id)); | ||||||
|  |       if (!context.mounted) return; | ||||||
|  |       showSnackBar('feedSubscribed'.tr()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Unsubscribe from web feed | ||||||
|  |     Future<void> unsubscribeFromFeed() async { | ||||||
|  |       final apiClient = ref.watch(apiClientProvider); | ||||||
|  |       await apiClient.delete('/sphere/feeds/$id/subscribe'); | ||||||
|  |       HapticFeedback.selectionClick(); | ||||||
|  |       ref.invalidate(marketplaceWebFeedSubscriptionProvider(feedId: id)); | ||||||
|  |       if (!context.mounted) return; | ||||||
|  |       showSnackBar('feedUnsubscribed'.tr()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // TODO: Replace with actual feed data provider once created | ||||||
|  |     final dummyFeed = SnWebFeed( | ||||||
|  |       id: id, | ||||||
|  |       url: 'https://example.com', | ||||||
|  |       title: 'Loading...', | ||||||
|  |       publisherId: 'publisher-id', | ||||||
|  |       createdAt: DateTime.now(), | ||||||
|  |       updatedAt: DateTime.now(), | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     return AppScaffold( | ||||||
|  |       appBar: AppBar(title: Text(dummyFeed.title)), | ||||||
|  |       body: Column( | ||||||
|  |         crossAxisAlignment: CrossAxisAlignment.stretch, | ||||||
|  |         children: [ | ||||||
|  |           // Feed meta | ||||||
|  |           Column( | ||||||
|  |             crossAxisAlignment: CrossAxisAlignment.stretch, | ||||||
|  |             children: [ | ||||||
|  |               Text(dummyFeed.description ?? ''), | ||||||
|  |               Row( | ||||||
|  |                 spacing: 4, | ||||||
|  |                 children: [ | ||||||
|  |                   const Icon(Symbols.rss_feed, size: 16), | ||||||
|  |                   Text('${feedContent.value?.length ?? 0} articles'), | ||||||
|  |                 ], | ||||||
|  |               ).opacity(0.85), | ||||||
|  |               Row( | ||||||
|  |                 spacing: 4, | ||||||
|  |                 children: [ | ||||||
|  |                   const Icon(Symbols.link, size: 16), | ||||||
|  |                   SelectableText(dummyFeed.url), | ||||||
|  |                 ], | ||||||
|  |               ).opacity(0.85), | ||||||
|  |             ], | ||||||
|  |           ).padding(horizontal: 24, vertical: 24), | ||||||
|  |           const Divider(height: 1), | ||||||
|  |           // Articles list | ||||||
|  |           Expanded( | ||||||
|  |             child: feedContent.when( | ||||||
|  |               data: | ||||||
|  |                   (articles) => RefreshIndicator( | ||||||
|  |                     onRefresh: | ||||||
|  |                         () => ref.refresh( | ||||||
|  |                           marketplaceWebFeedContentProvider(feedId: id).future, | ||||||
|  |                         ), | ||||||
|  |                     child: ListView.builder( | ||||||
|  |                       padding: const EdgeInsets.symmetric( | ||||||
|  |                         horizontal: 24, | ||||||
|  |                         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 | ||||||
|  |                             }, | ||||||
|  |                           ), | ||||||
|  |                         ); | ||||||
|  |                       }, | ||||||
|  |                     ), | ||||||
|  |                   ), | ||||||
|  |               error: | ||||||
|  |                   (err, _) => | ||||||
|  |                       Text( | ||||||
|  |                         'Error: $err', | ||||||
|  |                       ).textAlignment(TextAlign.center).center(), | ||||||
|  |               loading: () => const CircularProgressIndicator().center(), | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |           Padding( | ||||||
|  |             padding: EdgeInsets.symmetric(horizontal: 24, vertical: 8), | ||||||
|  |             child: subscribed.when( | ||||||
|  |               data: | ||||||
|  |                   (isSubscribed) => FilledButton.icon( | ||||||
|  |                     onPressed: | ||||||
|  |                         isSubscribed ? unsubscribeFromFeed : subscribeToFeed, | ||||||
|  |                     icon: Icon( | ||||||
|  |                       isSubscribed ? Symbols.remove_circle : Symbols.add_circle, | ||||||
|  |                     ), | ||||||
|  |                     label: Text( | ||||||
|  |                       isSubscribed ? 'unsubscribe'.tr() : 'subscribe'.tr(), | ||||||
|  |                     ), | ||||||
|  |                   ), | ||||||
|  |               loading: | ||||||
|  |                   () => const SizedBox( | ||||||
|  |                     height: 32, | ||||||
|  |                     width: 32, | ||||||
|  |                     child: CircularProgressIndicator(strokeWidth: 2), | ||||||
|  |                   ), | ||||||
|  |               error: | ||||||
|  |                   (_, _) => OutlinedButton.icon( | ||||||
|  |                     onPressed: subscribeToFeed, | ||||||
|  |                     icon: const Icon(Symbols.add_circle), | ||||||
|  |                     label: Text('subscribe').tr(), | ||||||
|  |                   ), | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |           Gap(MediaQuery.of(context).padding.bottom), | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										313
									
								
								lib/screens/discovery/feeds/feed_detail.g.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										313
									
								
								lib/screens/discovery/feeds/feed_detail.g.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,313 @@ | |||||||
|  | // GENERATED CODE - DO NOT MODIFY BY HAND | ||||||
|  |  | ||||||
|  | part of 'feed_detail.dart'; | ||||||
|  |  | ||||||
|  | // ************************************************************************** | ||||||
|  | // RiverpodGenerator | ||||||
|  | // ************************************************************************** | ||||||
|  |  | ||||||
|  | String _$marketplaceWebFeedContentHash() => | ||||||
|  |     r'4e65350bff4055302e15ec14266cdebb1cd89bbe'; | ||||||
|  |  | ||||||
|  | /// Copied from Dart SDK | ||||||
|  | class _SystemHash { | ||||||
|  |   _SystemHash._(); | ||||||
|  |  | ||||||
|  |   static int combine(int hash, int value) { | ||||||
|  |     // ignore: parameter_assignments | ||||||
|  |     hash = 0x1fffffff & (hash + value); | ||||||
|  |     // ignore: parameter_assignments | ||||||
|  |     hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); | ||||||
|  |     return hash ^ (hash >> 6); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static int finish(int hash) { | ||||||
|  |     // ignore: parameter_assignments | ||||||
|  |     hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); | ||||||
|  |     // ignore: parameter_assignments | ||||||
|  |     hash = hash ^ (hash >> 11); | ||||||
|  |     return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Provider for web feed articles content | ||||||
|  | /// | ||||||
|  | /// Copied from [marketplaceWebFeedContent]. | ||||||
|  | @ProviderFor(marketplaceWebFeedContent) | ||||||
|  | const marketplaceWebFeedContentProvider = MarketplaceWebFeedContentFamily(); | ||||||
|  |  | ||||||
|  | /// Provider for web feed articles content | ||||||
|  | /// | ||||||
|  | /// Copied from [marketplaceWebFeedContent]. | ||||||
|  | class MarketplaceWebFeedContentFamily | ||||||
|  |     extends Family<AsyncValue<List<SnWebArticle>>> { | ||||||
|  |   /// Provider for web feed articles content | ||||||
|  |   /// | ||||||
|  |   /// Copied from [marketplaceWebFeedContent]. | ||||||
|  |   const MarketplaceWebFeedContentFamily(); | ||||||
|  |  | ||||||
|  |   /// Provider for web feed articles content | ||||||
|  |   /// | ||||||
|  |   /// Copied from [marketplaceWebFeedContent]. | ||||||
|  |   MarketplaceWebFeedContentProvider call({required String feedId}) { | ||||||
|  |     return MarketplaceWebFeedContentProvider(feedId: feedId); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   MarketplaceWebFeedContentProvider getProviderOverride( | ||||||
|  |     covariant MarketplaceWebFeedContentProvider provider, | ||||||
|  |   ) { | ||||||
|  |     return call(feedId: 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'marketplaceWebFeedContentProvider'; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Provider for web feed articles content | ||||||
|  | /// | ||||||
|  | /// Copied from [marketplaceWebFeedContent]. | ||||||
|  | class MarketplaceWebFeedContentProvider | ||||||
|  |     extends AutoDisposeFutureProvider<List<SnWebArticle>> { | ||||||
|  |   /// Provider for web feed articles content | ||||||
|  |   /// | ||||||
|  |   /// Copied from [marketplaceWebFeedContent]. | ||||||
|  |   MarketplaceWebFeedContentProvider({required String feedId}) | ||||||
|  |     : this._internal( | ||||||
|  |         (ref) => marketplaceWebFeedContent( | ||||||
|  |           ref as MarketplaceWebFeedContentRef, | ||||||
|  |           feedId: feedId, | ||||||
|  |         ), | ||||||
|  |         from: marketplaceWebFeedContentProvider, | ||||||
|  |         name: r'marketplaceWebFeedContentProvider', | ||||||
|  |         debugGetCreateSourceHash: | ||||||
|  |             const bool.fromEnvironment('dart.vm.product') | ||||||
|  |                 ? null | ||||||
|  |                 : _$marketplaceWebFeedContentHash, | ||||||
|  |         dependencies: MarketplaceWebFeedContentFamily._dependencies, | ||||||
|  |         allTransitiveDependencies: | ||||||
|  |             MarketplaceWebFeedContentFamily._allTransitiveDependencies, | ||||||
|  |         feedId: feedId, | ||||||
|  |       ); | ||||||
|  |  | ||||||
|  |   MarketplaceWebFeedContentProvider._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 | ||||||
|  |   Override overrideWith( | ||||||
|  |     FutureOr<List<SnWebArticle>> Function(MarketplaceWebFeedContentRef provider) | ||||||
|  |     create, | ||||||
|  |   ) { | ||||||
|  |     return ProviderOverride( | ||||||
|  |       origin: this, | ||||||
|  |       override: MarketplaceWebFeedContentProvider._internal( | ||||||
|  |         (ref) => create(ref as MarketplaceWebFeedContentRef), | ||||||
|  |         from: from, | ||||||
|  |         name: null, | ||||||
|  |         dependencies: null, | ||||||
|  |         allTransitiveDependencies: null, | ||||||
|  |         debugGetCreateSourceHash: null, | ||||||
|  |         feedId: feedId, | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   AutoDisposeFutureProviderElement<List<SnWebArticle>> createElement() { | ||||||
|  |     return _MarketplaceWebFeedContentProviderElement(this); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   bool operator ==(Object other) { | ||||||
|  |     return other is MarketplaceWebFeedContentProvider && 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 MarketplaceWebFeedContentRef | ||||||
|  |     on AutoDisposeFutureProviderRef<List<SnWebArticle>> { | ||||||
|  |   /// The parameter `feedId` of this provider. | ||||||
|  |   String get feedId; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class _MarketplaceWebFeedContentProviderElement | ||||||
|  |     extends AutoDisposeFutureProviderElement<List<SnWebArticle>> | ||||||
|  |     with MarketplaceWebFeedContentRef { | ||||||
|  |   _MarketplaceWebFeedContentProviderElement(super.provider); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String get feedId => (origin as MarketplaceWebFeedContentProvider).feedId; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | String _$marketplaceWebFeedSubscriptionHash() => | ||||||
|  |     r'2ff06a48ed7d4236b57412ecca55e94c0a0b6330'; | ||||||
|  |  | ||||||
|  | /// Provider for web feed subscription status | ||||||
|  | /// | ||||||
|  | /// Copied from [marketplaceWebFeedSubscription]. | ||||||
|  | @ProviderFor(marketplaceWebFeedSubscription) | ||||||
|  | const marketplaceWebFeedSubscriptionProvider = | ||||||
|  |     MarketplaceWebFeedSubscriptionFamily(); | ||||||
|  |  | ||||||
|  | /// Provider for web feed subscription status | ||||||
|  | /// | ||||||
|  | /// Copied from [marketplaceWebFeedSubscription]. | ||||||
|  | class MarketplaceWebFeedSubscriptionFamily extends Family<AsyncValue<bool>> { | ||||||
|  |   /// Provider for web feed subscription status | ||||||
|  |   /// | ||||||
|  |   /// Copied from [marketplaceWebFeedSubscription]. | ||||||
|  |   const MarketplaceWebFeedSubscriptionFamily(); | ||||||
|  |  | ||||||
|  |   /// Provider for web feed subscription status | ||||||
|  |   /// | ||||||
|  |   /// Copied from [marketplaceWebFeedSubscription]. | ||||||
|  |   MarketplaceWebFeedSubscriptionProvider call({required String feedId}) { | ||||||
|  |     return MarketplaceWebFeedSubscriptionProvider(feedId: feedId); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   MarketplaceWebFeedSubscriptionProvider getProviderOverride( | ||||||
|  |     covariant MarketplaceWebFeedSubscriptionProvider provider, | ||||||
|  |   ) { | ||||||
|  |     return call(feedId: 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'marketplaceWebFeedSubscriptionProvider'; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Provider for web feed subscription status | ||||||
|  | /// | ||||||
|  | /// Copied from [marketplaceWebFeedSubscription]. | ||||||
|  | class MarketplaceWebFeedSubscriptionProvider | ||||||
|  |     extends AutoDisposeFutureProvider<bool> { | ||||||
|  |   /// Provider for web feed subscription status | ||||||
|  |   /// | ||||||
|  |   /// Copied from [marketplaceWebFeedSubscription]. | ||||||
|  |   MarketplaceWebFeedSubscriptionProvider({required String feedId}) | ||||||
|  |     : this._internal( | ||||||
|  |         (ref) => marketplaceWebFeedSubscription( | ||||||
|  |           ref as MarketplaceWebFeedSubscriptionRef, | ||||||
|  |           feedId: feedId, | ||||||
|  |         ), | ||||||
|  |         from: marketplaceWebFeedSubscriptionProvider, | ||||||
|  |         name: r'marketplaceWebFeedSubscriptionProvider', | ||||||
|  |         debugGetCreateSourceHash: | ||||||
|  |             const bool.fromEnvironment('dart.vm.product') | ||||||
|  |                 ? null | ||||||
|  |                 : _$marketplaceWebFeedSubscriptionHash, | ||||||
|  |         dependencies: MarketplaceWebFeedSubscriptionFamily._dependencies, | ||||||
|  |         allTransitiveDependencies: | ||||||
|  |             MarketplaceWebFeedSubscriptionFamily._allTransitiveDependencies, | ||||||
|  |         feedId: feedId, | ||||||
|  |       ); | ||||||
|  |  | ||||||
|  |   MarketplaceWebFeedSubscriptionProvider._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 | ||||||
|  |   Override overrideWith( | ||||||
|  |     FutureOr<bool> Function(MarketplaceWebFeedSubscriptionRef provider) create, | ||||||
|  |   ) { | ||||||
|  |     return ProviderOverride( | ||||||
|  |       origin: this, | ||||||
|  |       override: MarketplaceWebFeedSubscriptionProvider._internal( | ||||||
|  |         (ref) => create(ref as MarketplaceWebFeedSubscriptionRef), | ||||||
|  |         from: from, | ||||||
|  |         name: null, | ||||||
|  |         dependencies: null, | ||||||
|  |         allTransitiveDependencies: null, | ||||||
|  |         debugGetCreateSourceHash: null, | ||||||
|  |         feedId: feedId, | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   AutoDisposeFutureProviderElement<bool> createElement() { | ||||||
|  |     return _MarketplaceWebFeedSubscriptionProviderElement(this); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   bool operator ==(Object other) { | ||||||
|  |     return other is MarketplaceWebFeedSubscriptionProvider && | ||||||
|  |         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 MarketplaceWebFeedSubscriptionRef on AutoDisposeFutureProviderRef<bool> { | ||||||
|  |   /// The parameter `feedId` of this provider. | ||||||
|  |   String get feedId; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class _MarketplaceWebFeedSubscriptionProviderElement | ||||||
|  |     extends AutoDisposeFutureProviderElement<bool> | ||||||
|  |     with MarketplaceWebFeedSubscriptionRef { | ||||||
|  |   _MarketplaceWebFeedSubscriptionProviderElement(super.provider); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String get feedId => | ||||||
|  |       (origin as MarketplaceWebFeedSubscriptionProvider).feedId; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 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 | ||||||
							
								
								
									
										169
									
								
								lib/screens/discovery/feeds/feed_marketplace.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								lib/screens/discovery/feeds/feed_marketplace.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,169 @@ | |||||||
|  | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_hooks/flutter_hooks.dart'; | ||||||
|  | import 'package:go_router/go_router.dart'; | ||||||
|  | import 'package:gap/gap.dart'; | ||||||
|  | import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||||
|  | import 'package:island/models/webfeed.dart'; | ||||||
|  | import 'package:island/pods/network.dart'; | ||||||
|  | import 'package:island/widgets/app_scaffold.dart'; | ||||||
|  | import 'package:material_symbols_icons/symbols.dart'; | ||||||
|  | import 'dart:async'; | ||||||
|  |  | ||||||
|  | import 'package:riverpod_annotation/riverpod_annotation.dart'; | ||||||
|  | import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; | ||||||
|  |  | ||||||
|  | part 'feed_marketplace.g.dart'; | ||||||
|  |  | ||||||
|  | @riverpod | ||||||
|  | class MarketplaceWebFeedsNotifier extends _$MarketplaceWebFeedsNotifier | ||||||
|  |     with CursorPagingNotifierMixin<SnWebFeed> { | ||||||
|  |   String? _query; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Future<CursorPagingData<SnWebFeed>> build({required String? query}) { | ||||||
|  |     _query = query; | ||||||
|  |     return fetch(cursor: null); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Future<CursorPagingData<SnWebFeed>> fetch({required String? cursor}) async { | ||||||
|  |     final client = ref.read(apiClientProvider); | ||||||
|  |     final offset = cursor == null ? 0 : int.parse(cursor); | ||||||
|  |  | ||||||
|  |     final response = await client.get( | ||||||
|  |       '/sphere/feeds', | ||||||
|  |       queryParameters: { | ||||||
|  |         'offset': offset, | ||||||
|  |         'take': 20, | ||||||
|  |         if (_query != null && _query!.isNotEmpty) 'query': _query, | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     final total = int.parse(response.headers.value('X-Total') ?? '0'); | ||||||
|  |     final List<dynamic> data = response.data; | ||||||
|  |     final feeds = data.map((e) => SnWebFeed.fromJson(e)).toList(); | ||||||
|  |  | ||||||
|  |     final hasMore = offset + feeds.length < total; | ||||||
|  |     final nextCursor = hasMore ? (offset + feeds.length).toString() : null; | ||||||
|  |  | ||||||
|  |     return CursorPagingData( | ||||||
|  |       items: feeds, | ||||||
|  |       hasMore: hasMore, | ||||||
|  |       nextCursor: nextCursor, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Marketplace screen for browsing web feeds. | ||||||
|  | class MarketplaceWebFeedsScreen extends HookConsumerWidget { | ||||||
|  |   const MarketplaceWebFeedsScreen({super.key}); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
|  |     final query = useState<String?>(null); | ||||||
|  |     final searchController = useTextEditingController(); | ||||||
|  |     final focusNode = useFocusNode(); | ||||||
|  |     final debounceTimer = useState<Timer?>(null); | ||||||
|  |  | ||||||
|  |     // Clear search when query is cleared | ||||||
|  |     useEffect(() { | ||||||
|  |       if (query.value == null || query.value!.isEmpty) { | ||||||
|  |         searchController.clear(); | ||||||
|  |       } | ||||||
|  |       return null; | ||||||
|  |     }, [query.value]); | ||||||
|  |  | ||||||
|  |     // Clean up timer on dispose | ||||||
|  |     useEffect(() { | ||||||
|  |       return () { | ||||||
|  |         debounceTimer.value?.cancel(); | ||||||
|  |       }; | ||||||
|  |     }, []); | ||||||
|  |  | ||||||
|  |     return AppScaffold( | ||||||
|  |       appBar: AppBar( | ||||||
|  |         title: const Text('webFeeds').tr(), | ||||||
|  |         actions: const [Gap(8)], | ||||||
|  |       ), | ||||||
|  |       body: PagingHelperView( | ||||||
|  |         provider: marketplaceWebFeedsNotifierProvider(query: query.value), | ||||||
|  |         futureRefreshable: | ||||||
|  |             marketplaceWebFeedsNotifierProvider(query: query.value).future, | ||||||
|  |         notifierRefreshable: | ||||||
|  |             marketplaceWebFeedsNotifierProvider(query: query.value).notifier, | ||||||
|  |         contentBuilder: | ||||||
|  |             (data, widgetCount, endItemView) => Column( | ||||||
|  |               children: [ | ||||||
|  |                 // Search bar above the list | ||||||
|  |                 Padding( | ||||||
|  |                   padding: const EdgeInsets.all(16), | ||||||
|  |                   child: SearchBar( | ||||||
|  |                     elevation: WidgetStateProperty.all(4), | ||||||
|  |                     controller: searchController, | ||||||
|  |                     focusNode: focusNode, | ||||||
|  |                     hintText: 'search'.tr(), | ||||||
|  |                     leading: const Icon(Symbols.search), | ||||||
|  |                     padding: WidgetStateProperty.all( | ||||||
|  |                       const EdgeInsets.symmetric(horizontal: 24), | ||||||
|  |                     ), | ||||||
|  |                     onTapOutside: | ||||||
|  |                         (_) => FocusManager.instance.primaryFocus?.unfocus(), | ||||||
|  |                     trailing: [ | ||||||
|  |                       if (query.value != null && query.value!.isNotEmpty) | ||||||
|  |                         IconButton( | ||||||
|  |                           icon: const Icon(Symbols.close), | ||||||
|  |                           onPressed: () { | ||||||
|  |                             query.value = null; | ||||||
|  |                             searchController.clear(); | ||||||
|  |                             focusNode.unfocus(); | ||||||
|  |                           }, | ||||||
|  |                         ), | ||||||
|  |                     ], | ||||||
|  |                     onChanged: (value) { | ||||||
|  |                       // Debounce search to avoid excessive API calls | ||||||
|  |                       debounceTimer.value?.cancel(); | ||||||
|  |                       debounceTimer.value = Timer( | ||||||
|  |                         const Duration(milliseconds: 500), | ||||||
|  |                         () { | ||||||
|  |                           query.value = value.isEmpty ? null : value; | ||||||
|  |                         }, | ||||||
|  |                       ); | ||||||
|  |                     }, | ||||||
|  |                     onSubmitted: (value) { | ||||||
|  |                       query.value = value.isEmpty ? null : value; | ||||||
|  |                       focusNode.unfocus(); | ||||||
|  |                     }, | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |                 Expanded( | ||||||
|  |                   child: ListView.builder( | ||||||
|  |                     padding: EdgeInsets.zero, | ||||||
|  |                     itemCount: widgetCount, | ||||||
|  |                     itemBuilder: (context, index) { | ||||||
|  |                       if (index == widgetCount - 1) { | ||||||
|  |                         return endItemView; | ||||||
|  |                       } | ||||||
|  |  | ||||||
|  |                       final feed = data.items[index]; | ||||||
|  |                       return ListTile( | ||||||
|  |                         title: Text(feed.title), | ||||||
|  |                         subtitle: Text(feed.description ?? ''), | ||||||
|  |                         trailing: const Icon(Symbols.chevron_right), | ||||||
|  |                         onTap: () { | ||||||
|  |                           // Navigate to web feed detail page | ||||||
|  |                           context.pushNamed( | ||||||
|  |                             'webFeedDetail', | ||||||
|  |                             pathParameters: {'feedId': feed.id}, | ||||||
|  |                           ); | ||||||
|  |                         }, | ||||||
|  |                       ); | ||||||
|  |                     }, | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ], | ||||||
|  |             ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										180
									
								
								lib/screens/discovery/feeds/feed_marketplace.g.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								lib/screens/discovery/feeds/feed_marketplace.g.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,180 @@ | |||||||
|  | // GENERATED CODE - DO NOT MODIFY BY HAND | ||||||
|  |  | ||||||
|  | part of 'feed_marketplace.dart'; | ||||||
|  |  | ||||||
|  | // ************************************************************************** | ||||||
|  | // RiverpodGenerator | ||||||
|  | // ************************************************************************** | ||||||
|  |  | ||||||
|  | String _$marketplaceWebFeedsNotifierHash() => | ||||||
|  |     r'dbf885d95570ca9c2259a58998975db813b18cbb'; | ||||||
|  |  | ||||||
|  | /// Copied from Dart SDK | ||||||
|  | class _SystemHash { | ||||||
|  |   _SystemHash._(); | ||||||
|  |  | ||||||
|  |   static int combine(int hash, int value) { | ||||||
|  |     // ignore: parameter_assignments | ||||||
|  |     hash = 0x1fffffff & (hash + value); | ||||||
|  |     // ignore: parameter_assignments | ||||||
|  |     hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); | ||||||
|  |     return hash ^ (hash >> 6); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static int finish(int hash) { | ||||||
|  |     // ignore: parameter_assignments | ||||||
|  |     hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); | ||||||
|  |     // ignore: parameter_assignments | ||||||
|  |     hash = hash ^ (hash >> 11); | ||||||
|  |     return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | abstract class _$MarketplaceWebFeedsNotifier | ||||||
|  |     extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnWebFeed>> { | ||||||
|  |   late final String? query; | ||||||
|  |  | ||||||
|  |   FutureOr<CursorPagingData<SnWebFeed>> build({required String? query}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// See also [MarketplaceWebFeedsNotifier]. | ||||||
|  | @ProviderFor(MarketplaceWebFeedsNotifier) | ||||||
|  | const marketplaceWebFeedsNotifierProvider = MarketplaceWebFeedsNotifierFamily(); | ||||||
|  |  | ||||||
|  | /// See also [MarketplaceWebFeedsNotifier]. | ||||||
|  | class MarketplaceWebFeedsNotifierFamily | ||||||
|  |     extends Family<AsyncValue<CursorPagingData<SnWebFeed>>> { | ||||||
|  |   /// See also [MarketplaceWebFeedsNotifier]. | ||||||
|  |   const MarketplaceWebFeedsNotifierFamily(); | ||||||
|  |  | ||||||
|  |   /// See also [MarketplaceWebFeedsNotifier]. | ||||||
|  |   MarketplaceWebFeedsNotifierProvider call({required String? query}) { | ||||||
|  |     return MarketplaceWebFeedsNotifierProvider(query: query); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   MarketplaceWebFeedsNotifierProvider getProviderOverride( | ||||||
|  |     covariant MarketplaceWebFeedsNotifierProvider provider, | ||||||
|  |   ) { | ||||||
|  |     return call(query: provider.query); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   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'marketplaceWebFeedsNotifierProvider'; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// See also [MarketplaceWebFeedsNotifier]. | ||||||
|  | class MarketplaceWebFeedsNotifierProvider | ||||||
|  |     extends | ||||||
|  |         AutoDisposeAsyncNotifierProviderImpl< | ||||||
|  |           MarketplaceWebFeedsNotifier, | ||||||
|  |           CursorPagingData<SnWebFeed> | ||||||
|  |         > { | ||||||
|  |   /// See also [MarketplaceWebFeedsNotifier]. | ||||||
|  |   MarketplaceWebFeedsNotifierProvider({required String? query}) | ||||||
|  |     : this._internal( | ||||||
|  |         () => MarketplaceWebFeedsNotifier()..query = query, | ||||||
|  |         from: marketplaceWebFeedsNotifierProvider, | ||||||
|  |         name: r'marketplaceWebFeedsNotifierProvider', | ||||||
|  |         debugGetCreateSourceHash: | ||||||
|  |             const bool.fromEnvironment('dart.vm.product') | ||||||
|  |                 ? null | ||||||
|  |                 : _$marketplaceWebFeedsNotifierHash, | ||||||
|  |         dependencies: MarketplaceWebFeedsNotifierFamily._dependencies, | ||||||
|  |         allTransitiveDependencies: | ||||||
|  |             MarketplaceWebFeedsNotifierFamily._allTransitiveDependencies, | ||||||
|  |         query: query, | ||||||
|  |       ); | ||||||
|  |  | ||||||
|  |   MarketplaceWebFeedsNotifierProvider._internal( | ||||||
|  |     super._createNotifier, { | ||||||
|  |     required super.name, | ||||||
|  |     required super.dependencies, | ||||||
|  |     required super.allTransitiveDependencies, | ||||||
|  |     required super.debugGetCreateSourceHash, | ||||||
|  |     required super.from, | ||||||
|  |     required this.query, | ||||||
|  |   }) : super.internal(); | ||||||
|  |  | ||||||
|  |   final String? query; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   FutureOr<CursorPagingData<SnWebFeed>> runNotifierBuild( | ||||||
|  |     covariant MarketplaceWebFeedsNotifier notifier, | ||||||
|  |   ) { | ||||||
|  |     return notifier.build(query: query); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Override overrideWith(MarketplaceWebFeedsNotifier Function() create) { | ||||||
|  |     return ProviderOverride( | ||||||
|  |       origin: this, | ||||||
|  |       override: MarketplaceWebFeedsNotifierProvider._internal( | ||||||
|  |         () => create()..query = query, | ||||||
|  |         from: from, | ||||||
|  |         name: null, | ||||||
|  |         dependencies: null, | ||||||
|  |         allTransitiveDependencies: null, | ||||||
|  |         debugGetCreateSourceHash: null, | ||||||
|  |         query: query, | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   AutoDisposeAsyncNotifierProviderElement< | ||||||
|  |     MarketplaceWebFeedsNotifier, | ||||||
|  |     CursorPagingData<SnWebFeed> | ||||||
|  |   > | ||||||
|  |   createElement() { | ||||||
|  |     return _MarketplaceWebFeedsNotifierProviderElement(this); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   bool operator ==(Object other) { | ||||||
|  |     return other is MarketplaceWebFeedsNotifierProvider && other.query == query; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   int get hashCode { | ||||||
|  |     var hash = _SystemHash.combine(0, runtimeType.hashCode); | ||||||
|  |     hash = _SystemHash.combine(hash, query.hashCode); | ||||||
|  |  | ||||||
|  |     return _SystemHash.finish(hash); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @Deprecated('Will be removed in 3.0. Use Ref instead') | ||||||
|  | // ignore: unused_element | ||||||
|  | mixin MarketplaceWebFeedsNotifierRef | ||||||
|  |     on AutoDisposeAsyncNotifierProviderRef<CursorPagingData<SnWebFeed>> { | ||||||
|  |   /// The parameter `query` of this provider. | ||||||
|  |   String? get query; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class _MarketplaceWebFeedsNotifierProviderElement | ||||||
|  |     extends | ||||||
|  |         AutoDisposeAsyncNotifierProviderElement< | ||||||
|  |           MarketplaceWebFeedsNotifier, | ||||||
|  |           CursorPagingData<SnWebFeed> | ||||||
|  |         > | ||||||
|  |     with MarketplaceWebFeedsNotifierRef { | ||||||
|  |   _MarketplaceWebFeedsNotifierProviderElement(super.provider); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String? get query => (origin as MarketplaceWebFeedsNotifierProvider).query; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 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 | ||||||
| @@ -7,7 +7,7 @@ part of 'sticker_marketplace.dart'; | |||||||
| // ************************************************************************** | // ************************************************************************** | ||||||
|  |  | ||||||
| String _$marketplaceStickerPacksNotifierHash() => | String _$marketplaceStickerPacksNotifierHash() => | ||||||
|     r'711eafeadf488485521563d0831676c51772d13c'; |     r'3bde76e18bb024f45ff6261fe735cdba97b02808'; | ||||||
|  |  | ||||||
| /// Copied from Dart SDK | /// Copied from Dart SDK | ||||||
| class _SystemHash { | class _SystemHash { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user