Solian/lib/providers/account.dart

239 lines
6.6 KiB
Dart
Raw Normal View History

2024-06-08 13:35:50 +00:00
import 'dart:async';
2024-05-25 05:00:40 +00:00
import 'dart:convert';
2024-06-08 13:35:50 +00:00
import 'dart:developer';
2024-05-25 05:00:40 +00:00
import 'dart:io';
import 'dart:math' as math;
2024-06-08 13:35:50 +00:00
import 'package:device_info_plus/device_info_plus.dart';
2024-06-06 15:28:19 +00:00
import 'package:firebase_messaging/firebase_messaging.dart';
2024-05-25 05:00:40 +00:00
import 'package:get/get.dart';
import 'package:solian/models/notification.dart';
import 'package:solian/models/packet.dart';
import 'package:solian/models/pagination.dart';
2024-06-06 16:17:45 +00:00
import 'package:solian/platform.dart';
2024-05-25 05:00:40 +00:00
import 'package:solian/providers/auth.dart';
import 'package:solian/services.dart';
import 'package:web_socket_channel/io.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class AccountProvider extends GetxController {
final FlutterLocalNotificationsPlugin localNotify =
FlutterLocalNotificationsPlugin();
RxBool isConnected = false.obs;
RxBool isConnecting = false.obs;
RxInt notificationUnread = 0.obs;
RxList<Notification> notifications =
List<Notification>.empty(growable: true).obs;
IOWebSocketChannel? websocket;
@override
onInit() {
2024-06-08 13:35:50 +00:00
FirebaseMessaging.instance
.requestPermission(
alert: true,
announcement: true,
carPlay: true,
badge: true,
sound: true)
.then((status) {
2024-05-25 05:00:40 +00:00
notifyInitialization();
notifyPrefetch();
});
super.onInit();
}
void connect({noRetry = false}) async {
2024-06-03 15:36:46 +00:00
if (isConnected.value) {
return;
} else {
disconnect();
}
2024-05-25 05:00:40 +00:00
final AuthProvider auth = Get.find();
2024-06-09 15:00:11 +00:00
await auth.ensureCredentials();
2024-05-25 05:00:40 +00:00
if (auth.credentials == null) await auth.loadCredentials();
final uri = Uri.parse(
'${ServiceFinder.services['passport']}/api/ws?tk=${auth.credentials!.accessToken}'
.replaceFirst('http', 'ws'),
);
isConnecting.value = true;
try {
websocket = IOWebSocketChannel.connect(uri);
await websocket?.ready;
} catch (e) {
if (!noRetry) {
await auth.refreshCredentials();
return connect(noRetry: true);
}
}
listen();
isConnected.value = true;
isConnecting.value = false;
}
void disconnect() {
websocket?.sink.close(WebSocketStatus.normalClosure);
websocket = null;
2024-05-25 05:00:40 +00:00
isConnected.value = false;
}
void listen() {
websocket?.stream.listen(
(event) {
final packet = NetworkPackage.fromJson(jsonDecode(event));
switch (packet.method) {
case 'notifications.new':
final notification = Notification.fromJson(packet.payload!);
notificationUnread++;
notifications.add(notification);
notifyMessage(notification.subject, notification.content);
break;
}
},
onDone: () {
isConnected.value = false;
2024-06-08 13:35:50 +00:00
Future.delayed(const Duration(seconds: 1), () => connect());
2024-05-25 05:00:40 +00:00
},
onError: (err) {
isConnected.value = false;
Future.delayed(const Duration(seconds: 3), () => connect());
2024-05-25 05:00:40 +00:00
},
);
}
void notifyInitialization() {
const androidSettings = AndroidInitializationSettings('app_icon');
const darwinSettings = DarwinInitializationSettings(
notificationCategories: [
DarwinNotificationCategory('general'),
],
);
const linuxSettings =
LinuxInitializationSettings(defaultActionName: 'Open notification');
const InitializationSettings initializationSettings =
InitializationSettings(
android: androidSettings,
iOS: darwinSettings,
macOS: darwinSettings,
linux: linuxSettings,
);
localNotify.initialize(initializationSettings);
}
void notifyMessage(String title, String body) {
const androidSettings = AndroidNotificationDetails(
'general',
'General',
importance: Importance.high,
priority: Priority.high,
silent: true,
);
const darwinSettings = DarwinNotificationDetails(
presentAlert: true,
presentBanner: true,
presentBadge: true,
presentSound: false,
);
const linuxSettings = LinuxNotificationDetails();
localNotify.show(
math.max(1, math.Random().nextInt(100000000)),
title,
body,
const NotificationDetails(
android: androidSettings,
iOS: darwinSettings,
macOS: darwinSettings,
linux: linuxSettings,
),
);
}
Future<void> notifyPrefetch() async {
final AuthProvider auth = Get.find();
if (!await auth.isAuthorized) return;
2024-06-22 14:39:32 +00:00
final client = auth.configureClient('passport');
2024-05-25 05:00:40 +00:00
final resp = await client.get('/api/notifications?skip=0&take=100');
if (resp.statusCode == 200) {
final result = PaginationResult.fromJson(resp.body);
final data = result.data?.map((x) => Notification.fromJson(x)).toList();
if (data != null) {
notifications.addAll(data);
notificationUnread.value = data.length;
}
}
}
2024-06-06 15:28:19 +00:00
Future<void> registerPushNotifications() async {
final AuthProvider auth = Get.find();
if (!await auth.isAuthorized) throw Exception('unauthorized');
2024-06-06 16:17:45 +00:00
late final String? token;
late final String provider;
2024-06-08 13:35:50 +00:00
final deviceUuid = await _getDeviceUuid();
2024-06-09 15:00:11 +00:00
if (deviceUuid == null || deviceUuid.isEmpty) {
2024-06-08 13:35:50 +00:00
log("Unable to active push notifications, couldn't get device uuid");
}
2024-06-06 16:17:45 +00:00
if (PlatformInfo.isIOS || PlatformInfo.isMacOS) {
provider = 'apple';
2024-06-06 16:17:45 +00:00
token = await FirebaseMessaging.instance.getAPNSToken();
} else {
provider = 'firebase';
2024-06-06 16:17:45 +00:00
token = await FirebaseMessaging.instance.getToken();
}
2024-06-06 15:28:19 +00:00
2024-06-22 14:39:32 +00:00
final client = auth.configureClient('passport');
2024-06-06 15:28:19 +00:00
2024-06-06 16:00:28 +00:00
final resp = await client.post('/api/notifications/subscribe', {
2024-06-06 16:17:45 +00:00
'provider': provider,
2024-06-06 15:28:19 +00:00
'device_token': token,
'device_id': deviceUuid,
});
if (resp.statusCode != 200) {
throw Exception(resp.bodyString);
}
}
2024-06-08 13:35:50 +00:00
Future<String?> _getDeviceUuid() async {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
if (PlatformInfo.isWeb) {
final WebBrowserInfo webInfo = await deviceInfo.webBrowserInfo;
return webInfo.vendor! +
webInfo.userAgent! +
webInfo.hardwareConcurrency.toString();
}
if (PlatformInfo.isAndroid) {
final AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
return androidInfo.id;
}
if (PlatformInfo.isIOS) {
final IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
return iosInfo.identifierForVendor!;
}
if (PlatformInfo.isLinux) {
final LinuxDeviceInfo linuxInfo = await deviceInfo.linuxInfo;
return linuxInfo.machineId!;
}
if (PlatformInfo.isWindows) {
final WindowsDeviceInfo windowsInfo = await deviceInfo.windowsInfo;
return windowsInfo.deviceId;
}
return null;
}
2024-05-25 05:00:40 +00:00
}