finish sender_list && plain-text-search
This commit is contained in:
parent
c1e465020f
commit
6ec1d80f49
@ -32,6 +32,7 @@ enum ChannelSelector {
|
|||||||
class MessageFilter {
|
class MessageFilter {
|
||||||
List<String>? channelIDs;
|
List<String>? channelIDs;
|
||||||
List<String>? searchFilter;
|
List<String>? searchFilter;
|
||||||
|
List<String>? plainSearchFilter;
|
||||||
List<String>? senderNames;
|
List<String>? senderNames;
|
||||||
List<String>? usedKeys;
|
List<String>? usedKeys;
|
||||||
List<int>? priority;
|
List<int>? priority;
|
||||||
@ -42,6 +43,7 @@ class MessageFilter {
|
|||||||
MessageFilter({
|
MessageFilter({
|
||||||
this.channelIDs,
|
this.channelIDs,
|
||||||
this.searchFilter,
|
this.searchFilter,
|
||||||
|
this.plainSearchFilter,
|
||||||
this.senderNames,
|
this.senderNames,
|
||||||
this.usedKeys,
|
this.usedKeys,
|
||||||
this.priority,
|
this.priority,
|
||||||
@ -288,6 +290,7 @@ class APIClient {
|
|||||||
'next_page_token': [pageToken],
|
'next_page_token': [pageToken],
|
||||||
if (pageSize != null) 'page_size': [pageSize.toString()],
|
if (pageSize != null) 'page_size': [pageSize.toString()],
|
||||||
if (filter?.searchFilter != null) 'search': filter!.searchFilter!,
|
if (filter?.searchFilter != null) 'search': filter!.searchFilter!,
|
||||||
|
if (filter?.plainSearchFilter != null) 'string_search': filter!.plainSearchFilter!,
|
||||||
if (filter?.channelIDs != null) 'channel_id': filter!.channelIDs!,
|
if (filter?.channelIDs != null) 'channel_id': filter!.channelIDs!,
|
||||||
if (filter?.senderNames != null) 'sender': filter!.senderNames!,
|
if (filter?.senderNames != null) 'sender': filter!.senderNames!,
|
||||||
if (filter?.hasSenderName != null) 'has_sender': [filter!.hasSenderName!.toString()],
|
if (filter?.hasSenderName != null) 'has_sender': [filter!.hasSenderName!.toString()],
|
||||||
|
@ -3,6 +3,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|||||||
import 'package:simplecloudnotifier/components/modals/filter_modal_channel.dart';
|
import 'package:simplecloudnotifier/components/modals/filter_modal_channel.dart';
|
||||||
import 'package:simplecloudnotifier/components/modals/filter_modal_keytoken.dart';
|
import 'package:simplecloudnotifier/components/modals/filter_modal_keytoken.dart';
|
||||||
import 'package:simplecloudnotifier/components/modals/filter_modal_priority.dart';
|
import 'package:simplecloudnotifier/components/modals/filter_modal_priority.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/modals/filter_modal_searchplain.dart';
|
||||||
import 'package:simplecloudnotifier/components/modals/filter_modal_sendername.dart';
|
import 'package:simplecloudnotifier/components/modals/filter_modal_sendername.dart';
|
||||||
import 'package:simplecloudnotifier/components/modals/filter_modal_time.dart';
|
import 'package:simplecloudnotifier/components/modals/filter_modal_time.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
||||||
@ -16,7 +17,9 @@ class AppBarFilterDialog extends StatefulWidget {
|
|||||||
class _AppBarFilterDialogState extends State<AppBarFilterDialog> {
|
class _AppBarFilterDialogState extends State<AppBarFilterDialog> {
|
||||||
double _height = 0;
|
double _height = 0;
|
||||||
|
|
||||||
double _targetHeight = 4 + (48 * 6) + (16 * 5) + 4;
|
static const int _itemCount = 7;
|
||||||
|
|
||||||
|
static const double _targetHeight = 4 + (48 * _itemCount) + (16 * (_itemCount - 1)) + 4;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -117,6 +120,6 @@ class _AppBarFilterDialogState extends State<AppBarFilterDialog> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showPlainSearchModal(BuildContext context) {
|
void _showPlainSearchModal(BuildContext context) {
|
||||||
//TODO showDialog<void>(context: context, builder: (BuildContext context) => FilterModalSearchPlain());
|
showDialog<void>(context: context, builder: (BuildContext context) => FilterModalSearchPlain());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
60
flutter/lib/components/modals/filter_modal_searchplain.dart
Normal file
60
flutter/lib/components/modals/filter_modal_searchplain.dart
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:simplecloudnotifier/pages/message_list/message_filter_chiplet.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_events.dart';
|
||||||
|
|
||||||
|
class FilterModalSearchPlain extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_FilterModalSearchPlainState createState() => _FilterModalSearchPlainState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FilterModalSearchPlainState extends State<FilterModalSearchPlain> {
|
||||||
|
final _controller = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('Search'),
|
||||||
|
content: Container(
|
||||||
|
child: TextField(
|
||||||
|
autofocus: true,
|
||||||
|
controller: _controller,
|
||||||
|
decoration: InputDecoration(hintText: "Search..."),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
style: TextButton.styleFrom(textStyle: Theme.of(context).textTheme.labelLarge),
|
||||||
|
child: const Text('Apply'),
|
||||||
|
onPressed: _onOkay,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onOkay() {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
|
List<MessageFilterChiplet> chiplets = [];
|
||||||
|
if (_controller.text.isNotEmpty) {
|
||||||
|
chiplets.add(MessageFilterChiplet(
|
||||||
|
label: _controller.text,
|
||||||
|
value: _controller.text,
|
||||||
|
type: MessageFilterChipletType.plainSearch,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
AppEvents().notifyFilterListeners([MessageFilterChipletType.plainSearch], chiplets);
|
||||||
|
}
|
||||||
|
}
|
@ -73,15 +73,13 @@ class _FilterModalSendernameState extends State<FilterModalSendername> {
|
|||||||
TextButton(
|
TextButton(
|
||||||
style: TextButton.styleFrom(textStyle: Theme.of(context).textTheme.labelLarge),
|
style: TextButton.styleFrom(textStyle: Theme.of(context).textTheme.labelLarge),
|
||||||
child: const Text('Apply'),
|
child: const Text('Apply'),
|
||||||
onPressed: () {
|
onPressed: _onOkay,
|
||||||
onOkay();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onOkay() {
|
void _onOkay() {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
final chiplets = _selectedEntries
|
final chiplets = _selectedEntries
|
||||||
|
@ -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,)));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|||||||
|
|
||||||
enum MessageFilterChipletType {
|
enum MessageFilterChipletType {
|
||||||
search,
|
search,
|
||||||
|
plainSearch,
|
||||||
channel,
|
channel,
|
||||||
sender,
|
sender,
|
||||||
timeRange,
|
timeRange,
|
||||||
@ -21,6 +22,8 @@ class MessageFilterChiplet {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case MessageFilterChipletType.search:
|
case MessageFilterChipletType.search:
|
||||||
return FontAwesomeIcons.magnifyingGlass;
|
return FontAwesomeIcons.magnifyingGlass;
|
||||||
|
case MessageFilterChipletType.plainSearch:
|
||||||
|
return FontAwesomeIcons.magnifyingGlassPlus;
|
||||||
case MessageFilterChipletType.channel:
|
case MessageFilterChipletType.channel:
|
||||||
return FontAwesomeIcons.snake;
|
return FontAwesomeIcons.snake;
|
||||||
case MessageFilterChipletType.sender:
|
case MessageFilterChipletType.sender:
|
||||||
|
@ -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');
|
PagingController<String, SCNMessage> _pagingController = PagingController.fromValue(PagingState(nextPageKey: null, itemList: [], error: null), firstPageKey: '@start');
|
||||||
|
|
||||||
Map<String, Channel>? _channels = null;
|
Map<String, Channel>? _channels = null;
|
||||||
|
bool _channelsFetched = false;
|
||||||
|
|
||||||
bool _isInitialized = false;
|
bool _isInitialized = false;
|
||||||
|
|
||||||
@ -135,9 +136,12 @@ class _MessageListPageState extends State<MessageListPage> with RouteAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (_channels == null) {
|
if (_channels == null || !_channelsFetched) {
|
||||||
final channels = await APIClient.getChannelList(acc, ChannelSelector.allAny);
|
final channels = await APIClient.getChannelList(acc, ChannelSelector.allAny);
|
||||||
|
setState(() {
|
||||||
_channels = <String, Channel>{for (var v in channels) v.channel.channelID: v.channel};
|
_channels = <String, Channel>{for (var v in channels) v.channel.channelID: v.channel};
|
||||||
|
_channelsFetched = true;
|
||||||
|
});
|
||||||
|
|
||||||
SCNDataCache().setChannelCache(channels); // no await
|
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();
|
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();
|
var chipletsKeyTokens = _filterChiplets.where((p) => p.type == MessageFilterChipletType.sendkey).toList();
|
||||||
if (chipletsKeyTokens.isNotEmpty) {
|
if (chipletsKeyTokens.isNotEmpty) {
|
||||||
filter.usedKeys = chipletsKeyTokens.map((p) => p.value as String).toList();
|
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();
|
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;
|
return filter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import 'package:simplecloudnotifier/models/keytoken.dart';
|
|||||||
import 'package:simplecloudnotifier/models/scn_message.dart';
|
import 'package:simplecloudnotifier/models/scn_message.dart';
|
||||||
import 'package:simplecloudnotifier/models/user.dart';
|
import 'package:simplecloudnotifier/models/user.dart';
|
||||||
import 'package:simplecloudnotifier/pages/channel_view/channel_view.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_auth.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
||||||
import 'package:simplecloudnotifier/utils/navi.dart';
|
import 'package:simplecloudnotifier/utils/navi.dart';
|
||||||
@ -152,7 +153,9 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
icon: FontAwesomeIcons.solidSignature,
|
icon: FontAwesomeIcons.solidSignature,
|
||||||
title: 'Sender',
|
title: 'Sender',
|
||||||
values: [message.senderName!],
|
values: [message.senderName!],
|
||||||
mainAction: () => {/*TODO*/},
|
mainAction: () => {
|
||||||
|
Navi.push(context, () => FilteredMessageViewPage(title: message.senderName!, filter: MessageFilter(senderNames: [message.senderName!])))
|
||||||
|
},
|
||||||
),
|
),
|
||||||
UI.metaCard(
|
UI.metaCard(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
import 'package:simplecloudnotifier/models/sender_name_statistics.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';
|
import 'package:simplecloudnotifier/utils/navi.dart';
|
||||||
|
|
||||||
enum SenderListItemMode {
|
enum SenderListItemMode {
|
||||||
@ -27,8 +29,7 @@ class SenderListItem extends StatelessWidget {
|
|||||||
color: Theme.of(context).cardTheme.color,
|
color: Theme.of(context).cardTheme.color,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
//TODO
|
Navi.push(context, () => FilteredMessageViewPage(title: item.name, filter: MessageFilter(senderNames: [item.name])));
|
||||||
Navi.popToRoot(context);
|
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
@ -69,7 +70,7 @@ class SenderListItem extends StatelessWidget {
|
|||||||
SizedBox(width: 4),
|
SizedBox(width: 4),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
//TODO
|
Navi.push(context, () => FilteredMessageViewPage(title: item.name, filter: MessageFilter(senderNames: [item.name])));
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user