From c1e465020f7ac441a468e9cda8fed2b2ed8dc42a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Schw=C3=B6rer?= Date: Sun, 13 Apr 2025 17:21:12 +0200 Subject: [PATCH] Implement sender_list --- flutter/lib/pages/account/account.dart | 5 +- .../lib/pages/sender_list/sender_list.dart | 86 +++++++++++++++++++ .../pages/sender_list/sender_list_item.dart | 85 ++++++++++++++++++ 3 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 flutter/lib/pages/sender_list/sender_list.dart create mode 100644 flutter/lib/pages/sender_list/sender_list_item.dart diff --git a/flutter/lib/pages/account/account.dart b/flutter/lib/pages/account/account.dart index 7e9def0..274570f 100644 --- a/flutter/lib/pages/account/account.dart +++ b/flutter/lib/pages/account/account.dart @@ -8,6 +8,7 @@ import 'package:simplecloudnotifier/api/api_client.dart'; import 'package:simplecloudnotifier/models/user.dart'; import 'package:simplecloudnotifier/pages/account/login.dart'; import 'package:simplecloudnotifier/pages/channel_list/channel_list_extended.dart'; +import 'package:simplecloudnotifier/pages/sender_list/sender_list.dart'; import 'package:simplecloudnotifier/state/app_bar_state.dart'; import 'package:simplecloudnotifier/state/application_log.dart'; import 'package:simplecloudnotifier/state/globals.dart'; @@ -385,7 +386,9 @@ class _AccountRootPageState extends State { _buildNumberCard(context, 'Channels', futureChannelSubscribedCount, () { Navi.push(context, () => ChannelListExtendedPage()); }), - _buildNumberCard(context, 'Sender', futureSenderNamesCount, () {/*TODO*/}), + _buildNumberCard(context, 'Sender', futureSenderNamesCount, () { + Navi.push(context, () => SenderListPage()); + }), UI.buttonCard( context: context, margin: EdgeInsets.fromLTRB(0, 4, 0, 4), diff --git a/flutter/lib/pages/sender_list/sender_list.dart b/flutter/lib/pages/sender_list/sender_list.dart new file mode 100644 index 0000000..dd4b189 --- /dev/null +++ b/flutter/lib/pages/sender_list/sender_list.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; +import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; +import 'package:provider/provider.dart'; +import 'package:simplecloudnotifier/api/api_client.dart'; +import 'package:simplecloudnotifier/components/layout/scaffold.dart'; +import 'package:simplecloudnotifier/models/sender_name_statistics.dart'; +import 'package:simplecloudnotifier/state/application_log.dart'; +import 'package:simplecloudnotifier/state/app_auth.dart'; +import 'package:simplecloudnotifier/pages/sender_list/sender_list_item.dart'; + +class SenderListPage extends StatefulWidget { + const SenderListPage({super.key}); + + @override + State createState() => _SenderListPageState(); +} + +class _SenderListPageState extends State { + final PagingController _pagingController = PagingController.fromValue(PagingState(nextPageKey: null, itemList: [], error: null), firstPageKey: 0); + + @override + void initState() { + super.initState(); + + _pagingController.addPageRequestListener(_fetchPage); + + _pagingController.refresh(); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + } + + @override + void dispose() { + ApplicationLog.debug('SenderListPage::dispose'); + _pagingController.dispose(); + super.dispose(); + } + + Future _fetchPage(int pageKey) async { + final acc = Provider.of(context, listen: false); + + ApplicationLog.debug('Start SenderListPage::_pagingController::_fetchPage [ ${pageKey} ]'); + + if (!acc.isAuth()) { + _pagingController.error = 'Not logged in'; + return; + } + + try { + final items = (await APIClient.getSenderNameList(acc)).toList(); + + items.sort((a, b) => -1 * a.lastTimestamp.compareTo(b.lastTimestamp)); + + _pagingController.value = PagingState(nextPageKey: null, itemList: items, error: null); + } catch (exc, trace) { + _pagingController.error = exc.toString(); + ApplicationLog.error('Failed to list senders: ' + exc.toString(), trace: trace); + } + } + + @override + Widget build(BuildContext context) { + return SCNScaffold( + title: "Sender", + showSearch: false, + showShare: false, + child: Padding( + padding: EdgeInsets.fromLTRB(8, 4, 8, 4), + child: RefreshIndicator( + onRefresh: () => Future.sync( + () => _pagingController.refresh(), + ), + child: PagedListView( + pagingController: _pagingController, + builderDelegate: PagedChildBuilderDelegate( + itemBuilder: (context, item, index) => SenderListItem(item: item), + ), + ), + ), + ), + ); + } +} diff --git a/flutter/lib/pages/sender_list/sender_list_item.dart b/flutter/lib/pages/sender_list/sender_list_item.dart new file mode 100644 index 0000000..70b6c14 --- /dev/null +++ b/flutter/lib/pages/sender_list/sender_list_item.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:intl/intl.dart'; +import 'package:simplecloudnotifier/models/sender_name_statistics.dart'; +import 'package:simplecloudnotifier/utils/navi.dart'; + +enum SenderListItemMode { + Messages, + Extended, +} + +class SenderListItem extends StatelessWidget { + static final _dateFormat = DateFormat('yyyy-MM-dd HH:mm'); //TODO setting + + const SenderListItem({ + required this.item, + super.key, + }); + + final SenderNameStatistics item; + + @override + Widget build(BuildContext context) { + return Card.filled( + margin: EdgeInsets.fromLTRB(0, 4, 0, 4), + shape: BeveledRectangleBorder(borderRadius: BorderRadius.circular(0)), + color: Theme.of(context).cardTheme.color, + child: InkWell( + onTap: () { + //TODO + Navi.popToRoot(context); + }, + child: Padding( + padding: const EdgeInsets.all(8), + child: Row( + children: [ + Icon(FontAwesomeIcons.solidSignature, color: Theme.of(context).colorScheme.outline, size: 32), + SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row( + children: [ + Expanded( + child: Text( + item.name, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ), + ], + ), + SizedBox(height: 4), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: Text( + SenderListItem._dateFormat.format(DateTime.parse(item.lastTimestamp).toLocal()), + style: TextStyle(color: Theme.of(context).textTheme.bodyLarge?.color?.withAlpha(160)), + ), + ), + Text(item.count.toString(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), + ], + ), + ], + ), + ), + SizedBox(width: 4), + GestureDetector( + onTap: () { + //TODO + }, + child: Padding( + padding: const EdgeInsets.all(8), + child: Icon(FontAwesomeIcons.solidEnvelopes, color: Theme.of(context).colorScheme.onPrimaryContainer.withAlpha(128), size: 24), + ), + ), + ], + ), + ), + ), + ); + } +}