Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 70 additions & 67 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -877,81 +877,84 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
),
),
),
home: CryptoNotifications(
child: Util.isDesktop
? FutureBuilder(
future: loadShared(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (_desktopHasPassword) {
String? startupWalletId;
if (ref
// Wraps the navigator rather than sitting in it: as home: this listener
// was a route, so the first post-login navigation disposed it and no
// notification could be delivered.
builder: (context, child) =>
CryptoNotifications(child: child ?? const SizedBox.shrink()),
home: Util.isDesktop
? FutureBuilder(
future: loadShared(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (_desktopHasPassword) {
String? startupWalletId;
if (ref
.read(prefsChangeNotifierProvider)
.gotoWalletOnStartup) {
startupWalletId = ref
.read(prefsChangeNotifierProvider)
.gotoWalletOnStartup) {
startupWalletId = ref
.read(prefsChangeNotifierProvider)
.startupWalletId;
}

return DesktopLoginView(
startupWalletId: startupWalletId,
load: () => load(true),
);
} else {
return const IntroView();
.startupWalletId;
}

return DesktopLoginView(
startupWalletId: startupWalletId,
load: () => load(true),
);
} else {
return const LoadingView();
return const IntroView();
}
},
)
: FutureBuilder(
future: load(false),
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// FlutterNativeSplash.remove();
if (ref.read(pAllWalletsInfo).isNotEmpty ||
ref.read(prefsChangeNotifierProvider).hasPin) {
// return HomeView();

String? startupWalletId;
if (ref
} else {
return const LoadingView();
}
},
)
: FutureBuilder(
future: load(false),
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// FlutterNativeSplash.remove();
if (ref.read(pAllWalletsInfo).isNotEmpty ||
ref.read(prefsChangeNotifierProvider).hasPin) {
// return HomeView();

String? startupWalletId;
if (ref
.read(prefsChangeNotifierProvider)
.gotoWalletOnStartup) {
startupWalletId = ref
.read(prefsChangeNotifierProvider)
.gotoWalletOnStartup) {
startupWalletId = ref
.read(prefsChangeNotifierProvider)
.startupWalletId;
}

return LockscreenView(
isInitialAppLogin: true,
routeOnSuccess: HomeView.routeName,
routeOnSuccessArguments: startupWalletId,
biometricsAuthenticationTitle:
"Unlock ${AppConfig.prefix}",
biometricsLocalizedReason:
"Unlock your ${AppConfig.appName} using biometrics",
biometricsCancelButtonString: "Cancel",
);
} else {
if (AppConfig.appName == "Campfire" &&
!CampfireMigration.didRun &&
CampfireMigration.hasOldWallets) {
return const CampfireMigrateView();
} else {
return const IntroView();
}
.startupWalletId;
}

return LockscreenView(
isInitialAppLogin: true,
routeOnSuccess: HomeView.routeName,
routeOnSuccessArguments: startupWalletId,
biometricsAuthenticationTitle:
"Unlock ${AppConfig.prefix}",
biometricsLocalizedReason:
"Unlock your ${AppConfig.appName} using biometrics",
biometricsCancelButtonString: "Cancel",
);
} else {
// CURRENTLY DISABLED as cannot be animated
// technically not needed as FlutterNativeSplash will overlay
// anything returned here until the future completes but
// FutureBuilder requires you to return something
return const LoadingView();
if (AppConfig.appName == "Campfire" &&
!CampfireMigration.didRun &&
CampfireMigration.hasOldWallets) {
return const CampfireMigrateView();
} else {
return const IntroView();
}
}
},
),
),
} else {
// CURRENTLY DISABLED as cannot be animated
// technically not needed as FlutterNativeSplash will overlay
// anything returned here until the future completes but
// FutureBuilder requires you to return something
return const LoadingView();
}
},
),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import '../../../../../pages_desktop_specific/desktop_home_view.dart';
import '../../../../../providers/frost_wallet/frost_wallet_providers.dart';
import '../../../../../providers/global/secure_store_provider.dart';
import '../../../../../providers/providers.dart';
import '../../../../../services/transaction_notification_tracker.dart';
import '../../../../../themes/stack_colors.dart';
import '../../../../../utilities/assets.dart';
import '../../../../../utilities/logger.dart';
Expand Down Expand Up @@ -141,6 +142,9 @@ class _FrostCreateStep5State extends ConsumerState<FrostCreateStep5> {
coin: data.info.frostCurrency,
name: data.info.walletName,
);
await TransactionNotificationTracker(
walletId: info.walletId,
).markInitialized();

final wallet = await Wallet.create(
walletInfo: info,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ class _NewWalletRecoveryPhraseWarningViewState
}

final txTracker = TransactionNotificationTracker(walletId: info.walletId);
await txTracker.markInitialized();

String? mnemonicPassphrase;
String? mnemonic;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import '../../../pages_desktop_specific/desktop_home_view.dart';
import '../../../pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
import '../../../providers/global/secure_store_provider.dart';
import '../../../providers/providers.dart';
import '../../../services/transaction_notification_tracker.dart';
import '../../../themes/stack_colors.dart';
import '../../../utilities/address_utils.dart';
import '../../../utilities/assets.dart';
Expand Down Expand Up @@ -327,10 +326,6 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
.save(node, null, false);
}

final txTracker = TransactionNotificationTracker(
walletId: info.walletId,
);

try {
final wallet = await Wallet.create(
walletInfo: info,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import 'dart:async';

import 'package:event_bus/event_bus.dart';

import '../../../../wallets/crypto_currency/crypto_currency.dart';

abstract class CryptoNotificationsEventBus {
static final instance = EventBus();
static int _listenerCount = 0;

static bool get hasListeners => _listenerCount > 0;

static void registerListener() => _listenerCount++;

static void unregisterListener() {
assert(_listenerCount > 0);
if (_listenerCount > 0) {
_listenerCount--;
}
}
}

class CryptoNotificationEvent {
final String title;
final String walletId;
final String walletName;
final DateTime date;
final bool shouldWatchForUpdates;
final CryptoCurrency coin;
final String? txid;
final int? confirmations;
final int? requiredConfirmations;
final String? changeNowId;
final String? payload;
final Completer<void> _deliveryCompleter = Completer<void>();

Future<void> get delivered => _deliveryCompleter.future;

CryptoNotificationEvent({
required this.title,
required this.walletId,
required this.walletName,
required this.date,
required this.shouldWatchForUpdates,
required this.coin,
this.txid,
this.confirmations,
this.requiredConfirmations,
this.changeNowId,
this.payload,
});

void completeDelivery() {
if (!_deliveryCompleter.isCompleted) {
_deliveryCompleter.complete();
}
}

void failDelivery(Object error, StackTrace stackTrace) {
if (!_deliveryCompleter.isCompleted) {
_deliveryCompleter.completeError(error, stackTrace);
}
}
}
68 changes: 53 additions & 15 deletions lib/services/notifications_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*
*/

import 'dart:async';
import 'dart:io';

import 'package:flutter_local_notifications/flutter_local_notifications.dart';

Expand All @@ -18,7 +18,8 @@ import '../utilities/prefs.dart';
import 'notifications_service.dart';

abstract final class NotificationApi {
static Completer<void>? _initCalledCompleter;
static Future<void>? _initializationFuture;
static Future<bool?>? _permissionRequestFuture;
static final _notifications = FlutterLocalNotificationsPlugin();
// static final onNotifications = BehaviorSubject<String?>();

Expand All @@ -38,16 +39,24 @@ abstract final class NotificationApi {
}

static Future<void> init({bool initScheduled = false}) async {
if (_initCalledCompleter == null) {
_initCalledCompleter = Completer<void>();
} else {
if (_initCalledCompleter!.isCompleted) {
return;
} else {
return await _initCalledCompleter!.future;
final existing = _initializationFuture;
if (existing != null) {
return existing;
}

final initialization = _initialize();
_initializationFuture = initialization;
try {
await initialization;
} catch (_) {
if (identical(_initializationFuture, initialization)) {
_initializationFuture = null;
}
rethrow;
}
}

static Future<void> _initialize() async {
const android = AndroidInitializationSettings('app_icon_alpha');
const iOS = DarwinInitializationSettings();
const linux = LinuxInitializationSettings(
Expand All @@ -69,7 +78,21 @@ abstract final class NotificationApi {
// onNotifications.add(payload.payload);
// },
);
_initCalledCompleter!.complete();
}

static Future<void> _requestPermissionIfNeeded() async {
if (!Platform.isAndroid) {
return;
}
_permissionRequestFuture ??= _notifications
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin
>()
?.requestNotificationsPermission();
final granted = await _permissionRequestFuture;
if (granted == false) {
Logging.instance.i("System notification permission was denied");
}
}

static Future<void> clearNotifications() async {
Expand All @@ -92,6 +115,7 @@ abstract final class NotificationApi {
String? payload,
}) async {
await init();
await _requestPermissionIfNeeded();
final id = await prefs.incrementCurrentNotificationIndex();
await _notifications.show(
id,
Expand All @@ -117,11 +141,9 @@ abstract final class NotificationApi {
String? changeNowId,
String? payload,
}) async {
final id = await _showOsNotification(
title: title,
body: body,
payload: payload,
);
await init();
await _requestPermissionIfNeeded();
final id = await prefs.incrementCurrentNotificationIndex();

String confirms = "";
if (txid != null &&
Expand All @@ -145,6 +167,22 @@ abstract final class NotificationApi {
);

await notificationsService.add(model, true);

try {
await _notifications.show(
id,
title,
body,
await _notificationDetails(),
payload: payload,
);
} catch (error, stackTrace) {
Logging.instance.w(
"System notification delivery failed",
error: error,
stackTrace: stackTrace,
);
}
}

static Future<void> showLocalOnly({
Expand Down
Loading
Loading