✨ Stickers order by usage
This commit is contained in:
@@ -875,5 +875,7 @@
|
|||||||
"socialCreditsLevelPoor": "Poor",
|
"socialCreditsLevelPoor": "Poor",
|
||||||
"socialCreditsLevelNormal": "Normal",
|
"socialCreditsLevelNormal": "Normal",
|
||||||
"socialCreditsLevelGood": "Good",
|
"socialCreditsLevelGood": "Good",
|
||||||
"socialCreditsLevelExcellent": "Excellent"
|
"socialCreditsLevelExcellent": "Excellent",
|
||||||
|
"orderByPopularity": "Sort by popularity",
|
||||||
|
"orderByReleaseDate": "Sort by release date"
|
||||||
}
|
}
|
||||||
|
@@ -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/posts/post_manage_list.dart';
|
||||||
import 'package:island/screens/creators/stickers/stickers.dart';
|
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/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/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';
|
||||||
|
@@ -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<SnStickerPack> {
|
|
||||||
@override
|
|
||||||
Future<CursorPagingData<SnStickerPack>> build() {
|
|
||||||
return fetch(cursor: null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<CursorPagingData<SnStickerPack>> 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<dynamic> 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},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -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<SnStickerPack>
|
|
||||||
>.internal(
|
|
||||||
MarketplaceStickerPacksNotifier.new,
|
|
||||||
name: r'marketplaceStickerPacksNotifierProvider',
|
|
||||||
debugGetCreateSourceHash:
|
|
||||||
const bool.fromEnvironment('dart.vm.product')
|
|
||||||
? null
|
|
||||||
: _$marketplaceStickerPacksNotifierHash,
|
|
||||||
dependencies: null,
|
|
||||||
allTransitiveDependencies: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
typedef _$MarketplaceStickerPacksNotifier =
|
|
||||||
AutoDisposeAsyncNotifier<CursorPagingData<SnStickerPack>>;
|
|
||||||
// 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
|
|
124
lib/screens/stickers/sticker_marketplace.dart
Normal file
124
lib/screens/stickers/sticker_marketplace.dart
Normal file
@@ -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<SnStickerPack> {
|
||||||
|
@override
|
||||||
|
Future<CursorPagingData<SnStickerPack>> build({required bool byUsage}) {
|
||||||
|
return fetch(cursor: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<CursorPagingData<SnStickerPack>> 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<dynamic> 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},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
183
lib/screens/stickers/sticker_marketplace.g.dart
Normal file
183
lib/screens/stickers/sticker_marketplace.g.dart
Normal file
@@ -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<CursorPagingData<SnStickerPack>> {
|
||||||
|
late final bool byUsage;
|
||||||
|
|
||||||
|
FutureOr<CursorPagingData<SnStickerPack>> build({required bool byUsage});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [MarketplaceStickerPacksNotifier].
|
||||||
|
@ProviderFor(MarketplaceStickerPacksNotifier)
|
||||||
|
const marketplaceStickerPacksNotifierProvider =
|
||||||
|
MarketplaceStickerPacksNotifierFamily();
|
||||||
|
|
||||||
|
/// See also [MarketplaceStickerPacksNotifier].
|
||||||
|
class MarketplaceStickerPacksNotifierFamily
|
||||||
|
extends Family<AsyncValue<CursorPagingData<SnStickerPack>>> {
|
||||||
|
/// 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<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'marketplaceStickerPacksNotifierProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [MarketplaceStickerPacksNotifier].
|
||||||
|
class MarketplaceStickerPacksNotifierProvider
|
||||||
|
extends
|
||||||
|
AutoDisposeAsyncNotifierProviderImpl<
|
||||||
|
MarketplaceStickerPacksNotifier,
|
||||||
|
CursorPagingData<SnStickerPack>
|
||||||
|
> {
|
||||||
|
/// 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<CursorPagingData<SnStickerPack>> 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<SnStickerPack>
|
||||||
|
>
|
||||||
|
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<CursorPagingData<SnStickerPack>> {
|
||||||
|
/// The parameter `byUsage` of this provider.
|
||||||
|
bool get byUsage;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MarketplaceStickerPacksNotifierProviderElement
|
||||||
|
extends
|
||||||
|
AutoDisposeAsyncNotifierProviderElement<
|
||||||
|
MarketplaceStickerPacksNotifier,
|
||||||
|
CursorPagingData<SnStickerPack>
|
||||||
|
>
|
||||||
|
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
|
Reference in New Issue
Block a user