✨ Award history
This commit is contained in:
164
lib/widgets/post/post_award_history_sheet.dart
Normal file
164
lib/widgets/post/post_award_history_sheet.dart
Normal file
@@ -0,0 +1,164 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/models/post.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/widgets/content/sheet.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
|
||||
|
||||
part 'post_award_history_sheet.g.dart';
|
||||
|
||||
@riverpod
|
||||
class PostAwardListNotifier extends _$PostAwardListNotifier
|
||||
with CursorPagingNotifierMixin<SnPostAward> {
|
||||
static const int _pageSize = 20;
|
||||
|
||||
@override
|
||||
Future<CursorPagingData<SnPostAward>> build({required String postId}) {
|
||||
return fetch(cursor: null);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<CursorPagingData<SnPostAward>> fetch({required String? cursor}) async {
|
||||
final client = ref.read(apiClientProvider);
|
||||
final offset = cursor == null ? 0 : int.parse(cursor);
|
||||
|
||||
final queryParams = {'offset': offset, 'take': _pageSize};
|
||||
|
||||
final response = await client.get(
|
||||
'/sphere/posts/$postId/awards',
|
||||
queryParameters: queryParams,
|
||||
);
|
||||
final total = int.parse(response.headers.value('X-Total') ?? '0');
|
||||
final List<dynamic> data = response.data;
|
||||
final awards = data.map((json) => SnPostAward.fromJson(json)).toList();
|
||||
|
||||
final hasMore = offset + awards.length < total;
|
||||
final nextCursor = hasMore ? (offset + awards.length).toString() : null;
|
||||
|
||||
return CursorPagingData(
|
||||
items: awards,
|
||||
hasMore: hasMore,
|
||||
nextCursor: nextCursor,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PostAwardHistorySheet extends HookConsumerWidget {
|
||||
final String postId;
|
||||
|
||||
const PostAwardHistorySheet({super.key, required this.postId});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final provider = postAwardListNotifierProvider(postId: postId);
|
||||
|
||||
return SheetScaffold(
|
||||
titleText: 'Award History',
|
||||
child: PagingHelperView(
|
||||
provider: provider,
|
||||
futureRefreshable: provider.future,
|
||||
notifierRefreshable: provider.notifier,
|
||||
contentBuilder:
|
||||
(data, widgetCount, endItemView) => ListView.builder(
|
||||
itemCount: widgetCount,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == widgetCount - 1) {
|
||||
return endItemView;
|
||||
}
|
||||
|
||||
final award = data.items[index];
|
||||
return Column(
|
||||
children: [
|
||||
PostAwardItem(award: award),
|
||||
const Divider(height: 1),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PostAwardItem extends StatelessWidget {
|
||||
final SnPostAward award;
|
||||
|
||||
const PostAwardItem({super.key, required this.award});
|
||||
|
||||
String _getAttitudeText(int attitude) {
|
||||
switch (attitude) {
|
||||
case 0:
|
||||
return 'Positive';
|
||||
case 2:
|
||||
return 'Negative';
|
||||
default:
|
||||
return 'Neutral';
|
||||
}
|
||||
}
|
||||
|
||||
Color _getAttitudeColor(int attitude, BuildContext context) {
|
||||
switch (attitude) {
|
||||
case 0:
|
||||
return Colors.green;
|
||||
case 2:
|
||||
return Colors.red;
|
||||
default:
|
||||
return Theme.of(context).colorScheme.onSurfaceVariant;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: _getAttitudeColor(
|
||||
award.attitude,
|
||||
context,
|
||||
).withOpacity(0.1),
|
||||
child: Icon(
|
||||
award.attitude == 0
|
||||
? Icons.thumb_up
|
||||
: award.attitude == 2
|
||||
? Icons.thumb_down
|
||||
: Icons.thumbs_up_down,
|
||||
color: _getAttitudeColor(award.attitude, context),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
'${award.amount} pts',
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
_getAttitudeText(award.attitude),
|
||||
style: TextStyle(
|
||||
color: _getAttitudeColor(award.attitude, context),
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
if (award.message != null && award.message!.isNotEmpty) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text(award.message!, style: Theme.of(context).textTheme.bodyMedium),
|
||||
],
|
||||
const SizedBox(height: 2),
|
||||
if (award.createdAt != null) ...[
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
award.createdAt!.toLocal().toString().split('.')[0],
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
isThreeLine: award.message != null && award.message!.isNotEmpty,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
);
|
||||
}
|
||||
}
|
180
lib/widgets/post/post_award_history_sheet.g.dart
Normal file
180
lib/widgets/post/post_award_history_sheet.g.dart
Normal file
@@ -0,0 +1,180 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'post_award_history_sheet.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$postAwardListNotifierHash() =>
|
||||
r'492ae59a5dbbfb5c98f863f036023193b6e08668';
|
||||
|
||||
/// 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 _$PostAwardListNotifier
|
||||
extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnPostAward>> {
|
||||
late final String postId;
|
||||
|
||||
FutureOr<CursorPagingData<SnPostAward>> build({required String postId});
|
||||
}
|
||||
|
||||
/// See also [PostAwardListNotifier].
|
||||
@ProviderFor(PostAwardListNotifier)
|
||||
const postAwardListNotifierProvider = PostAwardListNotifierFamily();
|
||||
|
||||
/// See also [PostAwardListNotifier].
|
||||
class PostAwardListNotifierFamily
|
||||
extends Family<AsyncValue<CursorPagingData<SnPostAward>>> {
|
||||
/// See also [PostAwardListNotifier].
|
||||
const PostAwardListNotifierFamily();
|
||||
|
||||
/// See also [PostAwardListNotifier].
|
||||
PostAwardListNotifierProvider call({required String postId}) {
|
||||
return PostAwardListNotifierProvider(postId: postId);
|
||||
}
|
||||
|
||||
@override
|
||||
PostAwardListNotifierProvider getProviderOverride(
|
||||
covariant PostAwardListNotifierProvider provider,
|
||||
) {
|
||||
return call(postId: provider.postId);
|
||||
}
|
||||
|
||||
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'postAwardListNotifierProvider';
|
||||
}
|
||||
|
||||
/// See also [PostAwardListNotifier].
|
||||
class PostAwardListNotifierProvider
|
||||
extends
|
||||
AutoDisposeAsyncNotifierProviderImpl<
|
||||
PostAwardListNotifier,
|
||||
CursorPagingData<SnPostAward>
|
||||
> {
|
||||
/// See also [PostAwardListNotifier].
|
||||
PostAwardListNotifierProvider({required String postId})
|
||||
: this._internal(
|
||||
() => PostAwardListNotifier()..postId = postId,
|
||||
from: postAwardListNotifierProvider,
|
||||
name: r'postAwardListNotifierProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$postAwardListNotifierHash,
|
||||
dependencies: PostAwardListNotifierFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
PostAwardListNotifierFamily._allTransitiveDependencies,
|
||||
postId: postId,
|
||||
);
|
||||
|
||||
PostAwardListNotifierProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.postId,
|
||||
}) : super.internal();
|
||||
|
||||
final String postId;
|
||||
|
||||
@override
|
||||
FutureOr<CursorPagingData<SnPostAward>> runNotifierBuild(
|
||||
covariant PostAwardListNotifier notifier,
|
||||
) {
|
||||
return notifier.build(postId: postId);
|
||||
}
|
||||
|
||||
@override
|
||||
Override overrideWith(PostAwardListNotifier Function() create) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: PostAwardListNotifierProvider._internal(
|
||||
() => create()..postId = postId,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
postId: postId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeAsyncNotifierProviderElement<
|
||||
PostAwardListNotifier,
|
||||
CursorPagingData<SnPostAward>
|
||||
>
|
||||
createElement() {
|
||||
return _PostAwardListNotifierProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is PostAwardListNotifierProvider && other.postId == postId;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, postId.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin PostAwardListNotifierRef
|
||||
on AutoDisposeAsyncNotifierProviderRef<CursorPagingData<SnPostAward>> {
|
||||
/// The parameter `postId` of this provider.
|
||||
String get postId;
|
||||
}
|
||||
|
||||
class _PostAwardListNotifierProviderElement
|
||||
extends
|
||||
AutoDisposeAsyncNotifierProviderElement<
|
||||
PostAwardListNotifier,
|
||||
CursorPagingData<SnPostAward>
|
||||
>
|
||||
with PostAwardListNotifierRef {
|
||||
_PostAwardListNotifierProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
String get postId => (origin as PostAwardListNotifierProvider).postId;
|
||||
}
|
||||
|
||||
// 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