🔀 Merge pull request '♻️ 使用 Drift 作为本地数据库' (#3) from refactor/drift-as-local-db into master
Reviewed-on: #3
This commit was merged in pull request #3.
	This commit is contained in:
		@@ -51,6 +51,14 @@
 | 
			
		||||
                 to determine the Window background behind the Flutter UI. -->
 | 
			
		||||
            <meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
 | 
			
		||||
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.intent.action.VIEW" />
 | 
			
		||||
 | 
			
		||||
                <category android:name="android.intent.category.DEFAULT" />
 | 
			
		||||
                <category android:name="android.intent.category.BROWSABLE" />
 | 
			
		||||
                <data android:scheme="solink" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
            <intent-filter android:autoVerify="true">
 | 
			
		||||
                <action android:name="android.intent.action.VIEW" />
 | 
			
		||||
                <category android:name="android.intent.category.DEFAULT" />
 | 
			
		||||
@@ -61,14 +69,6 @@
 | 
			
		||||
                <data android:scheme="https" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.intent.action.VIEW" />
 | 
			
		||||
 | 
			
		||||
                <category android:name="android.intent.category.DEFAULT" />
 | 
			
		||||
                <category android:name="android.intent.category.BROWSABLE" />
 | 
			
		||||
                <data android:scheme="solink" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
            <meta-data
 | 
			
		||||
                android:name="io.flutter.embedding.android.NormalTheme"
 | 
			
		||||
                android:resource="@style/NormalTheme"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
description: This file stores settings for Dart & Flutter DevTools.
 | 
			
		||||
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
 | 
			
		||||
extensions:
 | 
			
		||||
  - provider: true
 | 
			
		||||
  - provider: true
 | 
			
		||||
  - drift: true
 | 
			
		||||
@@ -54,22 +54,22 @@ PODS:
 | 
			
		||||
  - Firebase/Performance (11.0.0):
 | 
			
		||||
    - Firebase/CoreOnly
 | 
			
		||||
    - FirebasePerformance (~> 11.0.0)
 | 
			
		||||
  - firebase_analytics (11.3.0):
 | 
			
		||||
  - firebase_analytics (11.3.1):
 | 
			
		||||
    - Firebase/Analytics (= 11.0.0)
 | 
			
		||||
    - firebase_core
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - firebase_core (3.4.1):
 | 
			
		||||
    - Firebase/CoreOnly (= 11.0.0)
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - firebase_crashlytics (4.1.0):
 | 
			
		||||
  - firebase_crashlytics (4.1.1):
 | 
			
		||||
    - Firebase/Crashlytics (= 11.0.0)
 | 
			
		||||
    - firebase_core
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - firebase_messaging (15.1.0):
 | 
			
		||||
  - firebase_messaging (15.1.1):
 | 
			
		||||
    - Firebase/Messaging (= 11.0.0)
 | 
			
		||||
    - firebase_core
 | 
			
		||||
    - Flutter
 | 
			
		||||
  - firebase_performance (0.10.0-5):
 | 
			
		||||
  - firebase_performance (0.10.0-6):
 | 
			
		||||
    - Firebase/Performance (= 11.0.0)
 | 
			
		||||
    - firebase_core
 | 
			
		||||
    - Flutter
 | 
			
		||||
@@ -264,6 +264,24 @@ PODS:
 | 
			
		||||
  - sqflite (0.0.3):
 | 
			
		||||
    - Flutter
 | 
			
		||||
    - FlutterMacOS
 | 
			
		||||
  - "sqlite3 (3.46.1+1)":
 | 
			
		||||
    - "sqlite3/common (= 3.46.1+1)"
 | 
			
		||||
  - "sqlite3/common (3.46.1+1)"
 | 
			
		||||
  - "sqlite3/dbstatvtab (3.46.1+1)":
 | 
			
		||||
    - sqlite3/common
 | 
			
		||||
  - "sqlite3/fts5 (3.46.1+1)":
 | 
			
		||||
    - sqlite3/common
 | 
			
		||||
  - "sqlite3/perf-threadsafe (3.46.1+1)":
 | 
			
		||||
    - sqlite3/common
 | 
			
		||||
  - "sqlite3/rtree (3.46.1+1)":
 | 
			
		||||
    - sqlite3/common
 | 
			
		||||
  - sqlite3_flutter_libs (0.0.1):
 | 
			
		||||
    - Flutter
 | 
			
		||||
    - "sqlite3 (~> 3.46.0+1)"
 | 
			
		||||
    - sqlite3/dbstatvtab
 | 
			
		||||
    - sqlite3/fts5
 | 
			
		||||
    - sqlite3/perf-threadsafe
 | 
			
		||||
    - sqlite3/rtree
 | 
			
		||||
  - SwiftyGif (5.4.5)
 | 
			
		||||
  - TOCropViewController (2.7.4)
 | 
			
		||||
  - url_launcher_ios (0.0.1):
 | 
			
		||||
@@ -305,6 +323,7 @@ DEPENDENCIES:
 | 
			
		||||
  - share_plus (from `.symlinks/plugins/share_plus/ios`)
 | 
			
		||||
  - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
 | 
			
		||||
  - sqflite (from `.symlinks/plugins/sqflite/darwin`)
 | 
			
		||||
  - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`)
 | 
			
		||||
  - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
 | 
			
		||||
  - volume_controller (from `.symlinks/plugins/volume_controller/ios`)
 | 
			
		||||
  - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
 | 
			
		||||
@@ -334,6 +353,7 @@ SPEC REPOS:
 | 
			
		||||
    - PromisesObjC
 | 
			
		||||
    - PromisesSwift
 | 
			
		||||
    - SDWebImage
 | 
			
		||||
    - sqlite3
 | 
			
		||||
    - SwiftyGif
 | 
			
		||||
    - TOCropViewController
 | 
			
		||||
    - WebRTC-SDK
 | 
			
		||||
@@ -399,6 +419,8 @@ EXTERNAL SOURCES:
 | 
			
		||||
    :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
 | 
			
		||||
  sqflite:
 | 
			
		||||
    :path: ".symlinks/plugins/sqflite/darwin"
 | 
			
		||||
  sqlite3_flutter_libs:
 | 
			
		||||
    :path: ".symlinks/plugins/sqlite3_flutter_libs/ios"
 | 
			
		||||
  url_launcher_ios:
 | 
			
		||||
    :path: ".symlinks/plugins/url_launcher_ios/ios"
 | 
			
		||||
  volume_controller:
 | 
			
		||||
@@ -413,11 +435,11 @@ SPEC CHECKSUMS:
 | 
			
		||||
  DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
 | 
			
		||||
  file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
 | 
			
		||||
  Firebase: 9f574c08c2396885b5e7e100ed4293d956218af9
 | 
			
		||||
  firebase_analytics: 1a66fe8d4375eccff44671ea37897683a78b2675
 | 
			
		||||
  firebase_analytics: b8ce6c2c4b245d3c3bb3a147965d09da0f455959
 | 
			
		||||
  firebase_core: ba84e940cf5cbbc601095f86556560937419195c
 | 
			
		||||
  firebase_crashlytics: e4f04180f443d5a8b56fbc0685bdbd7d90dd26f0
 | 
			
		||||
  firebase_messaging: 15d8b557010f3bb7b98d0302e1c7c8fbcd244425
 | 
			
		||||
  firebase_performance: d373c742649e2d85d92cc223b4511c3d132887ef
 | 
			
		||||
  firebase_crashlytics: 4111f8198b78c99471c955af488cecd8224967e6
 | 
			
		||||
  firebase_messaging: c40f84e7a98da956d5262fada373b5c458edcf13
 | 
			
		||||
  firebase_performance: 8b7b9ca5adf3a9b3afa12b4eb96b9cabefc2c248
 | 
			
		||||
  FirebaseABTesting: c2e22c3aab99afa81d0561708b2c1c356c556976
 | 
			
		||||
  FirebaseAnalytics: 27eb78b97880ea4a004839b9bac0b58880f5a92a
 | 
			
		||||
  FirebaseCore: 3cf438f431f18c12cdf2aaf64434648b63f7e383
 | 
			
		||||
@@ -460,6 +482,8 @@ SPEC CHECKSUMS:
 | 
			
		||||
  share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
 | 
			
		||||
  shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
 | 
			
		||||
  sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
 | 
			
		||||
  sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb
 | 
			
		||||
  sqlite3_flutter_libs: c00457ebd31e59fa6bb830380ddba24d44fbcd3b
 | 
			
		||||
  SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
 | 
			
		||||
  TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654
 | 
			
		||||
  url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
 | 
			
		||||
 
 | 
			
		||||
@@ -2,45 +2,32 @@ import 'package:get/get.dart';
 | 
			
		||||
import 'package:solian/models/channel.dart';
 | 
			
		||||
import 'package:solian/models/event.dart';
 | 
			
		||||
import 'package:solian/platform.dart';
 | 
			
		||||
import 'package:solian/providers/message/adaptor.dart';
 | 
			
		||||
import 'package:solian/providers/message/events.dart';
 | 
			
		||||
import 'package:solian/providers/database/database.dart';
 | 
			
		||||
import 'package:solian/providers/database/services/messages.dart';
 | 
			
		||||
 | 
			
		||||
class ChatEventController {
 | 
			
		||||
  late final MessageHistoryDb database;
 | 
			
		||||
  late final MessagesFetchingProvider src;
 | 
			
		||||
 | 
			
		||||
  final RxList<LocalEvent> currentEvents = RxList.empty(growable: true);
 | 
			
		||||
  final RxList<LocalMessageEventTableData> currentEvents =
 | 
			
		||||
      RxList.empty(growable: true);
 | 
			
		||||
  final RxInt totalEvents = 0.obs;
 | 
			
		||||
 | 
			
		||||
  final RxBool isLoading = false.obs;
 | 
			
		||||
  final RxBool isLoading = true.obs;
 | 
			
		||||
 | 
			
		||||
  Channel? channel;
 | 
			
		||||
  String? scope;
 | 
			
		||||
 | 
			
		||||
  Future<void> initialize() async {
 | 
			
		||||
    if (!PlatformInfo.isWeb) {
 | 
			
		||||
      database = await createHistoryDb();
 | 
			
		||||
    }
 | 
			
		||||
    src = Get.find();
 | 
			
		||||
    currentEvents.clear();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<LocalEvent?> getEvent(int id) async {
 | 
			
		||||
  Future<LocalMessageEventTableData?> getEvent(int id) async {
 | 
			
		||||
    if (channel == null || scope == null) return null;
 | 
			
		||||
 | 
			
		||||
    if (PlatformInfo.isWeb) {
 | 
			
		||||
      final remoteRecord = await getRemoteEvent(id, channel!, scope!);
 | 
			
		||||
      if (remoteRecord == null) return null;
 | 
			
		||||
      return LocalEvent(
 | 
			
		||||
        remoteRecord.id,
 | 
			
		||||
        remoteRecord,
 | 
			
		||||
        remoteRecord.channelId,
 | 
			
		||||
        remoteRecord.createdAt,
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      return await database.getEvent(id, channel!, scope: scope!);
 | 
			
		||||
    }
 | 
			
		||||
    return await src.getEvent(id, channel!, scope: scope!);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> getEvents(Channel channel, String scope) async {
 | 
			
		||||
  Future<void> getInitialEvents(Channel channel, String scope) async {
 | 
			
		||||
    this.channel = channel;
 | 
			
		||||
    this.scope = scope;
 | 
			
		||||
 | 
			
		||||
@@ -48,24 +35,30 @@ class ChatEventController {
 | 
			
		||||
 | 
			
		||||
    isLoading.value = true;
 | 
			
		||||
    if (PlatformInfo.isWeb) {
 | 
			
		||||
      final result = await getRemoteEvents(
 | 
			
		||||
      final result = await src.fetchRemoteEvents(
 | 
			
		||||
        channel,
 | 
			
		||||
        scope,
 | 
			
		||||
        remainDepth: 3,
 | 
			
		||||
        depth: 1,
 | 
			
		||||
        offset: 0,
 | 
			
		||||
      );
 | 
			
		||||
      totalEvents.value = result?.$2 ?? 0;
 | 
			
		||||
      if (result != null) {
 | 
			
		||||
        for (final x in result.$1.reversed) {
 | 
			
		||||
          final entry = LocalEvent(x.id, x, x.channelId, x.createdAt);
 | 
			
		||||
          final entry = LocalMessageEventTableData(
 | 
			
		||||
            id: x.id,
 | 
			
		||||
            channelId: x.channelId,
 | 
			
		||||
            createdAt: x.createdAt,
 | 
			
		||||
            data: x,
 | 
			
		||||
          );
 | 
			
		||||
          insertEvent(entry);
 | 
			
		||||
          applyEvent(entry);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      final result = await database.syncRemoteEvents(
 | 
			
		||||
      final result = await src.pullRemoteEvents(
 | 
			
		||||
        channel,
 | 
			
		||||
        scope: scope,
 | 
			
		||||
        depth: 1,
 | 
			
		||||
      );
 | 
			
		||||
      totalEvents.value = result?.$2 ?? 0;
 | 
			
		||||
      await syncLocal(channel);
 | 
			
		||||
@@ -76,22 +69,27 @@ class ChatEventController {
 | 
			
		||||
  Future<void> loadEvents(Channel channel, String scope) async {
 | 
			
		||||
    isLoading.value = true;
 | 
			
		||||
    if (PlatformInfo.isWeb) {
 | 
			
		||||
      final result = await getRemoteEvents(
 | 
			
		||||
      final result = await src.fetchRemoteEvents(
 | 
			
		||||
        channel,
 | 
			
		||||
        scope,
 | 
			
		||||
        remainDepth: 3,
 | 
			
		||||
        depth: 3,
 | 
			
		||||
        offset: currentEvents.length,
 | 
			
		||||
      );
 | 
			
		||||
      if (result != null) {
 | 
			
		||||
        totalEvents.value = result.$2;
 | 
			
		||||
        for (final x in result.$1.reversed) {
 | 
			
		||||
          final entry = LocalEvent(x.id, x, x.channelId, x.createdAt);
 | 
			
		||||
          final entry = LocalMessageEventTableData(
 | 
			
		||||
            id: x.id,
 | 
			
		||||
            channelId: x.channelId,
 | 
			
		||||
            createdAt: x.createdAt,
 | 
			
		||||
            data: x,
 | 
			
		||||
          );
 | 
			
		||||
          currentEvents.add(entry);
 | 
			
		||||
          applyEvent(entry);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      final result = await database.syncRemoteEvents(
 | 
			
		||||
      final result = await src.pullRemoteEvents(
 | 
			
		||||
        channel,
 | 
			
		||||
        depth: 3,
 | 
			
		||||
        scope: scope,
 | 
			
		||||
@@ -105,7 +103,7 @@ class ChatEventController {
 | 
			
		||||
 | 
			
		||||
  Future<bool> syncLocal(Channel channel) async {
 | 
			
		||||
    if (PlatformInfo.isWeb) return false;
 | 
			
		||||
    final data = await database.localEvents.findAllByChannel(channel.id);
 | 
			
		||||
    final data = await src.listEvents(channel);
 | 
			
		||||
    currentEvents.replaceRange(0, currentEvents.length, data);
 | 
			
		||||
    for (final x in data.reversed) {
 | 
			
		||||
      applyEvent(x);
 | 
			
		||||
@@ -114,26 +112,29 @@ class ChatEventController {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  receiveEvent(Event remote) async {
 | 
			
		||||
    LocalEvent entry;
 | 
			
		||||
    LocalMessageEventTableData entry;
 | 
			
		||||
    if (PlatformInfo.isWeb) {
 | 
			
		||||
      entry = LocalEvent(
 | 
			
		||||
        remote.id,
 | 
			
		||||
        remote,
 | 
			
		||||
        remote.channelId,
 | 
			
		||||
        remote.createdAt,
 | 
			
		||||
      entry = LocalMessageEventTableData(
 | 
			
		||||
        id: remote.id,
 | 
			
		||||
        channelId: remote.channelId,
 | 
			
		||||
        createdAt: remote.createdAt,
 | 
			
		||||
        data: remote,
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      entry = await database.receiveEvent(remote);
 | 
			
		||||
      entry = await src.receiveEvent(remote);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    totalEvents.value++;
 | 
			
		||||
    insertEvent(entry);
 | 
			
		||||
    applyEvent(entry);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  insertEvent(LocalEvent entry) {
 | 
			
		||||
  void insertEvent(LocalMessageEventTableData entry) {
 | 
			
		||||
    if (entry.channelId != channel?.id) return;
 | 
			
		||||
 | 
			
		||||
    final idx = currentEvents.indexWhere((x) => x.data.uuid == entry.data.uuid);
 | 
			
		||||
    final idx = currentEvents.indexWhere(
 | 
			
		||||
      (x) => x.data!.uuid == entry.data!.uuid,
 | 
			
		||||
    );
 | 
			
		||||
    if (idx != -1) {
 | 
			
		||||
      currentEvents[idx] = entry;
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -141,36 +142,36 @@ class ChatEventController {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  applyEvent(LocalEvent entry) {
 | 
			
		||||
  void applyEvent(LocalMessageEventTableData entry) {
 | 
			
		||||
    if (entry.channelId != channel?.id) return;
 | 
			
		||||
 | 
			
		||||
    switch (entry.data.type) {
 | 
			
		||||
    switch (entry.data!.type) {
 | 
			
		||||
      case 'messages.edit':
 | 
			
		||||
        final body = EventMessageBody.fromJson(entry.data.body);
 | 
			
		||||
        final body = EventMessageBody.fromJson(entry.data!.body);
 | 
			
		||||
        if (body.relatedEvent != null) {
 | 
			
		||||
          final idx =
 | 
			
		||||
              currentEvents.indexWhere((x) => x.data.id == body.relatedEvent);
 | 
			
		||||
              currentEvents.indexWhere((x) => x.data!.id == body.relatedEvent);
 | 
			
		||||
          if (idx != -1) {
 | 
			
		||||
            currentEvents[idx].data.body = entry.data.body;
 | 
			
		||||
            currentEvents[idx].data.updatedAt = entry.data.updatedAt;
 | 
			
		||||
            currentEvents[idx].data!.body = entry.data!.body;
 | 
			
		||||
            currentEvents[idx].data!.updatedAt = entry.data!.updatedAt;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      case 'messages.delete':
 | 
			
		||||
        final body = EventMessageBody.fromJson(entry.data.body);
 | 
			
		||||
        final body = EventMessageBody.fromJson(entry.data!.body);
 | 
			
		||||
        if (body.relatedEvent != null) {
 | 
			
		||||
          currentEvents.removeWhere((x) => x.id == body.relatedEvent);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addPendingEvent(Event info) async {
 | 
			
		||||
  Future<void> addPendingEvent(Event info) async {
 | 
			
		||||
    currentEvents.insert(
 | 
			
		||||
      0,
 | 
			
		||||
      LocalEvent(
 | 
			
		||||
        info.id,
 | 
			
		||||
        info,
 | 
			
		||||
        info.channelId,
 | 
			
		||||
        DateTime.now(),
 | 
			
		||||
      LocalMessageEventTableData(
 | 
			
		||||
        id: info.id,
 | 
			
		||||
        channelId: info.channelId,
 | 
			
		||||
        createdAt: DateTime.now(),
 | 
			
		||||
        data: info,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,8 @@ import 'package:solian/firebase_options.dart';
 | 
			
		||||
import 'package:solian/platform.dart';
 | 
			
		||||
import 'package:solian/providers/attachment_uploader.dart';
 | 
			
		||||
import 'package:solian/providers/daily_sign.dart';
 | 
			
		||||
import 'package:solian/providers/database/database.dart';
 | 
			
		||||
import 'package:solian/providers/database/services/messages.dart';
 | 
			
		||||
import 'package:solian/providers/last_read.dart';
 | 
			
		||||
import 'package:solian/providers/link_expander.dart';
 | 
			
		||||
import 'package:solian/providers/navigation.dart';
 | 
			
		||||
@@ -43,6 +45,7 @@ void main() async {
 | 
			
		||||
 | 
			
		||||
  GoRouter.optionURLReflectsImperativeAPIs = true;
 | 
			
		||||
 | 
			
		||||
  Get.put(DatabaseProvider());
 | 
			
		||||
  Get.put(AppTranslations());
 | 
			
		||||
  await AppTranslations.init();
 | 
			
		||||
 | 
			
		||||
@@ -135,6 +138,7 @@ class SolianApp extends StatelessWidget {
 | 
			
		||||
    Get.lazyPut(() => StatusProvider());
 | 
			
		||||
    Get.lazyPut(() => ChannelProvider());
 | 
			
		||||
    Get.lazyPut(() => RealmProvider());
 | 
			
		||||
    Get.lazyPut(() => MessagesFetchingProvider());
 | 
			
		||||
    Get.lazyPut(() => ChatCallProvider());
 | 
			
		||||
    Get.lazyPut(() => AttachmentUploaderController());
 | 
			
		||||
    Get.lazyPut(() => LinkExpandProvider());
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@ import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
 | 
			
		||||
import 'package:get/get.dart';
 | 
			
		||||
import 'package:get/get_connect/http/src/request/request.dart';
 | 
			
		||||
import 'package:solian/controllers/chat_events_controller.dart';
 | 
			
		||||
import 'package:solian/exceptions/request.dart';
 | 
			
		||||
import 'package:solian/exceptions/unauthorized.dart';
 | 
			
		||||
import 'package:solian/providers/websocket.dart';
 | 
			
		||||
@@ -199,11 +198,6 @@ class AuthProvider extends GetConnect {
 | 
			
		||||
    Get.find<WebSocketProvider>().notifications.clear();
 | 
			
		||||
    Get.find<WebSocketProvider>().notificationUnread.value = 0;
 | 
			
		||||
 | 
			
		||||
    final chatHistory = ChatEventController();
 | 
			
		||||
    chatHistory.initialize().then((_) async {
 | 
			
		||||
      await chatHistory.database.localEvents.wipeLocalEvents();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    storage.deleteAll();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								lib/providers/database/database.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								lib/providers/database/database.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
import 'package:drift/drift.dart';
 | 
			
		||||
import 'package:drift_flutter/drift_flutter.dart';
 | 
			
		||||
import 'package:get/get.dart' hide Value;
 | 
			
		||||
import 'package:solian/providers/database/tables/messages.dart';
 | 
			
		||||
 | 
			
		||||
import 'package:solian/models/event.dart';
 | 
			
		||||
 | 
			
		||||
part 'database.g.dart';
 | 
			
		||||
 | 
			
		||||
@DriftDatabase(tables: [LocalMessageEventTable])
 | 
			
		||||
class AppDatabase extends _$AppDatabase {
 | 
			
		||||
  AppDatabase() : super(_openConnection());
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  int get schemaVersion => 1;
 | 
			
		||||
 | 
			
		||||
  static QueryExecutor _openConnection() {
 | 
			
		||||
    return driftDatabase(name: 'solar_network_local_db');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class DatabaseProvider extends GetxController {
 | 
			
		||||
  final database = AppDatabase();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										429
									
								
								lib/providers/database/database.g.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										429
									
								
								lib/providers/database/database.g.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,429 @@
 | 
			
		||||
// GENERATED CODE - DO NOT MODIFY BY HAND
 | 
			
		||||
 | 
			
		||||
part of 'database.dart';
 | 
			
		||||
 | 
			
		||||
// ignore_for_file: type=lint
 | 
			
		||||
class $LocalMessageEventTableTable extends LocalMessageEventTable
 | 
			
		||||
    with TableInfo<$LocalMessageEventTableTable, LocalMessageEventTableData> {
 | 
			
		||||
  @override
 | 
			
		||||
  final GeneratedDatabase attachedDatabase;
 | 
			
		||||
  final String? _alias;
 | 
			
		||||
  $LocalMessageEventTableTable(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 _dataMeta = const VerificationMeta('data');
 | 
			
		||||
  @override
 | 
			
		||||
  late final GeneratedColumnWithTypeConverter<Event?, String> data =
 | 
			
		||||
      GeneratedColumn<String>('data', aliasedName, false,
 | 
			
		||||
              type: DriftSqlType.string, requiredDuringInsert: true)
 | 
			
		||||
          .withConverter<Event?>($LocalMessageEventTableTable.$converterdata);
 | 
			
		||||
  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: Constant(DateTime.now()));
 | 
			
		||||
  @override
 | 
			
		||||
  List<GeneratedColumn> get $columns => [id, channelId, data, createdAt];
 | 
			
		||||
  @override
 | 
			
		||||
  String get aliasedName => _alias ?? actualTableName;
 | 
			
		||||
  @override
 | 
			
		||||
  String get actualTableName => $name;
 | 
			
		||||
  static const String $name = 'local_message_event_table';
 | 
			
		||||
  @override
 | 
			
		||||
  VerificationContext validateIntegrity(
 | 
			
		||||
      Insertable<LocalMessageEventTableData> 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(_dataMeta, 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
 | 
			
		||||
  LocalMessageEventTableData map(Map<String, dynamic> data,
 | 
			
		||||
      {String? tablePrefix}) {
 | 
			
		||||
    final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
 | 
			
		||||
    return LocalMessageEventTableData(
 | 
			
		||||
      id: attachedDatabase.typeMapping
 | 
			
		||||
          .read(DriftSqlType.int, data['${effectivePrefix}id'])!,
 | 
			
		||||
      channelId: attachedDatabase.typeMapping
 | 
			
		||||
          .read(DriftSqlType.int, data['${effectivePrefix}channel_id'])!,
 | 
			
		||||
      data: $LocalMessageEventTableTable.$converterdata.fromSql(attachedDatabase
 | 
			
		||||
          .typeMapping
 | 
			
		||||
          .read(DriftSqlType.string, data['${effectivePrefix}data'])!),
 | 
			
		||||
      createdAt: attachedDatabase.typeMapping
 | 
			
		||||
          .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  $LocalMessageEventTableTable createAlias(String alias) {
 | 
			
		||||
    return $LocalMessageEventTableTable(attachedDatabase, alias);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static TypeConverter<Event?, String> $converterdata =
 | 
			
		||||
      const MessageEventConverter();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class LocalMessageEventTableData extends DataClass
 | 
			
		||||
    implements Insertable<LocalMessageEventTableData> {
 | 
			
		||||
  final int id;
 | 
			
		||||
  final int channelId;
 | 
			
		||||
  final Event? data;
 | 
			
		||||
  final DateTime createdAt;
 | 
			
		||||
  const LocalMessageEventTableData(
 | 
			
		||||
      {required this.id,
 | 
			
		||||
      required this.channelId,
 | 
			
		||||
      this.data,
 | 
			
		||||
      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);
 | 
			
		||||
    if (!nullToAbsent || data != null) {
 | 
			
		||||
      map['data'] = Variable<String>(
 | 
			
		||||
          $LocalMessageEventTableTable.$converterdata.toSql(data));
 | 
			
		||||
    }
 | 
			
		||||
    map['created_at'] = Variable<DateTime>(createdAt);
 | 
			
		||||
    return map;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  LocalMessageEventTableCompanion toCompanion(bool nullToAbsent) {
 | 
			
		||||
    return LocalMessageEventTableCompanion(
 | 
			
		||||
      id: Value(id),
 | 
			
		||||
      channelId: Value(channelId),
 | 
			
		||||
      data: data == null && nullToAbsent ? const Value.absent() : Value(data),
 | 
			
		||||
      createdAt: Value(createdAt),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  factory LocalMessageEventTableData.fromJson(Map<String, dynamic> json,
 | 
			
		||||
      {ValueSerializer? serializer}) {
 | 
			
		||||
    serializer ??= driftRuntimeOptions.defaultSerializer;
 | 
			
		||||
    return LocalMessageEventTableData(
 | 
			
		||||
      id: serializer.fromJson<int>(json['id']),
 | 
			
		||||
      channelId: serializer.fromJson<int>(json['channelId']),
 | 
			
		||||
      data: serializer.fromJson<Event?>(json['data']),
 | 
			
		||||
      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),
 | 
			
		||||
      'data': serializer.toJson<Event?>(data),
 | 
			
		||||
      'createdAt': serializer.toJson<DateTime>(createdAt),
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  LocalMessageEventTableData copyWith(
 | 
			
		||||
          {int? id,
 | 
			
		||||
          int? channelId,
 | 
			
		||||
          Value<Event?> data = const Value.absent(),
 | 
			
		||||
          DateTime? createdAt}) =>
 | 
			
		||||
      LocalMessageEventTableData(
 | 
			
		||||
        id: id ?? this.id,
 | 
			
		||||
        channelId: channelId ?? this.channelId,
 | 
			
		||||
        data: data.present ? data.value : this.data,
 | 
			
		||||
        createdAt: createdAt ?? this.createdAt,
 | 
			
		||||
      );
 | 
			
		||||
  LocalMessageEventTableData copyWithCompanion(
 | 
			
		||||
      LocalMessageEventTableCompanion data) {
 | 
			
		||||
    return LocalMessageEventTableData(
 | 
			
		||||
      id: data.id.present ? data.id.value : this.id,
 | 
			
		||||
      channelId: data.channelId.present ? data.channelId.value : this.channelId,
 | 
			
		||||
      data: data.data.present ? data.data.value : this.data,
 | 
			
		||||
      createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String toString() {
 | 
			
		||||
    return (StringBuffer('LocalMessageEventTableData(')
 | 
			
		||||
          ..write('id: $id, ')
 | 
			
		||||
          ..write('channelId: $channelId, ')
 | 
			
		||||
          ..write('data: $data, ')
 | 
			
		||||
          ..write('createdAt: $createdAt')
 | 
			
		||||
          ..write(')'))
 | 
			
		||||
        .toString();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  int get hashCode => Object.hash(id, channelId, data, createdAt);
 | 
			
		||||
  @override
 | 
			
		||||
  bool operator ==(Object other) =>
 | 
			
		||||
      identical(this, other) ||
 | 
			
		||||
      (other is LocalMessageEventTableData &&
 | 
			
		||||
          other.id == this.id &&
 | 
			
		||||
          other.channelId == this.channelId &&
 | 
			
		||||
          other.data == this.data &&
 | 
			
		||||
          other.createdAt == this.createdAt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class LocalMessageEventTableCompanion
 | 
			
		||||
    extends UpdateCompanion<LocalMessageEventTableData> {
 | 
			
		||||
  final Value<int> id;
 | 
			
		||||
  final Value<int> channelId;
 | 
			
		||||
  final Value<Event?> data;
 | 
			
		||||
  final Value<DateTime> createdAt;
 | 
			
		||||
  const LocalMessageEventTableCompanion({
 | 
			
		||||
    this.id = const Value.absent(),
 | 
			
		||||
    this.channelId = const Value.absent(),
 | 
			
		||||
    this.data = const Value.absent(),
 | 
			
		||||
    this.createdAt = const Value.absent(),
 | 
			
		||||
  });
 | 
			
		||||
  LocalMessageEventTableCompanion.insert({
 | 
			
		||||
    this.id = const Value.absent(),
 | 
			
		||||
    required int channelId,
 | 
			
		||||
    required Event? data,
 | 
			
		||||
    this.createdAt = const Value.absent(),
 | 
			
		||||
  })  : channelId = Value(channelId),
 | 
			
		||||
        data = Value(data);
 | 
			
		||||
  static Insertable<LocalMessageEventTableData> custom({
 | 
			
		||||
    Expression<int>? id,
 | 
			
		||||
    Expression<int>? channelId,
 | 
			
		||||
    Expression<String>? data,
 | 
			
		||||
    Expression<DateTime>? createdAt,
 | 
			
		||||
  }) {
 | 
			
		||||
    return RawValuesInsertable({
 | 
			
		||||
      if (id != null) 'id': id,
 | 
			
		||||
      if (channelId != null) 'channel_id': channelId,
 | 
			
		||||
      if (data != null) 'data': data,
 | 
			
		||||
      if (createdAt != null) 'created_at': createdAt,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  LocalMessageEventTableCompanion copyWith(
 | 
			
		||||
      {Value<int>? id,
 | 
			
		||||
      Value<int>? channelId,
 | 
			
		||||
      Value<Event?>? data,
 | 
			
		||||
      Value<DateTime>? createdAt}) {
 | 
			
		||||
    return LocalMessageEventTableCompanion(
 | 
			
		||||
      id: id ?? this.id,
 | 
			
		||||
      channelId: channelId ?? this.channelId,
 | 
			
		||||
      data: data ?? this.data,
 | 
			
		||||
      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 (data.present) {
 | 
			
		||||
      map['data'] = Variable<String>(
 | 
			
		||||
          $LocalMessageEventTableTable.$converterdata.toSql(data.value));
 | 
			
		||||
    }
 | 
			
		||||
    if (createdAt.present) {
 | 
			
		||||
      map['created_at'] = Variable<DateTime>(createdAt.value);
 | 
			
		||||
    }
 | 
			
		||||
    return map;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String toString() {
 | 
			
		||||
    return (StringBuffer('LocalMessageEventTableCompanion(')
 | 
			
		||||
          ..write('id: $id, ')
 | 
			
		||||
          ..write('channelId: $channelId, ')
 | 
			
		||||
          ..write('data: $data, ')
 | 
			
		||||
          ..write('createdAt: $createdAt')
 | 
			
		||||
          ..write(')'))
 | 
			
		||||
        .toString();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
abstract class _$AppDatabase extends GeneratedDatabase {
 | 
			
		||||
  _$AppDatabase(QueryExecutor e) : super(e);
 | 
			
		||||
  $AppDatabaseManager get managers => $AppDatabaseManager(this);
 | 
			
		||||
  late final $LocalMessageEventTableTable localMessageEventTable =
 | 
			
		||||
      $LocalMessageEventTableTable(this);
 | 
			
		||||
  @override
 | 
			
		||||
  Iterable<TableInfo<Table, Object?>> get allTables =>
 | 
			
		||||
      allSchemaEntities.whereType<TableInfo<Table, Object?>>();
 | 
			
		||||
  @override
 | 
			
		||||
  List<DatabaseSchemaEntity> get allSchemaEntities => [localMessageEventTable];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef $$LocalMessageEventTableTableCreateCompanionBuilder
 | 
			
		||||
    = LocalMessageEventTableCompanion Function({
 | 
			
		||||
  Value<int> id,
 | 
			
		||||
  required int channelId,
 | 
			
		||||
  required Event? data,
 | 
			
		||||
  Value<DateTime> createdAt,
 | 
			
		||||
});
 | 
			
		||||
typedef $$LocalMessageEventTableTableUpdateCompanionBuilder
 | 
			
		||||
    = LocalMessageEventTableCompanion Function({
 | 
			
		||||
  Value<int> id,
 | 
			
		||||
  Value<int> channelId,
 | 
			
		||||
  Value<Event?> data,
 | 
			
		||||
  Value<DateTime> createdAt,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
class $$LocalMessageEventTableTableFilterComposer
 | 
			
		||||
    extends FilterComposer<_$AppDatabase, $LocalMessageEventTableTable> {
 | 
			
		||||
  $$LocalMessageEventTableTableFilterComposer(super.$state);
 | 
			
		||||
  ColumnFilters<int> get id => $state.composableBuilder(
 | 
			
		||||
      column: $state.table.id,
 | 
			
		||||
      builder: (column, joinBuilders) =>
 | 
			
		||||
          ColumnFilters(column, joinBuilders: joinBuilders));
 | 
			
		||||
 | 
			
		||||
  ColumnFilters<int> get channelId => $state.composableBuilder(
 | 
			
		||||
      column: $state.table.channelId,
 | 
			
		||||
      builder: (column, joinBuilders) =>
 | 
			
		||||
          ColumnFilters(column, joinBuilders: joinBuilders));
 | 
			
		||||
 | 
			
		||||
  ColumnWithTypeConverterFilters<Event?, Event, String> get data =>
 | 
			
		||||
      $state.composableBuilder(
 | 
			
		||||
          column: $state.table.data,
 | 
			
		||||
          builder: (column, joinBuilders) => ColumnWithTypeConverterFilters(
 | 
			
		||||
              column,
 | 
			
		||||
              joinBuilders: joinBuilders));
 | 
			
		||||
 | 
			
		||||
  ColumnFilters<DateTime> get createdAt => $state.composableBuilder(
 | 
			
		||||
      column: $state.table.createdAt,
 | 
			
		||||
      builder: (column, joinBuilders) =>
 | 
			
		||||
          ColumnFilters(column, joinBuilders: joinBuilders));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class $$LocalMessageEventTableTableOrderingComposer
 | 
			
		||||
    extends OrderingComposer<_$AppDatabase, $LocalMessageEventTableTable> {
 | 
			
		||||
  $$LocalMessageEventTableTableOrderingComposer(super.$state);
 | 
			
		||||
  ColumnOrderings<int> get id => $state.composableBuilder(
 | 
			
		||||
      column: $state.table.id,
 | 
			
		||||
      builder: (column, joinBuilders) =>
 | 
			
		||||
          ColumnOrderings(column, joinBuilders: joinBuilders));
 | 
			
		||||
 | 
			
		||||
  ColumnOrderings<int> get channelId => $state.composableBuilder(
 | 
			
		||||
      column: $state.table.channelId,
 | 
			
		||||
      builder: (column, joinBuilders) =>
 | 
			
		||||
          ColumnOrderings(column, joinBuilders: joinBuilders));
 | 
			
		||||
 | 
			
		||||
  ColumnOrderings<String> get data => $state.composableBuilder(
 | 
			
		||||
      column: $state.table.data,
 | 
			
		||||
      builder: (column, joinBuilders) =>
 | 
			
		||||
          ColumnOrderings(column, joinBuilders: joinBuilders));
 | 
			
		||||
 | 
			
		||||
  ColumnOrderings<DateTime> get createdAt => $state.composableBuilder(
 | 
			
		||||
      column: $state.table.createdAt,
 | 
			
		||||
      builder: (column, joinBuilders) =>
 | 
			
		||||
          ColumnOrderings(column, joinBuilders: joinBuilders));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class $$LocalMessageEventTableTableTableManager extends RootTableManager<
 | 
			
		||||
    _$AppDatabase,
 | 
			
		||||
    $LocalMessageEventTableTable,
 | 
			
		||||
    LocalMessageEventTableData,
 | 
			
		||||
    $$LocalMessageEventTableTableFilterComposer,
 | 
			
		||||
    $$LocalMessageEventTableTableOrderingComposer,
 | 
			
		||||
    $$LocalMessageEventTableTableCreateCompanionBuilder,
 | 
			
		||||
    $$LocalMessageEventTableTableUpdateCompanionBuilder,
 | 
			
		||||
    (
 | 
			
		||||
      LocalMessageEventTableData,
 | 
			
		||||
      BaseReferences<_$AppDatabase, $LocalMessageEventTableTable,
 | 
			
		||||
          LocalMessageEventTableData>
 | 
			
		||||
    ),
 | 
			
		||||
    LocalMessageEventTableData,
 | 
			
		||||
    PrefetchHooks Function()> {
 | 
			
		||||
  $$LocalMessageEventTableTableTableManager(
 | 
			
		||||
      _$AppDatabase db, $LocalMessageEventTableTable table)
 | 
			
		||||
      : super(TableManagerState(
 | 
			
		||||
          db: db,
 | 
			
		||||
          table: table,
 | 
			
		||||
          filteringComposer: $$LocalMessageEventTableTableFilterComposer(
 | 
			
		||||
              ComposerState(db, table)),
 | 
			
		||||
          orderingComposer: $$LocalMessageEventTableTableOrderingComposer(
 | 
			
		||||
              ComposerState(db, table)),
 | 
			
		||||
          updateCompanionCallback: ({
 | 
			
		||||
            Value<int> id = const Value.absent(),
 | 
			
		||||
            Value<int> channelId = const Value.absent(),
 | 
			
		||||
            Value<Event?> data = const Value.absent(),
 | 
			
		||||
            Value<DateTime> createdAt = const Value.absent(),
 | 
			
		||||
          }) =>
 | 
			
		||||
              LocalMessageEventTableCompanion(
 | 
			
		||||
            id: id,
 | 
			
		||||
            channelId: channelId,
 | 
			
		||||
            data: data,
 | 
			
		||||
            createdAt: createdAt,
 | 
			
		||||
          ),
 | 
			
		||||
          createCompanionCallback: ({
 | 
			
		||||
            Value<int> id = const Value.absent(),
 | 
			
		||||
            required int channelId,
 | 
			
		||||
            required Event? data,
 | 
			
		||||
            Value<DateTime> createdAt = const Value.absent(),
 | 
			
		||||
          }) =>
 | 
			
		||||
              LocalMessageEventTableCompanion.insert(
 | 
			
		||||
            id: id,
 | 
			
		||||
            channelId: channelId,
 | 
			
		||||
            data: data,
 | 
			
		||||
            createdAt: createdAt,
 | 
			
		||||
          ),
 | 
			
		||||
          withReferenceMapper: (p0) => p0
 | 
			
		||||
              .map((e) => (e.readTable(table), BaseReferences(db, table, e)))
 | 
			
		||||
              .toList(),
 | 
			
		||||
          prefetchHooksCallback: null,
 | 
			
		||||
        ));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef $$LocalMessageEventTableTableProcessedTableManager
 | 
			
		||||
    = ProcessedTableManager<
 | 
			
		||||
        _$AppDatabase,
 | 
			
		||||
        $LocalMessageEventTableTable,
 | 
			
		||||
        LocalMessageEventTableData,
 | 
			
		||||
        $$LocalMessageEventTableTableFilterComposer,
 | 
			
		||||
        $$LocalMessageEventTableTableOrderingComposer,
 | 
			
		||||
        $$LocalMessageEventTableTableCreateCompanionBuilder,
 | 
			
		||||
        $$LocalMessageEventTableTableUpdateCompanionBuilder,
 | 
			
		||||
        (
 | 
			
		||||
          LocalMessageEventTableData,
 | 
			
		||||
          BaseReferences<_$AppDatabase, $LocalMessageEventTableTable,
 | 
			
		||||
              LocalMessageEventTableData>
 | 
			
		||||
        ),
 | 
			
		||||
        LocalMessageEventTableData,
 | 
			
		||||
        PrefetchHooks Function()>;
 | 
			
		||||
 | 
			
		||||
class $AppDatabaseManager {
 | 
			
		||||
  final _$AppDatabase _db;
 | 
			
		||||
  $AppDatabaseManager(this._db);
 | 
			
		||||
  $$LocalMessageEventTableTableTableManager get localMessageEventTable =>
 | 
			
		||||
      $$LocalMessageEventTableTableTableManager(
 | 
			
		||||
          _db, _db.localMessageEventTable);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										203
									
								
								lib/providers/database/services/messages.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								lib/providers/database/services/messages.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,203 @@
 | 
			
		||||
import 'package:drift/drift.dart';
 | 
			
		||||
import 'package:get/get.dart' hide Value;
 | 
			
		||||
import 'package:solian/exceptions/request.dart';
 | 
			
		||||
import 'package:solian/models/channel.dart';
 | 
			
		||||
import 'package:solian/models/event.dart';
 | 
			
		||||
import 'package:solian/models/pagination.dart';
 | 
			
		||||
import 'package:solian/providers/auth.dart';
 | 
			
		||||
import 'package:solian/providers/database/database.dart';
 | 
			
		||||
 | 
			
		||||
class MessagesFetchingProvider extends GetxController {
 | 
			
		||||
  Future<(List<Event>, int)?> getWhatsNewEvents(int pivot, {take = 10}) async {
 | 
			
		||||
    final AuthProvider auth = Get.find();
 | 
			
		||||
    if (auth.isAuthorized.isFalse) return null;
 | 
			
		||||
 | 
			
		||||
    final client = auth.configureClient('messaging');
 | 
			
		||||
 | 
			
		||||
    final resp = await client.get(
 | 
			
		||||
      '/whats-new?pivot=$pivot&take=$take',
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (resp.statusCode != 200) {
 | 
			
		||||
      throw RequestException(resp);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final PaginationResult response = PaginationResult.fromJson(resp.body);
 | 
			
		||||
    final result =
 | 
			
		||||
        response.data?.map((e) => Event.fromJson(e)).toList() ?? List.empty();
 | 
			
		||||
 | 
			
		||||
    return (result, response.count);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<Event?> fetchRemoteEvent(int id, Channel channel, String scope) async {
 | 
			
		||||
    final AuthProvider auth = Get.find();
 | 
			
		||||
    if (auth.isAuthorized.isFalse) return null;
 | 
			
		||||
 | 
			
		||||
    final client = auth.configureClient('messaging');
 | 
			
		||||
 | 
			
		||||
    final resp = await client.get(
 | 
			
		||||
      '/channels/$scope/${channel.alias}/events/$id',
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (resp.statusCode == 404) {
 | 
			
		||||
      return null;
 | 
			
		||||
    } else if (resp.statusCode != 200) {
 | 
			
		||||
      throw RequestException(resp);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return Event.fromJson(resp.body);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<(List<Event>, int)?> fetchRemoteEvents(
 | 
			
		||||
    Channel channel,
 | 
			
		||||
    String scope, {
 | 
			
		||||
    required int depth,
 | 
			
		||||
    bool Function(List<Event> items)? onBrake,
 | 
			
		||||
    take = 10,
 | 
			
		||||
    offset = 0,
 | 
			
		||||
  }) async {
 | 
			
		||||
    if (depth <= 0) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final AuthProvider auth = Get.find();
 | 
			
		||||
    if (auth.isAuthorized.isFalse) return null;
 | 
			
		||||
 | 
			
		||||
    final client = auth.configureClient('messaging');
 | 
			
		||||
 | 
			
		||||
    final resp = await client.get(
 | 
			
		||||
      '/channels/$scope/${channel.alias}/events?take=$take&offset=$offset',
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (resp.statusCode != 200) {
 | 
			
		||||
      throw RequestException(resp);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final PaginationResult response = PaginationResult.fromJson(resp.body);
 | 
			
		||||
    final result =
 | 
			
		||||
        response.data?.map((e) => Event.fromJson(e)).toList() ?? List.empty();
 | 
			
		||||
 | 
			
		||||
    if (onBrake != null && onBrake(result)) {
 | 
			
		||||
      return (result, response.count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final expandResult = (await fetchRemoteEvents(
 | 
			
		||||
          channel,
 | 
			
		||||
          scope,
 | 
			
		||||
          depth: depth - 1,
 | 
			
		||||
          take: take,
 | 
			
		||||
          offset: offset + result.length,
 | 
			
		||||
        ))
 | 
			
		||||
            ?.$1 ??
 | 
			
		||||
        List.empty();
 | 
			
		||||
 | 
			
		||||
    return ([...result, ...expandResult], response.count);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<LocalMessageEventTableData> receiveEvent(Event remote) async {
 | 
			
		||||
    // Insert record
 | 
			
		||||
    final database = Get.find<DatabaseProvider>().database;
 | 
			
		||||
    final entry = await database
 | 
			
		||||
        .into(database.localMessageEventTable)
 | 
			
		||||
        .insertReturning(LocalMessageEventTableCompanion.insert(
 | 
			
		||||
          id: Value(remote.id),
 | 
			
		||||
          channelId: remote.channelId,
 | 
			
		||||
          data: remote,
 | 
			
		||||
          createdAt: Value(remote.createdAt),
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
    // Handle side-effect like editing and deleting
 | 
			
		||||
    switch (remote.type) {
 | 
			
		||||
      case 'messages.edit':
 | 
			
		||||
        final body = EventMessageBody.fromJson(remote.body);
 | 
			
		||||
        if (body.relatedEvent != null) {
 | 
			
		||||
          final target = await (database.select(database.localMessageEventTable)
 | 
			
		||||
                ..where((x) => x.id.equals(body.relatedEvent!)))
 | 
			
		||||
              .getSingleOrNull();
 | 
			
		||||
          if (target != null) {
 | 
			
		||||
            target.data!.body = remote.body;
 | 
			
		||||
            target.data!.updatedAt = remote.updatedAt;
 | 
			
		||||
            await (database.update(database.localMessageEventTable)
 | 
			
		||||
                  ..where((x) => x.id.equals(target.id)))
 | 
			
		||||
                .write(
 | 
			
		||||
              LocalMessageEventTableCompanion(data: Value(target.data)),
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      case 'messages.delete':
 | 
			
		||||
        final body = EventMessageBody.fromJson(remote.body);
 | 
			
		||||
        if (body.relatedEvent != null) {
 | 
			
		||||
          await (database.delete(database.localMessageEventTable)
 | 
			
		||||
                ..where((x) => x.id.equals(body.relatedEvent!)))
 | 
			
		||||
              .go();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return entry;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<LocalMessageEventTableData?> getEvent(int id, Channel channel,
 | 
			
		||||
      {String scope = 'global'}) async {
 | 
			
		||||
    final database = Get.find<DatabaseProvider>().database;
 | 
			
		||||
    final localRecord = await (database.select(database.localMessageEventTable)
 | 
			
		||||
          ..where((x) => x.id.equals(id)))
 | 
			
		||||
        .getSingleOrNull();
 | 
			
		||||
    if (localRecord != null) return localRecord;
 | 
			
		||||
 | 
			
		||||
    final remoteRecord = await fetchRemoteEvent(id, channel, scope);
 | 
			
		||||
    if (remoteRecord == null) return null;
 | 
			
		||||
 | 
			
		||||
    return await receiveEvent(remoteRecord);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Pull the remote events to local database
 | 
			
		||||
  Future<(List<Event>, int)?> pullRemoteEvents(Channel channel,
 | 
			
		||||
      {String scope = 'global', depth = 10, offset = 0}) async {
 | 
			
		||||
    final database = Get.find<DatabaseProvider>().database;
 | 
			
		||||
    final lastOne = await (database.select(database.localMessageEventTable)
 | 
			
		||||
          ..where((x) => x.channelId.equals(channel.id))
 | 
			
		||||
          ..orderBy([(t) => OrderingTerm.desc(t.id)])
 | 
			
		||||
          ..limit(1))
 | 
			
		||||
        .getSingleOrNull();
 | 
			
		||||
 | 
			
		||||
    final data = await fetchRemoteEvents(
 | 
			
		||||
      channel,
 | 
			
		||||
      scope,
 | 
			
		||||
      depth: depth,
 | 
			
		||||
      offset: offset,
 | 
			
		||||
      onBrake: (items) {
 | 
			
		||||
        return items.any((x) => x.id == lastOne?.id);
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
    if (data != null) {
 | 
			
		||||
      await database.batch((batch) {
 | 
			
		||||
        batch.insertAllOnConflictUpdate(
 | 
			
		||||
          database.localMessageEventTable,
 | 
			
		||||
          data.$1.map((x) => LocalMessageEventTableCompanion(
 | 
			
		||||
                id: Value(x.id),
 | 
			
		||||
                channelId: Value(x.channelId),
 | 
			
		||||
                data: Value(x),
 | 
			
		||||
                createdAt: Value(x.createdAt),
 | 
			
		||||
              )),
 | 
			
		||||
        );
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<List<LocalMessageEventTableData>> listEvents(Channel channel) async {
 | 
			
		||||
    final database = Get.find<DatabaseProvider>().database;
 | 
			
		||||
    return await (database.select(database.localMessageEventTable)
 | 
			
		||||
          ..where((x) => x.channelId.equals(channel.id))
 | 
			
		||||
          ..orderBy([(t) => OrderingTerm.desc(t.id)]))
 | 
			
		||||
        .get();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<LocalMessageEventTableData?> getLastInChannel(Channel channel) async {
 | 
			
		||||
    final database = Get.find<DatabaseProvider>().database;
 | 
			
		||||
    return await (database.select(database.localMessageEventTable)
 | 
			
		||||
          ..where((x) => x.channelId.equals(channel.id))
 | 
			
		||||
          ..orderBy([(t) => OrderingTerm.desc(t.id)]))
 | 
			
		||||
        .getSingleOrNull();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								lib/providers/database/tables/json.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								lib/providers/database/tables/json.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
import 'dart:convert';
 | 
			
		||||
 | 
			
		||||
import 'package:drift/drift.dart';
 | 
			
		||||
 | 
			
		||||
class JsonConverter extends TypeConverter<Object?, String> {
 | 
			
		||||
  const JsonConverter();
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Object? fromSql(String fromDb) => jsonDecode(fromDb);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String toSql(Object? value) => jsonEncode(value);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								lib/providers/database/tables/messages.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								lib/providers/database/tables/messages.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
import 'dart:convert';
 | 
			
		||||
 | 
			
		||||
import 'package:drift/drift.dart';
 | 
			
		||||
import 'package:solian/models/event.dart';
 | 
			
		||||
 | 
			
		||||
class LocalMessageEventTable extends Table {
 | 
			
		||||
  IntColumn get id => integer().autoIncrement()();
 | 
			
		||||
  IntColumn get channelId => integer()();
 | 
			
		||||
  TextColumn get data => text().map(const MessageEventConverter())();
 | 
			
		||||
  DateTimeColumn get createdAt =>
 | 
			
		||||
      dateTime().withDefault(Constant(DateTime.now()))();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class MessageEventConverter extends TypeConverter<Event?, String> {
 | 
			
		||||
  const MessageEventConverter();
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Event? fromSql(String fromDb) => Event.fromJson(jsonDecode(fromDb));
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String toSql(Event? value) => jsonEncode(value?.toJson());
 | 
			
		||||
}
 | 
			
		||||
@@ -1,173 +0,0 @@
 | 
			
		||||
import 'package:floor/floor.dart';
 | 
			
		||||
import 'package:get/get.dart';
 | 
			
		||||
import 'package:solian/exceptions/request.dart';
 | 
			
		||||
import 'package:solian/models/channel.dart';
 | 
			
		||||
import 'package:solian/models/event.dart';
 | 
			
		||||
import 'package:solian/models/pagination.dart';
 | 
			
		||||
import 'package:solian/providers/auth.dart';
 | 
			
		||||
import 'package:solian/providers/message/events.dart';
 | 
			
		||||
 | 
			
		||||
Future<MessageHistoryDb> createHistoryDb() async {
 | 
			
		||||
  final migration1to2 = Migration(1, 2, (database) async {
 | 
			
		||||
    await database.execute('DROP TABLE IF EXISTS LocalMessage');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return await $FloorMessageHistoryDb
 | 
			
		||||
      .databaseBuilder('messaging_data.dart')
 | 
			
		||||
      .addMigrations([migration1to2]).build();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Future<(List<Event>, int)?> getWhatsNewEvents(int pivot, {take = 10}) async {
 | 
			
		||||
  final AuthProvider auth = Get.find();
 | 
			
		||||
  if (auth.isAuthorized.isFalse) return null;
 | 
			
		||||
 | 
			
		||||
  final client = auth.configureClient('messaging');
 | 
			
		||||
 | 
			
		||||
  final resp = await client.get(
 | 
			
		||||
    '/whats-new?pivot=$pivot&take=$take',
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  if (resp.statusCode != 200) {
 | 
			
		||||
    throw RequestException(resp);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  final PaginationResult response = PaginationResult.fromJson(resp.body);
 | 
			
		||||
  final result =
 | 
			
		||||
      response.data?.map((e) => Event.fromJson(e)).toList() ?? List.empty();
 | 
			
		||||
 | 
			
		||||
  return (result, response.count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Future<Event?> getRemoteEvent(int id, Channel channel, String scope) async {
 | 
			
		||||
  final AuthProvider auth = Get.find();
 | 
			
		||||
  if (auth.isAuthorized.isFalse) return null;
 | 
			
		||||
 | 
			
		||||
  final client = auth.configureClient('messaging');
 | 
			
		||||
 | 
			
		||||
  final resp = await client.get(
 | 
			
		||||
    '/channels/$scope/${channel.alias}/events/$id',
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  if (resp.statusCode == 404) {
 | 
			
		||||
    return null;
 | 
			
		||||
  } else if (resp.statusCode != 200) {
 | 
			
		||||
    throw RequestException(resp);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return Event.fromJson(resp.body);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Future<(List<Event>, int)?> getRemoteEvents(
 | 
			
		||||
  Channel channel,
 | 
			
		||||
  String scope, {
 | 
			
		||||
  required int remainDepth,
 | 
			
		||||
  bool Function(List<Event> items)? onBrake,
 | 
			
		||||
  take = 10,
 | 
			
		||||
  offset = 0,
 | 
			
		||||
}) async {
 | 
			
		||||
  if (remainDepth <= 0) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  final AuthProvider auth = Get.find();
 | 
			
		||||
  if (auth.isAuthorized.isFalse) return null;
 | 
			
		||||
 | 
			
		||||
  final client = auth.configureClient('messaging');
 | 
			
		||||
 | 
			
		||||
  final resp = await client.get(
 | 
			
		||||
    '/channels/$scope/${channel.alias}/events?take=$take&offset=$offset',
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  if (resp.statusCode != 200) {
 | 
			
		||||
    throw RequestException(resp);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  final PaginationResult response = PaginationResult.fromJson(resp.body);
 | 
			
		||||
  final result =
 | 
			
		||||
      response.data?.map((e) => Event.fromJson(e)).toList() ?? List.empty();
 | 
			
		||||
 | 
			
		||||
  if (onBrake != null && onBrake(result)) {
 | 
			
		||||
    return (result, response.count);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  final expandResult = (await getRemoteEvents(
 | 
			
		||||
        channel,
 | 
			
		||||
        scope,
 | 
			
		||||
        remainDepth: remainDepth - 1,
 | 
			
		||||
        take: take,
 | 
			
		||||
        offset: offset + result.length,
 | 
			
		||||
      ))
 | 
			
		||||
          ?.$1 ??
 | 
			
		||||
      List.empty();
 | 
			
		||||
 | 
			
		||||
  return ([...result, ...expandResult], response.count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension MessageHistoryAdaptor on MessageHistoryDb {
 | 
			
		||||
  Future<LocalEvent> receiveEvent(Event remote) async {
 | 
			
		||||
    final entry = LocalEvent(
 | 
			
		||||
      remote.id,
 | 
			
		||||
      remote,
 | 
			
		||||
      remote.channelId,
 | 
			
		||||
      remote.createdAt,
 | 
			
		||||
    );
 | 
			
		||||
    await localEvents.insert(entry);
 | 
			
		||||
    switch (remote.type) {
 | 
			
		||||
      case 'messages.edit':
 | 
			
		||||
        final body = EventMessageBody.fromJson(remote.body);
 | 
			
		||||
        if (body.relatedEvent != null) {
 | 
			
		||||
          final target = await localEvents.findById(body.relatedEvent!);
 | 
			
		||||
          if (target != null) {
 | 
			
		||||
            target.data.body = remote.body;
 | 
			
		||||
            target.data.updatedAt = remote.updatedAt;
 | 
			
		||||
            await localEvents.update(target);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      case 'messages.delete':
 | 
			
		||||
        final body = EventMessageBody.fromJson(remote.body);
 | 
			
		||||
        if (body.relatedEvent != null) {
 | 
			
		||||
          await localEvents.delete(body.relatedEvent!);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return entry;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<LocalEvent?> getEvent(int id, Channel channel,
 | 
			
		||||
      {String scope = 'global'}) async {
 | 
			
		||||
    final localRecord = await localEvents.findById(id);
 | 
			
		||||
    if (localRecord != null) return localRecord;
 | 
			
		||||
 | 
			
		||||
    final remoteRecord = await getRemoteEvent(id, channel, scope);
 | 
			
		||||
    if (remoteRecord == null) return null;
 | 
			
		||||
 | 
			
		||||
    return await receiveEvent(remoteRecord);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<(List<Event>, int)?> syncRemoteEvents(Channel channel,
 | 
			
		||||
      {String scope = 'global', depth = 10, offset = 0}) async {
 | 
			
		||||
    final lastOne = await localEvents.findLastByChannel(channel.id);
 | 
			
		||||
 | 
			
		||||
    final data = await getRemoteEvents(
 | 
			
		||||
      channel,
 | 
			
		||||
      scope,
 | 
			
		||||
      remainDepth: depth,
 | 
			
		||||
      offset: offset,
 | 
			
		||||
      onBrake: (items) {
 | 
			
		||||
        return items.any((x) => x.id == lastOne?.id);
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
    if (data != null) {
 | 
			
		||||
      await localEvents.insertBulk(
 | 
			
		||||
        data.$1
 | 
			
		||||
            .map((x) => LocalEvent(x.id, x, x.channelId, x.createdAt))
 | 
			
		||||
            .toList(),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<List<LocalEvent>> listEvents(Channel channel) async {
 | 
			
		||||
    return await localEvents.findAllByChannel(channel.id);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,83 +0,0 @@
 | 
			
		||||
import 'dart:async';
 | 
			
		||||
import 'dart:convert';
 | 
			
		||||
import 'package:floor/floor.dart';
 | 
			
		||||
import 'package:solian/models/event.dart';
 | 
			
		||||
import 'package:sqflite/sqflite.dart' as sqflite;
 | 
			
		||||
 | 
			
		||||
part 'events.g.dart';
 | 
			
		||||
 | 
			
		||||
@entity
 | 
			
		||||
class LocalEvent {
 | 
			
		||||
  @primaryKey
 | 
			
		||||
  final int id;
 | 
			
		||||
 | 
			
		||||
  final Event data;
 | 
			
		||||
  final int channelId;
 | 
			
		||||
 | 
			
		||||
  final DateTime createdAt;
 | 
			
		||||
 | 
			
		||||
  LocalEvent(this.id, this.data, this.channelId, this.createdAt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class DateTimeConverter extends TypeConverter<DateTime, int> {
 | 
			
		||||
  @override
 | 
			
		||||
  DateTime decode(int databaseValue) {
 | 
			
		||||
    return DateTime.fromMillisecondsSinceEpoch(databaseValue);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  int encode(DateTime value) {
 | 
			
		||||
    return value.millisecondsSinceEpoch;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class RemoteEventConverter extends TypeConverter<Event, String> {
 | 
			
		||||
  @override
 | 
			
		||||
  Event decode(String databaseValue) {
 | 
			
		||||
    return Event.fromJson(jsonDecode(databaseValue));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String encode(Event value) {
 | 
			
		||||
    return jsonEncode(value.toJson());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@dao
 | 
			
		||||
abstract class LocalEventDao {
 | 
			
		||||
  @Query('SELECT COUNT(id) FROM LocalEvent WHERE channelId = :channelId')
 | 
			
		||||
  Future<int?> countByChannel(int channelId);
 | 
			
		||||
 | 
			
		||||
  @Query('SELECT * FROM LocalEvent WHERE id = :id')
 | 
			
		||||
  Future<LocalEvent?> findById(int id);
 | 
			
		||||
 | 
			
		||||
  @Query('SELECT * FROM LocalEvent WHERE channelId = :channelId ORDER BY createdAt DESC')
 | 
			
		||||
  Future<List<LocalEvent>> findAllByChannel(int channelId);
 | 
			
		||||
 | 
			
		||||
  @Query('SELECT * FROM LocalEvent WHERE channelId = :channelId ORDER BY createdAt DESC LIMIT 1')
 | 
			
		||||
  Future<LocalEvent?> findLastByChannel(int channelId);
 | 
			
		||||
 | 
			
		||||
  @Insert(onConflict: OnConflictStrategy.replace)
 | 
			
		||||
  Future<void> insert(LocalEvent m);
 | 
			
		||||
 | 
			
		||||
  @Insert(onConflict: OnConflictStrategy.replace)
 | 
			
		||||
  Future<void> insertBulk(List<LocalEvent> m);
 | 
			
		||||
 | 
			
		||||
  @Update(onConflict: OnConflictStrategy.replace)
 | 
			
		||||
  Future<void> update(LocalEvent m);
 | 
			
		||||
 | 
			
		||||
  @Query('DELETE FROM LocalEvent WHERE id = :id')
 | 
			
		||||
  Future<void> delete(int id);
 | 
			
		||||
 | 
			
		||||
  @Query('DELETE FROM LocalEvent WHERE channelId = :channelId')
 | 
			
		||||
  Future<List<LocalEvent>> deleteByChannel(int channelId);
 | 
			
		||||
 | 
			
		||||
  @Query('DELETE FROM LocalEvent')
 | 
			
		||||
  Future<void> wipeLocalEvents();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@TypeConverters([DateTimeConverter, RemoteEventConverter])
 | 
			
		||||
@Database(version: 2, entities: [LocalEvent])
 | 
			
		||||
abstract class MessageHistoryDb extends FloorDatabase {
 | 
			
		||||
  LocalEventDao get localEvents;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,228 +0,0 @@
 | 
			
		||||
// GENERATED CODE - DO NOT MODIFY BY HAND
 | 
			
		||||
 | 
			
		||||
part of 'events.dart';
 | 
			
		||||
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
// FloorGenerator
 | 
			
		||||
// **************************************************************************
 | 
			
		||||
 | 
			
		||||
abstract class $MessageHistoryDbBuilderContract {
 | 
			
		||||
  /// Adds migrations to the builder.
 | 
			
		||||
  $MessageHistoryDbBuilderContract addMigrations(List<Migration> migrations);
 | 
			
		||||
 | 
			
		||||
  /// Adds a database [Callback] to the builder.
 | 
			
		||||
  $MessageHistoryDbBuilderContract addCallback(Callback callback);
 | 
			
		||||
 | 
			
		||||
  /// Creates the database and initializes it.
 | 
			
		||||
  Future<MessageHistoryDb> build();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ignore: avoid_classes_with_only_static_members
 | 
			
		||||
class $FloorMessageHistoryDb {
 | 
			
		||||
  /// Creates a database builder for a persistent database.
 | 
			
		||||
  /// Once a database is built, you should keep a reference to it and re-use it.
 | 
			
		||||
  static $MessageHistoryDbBuilderContract databaseBuilder(String name) =>
 | 
			
		||||
      _$MessageHistoryDbBuilder(name);
 | 
			
		||||
 | 
			
		||||
  /// Creates a database builder for an in memory database.
 | 
			
		||||
  /// Information stored in an in memory database disappears when the process is killed.
 | 
			
		||||
  /// Once a database is built, you should keep a reference to it and re-use it.
 | 
			
		||||
  static $MessageHistoryDbBuilderContract inMemoryDatabaseBuilder() =>
 | 
			
		||||
      _$MessageHistoryDbBuilder(null);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _$MessageHistoryDbBuilder implements $MessageHistoryDbBuilderContract {
 | 
			
		||||
  _$MessageHistoryDbBuilder(this.name);
 | 
			
		||||
 | 
			
		||||
  final String? name;
 | 
			
		||||
 | 
			
		||||
  final List<Migration> _migrations = [];
 | 
			
		||||
 | 
			
		||||
  Callback? _callback;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  $MessageHistoryDbBuilderContract addMigrations(List<Migration> migrations) {
 | 
			
		||||
    _migrations.addAll(migrations);
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  $MessageHistoryDbBuilderContract addCallback(Callback callback) {
 | 
			
		||||
    _callback = callback;
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<MessageHistoryDb> build() async {
 | 
			
		||||
    final path = name != null
 | 
			
		||||
        ? await sqfliteDatabaseFactory.getDatabasePath(name!)
 | 
			
		||||
        : ':memory:';
 | 
			
		||||
    final database = _$MessageHistoryDb();
 | 
			
		||||
    database.database = await database.open(
 | 
			
		||||
      path,
 | 
			
		||||
      _migrations,
 | 
			
		||||
      _callback,
 | 
			
		||||
    );
 | 
			
		||||
    return database;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _$MessageHistoryDb extends MessageHistoryDb {
 | 
			
		||||
  _$MessageHistoryDb([StreamController<String>? listener]) {
 | 
			
		||||
    changeListener = listener ?? StreamController<String>.broadcast();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  LocalEventDao? _localEventsInstance;
 | 
			
		||||
 | 
			
		||||
  Future<sqflite.Database> open(
 | 
			
		||||
    String path,
 | 
			
		||||
    List<Migration> migrations, [
 | 
			
		||||
    Callback? callback,
 | 
			
		||||
  ]) async {
 | 
			
		||||
    final databaseOptions = sqflite.OpenDatabaseOptions(
 | 
			
		||||
      version: 2,
 | 
			
		||||
      onConfigure: (database) async {
 | 
			
		||||
        await database.execute('PRAGMA foreign_keys = ON');
 | 
			
		||||
        await callback?.onConfigure?.call(database);
 | 
			
		||||
      },
 | 
			
		||||
      onOpen: (database) async {
 | 
			
		||||
        await callback?.onOpen?.call(database);
 | 
			
		||||
      },
 | 
			
		||||
      onUpgrade: (database, startVersion, endVersion) async {
 | 
			
		||||
        await MigrationAdapter.runMigrations(
 | 
			
		||||
            database, startVersion, endVersion, migrations);
 | 
			
		||||
 | 
			
		||||
        await callback?.onUpgrade?.call(database, startVersion, endVersion);
 | 
			
		||||
      },
 | 
			
		||||
      onCreate: (database, version) async {
 | 
			
		||||
        await database.execute(
 | 
			
		||||
            'CREATE TABLE IF NOT EXISTS `LocalEvent` (`id` INTEGER NOT NULL, `data` TEXT NOT NULL, `channelId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, PRIMARY KEY (`id`))');
 | 
			
		||||
 | 
			
		||||
        await callback?.onCreate?.call(database, version);
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
    return sqfliteDatabaseFactory.openDatabase(path, options: databaseOptions);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  LocalEventDao get localEvents {
 | 
			
		||||
    return _localEventsInstance ??= _$LocalEventDao(database, changeListener);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _$LocalEventDao extends LocalEventDao {
 | 
			
		||||
  _$LocalEventDao(
 | 
			
		||||
    this.database,
 | 
			
		||||
    this.changeListener,
 | 
			
		||||
  )   : _queryAdapter = QueryAdapter(database),
 | 
			
		||||
        _localEventInsertionAdapter = InsertionAdapter(
 | 
			
		||||
            database,
 | 
			
		||||
            'LocalEvent',
 | 
			
		||||
            (LocalEvent item) => <String, Object?>{
 | 
			
		||||
                  'id': item.id,
 | 
			
		||||
                  'data': _remoteEventConverter.encode(item.data),
 | 
			
		||||
                  'channelId': item.channelId,
 | 
			
		||||
                  'createdAt': _dateTimeConverter.encode(item.createdAt)
 | 
			
		||||
                }),
 | 
			
		||||
        _localEventUpdateAdapter = UpdateAdapter(
 | 
			
		||||
            database,
 | 
			
		||||
            'LocalEvent',
 | 
			
		||||
            ['id'],
 | 
			
		||||
            (LocalEvent item) => <String, Object?>{
 | 
			
		||||
                  'id': item.id,
 | 
			
		||||
                  'data': _remoteEventConverter.encode(item.data),
 | 
			
		||||
                  'channelId': item.channelId,
 | 
			
		||||
                  'createdAt': _dateTimeConverter.encode(item.createdAt)
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
  final sqflite.DatabaseExecutor database;
 | 
			
		||||
 | 
			
		||||
  final StreamController<String> changeListener;
 | 
			
		||||
 | 
			
		||||
  final QueryAdapter _queryAdapter;
 | 
			
		||||
 | 
			
		||||
  final InsertionAdapter<LocalEvent> _localEventInsertionAdapter;
 | 
			
		||||
 | 
			
		||||
  final UpdateAdapter<LocalEvent> _localEventUpdateAdapter;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<int?> countByChannel(int channelId) async {
 | 
			
		||||
    return _queryAdapter.query(
 | 
			
		||||
        'SELECT COUNT(id) FROM LocalEvent WHERE channelId = ?1',
 | 
			
		||||
        mapper: (Map<String, Object?> row) => row.values.first as int,
 | 
			
		||||
        arguments: [channelId]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<LocalEvent?> findById(int id) async {
 | 
			
		||||
    return _queryAdapter.query('SELECT * FROM LocalEvent WHERE id = ?1',
 | 
			
		||||
        mapper: (Map<String, Object?> row) => LocalEvent(
 | 
			
		||||
            row['id'] as int,
 | 
			
		||||
            _remoteEventConverter.decode(row['data'] as String),
 | 
			
		||||
            row['channelId'] as int,
 | 
			
		||||
            _dateTimeConverter.decode(row['createdAt'] as int)),
 | 
			
		||||
        arguments: [id]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<List<LocalEvent>> findAllByChannel(int channelId) async {
 | 
			
		||||
    return _queryAdapter.queryList(
 | 
			
		||||
        'SELECT * FROM LocalEvent WHERE channelId = ?1 ORDER BY createdAt DESC',
 | 
			
		||||
        mapper: (Map<String, Object?> row) => LocalEvent(
 | 
			
		||||
            row['id'] as int,
 | 
			
		||||
            _remoteEventConverter.decode(row['data'] as String),
 | 
			
		||||
            row['channelId'] as int,
 | 
			
		||||
            _dateTimeConverter.decode(row['createdAt'] as int)),
 | 
			
		||||
        arguments: [channelId]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<LocalEvent?> findLastByChannel(int channelId) async {
 | 
			
		||||
    return _queryAdapter.query(
 | 
			
		||||
        'SELECT * FROM LocalEvent WHERE channelId = ?1 ORDER BY createdAt DESC LIMIT 1',
 | 
			
		||||
        mapper: (Map<String, Object?> row) => LocalEvent(row['id'] as int, _remoteEventConverter.decode(row['data'] as String), row['channelId'] as int, _dateTimeConverter.decode(row['createdAt'] as int)),
 | 
			
		||||
        arguments: [channelId]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<void> delete(int id) async {
 | 
			
		||||
    await _queryAdapter
 | 
			
		||||
        .queryNoReturn('DELETE FROM LocalEvent WHERE id = ?1', arguments: [id]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<List<LocalEvent>> deleteByChannel(int channelId) async {
 | 
			
		||||
    return _queryAdapter.queryList(
 | 
			
		||||
        'DELETE FROM LocalEvent WHERE channelId = ?1',
 | 
			
		||||
        mapper: (Map<String, Object?> row) => LocalEvent(
 | 
			
		||||
            row['id'] as int,
 | 
			
		||||
            _remoteEventConverter.decode(row['data'] as String),
 | 
			
		||||
            row['channelId'] as int,
 | 
			
		||||
            _dateTimeConverter.decode(row['createdAt'] as int)),
 | 
			
		||||
        arguments: [channelId]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<void> wipeLocalEvents() async {
 | 
			
		||||
    await _queryAdapter.queryNoReturn('DELETE FROM LocalEvent');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<void> insert(LocalEvent m) async {
 | 
			
		||||
    await _localEventInsertionAdapter.insert(m, OnConflictStrategy.replace);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<void> insertBulk(List<LocalEvent> m) async {
 | 
			
		||||
    await _localEventInsertionAdapter.insertList(m, OnConflictStrategy.replace);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<void> update(LocalEvent m) async {
 | 
			
		||||
    await _localEventUpdateAdapter.update(m, OnConflictStrategy.replace);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ignore_for_file: unused_element
 | 
			
		||||
final _dateTimeConverter = DateTimeConverter();
 | 
			
		||||
final _remoteEventConverter = RemoteEventConverter();
 | 
			
		||||
@@ -2,7 +2,6 @@ import 'dart:async';
 | 
			
		||||
import 'dart:ui';
 | 
			
		||||
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_animate/flutter_animate.dart';
 | 
			
		||||
import 'package:get/get.dart';
 | 
			
		||||
import 'package:solian/controllers/chat_events_controller.dart';
 | 
			
		||||
import 'package:solian/exts.dart';
 | 
			
		||||
@@ -156,7 +155,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen>
 | 
			
		||||
 | 
			
		||||
  void _keepUpdateWithServer() {
 | 
			
		||||
    _getOngoingCall();
 | 
			
		||||
    _chatController.getEvents(_channel!, widget.realm);
 | 
			
		||||
    _chatController.getInitialEvents(_channel!, widget.realm);
 | 
			
		||||
    setState(() => _isOutOfSyncSince = null);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -193,7 +192,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen>
 | 
			
		||||
 | 
			
		||||
    _getOngoingCall();
 | 
			
		||||
    _getChannel().then((_) {
 | 
			
		||||
      _chatController.getEvents(_channel!, widget.realm);
 | 
			
		||||
      _chatController.getInitialEvents(_channel!, widget.realm);
 | 
			
		||||
      _listenMessages();
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
@@ -295,13 +294,6 @@ class _ChannelChatScreenState extends State<ChannelChatScreen>
 | 
			
		||||
                      },
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                  Obx(() {
 | 
			
		||||
                    if (_chatController.isLoading.isTrue) {
 | 
			
		||||
                      return const LinearProgressIndicator().animate().slideY();
 | 
			
		||||
                    } else {
 | 
			
		||||
                      return const SizedBox.shrink();
 | 
			
		||||
                    }
 | 
			
		||||
                  }),
 | 
			
		||||
                  ClipRect(
 | 
			
		||||
                    child: BackdropFilter(
 | 
			
		||||
                      filter: ImageFilter.blur(sigmaX: 50, sigmaY: 50),
 | 
			
		||||
 
 | 
			
		||||
@@ -18,8 +18,8 @@ import 'package:solian/models/post.dart';
 | 
			
		||||
import 'package:solian/providers/auth.dart';
 | 
			
		||||
import 'package:solian/providers/content/posts.dart';
 | 
			
		||||
import 'package:solian/providers/daily_sign.dart';
 | 
			
		||||
import 'package:solian/providers/database/services/messages.dart';
 | 
			
		||||
import 'package:solian/providers/last_read.dart';
 | 
			
		||||
import 'package:solian/providers/message/adaptor.dart';
 | 
			
		||||
import 'package:solian/providers/websocket.dart';
 | 
			
		||||
import 'package:solian/router.dart';
 | 
			
		||||
import 'package:solian/screens/account/notification.dart';
 | 
			
		||||
@@ -72,7 +72,8 @@ class _DashboardScreenState extends State<DashboardScreen> {
 | 
			
		||||
  Future<void> _pullMessages() async {
 | 
			
		||||
    if (_lastRead.messagesLastReadAt == null) return;
 | 
			
		||||
    log('[Dashboard] Pulling messages with pivot: ${_lastRead.messagesLastReadAt}');
 | 
			
		||||
    final out = await getWhatsNewEvents(_lastRead.messagesLastReadAt!);
 | 
			
		||||
    final src = Get.find<MessagesFetchingProvider>();
 | 
			
		||||
    final out = await src.getWhatsNewEvents(_lastRead.messagesLastReadAt!);
 | 
			
		||||
    if (out == null) return;
 | 
			
		||||
    setState(() {
 | 
			
		||||
      _currentMessages = out.$1;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:get/get.dart';
 | 
			
		||||
import 'package:provider/provider.dart';
 | 
			
		||||
import 'package:shared_preferences/shared_preferences.dart';
 | 
			
		||||
import 'package:solian/controllers/chat_events_controller.dart';
 | 
			
		||||
import 'package:solian/exts.dart';
 | 
			
		||||
import 'package:solian/providers/theme_switcher.dart';
 | 
			
		||||
import 'package:solian/router.dart';
 | 
			
		||||
@@ -97,10 +96,7 @@ class _SettingScreenState extends State<SettingScreen> {
 | 
			
		||||
            contentPadding: const EdgeInsets.symmetric(horizontal: 22),
 | 
			
		||||
            title: Text('messageHistoryWipe'.tr),
 | 
			
		||||
            onTap: () {
 | 
			
		||||
              final chatHistory = ChatEventController();
 | 
			
		||||
              chatHistory.initialize().then((_) async {
 | 
			
		||||
                await chatHistory.database.localEvents.wipeLocalEvents();
 | 
			
		||||
              });
 | 
			
		||||
              // TODO Wipe message history
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
 
 | 
			
		||||
@@ -105,7 +105,7 @@ class _ChannelListWidgetState extends State<ChannelListWidget> {
 | 
			
		||||
    return FutureBuilder(
 | 
			
		||||
      future: Future.delayed(
 | 
			
		||||
        const Duration(milliseconds: 500),
 | 
			
		||||
        () => _eventController.database.localEvents.findLastByChannel(item.id),
 | 
			
		||||
        () => _eventController.src.getLastInChannel(item),
 | 
			
		||||
      ),
 | 
			
		||||
      builder: (context, snapshot) {
 | 
			
		||||
        if (!snapshot.hasData && snapshot.data == null) {
 | 
			
		||||
@@ -114,8 +114,9 @@ class _ChannelListWidgetState extends State<ChannelListWidget> {
 | 
			
		||||
          ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        final data = snapshot.data!.data!;
 | 
			
		||||
        return Text(
 | 
			
		||||
          '${snapshot.data!.data.sender.account.nick}: ${snapshot.data!.data.body['text'] ?? 'Unsupported message to preview'}',
 | 
			
		||||
          '${data.sender.account.nick}: ${data.body['text'] ?? 'Unsupported message to preview'}',
 | 
			
		||||
          maxLines: 1,
 | 
			
		||||
          overflow: TextOverflow.ellipsis,
 | 
			
		||||
        );
 | 
			
		||||
 
 | 
			
		||||
@@ -97,7 +97,7 @@ class ChatEvent extends StatelessWidget {
 | 
			
		||||
        return Container(
 | 
			
		||||
          constraints: const BoxConstraints(maxWidth: 480),
 | 
			
		||||
          child: ChatEvent(
 | 
			
		||||
            item: snapshot.data!.data,
 | 
			
		||||
            item: snapshot.data!.data!,
 | 
			
		||||
            isMerged: false,
 | 
			
		||||
            isQuote: true,
 | 
			
		||||
          ),
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import 'package:solian/models/event.dart';
 | 
			
		||||
import 'package:solian/providers/last_read.dart';
 | 
			
		||||
import 'package:solian/widgets/chat/chat_event.dart';
 | 
			
		||||
import 'package:solian/widgets/chat/chat_event_action.dart';
 | 
			
		||||
import 'package:very_good_infinite_list/very_good_infinite_list.dart';
 | 
			
		||||
 | 
			
		||||
class ChatEventList extends StatelessWidget {
 | 
			
		||||
  final String scope;
 | 
			
		||||
@@ -36,8 +37,9 @@ class ChatEventList extends StatelessWidget {
 | 
			
		||||
      reverse: true,
 | 
			
		||||
      slivers: [
 | 
			
		||||
        Obx(() {
 | 
			
		||||
          return SliverList.builder(
 | 
			
		||||
          return SliverInfiniteList(
 | 
			
		||||
            key: Key('chat-history#${channel.id}'),
 | 
			
		||||
            isLoading: chatController.isLoading.value,
 | 
			
		||||
            itemCount: chatController.currentEvents.length,
 | 
			
		||||
            itemBuilder: (context, index) {
 | 
			
		||||
              Get.find<LastReadProvider>().messagesLastReadAt =
 | 
			
		||||
@@ -62,7 +64,7 @@ class ChatEventList extends StatelessWidget {
 | 
			
		||||
              return GestureDetector(
 | 
			
		||||
                behavior: HitTestBehavior.opaque,
 | 
			
		||||
                child: ChatEvent(
 | 
			
		||||
                  key: Key('m${item.uuid}'),
 | 
			
		||||
                  key: Key('m${item!.uuid}'),
 | 
			
		||||
                  item: item,
 | 
			
		||||
                  isMerged: isMerged,
 | 
			
		||||
                  chatController: chatController,
 | 
			
		||||
@@ -89,28 +91,12 @@ class ChatEventList extends StatelessWidget {
 | 
			
		||||
                },
 | 
			
		||||
              );
 | 
			
		||||
            },
 | 
			
		||||
          );
 | 
			
		||||
        }),
 | 
			
		||||
        Obx(() {
 | 
			
		||||
          final amount =
 | 
			
		||||
              chatController.totalEvents - chatController.currentEvents.length;
 | 
			
		||||
 | 
			
		||||
          if (amount.value <= 0 || chatController.isLoading.isTrue) {
 | 
			
		||||
            return const SliverToBoxAdapter(child: SizedBox.shrink());
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          return SliverToBoxAdapter(
 | 
			
		||||
            child: ListTile(
 | 
			
		||||
              tileColor: Theme.of(context).colorScheme.surfaceContainerLow,
 | 
			
		||||
              leading: const Icon(Icons.sync_disabled),
 | 
			
		||||
              title: Text('messageUnSync'.tr),
 | 
			
		||||
              subtitle: Text('messageUnSyncCaption'.trParams({
 | 
			
		||||
                'count': amount.string,
 | 
			
		||||
              })),
 | 
			
		||||
              onTap: () {
 | 
			
		||||
                chatController.loadEvents(channel, scope);
 | 
			
		||||
              },
 | 
			
		||||
            ),
 | 
			
		||||
            onFetchData: () {
 | 
			
		||||
              chatController.loadEvents(
 | 
			
		||||
                chatController.channel!,
 | 
			
		||||
                chatController.scope!,
 | 
			
		||||
              );
 | 
			
		||||
            },
 | 
			
		||||
          );
 | 
			
		||||
        }),
 | 
			
		||||
      ],
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,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 <url_launcher_linux/url_launcher_plugin.h>
 | 
			
		||||
 | 
			
		||||
void fl_register_plugins(FlPluginRegistry* registry) {
 | 
			
		||||
@@ -41,6 +42,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) url_launcher_linux_registrar =
 | 
			
		||||
      fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
 | 
			
		||||
  url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
 | 
			
		||||
  media_kit_libs_linux
 | 
			
		||||
  media_kit_video
 | 
			
		||||
  pasteboard
 | 
			
		||||
  sqlite3_flutter_libs
 | 
			
		||||
  url_launcher_linux
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@ import screen_brightness_macos
 | 
			
		||||
import share_plus
 | 
			
		||||
import shared_preferences_foundation
 | 
			
		||||
import sqflite
 | 
			
		||||
import sqlite3_flutter_libs
 | 
			
		||||
import url_launcher_macos
 | 
			
		||||
import wakelock_plus
 | 
			
		||||
 | 
			
		||||
@@ -55,6 +56,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"))
 | 
			
		||||
  UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
 | 
			
		||||
  WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										214
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										214
									
								
								pubspec.lock
									
									
									
									
									
								
							@@ -30,6 +30,14 @@ packages:
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "6.7.0"
 | 
			
		||||
  analyzer_plugin:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: analyzer_plugin
 | 
			
		||||
      sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.11.3"
 | 
			
		||||
  animations:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@@ -162,10 +170,10 @@ packages:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: cached_network_image
 | 
			
		||||
      sha256: "4a5d8d2c728b0f3d0245f69f921d7be90cae4c2fd5288f773088672c0893f819"
 | 
			
		||||
      sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "3.4.0"
 | 
			
		||||
    version: "3.4.1"
 | 
			
		||||
  cached_network_image_platform_interface:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -178,10 +186,10 @@ packages:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: cached_network_image_web
 | 
			
		||||
      sha256: "6322dde7a5ad92202e64df659241104a43db20ed594c41ca18de1014598d7996"
 | 
			
		||||
      sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.3.0"
 | 
			
		||||
    version: "1.3.1"
 | 
			
		||||
  carousel_slider:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@@ -298,10 +306,10 @@ packages:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: dart_style
 | 
			
		||||
      sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9"
 | 
			
		||||
      sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.3.6"
 | 
			
		||||
    version: "2.3.7"
 | 
			
		||||
  dart_webrtc:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -326,14 +334,6 @@ packages:
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.4.4"
 | 
			
		||||
  dev_build:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: dev_build
 | 
			
		||||
      sha256: f526d1fbe68875f6119ffc333f114dfe6aa93ad04439276d53968f7977cc410e
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.0.0+11"
 | 
			
		||||
  device_info_plus:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@@ -374,6 +374,30 @@ packages:
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.0.2"
 | 
			
		||||
  drift:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: drift
 | 
			
		||||
      sha256: "5b561ec76fff260e1e0593a29ca0d058a140a4b4dfb11dcc0c3813820cd20200"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.20.2"
 | 
			
		||||
  drift_dev:
 | 
			
		||||
    dependency: "direct dev"
 | 
			
		||||
    description:
 | 
			
		||||
      name: drift_dev
 | 
			
		||||
      sha256: "3ee987578ca2281b5ff91eadd757cd6dd36001458d6e33784f990d67ff38f756"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.20.3"
 | 
			
		||||
  drift_flutter:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: drift_flutter
 | 
			
		||||
      sha256: c670c947fe17ad149678a43fdbbfdb69321f0c83d315043e34e8ad2729e11f49
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.2.0"
 | 
			
		||||
  dropdown_button2:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@@ -466,26 +490,26 @@ packages:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_analytics
 | 
			
		||||
      sha256: "7e032ade38dec2a92f543ba02c5f72f54ffaa095c60d2132b867eab56de3bc73"
 | 
			
		||||
      sha256: "7b5ae39d853ead76f9d030dc23389bfec4ea826d7cccb4eea4873dcb0cdd172b"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "11.3.0"
 | 
			
		||||
    version: "11.3.1"
 | 
			
		||||
  firebase_analytics_platform_interface:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_analytics_platform_interface
 | 
			
		||||
      sha256: b62a2444767d95067a7e36b1d6e335e0b877968574bbbfb656168c46f2e95a13
 | 
			
		||||
      sha256: "0205e05bb37abd29d5dec5cd89aeb04f3f58bf849aad21dd938be0507d52a40c"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "4.2.2"
 | 
			
		||||
    version: "4.2.3"
 | 
			
		||||
  firebase_analytics_web:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_analytics_web
 | 
			
		||||
      sha256: bad44f71f96cfca6c16c9dd4f70b85f123ddca7d5dd698977449fadf298b1782
 | 
			
		||||
      sha256: "434807f8b30526e21cc062410c28ee5c6680a13626c4443b5ffede29f84b0c74"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.5.9+2"
 | 
			
		||||
    version: "0.5.10"
 | 
			
		||||
  firebase_core:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@@ -514,26 +538,26 @@ packages:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_crashlytics
 | 
			
		||||
      sha256: "4c9872020c0d97a161362ee6af7000cfdb8666234ddc290a15252ad379bb235a"
 | 
			
		||||
      sha256: c4fdbb14ba6f36794f89dc27fb5c759c9cc67ecbaeb079edc4dba515bbf9f555
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "4.1.0"
 | 
			
		||||
    version: "4.1.1"
 | 
			
		||||
  firebase_crashlytics_platform_interface:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_crashlytics_platform_interface
 | 
			
		||||
      sha256: ede8a199ff03378857d3c8cbb7fa58d37c27bb5a6b75faf8415ff6925dcaae2a
 | 
			
		||||
      sha256: "891d6f7ba4b93672d0e1265f27b6a9dccd56ba2cc30ce6496586b32d1d8770ac"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "3.6.41"
 | 
			
		||||
    version: "3.6.42"
 | 
			
		||||
  firebase_messaging:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_messaging
 | 
			
		||||
      sha256: "29941ba5a3204d80656c0e52103369aa9a53edfd9ceae05a2bb3376f24fda453"
 | 
			
		||||
      sha256: cc02c4afd6510cd84586020670140c4a23fbe52af16cd260ccf8ede101bb8d1b
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "15.1.0"
 | 
			
		||||
    version: "15.1.1"
 | 
			
		||||
  firebase_messaging_platform_interface:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -554,26 +578,26 @@ packages:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_performance
 | 
			
		||||
      sha256: "66666f697ecdcca2616af99f8ccfa74d795e5819c598227f2784fc00b1c6e421"
 | 
			
		||||
      sha256: "879ce4d83242cb7d1ec67a8b45daa351f230211778e34eeea979894839c4832a"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.10.0+5"
 | 
			
		||||
    version: "0.10.0+6"
 | 
			
		||||
  firebase_performance_platform_interface:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_performance_platform_interface
 | 
			
		||||
      sha256: ceaa026d067347cc6ea11113ba926ae450f56e305c186d1edce78f05983b481a
 | 
			
		||||
      sha256: ac68eba644f593903a931ba7f26f0677b725d5a60f8f7bc0fed01d88a11d1463
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.1.4+41"
 | 
			
		||||
    version: "0.1.4+42"
 | 
			
		||||
  firebase_performance_web:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: firebase_performance_web
 | 
			
		||||
      sha256: "6d121cd7e27b63995998dc4039caf0cbf304c2eee6fc6ed9ac7f80860cc0e51c"
 | 
			
		||||
      sha256: ff53b9c5d8601fc983d0173b88fdfd8abcc74948f0a3753f3c1ec276b680f23c
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.1.6+13"
 | 
			
		||||
    version: "0.1.7"
 | 
			
		||||
  fixnum:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -590,38 +614,6 @@ packages:
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.69.0"
 | 
			
		||||
  floor:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: floor
 | 
			
		||||
      sha256: c1b06023912b5b8e49deb6a9d867863c535ae1a232d991c3582bba3ee8687867
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.5.0"
 | 
			
		||||
  floor_annotation:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: floor_annotation
 | 
			
		||||
      sha256: a40949580a7ab0eee572686e2d3b1638fd6bd6a753e661d792ab4236b365b23b
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.5.0"
 | 
			
		||||
  floor_common:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: floor_common
 | 
			
		||||
      sha256: "41c9914862f83a821815e1b1ffd47a1e1ae2130c35ff882ba2d000a67713ba64"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.5.0"
 | 
			
		||||
  floor_generator:
 | 
			
		||||
    dependency: "direct dev"
 | 
			
		||||
    description:
 | 
			
		||||
      name: floor_generator
 | 
			
		||||
      sha256: "1499b3ab878a807e6fbe6f140dc014124845cd1df3090a113aae5fa7577a1e77"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.5.0"
 | 
			
		||||
  flutter:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description: flutter
 | 
			
		||||
@@ -861,14 +853,6 @@ packages:
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "10.7.0"
 | 
			
		||||
  freezed:
 | 
			
		||||
    dependency: "direct dev"
 | 
			
		||||
    description:
 | 
			
		||||
      name: freezed
 | 
			
		||||
      sha256: "44c19278dd9d89292cf46e97dc0c1e52ce03275f40a97c5a348e802a924bf40e"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.5.7"
 | 
			
		||||
  freezed_annotation:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@@ -1157,14 +1141,6 @@ packages:
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "4.0.0"
 | 
			
		||||
  lists:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: lists
 | 
			
		||||
      sha256: "4ca5c19ae4350de036a7e996cdd1ee39c93ac0a2b840f4915459b7d0a7d4ab27"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.0.1"
 | 
			
		||||
  livekit_client:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@@ -1473,10 +1449,10 @@ packages:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: permission_handler_platform_interface
 | 
			
		||||
      sha256: fe0ffe274d665be8e34f9c59705441a7d248edebbe5d9e3ec2665f88b79358ea
 | 
			
		||||
      sha256: e9c8eadee926c4532d0305dff94b85bf961f16759c3af791486613152af4b4f9
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "4.2.2"
 | 
			
		||||
    version: "4.2.3"
 | 
			
		||||
  permission_handler_windows:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -1557,14 +1533,6 @@ packages:
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.5.1"
 | 
			
		||||
  process_run:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: process_run
 | 
			
		||||
      sha256: "112a77da35be50617ed9e2230df68d0817972f225e7f97ce8336f76b4e601606"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.2.0"
 | 
			
		||||
  protobuf:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -1645,6 +1613,14 @@ packages:
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.3.0"
 | 
			
		||||
  recase:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: recase
 | 
			
		||||
      sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "4.1.0"
 | 
			
		||||
  rxdart:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -1851,7 +1827,7 @@ packages:
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "7.0.0"
 | 
			
		||||
  sqflite:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: sqflite
 | 
			
		||||
      sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d
 | 
			
		||||
@@ -1866,22 +1842,6 @@ packages:
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.5.4+2"
 | 
			
		||||
  sqflite_common_ffi:
 | 
			
		||||
    dependency: "direct dev"
 | 
			
		||||
    description:
 | 
			
		||||
      name: sqflite_common_ffi
 | 
			
		||||
      sha256: "4d6137c29e930d6e4a8ff373989dd9de7bac12e3bc87bce950f6e844e8ad3bb5"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.3.3"
 | 
			
		||||
  sqflite_common_ffi_web:
 | 
			
		||||
    dependency: "direct dev"
 | 
			
		||||
    description:
 | 
			
		||||
      name: sqflite_common_ffi_web
 | 
			
		||||
      sha256: cfc9d1c61a3e06e5b2e96994a44b11125b4f451fee95b9fad8bd473b4613d592
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.4.3+1"
 | 
			
		||||
  sqlite3:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -1890,14 +1850,22 @@ packages:
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.4.6"
 | 
			
		||||
  sqlite3_flutter_libs:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: sqlite3_flutter_libs
 | 
			
		||||
      sha256: "62bbb4073edbcdf53f40c80775f33eea01d301b7b81417e5b3fb7395416258c1"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.5.24"
 | 
			
		||||
  sqlparser:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: sqlparser
 | 
			
		||||
      sha256: "7b20045d1ccfb7bc1df7e8f9fee5ae58673fce6ff62cefbb0e0fd7214e90e5a0"
 | 
			
		||||
      sha256: "852cf80f9e974ac8e1b613758a8aa640215f7701352b66a7f468e95711eb570b"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.34.1"
 | 
			
		||||
    version: "0.38.1"
 | 
			
		||||
  stack_trace:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -1930,14 +1898,6 @@ packages:
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.2.0"
 | 
			
		||||
  strings:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: strings
 | 
			
		||||
      sha256: "052836499f03897d3860a603b330c1ea3c8a14177b21f34b15a1295f36024aae"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "3.1.2"
 | 
			
		||||
  synchronized:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -1986,14 +1946,6 @@ packages:
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.3.2"
 | 
			
		||||
  unicode:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: unicode
 | 
			
		||||
      sha256: "0f69e46593d65245774d4f17125c6084d2c20b4e473a983f6e21b7d7762218f1"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.3.1"
 | 
			
		||||
  universal_io:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -2122,6 +2074,14 @@ packages:
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.1.4"
 | 
			
		||||
  very_good_infinite_list:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: very_good_infinite_list
 | 
			
		||||
      sha256: "03445e302f9e0878b6b429c096825463e0990dd38fa69a3c5c74c646afd0e485"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.8.0"
 | 
			
		||||
  vm_service:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@@ -2206,10 +2166,10 @@ packages:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: win32_registry
 | 
			
		||||
      sha256: "723b7f851e5724c55409bb3d5a32b203b3afe8587eaf5dafb93a5fed8ecda0d6"
 | 
			
		||||
      sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.1.4"
 | 
			
		||||
    version: "1.1.5"
 | 
			
		||||
  xdg_directories:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								pubspec.yaml
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								pubspec.yaml
									
									
									
									
									
								
							@@ -40,8 +40,6 @@ dependencies:
 | 
			
		||||
  package_info_plus: ^8.0.0
 | 
			
		||||
  device_info_plus: ^10.1.0
 | 
			
		||||
  flutter_acrylic: ^1.1.4
 | 
			
		||||
  floor: ^1.5.0
 | 
			
		||||
  sqflite: ^2.3.3+1
 | 
			
		||||
  protocol_handler: ^0.2.0
 | 
			
		||||
  markdown: ^7.2.2
 | 
			
		||||
  pasteboard: ^0.3.0
 | 
			
		||||
@@ -77,6 +75,9 @@ dependencies:
 | 
			
		||||
  media_kit: ^1.1.11
 | 
			
		||||
  media_kit_video: ^1.2.5
 | 
			
		||||
  media_kit_libs_video: ^1.0.5
 | 
			
		||||
  drift: ^2.20.2
 | 
			
		||||
  drift_flutter: ^0.2.0
 | 
			
		||||
  very_good_infinite_list: ^0.8.0
 | 
			
		||||
 | 
			
		||||
dev_dependencies:
 | 
			
		||||
  flutter_test:
 | 
			
		||||
@@ -85,13 +86,10 @@ dev_dependencies:
 | 
			
		||||
  flutter_lints: ^4.0.0
 | 
			
		||||
  flutter_launcher_icons: ^0.13.1
 | 
			
		||||
 | 
			
		||||
  floor_generator: ^1.4.0
 | 
			
		||||
  build_runner: ^2.4.12
 | 
			
		||||
  sqflite_common_ffi: ^2.3.3
 | 
			
		||||
  sqflite_common_ffi_web: ^0.4.3+1
 | 
			
		||||
  flutter_native_splash: ^2.4.1
 | 
			
		||||
  freezed: ^2.5.7
 | 
			
		||||
  json_serializable: ^6.8.0
 | 
			
		||||
  drift_dev: ^2.20.3
 | 
			
		||||
 | 
			
		||||
flutter:
 | 
			
		||||
  uses-material-design: true
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@
 | 
			
		||||
#include <protocol_handler_windows/protocol_handler_windows_plugin_c_api.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 <url_launcher_windows/url_launcher_windows.h>
 | 
			
		||||
 | 
			
		||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
 | 
			
		||||
@@ -57,6 +58,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
 | 
			
		||||
      registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin"));
 | 
			
		||||
  SharePlusWindowsPluginCApiRegisterWithRegistrar(
 | 
			
		||||
      registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
 | 
			
		||||
  Sqlite3FlutterLibsPluginRegisterWithRegistrar(
 | 
			
		||||
      registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin"));
 | 
			
		||||
  UrlLauncherWindowsRegisterWithRegistrar(
 | 
			
		||||
      registry->GetRegistrarForPlugin("UrlLauncherWindows"));
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
 | 
			
		||||
  protocol_handler_windows
 | 
			
		||||
  screen_brightness_windows
 | 
			
		||||
  share_plus
 | 
			
		||||
  sqlite3_flutter_libs
 | 
			
		||||
  url_launcher_windows
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user