2024-05-25 05:00:40 +00:00
|
|
|
import 'dart:convert';
|
|
|
|
import 'dart:io';
|
|
|
|
import 'dart:math' as math;
|
|
|
|
|
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:permission_handler/permission_handler.dart';
|
2024-06-06 15:28:19 +00:00
|
|
|
import 'package:platform_device_id/platform_device_id.dart';
|
2024-05-25 05:00:40 +00:00
|
|
|
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() {
|
|
|
|
Permission.notification.request().then((status) {
|
|
|
|
notifyInitialization();
|
|
|
|
notifyPrefetch();
|
|
|
|
});
|
|
|
|
|
|
|
|
super.onInit();
|
|
|
|
}
|
|
|
|
|
|
|
|
void connect({noRetry = false}) async {
|
2024-06-03 15:36:46 +00:00
|
|
|
if (isConnected.value) {
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
disconnect();
|
|
|
|
}
|
2024-06-02 15:20:34 +00:00
|
|
|
|
2024-05-25 05:00:40 +00:00
|
|
|
final AuthProvider auth = Get.find();
|
2024-06-06 12:49:18 +00:00
|
|
|
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);
|
2024-06-02 15:20:34 +00:00
|
|
|
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-06 12:49:18 +00:00
|
|
|
Future.delayed(const Duration(seconds: 3), () => connect());
|
2024-05-25 05:00:40 +00:00
|
|
|
},
|
|
|
|
onError: (err) {
|
|
|
|
isConnected.value = false;
|
2024-06-06 12:49:18 +00:00
|
|
|
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-06 12:49:18 +00:00
|
|
|
final client = auth.configureClient(service: '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-06 15:28:19 +00:00
|
|
|
final deviceUuid = await PlatformDeviceId.getDeviceId;
|
2024-06-06 16:17:45 +00:00
|
|
|
|
|
|
|
if (PlatformInfo.isIOS || PlatformInfo.isMacOS) {
|
|
|
|
provider = "apple";
|
|
|
|
token = await FirebaseMessaging.instance.getAPNSToken();
|
|
|
|
} else {
|
|
|
|
provider = "firebase";
|
|
|
|
token = await FirebaseMessaging.instance.getToken();
|
|
|
|
}
|
2024-06-06 15:28:19 +00:00
|
|
|
|
|
|
|
final client = auth.configureClient(service: 'passport');
|
|
|
|
|
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-05-25 05:00:40 +00:00
|
|
|
}
|