Add various informative alert-boxes
This commit is contained in:
@@ -123,7 +123,7 @@ class APIClient {
|
|||||||
|
|
||||||
RequestLog.addRequestAPIError(name, t0, method, uri, req.body, req.headers, responseStatusCode, responseBody, responseHeaders, apierr);
|
RequestLog.addRequestAPIError(name, t0, method, uri, req.body, req.headers, responseStatusCode, responseBody, responseHeaders, apierr);
|
||||||
Toaster.error("Error", apierr.message);
|
Toaster.error("Error", apierr.message);
|
||||||
throw APIException(responseStatusCode, apierr.error, apierr.errhighlight, apierr.message);
|
throw APIException(responseStatusCode, apierr.error, apierr.errhighlight, apierr.message, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ class APIException implements Exception {
|
|||||||
final int error;
|
final int error;
|
||||||
final int errHighlight;
|
final int errHighlight;
|
||||||
final String message;
|
final String message;
|
||||||
|
final bool toastShown;
|
||||||
|
|
||||||
APIException(this.httpStatus, this.error, this.errHighlight, this.message);
|
APIException(this.httpStatus, this.error, this.errHighlight, this.message, this.toastShown);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
|
|||||||
@@ -2,33 +2,56 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
enum BadgeMode { error, warn, info }
|
enum BadgeMode { error, warn, info }
|
||||||
|
|
||||||
class BadgeDisplay extends StatelessWidget {
|
class BadgeDisplay extends StatefulWidget {
|
||||||
final String text;
|
final String text;
|
||||||
final BadgeMode mode;
|
final BadgeMode mode;
|
||||||
final IconData? icon;
|
final IconData? icon;
|
||||||
|
final TextAlign textAlign;
|
||||||
|
final bool closable;
|
||||||
|
final EdgeInsets extraPadding;
|
||||||
|
final bool hidden;
|
||||||
|
|
||||||
const BadgeDisplay({
|
const BadgeDisplay({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.text,
|
required this.text,
|
||||||
required this.mode,
|
required this.mode,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
|
this.textAlign = TextAlign.center,
|
||||||
|
this.closable = false,
|
||||||
|
this.extraPadding = EdgeInsets.zero,
|
||||||
|
this.hidden = false,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<BadgeDisplay> createState() => _BadgeDisplayState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BadgeDisplayState extends State<BadgeDisplay> {
|
||||||
|
bool _isVisible = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if (!_isVisible || widget.hidden) return const SizedBox.shrink();
|
||||||
|
|
||||||
var col = Colors.grey;
|
var col = Colors.grey;
|
||||||
var colFG = Colors.black;
|
var colFG = Colors.black;
|
||||||
|
|
||||||
if (mode == BadgeMode.error) col = Colors.red;
|
if (widget.mode == BadgeMode.error) col = Colors.red;
|
||||||
if (mode == BadgeMode.warn) col = Colors.orange;
|
if (widget.mode == BadgeMode.warn) col = Colors.orange;
|
||||||
if (mode == BadgeMode.info) col = Colors.blue;
|
if (widget.mode == BadgeMode.info) col = Colors.blue;
|
||||||
|
|
||||||
if (mode == BadgeMode.error) colFG = Colors.red[900]!;
|
if (widget.mode == BadgeMode.error) colFG = Colors.red[900]!;
|
||||||
if (mode == BadgeMode.warn) colFG = Colors.black;
|
if (widget.mode == BadgeMode.warn) colFG = Colors.black;
|
||||||
if (mode == BadgeMode.info) colFG = Colors.black;
|
if (widget.mode == BadgeMode.info) colFG = Colors.black;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.fromLTRB(8, 2, 8, 2),
|
margin: widget.extraPadding,
|
||||||
|
child: Stack(
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
children: [
|
||||||
|
// The badge itself
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(8, 2, widget.closable ? 16 : 8, 2),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: col[100],
|
color: col[100],
|
||||||
border: Border.all(color: col[300]!),
|
border: Border.all(color: col[300]!),
|
||||||
@@ -36,16 +59,47 @@ class BadgeDisplay extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
if (icon != null) Icon(icon!, color: colFG, size: 16.0),
|
if (widget.icon != null) Icon(widget.icon!, color: colFG, size: 16.0),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
text,
|
widget.text,
|
||||||
textAlign: TextAlign.center,
|
textAlign: widget.textAlign,
|
||||||
style: TextStyle(color: colFG, fontSize: 14.0),
|
style: TextStyle(color: colFG, fontSize: 14.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
if (widget.closable) _buildCloseButton(context, colFG),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Positioned _buildCloseButton(BuildContext context, Color colFG) {
|
||||||
|
return Positioned(
|
||||||
|
top: 1,
|
||||||
|
right: 1,
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_isVisible = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(2),
|
||||||
|
child: Icon(
|
||||||
|
Icons.close,
|
||||||
|
size: 14,
|
||||||
|
color: colFG,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ 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:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/api/api_exception.dart';
|
||||||
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
||||||
import 'package:simplecloudnotifier/models/user.dart';
|
import 'package:simplecloudnotifier/models/user.dart';
|
||||||
import 'package:simplecloudnotifier/pages/account/login.dart';
|
import 'package:simplecloudnotifier/pages/account/login.dart';
|
||||||
@@ -166,6 +167,9 @@ class _AccountRootPageState extends State<AccountRootPage> {
|
|||||||
_futureSenderNamesCount = ImmediateFuture.ofValue(senderNames.length);
|
_futureSenderNamesCount = ImmediateFuture.ofValue(senderNames.length);
|
||||||
_futureUser = ImmediateFuture.ofValue(user);
|
_futureUser = ImmediateFuture.ofValue(user);
|
||||||
});
|
});
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
ApplicationLog.error('Failed to refresh account data: ' + exc.toString(), trace: trace);
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to refresh account data');
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
ApplicationLog.error('Failed to refresh account data: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to refresh account data: ' + exc.toString(), trace: trace);
|
||||||
Toaster.error("Error", 'Failed to refresh account data');
|
Toaster.error("Error", 'Failed to refresh account data');
|
||||||
@@ -417,7 +421,13 @@ class _AccountRootPageState extends State<AccountRootPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navi.push(context, () => FilteredMessageViewPage(title: "All Messages", filter: MessageFilter(senderUserID: [user.userID])));
|
Navi.push(
|
||||||
|
context,
|
||||||
|
() => FilteredMessageViewPage(
|
||||||
|
title: "All Messages",
|
||||||
|
alertText: "All messages sent from your account",
|
||||||
|
filter: MessageFilter(senderUserID: [user.userID]),
|
||||||
|
));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
@@ -528,9 +538,12 @@ class _AccountRootPageState extends State<AccountRootPage> {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (context) => ShowTokenModal(account: acc, isAfterRegister: true),
|
builder: (context) => ShowTokenModal(account: acc, isAfterRegister: true),
|
||||||
);
|
);
|
||||||
} catch (exc, trace) {
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to create user account');
|
||||||
ApplicationLog.error('Failed to create user account: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to create user account: ' + exc.toString(), trace: trace);
|
||||||
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to create user account');
|
Toaster.error("Error", 'Failed to create user account');
|
||||||
|
ApplicationLog.error('Failed to create user account: ' + exc.toString(), trace: trace);
|
||||||
} finally {
|
} finally {
|
||||||
setState(() => loading = false);
|
setState(() => loading = false);
|
||||||
}
|
}
|
||||||
@@ -574,6 +587,9 @@ class _AccountRootPageState extends State<AccountRootPage> {
|
|||||||
//TODO clear messages/channels/etc in open views
|
//TODO clear messages/channels/etc in open views
|
||||||
acc.clear();
|
acc.clear();
|
||||||
await acc.save();
|
await acc.save();
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to delete user');
|
||||||
|
ApplicationLog.error('Failed to delete user: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to delete user');
|
Toaster.error("Error", 'Failed to delete user');
|
||||||
ApplicationLog.error('Failed to delete user: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to delete user: ' + exc.toString(), trace: trace);
|
||||||
@@ -601,6 +617,9 @@ class _AccountRootPageState extends State<AccountRootPage> {
|
|||||||
Toaster.success("Success", 'Username changed');
|
Toaster.success("Success", 'Username changed');
|
||||||
|
|
||||||
_backgroundRefresh();
|
_backgroundRefresh();
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to update username');
|
||||||
|
ApplicationLog.error('Failed to update username: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to update username');
|
Toaster.error("Error", 'Failed to update username');
|
||||||
ApplicationLog.error('Failed to update username: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to update username: ' + exc.toString(), trace: trace);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ 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:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/api/api_exception.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
import 'package:simplecloudnotifier/state/globals.dart';
|
import 'package:simplecloudnotifier/state/globals.dart';
|
||||||
@@ -162,9 +163,12 @@ class _AccountLoginPageState extends State<AccountLoginPage> {
|
|||||||
|
|
||||||
Toaster.success("Login", "Successfully logged in");
|
Toaster.success("Login", "Successfully logged in");
|
||||||
Navi.popToRoot(context);
|
Navi.popToRoot(context);
|
||||||
} catch (exc, trace) {
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to verify token');
|
||||||
ApplicationLog.error('Failed to verify token: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to verify token: ' + exc.toString(), trace: trace);
|
||||||
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to verify token');
|
Toaster.error("Error", 'Failed to verify token');
|
||||||
|
ApplicationLog.error('Failed to verify token: ' + exc.toString(), trace: trace);
|
||||||
} finally {
|
} finally {
|
||||||
setState(() => loading = false);
|
setState(() => loading = false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ class ShowTokenModal extends StatelessWidget {
|
|||||||
text: alertText,
|
text: alertText,
|
||||||
icon: null,
|
icon: null,
|
||||||
mode: BadgeMode.info,
|
mode: BadgeMode.info,
|
||||||
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
if (this.account.userID != null)
|
if (this.account.userID != null)
|
||||||
|
|||||||
@@ -144,7 +144,19 @@ class _ChannelRootPageState extends State<ChannelRootPage> with RouteAware {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: RefreshIndicator(
|
body: _buildList(context),
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
heroTag: 'fab_channel_list_qr',
|
||||||
|
onPressed: () {
|
||||||
|
Navi.push(context, () => ChannelScannerPage());
|
||||||
|
},
|
||||||
|
child: const Icon(FontAwesomeIcons.qrcode),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildList(BuildContext context) {
|
||||||
|
return RefreshIndicator(
|
||||||
onRefresh: () => Future.sync(
|
onRefresh: () => Future.sync(
|
||||||
() => _pagingController.refresh(),
|
() => _pagingController.refresh(),
|
||||||
),
|
),
|
||||||
@@ -165,14 +177,6 @@ class _ChannelRootPageState extends State<ChannelRootPage> with RouteAware {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
floatingActionButton: FloatingActionButton(
|
|
||||||
heroTag: 'fab_channel_list_qr',
|
|
||||||
onPressed: () {
|
|
||||||
Navi.push(context, () => ChannelScannerPage());
|
|
||||||
},
|
|
||||||
child: const Icon(FontAwesomeIcons.qrcode),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/badge_display/badge_display.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/channel.dart';
|
import 'package:simplecloudnotifier/models/channel.dart';
|
||||||
import 'package:simplecloudnotifier/pages/channel_scanner/channel_scanner.dart';
|
import 'package:simplecloudnotifier/pages/channel_scanner/channel_scanner.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_settings.dart';
|
||||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_auth.dart';
|
import 'package:simplecloudnotifier/state/app_auth.dart';
|
||||||
import 'package:simplecloudnotifier/pages/channel_list/channel_list_item.dart';
|
import 'package:simplecloudnotifier/pages/channel_list/channel_list_item.dart';
|
||||||
@@ -121,7 +123,35 @@ class _ChannelListExtendedPageState extends State<ChannelListExtendedPage> with
|
|||||||
showShare: false,
|
showShare: false,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||||
child: RefreshIndicator(
|
child: Column(
|
||||||
|
children: [
|
||||||
|
BadgeDisplay(
|
||||||
|
text: "All channels accessible from this account\n(Your own channels and subscribed channels)",
|
||||||
|
icon: null,
|
||||||
|
mode: BadgeMode.info,
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
closable: true,
|
||||||
|
extraPadding: EdgeInsets.fromLTRB(0, 0, 0, 16),
|
||||||
|
hidden: !AppSettings().showInfoAlerts,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _buildList(context),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
heroTag: 'fab_channel_list_extended-plus',
|
||||||
|
onPressed: () {
|
||||||
|
Navi.push(context, () => ChannelScannerPage());
|
||||||
|
},
|
||||||
|
child: const Icon(FontAwesomeIcons.plus),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildList(BuildContext context) {
|
||||||
|
return RefreshIndicator(
|
||||||
onRefresh: () => Future.sync(
|
onRefresh: () => Future.sync(
|
||||||
() => _pagingController.refresh(),
|
() => _pagingController.refresh(),
|
||||||
),
|
),
|
||||||
@@ -142,15 +172,6 @@ class _ChannelListExtendedPageState extends State<ChannelListExtendedPage> with
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
floatingActionButton: FloatingActionButton(
|
|
||||||
heroTag: 'fab_channel_list_extended-plus',
|
|
||||||
onPressed: () {
|
|
||||||
Navi.push(context, () => ChannelScannerPage());
|
|
||||||
},
|
|
||||||
child: const Icon(FontAwesomeIcons.plus),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ 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:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/api/api_exception.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/models/subscription.dart';
|
import 'package:simplecloudnotifier/models/subscription.dart';
|
||||||
@@ -220,6 +221,9 @@ class _ChannelListItemState extends State<ChannelListItem> {
|
|||||||
} else {
|
} else {
|
||||||
Toaster.success("Success", 'Requested widget.subscription to channel');
|
Toaster.success("Success", 'Requested widget.subscription to channel');
|
||||||
}
|
}
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to subscribe to channel');
|
Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
@@ -238,6 +242,9 @@ class _ChannelListItemState extends State<ChannelListItem> {
|
|||||||
widget.onSubscriptionChanged.call(sub.channelID, null);
|
widget.onSubscriptionChanged.call(sub.channelID, null);
|
||||||
|
|
||||||
Toaster.success("Success", 'Unsubscribed from channel');
|
Toaster.success("Success", 'Unsubscribed from channel');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
@@ -256,6 +263,9 @@ class _ChannelListItemState extends State<ChannelListItem> {
|
|||||||
widget.onSubscriptionChanged.call(sub.channelID, newSub);
|
widget.onSubscriptionChanged.call(sub.channelID, newSub);
|
||||||
|
|
||||||
Toaster.success("Success", 'Unsubscribed from channel');
|
Toaster.success("Success", 'Unsubscribed from channel');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
@@ -274,6 +284,9 @@ class _ChannelListItemState extends State<ChannelListItem> {
|
|||||||
widget.onSubscriptionChanged.call(sub.channelID, newSub);
|
widget.onSubscriptionChanged.call(sub.channelID, newSub);
|
||||||
|
|
||||||
Toaster.success("Success", 'Subscribed to channel');
|
Toaster.success("Success", 'Subscribed to channel');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to subscribe to channel');
|
Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|||||||
import 'package:qr_flutter/qr_flutter.dart';
|
import 'package:qr_flutter/qr_flutter.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/api/api_exception.dart';
|
||||||
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/channel.dart';
|
import 'package:simplecloudnotifier/models/channel.dart';
|
||||||
@@ -305,7 +306,7 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
icon: FontAwesomeIcons.solidEnvelope,
|
icon: FontAwesomeIcons.solidEnvelope,
|
||||||
title: 'Messages',
|
title: 'Messages',
|
||||||
values: [channel.messagesSent.toString()],
|
values: [channel.messagesSent.toString()],
|
||||||
mainAction: (subscription != null && subscription!.confirmed) ? () => Navi.push(context, () => FilteredMessageViewPage(title: channel.displayName, filter: MessageFilter(channelIDs: [channel.channelID]))) : null,
|
mainAction: (subscription != null && subscription!.confirmed) ? () => Navi.push(context, () => FilteredMessageViewPage(title: channel.displayName, alertText: null, filter: MessageFilter(channelIDs: [channel.channelID]))) : null,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -537,6 +538,9 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
widget.needsReload?.call();
|
widget.needsReload?.call();
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
ApplicationLog.error('Failed to save DisplayName: ' + exc.toString(), trace: trace);
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to save DisplayName');
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
ApplicationLog.error('Failed to save DisplayName: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to save DisplayName: ' + exc.toString(), trace: trace);
|
||||||
Toaster.error("Error", 'Failed to save DisplayName');
|
Toaster.error("Error", 'Failed to save DisplayName');
|
||||||
@@ -569,9 +573,12 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
widget.needsReload?.call();
|
widget.needsReload?.call();
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
ApplicationLog.error('Failed to save DescriptionName: ' + exc.toString(), trace: trace);
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to save description');
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
ApplicationLog.error('Failed to save DescriptionName: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to save DescriptionName: ' + exc.toString(), trace: trace);
|
||||||
Toaster.error("Error", 'Failed to save DescriptionName');
|
Toaster.error("Error", 'Failed to save description');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -589,6 +596,9 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
} else {
|
} else {
|
||||||
Toaster.success("Success", 'Requested subscription to channel');
|
Toaster.success("Success", 'Requested subscription to channel');
|
||||||
}
|
}
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to subscribe to channel');
|
Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
@@ -612,6 +622,9 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
await _initStateAsync(false);
|
await _initStateAsync(false);
|
||||||
|
|
||||||
Toaster.success("Success", 'Unsubscribed from channel');
|
Toaster.success("Success", 'Unsubscribed from channel');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
@@ -630,6 +643,9 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
await _initStateAsync(false);
|
await _initStateAsync(false);
|
||||||
|
|
||||||
Toaster.success("Success", 'Unsubscribed from channel');
|
Toaster.success("Success", 'Unsubscribed from channel');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
@@ -648,6 +664,9 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
await _initStateAsync(false);
|
await _initStateAsync(false);
|
||||||
|
|
||||||
Toaster.success("Success", 'Subscribed to channel');
|
Toaster.success("Success", 'Subscribed to channel');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to subscribe to channel');
|
Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
@@ -664,6 +683,9 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
await _initStateAsync(false);
|
await _initStateAsync(false);
|
||||||
|
|
||||||
Toaster.success("Success", 'Subscription succesfully revoked');
|
Toaster.success("Success", 'Subscription succesfully revoked');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to revoke subscription');
|
||||||
|
ApplicationLog.error('Failed to revoke subscription: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to revoke subscription');
|
Toaster.error("Error", 'Failed to revoke subscription');
|
||||||
ApplicationLog.error('Failed to revoke subscription: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to revoke subscription: ' + exc.toString(), trace: trace);
|
||||||
@@ -680,6 +702,9 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
await _initStateAsync(false);
|
await _initStateAsync(false);
|
||||||
|
|
||||||
Toaster.success("Success", 'Subscription succesfully confirmed');
|
Toaster.success("Success", 'Subscription succesfully confirmed');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to confirm subscription');
|
||||||
|
ApplicationLog.error('Failed to confirm subscription: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to confirm subscription');
|
Toaster.error("Error", 'Failed to confirm subscription');
|
||||||
ApplicationLog.error('Failed to confirm subscription: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to confirm subscription: ' + exc.toString(), trace: trace);
|
||||||
@@ -696,6 +721,9 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
await _initStateAsync(false);
|
await _initStateAsync(false);
|
||||||
|
|
||||||
Toaster.success("Success", 'Subscription request succesfully denied');
|
Toaster.success("Success", 'Subscription request succesfully denied');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to deny subscription');
|
||||||
|
ApplicationLog.error('Failed to deny subscription: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to deny subscription');
|
Toaster.error("Error", 'Failed to deny subscription');
|
||||||
ApplicationLog.error('Failed to deny subscription: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to deny subscription: ' + exc.toString(), trace: trace);
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/badge_display/badge_display.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/client.dart';
|
import 'package:simplecloudnotifier/models/client.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_settings.dart';
|
||||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_auth.dart';
|
import 'package:simplecloudnotifier/state/app_auth.dart';
|
||||||
import 'package:simplecloudnotifier/pages/client_list/client_list_item.dart';
|
import 'package:simplecloudnotifier/pages/client_list/client_list_item.dart';
|
||||||
@@ -69,7 +71,28 @@ class _ClientListPageState extends State<ClientListPage> {
|
|||||||
showShare: false,
|
showShare: false,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||||
child: RefreshIndicator(
|
child: Column(
|
||||||
|
children: [
|
||||||
|
BadgeDisplay(
|
||||||
|
text: "All clients connected with this account",
|
||||||
|
icon: null,
|
||||||
|
mode: BadgeMode.info,
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
closable: true,
|
||||||
|
extraPadding: EdgeInsets.fromLTRB(0, 0, 0, 16),
|
||||||
|
hidden: !AppSettings().showInfoAlerts,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _buildList(context),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildList(BuildContext context) {
|
||||||
|
return RefreshIndicator(
|
||||||
onRefresh: () => Future.sync(
|
onRefresh: () => Future.sync(
|
||||||
() => _pagingController.refresh(),
|
() => _pagingController.refresh(),
|
||||||
),
|
),
|
||||||
@@ -79,8 +102,6 @@ class _ClientListPageState extends State<ClientListPage> {
|
|||||||
itemBuilder: (context, item, index) => ClientListItem(item: item),
|
itemBuilder: (context, item, index) => ClientListItem(item: item),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/badge_display/badge_display.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.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';
|
||||||
@@ -17,11 +18,13 @@ class FilteredMessageViewPage extends StatefulWidget {
|
|||||||
const FilteredMessageViewPage({
|
const FilteredMessageViewPage({
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.filter,
|
required this.filter,
|
||||||
|
required this.alertText,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
final MessageFilter filter;
|
final MessageFilter filter;
|
||||||
|
final String? alertText;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<FilteredMessageViewPage> createState() => _FilteredMessageViewPageState();
|
State<FilteredMessageViewPage> createState() => _FilteredMessageViewPageState();
|
||||||
@@ -87,18 +90,40 @@ class _FilteredMessageViewPageState extends State<FilteredMessageViewPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
Widget child = _buildMessageList(context);
|
||||||
|
|
||||||
|
if (widget.alertText != null) {
|
||||||
|
child = Column(
|
||||||
|
children: [
|
||||||
|
BadgeDisplay(
|
||||||
|
text: widget.alertText!,
|
||||||
|
icon: null,
|
||||||
|
mode: BadgeMode.info,
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
closable: true,
|
||||||
|
extraPadding: EdgeInsets.fromLTRB(0, 0, 0, 16),
|
||||||
|
hidden: !AppSettings().showInfoAlerts,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: child,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return SCNScaffold(
|
return SCNScaffold(
|
||||||
title: this.widget.title,
|
title: this.widget.title,
|
||||||
showSearch: false,
|
showSearch: false,
|
||||||
showShare: false,
|
showShare: false,
|
||||||
child: _buildMessageList(context),
|
child: Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMessageList(BuildContext context) {
|
Widget _buildMessageList(BuildContext context) {
|
||||||
return Padding(
|
return RefreshIndicator(
|
||||||
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
|
||||||
child: RefreshIndicator(
|
|
||||||
onRefresh: () => Future.sync(
|
onRefresh: () => Future.sync(
|
||||||
() => _pagingController.refresh(),
|
() => _pagingController.refresh(),
|
||||||
),
|
),
|
||||||
@@ -114,7 +139,6 @@ class _FilteredMessageViewPageState extends State<FilteredMessageViewPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/badge_display/badge_display.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/keytoken.dart';
|
import 'package:simplecloudnotifier/models/keytoken.dart';
|
||||||
import 'package:simplecloudnotifier/pages/keytoken_list/keytoken_create_modal.dart';
|
import 'package:simplecloudnotifier/pages/keytoken_list/keytoken_create_modal.dart';
|
||||||
import 'package:simplecloudnotifier/pages/keytoken_list/keytoken_created_modal.dart';
|
import 'package:simplecloudnotifier/pages/keytoken_list/keytoken_created_modal.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_settings.dart';
|
||||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_auth.dart';
|
import 'package:simplecloudnotifier/state/app_auth.dart';
|
||||||
import 'package:simplecloudnotifier/pages/keytoken_list/keytoken_list_item.dart';
|
import 'package:simplecloudnotifier/pages/keytoken_list/keytoken_list_item.dart';
|
||||||
@@ -72,16 +74,21 @@ class _KeyTokenListPageState extends State<KeyTokenListPage> {
|
|||||||
showShare: false,
|
showShare: false,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||||
child: RefreshIndicator(
|
child: Column(
|
||||||
onRefresh: () => Future.sync(
|
children: [
|
||||||
() => _pagingController.refresh(),
|
BadgeDisplay(
|
||||||
),
|
text: "These are your keys.\nKeys can be used to send messages and access your account.\n\nKeys can have different sets of permissions, the Admin-Key has full-access to your account",
|
||||||
child: PagedListView<int, KeyToken>(
|
icon: null,
|
||||||
pagingController: _pagingController,
|
mode: BadgeMode.info,
|
||||||
builderDelegate: PagedChildBuilderDelegate<KeyToken>(
|
textAlign: TextAlign.left,
|
||||||
itemBuilder: (context, item, index) => KeyTokenListItem(item: item, needsReload: _fullRefresh),
|
closable: true,
|
||||||
),
|
extraPadding: EdgeInsets.fromLTRB(0, 0, 0, 16),
|
||||||
|
hidden: !AppSettings().showInfoAlerts,
|
||||||
),
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _buildList(context),
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
@@ -97,6 +104,20 @@ class _KeyTokenListPageState extends State<KeyTokenListPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildList(BuildContext context) {
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () => Future.sync(
|
||||||
|
() => _pagingController.refresh(),
|
||||||
|
),
|
||||||
|
child: PagedListView<int, KeyToken>(
|
||||||
|
pagingController: _pagingController,
|
||||||
|
builderDelegate: PagedChildBuilderDelegate<KeyToken>(
|
||||||
|
itemBuilder: (context, item, index) => KeyTokenListItem(item: item, needsReload: _fullRefresh),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _created(KeyToken token, String tokValue) {
|
void _created(KeyToken token, String tokValue) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_pagingController.itemList?.insert(0, token);
|
_pagingController.itemList?.insert(0, token);
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ class KeyTokenListItem extends StatelessWidget {
|
|||||||
SizedBox(width: 4),
|
SizedBox(width: 4),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navi.push(context, () => FilteredMessageViewPage(title: item.name, filter: MessageFilter(usedKeys: [item.keytokenID])));
|
Navi.push(context, () => FilteredMessageViewPage(title: item.name, alertText: 'All message sent with the key \'${item.name}\'', filter: MessageFilter(usedKeys: [item.keytokenID])));
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
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:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/api/api_exception.dart';
|
||||||
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/channel.dart';
|
import 'package:simplecloudnotifier/models/channel.dart';
|
||||||
@@ -230,7 +231,7 @@ class _KeyTokenViewPageState extends State<KeyTokenViewPage> {
|
|||||||
title: 'Messages',
|
title: 'Messages',
|
||||||
values: [keytoken.messagesSent.toString()],
|
values: [keytoken.messagesSent.toString()],
|
||||||
mainAction: () {
|
mainAction: () {
|
||||||
Navi.push(context, () => FilteredMessageViewPage(title: keytoken.name, filter: MessageFilter(usedKeys: [keytoken.keytokenID])));
|
Navi.push(context, () => FilteredMessageViewPage(title: keytoken.name, alertText: 'All message sent with the key \'${keytoken.name}\'', filter: MessageFilter(usedKeys: [keytoken.keytokenID])));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
..._buildPermissionCard(context, true, keytoken.toPreview()),
|
..._buildPermissionCard(context, true, keytoken.toPreview()),
|
||||||
@@ -543,6 +544,9 @@ class _KeyTokenViewPageState extends State<KeyTokenViewPage> {
|
|||||||
Toaster.info('Logout', 'Successfully deleted the key');
|
Toaster.info('Logout', 'Successfully deleted the key');
|
||||||
|
|
||||||
Navi.pop(context);
|
Navi.pop(context);
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
ApplicationLog.error('Failed to delete key: ' + exc.toString(), trace: trace);
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to delete key');
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to delete key');
|
Toaster.error("Error", 'Failed to delete key');
|
||||||
ApplicationLog.error('Failed to delete key: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to delete key: ' + exc.toString(), trace: trace);
|
||||||
@@ -563,6 +567,9 @@ class _KeyTokenViewPageState extends State<KeyTokenViewPage> {
|
|||||||
Toaster.info("Success", "Key updated");
|
Toaster.info("Success", "Key updated");
|
||||||
|
|
||||||
widget.needsReload?.call();
|
widget.needsReload?.call();
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
ApplicationLog.error('Failed to update key: ' + exc.toString(), trace: trace);
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to update key');
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
ApplicationLog.error('Failed to update key: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to update key: ' + exc.toString(), trace: trace);
|
||||||
Toaster.error("Error", 'Failed to update key');
|
Toaster.error("Error", 'Failed to update key');
|
||||||
@@ -583,6 +590,9 @@ class _KeyTokenViewPageState extends State<KeyTokenViewPage> {
|
|||||||
Toaster.info("Success", "Key updated");
|
Toaster.info("Success", "Key updated");
|
||||||
|
|
||||||
widget.needsReload?.call();
|
widget.needsReload?.call();
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
ApplicationLog.error('Failed to update key: ' + exc.toString(), trace: trace);
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to update key');
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
ApplicationLog.error('Failed to update key: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to update key: ' + exc.toString(), trace: trace);
|
||||||
Toaster.error("Error", 'Failed to update key');
|
Toaster.error("Error", 'Failed to update key');
|
||||||
@@ -603,6 +613,9 @@ class _KeyTokenViewPageState extends State<KeyTokenViewPage> {
|
|||||||
Toaster.info("Success", "Key updated");
|
Toaster.info("Success", "Key updated");
|
||||||
|
|
||||||
widget.needsReload?.call();
|
widget.needsReload?.call();
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
ApplicationLog.error('Failed to update key: ' + exc.toString(), trace: trace);
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to update key');
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
ApplicationLog.error('Failed to update key: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to update key: ' + exc.toString(), trace: trace);
|
||||||
Toaster.error("Error", 'Failed to update key');
|
Toaster.error("Error", 'Failed to update key');
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
title: 'Sender',
|
title: 'Sender',
|
||||||
values: [message.senderName!],
|
values: [message.senderName!],
|
||||||
mainAction: () => {
|
mainAction: () => {
|
||||||
Navi.push(context, () => FilteredMessageViewPage(title: message.senderName!, filter: MessageFilter(senderNames: [message.senderName!])))
|
Navi.push(context, () => FilteredMessageViewPage(title: message.senderName!, alertText: 'All message sent from \'${message.senderName!}\'', filter: MessageFilter(senderNames: [message.senderName!])))
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
UI.metaCard(
|
UI.metaCard(
|
||||||
@@ -169,7 +169,7 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
if (message.senderUserID == userAccUserID) {
|
if (message.senderUserID == userAccUserID) {
|
||||||
Navi.push(context, () => KeyTokenViewPage(keytokenID: message.usedKeyID, preloadedData: null, needsReload: null));
|
Navi.push(context, () => KeyTokenViewPage(keytokenID: message.usedKeyID, preloadedData: null, needsReload: null));
|
||||||
} else {
|
} else {
|
||||||
Navi.push(context, () => FilteredMessageViewPage(title: token?.name ?? message.usedKeyID, filter: MessageFilter(usedKeys: [message.usedKeyID])));
|
Navi.push(context, () => FilteredMessageViewPage(title: token?.name ?? message.usedKeyID, alertText: 'All message sent with the specified key', filter: MessageFilter(usedKeys: [message.usedKeyID])));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -201,14 +201,14 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
icon: FontAwesomeIcons.solidUser,
|
icon: FontAwesomeIcons.solidUser,
|
||||||
title: 'User',
|
title: 'User',
|
||||||
values: [user?.userID ?? message.senderUserID, user?.username ?? ''],
|
values: [user?.userID ?? message.senderUserID, user?.username ?? ''],
|
||||||
mainAction: () => Navi.push(context, () => FilteredMessageViewPage(title: user?.username ?? message.senderUserID, filter: MessageFilter(senderUserID: [message.senderUserID]))),
|
mainAction: () => Navi.push(context, () => FilteredMessageViewPage(title: user?.username ?? message.senderUserID, alertText: 'All message sent by the specified account', filter: MessageFilter(senderUserID: [message.senderUserID]))),
|
||||||
),
|
),
|
||||||
UI.metaCard(
|
UI.metaCard(
|
||||||
context: context,
|
context: context,
|
||||||
icon: FontAwesomeIcons.solidBolt,
|
icon: FontAwesomeIcons.solidBolt,
|
||||||
title: 'Priority',
|
title: 'Priority',
|
||||||
values: [_prettyPrintPriority(message.priority)],
|
values: [_prettyPrintPriority(message.priority)],
|
||||||
mainAction: () => Navi.push(context, () => FilteredMessageViewPage(title: "Priority ${message.priority}", filter: MessageFilter(priority: [message.priority]))),
|
mainAction: () => Navi.push(context, () => FilteredMessageViewPage(title: "Priority ${message.priority}", alertText: 'All message sent with priority ${message.priority}', filter: MessageFilter(priority: [message.priority]))),
|
||||||
),
|
),
|
||||||
if (message.senderUserID == userAccUserID)
|
if (message.senderUserID == userAccUserID)
|
||||||
UI.button(
|
UI.button(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
import 'package:qr_flutter/qr_flutter.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/api/api_exception.dart';
|
||||||
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
||||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
import 'package:simplecloudnotifier/state/globals.dart';
|
import 'package:simplecloudnotifier/state/globals.dart';
|
||||||
@@ -289,6 +290,9 @@ class _SendRootPageState extends State<SendRootPage> {
|
|||||||
_msgTitle.clear();
|
_msgTitle.clear();
|
||||||
_msgContent.clear();
|
_msgContent.clear();
|
||||||
});
|
});
|
||||||
|
} on APIException catch (e, stackTrace) {
|
||||||
|
if (!e.toastShown) Toaster.error("Error", 'Failed to send message: ${e.toString()}');
|
||||||
|
ApplicationLog.error('Failed to send message', trace: stackTrace);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
Toaster.error("Error", 'Failed to send message: ${e.toString()}');
|
Toaster.error("Error", 'Failed to send message: ${e.toString()}');
|
||||||
ApplicationLog.error('Failed to send message', trace: stackTrace);
|
ApplicationLog.error('Failed to send message', trace: stackTrace);
|
||||||
@@ -308,6 +312,9 @@ class _SendRootPageState extends State<SendRootPage> {
|
|||||||
_msgTitle.clear();
|
_msgTitle.clear();
|
||||||
_msgContent.clear();
|
_msgContent.clear();
|
||||||
});
|
});
|
||||||
|
} on APIException catch (e, stackTrace) {
|
||||||
|
if (!e.toastShown) Toaster.error("Error", 'Failed to send message: ${e.toString()}');
|
||||||
|
ApplicationLog.error('Failed to send message', trace: stackTrace);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
Toaster.error("Error", 'Failed to send message: ${e.toString()}');
|
Toaster.error("Error", 'Failed to send message: ${e.toString()}');
|
||||||
ApplicationLog.error('Failed to send message', trace: stackTrace);
|
ApplicationLog.error('Failed to send message', trace: stackTrace);
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/badge_display/badge_display.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/sender_name_statistics.dart';
|
import 'package:simplecloudnotifier/models/sender_name_statistics.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_settings.dart';
|
||||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_auth.dart';
|
import 'package:simplecloudnotifier/state/app_auth.dart';
|
||||||
import 'package:simplecloudnotifier/pages/sender_list/sender_list_item.dart';
|
import 'package:simplecloudnotifier/pages/sender_list/sender_list_item.dart';
|
||||||
@@ -69,7 +71,28 @@ class _SenderListPageState extends State<SenderListPage> {
|
|||||||
showShare: false,
|
showShare: false,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||||
child: RefreshIndicator(
|
child: Column(
|
||||||
|
children: [
|
||||||
|
BadgeDisplay(
|
||||||
|
text: "All sender used to send messages to this account",
|
||||||
|
icon: null,
|
||||||
|
mode: BadgeMode.info,
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
closable: true,
|
||||||
|
extraPadding: EdgeInsets.fromLTRB(0, 0, 0, 16),
|
||||||
|
hidden: !AppSettings().showInfoAlerts,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _buildList(context),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildList(BuildContext context) {
|
||||||
|
return RefreshIndicator(
|
||||||
onRefresh: () => Future.sync(
|
onRefresh: () => Future.sync(
|
||||||
() => _pagingController.refresh(),
|
() => _pagingController.refresh(),
|
||||||
),
|
),
|
||||||
@@ -79,8 +102,6 @@ class _SenderListPageState extends State<SenderListPage> {
|
|||||||
itemBuilder: (context, item, index) => SenderListItem(item: item),
|
itemBuilder: (context, item, index) => SenderListItem(item: item),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class SenderListItem extends StatelessWidget {
|
|||||||
color: Theme.of(context).cardTheme.color,
|
color: Theme.of(context).cardTheme.color,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navi.push(context, () => FilteredMessageViewPage(title: item.name, filter: MessageFilter(senderNames: [item.name])));
|
Navi.push(context, () => FilteredMessageViewPage(title: item.name, alertText: 'All message sent from \'${item.name!}\'', filter: MessageFilter(senderNames: [item.name])));
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
@@ -71,7 +71,7 @@ class SenderListItem extends StatelessWidget {
|
|||||||
SizedBox(width: 4),
|
SizedBox(width: 4),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navi.push(context, () => FilteredMessageViewPage(title: item.name, filter: MessageFilter(senderNames: [item.name])));
|
Navi.push(context, () => FilteredMessageViewPage(title: item.name, alertText: 'All message sent from \'${item.name!}\'', filter: MessageFilter(senderNames: [item.name])));
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
|
|||||||
@@ -146,6 +146,12 @@ class _SettingsRootPageState extends State<SettingsRootPage> {
|
|||||||
title: Text('Refresh messages on app resume'),
|
title: Text('Refresh messages on app resume'),
|
||||||
onToggle: (value) => AppSettings().update((p) => p.alwaysBackgroundRefreshMessageListOnLifecycleResume = !p.alwaysBackgroundRefreshMessageListOnLifecycleResume),
|
onToggle: (value) => AppSettings().update((p) => p.alwaysBackgroundRefreshMessageListOnLifecycleResume = !p.alwaysBackgroundRefreshMessageListOnLifecycleResume),
|
||||||
),
|
),
|
||||||
|
SettingsTile.switchTile(
|
||||||
|
initialValue: cfg.showInfoAlerts,
|
||||||
|
leading: Icon(FontAwesomeIcons.solidCircleInfo),
|
||||||
|
title: Text('Show various helpful info boxes'),
|
||||||
|
onToggle: (value) => AppSettings().update((p) => p.showInfoAlerts = !p.showInfoAlerts),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SettingsSection(
|
SettingsSection(
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/components/badge_display/badge_display.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/channel.dart';
|
import 'package:simplecloudnotifier/models/channel.dart';
|
||||||
import 'package:simplecloudnotifier/models/subscription.dart';
|
import 'package:simplecloudnotifier/models/subscription.dart';
|
||||||
import 'package:simplecloudnotifier/models/user.dart';
|
import 'package:simplecloudnotifier/models/user.dart';
|
||||||
|
import 'package:simplecloudnotifier/state/app_settings.dart';
|
||||||
import 'package:simplecloudnotifier/state/application_log.dart';
|
import 'package:simplecloudnotifier/state/application_log.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_auth.dart';
|
import 'package:simplecloudnotifier/state/app_auth.dart';
|
||||||
import 'package:simplecloudnotifier/pages/subscription_list/subscription_list_item.dart';
|
import 'package:simplecloudnotifier/pages/subscription_list/subscription_list_item.dart';
|
||||||
@@ -93,7 +95,28 @@ class _SubscriptionListPageState extends State<SubscriptionListPage> {
|
|||||||
showShare: false,
|
showShare: false,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||||
child: RefreshIndicator(
|
child: Column(
|
||||||
|
children: [
|
||||||
|
BadgeDisplay(
|
||||||
|
text: "These are subscriptions to individual Channels\n\nThey contain to your own channels, subscriptions to foreign channels and subscriptions of other users to your channels (active and requested).",
|
||||||
|
icon: null,
|
||||||
|
mode: BadgeMode.info,
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
closable: true,
|
||||||
|
extraPadding: EdgeInsets.fromLTRB(0, 0, 0, 16),
|
||||||
|
hidden: !AppSettings().showInfoAlerts,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _buildList(context),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildList(BuildContext context) {
|
||||||
|
return RefreshIndicator(
|
||||||
onRefresh: () => Future.sync(
|
onRefresh: () => Future.sync(
|
||||||
() => _pagingController.refresh(),
|
() => _pagingController.refresh(),
|
||||||
),
|
),
|
||||||
@@ -103,8 +126,6 @@ class _SubscriptionListPageState extends State<SubscriptionListPage> {
|
|||||||
itemBuilder: (context, item, index) => SubscriptionListItem(item: item, userCache: userCache, channelCache: channelCache, needsReload: fullRefresh),
|
itemBuilder: (context, item, index) => SubscriptionListItem(item: item, userCache: userCache, channelCache: channelCache, needsReload: fullRefresh),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
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:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
|
import 'package:simplecloudnotifier/api/api_exception.dart';
|
||||||
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
import 'package:simplecloudnotifier/components/error_display/error_display.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/channel.dart';
|
import 'package:simplecloudnotifier/models/channel.dart';
|
||||||
@@ -407,6 +408,9 @@ class _SubscriptionViewPageState extends State<SubscriptionViewPage> {
|
|||||||
await _initStateAsync(false);
|
await _initStateAsync(false);
|
||||||
|
|
||||||
Toaster.success("Success", 'Subscription succesfully confirmed');
|
Toaster.success("Success", 'Subscription succesfully confirmed');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to confirm subscription');
|
||||||
|
ApplicationLog.error('Failed to confirm subscription: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to confirm subscription');
|
Toaster.error("Error", 'Failed to confirm subscription');
|
||||||
ApplicationLog.error('Failed to confirm subscription: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to confirm subscription: ' + exc.toString(), trace: trace);
|
||||||
@@ -429,6 +433,9 @@ class _SubscriptionViewPageState extends State<SubscriptionViewPage> {
|
|||||||
|
|
||||||
Toaster.success("Success", 'Unsubscribed from channel');
|
Toaster.success("Success", 'Unsubscribed from channel');
|
||||||
Navi.pop(context);
|
Navi.pop(context);
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
@@ -447,6 +454,9 @@ class _SubscriptionViewPageState extends State<SubscriptionViewPage> {
|
|||||||
await _initStateAsync(false);
|
await _initStateAsync(false);
|
||||||
|
|
||||||
Toaster.success("Success", 'Unsubscribed from channel');
|
Toaster.success("Success", 'Unsubscribed from channel');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
Toaster.error("Error", 'Failed to unsubscribe from channel');
|
||||||
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to unsubscribe from channel: ' + exc.toString(), trace: trace);
|
||||||
@@ -465,6 +475,9 @@ class _SubscriptionViewPageState extends State<SubscriptionViewPage> {
|
|||||||
await _initStateAsync(false);
|
await _initStateAsync(false);
|
||||||
|
|
||||||
Toaster.success("Success", 'Subscribed to channel');
|
Toaster.success("Success", 'Subscribed to channel');
|
||||||
|
} on APIException catch (exc, trace) {
|
||||||
|
if (!exc.toastShown) Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
} catch (exc, trace) {
|
} catch (exc, trace) {
|
||||||
Toaster.error("Error", 'Failed to subscribe to channel');
|
Toaster.error("Error", 'Failed to subscribe to channel');
|
||||||
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
ApplicationLog.error('Failed to subscribe to channel: ' + exc.toString(), trace: trace);
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ class AppSettings extends ChangeNotifier {
|
|||||||
bool alwaysBackgroundRefreshMessageListOnLifecycleResume = true;
|
bool alwaysBackgroundRefreshMessageListOnLifecycleResume = true;
|
||||||
AppSettingsDateFormat dateFormat = AppSettingsDateFormat.ISO;
|
AppSettingsDateFormat dateFormat = AppSettingsDateFormat.ISO;
|
||||||
int messagePreviewLength = 3;
|
int messagePreviewLength = 3;
|
||||||
|
bool showInfoAlerts = true;
|
||||||
|
|
||||||
AppNotificationSettings notification0 = AppNotificationSettings();
|
AppNotificationSettings notification0 = AppNotificationSettings();
|
||||||
AppNotificationSettings notification1 = AppNotificationSettings();
|
AppNotificationSettings notification1 = AppNotificationSettings();
|
||||||
@@ -80,6 +81,7 @@ class AppSettings extends ChangeNotifier {
|
|||||||
alwaysBackgroundRefreshMessageListOnLifecycleResume = true;
|
alwaysBackgroundRefreshMessageListOnLifecycleResume = true;
|
||||||
dateFormat = AppSettingsDateFormat.ISO;
|
dateFormat = AppSettingsDateFormat.ISO;
|
||||||
messagePreviewLength = 3;
|
messagePreviewLength = 3;
|
||||||
|
showInfoAlerts = true;
|
||||||
|
|
||||||
notification0 = AppNotificationSettings();
|
notification0 = AppNotificationSettings();
|
||||||
notification1 = AppNotificationSettings();
|
notification1 = AppNotificationSettings();
|
||||||
@@ -97,6 +99,7 @@ class AppSettings extends ChangeNotifier {
|
|||||||
alwaysBackgroundRefreshMessageListOnLifecycleResume = Globals().sharedPrefs.getBool('settings.alwaysBackgroundRefreshMessageListOnLifecycleResume') ?? alwaysBackgroundRefreshMessageListOnLifecycleResume;
|
alwaysBackgroundRefreshMessageListOnLifecycleResume = Globals().sharedPrefs.getBool('settings.alwaysBackgroundRefreshMessageListOnLifecycleResume') ?? alwaysBackgroundRefreshMessageListOnLifecycleResume;
|
||||||
dateFormat = AppSettingsDateFormat.parse(Globals().sharedPrefs.getString('settings.dateFormat')) ?? dateFormat;
|
dateFormat = AppSettingsDateFormat.parse(Globals().sharedPrefs.getString('settings.dateFormat')) ?? dateFormat;
|
||||||
messagePreviewLength = Globals().sharedPrefs.getInt('settings.messagePreviewLength') ?? messagePreviewLength;
|
messagePreviewLength = Globals().sharedPrefs.getInt('settings.messagePreviewLength') ?? messagePreviewLength;
|
||||||
|
showInfoAlerts = Globals().sharedPrefs.getBool('settings.showInfoAlerts') ?? showInfoAlerts;
|
||||||
|
|
||||||
notification0 = AppNotificationSettings.load(Globals().sharedPrefs, 'settings.notification0');
|
notification0 = AppNotificationSettings.load(Globals().sharedPrefs, 'settings.notification0');
|
||||||
notification1 = AppNotificationSettings.load(Globals().sharedPrefs, 'settings.notification1');
|
notification1 = AppNotificationSettings.load(Globals().sharedPrefs, 'settings.notification1');
|
||||||
@@ -112,6 +115,7 @@ class AppSettings extends ChangeNotifier {
|
|||||||
await Globals().sharedPrefs.setBool('settings.alwaysBackgroundRefreshMessageListOnLifecycleResume', alwaysBackgroundRefreshMessageListOnLifecycleResume);
|
await Globals().sharedPrefs.setBool('settings.alwaysBackgroundRefreshMessageListOnLifecycleResume', alwaysBackgroundRefreshMessageListOnLifecycleResume);
|
||||||
await Globals().sharedPrefs.setString('settings.dateFormat', dateFormat.key);
|
await Globals().sharedPrefs.setString('settings.dateFormat', dateFormat.key);
|
||||||
await Globals().sharedPrefs.setInt('settings.messagePreviewLength', messagePreviewLength);
|
await Globals().sharedPrefs.setInt('settings.messagePreviewLength', messagePreviewLength);
|
||||||
|
await Globals().sharedPrefs.setBool('settings.showInfoAlerts', showInfoAlerts);
|
||||||
|
|
||||||
await notification0.save(Globals().sharedPrefs, 'settings.notification0');
|
await notification0.save(Globals().sharedPrefs, 'settings.notification0');
|
||||||
await notification1.save(Globals().sharedPrefs, 'settings.notification1');
|
await notification1.save(Globals().sharedPrefs, 'settings.notification1');
|
||||||
|
|||||||
Reference in New Issue
Block a user