From cdf272226834b7159c0899c36218025522283b13 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Fri, 22 Aug 2025 22:59:41 +0800 Subject: [PATCH] :sparkles: Stickers order by usage --- assets/i18n/en-US.json | 4 +- lib/route.dart | 2 +- lib/screens/stickers/marketplace.dart | 103 ---------- lib/screens/stickers/marketplace.g.dart | 32 --- lib/screens/stickers/sticker_marketplace.dart | 124 ++++++++++++ .../stickers/sticker_marketplace.g.dart | 183 ++++++++++++++++++ 6 files changed, 311 insertions(+), 137 deletions(-) delete mode 100644 lib/screens/stickers/marketplace.dart delete mode 100644 lib/screens/stickers/marketplace.g.dart create mode 100644 lib/screens/stickers/sticker_marketplace.dart create mode 100644 lib/screens/stickers/sticker_marketplace.g.dart diff --git a/assets/i18n/en-US.json b/assets/i18n/en-US.json index e782ae34..11a6662c 100644 --- a/assets/i18n/en-US.json +++ b/assets/i18n/en-US.json @@ -875,5 +875,7 @@ "socialCreditsLevelPoor": "Poor", "socialCreditsLevelNormal": "Normal", "socialCreditsLevelGood": "Good", - "socialCreditsLevelExcellent": "Excellent" + "socialCreditsLevelExcellent": "Excellent", + "orderByPopularity": "Sort by popularity", + "orderByReleaseDate": "Sort by release date" } diff --git a/lib/route.dart b/lib/route.dart index fa5e92de..a9e18bb7 100644 --- a/lib/route.dart +++ b/lib/route.dart @@ -35,7 +35,7 @@ import 'package:island/screens/creators/hub.dart'; import 'package:island/screens/creators/posts/post_manage_list.dart'; import 'package:island/screens/creators/stickers/stickers.dart'; import 'package:island/screens/creators/stickers/pack_detail.dart'; -import 'package:island/screens/stickers/marketplace.dart'; +import 'package:island/screens/stickers/sticker_marketplace.dart'; import 'package:island/screens/stickers/pack_detail.dart'; import 'package:island/screens/creators/poll/poll_list.dart'; import 'package:island/screens/creators/publishers.dart'; diff --git a/lib/screens/stickers/marketplace.dart b/lib/screens/stickers/marketplace.dart deleted file mode 100644 index 5cbd331b..00000000 --- a/lib/screens/stickers/marketplace.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:gap/gap.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:island/models/sticker.dart'; -import 'package:island/pods/network.dart'; -import 'package:island/widgets/app_scaffold.dart'; -import 'package:material_symbols_icons/symbols.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; -import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; - -part 'marketplace.g.dart'; - -@riverpod -class MarketplaceStickerPacksNotifier extends _$MarketplaceStickerPacksNotifier - with CursorPagingNotifierMixin { - @override - Future> build() { - return fetch(cursor: null); - } - - @override - Future> fetch({ - required String? cursor, - }) async { - final client = ref.read(apiClientProvider); - final offset = cursor == null ? 0 : int.parse(cursor); - - final response = await client.get( - '/sphere/stickers', - queryParameters: {'offset': offset, 'take': 20}, - ); - - final total = int.parse(response.headers.value('X-Total') ?? '0'); - final List data = response.data; - final stickers = data.map((e) => SnStickerPack.fromJson(e)).toList(); - - final hasMore = offset + stickers.length < total; - final nextCursor = hasMore ? (offset + stickers.length).toString() : null; - - return CursorPagingData( - items: stickers, - hasMore: hasMore, - nextCursor: nextCursor, - ); - } -} - -/// User-facing marketplace screen for browsing sticker packs. -/// This version does NOT rely on publisher name (no pubName). -class MarketplaceStickersScreen extends HookConsumerWidget { - const MarketplaceStickersScreen({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - return AppScaffold( - appBar: AppBar( - title: const Text('stickers').tr(), - actions: const [Gap(8)], - ), - body: const SliverMarketplaceStickerPacksList(), - ); - } -} - -class SliverMarketplaceStickerPacksList extends HookConsumerWidget { - const SliverMarketplaceStickerPacksList({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - return PagingHelperView( - provider: marketplaceStickerPacksNotifierProvider, - futureRefreshable: marketplaceStickerPacksNotifierProvider.future, - notifierRefreshable: marketplaceStickerPacksNotifierProvider.notifier, - contentBuilder: - (data, widgetCount, endItemView) => ListView.builder( - padding: EdgeInsets.zero, - itemCount: widgetCount, - itemBuilder: (context, index) { - if (index == widgetCount - 1) { - return endItemView; - } - - final pack = data.items[index]; - return ListTile( - title: Text(pack.name), - subtitle: Text(pack.description), - trailing: const Icon(Symbols.chevron_right), - onTap: () { - // Navigate to user-facing sticker pack detail page. - // Adjust the route name/parameters if your app uses different ones. - context.pushNamed( - 'stickerPackDetail', - pathParameters: {'packId': pack.id}, - ); - }, - ); - }, - ), - ); - } -} diff --git a/lib/screens/stickers/marketplace.g.dart b/lib/screens/stickers/marketplace.g.dart deleted file mode 100644 index 939b81f0..00000000 --- a/lib/screens/stickers/marketplace.g.dart +++ /dev/null @@ -1,32 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'marketplace.dart'; - -// ************************************************************************** -// RiverpodGenerator -// ************************************************************************** - -String _$marketplaceStickerPacksNotifierHash() => - r'b62ae8b7f5c4f8bb3be8c17fc005ea26da355187'; - -/// See also [MarketplaceStickerPacksNotifier]. -@ProviderFor(MarketplaceStickerPacksNotifier) -final marketplaceStickerPacksNotifierProvider = - AutoDisposeAsyncNotifierProvider< - MarketplaceStickerPacksNotifier, - CursorPagingData - >.internal( - MarketplaceStickerPacksNotifier.new, - name: r'marketplaceStickerPacksNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$marketplaceStickerPacksNotifierHash, - dependencies: null, - allTransitiveDependencies: null, - ); - -typedef _$MarketplaceStickerPacksNotifier = - AutoDisposeAsyncNotifier>; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/screens/stickers/sticker_marketplace.dart b/lib/screens/stickers/sticker_marketplace.dart new file mode 100644 index 00000000..35796791 --- /dev/null +++ b/lib/screens/stickers/sticker_marketplace.dart @@ -0,0 +1,124 @@ +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/sticker.dart'; +import 'package:island/pods/network.dart'; +import 'package:island/widgets/app_scaffold.dart'; +import 'package:material_symbols_icons/symbols.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; + +part 'sticker_marketplace.g.dart'; + +@riverpod +class MarketplaceStickerPacksNotifier extends _$MarketplaceStickerPacksNotifier + with CursorPagingNotifierMixin { + @override + Future> build({required bool byUsage}) { + return fetch(cursor: null); + } + + @override + Future> fetch({ + required String? cursor, + }) async { + final client = ref.read(apiClientProvider); + final offset = cursor == null ? 0 : int.parse(cursor); + + final response = await client.get( + '/sphere/stickers', + queryParameters: { + 'offset': offset, + 'take': 20, + 'order': byUsage ? 'usage' : 'date', + }, + ); + + final total = int.parse(response.headers.value('X-Total') ?? '0'); + final List data = response.data; + final stickers = data.map((e) => SnStickerPack.fromJson(e)).toList(); + + final hasMore = offset + stickers.length < total; + final nextCursor = hasMore ? (offset + stickers.length).toString() : null; + + return CursorPagingData( + items: stickers, + hasMore: hasMore, + nextCursor: nextCursor, + ); + } +} + +/// User-facing marketplace screen for browsing sticker packs. +/// This version does NOT rely on publisher name (no pubName). +class MarketplaceStickersScreen extends HookConsumerWidget { + const MarketplaceStickersScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final byUsage = useState(true); + + return AppScaffold( + appBar: AppBar( + title: const Text('stickers').tr(), + actions: [ + IconButton( + onPressed: () { + byUsage.value = !byUsage.value; + }, + icon: + byUsage.value + ? const Icon(Symbols.local_fire_department) + : const Icon(Symbols.access_time), + tooltip: + byUsage.value + ? 'orderByPopularity'.tr() + : 'orderByReleaseDate'.tr(), + ), + const Gap(8), + ], + ), + body: PagingHelperView( + provider: marketplaceStickerPacksNotifierProvider( + byUsage: byUsage.value, + ), + futureRefreshable: + marketplaceStickerPacksNotifierProvider( + byUsage: byUsage.value, + ).future, + notifierRefreshable: + marketplaceStickerPacksNotifierProvider( + byUsage: byUsage.value, + ).notifier, + contentBuilder: + (data, widgetCount, endItemView) => ListView.builder( + padding: EdgeInsets.zero, + itemCount: widgetCount, + itemBuilder: (context, index) { + if (index == widgetCount - 1) { + return endItemView; + } + + final pack = data.items[index]; + return ListTile( + title: Text(pack.name), + subtitle: Text(pack.description), + trailing: const Icon(Symbols.chevron_right), + onTap: () { + // Navigate to user-facing sticker pack detail page. + // Adjust the route name/parameters if your app uses different ones. + context.pushNamed( + 'stickerPackDetail', + pathParameters: {'packId': pack.id}, + ); + }, + ); + }, + ), + ), + ); + } +} diff --git a/lib/screens/stickers/sticker_marketplace.g.dart b/lib/screens/stickers/sticker_marketplace.g.dart new file mode 100644 index 00000000..9b06ac89 --- /dev/null +++ b/lib/screens/stickers/sticker_marketplace.g.dart @@ -0,0 +1,183 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'sticker_marketplace.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$marketplaceStickerPacksNotifierHash() => + r'7e985cdee651a2ae868c0acb15da2b7a10525ae3'; + +/// 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 _$MarketplaceStickerPacksNotifier + extends BuildlessAutoDisposeAsyncNotifier> { + late final bool byUsage; + + FutureOr> build({required bool byUsage}); +} + +/// See also [MarketplaceStickerPacksNotifier]. +@ProviderFor(MarketplaceStickerPacksNotifier) +const marketplaceStickerPacksNotifierProvider = + MarketplaceStickerPacksNotifierFamily(); + +/// See also [MarketplaceStickerPacksNotifier]. +class MarketplaceStickerPacksNotifierFamily + extends Family>> { + /// See also [MarketplaceStickerPacksNotifier]. + const MarketplaceStickerPacksNotifierFamily(); + + /// See also [MarketplaceStickerPacksNotifier]. + MarketplaceStickerPacksNotifierProvider call({required bool byUsage}) { + return MarketplaceStickerPacksNotifierProvider(byUsage: byUsage); + } + + @override + MarketplaceStickerPacksNotifierProvider getProviderOverride( + covariant MarketplaceStickerPacksNotifierProvider provider, + ) { + return call(byUsage: provider.byUsage); + } + + static const Iterable? _dependencies = null; + + @override + Iterable? get dependencies => _dependencies; + + static const Iterable? _allTransitiveDependencies = null; + + @override + Iterable? get allTransitiveDependencies => + _allTransitiveDependencies; + + @override + String? get name => r'marketplaceStickerPacksNotifierProvider'; +} + +/// See also [MarketplaceStickerPacksNotifier]. +class MarketplaceStickerPacksNotifierProvider + extends + AutoDisposeAsyncNotifierProviderImpl< + MarketplaceStickerPacksNotifier, + CursorPagingData + > { + /// See also [MarketplaceStickerPacksNotifier]. + MarketplaceStickerPacksNotifierProvider({required bool byUsage}) + : this._internal( + () => MarketplaceStickerPacksNotifier()..byUsage = byUsage, + from: marketplaceStickerPacksNotifierProvider, + name: r'marketplaceStickerPacksNotifierProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : _$marketplaceStickerPacksNotifierHash, + dependencies: MarketplaceStickerPacksNotifierFamily._dependencies, + allTransitiveDependencies: + MarketplaceStickerPacksNotifierFamily._allTransitiveDependencies, + byUsage: byUsage, + ); + + MarketplaceStickerPacksNotifierProvider._internal( + super._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.debugGetCreateSourceHash, + required super.from, + required this.byUsage, + }) : super.internal(); + + final bool byUsage; + + @override + FutureOr> runNotifierBuild( + covariant MarketplaceStickerPacksNotifier notifier, + ) { + return notifier.build(byUsage: byUsage); + } + + @override + Override overrideWith(MarketplaceStickerPacksNotifier Function() create) { + return ProviderOverride( + origin: this, + override: MarketplaceStickerPacksNotifierProvider._internal( + () => create()..byUsage = byUsage, + from: from, + name: null, + dependencies: null, + allTransitiveDependencies: null, + debugGetCreateSourceHash: null, + byUsage: byUsage, + ), + ); + } + + @override + AutoDisposeAsyncNotifierProviderElement< + MarketplaceStickerPacksNotifier, + CursorPagingData + > + createElement() { + return _MarketplaceStickerPacksNotifierProviderElement(this); + } + + @override + bool operator ==(Object other) { + return other is MarketplaceStickerPacksNotifierProvider && + other.byUsage == byUsage; + } + + @override + int get hashCode { + var hash = _SystemHash.combine(0, runtimeType.hashCode); + hash = _SystemHash.combine(hash, byUsage.hashCode); + + return _SystemHash.finish(hash); + } +} + +@Deprecated('Will be removed in 3.0. Use Ref instead') +// ignore: unused_element +mixin MarketplaceStickerPacksNotifierRef + on AutoDisposeAsyncNotifierProviderRef> { + /// The parameter `byUsage` of this provider. + bool get byUsage; +} + +class _MarketplaceStickerPacksNotifierProviderElement + extends + AutoDisposeAsyncNotifierProviderElement< + MarketplaceStickerPacksNotifier, + CursorPagingData + > + with MarketplaceStickerPacksNotifierRef { + _MarketplaceStickerPacksNotifierProviderElement(super.provider); + + @override + bool get byUsage => + (origin as MarketplaceStickerPacksNotifierProvider).byUsage; +} + +// 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