Skip to content
Snippets Groups Projects
Commit 35ce8453 authored by vjrj's avatar vjrj
Browse files

More work with notifications

parent 86c3c28e
No related branches found
No related tags found
No related merge requests found
assets/img/animated-bell.gif

118 KiB

......@@ -131,5 +131,6 @@
"allow_notifications_btn": "ALLOW",
"deny_notifications_btn": "DENY",
"notification_new_payment_title": "New payment",
"notification_new_payment_desc": "You have received a new Ğ1 payment"
"notification_open": "OPEN",
"notification_new_payment_desc": "You have received a {amount} payment"
}
......@@ -131,5 +131,6 @@
"allow_notifications_btn": "PERMITIR",
"deny_notifications_btn": "DENEGAR",
"notification_new_payment_title": "Nuevo pago",
"notification_new_payment_desc": "Has recibido un nuevo pago de Junas"
"notification_new_payment_desc": "Has recibido un nuevo pago de {amount}",
"notification_open": "ABRIR"
}
......@@ -72,6 +72,7 @@ class TransactionsAndBalanceState extends Equatable {
required this.balance,
required this.lastChecked,
this.lastSent,
this.lastReceivedAmount,
this.lastReceived,
this.lastReceivedNotif});
......@@ -81,6 +82,7 @@ class TransactionsAndBalanceState extends Equatable {
final double balance;
final DateTime lastChecked;
final DateTime? lastSent;
final double? lastReceivedAmount;
final DateTime? lastReceived;
final DateTime? lastReceivedNotif;
......@@ -92,6 +94,7 @@ class TransactionsAndBalanceState extends Equatable {
balance,
lastChecked,
lastSent,
lastReceivedAmount,
lastReceived,
lastReceivedNotif
];
......
......@@ -163,6 +163,8 @@ abstract class _$TransactionsAndBalanceStateCWProxy {
TransactionsAndBalanceState lastSent(DateTime? lastSent);
TransactionsAndBalanceState lastReceivedAmount(double? lastReceivedAmount);
TransactionsAndBalanceState lastReceived(DateTime? lastReceived);
TransactionsAndBalanceState lastReceivedNotif(DateTime? lastReceivedNotif);
......@@ -178,6 +180,7 @@ abstract class _$TransactionsAndBalanceStateCWProxy {
double? balance,
DateTime? lastChecked,
DateTime? lastSent,
double? lastReceivedAmount,
DateTime? lastReceived,
DateTime? lastReceivedNotif,
});
......@@ -205,6 +208,10 @@ class _$TransactionsAndBalanceStateCWProxyImpl
TransactionsAndBalanceState lastSent(DateTime? lastSent) =>
this(lastSent: lastSent);
@override
TransactionsAndBalanceState lastReceivedAmount(double? lastReceivedAmount) =>
this(lastReceivedAmount: lastReceivedAmount);
@override
TransactionsAndBalanceState lastReceived(DateTime? lastReceived) =>
this(lastReceived: lastReceived);
......@@ -226,6 +233,7 @@ class _$TransactionsAndBalanceStateCWProxyImpl
Object? balance = const $CopyWithPlaceholder(),
Object? lastChecked = const $CopyWithPlaceholder(),
Object? lastSent = const $CopyWithPlaceholder(),
Object? lastReceivedAmount = const $CopyWithPlaceholder(),
Object? lastReceived = const $CopyWithPlaceholder(),
Object? lastReceivedNotif = const $CopyWithPlaceholder(),
}) {
......@@ -248,6 +256,10 @@ class _$TransactionsAndBalanceStateCWProxyImpl
? _value.lastSent
// ignore: cast_nullable_to_non_nullable
: lastSent as DateTime?,
lastReceivedAmount: lastReceivedAmount == const $CopyWithPlaceholder()
? _value.lastReceivedAmount
// ignore: cast_nullable_to_non_nullable
: lastReceivedAmount as double?,
lastReceived: lastReceived == const $CopyWithPlaceholder()
? _value.lastReceived
// ignore: cast_nullable_to_non_nullable
......@@ -317,6 +329,7 @@ TransactionsAndBalanceState _$TransactionsAndBalanceStateFromJson(
lastSent: json['lastSent'] == null
? null
: DateTime.parse(json['lastSent'] as String),
lastReceivedAmount: (json['lastReceivedAmount'] as num?)?.toDouble(),
lastReceived: json['lastReceived'] == null
? null
: DateTime.parse(json['lastReceived'] as String),
......@@ -332,6 +345,7 @@ Map<String, dynamic> _$TransactionsAndBalanceStateToJson(
'balance': instance.balance,
'lastChecked': instance.lastChecked.toIso8601String(),
'lastSent': instance.lastSent?.toIso8601String(),
'lastReceivedAmount': instance.lastReceivedAmount,
'lastReceived': instance.lastReceived?.toIso8601String(),
'lastReceivedNotif': instance.lastReceivedNotif?.toIso8601String(),
};
......@@ -45,12 +45,15 @@ class TransactionsCubit extends HydratedCubit<TransactionsAndBalanceState> {
final DateTime? lastReceived = state.lastReceived;
final DateTime lastReceivedNotification =
state.lastReceivedNotif ?? DateTime(1970);
final double? lastReceivedAmount = state.lastReceivedAmount;
// final DateTime? lastSent = transBalanceState.lastSent;
if (lastReceived != null &&
lastReceivedNotification.compareTo(lastReceived) == 1) {
// Notify
emit(state.copyWith(lastReceivedNotif: lastReceived));
NotificationController.createNewNotification(
lastReceived.millisecondsSinceEpoch.toString());
lastReceived.millisecondsSinceEpoch.toString(),
amount: lastReceivedAmount! / 100);
}
}
......
......@@ -91,9 +91,11 @@ TransactionsAndBalanceState transactionsGvaParser(Map<String, dynamic> txData) {
}
DateTime? lastSent;
DateTime? lastReceived;
double? lastReceivedAmount;
for (final Transaction tx in txs) {
if (tx.type == TransactionType.received && lastReceived == null) {
lastReceived = tx.time;
lastReceivedAmount = tx.amount;
}
if (tx.type == TransactionType.sent && lastSent == null) {
lastSent = tx.time;
......@@ -108,6 +110,7 @@ TransactionsAndBalanceState transactionsGvaParser(Map<String, dynamic> txData) {
balance: amount,
lastChecked: DateTime.now(),
lastReceived: lastReceived,
lastReceivedAmount: lastReceivedAmount,
lastSent: lastSent);
}
......
......@@ -298,7 +298,7 @@ class _GinkgoAppState extends State<GinkgoApp> {
}, duration: const Duration(minutes: 90));
Once.runCustom('fetch_transactions', callback: () {
fetchTransactions(context);
}, duration: const Duration(minutes: 10));
}, duration: const Duration(minutes: kReleaseMode ? 10 : 2));
fetchTransactions(context);
return ConnectivityAppWrapper(
app: FilesystemPickerDefaultOptions(
......@@ -329,6 +329,7 @@ class _GinkgoAppState extends State<GinkgoApp> {
? const SkeletonScreen()
: const AppIntro(),
builder: (BuildContext buildContext, Widget? widget) {
NotificationController.locale = context.locale;
return ResponsiveWrapper.builder(
BouncingScrollWrapper.builder(
context,
......
import 'package:awesome_notifications/awesome_notifications.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'config/theme.dart';
import 'main.dart';
import 'ui/logger.dart';
import 'ui/ui_helpers.dart';
// ignore: avoid_classes_with_only_static_members
/// *********************************************
......@@ -12,6 +15,7 @@ import 'ui/logger.dart';
///
class NotificationController {
static ReceivedAction? initialAction;
static Locale locale = const Locale('en', 'UK');
/// *********************************************
/// INITIALIZATIONS
......@@ -30,8 +34,8 @@ class NotificationController {
groupAlertBehavior: GroupAlertBehavior.Children,
importance: NotificationImportance.High,
defaultPrivacy: NotificationPrivacy.Private,
defaultColor: const Color(0xFF526600),
ledColor: const Color(0xFF526600))
defaultColor: lightColorScheme.primary,
ledColor: lightColorScheme.primary)
],
debug: true);
......@@ -91,7 +95,7 @@ class NotificationController {
children: <Widget>[
Expanded(
child: Image.asset(
'assets/animated-bell.gif',
'assets/img/animated-bell.gif',
height: MediaQuery.of(context).size.height * 0.3,
fit: BoxFit.fitWidth,
),
......@@ -115,17 +119,16 @@ class NotificationController {
?.copyWith(color: Colors.red),
)),
TextButton(
onPressed: () async {
userAuthorized = true;
Navigator.of(ctx).pop();
},
child: Text(
tr('allow_notifications_btn'),
onPressed: () async {
userAuthorized = true;
Navigator.of(ctx).pop();
},
child: Text(tr('allow_notifications_btn'),
style: Theme.of(context)
.textTheme
.titleLarge
?.copyWith(color: Colors.deepPurple),
)),
?.copyWith(color: lightColorScheme.primary)),
),
],
);
});
......@@ -137,41 +140,62 @@ class NotificationController {
/// NOTIFICATION CREATION METHODS
/// *********************************************
///
static Future<void> createNewNotification(String id) async {
bool isAllowed = await AwesomeNotifications().isNotificationAllowed();
if (!isAllowed) {
isAllowed = await displayNotificationRationale();
}
if (!isAllowed) {
return;
}
static Future<void> createNewNotification(String id,
{required double amount}) async {
final String title = tr('notification_new_payment_title');
final String desc = tr('notification_new_payment_desc',
namedArgs: <String, String>{
'amount': formatAmountWithLocale(locale.languageCode, amount)
});
if (kIsWeb) {
// dart:html cannot be used in Android
/* if (html.Notification.permission != 'granted') {
await html.Notification.requestPermission();
}
if (html.Notification.permission == 'granted') {
final html.Notification notification = html.Notification(
title, body: desc,
// icon:
);
// html.Notification.show();
} */
} else {
bool isAllowed = await AwesomeNotifications().isNotificationAllowed();
if (!isAllowed) {
isAllowed = await displayNotificationRationale();
}
if (!isAllowed) {
return;
}
await AwesomeNotifications().createNotification(
content: NotificationContent(
id: -1,
// -1 is replaced by a random number
channelKey: 'alerts',
title: tr('notification_new_payment_title'),
body: tr('notification_new_payment_desc'),
bigPicture:
'https://storage.googleapis.com/cms-storage-bucket/d406c736e7c4c57f5f61.png',
largeIcon: 'https://storage.googleapis.com/cms-storage-bucket/0dbfcc7a59cd1cf16282.png',
//'asset://assets/images/balloons-in-sky.jpg',
notificationLayout: NotificationLayout.BigPicture,
payload: <String, String>{'notificationId': id}),
actionButtons: <NotificationActionButton>[
NotificationActionButton(key: 'REDIRECT', label: 'Redirect'),
/* NotificationActionButton(
await AwesomeNotifications().createNotification(
content: NotificationContent(
id: -1,
// -1 is replaced by a random number
channelKey: 'alerts',
title: title,
body: desc,
largeIcon:
'https://git.duniter.org/vjrj/ginkgo/-/raw/master/assets/img/coin.png',
bigPicture: 'https://git.duniter.org/vjrj/ginkgo/-/raw/master/assets/img/gbrevedot_color.svg',
//'asset://assets/images/balloons-in-sky.jpg',
notificationLayout: NotificationLayout.BigPicture,
payload: <String, String>{'notificationId': id}),
actionButtons: <NotificationActionButton>[
NotificationActionButton(
key: 'notification_open', label: tr('notification_open')),
/* NotificationActionButton(
key: 'REPLY',
label: 'Reply Message',
requireInputText: true,
actionType: ActionType.SilentAction), */
NotificationActionButton(
/* NotificationActionButton(
key: 'DISMISS',
label: 'Dismiss',
actionType: ActionType.DismissAction,
isDangerousOption: true)
]);
isDangerousOption: true) */
]);
}
}
static Future<void> scheduleNewNotification() async {
......
......@@ -7,6 +7,7 @@ import 'package:share_plus/share_plus.dart';
import '../../data/models/app_cubit.dart';
import '../../data/models/app_state.dart';
import '../../data/models/node_type.dart';
import '../../notification_controller.dart';
import '../../shared_prefs.dart';
import '../ui_helpers.dart';
import '../widgets/bottom_widget.dart';
......@@ -25,8 +26,7 @@ class FifthScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<AppCubit, AppState>(
builder: (BuildContext context, AppState state) =>
Scaffold(
builder: (BuildContext context, AppState state) => Scaffold(
appBar: AppBar(title: Text(tr('bottom_nav_fifth'))),
drawer: const CardDrawer(),
body: ListView(
......@@ -43,6 +43,7 @@ class FifthScreen extends StatelessWidget {
),
onChanged: (Locale? newLocale) {
context.setLocale(newLocale!);
NotificationController.locale = newLocale;
},
items: const <DropdownMenuItem<Locale>>[
DropdownMenuItem<Locale>(
......@@ -89,9 +90,8 @@ class FifthScreen extends StatelessWidget {
GridItem(
title: 'share_your_key',
icon: Icons.share,
onTap: () =>
Share.share(
SharedPreferencesHelper().getPubKey())),
onTap: () => Share.share(
SharedPreferencesHelper().getPubKey())),
GridItem(
title: 'copy_your_key',
icon: Icons.copy,
......@@ -140,7 +140,6 @@ class FifthScreen extends StatelessWidget {
},
);
}),
]),
if (state.expertMode)
const TextDivider(text: 'technical_info_title'),
......@@ -155,7 +154,7 @@ class FifthScreen extends StatelessWidget {
title: 'code_card_title',
icon: Icons.code_rounded,
url:
Uri.parse('https://git.duniter.org/vjrj/ginkgo')),
Uri.parse('https://git.duniter.org/vjrj/ginkgo')),
const BottomWidget(),
SwitchListTile(
title: Text(tr('expert_mode')),
......
......@@ -121,10 +121,15 @@ bool smallScreen(BuildContext context) =>
MediaQuery.of(context).size.width <= smallScreenWidth;
String formatAmount(BuildContext context, double amount) {
return formatAmountWithLocale(
Localizations.localeOf(context).toString(), amount);
}
String formatAmountWithLocale(String locale, double amount) {
final NumberFormat currencyFormatter = NumberFormat.currency(
// in English $10 is G110 ... confusing
symbol: 'Ğ1 ',
locale: Localizations.localeOf(context).toString(),
locale: locale,
decimalDigits: 2,
);
return currencyFormatter.format(amount);
......
......@@ -83,7 +83,7 @@ class _TransactionsAndBalanceWidgetState
builder: (BuildContext lContext,
BoxConstraints constraints) =>
IconButton(
icon: const Icon(Icons.savings),
icon: const Icon(Icons.account_balance_wallet),
onPressed: () {
if (Backdrop.of(lContext).isBackLayerConcealed) {
Backdrop.of(lContext).revealBackLayer();
......
......@@ -116,6 +116,7 @@ flutter:
- assets/img/logo-cesium.png
- assets/tx.json
- assets/gva-tx.json
- assets/img/animated-bell.gif
fonts:
- family: Nunito
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment