♻️ Use sqlite to replace hive #5
| @@ -683,5 +683,9 @@ | |||||||
|   "postChannelRealm": "Realms", |   "postChannelRealm": "Realms", | ||||||
|   "postFilterReset": "Reset Filter", |   "postFilterReset": "Reset Filter", | ||||||
|   "postFilterResetDescription": "Clear filter and show all posts.", |   "postFilterResetDescription": "Clear filter and show all posts.", | ||||||
|   "postFilterWithCategory": "Viewing posts in {}" |   "postFilterWithCategory": "Viewing posts in {}", | ||||||
|  |   "databaseSize": "Database Size", | ||||||
|  |   "databaseDelete": "Delete Database", | ||||||
|  |   "databaseDeleteDescription": "Remove the database on your local disk, the content will be fetched from server again.", | ||||||
|  |   "databaseDeleted": "The local database has been deleted." | ||||||
| } | } | ||||||
|   | |||||||
| @@ -681,5 +681,9 @@ | |||||||
|   "postChannelRealm": "领域", |   "postChannelRealm": "领域", | ||||||
|   "postFilterReset": "重置过滤器", |   "postFilterReset": "重置过滤器", | ||||||
|   "postFilterResetDescription": "清除过滤器并显示所有帖子。", |   "postFilterResetDescription": "清除过滤器并显示所有帖子。", | ||||||
|   "postFilterWithCategory": "查看{}区中的帖子" |   "postFilterWithCategory": "查看{}区中的帖子", | ||||||
|  |   "databaseSize": "数据库大小", | ||||||
|  |   "databaseDelete": "删除数据库", | ||||||
|  |   "databaseDeleteDescription": "删除本地数据库,内容将从服务器重新获取。", | ||||||
|  |   "databaseDeleted": "本地数据库已被删除。" | ||||||
| } | } | ||||||
|   | |||||||
| @@ -681,5 +681,9 @@ | |||||||
|   "postChannelRealm": "領域", |   "postChannelRealm": "領域", | ||||||
|   "postFilterReset": "重置過濾器", |   "postFilterReset": "重置過濾器", | ||||||
|   "postFilterResetDescription": "清除過濾器並顯示所有帖子。", |   "postFilterResetDescription": "清除過濾器並顯示所有帖子。", | ||||||
|   "postFilterWithCategory": "查看{}區中的帖子" |   "postFilterWithCategory": "查看{}區中的帖子", | ||||||
|  |   "databaseSize": "數據庫大小", | ||||||
|  |   "databaseDelete": "刪除數據庫", | ||||||
|  |   "databaseDeleteDescription": "刪除本地數據庫,內容將從服務器重新獲取。", | ||||||
|  |   "databaseDeleted": "本地數據庫已被刪除。" | ||||||
| } | } | ||||||
|   | |||||||
| @@ -681,5 +681,9 @@ | |||||||
|   "postChannelRealm": "領域", |   "postChannelRealm": "領域", | ||||||
|   "postFilterReset": "重置過濾器", |   "postFilterReset": "重置過濾器", | ||||||
|   "postFilterResetDescription": "清除過濾器並顯示所有帖子。", |   "postFilterResetDescription": "清除過濾器並顯示所有帖子。", | ||||||
|   "postFilterWithCategory": "查看{}區中的帖子" |   "postFilterWithCategory": "查看{}區中的帖子", | ||||||
|  |   "databaseSize": "數據庫大小", | ||||||
|  |   "databaseDelete": "刪除數據庫", | ||||||
|  |   "databaseDeleteDescription": "刪除本地數據庫,內容將從服務器重新獲取。", | ||||||
|  |   "databaseDeleted": "本地數據庫已被刪除。" | ||||||
| } | } | ||||||
|   | |||||||
| @@ -221,6 +221,25 @@ PODS: | |||||||
|   - sqflite_darwin (0.0.4): |   - sqflite_darwin (0.0.4): | ||||||
|     - Flutter |     - Flutter | ||||||
|     - FlutterMacOS |     - FlutterMacOS | ||||||
|  |   - sqlite3 (3.49.0): | ||||||
|  |     - sqlite3/common (= 3.49.0) | ||||||
|  |   - sqlite3/common (3.49.0) | ||||||
|  |   - sqlite3/dbstatvtab (3.49.0): | ||||||
|  |     - sqlite3/common | ||||||
|  |   - sqlite3/fts5 (3.49.0): | ||||||
|  |     - sqlite3/common | ||||||
|  |   - sqlite3/perf-threadsafe (3.49.0): | ||||||
|  |     - sqlite3/common | ||||||
|  |   - sqlite3/rtree (3.49.0): | ||||||
|  |     - sqlite3/common | ||||||
|  |   - sqlite3_flutter_libs (0.0.1): | ||||||
|  |     - Flutter | ||||||
|  |     - FlutterMacOS | ||||||
|  |     - sqlite3 (~> 3.49.0) | ||||||
|  |     - sqlite3/dbstatvtab | ||||||
|  |     - sqlite3/fts5 | ||||||
|  |     - sqlite3/perf-threadsafe | ||||||
|  |     - sqlite3/rtree | ||||||
|   - SwiftyGif (5.4.5) |   - SwiftyGif (5.4.5) | ||||||
|   - url_launcher_ios (0.0.1): |   - url_launcher_ios (0.0.1): | ||||||
|     - Flutter |     - Flutter | ||||||
| @@ -268,6 +287,7 @@ DEPENDENCIES: | |||||||
|   - share_plus (from `.symlinks/plugins/share_plus/ios`) |   - share_plus (from `.symlinks/plugins/share_plus/ios`) | ||||||
|   - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) |   - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) | ||||||
|   - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) |   - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) | ||||||
|  |   - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`) | ||||||
|   - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) |   - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) | ||||||
|   - video_compress (from `.symlinks/plugins/video_compress/ios`) |   - video_compress (from `.symlinks/plugins/video_compress/ios`) | ||||||
|   - volume_controller (from `.symlinks/plugins/volume_controller/ios`) |   - volume_controller (from `.symlinks/plugins/volume_controller/ios`) | ||||||
| @@ -294,6 +314,7 @@ SPEC REPOS: | |||||||
|     - PromisesObjC |     - PromisesObjC | ||||||
|     - SAMKeychain |     - SAMKeychain | ||||||
|     - SDWebImage |     - SDWebImage | ||||||
|  |     - sqlite3 | ||||||
|     - SwiftyGif |     - SwiftyGif | ||||||
|     - WebRTC-SDK |     - WebRTC-SDK | ||||||
|  |  | ||||||
| @@ -360,6 +381,8 @@ EXTERNAL SOURCES: | |||||||
|     :path: ".symlinks/plugins/shared_preferences_foundation/darwin" |     :path: ".symlinks/plugins/shared_preferences_foundation/darwin" | ||||||
|   sqflite_darwin: |   sqflite_darwin: | ||||||
|     :path: ".symlinks/plugins/sqflite_darwin/darwin" |     :path: ".symlinks/plugins/sqflite_darwin/darwin" | ||||||
|  |   sqlite3_flutter_libs: | ||||||
|  |     :path: ".symlinks/plugins/sqlite3_flutter_libs/darwin" | ||||||
|   url_launcher_ios: |   url_launcher_ios: | ||||||
|     :path: ".symlinks/plugins/url_launcher_ios/ios" |     :path: ".symlinks/plugins/url_launcher_ios/ios" | ||||||
|   video_compress: |   video_compress: | ||||||
| @@ -421,6 +444,8 @@ SPEC CHECKSUMS: | |||||||
|   share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f |   share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f | ||||||
|   shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 |   shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 | ||||||
|   sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d |   sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d | ||||||
|  |   sqlite3: 4922312598b67e1825c6a6c821296dcbf6783046 | ||||||
|  |   sqlite3_flutter_libs: 069c435986dd4b63461aecd68f4b30be4a9e9daa | ||||||
|   SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 |   SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 | ||||||
|   url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe |   url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe | ||||||
|   video_compress: fce97e4fb1dfd88175aa07d2ffc8a2f297f87fbe |   video_compress: fce97e4fb1dfd88175aa07d2ffc8a2f297f87fbe | ||||||
|   | |||||||
| @@ -2,11 +2,12 @@ import 'dart:async'; | |||||||
| import 'dart:convert'; | import 'dart:convert'; | ||||||
| import 'dart:math' as math; | import 'dart:math' as math; | ||||||
|  |  | ||||||
| import 'package:collection/collection.dart'; |  | ||||||
| import 'package:dio/dio.dart'; | import 'package:dio/dio.dart'; | ||||||
|  | import 'package:drift/drift.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:hive/hive.dart'; |  | ||||||
| import 'package:provider/provider.dart'; | import 'package:provider/provider.dart'; | ||||||
|  | import 'package:surface/database/database.dart'; | ||||||
|  | import 'package:surface/providers/database.dart'; | ||||||
| import 'package:surface/providers/sn_attachment.dart'; | import 'package:surface/providers/sn_attachment.dart'; | ||||||
| import 'package:surface/providers/sn_network.dart'; | import 'package:surface/providers/sn_network.dart'; | ||||||
| import 'package:surface/providers/user_directory.dart'; | import 'package:surface/providers/user_directory.dart'; | ||||||
| @@ -16,13 +17,13 @@ import 'package:surface/types/websocket.dart'; | |||||||
| import 'package:uuid/uuid.dart'; | import 'package:uuid/uuid.dart'; | ||||||
|  |  | ||||||
| class ChatMessageController extends ChangeNotifier { | class ChatMessageController extends ChangeNotifier { | ||||||
|   static const kChatMessageBoxPrefix = 'nex_chat_messages_'; |  | ||||||
|   static const kSingleBatchLoadLimit = 100; |   static const kSingleBatchLoadLimit = 100; | ||||||
|  |  | ||||||
|   late final SnNetworkProvider _sn; |   late final SnNetworkProvider _sn; | ||||||
|   late final UserDirectoryProvider _ud; |   late final UserDirectoryProvider _ud; | ||||||
|   late final WebSocketProvider _ws; |   late final WebSocketProvider _ws; | ||||||
|   late final SnAttachmentProvider _attach; |   late final SnAttachmentProvider _attach; | ||||||
|  |   late final DatabaseProvider _dt; | ||||||
|  |  | ||||||
|   StreamSubscription? _wsSubscription; |   StreamSubscription? _wsSubscription; | ||||||
|  |  | ||||||
| @@ -31,6 +32,7 @@ class ChatMessageController extends ChangeNotifier { | |||||||
|     _ud = context.read<UserDirectoryProvider>(); |     _ud = context.read<UserDirectoryProvider>(); | ||||||
|     _ws = context.read<WebSocketProvider>(); |     _ws = context.read<WebSocketProvider>(); | ||||||
|     _attach = context.read<SnAttachmentProvider>(); |     _attach = context.read<SnAttachmentProvider>(); | ||||||
|  |     _dt = context.read<DatabaseProvider>(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool isPending = true; |   bool isPending = true; | ||||||
| @@ -38,9 +40,9 @@ class ChatMessageController extends ChangeNotifier { | |||||||
|  |  | ||||||
|   int? messageTotal; |   int? messageTotal; | ||||||
|  |  | ||||||
|   bool get isAllLoaded => messageTotal != null && messages.length >= messageTotal!; |   bool get isAllLoaded => | ||||||
|  |       messageTotal != null && messages.length >= messageTotal!; | ||||||
|  |  | ||||||
|   String? _boxKey; |  | ||||||
|   SnChannel? channel; |   SnChannel? channel; | ||||||
|   SnChannelMember? profile; |   SnChannelMember? profile; | ||||||
|  |  | ||||||
| @@ -51,25 +53,17 @@ class ChatMessageController extends ChangeNotifier { | |||||||
|   /// Stored as a list of nonce to provide the loading state |   /// Stored as a list of nonce to provide the loading state | ||||||
|   final List<String> unconfirmedMessages = List.empty(growable: true); |   final List<String> unconfirmedMessages = List.empty(growable: true); | ||||||
|  |  | ||||||
|   Box<SnChatMessage>? get _box => (_boxKey == null || isPending) ? null : Hive.box<SnChatMessage>(_boxKey!); |  | ||||||
|  |  | ||||||
|   final List<SnChannelMember> typingMembers = List.empty(growable: true); |   final List<SnChannelMember> typingMembers = List.empty(growable: true); | ||||||
|   final Map<int, Timer> typingInactiveTimer = {}; |   final Map<int, Timer> typingInactiveTimer = {}; | ||||||
|  |  | ||||||
|   Future<void> initialize(SnChannel chan) async { |   Future<void> initialize(SnChannel chan) async { | ||||||
|     channel = chan; |     channel = chan; | ||||||
|  |  | ||||||
|     // Initialize local data |  | ||||||
|     _boxKey = '$kChatMessageBoxPrefix${chan.id}'; |  | ||||||
|     await Hive.openBox<SnChatMessage>(_boxKey!); |  | ||||||
|  |  | ||||||
|     // Fetch channel profile |     // Fetch channel profile | ||||||
|     final resp = await _sn.client.get( |     final resp = await _sn.client.get( | ||||||
|       '/cgi/im/channels/${chan.keyPath}/me', |       '/cgi/im/channels/${chan.keyPath}/me', | ||||||
|     ); |     ); | ||||||
|     profile = SnChannelMember.fromJson( |     profile = SnChannelMember.fromJson(resp.data); | ||||||
|       resp.data as Map<String, dynamic>, |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     _wsSubscription = _ws.pk.stream.listen((event) { |     _wsSubscription = _ws.pk.stream.listen((event) { | ||||||
|       switch (event.method) { |       switch (event.method) { | ||||||
| @@ -87,7 +81,8 @@ class ChatMessageController extends ChangeNotifier { | |||||||
|             notifyListeners(); |             notifyListeners(); | ||||||
|           } |           } | ||||||
|           typingInactiveTimer[member.id]?.cancel(); |           typingInactiveTimer[member.id]?.cancel(); | ||||||
|           typingInactiveTimer[member.id] = Timer(const Duration(seconds: 3), () { |           typingInactiveTimer[member.id] = | ||||||
|  |               Timer(const Duration(seconds: 3), () { | ||||||
|             typingMembers.removeWhere((x) => x.id == member.id); |             typingMembers.removeWhere((x) => x.id == member.id); | ||||||
|             typingInactiveTimer.remove(member.id); |             typingInactiveTimer.remove(member.id); | ||||||
|             notifyListeners(); |             notifyListeners(); | ||||||
| @@ -129,10 +124,16 @@ class ChatMessageController extends ChangeNotifier { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   Future<void> _saveMessageToLocal(Iterable<SnChatMessage> messages) async { |   Future<void> _saveMessageToLocal(Iterable<SnChatMessage> messages) async { | ||||||
|     if (_box == null) return; |     await _dt.db.snLocalChatMessage.insertAll( | ||||||
|     await _box!.putAll({ |         messages.map( | ||||||
|       for (final message in messages) message.id: message, |           (ele) => SnLocalChatMessageCompanion.insert( | ||||||
|     }); |             id: Value(ele.id), | ||||||
|  |             content: ele, | ||||||
|  |             channelId: channel!.id, | ||||||
|  |             createdAt: Value(ele.createdAt), | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |         onConflict: DoNothing()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   Future<void> _addUnconfirmedMessage(SnChatMessage message) async { |   Future<void> _addUnconfirmedMessage(SnChatMessage message) async { | ||||||
| @@ -184,8 +185,21 @@ class ChatMessageController extends ChangeNotifier { | |||||||
|     await _applyMessage(message); |     await _applyMessage(message); | ||||||
|     notifyListeners(); |     notifyListeners(); | ||||||
|  |  | ||||||
|     if (_box == null) return; |     if (isCheckedUpdate) { | ||||||
|     await _box!.put(message.id, message); |       await _dt.db.snLocalChatMessage.insertOne( | ||||||
|  |         SnLocalChatMessageCompanion.insert( | ||||||
|  |           id: Value(message.id), | ||||||
|  |           content: message, | ||||||
|  |           channelId: channel!.id, | ||||||
|  |           createdAt: Value(message.createdAt), | ||||||
|  |         ), | ||||||
|  |         onConflict: DoUpdate((_) => SnLocalChatMessageCompanion.custom( | ||||||
|  |               content: Constant(jsonEncode(message.toJson())), | ||||||
|  |             )), | ||||||
|  |       ); | ||||||
|  |     } else { | ||||||
|  |       incomeStrandedQueue.add(message); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   Future<void> _applyMessage(SnChatMessage message) async { |   Future<void> _applyMessage(SnChatMessage message) async { | ||||||
| @@ -194,7 +208,8 @@ class ChatMessageController extends ChangeNotifier { | |||||||
|     switch (message.type) { |     switch (message.type) { | ||||||
|       case 'messages.edit': |       case 'messages.edit': | ||||||
|         if (message.relatedEventId != null) { |         if (message.relatedEventId != null) { | ||||||
|           final idx = messages.indexWhere((x) => x.id == message.relatedEventId); |           final idx = | ||||||
|  |               messages.indexWhere((x) => x.id == message.relatedEventId); | ||||||
|           if (idx != -1) { |           if (idx != -1) { | ||||||
|             final newBody = message.body; |             final newBody = message.body; | ||||||
|             newBody.remove('related_event'); |             newBody.remove('related_event'); | ||||||
| @@ -202,16 +217,24 @@ class ChatMessageController extends ChangeNotifier { | |||||||
|               body: newBody, |               body: newBody, | ||||||
|               updatedAt: message.updatedAt, |               updatedAt: message.updatedAt, | ||||||
|             ); |             ); | ||||||
|             if (_box!.containsKey(message.relatedEventId)) { |             if (message.relatedEventId != null) { | ||||||
|               await _box!.put(message.relatedEventId, messages[idx]); |               await (_dt.db.snLocalChatMessage.update() | ||||||
|  |                     ..where((e) => e.id.equals(message.relatedEventId!))) | ||||||
|  |                   .write( | ||||||
|  |                 SnLocalChatMessageCompanion.custom( | ||||||
|  |                   content: Constant(jsonEncode(messages[idx].toJson())), | ||||||
|  |                 ), | ||||||
|  |               ); | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       case 'messages.delete': |       case 'messages.delete': | ||||||
|         if (message.relatedEventId != null) { |         if (message.relatedEventId != null) { | ||||||
|           messages.removeWhere((x) => x.id == message.relatedEventId); |           messages.removeWhere((x) => x.id == message.relatedEventId); | ||||||
|           if (_box!.containsKey(message.relatedEventId)) { |           if (message.relatedEventId != null) { | ||||||
|             await _box!.delete(message.relatedEventId); |             await (_dt.db.snLocalChatMessage.delete() | ||||||
|  |                   ..where((e) => e.id.equals(message.relatedEventId!))) | ||||||
|  |                 .go(); | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -233,7 +256,8 @@ class ChatMessageController extends ChangeNotifier { | |||||||
|       'algorithm': 'plain', |       'algorithm': 'plain', | ||||||
|       if (quoteId != null) 'quote_event': quoteId, |       if (quoteId != null) 'quote_event': quoteId, | ||||||
|       if (relatedId != null) 'related_event': relatedId, |       if (relatedId != null) 'related_event': relatedId, | ||||||
|       if (attachments != null && attachments.isNotEmpty) 'attachments': attachments, |       if (attachments != null && attachments.isNotEmpty) | ||||||
|  |         'attachments': attachments, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     // Mock the message locally |     // Mock the message locally | ||||||
| @@ -287,20 +311,34 @@ class ChatMessageController extends ChangeNotifier { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   bool isCheckedUpdate = false; | ||||||
|  |   List<SnChatMessage> incomeStrandedQueue = List.empty(growable: true); | ||||||
|  |  | ||||||
|   /// Check the local storage is up to date with the server. |   /// Check the local storage is up to date with the server. | ||||||
|   /// If the local storage is not up to date, it will be updated. |   /// If the local storage is not up to date, it will be updated. | ||||||
|   Future<void> checkUpdate() async { |   Future<void> checkUpdate() async { | ||||||
|     if (_box == null) return; |  | ||||||
|     if (_box!.isEmpty) return; |  | ||||||
|  |  | ||||||
|     isLoading = true; |     isLoading = true; | ||||||
|     notifyListeners(); |     notifyListeners(); | ||||||
|  |  | ||||||
|  |     final mostRecentMessage = await (_dt.db.snLocalChatMessage.select() | ||||||
|  |           ..limit(1) | ||||||
|  |           ..orderBy([ | ||||||
|  |             (e) => | ||||||
|  |                 OrderingTerm(expression: e.createdAt, mode: OrderingMode.desc) | ||||||
|  |           ])) | ||||||
|  |         .getSingleOrNull(); | ||||||
|  |     if (mostRecentMessage == null) { | ||||||
|  |       // Initial load | ||||||
|  |       await loadMessages(take: 20); | ||||||
|  |       isCheckedUpdate = true; | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     try { |     try { | ||||||
|       final resp = await _sn.client.get( |       final resp = await _sn.client.get( | ||||||
|         '/cgi/im/channels/${channel!.keyPath}/events/update', |         '/cgi/im/channels/${channel!.keyPath}/events/update', | ||||||
|         queryParameters: { |         queryParameters: { | ||||||
|           'pivot': _box!.values.last.id, |           'pivot': mostRecentMessage.content.id, | ||||||
|         }, |         }, | ||||||
|       ); |       ); | ||||||
|       if (resp.data['up_to_date'] == true) return; |       if (resp.data['up_to_date'] == true) return; | ||||||
| @@ -316,6 +354,12 @@ class ChatMessageController extends ChangeNotifier { | |||||||
|     } finally { |     } finally { | ||||||
|       await loadMessages(); |       await loadMessages(); | ||||||
|       isLoading = false; |       isLoading = false; | ||||||
|  |  | ||||||
|  |       isCheckedUpdate = true; | ||||||
|  |       _saveMessageToLocal(incomeStrandedQueue).then((_) { | ||||||
|  |         incomeStrandedQueue.clear(); | ||||||
|  |       }); | ||||||
|  |  | ||||||
|       notifyListeners(); |       notifyListeners(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -324,13 +368,18 @@ class ChatMessageController extends ChangeNotifier { | |||||||
|   /// If it was not found in local storage we will look it up in remote |   /// If it was not found in local storage we will look it up in remote | ||||||
|   Future<SnChatMessage?> getMessage(int id) async { |   Future<SnChatMessage?> getMessage(int id) async { | ||||||
|     SnChatMessage? out; |     SnChatMessage? out; | ||||||
|     if (_box != null && _box!.containsKey(id)) { |     final local = await (_dt.db.snLocalChatMessage.select() | ||||||
|       out = _box!.get(id); |           ..limit(1) | ||||||
|  |           ..where((e) => e.id.equals(id))) | ||||||
|  |         .getSingleOrNull(); | ||||||
|  |     if (local != null) { | ||||||
|  |       out = local.content; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (out == null) { |     if (out == null) { | ||||||
|       try { |       try { | ||||||
|         final resp = await _sn.client.get('/cgi/im/channels/${channel!.keyPath}/events/$id'); |         final resp = await _sn.client | ||||||
|  |             .get('/cgi/im/channels/${channel!.keyPath}/events/$id'); | ||||||
|         out = SnChatMessage.fromJson(resp.data); |         out = SnChatMessage.fromJson(resp.data); | ||||||
|         _saveMessageToLocal([out]); |         _saveMessageToLocal([out]); | ||||||
|       } catch (_) { |       } catch (_) { | ||||||
| @@ -364,16 +413,21 @@ class ChatMessageController extends ChangeNotifier { | |||||||
|     bool forceLocal = false, |     bool forceLocal = false, | ||||||
|     bool forceRemote = false, |     bool forceRemote = false, | ||||||
|   }) async { |   }) async { | ||||||
|  |     final localTotal = await _dt.db.snLocalChatMessage | ||||||
|  |         .count(where: (e) => e.channelId.equals(channel!.id)) | ||||||
|  |         .getSingle(); | ||||||
|  |  | ||||||
|     late List<SnChatMessage> out; |     late List<SnChatMessage> out; | ||||||
|     if (_box != null && (_box!.length >= take + offset || forceLocal) && !forceRemote) { |     if ((localTotal >= take + offset || forceLocal) && !forceRemote) { | ||||||
|       out = _box!.keys |       final result = await (_dt.db.snLocalChatMessage.select() | ||||||
|           .toList() |             ..where((e) => e.channelId.equals(channel!.id)) | ||||||
|           .cast<int>() |             ..orderBy([ | ||||||
|           .sorted((a, b) => b.compareTo(a)) |               (e) => | ||||||
|           .skip(offset) |                   OrderingTerm(expression: e.createdAt, mode: OrderingMode.desc) | ||||||
|           .take(take) |             ]) | ||||||
|           .map((key) => _box!.get(key)!) |             ..limit(take, offset: offset)) | ||||||
|           .toList(); |           .get(); | ||||||
|  |       out = result.map((e) => e.content).toList(); | ||||||
|     } else { |     } else { | ||||||
|       final resp = await _sn.client.get( |       final resp = await _sn.client.get( | ||||||
|         '/cgi/im/channels/${channel!.keyPath}/events', |         '/cgi/im/channels/${channel!.keyPath}/events', | ||||||
| @@ -408,7 +462,8 @@ class ChatMessageController extends ChangeNotifier { | |||||||
|           quoteEvent: quoteEvent, |           quoteEvent: quoteEvent, | ||||||
|           attachments: attachments |           attachments: attachments | ||||||
|               .where( |               .where( | ||||||
|                 (ele) => out[i].body['attachments']?.contains(ele?.rid) ?? false, |                 (ele) => | ||||||
|  |                     out[i].body['attachments']?.contains(ele?.rid) ?? false, | ||||||
|               ) |               ) | ||||||
|               .toList(), |               .toList(), | ||||||
|         ), |         ), | ||||||
| @@ -416,7 +471,10 @@ class ChatMessageController extends ChangeNotifier { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Preload sender accounts |     // Preload sender accounts | ||||||
|     final accountId = out.where((ele) => ele.sender.accountId >= 0).map((ele) => ele.sender.accountId).toSet(); |     final accountId = out | ||||||
|  |         .where((ele) => ele.sender.accountId >= 0) | ||||||
|  |         .map((ele) => ele.sender.accountId) | ||||||
|  |         .toSet(); | ||||||
|     await _ud.listAccount(accountId); |     await _ud.listAccount(accountId); | ||||||
|  |  | ||||||
|     return out; |     return out; | ||||||
| @@ -443,7 +501,6 @@ class ChatMessageController extends ChangeNotifier { | |||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   void dispose() { |   void dispose() { | ||||||
|     _box?.close(); |  | ||||||
|     _wsSubscription?.cancel(); |     _wsSubscription?.cancel(); | ||||||
|     super.dispose(); |     super.dispose(); | ||||||
|   } |   } | ||||||
|   | |||||||
							
								
								
									
										74
									
								
								lib/database/chat.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								lib/database/chat.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | import 'dart:convert'; | ||||||
|  |  | ||||||
|  | import 'package:drift/drift.dart'; | ||||||
|  | import 'package:surface/types/chat.dart'; | ||||||
|  |  | ||||||
|  | class SnChannelConverter extends TypeConverter<SnChannel, String> | ||||||
|  |     with JsonTypeConverter2<SnChannel, String, Map<String, Object?>> { | ||||||
|  |   const SnChannelConverter(); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   SnChannel fromSql(String fromDb) { | ||||||
|  |     return fromJson(jsonDecode(fromDb) as Map<String, dynamic>); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toSql(SnChannel value) { | ||||||
|  |     return jsonEncode(toJson(value)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   SnChannel fromJson(Map<String, Object?> json) { | ||||||
|  |     return SnChannel.fromJson(json); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Map<String, Object?> toJson(SnChannel value) { | ||||||
|  |     return value.toJson(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class SnLocalChatChannel extends Table { | ||||||
|  |   IntColumn get id => integer().autoIncrement()(); | ||||||
|  |  | ||||||
|  |   TextColumn get alias => text()(); | ||||||
|  |  | ||||||
|  |   TextColumn get content => text().map(const SnChannelConverter())(); | ||||||
|  |  | ||||||
|  |   DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class SnMessageConverter extends TypeConverter<SnChatMessage, String> | ||||||
|  |     with JsonTypeConverter2<SnChatMessage, String, Map<String, Object?>> { | ||||||
|  |   const SnMessageConverter(); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   SnChatMessage fromSql(String fromDb) { | ||||||
|  |     return fromJson(jsonDecode(fromDb) as Map<String, dynamic>); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toSql(SnChatMessage value) { | ||||||
|  |     return jsonEncode(toJson(value)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   SnChatMessage fromJson(Map<String, Object?> json) { | ||||||
|  |     return SnChatMessage.fromJson(json); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Map<String, Object?> toJson(SnChatMessage value) { | ||||||
|  |     return value.toJson(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class SnLocalChatMessage extends Table { | ||||||
|  |   IntColumn get id => integer().autoIncrement()(); | ||||||
|  |  | ||||||
|  |   IntColumn get channelId => integer()(); | ||||||
|  |  | ||||||
|  |   TextColumn get content => text().map(const SnMessageConverter())(); | ||||||
|  |  | ||||||
|  |   DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								lib/database/database.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								lib/database/database.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | import 'package:drift/drift.dart'; | ||||||
|  | import 'package:drift_flutter/drift_flutter.dart'; | ||||||
|  | import 'package:path_provider/path_provider.dart'; | ||||||
|  | import 'package:surface/database/chat.dart'; | ||||||
|  | import 'package:surface/types/chat.dart'; | ||||||
|  |  | ||||||
|  | part 'database.g.dart'; | ||||||
|  |  | ||||||
|  | @DriftDatabase(tables: [SnLocalChatChannel, SnLocalChatMessage]) | ||||||
|  | class AppDatabase extends _$AppDatabase { | ||||||
|  |   AppDatabase() : super(_openConnection()); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   int get schemaVersion => 1; | ||||||
|  |  | ||||||
|  |   static QueryExecutor _openConnection() { | ||||||
|  |     return driftDatabase( | ||||||
|  |       name: 'solar_network_data', | ||||||
|  |       native: const DriftNativeOptions( | ||||||
|  |         databaseDirectory: getApplicationSupportDirectory, | ||||||
|  |       ), | ||||||
|  |       web: DriftWebOptions( | ||||||
|  |         sqlite3Wasm: Uri.parse('sqlite3.wasm'), | ||||||
|  |         driftWorker: Uri.parse('drift_worker.dart.js'), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										880
									
								
								lib/database/database.g.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										880
									
								
								lib/database/database.g.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,880 @@ | |||||||
|  | // GENERATED CODE - DO NOT MODIFY BY HAND | ||||||
|  |  | ||||||
|  | part of 'database.dart'; | ||||||
|  |  | ||||||
|  | // ignore_for_file: type=lint | ||||||
|  | class $SnLocalChatChannelTable extends SnLocalChatChannel | ||||||
|  |     with TableInfo<$SnLocalChatChannelTable, SnLocalChatChannelData> { | ||||||
|  |   @override | ||||||
|  |   final GeneratedDatabase attachedDatabase; | ||||||
|  |   final String? _alias; | ||||||
|  |   $SnLocalChatChannelTable(this.attachedDatabase, [this._alias]); | ||||||
|  |   static const VerificationMeta _idMeta = const VerificationMeta('id'); | ||||||
|  |   @override | ||||||
|  |   late final GeneratedColumn<int> id = GeneratedColumn<int>( | ||||||
|  |       'id', aliasedName, false, | ||||||
|  |       hasAutoIncrement: true, | ||||||
|  |       type: DriftSqlType.int, | ||||||
|  |       requiredDuringInsert: false, | ||||||
|  |       defaultConstraints: | ||||||
|  |           GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); | ||||||
|  |   static const VerificationMeta _aliasMeta = const VerificationMeta('alias'); | ||||||
|  |   @override | ||||||
|  |   late final GeneratedColumn<String> alias = GeneratedColumn<String>( | ||||||
|  |       'alias', aliasedName, false, | ||||||
|  |       type: DriftSqlType.string, requiredDuringInsert: true); | ||||||
|  |   static const VerificationMeta _contentMeta = | ||||||
|  |       const VerificationMeta('content'); | ||||||
|  |   @override | ||||||
|  |   late final GeneratedColumnWithTypeConverter<SnChannel, String> content = | ||||||
|  |       GeneratedColumn<String>('content', aliasedName, false, | ||||||
|  |               type: DriftSqlType.string, requiredDuringInsert: true) | ||||||
|  |           .withConverter<SnChannel>($SnLocalChatChannelTable.$convertercontent); | ||||||
|  |   static const VerificationMeta _createdAtMeta = | ||||||
|  |       const VerificationMeta('createdAt'); | ||||||
|  |   @override | ||||||
|  |   late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>( | ||||||
|  |       'created_at', aliasedName, false, | ||||||
|  |       type: DriftSqlType.dateTime, | ||||||
|  |       requiredDuringInsert: false, | ||||||
|  |       defaultValue: currentDateAndTime); | ||||||
|  |   @override | ||||||
|  |   List<GeneratedColumn> get $columns => [id, alias, content, createdAt]; | ||||||
|  |   @override | ||||||
|  |   String get aliasedName => _alias ?? actualTableName; | ||||||
|  |   @override | ||||||
|  |   String get actualTableName => $name; | ||||||
|  |   static const String $name = 'sn_local_chat_channel'; | ||||||
|  |   @override | ||||||
|  |   VerificationContext validateIntegrity( | ||||||
|  |       Insertable<SnLocalChatChannelData> instance, | ||||||
|  |       {bool isInserting = false}) { | ||||||
|  |     final context = VerificationContext(); | ||||||
|  |     final data = instance.toColumns(true); | ||||||
|  |     if (data.containsKey('id')) { | ||||||
|  |       context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); | ||||||
|  |     } | ||||||
|  |     if (data.containsKey('alias')) { | ||||||
|  |       context.handle( | ||||||
|  |           _aliasMeta, alias.isAcceptableOrUnknown(data['alias']!, _aliasMeta)); | ||||||
|  |     } else if (isInserting) { | ||||||
|  |       context.missing(_aliasMeta); | ||||||
|  |     } | ||||||
|  |     context.handle(_contentMeta, const VerificationResult.success()); | ||||||
|  |     if (data.containsKey('created_at')) { | ||||||
|  |       context.handle(_createdAtMeta, | ||||||
|  |           createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); | ||||||
|  |     } | ||||||
|  |     return context; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Set<GeneratedColumn> get $primaryKey => {id}; | ||||||
|  |   @override | ||||||
|  |   SnLocalChatChannelData map(Map<String, dynamic> data, {String? tablePrefix}) { | ||||||
|  |     final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; | ||||||
|  |     return SnLocalChatChannelData( | ||||||
|  |       id: attachedDatabase.typeMapping | ||||||
|  |           .read(DriftSqlType.int, data['${effectivePrefix}id'])!, | ||||||
|  |       alias: attachedDatabase.typeMapping | ||||||
|  |           .read(DriftSqlType.string, data['${effectivePrefix}alias'])!, | ||||||
|  |       content: $SnLocalChatChannelTable.$convertercontent.fromSql( | ||||||
|  |           attachedDatabase.typeMapping | ||||||
|  |               .read(DriftSqlType.string, data['${effectivePrefix}content'])!), | ||||||
|  |       createdAt: attachedDatabase.typeMapping | ||||||
|  |           .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   $SnLocalChatChannelTable createAlias(String alias) { | ||||||
|  |     return $SnLocalChatChannelTable(attachedDatabase, alias); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static JsonTypeConverter2<SnChannel, String, Map<String, Object?>> | ||||||
|  |       $convertercontent = const SnChannelConverter(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class SnLocalChatChannelData extends DataClass | ||||||
|  |     implements Insertable<SnLocalChatChannelData> { | ||||||
|  |   final int id; | ||||||
|  |   final String alias; | ||||||
|  |   final SnChannel content; | ||||||
|  |   final DateTime createdAt; | ||||||
|  |   const SnLocalChatChannelData( | ||||||
|  |       {required this.id, | ||||||
|  |       required this.alias, | ||||||
|  |       required this.content, | ||||||
|  |       required this.createdAt}); | ||||||
|  |   @override | ||||||
|  |   Map<String, Expression> toColumns(bool nullToAbsent) { | ||||||
|  |     final map = <String, Expression>{}; | ||||||
|  |     map['id'] = Variable<int>(id); | ||||||
|  |     map['alias'] = Variable<String>(alias); | ||||||
|  |     { | ||||||
|  |       map['content'] = Variable<String>( | ||||||
|  |           $SnLocalChatChannelTable.$convertercontent.toSql(content)); | ||||||
|  |     } | ||||||
|  |     map['created_at'] = Variable<DateTime>(createdAt); | ||||||
|  |     return map; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SnLocalChatChannelCompanion toCompanion(bool nullToAbsent) { | ||||||
|  |     return SnLocalChatChannelCompanion( | ||||||
|  |       id: Value(id), | ||||||
|  |       alias: Value(alias), | ||||||
|  |       content: Value(content), | ||||||
|  |       createdAt: Value(createdAt), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   factory SnLocalChatChannelData.fromJson(Map<String, dynamic> json, | ||||||
|  |       {ValueSerializer? serializer}) { | ||||||
|  |     serializer ??= driftRuntimeOptions.defaultSerializer; | ||||||
|  |     return SnLocalChatChannelData( | ||||||
|  |       id: serializer.fromJson<int>(json['id']), | ||||||
|  |       alias: serializer.fromJson<String>(json['alias']), | ||||||
|  |       content: $SnLocalChatChannelTable.$convertercontent | ||||||
|  |           .fromJson(serializer.fromJson<Map<String, Object?>>(json['content'])), | ||||||
|  |       createdAt: serializer.fromJson<DateTime>(json['createdAt']), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |   @override | ||||||
|  |   Map<String, dynamic> toJson({ValueSerializer? serializer}) { | ||||||
|  |     serializer ??= driftRuntimeOptions.defaultSerializer; | ||||||
|  |     return <String, dynamic>{ | ||||||
|  |       'id': serializer.toJson<int>(id), | ||||||
|  |       'alias': serializer.toJson<String>(alias), | ||||||
|  |       'content': serializer.toJson<Map<String, Object?>>( | ||||||
|  |           $SnLocalChatChannelTable.$convertercontent.toJson(content)), | ||||||
|  |       'createdAt': serializer.toJson<DateTime>(createdAt), | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SnLocalChatChannelData copyWith( | ||||||
|  |           {int? id, String? alias, SnChannel? content, DateTime? createdAt}) => | ||||||
|  |       SnLocalChatChannelData( | ||||||
|  |         id: id ?? this.id, | ||||||
|  |         alias: alias ?? this.alias, | ||||||
|  |         content: content ?? this.content, | ||||||
|  |         createdAt: createdAt ?? this.createdAt, | ||||||
|  |       ); | ||||||
|  |   SnLocalChatChannelData copyWithCompanion(SnLocalChatChannelCompanion data) { | ||||||
|  |     return SnLocalChatChannelData( | ||||||
|  |       id: data.id.present ? data.id.value : this.id, | ||||||
|  |       alias: data.alias.present ? data.alias.value : this.alias, | ||||||
|  |       content: data.content.present ? data.content.value : this.content, | ||||||
|  |       createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toString() { | ||||||
|  |     return (StringBuffer('SnLocalChatChannelData(') | ||||||
|  |           ..write('id: $id, ') | ||||||
|  |           ..write('alias: $alias, ') | ||||||
|  |           ..write('content: $content, ') | ||||||
|  |           ..write('createdAt: $createdAt') | ||||||
|  |           ..write(')')) | ||||||
|  |         .toString(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   int get hashCode => Object.hash(id, alias, content, createdAt); | ||||||
|  |   @override | ||||||
|  |   bool operator ==(Object other) => | ||||||
|  |       identical(this, other) || | ||||||
|  |       (other is SnLocalChatChannelData && | ||||||
|  |           other.id == this.id && | ||||||
|  |           other.alias == this.alias && | ||||||
|  |           other.content == this.content && | ||||||
|  |           other.createdAt == this.createdAt); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class SnLocalChatChannelCompanion | ||||||
|  |     extends UpdateCompanion<SnLocalChatChannelData> { | ||||||
|  |   final Value<int> id; | ||||||
|  |   final Value<String> alias; | ||||||
|  |   final Value<SnChannel> content; | ||||||
|  |   final Value<DateTime> createdAt; | ||||||
|  |   const SnLocalChatChannelCompanion({ | ||||||
|  |     this.id = const Value.absent(), | ||||||
|  |     this.alias = const Value.absent(), | ||||||
|  |     this.content = const Value.absent(), | ||||||
|  |     this.createdAt = const Value.absent(), | ||||||
|  |   }); | ||||||
|  |   SnLocalChatChannelCompanion.insert({ | ||||||
|  |     this.id = const Value.absent(), | ||||||
|  |     required String alias, | ||||||
|  |     required SnChannel content, | ||||||
|  |     this.createdAt = const Value.absent(), | ||||||
|  |   })  : alias = Value(alias), | ||||||
|  |         content = Value(content); | ||||||
|  |   static Insertable<SnLocalChatChannelData> custom({ | ||||||
|  |     Expression<int>? id, | ||||||
|  |     Expression<String>? alias, | ||||||
|  |     Expression<String>? content, | ||||||
|  |     Expression<DateTime>? createdAt, | ||||||
|  |   }) { | ||||||
|  |     return RawValuesInsertable({ | ||||||
|  |       if (id != null) 'id': id, | ||||||
|  |       if (alias != null) 'alias': alias, | ||||||
|  |       if (content != null) 'content': content, | ||||||
|  |       if (createdAt != null) 'created_at': createdAt, | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SnLocalChatChannelCompanion copyWith( | ||||||
|  |       {Value<int>? id, | ||||||
|  |       Value<String>? alias, | ||||||
|  |       Value<SnChannel>? content, | ||||||
|  |       Value<DateTime>? createdAt}) { | ||||||
|  |     return SnLocalChatChannelCompanion( | ||||||
|  |       id: id ?? this.id, | ||||||
|  |       alias: alias ?? this.alias, | ||||||
|  |       content: content ?? this.content, | ||||||
|  |       createdAt: createdAt ?? this.createdAt, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Map<String, Expression> toColumns(bool nullToAbsent) { | ||||||
|  |     final map = <String, Expression>{}; | ||||||
|  |     if (id.present) { | ||||||
|  |       map['id'] = Variable<int>(id.value); | ||||||
|  |     } | ||||||
|  |     if (alias.present) { | ||||||
|  |       map['alias'] = Variable<String>(alias.value); | ||||||
|  |     } | ||||||
|  |     if (content.present) { | ||||||
|  |       map['content'] = Variable<String>( | ||||||
|  |           $SnLocalChatChannelTable.$convertercontent.toSql(content.value)); | ||||||
|  |     } | ||||||
|  |     if (createdAt.present) { | ||||||
|  |       map['created_at'] = Variable<DateTime>(createdAt.value); | ||||||
|  |     } | ||||||
|  |     return map; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toString() { | ||||||
|  |     return (StringBuffer('SnLocalChatChannelCompanion(') | ||||||
|  |           ..write('id: $id, ') | ||||||
|  |           ..write('alias: $alias, ') | ||||||
|  |           ..write('content: $content, ') | ||||||
|  |           ..write('createdAt: $createdAt') | ||||||
|  |           ..write(')')) | ||||||
|  |         .toString(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class $SnLocalChatMessageTable extends SnLocalChatMessage | ||||||
|  |     with TableInfo<$SnLocalChatMessageTable, SnLocalChatMessageData> { | ||||||
|  |   @override | ||||||
|  |   final GeneratedDatabase attachedDatabase; | ||||||
|  |   final String? _alias; | ||||||
|  |   $SnLocalChatMessageTable(this.attachedDatabase, [this._alias]); | ||||||
|  |   static const VerificationMeta _idMeta = const VerificationMeta('id'); | ||||||
|  |   @override | ||||||
|  |   late final GeneratedColumn<int> id = GeneratedColumn<int>( | ||||||
|  |       'id', aliasedName, false, | ||||||
|  |       hasAutoIncrement: true, | ||||||
|  |       type: DriftSqlType.int, | ||||||
|  |       requiredDuringInsert: false, | ||||||
|  |       defaultConstraints: | ||||||
|  |           GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT')); | ||||||
|  |   static const VerificationMeta _channelIdMeta = | ||||||
|  |       const VerificationMeta('channelId'); | ||||||
|  |   @override | ||||||
|  |   late final GeneratedColumn<int> channelId = GeneratedColumn<int>( | ||||||
|  |       'channel_id', aliasedName, false, | ||||||
|  |       type: DriftSqlType.int, requiredDuringInsert: true); | ||||||
|  |   static const VerificationMeta _contentMeta = | ||||||
|  |       const VerificationMeta('content'); | ||||||
|  |   @override | ||||||
|  |   late final GeneratedColumnWithTypeConverter<SnChatMessage, String> content = | ||||||
|  |       GeneratedColumn<String>('content', aliasedName, false, | ||||||
|  |               type: DriftSqlType.string, requiredDuringInsert: true) | ||||||
|  |           .withConverter<SnChatMessage>( | ||||||
|  |               $SnLocalChatMessageTable.$convertercontent); | ||||||
|  |   static const VerificationMeta _createdAtMeta = | ||||||
|  |       const VerificationMeta('createdAt'); | ||||||
|  |   @override | ||||||
|  |   late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>( | ||||||
|  |       'created_at', aliasedName, false, | ||||||
|  |       type: DriftSqlType.dateTime, | ||||||
|  |       requiredDuringInsert: false, | ||||||
|  |       defaultValue: currentDateAndTime); | ||||||
|  |   @override | ||||||
|  |   List<GeneratedColumn> get $columns => [id, channelId, content, createdAt]; | ||||||
|  |   @override | ||||||
|  |   String get aliasedName => _alias ?? actualTableName; | ||||||
|  |   @override | ||||||
|  |   String get actualTableName => $name; | ||||||
|  |   static const String $name = 'sn_local_chat_message'; | ||||||
|  |   @override | ||||||
|  |   VerificationContext validateIntegrity( | ||||||
|  |       Insertable<SnLocalChatMessageData> instance, | ||||||
|  |       {bool isInserting = false}) { | ||||||
|  |     final context = VerificationContext(); | ||||||
|  |     final data = instance.toColumns(true); | ||||||
|  |     if (data.containsKey('id')) { | ||||||
|  |       context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); | ||||||
|  |     } | ||||||
|  |     if (data.containsKey('channel_id')) { | ||||||
|  |       context.handle(_channelIdMeta, | ||||||
|  |           channelId.isAcceptableOrUnknown(data['channel_id']!, _channelIdMeta)); | ||||||
|  |     } else if (isInserting) { | ||||||
|  |       context.missing(_channelIdMeta); | ||||||
|  |     } | ||||||
|  |     context.handle(_contentMeta, const VerificationResult.success()); | ||||||
|  |     if (data.containsKey('created_at')) { | ||||||
|  |       context.handle(_createdAtMeta, | ||||||
|  |           createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); | ||||||
|  |     } | ||||||
|  |     return context; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Set<GeneratedColumn> get $primaryKey => {id}; | ||||||
|  |   @override | ||||||
|  |   SnLocalChatMessageData map(Map<String, dynamic> data, {String? tablePrefix}) { | ||||||
|  |     final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; | ||||||
|  |     return SnLocalChatMessageData( | ||||||
|  |       id: attachedDatabase.typeMapping | ||||||
|  |           .read(DriftSqlType.int, data['${effectivePrefix}id'])!, | ||||||
|  |       channelId: attachedDatabase.typeMapping | ||||||
|  |           .read(DriftSqlType.int, data['${effectivePrefix}channel_id'])!, | ||||||
|  |       content: $SnLocalChatMessageTable.$convertercontent.fromSql( | ||||||
|  |           attachedDatabase.typeMapping | ||||||
|  |               .read(DriftSqlType.string, data['${effectivePrefix}content'])!), | ||||||
|  |       createdAt: attachedDatabase.typeMapping | ||||||
|  |           .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   $SnLocalChatMessageTable createAlias(String alias) { | ||||||
|  |     return $SnLocalChatMessageTable(attachedDatabase, alias); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static JsonTypeConverter2<SnChatMessage, String, Map<String, Object?>> | ||||||
|  |       $convertercontent = const SnMessageConverter(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class SnLocalChatMessageData extends DataClass | ||||||
|  |     implements Insertable<SnLocalChatMessageData> { | ||||||
|  |   final int id; | ||||||
|  |   final int channelId; | ||||||
|  |   final SnChatMessage content; | ||||||
|  |   final DateTime createdAt; | ||||||
|  |   const SnLocalChatMessageData( | ||||||
|  |       {required this.id, | ||||||
|  |       required this.channelId, | ||||||
|  |       required this.content, | ||||||
|  |       required this.createdAt}); | ||||||
|  |   @override | ||||||
|  |   Map<String, Expression> toColumns(bool nullToAbsent) { | ||||||
|  |     final map = <String, Expression>{}; | ||||||
|  |     map['id'] = Variable<int>(id); | ||||||
|  |     map['channel_id'] = Variable<int>(channelId); | ||||||
|  |     { | ||||||
|  |       map['content'] = Variable<String>( | ||||||
|  |           $SnLocalChatMessageTable.$convertercontent.toSql(content)); | ||||||
|  |     } | ||||||
|  |     map['created_at'] = Variable<DateTime>(createdAt); | ||||||
|  |     return map; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SnLocalChatMessageCompanion toCompanion(bool nullToAbsent) { | ||||||
|  |     return SnLocalChatMessageCompanion( | ||||||
|  |       id: Value(id), | ||||||
|  |       channelId: Value(channelId), | ||||||
|  |       content: Value(content), | ||||||
|  |       createdAt: Value(createdAt), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   factory SnLocalChatMessageData.fromJson(Map<String, dynamic> json, | ||||||
|  |       {ValueSerializer? serializer}) { | ||||||
|  |     serializer ??= driftRuntimeOptions.defaultSerializer; | ||||||
|  |     return SnLocalChatMessageData( | ||||||
|  |       id: serializer.fromJson<int>(json['id']), | ||||||
|  |       channelId: serializer.fromJson<int>(json['channelId']), | ||||||
|  |       content: $SnLocalChatMessageTable.$convertercontent | ||||||
|  |           .fromJson(serializer.fromJson<Map<String, Object?>>(json['content'])), | ||||||
|  |       createdAt: serializer.fromJson<DateTime>(json['createdAt']), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |   @override | ||||||
|  |   Map<String, dynamic> toJson({ValueSerializer? serializer}) { | ||||||
|  |     serializer ??= driftRuntimeOptions.defaultSerializer; | ||||||
|  |     return <String, dynamic>{ | ||||||
|  |       'id': serializer.toJson<int>(id), | ||||||
|  |       'channelId': serializer.toJson<int>(channelId), | ||||||
|  |       'content': serializer.toJson<Map<String, Object?>>( | ||||||
|  |           $SnLocalChatMessageTable.$convertercontent.toJson(content)), | ||||||
|  |       'createdAt': serializer.toJson<DateTime>(createdAt), | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SnLocalChatMessageData copyWith( | ||||||
|  |           {int? id, | ||||||
|  |           int? channelId, | ||||||
|  |           SnChatMessage? content, | ||||||
|  |           DateTime? createdAt}) => | ||||||
|  |       SnLocalChatMessageData( | ||||||
|  |         id: id ?? this.id, | ||||||
|  |         channelId: channelId ?? this.channelId, | ||||||
|  |         content: content ?? this.content, | ||||||
|  |         createdAt: createdAt ?? this.createdAt, | ||||||
|  |       ); | ||||||
|  |   SnLocalChatMessageData copyWithCompanion(SnLocalChatMessageCompanion data) { | ||||||
|  |     return SnLocalChatMessageData( | ||||||
|  |       id: data.id.present ? data.id.value : this.id, | ||||||
|  |       channelId: data.channelId.present ? data.channelId.value : this.channelId, | ||||||
|  |       content: data.content.present ? data.content.value : this.content, | ||||||
|  |       createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toString() { | ||||||
|  |     return (StringBuffer('SnLocalChatMessageData(') | ||||||
|  |           ..write('id: $id, ') | ||||||
|  |           ..write('channelId: $channelId, ') | ||||||
|  |           ..write('content: $content, ') | ||||||
|  |           ..write('createdAt: $createdAt') | ||||||
|  |           ..write(')')) | ||||||
|  |         .toString(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   int get hashCode => Object.hash(id, channelId, content, createdAt); | ||||||
|  |   @override | ||||||
|  |   bool operator ==(Object other) => | ||||||
|  |       identical(this, other) || | ||||||
|  |       (other is SnLocalChatMessageData && | ||||||
|  |           other.id == this.id && | ||||||
|  |           other.channelId == this.channelId && | ||||||
|  |           other.content == this.content && | ||||||
|  |           other.createdAt == this.createdAt); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class SnLocalChatMessageCompanion | ||||||
|  |     extends UpdateCompanion<SnLocalChatMessageData> { | ||||||
|  |   final Value<int> id; | ||||||
|  |   final Value<int> channelId; | ||||||
|  |   final Value<SnChatMessage> content; | ||||||
|  |   final Value<DateTime> createdAt; | ||||||
|  |   const SnLocalChatMessageCompanion({ | ||||||
|  |     this.id = const Value.absent(), | ||||||
|  |     this.channelId = const Value.absent(), | ||||||
|  |     this.content = const Value.absent(), | ||||||
|  |     this.createdAt = const Value.absent(), | ||||||
|  |   }); | ||||||
|  |   SnLocalChatMessageCompanion.insert({ | ||||||
|  |     this.id = const Value.absent(), | ||||||
|  |     required int channelId, | ||||||
|  |     required SnChatMessage content, | ||||||
|  |     this.createdAt = const Value.absent(), | ||||||
|  |   })  : channelId = Value(channelId), | ||||||
|  |         content = Value(content); | ||||||
|  |   static Insertable<SnLocalChatMessageData> custom({ | ||||||
|  |     Expression<int>? id, | ||||||
|  |     Expression<int>? channelId, | ||||||
|  |     Expression<String>? content, | ||||||
|  |     Expression<DateTime>? createdAt, | ||||||
|  |   }) { | ||||||
|  |     return RawValuesInsertable({ | ||||||
|  |       if (id != null) 'id': id, | ||||||
|  |       if (channelId != null) 'channel_id': channelId, | ||||||
|  |       if (content != null) 'content': content, | ||||||
|  |       if (createdAt != null) 'created_at': createdAt, | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   SnLocalChatMessageCompanion copyWith( | ||||||
|  |       {Value<int>? id, | ||||||
|  |       Value<int>? channelId, | ||||||
|  |       Value<SnChatMessage>? content, | ||||||
|  |       Value<DateTime>? createdAt}) { | ||||||
|  |     return SnLocalChatMessageCompanion( | ||||||
|  |       id: id ?? this.id, | ||||||
|  |       channelId: channelId ?? this.channelId, | ||||||
|  |       content: content ?? this.content, | ||||||
|  |       createdAt: createdAt ?? this.createdAt, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Map<String, Expression> toColumns(bool nullToAbsent) { | ||||||
|  |     final map = <String, Expression>{}; | ||||||
|  |     if (id.present) { | ||||||
|  |       map['id'] = Variable<int>(id.value); | ||||||
|  |     } | ||||||
|  |     if (channelId.present) { | ||||||
|  |       map['channel_id'] = Variable<int>(channelId.value); | ||||||
|  |     } | ||||||
|  |     if (content.present) { | ||||||
|  |       map['content'] = Variable<String>( | ||||||
|  |           $SnLocalChatMessageTable.$convertercontent.toSql(content.value)); | ||||||
|  |     } | ||||||
|  |     if (createdAt.present) { | ||||||
|  |       map['created_at'] = Variable<DateTime>(createdAt.value); | ||||||
|  |     } | ||||||
|  |     return map; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String toString() { | ||||||
|  |     return (StringBuffer('SnLocalChatMessageCompanion(') | ||||||
|  |           ..write('id: $id, ') | ||||||
|  |           ..write('channelId: $channelId, ') | ||||||
|  |           ..write('content: $content, ') | ||||||
|  |           ..write('createdAt: $createdAt') | ||||||
|  |           ..write(')')) | ||||||
|  |         .toString(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | abstract class _$AppDatabase extends GeneratedDatabase { | ||||||
|  |   _$AppDatabase(QueryExecutor e) : super(e); | ||||||
|  |   $AppDatabaseManager get managers => $AppDatabaseManager(this); | ||||||
|  |   late final $SnLocalChatChannelTable snLocalChatChannel = | ||||||
|  |       $SnLocalChatChannelTable(this); | ||||||
|  |   late final $SnLocalChatMessageTable snLocalChatMessage = | ||||||
|  |       $SnLocalChatMessageTable(this); | ||||||
|  |   @override | ||||||
|  |   Iterable<TableInfo<Table, Object?>> get allTables => | ||||||
|  |       allSchemaEntities.whereType<TableInfo<Table, Object?>>(); | ||||||
|  |   @override | ||||||
|  |   List<DatabaseSchemaEntity> get allSchemaEntities => | ||||||
|  |       [snLocalChatChannel, snLocalChatMessage]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | typedef $$SnLocalChatChannelTableCreateCompanionBuilder | ||||||
|  |     = SnLocalChatChannelCompanion Function({ | ||||||
|  |   Value<int> id, | ||||||
|  |   required String alias, | ||||||
|  |   required SnChannel content, | ||||||
|  |   Value<DateTime> createdAt, | ||||||
|  | }); | ||||||
|  | typedef $$SnLocalChatChannelTableUpdateCompanionBuilder | ||||||
|  |     = SnLocalChatChannelCompanion Function({ | ||||||
|  |   Value<int> id, | ||||||
|  |   Value<String> alias, | ||||||
|  |   Value<SnChannel> content, | ||||||
|  |   Value<DateTime> createdAt, | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | class $$SnLocalChatChannelTableFilterComposer | ||||||
|  |     extends Composer<_$AppDatabase, $SnLocalChatChannelTable> { | ||||||
|  |   $$SnLocalChatChannelTableFilterComposer({ | ||||||
|  |     required super.$db, | ||||||
|  |     required super.$table, | ||||||
|  |     super.joinBuilder, | ||||||
|  |     super.$addJoinBuilderToRootComposer, | ||||||
|  |     super.$removeJoinBuilderFromRootComposer, | ||||||
|  |   }); | ||||||
|  |   ColumnFilters<int> get id => $composableBuilder( | ||||||
|  |       column: $table.id, builder: (column) => ColumnFilters(column)); | ||||||
|  |  | ||||||
|  |   ColumnFilters<String> get alias => $composableBuilder( | ||||||
|  |       column: $table.alias, builder: (column) => ColumnFilters(column)); | ||||||
|  |  | ||||||
|  |   ColumnWithTypeConverterFilters<SnChannel, SnChannel, String> get content => | ||||||
|  |       $composableBuilder( | ||||||
|  |           column: $table.content, | ||||||
|  |           builder: (column) => ColumnWithTypeConverterFilters(column)); | ||||||
|  |  | ||||||
|  |   ColumnFilters<DateTime> get createdAt => $composableBuilder( | ||||||
|  |       column: $table.createdAt, builder: (column) => ColumnFilters(column)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class $$SnLocalChatChannelTableOrderingComposer | ||||||
|  |     extends Composer<_$AppDatabase, $SnLocalChatChannelTable> { | ||||||
|  |   $$SnLocalChatChannelTableOrderingComposer({ | ||||||
|  |     required super.$db, | ||||||
|  |     required super.$table, | ||||||
|  |     super.joinBuilder, | ||||||
|  |     super.$addJoinBuilderToRootComposer, | ||||||
|  |     super.$removeJoinBuilderFromRootComposer, | ||||||
|  |   }); | ||||||
|  |   ColumnOrderings<int> get id => $composableBuilder( | ||||||
|  |       column: $table.id, builder: (column) => ColumnOrderings(column)); | ||||||
|  |  | ||||||
|  |   ColumnOrderings<String> get alias => $composableBuilder( | ||||||
|  |       column: $table.alias, builder: (column) => ColumnOrderings(column)); | ||||||
|  |  | ||||||
|  |   ColumnOrderings<String> get content => $composableBuilder( | ||||||
|  |       column: $table.content, builder: (column) => ColumnOrderings(column)); | ||||||
|  |  | ||||||
|  |   ColumnOrderings<DateTime> get createdAt => $composableBuilder( | ||||||
|  |       column: $table.createdAt, builder: (column) => ColumnOrderings(column)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class $$SnLocalChatChannelTableAnnotationComposer | ||||||
|  |     extends Composer<_$AppDatabase, $SnLocalChatChannelTable> { | ||||||
|  |   $$SnLocalChatChannelTableAnnotationComposer({ | ||||||
|  |     required super.$db, | ||||||
|  |     required super.$table, | ||||||
|  |     super.joinBuilder, | ||||||
|  |     super.$addJoinBuilderToRootComposer, | ||||||
|  |     super.$removeJoinBuilderFromRootComposer, | ||||||
|  |   }); | ||||||
|  |   GeneratedColumn<int> get id => | ||||||
|  |       $composableBuilder(column: $table.id, builder: (column) => column); | ||||||
|  |  | ||||||
|  |   GeneratedColumn<String> get alias => | ||||||
|  |       $composableBuilder(column: $table.alias, builder: (column) => column); | ||||||
|  |  | ||||||
|  |   GeneratedColumnWithTypeConverter<SnChannel, String> get content => | ||||||
|  |       $composableBuilder(column: $table.content, builder: (column) => column); | ||||||
|  |  | ||||||
|  |   GeneratedColumn<DateTime> get createdAt => | ||||||
|  |       $composableBuilder(column: $table.createdAt, builder: (column) => column); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class $$SnLocalChatChannelTableTableManager extends RootTableManager< | ||||||
|  |     _$AppDatabase, | ||||||
|  |     $SnLocalChatChannelTable, | ||||||
|  |     SnLocalChatChannelData, | ||||||
|  |     $$SnLocalChatChannelTableFilterComposer, | ||||||
|  |     $$SnLocalChatChannelTableOrderingComposer, | ||||||
|  |     $$SnLocalChatChannelTableAnnotationComposer, | ||||||
|  |     $$SnLocalChatChannelTableCreateCompanionBuilder, | ||||||
|  |     $$SnLocalChatChannelTableUpdateCompanionBuilder, | ||||||
|  |     ( | ||||||
|  |       SnLocalChatChannelData, | ||||||
|  |       BaseReferences<_$AppDatabase, $SnLocalChatChannelTable, | ||||||
|  |           SnLocalChatChannelData> | ||||||
|  |     ), | ||||||
|  |     SnLocalChatChannelData, | ||||||
|  |     PrefetchHooks Function()> { | ||||||
|  |   $$SnLocalChatChannelTableTableManager( | ||||||
|  |       _$AppDatabase db, $SnLocalChatChannelTable table) | ||||||
|  |       : super(TableManagerState( | ||||||
|  |           db: db, | ||||||
|  |           table: table, | ||||||
|  |           createFilteringComposer: () => | ||||||
|  |               $$SnLocalChatChannelTableFilterComposer($db: db, $table: table), | ||||||
|  |           createOrderingComposer: () => | ||||||
|  |               $$SnLocalChatChannelTableOrderingComposer($db: db, $table: table), | ||||||
|  |           createComputedFieldComposer: () => | ||||||
|  |               $$SnLocalChatChannelTableAnnotationComposer( | ||||||
|  |                   $db: db, $table: table), | ||||||
|  |           updateCompanionCallback: ({ | ||||||
|  |             Value<int> id = const Value.absent(), | ||||||
|  |             Value<String> alias = const Value.absent(), | ||||||
|  |             Value<SnChannel> content = const Value.absent(), | ||||||
|  |             Value<DateTime> createdAt = const Value.absent(), | ||||||
|  |           }) => | ||||||
|  |               SnLocalChatChannelCompanion( | ||||||
|  |             id: id, | ||||||
|  |             alias: alias, | ||||||
|  |             content: content, | ||||||
|  |             createdAt: createdAt, | ||||||
|  |           ), | ||||||
|  |           createCompanionCallback: ({ | ||||||
|  |             Value<int> id = const Value.absent(), | ||||||
|  |             required String alias, | ||||||
|  |             required SnChannel content, | ||||||
|  |             Value<DateTime> createdAt = const Value.absent(), | ||||||
|  |           }) => | ||||||
|  |               SnLocalChatChannelCompanion.insert( | ||||||
|  |             id: id, | ||||||
|  |             alias: alias, | ||||||
|  |             content: content, | ||||||
|  |             createdAt: createdAt, | ||||||
|  |           ), | ||||||
|  |           withReferenceMapper: (p0) => p0 | ||||||
|  |               .map((e) => (e.readTable(table), BaseReferences(db, table, e))) | ||||||
|  |               .toList(), | ||||||
|  |           prefetchHooksCallback: null, | ||||||
|  |         )); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | typedef $$SnLocalChatChannelTableProcessedTableManager = ProcessedTableManager< | ||||||
|  |     _$AppDatabase, | ||||||
|  |     $SnLocalChatChannelTable, | ||||||
|  |     SnLocalChatChannelData, | ||||||
|  |     $$SnLocalChatChannelTableFilterComposer, | ||||||
|  |     $$SnLocalChatChannelTableOrderingComposer, | ||||||
|  |     $$SnLocalChatChannelTableAnnotationComposer, | ||||||
|  |     $$SnLocalChatChannelTableCreateCompanionBuilder, | ||||||
|  |     $$SnLocalChatChannelTableUpdateCompanionBuilder, | ||||||
|  |     ( | ||||||
|  |       SnLocalChatChannelData, | ||||||
|  |       BaseReferences<_$AppDatabase, $SnLocalChatChannelTable, | ||||||
|  |           SnLocalChatChannelData> | ||||||
|  |     ), | ||||||
|  |     SnLocalChatChannelData, | ||||||
|  |     PrefetchHooks Function()>; | ||||||
|  | typedef $$SnLocalChatMessageTableCreateCompanionBuilder | ||||||
|  |     = SnLocalChatMessageCompanion Function({ | ||||||
|  |   Value<int> id, | ||||||
|  |   required int channelId, | ||||||
|  |   required SnChatMessage content, | ||||||
|  |   Value<DateTime> createdAt, | ||||||
|  | }); | ||||||
|  | typedef $$SnLocalChatMessageTableUpdateCompanionBuilder | ||||||
|  |     = SnLocalChatMessageCompanion Function({ | ||||||
|  |   Value<int> id, | ||||||
|  |   Value<int> channelId, | ||||||
|  |   Value<SnChatMessage> content, | ||||||
|  |   Value<DateTime> createdAt, | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | class $$SnLocalChatMessageTableFilterComposer | ||||||
|  |     extends Composer<_$AppDatabase, $SnLocalChatMessageTable> { | ||||||
|  |   $$SnLocalChatMessageTableFilterComposer({ | ||||||
|  |     required super.$db, | ||||||
|  |     required super.$table, | ||||||
|  |     super.joinBuilder, | ||||||
|  |     super.$addJoinBuilderToRootComposer, | ||||||
|  |     super.$removeJoinBuilderFromRootComposer, | ||||||
|  |   }); | ||||||
|  |   ColumnFilters<int> get id => $composableBuilder( | ||||||
|  |       column: $table.id, builder: (column) => ColumnFilters(column)); | ||||||
|  |  | ||||||
|  |   ColumnFilters<int> get channelId => $composableBuilder( | ||||||
|  |       column: $table.channelId, builder: (column) => ColumnFilters(column)); | ||||||
|  |  | ||||||
|  |   ColumnWithTypeConverterFilters<SnChatMessage, SnChatMessage, String> | ||||||
|  |       get content => $composableBuilder( | ||||||
|  |           column: $table.content, | ||||||
|  |           builder: (column) => ColumnWithTypeConverterFilters(column)); | ||||||
|  |  | ||||||
|  |   ColumnFilters<DateTime> get createdAt => $composableBuilder( | ||||||
|  |       column: $table.createdAt, builder: (column) => ColumnFilters(column)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class $$SnLocalChatMessageTableOrderingComposer | ||||||
|  |     extends Composer<_$AppDatabase, $SnLocalChatMessageTable> { | ||||||
|  |   $$SnLocalChatMessageTableOrderingComposer({ | ||||||
|  |     required super.$db, | ||||||
|  |     required super.$table, | ||||||
|  |     super.joinBuilder, | ||||||
|  |     super.$addJoinBuilderToRootComposer, | ||||||
|  |     super.$removeJoinBuilderFromRootComposer, | ||||||
|  |   }); | ||||||
|  |   ColumnOrderings<int> get id => $composableBuilder( | ||||||
|  |       column: $table.id, builder: (column) => ColumnOrderings(column)); | ||||||
|  |  | ||||||
|  |   ColumnOrderings<int> get channelId => $composableBuilder( | ||||||
|  |       column: $table.channelId, builder: (column) => ColumnOrderings(column)); | ||||||
|  |  | ||||||
|  |   ColumnOrderings<String> get content => $composableBuilder( | ||||||
|  |       column: $table.content, builder: (column) => ColumnOrderings(column)); | ||||||
|  |  | ||||||
|  |   ColumnOrderings<DateTime> get createdAt => $composableBuilder( | ||||||
|  |       column: $table.createdAt, builder: (column) => ColumnOrderings(column)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class $$SnLocalChatMessageTableAnnotationComposer | ||||||
|  |     extends Composer<_$AppDatabase, $SnLocalChatMessageTable> { | ||||||
|  |   $$SnLocalChatMessageTableAnnotationComposer({ | ||||||
|  |     required super.$db, | ||||||
|  |     required super.$table, | ||||||
|  |     super.joinBuilder, | ||||||
|  |     super.$addJoinBuilderToRootComposer, | ||||||
|  |     super.$removeJoinBuilderFromRootComposer, | ||||||
|  |   }); | ||||||
|  |   GeneratedColumn<int> get id => | ||||||
|  |       $composableBuilder(column: $table.id, builder: (column) => column); | ||||||
|  |  | ||||||
|  |   GeneratedColumn<int> get channelId => | ||||||
|  |       $composableBuilder(column: $table.channelId, builder: (column) => column); | ||||||
|  |  | ||||||
|  |   GeneratedColumnWithTypeConverter<SnChatMessage, String> get content => | ||||||
|  |       $composableBuilder(column: $table.content, builder: (column) => column); | ||||||
|  |  | ||||||
|  |   GeneratedColumn<DateTime> get createdAt => | ||||||
|  |       $composableBuilder(column: $table.createdAt, builder: (column) => column); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class $$SnLocalChatMessageTableTableManager extends RootTableManager< | ||||||
|  |     _$AppDatabase, | ||||||
|  |     $SnLocalChatMessageTable, | ||||||
|  |     SnLocalChatMessageData, | ||||||
|  |     $$SnLocalChatMessageTableFilterComposer, | ||||||
|  |     $$SnLocalChatMessageTableOrderingComposer, | ||||||
|  |     $$SnLocalChatMessageTableAnnotationComposer, | ||||||
|  |     $$SnLocalChatMessageTableCreateCompanionBuilder, | ||||||
|  |     $$SnLocalChatMessageTableUpdateCompanionBuilder, | ||||||
|  |     ( | ||||||
|  |       SnLocalChatMessageData, | ||||||
|  |       BaseReferences<_$AppDatabase, $SnLocalChatMessageTable, | ||||||
|  |           SnLocalChatMessageData> | ||||||
|  |     ), | ||||||
|  |     SnLocalChatMessageData, | ||||||
|  |     PrefetchHooks Function()> { | ||||||
|  |   $$SnLocalChatMessageTableTableManager( | ||||||
|  |       _$AppDatabase db, $SnLocalChatMessageTable table) | ||||||
|  |       : super(TableManagerState( | ||||||
|  |           db: db, | ||||||
|  |           table: table, | ||||||
|  |           createFilteringComposer: () => | ||||||
|  |               $$SnLocalChatMessageTableFilterComposer($db: db, $table: table), | ||||||
|  |           createOrderingComposer: () => | ||||||
|  |               $$SnLocalChatMessageTableOrderingComposer($db: db, $table: table), | ||||||
|  |           createComputedFieldComposer: () => | ||||||
|  |               $$SnLocalChatMessageTableAnnotationComposer( | ||||||
|  |                   $db: db, $table: table), | ||||||
|  |           updateCompanionCallback: ({ | ||||||
|  |             Value<int> id = const Value.absent(), | ||||||
|  |             Value<int> channelId = const Value.absent(), | ||||||
|  |             Value<SnChatMessage> content = const Value.absent(), | ||||||
|  |             Value<DateTime> createdAt = const Value.absent(), | ||||||
|  |           }) => | ||||||
|  |               SnLocalChatMessageCompanion( | ||||||
|  |             id: id, | ||||||
|  |             channelId: channelId, | ||||||
|  |             content: content, | ||||||
|  |             createdAt: createdAt, | ||||||
|  |           ), | ||||||
|  |           createCompanionCallback: ({ | ||||||
|  |             Value<int> id = const Value.absent(), | ||||||
|  |             required int channelId, | ||||||
|  |             required SnChatMessage content, | ||||||
|  |             Value<DateTime> createdAt = const Value.absent(), | ||||||
|  |           }) => | ||||||
|  |               SnLocalChatMessageCompanion.insert( | ||||||
|  |             id: id, | ||||||
|  |             channelId: channelId, | ||||||
|  |             content: content, | ||||||
|  |             createdAt: createdAt, | ||||||
|  |           ), | ||||||
|  |           withReferenceMapper: (p0) => p0 | ||||||
|  |               .map((e) => (e.readTable(table), BaseReferences(db, table, e))) | ||||||
|  |               .toList(), | ||||||
|  |           prefetchHooksCallback: null, | ||||||
|  |         )); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | typedef $$SnLocalChatMessageTableProcessedTableManager = ProcessedTableManager< | ||||||
|  |     _$AppDatabase, | ||||||
|  |     $SnLocalChatMessageTable, | ||||||
|  |     SnLocalChatMessageData, | ||||||
|  |     $$SnLocalChatMessageTableFilterComposer, | ||||||
|  |     $$SnLocalChatMessageTableOrderingComposer, | ||||||
|  |     $$SnLocalChatMessageTableAnnotationComposer, | ||||||
|  |     $$SnLocalChatMessageTableCreateCompanionBuilder, | ||||||
|  |     $$SnLocalChatMessageTableUpdateCompanionBuilder, | ||||||
|  |     ( | ||||||
|  |       SnLocalChatMessageData, | ||||||
|  |       BaseReferences<_$AppDatabase, $SnLocalChatMessageTable, | ||||||
|  |           SnLocalChatMessageData> | ||||||
|  |     ), | ||||||
|  |     SnLocalChatMessageData, | ||||||
|  |     PrefetchHooks Function()>; | ||||||
|  |  | ||||||
|  | class $AppDatabaseManager { | ||||||
|  |   final _$AppDatabase _db; | ||||||
|  |   $AppDatabaseManager(this._db); | ||||||
|  |   $$SnLocalChatChannelTableTableManager get snLocalChatChannel => | ||||||
|  |       $$SnLocalChatChannelTableTableManager(_db, _db.snLocalChatChannel); | ||||||
|  |   $$SnLocalChatMessageTableTableManager get snLocalChatMessage => | ||||||
|  |       $$SnLocalChatMessageTableTableManager(_db, _db.snLocalChatMessage); | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								lib/database/drift_worker.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								lib/database/drift_worker.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | import 'package:drift/wasm.dart'; | ||||||
|  |  | ||||||
|  | // Use `dart compile js -O4 ./drift_worker.dart` to compile this file. | ||||||
|  | // And place it in the web/ directory. | ||||||
|  |  | ||||||
|  | // When compiled with dart2js, this file defines a dedicated or shared web | ||||||
|  | // worker used by drift. | ||||||
|  | void main() => WasmDatabase.workerMainForOpen(); | ||||||
| @@ -13,7 +13,6 @@ import 'package:flutter/foundation.dart'; | |||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:flutter/services.dart'; | import 'package:flutter/services.dart'; | ||||||
| import 'package:go_router/go_router.dart'; | import 'package:go_router/go_router.dart'; | ||||||
| import 'package:hive_flutter/hive_flutter.dart'; |  | ||||||
| import 'package:hotkey_manager/hotkey_manager.dart'; | import 'package:hotkey_manager/hotkey_manager.dart'; | ||||||
| import 'package:package_info_plus/package_info_plus.dart'; | import 'package:package_info_plus/package_info_plus.dart'; | ||||||
| import 'package:provider/provider.dart'; | import 'package:provider/provider.dart'; | ||||||
| @@ -24,6 +23,7 @@ import 'package:surface/firebase_options.dart'; | |||||||
| import 'package:surface/providers/channel.dart'; | import 'package:surface/providers/channel.dart'; | ||||||
| import 'package:surface/providers/chat_call.dart'; | import 'package:surface/providers/chat_call.dart'; | ||||||
| import 'package:surface/providers/config.dart'; | import 'package:surface/providers/config.dart'; | ||||||
|  | import 'package:surface/providers/database.dart'; | ||||||
| import 'package:surface/providers/link_preview.dart'; | import 'package:surface/providers/link_preview.dart'; | ||||||
| import 'package:surface/providers/navigation.dart'; | import 'package:surface/providers/navigation.dart'; | ||||||
| import 'package:surface/providers/notification.dart'; | import 'package:surface/providers/notification.dart'; | ||||||
| @@ -40,8 +40,6 @@ import 'package:surface/providers/userinfo.dart'; | |||||||
| import 'package:surface/providers/websocket.dart'; | import 'package:surface/providers/websocket.dart'; | ||||||
| import 'package:surface/providers/widget.dart'; | import 'package:surface/providers/widget.dart'; | ||||||
| import 'package:surface/router.dart'; | import 'package:surface/router.dart'; | ||||||
| import 'package:surface/types/chat.dart'; |  | ||||||
| import 'package:surface/types/realm.dart'; |  | ||||||
| import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy; | import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy; | ||||||
| import 'package:surface/widgets/dialog.dart'; | import 'package:surface/widgets/dialog.dart'; | ||||||
| import 'package:tray_manager/tray_manager.dart'; | import 'package:tray_manager/tray_manager.dart'; | ||||||
| @@ -82,12 +80,6 @@ void main() async { | |||||||
|  |  | ||||||
|   await EasyLocalization.ensureInitialized(); |   await EasyLocalization.ensureInitialized(); | ||||||
|  |  | ||||||
|   await Hive.initFlutter(); |  | ||||||
|   Hive.registerAdapter(SnChannelImplAdapter()); |  | ||||||
|   Hive.registerAdapter(SnRealmImplAdapter()); |  | ||||||
|   Hive.registerAdapter(SnChannelMemberImplAdapter()); |  | ||||||
|   Hive.registerAdapter(SnChatMessageImplAdapter()); |  | ||||||
|  |  | ||||||
|   if (!kIsWeb && !Platform.isLinux) { |   if (!kIsWeb && !Platform.isLinux) { | ||||||
|     await Firebase.initializeApp( |     await Firebase.initializeApp( | ||||||
|       options: DefaultFirebaseOptions.currentPlatform, |       options: DefaultFirebaseOptions.currentPlatform, | ||||||
| @@ -114,7 +106,8 @@ void main() async { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (!kIsWeb && Platform.isAndroid) { |   if (!kIsWeb && Platform.isAndroid) { | ||||||
|     final ImagePickerPlatform imagePickerImplementation = ImagePickerPlatform.instance; |     final ImagePickerPlatform imagePickerImplementation = | ||||||
|  |         ImagePickerPlatform.instance; | ||||||
|     if (imagePickerImplementation is ImagePickerAndroid) { |     if (imagePickerImplementation is ImagePickerAndroid) { | ||||||
|       imagePickerImplementation.useAndroidPhotoPicker = true; |       imagePickerImplementation.useAndroidPhotoPicker = true; | ||||||
|     } |     } | ||||||
| @@ -142,6 +135,9 @@ class SolianApp extends StatelessWidget { | |||||||
|         assetLoader: JsonAssetLoader(), |         assetLoader: JsonAssetLoader(), | ||||||
|         child: MultiProvider( |         child: MultiProvider( | ||||||
|           providers: [ |           providers: [ | ||||||
|  |             // Infrastructure layer | ||||||
|  |             Provider(create: (ctx) => DatabaseProvider(ctx)), | ||||||
|  |  | ||||||
|             // System extensions layer |             // System extensions layer | ||||||
|             Provider(create: (ctx) => HomeWidgetProvider(ctx)), |             Provider(create: (ctx) => HomeWidgetProvider(ctx)), | ||||||
|  |  | ||||||
| @@ -230,7 +226,8 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener { | |||||||
|     if (prefs.containsKey('first_boot_time')) { |     if (prefs.containsKey('first_boot_time')) { | ||||||
|       final rawTime = prefs.getString('first_boot_time'); |       final rawTime = prefs.getString('first_boot_time'); | ||||||
|       final time = DateTime.tryParse(rawTime ?? ''); |       final time = DateTime.tryParse(rawTime ?? ''); | ||||||
|       if (time != null && time.isBefore(DateTime.now().subtract(const Duration(days: 3)))) { |       if (time != null && | ||||||
|  |           time.isBefore(DateTime.now().subtract(const Duration(days: 3)))) { | ||||||
|         final inAppReview = InAppReview.instance; |         final inAppReview = InAppReview.instance; | ||||||
|         if (prefs.getBool('rating_requested') == true) return; |         if (prefs.getBool('rating_requested') == true) return; | ||||||
|         if (await inAppReview.isAvailable()) { |         if (await inAppReview.isAvailable()) { | ||||||
| @@ -258,13 +255,18 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener { | |||||||
|       ).get( |       ).get( | ||||||
|         'https://git.solsynth.dev/api/v1/repos/HyperNet/Surface/tags?page=1&limit=1', |         'https://git.solsynth.dev/api/v1/repos/HyperNet/Surface/tags?page=1&limit=1', | ||||||
|       ); |       ); | ||||||
|       final remoteVersionString = (resp.data as List).firstOrNull?['name'] ?? '0.0.0+0'; |       final remoteVersionString = | ||||||
|  |           (resp.data as List).firstOrNull?['name'] ?? '0.0.0+0'; | ||||||
|       final remoteVersion = Version.parse(remoteVersionString.split('+').first); |       final remoteVersion = Version.parse(remoteVersionString.split('+').first); | ||||||
|       final localVersion = Version.parse(localVersionString.split('+').first); |       final localVersion = Version.parse(localVersionString.split('+').first); | ||||||
|       final remoteBuildNumber = int.tryParse(remoteVersionString.split('+').last) ?? 0; |       final remoteBuildNumber = | ||||||
|       final localBuildNumber = int.tryParse(localVersionString.split('+').last) ?? 0; |           int.tryParse(remoteVersionString.split('+').last) ?? 0; | ||||||
|  |       final localBuildNumber = | ||||||
|  |           int.tryParse(localVersionString.split('+').last) ?? 0; | ||||||
|       log("[Update] Local: $localVersionString, Remote: $remoteVersionString"); |       log("[Update] Local: $localVersionString, Remote: $remoteVersionString"); | ||||||
|       if ((remoteVersion > localVersion || remoteBuildNumber > localBuildNumber) && mounted) { |       if ((remoteVersion > localVersion || | ||||||
|  |               remoteBuildNumber > localBuildNumber) && | ||||||
|  |           mounted) { | ||||||
|         final config = context.read<ConfigProvider>(); |         final config = context.read<ConfigProvider>(); | ||||||
|         config.setUpdate(remoteVersionString); |         config.setUpdate(remoteVersionString); | ||||||
|         log("[Update] Update available: $remoteVersionString"); |         log("[Update] Update available: $remoteVersionString"); | ||||||
| @@ -331,7 +333,9 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener { | |||||||
|   Future<void> _trayInitialization() async { |   Future<void> _trayInitialization() async { | ||||||
|     if (kIsWeb || Platform.isAndroid || Platform.isIOS) return; |     if (kIsWeb || Platform.isAndroid || Platform.isIOS) return; | ||||||
|  |  | ||||||
|     final icon = Platform.isWindows ? 'assets/icon/tray-icon.ico' : 'assets/icon/tray-icon.png'; |     final icon = Platform.isWindows | ||||||
|  |         ? 'assets/icon/tray-icon.ico' | ||||||
|  |         : 'assets/icon/tray-icon.png'; | ||||||
|     final appVersion = await PackageInfo.fromPlatform(); |     final appVersion = await PackageInfo.fromPlatform(); | ||||||
|  |  | ||||||
|     trayManager.addListener(this); |     trayManager.addListener(this); | ||||||
|   | |||||||
| @@ -1,7 +1,10 @@ | |||||||
|  | import 'dart:convert'; | ||||||
|  |  | ||||||
|  | import 'package:drift/drift.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:hive_flutter/hive_flutter.dart'; |  | ||||||
| import 'package:provider/provider.dart'; | import 'package:provider/provider.dart'; | ||||||
| import 'package:surface/controllers/chat_message_controller.dart'; | import 'package:surface/database/database.dart'; | ||||||
|  | import 'package:surface/providers/database.dart'; | ||||||
| import 'package:surface/providers/sn_network.dart'; | import 'package:surface/providers/sn_network.dart'; | ||||||
| import 'package:surface/providers/user_directory.dart'; | import 'package:surface/providers/user_directory.dart'; | ||||||
| import 'package:surface/types/chat.dart'; | import 'package:surface/types/chat.dart'; | ||||||
| @@ -12,24 +15,32 @@ class ChatChannelProvider extends ChangeNotifier { | |||||||
|  |  | ||||||
|   late final SnNetworkProvider _sn; |   late final SnNetworkProvider _sn; | ||||||
|   late final UserDirectoryProvider _ud; |   late final UserDirectoryProvider _ud; | ||||||
|  |   late final DatabaseProvider _dt; | ||||||
|   Box<SnChannel>? get _channelBox => Hive.box<SnChannel>(kChatChannelBoxName); |  | ||||||
|  |  | ||||||
|   ChatChannelProvider(BuildContext context) { |   ChatChannelProvider(BuildContext context) { | ||||||
|     _sn = context.read<SnNetworkProvider>(); |     _sn = context.read<SnNetworkProvider>(); | ||||||
|     _ud = context.read<UserDirectoryProvider>(); |     _ud = context.read<UserDirectoryProvider>(); | ||||||
|     _initializeLocalData(); |     _dt = context.read<DatabaseProvider>(); | ||||||
|   } |  | ||||||
|  |  | ||||||
|   Future<void> _initializeLocalData() async { |  | ||||||
|     await Hive.openBox<SnChannel>(kChatChannelBoxName); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   Future<void> _saveChannelToLocal(Iterable<SnChannel> channels) async { |   Future<void> _saveChannelToLocal(Iterable<SnChannel> channels) async { | ||||||
|     if (_channelBox == null) return; |     await Future.wait( | ||||||
|     await _channelBox!.putAll({ |       channels.map( | ||||||
|       for (final channel in channels) channel.key: channel, |         (ele) => _dt.db.snLocalChatChannel.insertOne( | ||||||
|     }); |           SnLocalChatChannelCompanion.insert( | ||||||
|  |             id: Value(ele.id), | ||||||
|  |             alias: ele.key, | ||||||
|  |             content: ele, | ||||||
|  |             createdAt: Value(ele.createdAt), | ||||||
|  |           ), | ||||||
|  |           onConflict: DoUpdate( | ||||||
|  |             (_) => SnLocalChatChannelCompanion.custom( | ||||||
|  |               content: Constant(jsonEncode(ele.toJson())), | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   Future<List<SnChannel>> _fetchChannelsFromServer({ |   Future<List<SnChannel>> _fetchChannelsFromServer({ | ||||||
| @@ -54,12 +65,13 @@ class ChatChannelProvider extends ChangeNotifier { | |||||||
|   /// It will use the local storage as much as possible. |   /// It will use the local storage as much as possible. | ||||||
|   /// The alias should include the scope, formatted as `scope:alias`. |   /// The alias should include the scope, formatted as `scope:alias`. | ||||||
|   Future<SnChannel> getChannel(String key) async { |   Future<SnChannel> getChannel(String key) async { | ||||||
|     if (_channelBox != null) { |     final local = await (_dt.db.snLocalChatChannel.select() | ||||||
|       final local = _channelBox!.get(key); |           ..where((e) => e.alias.equals(key))) | ||||||
|       if (local != null) return local; |         .getSingleOrNull(); | ||||||
|     } |     if (local != null) return local.content; | ||||||
|  |  | ||||||
|     var resp = await _sn.client.get('/cgi/im/channels/$key'); |     var resp = | ||||||
|  |         await _sn.client.get('/cgi/im/channels/${key.replaceAll(':', '/')}'); | ||||||
|     var out = SnChannel.fromJson(resp.data); |     var out = SnChannel.fromJson(resp.data); | ||||||
|  |  | ||||||
|     // Preload realm of the channel |     // Preload realm of the channel | ||||||
| @@ -77,8 +89,19 @@ class ChatChannelProvider extends ChangeNotifier { | |||||||
|   /// And the second time is when the data was fetched from the server. |   /// And the second time is when the data was fetched from the server. | ||||||
|   /// But there is some exception that will only cause one of them to be emitted. |   /// But there is some exception that will only cause one of them to be emitted. | ||||||
|   /// Like the local storage is broken or the server is down. |   /// Like the local storage is broken or the server is down. | ||||||
|   Stream<List<SnChannel>> fetchChannels() async* { |   Stream<List<SnChannel>> fetchChannels( | ||||||
|     if (_channelBox != null) yield _channelBox!.values.toList(); |       {bool noRemote = false, bool noLocal = false}) async* { | ||||||
|  |     if (!noLocal) { | ||||||
|  |       final local = await (_dt.db.snLocalChatChannel.select() | ||||||
|  |             ..orderBy([ | ||||||
|  |               (e) => | ||||||
|  |                   OrderingTerm(expression: e.createdAt, mode: OrderingMode.desc) | ||||||
|  |             ])) | ||||||
|  |           .get(); | ||||||
|  |       yield local.map((e) => e.content).toList(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (noRemote) return; | ||||||
|  |  | ||||||
|     var resp = await _sn.client.get('/cgi/id/realms/me/available'); |     var resp = await _sn.client.get('/cgi/id/realms/me/available'); | ||||||
|     final realms = List<SnRealm>.from( |     final realms = List<SnRealm>.from( | ||||||
| @@ -120,23 +143,23 @@ class ChatChannelProvider extends ChangeNotifier { | |||||||
|   Future<List<SnChatMessage>> getLastMessages( |   Future<List<SnChatMessage>> getLastMessages( | ||||||
|     Iterable<SnChannel> channels, |     Iterable<SnChannel> channels, | ||||||
|   ) async { |   ) async { | ||||||
|     final result = List<SnChatMessage>.empty(growable: true); |     final result = List<Future<SnLocalChatMessageData?>>.empty(growable: true); | ||||||
|     for (final channel in channels) { |     for (final channel in channels) { | ||||||
|       final channelBox = await Hive.openBox<SnChatMessage>( |       final out = (_dt.db.snLocalChatMessage.select() | ||||||
|         '${ChatMessageController.kChatMessageBoxPrefix}${channel.id}', |             ..where((e) => e.channelId.equals(channel.id)) | ||||||
|       ); |             ..orderBy([ | ||||||
|       final lastMessage = |               (e) => | ||||||
|           channelBox.isNotEmpty ? channelBox.values.reduce((a, b) => a.createdAt.isAfter(b.createdAt) ? a : b) : null; |                   OrderingTerm(expression: e.createdAt, mode: OrderingMode.desc) | ||||||
|       if (lastMessage != null) result.add(lastMessage); |             ]) | ||||||
|       channelBox.close(); |             ..limit(1)) | ||||||
|  |           .getSingleOrNull(); | ||||||
|  |       result.add(out); | ||||||
|     } |     } | ||||||
|     await _ud.listAccount(result.map((ele) => ele.sender.accountId).toSet()); |     final out = (await Future.wait(result)) | ||||||
|     return result; |         .where((e) => e != null) | ||||||
|   } |         .map((e) => e!.content) | ||||||
|  |         .toList(); | ||||||
|   @override |     await _ud.listAccount(out.map((ele) => ele.sender.accountId).toSet()); | ||||||
|   void dispose() { |     return out; | ||||||
|     _channelBox?.close(); |  | ||||||
|     super.dispose(); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								lib/providers/database.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								lib/providers/database.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | import 'dart:io'; | ||||||
|  |  | ||||||
|  | import 'package:flutter/foundation.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:path/path.dart' show join; | ||||||
|  | import 'package:path_provider/path_provider.dart'; | ||||||
|  | import 'package:surface/database/database.dart'; | ||||||
|  |  | ||||||
|  | class DatabaseProvider { | ||||||
|  |   late AppDatabase db; | ||||||
|  |  | ||||||
|  |   DatabaseProvider(BuildContext context) { | ||||||
|  |     db = AppDatabase(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   Future<int> getDatabaseSize() async { | ||||||
|  |     if (kIsWeb) return 0; | ||||||
|  |     final basepath = await getApplicationSupportDirectory(); | ||||||
|  |     return await File(join(basepath.path, 'solar_network_data.sqlite')) | ||||||
|  |         .length(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   Future<void> removeDatabase() async { | ||||||
|  |     if (kIsWeb) return; | ||||||
|  |     final basepath = await getApplicationSupportDirectory(); | ||||||
|  |     final file = File(join(basepath.path, 'solar_network_data.sqlite')); | ||||||
|  |     db.close(); | ||||||
|  |     await file.delete(); | ||||||
|  |     db = AppDatabase(); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -4,10 +4,10 @@ import 'package:easy_localization/easy_localization.dart'; | |||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:gap/gap.dart'; | import 'package:gap/gap.dart'; | ||||||
| import 'package:go_router/go_router.dart'; | import 'package:go_router/go_router.dart'; | ||||||
| import 'package:hive_flutter/hive_flutter.dart'; |  | ||||||
| import 'package:material_symbols_icons/symbols.dart'; | import 'package:material_symbols_icons/symbols.dart'; | ||||||
| import 'package:provider/provider.dart'; | import 'package:provider/provider.dart'; | ||||||
| import 'package:styled_widget/styled_widget.dart'; | import 'package:styled_widget/styled_widget.dart'; | ||||||
|  | import 'package:surface/providers/database.dart'; | ||||||
| import 'package:surface/providers/sn_network.dart'; | import 'package:surface/providers/sn_network.dart'; | ||||||
| import 'package:surface/providers/userinfo.dart'; | import 'package:surface/providers/userinfo.dart'; | ||||||
| import 'package:surface/providers/websocket.dart'; | import 'package:surface/providers/websocket.dart'; | ||||||
| @@ -45,7 +45,8 @@ class AccountScreen extends StatelessWidget { | |||||||
|             ? Stack( |             ? Stack( | ||||||
|                 fit: StackFit.expand, |                 fit: StackFit.expand, | ||||||
|                 children: [ |                 children: [ | ||||||
|                   AutoResizeUniversalImage(sn.getAttachmentUrl(ua.user!.banner), fit: BoxFit.cover), |                   AutoResizeUniversalImage(sn.getAttachmentUrl(ua.user!.banner), | ||||||
|  |                       fit: BoxFit.cover), | ||||||
|                   Positioned( |                   Positioned( | ||||||
|                     top: 0, |                     top: 0, | ||||||
|                     left: 0, |                     left: 0, | ||||||
| @@ -79,7 +80,9 @@ class AccountScreen extends StatelessWidget { | |||||||
|         ], |         ], | ||||||
|       ), |       ), | ||||||
|       body: SingleChildScrollView( |       body: SingleChildScrollView( | ||||||
|         child: ua.isAuthorized ? _AuthorizedAccountScreen() : _UnauthorizedAccountScreen(), |         child: ua.isAuthorized | ||||||
|  |             ? _AuthorizedAccountScreen() | ||||||
|  |             : _UnauthorizedAccountScreen(), | ||||||
|       ), |       ), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| @@ -115,12 +118,15 @@ class _AuthorizedAccountScreen extends StatelessWidget { | |||||||
|                     crossAxisAlignment: CrossAxisAlignment.baseline, |                     crossAxisAlignment: CrossAxisAlignment.baseline, | ||||||
|                     textBaseline: TextBaseline.alphabetic, |                     textBaseline: TextBaseline.alphabetic, | ||||||
|                     children: [ |                     children: [ | ||||||
|                       Text(ua.user!.nick).textStyle(Theme.of(context).textTheme.titleLarge!), |                       Text(ua.user!.nick) | ||||||
|  |                           .textStyle(Theme.of(context).textTheme.titleLarge!), | ||||||
|                       const Gap(4), |                       const Gap(4), | ||||||
|                       Text('@${ua.user!.name}').textStyle(Theme.of(context).textTheme.bodySmall!), |                       Text('@${ua.user!.name}') | ||||||
|  |                           .textStyle(Theme.of(context).textTheme.bodySmall!), | ||||||
|                     ], |                     ], | ||||||
|                   ), |                   ), | ||||||
|                   Text(ua.user!.description).textStyle(Theme.of(context).textTheme.bodyMedium!), |                   Text(ua.user!.description) | ||||||
|  |                       .textStyle(Theme.of(context).textTheme.bodyMedium!), | ||||||
|                 ], |                 ], | ||||||
|               ), |               ), | ||||||
|             ); |             ); | ||||||
| @@ -193,8 +199,7 @@ class _AuthorizedAccountScreen extends StatelessWidget { | |||||||
|             ua.logoutUser(); |             ua.logoutUser(); | ||||||
|             final ws = context.read<WebSocketProvider>(); |             final ws = context.read<WebSocketProvider>(); | ||||||
|             ws.disconnect(); |             ws.disconnect(); | ||||||
|             await Hive.deleteFromDisk(); |             context.read<DatabaseProvider>().removeDatabase(); | ||||||
|             await Hive.initFlutter(); |  | ||||||
|           }, |           }, | ||||||
|         ), |         ), | ||||||
|       ], |       ], | ||||||
| @@ -220,7 +225,9 @@ class _UnauthorizedAccountScreen extends StatelessWidget { | |||||||
|                   child: Icon(Symbols.waving_hand, size: 28), |                   child: Icon(Symbols.waving_hand, size: 28), | ||||||
|                 ), |                 ), | ||||||
|                 const Gap(8), |                 const Gap(8), | ||||||
|                 Text('accountIntroTitle').tr().textStyle(Theme.of(context).textTheme.titleLarge!), |                 Text('accountIntroTitle') | ||||||
|  |                     .tr() | ||||||
|  |                     .textStyle(Theme.of(context).textTheme.titleLarge!), | ||||||
|                 Text('accountIntroSubtitle').tr(), |                 Text('accountIntroSubtitle').tr(), | ||||||
|               ], |               ], | ||||||
|             ).padding(all: 20), |             ).padding(all: 20), | ||||||
|   | |||||||
| @@ -6,7 +6,9 @@ import 'package:go_router/go_router.dart'; | |||||||
| import 'package:material_symbols_icons/symbols.dart'; | import 'package:material_symbols_icons/symbols.dart'; | ||||||
| import 'package:provider/provider.dart'; | import 'package:provider/provider.dart'; | ||||||
| import 'package:surface/providers/channel.dart'; | import 'package:surface/providers/channel.dart'; | ||||||
|  | import 'package:surface/providers/sn_network.dart'; | ||||||
| import 'package:surface/providers/user_directory.dart'; | import 'package:surface/providers/user_directory.dart'; | ||||||
|  | import 'package:surface/providers/userinfo.dart'; | ||||||
| import 'package:surface/types/chat.dart'; | import 'package:surface/types/chat.dart'; | ||||||
| import 'package:surface/widgets/account/account_image.dart'; | import 'package:surface/widgets/account/account_image.dart'; | ||||||
| import 'package:surface/widgets/account/account_select.dart'; | import 'package:surface/widgets/account/account_select.dart'; | ||||||
| @@ -17,9 +19,6 @@ import 'package:surface/widgets/navigation/app_scaffold.dart'; | |||||||
| import 'package:surface/widgets/unauthorized_hint.dart'; | import 'package:surface/widgets/unauthorized_hint.dart'; | ||||||
| import 'package:uuid/uuid.dart'; | import 'package:uuid/uuid.dart'; | ||||||
|  |  | ||||||
| import '../providers/sn_network.dart'; |  | ||||||
| import '../providers/userinfo.dart'; |  | ||||||
|  |  | ||||||
| class ChatScreen extends StatefulWidget { | class ChatScreen extends StatefulWidget { | ||||||
|   const ChatScreen({super.key}); |   const ChatScreen({super.key}); | ||||||
|  |  | ||||||
| @@ -35,7 +34,7 @@ class _ChatScreenState extends State<ChatScreen> { | |||||||
|   List<SnChannel>? _channels; |   List<SnChannel>? _channels; | ||||||
|   Map<int, SnChatMessage>? _lastMessages; |   Map<int, SnChatMessage>? _lastMessages; | ||||||
|  |  | ||||||
|   void _refreshChannels() { |   void _refreshChannels({bool noRemote = false}) { | ||||||
|     final ua = context.read<UserProvider>(); |     final ua = context.read<UserProvider>(); | ||||||
|     if (!ua.isAuthorized) { |     if (!ua.isAuthorized) { | ||||||
|       setState(() => _isBusy = false); |       setState(() => _isBusy = false); | ||||||
| @@ -43,12 +42,15 @@ class _ChatScreenState extends State<ChatScreen> { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     final chan = context.read<ChatChannelProvider>(); |     final chan = context.read<ChatChannelProvider>(); | ||||||
|     chan.fetchChannels().listen((channels) async { |     chan.fetchChannels(noRemote: noRemote).listen((channels) async { | ||||||
|       final lastMessages = await chan.getLastMessages(channels); |       final lastMessages = await chan.getLastMessages(channels); | ||||||
|       _lastMessages = {for (final val in lastMessages) val.channelId: val}; |       _lastMessages = {for (final val in lastMessages) val.channelId: val}; | ||||||
|       channels.sort((a, b) { |       channels.sort((a, b) { | ||||||
|         if (_lastMessages!.containsKey(a.id) && _lastMessages!.containsKey(b.id)) { |         if (_lastMessages!.containsKey(a.id) && | ||||||
|           return _lastMessages![b.id]!.createdAt.compareTo(_lastMessages![a.id]!.createdAt); |             _lastMessages!.containsKey(b.id)) { | ||||||
|  |           return _lastMessages![b.id]! | ||||||
|  |               .createdAt | ||||||
|  |               .compareTo(_lastMessages![a.id]!.createdAt); | ||||||
|         } |         } | ||||||
|         if (_lastMessages!.containsKey(a.id)) return -1; |         if (_lastMessages!.containsKey(a.id)) return -1; | ||||||
|         if (_lastMessages!.containsKey(b.id)) return 1; |         if (_lastMessages!.containsKey(b.id)) return 1; | ||||||
| @@ -86,7 +88,8 @@ class _ChatScreenState extends State<ChatScreen> { | |||||||
|   void _newDirectMessage() async { |   void _newDirectMessage() async { | ||||||
|     final user = await showModalBottomSheet( |     final user = await showModalBottomSheet( | ||||||
|       context: context, |       context: context, | ||||||
|       builder: (context) => AccountSelect(title: 'channelNewDirectMessage'.tr()), |       builder: (context) => | ||||||
|  |           AccountSelect(title: 'channelNewDirectMessage'.tr()), | ||||||
|     ); |     ); | ||||||
|     if (user == null) return; |     if (user == null) return; | ||||||
|     if (!mounted) return; |     if (!mounted) return; | ||||||
| @@ -98,7 +101,8 @@ class _ChatScreenState extends State<ChatScreen> { | |||||||
|       await sn.client.post('/cgi/im/channels/global/dm', data: { |       await sn.client.post('/cgi/im/channels/global/dm', data: { | ||||||
|         'alias': uuid.v4().replaceAll('-', '').substring(0, 12), |         'alias': uuid.v4().replaceAll('-', '').substring(0, 12), | ||||||
|         'name': 'DM', |         'name': 'DM', | ||||||
|         'description': 'A direct message channel between @${ua.user?.name} and @${user.name}', |         'description': | ||||||
|  |             'A direct message channel between @${ua.user?.name} and @${user.name}', | ||||||
|         'related_user': user.id, |         'related_user': user.id, | ||||||
|       }); |       }); | ||||||
|       _fabKey.currentState!.toggle(); |       _fabKey.currentState!.toggle(); | ||||||
| @@ -144,20 +148,27 @@ class _ChatScreenState extends State<ChatScreen> { | |||||||
|         type: ExpandableFabType.up, |         type: ExpandableFabType.up, | ||||||
|         childrenAnimation: ExpandableFabAnimation.none, |         childrenAnimation: ExpandableFabAnimation.none, | ||||||
|         overlayStyle: ExpandableFabOverlayStyle( |         overlayStyle: ExpandableFabOverlayStyle( | ||||||
|           color: Theme.of(context).colorScheme.surface.withAlpha((255 * 0.5).round()), |           color: Theme.of(context) | ||||||
|  |               .colorScheme | ||||||
|  |               .surface | ||||||
|  |               .withAlpha((255 * 0.5).round()), | ||||||
|         ), |         ), | ||||||
|         openButtonBuilder: RotateFloatingActionButtonBuilder( |         openButtonBuilder: RotateFloatingActionButtonBuilder( | ||||||
|           child: const Icon(Symbols.add, size: 28), |           child: const Icon(Symbols.add, size: 28), | ||||||
|           fabSize: ExpandableFabSize.regular, |           fabSize: ExpandableFabSize.regular, | ||||||
|           foregroundColor: Theme.of(context).floatingActionButtonTheme.foregroundColor, |           foregroundColor: | ||||||
|           backgroundColor: Theme.of(context).floatingActionButtonTheme.backgroundColor, |               Theme.of(context).floatingActionButtonTheme.foregroundColor, | ||||||
|  |           backgroundColor: | ||||||
|  |               Theme.of(context).floatingActionButtonTheme.backgroundColor, | ||||||
|           shape: const CircleBorder(), |           shape: const CircleBorder(), | ||||||
|         ), |         ), | ||||||
|         closeButtonBuilder: DefaultFloatingActionButtonBuilder( |         closeButtonBuilder: DefaultFloatingActionButtonBuilder( | ||||||
|           child: const Icon(Symbols.close, size: 28), |           child: const Icon(Symbols.close, size: 28), | ||||||
|           fabSize: ExpandableFabSize.regular, |           fabSize: ExpandableFabSize.regular, | ||||||
|           foregroundColor: Theme.of(context).floatingActionButtonTheme.foregroundColor, |           foregroundColor: | ||||||
|           backgroundColor: Theme.of(context).floatingActionButtonTheme.backgroundColor, |               Theme.of(context).floatingActionButtonTheme.foregroundColor, | ||||||
|  |           backgroundColor: | ||||||
|  |               Theme.of(context).floatingActionButtonTheme.backgroundColor, | ||||||
|           shape: const CircleBorder(), |           shape: const CircleBorder(), | ||||||
|         ), |         ), | ||||||
|         children: [ |         children: [ | ||||||
| @@ -208,13 +219,17 @@ class _ChatScreenState extends State<ChatScreen> { | |||||||
|                     final lastMessage = _lastMessages?[channel.id]; |                     final lastMessage = _lastMessages?[channel.id]; | ||||||
|  |  | ||||||
|                     if (channel.type == 1) { |                     if (channel.type == 1) { | ||||||
|                       final otherMember = channel.members?.cast<SnChannelMember?>().firstWhere( |                       final otherMember = | ||||||
|                             (ele) => ele?.accountId != ua.user?.id, |                           channel.members?.cast<SnChannelMember?>().firstWhere( | ||||||
|                             orElse: () => null, |                                 (ele) => ele?.accountId != ua.user?.id, | ||||||
|                           ); |                                 orElse: () => null, | ||||||
|  |                               ); | ||||||
|  |  | ||||||
|                       return ListTile( |                       return ListTile( | ||||||
|                         title: Text(ud.getAccountFromCache(otherMember?.accountId)?.nick ?? channel.name), |                         title: Text(ud | ||||||
|  |                                 .getAccountFromCache(otherMember?.accountId) | ||||||
|  |                                 ?.nick ?? | ||||||
|  |                             channel.name), | ||||||
|                         subtitle: lastMessage != null |                         subtitle: lastMessage != null | ||||||
|                             ? Text( |                             ? Text( | ||||||
|                                 '${ud.getAccountFromCache(lastMessage.sender.accountId)?.nick}: ${lastMessage.body['text'] ?? 'Unable preview'}', |                                 '${ud.getAccountFromCache(lastMessage.sender.accountId)?.nick}: ${lastMessage.body['text'] ?? 'Unable preview'}', | ||||||
| @@ -228,9 +243,12 @@ class _ChatScreenState extends State<ChatScreen> { | |||||||
|                                 maxLines: 1, |                                 maxLines: 1, | ||||||
|                                 overflow: TextOverflow.ellipsis, |                                 overflow: TextOverflow.ellipsis, | ||||||
|                               ), |                               ), | ||||||
|                         contentPadding: const EdgeInsets.symmetric(horizontal: 16), |                         contentPadding: | ||||||
|  |                             const EdgeInsets.symmetric(horizontal: 16), | ||||||
|                         leading: AccountImage( |                         leading: AccountImage( | ||||||
|                           content: ud.getAccountFromCache(otherMember?.accountId)?.avatar, |                           content: ud | ||||||
|  |                               .getAccountFromCache(otherMember?.accountId) | ||||||
|  |                               ?.avatar, | ||||||
|                         ), |                         ), | ||||||
|                         onTap: () { |                         onTap: () { | ||||||
|                           GoRouter.of(context).pushNamed( |                           GoRouter.of(context).pushNamed( | ||||||
| @@ -240,7 +258,7 @@ class _ChatScreenState extends State<ChatScreen> { | |||||||
|                               'alias': channel.alias, |                               'alias': channel.alias, | ||||||
|                             }, |                             }, | ||||||
|                           ).then((value) { |                           ).then((value) { | ||||||
|                             if (mounted) _refreshChannels(); |                             if (mounted) _refreshChannels(noRemote: true); | ||||||
|                           }); |                           }); | ||||||
|                         }, |                         }, | ||||||
|                       ); |                       ); | ||||||
| @@ -259,7 +277,8 @@ class _ChatScreenState extends State<ChatScreen> { | |||||||
|                               maxLines: 1, |                               maxLines: 1, | ||||||
|                               overflow: TextOverflow.ellipsis, |                               overflow: TextOverflow.ellipsis, | ||||||
|                             ), |                             ), | ||||||
|                       contentPadding: const EdgeInsets.symmetric(horizontal: 16), |                       contentPadding: | ||||||
|  |                           const EdgeInsets.symmetric(horizontal: 16), | ||||||
|                       leading: AccountImage( |                       leading: AccountImage( | ||||||
|                         content: null, |                         content: null, | ||||||
|                         fallbackWidget: const Icon(Symbols.chat, size: 20), |                         fallbackWidget: const Icon(Symbols.chat, size: 20), | ||||||
|   | |||||||
| @@ -243,7 +243,12 @@ class _ExploreScreenState extends State<ExploreScreen> with SingleTickerProvider | |||||||
|                         children: [ |                         children: [ | ||||||
|                           Icon(Symbols.globe, size: 20, color: Theme.of(context).appBarTheme.foregroundColor), |                           Icon(Symbols.globe, size: 20, color: Theme.of(context).appBarTheme.foregroundColor), | ||||||
|                           const Gap(8), |                           const Gap(8), | ||||||
|                           Text('postChannelGlobal').tr().textColor(Theme.of(context).appBarTheme.foregroundColor), |                           Flexible( | ||||||
|  |                             child: Text( | ||||||
|  |                               'postChannelGlobal', | ||||||
|  |                               maxLines: 1, | ||||||
|  |                             ).tr().textColor(Theme.of(context).appBarTheme.foregroundColor), | ||||||
|  |                           ), | ||||||
|                         ], |                         ], | ||||||
|                       ), |                       ), | ||||||
|                     ), |                     ), | ||||||
| @@ -254,7 +259,13 @@ class _ExploreScreenState extends State<ExploreScreen> with SingleTickerProvider | |||||||
|                         children: [ |                         children: [ | ||||||
|                           Icon(Symbols.group, size: 20, color: Theme.of(context).appBarTheme.foregroundColor), |                           Icon(Symbols.group, size: 20, color: Theme.of(context).appBarTheme.foregroundColor), | ||||||
|                           const Gap(8), |                           const Gap(8), | ||||||
|                           Text('postChannelFriends').tr().textColor(Theme.of(context).appBarTheme.foregroundColor), |                           Flexible( | ||||||
|  |                             child: Text( | ||||||
|  |                               'postChannelFriends', | ||||||
|  |                               maxLines: 1, | ||||||
|  |                               textAlign: TextAlign.center, | ||||||
|  |                             ).tr().textColor(Theme.of(context).appBarTheme.foregroundColor), | ||||||
|  |                           ), | ||||||
|                         ], |                         ], | ||||||
|                       ), |                       ), | ||||||
|                     ), |                     ), | ||||||
| @@ -265,7 +276,12 @@ class _ExploreScreenState extends State<ExploreScreen> with SingleTickerProvider | |||||||
|                         children: [ |                         children: [ | ||||||
|                           Icon(Symbols.subscriptions, size: 20, color: Theme.of(context).appBarTheme.foregroundColor), |                           Icon(Symbols.subscriptions, size: 20, color: Theme.of(context).appBarTheme.foregroundColor), | ||||||
|                           const Gap(8), |                           const Gap(8), | ||||||
|                           Text('postChannelFollowing').tr().textColor(Theme.of(context).appBarTheme.foregroundColor), |                           Flexible( | ||||||
|  |                             child: Text( | ||||||
|  |                               'postChannelFollowing', | ||||||
|  |                               maxLines: 1, | ||||||
|  |                             ).tr().textColor(Theme.of(context).appBarTheme.foregroundColor), | ||||||
|  |                           ), | ||||||
|                         ], |                         ], | ||||||
|                       ), |                       ), | ||||||
|                     ), |                     ), | ||||||
| @@ -276,7 +292,12 @@ class _ExploreScreenState extends State<ExploreScreen> with SingleTickerProvider | |||||||
|                         children: [ |                         children: [ | ||||||
|                           Icon(Symbols.workspaces, size: 20, color: Theme.of(context).appBarTheme.foregroundColor), |                           Icon(Symbols.workspaces, size: 20, color: Theme.of(context).appBarTheme.foregroundColor), | ||||||
|                           const Gap(8), |                           const Gap(8), | ||||||
|                           Text('postChannelRealm').tr().textColor(Theme.of(context).appBarTheme.foregroundColor), |                           Flexible( | ||||||
|  |                             child: Text( | ||||||
|  |                               'postChannelRealm', | ||||||
|  |                               maxLines: 1, | ||||||
|  |                             ).tr().textColor(Theme.of(context).appBarTheme.foregroundColor), | ||||||
|  |                           ), | ||||||
|                         ], |                         ], | ||||||
|                       ), |                       ), | ||||||
|                     ), |                     ), | ||||||
|   | |||||||
| @@ -5,8 +5,10 @@ import 'package:dropdown_button2/dropdown_button2.dart'; | |||||||
| import 'package:easy_localization/easy_localization.dart'; | import 'package:easy_localization/easy_localization.dart'; | ||||||
| import 'package:flutter/foundation.dart'; | import 'package:flutter/foundation.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter/services.dart'; | ||||||
| import 'package:flutter_colorpicker/flutter_colorpicker.dart'; | import 'package:flutter_colorpicker/flutter_colorpicker.dart'; | ||||||
| import 'package:go_router/go_router.dart'; | import 'package:go_router/go_router.dart'; | ||||||
|  | import 'package:google_fonts/google_fonts.dart'; | ||||||
| import 'package:image_picker/image_picker.dart'; | import 'package:image_picker/image_picker.dart'; | ||||||
| import 'package:material_symbols_icons/symbols.dart'; | import 'package:material_symbols_icons/symbols.dart'; | ||||||
| import 'package:path_provider/path_provider.dart'; | import 'package:path_provider/path_provider.dart'; | ||||||
| @@ -14,6 +16,7 @@ import 'package:provider/provider.dart'; | |||||||
| import 'package:shared_preferences/shared_preferences.dart'; | import 'package:shared_preferences/shared_preferences.dart'; | ||||||
| import 'package:styled_widget/styled_widget.dart'; | import 'package:styled_widget/styled_widget.dart'; | ||||||
| import 'package:surface/providers/config.dart'; | import 'package:surface/providers/config.dart'; | ||||||
|  | import 'package:surface/providers/database.dart'; | ||||||
| import 'package:surface/providers/sn_network.dart'; | import 'package:surface/providers/sn_network.dart'; | ||||||
| import 'package:surface/providers/theme.dart'; | import 'package:surface/providers/theme.dart'; | ||||||
| import 'package:surface/theme.dart'; | import 'package:surface/theme.dart'; | ||||||
| @@ -67,6 +70,7 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     final sn = context.read<SnNetworkProvider>(); |     final sn = context.read<SnNetworkProvider>(); | ||||||
|  |     final dt = context.read<DatabaseProvider>(); | ||||||
|  |  | ||||||
|     return AppScaffold( |     return AppScaffold( | ||||||
|       appBar: AppBar( |       appBar: AppBar( | ||||||
| @@ -81,7 +85,11 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||
|             Column( |             Column( | ||||||
|               crossAxisAlignment: CrossAxisAlignment.start, |               crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|               children: [ |               children: [ | ||||||
|                 Text('settingsAppearance').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4), |                 Text('settingsAppearance') | ||||||
|  |                     .bold() | ||||||
|  |                     .fontSize(17) | ||||||
|  |                     .tr() | ||||||
|  |                     .padding(horizontal: 20, bottom: 4), | ||||||
|                 ListTile( |                 ListTile( | ||||||
|                   title: Text('settingsDisplayLanguage').tr(), |                   title: Text('settingsDisplayLanguage').tr(), | ||||||
|                   subtitle: Text('settingsDisplayLanguageDescription').tr(), |                   subtitle: Text('settingsDisplayLanguageDescription').tr(), | ||||||
| @@ -91,15 +99,21 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||
|                     child: DropdownButton2<Locale?>( |                     child: DropdownButton2<Locale?>( | ||||||
|                       isExpanded: true, |                       isExpanded: true, | ||||||
|                       items: [ |                       items: [ | ||||||
|                         ...EasyLocalization.of(context)!.supportedLocales.mapIndexed((idx, ele) { |                         ...EasyLocalization.of(context)! | ||||||
|  |                             .supportedLocales | ||||||
|  |                             .mapIndexed((idx, ele) { | ||||||
|                           return DropdownMenuItem<Locale?>( |                           return DropdownMenuItem<Locale?>( | ||||||
|                             value: ele, |                             value: ele, | ||||||
|                             child: Text('${ele.languageCode}-${ele.countryCode}').fontSize(14), |                             child: | ||||||
|  |                                 Text('${ele.languageCode}-${ele.countryCode}') | ||||||
|  |                                     .fontSize(14), | ||||||
|                           ); |                           ); | ||||||
|                         }), |                         }), | ||||||
|                         DropdownMenuItem<Locale?>( |                         DropdownMenuItem<Locale?>( | ||||||
|                           value: null, |                           value: null, | ||||||
|                           child: Text('settingsDisplayLanguageSystem').tr().fontSize(14), |                           child: Text('settingsDisplayLanguageSystem') | ||||||
|  |                               .tr() | ||||||
|  |                               .fontSize(14), | ||||||
|                         ), |                         ), | ||||||
|                       ], |                       ], | ||||||
|                       value: EasyLocalization.of(context)!.currentLocale, |                       value: EasyLocalization.of(context)!.currentLocale, | ||||||
| @@ -132,10 +146,12 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||
|                     leading: const Icon(Symbols.image), |                     leading: const Icon(Symbols.image), | ||||||
|                     trailing: const Icon(Symbols.chevron_right), |                     trailing: const Icon(Symbols.chevron_right), | ||||||
|                     onTap: () async { |                     onTap: () async { | ||||||
|                       final image = await ImagePicker().pickImage(source: ImageSource.gallery); |                       final image = await ImagePicker() | ||||||
|  |                           .pickImage(source: ImageSource.gallery); | ||||||
|                       if (image == null) return; |                       if (image == null) return; | ||||||
|  |  | ||||||
|                       await File(image.path).copy('$_docBasepath/app_background_image'); |                       await File(image.path) | ||||||
|  |                           .copy('$_docBasepath/app_background_image'); | ||||||
|                       _prefs.setBool(kAppBackgroundStoreKey, true); |                       _prefs.setBool(kAppBackgroundStoreKey, true); | ||||||
|  |  | ||||||
|                       setState(() {}); |                       setState(() {}); | ||||||
| @@ -143,7 +159,8 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||
|                   ), |                   ), | ||||||
|                 if (!kIsWeb) |                 if (!kIsWeb) | ||||||
|                   FutureBuilder<bool>( |                   FutureBuilder<bool>( | ||||||
|                       future: File('$_docBasepath/app_background_image').exists(), |                       future: | ||||||
|  |                           File('$_docBasepath/app_background_image').exists(), | ||||||
|                       builder: (context, snapshot) { |                       builder: (context, snapshot) { | ||||||
|                         if (!snapshot.hasData || !snapshot.data!) { |                         if (!snapshot.hasData || !snapshot.data!) { | ||||||
|                           return const SizedBox.shrink(); |                           return const SizedBox.shrink(); | ||||||
| @@ -151,12 +168,16 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||
|  |  | ||||||
|                         return ListTile( |                         return ListTile( | ||||||
|                           title: Text('settingsBackgroundImageClear').tr(), |                           title: Text('settingsBackgroundImageClear').tr(), | ||||||
|                           subtitle: Text('settingsBackgroundImageClearDescription').tr(), |                           subtitle: | ||||||
|                           contentPadding: const EdgeInsets.symmetric(horizontal: 24), |                               Text('settingsBackgroundImageClearDescription') | ||||||
|  |                                   .tr(), | ||||||
|  |                           contentPadding: | ||||||
|  |                               const EdgeInsets.symmetric(horizontal: 24), | ||||||
|                           leading: const Icon(Symbols.texture), |                           leading: const Icon(Symbols.texture), | ||||||
|                           trailing: const Icon(Symbols.chevron_right), |                           trailing: const Icon(Symbols.chevron_right), | ||||||
|                           onTap: () { |                           onTap: () { | ||||||
|                             File('$_docBasepath/app_background_image').deleteSync(); |                             File('$_docBasepath/app_background_image') | ||||||
|  |                                 .deleteSync(); | ||||||
|                             _prefs.remove(kAppBackgroundStoreKey); |                             _prefs.remove(kAppBackgroundStoreKey); | ||||||
|                             setState(() {}); |                             setState(() {}); | ||||||
|                           }, |                           }, | ||||||
| @@ -186,34 +207,35 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||
|                   contentPadding: const EdgeInsets.symmetric(horizontal: 24), |                   contentPadding: const EdgeInsets.symmetric(horizontal: 24), | ||||||
|                   trailing: const Icon(Symbols.chevron_right), |                   trailing: const Icon(Symbols.chevron_right), | ||||||
|                   onTap: () async { |                   onTap: () async { | ||||||
|                     Color pickerColor = Color(_prefs.getInt(kAppColorSchemeStoreKey) ?? Colors.indigo.value); |                     Color pickerColor = Color( | ||||||
|  |                         _prefs.getInt(kAppColorSchemeStoreKey) ?? | ||||||
|  |                             Colors.indigo.value); | ||||||
|                     final color = await showDialog<Color?>( |                     final color = await showDialog<Color?>( | ||||||
|                       context: context, |                       context: context, | ||||||
|                       builder: (context) => |                       builder: (context) => AlertDialog( | ||||||
|                           AlertDialog( |                         content: SingleChildScrollView( | ||||||
|                             content: SingleChildScrollView( |                           child: ColorPicker( | ||||||
|                               child: ColorPicker( |                             pickerColor: pickerColor, | ||||||
|                                 pickerColor: pickerColor, |                             onColorChanged: (color) => pickerColor = color, | ||||||
|                                 onColorChanged: (color) => pickerColor = color, |                             enableAlpha: false, | ||||||
|                                 enableAlpha: false, |                             hexInputBar: true, | ||||||
|                                 hexInputBar: true, |  | ||||||
|                               ), |  | ||||||
|                             ), |  | ||||||
|                             actions: <Widget>[ |  | ||||||
|                               TextButton( |  | ||||||
|                                 child: const Text('dialogDismiss').tr(), |  | ||||||
|                                 onPressed: () { |  | ||||||
|                                   Navigator.of(context).pop(); |  | ||||||
|                                 }, |  | ||||||
|                               ), |  | ||||||
|                               TextButton( |  | ||||||
|                                 child: const Text('dialogConfirm').tr(), |  | ||||||
|                                 onPressed: () { |  | ||||||
|                                   Navigator.of(context).pop(pickerColor); |  | ||||||
|                                 }, |  | ||||||
|                               ), |  | ||||||
|                             ], |  | ||||||
|                           ), |                           ), | ||||||
|  |                         ), | ||||||
|  |                         actions: <Widget>[ | ||||||
|  |                           TextButton( | ||||||
|  |                             child: const Text('dialogDismiss').tr(), | ||||||
|  |                             onPressed: () { | ||||||
|  |                               Navigator.of(context).pop(); | ||||||
|  |                             }, | ||||||
|  |                           ), | ||||||
|  |                           TextButton( | ||||||
|  |                             child: const Text('dialogConfirm').tr(), | ||||||
|  |                             onPressed: () { | ||||||
|  |                               Navigator.of(context).pop(pickerColor); | ||||||
|  |                             }, | ||||||
|  |                           ), | ||||||
|  |                         ], | ||||||
|  |                       ), | ||||||
|                     ); |                     ); | ||||||
|  |  | ||||||
|                     if (color == null || !context.mounted) return; |                     if (color == null || !context.mounted) return; | ||||||
| @@ -248,16 +270,17 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||
|                       ], |                       ], | ||||||
|                       value: _prefs.getInt(kAppColorSchemeStoreKey) == null |                       value: _prefs.getInt(kAppColorSchemeStoreKey) == null | ||||||
|                           ? 1 |                           ? 1 | ||||||
|                           : kColorSchemes.values |                           : kColorSchemes.values.toList().indexWhere((ele) => | ||||||
|                           .toList() |                               ele.value == | ||||||
|                           .indexWhere((ele) => ele.value == _prefs.getInt(kAppColorSchemeStoreKey)), |                               _prefs.getInt(kAppColorSchemeStoreKey)), | ||||||
|                       onChanged: (int? value) { |                       onChanged: (int? value) { | ||||||
|                         if (value != null && value != -1) { |                         if (value != null && value != -1) { | ||||||
|                           _prefs.setInt(kAppColorSchemeStoreKey, kColorSchemes.values |                           _prefs.setInt(kAppColorSchemeStoreKey, | ||||||
|                               .elementAt(value) |                               kColorSchemes.values.elementAt(value).value); | ||||||
|                               .value); |  | ||||||
|                           final th = context.read<ThemeProvider>(); |                           final th = context.read<ThemeProvider>(); | ||||||
|                           th.reloadTheme(seedColorOverride: kColorSchemes.values.elementAt(value)); |                           th.reloadTheme( | ||||||
|  |                               seedColorOverride: | ||||||
|  |                                   kColorSchemes.values.elementAt(value)); | ||||||
|                           setState(() {}); |                           setState(() {}); | ||||||
|  |  | ||||||
|                           context.showSnackbar('colorSchemeApplied'.tr()); |                           context.showSnackbar('colorSchemeApplied'.tr()); | ||||||
| @@ -293,7 +316,8 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||
|                 CheckboxListTile( |                 CheckboxListTile( | ||||||
|                   secondary: const Icon(Symbols.left_panel_close), |                   secondary: const Icon(Symbols.left_panel_close), | ||||||
|                   title: Text('settingsDrawerPreferCollapse').tr(), |                   title: Text('settingsDrawerPreferCollapse').tr(), | ||||||
|                   subtitle: Text('settingsDrawerPreferCollapseDescription').tr(), |                   subtitle: | ||||||
|  |                       Text('settingsDrawerPreferCollapseDescription').tr(), | ||||||
|                   contentPadding: const EdgeInsets.only(left: 24, right: 17), |                   contentPadding: const EdgeInsets.only(left: 24, right: 17), | ||||||
|                   value: _prefs.getBool(kAppDrawerPreferCollapse) ?? false, |                   value: _prefs.getBool(kAppDrawerPreferCollapse) ?? false, | ||||||
|                   onChanged: (value) { |                   onChanged: (value) { | ||||||
| @@ -308,7 +332,11 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||
|             Column( |             Column( | ||||||
|               crossAxisAlignment: CrossAxisAlignment.start, |               crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|               children: [ |               children: [ | ||||||
|                 Text('settingsFeatures').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4), |                 Text('settingsFeatures') | ||||||
|  |                     .bold() | ||||||
|  |                     .fontSize(17) | ||||||
|  |                     .tr() | ||||||
|  |                     .padding(horizontal: 20, bottom: 4), | ||||||
|                 CheckboxListTile( |                 CheckboxListTile( | ||||||
|                   secondary: const Icon(Symbols.vibration), |                   secondary: const Icon(Symbols.vibration), | ||||||
|                   contentPadding: const EdgeInsets.only(left: 24, right: 17), |                   contentPadding: const EdgeInsets.only(left: 24, right: 17), | ||||||
| @@ -350,7 +378,11 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||
|             Column( |             Column( | ||||||
|               crossAxisAlignment: CrossAxisAlignment.start, |               crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|               children: [ |               children: [ | ||||||
|                 Text('settingsNetwork').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4), |                 Text('settingsNetwork') | ||||||
|  |                     .bold() | ||||||
|  |                     .fontSize(17) | ||||||
|  |                     .tr() | ||||||
|  |                     .padding(horizontal: 20, bottom: 4), | ||||||
|                 TextField( |                 TextField( | ||||||
|                   controller: _serverUrlController, |                   controller: _serverUrlController, | ||||||
|                   decoration: InputDecoration( |                   decoration: InputDecoration( | ||||||
| @@ -371,7 +403,8 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||
|                       }, |                       }, | ||||||
|                     ), |                     ), | ||||||
|                   ), |                   ), | ||||||
|                   onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), |                   onTapOutside: (_) => | ||||||
|  |                       FocusManager.instance.primaryFocus?.unfocus(), | ||||||
|                 ).padding(horizontal: 16, top: 8, bottom: 4), |                 ).padding(horizontal: 16, top: 8, bottom: 4), | ||||||
|                 ListTile( |                 ListTile( | ||||||
|                   title: Text('settingsNetworkServerPreset').tr(), |                   title: Text('settingsNetworkServerPreset').tr(), | ||||||
| @@ -383,12 +416,13 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||
|                       isExpanded: true, |                       isExpanded: true, | ||||||
|                       items: [ |                       items: [ | ||||||
|                         ...kNetworkServerDirectory, |                         ...kNetworkServerDirectory, | ||||||
|                         if (!kNetworkServerDirectory.map((ele) => ele.$2).contains(_serverUrlController.text)) |                         if (!kNetworkServerDirectory | ||||||
|  |                             .map((ele) => ele.$2) | ||||||
|  |                             .contains(_serverUrlController.text)) | ||||||
|                           ('Custom', _serverUrlController.text), |                           ('Custom', _serverUrlController.text), | ||||||
|                       ] |                       ] | ||||||
|                           .map( |                           .map( | ||||||
|                             (item) => |                             (item) => DropdownMenuItem<String>( | ||||||
|                             DropdownMenuItem<String>( |  | ||||||
|                               value: item.$2, |                               value: item.$2, | ||||||
|                               child: Column( |                               child: Column( | ||||||
|                                 mainAxisSize: MainAxisSize.max, |                                 mainAxisSize: MainAxisSize.max, | ||||||
| @@ -396,11 +430,12 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||
|                                 crossAxisAlignment: CrossAxisAlignment.start, |                                 crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|                                 children: [ |                                 children: [ | ||||||
|                                   Text(item.$1).fontSize(14), |                                   Text(item.$1).fontSize(14), | ||||||
|                                   Text(item.$2, overflow: TextOverflow.ellipsis).fontSize(11) |                                   Text(item.$2, overflow: TextOverflow.ellipsis) | ||||||
|  |                                       .fontSize(11) | ||||||
|                                 ], |                                 ], | ||||||
|                               ), |                               ), | ||||||
|                             ), |                             ), | ||||||
|                       ) |                           ) | ||||||
|                           .toList(), |                           .toList(), | ||||||
|                       value: _serverUrlController.text, |                       value: _serverUrlController.text, | ||||||
|                       onChanged: (String? value) { |                       onChanged: (String? value) { | ||||||
| @@ -442,7 +477,11 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||
|             Column( |             Column( | ||||||
|               crossAxisAlignment: CrossAxisAlignment.start, |               crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|               children: [ |               children: [ | ||||||
|                 Text('settingsPerformance').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4), |                 Text('settingsPerformance') | ||||||
|  |                     .bold() | ||||||
|  |                     .fontSize(17) | ||||||
|  |                     .tr() | ||||||
|  |                     .padding(horizontal: 20, bottom: 4), | ||||||
|                 ListTile( |                 ListTile( | ||||||
|                   title: Text('settingsImageQuality').tr(), |                   title: Text('settingsImageQuality').tr(), | ||||||
|                   subtitle: Text('settingsImageQualityDescription').tr(), |                   subtitle: Text('settingsImageQualityDescription').tr(), | ||||||
| @@ -450,21 +489,22 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||
|                   leading: const Icon(Symbols.image), |                   leading: const Icon(Symbols.image), | ||||||
|                   trailing: DropdownButtonHideUnderline( |                   trailing: DropdownButtonHideUnderline( | ||||||
|                     child: DropdownButton2<FilterQuality>( |                     child: DropdownButton2<FilterQuality>( | ||||||
|                       value: kImageQualityLevel.values.elementAtOrNull(_prefs.getInt('app_image_quality') ?? 3) ?? |                       value: kImageQualityLevel.values.elementAtOrNull( | ||||||
|  |                               _prefs.getInt('app_image_quality') ?? 3) ?? | ||||||
|                           FilterQuality.high, |                           FilterQuality.high, | ||||||
|                       isExpanded: true, |                       isExpanded: true, | ||||||
|                       items: kImageQualityLevel.entries |                       items: kImageQualityLevel.entries | ||||||
|                           .map( |                           .map( | ||||||
|                             (item) => |                             (item) => DropdownMenuItem<FilterQuality>( | ||||||
|                             DropdownMenuItem<FilterQuality>( |  | ||||||
|                               value: item.value, |                               value: item.value, | ||||||
|                               child: Text(item.key).tr().fontSize(14), |                               child: Text(item.key).tr().fontSize(14), | ||||||
|                             ), |                             ), | ||||||
|                       ) |                           ) | ||||||
|                           .toList(), |                           .toList(), | ||||||
|                       onChanged: (FilterQuality? value) { |                       onChanged: (FilterQuality? value) { | ||||||
|                         if (value == null) return; |                         if (value == null) return; | ||||||
|                         _prefs.setInt('app_image_quality', kImageQualityLevel.values.toList().indexOf(value)); |                         _prefs.setInt('app_image_quality', | ||||||
|  |                             kImageQualityLevel.values.toList().indexOf(value)); | ||||||
|                         setState(() {}); |                         setState(() {}); | ||||||
|                       }, |                       }, | ||||||
|                       buttonStyleData: const ButtonStyleData( |                       buttonStyleData: const ButtonStyleData( | ||||||
| @@ -486,7 +526,42 @@ class _SettingsScreenState extends State<SettingsScreen> { | |||||||
|             Column( |             Column( | ||||||
|               crossAxisAlignment: CrossAxisAlignment.start, |               crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|               children: [ |               children: [ | ||||||
|                 Text('settingsMisc').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4), |                 Text('settingsMisc') | ||||||
|  |                     .bold() | ||||||
|  |                     .fontSize(17) | ||||||
|  |                     .tr() | ||||||
|  |                     .padding(horizontal: 20, bottom: 4), | ||||||
|  |                 ListTile( | ||||||
|  |                   leading: const Icon(Symbols.database), | ||||||
|  |                   contentPadding: const EdgeInsets.symmetric(horizontal: 24), | ||||||
|  |                   title: Text('databaseSize').tr(), | ||||||
|  |                   subtitle: FutureBuilder( | ||||||
|  |                     future: dt.getDatabaseSize(), | ||||||
|  |                     builder: (context, snapshot) { | ||||||
|  |                       if (!snapshot.hasData || kIsWeb) { | ||||||
|  |                         return Text('unknown').tr(); | ||||||
|  |                       } | ||||||
|  |                       return Text( | ||||||
|  |                         snapshot.data!.formatBytes(), | ||||||
|  |                         style: GoogleFonts.robotoMono(), | ||||||
|  |                       ); | ||||||
|  |                     }, | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |                 ListTile( | ||||||
|  |                   leading: const Icon(Symbols.database_off), | ||||||
|  |                   contentPadding: const EdgeInsets.symmetric(horizontal: 24), | ||||||
|  |                   title: Text('databaseDelete').tr(), | ||||||
|  |                   subtitle: Text('databaseDeleteDescription').tr(), | ||||||
|  |                   trailing: const Icon(Symbols.chevron_right), | ||||||
|  |                   onTap: () async { | ||||||
|  |                     await dt.removeDatabase(); | ||||||
|  |                     if (!context.mounted) return; | ||||||
|  |                     HapticFeedback.heavyImpact(); | ||||||
|  |                     context.showSnackbar('databaseDeleted'.tr()); | ||||||
|  |                     setState(() {}); | ||||||
|  |                   }, | ||||||
|  |                 ), | ||||||
|                 ListTile( |                 ListTile( | ||||||
|                   title: Text('settingsMiscAbout').tr(), |                   title: Text('settingsMiscAbout').tr(), | ||||||
|                   subtitle: Text('settingsMiscAboutDescription').tr(), |                   subtitle: Text('settingsMiscAboutDescription').tr(), | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ class SnAccount with _$SnAccount { | |||||||
|   const SnAccount._(); |   const SnAccount._(); | ||||||
|  |  | ||||||
|   const factory SnAccount({ |   const factory SnAccount({ | ||||||
|     @HiveField(0) required int id, |     required int id, | ||||||
|     required DateTime createdAt, |     required DateTime createdAt, | ||||||
|     required DateTime updatedAt, |     required DateTime updatedAt, | ||||||
|     required DateTime? deletedAt, |     required DateTime? deletedAt, | ||||||
|   | |||||||
| @@ -58,7 +58,7 @@ abstract class $SnAccountCopyWith<$Res> { | |||||||
|       _$SnAccountCopyWithImpl<$Res, SnAccount>; |       _$SnAccountCopyWithImpl<$Res, SnAccount>; | ||||||
|   @useResult |   @useResult | ||||||
|   $Res call( |   $Res call( | ||||||
|       {@HiveField(0) int id, |       {int id, | ||||||
|       DateTime createdAt, |       DateTime createdAt, | ||||||
|       DateTime updatedAt, |       DateTime updatedAt, | ||||||
|       DateTime? deletedAt, |       DateTime? deletedAt, | ||||||
| @@ -226,7 +226,7 @@ abstract class _$$SnAccountImplCopyWith<$Res> | |||||||
|   @override |   @override | ||||||
|   @useResult |   @useResult | ||||||
|   $Res call( |   $Res call( | ||||||
|       {@HiveField(0) int id, |       {int id, | ||||||
|       DateTime createdAt, |       DateTime createdAt, | ||||||
|       DateTime updatedAt, |       DateTime updatedAt, | ||||||
|       DateTime? deletedAt, |       DateTime? deletedAt, | ||||||
| @@ -374,7 +374,7 @@ class __$$SnAccountImplCopyWithImpl<$Res> | |||||||
| @JsonSerializable() | @JsonSerializable() | ||||||
| class _$SnAccountImpl extends _SnAccount { | class _$SnAccountImpl extends _SnAccount { | ||||||
|   const _$SnAccountImpl( |   const _$SnAccountImpl( | ||||||
|       {@HiveField(0) required this.id, |       {required this.id, | ||||||
|       required this.createdAt, |       required this.createdAt, | ||||||
|       required this.updatedAt, |       required this.updatedAt, | ||||||
|       required this.deletedAt, |       required this.deletedAt, | ||||||
| @@ -556,7 +556,7 @@ class _$SnAccountImpl extends _SnAccount { | |||||||
|  |  | ||||||
| abstract class _SnAccount extends SnAccount { | abstract class _SnAccount extends SnAccount { | ||||||
|   const factory _SnAccount( |   const factory _SnAccount( | ||||||
|       {@HiveField(0) required final int id, |       {required final int id, | ||||||
|       required final DateTime createdAt, |       required final DateTime createdAt, | ||||||
|       required final DateTime updatedAt, |       required final DateTime updatedAt, | ||||||
|       required final DateTime? deletedAt, |       required final DateTime? deletedAt, | ||||||
|   | |||||||
| @@ -14,21 +14,21 @@ class SnChannel with _$SnChannel { | |||||||
|  |  | ||||||
|   @HiveType(typeId: 2) |   @HiveType(typeId: 2) | ||||||
|   const factory SnChannel({ |   const factory SnChannel({ | ||||||
|     @HiveField(0) required int id, |     required int id, | ||||||
|     @HiveField(1) required DateTime createdAt, |     required DateTime createdAt, | ||||||
|     @HiveField(2) required DateTime updatedAt, |     required DateTime updatedAt, | ||||||
|     @HiveField(3) required dynamic deletedAt, |     required dynamic deletedAt, | ||||||
|     @HiveField(4) required String alias, |     required String alias, | ||||||
|     @HiveField(5) required String name, |     required String name, | ||||||
|     @HiveField(6) required String description, |     required String description, | ||||||
|     @HiveField(7) required List<SnChannelMember>? members, |     required List<SnChannelMember>? members, | ||||||
|     List<SnChatMessage>? messages, |     List<SnChatMessage>? messages, | ||||||
|     @HiveField(8) required int type, |     required int type, | ||||||
|     @HiveField(9) required int accountId, |     required int accountId, | ||||||
|     @HiveField(10) required SnRealm? realm, |     required SnRealm? realm, | ||||||
|     @HiveField(11) required int? realmId, |     required int? realmId, | ||||||
|     @HiveField(12) required bool isPublic, |     required bool isPublic, | ||||||
|     @HiveField(13) required bool isCommunity, |     required bool isCommunity, | ||||||
|   }) = _SnChannel; |   }) = _SnChannel; | ||||||
|  |  | ||||||
|   factory SnChannel.fromJson(Map<String, dynamic> json) => |   factory SnChannel.fromJson(Map<String, dynamic> json) => | ||||||
| @@ -44,17 +44,17 @@ class SnChannelMember with _$SnChannelMember { | |||||||
|  |  | ||||||
|   @HiveType(typeId: 3) |   @HiveType(typeId: 3) | ||||||
|   const factory SnChannelMember({ |   const factory SnChannelMember({ | ||||||
|     @HiveField(0) required int id, |     required int id, | ||||||
|     @HiveField(1) required DateTime createdAt, |     required DateTime createdAt, | ||||||
|     @HiveField(2) required DateTime updatedAt, |     required DateTime updatedAt, | ||||||
|     @HiveField(3) required DateTime? deletedAt, |     required DateTime? deletedAt, | ||||||
|     @HiveField(4) required int channelId, |     required int channelId, | ||||||
|     @HiveField(5) required int accountId, |     required int accountId, | ||||||
|     @HiveField(6) required String? nick, |     required String? nick, | ||||||
|     @HiveField(7) required SnChannel? channel, |     required SnChannel? channel, | ||||||
|     @HiveField(8) required SnAccount? account, |     required SnAccount? account, | ||||||
|     @Default(0) int notify, |     @Default(0) int notify, | ||||||
|     @HiveField(9) required int powerLevel, |     required int powerLevel, | ||||||
|     dynamic calls, |     dynamic calls, | ||||||
|     dynamic events, |     dynamic events, | ||||||
|   }) = _SnChannelMember; |   }) = _SnChannelMember; | ||||||
| @@ -69,19 +69,19 @@ class SnChatMessage with _$SnChatMessage { | |||||||
|  |  | ||||||
|   @HiveType(typeId: 4) |   @HiveType(typeId: 4) | ||||||
|   const factory SnChatMessage({ |   const factory SnChatMessage({ | ||||||
|     @HiveField(0) required int id, |     required int id, | ||||||
|     @HiveField(1) required DateTime createdAt, |     required DateTime createdAt, | ||||||
|     @HiveField(2) required DateTime updatedAt, |     required DateTime updatedAt, | ||||||
|     @HiveField(3) required DateTime? deletedAt, |     required DateTime? deletedAt, | ||||||
|     @HiveField(4) required String uuid, |     required String uuid, | ||||||
|     @HiveField(5) @Default({}) Map<String, dynamic> body, |     @Default({}) Map<String, dynamic> body, | ||||||
|     @HiveField(6) required String type, |     required String type, | ||||||
|     @HiveField(7) required SnChannel channel, |     required SnChannel channel, | ||||||
|     @HiveField(8) required SnChannelMember sender, |     required SnChannelMember sender, | ||||||
|     @HiveField(9) required int channelId, |     required int channelId, | ||||||
|     @HiveField(10) required int senderId, |     required int senderId, | ||||||
|     @HiveField(11) required int? quoteEventId, |     required int? quoteEventId, | ||||||
|     @HiveField(12) required int? relatedEventId, |     required int? relatedEventId, | ||||||
|     SnChatMessagePreload? preload, |     SnChatMessagePreload? preload, | ||||||
|   }) = _SnChatMessage; |   }) = _SnChatMessage; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -66,21 +66,21 @@ abstract class $SnChannelCopyWith<$Res> { | |||||||
|       _$SnChannelCopyWithImpl<$Res, SnChannel>; |       _$SnChannelCopyWithImpl<$Res, SnChannel>; | ||||||
|   @useResult |   @useResult | ||||||
|   $Res call( |   $Res call( | ||||||
|       {@HiveField(0) int id, |       {int id, | ||||||
|       @HiveField(1) DateTime createdAt, |       DateTime createdAt, | ||||||
|       @HiveField(2) DateTime updatedAt, |       DateTime updatedAt, | ||||||
|       @HiveField(3) dynamic deletedAt, |       dynamic deletedAt, | ||||||
|       @HiveField(4) String alias, |       String alias, | ||||||
|       @HiveField(5) String name, |       String name, | ||||||
|       @HiveField(6) String description, |       String description, | ||||||
|       @HiveField(7) List<SnChannelMember>? members, |       List<SnChannelMember>? members, | ||||||
|       List<SnChatMessage>? messages, |       List<SnChatMessage>? messages, | ||||||
|       @HiveField(8) int type, |       int type, | ||||||
|       @HiveField(9) int accountId, |       int accountId, | ||||||
|       @HiveField(10) SnRealm? realm, |       SnRealm? realm, | ||||||
|       @HiveField(11) int? realmId, |       int? realmId, | ||||||
|       @HiveField(12) bool isPublic, |       bool isPublic, | ||||||
|       @HiveField(13) bool isCommunity}); |       bool isCommunity}); | ||||||
|  |  | ||||||
|   $SnRealmCopyWith<$Res>? get realm; |   $SnRealmCopyWith<$Res>? get realm; | ||||||
| } | } | ||||||
| @@ -204,21 +204,21 @@ abstract class _$$SnChannelImplCopyWith<$Res> | |||||||
|   @override |   @override | ||||||
|   @useResult |   @useResult | ||||||
|   $Res call( |   $Res call( | ||||||
|       {@HiveField(0) int id, |       {int id, | ||||||
|       @HiveField(1) DateTime createdAt, |       DateTime createdAt, | ||||||
|       @HiveField(2) DateTime updatedAt, |       DateTime updatedAt, | ||||||
|       @HiveField(3) dynamic deletedAt, |       dynamic deletedAt, | ||||||
|       @HiveField(4) String alias, |       String alias, | ||||||
|       @HiveField(5) String name, |       String name, | ||||||
|       @HiveField(6) String description, |       String description, | ||||||
|       @HiveField(7) List<SnChannelMember>? members, |       List<SnChannelMember>? members, | ||||||
|       List<SnChatMessage>? messages, |       List<SnChatMessage>? messages, | ||||||
|       @HiveField(8) int type, |       int type, | ||||||
|       @HiveField(9) int accountId, |       int accountId, | ||||||
|       @HiveField(10) SnRealm? realm, |       SnRealm? realm, | ||||||
|       @HiveField(11) int? realmId, |       int? realmId, | ||||||
|       @HiveField(12) bool isPublic, |       bool isPublic, | ||||||
|       @HiveField(13) bool isCommunity}); |       bool isCommunity}); | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   $SnRealmCopyWith<$Res>? get realm; |   $SnRealmCopyWith<$Res>? get realm; | ||||||
| @@ -323,21 +323,21 @@ class __$$SnChannelImplCopyWithImpl<$Res> | |||||||
| @HiveType(typeId: 2) | @HiveType(typeId: 2) | ||||||
| class _$SnChannelImpl extends _SnChannel { | class _$SnChannelImpl extends _SnChannel { | ||||||
|   const _$SnChannelImpl( |   const _$SnChannelImpl( | ||||||
|       {@HiveField(0) required this.id, |       {required this.id, | ||||||
|       @HiveField(1) required this.createdAt, |       required this.createdAt, | ||||||
|       @HiveField(2) required this.updatedAt, |       required this.updatedAt, | ||||||
|       @HiveField(3) required this.deletedAt, |       required this.deletedAt, | ||||||
|       @HiveField(4) required this.alias, |       required this.alias, | ||||||
|       @HiveField(5) required this.name, |       required this.name, | ||||||
|       @HiveField(6) required this.description, |       required this.description, | ||||||
|       @HiveField(7) required final List<SnChannelMember>? members, |       required final List<SnChannelMember>? members, | ||||||
|       final List<SnChatMessage>? messages, |       final List<SnChatMessage>? messages, | ||||||
|       @HiveField(8) required this.type, |       required this.type, | ||||||
|       @HiveField(9) required this.accountId, |       required this.accountId, | ||||||
|       @HiveField(10) required this.realm, |       required this.realm, | ||||||
|       @HiveField(11) required this.realmId, |       required this.realmId, | ||||||
|       @HiveField(12) required this.isPublic, |       required this.isPublic, | ||||||
|       @HiveField(13) required this.isCommunity}) |       required this.isCommunity}) | ||||||
|       : _members = members, |       : _members = members, | ||||||
|         _messages = messages, |         _messages = messages, | ||||||
|         super._(); |         super._(); | ||||||
| @@ -477,21 +477,21 @@ class _$SnChannelImpl extends _SnChannel { | |||||||
|  |  | ||||||
| abstract class _SnChannel extends SnChannel { | abstract class _SnChannel extends SnChannel { | ||||||
|   const factory _SnChannel( |   const factory _SnChannel( | ||||||
|       {@HiveField(0) required final int id, |       {required final int id, | ||||||
|       @HiveField(1) required final DateTime createdAt, |       required final DateTime createdAt, | ||||||
|       @HiveField(2) required final DateTime updatedAt, |       required final DateTime updatedAt, | ||||||
|       @HiveField(3) required final dynamic deletedAt, |       required final dynamic deletedAt, | ||||||
|       @HiveField(4) required final String alias, |       required final String alias, | ||||||
|       @HiveField(5) required final String name, |       required final String name, | ||||||
|       @HiveField(6) required final String description, |       required final String description, | ||||||
|       @HiveField(7) required final List<SnChannelMember>? members, |       required final List<SnChannelMember>? members, | ||||||
|       final List<SnChatMessage>? messages, |       final List<SnChatMessage>? messages, | ||||||
|       @HiveField(8) required final int type, |       required final int type, | ||||||
|       @HiveField(9) required final int accountId, |       required final int accountId, | ||||||
|       @HiveField(10) required final SnRealm? realm, |       required final SnRealm? realm, | ||||||
|       @HiveField(11) required final int? realmId, |       required final int? realmId, | ||||||
|       @HiveField(12) required final bool isPublic, |       required final bool isPublic, | ||||||
|       @HiveField(13) required final bool isCommunity}) = _$SnChannelImpl; |       required final bool isCommunity}) = _$SnChannelImpl; | ||||||
|   const _SnChannel._() : super._(); |   const _SnChannel._() : super._(); | ||||||
|  |  | ||||||
|   factory _SnChannel.fromJson(Map<String, dynamic> json) = |   factory _SnChannel.fromJson(Map<String, dynamic> json) = | ||||||
| @@ -597,17 +597,17 @@ abstract class $SnChannelMemberCopyWith<$Res> { | |||||||
|       _$SnChannelMemberCopyWithImpl<$Res, SnChannelMember>; |       _$SnChannelMemberCopyWithImpl<$Res, SnChannelMember>; | ||||||
|   @useResult |   @useResult | ||||||
|   $Res call( |   $Res call( | ||||||
|       {@HiveField(0) int id, |       {int id, | ||||||
|       @HiveField(1) DateTime createdAt, |       DateTime createdAt, | ||||||
|       @HiveField(2) DateTime updatedAt, |       DateTime updatedAt, | ||||||
|       @HiveField(3) DateTime? deletedAt, |       DateTime? deletedAt, | ||||||
|       @HiveField(4) int channelId, |       int channelId, | ||||||
|       @HiveField(5) int accountId, |       int accountId, | ||||||
|       @HiveField(6) String? nick, |       String? nick, | ||||||
|       @HiveField(7) SnChannel? channel, |       SnChannel? channel, | ||||||
|       @HiveField(8) SnAccount? account, |       SnAccount? account, | ||||||
|       int notify, |       int notify, | ||||||
|       @HiveField(9) int powerLevel, |       int powerLevel, | ||||||
|       dynamic calls, |       dynamic calls, | ||||||
|       dynamic events}); |       dynamic events}); | ||||||
|  |  | ||||||
| @@ -738,17 +738,17 @@ abstract class _$$SnChannelMemberImplCopyWith<$Res> | |||||||
|   @override |   @override | ||||||
|   @useResult |   @useResult | ||||||
|   $Res call( |   $Res call( | ||||||
|       {@HiveField(0) int id, |       {int id, | ||||||
|       @HiveField(1) DateTime createdAt, |       DateTime createdAt, | ||||||
|       @HiveField(2) DateTime updatedAt, |       DateTime updatedAt, | ||||||
|       @HiveField(3) DateTime? deletedAt, |       DateTime? deletedAt, | ||||||
|       @HiveField(4) int channelId, |       int channelId, | ||||||
|       @HiveField(5) int accountId, |       int accountId, | ||||||
|       @HiveField(6) String? nick, |       String? nick, | ||||||
|       @HiveField(7) SnChannel? channel, |       SnChannel? channel, | ||||||
|       @HiveField(8) SnAccount? account, |       SnAccount? account, | ||||||
|       int notify, |       int notify, | ||||||
|       @HiveField(9) int powerLevel, |       int powerLevel, | ||||||
|       dynamic calls, |       dynamic calls, | ||||||
|       dynamic events}); |       dynamic events}); | ||||||
|  |  | ||||||
| @@ -847,17 +847,17 @@ class __$$SnChannelMemberImplCopyWithImpl<$Res> | |||||||
| @HiveType(typeId: 3) | @HiveType(typeId: 3) | ||||||
| class _$SnChannelMemberImpl extends _SnChannelMember { | class _$SnChannelMemberImpl extends _SnChannelMember { | ||||||
|   const _$SnChannelMemberImpl( |   const _$SnChannelMemberImpl( | ||||||
|       {@HiveField(0) required this.id, |       {required this.id, | ||||||
|       @HiveField(1) required this.createdAt, |       required this.createdAt, | ||||||
|       @HiveField(2) required this.updatedAt, |       required this.updatedAt, | ||||||
|       @HiveField(3) required this.deletedAt, |       required this.deletedAt, | ||||||
|       @HiveField(4) required this.channelId, |       required this.channelId, | ||||||
|       @HiveField(5) required this.accountId, |       required this.accountId, | ||||||
|       @HiveField(6) required this.nick, |       required this.nick, | ||||||
|       @HiveField(7) required this.channel, |       required this.channel, | ||||||
|       @HiveField(8) required this.account, |       required this.account, | ||||||
|       this.notify = 0, |       this.notify = 0, | ||||||
|       @HiveField(9) required this.powerLevel, |       required this.powerLevel, | ||||||
|       this.calls, |       this.calls, | ||||||
|       this.events}) |       this.events}) | ||||||
|       : super._(); |       : super._(); | ||||||
| @@ -971,17 +971,17 @@ class _$SnChannelMemberImpl extends _SnChannelMember { | |||||||
|  |  | ||||||
| abstract class _SnChannelMember extends SnChannelMember { | abstract class _SnChannelMember extends SnChannelMember { | ||||||
|   const factory _SnChannelMember( |   const factory _SnChannelMember( | ||||||
|       {@HiveField(0) required final int id, |       {required final int id, | ||||||
|       @HiveField(1) required final DateTime createdAt, |       required final DateTime createdAt, | ||||||
|       @HiveField(2) required final DateTime updatedAt, |       required final DateTime updatedAt, | ||||||
|       @HiveField(3) required final DateTime? deletedAt, |       required final DateTime? deletedAt, | ||||||
|       @HiveField(4) required final int channelId, |       required final int channelId, | ||||||
|       @HiveField(5) required final int accountId, |       required final int accountId, | ||||||
|       @HiveField(6) required final String? nick, |       required final String? nick, | ||||||
|       @HiveField(7) required final SnChannel? channel, |       required final SnChannel? channel, | ||||||
|       @HiveField(8) required final SnAccount? account, |       required final SnAccount? account, | ||||||
|       final int notify, |       final int notify, | ||||||
|       @HiveField(9) required final int powerLevel, |       required final int powerLevel, | ||||||
|       final dynamic calls, |       final dynamic calls, | ||||||
|       final dynamic events}) = _$SnChannelMemberImpl; |       final dynamic events}) = _$SnChannelMemberImpl; | ||||||
|   const _SnChannelMember._() : super._(); |   const _SnChannelMember._() : super._(); | ||||||
| @@ -1085,19 +1085,19 @@ abstract class $SnChatMessageCopyWith<$Res> { | |||||||
|       _$SnChatMessageCopyWithImpl<$Res, SnChatMessage>; |       _$SnChatMessageCopyWithImpl<$Res, SnChatMessage>; | ||||||
|   @useResult |   @useResult | ||||||
|   $Res call( |   $Res call( | ||||||
|       {@HiveField(0) int id, |       {int id, | ||||||
|       @HiveField(1) DateTime createdAt, |       DateTime createdAt, | ||||||
|       @HiveField(2) DateTime updatedAt, |       DateTime updatedAt, | ||||||
|       @HiveField(3) DateTime? deletedAt, |       DateTime? deletedAt, | ||||||
|       @HiveField(4) String uuid, |       String uuid, | ||||||
|       @HiveField(5) Map<String, dynamic> body, |       Map<String, dynamic> body, | ||||||
|       @HiveField(6) String type, |       String type, | ||||||
|       @HiveField(7) SnChannel channel, |       SnChannel channel, | ||||||
|       @HiveField(8) SnChannelMember sender, |       SnChannelMember sender, | ||||||
|       @HiveField(9) int channelId, |       int channelId, | ||||||
|       @HiveField(10) int senderId, |       int senderId, | ||||||
|       @HiveField(11) int? quoteEventId, |       int? quoteEventId, | ||||||
|       @HiveField(12) int? relatedEventId, |       int? relatedEventId, | ||||||
|       SnChatMessagePreload? preload}); |       SnChatMessagePreload? preload}); | ||||||
|  |  | ||||||
|   $SnChannelCopyWith<$Res> get channel; |   $SnChannelCopyWith<$Res> get channel; | ||||||
| @@ -1239,19 +1239,19 @@ abstract class _$$SnChatMessageImplCopyWith<$Res> | |||||||
|   @override |   @override | ||||||
|   @useResult |   @useResult | ||||||
|   $Res call( |   $Res call( | ||||||
|       {@HiveField(0) int id, |       {int id, | ||||||
|       @HiveField(1) DateTime createdAt, |       DateTime createdAt, | ||||||
|       @HiveField(2) DateTime updatedAt, |       DateTime updatedAt, | ||||||
|       @HiveField(3) DateTime? deletedAt, |       DateTime? deletedAt, | ||||||
|       @HiveField(4) String uuid, |       String uuid, | ||||||
|       @HiveField(5) Map<String, dynamic> body, |       Map<String, dynamic> body, | ||||||
|       @HiveField(6) String type, |       String type, | ||||||
|       @HiveField(7) SnChannel channel, |       SnChannel channel, | ||||||
|       @HiveField(8) SnChannelMember sender, |       SnChannelMember sender, | ||||||
|       @HiveField(9) int channelId, |       int channelId, | ||||||
|       @HiveField(10) int senderId, |       int senderId, | ||||||
|       @HiveField(11) int? quoteEventId, |       int? quoteEventId, | ||||||
|       @HiveField(12) int? relatedEventId, |       int? relatedEventId, | ||||||
|       SnChatMessagePreload? preload}); |       SnChatMessagePreload? preload}); | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
| @@ -1356,19 +1356,19 @@ class __$$SnChatMessageImplCopyWithImpl<$Res> | |||||||
| @HiveType(typeId: 4) | @HiveType(typeId: 4) | ||||||
| class _$SnChatMessageImpl extends _SnChatMessage { | class _$SnChatMessageImpl extends _SnChatMessage { | ||||||
|   const _$SnChatMessageImpl( |   const _$SnChatMessageImpl( | ||||||
|       {@HiveField(0) required this.id, |       {required this.id, | ||||||
|       @HiveField(1) required this.createdAt, |       required this.createdAt, | ||||||
|       @HiveField(2) required this.updatedAt, |       required this.updatedAt, | ||||||
|       @HiveField(3) required this.deletedAt, |       required this.deletedAt, | ||||||
|       @HiveField(4) required this.uuid, |       required this.uuid, | ||||||
|       @HiveField(5) final Map<String, dynamic> body = const {}, |       final Map<String, dynamic> body = const {}, | ||||||
|       @HiveField(6) required this.type, |       required this.type, | ||||||
|       @HiveField(7) required this.channel, |       required this.channel, | ||||||
|       @HiveField(8) required this.sender, |       required this.sender, | ||||||
|       @HiveField(9) required this.channelId, |       required this.channelId, | ||||||
|       @HiveField(10) required this.senderId, |       required this.senderId, | ||||||
|       @HiveField(11) required this.quoteEventId, |       required this.quoteEventId, | ||||||
|       @HiveField(12) required this.relatedEventId, |       required this.relatedEventId, | ||||||
|       this.preload}) |       this.preload}) | ||||||
|       : _body = body, |       : _body = body, | ||||||
|         super._(); |         super._(); | ||||||
| @@ -1495,19 +1495,19 @@ class _$SnChatMessageImpl extends _SnChatMessage { | |||||||
|  |  | ||||||
| abstract class _SnChatMessage extends SnChatMessage { | abstract class _SnChatMessage extends SnChatMessage { | ||||||
|   const factory _SnChatMessage( |   const factory _SnChatMessage( | ||||||
|       {@HiveField(0) required final int id, |       {required final int id, | ||||||
|       @HiveField(1) required final DateTime createdAt, |       required final DateTime createdAt, | ||||||
|       @HiveField(2) required final DateTime updatedAt, |       required final DateTime updatedAt, | ||||||
|       @HiveField(3) required final DateTime? deletedAt, |       required final DateTime? deletedAt, | ||||||
|       @HiveField(4) required final String uuid, |       required final String uuid, | ||||||
|       @HiveField(5) final Map<String, dynamic> body, |       final Map<String, dynamic> body, | ||||||
|       @HiveField(6) required final String type, |       required final String type, | ||||||
|       @HiveField(7) required final SnChannel channel, |       required final SnChannel channel, | ||||||
|       @HiveField(8) required final SnChannelMember sender, |       required final SnChannelMember sender, | ||||||
|       @HiveField(9) required final int channelId, |       required final int channelId, | ||||||
|       @HiveField(10) required final int senderId, |       required final int senderId, | ||||||
|       @HiveField(11) required final int? quoteEventId, |       required final int? quoteEventId, | ||||||
|       @HiveField(12) required final int? relatedEventId, |       required final int? relatedEventId, | ||||||
|       final SnChatMessagePreload? preload}) = _$SnChatMessageImpl; |       final SnChatMessagePreload? preload}) = _$SnChatMessageImpl; | ||||||
|   const _SnChatMessage._() : super._(); |   const _SnChatMessage._() : super._(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -29,20 +29,20 @@ class SnRealm with _$SnRealm { | |||||||
|  |  | ||||||
|   @HiveType(typeId: 1) |   @HiveType(typeId: 1) | ||||||
|   const factory SnRealm({ |   const factory SnRealm({ | ||||||
|     @HiveField(0) required int id, |     required int id, | ||||||
|     @HiveField(1) required DateTime createdAt, |     required DateTime createdAt, | ||||||
|     @HiveField(2) required DateTime updatedAt, |     required DateTime updatedAt, | ||||||
|     @HiveField(3) required DateTime? deletedAt, |     required DateTime? deletedAt, | ||||||
|     @HiveField(4) required String alias, |     required String alias, | ||||||
|     @HiveField(5) required String name, |     required String name, | ||||||
|     @HiveField(6) required String description, |     required String description, | ||||||
|     List<SnRealmMember>? members, |     List<SnRealmMember>? members, | ||||||
|     @HiveField(7) required String? avatar, |     required String? avatar, | ||||||
|     @HiveField(8) required String? banner, |     required String? banner, | ||||||
|     @HiveField(9) required Map<String, dynamic>? accessPolicy, |     required Map<String, dynamic>? accessPolicy, | ||||||
|     @HiveField(10) required int accountId, |     required int accountId, | ||||||
|     @HiveField(11) required bool isPublic, |     required bool isPublic, | ||||||
|     @HiveField(12) required bool isCommunity, |     required bool isCommunity, | ||||||
|     @Default(0) int popularity, |     @Default(0) int popularity, | ||||||
|   }) = _SnRealm; |   }) = _SnRealm; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -411,20 +411,20 @@ abstract class $SnRealmCopyWith<$Res> { | |||||||
|       _$SnRealmCopyWithImpl<$Res, SnRealm>; |       _$SnRealmCopyWithImpl<$Res, SnRealm>; | ||||||
|   @useResult |   @useResult | ||||||
|   $Res call( |   $Res call( | ||||||
|       {@HiveField(0) int id, |       {int id, | ||||||
|       @HiveField(1) DateTime createdAt, |       DateTime createdAt, | ||||||
|       @HiveField(2) DateTime updatedAt, |       DateTime updatedAt, | ||||||
|       @HiveField(3) DateTime? deletedAt, |       DateTime? deletedAt, | ||||||
|       @HiveField(4) String alias, |       String alias, | ||||||
|       @HiveField(5) String name, |       String name, | ||||||
|       @HiveField(6) String description, |       String description, | ||||||
|       List<SnRealmMember>? members, |       List<SnRealmMember>? members, | ||||||
|       @HiveField(7) String? avatar, |       String? avatar, | ||||||
|       @HiveField(8) String? banner, |       String? banner, | ||||||
|       @HiveField(9) Map<String, dynamic>? accessPolicy, |       Map<String, dynamic>? accessPolicy, | ||||||
|       @HiveField(10) int accountId, |       int accountId, | ||||||
|       @HiveField(11) bool isPublic, |       bool isPublic, | ||||||
|       @HiveField(12) bool isCommunity, |       bool isCommunity, | ||||||
|       int popularity}); |       int popularity}); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -532,20 +532,20 @@ abstract class _$$SnRealmImplCopyWith<$Res> implements $SnRealmCopyWith<$Res> { | |||||||
|   @override |   @override | ||||||
|   @useResult |   @useResult | ||||||
|   $Res call( |   $Res call( | ||||||
|       {@HiveField(0) int id, |       {int id, | ||||||
|       @HiveField(1) DateTime createdAt, |       DateTime createdAt, | ||||||
|       @HiveField(2) DateTime updatedAt, |       DateTime updatedAt, | ||||||
|       @HiveField(3) DateTime? deletedAt, |       DateTime? deletedAt, | ||||||
|       @HiveField(4) String alias, |       String alias, | ||||||
|       @HiveField(5) String name, |       String name, | ||||||
|       @HiveField(6) String description, |       String description, | ||||||
|       List<SnRealmMember>? members, |       List<SnRealmMember>? members, | ||||||
|       @HiveField(7) String? avatar, |       String? avatar, | ||||||
|       @HiveField(8) String? banner, |       String? banner, | ||||||
|       @HiveField(9) Map<String, dynamic>? accessPolicy, |       Map<String, dynamic>? accessPolicy, | ||||||
|       @HiveField(10) int accountId, |       int accountId, | ||||||
|       @HiveField(11) bool isPublic, |       bool isPublic, | ||||||
|       @HiveField(12) bool isCommunity, |       bool isCommunity, | ||||||
|       int popularity}); |       int popularity}); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -648,20 +648,20 @@ class __$$SnRealmImplCopyWithImpl<$Res> | |||||||
| @HiveType(typeId: 1) | @HiveType(typeId: 1) | ||||||
| class _$SnRealmImpl extends _SnRealm { | class _$SnRealmImpl extends _SnRealm { | ||||||
|   const _$SnRealmImpl( |   const _$SnRealmImpl( | ||||||
|       {@HiveField(0) required this.id, |       {required this.id, | ||||||
|       @HiveField(1) required this.createdAt, |       required this.createdAt, | ||||||
|       @HiveField(2) required this.updatedAt, |       required this.updatedAt, | ||||||
|       @HiveField(3) required this.deletedAt, |       required this.deletedAt, | ||||||
|       @HiveField(4) required this.alias, |       required this.alias, | ||||||
|       @HiveField(5) required this.name, |       required this.name, | ||||||
|       @HiveField(6) required this.description, |       required this.description, | ||||||
|       final List<SnRealmMember>? members, |       final List<SnRealmMember>? members, | ||||||
|       @HiveField(7) required this.avatar, |       required this.avatar, | ||||||
|       @HiveField(8) required this.banner, |       required this.banner, | ||||||
|       @HiveField(9) required final Map<String, dynamic>? accessPolicy, |       required final Map<String, dynamic>? accessPolicy, | ||||||
|       @HiveField(10) required this.accountId, |       required this.accountId, | ||||||
|       @HiveField(11) required this.isPublic, |       required this.isPublic, | ||||||
|       @HiveField(12) required this.isCommunity, |       required this.isCommunity, | ||||||
|       this.popularity = 0}) |       this.popularity = 0}) | ||||||
|       : _members = members, |       : _members = members, | ||||||
|         _accessPolicy = accessPolicy, |         _accessPolicy = accessPolicy, | ||||||
| @@ -805,20 +805,20 @@ class _$SnRealmImpl extends _SnRealm { | |||||||
|  |  | ||||||
| abstract class _SnRealm extends SnRealm { | abstract class _SnRealm extends SnRealm { | ||||||
|   const factory _SnRealm( |   const factory _SnRealm( | ||||||
|       {@HiveField(0) required final int id, |       {required final int id, | ||||||
|       @HiveField(1) required final DateTime createdAt, |       required final DateTime createdAt, | ||||||
|       @HiveField(2) required final DateTime updatedAt, |       required final DateTime updatedAt, | ||||||
|       @HiveField(3) required final DateTime? deletedAt, |       required final DateTime? deletedAt, | ||||||
|       @HiveField(4) required final String alias, |       required final String alias, | ||||||
|       @HiveField(5) required final String name, |       required final String name, | ||||||
|       @HiveField(6) required final String description, |       required final String description, | ||||||
|       final List<SnRealmMember>? members, |       final List<SnRealmMember>? members, | ||||||
|       @HiveField(7) required final String? avatar, |       required final String? avatar, | ||||||
|       @HiveField(8) required final String? banner, |       required final String? banner, | ||||||
|       @HiveField(9) required final Map<String, dynamic>? accessPolicy, |       required final Map<String, dynamic>? accessPolicy, | ||||||
|       @HiveField(10) required final int accountId, |       required final int accountId, | ||||||
|       @HiveField(11) required final bool isPublic, |       required final bool isPublic, | ||||||
|       @HiveField(12) required final bool isCommunity, |       required final bool isCommunity, | ||||||
|       final int popularity}) = _$SnRealmImpl; |       final int popularity}) = _$SnRealmImpl; | ||||||
|   const _SnRealm._() : super._(); |   const _SnRealm._() : super._(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ | |||||||
| #include <media_kit_libs_linux/media_kit_libs_linux_plugin.h> | #include <media_kit_libs_linux/media_kit_libs_linux_plugin.h> | ||||||
| #include <media_kit_video/media_kit_video_plugin.h> | #include <media_kit_video/media_kit_video_plugin.h> | ||||||
| #include <pasteboard/pasteboard_plugin.h> | #include <pasteboard/pasteboard_plugin.h> | ||||||
|  | #include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h> | ||||||
| #include <tray_manager/tray_manager_plugin.h> | #include <tray_manager/tray_manager_plugin.h> | ||||||
| #include <url_launcher_linux/url_launcher_plugin.h> | #include <url_launcher_linux/url_launcher_plugin.h> | ||||||
|  |  | ||||||
| @@ -46,6 +47,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { | |||||||
|   g_autoptr(FlPluginRegistrar) pasteboard_registrar = |   g_autoptr(FlPluginRegistrar) pasteboard_registrar = | ||||||
|       fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin"); |       fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin"); | ||||||
|   pasteboard_plugin_register_with_registrar(pasteboard_registrar); |   pasteboard_plugin_register_with_registrar(pasteboard_registrar); | ||||||
|  |   g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar = | ||||||
|  |       fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin"); | ||||||
|  |   sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar); | ||||||
|   g_autoptr(FlPluginRegistrar) tray_manager_registrar = |   g_autoptr(FlPluginRegistrar) tray_manager_registrar = | ||||||
|       fl_plugin_registry_get_registrar_for_plugin(registry, "TrayManagerPlugin"); |       fl_plugin_registry_get_registrar_for_plugin(registry, "TrayManagerPlugin"); | ||||||
|   tray_manager_plugin_register_with_registrar(tray_manager_registrar); |   tray_manager_plugin_register_with_registrar(tray_manager_registrar); | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ list(APPEND FLUTTER_PLUGIN_LIST | |||||||
|   media_kit_libs_linux |   media_kit_libs_linux | ||||||
|   media_kit_video |   media_kit_video | ||||||
|   pasteboard |   pasteboard | ||||||
|  |   sqlite3_flutter_libs | ||||||
|   tray_manager |   tray_manager | ||||||
|   url_launcher_linux |   url_launcher_linux | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -30,6 +30,7 @@ import screen_brightness_macos | |||||||
| import share_plus | import share_plus | ||||||
| import shared_preferences_foundation | import shared_preferences_foundation | ||||||
| import sqflite_darwin | import sqflite_darwin | ||||||
|  | import sqlite3_flutter_libs | ||||||
| import tray_manager | import tray_manager | ||||||
| import url_launcher_macos | import url_launcher_macos | ||||||
| import video_compress | import video_compress | ||||||
| @@ -61,6 +62,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { | |||||||
|   SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) |   SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) | ||||||
|   SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) |   SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) | ||||||
|   SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) |   SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) | ||||||
|  |   Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) | ||||||
|   TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin")) |   TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin")) | ||||||
|   UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) |   UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) | ||||||
|   VideoCompressPlugin.register(with: registry.registrar(forPlugin: "VideoCompressPlugin")) |   VideoCompressPlugin.register(with: registry.registrar(forPlugin: "VideoCompressPlugin")) | ||||||
|   | |||||||
							
								
								
									
										88
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								pubspec.lock
									
									
									
									
									
								
							| @@ -230,6 +230,14 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "1.3.0" |     version: "1.3.0" | ||||||
|  |   charcode: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: charcode | ||||||
|  |       sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "1.4.0" | ||||||
|   checked_yaml: |   checked_yaml: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
| @@ -414,6 +422,30 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "1.0.2" |     version: "1.0.2" | ||||||
|  |   drift: | ||||||
|  |     dependency: "direct main" | ||||||
|  |     description: | ||||||
|  |       name: drift | ||||||
|  |       sha256: "97d5832657d49f26e7a8e07de397ddc63790b039372878d5117af816d0fdb5cb" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "2.25.1" | ||||||
|  |   drift_dev: | ||||||
|  |     dependency: "direct dev" | ||||||
|  |     description: | ||||||
|  |       name: drift_dev | ||||||
|  |       sha256: f1db88482dbb016b9bbddddf746d5d0a6938b156ff20e07320052981f97388cc | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "2.25.2" | ||||||
|  |   drift_flutter: | ||||||
|  |     dependency: "direct main" | ||||||
|  |     description: | ||||||
|  |       name: drift_flutter | ||||||
|  |       sha256: "0cadbf3b8733409a6cf61d18ba2e94e149df81df7de26f48ae0695b48fd71922" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "0.2.4" | ||||||
|   dropdown_button2: |   dropdown_button2: | ||||||
|     dependency: "direct main" |     dependency: "direct main" | ||||||
|     description: |     description: | ||||||
| @@ -914,30 +946,6 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "2.3.2" |     version: "2.3.2" | ||||||
|   hive: |  | ||||||
|     dependency: "direct main" |  | ||||||
|     description: |  | ||||||
|       name: hive |  | ||||||
|       sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" |  | ||||||
|       url: "https://pub.dev" |  | ||||||
|     source: hosted |  | ||||||
|     version: "2.2.3" |  | ||||||
|   hive_flutter: |  | ||||||
|     dependency: "direct main" |  | ||||||
|     description: |  | ||||||
|       name: hive_flutter |  | ||||||
|       sha256: dca1da446b1d808a51689fb5d0c6c9510c0a2ba01e22805d492c73b68e33eecc |  | ||||||
|       url: "https://pub.dev" |  | ||||||
|     source: hosted |  | ||||||
|     version: "1.1.0" |  | ||||||
|   hive_generator: |  | ||||||
|     dependency: "direct dev" |  | ||||||
|     description: |  | ||||||
|       name: hive_generator |  | ||||||
|       sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4" |  | ||||||
|       url: "https://pub.dev" |  | ||||||
|     source: hosted |  | ||||||
|     version: "2.0.1" |  | ||||||
|   home_widget: |   home_widget: | ||||||
|     dependency: "direct main" |     dependency: "direct main" | ||||||
|     description: |     description: | ||||||
| @@ -1642,6 +1650,14 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "4.1.0" |     version: "4.1.0" | ||||||
|  |   recase: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: recase | ||||||
|  |       sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "4.1.0" | ||||||
|   receive_sharing_intent: |   receive_sharing_intent: | ||||||
|     dependency: "direct main" |     dependency: "direct main" | ||||||
|     description: |     description: | ||||||
| @@ -1935,6 +1951,30 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "2.4.0" |     version: "2.4.0" | ||||||
|  |   sqlite3: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: sqlite3 | ||||||
|  |       sha256: "32b632dda27d664f85520093ed6f735ae5c49b5b75345afb8b19411bc59bb53d" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "2.7.4" | ||||||
|  |   sqlite3_flutter_libs: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: sqlite3_flutter_libs | ||||||
|  |       sha256: "57fafacd815c981735406215966ff7caaa8eab984b094f52e692accefcbd9233" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "0.5.30" | ||||||
|  |   sqlparser: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: sqlparser | ||||||
|  |       sha256: "27dd0a9f0c02e22ac0eb42a23df9ea079ce69b52bb4a3b478d64e0ef34a263ee" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "0.41.0" | ||||||
|   stack_trace: |   stack_trace: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
|   | |||||||
| @@ -72,8 +72,6 @@ dependencies: | |||||||
|   collection: ^1.18.0 |   collection: ^1.18.0 | ||||||
|   mime: ^2.0.0 |   mime: ^2.0.0 | ||||||
|   web_socket_channel: ^3.0.1 |   web_socket_channel: ^3.0.1 | ||||||
|   hive: ^2.2.3 |  | ||||||
|   hive_flutter: ^1.1.0 |  | ||||||
|   swipe_to: ^1.0.6 |   swipe_to: ^1.0.6 | ||||||
|   firebase_core: ^3.8.0 |   firebase_core: ^3.8.0 | ||||||
|   firebase_messaging: ^15.1.5 |   firebase_messaging: ^15.1.5 | ||||||
| @@ -123,6 +121,8 @@ dependencies: | |||||||
|   image_picker_android: ^0.8.12+20 |   image_picker_android: ^0.8.12+20 | ||||||
|   cached_network_image_platform_interface: ^4.1.1 |   cached_network_image_platform_interface: ^4.1.1 | ||||||
|   image_picker_platform_interface: ^2.10.1 |   image_picker_platform_interface: ^2.10.1 | ||||||
|  |   drift: ^2.25.1 | ||||||
|  |   drift_flutter: ^0.2.4 | ||||||
|  |  | ||||||
| dev_dependencies: | dev_dependencies: | ||||||
|   flutter_test: |   flutter_test: | ||||||
| @@ -134,13 +134,13 @@ dev_dependencies: | |||||||
|   # package. See that file for information about deactivating specific lint |   # package. See that file for information about deactivating specific lint | ||||||
|   # rules and activating additional ones. |   # rules and activating additional ones. | ||||||
|   flutter_lints: ^5.0.0 |   flutter_lints: ^5.0.0 | ||||||
|   build_runner: ^2.4.13 |   build_runner: ^2.4.15 | ||||||
|   freezed: ^2.5.7 |   freezed: ^2.5.7 | ||||||
|   json_serializable: ^6.8.0 |   json_serializable: ^6.8.0 | ||||||
|   icons_launcher: ^3.0.0 |   icons_launcher: ^3.0.0 | ||||||
|   flutter_native_splash: ^2.4.2 |   flutter_native_splash: ^2.4.2 | ||||||
|   hive_generator: ^2.0.1 |  | ||||||
|   flutter_launcher_icons: ^0.14.1 |   flutter_launcher_icons: ^0.14.1 | ||||||
|  |   drift_dev: ^2.25.2 | ||||||
|  |  | ||||||
| # For information on the generic Dart part of this file, see the | # For information on the generic Dart part of this file, see the | ||||||
| # following page: https://dart.dev/tools/pub/pubspec | # following page: https://dart.dev/tools/pub/pubspec | ||||||
|   | |||||||
							
								
								
									
										13631
									
								
								web/drift_worker.dart.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13631
									
								
								web/drift_worker.dart.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -1,11 +1,11 @@ | |||||||
| { | { | ||||||
|     "name": "surface", |     "name": "Solar Network", | ||||||
|     "short_name": "surface", |     "short_name": "Solian", | ||||||
|     "start_url": ".", |     "start_url": ".", | ||||||
|     "display": "standalone", |     "display": "standalone", | ||||||
|     "background_color": "#ffffff", |     "background_color": "#ffffff", | ||||||
|     "theme_color": "#ffffff", |     "theme_color": "#ffffff", | ||||||
|     "description": "A new Flutter project.", |     "description": "The Solar Network is a social network app.", | ||||||
|     "orientation": "portrait-primary", |     "orientation": "portrait-primary", | ||||||
|     "prefer_related_applications": false, |     "prefer_related_applications": false, | ||||||
|     "icons": [ |     "icons": [ | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								web/sqlite3.wasm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								web/sqlite3.wasm
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -23,6 +23,7 @@ | |||||||
| #include <permission_handler_windows/permission_handler_windows_plugin.h> | #include <permission_handler_windows/permission_handler_windows_plugin.h> | ||||||
| #include <screen_brightness_windows/screen_brightness_windows_plugin.h> | #include <screen_brightness_windows/screen_brightness_windows_plugin.h> | ||||||
| #include <share_plus/share_plus_windows_plugin_c_api.h> | #include <share_plus/share_plus_windows_plugin_c_api.h> | ||||||
|  | #include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h> | ||||||
| #include <tray_manager/tray_manager_plugin.h> | #include <tray_manager/tray_manager_plugin.h> | ||||||
| #include <url_launcher_windows/url_launcher_windows.h> | #include <url_launcher_windows/url_launcher_windows.h> | ||||||
|  |  | ||||||
| @@ -61,6 +62,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { | |||||||
|       registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin")); |       registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin")); | ||||||
|   SharePlusWindowsPluginCApiRegisterWithRegistrar( |   SharePlusWindowsPluginCApiRegisterWithRegistrar( | ||||||
|       registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); |       registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); | ||||||
|  |   Sqlite3FlutterLibsPluginRegisterWithRegistrar( | ||||||
|  |       registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin")); | ||||||
|   TrayManagerPluginRegisterWithRegistrar( |   TrayManagerPluginRegisterWithRegistrar( | ||||||
|       registry->GetRegistrarForPlugin("TrayManagerPlugin")); |       registry->GetRegistrarForPlugin("TrayManagerPlugin")); | ||||||
|   UrlLauncherWindowsRegisterWithRegistrar( |   UrlLauncherWindowsRegisterWithRegistrar( | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ list(APPEND FLUTTER_PLUGIN_LIST | |||||||
|   permission_handler_windows |   permission_handler_windows | ||||||
|   screen_brightness_windows |   screen_brightness_windows | ||||||
|   share_plus |   share_plus | ||||||
|  |   sqlite3_flutter_libs | ||||||
|   tray_manager |   tray_manager | ||||||
|   url_launcher_windows |   url_launcher_windows | ||||||
| ) | ) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user