♻️ Continued to move riverpod paging utils to own pagination utils
This commit is contained in:
@@ -27,7 +27,7 @@ mixin AsyncPaginationController<T> on AsyncNotifier<List<T>>
|
||||
int? totalCount;
|
||||
|
||||
@override
|
||||
int fetchedCount = 0;
|
||||
int get fetchedCount => state.value?.length ?? 0;
|
||||
|
||||
@override
|
||||
bool get fetchedAll => totalCount != null && fetchedCount >= totalCount!;
|
||||
@@ -38,14 +38,12 @@ mixin AsyncPaginationController<T> on AsyncNotifier<List<T>>
|
||||
@override
|
||||
Future<void> refresh() async {
|
||||
totalCount = null;
|
||||
fetchedCount = 0;
|
||||
state = AsyncData<List<T>>([]);
|
||||
|
||||
final newState = await AsyncValue.guard<List<T>>(() async {
|
||||
return await fetch();
|
||||
});
|
||||
state = newState;
|
||||
fetchedCount = newState.value?.length ?? 0;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -60,7 +58,47 @@ mixin AsyncPaginationController<T> on AsyncNotifier<List<T>>
|
||||
});
|
||||
|
||||
state = newState;
|
||||
fetchedCount = newState.value?.length ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
mixin FamilyAsyncPaginationController<T, Arg>
|
||||
on AutoDisposeFamilyAsyncNotifier<List<T>, Arg>
|
||||
implements PaginationController<T> {
|
||||
@override
|
||||
int? totalCount;
|
||||
|
||||
@override
|
||||
int get fetchedCount => state.value?.length ?? 0;
|
||||
|
||||
@override
|
||||
bool get fetchedAll => totalCount != null && fetchedCount >= totalCount!;
|
||||
|
||||
@override
|
||||
FutureOr<List<T>> build(Arg arg) async => fetch();
|
||||
|
||||
@override
|
||||
Future<void> refresh() async {
|
||||
totalCount = null;
|
||||
state = AsyncData<List<T>>([]);
|
||||
|
||||
final newState = await AsyncValue.guard<List<T>>(() async {
|
||||
return await fetch();
|
||||
});
|
||||
state = newState;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> fetchFurther() async {
|
||||
if (fetchedAll) return;
|
||||
|
||||
state = AsyncLoading<List<T>>();
|
||||
|
||||
final newState = await AsyncValue.guard<List<T>>(() async {
|
||||
final elements = await fetch();
|
||||
return [...?state.valueOrNull, ...elements];
|
||||
});
|
||||
|
||||
state = newState;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,10 +109,8 @@ mixin AsyncPaginationFilter<F, T> on AsyncPaginationController<T>
|
||||
if (currentFilter == filter) return;
|
||||
// Reset the data
|
||||
totalCount = null;
|
||||
fetchedCount = 0;
|
||||
currentFilter = filter;
|
||||
|
||||
state = AsyncData<List<T>>([]);
|
||||
currentFilter = filter;
|
||||
|
||||
final newState = await AsyncValue.guard<List<T>>(() async {
|
||||
return await fetch();
|
||||
|
||||
@@ -3,16 +3,17 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/pods/paging.dart';
|
||||
import 'package:island/pods/userinfo.dart';
|
||||
import 'package:island/widgets/account/account_pfc.dart';
|
||||
import 'package:island/widgets/account/account_picker.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:island/widgets/content/cloud_files.dart';
|
||||
import 'package:island/widgets/paging/pagination_list.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:relative_time/relative_time.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
|
||||
import 'package:island/models/relationship.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
|
||||
@@ -28,39 +29,31 @@ Future<List<SnRelationship>> sentFriendRequest(Ref ref) async {
|
||||
.toList();
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class RelationshipListNotifier extends _$RelationshipListNotifier
|
||||
with CursorPagingNotifierMixin<SnRelationship> {
|
||||
@override
|
||||
Future<CursorPagingData<SnRelationship>> build() => fetch(cursor: null);
|
||||
final relationshipListNotifierProvider = AsyncNotifierProvider(
|
||||
RelationshipListNotifier.new,
|
||||
);
|
||||
|
||||
class RelationshipListNotifier extends AsyncNotifier<List<SnRelationship>>
|
||||
with AsyncPaginationController<SnRelationship> {
|
||||
@override
|
||||
Future<CursorPagingData<SnRelationship>> fetch({
|
||||
required String? cursor,
|
||||
}) async {
|
||||
Future<List<SnRelationship>> fetch() async {
|
||||
final client = ref.read(apiClientProvider);
|
||||
final offset = cursor == null ? 0 : int.parse(cursor);
|
||||
final take = 20;
|
||||
|
||||
final response = await client.get(
|
||||
'/pass/relationships',
|
||||
queryParameters: {'offset': offset, 'take': take},
|
||||
queryParameters: {'offset': fetchedCount.toString(), 'take': take},
|
||||
);
|
||||
|
||||
final List<SnRelationship> items =
|
||||
(response.data as List)
|
||||
.map((e) => SnRelationship.fromJson(e as Map<String, dynamic>))
|
||||
.cast<SnRelationship>()
|
||||
.toList();
|
||||
|
||||
final total = int.tryParse(response.headers['x-total']?.first ?? '') ?? 0;
|
||||
final hasMore = offset + items.length < total;
|
||||
final nextCursor = hasMore ? (offset + items.length).toString() : null;
|
||||
totalCount = int.tryParse(response.headers['x-total']?.first ?? '') ?? 0;
|
||||
|
||||
return CursorPagingData(
|
||||
items: items,
|
||||
hasMore: hasMore,
|
||||
nextCursor: nextCursor,
|
||||
);
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,7 +235,7 @@ class RelationshipScreen extends HookConsumerWidget {
|
||||
await client.post(
|
||||
'/pass/relationships/${relationship.accountId}/friends/${isAccept ? 'accept' : 'decline'}',
|
||||
);
|
||||
relationshipNotifier.forceRefresh();
|
||||
relationshipNotifier.refresh();
|
||||
if (!context.mounted) return;
|
||||
if (isAccept) {
|
||||
showSnackBar(
|
||||
@@ -270,7 +263,7 @@ class RelationshipScreen extends HookConsumerWidget {
|
||||
'/pass/relationships/${relationship.accountId}',
|
||||
data: {'status': newStatus},
|
||||
);
|
||||
relationshipNotifier.forceRefresh();
|
||||
relationshipNotifier.refresh();
|
||||
}
|
||||
|
||||
final user = ref.watch(userInfoProvider);
|
||||
@@ -305,26 +298,15 @@ class RelationshipScreen extends HookConsumerWidget {
|
||||
),
|
||||
const Divider(height: 1),
|
||||
Expanded(
|
||||
child: PagingHelperView(
|
||||
child: PaginationList(
|
||||
provider: relationshipListNotifierProvider,
|
||||
futureRefreshable: relationshipListNotifierProvider.future,
|
||||
notifierRefreshable: relationshipListNotifierProvider.notifier,
|
||||
contentBuilder:
|
||||
(data, widgetCount, endItemView) => ListView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: widgetCount,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == widgetCount - 1) {
|
||||
return endItemView;
|
||||
}
|
||||
|
||||
final relationship = data.items[index];
|
||||
notifier: relationshipListNotifierProvider.notifier,
|
||||
itemBuilder: (context, index, relationship) {
|
||||
return RelationshipListTile(
|
||||
relationship: relationship,
|
||||
submitting: submitting.value,
|
||||
onAccept: () => handleFriendRequest(relationship, true),
|
||||
onDecline:
|
||||
() => handleFriendRequest(relationship, false),
|
||||
onDecline: () => handleFriendRequest(relationship, false),
|
||||
currentUserId: user.value?.id,
|
||||
showRelatedAccount: false,
|
||||
onUpdateStatus: updateRelationship,
|
||||
@@ -332,7 +314,6 @@ class RelationshipScreen extends HookConsumerWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -26,26 +26,5 @@ final sentFriendRequestProvider =
|
||||
// ignore: unused_element
|
||||
typedef SentFriendRequestRef =
|
||||
AutoDisposeFutureProviderRef<List<SnRelationship>>;
|
||||
String _$relationshipListNotifierHash() =>
|
||||
r'fc46920256f7c48445c00652165e879890f2c9a3';
|
||||
|
||||
/// See also [RelationshipListNotifier].
|
||||
@ProviderFor(RelationshipListNotifier)
|
||||
final relationshipListNotifierProvider = AutoDisposeAsyncNotifierProvider<
|
||||
RelationshipListNotifier,
|
||||
CursorPagingData<SnRelationship>
|
||||
>.internal(
|
||||
RelationshipListNotifier.new,
|
||||
name: r'relationshipListNotifierProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$relationshipListNotifierHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$RelationshipListNotifier =
|
||||
AutoDisposeAsyncNotifier<CursorPagingData<SnRelationship>>;
|
||||
// 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
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/models/chat.dart';
|
||||
import 'package:island/pods/chat/chat_room.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/pods/paging.dart';
|
||||
import 'package:island/widgets/account/account_pfc.dart';
|
||||
import 'package:island/widgets/account/account_picker.dart';
|
||||
import 'package:island/widgets/account/status.dart';
|
||||
@@ -17,9 +18,9 @@ import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:island/widgets/content/cloud_files.dart';
|
||||
import 'package:island/widgets/content/sheet.dart';
|
||||
import 'package:island/screens/chat/chat_form.dart';
|
||||
import 'package:island/widgets/paging/pagination_list.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:island/pods/database.dart';
|
||||
import 'package:island/screens/chat/search_messages.dart';
|
||||
@@ -600,39 +601,36 @@ class ChatMemberNotifier extends StateNotifier<ChatRoomMemberState> {
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class ChatMemberListNotifier extends _$ChatMemberListNotifier
|
||||
with CursorPagingNotifierMixin<SnChatMember> {
|
||||
@override
|
||||
Future<CursorPagingData<SnChatMember>> build(String roomId) {
|
||||
return fetch();
|
||||
}
|
||||
final chatMemberListNotifierProvider = AsyncNotifierProvider.autoDispose
|
||||
.family<ChatMemberListNotifier, List<SnChatMember>, String>(
|
||||
ChatMemberListNotifier.new,
|
||||
);
|
||||
|
||||
class ChatMemberListNotifier
|
||||
extends AutoDisposeFamilyAsyncNotifier<List<SnChatMember>, String>
|
||||
with FamilyAsyncPaginationController<SnChatMember, String> {
|
||||
static const pageSize = 20;
|
||||
|
||||
@override
|
||||
Future<CursorPagingData<SnChatMember>> fetch({String? cursor}) async {
|
||||
final offset = cursor == null ? 0 : int.parse(cursor);
|
||||
final take = 20;
|
||||
|
||||
Future<List<SnChatMember>> fetch() async {
|
||||
final apiClient = ref.watch(apiClientProvider);
|
||||
final response = await apiClient.get(
|
||||
'/sphere/chat/$roomId/members',
|
||||
queryParameters: {'offset': offset, 'take': take, 'withStatus': true},
|
||||
'/sphere/chat/$arg/members',
|
||||
queryParameters: {
|
||||
'offset': fetchedCount.toString(),
|
||||
'take': pageSize,
|
||||
'withStatus': true,
|
||||
},
|
||||
);
|
||||
|
||||
final total = int.parse(response.headers.value('X-Total') ?? '0');
|
||||
final List<dynamic> data = response.data;
|
||||
final members = data.map((e) => SnChatMember.fromJson(e)).toList();
|
||||
totalCount = int.parse(response.headers.value('X-Total') ?? '0');
|
||||
final members =
|
||||
response.data
|
||||
.map((e) => SnChatMember.fromJson(e))
|
||||
.cast<SnChatMember>()
|
||||
.toList();
|
||||
|
||||
// Calculate next cursor based on total count
|
||||
final nextOffset = offset + members.length;
|
||||
final String? nextCursor =
|
||||
nextOffset < total ? nextOffset.toString() : null;
|
||||
|
||||
return CursorPagingData(
|
||||
items: members,
|
||||
nextCursor: nextCursor,
|
||||
hasMore: members.length < total,
|
||||
);
|
||||
return members;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -727,19 +725,10 @@ class _ChatMemberListSheet extends HookConsumerWidget {
|
||||
),
|
||||
const Divider(height: 1),
|
||||
Expanded(
|
||||
child: PagingHelperView(
|
||||
child: PaginationList(
|
||||
provider: memberListProvider,
|
||||
futureRefreshable: memberListProvider.future,
|
||||
notifierRefreshable: memberListProvider.notifier,
|
||||
contentBuilder: (data, widgetCount, endItemView) {
|
||||
return ListView.builder(
|
||||
itemCount: widgetCount,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == data.items.length) {
|
||||
return endItemView;
|
||||
}
|
||||
|
||||
final member = data.items[index];
|
||||
notifier: memberListProvider.notifier,
|
||||
itemBuilder: (context, idx, member) {
|
||||
return ListTile(
|
||||
contentPadding: EdgeInsets.only(left: 16, right: 12),
|
||||
leading: AccountPfcGestureDetector(
|
||||
@@ -776,9 +765,7 @@ class _ChatMemberListSheet extends HookConsumerWidget {
|
||||
).then((confirm) async {
|
||||
if (confirm != true) return;
|
||||
try {
|
||||
final apiClient = ref.watch(
|
||||
apiClientProvider,
|
||||
);
|
||||
final apiClient = ref.watch(apiClientProvider);
|
||||
await apiClient.delete(
|
||||
'/sphere/chat/$roomId/members/${member.accountId}',
|
||||
);
|
||||
@@ -796,8 +783,6 @@ class _ChatMemberListSheet extends HookConsumerWidget {
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -149,154 +149,5 @@ class _TotalMessagesCountProviderElement
|
||||
String get roomId => (origin as TotalMessagesCountProvider).roomId;
|
||||
}
|
||||
|
||||
String _$chatMemberListNotifierHash() =>
|
||||
r'3ea30150278523e9f6b23f9200ea9a9fbae9c973';
|
||||
|
||||
abstract class _$ChatMemberListNotifier
|
||||
extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnChatMember>> {
|
||||
late final String roomId;
|
||||
|
||||
FutureOr<CursorPagingData<SnChatMember>> build(String roomId);
|
||||
}
|
||||
|
||||
/// See also [ChatMemberListNotifier].
|
||||
@ProviderFor(ChatMemberListNotifier)
|
||||
const chatMemberListNotifierProvider = ChatMemberListNotifierFamily();
|
||||
|
||||
/// See also [ChatMemberListNotifier].
|
||||
class ChatMemberListNotifierFamily
|
||||
extends Family<AsyncValue<CursorPagingData<SnChatMember>>> {
|
||||
/// See also [ChatMemberListNotifier].
|
||||
const ChatMemberListNotifierFamily();
|
||||
|
||||
/// See also [ChatMemberListNotifier].
|
||||
ChatMemberListNotifierProvider call(String roomId) {
|
||||
return ChatMemberListNotifierProvider(roomId);
|
||||
}
|
||||
|
||||
@override
|
||||
ChatMemberListNotifierProvider getProviderOverride(
|
||||
covariant ChatMemberListNotifierProvider provider,
|
||||
) {
|
||||
return call(provider.roomId);
|
||||
}
|
||||
|
||||
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'chatMemberListNotifierProvider';
|
||||
}
|
||||
|
||||
/// See also [ChatMemberListNotifier].
|
||||
class ChatMemberListNotifierProvider
|
||||
extends
|
||||
AutoDisposeAsyncNotifierProviderImpl<
|
||||
ChatMemberListNotifier,
|
||||
CursorPagingData<SnChatMember>
|
||||
> {
|
||||
/// See also [ChatMemberListNotifier].
|
||||
ChatMemberListNotifierProvider(String roomId)
|
||||
: this._internal(
|
||||
() => ChatMemberListNotifier()..roomId = roomId,
|
||||
from: chatMemberListNotifierProvider,
|
||||
name: r'chatMemberListNotifierProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$chatMemberListNotifierHash,
|
||||
dependencies: ChatMemberListNotifierFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
ChatMemberListNotifierFamily._allTransitiveDependencies,
|
||||
roomId: roomId,
|
||||
);
|
||||
|
||||
ChatMemberListNotifierProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.roomId,
|
||||
}) : super.internal();
|
||||
|
||||
final String roomId;
|
||||
|
||||
@override
|
||||
FutureOr<CursorPagingData<SnChatMember>> runNotifierBuild(
|
||||
covariant ChatMemberListNotifier notifier,
|
||||
) {
|
||||
return notifier.build(roomId);
|
||||
}
|
||||
|
||||
@override
|
||||
Override overrideWith(ChatMemberListNotifier Function() create) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: ChatMemberListNotifierProvider._internal(
|
||||
() => create()..roomId = roomId,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
roomId: roomId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeAsyncNotifierProviderElement<
|
||||
ChatMemberListNotifier,
|
||||
CursorPagingData<SnChatMember>
|
||||
>
|
||||
createElement() {
|
||||
return _ChatMemberListNotifierProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is ChatMemberListNotifierProvider && other.roomId == roomId;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, roomId.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin ChatMemberListNotifierRef
|
||||
on AutoDisposeAsyncNotifierProviderRef<CursorPagingData<SnChatMember>> {
|
||||
/// The parameter `roomId` of this provider.
|
||||
String get roomId;
|
||||
}
|
||||
|
||||
class _ChatMemberListNotifierProviderElement
|
||||
extends
|
||||
AutoDisposeAsyncNotifierProviderElement<
|
||||
ChatMemberListNotifier,
|
||||
CursorPagingData<SnChatMember>
|
||||
>
|
||||
with ChatMemberListNotifierRef {
|
||||
_ChatMemberListNotifierProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
String get roomId => (origin as ChatMemberListNotifierProvider).roomId;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:island/models/post.dart';
|
||||
import 'package:island/models/publisher.dart';
|
||||
import 'package:island/models/heatmap.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/pods/paging.dart';
|
||||
import 'package:island/screens/creators/publishers_form.dart';
|
||||
import 'package:island/services/responsive.dart';
|
||||
import 'package:island/utils/text.dart';
|
||||
@@ -18,11 +19,11 @@ import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:island/widgets/content/cloud_files.dart';
|
||||
import 'package:island/widgets/content/sheet.dart';
|
||||
import 'package:island/widgets/paging/pagination_list.dart';
|
||||
import 'package:island/widgets/response.dart';
|
||||
import 'package:island/widgets/activity_heatmap.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
part 'hub.g.dart';
|
||||
@@ -77,38 +78,31 @@ Future<List<SnPublisherMember>> publisherInvites(Ref ref) async {
|
||||
.toList();
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class PublisherMemberListNotifier extends _$PublisherMemberListNotifier
|
||||
with CursorPagingNotifierMixin<SnPublisherMember> {
|
||||
static const int _pageSize = 20;
|
||||
final publisherMemberListNotifierProvider = AsyncNotifierProvider.family
|
||||
.autoDispose(PublisherMemberListNotifier.new);
|
||||
|
||||
class PublisherMemberListNotifier
|
||||
extends AutoDisposeFamilyAsyncNotifier<List<SnPublisherMember>, String>
|
||||
with FamilyAsyncPaginationController<SnPublisherMember, String> {
|
||||
static const int pageSize = 20;
|
||||
|
||||
@override
|
||||
Future<CursorPagingData<SnPublisherMember>> build(String uname) async {
|
||||
return fetch();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<CursorPagingData<SnPublisherMember>> fetch({String? cursor}) async {
|
||||
Future<List<SnPublisherMember>> fetch() async {
|
||||
final apiClient = ref.read(apiClientProvider);
|
||||
final offset = cursor != null ? int.parse(cursor) : 0;
|
||||
|
||||
final response = await apiClient.get(
|
||||
'/sphere/publishers/$uname/members',
|
||||
queryParameters: {'offset': offset, 'take': _pageSize},
|
||||
'/sphere/publishers/$arg/members',
|
||||
queryParameters: {'offset': fetchedCount.toString(), 'take': pageSize},
|
||||
);
|
||||
|
||||
final total = int.parse(response.headers.value('X-Total') ?? '0');
|
||||
final List<dynamic> data = response.data;
|
||||
final members = data.map((e) => SnPublisherMember.fromJson(e)).toList();
|
||||
totalCount = int.parse(response.headers.value('X-Total') ?? '0');
|
||||
final members =
|
||||
response.data
|
||||
.map((e) => SnPublisherMember.fromJson(e))
|
||||
.cast<SnPublisherMember>()
|
||||
.toList();
|
||||
|
||||
final hasMore = offset + members.length < total;
|
||||
final nextCursor = hasMore ? (offset + members.length).toString() : null;
|
||||
|
||||
return CursorPagingData(
|
||||
items: members,
|
||||
hasMore: hasMore,
|
||||
nextCursor: nextCursor,
|
||||
);
|
||||
return members;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -902,19 +896,10 @@ class _PublisherMemberListSheet extends HookConsumerWidget {
|
||||
),
|
||||
const Divider(height: 1),
|
||||
Expanded(
|
||||
child: PagingHelperView(
|
||||
child: PaginationList(
|
||||
provider: memberListProvider,
|
||||
futureRefreshable: memberListProvider.future,
|
||||
notifierRefreshable: memberListProvider.notifier,
|
||||
contentBuilder: (data, widgetCount, endItemView) {
|
||||
return ListView.builder(
|
||||
itemCount: widgetCount,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == data.items.length) {
|
||||
return endItemView;
|
||||
}
|
||||
|
||||
final member = data.items[index];
|
||||
notifier: memberListProvider.notifier,
|
||||
itemBuilder: (context, index, member) {
|
||||
return ListTile(
|
||||
contentPadding: EdgeInsets.only(left: 16, right: 12),
|
||||
leading: ProfilePictureWidget(
|
||||
@@ -976,9 +961,7 @@ class _PublisherMemberListSheet extends HookConsumerWidget {
|
||||
).then((confirm) async {
|
||||
if (confirm != true) return;
|
||||
try {
|
||||
final apiClient = ref.watch(
|
||||
apiClientProvider,
|
||||
);
|
||||
final apiClient = ref.watch(apiClientProvider);
|
||||
await apiClient.delete(
|
||||
'/sphere/publishers/$publisherUname/members/${member.accountId}',
|
||||
);
|
||||
@@ -996,8 +979,6 @@ class _PublisherMemberListSheet extends HookConsumerWidget {
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -534,158 +534,5 @@ final publisherInvitesProvider =
|
||||
// ignore: unused_element
|
||||
typedef PublisherInvitesRef =
|
||||
AutoDisposeFutureProviderRef<List<SnPublisherMember>>;
|
||||
String _$publisherMemberListNotifierHash() =>
|
||||
r'b4afd5d591a6f3d29f1b45fb1b6d17cb34f3f11b';
|
||||
|
||||
abstract class _$PublisherMemberListNotifier
|
||||
extends
|
||||
BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnPublisherMember>> {
|
||||
late final String uname;
|
||||
|
||||
FutureOr<CursorPagingData<SnPublisherMember>> build(String uname);
|
||||
}
|
||||
|
||||
/// See also [PublisherMemberListNotifier].
|
||||
@ProviderFor(PublisherMemberListNotifier)
|
||||
const publisherMemberListNotifierProvider = PublisherMemberListNotifierFamily();
|
||||
|
||||
/// See also [PublisherMemberListNotifier].
|
||||
class PublisherMemberListNotifierFamily
|
||||
extends Family<AsyncValue<CursorPagingData<SnPublisherMember>>> {
|
||||
/// See also [PublisherMemberListNotifier].
|
||||
const PublisherMemberListNotifierFamily();
|
||||
|
||||
/// See also [PublisherMemberListNotifier].
|
||||
PublisherMemberListNotifierProvider call(String uname) {
|
||||
return PublisherMemberListNotifierProvider(uname);
|
||||
}
|
||||
|
||||
@override
|
||||
PublisherMemberListNotifierProvider getProviderOverride(
|
||||
covariant PublisherMemberListNotifierProvider provider,
|
||||
) {
|
||||
return call(provider.uname);
|
||||
}
|
||||
|
||||
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'publisherMemberListNotifierProvider';
|
||||
}
|
||||
|
||||
/// See also [PublisherMemberListNotifier].
|
||||
class PublisherMemberListNotifierProvider
|
||||
extends
|
||||
AutoDisposeAsyncNotifierProviderImpl<
|
||||
PublisherMemberListNotifier,
|
||||
CursorPagingData<SnPublisherMember>
|
||||
> {
|
||||
/// See also [PublisherMemberListNotifier].
|
||||
PublisherMemberListNotifierProvider(String uname)
|
||||
: this._internal(
|
||||
() => PublisherMemberListNotifier()..uname = uname,
|
||||
from: publisherMemberListNotifierProvider,
|
||||
name: r'publisherMemberListNotifierProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$publisherMemberListNotifierHash,
|
||||
dependencies: PublisherMemberListNotifierFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
PublisherMemberListNotifierFamily._allTransitiveDependencies,
|
||||
uname: uname,
|
||||
);
|
||||
|
||||
PublisherMemberListNotifierProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.uname,
|
||||
}) : super.internal();
|
||||
|
||||
final String uname;
|
||||
|
||||
@override
|
||||
FutureOr<CursorPagingData<SnPublisherMember>> runNotifierBuild(
|
||||
covariant PublisherMemberListNotifier notifier,
|
||||
) {
|
||||
return notifier.build(uname);
|
||||
}
|
||||
|
||||
@override
|
||||
Override overrideWith(PublisherMemberListNotifier Function() create) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: PublisherMemberListNotifierProvider._internal(
|
||||
() => create()..uname = uname,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
uname: uname,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeAsyncNotifierProviderElement<
|
||||
PublisherMemberListNotifier,
|
||||
CursorPagingData<SnPublisherMember>
|
||||
>
|
||||
createElement() {
|
||||
return _PublisherMemberListNotifierProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is PublisherMemberListNotifierProvider && other.uname == uname;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, uname.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin PublisherMemberListNotifierRef
|
||||
on
|
||||
AutoDisposeAsyncNotifierProviderRef<
|
||||
CursorPagingData<SnPublisherMember>
|
||||
> {
|
||||
/// The parameter `uname` of this provider.
|
||||
String get uname;
|
||||
}
|
||||
|
||||
class _PublisherMemberListNotifierProviderElement
|
||||
extends
|
||||
AutoDisposeAsyncNotifierProviderElement<
|
||||
PublisherMemberListNotifier,
|
||||
CursorPagingData<SnPublisherMember>
|
||||
>
|
||||
with PublisherMemberListNotifierRef {
|
||||
_PublisherMemberListNotifierProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
String get uname => (origin as PublisherMemberListNotifierProvider).uname;
|
||||
}
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
|
||||
@@ -7,16 +7,17 @@ import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/models/account.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/pods/paging.dart';
|
||||
import 'package:island/pods/websocket.dart';
|
||||
import 'package:island/route.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/widgets/content/cloud_files.dart';
|
||||
import 'package:island/widgets/content/markdown.dart';
|
||||
import 'package:island/widgets/content/sheet.dart';
|
||||
import 'package:island/widgets/paging/pagination_list.dart';
|
||||
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
||||
import 'package:relative_time/relative_time.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
@@ -81,45 +82,37 @@ class NotificationUnreadCountNotifier
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class NotificationListNotifier extends _$NotificationListNotifier
|
||||
with CursorPagingNotifierMixin<SnNotification> {
|
||||
static const int _pageSize = 5;
|
||||
final notificationListNotifierProvider = AsyncNotifierProvider(
|
||||
NotificationListNotifier.new,
|
||||
);
|
||||
|
||||
class NotificationListNotifier extends AsyncNotifier<List<SnNotification>>
|
||||
with AsyncPaginationController<SnNotification> {
|
||||
static const int pageSize = 5;
|
||||
|
||||
@override
|
||||
Future<CursorPagingData<SnNotification>> build() => fetch(cursor: null);
|
||||
|
||||
@override
|
||||
Future<CursorPagingData<SnNotification>> fetch({
|
||||
required String? cursor,
|
||||
}) async {
|
||||
Future<List<SnNotification>> fetch() async {
|
||||
final client = ref.read(apiClientProvider);
|
||||
final offset = cursor == null ? 0 : int.parse(cursor);
|
||||
|
||||
final queryParams = {'offset': offset, 'take': _pageSize};
|
||||
final queryParams = {'offset': fetchedCount.toString(), 'take': pageSize};
|
||||
|
||||
final response = await client.get(
|
||||
'/ring/notifications',
|
||||
queryParameters: queryParams,
|
||||
);
|
||||
final total = int.parse(response.headers.value('X-Total') ?? '0');
|
||||
final List<dynamic> data = response.data;
|
||||
totalCount = int.parse(response.headers.value('X-Total') ?? '0');
|
||||
final notifications =
|
||||
data.map((json) => SnNotification.fromJson(json)).toList();
|
||||
response.data
|
||||
.map((json) => SnNotification.fromJson(json))
|
||||
.cast<SnNotification>()
|
||||
.toList();
|
||||
|
||||
final hasMore = offset + notifications.length < total;
|
||||
final nextCursor =
|
||||
hasMore ? (offset + notifications.length).toString() : null;
|
||||
final unreadCount = notifications.where((n) => n.viewedAt == null).length;
|
||||
ref
|
||||
.read(notificationUnreadCountNotifierProvider.notifier)
|
||||
.decrement(unreadCount);
|
||||
|
||||
return CursorPagingData(
|
||||
items: notifications,
|
||||
hasMore: hasMore,
|
||||
nextCursor: nextCursor,
|
||||
);
|
||||
return notifications;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,30 +167,17 @@ class NotificationSheet extends HookConsumerWidget {
|
||||
icon: const Icon(Symbols.mark_as_unread),
|
||||
),
|
||||
],
|
||||
child: PagingHelperView(
|
||||
child: PaginationList(
|
||||
provider: notificationListNotifierProvider,
|
||||
futureRefreshable: notificationListNotifierProvider.future,
|
||||
notifierRefreshable: notificationListNotifierProvider.notifier,
|
||||
contentBuilder:
|
||||
(data, widgetCount, endItemView) => ListView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: widgetCount,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == widgetCount - 1) {
|
||||
return endItemView;
|
||||
}
|
||||
|
||||
final notification = data.items[index];
|
||||
notifier: notificationListNotifierProvider.notifier,
|
||||
itemBuilder: (context, index, notification) {
|
||||
final pfp = notification.meta['pfp'] as String?;
|
||||
final images = notification.meta['images'] as List?;
|
||||
final imageIds = images?.cast<String>() ?? [];
|
||||
|
||||
return ListTile(
|
||||
isThreeLine: true,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
leading:
|
||||
pfp != null
|
||||
? ProfilePictureWidget(fileId: pfp, radius: 20)
|
||||
@@ -206,10 +186,7 @@ class NotificationSheet extends HookConsumerWidget {
|
||||
Theme.of(context).colorScheme.primaryContainer,
|
||||
child: Icon(
|
||||
_getNotificationIcon(notification.topic),
|
||||
color:
|
||||
Theme.of(
|
||||
context,
|
||||
).colorScheme.onPrimaryContainer,
|
||||
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
title: Text(notification.title),
|
||||
@@ -222,9 +199,7 @@ class NotificationSheet extends HookConsumerWidget {
|
||||
spacing: 6,
|
||||
children: [
|
||||
Text(
|
||||
DateFormat().format(
|
||||
notification.createdAt.toLocal(),
|
||||
),
|
||||
DateFormat().format(notification.createdAt.toLocal()),
|
||||
).fontSize(11),
|
||||
Text('·').fontSize(11).bold(),
|
||||
Text(
|
||||
@@ -236,9 +211,7 @@ class NotificationSheet extends HookConsumerWidget {
|
||||
).opacity(0.75).padding(bottom: 4),
|
||||
MarkdownTextContent(
|
||||
content: notification.content,
|
||||
textStyle: Theme.of(
|
||||
context,
|
||||
).textTheme.bodyMedium?.copyWith(
|
||||
textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.onSurface.withOpacity(0.8),
|
||||
@@ -297,7 +270,6 @@ class NotificationSheet extends HookConsumerWidget {
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,26 +27,5 @@ final notificationUnreadCountNotifierProvider =
|
||||
);
|
||||
|
||||
typedef _$NotificationUnreadCountNotifier = AutoDisposeAsyncNotifier<int>;
|
||||
String _$notificationListNotifierHash() =>
|
||||
r'260046e11f45b0d67ab25bcbdc8604890d71ccc7';
|
||||
|
||||
/// See also [NotificationListNotifier].
|
||||
@ProviderFor(NotificationListNotifier)
|
||||
final notificationListNotifierProvider = AutoDisposeAsyncNotifierProvider<
|
||||
NotificationListNotifier,
|
||||
CursorPagingData<SnNotification>
|
||||
>.internal(
|
||||
NotificationListNotifier.new,
|
||||
name: r'notificationListNotifierProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$notificationListNotifierHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$NotificationListNotifier =
|
||||
AutoDisposeAsyncNotifier<CursorPagingData<SnNotification>>;
|
||||
// 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
|
||||
|
||||
@@ -19,7 +19,8 @@ import 'package:island/widgets/payment/payment_overlay.dart';
|
||||
import 'package:island/widgets/response.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
|
||||
import 'package:island/pods/paging.dart';
|
||||
import 'package:island/widgets/paging/pagination_list.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:flutter_otp_text_field/flutter_otp_text_field.dart';
|
||||
|
||||
@@ -990,70 +991,87 @@ class _CreateTransferSheetState extends State<CreateTransferSheet> {
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class TransactionListNotifier extends _$TransactionListNotifier
|
||||
with CursorPagingNotifierMixin<SnTransaction> {
|
||||
static const int _pageSize = 20;
|
||||
final transactionListNotifierProvider = AsyncNotifierProvider(
|
||||
TransactionListNotifier.new,
|
||||
);
|
||||
|
||||
class TransactionListNotifier extends AsyncNotifier<List<SnTransaction>>
|
||||
with AsyncPaginationController<SnTransaction> {
|
||||
static const int pageSize = 20;
|
||||
|
||||
@override
|
||||
Future<CursorPagingData<SnTransaction>> build() => fetch(cursor: null);
|
||||
|
||||
@override
|
||||
Future<CursorPagingData<SnTransaction>> fetch({
|
||||
required String? cursor,
|
||||
}) async {
|
||||
Future<List<SnTransaction>> fetch() async {
|
||||
final client = ref.read(apiClientProvider);
|
||||
final offset = cursor == null ? 0 : int.parse(cursor);
|
||||
final offset = fetchedCount;
|
||||
|
||||
final queryParams = {'offset': offset, 'take': _pageSize};
|
||||
final queryParams = {'offset': offset, 'take': pageSize};
|
||||
|
||||
final response = await client.get(
|
||||
'/pass/wallets/transactions',
|
||||
queryParameters: queryParams,
|
||||
);
|
||||
final total = int.parse(response.headers.value('X-Total') ?? '0');
|
||||
totalCount = int.parse(response.headers.value('X-Total') ?? '0');
|
||||
final List<dynamic> data = response.data;
|
||||
final transactions =
|
||||
data.map((json) => SnTransaction.fromJson(json)).toList();
|
||||
|
||||
final hasMore = offset + transactions.length < total;
|
||||
final nextCursor =
|
||||
hasMore ? (offset + transactions.length).toString() : null;
|
||||
|
||||
return CursorPagingData(
|
||||
items: transactions,
|
||||
hasMore: hasMore,
|
||||
nextCursor: nextCursor,
|
||||
);
|
||||
return transactions;
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Future<List<SnWalletFund>> walletFunds(
|
||||
Ref ref, {
|
||||
int offset = 0,
|
||||
int take = 20,
|
||||
}) async {
|
||||
final client = ref.watch(apiClientProvider);
|
||||
final resp = await client.get(
|
||||
'/pass/wallets/funds?offset=$offset&take=$take',
|
||||
final walletFundsNotifierProvider = AsyncNotifierProvider(
|
||||
WalletFundsNotifier.new,
|
||||
);
|
||||
|
||||
class WalletFundsNotifier extends AsyncNotifier<List<SnWalletFund>>
|
||||
with AsyncPaginationController<SnWalletFund> {
|
||||
static const int pageSize = 20;
|
||||
|
||||
@override
|
||||
Future<List<SnWalletFund>> fetch() async {
|
||||
final client = ref.read(apiClientProvider);
|
||||
final offset = fetchedCount;
|
||||
|
||||
final response = await client.get(
|
||||
'/pass/wallets/funds?offset=$offset&take=$pageSize',
|
||||
);
|
||||
return (resp.data as List).map((e) => SnWalletFund.fromJson(e)).toList();
|
||||
// Assuming total count header is present or we just check if list is empty
|
||||
final list =
|
||||
(response.data as List).map((e) => SnWalletFund.fromJson(e)).toList();
|
||||
if (list.length < pageSize) {
|
||||
totalCount = fetchedCount + list.length;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Future<List<SnWalletFundRecipient>> walletFundRecipients(
|
||||
Ref ref, {
|
||||
int offset = 0,
|
||||
int take = 20,
|
||||
}) async {
|
||||
final client = ref.watch(apiClientProvider);
|
||||
final resp = await client.get(
|
||||
'/pass/wallets/funds/recipients?offset=$offset&take=$take',
|
||||
final walletFundRecipientsNotifierProvider = AsyncNotifierProvider(
|
||||
WalletFundRecipientsNotifier.new,
|
||||
);
|
||||
|
||||
class WalletFundRecipientsNotifier
|
||||
extends AsyncNotifier<List<SnWalletFundRecipient>>
|
||||
with AsyncPaginationController<SnWalletFundRecipient> {
|
||||
static const int _pageSize = 20;
|
||||
|
||||
@override
|
||||
Future<List<SnWalletFundRecipient>> fetch() async {
|
||||
final client = ref.read(apiClientProvider);
|
||||
final offset = fetchedCount;
|
||||
|
||||
final response = await client.get(
|
||||
'/pass/wallets/funds/recipients?offset=$offset&take=$_pageSize',
|
||||
);
|
||||
return (resp.data as List)
|
||||
final list =
|
||||
(response.data as List)
|
||||
.map((e) => SnWalletFundRecipient.fromJson(e))
|
||||
.toList();
|
||||
|
||||
if (list.length < _pageSize) {
|
||||
totalCount = fetchedCount + list.length;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
@@ -1408,26 +1426,11 @@ class WalletScreen extends HookConsumerWidget {
|
||||
controller: tabController,
|
||||
children: [
|
||||
// Transactions Tab
|
||||
CustomScrollView(
|
||||
slivers: [
|
||||
PagingHelperSliverView(
|
||||
PaginationList(
|
||||
padding: EdgeInsets.zero,
|
||||
provider: transactionListNotifierProvider,
|
||||
futureRefreshable: transactionListNotifierProvider.future,
|
||||
notifierRefreshable:
|
||||
transactionListNotifierProvider.notifier,
|
||||
contentBuilder:
|
||||
(
|
||||
data,
|
||||
widgetCount,
|
||||
endItemView,
|
||||
) => SliverList.builder(
|
||||
itemCount: widgetCount,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == widgetCount - 1) {
|
||||
return endItemView;
|
||||
}
|
||||
|
||||
final transaction = data.items[index];
|
||||
notifier: transactionListNotifierProvider.notifier,
|
||||
itemBuilder: (context, index, transaction) {
|
||||
final isIncome =
|
||||
transaction.payeeWalletId == wallet.value?.id;
|
||||
|
||||
@@ -1446,9 +1449,7 @@ class WalletScreen extends HookConsumerWidget {
|
||||
child: ListTile(
|
||||
key: ValueKey(transaction.id),
|
||||
leading: Icon(
|
||||
isIncome
|
||||
? Symbols.payment_arrow_down
|
||||
: Symbols.paid,
|
||||
isIncome ? Symbols.payment_arrow_down : Symbols.paid,
|
||||
),
|
||||
title: Text(
|
||||
transaction.remarks ?? '',
|
||||
@@ -1463,17 +1464,13 @@ class WalletScreen extends HookConsumerWidget {
|
||||
trailing: Text(
|
||||
'${isIncome ? '+' : '-'}${transaction.amount.toStringAsFixed(2)} ${transaction.currency}',
|
||||
style: TextStyle(
|
||||
color:
|
||||
isIncome ? Colors.green : Colors.red,
|
||||
color: isIncome ? Colors.green : Colors.red,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// My Funds Tab
|
||||
_buildFundsList(context, ref),
|
||||
@@ -1522,7 +1519,7 @@ class WalletScreen extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
Widget _buildFundsList(BuildContext context, WidgetRef ref) {
|
||||
final funds = ref.watch(walletFundsProvider());
|
||||
final funds = ref.watch(walletFundsNotifierProvider);
|
||||
|
||||
return funds.when(
|
||||
data: (fundList) {
|
||||
@@ -1784,7 +1781,7 @@ class WalletScreen extends HookConsumerWidget {
|
||||
if (paidOrder != null) {
|
||||
// Wait for server to handle order
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
ref.invalidate(walletFundsProvider);
|
||||
ref.invalidate(walletFundsNotifierProvider);
|
||||
ref.invalidate(walletCurrentProvider);
|
||||
if (context.mounted) {
|
||||
showSnackBar('fundCreatedSuccessfully'.tr());
|
||||
|
||||
@@ -40,7 +40,7 @@ final walletStatsProvider = AutoDisposeFutureProvider<SnWalletStats>.internal(
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef WalletStatsRef = AutoDisposeFutureProviderRef<SnWalletStats>;
|
||||
String _$walletFundsHash() => r'f60718c01ca5b7618a02682a0417669f750644a3';
|
||||
String _$walletFundHash() => r'459efdee5e2775eedaa4312e0d317c218fa7e1fa';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
@@ -63,284 +63,6 @@ class _SystemHash {
|
||||
}
|
||||
}
|
||||
|
||||
/// See also [walletFunds].
|
||||
@ProviderFor(walletFunds)
|
||||
const walletFundsProvider = WalletFundsFamily();
|
||||
|
||||
/// See also [walletFunds].
|
||||
class WalletFundsFamily extends Family<AsyncValue<List<SnWalletFund>>> {
|
||||
/// See also [walletFunds].
|
||||
const WalletFundsFamily();
|
||||
|
||||
/// See also [walletFunds].
|
||||
WalletFundsProvider call({int offset = 0, int take = 20}) {
|
||||
return WalletFundsProvider(offset: offset, take: take);
|
||||
}
|
||||
|
||||
@override
|
||||
WalletFundsProvider getProviderOverride(
|
||||
covariant WalletFundsProvider provider,
|
||||
) {
|
||||
return call(offset: provider.offset, take: provider.take);
|
||||
}
|
||||
|
||||
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'walletFundsProvider';
|
||||
}
|
||||
|
||||
/// See also [walletFunds].
|
||||
class WalletFundsProvider
|
||||
extends AutoDisposeFutureProvider<List<SnWalletFund>> {
|
||||
/// See also [walletFunds].
|
||||
WalletFundsProvider({int offset = 0, int take = 20})
|
||||
: this._internal(
|
||||
(ref) => walletFunds(ref as WalletFundsRef, offset: offset, take: take),
|
||||
from: walletFundsProvider,
|
||||
name: r'walletFundsProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$walletFundsHash,
|
||||
dependencies: WalletFundsFamily._dependencies,
|
||||
allTransitiveDependencies: WalletFundsFamily._allTransitiveDependencies,
|
||||
offset: offset,
|
||||
take: take,
|
||||
);
|
||||
|
||||
WalletFundsProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.offset,
|
||||
required this.take,
|
||||
}) : super.internal();
|
||||
|
||||
final int offset;
|
||||
final int take;
|
||||
|
||||
@override
|
||||
Override overrideWith(
|
||||
FutureOr<List<SnWalletFund>> Function(WalletFundsRef provider) create,
|
||||
) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: WalletFundsProvider._internal(
|
||||
(ref) => create(ref as WalletFundsRef),
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
offset: offset,
|
||||
take: take,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeFutureProviderElement<List<SnWalletFund>> createElement() {
|
||||
return _WalletFundsProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is WalletFundsProvider &&
|
||||
other.offset == offset &&
|
||||
other.take == take;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, offset.hashCode);
|
||||
hash = _SystemHash.combine(hash, take.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin WalletFundsRef on AutoDisposeFutureProviderRef<List<SnWalletFund>> {
|
||||
/// The parameter `offset` of this provider.
|
||||
int get offset;
|
||||
|
||||
/// The parameter `take` of this provider.
|
||||
int get take;
|
||||
}
|
||||
|
||||
class _WalletFundsProviderElement
|
||||
extends AutoDisposeFutureProviderElement<List<SnWalletFund>>
|
||||
with WalletFundsRef {
|
||||
_WalletFundsProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
int get offset => (origin as WalletFundsProvider).offset;
|
||||
@override
|
||||
int get take => (origin as WalletFundsProvider).take;
|
||||
}
|
||||
|
||||
String _$walletFundRecipientsHash() =>
|
||||
r'3a5e32b2d20700edd5944885693aff127b58adb1';
|
||||
|
||||
/// See also [walletFundRecipients].
|
||||
@ProviderFor(walletFundRecipients)
|
||||
const walletFundRecipientsProvider = WalletFundRecipientsFamily();
|
||||
|
||||
/// See also [walletFundRecipients].
|
||||
class WalletFundRecipientsFamily
|
||||
extends Family<AsyncValue<List<SnWalletFundRecipient>>> {
|
||||
/// See also [walletFundRecipients].
|
||||
const WalletFundRecipientsFamily();
|
||||
|
||||
/// See also [walletFundRecipients].
|
||||
WalletFundRecipientsProvider call({int offset = 0, int take = 20}) {
|
||||
return WalletFundRecipientsProvider(offset: offset, take: take);
|
||||
}
|
||||
|
||||
@override
|
||||
WalletFundRecipientsProvider getProviderOverride(
|
||||
covariant WalletFundRecipientsProvider provider,
|
||||
) {
|
||||
return call(offset: provider.offset, take: provider.take);
|
||||
}
|
||||
|
||||
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'walletFundRecipientsProvider';
|
||||
}
|
||||
|
||||
/// See also [walletFundRecipients].
|
||||
class WalletFundRecipientsProvider
|
||||
extends AutoDisposeFutureProvider<List<SnWalletFundRecipient>> {
|
||||
/// See also [walletFundRecipients].
|
||||
WalletFundRecipientsProvider({int offset = 0, int take = 20})
|
||||
: this._internal(
|
||||
(ref) => walletFundRecipients(
|
||||
ref as WalletFundRecipientsRef,
|
||||
offset: offset,
|
||||
take: take,
|
||||
),
|
||||
from: walletFundRecipientsProvider,
|
||||
name: r'walletFundRecipientsProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$walletFundRecipientsHash,
|
||||
dependencies: WalletFundRecipientsFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
WalletFundRecipientsFamily._allTransitiveDependencies,
|
||||
offset: offset,
|
||||
take: take,
|
||||
);
|
||||
|
||||
WalletFundRecipientsProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.offset,
|
||||
required this.take,
|
||||
}) : super.internal();
|
||||
|
||||
final int offset;
|
||||
final int take;
|
||||
|
||||
@override
|
||||
Override overrideWith(
|
||||
FutureOr<List<SnWalletFundRecipient>> Function(
|
||||
WalletFundRecipientsRef provider,
|
||||
)
|
||||
create,
|
||||
) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: WalletFundRecipientsProvider._internal(
|
||||
(ref) => create(ref as WalletFundRecipientsRef),
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
offset: offset,
|
||||
take: take,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeFutureProviderElement<List<SnWalletFundRecipient>>
|
||||
createElement() {
|
||||
return _WalletFundRecipientsProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is WalletFundRecipientsProvider &&
|
||||
other.offset == offset &&
|
||||
other.take == take;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, offset.hashCode);
|
||||
hash = _SystemHash.combine(hash, take.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin WalletFundRecipientsRef
|
||||
on AutoDisposeFutureProviderRef<List<SnWalletFundRecipient>> {
|
||||
/// The parameter `offset` of this provider.
|
||||
int get offset;
|
||||
|
||||
/// The parameter `take` of this provider.
|
||||
int get take;
|
||||
}
|
||||
|
||||
class _WalletFundRecipientsProviderElement
|
||||
extends AutoDisposeFutureProviderElement<List<SnWalletFundRecipient>>
|
||||
with WalletFundRecipientsRef {
|
||||
_WalletFundRecipientsProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
int get offset => (origin as WalletFundRecipientsProvider).offset;
|
||||
@override
|
||||
int get take => (origin as WalletFundRecipientsProvider).take;
|
||||
}
|
||||
|
||||
String _$walletFundHash() => r'459efdee5e2775eedaa4312e0d317c218fa7e1fa';
|
||||
|
||||
/// See also [walletFund].
|
||||
@ProviderFor(walletFund)
|
||||
const walletFundProvider = WalletFundFamily();
|
||||
@@ -459,26 +181,5 @@ class _WalletFundProviderElement
|
||||
String get fundId => (origin as WalletFundProvider).fundId;
|
||||
}
|
||||
|
||||
String _$transactionListNotifierHash() =>
|
||||
r'74d3c15f45a6e55b36150ab38e98475a508fc932';
|
||||
|
||||
/// See also [TransactionListNotifier].
|
||||
@ProviderFor(TransactionListNotifier)
|
||||
final transactionListNotifierProvider = AutoDisposeAsyncNotifierProvider<
|
||||
TransactionListNotifier,
|
||||
CursorPagingData<SnTransaction>
|
||||
>.internal(
|
||||
TransactionListNotifier.new,
|
||||
name: r'transactionListNotifierProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$transactionListNotifierHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$TransactionListNotifier =
|
||||
AutoDisposeAsyncNotifier<CursorPagingData<SnTransaction>>;
|
||||
// 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
|
||||
|
||||
@@ -22,6 +22,8 @@ class ComposeFundSheet extends HookConsumerWidget {
|
||||
final isPushing = useState(false);
|
||||
final errorText = useState<String?>(null);
|
||||
|
||||
final fundsData = ref.watch(walletFundsNotifierProvider);
|
||||
|
||||
return SheetScaffold(
|
||||
heightFactor: 0.6,
|
||||
titleText: 'fund'.tr(),
|
||||
@@ -41,16 +43,13 @@ class ComposeFundSheet extends HookConsumerWidget {
|
||||
child: TabBarView(
|
||||
children: [
|
||||
// Link/Select existing fund list
|
||||
ref
|
||||
.watch(walletFundsProvider())
|
||||
.when(
|
||||
fundsData.when(
|
||||
data:
|
||||
(funds) =>
|
||||
funds.isEmpty
|
||||
? Center(
|
||||
child: Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Symbols.money_bag,
|
||||
@@ -78,14 +77,11 @@ class ComposeFundSheet extends HookConsumerWidget {
|
||||
final fund = funds[index];
|
||||
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(
|
||||
bottom: 8,
|
||||
),
|
||||
margin: const EdgeInsets.only(bottom: 8),
|
||||
child: InkWell(
|
||||
onTap:
|
||||
() => Navigator.of(
|
||||
context,
|
||||
).pop(fund),
|
||||
() =>
|
||||
Navigator.of(context).pop(fund),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
@@ -97,9 +93,9 @@ class ComposeFundSheet extends HookConsumerWidget {
|
||||
Icon(
|
||||
Symbols.money_bag,
|
||||
color:
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.primary,
|
||||
Theme.of(
|
||||
context,
|
||||
).colorScheme.primary,
|
||||
fill: 1,
|
||||
),
|
||||
const Gap(8),
|
||||
@@ -111,9 +107,7 @@ class ComposeFundSheet extends HookConsumerWidget {
|
||||
fontWeight:
|
||||
FontWeight.bold,
|
||||
color:
|
||||
Theme.of(
|
||||
context,
|
||||
)
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.primary,
|
||||
),
|
||||
@@ -130,9 +124,7 @@ class ComposeFundSheet extends HookConsumerWidget {
|
||||
_getFundStatusColor(
|
||||
context,
|
||||
fund.status,
|
||||
).withOpacity(
|
||||
0.1,
|
||||
),
|
||||
).withOpacity(0.1),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
12,
|
||||
@@ -157,16 +149,14 @@ class ComposeFundSheet extends HookConsumerWidget {
|
||||
],
|
||||
),
|
||||
if (fund.message != null &&
|
||||
fund
|
||||
.message!
|
||||
.isNotEmpty) ...[
|
||||
fund.message!.isNotEmpty) ...[
|
||||
const Gap(8),
|
||||
Text(
|
||||
fund.message!,
|
||||
style:
|
||||
Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium,
|
||||
Theme.of(
|
||||
context,
|
||||
).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
const Gap(8),
|
||||
@@ -189,12 +179,9 @@ class ComposeFundSheet extends HookConsumerWidget {
|
||||
},
|
||||
),
|
||||
loading:
|
||||
() => const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
() => const Center(child: CircularProgressIndicator()),
|
||||
error:
|
||||
(error, stack) =>
|
||||
Center(child: Text('Error: $error')),
|
||||
(error, stack) => Center(child: Text('Error: $error')),
|
||||
),
|
||||
|
||||
// Create new fund and return it
|
||||
@@ -314,7 +301,9 @@ class ComposeFundSheet extends HookConsumerWidget {
|
||||
await Future.delayed(
|
||||
const Duration(seconds: 1),
|
||||
);
|
||||
ref.invalidate(walletFundsProvider);
|
||||
ref.invalidate(
|
||||
walletFundsNotifierProvider,
|
||||
);
|
||||
|
||||
// Return the created fund
|
||||
final updatedResp = await client.get(
|
||||
|
||||
Reference in New Issue
Block a user