finish sender_list && plain-text-search

This commit is contained in:
2025-04-13 17:43:18 +02:00
parent c1e465020f
commit 6ec1d80f49
9 changed files with 219 additions and 12 deletions

View File

@@ -0,0 +1,120 @@
import 'package:flutter/material.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:simplecloudnotifier/api/api_client.dart';
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
import 'package:simplecloudnotifier/models/channel.dart';
import 'package:simplecloudnotifier/models/scn_message.dart';
import 'package:simplecloudnotifier/pages/message_list/message_list_item.dart';
import 'package:simplecloudnotifier/pages/message_view/message_view.dart';
import 'package:simplecloudnotifier/settings/app_settings.dart';
import 'package:simplecloudnotifier/state/app_auth.dart';
import 'package:simplecloudnotifier/state/application_log.dart';
import 'package:simplecloudnotifier/state/scn_data_cache.dart';
import 'package:simplecloudnotifier/utils/navi.dart';
import 'package:provider/provider.dart';
class FilteredMessageViewPage extends StatefulWidget {
const FilteredMessageViewPage({
required this.title,
required this.filter,
super.key,
});
final String title;
final MessageFilter filter;
@override
State<FilteredMessageViewPage> createState() => _FilteredMessageViewPageState();
}
class _FilteredMessageViewPageState extends State<FilteredMessageViewPage> {
PagingController<String, SCNMessage> _pagingController = PagingController.fromValue(PagingState(nextPageKey: null, itemList: [], error: null), firstPageKey: '@start');
Map<String, Channel>? _channels = null;
bool _channelsFetched = false;
@override
void initState() {
super.initState();
_channels = SCNDataCache().getChannelMap();
_pagingController.addPageRequestListener(_fetchPage);
_pagingController.refresh();
}
@override
void dispose() {
_pagingController.dispose();
super.dispose();
}
Future<void> _fetchPage(String thisPageToken) async {
final acc = Provider.of<AppAuth>(context, listen: false);
final cfg = Provider.of<AppSettings>(context, listen: false);
ApplicationLog.debug('Start FilteredMessageViewPage::_pagingController::_fetchPage [ ${thisPageToken} ]');
if (!acc.isAuth()) {
_pagingController.error = 'Not logged in';
return;
}
try {
if (_channels == null || !_channelsFetched) {
final channels = await APIClient.getChannelList(acc, ChannelSelector.allAny);
setState(() {
_channels = <String, Channel>{for (var v in channels) v.channel.channelID: v.channel};
_channelsFetched = true;
});
}
final (npt, newItems) = await APIClient.getMessageList(acc, thisPageToken, pageSize: cfg.messagePageSize, filter: this.widget.filter);
SCNDataCache().addToMessageCache(newItems); // no await
if (npt == '@end') {
_pagingController.appendLastPage(newItems);
} else {
_pagingController.appendPage(newItems, npt);
}
} catch (exc, trace) {
_pagingController.error = exc.toString();
ApplicationLog.error('Failed to list channel-messages: ' + exc.toString(), trace: trace);
}
}
@override
Widget build(BuildContext context) {
return SCNScaffold(
title: this.widget.title,
showSearch: false,
showShare: false,
child: _buildMessageList(context),
);
}
Widget _buildMessageList(BuildContext context) {
return Padding(
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
child: RefreshIndicator(
onRefresh: () => Future.sync(
() => _pagingController.refresh(),
),
child: PagedListView<String, SCNMessage>(
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<SCNMessage>(
itemBuilder: (context, item, index) => MessageListItem(
message: item,
allChannels: _channels ?? {},
onPressed: () {
Navi.push(context, () => MessageViewPage(messageID: item.messageID, preloadedData: (item,)));
},
),
),
),
),
);
}
}

View File

@@ -3,6 +3,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
enum MessageFilterChipletType {
search,
plainSearch,
channel,
sender,
timeRange,
@@ -21,6 +22,8 @@ class MessageFilterChiplet {
switch (type) {
case MessageFilterChipletType.search:
return FontAwesomeIcons.magnifyingGlass;
case MessageFilterChipletType.plainSearch:
return FontAwesomeIcons.magnifyingGlassPlus;
case MessageFilterChipletType.channel:
return FontAwesomeIcons.snake;
case MessageFilterChipletType.sender:

View File

@@ -30,6 +30,7 @@ class _MessageListPageState extends State<MessageListPage> with RouteAware {
PagingController<String, SCNMessage> _pagingController = PagingController.fromValue(PagingState(nextPageKey: null, itemList: [], error: null), firstPageKey: '@start');
Map<String, Channel>? _channels = null;
bool _channelsFetched = false;
bool _isInitialized = false;
@@ -135,9 +136,12 @@ class _MessageListPageState extends State<MessageListPage> with RouteAware {
}
try {
if (_channels == null) {
if (_channels == null || !_channelsFetched) {
final channels = await APIClient.getChannelList(acc, ChannelSelector.allAny);
_channels = <String, Channel>{for (var v in channels) v.channel.channelID: v.channel};
setState(() {
_channels = <String, Channel>{for (var v in channels) v.channel.channelID: v.channel};
_channelsFetched = true;
});
SCNDataCache().setChannelCache(channels); // no await
}
@@ -314,6 +318,11 @@ class _MessageListPageState extends State<MessageListPage> with RouteAware {
filter.searchFilter = chipletsSearch.map((p) => p.value as String).toList();
}
var chipletsPlainSearch = _filterChiplets.where((p) => p.type == MessageFilterChipletType.plainSearch).toList();
if (chipletsPlainSearch.isNotEmpty) {
filter.plainSearchFilter = chipletsPlainSearch.map((p) => p.value as String).toList();
}
var chipletsKeyTokens = _filterChiplets.where((p) => p.type == MessageFilterChipletType.sendkey).toList();
if (chipletsKeyTokens.isNotEmpty) {
filter.usedKeys = chipletsKeyTokens.map((p) => p.value as String).toList();
@@ -329,6 +338,13 @@ class _MessageListPageState extends State<MessageListPage> with RouteAware {
filter.senderNames = chipletSender.map((p) => p.value as String).toList();
}
var chipletsTimeRange = _filterChiplets.where((p) => p.type == MessageFilterChipletType.timeRange).toList();
if (chipletsTimeRange.isNotEmpty) {
//TODO
//filter.timeAfter = chipletsTimeRange[0].value1 as DateTime;
//filter.timeBefore = chipletsTimeRange[0].value2 as DateTime;
}
return filter;
}
}

View File

@@ -11,6 +11,7 @@ import 'package:simplecloudnotifier/models/keytoken.dart';
import 'package:simplecloudnotifier/models/scn_message.dart';
import 'package:simplecloudnotifier/models/user.dart';
import 'package:simplecloudnotifier/pages/channel_view/channel_view.dart';
import 'package:simplecloudnotifier/pages/filtered_message_view/filtered_message_view.dart';
import 'package:simplecloudnotifier/state/app_auth.dart';
import 'package:simplecloudnotifier/state/app_bar_state.dart';
import 'package:simplecloudnotifier/utils/navi.dart';
@@ -152,7 +153,9 @@ class _MessageViewPageState extends State<MessageViewPage> {
icon: FontAwesomeIcons.solidSignature,
title: 'Sender',
values: [message.senderName!],
mainAction: () => {/*TODO*/},
mainAction: () => {
Navi.push(context, () => FilteredMessageViewPage(title: message.senderName!, filter: MessageFilter(senderNames: [message.senderName!])))
},
),
UI.metaCard(
context: context,

View File

@@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:intl/intl.dart';
import 'package:simplecloudnotifier/api/api_client.dart';
import 'package:simplecloudnotifier/models/sender_name_statistics.dart';
import 'package:simplecloudnotifier/pages/filtered_message_view/filtered_message_view.dart';
import 'package:simplecloudnotifier/utils/navi.dart';
enum SenderListItemMode {
@@ -27,8 +29,7 @@ class SenderListItem extends StatelessWidget {
color: Theme.of(context).cardTheme.color,
child: InkWell(
onTap: () {
//TODO
Navi.popToRoot(context);
Navi.push(context, () => FilteredMessageViewPage(title: item.name, filter: MessageFilter(senderNames: [item.name])));
},
child: Padding(
padding: const EdgeInsets.all(8),
@@ -69,7 +70,7 @@ class SenderListItem extends StatelessWidget {
SizedBox(width: 4),
GestureDetector(
onTap: () {
//TODO
Navi.push(context, () => FilteredMessageViewPage(title: item.name, filter: MessageFilter(senderNames: [item.name])));
},
child: Padding(
padding: const EdgeInsets.all(8),