🔀 Merge pull request '♻️ 使用 Drift 作为本地数据库' (#3) from refactor/drift-as-local-db into master
Reviewed-on: #3
This commit is contained in:
commit
40b885b27b
@ -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
|
||||
)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user