From eda27359af10a79175694fa6b380ff6d27a70493 Mon Sep 17 00:00:00 2001 From: htylight Date: Sun, 10 Sep 2023 11:30:20 +0800 Subject: [PATCH] encrypt notification api and hive api --- lib/database/hive_database.dart | 149 ++++-- lib/main.dart | 11 +- lib/models/contact_model.dart | 6 +- lib/models/websocket_model.dart | 140 +----- lib/notification_api copy.dart | 228 --------- lib/notification_api.dart | 459 +++++++++++------- lib/router/router.dart | 10 +- lib/screens/chat/chat_screen.dart | 19 +- lib/screens/contact/contact_screen.dart | 22 +- .../contact_add/create_group_chat_screen.dart | 2 +- lib/screens/home/home_screen.dart | 52 +- lib/screens/more/more_screen.dart | 2 +- .../more/setting_screen/setting_screen.dart | 25 +- .../signin_signup/components/signup.dart | 4 +- .../components/username_signin.dart | 2 + pubspec.lock | 76 +-- 16 files changed, 518 insertions(+), 689 deletions(-) delete mode 100755 lib/notification_api copy.dart diff --git a/lib/database/hive_database.dart b/lib/database/hive_database.dart index 6364d4b..6ef5c33 100644 --- a/lib/database/hive_database.dart +++ b/lib/database/hive_database.dart @@ -8,57 +8,130 @@ import 'package:together_mobile/models/init_get_it.dart'; import 'package:together_mobile/models/user_model.dart'; import 'package:together_mobile/utils/app_dir.dart'; -void initDatabase() async { - List encryptionKeyUint8List = await getEncryptKey(); +class HiveDatabase { + static bool _isInitialised = false; - await Hive.initFlutter(await getBoxDir()); + static Future init() async { + if (_isInitialised) { + return; + } + await Hive.close(); - Box chatSettingBox = - await Hive.openBox('chat_setting'); + List encryptionKeyUint8List = await _getEncryptKey(); - final openedChats = chatSettingBox.values.where((element) => element.isOpen); + await Hive.initFlutter(await getBoxDir()); - for (var chatBox in openedChats) { - Hive.openBox( - 'message_${chatBox.contactId}', + Box chatSettingBox = + await Hive.openBox('chat_setting'); + + final openedChats = + chatSettingBox.values.where((element) => element.isOpen); + + for (var chatBox in openedChats) { + Hive.openBox( + 'message_${chatBox.contactId}', + encryptionCipher: HiveAesCipher(encryptionKeyUint8List), + compactionStrategy: (entries, deletedEntries) => entries > 200, + ); + } + + _isInitialised = true; + } + + static void registerAdapter() { + Hive.registerAdapter(ChatSettingAdapter()); + Hive.registerAdapter(MessageTAdapter()); + } + + static Future openNewMessageBox(String contactId, int type) async { + final encryptionKeyUint8List = await _getEncryptKey(); + + var chatSettingBox = Hive.box('chat_setting'); + chatSettingBox.add( + ChatSetting(contactId, type, false, false, false, DateTime.now(), 0)); + + await Hive.openBox( + 'message_$contactId', encryptionCipher: HiveAesCipher(encryptionKeyUint8List), compactionStrategy: (entries, deletedEntries) => entries > 200, ); } -} -void registerAdapter() { - Hive.registerAdapter(ChatSettingAdapter()); - Hive.registerAdapter(MessageTAdapter()); -} + static Future> _getEncryptKey() async { + final id = getIt.get().id; + const secureStorage = FlutterSecureStorage(); + final encryptionKeyString = await secureStorage.read(key: 'encryptKey:$id'); + if (encryptionKeyString == null) { + final key = Hive.generateSecureKey(); + await secureStorage.write( + key: 'encryptKey:$id', + value: base64Encode(key), + ); + } + String? key = await secureStorage.read(key: 'encryptKey:$id'); + final encryptionKeyUint8List = base64Url.decode(key!); -Future> getEncryptKey() async { - final id = getIt.get().id; - const secureStorage = FlutterSecureStorage(); - final encryptionKeyString = await secureStorage.read(key: 'encryptKey:$id'); - if (encryptionKeyString == null) { - final key = Hive.generateSecureKey(); - await secureStorage.write( - key: 'encryptKey:$id', - value: base64Encode(key), - ); + return encryptionKeyUint8List; } - // String? key = await secureStorage.read(key: 'encryptKey:$id'); - final encryptionKeyUint8List = base64Url.decode(encryptionKeyString!); - return encryptionKeyUint8List; + static void close() { + _isInitialised = false; + } } -Future openNewMessageBox(String contactId, int type) async { - final encryptionKeyUint8List = await getEncryptKey(); +// Future initDatabase() async { +// await Hive.close(); - var chatSettingBox = Hive.box('chat_setting'); - chatSettingBox.add( - ChatSetting(contactId, type, false, false, false, DateTime.now(), 0)); +// List encryptionKeyUint8List = await getEncryptKey(); - await Hive.openBox( - 'message_$contactId', - encryptionCipher: HiveAesCipher(encryptionKeyUint8List), - compactionStrategy: (entries, deletedEntries) => entries > 200, - ); -} +// await Hive.initFlutter(await getBoxDir()); + +// Box chatSettingBox = +// await Hive.openBox('chat_setting'); + +// final openedChats = chatSettingBox.values.where((element) => element.isOpen); + +// for (var chatBox in openedChats) { +// Hive.openBox( +// 'message_${chatBox.contactId}', +// encryptionCipher: HiveAesCipher(encryptionKeyUint8List), +// compactionStrategy: (entries, deletedEntries) => entries > 200, +// ); +// } +// } + +// void registerAdapter() { +// Hive.registerAdapter(ChatSettingAdapter()); +// Hive.registerAdapter(MessageTAdapter()); +// } + +// Future> getEncryptKey() async { +// final id = getIt.get().id; +// const secureStorage = FlutterSecureStorage(); +// final encryptionKeyString = await secureStorage.read(key: 'encryptKey:$id'); +// if (encryptionKeyString == null) { +// final key = Hive.generateSecureKey(); +// await secureStorage.write( +// key: 'encryptKey:$id', +// value: base64Encode(key), +// ); +// } +// String? key = await secureStorage.read(key: 'encryptKey:$id'); +// final encryptionKeyUint8List = base64Url.decode(key!); + +// return encryptionKeyUint8List; +// } + +// Future openNewMessageBox(String contactId, int type) async { +// final encryptionKeyUint8List = await getEncryptKey(); + +// var chatSettingBox = Hive.box('chat_setting'); +// chatSettingBox.add( +// ChatSetting(contactId, type, false, false, false, DateTime.now(), 0)); + +// await Hive.openBox( +// 'message_$contactId', +// encryptionCipher: HiveAesCipher(encryptionKeyUint8List), +// compactionStrategy: (entries, deletedEntries) => entries > 200, +// ); +// } diff --git a/lib/main.dart b/lib/main.dart index caef4e3..0c98900 100755 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,11 +6,10 @@ import 'package:together_mobile/router/router.dart'; import 'package:together_mobile/models/init_get_it.dart'; import 'notification_api.dart'; - void main() async { initGetIt(); - await initNotification(); - registerAdapter(); + await NotificationAPI.init(); + HiveDatabase.registerAdapter(); runApp(const Together()); } @@ -29,7 +28,11 @@ class Together extends StatelessWidget { // screen excluding the input widgets. builder: (context, child) { return GestureDetector( - onTap: () => FocusManager.instance.primaryFocus?.unfocus(), + onTap: () { + if (FocusManager.instance.primaryFocus != null) { + FocusManager.instance.primaryFocus!.unfocus(); + } + }, child: child, ); }, diff --git a/lib/models/contact_model.dart b/lib/models/contact_model.dart index 80ca194..ae96ec6 100644 --- a/lib/models/contact_model.dart +++ b/lib/models/contact_model.dart @@ -37,12 +37,16 @@ class Contact extends ChangeNotifier { friendCount += 1; friends[key] = FriendSetting.fromJson(value); }); - json['groupChats'].forEach((key, value) { groupChats[key] = GroupChatSetting.fromJson(value); groupChatCount += 1; }); + // json['groupChats'].forEach((key, value) { + // groupChats[key] = GroupChatSetting.fromJson(value); + // groupChatCount += 1; + // }); + friendGroups = List.from(json['friendGroups']); } diff --git a/lib/models/websocket_model.dart b/lib/models/websocket_model.dart index cd05ddd..9d44b9b 100644 --- a/lib/models/websocket_model.dart +++ b/lib/models/websocket_model.dart @@ -3,14 +3,11 @@ import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:hive_flutter/hive_flutter.dart'; -import 'package:together_mobile/models/route_state_model.dart'; -import 'package:together_mobile/request/user_profile.dart'; -import 'package:together_mobile/utils/app_dir.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; import 'package:web_socket_channel/status.dart' as status; +import 'package:together_mobile/models/route_state_model.dart'; import 'package:together_mobile/database/box_type.dart'; import 'package:together_mobile/models/apply_list_model.dart'; import 'package:together_mobile/models/contact_model.dart'; @@ -24,8 +21,6 @@ enum SocketStatus { error, } -Map)> messages = {}; - class WebSocketManager extends ChangeNotifier { late Uri wsUrl; late WebSocketChannel channel; @@ -46,7 +41,7 @@ class WebSocketManager extends ChangeNotifier { print('websocket connected <$channel>'); heartBeatInspect(); if (reconnectTimer != null) { - reconnectTimer!.cancel(); + // reconnectTimer!.cancel(); reconnectTimer = null; reconnectTimes = 0; } @@ -219,7 +214,7 @@ void receiveFriendMsg(Map msg) async { String avatar = getIt.get().friends[senderId]!.avatar; if (!getIt.get().isVisible) { - _showMessageNotifications( + NotificationAPI.showMessageNotifications( senderId: senderId, name: name, avatar: avatar, @@ -227,7 +222,7 @@ void receiveFriendMsg(Map msg) async { ); } else if (routeName == 'Message') { if (!getIt.get().query.containsValue(senderId)) { - _showMessageNotifications( + NotificationAPI.showMessageNotifications( senderId: senderId, name: name, avatar: avatar, @@ -235,7 +230,7 @@ void receiveFriendMsg(Map msg) async { ); } } else if (routeName != 'Chat') { - _showMessageNotifications( + NotificationAPI.showMessageNotifications( senderId: senderId, name: name, avatar: avatar, @@ -381,7 +376,7 @@ void receiveGroupChatMsg(Map msg) async { String routeName = getIt.get().currentPathName; if (!getIt.get().isVisible) { - _showMessageNotifications( + NotificationAPI.showMessageNotifications( senderId: senderId, name: name, avatar: avatar, @@ -390,7 +385,7 @@ void receiveGroupChatMsg(Map msg) async { ); } else if (routeName == 'Message') { if (!getIt.get().query.containsValue(senderId)) { - _showMessageNotifications( + NotificationAPI.showMessageNotifications( senderId: senderId, name: name, avatar: avatar, @@ -399,7 +394,7 @@ void receiveGroupChatMsg(Map msg) async { ); } } else if (routeName != 'Chat') { - _showMessageNotifications( + NotificationAPI.showMessageNotifications( senderId: senderId, name: name, avatar: avatar, @@ -408,122 +403,3 @@ void receiveGroupChatMsg(Map msg) async { ); } } - -Future _showMessageNotifications({ - required String senderId, - required String name, - required String avatar, - required String text, - String? groupChatId, -}) async { - String groupChannelId = 'TOGETHER_${getIt.get().id}'; - const String groupChannelName = 'together messages'; - - String avatarPath = ''; - - if (avatar.isNotEmpty) { - if (groupChatId == null) { - avatarPath = await getAvatarPath('user', avatar); - if (!File(avatarPath).existsSync()) { - File file = await File(avatarPath).create(recursive: true); - final bytes = await downloadUserAvatar(avatar); - await file.writeAsBytes(bytes); - } - } else { - // TODO: download group chat avatar. This will done after changing group chat avatar is implemented - } - } - - late Person person; - - if (avatarPath.isNotEmpty) { - person = Person( - name: name, - key: senderId, - bot: false, - icon: BitmapFilePathAndroidIcon(avatarPath), - ); - } else { - person = Person( - name: name, - key: senderId, - bot: false, - ); - } - - if (groupChatId == null) { - if (messages.containsKey(senderId)) { - messages[senderId]!.$2.add(Message(text, DateTime.now(), person)); - } else { - messages[senderId] = (++id, [Message(text, DateTime.now(), person)]); - } - } else { - if (messages.containsKey(groupChatId)) { - messages[groupChatId]!.$2.add(Message(text, DateTime.now(), person)); - } else { - messages[groupChatId] = (++id, [Message(text, DateTime.now(), person)]); - } - } - - late final MessagingStyleInformation messagingStyle; - if (groupChatId == null) { - messagingStyle = MessagingStyleInformation( - person, - messages: messages[senderId]!.$2, - conversationTitle: name, - ); - } else { - String groupChatName = - getIt.get().groupChats[groupChatId]!.nameRemark.isEmpty - ? getIt.get().groupChats[groupChatId]!.name - : getIt.get().groupChats[groupChatId]!.nameRemark; - messagingStyle = MessagingStyleInformation( - person, - messages: messages[groupChatId]!.$2, - conversationTitle: groupChatName, - groupConversation: true, - ); - } - - AndroidNotificationDetails androidNotificationDetails = - AndroidNotificationDetails( - groupChannelId, - groupChannelName, - importance: Importance.max, - priority: Priority.high, - category: AndroidNotificationCategory.message, - styleInformation: messagingStyle, - ); - - NotificationDetails notificationDetails = NotificationDetails( - android: androidNotificationDetails, - ); - - late String payload; - - if (groupChatId == null) { - payload = json.encode({ - 'event': 'friend-message', - 'friendId': senderId, - }); - await flutterLocalNotificationsPlugin.show( - messages[senderId]!.$1, - name, - text, - notificationDetails, - payload: payload, - ); - } else { - payload = json.encode({ - 'event': 'group-chat-message', - 'groupChatId': groupChatId, - }); - await flutterLocalNotificationsPlugin.show( - messages[groupChatId]!.$1, - name, - text, - notificationDetails, - payload: payload, - ); - } -} diff --git a/lib/notification_api copy.dart b/lib/notification_api copy.dart deleted file mode 100755 index 881a27d..0000000 --- a/lib/notification_api copy.dart +++ /dev/null @@ -1,228 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_local_notifications/flutter_local_notifications.dart'; -import 'package:flutter_timezone/flutter_timezone.dart'; - -import 'package:timezone/data/latest_all.dart' as tz; -import 'package:timezone/timezone.dart' as tz; - -class NotificationAPI { - static int id = 0; - static final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = - FlutterLocalNotificationsPlugin(); - static final StreamController - didReceiveNotificationStream = - StreamController.broadcast(); - final StreamController selectNotificationStream = - StreamController.broadcast(); - static Future init() async {} -} - -/// Before release build of app, read [Release build configuration] -/// https://pub-web.flutter-io.cn/packages/flutter_local_notifications - -int id = 0; - -/// Init notification plugin -final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = - FlutterLocalNotificationsPlugin(); - -/// Streams are created so that app can respond to notification-related events -/// since the plugin is initialised in the `main` function -/// Only be used in IOS? -final StreamController didReceiveNotificationStream = - StreamController.broadcast(); - -final StreamController selectNotificationStream = - StreamController.broadcast(); - -const MethodChannel platform = - MethodChannel('dexterx.dev/flutter_local_notifications_example'); - -const String portName = 'notification_send_port'; - -/// Here, the first argument is the id of notification and is common to all -/// methods that would result in a notification being shown. This is typically -/// set a unique value per notification as using the same id multiple times -/// would result in a notification being updated/overwritten. -class ReceivedNotification { - final int id; - final String? title; - final String? body; - final String? payload; - - const ReceivedNotification({ - required this.id, - required this.title, - required this.body, - required this.payload, - }); -} - -String? selectedNotificationPayload; - -late final NotificationAppLaunchDetails? notificationAppLaunchDetails; - -/// A notification action which triggers a url launch event -const String urlLanunchActionId = 'id_1'; - -/// A notification action which triggers a App navigation event -const String navigationActionId = 'id-3'; - -/// Defines a iOS/MacOS notification category for text input actions. -const String darwinNotificationCategoryText = 'textCategory'; - -/// Defines a iOS/MacOS notification category for plain actions. -const String darwinNotificationCategoryPlain = 'plainCategory'; - -@pragma('vm:entry-point') -void notificationTapBackground(NotificationResponse notificationResponse) { - print('notification(${notificationResponse.id}) action tapped: ' - '${notificationResponse.actionId} with' - ' payload: ${notificationResponse.payload}'); - - if (notificationResponse.input?.isNotEmpty ?? false) { - print( - 'notification action tapped with input: ${notificationResponse.input}'); - } -} - -/// Scheduling notifications now requires developers to specify a date and -/// time relative to a specific time zone. -/// This is to solve issues with daylight saving time that existed in the -/// schedule method that is now deprecated. -Future _configureLocalTimeZone() async { - if (kIsWeb || Platform.isLinux) { - return; - } - - tz.initializeTimeZones(); - final String timeZoneName = await FlutterTimezone.getLocalTimezone(); - tz.setLocalLocation(tz.getLocation(timeZoneName)); -} - -Future initNotification() async { - WidgetsFlutterBinding.ensureInitialized(); - - await _configureLocalTimeZone(); - - // not null if platform is Linux - notificationAppLaunchDetails = !kIsWeb && Platform.isLinux - ? null - // Use the getNotificationAppLaunchDetails method when the app starts - // if you need to handle when a notification triggering the launch for - // an app e.g. change the home route of the app for deep-linking. - : await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails(); - - String initRouteName = 'Home'; - - if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) { - // If the app was launched via notification - // (e.g. taps a notification out of the app). - - // route change depends on what type message is. Such as receiving a chat - // message, this route should change to message screen, and receiving a apply - // request shuold change to apply screen - initRouteName = 'Message'; // TODO: Set the route according to message type - } - - const AndroidInitializationSettings androidInitializationSettings = - AndroidInitializationSettings('app_icon'); - - final List darwinNotificationCategories = - [ - DarwinNotificationCategory( - darwinNotificationCategoryText, - actions: [ - DarwinNotificationAction.text( - 'text_1', - 'action 1', - buttonTitle: 'send', - placeholder: 'Placeholder', - ), - ], - ), - DarwinNotificationCategory( - darwinNotificationCategoryPlain, - actions: [ - DarwinNotificationAction.plain('id_1', 'Action 1'), - DarwinNotificationAction.plain( - 'id_2', - 'Action 2 (destructive)', - options: { - DarwinNotificationActionOption.destructive, - }, - ), - DarwinNotificationAction.plain( - navigationActionId, - 'Action 3 (foreground)', - options: { - DarwinNotificationActionOption.foreground, - }, - ), - DarwinNotificationAction.plain( - 'id_4', - 'Action 4 (auth required)', - options: { - DarwinNotificationActionOption.authenticationRequired, - }, - ), - ], - options: { - DarwinNotificationCategoryOption.hiddenPreviewShowTitle, - }, - ), - ]; - - /// Note: permissions aren't requested here just to demonstrate that can be - /// done later - final DarwinInitializationSettings darwinInitializationSettings = - DarwinInitializationSettings( - requestAlertPermission: false, - requestBadgePermission: false, - requestSoundPermission: false, - onDidReceiveLocalNotification: ( - int id, - String? title, - String? body, - String? payload, - ) async { - didReceiveNotificationStream.add( - ReceivedNotification( - id: id, - title: title, - body: body, - payload: payload, - ), - ); - }, - notificationCategories: darwinNotificationCategories, - ); - - final InitializationSettings initializationSettings = InitializationSettings( - android: androidInitializationSettings, - iOS: darwinInitializationSettings, - ); - - await flutterLocalNotificationsPlugin.initialize( - initializationSettings, - // This callback fire when a notification has tapped - onDidReceiveNotificationResponse: (NotificationResponse notificationRes) { - switch (notificationRes.notificationResponseType) { - case NotificationResponseType.selectedNotification: - selectNotificationStream.add(notificationRes.payload); - break; - case NotificationResponseType.selectedNotificationAction: - if (notificationRes.actionId == navigationActionId) { - selectNotificationStream.add(notificationRes.payload); - } - break; - } - }, - onDidReceiveBackgroundNotificationResponse: notificationTapBackground, - ); -} diff --git a/lib/notification_api.dart b/lib/notification_api.dart index 2fa6553..80d3654 100755 --- a/lib/notification_api.dart +++ b/lib/notification_api.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'dart:io'; import 'package:flutter/cupertino.dart'; @@ -10,28 +11,11 @@ import 'package:flutter_timezone/flutter_timezone.dart'; import 'package:timezone/data/latest_all.dart' as tz; import 'package:timezone/timezone.dart' as tz; -/// Before release build of app, read [Release build configuration] -/// https://pub-web.flutter-io.cn/packages/flutter_local_notifications - -int id = 0; - -/// Init notification plugin -final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = - FlutterLocalNotificationsPlugin(); - -/// Streams are created so that app can respond to notification-related events -/// since the plugin is initialised in the `main` function -/// Only be used in IOS? -final StreamController didReceiveNotificationStream = - StreamController.broadcast(); - -final StreamController selectNotificationStream = - StreamController.broadcast(); - -const MethodChannel platform = - MethodChannel('dexterx.dev/flutter_local_notifications_example'); - -const String portName = 'notification_send_port'; +import 'models/init_get_it.dart'; +import 'models/contact_model.dart'; +import 'models/user_model.dart'; +import 'request/user_profile.dart'; +import 'utils/app_dir.dart'; /// Here, the first argument is the id of notification and is common to all /// methods that would result in a notification being shown. This is typically @@ -51,166 +35,317 @@ class ReceivedNotification { }); } -String? selectedNotificationPayload; +/// Before release build of app, read [Release build configuration] +/// https://pub-web.flutter-io.cn/packages/flutter_local_notifications -late final NotificationAppLaunchDetails? notificationAppLaunchDetails; +class NotificationAPI { + static int id = 0; -/// A notification action which triggers a url launch event -const String urlLanunchActionId = 'id_1'; + static Map)> messages = {}; -/// A notification action which triggers a App navigation event -const String navigationActionId = 'id-3'; + /// Init notification plugin + static final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = + FlutterLocalNotificationsPlugin(); -/// Defines a iOS/MacOS notification category for text input actions. -const String darwinNotificationCategoryText = 'textCategory'; + /// Streams are created so that app can respond to notification-related events + /// since the plugin is initialised in the `main` function + /// Only be used in IOS? + static final StreamController + didReceiveNotificationStream = + StreamController.broadcast(); -/// Defines a iOS/MacOS notification category for plain actions. -const String darwinNotificationCategoryPlain = 'plainCategory'; + static final StreamController selectNotificationStream = + StreamController.broadcast(); -@pragma('vm:entry-point') -void notificationTapBackground(NotificationResponse notificationResponse) { - print('notification(${notificationResponse.id}) action tapped: ' - '${notificationResponse.actionId} with' - ' payload: ${notificationResponse.payload}'); + static const MethodChannel platform = + MethodChannel('dexterx.dev/flutter_local_notifications_example'); - if (notificationResponse.input?.isNotEmpty ?? false) { - print( - 'notification action tapped with input: ${notificationResponse.input}'); - } -} + static const String portName = 'notification_send_port'; -/// Scheduling notifications now requires developers to specify a date and -/// time relative to a specific time zone. -/// This is to solve issues with daylight saving time that existed in the -/// schedule method that is now deprecated. -Future _configureLocalTimeZone() async { - if (kIsWeb || Platform.isLinux) { - return; - } + String? selectedNotificationPayload; - tz.initializeTimeZones(); - final String timeZoneName = await FlutterTimezone.getLocalTimezone(); - tz.setLocalLocation(tz.getLocation(timeZoneName)); -} + static late final NotificationAppLaunchDetails? notificationAppLaunchDetails; -Future initNotification() async { - WidgetsFlutterBinding.ensureInitialized(); + /// A notification action which triggers a url launch event + static const String urlLanunchActionId = 'id_1'; - await _configureLocalTimeZone(); + /// A notification action which triggers a App navigation event + static const String navigationActionId = 'id-3'; - // not null if platform is Linux - notificationAppLaunchDetails = !kIsWeb && Platform.isLinux - ? null - // Use the getNotificationAppLaunchDetails method when the app starts - // if you need to handle when a notification triggering the launch for - // an app e.g. change the home route of the app for deep-linking. - : await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails(); + /// Defines a iOS/MacOS notification category for text input actions. + static const String darwinNotificationCategoryText = 'textCategory'; - String initRouteName = 'Home'; + /// Defines a iOS/MacOS notification category for plain actions. + static const String darwinNotificationCategoryPlain = 'plainCategory'; - if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) { - // If the app was launched via notification - // (e.g. taps a notification out of the app). + static Future init() async { + WidgetsFlutterBinding.ensureInitialized(); - // route change depends on what type message is. Such as receiving a chat - // message, this route should change to message screen, and receiving a apply - // request shuold change to apply screen - initRouteName = 'Message'; // TODO: Set the route according to message type - } + await _configureLocalTimeZone(); - const AndroidInitializationSettings androidInitializationSettings = - AndroidInitializationSettings('app_icon'); + // not null if platform is Linux + notificationAppLaunchDetails = !kIsWeb && Platform.isLinux + ? null + // Use the getNotificationAppLaunchDetails method when the app starts + // if you need to handle when a notification triggering the launch for + // an app e.g. change the home route of the app for deep-linking. + : await flutterLocalNotificationsPlugin + .getNotificationAppLaunchDetails(); - final List darwinNotificationCategories = - [ - DarwinNotificationCategory( - darwinNotificationCategoryText, - actions: [ - DarwinNotificationAction.text( - 'text_1', - 'action 1', - buttonTitle: 'send', - placeholder: 'Placeholder', - ), - ], - ), - DarwinNotificationCategory( - darwinNotificationCategoryPlain, - actions: [ - DarwinNotificationAction.plain('id_1', 'Action 1'), - DarwinNotificationAction.plain( - 'id_2', - 'Action 2 (destructive)', - options: { - DarwinNotificationActionOption.destructive, - }, - ), - DarwinNotificationAction.plain( - navigationActionId, - 'Action 3 (foreground)', - options: { - DarwinNotificationActionOption.foreground, - }, - ), - DarwinNotificationAction.plain( - 'id_4', - 'Action 4 (auth required)', - options: { - DarwinNotificationActionOption.authenticationRequired, - }, - ), - ], - options: { - DarwinNotificationCategoryOption.hiddenPreviewShowTitle, + String initRouteName = 'Home'; + + if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) { + // If the app was launched via notification + // (e.g. taps a notification out of the app). + + // route change depends on what type message is. Such as receiving a chat + // message, this route should change to message screen, and receiving a apply + // request shuold change to apply screen + initRouteName = + 'Message'; // TODO: Set the route according to message type + } + + const AndroidInitializationSettings androidInitializationSettings = + AndroidInitializationSettings('app_icon'); + + final List darwinNotificationCategories = + [ + DarwinNotificationCategory( + darwinNotificationCategoryText, + actions: [ + DarwinNotificationAction.text( + 'text_1', + 'action 1', + buttonTitle: 'send', + placeholder: 'Placeholder', + ), + ], + ), + DarwinNotificationCategory( + darwinNotificationCategoryPlain, + actions: [ + DarwinNotificationAction.plain('id_1', 'Action 1'), + DarwinNotificationAction.plain( + 'id_2', + 'Action 2 (destructive)', + options: { + DarwinNotificationActionOption.destructive, + }, + ), + DarwinNotificationAction.plain( + navigationActionId, + 'Action 3 (foreground)', + options: { + DarwinNotificationActionOption.foreground, + }, + ), + DarwinNotificationAction.plain( + 'id_4', + 'Action 4 (auth required)', + options: { + DarwinNotificationActionOption.authenticationRequired, + }, + ), + ], + options: { + DarwinNotificationCategoryOption.hiddenPreviewShowTitle, + }, + ), + ]; + + /// Note: permissions aren't requested here just to demonstrate that can be + /// done later + final DarwinInitializationSettings darwinInitializationSettings = + DarwinInitializationSettings( + requestAlertPermission: false, + requestBadgePermission: false, + requestSoundPermission: false, + onDidReceiveLocalNotification: ( + int id, + String? title, + String? body, + String? payload, + ) async { + didReceiveNotificationStream.add( + ReceivedNotification( + id: id, + title: title, + body: body, + payload: payload, + ), + ); }, - ), - ]; + notificationCategories: darwinNotificationCategories, + ); - /// Note: permissions aren't requested here just to demonstrate that can be - /// done later - final DarwinInitializationSettings darwinInitializationSettings = - DarwinInitializationSettings( - requestAlertPermission: false, - requestBadgePermission: false, - requestSoundPermission: false, - onDidReceiveLocalNotification: ( - int id, - String? title, - String? body, - String? payload, - ) async { - didReceiveNotificationStream.add( - ReceivedNotification( - id: id, - title: title, - body: body, - payload: payload, - ), - ); - }, - notificationCategories: darwinNotificationCategories, - ); + final InitializationSettings initializationSettings = + InitializationSettings( + android: androidInitializationSettings, + iOS: darwinInitializationSettings, + ); - final InitializationSettings initializationSettings = InitializationSettings( - android: androidInitializationSettings, - iOS: darwinInitializationSettings, - ); - - await flutterLocalNotificationsPlugin.initialize( - initializationSettings, - // This callback fire when a notification has tapped - onDidReceiveNotificationResponse: (NotificationResponse notificationRes) { - switch (notificationRes.notificationResponseType) { - case NotificationResponseType.selectedNotification: - selectNotificationStream.add(notificationRes.payload); - break; - case NotificationResponseType.selectedNotificationAction: - if (notificationRes.actionId == navigationActionId) { + await flutterLocalNotificationsPlugin.initialize( + initializationSettings, + // This callback fire when a notification has tapped + onDidReceiveNotificationResponse: (NotificationResponse notificationRes) { + switch (notificationRes.notificationResponseType) { + case NotificationResponseType.selectedNotification: selectNotificationStream.add(notificationRes.payload); - } - break; + break; + case NotificationResponseType.selectedNotificationAction: + if (notificationRes.actionId == navigationActionId) { + selectNotificationStream.add(notificationRes.payload); + } + break; + } + }, + onDidReceiveBackgroundNotificationResponse: _notificationTapBackground, + ); + } + + @pragma('vm:entry-point') + static void _notificationTapBackground( + NotificationResponse notificationResponse) { + print('notification(${notificationResponse.id}) action tapped: ' + '${notificationResponse.actionId} with' + ' payload: ${notificationResponse.payload}'); + + if (notificationResponse.input?.isNotEmpty ?? false) { + print( + 'notification action tapped with input: ${notificationResponse.input}'); + } + } + + /// Scheduling notifications now requires developers to specify a date and + /// time relative to a specific time zone. + /// This is to solve issues with daylight saving time that existed in the + /// schedule method that is now deprecated. + static Future _configureLocalTimeZone() async { + if (kIsWeb || Platform.isLinux) { + return; + } + + tz.initializeTimeZones(); + final String timeZoneName = await FlutterTimezone.getLocalTimezone(); + tz.setLocalLocation(tz.getLocation(timeZoneName)); + } + + static Future showMessageNotifications({ + required String senderId, + required String name, + required String avatar, + required String text, + String? groupChatId, + }) async { + String groupChannelId = 'TOGETHER_${getIt.get().id}'; + const String groupChannelName = 'together messages'; + + String avatarPath = ''; + + if (avatar.isNotEmpty) { + if (groupChatId == null) { + avatarPath = await getAvatarPath('user', avatar); + if (!File(avatarPath).existsSync()) { + File file = await File(avatarPath).create(recursive: true); + final bytes = await downloadUserAvatar(avatar); + await file.writeAsBytes(bytes); + } + } else { + // TODO: download group chat avatar. This will done after changing group chat avatar is implemented } - }, - onDidReceiveBackgroundNotificationResponse: notificationTapBackground, - ); + } + + late Person person; + + if (avatarPath.isNotEmpty) { + person = Person( + name: name, + key: senderId, + bot: false, + icon: BitmapFilePathAndroidIcon(avatarPath), + ); + } else { + person = Person( + name: name, + key: senderId, + bot: false, + ); + } + + if (groupChatId == null) { + if (messages.containsKey(senderId)) { + messages[senderId]!.$2.add(Message(text, DateTime.now(), person)); + } else { + messages[senderId] = (++id, [Message(text, DateTime.now(), person)]); + } + } else { + if (messages.containsKey(groupChatId)) { + messages[groupChatId]!.$2.add(Message(text, DateTime.now(), person)); + } else { + messages[groupChatId] = (++id, [Message(text, DateTime.now(), person)]); + } + } + + late final MessagingStyleInformation messagingStyle; + if (groupChatId == null) { + messagingStyle = MessagingStyleInformation( + person, + messages: messages[senderId]!.$2, + conversationTitle: name, + ); + } else { + String groupChatName = + getIt.get().groupChats[groupChatId]!.nameRemark.isEmpty + ? getIt.get().groupChats[groupChatId]!.name + : getIt.get().groupChats[groupChatId]!.nameRemark; + messagingStyle = MessagingStyleInformation( + person, + messages: messages[groupChatId]!.$2, + conversationTitle: groupChatName, + groupConversation: true, + ); + } + + AndroidNotificationDetails androidNotificationDetails = + AndroidNotificationDetails( + groupChannelId, + groupChannelName, + importance: Importance.max, + priority: Priority.high, + category: AndroidNotificationCategory.message, + styleInformation: messagingStyle, + ); + + NotificationDetails notificationDetails = NotificationDetails( + android: androidNotificationDetails, + ); + + late String payload; + + if (groupChatId == null) { + payload = json.encode({ + 'event': 'friend-message', + 'friendId': senderId, + }); + await flutterLocalNotificationsPlugin.show( + messages[senderId]!.$1, + name, + text, + notificationDetails, + payload: payload, + ); + } else { + payload = json.encode({ + 'event': 'group-chat-message', + 'groupChatId': groupChatId, + }); + await flutterLocalNotificationsPlugin.show( + messages[groupChatId]!.$1, + name, + text, + notificationDetails, + payload: payload, + ); + } + } } diff --git a/lib/router/router.dart b/lib/router/router.dart index 6711fc8..587c5d0 100755 --- a/lib/router/router.dart +++ b/lib/router/router.dart @@ -1,6 +1,7 @@ import 'package:flutter/widgets.dart'; import 'package:go_router/go_router.dart'; +import 'package:together_mobile/database/hive_database.dart'; import 'router_key.dart'; import 'chat_router.dart'; @@ -18,14 +19,13 @@ import 'package:together_mobile/notification_api.dart'; // Used to listen the page transition // https://blog.csdn.net/sinat_17775997/article/details/106570011#:~:text=Flutter%E7%9B%91%E5%90%AC%E8%B7%AF%E7%94%B1%E8%BF%94%E5%9B%9E%20%E6%96%B9%E6%B3%95%E4%B8%80%EF%BC%9A,push%E6%96%B9%E6%B3%95.then%EF%BC%8C%E6%94%AF%E6%8C%81%E6%89%8B%E5%8A%BF%E8%BF%94%E5%9B%9E%E2%9C%85%EF%BC%8C%E5%8F%AF%E4%BB%A5%E8%8E%B7%E5%8F%96%E4%BC%A0%E5%9B%9E%E5%8F%82%E6%95%B0%E2%9C%85%E3%80%82%20%E6%96%B9%E6%B3%95%E4%BA%8C%EF%BC%9A%20didPopNext%EF%BC%8C%E6%94%AF%E6%8C%81%E6%89%8B%E5%8A%BF%E8%BF%94%E5%9B%9E%E2%9C%85%EF%BC%8C%E4%BD%86%E5%9B%9E%E4%BC%A0%E5%8F%82%E6%95%B0%E8%8E%B7%E5%8F%96%E4%B8%8D%E5%88%B0%E2%9D%8C%E3%80%82 -final RouteObserver> routeObserver = - RouteObserver>(); +// final RouteObserver> routeObserver = +// RouteObserver>(); final GoRouter router = GoRouter( initialLocation: '/welcome', navigatorKey: rootNavigatorKey, debugLogDiagnostics: true, - observers: [routeObserver], routes: [ GoRoute( path: '/welcome', @@ -39,6 +39,7 @@ final GoRouter router = GoRouter( if (res['code'] == 10200) { await getIt.get().updateToken(res['token']); getIt.get().init(res['data']); + await HiveDatabase.init(); return '/chat'; } } @@ -109,7 +110,8 @@ final GoRouter router = GoRouter( initIndex = 2; } return HomeScreenWithNavBar( - notificationAppLaunchDetails: notificationAppLaunchDetails, + notificationAppLaunchDetails: + NotificationAPI.notificationAppLaunchDetails, initIndex: initIndex, child: child, ); diff --git a/lib/screens/chat/chat_screen.dart b/lib/screens/chat/chat_screen.dart index c9d391a..83b285a 100755 --- a/lib/screens/chat/chat_screen.dart +++ b/lib/screens/chat/chat_screen.dart @@ -5,7 +5,6 @@ import 'package:hive_flutter/hive_flutter.dart'; import 'package:together_mobile/database/hive_database.dart'; import 'package:together_mobile/screens/chat/components/group_chat_chat_tile.dart'; -import 'package:together_mobile/utils/app_dir.dart'; import 'components/friend_chat_tile.dart'; import 'components/add_menu.dart'; import 'package:together_mobile/database/box_type.dart'; @@ -30,23 +29,7 @@ class ChatScreen extends StatefulWidget { class _ChatScreenState extends State { Future _initData() async { if (!getIt.get().isInitialised) { - List encryptionKeyUint8List = await getEncryptKey(); - - await Hive.initFlutter(await getBoxDir()); - - Box chatSettingBox = - await Hive.openBox('chat_setting'); - - final openedChats = - chatSettingBox.values.where((element) => element.isOpen); - - for (var chatBox in openedChats) { - await Hive.openBox( - 'message_${chatBox.contactId}', - encryptionCipher: HiveAesCipher(encryptionKeyUint8List), - compactionStrategy: (entries, deletedEntries) => entries > 200, - ); - } + await HiveDatabase.init(); getIt.get().connect(getIt.get().id); diff --git a/lib/screens/contact/contact_screen.dart b/lib/screens/contact/contact_screen.dart index e1f407f..3f5a1f8 100755 --- a/lib/screens/contact/contact_screen.dart +++ b/lib/screens/contact/contact_screen.dart @@ -8,10 +8,8 @@ import 'package:get_it_mixin/get_it_mixin.dart'; import 'package:together_mobile/common/constants.dart'; import 'package:together_mobile/models/apply_list_model.dart'; import 'package:together_mobile/models/contact_model.dart'; -import 'package:together_mobile/models/route_state_model.dart'; import 'package:together_mobile/models/user_model.dart'; import 'package:together_mobile/request/server.dart'; -import 'package:together_mobile/router/router.dart'; import 'package:together_mobile/screens/contact/components/friend_group.dart'; import 'package:together_mobile/screens/contact/components/friend_tile.dart'; import 'package:together_mobile/screens/contact/components/group_chat_tile.dart'; @@ -23,31 +21,13 @@ class ContactScreen extends StatefulWidget with GetItStatefulWidgetMixin { State createState() => _ContactScreenState(); } -class _ContactScreenState extends State - with GetItStateMixin, RouteAware { +class _ContactScreenState extends State with GetItStateMixin { final Map _shows = { 'friendGroups': true, 'allFriends': false, 'groupChats': false }; - @override - void didChangeDependencies() { - super.didChangeDependencies(); - routeObserver.subscribe(this, ModalRoute.of(context)!); - } - - @override - void didPush() { - get().changeRoute('Contact'); - } - - @override - void dispose() { - routeObserver.unsubscribe(this); - super.dispose(); - } - @override Widget build(BuildContext context) { // Seems like `watchOnly` cannot watch collecion type. diff --git a/lib/screens/contact_add/create_group_chat_screen.dart b/lib/screens/contact_add/create_group_chat_screen.dart index b3fafb8..46ba0be 100644 --- a/lib/screens/contact_add/create_group_chat_screen.dart +++ b/lib/screens/contact_add/create_group_chat_screen.dart @@ -196,7 +196,7 @@ class _CreateGroupChatScreenState extends State { 'groupGhat': res['data'] }), ); - await openNewMessageBox(id, 1); + await HiveDatabase.openNewMessageBox(id, 1); // ignore: use_build_context_synchronously context.goNamed( 'Message', diff --git a/lib/screens/home/home_screen.dart b/lib/screens/home/home_screen.dart index b618c40..a8623ae 100755 --- a/lib/screens/home/home_screen.dart +++ b/lib/screens/home/home_screen.dart @@ -15,9 +15,7 @@ import 'package:together_mobile/models/apply_list_model.dart'; import 'package:together_mobile/models/init_get_it.dart'; import 'package:together_mobile/models/route_state_model.dart'; import 'package:together_mobile/models/user_model.dart'; -import 'package:together_mobile/models/websocket_model.dart'; import 'package:together_mobile/notification_api.dart'; -import 'package:together_mobile/utils/app_dir.dart'; class HomeScreenWithNavBar extends StatefulWidget with GetItStatefulWidgetMixin { @@ -47,23 +45,7 @@ class _HomeScreenWithNavBarState extends State with GetItStateMixin, WidgetsBindingObserver { Future _openBox() async { if (!getIt.get().isInitialised) { - List encryptionKeyUint8List = await getEncryptKey(); - - await Hive.initFlutter(await getBoxDir()); - - Box chatSettingBox = - await Hive.openBox('chat_setting'); - - final openedChats = - chatSettingBox.values.where((element) => element.isOpen); - - for (var chatBox in openedChats) { - await Hive.openBox( - 'message_${chatBox.contactId}', - encryptionCipher: HiveAesCipher(encryptionKeyUint8List), - compactionStrategy: (entries, deletedEntries) => entries > 200, - ); - } + await HiveDatabase.init(); } return Future(() => true); } @@ -84,8 +66,8 @@ class _HomeScreenWithNavBarState extends State void dispose() { super.dispose(); WidgetsBinding.instance.removeObserver(this); - didReceiveNotificationStream.close(); - selectNotificationStream.close(); + NotificationAPI.didReceiveNotificationStream.close(); + NotificationAPI.selectNotificationStream.close(); } @override @@ -94,8 +76,8 @@ class _HomeScreenWithNavBarState extends State switch (state) { case AppLifecycleState.resumed: print('应用进入前台=================='); - messages.clear(); - flutterLocalNotificationsPlugin.cancelAll(); + NotificationAPI.messages.clear(); + NotificationAPI.flutterLocalNotificationsPlugin.cancelAll(); getIt.get().changeVisibility(true); break; case AppLifecycleState.inactive: @@ -110,12 +92,14 @@ class _HomeScreenWithNavBarState extends State print('当前页面即将退出==================='); getIt.get().changeVisibility(false); break; + case AppLifecycleState.hidden: + print("当前应用被隐藏======================="); } } Future _isAndroidPermissionGranted() async { if (Platform.isAndroid) { - final bool granted = await flutterLocalNotificationsPlugin + final bool granted = await NotificationAPI.flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.areNotificationsEnabled() ?? @@ -129,7 +113,7 @@ class _HomeScreenWithNavBarState extends State Future _requestPermissions() async { if (Platform.isIOS || Platform.isMacOS) { - await flutterLocalNotificationsPlugin + await NotificationAPI.flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< IOSFlutterLocalNotificationsPlugin>() ?.requestPermissions( @@ -137,7 +121,7 @@ class _HomeScreenWithNavBarState extends State badge: true, sound: true, ); - await flutterLocalNotificationsPlugin + await NotificationAPI.flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< MacOSFlutterLocalNotificationsPlugin>() ?.requestPermissions( @@ -147,8 +131,9 @@ class _HomeScreenWithNavBarState extends State ); } else if (Platform.isAndroid) { final AndroidFlutterLocalNotificationsPlugin? androidImplementation = - flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>(); + NotificationAPI.flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>(); final bool? grantedNotificationPermission = await androidImplementation?.requestPermission(); @@ -159,7 +144,7 @@ class _HomeScreenWithNavBarState extends State } void _configureDidReceiveLocalNotificationSubject() { - didReceiveNotificationStream.stream.listen( + NotificationAPI.didReceiveNotificationStream.stream.listen( (ReceivedNotification? receivedNotification) async { await showDialog( context: context, @@ -184,8 +169,9 @@ class _HomeScreenWithNavBarState extends State void _configureSelectNotificationSubject() { // this listener will be call after taps a notification - selectNotificationStream.stream.listen((String? payload) async { - messages.clear(); + NotificationAPI.selectNotificationStream.stream + .listen((String? payload) async { + NotificationAPI.messages.clear(); if (payload != null) { Map payloadData = json.decode(payload); switch (payloadData['event']) { @@ -288,8 +274,8 @@ class _HomeScreenWithNavBarState extends State void _onItemTapped(int index, BuildContext context) { switch (index) { case 0: - messages.clear(); - flutterLocalNotificationsPlugin.cancelAll(); + NotificationAPI.messages.clear(); + NotificationAPI.flutterLocalNotificationsPlugin.cancelAll(); GoRouter.of(context).go('/chat'); break; diff --git a/lib/screens/more/more_screen.dart b/lib/screens/more/more_screen.dart index e6b96b0..6b1088c 100755 --- a/lib/screens/more/more_screen.dart +++ b/lib/screens/more/more_screen.dart @@ -86,7 +86,7 @@ class _MoreScreenState extends State { ), ListTile( onTap: () { - context.pushNamed('Setting'); + context.goNamed('Setting'); }, contentPadding: const EdgeInsets.all(10), visualDensity: VisualDensity.compact, diff --git a/lib/screens/more/setting_screen/setting_screen.dart b/lib/screens/more/setting_screen/setting_screen.dart index 806a94c..cae8020 100644 --- a/lib/screens/more/setting_screen/setting_screen.dart +++ b/lib/screens/more/setting_screen/setting_screen.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -import 'package:hive_flutter/hive_flutter.dart'; +import 'package:together_mobile/database/hive_database.dart'; import 'package:together_mobile/models/apply_list_model.dart'; import 'package:together_mobile/models/contact_model.dart'; @@ -33,16 +33,21 @@ class SettingScreen extends StatelessWidget { child: TextButton( onPressed: () async { await getIt.get().clear(); - getIt.get().disconnect(); - getIt.get().clear(); - getIt.get().clear(); - getIt.get().clear(); - getIt.get().clear(); - getIt.get().clear(); - // Hive.deleteFromDisk(); - Hive.close(); - // ignore: use_build_context_synchronously context.go('/welcome'); + // ignore: use_build_context_synchronously + Timer( + const Duration(milliseconds: 200), + () { + getIt.get().disconnect(); + getIt.get().clear(); + getIt.get().clear(); + getIt.get().clear(); + getIt.get().clear(); + getIt.get().clear(); + // Hive.deleteFromDisk(); + HiveDatabase.close(); + }, + ); }, child: const Text('退出登陆'), ), diff --git a/lib/screens/signin_signup/components/signup.dart b/lib/screens/signin_signup/components/signup.dart index fc83f1e..02d50b9 100755 --- a/lib/screens/signin_signup/components/signup.dart +++ b/lib/screens/signin_signup/components/signup.dart @@ -31,7 +31,7 @@ class _SignupBodyState extends State { 'code': false, }; - late Timer _timer; + Timer? _timer; int _count = 60; bool _isGetCode = false; @@ -46,7 +46,7 @@ class _SignupBodyState extends State { void dispose() { usernameController.dispose(); passwordController.dispose(); - _timer.cancel(); + _timer?.cancel(); super.dispose(); } diff --git a/lib/screens/signin_signup/components/username_signin.dart b/lib/screens/signin_signup/components/username_signin.dart index 8c98eeb..e4bb0c5 100755 --- a/lib/screens/signin_signup/components/username_signin.dart +++ b/lib/screens/signin_signup/components/username_signin.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:cherry_toast/cherry_toast.dart'; import 'package:go_router/go_router.dart'; import 'package:device_info_plus/device_info_plus.dart'; +import 'package:together_mobile/database/hive_database.dart'; import 'package:together_mobile/models/token_model.dart'; import 'common_widgets.dart'; @@ -139,6 +140,7 @@ class _UsernameSigninBodyState extends State { getIt.get().init(); getIt.get().updateToken(res['token']); + await HiveDatabase.init(); // ignore: use_build_context_synchronously context.go('/chat'); } else { diff --git a/pubspec.lock b/pubspec.lock index 79ccbcb..ec5e2af 100755 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: "direct main" description: name: archive - sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a" + sha256: "49b1fad315e57ab0bbc15bcbb874e83116a1d78f77ebd500a4af6c9407d6b28e" url: "https://pub.flutter-io.cn" source: hosted - version: "3.3.7" + version: "3.3.8" args: dependency: transitive description: @@ -85,10 +85,10 @@ packages: dependency: transitive description: name: build_resolvers - sha256: "6c4dd11d05d056e76320b828a1db0fc01ccd376922526f8e9d6c796a5adbac20" + sha256: d912852cce27c9e80a93603db721c267716894462e7033165178b91138587972 url: "https://pub.flutter-io.cn" source: hosted - version: "2.2.1" + version: "2.3.2" build_runner: dependency: "direct dev" description: @@ -189,10 +189,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.flutter-io.cn" source: hosted - version: "1.17.1" + version: "1.17.2" colorfilter_generator: dependency: transitive description: @@ -293,18 +293,18 @@ packages: dependency: transitive description: name: extended_image - sha256: e77d18f956649ba6e5ecebd0cb68542120886336a75ee673788145bd4c3f0767 + sha256: b4d72a27851751cfadaf048936d42939db7cd66c08fdcfe651eeaa1179714ee6 url: "https://pub.flutter-io.cn" source: hosted - version: "8.0.2" + version: "8.1.1" extended_image_library: dependency: transitive description: name: extended_image_library - sha256: bb8d08c504ebc73d476ec1c99451a61f12e95538869e734fc4f55a3a2d5c98ec + sha256: "8bf87c0b14dcb59200c923a9a3952304e4732a0901e40811428834ef39018ee1" url: "https://pub.flutter-io.cn" source: hosted - version: "3.5.3" + version: "3.6.0" fake_async: dependency: transitive description: @@ -596,18 +596,18 @@ packages: dependency: "direct main" description: name: get_it - sha256: "529de303c739fca98cd7ece5fca500d8ff89649f1bb4b4e94fb20954abcd7468" + sha256: f79870884de16d689cf9a7d15eedf31ed61d750e813c538a6efb92660fea83c3 url: "https://pub.flutter-io.cn" source: hosted - version: "7.6.0" + version: "7.6.4" get_it_mixin: dependency: "direct main" description: name: get_it_mixin - sha256: d59fe7e49e258ddf9f10580b50cce5c129d3f2f6a340b684847615128c641261 + sha256: "0ab5c9f3cdaab813ec396de5d43ee3833c418424b3a99bec0071fcbf693c0bad" url: "https://pub.flutter-io.cn" source: hosted - version: "4.2.0" + version: "4.2.2" glob: dependency: transitive description: @@ -756,10 +756,10 @@ packages: dependency: "direct main" description: name: image_picker - sha256: "866724408a53806722893012bc8081ed78dc756b36f19e37712819445da3ad3a" + sha256: "7d7f2768df2a8b0a3cefa5ef4f84636121987d403130e70b17ef7e2cf650ba84" url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.3" + version: "1.0.4" image_picker_android: dependency: transitive description: @@ -860,18 +860,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.flutter-io.cn" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.flutter-io.cn" source: hosted - version: "0.2.0" + version: "0.5.0" matrix2d: dependency: transitive description: @@ -932,10 +932,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: "71c95ee27a9938693fa326b54ae60ae996f35a357acd957c71b2735aee2688c5" + sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.1" + version: "2.2.0" path_provider_foundation: dependency: transitive description: @@ -1153,10 +1153,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.flutter-io.cn" source: hosted - version: "1.9.1" + version: "1.10.0" sqflite: dependency: transitive description: @@ -1225,10 +1225,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.flutter-io.cn" source: hosted - version: "0.5.1" + version: "0.6.0" timezone: dependency: "direct main" description: @@ -1273,18 +1273,18 @@ packages: dependency: "direct main" description: name: video_player - sha256: "3fd106c74da32f336dc7feb65021da9b0207cb3124392935f1552834f7cce822" + sha256: d3910a8cefc0de8a432a4411dcf85030e885d8fef3ddea291f162253a05dbf01 url: "https://pub.flutter-io.cn" source: hosted - version: "2.7.0" + version: "2.7.1" video_player_android: dependency: transitive description: name: video_player_android - sha256: f338a5a396c845f4632959511cad3542cdf3167e1b2a1a948ef07f7123c03608 + sha256: "3fe89ab07fdbce786e7eb25b58532d6eaf189ceddc091cb66cba712f8d9e8e55" url: "https://pub.flutter-io.cn" source: hosted - version: "2.4.9" + version: "2.4.10" video_player_avfoundation: dependency: transitive description: @@ -1305,10 +1305,10 @@ packages: dependency: transitive description: name: video_player_web - sha256: "44ce41424d104dfb7cf6982cc6b84af2b007a24d126406025bf40de5d481c74c" + sha256: "66fc0d56554143fee4c623f70e45e4272b94fd246283cb67edabb9d1e4122a4f" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.16" + version: "2.1.0" watcher: dependency: transitive description: @@ -1317,6 +1317,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.1.4-beta" web_socket_channel: dependency: "direct main" description: @@ -1366,5 +1374,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.2 <4.0.0" - flutter: ">=3.10.0" + dart: ">=3.1.0 <4.0.0" + flutter: ">=3.13.0"