♻️ Use sqlite to replace hive #5
| @@ -683,5 +683,9 @@ | ||||
|   "postChannelRealm": "Realms", | ||||
|   "postFilterReset": "Reset Filter", | ||||
|   "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": "领域", | ||||
|   "postFilterReset": "重置过滤器", | ||||
|   "postFilterResetDescription": "清除过滤器并显示所有帖子。", | ||||
|   "postFilterWithCategory": "查看{}区中的帖子" | ||||
|   "postFilterWithCategory": "查看{}区中的帖子", | ||||
|   "databaseSize": "数据库大小", | ||||
|   "databaseDelete": "删除数据库", | ||||
|   "databaseDeleteDescription": "删除本地数据库,内容将从服务器重新获取。", | ||||
|   "databaseDeleted": "本地数据库已被删除。" | ||||
| } | ||||
|   | ||||
| @@ -681,5 +681,9 @@ | ||||
|   "postChannelRealm": "領域", | ||||
|   "postFilterReset": "重置過濾器", | ||||
|   "postFilterResetDescription": "清除過濾器並顯示所有帖子。", | ||||
|   "postFilterWithCategory": "查看{}區中的帖子" | ||||
|   "postFilterWithCategory": "查看{}區中的帖子", | ||||
|   "databaseSize": "數據庫大小", | ||||
|   "databaseDelete": "刪除數據庫", | ||||
|   "databaseDeleteDescription": "刪除本地數據庫,內容將從服務器重新獲取。", | ||||
|   "databaseDeleted": "本地數據庫已被刪除。" | ||||
| } | ||||
|   | ||||
| @@ -681,5 +681,9 @@ | ||||
|   "postChannelRealm": "領域", | ||||
|   "postFilterReset": "重置過濾器", | ||||
|   "postFilterResetDescription": "清除過濾器並顯示所有帖子。", | ||||
|   "postFilterWithCategory": "查看{}區中的帖子" | ||||
|   "postFilterWithCategory": "查看{}區中的帖子", | ||||
|   "databaseSize": "數據庫大小", | ||||
|   "databaseDelete": "刪除數據庫", | ||||
|   "databaseDeleteDescription": "刪除本地數據庫,內容將從服務器重新獲取。", | ||||
|   "databaseDeleted": "本地數據庫已被刪除。" | ||||
| } | ||||
|   | ||||
| @@ -221,6 +221,25 @@ PODS: | ||||
|   - sqflite_darwin (0.0.4): | ||||
|     - Flutter | ||||
|     - 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) | ||||
|   - url_launcher_ios (0.0.1): | ||||
|     - Flutter | ||||
| @@ -268,6 +287,7 @@ DEPENDENCIES: | ||||
|   - share_plus (from `.symlinks/plugins/share_plus/ios`) | ||||
|   - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/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`) | ||||
|   - video_compress (from `.symlinks/plugins/video_compress/ios`) | ||||
|   - volume_controller (from `.symlinks/plugins/volume_controller/ios`) | ||||
| @@ -294,6 +314,7 @@ SPEC REPOS: | ||||
|     - PromisesObjC | ||||
|     - SAMKeychain | ||||
|     - SDWebImage | ||||
|     - sqlite3 | ||||
|     - SwiftyGif | ||||
|     - WebRTC-SDK | ||||
|  | ||||
| @@ -360,6 +381,8 @@ EXTERNAL SOURCES: | ||||
|     :path: ".symlinks/plugins/shared_preferences_foundation/darwin" | ||||
|   sqflite_darwin: | ||||
|     :path: ".symlinks/plugins/sqflite_darwin/darwin" | ||||
|   sqlite3_flutter_libs: | ||||
|     :path: ".symlinks/plugins/sqlite3_flutter_libs/darwin" | ||||
|   url_launcher_ios: | ||||
|     :path: ".symlinks/plugins/url_launcher_ios/ios" | ||||
|   video_compress: | ||||
| @@ -421,6 +444,8 @@ SPEC CHECKSUMS: | ||||
|   share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f | ||||
|   shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 | ||||
|   sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d | ||||
|   sqlite3: 4922312598b67e1825c6a6c821296dcbf6783046 | ||||
|   sqlite3_flutter_libs: 069c435986dd4b63461aecd68f4b30be4a9e9daa | ||||
|   SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 | ||||
|   url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe | ||||
|   video_compress: fce97e4fb1dfd88175aa07d2ffc8a2f297f87fbe | ||||
|   | ||||
| @@ -2,11 +2,12 @@ import 'dart:async'; | ||||
| import 'dart:convert'; | ||||
| import 'dart:math' as math; | ||||
|  | ||||
| import 'package:collection/collection.dart'; | ||||
| import 'package:dio/dio.dart'; | ||||
| import 'package:drift/drift.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hive/hive.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_network.dart'; | ||||
| import 'package:surface/providers/user_directory.dart'; | ||||
| @@ -16,13 +17,13 @@ import 'package:surface/types/websocket.dart'; | ||||
| import 'package:uuid/uuid.dart'; | ||||
|  | ||||
| class ChatMessageController extends ChangeNotifier { | ||||
|   static const kChatMessageBoxPrefix = 'nex_chat_messages_'; | ||||
|   static const kSingleBatchLoadLimit = 100; | ||||
|  | ||||
|   late final SnNetworkProvider _sn; | ||||
|   late final UserDirectoryProvider _ud; | ||||
|   late final WebSocketProvider _ws; | ||||
|   late final SnAttachmentProvider _attach; | ||||
|   late final DatabaseProvider _dt; | ||||
|  | ||||
|   StreamSubscription? _wsSubscription; | ||||
|  | ||||
| @@ -31,6 +32,7 @@ class ChatMessageController extends ChangeNotifier { | ||||
|     _ud = context.read<UserDirectoryProvider>(); | ||||
|     _ws = context.read<WebSocketProvider>(); | ||||
|     _attach = context.read<SnAttachmentProvider>(); | ||||
|     _dt = context.read<DatabaseProvider>(); | ||||
|   } | ||||
|  | ||||
|   bool isPending = true; | ||||
| @@ -38,9 +40,9 @@ class ChatMessageController extends ChangeNotifier { | ||||
|  | ||||
|   int? messageTotal; | ||||
|  | ||||
|   bool get isAllLoaded => messageTotal != null && messages.length >= messageTotal!; | ||||
|   bool get isAllLoaded => | ||||
|       messageTotal != null && messages.length >= messageTotal!; | ||||
|  | ||||
|   String? _boxKey; | ||||
|   SnChannel? channel; | ||||
|   SnChannelMember? profile; | ||||
|  | ||||
| @@ -51,25 +53,17 @@ class ChatMessageController extends ChangeNotifier { | ||||
|   /// Stored as a list of nonce to provide the loading state | ||||
|   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 Map<int, Timer> typingInactiveTimer = {}; | ||||
|  | ||||
|   Future<void> initialize(SnChannel chan) async { | ||||
|     channel = chan; | ||||
|  | ||||
|     // Initialize local data | ||||
|     _boxKey = '$kChatMessageBoxPrefix${chan.id}'; | ||||
|     await Hive.openBox<SnChatMessage>(_boxKey!); | ||||
|  | ||||
|     // Fetch channel profile | ||||
|     final resp = await _sn.client.get( | ||||
|       '/cgi/im/channels/${chan.keyPath}/me', | ||||
|     ); | ||||
|     profile = SnChannelMember.fromJson( | ||||
|       resp.data as Map<String, dynamic>, | ||||
|     ); | ||||
|     profile = SnChannelMember.fromJson(resp.data); | ||||
|  | ||||
|     _wsSubscription = _ws.pk.stream.listen((event) { | ||||
|       switch (event.method) { | ||||
| @@ -87,7 +81,8 @@ class ChatMessageController extends ChangeNotifier { | ||||
|             notifyListeners(); | ||||
|           } | ||||
|           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); | ||||
|             typingInactiveTimer.remove(member.id); | ||||
|             notifyListeners(); | ||||
| @@ -129,10 +124,16 @@ class ChatMessageController extends ChangeNotifier { | ||||
|   } | ||||
|  | ||||
|   Future<void> _saveMessageToLocal(Iterable<SnChatMessage> messages) async { | ||||
|     if (_box == null) return; | ||||
|     await _box!.putAll({ | ||||
|       for (final message in messages) message.id: message, | ||||
|     }); | ||||
|     await _dt.db.snLocalChatMessage.insertAll( | ||||
|         messages.map( | ||||
|           (ele) => SnLocalChatMessageCompanion.insert( | ||||
|             id: Value(ele.id), | ||||
|             content: ele, | ||||
|             channelId: channel!.id, | ||||
|             createdAt: Value(ele.createdAt), | ||||
|           ), | ||||
|         ), | ||||
|         onConflict: DoNothing()); | ||||
|   } | ||||
|  | ||||
|   Future<void> _addUnconfirmedMessage(SnChatMessage message) async { | ||||
| @@ -184,8 +185,21 @@ class ChatMessageController extends ChangeNotifier { | ||||
|     await _applyMessage(message); | ||||
|     notifyListeners(); | ||||
|  | ||||
|     if (_box == null) return; | ||||
|     await _box!.put(message.id, message); | ||||
|     if (isCheckedUpdate) { | ||||
|       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 { | ||||
| @@ -194,7 +208,8 @@ class ChatMessageController extends ChangeNotifier { | ||||
|     switch (message.type) { | ||||
|       case 'messages.edit': | ||||
|         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) { | ||||
|             final newBody = message.body; | ||||
|             newBody.remove('related_event'); | ||||
| @@ -202,16 +217,24 @@ class ChatMessageController extends ChangeNotifier { | ||||
|               body: newBody, | ||||
|               updatedAt: message.updatedAt, | ||||
|             ); | ||||
|             if (_box!.containsKey(message.relatedEventId)) { | ||||
|               await _box!.put(message.relatedEventId, messages[idx]); | ||||
|             if (message.relatedEventId != null) { | ||||
|               await (_dt.db.snLocalChatMessage.update() | ||||
|                     ..where((e) => e.id.equals(message.relatedEventId!))) | ||||
|                   .write( | ||||
|                 SnLocalChatMessageCompanion.custom( | ||||
|                   content: Constant(jsonEncode(messages[idx].toJson())), | ||||
|                 ), | ||||
|               ); | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       case 'messages.delete': | ||||
|         if (message.relatedEventId != null) { | ||||
|           messages.removeWhere((x) => x.id == message.relatedEventId); | ||||
|           if (_box!.containsKey(message.relatedEventId)) { | ||||
|             await _box!.delete(message.relatedEventId); | ||||
|           if (message.relatedEventId != null) { | ||||
|             await (_dt.db.snLocalChatMessage.delete() | ||||
|                   ..where((e) => e.id.equals(message.relatedEventId!))) | ||||
|                 .go(); | ||||
|           } | ||||
|         } | ||||
|     } | ||||
| @@ -233,7 +256,8 @@ class ChatMessageController extends ChangeNotifier { | ||||
|       'algorithm': 'plain', | ||||
|       if (quoteId != null) 'quote_event': quoteId, | ||||
|       if (relatedId != null) 'related_event': relatedId, | ||||
|       if (attachments != null && attachments.isNotEmpty) 'attachments': attachments, | ||||
|       if (attachments != null && attachments.isNotEmpty) | ||||
|         'attachments': attachments, | ||||
|     }; | ||||
|  | ||||
|     // 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. | ||||
|   /// If the local storage is not up to date, it will be updated. | ||||
|   Future<void> checkUpdate() async { | ||||
|     if (_box == null) return; | ||||
|     if (_box!.isEmpty) return; | ||||
|  | ||||
|     isLoading = true; | ||||
|     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 { | ||||
|       final resp = await _sn.client.get( | ||||
|         '/cgi/im/channels/${channel!.keyPath}/events/update', | ||||
|         queryParameters: { | ||||
|           'pivot': _box!.values.last.id, | ||||
|           'pivot': mostRecentMessage.content.id, | ||||
|         }, | ||||
|       ); | ||||
|       if (resp.data['up_to_date'] == true) return; | ||||
| @@ -316,6 +354,12 @@ class ChatMessageController extends ChangeNotifier { | ||||
|     } finally { | ||||
|       await loadMessages(); | ||||
|       isLoading = false; | ||||
|  | ||||
|       isCheckedUpdate = true; | ||||
|       _saveMessageToLocal(incomeStrandedQueue).then((_) { | ||||
|         incomeStrandedQueue.clear(); | ||||
|       }); | ||||
|  | ||||
|       notifyListeners(); | ||||
|     } | ||||
|   } | ||||
| @@ -324,13 +368,18 @@ class ChatMessageController extends ChangeNotifier { | ||||
|   /// If it was not found in local storage we will look it up in remote | ||||
|   Future<SnChatMessage?> getMessage(int id) async { | ||||
|     SnChatMessage? out; | ||||
|     if (_box != null && _box!.containsKey(id)) { | ||||
|       out = _box!.get(id); | ||||
|     final local = await (_dt.db.snLocalChatMessage.select() | ||||
|           ..limit(1) | ||||
|           ..where((e) => e.id.equals(id))) | ||||
|         .getSingleOrNull(); | ||||
|     if (local != null) { | ||||
|       out = local.content; | ||||
|     } | ||||
|  | ||||
|     if (out == null) { | ||||
|       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); | ||||
|         _saveMessageToLocal([out]); | ||||
|       } catch (_) { | ||||
| @@ -364,16 +413,21 @@ class ChatMessageController extends ChangeNotifier { | ||||
|     bool forceLocal = false, | ||||
|     bool forceRemote = false, | ||||
|   }) async { | ||||
|     final localTotal = await _dt.db.snLocalChatMessage | ||||
|         .count(where: (e) => e.channelId.equals(channel!.id)) | ||||
|         .getSingle(); | ||||
|  | ||||
|     late List<SnChatMessage> out; | ||||
|     if (_box != null && (_box!.length >= take + offset || forceLocal) && !forceRemote) { | ||||
|       out = _box!.keys | ||||
|           .toList() | ||||
|           .cast<int>() | ||||
|           .sorted((a, b) => b.compareTo(a)) | ||||
|           .skip(offset) | ||||
|           .take(take) | ||||
|           .map((key) => _box!.get(key)!) | ||||
|           .toList(); | ||||
|     if ((localTotal >= take + offset || forceLocal) && !forceRemote) { | ||||
|       final result = await (_dt.db.snLocalChatMessage.select() | ||||
|             ..where((e) => e.channelId.equals(channel!.id)) | ||||
|             ..orderBy([ | ||||
|               (e) => | ||||
|                   OrderingTerm(expression: e.createdAt, mode: OrderingMode.desc) | ||||
|             ]) | ||||
|             ..limit(take, offset: offset)) | ||||
|           .get(); | ||||
|       out = result.map((e) => e.content).toList(); | ||||
|     } else { | ||||
|       final resp = await _sn.client.get( | ||||
|         '/cgi/im/channels/${channel!.keyPath}/events', | ||||
| @@ -408,7 +462,8 @@ class ChatMessageController extends ChangeNotifier { | ||||
|           quoteEvent: quoteEvent, | ||||
|           attachments: attachments | ||||
|               .where( | ||||
|                 (ele) => out[i].body['attachments']?.contains(ele?.rid) ?? false, | ||||
|                 (ele) => | ||||
|                     out[i].body['attachments']?.contains(ele?.rid) ?? false, | ||||
|               ) | ||||
|               .toList(), | ||||
|         ), | ||||
| @@ -416,7 +471,10 @@ class ChatMessageController extends ChangeNotifier { | ||||
|     } | ||||
|  | ||||
|     // 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); | ||||
|  | ||||
|     return out; | ||||
| @@ -443,7 +501,6 @@ class ChatMessageController extends ChangeNotifier { | ||||
|  | ||||
|   @override | ||||
|   void dispose() { | ||||
|     _box?.close(); | ||||
|     _wsSubscription?.cancel(); | ||||
|     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/services.dart'; | ||||
| import 'package:go_router/go_router.dart'; | ||||
| import 'package:hive_flutter/hive_flutter.dart'; | ||||
| import 'package:hotkey_manager/hotkey_manager.dart'; | ||||
| import 'package:package_info_plus/package_info_plus.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/chat_call.dart'; | ||||
| import 'package:surface/providers/config.dart'; | ||||
| import 'package:surface/providers/database.dart'; | ||||
| import 'package:surface/providers/link_preview.dart'; | ||||
| import 'package:surface/providers/navigation.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/widget.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:surface/widgets/dialog.dart'; | ||||
| import 'package:tray_manager/tray_manager.dart'; | ||||
| @@ -82,12 +80,6 @@ void main() async { | ||||
|  | ||||
|   await EasyLocalization.ensureInitialized(); | ||||
|  | ||||
|   await Hive.initFlutter(); | ||||
|   Hive.registerAdapter(SnChannelImplAdapter()); | ||||
|   Hive.registerAdapter(SnRealmImplAdapter()); | ||||
|   Hive.registerAdapter(SnChannelMemberImplAdapter()); | ||||
|   Hive.registerAdapter(SnChatMessageImplAdapter()); | ||||
|  | ||||
|   if (!kIsWeb && !Platform.isLinux) { | ||||
|     await Firebase.initializeApp( | ||||
|       options: DefaultFirebaseOptions.currentPlatform, | ||||
| @@ -114,7 +106,8 @@ void main() async { | ||||
|   } | ||||
|  | ||||
|   if (!kIsWeb && Platform.isAndroid) { | ||||
|     final ImagePickerPlatform imagePickerImplementation = ImagePickerPlatform.instance; | ||||
|     final ImagePickerPlatform imagePickerImplementation = | ||||
|         ImagePickerPlatform.instance; | ||||
|     if (imagePickerImplementation is ImagePickerAndroid) { | ||||
|       imagePickerImplementation.useAndroidPhotoPicker = true; | ||||
|     } | ||||
| @@ -142,6 +135,9 @@ class SolianApp extends StatelessWidget { | ||||
|         assetLoader: JsonAssetLoader(), | ||||
|         child: MultiProvider( | ||||
|           providers: [ | ||||
|             // Infrastructure layer | ||||
|             Provider(create: (ctx) => DatabaseProvider(ctx)), | ||||
|  | ||||
|             // System extensions layer | ||||
|             Provider(create: (ctx) => HomeWidgetProvider(ctx)), | ||||
|  | ||||
| @@ -230,7 +226,8 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener { | ||||
|     if (prefs.containsKey('first_boot_time')) { | ||||
|       final rawTime = prefs.getString('first_boot_time'); | ||||
|       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; | ||||
|         if (prefs.getBool('rating_requested') == true) return; | ||||
|         if (await inAppReview.isAvailable()) { | ||||
| @@ -258,13 +255,18 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener { | ||||
|       ).get( | ||||
|         '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 localVersion = Version.parse(localVersionString.split('+').first); | ||||
|       final remoteBuildNumber = int.tryParse(remoteVersionString.split('+').last) ?? 0; | ||||
|       final localBuildNumber = int.tryParse(localVersionString.split('+').last) ?? 0; | ||||
|       final remoteBuildNumber = | ||||
|           int.tryParse(remoteVersionString.split('+').last) ?? 0; | ||||
|       final localBuildNumber = | ||||
|           int.tryParse(localVersionString.split('+').last) ?? 0; | ||||
|       log("[Update] Local: $localVersionString, Remote: $remoteVersionString"); | ||||
|       if ((remoteVersion > localVersion || remoteBuildNumber > localBuildNumber) && mounted) { | ||||
|       if ((remoteVersion > localVersion || | ||||
|               remoteBuildNumber > localBuildNumber) && | ||||
|           mounted) { | ||||
|         final config = context.read<ConfigProvider>(); | ||||
|         config.setUpdate(remoteVersionString); | ||||
|         log("[Update] Update available: $remoteVersionString"); | ||||
| @@ -331,7 +333,9 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener { | ||||
|   Future<void> _trayInitialization() async { | ||||
|     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(); | ||||
|  | ||||
|     trayManager.addListener(this); | ||||
|   | ||||
| @@ -1,7 +1,10 @@ | ||||
| import 'dart:convert'; | ||||
|  | ||||
| import 'package:drift/drift.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hive_flutter/hive_flutter.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/user_directory.dart'; | ||||
| import 'package:surface/types/chat.dart'; | ||||
| @@ -12,24 +15,32 @@ class ChatChannelProvider extends ChangeNotifier { | ||||
|  | ||||
|   late final SnNetworkProvider _sn; | ||||
|   late final UserDirectoryProvider _ud; | ||||
|  | ||||
|   Box<SnChannel>? get _channelBox => Hive.box<SnChannel>(kChatChannelBoxName); | ||||
|   late final DatabaseProvider _dt; | ||||
|  | ||||
|   ChatChannelProvider(BuildContext context) { | ||||
|     _sn = context.read<SnNetworkProvider>(); | ||||
|     _ud = context.read<UserDirectoryProvider>(); | ||||
|     _initializeLocalData(); | ||||
|   } | ||||
|  | ||||
|   Future<void> _initializeLocalData() async { | ||||
|     await Hive.openBox<SnChannel>(kChatChannelBoxName); | ||||
|     _dt = context.read<DatabaseProvider>(); | ||||
|   } | ||||
|  | ||||
|   Future<void> _saveChannelToLocal(Iterable<SnChannel> channels) async { | ||||
|     if (_channelBox == null) return; | ||||
|     await _channelBox!.putAll({ | ||||
|       for (final channel in channels) channel.key: channel, | ||||
|     }); | ||||
|     await Future.wait( | ||||
|       channels.map( | ||||
|         (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({ | ||||
| @@ -54,12 +65,13 @@ class ChatChannelProvider extends ChangeNotifier { | ||||
|   /// It will use the local storage as much as possible. | ||||
|   /// The alias should include the scope, formatted as `scope:alias`. | ||||
|   Future<SnChannel> getChannel(String key) async { | ||||
|     if (_channelBox != null) { | ||||
|       final local = _channelBox!.get(key); | ||||
|       if (local != null) return local; | ||||
|     } | ||||
|     final local = await (_dt.db.snLocalChatChannel.select() | ||||
|           ..where((e) => e.alias.equals(key))) | ||||
|         .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); | ||||
|  | ||||
|     // 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. | ||||
|   /// 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. | ||||
|   Stream<List<SnChannel>> fetchChannels() async* { | ||||
|     if (_channelBox != null) yield _channelBox!.values.toList(); | ||||
|   Stream<List<SnChannel>> fetchChannels( | ||||
|       {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'); | ||||
|     final realms = List<SnRealm>.from( | ||||
| @@ -120,23 +143,23 @@ class ChatChannelProvider extends ChangeNotifier { | ||||
|   Future<List<SnChatMessage>> getLastMessages( | ||||
|     Iterable<SnChannel> channels, | ||||
|   ) async { | ||||
|     final result = List<SnChatMessage>.empty(growable: true); | ||||
|     final result = List<Future<SnLocalChatMessageData?>>.empty(growable: true); | ||||
|     for (final channel in channels) { | ||||
|       final channelBox = await Hive.openBox<SnChatMessage>( | ||||
|         '${ChatMessageController.kChatMessageBoxPrefix}${channel.id}', | ||||
|       ); | ||||
|       final lastMessage = | ||||
|           channelBox.isNotEmpty ? channelBox.values.reduce((a, b) => a.createdAt.isAfter(b.createdAt) ? a : b) : null; | ||||
|       if (lastMessage != null) result.add(lastMessage); | ||||
|       channelBox.close(); | ||||
|       final out = (_dt.db.snLocalChatMessage.select() | ||||
|             ..where((e) => e.channelId.equals(channel.id)) | ||||
|             ..orderBy([ | ||||
|               (e) => | ||||
|                   OrderingTerm(expression: e.createdAt, mode: OrderingMode.desc) | ||||
|             ]) | ||||
|             ..limit(1)) | ||||
|           .getSingleOrNull(); | ||||
|       result.add(out); | ||||
|     } | ||||
|     await _ud.listAccount(result.map((ele) => ele.sender.accountId).toSet()); | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void dispose() { | ||||
|     _channelBox?.close(); | ||||
|     super.dispose(); | ||||
|     final out = (await Future.wait(result)) | ||||
|         .where((e) => e != null) | ||||
|         .map((e) => e!.content) | ||||
|         .toList(); | ||||
|     await _ud.listAccount(out.map((ele) => ele.sender.accountId).toSet()); | ||||
|     return out; | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										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:gap/gap.dart'; | ||||
| import 'package:go_router/go_router.dart'; | ||||
| import 'package:hive_flutter/hive_flutter.dart'; | ||||
| import 'package:material_symbols_icons/symbols.dart'; | ||||
| import 'package:provider/provider.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/userinfo.dart'; | ||||
| import 'package:surface/providers/websocket.dart'; | ||||
| @@ -45,7 +45,8 @@ class AccountScreen extends StatelessWidget { | ||||
|             ? Stack( | ||||
|                 fit: StackFit.expand, | ||||
|                 children: [ | ||||
|                   AutoResizeUniversalImage(sn.getAttachmentUrl(ua.user!.banner), fit: BoxFit.cover), | ||||
|                   AutoResizeUniversalImage(sn.getAttachmentUrl(ua.user!.banner), | ||||
|                       fit: BoxFit.cover), | ||||
|                   Positioned( | ||||
|                     top: 0, | ||||
|                     left: 0, | ||||
| @@ -79,7 +80,9 @@ class AccountScreen extends StatelessWidget { | ||||
|         ], | ||||
|       ), | ||||
|       body: SingleChildScrollView( | ||||
|         child: ua.isAuthorized ? _AuthorizedAccountScreen() : _UnauthorizedAccountScreen(), | ||||
|         child: ua.isAuthorized | ||||
|             ? _AuthorizedAccountScreen() | ||||
|             : _UnauthorizedAccountScreen(), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| @@ -115,12 +118,15 @@ class _AuthorizedAccountScreen extends StatelessWidget { | ||||
|                     crossAxisAlignment: CrossAxisAlignment.baseline, | ||||
|                     textBaseline: TextBaseline.alphabetic, | ||||
|                     children: [ | ||||
|                       Text(ua.user!.nick).textStyle(Theme.of(context).textTheme.titleLarge!), | ||||
|                       Text(ua.user!.nick) | ||||
|                           .textStyle(Theme.of(context).textTheme.titleLarge!), | ||||
|                       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(); | ||||
|             final ws = context.read<WebSocketProvider>(); | ||||
|             ws.disconnect(); | ||||
|             await Hive.deleteFromDisk(); | ||||
|             await Hive.initFlutter(); | ||||
|             context.read<DatabaseProvider>().removeDatabase(); | ||||
|           }, | ||||
|         ), | ||||
|       ], | ||||
| @@ -220,7 +225,9 @@ class _UnauthorizedAccountScreen extends StatelessWidget { | ||||
|                   child: Icon(Symbols.waving_hand, size: 28), | ||||
|                 ), | ||||
|                 const Gap(8), | ||||
|                 Text('accountIntroTitle').tr().textStyle(Theme.of(context).textTheme.titleLarge!), | ||||
|                 Text('accountIntroTitle') | ||||
|                     .tr() | ||||
|                     .textStyle(Theme.of(context).textTheme.titleLarge!), | ||||
|                 Text('accountIntroSubtitle').tr(), | ||||
|               ], | ||||
|             ).padding(all: 20), | ||||
|   | ||||
| @@ -6,7 +6,9 @@ import 'package:go_router/go_router.dart'; | ||||
| import 'package:material_symbols_icons/symbols.dart'; | ||||
| import 'package:provider/provider.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/userinfo.dart'; | ||||
| import 'package:surface/types/chat.dart'; | ||||
| import 'package:surface/widgets/account/account_image.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:uuid/uuid.dart'; | ||||
|  | ||||
| import '../providers/sn_network.dart'; | ||||
| import '../providers/userinfo.dart'; | ||||
|  | ||||
| class ChatScreen extends StatefulWidget { | ||||
|   const ChatScreen({super.key}); | ||||
|  | ||||
| @@ -35,7 +34,7 @@ class _ChatScreenState extends State<ChatScreen> { | ||||
|   List<SnChannel>? _channels; | ||||
|   Map<int, SnChatMessage>? _lastMessages; | ||||
|  | ||||
|   void _refreshChannels() { | ||||
|   void _refreshChannels({bool noRemote = false}) { | ||||
|     final ua = context.read<UserProvider>(); | ||||
|     if (!ua.isAuthorized) { | ||||
|       setState(() => _isBusy = false); | ||||
| @@ -43,12 +42,15 @@ class _ChatScreenState extends State<ChatScreen> { | ||||
|     } | ||||
|  | ||||
|     final chan = context.read<ChatChannelProvider>(); | ||||
|     chan.fetchChannels().listen((channels) async { | ||||
|     chan.fetchChannels(noRemote: noRemote).listen((channels) async { | ||||
|       final lastMessages = await chan.getLastMessages(channels); | ||||
|       _lastMessages = {for (final val in lastMessages) val.channelId: val}; | ||||
|       channels.sort((a, b) { | ||||
|         if (_lastMessages!.containsKey(a.id) && _lastMessages!.containsKey(b.id)) { | ||||
|           return _lastMessages![b.id]!.createdAt.compareTo(_lastMessages![a.id]!.createdAt); | ||||
|         if (_lastMessages!.containsKey(a.id) && | ||||
|             _lastMessages!.containsKey(b.id)) { | ||||
|           return _lastMessages![b.id]! | ||||
|               .createdAt | ||||
|               .compareTo(_lastMessages![a.id]!.createdAt); | ||||
|         } | ||||
|         if (_lastMessages!.containsKey(a.id)) return -1; | ||||
|         if (_lastMessages!.containsKey(b.id)) return 1; | ||||
| @@ -86,7 +88,8 @@ class _ChatScreenState extends State<ChatScreen> { | ||||
|   void _newDirectMessage() async { | ||||
|     final user = await showModalBottomSheet( | ||||
|       context: context, | ||||
|       builder: (context) => AccountSelect(title: 'channelNewDirectMessage'.tr()), | ||||
|       builder: (context) => | ||||
|           AccountSelect(title: 'channelNewDirectMessage'.tr()), | ||||
|     ); | ||||
|     if (user == null) return; | ||||
|     if (!mounted) return; | ||||
| @@ -98,7 +101,8 @@ class _ChatScreenState extends State<ChatScreen> { | ||||
|       await sn.client.post('/cgi/im/channels/global/dm', data: { | ||||
|         'alias': uuid.v4().replaceAll('-', '').substring(0, 12), | ||||
|         '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, | ||||
|       }); | ||||
|       _fabKey.currentState!.toggle(); | ||||
| @@ -144,20 +148,27 @@ class _ChatScreenState extends State<ChatScreen> { | ||||
|         type: ExpandableFabType.up, | ||||
|         childrenAnimation: ExpandableFabAnimation.none, | ||||
|         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( | ||||
|           child: const Icon(Symbols.add, size: 28), | ||||
|           fabSize: ExpandableFabSize.regular, | ||||
|           foregroundColor: Theme.of(context).floatingActionButtonTheme.foregroundColor, | ||||
|           backgroundColor: Theme.of(context).floatingActionButtonTheme.backgroundColor, | ||||
|           foregroundColor: | ||||
|               Theme.of(context).floatingActionButtonTheme.foregroundColor, | ||||
|           backgroundColor: | ||||
|               Theme.of(context).floatingActionButtonTheme.backgroundColor, | ||||
|           shape: const CircleBorder(), | ||||
|         ), | ||||
|         closeButtonBuilder: DefaultFloatingActionButtonBuilder( | ||||
|           child: const Icon(Symbols.close, size: 28), | ||||
|           fabSize: ExpandableFabSize.regular, | ||||
|           foregroundColor: Theme.of(context).floatingActionButtonTheme.foregroundColor, | ||||
|           backgroundColor: Theme.of(context).floatingActionButtonTheme.backgroundColor, | ||||
|           foregroundColor: | ||||
|               Theme.of(context).floatingActionButtonTheme.foregroundColor, | ||||
|           backgroundColor: | ||||
|               Theme.of(context).floatingActionButtonTheme.backgroundColor, | ||||
|           shape: const CircleBorder(), | ||||
|         ), | ||||
|         children: [ | ||||
| @@ -208,13 +219,17 @@ class _ChatScreenState extends State<ChatScreen> { | ||||
|                     final lastMessage = _lastMessages?[channel.id]; | ||||
|  | ||||
|                     if (channel.type == 1) { | ||||
|                       final otherMember = channel.members?.cast<SnChannelMember?>().firstWhere( | ||||
|                       final otherMember = | ||||
|                           channel.members?.cast<SnChannelMember?>().firstWhere( | ||||
|                                 (ele) => ele?.accountId != ua.user?.id, | ||||
|                                 orElse: () => null, | ||||
|                               ); | ||||
|  | ||||
|                       return ListTile( | ||||
|                         title: Text(ud.getAccountFromCache(otherMember?.accountId)?.nick ?? channel.name), | ||||
|                         title: Text(ud | ||||
|                                 .getAccountFromCache(otherMember?.accountId) | ||||
|                                 ?.nick ?? | ||||
|                             channel.name), | ||||
|                         subtitle: lastMessage != null | ||||
|                             ? Text( | ||||
|                                 '${ud.getAccountFromCache(lastMessage.sender.accountId)?.nick}: ${lastMessage.body['text'] ?? 'Unable preview'}', | ||||
| @@ -228,9 +243,12 @@ class _ChatScreenState extends State<ChatScreen> { | ||||
|                                 maxLines: 1, | ||||
|                                 overflow: TextOverflow.ellipsis, | ||||
|                               ), | ||||
|                         contentPadding: const EdgeInsets.symmetric(horizontal: 16), | ||||
|                         contentPadding: | ||||
|                             const EdgeInsets.symmetric(horizontal: 16), | ||||
|                         leading: AccountImage( | ||||
|                           content: ud.getAccountFromCache(otherMember?.accountId)?.avatar, | ||||
|                           content: ud | ||||
|                               .getAccountFromCache(otherMember?.accountId) | ||||
|                               ?.avatar, | ||||
|                         ), | ||||
|                         onTap: () { | ||||
|                           GoRouter.of(context).pushNamed( | ||||
| @@ -240,7 +258,7 @@ class _ChatScreenState extends State<ChatScreen> { | ||||
|                               'alias': channel.alias, | ||||
|                             }, | ||||
|                           ).then((value) { | ||||
|                             if (mounted) _refreshChannels(); | ||||
|                             if (mounted) _refreshChannels(noRemote: true); | ||||
|                           }); | ||||
|                         }, | ||||
|                       ); | ||||
| @@ -259,7 +277,8 @@ class _ChatScreenState extends State<ChatScreen> { | ||||
|                               maxLines: 1, | ||||
|                               overflow: TextOverflow.ellipsis, | ||||
|                             ), | ||||
|                       contentPadding: const EdgeInsets.symmetric(horizontal: 16), | ||||
|                       contentPadding: | ||||
|                           const EdgeInsets.symmetric(horizontal: 16), | ||||
|                       leading: AccountImage( | ||||
|                         content: null, | ||||
|                         fallbackWidget: const Icon(Symbols.chat, size: 20), | ||||
|   | ||||
| @@ -243,7 +243,12 @@ class _ExploreScreenState extends State<ExploreScreen> with SingleTickerProvider | ||||
|                         children: [ | ||||
|                           Icon(Symbols.globe, size: 20, color: Theme.of(context).appBarTheme.foregroundColor), | ||||
|                           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: [ | ||||
|                           Icon(Symbols.group, size: 20, color: Theme.of(context).appBarTheme.foregroundColor), | ||||
|                           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: [ | ||||
|                           Icon(Symbols.subscriptions, size: 20, color: Theme.of(context).appBarTheme.foregroundColor), | ||||
|                           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: [ | ||||
|                           Icon(Symbols.workspaces, size: 20, color: Theme.of(context).appBarTheme.foregroundColor), | ||||
|                           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:flutter/foundation.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter/services.dart'; | ||||
| import 'package:flutter_colorpicker/flutter_colorpicker.dart'; | ||||
| import 'package:go_router/go_router.dart'; | ||||
| import 'package:google_fonts/google_fonts.dart'; | ||||
| import 'package:image_picker/image_picker.dart'; | ||||
| import 'package:material_symbols_icons/symbols.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:styled_widget/styled_widget.dart'; | ||||
| import 'package:surface/providers/config.dart'; | ||||
| import 'package:surface/providers/database.dart'; | ||||
| import 'package:surface/providers/sn_network.dart'; | ||||
| import 'package:surface/providers/theme.dart'; | ||||
| import 'package:surface/theme.dart'; | ||||
| @@ -67,6 +70,7 @@ class _SettingsScreenState extends State<SettingsScreen> { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     final sn = context.read<SnNetworkProvider>(); | ||||
|     final dt = context.read<DatabaseProvider>(); | ||||
|  | ||||
|     return AppScaffold( | ||||
|       appBar: AppBar( | ||||
| @@ -81,7 +85,11 @@ class _SettingsScreenState extends State<SettingsScreen> { | ||||
|             Column( | ||||
|               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|               children: [ | ||||
|                 Text('settingsAppearance').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4), | ||||
|                 Text('settingsAppearance') | ||||
|                     .bold() | ||||
|                     .fontSize(17) | ||||
|                     .tr() | ||||
|                     .padding(horizontal: 20, bottom: 4), | ||||
|                 ListTile( | ||||
|                   title: Text('settingsDisplayLanguage').tr(), | ||||
|                   subtitle: Text('settingsDisplayLanguageDescription').tr(), | ||||
| @@ -91,15 +99,21 @@ class _SettingsScreenState extends State<SettingsScreen> { | ||||
|                     child: DropdownButton2<Locale?>( | ||||
|                       isExpanded: true, | ||||
|                       items: [ | ||||
|                         ...EasyLocalization.of(context)!.supportedLocales.mapIndexed((idx, ele) { | ||||
|                         ...EasyLocalization.of(context)! | ||||
|                             .supportedLocales | ||||
|                             .mapIndexed((idx, ele) { | ||||
|                           return DropdownMenuItem<Locale?>( | ||||
|                             value: ele, | ||||
|                             child: Text('${ele.languageCode}-${ele.countryCode}').fontSize(14), | ||||
|                             child: | ||||
|                                 Text('${ele.languageCode}-${ele.countryCode}') | ||||
|                                     .fontSize(14), | ||||
|                           ); | ||||
|                         }), | ||||
|                         DropdownMenuItem<Locale?>( | ||||
|                           value: null, | ||||
|                           child: Text('settingsDisplayLanguageSystem').tr().fontSize(14), | ||||
|                           child: Text('settingsDisplayLanguageSystem') | ||||
|                               .tr() | ||||
|                               .fontSize(14), | ||||
|                         ), | ||||
|                       ], | ||||
|                       value: EasyLocalization.of(context)!.currentLocale, | ||||
| @@ -132,10 +146,12 @@ class _SettingsScreenState extends State<SettingsScreen> { | ||||
|                     leading: const Icon(Symbols.image), | ||||
|                     trailing: const Icon(Symbols.chevron_right), | ||||
|                     onTap: () async { | ||||
|                       final image = await ImagePicker().pickImage(source: ImageSource.gallery); | ||||
|                       final image = await ImagePicker() | ||||
|                           .pickImage(source: ImageSource.gallery); | ||||
|                       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); | ||||
|  | ||||
|                       setState(() {}); | ||||
| @@ -143,7 +159,8 @@ class _SettingsScreenState extends State<SettingsScreen> { | ||||
|                   ), | ||||
|                 if (!kIsWeb) | ||||
|                   FutureBuilder<bool>( | ||||
|                       future: File('$_docBasepath/app_background_image').exists(), | ||||
|                       future: | ||||
|                           File('$_docBasepath/app_background_image').exists(), | ||||
|                       builder: (context, snapshot) { | ||||
|                         if (!snapshot.hasData || !snapshot.data!) { | ||||
|                           return const SizedBox.shrink(); | ||||
| @@ -151,12 +168,16 @@ class _SettingsScreenState extends State<SettingsScreen> { | ||||
|  | ||||
|                         return ListTile( | ||||
|                           title: Text('settingsBackgroundImageClear').tr(), | ||||
|                           subtitle: Text('settingsBackgroundImageClearDescription').tr(), | ||||
|                           contentPadding: const EdgeInsets.symmetric(horizontal: 24), | ||||
|                           subtitle: | ||||
|                               Text('settingsBackgroundImageClearDescription') | ||||
|                                   .tr(), | ||||
|                           contentPadding: | ||||
|                               const EdgeInsets.symmetric(horizontal: 24), | ||||
|                           leading: const Icon(Symbols.texture), | ||||
|                           trailing: const Icon(Symbols.chevron_right), | ||||
|                           onTap: () { | ||||
|                             File('$_docBasepath/app_background_image').deleteSync(); | ||||
|                             File('$_docBasepath/app_background_image') | ||||
|                                 .deleteSync(); | ||||
|                             _prefs.remove(kAppBackgroundStoreKey); | ||||
|                             setState(() {}); | ||||
|                           }, | ||||
| @@ -186,11 +207,12 @@ class _SettingsScreenState extends State<SettingsScreen> { | ||||
|                   contentPadding: const EdgeInsets.symmetric(horizontal: 24), | ||||
|                   trailing: const Icon(Symbols.chevron_right), | ||||
|                   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?>( | ||||
|                       context: context, | ||||
|                       builder: (context) => | ||||
|                           AlertDialog( | ||||
|                       builder: (context) => AlertDialog( | ||||
|                         content: SingleChildScrollView( | ||||
|                           child: ColorPicker( | ||||
|                             pickerColor: pickerColor, | ||||
| @@ -248,16 +270,17 @@ class _SettingsScreenState extends State<SettingsScreen> { | ||||
|                       ], | ||||
|                       value: _prefs.getInt(kAppColorSchemeStoreKey) == null | ||||
|                           ? 1 | ||||
|                           : kColorSchemes.values | ||||
|                           .toList() | ||||
|                           .indexWhere((ele) => ele.value == _prefs.getInt(kAppColorSchemeStoreKey)), | ||||
|                           : kColorSchemes.values.toList().indexWhere((ele) => | ||||
|                               ele.value == | ||||
|                               _prefs.getInt(kAppColorSchemeStoreKey)), | ||||
|                       onChanged: (int? value) { | ||||
|                         if (value != null && value != -1) { | ||||
|                           _prefs.setInt(kAppColorSchemeStoreKey, kColorSchemes.values | ||||
|                               .elementAt(value) | ||||
|                               .value); | ||||
|                           _prefs.setInt(kAppColorSchemeStoreKey, | ||||
|                               kColorSchemes.values.elementAt(value).value); | ||||
|                           final th = context.read<ThemeProvider>(); | ||||
|                           th.reloadTheme(seedColorOverride: kColorSchemes.values.elementAt(value)); | ||||
|                           th.reloadTheme( | ||||
|                               seedColorOverride: | ||||
|                                   kColorSchemes.values.elementAt(value)); | ||||
|                           setState(() {}); | ||||
|  | ||||
|                           context.showSnackbar('colorSchemeApplied'.tr()); | ||||
| @@ -293,7 +316,8 @@ class _SettingsScreenState extends State<SettingsScreen> { | ||||
|                 CheckboxListTile( | ||||
|                   secondary: const Icon(Symbols.left_panel_close), | ||||
|                   title: Text('settingsDrawerPreferCollapse').tr(), | ||||
|                   subtitle: Text('settingsDrawerPreferCollapseDescription').tr(), | ||||
|                   subtitle: | ||||
|                       Text('settingsDrawerPreferCollapseDescription').tr(), | ||||
|                   contentPadding: const EdgeInsets.only(left: 24, right: 17), | ||||
|                   value: _prefs.getBool(kAppDrawerPreferCollapse) ?? false, | ||||
|                   onChanged: (value) { | ||||
| @@ -308,7 +332,11 @@ class _SettingsScreenState extends State<SettingsScreen> { | ||||
|             Column( | ||||
|               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|               children: [ | ||||
|                 Text('settingsFeatures').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4), | ||||
|                 Text('settingsFeatures') | ||||
|                     .bold() | ||||
|                     .fontSize(17) | ||||
|                     .tr() | ||||
|                     .padding(horizontal: 20, bottom: 4), | ||||
|                 CheckboxListTile( | ||||
|                   secondary: const Icon(Symbols.vibration), | ||||
|                   contentPadding: const EdgeInsets.only(left: 24, right: 17), | ||||
| @@ -350,7 +378,11 @@ class _SettingsScreenState extends State<SettingsScreen> { | ||||
|             Column( | ||||
|               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|               children: [ | ||||
|                 Text('settingsNetwork').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4), | ||||
|                 Text('settingsNetwork') | ||||
|                     .bold() | ||||
|                     .fontSize(17) | ||||
|                     .tr() | ||||
|                     .padding(horizontal: 20, bottom: 4), | ||||
|                 TextField( | ||||
|                   controller: _serverUrlController, | ||||
|                   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), | ||||
|                 ListTile( | ||||
|                   title: Text('settingsNetworkServerPreset').tr(), | ||||
| @@ -383,12 +416,13 @@ class _SettingsScreenState extends State<SettingsScreen> { | ||||
|                       isExpanded: true, | ||||
|                       items: [ | ||||
|                         ...kNetworkServerDirectory, | ||||
|                         if (!kNetworkServerDirectory.map((ele) => ele.$2).contains(_serverUrlController.text)) | ||||
|                         if (!kNetworkServerDirectory | ||||
|                             .map((ele) => ele.$2) | ||||
|                             .contains(_serverUrlController.text)) | ||||
|                           ('Custom', _serverUrlController.text), | ||||
|                       ] | ||||
|                           .map( | ||||
|                             (item) => | ||||
|                             DropdownMenuItem<String>( | ||||
|                             (item) => DropdownMenuItem<String>( | ||||
|                               value: item.$2, | ||||
|                               child: Column( | ||||
|                                 mainAxisSize: MainAxisSize.max, | ||||
| @@ -396,7 +430,8 @@ class _SettingsScreenState extends State<SettingsScreen> { | ||||
|                                 crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                                 children: [ | ||||
|                                   Text(item.$1).fontSize(14), | ||||
|                                   Text(item.$2, overflow: TextOverflow.ellipsis).fontSize(11) | ||||
|                                   Text(item.$2, overflow: TextOverflow.ellipsis) | ||||
|                                       .fontSize(11) | ||||
|                                 ], | ||||
|                               ), | ||||
|                             ), | ||||
| @@ -442,7 +477,11 @@ class _SettingsScreenState extends State<SettingsScreen> { | ||||
|             Column( | ||||
|               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|               children: [ | ||||
|                 Text('settingsPerformance').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4), | ||||
|                 Text('settingsPerformance') | ||||
|                     .bold() | ||||
|                     .fontSize(17) | ||||
|                     .tr() | ||||
|                     .padding(horizontal: 20, bottom: 4), | ||||
|                 ListTile( | ||||
|                   title: Text('settingsImageQuality').tr(), | ||||
|                   subtitle: Text('settingsImageQualityDescription').tr(), | ||||
| @@ -450,13 +489,13 @@ class _SettingsScreenState extends State<SettingsScreen> { | ||||
|                   leading: const Icon(Symbols.image), | ||||
|                   trailing: DropdownButtonHideUnderline( | ||||
|                     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, | ||||
|                       isExpanded: true, | ||||
|                       items: kImageQualityLevel.entries | ||||
|                           .map( | ||||
|                             (item) => | ||||
|                             DropdownMenuItem<FilterQuality>( | ||||
|                             (item) => DropdownMenuItem<FilterQuality>( | ||||
|                               value: item.value, | ||||
|                               child: Text(item.key).tr().fontSize(14), | ||||
|                             ), | ||||
| @@ -464,7 +503,8 @@ class _SettingsScreenState extends State<SettingsScreen> { | ||||
|                           .toList(), | ||||
|                       onChanged: (FilterQuality? value) { | ||||
|                         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(() {}); | ||||
|                       }, | ||||
|                       buttonStyleData: const ButtonStyleData( | ||||
| @@ -486,7 +526,42 @@ class _SettingsScreenState extends State<SettingsScreen> { | ||||
|             Column( | ||||
|               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|               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( | ||||
|                   title: Text('settingsMiscAbout').tr(), | ||||
|                   subtitle: Text('settingsMiscAboutDescription').tr(), | ||||
|   | ||||
| @@ -9,7 +9,7 @@ class SnAccount with _$SnAccount { | ||||
|   const SnAccount._(); | ||||
|  | ||||
|   const factory SnAccount({ | ||||
|     @HiveField(0) required int id, | ||||
|     required int id, | ||||
|     required DateTime createdAt, | ||||
|     required DateTime updatedAt, | ||||
|     required DateTime? deletedAt, | ||||
|   | ||||
| @@ -58,7 +58,7 @@ abstract class $SnAccountCopyWith<$Res> { | ||||
|       _$SnAccountCopyWithImpl<$Res, SnAccount>; | ||||
|   @useResult | ||||
|   $Res call( | ||||
|       {@HiveField(0) int id, | ||||
|       {int id, | ||||
|       DateTime createdAt, | ||||
|       DateTime updatedAt, | ||||
|       DateTime? deletedAt, | ||||
| @@ -226,7 +226,7 @@ abstract class _$$SnAccountImplCopyWith<$Res> | ||||
|   @override | ||||
|   @useResult | ||||
|   $Res call( | ||||
|       {@HiveField(0) int id, | ||||
|       {int id, | ||||
|       DateTime createdAt, | ||||
|       DateTime updatedAt, | ||||
|       DateTime? deletedAt, | ||||
| @@ -374,7 +374,7 @@ class __$$SnAccountImplCopyWithImpl<$Res> | ||||
| @JsonSerializable() | ||||
| class _$SnAccountImpl extends _SnAccount { | ||||
|   const _$SnAccountImpl( | ||||
|       {@HiveField(0) required this.id, | ||||
|       {required this.id, | ||||
|       required this.createdAt, | ||||
|       required this.updatedAt, | ||||
|       required this.deletedAt, | ||||
| @@ -556,7 +556,7 @@ class _$SnAccountImpl extends _SnAccount { | ||||
|  | ||||
| abstract class _SnAccount extends SnAccount { | ||||
|   const factory _SnAccount( | ||||
|       {@HiveField(0) required final int id, | ||||
|       {required final int id, | ||||
|       required final DateTime createdAt, | ||||
|       required final DateTime updatedAt, | ||||
|       required final DateTime? deletedAt, | ||||
|   | ||||
| @@ -14,21 +14,21 @@ class SnChannel with _$SnChannel { | ||||
|  | ||||
|   @HiveType(typeId: 2) | ||||
|   const factory SnChannel({ | ||||
|     @HiveField(0) required int id, | ||||
|     @HiveField(1) required DateTime createdAt, | ||||
|     @HiveField(2) required DateTime updatedAt, | ||||
|     @HiveField(3) required dynamic deletedAt, | ||||
|     @HiveField(4) required String alias, | ||||
|     @HiveField(5) required String name, | ||||
|     @HiveField(6) required String description, | ||||
|     @HiveField(7) required List<SnChannelMember>? members, | ||||
|     required int id, | ||||
|     required DateTime createdAt, | ||||
|     required DateTime updatedAt, | ||||
|     required dynamic deletedAt, | ||||
|     required String alias, | ||||
|     required String name, | ||||
|     required String description, | ||||
|     required List<SnChannelMember>? members, | ||||
|     List<SnChatMessage>? messages, | ||||
|     @HiveField(8) required int type, | ||||
|     @HiveField(9) required int accountId, | ||||
|     @HiveField(10) required SnRealm? realm, | ||||
|     @HiveField(11) required int? realmId, | ||||
|     @HiveField(12) required bool isPublic, | ||||
|     @HiveField(13) required bool isCommunity, | ||||
|     required int type, | ||||
|     required int accountId, | ||||
|     required SnRealm? realm, | ||||
|     required int? realmId, | ||||
|     required bool isPublic, | ||||
|     required bool isCommunity, | ||||
|   }) = _SnChannel; | ||||
|  | ||||
|   factory SnChannel.fromJson(Map<String, dynamic> json) => | ||||
| @@ -44,17 +44,17 @@ class SnChannelMember with _$SnChannelMember { | ||||
|  | ||||
|   @HiveType(typeId: 3) | ||||
|   const factory SnChannelMember({ | ||||
|     @HiveField(0) required int id, | ||||
|     @HiveField(1) required DateTime createdAt, | ||||
|     @HiveField(2) required DateTime updatedAt, | ||||
|     @HiveField(3) required DateTime? deletedAt, | ||||
|     @HiveField(4) required int channelId, | ||||
|     @HiveField(5) required int accountId, | ||||
|     @HiveField(6) required String? nick, | ||||
|     @HiveField(7) required SnChannel? channel, | ||||
|     @HiveField(8) required SnAccount? account, | ||||
|     required int id, | ||||
|     required DateTime createdAt, | ||||
|     required DateTime updatedAt, | ||||
|     required DateTime? deletedAt, | ||||
|     required int channelId, | ||||
|     required int accountId, | ||||
|     required String? nick, | ||||
|     required SnChannel? channel, | ||||
|     required SnAccount? account, | ||||
|     @Default(0) int notify, | ||||
|     @HiveField(9) required int powerLevel, | ||||
|     required int powerLevel, | ||||
|     dynamic calls, | ||||
|     dynamic events, | ||||
|   }) = _SnChannelMember; | ||||
| @@ -69,19 +69,19 @@ class SnChatMessage with _$SnChatMessage { | ||||
|  | ||||
|   @HiveType(typeId: 4) | ||||
|   const factory SnChatMessage({ | ||||
|     @HiveField(0) required int id, | ||||
|     @HiveField(1) required DateTime createdAt, | ||||
|     @HiveField(2) required DateTime updatedAt, | ||||
|     @HiveField(3) required DateTime? deletedAt, | ||||
|     @HiveField(4) required String uuid, | ||||
|     @HiveField(5) @Default({}) Map<String, dynamic> body, | ||||
|     @HiveField(6) required String type, | ||||
|     @HiveField(7) required SnChannel channel, | ||||
|     @HiveField(8) required SnChannelMember sender, | ||||
|     @HiveField(9) required int channelId, | ||||
|     @HiveField(10) required int senderId, | ||||
|     @HiveField(11) required int? quoteEventId, | ||||
|     @HiveField(12) required int? relatedEventId, | ||||
|     required int id, | ||||
|     required DateTime createdAt, | ||||
|     required DateTime updatedAt, | ||||
|     required DateTime? deletedAt, | ||||
|     required String uuid, | ||||
|     @Default({}) Map<String, dynamic> body, | ||||
|     required String type, | ||||
|     required SnChannel channel, | ||||
|     required SnChannelMember sender, | ||||
|     required int channelId, | ||||
|     required int senderId, | ||||
|     required int? quoteEventId, | ||||
|     required int? relatedEventId, | ||||
|     SnChatMessagePreload? preload, | ||||
|   }) = _SnChatMessage; | ||||
|  | ||||
|   | ||||
| @@ -66,21 +66,21 @@ abstract class $SnChannelCopyWith<$Res> { | ||||
|       _$SnChannelCopyWithImpl<$Res, SnChannel>; | ||||
|   @useResult | ||||
|   $Res call( | ||||
|       {@HiveField(0) int id, | ||||
|       @HiveField(1) DateTime createdAt, | ||||
|       @HiveField(2) DateTime updatedAt, | ||||
|       @HiveField(3) dynamic deletedAt, | ||||
|       @HiveField(4) String alias, | ||||
|       @HiveField(5) String name, | ||||
|       @HiveField(6) String description, | ||||
|       @HiveField(7) List<SnChannelMember>? members, | ||||
|       {int id, | ||||
|       DateTime createdAt, | ||||
|       DateTime updatedAt, | ||||
|       dynamic deletedAt, | ||||
|       String alias, | ||||
|       String name, | ||||
|       String description, | ||||
|       List<SnChannelMember>? members, | ||||
|       List<SnChatMessage>? messages, | ||||
|       @HiveField(8) int type, | ||||
|       @HiveField(9) int accountId, | ||||
|       @HiveField(10) SnRealm? realm, | ||||
|       @HiveField(11) int? realmId, | ||||
|       @HiveField(12) bool isPublic, | ||||
|       @HiveField(13) bool isCommunity}); | ||||
|       int type, | ||||
|       int accountId, | ||||
|       SnRealm? realm, | ||||
|       int? realmId, | ||||
|       bool isPublic, | ||||
|       bool isCommunity}); | ||||
|  | ||||
|   $SnRealmCopyWith<$Res>? get realm; | ||||
| } | ||||
| @@ -204,21 +204,21 @@ abstract class _$$SnChannelImplCopyWith<$Res> | ||||
|   @override | ||||
|   @useResult | ||||
|   $Res call( | ||||
|       {@HiveField(0) int id, | ||||
|       @HiveField(1) DateTime createdAt, | ||||
|       @HiveField(2) DateTime updatedAt, | ||||
|       @HiveField(3) dynamic deletedAt, | ||||
|       @HiveField(4) String alias, | ||||
|       @HiveField(5) String name, | ||||
|       @HiveField(6) String description, | ||||
|       @HiveField(7) List<SnChannelMember>? members, | ||||
|       {int id, | ||||
|       DateTime createdAt, | ||||
|       DateTime updatedAt, | ||||
|       dynamic deletedAt, | ||||
|       String alias, | ||||
|       String name, | ||||
|       String description, | ||||
|       List<SnChannelMember>? members, | ||||
|       List<SnChatMessage>? messages, | ||||
|       @HiveField(8) int type, | ||||
|       @HiveField(9) int accountId, | ||||
|       @HiveField(10) SnRealm? realm, | ||||
|       @HiveField(11) int? realmId, | ||||
|       @HiveField(12) bool isPublic, | ||||
|       @HiveField(13) bool isCommunity}); | ||||
|       int type, | ||||
|       int accountId, | ||||
|       SnRealm? realm, | ||||
|       int? realmId, | ||||
|       bool isPublic, | ||||
|       bool isCommunity}); | ||||
|  | ||||
|   @override | ||||
|   $SnRealmCopyWith<$Res>? get realm; | ||||
| @@ -323,21 +323,21 @@ class __$$SnChannelImplCopyWithImpl<$Res> | ||||
| @HiveType(typeId: 2) | ||||
| class _$SnChannelImpl extends _SnChannel { | ||||
|   const _$SnChannelImpl( | ||||
|       {@HiveField(0) required this.id, | ||||
|       @HiveField(1) required this.createdAt, | ||||
|       @HiveField(2) required this.updatedAt, | ||||
|       @HiveField(3) required this.deletedAt, | ||||
|       @HiveField(4) required this.alias, | ||||
|       @HiveField(5) required this.name, | ||||
|       @HiveField(6) required this.description, | ||||
|       @HiveField(7) required final List<SnChannelMember>? members, | ||||
|       {required this.id, | ||||
|       required this.createdAt, | ||||
|       required this.updatedAt, | ||||
|       required this.deletedAt, | ||||
|       required this.alias, | ||||
|       required this.name, | ||||
|       required this.description, | ||||
|       required final List<SnChannelMember>? members, | ||||
|       final List<SnChatMessage>? messages, | ||||
|       @HiveField(8) required this.type, | ||||
|       @HiveField(9) required this.accountId, | ||||
|       @HiveField(10) required this.realm, | ||||
|       @HiveField(11) required this.realmId, | ||||
|       @HiveField(12) required this.isPublic, | ||||
|       @HiveField(13) required this.isCommunity}) | ||||
|       required this.type, | ||||
|       required this.accountId, | ||||
|       required this.realm, | ||||
|       required this.realmId, | ||||
|       required this.isPublic, | ||||
|       required this.isCommunity}) | ||||
|       : _members = members, | ||||
|         _messages = messages, | ||||
|         super._(); | ||||
| @@ -477,21 +477,21 @@ class _$SnChannelImpl extends _SnChannel { | ||||
|  | ||||
| abstract class _SnChannel extends SnChannel { | ||||
|   const factory _SnChannel( | ||||
|       {@HiveField(0) required final int id, | ||||
|       @HiveField(1) required final DateTime createdAt, | ||||
|       @HiveField(2) required final DateTime updatedAt, | ||||
|       @HiveField(3) required final dynamic deletedAt, | ||||
|       @HiveField(4) required final String alias, | ||||
|       @HiveField(5) required final String name, | ||||
|       @HiveField(6) required final String description, | ||||
|       @HiveField(7) required final List<SnChannelMember>? members, | ||||
|       {required final int id, | ||||
|       required final DateTime createdAt, | ||||
|       required final DateTime updatedAt, | ||||
|       required final dynamic deletedAt, | ||||
|       required final String alias, | ||||
|       required final String name, | ||||
|       required final String description, | ||||
|       required final List<SnChannelMember>? members, | ||||
|       final List<SnChatMessage>? messages, | ||||
|       @HiveField(8) required final int type, | ||||
|       @HiveField(9) required final int accountId, | ||||
|       @HiveField(10) required final SnRealm? realm, | ||||
|       @HiveField(11) required final int? realmId, | ||||
|       @HiveField(12) required final bool isPublic, | ||||
|       @HiveField(13) required final bool isCommunity}) = _$SnChannelImpl; | ||||
|       required final int type, | ||||
|       required final int accountId, | ||||
|       required final SnRealm? realm, | ||||
|       required final int? realmId, | ||||
|       required final bool isPublic, | ||||
|       required final bool isCommunity}) = _$SnChannelImpl; | ||||
|   const _SnChannel._() : super._(); | ||||
|  | ||||
|   factory _SnChannel.fromJson(Map<String, dynamic> json) = | ||||
| @@ -597,17 +597,17 @@ abstract class $SnChannelMemberCopyWith<$Res> { | ||||
|       _$SnChannelMemberCopyWithImpl<$Res, SnChannelMember>; | ||||
|   @useResult | ||||
|   $Res call( | ||||
|       {@HiveField(0) int id, | ||||
|       @HiveField(1) DateTime createdAt, | ||||
|       @HiveField(2) DateTime updatedAt, | ||||
|       @HiveField(3) DateTime? deletedAt, | ||||
|       @HiveField(4) int channelId, | ||||
|       @HiveField(5) int accountId, | ||||
|       @HiveField(6) String? nick, | ||||
|       @HiveField(7) SnChannel? channel, | ||||
|       @HiveField(8) SnAccount? account, | ||||
|       {int id, | ||||
|       DateTime createdAt, | ||||
|       DateTime updatedAt, | ||||
|       DateTime? deletedAt, | ||||
|       int channelId, | ||||
|       int accountId, | ||||
|       String? nick, | ||||
|       SnChannel? channel, | ||||
|       SnAccount? account, | ||||
|       int notify, | ||||
|       @HiveField(9) int powerLevel, | ||||
|       int powerLevel, | ||||
|       dynamic calls, | ||||
|       dynamic events}); | ||||
|  | ||||
| @@ -738,17 +738,17 @@ abstract class _$$SnChannelMemberImplCopyWith<$Res> | ||||
|   @override | ||||
|   @useResult | ||||
|   $Res call( | ||||
|       {@HiveField(0) int id, | ||||
|       @HiveField(1) DateTime createdAt, | ||||
|       @HiveField(2) DateTime updatedAt, | ||||
|       @HiveField(3) DateTime? deletedAt, | ||||
|       @HiveField(4) int channelId, | ||||
|       @HiveField(5) int accountId, | ||||
|       @HiveField(6) String? nick, | ||||
|       @HiveField(7) SnChannel? channel, | ||||
|       @HiveField(8) SnAccount? account, | ||||
|       {int id, | ||||
|       DateTime createdAt, | ||||
|       DateTime updatedAt, | ||||
|       DateTime? deletedAt, | ||||
|       int channelId, | ||||
|       int accountId, | ||||
|       String? nick, | ||||
|       SnChannel? channel, | ||||
|       SnAccount? account, | ||||
|       int notify, | ||||
|       @HiveField(9) int powerLevel, | ||||
|       int powerLevel, | ||||
|       dynamic calls, | ||||
|       dynamic events}); | ||||
|  | ||||
| @@ -847,17 +847,17 @@ class __$$SnChannelMemberImplCopyWithImpl<$Res> | ||||
| @HiveType(typeId: 3) | ||||
| class _$SnChannelMemberImpl extends _SnChannelMember { | ||||
|   const _$SnChannelMemberImpl( | ||||
|       {@HiveField(0) required this.id, | ||||
|       @HiveField(1) required this.createdAt, | ||||
|       @HiveField(2) required this.updatedAt, | ||||
|       @HiveField(3) required this.deletedAt, | ||||
|       @HiveField(4) required this.channelId, | ||||
|       @HiveField(5) required this.accountId, | ||||
|       @HiveField(6) required this.nick, | ||||
|       @HiveField(7) required this.channel, | ||||
|       @HiveField(8) required this.account, | ||||
|       {required this.id, | ||||
|       required this.createdAt, | ||||
|       required this.updatedAt, | ||||
|       required this.deletedAt, | ||||
|       required this.channelId, | ||||
|       required this.accountId, | ||||
|       required this.nick, | ||||
|       required this.channel, | ||||
|       required this.account, | ||||
|       this.notify = 0, | ||||
|       @HiveField(9) required this.powerLevel, | ||||
|       required this.powerLevel, | ||||
|       this.calls, | ||||
|       this.events}) | ||||
|       : super._(); | ||||
| @@ -971,17 +971,17 @@ class _$SnChannelMemberImpl extends _SnChannelMember { | ||||
|  | ||||
| abstract class _SnChannelMember extends SnChannelMember { | ||||
|   const factory _SnChannelMember( | ||||
|       {@HiveField(0) required final int id, | ||||
|       @HiveField(1) required final DateTime createdAt, | ||||
|       @HiveField(2) required final DateTime updatedAt, | ||||
|       @HiveField(3) required final DateTime? deletedAt, | ||||
|       @HiveField(4) required final int channelId, | ||||
|       @HiveField(5) required final int accountId, | ||||
|       @HiveField(6) required final String? nick, | ||||
|       @HiveField(7) required final SnChannel? channel, | ||||
|       @HiveField(8) required final SnAccount? account, | ||||
|       {required final int id, | ||||
|       required final DateTime createdAt, | ||||
|       required final DateTime updatedAt, | ||||
|       required final DateTime? deletedAt, | ||||
|       required final int channelId, | ||||
|       required final int accountId, | ||||
|       required final String? nick, | ||||
|       required final SnChannel? channel, | ||||
|       required final SnAccount? account, | ||||
|       final int notify, | ||||
|       @HiveField(9) required final int powerLevel, | ||||
|       required final int powerLevel, | ||||
|       final dynamic calls, | ||||
|       final dynamic events}) = _$SnChannelMemberImpl; | ||||
|   const _SnChannelMember._() : super._(); | ||||
| @@ -1085,19 +1085,19 @@ abstract class $SnChatMessageCopyWith<$Res> { | ||||
|       _$SnChatMessageCopyWithImpl<$Res, SnChatMessage>; | ||||
|   @useResult | ||||
|   $Res call( | ||||
|       {@HiveField(0) int id, | ||||
|       @HiveField(1) DateTime createdAt, | ||||
|       @HiveField(2) DateTime updatedAt, | ||||
|       @HiveField(3) DateTime? deletedAt, | ||||
|       @HiveField(4) String uuid, | ||||
|       @HiveField(5) Map<String, dynamic> body, | ||||
|       @HiveField(6) String type, | ||||
|       @HiveField(7) SnChannel channel, | ||||
|       @HiveField(8) SnChannelMember sender, | ||||
|       @HiveField(9) int channelId, | ||||
|       @HiveField(10) int senderId, | ||||
|       @HiveField(11) int? quoteEventId, | ||||
|       @HiveField(12) int? relatedEventId, | ||||
|       {int id, | ||||
|       DateTime createdAt, | ||||
|       DateTime updatedAt, | ||||
|       DateTime? deletedAt, | ||||
|       String uuid, | ||||
|       Map<String, dynamic> body, | ||||
|       String type, | ||||
|       SnChannel channel, | ||||
|       SnChannelMember sender, | ||||
|       int channelId, | ||||
|       int senderId, | ||||
|       int? quoteEventId, | ||||
|       int? relatedEventId, | ||||
|       SnChatMessagePreload? preload}); | ||||
|  | ||||
|   $SnChannelCopyWith<$Res> get channel; | ||||
| @@ -1239,19 +1239,19 @@ abstract class _$$SnChatMessageImplCopyWith<$Res> | ||||
|   @override | ||||
|   @useResult | ||||
|   $Res call( | ||||
|       {@HiveField(0) int id, | ||||
|       @HiveField(1) DateTime createdAt, | ||||
|       @HiveField(2) DateTime updatedAt, | ||||
|       @HiveField(3) DateTime? deletedAt, | ||||
|       @HiveField(4) String uuid, | ||||
|       @HiveField(5) Map<String, dynamic> body, | ||||
|       @HiveField(6) String type, | ||||
|       @HiveField(7) SnChannel channel, | ||||
|       @HiveField(8) SnChannelMember sender, | ||||
|       @HiveField(9) int channelId, | ||||
|       @HiveField(10) int senderId, | ||||
|       @HiveField(11) int? quoteEventId, | ||||
|       @HiveField(12) int? relatedEventId, | ||||
|       {int id, | ||||
|       DateTime createdAt, | ||||
|       DateTime updatedAt, | ||||
|       DateTime? deletedAt, | ||||
|       String uuid, | ||||
|       Map<String, dynamic> body, | ||||
|       String type, | ||||
|       SnChannel channel, | ||||
|       SnChannelMember sender, | ||||
|       int channelId, | ||||
|       int senderId, | ||||
|       int? quoteEventId, | ||||
|       int? relatedEventId, | ||||
|       SnChatMessagePreload? preload}); | ||||
|  | ||||
|   @override | ||||
| @@ -1356,19 +1356,19 @@ class __$$SnChatMessageImplCopyWithImpl<$Res> | ||||
| @HiveType(typeId: 4) | ||||
| class _$SnChatMessageImpl extends _SnChatMessage { | ||||
|   const _$SnChatMessageImpl( | ||||
|       {@HiveField(0) required this.id, | ||||
|       @HiveField(1) required this.createdAt, | ||||
|       @HiveField(2) required this.updatedAt, | ||||
|       @HiveField(3) required this.deletedAt, | ||||
|       @HiveField(4) required this.uuid, | ||||
|       @HiveField(5) final Map<String, dynamic> body = const {}, | ||||
|       @HiveField(6) required this.type, | ||||
|       @HiveField(7) required this.channel, | ||||
|       @HiveField(8) required this.sender, | ||||
|       @HiveField(9) required this.channelId, | ||||
|       @HiveField(10) required this.senderId, | ||||
|       @HiveField(11) required this.quoteEventId, | ||||
|       @HiveField(12) required this.relatedEventId, | ||||
|       {required this.id, | ||||
|       required this.createdAt, | ||||
|       required this.updatedAt, | ||||
|       required this.deletedAt, | ||||
|       required this.uuid, | ||||
|       final Map<String, dynamic> body = const {}, | ||||
|       required this.type, | ||||
|       required this.channel, | ||||
|       required this.sender, | ||||
|       required this.channelId, | ||||
|       required this.senderId, | ||||
|       required this.quoteEventId, | ||||
|       required this.relatedEventId, | ||||
|       this.preload}) | ||||
|       : _body = body, | ||||
|         super._(); | ||||
| @@ -1495,19 +1495,19 @@ class _$SnChatMessageImpl extends _SnChatMessage { | ||||
|  | ||||
| abstract class _SnChatMessage extends SnChatMessage { | ||||
|   const factory _SnChatMessage( | ||||
|       {@HiveField(0) required final int id, | ||||
|       @HiveField(1) required final DateTime createdAt, | ||||
|       @HiveField(2) required final DateTime updatedAt, | ||||
|       @HiveField(3) required final DateTime? deletedAt, | ||||
|       @HiveField(4) required final String uuid, | ||||
|       @HiveField(5) final Map<String, dynamic> body, | ||||
|       @HiveField(6) required final String type, | ||||
|       @HiveField(7) required final SnChannel channel, | ||||
|       @HiveField(8) required final SnChannelMember sender, | ||||
|       @HiveField(9) required final int channelId, | ||||
|       @HiveField(10) required final int senderId, | ||||
|       @HiveField(11) required final int? quoteEventId, | ||||
|       @HiveField(12) required final int? relatedEventId, | ||||
|       {required final int id, | ||||
|       required final DateTime createdAt, | ||||
|       required final DateTime updatedAt, | ||||
|       required final DateTime? deletedAt, | ||||
|       required final String uuid, | ||||
|       final Map<String, dynamic> body, | ||||
|       required final String type, | ||||
|       required final SnChannel channel, | ||||
|       required final SnChannelMember sender, | ||||
|       required final int channelId, | ||||
|       required final int senderId, | ||||
|       required final int? quoteEventId, | ||||
|       required final int? relatedEventId, | ||||
|       final SnChatMessagePreload? preload}) = _$SnChatMessageImpl; | ||||
|   const _SnChatMessage._() : super._(); | ||||
|  | ||||
|   | ||||
| @@ -29,20 +29,20 @@ class SnRealm with _$SnRealm { | ||||
|  | ||||
|   @HiveType(typeId: 1) | ||||
|   const factory SnRealm({ | ||||
|     @HiveField(0) required int id, | ||||
|     @HiveField(1) required DateTime createdAt, | ||||
|     @HiveField(2) required DateTime updatedAt, | ||||
|     @HiveField(3) required DateTime? deletedAt, | ||||
|     @HiveField(4) required String alias, | ||||
|     @HiveField(5) required String name, | ||||
|     @HiveField(6) required String description, | ||||
|     required int id, | ||||
|     required DateTime createdAt, | ||||
|     required DateTime updatedAt, | ||||
|     required DateTime? deletedAt, | ||||
|     required String alias, | ||||
|     required String name, | ||||
|     required String description, | ||||
|     List<SnRealmMember>? members, | ||||
|     @HiveField(7) required String? avatar, | ||||
|     @HiveField(8) required String? banner, | ||||
|     @HiveField(9) required Map<String, dynamic>? accessPolicy, | ||||
|     @HiveField(10) required int accountId, | ||||
|     @HiveField(11) required bool isPublic, | ||||
|     @HiveField(12) required bool isCommunity, | ||||
|     required String? avatar, | ||||
|     required String? banner, | ||||
|     required Map<String, dynamic>? accessPolicy, | ||||
|     required int accountId, | ||||
|     required bool isPublic, | ||||
|     required bool isCommunity, | ||||
|     @Default(0) int popularity, | ||||
|   }) = _SnRealm; | ||||
|  | ||||
|   | ||||
| @@ -411,20 +411,20 @@ abstract class $SnRealmCopyWith<$Res> { | ||||
|       _$SnRealmCopyWithImpl<$Res, SnRealm>; | ||||
|   @useResult | ||||
|   $Res call( | ||||
|       {@HiveField(0) int id, | ||||
|       @HiveField(1) DateTime createdAt, | ||||
|       @HiveField(2) DateTime updatedAt, | ||||
|       @HiveField(3) DateTime? deletedAt, | ||||
|       @HiveField(4) String alias, | ||||
|       @HiveField(5) String name, | ||||
|       @HiveField(6) String description, | ||||
|       {int id, | ||||
|       DateTime createdAt, | ||||
|       DateTime updatedAt, | ||||
|       DateTime? deletedAt, | ||||
|       String alias, | ||||
|       String name, | ||||
|       String description, | ||||
|       List<SnRealmMember>? members, | ||||
|       @HiveField(7) String? avatar, | ||||
|       @HiveField(8) String? banner, | ||||
|       @HiveField(9) Map<String, dynamic>? accessPolicy, | ||||
|       @HiveField(10) int accountId, | ||||
|       @HiveField(11) bool isPublic, | ||||
|       @HiveField(12) bool isCommunity, | ||||
|       String? avatar, | ||||
|       String? banner, | ||||
|       Map<String, dynamic>? accessPolicy, | ||||
|       int accountId, | ||||
|       bool isPublic, | ||||
|       bool isCommunity, | ||||
|       int popularity}); | ||||
| } | ||||
|  | ||||
| @@ -532,20 +532,20 @@ abstract class _$$SnRealmImplCopyWith<$Res> implements $SnRealmCopyWith<$Res> { | ||||
|   @override | ||||
|   @useResult | ||||
|   $Res call( | ||||
|       {@HiveField(0) int id, | ||||
|       @HiveField(1) DateTime createdAt, | ||||
|       @HiveField(2) DateTime updatedAt, | ||||
|       @HiveField(3) DateTime? deletedAt, | ||||
|       @HiveField(4) String alias, | ||||
|       @HiveField(5) String name, | ||||
|       @HiveField(6) String description, | ||||
|       {int id, | ||||
|       DateTime createdAt, | ||||
|       DateTime updatedAt, | ||||
|       DateTime? deletedAt, | ||||
|       String alias, | ||||
|       String name, | ||||
|       String description, | ||||
|       List<SnRealmMember>? members, | ||||
|       @HiveField(7) String? avatar, | ||||
|       @HiveField(8) String? banner, | ||||
|       @HiveField(9) Map<String, dynamic>? accessPolicy, | ||||
|       @HiveField(10) int accountId, | ||||
|       @HiveField(11) bool isPublic, | ||||
|       @HiveField(12) bool isCommunity, | ||||
|       String? avatar, | ||||
|       String? banner, | ||||
|       Map<String, dynamic>? accessPolicy, | ||||
|       int accountId, | ||||
|       bool isPublic, | ||||
|       bool isCommunity, | ||||
|       int popularity}); | ||||
| } | ||||
|  | ||||
| @@ -648,20 +648,20 @@ class __$$SnRealmImplCopyWithImpl<$Res> | ||||
| @HiveType(typeId: 1) | ||||
| class _$SnRealmImpl extends _SnRealm { | ||||
|   const _$SnRealmImpl( | ||||
|       {@HiveField(0) required this.id, | ||||
|       @HiveField(1) required this.createdAt, | ||||
|       @HiveField(2) required this.updatedAt, | ||||
|       @HiveField(3) required this.deletedAt, | ||||
|       @HiveField(4) required this.alias, | ||||
|       @HiveField(5) required this.name, | ||||
|       @HiveField(6) required this.description, | ||||
|       {required this.id, | ||||
|       required this.createdAt, | ||||
|       required this.updatedAt, | ||||
|       required this.deletedAt, | ||||
|       required this.alias, | ||||
|       required this.name, | ||||
|       required this.description, | ||||
|       final List<SnRealmMember>? members, | ||||
|       @HiveField(7) required this.avatar, | ||||
|       @HiveField(8) required this.banner, | ||||
|       @HiveField(9) required final Map<String, dynamic>? accessPolicy, | ||||
|       @HiveField(10) required this.accountId, | ||||
|       @HiveField(11) required this.isPublic, | ||||
|       @HiveField(12) required this.isCommunity, | ||||
|       required this.avatar, | ||||
|       required this.banner, | ||||
|       required final Map<String, dynamic>? accessPolicy, | ||||
|       required this.accountId, | ||||
|       required this.isPublic, | ||||
|       required this.isCommunity, | ||||
|       this.popularity = 0}) | ||||
|       : _members = members, | ||||
|         _accessPolicy = accessPolicy, | ||||
| @@ -805,20 +805,20 @@ class _$SnRealmImpl extends _SnRealm { | ||||
|  | ||||
| abstract class _SnRealm extends SnRealm { | ||||
|   const factory _SnRealm( | ||||
|       {@HiveField(0) required final int id, | ||||
|       @HiveField(1) required final DateTime createdAt, | ||||
|       @HiveField(2) required final DateTime updatedAt, | ||||
|       @HiveField(3) required final DateTime? deletedAt, | ||||
|       @HiveField(4) required final String alias, | ||||
|       @HiveField(5) required final String name, | ||||
|       @HiveField(6) required final String description, | ||||
|       {required final int id, | ||||
|       required final DateTime createdAt, | ||||
|       required final DateTime updatedAt, | ||||
|       required final DateTime? deletedAt, | ||||
|       required final String alias, | ||||
|       required final String name, | ||||
|       required final String description, | ||||
|       final List<SnRealmMember>? members, | ||||
|       @HiveField(7) required final String? avatar, | ||||
|       @HiveField(8) required final String? banner, | ||||
|       @HiveField(9) required final Map<String, dynamic>? accessPolicy, | ||||
|       @HiveField(10) required final int accountId, | ||||
|       @HiveField(11) required final bool isPublic, | ||||
|       @HiveField(12) required final bool isCommunity, | ||||
|       required final String? avatar, | ||||
|       required final String? banner, | ||||
|       required final Map<String, dynamic>? accessPolicy, | ||||
|       required final int accountId, | ||||
|       required final bool isPublic, | ||||
|       required final bool isCommunity, | ||||
|       final int popularity}) = _$SnRealmImpl; | ||||
|   const _SnRealm._() : super._(); | ||||
|  | ||||
|   | ||||
| @@ -15,6 +15,7 @@ | ||||
| #include <media_kit_libs_linux/media_kit_libs_linux_plugin.h> | ||||
| #include <media_kit_video/media_kit_video_plugin.h> | ||||
| #include <pasteboard/pasteboard_plugin.h> | ||||
| #include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h> | ||||
| #include <tray_manager/tray_manager_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 = | ||||
|       fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin"); | ||||
|   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 = | ||||
|       fl_plugin_registry_get_registrar_for_plugin(registry, "TrayManagerPlugin"); | ||||
|   tray_manager_plugin_register_with_registrar(tray_manager_registrar); | ||||
|   | ||||
| @@ -12,6 +12,7 @@ list(APPEND FLUTTER_PLUGIN_LIST | ||||
|   media_kit_libs_linux | ||||
|   media_kit_video | ||||
|   pasteboard | ||||
|   sqlite3_flutter_libs | ||||
|   tray_manager | ||||
|   url_launcher_linux | ||||
| ) | ||||
|   | ||||
| @@ -30,6 +30,7 @@ import screen_brightness_macos | ||||
| import share_plus | ||||
| import shared_preferences_foundation | ||||
| import sqflite_darwin | ||||
| import sqlite3_flutter_libs | ||||
| import tray_manager | ||||
| import url_launcher_macos | ||||
| import video_compress | ||||
| @@ -61,6 +62,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { | ||||
|   SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) | ||||
|   SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) | ||||
|   SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) | ||||
|   Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) | ||||
|   TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin")) | ||||
|   UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) | ||||
|   VideoCompressPlugin.register(with: registry.registrar(forPlugin: "VideoCompressPlugin")) | ||||
|   | ||||
							
								
								
									
										88
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								pubspec.lock
									
									
									
									
									
								
							| @@ -230,6 +230,14 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.3.0" | ||||
|   charcode: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: charcode | ||||
|       sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.4.0" | ||||
|   checked_yaml: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -414,6 +422,30 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     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: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -914,30 +946,6 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     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: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -1642,6 +1650,14 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     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: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -1935,6 +1951,30 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     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: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|   | ||||
| @@ -72,8 +72,6 @@ dependencies: | ||||
|   collection: ^1.18.0 | ||||
|   mime: ^2.0.0 | ||||
|   web_socket_channel: ^3.0.1 | ||||
|   hive: ^2.2.3 | ||||
|   hive_flutter: ^1.1.0 | ||||
|   swipe_to: ^1.0.6 | ||||
|   firebase_core: ^3.8.0 | ||||
|   firebase_messaging: ^15.1.5 | ||||
| @@ -123,6 +121,8 @@ dependencies: | ||||
|   image_picker_android: ^0.8.12+20 | ||||
|   cached_network_image_platform_interface: ^4.1.1 | ||||
|   image_picker_platform_interface: ^2.10.1 | ||||
|   drift: ^2.25.1 | ||||
|   drift_flutter: ^0.2.4 | ||||
|  | ||||
| dev_dependencies: | ||||
|   flutter_test: | ||||
| @@ -134,13 +134,13 @@ dev_dependencies: | ||||
|   # package. See that file for information about deactivating specific lint | ||||
|   # rules and activating additional ones. | ||||
|   flutter_lints: ^5.0.0 | ||||
|   build_runner: ^2.4.13 | ||||
|   build_runner: ^2.4.15 | ||||
|   freezed: ^2.5.7 | ||||
|   json_serializable: ^6.8.0 | ||||
|   icons_launcher: ^3.0.0 | ||||
|   flutter_native_splash: ^2.4.2 | ||||
|   hive_generator: ^2.0.1 | ||||
|   flutter_launcher_icons: ^0.14.1 | ||||
|   drift_dev: ^2.25.2 | ||||
|  | ||||
| # For information on the generic Dart part of this file, see the | ||||
| # 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", | ||||
|     "short_name": "surface", | ||||
|     "name": "Solar Network", | ||||
|     "short_name": "Solian", | ||||
|     "start_url": ".", | ||||
|     "display": "standalone", | ||||
|     "background_color": "#ffffff", | ||||
|     "theme_color": "#ffffff", | ||||
|     "description": "A new Flutter project.", | ||||
|     "description": "The Solar Network is a social network app.", | ||||
|     "orientation": "portrait-primary", | ||||
|     "prefer_related_applications": false, | ||||
|     "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 <screen_brightness_windows/screen_brightness_windows_plugin.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 <url_launcher_windows/url_launcher_windows.h> | ||||
|  | ||||
| @@ -61,6 +62,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { | ||||
|       registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin")); | ||||
|   SharePlusWindowsPluginCApiRegisterWithRegistrar( | ||||
|       registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); | ||||
|   Sqlite3FlutterLibsPluginRegisterWithRegistrar( | ||||
|       registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin")); | ||||
|   TrayManagerPluginRegisterWithRegistrar( | ||||
|       registry->GetRegistrarForPlugin("TrayManagerPlugin")); | ||||
|   UrlLauncherWindowsRegisterWithRegistrar( | ||||
|   | ||||
| @@ -20,6 +20,7 @@ list(APPEND FLUTTER_PLUGIN_LIST | ||||
|   permission_handler_windows | ||||
|   screen_brightness_windows | ||||
|   share_plus | ||||
|   sqlite3_flutter_libs | ||||
|   tray_manager | ||||
|   url_launcher_windows | ||||
| ) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user