✨ Notifications
This commit is contained in:
parent
806ae602d5
commit
f376603482
@ -34,6 +34,8 @@ PODS:
|
|||||||
- DKImagePickerController/PhotoGallery
|
- DKImagePickerController/PhotoGallery
|
||||||
- Flutter
|
- Flutter
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
|
- flutter_local_notifications (0.0.1):
|
||||||
|
- Flutter
|
||||||
- flutter_secure_storage (6.0.0):
|
- flutter_secure_storage (6.0.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- image_picker_ios (0.0.1):
|
- image_picker_ios (0.0.1):
|
||||||
@ -41,6 +43,8 @@ PODS:
|
|||||||
- path_provider_foundation (0.0.1):
|
- path_provider_foundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- permission_handler_apple (9.3.0):
|
||||||
|
- Flutter
|
||||||
- SDWebImage (5.19.2):
|
- SDWebImage (5.19.2):
|
||||||
- SDWebImage/Core (= 5.19.2)
|
- SDWebImage/Core (= 5.19.2)
|
||||||
- SDWebImage/Core (5.19.2)
|
- SDWebImage/Core (5.19.2)
|
||||||
@ -51,9 +55,11 @@ PODS:
|
|||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
|
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
||||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
|
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
@ -68,12 +74,16 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/file_picker/ios"
|
:path: ".symlinks/plugins/file_picker/ios"
|
||||||
Flutter:
|
Flutter:
|
||||||
:path: Flutter
|
:path: Flutter
|
||||||
|
flutter_local_notifications:
|
||||||
|
:path: ".symlinks/plugins/flutter_local_notifications/ios"
|
||||||
flutter_secure_storage:
|
flutter_secure_storage:
|
||||||
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
||||||
image_picker_ios:
|
image_picker_ios:
|
||||||
:path: ".symlinks/plugins/image_picker_ios/ios"
|
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||||
|
permission_handler_apple:
|
||||||
|
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||||
|
|
||||||
@ -82,9 +92,11 @@ SPEC CHECKSUMS:
|
|||||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||||
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
|
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
|
||||||
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
|
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
|
||||||
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
||||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||||
|
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
||||||
SDWebImage: dfe95b2466a9823cf9f0c6d01217c06550d7b29a
|
SDWebImage: dfe95b2466a9823cf9f0c6d01217c06550d7b29a
|
||||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||||
|
@ -199,6 +199,7 @@
|
|||||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
287A33C298CA352A7E7F32A4 /* [CP] Embed Pods Frameworks */,
|
287A33C298CA352A7E7F32A4 /* [CP] Embed Pods Frameworks */,
|
||||||
|
0818E8E4321C0D7433E07576 /* [CP] Copy Pods Resources */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@ -270,6 +271,23 @@
|
|||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
0818E8E4321C0D7433E07576 /* [CP] Copy Pods Resources */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Copy Pods Resources";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
259653AE41D478F4C6BAE9B2 /* [CP] Check Pods Manifest.lock */ = {
|
259653AE41D478F4C6BAE9B2 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:solian/providers/account.dart';
|
||||||
import 'package:solian/providers/auth.dart';
|
import 'package:solian/providers/auth.dart';
|
||||||
import 'package:solian/providers/content/attachment.dart';
|
import 'package:solian/providers/content/attachment.dart';
|
||||||
import 'package:solian/providers/friend.dart';
|
import 'package:solian/providers/friend.dart';
|
||||||
@ -31,6 +32,14 @@ class SolianApp extends StatelessWidget {
|
|||||||
Get.lazyPut(() => AuthProvider());
|
Get.lazyPut(() => AuthProvider());
|
||||||
Get.lazyPut(() => FriendProvider());
|
Get.lazyPut(() => FriendProvider());
|
||||||
Get.lazyPut(() => AttachmentProvider());
|
Get.lazyPut(() => AttachmentProvider());
|
||||||
|
Get.lazyPut(() => AccountProvider());
|
||||||
|
|
||||||
|
final AuthProvider auth = Get.find();
|
||||||
|
auth.isAuthorized.then((value) async {
|
||||||
|
if (value) {
|
||||||
|
Get.find<AccountProvider>().connect();
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
return ScaffoldMessenger(
|
return ScaffoldMessenger(
|
||||||
|
161
lib/providers/account.dart
Normal file
161
lib/providers/account.dart
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
import 'package:solian/models/notification.dart';
|
||||||
|
import 'package:solian/models/packet.dart';
|
||||||
|
import 'package:solian/models/pagination.dart';
|
||||||
|
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 {
|
||||||
|
final AuthProvider auth = Get.find();
|
||||||
|
if (!await auth.isAuthorized) throw Exception('unauthorized');
|
||||||
|
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
onError: (err) {
|
||||||
|
isConnected.value = false;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
final client = GetConnect();
|
||||||
|
client.httpClient.baseUrl = ServiceFinder.services['passport'];
|
||||||
|
client.httpClient.addAuthenticator(auth.requestAuthenticator);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:get/get_connect/http/src/request/request.dart';
|
import 'package:get/get_connect/http/src/request/request.dart';
|
||||||
|
import 'package:solian/providers/account.dart';
|
||||||
import 'package:solian/services.dart';
|
import 'package:solian/services.dart';
|
||||||
import 'package:oauth2/oauth2.dart' as oauth2;
|
import 'package:oauth2/oauth2.dart' as oauth2;
|
||||||
|
|
||||||
@ -25,26 +26,30 @@ class AuthProvider extends GetConnect {
|
|||||||
|
|
||||||
oauth2.Credentials? credentials;
|
oauth2.Credentials? credentials;
|
||||||
|
|
||||||
|
Future<void> refreshCredentials() async {
|
||||||
|
final resp = await post('/api/auth/token', {
|
||||||
|
'refresh_token': credentials!.refreshToken,
|
||||||
|
'grant_type': 'refresh_token',
|
||||||
|
});
|
||||||
|
if (resp.statusCode != 200) {
|
||||||
|
throw Exception(resp.bodyString);
|
||||||
|
}
|
||||||
|
credentials = oauth2.Credentials(
|
||||||
|
resp.body['access_token'],
|
||||||
|
refreshToken: resp.body['refresh_token'],
|
||||||
|
idToken: resp.body['access_token'],
|
||||||
|
tokenEndpoint: tokenEndpoint,
|
||||||
|
expiration: DateTime.now().add(const Duration(minutes: 3)),
|
||||||
|
);
|
||||||
|
storage.write(
|
||||||
|
key: 'auth_credentials',
|
||||||
|
value: jsonEncode(credentials!.toJson()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<Request<T?>> requestAuthenticator<T>(Request<T?> request) async {
|
Future<Request<T?>> requestAuthenticator<T>(Request<T?> request) async {
|
||||||
if (credentials != null && credentials!.isExpired) {
|
if (credentials != null && credentials!.isExpired) {
|
||||||
final resp = await post('/api/auth/token', {
|
refreshCredentials();
|
||||||
'refresh_token': credentials!.refreshToken,
|
|
||||||
'grant_type': 'refresh_token',
|
|
||||||
});
|
|
||||||
if (resp.statusCode != 200) {
|
|
||||||
throw Exception(resp.bodyString);
|
|
||||||
}
|
|
||||||
credentials = oauth2.Credentials(
|
|
||||||
resp.body['access_token'],
|
|
||||||
refreshToken: resp.body['refresh_token'],
|
|
||||||
idToken: resp.body['access_token'],
|
|
||||||
tokenEndpoint: tokenEndpoint,
|
|
||||||
expiration: DateTime.now().add(const Duration(minutes: 3)),
|
|
||||||
);
|
|
||||||
storage.write(
|
|
||||||
key: 'auth_credentials',
|
|
||||||
value: jsonEncode(credentials!.toJson()),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (credentials != null) {
|
if (credentials != null) {
|
||||||
@ -91,12 +96,16 @@ class AuthProvider extends GetConnect {
|
|||||||
value: jsonEncode(credentials!.toJson()),
|
value: jsonEncode(credentials!.toJson()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Get.find<AccountProvider>().connect();
|
||||||
|
|
||||||
return credentials!;
|
return credentials!;
|
||||||
}
|
}
|
||||||
|
|
||||||
void signout() {
|
void signout() {
|
||||||
_cacheUserProfileResponse = null;
|
_cacheUserProfileResponse = null;
|
||||||
|
|
||||||
|
Get.find<AccountProvider>().disconnect();
|
||||||
|
|
||||||
storage.deleteAll();
|
storage.deleteAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
210
lib/screens/account/notification.dart
Normal file
210
lib/screens/account/notification.dart
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:solian/providers/account.dart';
|
||||||
|
import 'package:solian/providers/auth.dart';
|
||||||
|
import 'package:solian/services.dart';
|
||||||
|
import 'package:solian/models/notification.dart' as notify;
|
||||||
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
|
class NotificationScreen extends StatefulWidget {
|
||||||
|
const NotificationScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<NotificationScreen> createState() => _NotificationScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NotificationScreenState extends State<NotificationScreen> {
|
||||||
|
bool _isBusy = false;
|
||||||
|
|
||||||
|
Future<void> markAllRead() async {
|
||||||
|
final AuthProvider auth = Get.find();
|
||||||
|
if (!await auth.isAuthorized) return;
|
||||||
|
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
|
final AccountProvider provider = Get.find();
|
||||||
|
|
||||||
|
List<int> markList = List.empty(growable: true);
|
||||||
|
for (final element in provider.notifications) {
|
||||||
|
if (element.isRealtime) continue;
|
||||||
|
markList.add(element.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (markList.isNotEmpty) {
|
||||||
|
final client = GetConnect();
|
||||||
|
client.httpClient.baseUrl = ServiceFinder.services['passport'];
|
||||||
|
client.httpClient.addAuthenticator(auth.requestAuthenticator);
|
||||||
|
|
||||||
|
await client.put('/api/notifications/batch/read', {'messages': markList});
|
||||||
|
}
|
||||||
|
|
||||||
|
provider.notifications.clear();
|
||||||
|
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> markOneRead(notify.Notification element, int index) async {
|
||||||
|
final AuthProvider auth = Get.find();
|
||||||
|
if (!await auth.isAuthorized) return;
|
||||||
|
|
||||||
|
final AccountProvider provider = Get.find();
|
||||||
|
|
||||||
|
if (element.isRealtime) {
|
||||||
|
provider.notifications.removeAt(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
|
final client = GetConnect();
|
||||||
|
client.httpClient.baseUrl = ServiceFinder.services['passport'];
|
||||||
|
client.httpClient.addAuthenticator(auth.requestAuthenticator);
|
||||||
|
|
||||||
|
await client.put('/api/notifications/${element.id}/read', {});
|
||||||
|
|
||||||
|
provider.notifications.removeAt(index);
|
||||||
|
|
||||||
|
setState(() => _isBusy = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final AccountProvider provider = Get.find();
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
height: MediaQuery.of(context).size.height * 0.85,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'notification'.tr,
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
|
).paddingOnly(left: 24, right: 24, top: 32, bottom: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Obx(() {
|
||||||
|
return CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
if (_isBusy)
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: const LinearProgressIndicator().animate().scaleX(),
|
||||||
|
),
|
||||||
|
if (provider.notifications.isEmpty)
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(Icons.check),
|
||||||
|
title: Text('notifyEmpty'.tr),
|
||||||
|
subtitle: Text('notifyEmptyCaption'.tr),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (provider.notifications.isNotEmpty)
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: ListTile(
|
||||||
|
tileColor:
|
||||||
|
Theme.of(context).colorScheme.secondaryContainer,
|
||||||
|
leading: const Icon(Icons.checklist),
|
||||||
|
title: Text('notifyAllRead'.tr),
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 28),
|
||||||
|
onTap: _isBusy ? null : () => markAllRead(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SliverList.separated(
|
||||||
|
itemCount: provider.notifications.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
var element = provider.notifications[index];
|
||||||
|
return Dismissible(
|
||||||
|
key: Key(const Uuid().v4()),
|
||||||
|
background: Container(
|
||||||
|
color: Colors.lightBlue,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: const Icon(Icons.check, color: Colors.white),
|
||||||
|
),
|
||||||
|
child: ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 24,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
title: Text(element.subject),
|
||||||
|
subtitle: Column(
|
||||||
|
children: [
|
||||||
|
Text(element.content),
|
||||||
|
if (element.links != null)
|
||||||
|
Row(
|
||||||
|
children: element.links!
|
||||||
|
.map((e) => InkWell(
|
||||||
|
child: Text(
|
||||||
|
e.label,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onSecondaryContainer,
|
||||||
|
decoration:
|
||||||
|
TextDecoration.underline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
launchUrlString(e.url);
|
||||||
|
},
|
||||||
|
).paddingOnly(right: 5))
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onDismissed: (_) => markOneRead(element, index),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (_, __) =>
|
||||||
|
const Divider(thickness: 0.3, height: 0.3),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NotificationButton extends StatelessWidget {
|
||||||
|
const NotificationButton({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final AccountProvider provider = Get.find();
|
||||||
|
|
||||||
|
final button = IconButton(
|
||||||
|
icon: const Icon(Icons.notifications),
|
||||||
|
onPressed: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
useRootNavigator: true,
|
||||||
|
isScrollControlled: true,
|
||||||
|
context: context,
|
||||||
|
builder: (context) => const NotificationScreen(),
|
||||||
|
).then((_) => provider.notificationUnread.value = 0);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return Obx(() {
|
||||||
|
if (provider.notificationUnread.value > 0) {
|
||||||
|
return Badge(
|
||||||
|
isLabelVisible: true,
|
||||||
|
offset: const Offset(-8, 2),
|
||||||
|
label: Text(provider.notificationUnread.value.toString()),
|
||||||
|
child: button,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,8 @@ import 'package:solian/models/post.dart';
|
|||||||
import 'package:solian/providers/auth.dart';
|
import 'package:solian/providers/auth.dart';
|
||||||
import 'package:solian/providers/content/post_explore.dart';
|
import 'package:solian/providers/content/post_explore.dart';
|
||||||
import 'package:solian/router.dart';
|
import 'package:solian/router.dart';
|
||||||
|
import 'package:solian/screens/account/notification.dart';
|
||||||
|
import 'package:solian/theme.dart';
|
||||||
import 'package:solian/widgets/posts/post_action.dart';
|
import 'package:solian/widgets/posts/post_action.dart';
|
||||||
import 'package:solian/widgets/posts/post_item.dart';
|
import 'package:solian/widgets/posts/post_item.dart';
|
||||||
|
|
||||||
@ -69,32 +71,61 @@ class _SocialScreenState extends State<SocialScreen> {
|
|||||||
}),
|
}),
|
||||||
body: Material(
|
body: Material(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
child: RefreshIndicator(
|
child: SafeArea(
|
||||||
onRefresh: () => Future.sync(() => _pagingController.refresh()),
|
child: NestedScrollView(
|
||||||
child: PagedListView<int, Post>.separated(
|
headerSliverBuilder: (context, innerBoxIsScrolled) {
|
||||||
pagingController: _pagingController,
|
return [
|
||||||
builderDelegate: PagedChildBuilderDelegate<Post>(
|
SliverOverlapAbsorber(
|
||||||
itemBuilder: (context, item, index) {
|
handle:
|
||||||
return GestureDetector(
|
NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||||
child: PostItem(key: Key('p${item.alias}'), item: item)
|
sliver: SliverAppBar(
|
||||||
.paddingSymmetric(
|
title: Text('social'.tr),
|
||||||
vertical: (item.attachments?.isEmpty ?? false) ? 8 : 0,
|
centerTitle: false,
|
||||||
|
titleSpacing:
|
||||||
|
SolianTheme.isLargeScreen(context) ? null : 24,
|
||||||
|
forceElevated: innerBoxIsScrolled,
|
||||||
|
actions: const [
|
||||||
|
NotificationButton(),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
onTap: () {},
|
),
|
||||||
onLongPress: () {
|
];
|
||||||
showModalBottomSheet(
|
},
|
||||||
useRootNavigator: true,
|
body: MediaQuery.removePadding(
|
||||||
context: context,
|
removeTop: true,
|
||||||
builder: (context) => PostAction(item: item),
|
context: context,
|
||||||
).then((value) {
|
child: RefreshIndicator(
|
||||||
if (value == true) _pagingController.refresh();
|
onRefresh: () => Future.sync(() => _pagingController.refresh()),
|
||||||
});
|
child: PagedListView<int, Post>.separated(
|
||||||
},
|
pagingController: _pagingController,
|
||||||
);
|
builderDelegate: PagedChildBuilderDelegate<Post>(
|
||||||
},
|
itemBuilder: (context, item, index) {
|
||||||
|
return GestureDetector(
|
||||||
|
child: PostItem(
|
||||||
|
key: Key('p${item.alias}'),
|
||||||
|
item: item,
|
||||||
|
).paddingSymmetric(
|
||||||
|
vertical:
|
||||||
|
(item.attachments?.isEmpty ?? false) ? 8 : 0,
|
||||||
|
),
|
||||||
|
onTap: () {},
|
||||||
|
onLongPress: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
useRootNavigator: true,
|
||||||
|
context: context,
|
||||||
|
builder: (context) => PostAction(item: item),
|
||||||
|
).then((value) {
|
||||||
|
if (value == true) _pagingController.refresh();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
separatorBuilder: (_, __) =>
|
||||||
|
const Divider(thickness: 0.3, height: 0.3),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
separatorBuilder: (_, __) =>
|
|
||||||
const Divider(thickness: 0.3, height: 0.3),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -18,6 +18,7 @@ class SolianMessages extends Translations {
|
|||||||
'delete': 'Delete',
|
'delete': 'Delete',
|
||||||
'reply': 'Reply',
|
'reply': 'Reply',
|
||||||
'repost': 'Repost',
|
'repost': 'Repost',
|
||||||
|
'notification': 'Notification',
|
||||||
'errorHappened': 'An error occurred',
|
'errorHappened': 'An error occurred',
|
||||||
'email': 'Email',
|
'email': 'Email',
|
||||||
'username': 'Username',
|
'username': 'Username',
|
||||||
@ -49,7 +50,7 @@ class SolianMessages extends Translations {
|
|||||||
'signinRiskDetected':
|
'signinRiskDetected':
|
||||||
'Risk detected, click Next to open a webpage and signin through it to pass security check.',
|
'Risk detected, click Next to open a webpage and signin through it to pass security check.',
|
||||||
'signup': 'Sign up',
|
'signup': 'Sign up',
|
||||||
'signupGreeting': 'Welcome onboard 👋',
|
'signupGreeting': 'Welcome onboard',
|
||||||
'signupCaption':
|
'signupCaption':
|
||||||
'Create an account on Solarpass and then get the access of entire Solar Network!',
|
'Create an account on Solarpass and then get the access of entire Solar Network!',
|
||||||
'signout': 'Sign out',
|
'signout': 'Sign out',
|
||||||
@ -57,6 +58,9 @@ class SolianMessages extends Translations {
|
|||||||
'matureContent': 'Mature Content',
|
'matureContent': 'Mature Content',
|
||||||
'matureContentCaption':
|
'matureContentCaption':
|
||||||
'The content is rated and may not suitable for everyone to view',
|
'The content is rated and may not suitable for everyone to view',
|
||||||
|
'notifyAllRead': 'Mark all as read',
|
||||||
|
'notifyEmpty': 'All notifications read',
|
||||||
|
'notifyEmptyCaption': 'It seems like nothing happened recently',
|
||||||
'postAction': 'Post',
|
'postAction': 'Post',
|
||||||
'postPublishing': 'Post a post',
|
'postPublishing': 'Post a post',
|
||||||
'postIdentityNotify': 'You will post this post as',
|
'postIdentityNotify': 'You will post this post as',
|
||||||
@ -98,6 +102,7 @@ class SolianMessages extends Translations {
|
|||||||
'apply': '应用',
|
'apply': '应用',
|
||||||
'reply': '回复',
|
'reply': '回复',
|
||||||
'repost': '转帖',
|
'repost': '转帖',
|
||||||
|
'notification': '通知',
|
||||||
'errorHappened': '发生错误了',
|
'errorHappened': '发生错误了',
|
||||||
'email': '邮件地址',
|
'email': '邮件地址',
|
||||||
'username': '用户名',
|
'username': '用户名',
|
||||||
@ -131,6 +136,9 @@ class SolianMessages extends Translations {
|
|||||||
'riskDetection': '检测到风险',
|
'riskDetection': '检测到风险',
|
||||||
'matureContent': '评级内容',
|
'matureContent': '评级内容',
|
||||||
'matureContentCaption': '该内容已被评级为家长指导级或以上,这可能说明内容包含一系列不友好的成分',
|
'matureContentCaption': '该内容已被评级为家长指导级或以上,这可能说明内容包含一系列不友好的成分',
|
||||||
|
'notifyAllRead': '已读所有通知',
|
||||||
|
'notifyEmpty': '通知箱为空',
|
||||||
|
'notifyEmptyCaption': '看起来最近没发生什么呢',
|
||||||
'postAction': '发表',
|
'postAction': '发表',
|
||||||
'postPublishing': '发表帖子',
|
'postPublishing': '发表帖子',
|
||||||
'postIdentityNotify': '你将会以本身份发表帖子',
|
'postIdentityNotify': '你将会以本身份发表帖子',
|
||||||
|
@ -6,12 +6,14 @@ import FlutterMacOS
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import file_selector_macos
|
import file_selector_macos
|
||||||
|
import flutter_local_notifications
|
||||||
import flutter_secure_storage_macos
|
import flutter_secure_storage_macos
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||||
|
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
|
154
pubspec.lock
154
pubspec.lock
@ -89,6 +89,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.8"
|
version: "1.0.8"
|
||||||
|
dbus:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dbus
|
||||||
|
sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.10"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -145,6 +153,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.3+1"
|
version: "0.9.3+1"
|
||||||
|
fixnum:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fixnum
|
||||||
|
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -166,6 +182,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.2"
|
||||||
|
flutter_local_notifications:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_local_notifications
|
||||||
|
sha256: "40e6fbd2da7dcc7ed78432c5cdab1559674b4af035fddbfb2f9a8f9c2112fcef"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "17.1.2"
|
||||||
|
flutter_local_notifications_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_local_notifications_linux
|
||||||
|
sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.0+1"
|
||||||
|
flutter_local_notifications_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_local_notifications_platform_interface
|
||||||
|
sha256: "340abf67df238f7f0ef58f4a26d2a83e1ab74c77ab03cd2b2d5018ac64db30b7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.1.0"
|
||||||
flutter_markdown:
|
flutter_markdown:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -186,10 +226,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_secure_storage
|
name: flutter_secure_storage
|
||||||
sha256: "8496a89eea74e23f92581885f876455d9d460e71201405dffe5f55dfe1155864"
|
sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.2.1"
|
version: "9.2.2"
|
||||||
flutter_secure_storage_linux:
|
flutter_secure_storage_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -202,10 +242,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_secure_storage_macos
|
name: flutter_secure_storage_macos
|
||||||
sha256: b768a7dab26d6186b68e2831b3104f8968154f0f4fdbf66e7c2dd7bdf299daaf
|
sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.2"
|
||||||
flutter_secure_storage_platform_interface:
|
flutter_secure_storage_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -276,10 +316,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: go_router
|
name: go_router
|
||||||
sha256: "7685acd06244ba4be60f455c5cafe5790c63dc91fc03f7385b1e922a6b85b17c"
|
sha256: "6ad5662b014c06c20fa46ab78715c96b2222a7fe4f87bf77e0289592c2539e86"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.1.1"
|
version: "14.1.3"
|
||||||
http:
|
http:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -300,10 +340,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: image
|
name: image
|
||||||
sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e"
|
sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.7"
|
version: "4.2.0"
|
||||||
image_picker:
|
image_picker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -536,6 +576,54 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.2.1"
|
||||||
|
permission_handler:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: permission_handler
|
||||||
|
sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "11.3.1"
|
||||||
|
permission_handler_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_android
|
||||||
|
sha256: "8bb852cd759488893805c3161d0b2b5db55db52f773dbb014420b304055ba2c5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "12.0.6"
|
||||||
|
permission_handler_apple:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_apple
|
||||||
|
sha256: e9ad66020b89ff1b63908f247c2c6f931c6e62699b756ef8b3c4569350cd8662
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.4.4"
|
||||||
|
permission_handler_html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_html
|
||||||
|
sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.1"
|
||||||
|
permission_handler_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_platform_interface
|
||||||
|
sha256: "48d4fcf201a1dad93ee869ab0d4101d084f49136ec82a8a06ed9cfeacab9fd20"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.2.1"
|
||||||
|
permission_handler_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_windows
|
||||||
|
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.1"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -581,6 +669,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0"
|
version: "1.10.0"
|
||||||
|
sprintf:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sprintf
|
||||||
|
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.0"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -629,6 +725,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.6.1"
|
version: "3.6.1"
|
||||||
|
timezone:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: timezone
|
||||||
|
sha256: a6ccda4a69a442098b602c44e61a1e2b4bf6f5516e875bbf0f427d5df14745d5
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.3"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -649,10 +753,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_android
|
name: url_launcher_android
|
||||||
sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775"
|
sha256: "17cd5e205ea615e2c6ea7a77323a11712dffa0720a8a90540db57a01347f9ad9"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.1"
|
version: "6.3.2"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -701,6 +805,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.1"
|
||||||
|
uuid:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: uuid
|
||||||
|
sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.4.0"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -725,14 +837,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.1"
|
version: "0.5.1"
|
||||||
|
web_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web_socket
|
||||||
|
sha256: "217f49b5213796cb508d6a942a5dc604ce1cb6a0a6b3d8cb3f0c314f0ecea712"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.4"
|
||||||
|
web_socket_channel:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: web_socket_channel
|
||||||
|
sha256: a2d56211ee4d35d9b344d9d4ce60f362e4f5d1aafb988302906bd732bc731276
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
|
sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.5.0"
|
version: "5.5.1"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -750,5 +878,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "6.5.0"
|
version: "6.5.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.3.4 <4.0.0"
|
dart: ">=3.4.0 <4.0.0"
|
||||||
flutter: ">=3.19.0"
|
flutter: ">=3.19.0"
|
||||||
|
@ -52,6 +52,10 @@ dependencies:
|
|||||||
intl: ^0.19.0
|
intl: ^0.19.0
|
||||||
image: ^4.1.7
|
image: ^4.1.7
|
||||||
font_awesome_flutter: ^10.7.0
|
font_awesome_flutter: ^10.7.0
|
||||||
|
web_socket_channel: ^3.0.0
|
||||||
|
flutter_local_notifications: ^17.1.2
|
||||||
|
permission_handler: ^11.3.1
|
||||||
|
uuid: ^4.4.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <file_selector_windows/file_selector_windows.h>
|
#include <file_selector_windows/file_selector_windows.h>
|
||||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
|
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||||
#include <url_launcher_windows/url_launcher_windows.h>
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
@ -15,6 +16,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||||||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||||
|
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
file_selector_windows
|
file_selector_windows
|
||||||
flutter_secure_storage_windows
|
flutter_secure_storage_windows
|
||||||
|
permission_handler_windows
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user