🎨 Use feature based folder structure
This commit is contained in:
216
lib/stickers/stickers/pack_detail.dart
Normal file
216
lib/stickers/stickers/pack_detail.dart
Normal file
@@ -0,0 +1,216 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/creators/creators/stickers/stickers.dart';
|
||||
import 'package:island/stickers/stickers_models/sticker.dart';
|
||||
import 'package:island/core/network.dart';
|
||||
import 'package:island/shared/widgets/alert.dart';
|
||||
import 'package:island/shared/widgets/app_scaffold.dart';
|
||||
import 'package:island/drive/drive_widgets/cloud_files.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
part 'pack_detail.g.dart'; // generated by riverpod_annotation build_runner
|
||||
|
||||
/// Marketplace version of sticker pack detail page (no publisher dependency).
|
||||
/// Shows all stickers in the pack and provides a button to add the sticker.
|
||||
/// API interactions are intentionally left blank per request.
|
||||
@riverpod
|
||||
Future<List<SnSticker>> marketplaceStickerPackContent(
|
||||
Ref ref, {
|
||||
required String packId,
|
||||
}) async {
|
||||
final apiClient = ref.watch(apiClientProvider);
|
||||
final resp = await apiClient.get('/sphere/stickers/$packId/content');
|
||||
return (resp.data as List).map((e) => SnSticker.fromJson(e)).toList();
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Future<bool> marketplaceStickerPackOwnership(
|
||||
Ref ref, {
|
||||
required String packId,
|
||||
}) async {
|
||||
final api = ref.watch(apiClientProvider);
|
||||
try {
|
||||
await api.get('/sphere/stickers/$packId/own');
|
||||
// If not 404, consider owned
|
||||
return true;
|
||||
} on Object catch (e) {
|
||||
// Dio error handling agnostic: treat 404 as not-owned, rethrow others
|
||||
final msg = e.toString();
|
||||
if (msg.contains('404')) return false;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
class MarketplaceStickerPackDetailScreen extends HookConsumerWidget {
|
||||
final String id;
|
||||
const MarketplaceStickerPackDetailScreen({super.key, required this.id});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// Pack metadata provider exists globally in creators file; reuse it.
|
||||
final pack = ref.watch(stickerPackProvider(id));
|
||||
final packContent = ref.watch(
|
||||
marketplaceStickerPackContentProvider(packId: id),
|
||||
);
|
||||
final owned = ref.watch(
|
||||
marketplaceStickerPackOwnershipProvider(packId: id),
|
||||
);
|
||||
|
||||
// Add entire pack to user's collection
|
||||
Future<void> addPackToMyCollection() async {
|
||||
final apiClient = ref.watch(apiClientProvider);
|
||||
await apiClient.post('/sphere/stickers/$id/own');
|
||||
HapticFeedback.selectionClick();
|
||||
ref.invalidate(marketplaceStickerPackOwnershipProvider(packId: id));
|
||||
if (!context.mounted) return;
|
||||
showSnackBar('stickerPackAdded'.tr());
|
||||
}
|
||||
|
||||
// Remove ownership of the pack
|
||||
Future<void> removePackFromMyCollection() async {
|
||||
final apiClient = ref.watch(apiClientProvider);
|
||||
await apiClient.delete('/sphere/stickers/$id/own');
|
||||
HapticFeedback.selectionClick();
|
||||
ref.invalidate(marketplaceStickerPackOwnershipProvider(packId: id));
|
||||
if (!context.mounted) return;
|
||||
showSnackBar('stickerPackRemoved'.tr());
|
||||
}
|
||||
|
||||
return AppScaffold(
|
||||
appBar: AppBar(title: Text(pack.value?.name ?? 'loading'.tr())),
|
||||
body: pack.when(
|
||||
data: (p) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// Pack meta
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(p?.description ?? ''),
|
||||
Row(
|
||||
spacing: 4,
|
||||
children: [
|
||||
const Icon(Symbols.folder, size: 16),
|
||||
Text(
|
||||
'${packContent.value?.length ?? 0}/24',
|
||||
style: GoogleFonts.robotoMono(),
|
||||
),
|
||||
],
|
||||
).opacity(0.85),
|
||||
Row(
|
||||
spacing: 4,
|
||||
children: [
|
||||
const Icon(Symbols.sell, size: 16),
|
||||
Text(p?.prefix ?? '', style: GoogleFonts.robotoMono()),
|
||||
],
|
||||
).opacity(0.85),
|
||||
Row(
|
||||
spacing: 4,
|
||||
children: [
|
||||
const Icon(Symbols.tag, size: 16),
|
||||
SelectableText(
|
||||
p?.id ?? id,
|
||||
style: GoogleFonts.robotoMono(),
|
||||
),
|
||||
],
|
||||
).opacity(0.85),
|
||||
],
|
||||
).padding(horizontal: 24, vertical: 24),
|
||||
const Divider(height: 1),
|
||||
// Stickers grid
|
||||
Expanded(
|
||||
child: packContent.when(
|
||||
data: (stickers) => RefreshIndicator(
|
||||
onRefresh: () => ref.refresh(
|
||||
marketplaceStickerPackContentProvider(packId: id).future,
|
||||
),
|
||||
child: GridView.builder(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 20,
|
||||
),
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 96,
|
||||
mainAxisSpacing: 12,
|
||||
crossAxisSpacing: 12,
|
||||
),
|
||||
itemCount: stickers.length,
|
||||
itemBuilder: (context, index) {
|
||||
final sticker = stickers[index];
|
||||
return Tooltip(
|
||||
message: ':${p?.prefix ?? ''}+${sticker.slug}:',
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(8),
|
||||
),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.surfaceContainer,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(8),
|
||||
),
|
||||
),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: CloudImageWidget(
|
||||
file: sticker.image,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
error: (err, _) => Text(
|
||||
'Error: $err',
|
||||
).textAlignment(TextAlign.center).center(),
|
||||
loading: () => const CircularProgressIndicator().center(),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 8),
|
||||
child: owned.when(
|
||||
data: (isOwned) => FilledButton.icon(
|
||||
onPressed: isOwned
|
||||
? removePackFromMyCollection
|
||||
: addPackToMyCollection,
|
||||
icon: Icon(
|
||||
isOwned ? Symbols.remove_circle : Symbols.add_circle,
|
||||
),
|
||||
label: Text(isOwned ? 'removePack'.tr() : 'addPack'.tr()),
|
||||
),
|
||||
loading: () => const SizedBox(
|
||||
height: 32,
|
||||
width: 32,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
).center(),
|
||||
error: (_, _) => OutlinedButton.icon(
|
||||
onPressed: addPackToMyCollection,
|
||||
icon: const Icon(Symbols.add_circle),
|
||||
label: Text('addPack').tr(),
|
||||
),
|
||||
),
|
||||
),
|
||||
Gap(MediaQuery.of(context).padding.bottom + 16),
|
||||
],
|
||||
);
|
||||
},
|
||||
error: (err, _) =>
|
||||
Text('Error: $err').textAlignment(TextAlign.center).center(),
|
||||
loading: () => const CircularProgressIndicator().center(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
178
lib/stickers/stickers/pack_detail.g.dart
Normal file
178
lib/stickers/stickers/pack_detail.g.dart
Normal file
@@ -0,0 +1,178 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'pack_detail.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
/// Marketplace version of sticker pack detail page (no publisher dependency).
|
||||
/// Shows all stickers in the pack and provides a button to add the sticker.
|
||||
/// API interactions are intentionally left blank per request.
|
||||
|
||||
@ProviderFor(marketplaceStickerPackContent)
|
||||
final marketplaceStickerPackContentProvider =
|
||||
MarketplaceStickerPackContentFamily._();
|
||||
|
||||
/// Marketplace version of sticker pack detail page (no publisher dependency).
|
||||
/// Shows all stickers in the pack and provides a button to add the sticker.
|
||||
/// API interactions are intentionally left blank per request.
|
||||
|
||||
final class MarketplaceStickerPackContentProvider
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
AsyncValue<List<SnSticker>>,
|
||||
List<SnSticker>,
|
||||
FutureOr<List<SnSticker>>
|
||||
>
|
||||
with $FutureModifier<List<SnSticker>>, $FutureProvider<List<SnSticker>> {
|
||||
/// Marketplace version of sticker pack detail page (no publisher dependency).
|
||||
/// Shows all stickers in the pack and provides a button to add the sticker.
|
||||
/// API interactions are intentionally left blank per request.
|
||||
MarketplaceStickerPackContentProvider._({
|
||||
required MarketplaceStickerPackContentFamily super.from,
|
||||
required String super.argument,
|
||||
}) : super(
|
||||
retry: null,
|
||||
name: r'marketplaceStickerPackContentProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$marketplaceStickerPackContentHash();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return r'marketplaceStickerPackContentProvider'
|
||||
''
|
||||
'($argument)';
|
||||
}
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$FutureProviderElement<List<SnSticker>> $createElement(
|
||||
$ProviderPointer pointer,
|
||||
) => $FutureProviderElement(pointer);
|
||||
|
||||
@override
|
||||
FutureOr<List<SnSticker>> create(Ref ref) {
|
||||
final argument = this.argument as String;
|
||||
return marketplaceStickerPackContent(ref, packId: argument);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is MarketplaceStickerPackContentProvider &&
|
||||
other.argument == argument;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return argument.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
String _$marketplaceStickerPackContentHash() =>
|
||||
r'886f8305c978dbea6e5d990a7d555048ac704a5d';
|
||||
|
||||
/// Marketplace version of sticker pack detail page (no publisher dependency).
|
||||
/// Shows all stickers in the pack and provides a button to add the sticker.
|
||||
/// API interactions are intentionally left blank per request.
|
||||
|
||||
final class MarketplaceStickerPackContentFamily extends $Family
|
||||
with $FunctionalFamilyOverride<FutureOr<List<SnSticker>>, String> {
|
||||
MarketplaceStickerPackContentFamily._()
|
||||
: super(
|
||||
retry: null,
|
||||
name: r'marketplaceStickerPackContentProvider',
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
isAutoDispose: true,
|
||||
);
|
||||
|
||||
/// Marketplace version of sticker pack detail page (no publisher dependency).
|
||||
/// Shows all stickers in the pack and provides a button to add the sticker.
|
||||
/// API interactions are intentionally left blank per request.
|
||||
|
||||
MarketplaceStickerPackContentProvider call({required String packId}) =>
|
||||
MarketplaceStickerPackContentProvider._(argument: packId, from: this);
|
||||
|
||||
@override
|
||||
String toString() => r'marketplaceStickerPackContentProvider';
|
||||
}
|
||||
|
||||
@ProviderFor(marketplaceStickerPackOwnership)
|
||||
final marketplaceStickerPackOwnershipProvider =
|
||||
MarketplaceStickerPackOwnershipFamily._();
|
||||
|
||||
final class MarketplaceStickerPackOwnershipProvider
|
||||
extends $FunctionalProvider<AsyncValue<bool>, bool, FutureOr<bool>>
|
||||
with $FutureModifier<bool>, $FutureProvider<bool> {
|
||||
MarketplaceStickerPackOwnershipProvider._({
|
||||
required MarketplaceStickerPackOwnershipFamily super.from,
|
||||
required String super.argument,
|
||||
}) : super(
|
||||
retry: null,
|
||||
name: r'marketplaceStickerPackOwnershipProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$marketplaceStickerPackOwnershipHash();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return r'marketplaceStickerPackOwnershipProvider'
|
||||
''
|
||||
'($argument)';
|
||||
}
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$FutureProviderElement<bool> $createElement($ProviderPointer pointer) =>
|
||||
$FutureProviderElement(pointer);
|
||||
|
||||
@override
|
||||
FutureOr<bool> create(Ref ref) {
|
||||
final argument = this.argument as String;
|
||||
return marketplaceStickerPackOwnership(ref, packId: argument);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is MarketplaceStickerPackOwnershipProvider &&
|
||||
other.argument == argument;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return argument.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
String _$marketplaceStickerPackOwnershipHash() =>
|
||||
r'e5dd301c309fac958729d13d984ce7a77edbe7e6';
|
||||
|
||||
final class MarketplaceStickerPackOwnershipFamily extends $Family
|
||||
with $FunctionalFamilyOverride<FutureOr<bool>, String> {
|
||||
MarketplaceStickerPackOwnershipFamily._()
|
||||
: super(
|
||||
retry: null,
|
||||
name: r'marketplaceStickerPackOwnershipProvider',
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
isAutoDispose: true,
|
||||
);
|
||||
|
||||
MarketplaceStickerPackOwnershipProvider call({required String packId}) =>
|
||||
MarketplaceStickerPackOwnershipProvider._(argument: packId, from: this);
|
||||
|
||||
@override
|
||||
String toString() => r'marketplaceStickerPackOwnershipProvider';
|
||||
}
|
||||
288
lib/stickers/stickers/sticker_marketplace.dart
Normal file
288
lib/stickers/stickers/sticker_marketplace.dart
Normal file
@@ -0,0 +1,288 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/pagination/pagination.dart';
|
||||
import 'package:island/stickers/stickers_models/sticker.dart';
|
||||
import 'package:island/core/network.dart';
|
||||
import 'package:island/shared/widgets/app_scaffold.dart';
|
||||
import 'package:island/drive/drive_widgets/cloud_files.dart';
|
||||
import 'package:island/shared/widgets/pagination_list.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
part 'sticker_marketplace.freezed.dart';
|
||||
|
||||
@freezed
|
||||
sealed class MarketplaceStickerQuery with _$MarketplaceStickerQuery {
|
||||
const factory MarketplaceStickerQuery({
|
||||
required bool byUsage,
|
||||
required String? query,
|
||||
}) = _MarketplaceStickerQuery;
|
||||
}
|
||||
|
||||
final marketplaceStickerPacksNotifierProvider =
|
||||
AsyncNotifierProvider.autoDispose(MarketplaceStickerPacksNotifier.new);
|
||||
|
||||
class MarketplaceStickerPacksNotifier
|
||||
extends AsyncNotifier<PaginationState<SnStickerPack>>
|
||||
with
|
||||
AsyncPaginationController<SnStickerPack>,
|
||||
AsyncPaginationFilter<MarketplaceStickerQuery, SnStickerPack> {
|
||||
static const int pageSize = 20;
|
||||
|
||||
@override
|
||||
MarketplaceStickerQuery currentFilter = MarketplaceStickerQuery(
|
||||
byUsage: true,
|
||||
query: null,
|
||||
);
|
||||
|
||||
@override
|
||||
Future<List<SnStickerPack>> fetch() async {
|
||||
final client = ref.read(apiClientProvider);
|
||||
|
||||
final response = await client.get(
|
||||
'/sphere/stickers',
|
||||
queryParameters: {
|
||||
'offset': fetchedCount.toString(),
|
||||
'take': pageSize,
|
||||
'order': currentFilter.byUsage ? 'usage' : 'date',
|
||||
if (currentFilter.query != null && currentFilter.query!.isNotEmpty)
|
||||
'query': currentFilter.query,
|
||||
},
|
||||
);
|
||||
|
||||
totalCount = int.parse(response.headers.value('X-Total') ?? '0');
|
||||
final stickers = response.data
|
||||
.map((e) => SnStickerPack.fromJson(e))
|
||||
.cast<SnStickerPack>()
|
||||
.toList();
|
||||
|
||||
return stickers;
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 query = useState<MarketplaceStickerQuery>(
|
||||
MarketplaceStickerQuery(byUsage: true, query: null),
|
||||
);
|
||||
final searchController = useTextEditingController();
|
||||
final focusNode = useFocusNode();
|
||||
final debounceTimer = useState<Timer?>(null);
|
||||
|
||||
final notifier = ref.watch(
|
||||
marketplaceStickerPacksNotifierProvider.notifier,
|
||||
);
|
||||
|
||||
// Clear search when query is cleared
|
||||
useEffect(() {
|
||||
if (query.value.query == null || query.value.query!.isEmpty) {
|
||||
searchController.clear();
|
||||
}
|
||||
return null;
|
||||
}, [query]);
|
||||
|
||||
// Clean up timer on dispose
|
||||
useEffect(() {
|
||||
return () {
|
||||
debounceTimer.value?.cancel();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return AppScaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('stickers').tr(),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
query.value = query.value.copyWith(byUsage: !query.value.byUsage);
|
||||
notifier.applyFilter(query.value);
|
||||
},
|
||||
icon: query.value.byUsage
|
||||
? const Icon(Symbols.local_fire_department)
|
||||
: const Icon(Symbols.access_time),
|
||||
tooltip: query.value.byUsage
|
||||
? 'orderByPopularity'.tr()
|
||||
: 'orderByReleaseDate'.tr(),
|
||||
),
|
||||
const Gap(8),
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 0),
|
||||
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.query != null && query.value.query!.isNotEmpty)
|
||||
IconButton(
|
||||
icon: const Icon(Symbols.close),
|
||||
onPressed: () {
|
||||
query.value = query.value.copyWith(query: null);
|
||||
notifier.applyFilter(query.value);
|
||||
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 = query.value.copyWith(query: value);
|
||||
notifier.applyFilter(query.value);
|
||||
},
|
||||
);
|
||||
},
|
||||
onSubmitted: (value) {
|
||||
query.value = query.value.copyWith(query: value);
|
||||
notifier.applyFilter(query.value);
|
||||
focusNode.unfocus();
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: PaginationList(
|
||||
padding: EdgeInsets.only(top: 8),
|
||||
provider: marketplaceStickerPacksNotifierProvider,
|
||||
notifier: marketplaceStickerPacksNotifierProvider.notifier,
|
||||
itemBuilder: (context, idx, pack) => Card(
|
||||
margin: EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
||||
child: Column(
|
||||
children: [
|
||||
if (pack.stickers.isNotEmpty)
|
||||
Container(
|
||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
vertical: 20,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: List.generate(
|
||||
math.min(pack.stickers.length, 4),
|
||||
(index) => Padding(
|
||||
padding: EdgeInsets.only(
|
||||
right: index < 3 ? 8 : 0,
|
||||
),
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 80,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.tertiaryContainer,
|
||||
),
|
||||
child: CloudImageWidget(
|
||||
file: pack.stickers[index].image,
|
||||
),
|
||||
).clipRRect(all: 8),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (pack.stickers.length > 4)
|
||||
const SizedBox(height: 8),
|
||||
if (pack.stickers.length > 4)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: List.generate(
|
||||
math.min(pack.stickers.length - 4, 4),
|
||||
(index) => Padding(
|
||||
padding: EdgeInsets.only(
|
||||
right: index < 3 ? 8 : 0,
|
||||
),
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 80,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(
|
||||
8,
|
||||
),
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.tertiaryContainer,
|
||||
),
|
||||
child: CloudImageWidget(
|
||||
file: pack.stickers[index + 4].image,
|
||||
),
|
||||
).clipRRect(all: 8),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
).clipRRect(topLeft: 8, topRight: 8),
|
||||
ListTile(
|
||||
leading: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.tertiaryContainer,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(8),
|
||||
),
|
||||
),
|
||||
child: CloudImageWidget(
|
||||
file: pack.icon ?? pack.stickers.firstOrNull?.image,
|
||||
),
|
||||
).width(40).height(40).clipRRect(all: 8),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(8),
|
||||
),
|
||||
),
|
||||
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},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
268
lib/stickers/stickers/sticker_marketplace.freezed.dart
Normal file
268
lib/stickers/stickers/sticker_marketplace.freezed.dart
Normal file
@@ -0,0 +1,268 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'sticker_marketplace.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$MarketplaceStickerQuery {
|
||||
|
||||
bool get byUsage; String? get query;
|
||||
/// Create a copy of MarketplaceStickerQuery
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$MarketplaceStickerQueryCopyWith<MarketplaceStickerQuery> get copyWith => _$MarketplaceStickerQueryCopyWithImpl<MarketplaceStickerQuery>(this as MarketplaceStickerQuery, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is MarketplaceStickerQuery&&(identical(other.byUsage, byUsage) || other.byUsage == byUsage)&&(identical(other.query, query) || other.query == query));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,byUsage,query);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MarketplaceStickerQuery(byUsage: $byUsage, query: $query)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $MarketplaceStickerQueryCopyWith<$Res> {
|
||||
factory $MarketplaceStickerQueryCopyWith(MarketplaceStickerQuery value, $Res Function(MarketplaceStickerQuery) _then) = _$MarketplaceStickerQueryCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
bool byUsage, String? query
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$MarketplaceStickerQueryCopyWithImpl<$Res>
|
||||
implements $MarketplaceStickerQueryCopyWith<$Res> {
|
||||
_$MarketplaceStickerQueryCopyWithImpl(this._self, this._then);
|
||||
|
||||
final MarketplaceStickerQuery _self;
|
||||
final $Res Function(MarketplaceStickerQuery) _then;
|
||||
|
||||
/// Create a copy of MarketplaceStickerQuery
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? byUsage = null,Object? query = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
byUsage: null == byUsage ? _self.byUsage : byUsage // ignore: cast_nullable_to_non_nullable
|
||||
as bool,query: freezed == query ? _self.query : query // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [MarketplaceStickerQuery].
|
||||
extension MarketplaceStickerQueryPatterns on MarketplaceStickerQuery {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _MarketplaceStickerQuery value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _MarketplaceStickerQuery() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _MarketplaceStickerQuery value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _MarketplaceStickerQuery():
|
||||
return $default(_that);}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _MarketplaceStickerQuery value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _MarketplaceStickerQuery() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool byUsage, String? query)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _MarketplaceStickerQuery() when $default != null:
|
||||
return $default(_that.byUsage,_that.query);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool byUsage, String? query) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _MarketplaceStickerQuery():
|
||||
return $default(_that.byUsage,_that.query);}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool byUsage, String? query)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _MarketplaceStickerQuery() when $default != null:
|
||||
return $default(_that.byUsage,_that.query);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _MarketplaceStickerQuery implements MarketplaceStickerQuery {
|
||||
const _MarketplaceStickerQuery({required this.byUsage, required this.query});
|
||||
|
||||
|
||||
@override final bool byUsage;
|
||||
@override final String? query;
|
||||
|
||||
/// Create a copy of MarketplaceStickerQuery
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$MarketplaceStickerQueryCopyWith<_MarketplaceStickerQuery> get copyWith => __$MarketplaceStickerQueryCopyWithImpl<_MarketplaceStickerQuery>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _MarketplaceStickerQuery&&(identical(other.byUsage, byUsage) || other.byUsage == byUsage)&&(identical(other.query, query) || other.query == query));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,byUsage,query);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MarketplaceStickerQuery(byUsage: $byUsage, query: $query)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$MarketplaceStickerQueryCopyWith<$Res> implements $MarketplaceStickerQueryCopyWith<$Res> {
|
||||
factory _$MarketplaceStickerQueryCopyWith(_MarketplaceStickerQuery value, $Res Function(_MarketplaceStickerQuery) _then) = __$MarketplaceStickerQueryCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
bool byUsage, String? query
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$MarketplaceStickerQueryCopyWithImpl<$Res>
|
||||
implements _$MarketplaceStickerQueryCopyWith<$Res> {
|
||||
__$MarketplaceStickerQueryCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _MarketplaceStickerQuery _self;
|
||||
final $Res Function(_MarketplaceStickerQuery) _then;
|
||||
|
||||
/// Create a copy of MarketplaceStickerQuery
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? byUsage = null,Object? query = freezed,}) {
|
||||
return _then(_MarketplaceStickerQuery(
|
||||
byUsage: null == byUsage ? _self.byUsage : byUsage // ignore: cast_nullable_to_non_nullable
|
||||
as bool,query: freezed == query ? _self.query : query // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
43
lib/stickers/stickers_models/sticker.dart
Normal file
43
lib/stickers/stickers_models/sticker.dart
Normal file
@@ -0,0 +1,43 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:island/drive/drive_models/file.dart';
|
||||
import 'package:island/posts/posts_models/publisher.dart';
|
||||
|
||||
part 'sticker.freezed.dart';
|
||||
part 'sticker.g.dart';
|
||||
|
||||
@freezed
|
||||
sealed class SnSticker with _$SnSticker {
|
||||
const factory SnSticker({
|
||||
required String id,
|
||||
required String slug,
|
||||
required SnCloudFile image,
|
||||
required String packId,
|
||||
required SnStickerPack? pack,
|
||||
required DateTime createdAt,
|
||||
required DateTime updatedAt,
|
||||
required DateTime? deletedAt,
|
||||
}) = _SnSticker;
|
||||
|
||||
factory SnSticker.fromJson(Map<String, dynamic> json) =>
|
||||
_$SnStickerFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
sealed class SnStickerPack with _$SnStickerPack {
|
||||
const factory SnStickerPack({
|
||||
required String id,
|
||||
required String name,
|
||||
required String description,
|
||||
required String prefix,
|
||||
required String publisherId,
|
||||
required SnCloudFile? icon,
|
||||
required SnPublisher? publisher,
|
||||
required DateTime createdAt,
|
||||
required DateTime updatedAt,
|
||||
required DateTime? deletedAt,
|
||||
@Default([]) List<SnSticker> stickers,
|
||||
}) = _SnStickerPack;
|
||||
|
||||
factory SnStickerPack.fromJson(Map<String, dynamic> json) =>
|
||||
_$SnStickerPackFromJson(json);
|
||||
}
|
||||
675
lib/stickers/stickers_models/sticker.freezed.dart
Normal file
675
lib/stickers/stickers_models/sticker.freezed.dart
Normal file
@@ -0,0 +1,675 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'sticker.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$SnSticker {
|
||||
|
||||
String get id; String get slug; SnCloudFile get image; String get packId; SnStickerPack? get pack; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
||||
/// Create a copy of SnSticker
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnStickerCopyWith<SnSticker> get copyWith => _$SnStickerCopyWithImpl<SnSticker>(this as SnSticker, _$identity);
|
||||
|
||||
/// Serializes this SnSticker to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnSticker&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.image, image) || other.image == image)&&(identical(other.packId, packId) || other.packId == packId)&&(identical(other.pack, pack) || other.pack == pack)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,slug,image,packId,pack,createdAt,updatedAt,deletedAt);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SnSticker(id: $id, slug: $slug, image: $image, packId: $packId, pack: $pack, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $SnStickerCopyWith<$Res> {
|
||||
factory $SnStickerCopyWith(SnSticker value, $Res Function(SnSticker) _then) = _$SnStickerCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String id, String slug, SnCloudFile image, String packId, SnStickerPack? pack, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||
});
|
||||
|
||||
|
||||
$SnCloudFileCopyWith<$Res> get image;$SnStickerPackCopyWith<$Res>? get pack;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$SnStickerCopyWithImpl<$Res>
|
||||
implements $SnStickerCopyWith<$Res> {
|
||||
_$SnStickerCopyWithImpl(this._self, this._then);
|
||||
|
||||
final SnSticker _self;
|
||||
final $Res Function(SnSticker) _then;
|
||||
|
||||
/// Create a copy of SnSticker
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? slug = null,Object? image = null,Object? packId = null,Object? pack = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
|
||||
as String,image: null == image ? _self.image : image // ignore: cast_nullable_to_non_nullable
|
||||
as SnCloudFile,packId: null == packId ? _self.packId : packId // ignore: cast_nullable_to_non_nullable
|
||||
as String,pack: freezed == pack ? _self.pack : pack // ignore: cast_nullable_to_non_nullable
|
||||
as SnStickerPack?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
));
|
||||
}
|
||||
/// Create a copy of SnSticker
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnCloudFileCopyWith<$Res> get image {
|
||||
|
||||
return $SnCloudFileCopyWith<$Res>(_self.image, (value) {
|
||||
return _then(_self.copyWith(image: value));
|
||||
});
|
||||
}/// Create a copy of SnSticker
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnStickerPackCopyWith<$Res>? get pack {
|
||||
if (_self.pack == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnStickerPackCopyWith<$Res>(_self.pack!, (value) {
|
||||
return _then(_self.copyWith(pack: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [SnSticker].
|
||||
extension SnStickerPatterns on SnSticker {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnSticker value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SnSticker() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnSticker value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SnSticker():
|
||||
return $default(_that);}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnSticker value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SnSticker() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String slug, SnCloudFile image, String packId, SnStickerPack? pack, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SnSticker() when $default != null:
|
||||
return $default(_that.id,_that.slug,_that.image,_that.packId,_that.pack,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String slug, SnCloudFile image, String packId, SnStickerPack? pack, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SnSticker():
|
||||
return $default(_that.id,_that.slug,_that.image,_that.packId,_that.pack,_that.createdAt,_that.updatedAt,_that.deletedAt);}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String slug, SnCloudFile image, String packId, SnStickerPack? pack, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SnSticker() when $default != null:
|
||||
return $default(_that.id,_that.slug,_that.image,_that.packId,_that.pack,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _SnSticker implements SnSticker {
|
||||
const _SnSticker({required this.id, required this.slug, required this.image, required this.packId, required this.pack, required this.createdAt, required this.updatedAt, required this.deletedAt});
|
||||
factory _SnSticker.fromJson(Map<String, dynamic> json) => _$SnStickerFromJson(json);
|
||||
|
||||
@override final String id;
|
||||
@override final String slug;
|
||||
@override final SnCloudFile image;
|
||||
@override final String packId;
|
||||
@override final SnStickerPack? pack;
|
||||
@override final DateTime createdAt;
|
||||
@override final DateTime updatedAt;
|
||||
@override final DateTime? deletedAt;
|
||||
|
||||
/// Create a copy of SnSticker
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$SnStickerCopyWith<_SnSticker> get copyWith => __$SnStickerCopyWithImpl<_SnSticker>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$SnStickerToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnSticker&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.image, image) || other.image == image)&&(identical(other.packId, packId) || other.packId == packId)&&(identical(other.pack, pack) || other.pack == pack)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,slug,image,packId,pack,createdAt,updatedAt,deletedAt);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SnSticker(id: $id, slug: $slug, image: $image, packId: $packId, pack: $pack, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$SnStickerCopyWith<$Res> implements $SnStickerCopyWith<$Res> {
|
||||
factory _$SnStickerCopyWith(_SnSticker value, $Res Function(_SnSticker) _then) = __$SnStickerCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String id, String slug, SnCloudFile image, String packId, SnStickerPack? pack, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||
});
|
||||
|
||||
|
||||
@override $SnCloudFileCopyWith<$Res> get image;@override $SnStickerPackCopyWith<$Res>? get pack;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$SnStickerCopyWithImpl<$Res>
|
||||
implements _$SnStickerCopyWith<$Res> {
|
||||
__$SnStickerCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _SnSticker _self;
|
||||
final $Res Function(_SnSticker) _then;
|
||||
|
||||
/// Create a copy of SnSticker
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? slug = null,Object? image = null,Object? packId = null,Object? pack = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||
return _then(_SnSticker(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
|
||||
as String,image: null == image ? _self.image : image // ignore: cast_nullable_to_non_nullable
|
||||
as SnCloudFile,packId: null == packId ? _self.packId : packId // ignore: cast_nullable_to_non_nullable
|
||||
as String,pack: freezed == pack ? _self.pack : pack // ignore: cast_nullable_to_non_nullable
|
||||
as SnStickerPack?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,
|
||||
));
|
||||
}
|
||||
|
||||
/// Create a copy of SnSticker
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnCloudFileCopyWith<$Res> get image {
|
||||
|
||||
return $SnCloudFileCopyWith<$Res>(_self.image, (value) {
|
||||
return _then(_self.copyWith(image: value));
|
||||
});
|
||||
}/// Create a copy of SnSticker
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnStickerPackCopyWith<$Res>? get pack {
|
||||
if (_self.pack == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnStickerPackCopyWith<$Res>(_self.pack!, (value) {
|
||||
return _then(_self.copyWith(pack: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @nodoc
|
||||
mixin _$SnStickerPack {
|
||||
|
||||
String get id; String get name; String get description; String get prefix; String get publisherId; SnCloudFile? get icon; SnPublisher? get publisher; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; List<SnSticker> get stickers;
|
||||
/// Create a copy of SnStickerPack
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnStickerPackCopyWith<SnStickerPack> get copyWith => _$SnStickerPackCopyWithImpl<SnStickerPack>(this as SnStickerPack, _$identity);
|
||||
|
||||
/// Serializes this SnStickerPack to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnStickerPack&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.prefix, prefix) || other.prefix == prefix)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.icon, icon) || other.icon == icon)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&const DeepCollectionEquality().equals(other.stickers, stickers));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,name,description,prefix,publisherId,icon,publisher,createdAt,updatedAt,deletedAt,const DeepCollectionEquality().hash(stickers));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SnStickerPack(id: $id, name: $name, description: $description, prefix: $prefix, publisherId: $publisherId, icon: $icon, publisher: $publisher, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, stickers: $stickers)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $SnStickerPackCopyWith<$Res> {
|
||||
factory $SnStickerPackCopyWith(SnStickerPack value, $Res Function(SnStickerPack) _then) = _$SnStickerPackCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String id, String name, String description, String prefix, String publisherId, SnCloudFile? icon, SnPublisher? publisher, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, List<SnSticker> stickers
|
||||
});
|
||||
|
||||
|
||||
$SnCloudFileCopyWith<$Res>? get icon;$SnPublisherCopyWith<$Res>? get publisher;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$SnStickerPackCopyWithImpl<$Res>
|
||||
implements $SnStickerPackCopyWith<$Res> {
|
||||
_$SnStickerPackCopyWithImpl(this._self, this._then);
|
||||
|
||||
final SnStickerPack _self;
|
||||
final $Res Function(SnStickerPack) _then;
|
||||
|
||||
/// Create a copy of SnStickerPack
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? description = null,Object? prefix = null,Object? publisherId = null,Object? icon = freezed,Object? publisher = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? stickers = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
||||
as String,prefix: null == prefix ? _self.prefix : prefix // ignore: cast_nullable_to_non_nullable
|
||||
as String,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable
|
||||
as String,icon: freezed == icon ? _self.icon : icon // ignore: cast_nullable_to_non_nullable
|
||||
as SnCloudFile?,publisher: freezed == publisher ? _self.publisher : publisher // ignore: cast_nullable_to_non_nullable
|
||||
as SnPublisher?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,stickers: null == stickers ? _self.stickers : stickers // ignore: cast_nullable_to_non_nullable
|
||||
as List<SnSticker>,
|
||||
));
|
||||
}
|
||||
/// Create a copy of SnStickerPack
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnCloudFileCopyWith<$Res>? get icon {
|
||||
if (_self.icon == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnCloudFileCopyWith<$Res>(_self.icon!, (value) {
|
||||
return _then(_self.copyWith(icon: value));
|
||||
});
|
||||
}/// Create a copy of SnStickerPack
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnPublisherCopyWith<$Res>? get publisher {
|
||||
if (_self.publisher == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnPublisherCopyWith<$Res>(_self.publisher!, (value) {
|
||||
return _then(_self.copyWith(publisher: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [SnStickerPack].
|
||||
extension SnStickerPackPatterns on SnStickerPack {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnStickerPack value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SnStickerPack() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnStickerPack value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SnStickerPack():
|
||||
return $default(_that);}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnStickerPack value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SnStickerPack() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String name, String description, String prefix, String publisherId, SnCloudFile? icon, SnPublisher? publisher, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, List<SnSticker> stickers)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SnStickerPack() when $default != null:
|
||||
return $default(_that.id,_that.name,_that.description,_that.prefix,_that.publisherId,_that.icon,_that.publisher,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.stickers);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String name, String description, String prefix, String publisherId, SnCloudFile? icon, SnPublisher? publisher, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, List<SnSticker> stickers) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SnStickerPack():
|
||||
return $default(_that.id,_that.name,_that.description,_that.prefix,_that.publisherId,_that.icon,_that.publisher,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.stickers);}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String name, String description, String prefix, String publisherId, SnCloudFile? icon, SnPublisher? publisher, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, List<SnSticker> stickers)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SnStickerPack() when $default != null:
|
||||
return $default(_that.id,_that.name,_that.description,_that.prefix,_that.publisherId,_that.icon,_that.publisher,_that.createdAt,_that.updatedAt,_that.deletedAt,_that.stickers);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _SnStickerPack implements SnStickerPack {
|
||||
const _SnStickerPack({required this.id, required this.name, required this.description, required this.prefix, required this.publisherId, required this.icon, required this.publisher, required this.createdAt, required this.updatedAt, required this.deletedAt, final List<SnSticker> stickers = const []}): _stickers = stickers;
|
||||
factory _SnStickerPack.fromJson(Map<String, dynamic> json) => _$SnStickerPackFromJson(json);
|
||||
|
||||
@override final String id;
|
||||
@override final String name;
|
||||
@override final String description;
|
||||
@override final String prefix;
|
||||
@override final String publisherId;
|
||||
@override final SnCloudFile? icon;
|
||||
@override final SnPublisher? publisher;
|
||||
@override final DateTime createdAt;
|
||||
@override final DateTime updatedAt;
|
||||
@override final DateTime? deletedAt;
|
||||
final List<SnSticker> _stickers;
|
||||
@override@JsonKey() List<SnSticker> get stickers {
|
||||
if (_stickers is EqualUnmodifiableListView) return _stickers;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_stickers);
|
||||
}
|
||||
|
||||
|
||||
/// Create a copy of SnStickerPack
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$SnStickerPackCopyWith<_SnStickerPack> get copyWith => __$SnStickerPackCopyWithImpl<_SnStickerPack>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$SnStickerPackToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnStickerPack&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.prefix, prefix) || other.prefix == prefix)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.icon, icon) || other.icon == icon)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&const DeepCollectionEquality().equals(other._stickers, _stickers));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,name,description,prefix,publisherId,icon,publisher,createdAt,updatedAt,deletedAt,const DeepCollectionEquality().hash(_stickers));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SnStickerPack(id: $id, name: $name, description: $description, prefix: $prefix, publisherId: $publisherId, icon: $icon, publisher: $publisher, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, stickers: $stickers)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$SnStickerPackCopyWith<$Res> implements $SnStickerPackCopyWith<$Res> {
|
||||
factory _$SnStickerPackCopyWith(_SnStickerPack value, $Res Function(_SnStickerPack) _then) = __$SnStickerPackCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String id, String name, String description, String prefix, String publisherId, SnCloudFile? icon, SnPublisher? publisher, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, List<SnSticker> stickers
|
||||
});
|
||||
|
||||
|
||||
@override $SnCloudFileCopyWith<$Res>? get icon;@override $SnPublisherCopyWith<$Res>? get publisher;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$SnStickerPackCopyWithImpl<$Res>
|
||||
implements _$SnStickerPackCopyWith<$Res> {
|
||||
__$SnStickerPackCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _SnStickerPack _self;
|
||||
final $Res Function(_SnStickerPack) _then;
|
||||
|
||||
/// Create a copy of SnStickerPack
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? description = null,Object? prefix = null,Object? publisherId = null,Object? icon = freezed,Object? publisher = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? stickers = null,}) {
|
||||
return _then(_SnStickerPack(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
||||
as String,prefix: null == prefix ? _self.prefix : prefix // ignore: cast_nullable_to_non_nullable
|
||||
as String,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable
|
||||
as String,icon: freezed == icon ? _self.icon : icon // ignore: cast_nullable_to_non_nullable
|
||||
as SnCloudFile?,publisher: freezed == publisher ? _self.publisher : publisher // ignore: cast_nullable_to_non_nullable
|
||||
as SnPublisher?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,stickers: null == stickers ? _self._stickers : stickers // ignore: cast_nullable_to_non_nullable
|
||||
as List<SnSticker>,
|
||||
));
|
||||
}
|
||||
|
||||
/// Create a copy of SnStickerPack
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnCloudFileCopyWith<$Res>? get icon {
|
||||
if (_self.icon == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnCloudFileCopyWith<$Res>(_self.icon!, (value) {
|
||||
return _then(_self.copyWith(icon: value));
|
||||
});
|
||||
}/// Create a copy of SnStickerPack
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnPublisherCopyWith<$Res>? get publisher {
|
||||
if (_self.publisher == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnPublisherCopyWith<$Res>(_self.publisher!, (value) {
|
||||
return _then(_self.copyWith(publisher: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// dart format on
|
||||
74
lib/stickers/stickers_models/sticker.g.dart
Normal file
74
lib/stickers/stickers_models/sticker.g.dart
Normal file
@@ -0,0 +1,74 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'sticker.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_SnSticker _$SnStickerFromJson(Map<String, dynamic> json) => _SnSticker(
|
||||
id: json['id'] as String,
|
||||
slug: json['slug'] as String,
|
||||
image: SnCloudFile.fromJson(json['image'] as Map<String, dynamic>),
|
||||
packId: json['pack_id'] as String,
|
||||
pack: json['pack'] == null
|
||||
? null
|
||||
: SnStickerPack.fromJson(json['pack'] as Map<String, dynamic>),
|
||||
createdAt: DateTime.parse(json['created_at'] as String),
|
||||
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||
deletedAt: json['deleted_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['deleted_at'] as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$SnStickerToJson(_SnSticker instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'slug': instance.slug,
|
||||
'image': instance.image.toJson(),
|
||||
'pack_id': instance.packId,
|
||||
'pack': instance.pack?.toJson(),
|
||||
'created_at': instance.createdAt.toIso8601String(),
|
||||
'updated_at': instance.updatedAt.toIso8601String(),
|
||||
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||
};
|
||||
|
||||
_SnStickerPack _$SnStickerPackFromJson(Map<String, dynamic> json) =>
|
||||
_SnStickerPack(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String,
|
||||
description: json['description'] as String,
|
||||
prefix: json['prefix'] as String,
|
||||
publisherId: json['publisher_id'] as String,
|
||||
icon: json['icon'] == null
|
||||
? null
|
||||
: SnCloudFile.fromJson(json['icon'] as Map<String, dynamic>),
|
||||
publisher: json['publisher'] == null
|
||||
? null
|
||||
: SnPublisher.fromJson(json['publisher'] as Map<String, dynamic>),
|
||||
createdAt: DateTime.parse(json['created_at'] as String),
|
||||
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||
deletedAt: json['deleted_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['deleted_at'] as String),
|
||||
stickers:
|
||||
(json['stickers'] as List<dynamic>?)
|
||||
?.map((e) => SnSticker.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$SnStickerPackToJson(_SnStickerPack instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'name': instance.name,
|
||||
'description': instance.description,
|
||||
'prefix': instance.prefix,
|
||||
'publisher_id': instance.publisherId,
|
||||
'icon': instance.icon?.toJson(),
|
||||
'publisher': instance.publisher?.toJson(),
|
||||
'created_at': instance.createdAt.toIso8601String(),
|
||||
'updated_at': instance.updatedAt.toIso8601String(),
|
||||
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||
'stickers': instance.stickers.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
468
lib/stickers/stickers_widgets/stickers/sticker_picker.dart
Normal file
468
lib/stickers/stickers_widgets/stickers/sticker_picker.dart
Normal file
@@ -0,0 +1,468 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
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/stickers/stickers_models/sticker.dart';
|
||||
import 'package:island/core/network.dart';
|
||||
import 'package:island/drive/drive_widgets/cloud_files.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:flutter_popup_card/flutter_popup_card.dart';
|
||||
import 'package:island/shared/widgets/extended_refresh_indicator.dart';
|
||||
|
||||
part 'sticker_picker.g.dart';
|
||||
|
||||
/// Fetch user-added sticker packs (with stickers) from API:
|
||||
/// GET /sphere/stickers/me
|
||||
@riverpod
|
||||
Future<List<SnStickerPack>> myStickerPacks(Ref ref) async {
|
||||
final api = ref.watch(apiClientProvider);
|
||||
final resp = await api.get('/sphere/stickers/me');
|
||||
final data = resp.data;
|
||||
if (data is List) {
|
||||
return data
|
||||
.map((e) => SnStickerPack.fromJson(e as Map<String, dynamic>))
|
||||
.toList();
|
||||
}
|
||||
return const <SnStickerPack>[];
|
||||
}
|
||||
|
||||
/// Sticker Picker popover dialog
|
||||
/// - Displays user-owned sticker packs as tabs (chips)
|
||||
/// - Shows grid of stickers in selected pack
|
||||
/// - On tap, returns placeholder string :{prefix}+{slug}: via onPick callback
|
||||
class StickerPicker extends HookConsumerWidget {
|
||||
final void Function(String placeholder) onPick;
|
||||
|
||||
const StickerPicker({super.key, required this.onPick});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final packsAsync = ref.watch(myStickerPacksProvider);
|
||||
|
||||
return PopupCard(
|
||||
elevation: 8,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 520, maxHeight: 520),
|
||||
child: packsAsync.when(
|
||||
data: (packs) {
|
||||
if (packs.isEmpty) {
|
||||
return _EmptyState(
|
||||
onRefresh: () async {
|
||||
ref.invalidate(myStickerPacksProvider);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Maintain selected index locally with a ValueNotifier to avoid hooks dependency
|
||||
return _PackSwitcher(
|
||||
packs: packs,
|
||||
onPick: (pack, sticker) {
|
||||
final placeholder = ':${pack.prefix}+${sticker.slug}:';
|
||||
HapticFeedback.selectionClick();
|
||||
onPick(placeholder);
|
||||
if (Navigator.of(context).canPop()) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
onRefresh: () async {
|
||||
ref.invalidate(myStickerPacksProvider);
|
||||
},
|
||||
);
|
||||
},
|
||||
loading: () => const SizedBox(
|
||||
width: 320,
|
||||
height: 320,
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
error: (err, _) => SizedBox(
|
||||
width: 360,
|
||||
height: 200,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Symbols.error, size: 28),
|
||||
const Gap(8),
|
||||
Text('Error: $err', textAlign: TextAlign.center),
|
||||
const Gap(12),
|
||||
FilledButton.icon(
|
||||
onPressed: () => ref.invalidate(myStickerPacksProvider),
|
||||
icon: const Icon(Symbols.refresh),
|
||||
label: Text('retry').tr(),
|
||||
),
|
||||
],
|
||||
).padding(all: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _EmptyState extends StatelessWidget {
|
||||
final Future<void> Function() onRefresh;
|
||||
const _EmptyState({required this.onRefresh});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 360,
|
||||
height: 220,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Symbols.emoji_symbols, size: 28),
|
||||
const Gap(8),
|
||||
Text('noStickerPacks'.tr(), textAlign: TextAlign.center),
|
||||
const Gap(12),
|
||||
OutlinedButton.icon(
|
||||
onPressed: onRefresh,
|
||||
icon: const Icon(Symbols.refresh),
|
||||
label: Text('refresh').tr(),
|
||||
),
|
||||
],
|
||||
).padding(all: 16),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PackSwitcher extends StatefulWidget {
|
||||
final List<SnStickerPack> packs;
|
||||
final void Function(SnStickerPack pack, SnSticker sticker) onPick;
|
||||
final Future<void> Function() onRefresh;
|
||||
|
||||
const _PackSwitcher({
|
||||
required this.packs,
|
||||
required this.onPick,
|
||||
required this.onRefresh,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_PackSwitcher> createState() => _PackSwitcherState();
|
||||
}
|
||||
|
||||
class _PackSwitcherState extends State<_PackSwitcher> {
|
||||
int _index = 0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final packs = widget.packs;
|
||||
_index = _index.clamp(0, packs.length - 1);
|
||||
|
||||
final selectedPack = packs[_index];
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// Header
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Symbols.sticky_note_2, size: 20),
|
||||
const Gap(8),
|
||||
Text(
|
||||
'stickers'.tr(),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
visualDensity: VisualDensity.compact,
|
||||
tooltip: 'close'.tr(),
|
||||
onPressed: () => Navigator.of(context).maybePop(),
|
||||
icon: const Icon(Symbols.close),
|
||||
),
|
||||
],
|
||||
).padding(horizontal: 12, top: 8),
|
||||
|
||||
// Vertical, scrollable packs rail like common emoji pickers
|
||||
SizedBox(
|
||||
height: 48,
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: packs.length,
|
||||
separatorBuilder: (_, _) => const Gap(4),
|
||||
itemBuilder: (context, i) {
|
||||
final selected = _index == i;
|
||||
return Tooltip(
|
||||
message: packs[i].name,
|
||||
child: FilterChip(
|
||||
label: Text(packs[i].name, overflow: TextOverflow.ellipsis),
|
||||
selected: selected,
|
||||
onSelected: (_) {
|
||||
setState(() => _index = i);
|
||||
HapticFeedback.selectionClick();
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
).padding(bottom: 8),
|
||||
const Divider(height: 1),
|
||||
|
||||
// Content
|
||||
Expanded(
|
||||
child: ExtendedRefreshIndicator(
|
||||
onRefresh: widget.onRefresh,
|
||||
child: _StickersGrid(
|
||||
pack: selectedPack,
|
||||
onPick: (sticker) => widget.onPick(selectedPack, sticker),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _StickersGrid extends StatelessWidget {
|
||||
final SnStickerPack pack;
|
||||
final void Function(SnSticker sticker) onPick;
|
||||
|
||||
const _StickersGrid({required this.pack, required this.onPick});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final stickers = pack.stickers;
|
||||
|
||||
if (stickers.isEmpty) {
|
||||
return Center(child: Text('noStickersInPack'.tr()));
|
||||
}
|
||||
|
||||
return GridView.builder(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
|
||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 56,
|
||||
mainAxisSpacing: 8,
|
||||
crossAxisSpacing: 8,
|
||||
),
|
||||
itemCount: stickers.length,
|
||||
itemBuilder: (context, index) {
|
||||
final sticker = stickers[index];
|
||||
final placeholder = ':${pack.prefix}+${sticker.slug}:';
|
||||
return Tooltip(
|
||||
message: placeholder,
|
||||
child: InkWell(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
onTap: () => onPick(sticker),
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: CloudImageWidget(
|
||||
file: sticker.image,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Embedded Sticker Picker variant
|
||||
/// No background card, no title header, suitable for embedding in other UI
|
||||
class StickerPickerEmbedded extends HookConsumerWidget {
|
||||
final double? height;
|
||||
final void Function(String placeholder) onPick;
|
||||
|
||||
const StickerPickerEmbedded({super.key, required this.onPick, this.height});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final packsAsync = ref.watch(myStickerPacksProvider);
|
||||
|
||||
return packsAsync.when(
|
||||
data: (packs) {
|
||||
if (packs.isEmpty) {
|
||||
return _EmptyState(
|
||||
onRefresh: () async {
|
||||
ref.invalidate(myStickerPacksProvider);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return _EmbeddedPackSwitcher(
|
||||
packs: packs,
|
||||
onPick: (pack, sticker) {
|
||||
final placeholder = ':${pack.prefix}+${sticker.slug}:';
|
||||
HapticFeedback.selectionClick();
|
||||
onPick(placeholder);
|
||||
},
|
||||
onRefresh: () async {
|
||||
ref.invalidate(myStickerPacksProvider);
|
||||
},
|
||||
);
|
||||
},
|
||||
loading: () => SizedBox(
|
||||
width: 320,
|
||||
height: height ?? 320,
|
||||
child: const Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
error: (err, _) => SizedBox(
|
||||
width: 360,
|
||||
height: height ?? 200,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Symbols.error, size: 28),
|
||||
const Gap(8),
|
||||
Text('Error: $err', textAlign: TextAlign.center),
|
||||
const Gap(12),
|
||||
FilledButton.icon(
|
||||
onPressed: () => ref.invalidate(myStickerPacksProvider),
|
||||
icon: const Icon(Symbols.refresh),
|
||||
label: Text('retry').tr(),
|
||||
),
|
||||
],
|
||||
).padding(all: 16),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _EmbeddedPackSwitcher extends StatefulWidget {
|
||||
final List<SnStickerPack> packs;
|
||||
final void Function(SnStickerPack pack, SnSticker sticker) onPick;
|
||||
final Future<void> Function() onRefresh;
|
||||
|
||||
const _EmbeddedPackSwitcher({
|
||||
required this.packs,
|
||||
required this.onPick,
|
||||
required this.onRefresh,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_EmbeddedPackSwitcher> createState() => _EmbeddedPackSwitcherState();
|
||||
}
|
||||
|
||||
class _EmbeddedPackSwitcherState extends State<_EmbeddedPackSwitcher> {
|
||||
int _index = 0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final packs = widget.packs;
|
||||
_index = _index.clamp(0, packs.length - 1);
|
||||
|
||||
final selectedPack = packs[_index];
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const Gap(12),
|
||||
// Vertical, scrollable packs rail like common emoji pickers
|
||||
Card(
|
||||
margin: EdgeInsets.zero,
|
||||
child: SizedBox(
|
||||
height: 36,
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: packs.length,
|
||||
separatorBuilder: (_, _) => const Gap(4),
|
||||
itemBuilder: (context, i) {
|
||||
final selected = _index == i;
|
||||
return Tooltip(
|
||||
message: packs[i].name,
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeInOut,
|
||||
decoration: BoxDecoration(
|
||||
color: selected
|
||||
? Theme.of(context).colorScheme.primaryContainer
|
||||
: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
border: selected
|
||||
? Border.all(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
width: 4,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
margin: const EdgeInsets.only(right: 8),
|
||||
child: InkWell(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
onTap: () {
|
||||
setState(() => _index = i);
|
||||
HapticFeedback.selectionClick();
|
||||
},
|
||||
child: TweenAnimationBuilder<double>(
|
||||
tween: Tween<double>(end: selected ? 4 : 8),
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeInOut,
|
||||
builder: (context, value, _) {
|
||||
return packs[i].icon != null
|
||||
? CloudImageWidget(
|
||||
file: packs[i].icon!,
|
||||
).clipRRect(all: value)
|
||||
: CloudImageWidget(
|
||||
file: packs[i].stickers.firstOrNull?.image,
|
||||
).clipRRect(all: value);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
).padding(vertical: 4),
|
||||
).padding(horizontal: 12),
|
||||
|
||||
// Content
|
||||
Expanded(
|
||||
child: ExtendedRefreshIndicator(
|
||||
onRefresh: widget.onRefresh,
|
||||
child: _StickersGrid(
|
||||
pack: selectedPack,
|
||||
onPick: (sticker) => widget.onPick(selectedPack, sticker),
|
||||
).padding(horizontal: 2),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper to show sticker picker as an anchored popover near the trigger.
|
||||
/// Provide the button's BuildContext (typically from the onPressed closure).
|
||||
/// Fallbacks to dialog if overlay cannot be found (e.g., during tests).
|
||||
Future<void> showStickerPickerPopover(
|
||||
BuildContext context,
|
||||
Offset offset, {
|
||||
Alignment? alignment,
|
||||
required void Function(String placeholder) onPick,
|
||||
}) async {
|
||||
// Use flutter_popup_card to present the anchored popup near trigger.
|
||||
await showPopupCard<void>(
|
||||
context: context,
|
||||
offset: offset,
|
||||
alignment: alignment ?? Alignment.topLeft,
|
||||
dimBackground: true,
|
||||
builder: (ctx) => SizedBox(
|
||||
width: math.min(480, MediaQuery.of(context).size.width * 0.9),
|
||||
height: 480,
|
||||
child: ProviderScope(
|
||||
child: StickerPicker(
|
||||
onPick: (ph) {
|
||||
onPick(ph);
|
||||
Navigator.of(ctx).maybePop();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
58
lib/stickers/stickers_widgets/stickers/sticker_picker.g.dart
Normal file
58
lib/stickers/stickers_widgets/stickers/sticker_picker.g.dart
Normal file
@@ -0,0 +1,58 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'sticker_picker.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
/// Fetch user-added sticker packs (with stickers) from API:
|
||||
/// GET /sphere/stickers/me
|
||||
|
||||
@ProviderFor(myStickerPacks)
|
||||
final myStickerPacksProvider = MyStickerPacksProvider._();
|
||||
|
||||
/// Fetch user-added sticker packs (with stickers) from API:
|
||||
/// GET /sphere/stickers/me
|
||||
|
||||
final class MyStickerPacksProvider
|
||||
extends
|
||||
$FunctionalProvider<
|
||||
AsyncValue<List<SnStickerPack>>,
|
||||
List<SnStickerPack>,
|
||||
FutureOr<List<SnStickerPack>>
|
||||
>
|
||||
with
|
||||
$FutureModifier<List<SnStickerPack>>,
|
||||
$FutureProvider<List<SnStickerPack>> {
|
||||
/// Fetch user-added sticker packs (with stickers) from API:
|
||||
/// GET /sphere/stickers/me
|
||||
MyStickerPacksProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'myStickerPacksProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$myStickerPacksHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$FutureProviderElement<List<SnStickerPack>> $createElement(
|
||||
$ProviderPointer pointer,
|
||||
) => $FutureProviderElement(pointer);
|
||||
|
||||
@override
|
||||
FutureOr<List<SnStickerPack>> create(Ref ref) {
|
||||
return myStickerPacks(ref);
|
||||
}
|
||||
}
|
||||
|
||||
String _$myStickerPacksHash() => r'1e19832e8ab1cb139ad18aebfa5aebdf4fdea499';
|
||||
Reference in New Issue
Block a user