import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/poll.dart'; import 'package:island/pods/network.dart'; import 'package:island/widgets/poll/poll_feedback.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; part 'poll_list.g.dart'; @riverpod class PollListNotifier extends _$PollListNotifier with CursorPagingNotifierMixin { static const int _pageSize = 20; @override Future> build(String? pubName) { // immediately load first page return fetch(cursor: null); } @override Future> fetch({required String? cursor}) async { final client = ref.read(apiClientProvider); final offset = cursor == null ? 0 : int.parse(cursor); // read the current family argument passed to provider final currentPub = pubName; final queryParams = { 'offset': offset, 'take': _pageSize, if (currentPub != null) 'pub': currentPub, }; final response = await client.get( '/sphere/polls/me', queryParameters: queryParams, ); final total = int.parse(response.headers.value('X-Total') ?? '0'); final List data = response.data; final items = data.map((json) => SnPoll.fromJson(json)).toList(); final hasMore = offset + items.length < total; final nextCursor = hasMore ? (offset + items.length).toString() : null; return CursorPagingData( items: items, hasMore: hasMore, nextCursor: nextCursor, ); } } class CreatorPollListScreen extends HookConsumerWidget { const CreatorPollListScreen({super.key, required this.pubName}); final String pubName; Future _createPoll(BuildContext context) async { final result = await GoRouter.of( context, ).pushNamed('creatorPollNew', pathParameters: {'name': pubName}); if (result is SnPoll && context.mounted) { Navigator.of(context).maybePop(result); } } @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( appBar: AppBar(title: const Text('Polls')), floatingActionButton: FloatingActionButton( onPressed: () => _createPoll(context), child: const Icon(Icons.add), ), body: RefreshIndicator( onRefresh: () => ref.refresh(pollListNotifierProvider(pubName).future), child: CustomScrollView( slivers: [ PagingHelperSliverView( provider: pollListNotifierProvider(pubName), futureRefreshable: pollListNotifierProvider(pubName).future, notifierRefreshable: pollListNotifierProvider(pubName).notifier, contentBuilder: (data, widgetCount, endItemView) => SliverList.builder( itemCount: widgetCount, itemBuilder: (context, index) { if (index == widgetCount - 1) { return endItemView; } final poll = data.items[index]; return _CreatorPollItem(poll: poll, pubName: pubName); }, ), ), ], ), ), ); } } class _CreatorPollItem extends StatelessWidget { final String pubName; const _CreatorPollItem({required this.poll, required this.pubName}); final SnPoll poll; @override Widget build(BuildContext context) { final theme = Theme.of(context); final ended = poll.endedAt; final endedText = ended == null ? 'No end' : MaterialLocalizations.of(context).formatFullDate(ended); return Card( margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), clipBehavior: Clip.antiAlias, child: ListTile( title: Text(poll.title ?? 'Untitled poll'), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (poll.description != null && poll.description!.isNotEmpty) Padding( padding: const EdgeInsets.only(top: 4), child: Text( poll.description!, maxLines: 2, overflow: TextOverflow.ellipsis, ), ), Padding( padding: const EdgeInsets.only(top: 4), child: Text( 'Questions: ${poll.questions.length} ยท Ends: $endedText', style: theme.textTheme.bodySmall, ), ), ], ), trailing: PopupMenuButton( itemBuilder: (context) => [ PopupMenuItem( child: Row( children: [ const Icon(Symbols.edit), const Gap(16), Text('Edit'), ], ), onTap: () { GoRouter.of(context).pushNamed( 'creatorPollEdit', pathParameters: {'name': pubName, 'id': poll.id}, ); }, ), ], ), onTap: () { showModalBottomSheet( context: context, useRootNavigator: true, isScrollControlled: true, builder: (context) => PollFeedbackSheet(pollId: poll.id, poll: poll), ); }, ), ); } }