Working on message search+filter
This commit is contained in:
parent
e9ea573e33
commit
0bbe5fc7fa
@ -1,14 +1,18 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/layout/app_bar_filter_dialog.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/app_bar_progress_indicator.dart';
|
import 'package:simplecloudnotifier/components/layout/app_bar_progress_indicator.dart';
|
||||||
import 'package:simplecloudnotifier/pages/debug/debug_main.dart';
|
import 'package:simplecloudnotifier/pages/debug/debug_main.dart';
|
||||||
import 'package:simplecloudnotifier/settings/app_settings.dart';
|
import 'package:simplecloudnotifier/settings/app_settings.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_theme.dart';
|
import 'package:simplecloudnotifier/state/app_theme.dart';
|
||||||
import 'package:simplecloudnotifier/utils/navi.dart';
|
import 'package:simplecloudnotifier/utils/navi.dart';
|
||||||
|
import 'package:simplecloudnotifier/utils/toaster.dart';
|
||||||
|
|
||||||
class SCNAppBar extends StatelessWidget implements PreferredSizeWidget {
|
class SCNAppBar extends StatefulWidget implements PreferredSizeWidget {
|
||||||
const SCNAppBar({
|
SCNAppBar({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.showThemeSwitch,
|
required this.showThemeSwitch,
|
||||||
@ -23,6 +27,22 @@ class SCNAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
final bool showShare;
|
final bool showShare;
|
||||||
final void Function()? onShare;
|
final void Function()? onShare;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SCNAppBar> createState() => _SCNAppBarState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SCNAppBarState extends State<SCNAppBar> {
|
||||||
|
final TextEditingController _ctrlSearchField = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_ctrlSearchField.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final cfg = Provider.of<AppSettings>(context);
|
final cfg = Provider.of<AppSettings>(context);
|
||||||
@ -39,7 +59,7 @@ class SCNAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showThemeSwitch) {
|
if (widget.showThemeSwitch) {
|
||||||
actions.add(Consumer<AppTheme>(
|
actions.add(Consumer<AppTheme>(
|
||||||
builder: (context, appTheme, child) => IconButton(
|
builder: (context, appTheme, child) => IconButton(
|
||||||
icon: Icon(appTheme.darkMode ? FontAwesomeIcons.solidSun : FontAwesomeIcons.solidMoon),
|
icon: Icon(appTheme.darkMode ? FontAwesomeIcons.solidSun : FontAwesomeIcons.solidMoon),
|
||||||
@ -48,54 +68,117 @@ class SCNAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
),
|
),
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
actions.add(Visibility(
|
actions.add(_buildSpacer());
|
||||||
visible: false,
|
|
||||||
maintainSize: true,
|
|
||||||
maintainAnimation: true,
|
|
||||||
maintainState: true,
|
|
||||||
child: IconButton(
|
|
||||||
icon: const Icon(FontAwesomeIcons.square),
|
|
||||||
onPressed: () {/*TODO*/},
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showSearch) {
|
if (widget.showSearch) {
|
||||||
|
actions.add(IconButton(
|
||||||
|
icon: const Icon(FontAwesomeIcons.solidFilter),
|
||||||
|
tooltip: 'Filter',
|
||||||
|
onPressed: () => _showFilterDialog(context),
|
||||||
|
));
|
||||||
actions.add(IconButton(
|
actions.add(IconButton(
|
||||||
icon: const Icon(FontAwesomeIcons.solidMagnifyingGlass),
|
icon: const Icon(FontAwesomeIcons.solidMagnifyingGlass),
|
||||||
tooltip: 'Search',
|
tooltip: 'Search',
|
||||||
onPressed: () {/*TODO*/},
|
onPressed: () => AppBarState().setShowSearchField(true),
|
||||||
));
|
));
|
||||||
} else if (showShare) {
|
} else if (widget.showShare) {
|
||||||
|
actions.add(_buildSpacer());
|
||||||
actions.add(IconButton(
|
actions.add(IconButton(
|
||||||
icon: const Icon(FontAwesomeIcons.solidShareNodes),
|
icon: const Icon(FontAwesomeIcons.solidShareNodes),
|
||||||
tooltip: 'Share',
|
tooltip: 'Share',
|
||||||
onPressed: onShare ?? () {},
|
onPressed: widget.onShare ?? () {},
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
actions.add(Visibility(
|
actions.add(_buildSpacer());
|
||||||
visible: false,
|
|
||||||
maintainSize: true,
|
|
||||||
maintainAnimation: true,
|
|
||||||
maintainState: true,
|
|
||||||
child: IconButton(
|
|
||||||
icon: const Icon(FontAwesomeIcons.square),
|
|
||||||
onPressed: () {/*TODO*/},
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Consumer<AppBarState>(builder: (context, value, child) {
|
||||||
|
if (value.showSearchField) {
|
||||||
return AppBar(
|
return AppBar(
|
||||||
title: Text(title ?? 'Simple Cloud Notifier 2.0'),
|
leading: IconButton(
|
||||||
|
icon: const Icon(FontAwesomeIcons.solidArrowLeft),
|
||||||
|
onPressed: () {
|
||||||
|
value.setShowSearchField(false);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
title: _buildSearchTextField(context),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(FontAwesomeIcons.solidMagnifyingGlass),
|
||||||
|
onPressed: () {
|
||||||
|
value.setShowSearchField(false);
|
||||||
|
AppBarState().notifySearchListeners(_ctrlSearchField.text);
|
||||||
|
_ctrlSearchField.clear();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
backgroundColor: Theme.of(context).secondaryHeaderColor,
|
||||||
|
bottom: PreferredSize(
|
||||||
|
preferredSize: Size(double.infinity, 1.0),
|
||||||
|
child: AppBarProgressIndicator(show: value.loadingIndeterminate),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return AppBar(
|
||||||
|
title: Text(widget.title ?? 'SCN'),
|
||||||
actions: actions,
|
actions: actions,
|
||||||
backgroundColor: Theme.of(context).secondaryHeaderColor,
|
backgroundColor: Theme.of(context).secondaryHeaderColor,
|
||||||
bottom: PreferredSize(
|
bottom: PreferredSize(
|
||||||
preferredSize: Size(double.infinity, 1.0),
|
preferredSize: Size(double.infinity, 1.0),
|
||||||
child: AppBarProgressIndicator(),
|
child: AppBarProgressIndicator(show: value.loadingIndeterminate),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Visibility _buildSpacer() {
|
||||||
|
return Visibility(
|
||||||
|
visible: false,
|
||||||
|
maintainSize: true,
|
||||||
|
maintainAnimation: true,
|
||||||
|
maintainState: true,
|
||||||
|
child: IconButton(
|
||||||
|
icon: const Icon(FontAwesomeIcons.square),
|
||||||
|
onPressed: () {/* NO-OP */},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
Widget _buildSearchTextField(BuildContext context) {
|
||||||
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
return TextField(
|
||||||
|
controller: _ctrlSearchField,
|
||||||
|
autofocus: true,
|
||||||
|
style: TextStyle(fontSize: 20),
|
||||||
|
textInputAction: TextInputAction.search,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'Search',
|
||||||
|
),
|
||||||
|
onSubmitted: (value) {
|
||||||
|
AppBarState().setShowSearchField(false);
|
||||||
|
AppBarState().notifySearchListeners(_ctrlSearchField.text);
|
||||||
|
_ctrlSearchField.clear();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showFilterDialog(BuildContext context) {
|
||||||
|
double vpWidth = MediaQuery.sizeOf(context).width;
|
||||||
|
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: true,
|
||||||
|
barrierColor: Colors.transparent,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return Dialog(
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0)),
|
||||||
|
alignment: Alignment.topCenter,
|
||||||
|
insetPadding: EdgeInsets.fromLTRB(0, this.widget.preferredSize.height, 0, 0),
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
child: AppBarFilterDialog(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
86
flutter/lib/components/layout/app_bar_filter_dialog.dart
Normal file
86
flutter/lib/components/layout/app_bar_filter_dialog.dart
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:simplecloudnotifier/utils/navi.dart';
|
||||||
|
|
||||||
|
class AppBarFilterDialog extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_AppBarFilterDialogState createState() => _AppBarFilterDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AppBarFilterDialogState extends State<AppBarFilterDialog> {
|
||||||
|
double _height = 0;
|
||||||
|
|
||||||
|
double _targetHeight = 4 + (48 * 6) + (16 * 5) + 4;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
Future.delayed(Duration.zero, () {
|
||||||
|
setState(() {
|
||||||
|
_height = _targetHeight;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
double vpWidth = MediaQuery.sizeOf(context).width;
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.all(0),
|
||||||
|
width: vpWidth,
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
color: Theme.of(context).secondaryHeaderColor,
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: Duration(milliseconds: 350),
|
||||||
|
curve: Curves.easeInCubic,
|
||||||
|
height: _height,
|
||||||
|
child: ClipRect(
|
||||||
|
child: OverflowBox(
|
||||||
|
alignment: Alignment.topCenter,
|
||||||
|
maxWidth: vpWidth,
|
||||||
|
minWidth: vpWidth,
|
||||||
|
minHeight: 0,
|
||||||
|
maxHeight: _targetHeight,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(height: 4),
|
||||||
|
_buildFilterItem(context, FontAwesomeIcons.magnifyingGlass, 'Search'),
|
||||||
|
Divider(),
|
||||||
|
_buildFilterItem(context, FontAwesomeIcons.snake, 'Channel'),
|
||||||
|
Divider(),
|
||||||
|
_buildFilterItem(context, FontAwesomeIcons.signature, 'Sender'),
|
||||||
|
Divider(),
|
||||||
|
_buildFilterItem(context, FontAwesomeIcons.timer, 'Time'),
|
||||||
|
Divider(),
|
||||||
|
_buildFilterItem(context, FontAwesomeIcons.bolt, 'Priority'),
|
||||||
|
Divider(),
|
||||||
|
_buildFilterItem(context, FontAwesomeIcons.gearCode, 'Key'),
|
||||||
|
SizedBox(height: 4),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(child: GestureDetector(child: Container(width: vpWidth, color: Color(0x88000000)), onTap: () => Navi.popDialog(context))),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFilterItem(BuildContext context, IconData icon, String label) {
|
||||||
|
return ListTile(
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
title: Text(label),
|
||||||
|
leading: Icon(icon),
|
||||||
|
onTap: () {
|
||||||
|
Navi.popDialog(context);
|
||||||
|
//TOOD show more...
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +1,19 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
|
||||||
|
|
||||||
class AppBarProgressIndicator extends StatelessWidget implements PreferredSizeWidget {
|
class AppBarProgressIndicator extends StatelessWidget implements PreferredSizeWidget {
|
||||||
|
AppBarProgressIndicator({required this.show});
|
||||||
|
|
||||||
|
final bool show;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Size get preferredSize => Size(double.infinity, 1.0);
|
Size get preferredSize => Size(double.infinity, 1.0);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer<AppBarState>(
|
if (show) {
|
||||||
builder: (context, value, child) {
|
|
||||||
if (value.loadingIndeterminate) {
|
|
||||||
return LinearProgressIndicator(value: null);
|
return LinearProgressIndicator(value: null);
|
||||||
} else {
|
} else {
|
||||||
return SizedBox.square(dimension: 4); // 4 height is the same as the LinearProgressIndicator
|
return SizedBox.square(dimension: 4); // 4 height is the same as the LinearProgressIndicator
|
||||||
}
|
}
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
36
flutter/lib/pages/message_list/message_filter_chiplet.dart
Normal file
36
flutter/lib/pages/message_list/message_filter_chiplet.dart
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:flutter/src/widgets/icon_data.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
|
||||||
|
enum MessageFilterChipletType {
|
||||||
|
search,
|
||||||
|
channel,
|
||||||
|
sender,
|
||||||
|
timeRange,
|
||||||
|
priority,
|
||||||
|
sendkey,
|
||||||
|
}
|
||||||
|
|
||||||
|
class MessageFilterChiplet {
|
||||||
|
final String label;
|
||||||
|
final String value;
|
||||||
|
final MessageFilterChipletType type;
|
||||||
|
|
||||||
|
MessageFilterChiplet({required this.label, required this.value, required this.type});
|
||||||
|
|
||||||
|
IconData? icon() {
|
||||||
|
switch (type) {
|
||||||
|
case MessageFilterChipletType.search:
|
||||||
|
return FontAwesomeIcons.magnifyingGlass;
|
||||||
|
case MessageFilterChipletType.channel:
|
||||||
|
return FontAwesomeIcons.snake;
|
||||||
|
case MessageFilterChipletType.sender:
|
||||||
|
return FontAwesomeIcons.signature;
|
||||||
|
case MessageFilterChipletType.timeRange:
|
||||||
|
return FontAwesomeIcons.timer;
|
||||||
|
case MessageFilterChipletType.priority:
|
||||||
|
return FontAwesomeIcons.bolt;
|
||||||
|
case MessageFilterChipletType.sendkey:
|
||||||
|
return FontAwesomeIcons.gearCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
import 'package:simplecloudnotifier/models/channel.dart';
|
import 'package:simplecloudnotifier/models/channel.dart';
|
||||||
import 'package:simplecloudnotifier/models/scn_message.dart';
|
import 'package:simplecloudnotifier/models/scn_message.dart';
|
||||||
|
import 'package:simplecloudnotifier/pages/message_list/message_filter_chiplet.dart';
|
||||||
import 'package:simplecloudnotifier/pages/message_view/message_view.dart';
|
import 'package:simplecloudnotifier/pages/message_view/message_view.dart';
|
||||||
import 'package:simplecloudnotifier/settings/app_settings.dart';
|
import 'package:simplecloudnotifier/settings/app_settings.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
||||||
@ -34,10 +35,14 @@ class _MessageListPageState extends State<MessageListPage> with RouteAware {
|
|||||||
|
|
||||||
bool _isInitialized = false;
|
bool _isInitialized = false;
|
||||||
|
|
||||||
|
List<MessageFilterChiplet> _filterChiplets = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
|
AppBarState().subscribeSearchListener(_onAppBarSearch);
|
||||||
|
|
||||||
_pagingController.addPageRequestListener(_fetchPage);
|
_pagingController.addPageRequestListener(_fetchPage);
|
||||||
|
|
||||||
if (widget.isVisiblePage && !_isInitialized) _realInitState();
|
if (widget.isVisiblePage && !_isInitialized) _realInitState();
|
||||||
@ -94,6 +99,7 @@ class _MessageListPageState extends State<MessageListPage> with RouteAware {
|
|||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
ApplicationLog.debug('MessageListPage::dispose');
|
ApplicationLog.debug('MessageListPage::dispose');
|
||||||
|
AppBarState().unsubscribeSearchListener(_onAppBarSearch);
|
||||||
Navi.modalRouteObserver.unsubscribe(this);
|
Navi.modalRouteObserver.unsubscribe(this);
|
||||||
_pagingController.dispose();
|
_pagingController.dispose();
|
||||||
_lifecyleListener.dispose();
|
_lifecyleListener.dispose();
|
||||||
@ -222,6 +228,18 @@ class _MessageListPageState extends State<MessageListPage> with RouteAware {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
if (_filterChiplets.isNotEmpty)
|
||||||
|
Wrap(
|
||||||
|
alignment: WrapAlignment.start,
|
||||||
|
spacing: 5.0,
|
||||||
|
children: [
|
||||||
|
for (var chiplet in _filterChiplets) _buildFilterChip(context, chiplet),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
child: RefreshIndicator(
|
child: RefreshIndicator(
|
||||||
onRefresh: () => Future.sync(
|
onRefresh: () => Future.sync(
|
||||||
() => _pagingController.refresh(),
|
() => _pagingController.refresh(),
|
||||||
@ -239,6 +257,22 @@ class _MessageListPageState extends State<MessageListPage> with RouteAware {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFilterChip(BuildContext context, MessageFilterChiplet chiplet) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 2, 0, 2),
|
||||||
|
child: InputChip(
|
||||||
|
avatar: Icon(chiplet.icon()),
|
||||||
|
label: Text(chiplet.label),
|
||||||
|
onDeleted: () => setState(() => _filterChiplets.remove(chiplet)),
|
||||||
|
onPressed: () {/* TODO idk what to do here ? */},
|
||||||
|
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,4 +303,10 @@ class _MessageListPageState extends State<MessageListPage> with RouteAware {
|
|||||||
await cache.delete(val.messageID);
|
await cache.delete(val.messageID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onAppBarSearch(String str) {
|
||||||
|
setState(() {
|
||||||
|
_filterChiplets = _filterChiplets.where((element) => false).toList() + [MessageFilterChiplet(label: str, value: str, type: MessageFilterChipletType.search)];
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,6 +137,7 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
_buildMetaCard(context, FontAwesomeIcons.solidSnake, 'Channel', [message.channelID, channel?.displayName ?? message.channelInternalName], () => {/*TODO*/}),
|
_buildMetaCard(context, FontAwesomeIcons.solidSnake, 'Channel', [message.channelID, channel?.displayName ?? message.channelInternalName], () => {/*TODO*/}),
|
||||||
_buildMetaCard(context, FontAwesomeIcons.solidTimer, 'Timestamp', [message.timestamp], null),
|
_buildMetaCard(context, FontAwesomeIcons.solidTimer, 'Timestamp', [message.timestamp], null),
|
||||||
_buildMetaCard(context, FontAwesomeIcons.solidUser, 'User', [if (user != null) user.userID, if (user?.username != null) user!.username!], () => {/*TODO*/}), //TODO
|
_buildMetaCard(context, FontAwesomeIcons.solidUser, 'User', [if (user != null) user.userID, if (user?.username != null) user!.username!], () => {/*TODO*/}), //TODO
|
||||||
|
_buildMetaCard(context, FontAwesomeIcons.solidBolt, 'Priority', [_prettyPrintPriority(message.priority)], () => {/*TODO*/}), //TODO
|
||||||
if (message.senderUserID == userAccUserID) UI.button(text: "Delete Message", onPressed: () {/*TODO*/}, color: Colors.red[900]),
|
if (message.senderUserID == userAccUserID) UI.button(text: "Delete Message", onPressed: () {/*TODO*/}, color: Colors.red[900]),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -252,4 +253,17 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
String _preformatTitle(SCNMessage message) {
|
String _preformatTitle(SCNMessage message) {
|
||||||
return message.title.replaceAll('\n', '').replaceAll('\r', '').replaceAll('\t', ' ');
|
return message.title.replaceAll('\n', '').replaceAll('\r', '').replaceAll('\t', ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _prettyPrintPriority(int priority) {
|
||||||
|
switch (priority) {
|
||||||
|
case 0:
|
||||||
|
return 'Low (0)';
|
||||||
|
case 1:
|
||||||
|
return 'Normal (1)';
|
||||||
|
case 2:
|
||||||
|
return 'High (2)';
|
||||||
|
default:
|
||||||
|
return 'Unknown ($priority)';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,37 @@ class AppBarState extends ChangeNotifier {
|
|||||||
|
|
||||||
AppBarState._internal() {}
|
AppBarState._internal() {}
|
||||||
|
|
||||||
|
List<void Function(String)> _searchListeners = [];
|
||||||
|
|
||||||
bool _loadingIndeterminate = false;
|
bool _loadingIndeterminate = false;
|
||||||
bool get loadingIndeterminate => _loadingIndeterminate;
|
bool get loadingIndeterminate => _loadingIndeterminate;
|
||||||
|
|
||||||
|
bool _showSearchField = false;
|
||||||
|
bool get showSearchField => _showSearchField;
|
||||||
|
|
||||||
void setLoadingIndeterminate(bool v) {
|
void setLoadingIndeterminate(bool v) {
|
||||||
if (_loadingIndeterminate == v) return;
|
if (_loadingIndeterminate == v) return;
|
||||||
_loadingIndeterminate = v;
|
_loadingIndeterminate = v;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setShowSearchField(bool v) {
|
||||||
|
if (_showSearchField == v) return;
|
||||||
|
_showSearchField = v;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void subscribeSearchListener(void Function(String) listener) {
|
||||||
|
_searchListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unsubscribeSearchListener(void Function(String) listener) {
|
||||||
|
_searchListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifySearchListeners(String query) {
|
||||||
|
for (var listener in _searchListeners) {
|
||||||
|
listener(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,11 +158,11 @@ class FBMessage extends HiveObject implements FieldDebuggable {
|
|||||||
this.notificationAndroidCount = rmsg.notification?.android?.count,
|
this.notificationAndroidCount = rmsg.notification?.android?.count,
|
||||||
this.notificationAndroidImageUrl = rmsg.notification?.android?.imageUrl,
|
this.notificationAndroidImageUrl = rmsg.notification?.android?.imageUrl,
|
||||||
this.notificationAndroidLink = rmsg.notification?.android?.link,
|
this.notificationAndroidLink = rmsg.notification?.android?.link,
|
||||||
this.notificationAndroidPriority = rmsg.notification?.android?.priority?.toString(),
|
this.notificationAndroidPriority = rmsg.notification?.android?.priority.toString(),
|
||||||
this.notificationAndroidSmallIcon = rmsg.notification?.android?.smallIcon,
|
this.notificationAndroidSmallIcon = rmsg.notification?.android?.smallIcon,
|
||||||
this.notificationAndroidSound = rmsg.notification?.android?.sound,
|
this.notificationAndroidSound = rmsg.notification?.android?.sound,
|
||||||
this.notificationAndroidTicker = rmsg.notification?.android?.ticker,
|
this.notificationAndroidTicker = rmsg.notification?.android?.ticker,
|
||||||
this.notificationAndroidVisibility = rmsg.notification?.android?.visibility?.toString(),
|
this.notificationAndroidVisibility = rmsg.notification?.android?.visibility.toString(),
|
||||||
this.notificationAndroidTag = rmsg.notification?.android?.tag,
|
this.notificationAndroidTag = rmsg.notification?.android?.tag,
|
||||||
this.notificationAppleBadge = rmsg.notification?.apple?.badge,
|
this.notificationAppleBadge = rmsg.notification?.apple?.badge,
|
||||||
this.notificationAppleSound = rmsg.notification?.apple?.sound?.toString(),
|
this.notificationAppleSound = rmsg.notification?.apple?.sound?.toString(),
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import 'dart:ffi';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:device_info_plus/device_info_plus.dart';
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
|
@ -8,15 +8,21 @@ class Navi {
|
|||||||
|
|
||||||
static void push<T extends Widget>(BuildContext context, T Function() builder) {
|
static void push<T extends Widget>(BuildContext context, T Function() builder) {
|
||||||
Provider.of<AppBarState>(context, listen: false).setLoadingIndeterminate(false);
|
Provider.of<AppBarState>(context, listen: false).setLoadingIndeterminate(false);
|
||||||
|
Provider.of<AppBarState>(context, listen: false).setShowSearchField(false);
|
||||||
|
|
||||||
Navigator.push(context, MaterialPageRoute<T>(builder: (context) => builder()));
|
Navigator.push(context, MaterialPageRoute<T>(builder: (context) => builder()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void popToRoot(BuildContext context) {
|
static void popToRoot(BuildContext context) {
|
||||||
Provider.of<AppBarState>(context, listen: false).setLoadingIndeterminate(false);
|
Provider.of<AppBarState>(context, listen: false).setLoadingIndeterminate(false);
|
||||||
|
Provider.of<AppBarState>(context, listen: false).setShowSearchField(false);
|
||||||
|
|
||||||
Navigator.popUntil(context, (route) => route.isFirst);
|
Navigator.popUntil(context, (route) => route.isFirst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void popDialog(BuildContext dialogContext) {
|
||||||
|
Navigator.pop(dialogContext);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SCNRouteObserver extends RouteObserver<PageRoute<dynamic>> {
|
class SCNRouteObserver extends RouteObserver<PageRoute<dynamic>> {
|
||||||
@ -25,6 +31,7 @@ class SCNRouteObserver extends RouteObserver<PageRoute<dynamic>> {
|
|||||||
super.didPush(route, previousRoute);
|
super.didPush(route, previousRoute);
|
||||||
if (route is PageRoute) {
|
if (route is PageRoute) {
|
||||||
AppBarState().setLoadingIndeterminate(false);
|
AppBarState().setLoadingIndeterminate(false);
|
||||||
|
AppBarState().setShowSearchField(false);
|
||||||
|
|
||||||
print('[SCNRouteObserver] .didPush()');
|
print('[SCNRouteObserver] .didPush()');
|
||||||
}
|
}
|
||||||
@ -35,6 +42,7 @@ class SCNRouteObserver extends RouteObserver<PageRoute<dynamic>> {
|
|||||||
super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
|
super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
|
||||||
if (newRoute is PageRoute) {
|
if (newRoute is PageRoute) {
|
||||||
AppBarState().setLoadingIndeterminate(false);
|
AppBarState().setLoadingIndeterminate(false);
|
||||||
|
AppBarState().setShowSearchField(false);
|
||||||
|
|
||||||
print('[SCNRouteObserver] .didReplace()');
|
print('[SCNRouteObserver] .didReplace()');
|
||||||
}
|
}
|
||||||
@ -45,6 +53,7 @@ class SCNRouteObserver extends RouteObserver<PageRoute<dynamic>> {
|
|||||||
super.didPop(route, previousRoute);
|
super.didPop(route, previousRoute);
|
||||||
if (previousRoute is PageRoute && route is PageRoute) {
|
if (previousRoute is PageRoute && route is PageRoute) {
|
||||||
AppBarState().setLoadingIndeterminate(false);
|
AppBarState().setLoadingIndeterminate(false);
|
||||||
|
AppBarState().setShowSearchField(false);
|
||||||
|
|
||||||
print('[SCNRouteObserver] .didPop()');
|
print('[SCNRouteObserver] .didPop()');
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user