You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
4.7 KiB
147 lines
4.7 KiB
part of '../neon.dart'; |
|
|
|
abstract class PushNotificationsBlocEvents {} |
|
|
|
abstract class PushNotificationsBlocStates { |
|
Stream<NextcloudPushNotification> get notifications; |
|
} |
|
|
|
class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents, PushNotificationsBlocStates { |
|
PushNotificationsBloc( |
|
this._accountsBloc, |
|
this._sharedPreferences, |
|
this._globalOptions, |
|
this._env, |
|
this._platform, |
|
) { |
|
if (_platform.canUsePushNotifications) { |
|
unawaited(UnifiedPush.getDistributors().then(_globalOptions.updateDistributors)); |
|
|
|
_globalOptions.pushNotificationsEnabled.stream.listen((final enabled) async { |
|
if (enabled != _pushNotificationsEnabled) { |
|
_pushNotificationsEnabled = enabled; |
|
if (enabled) { |
|
// We just use a single RSA keypair for all accounts |
|
_keypair = await PushUtils.loadRSAKeypair(_storage); |
|
await _setupUnifiedPush(); |
|
} |
|
} |
|
}); |
|
} |
|
} |
|
|
|
final AccountsBloc _accountsBloc; |
|
final NeonPlatform _platform; |
|
final SharedPreferences _sharedPreferences; |
|
late final _storage = AppStorage('notifications', _sharedPreferences); |
|
final GlobalOptions _globalOptions; |
|
final Env? _env; |
|
late RSAKeypair _keypair; |
|
bool? _pushNotificationsEnabled; |
|
|
|
final _notificationsController = StreamController<NextcloudPushNotification>(); |
|
|
|
@override |
|
void dispose() { |
|
unawaited(_notificationsController.close()); |
|
} |
|
|
|
@override |
|
late Stream<NextcloudPushNotification> notifications = _notificationsController.stream.asBroadcastStream(); |
|
|
|
String _keyLastEndpoint(final Account account) => 'last-endpoint-${account.id}'; |
|
|
|
Future _setupUnifiedPush() async { |
|
await UnifiedPush.initialize( |
|
onNewEndpoint: (final endpoint, final instance) async { |
|
Account? account; |
|
for (final a in _accountsBloc.accounts.value) { |
|
if (a.id == instance) { |
|
account = a; |
|
break; |
|
} |
|
} |
|
if (account == null) { |
|
debugPrint('Account for $instance not found, can not process endpoint'); |
|
return; |
|
} |
|
|
|
if (_storage.getString(_keyLastEndpoint(account)) == endpoint) { |
|
debugPrint('Endpoint not changed'); |
|
return; |
|
} |
|
|
|
debugPrint('Registering account $instance for push notifications on $endpoint'); |
|
|
|
var proxyServerForNextcloud = 'https://nc.proxy.neon.provokateurin.de/'; |
|
var proxyServerForClient = proxyServerForNextcloud; |
|
if (_env != null) { |
|
proxyServerForNextcloud = 'http://host.docker.internal:8080/'; |
|
proxyServerForClient = 'http://${_env!.testHost}:8080/'; |
|
} |
|
|
|
final subscription = await account.client.notifications.registerDevice( |
|
pushTokenHash: account.client.notifications.generatePushTokenHash(endpoint), |
|
devicePublicKey: _keypair.publicKey.toFormattedPEM(), |
|
proxyServer: proxyServerForNextcloud, |
|
); |
|
|
|
await account.client.notifications.registerDeviceAtPushProxy( |
|
endpoint, |
|
subscription.ocs.data, |
|
proxyServerForClient, |
|
); |
|
|
|
await _storage.setString(_keyLastEndpoint(account), endpoint); |
|
|
|
debugPrint('Account $instance registered for push notifications'); |
|
}, |
|
onMessage: PushUtils.onMessage, |
|
); |
|
|
|
_globalOptions.pushNotificationsDistributor.stream.listen((final distributor) async { |
|
final disabled = distributor == null; |
|
final sameDistributor = distributor == await UnifiedPush.getDistributor(); |
|
final accounts = _accountsBloc.accounts.value; |
|
if (disabled || !sameDistributor) { |
|
await _unregisterUnifiedPushInstances(accounts); |
|
} |
|
if (!disabled && !sameDistributor) { |
|
debugPrint('UnifiedPush distributor changed to $distributor'); |
|
await UnifiedPush.saveDistributor(distributor); |
|
} |
|
if (!disabled) { |
|
await _registerUnifiedPushInstances(accounts); |
|
} |
|
}); |
|
|
|
_accountsBloc.accounts.listen(_registerUnifiedPushInstances); |
|
} |
|
|
|
Future _unregisterUnifiedPushInstances(final List<Account> accounts) async { |
|
for (final account in accounts) { |
|
await UnifiedPush.unregister(account.client.id); |
|
} |
|
} |
|
|
|
Future _registerUnifiedPushInstances(final List<Account> accounts) async { |
|
// Notifications will only work on accounts with app password |
|
for (final account in accounts.where((final a) => a.password != null)) { |
|
await UnifiedPush.registerApp(account.client.id); |
|
} |
|
} |
|
} |
|
|
|
class NextcloudPushNotification { |
|
NextcloudPushNotification({ |
|
required this.instance, |
|
required this.priority, |
|
required this.type, |
|
required this.subject, |
|
}); |
|
|
|
final String instance; |
|
final String priority; |
|
final String type; |
|
final NotificationsPushNotificationDecryptedSubject subject; |
|
}
|
|
|