♻️ Use sqlite to replace hive #5

Merged
LittleSheep merged 4 commits from refactor/sqlite into master 2025-02-22 12:49:51 +00:00
33 changed files with 15432 additions and 482 deletions

View File

@ -683,5 +683,9 @@
"postChannelRealm": "Realms", "postChannelRealm": "Realms",
"postFilterReset": "Reset Filter", "postFilterReset": "Reset Filter",
"postFilterResetDescription": "Clear filter and show all posts.", "postFilterResetDescription": "Clear filter and show all posts.",
"postFilterWithCategory": "Viewing posts in {}" "postFilterWithCategory": "Viewing posts in {}",
"databaseSize": "Database Size",
"databaseDelete": "Delete Database",
"databaseDeleteDescription": "Remove the database on your local disk, the content will be fetched from server again.",
"databaseDeleted": "The local database has been deleted."
} }

View File

@ -681,5 +681,9 @@
"postChannelRealm": "领域", "postChannelRealm": "领域",
"postFilterReset": "重置过滤器", "postFilterReset": "重置过滤器",
"postFilterResetDescription": "清除过滤器并显示所有帖子。", "postFilterResetDescription": "清除过滤器并显示所有帖子。",
"postFilterWithCategory": "查看{}区中的帖子" "postFilterWithCategory": "查看{}区中的帖子",
"databaseSize": "数据库大小",
"databaseDelete": "删除数据库",
"databaseDeleteDescription": "删除本地数据库,内容将从服务器重新获取。",
"databaseDeleted": "本地数据库已被删除。"
} }

View File

@ -681,5 +681,9 @@
"postChannelRealm": "領域", "postChannelRealm": "領域",
"postFilterReset": "重置過濾器", "postFilterReset": "重置過濾器",
"postFilterResetDescription": "清除過濾器並顯示所有帖子。", "postFilterResetDescription": "清除過濾器並顯示所有帖子。",
"postFilterWithCategory": "查看{}區中的帖子" "postFilterWithCategory": "查看{}區中的帖子",
"databaseSize": "數據庫大小",
"databaseDelete": "刪除數據庫",
"databaseDeleteDescription": "刪除本地數據庫,內容將從服務器重新獲取。",
"databaseDeleted": "本地數據庫已被刪除。"
} }

View File

@ -681,5 +681,9 @@
"postChannelRealm": "領域", "postChannelRealm": "領域",
"postFilterReset": "重置過濾器", "postFilterReset": "重置過濾器",
"postFilterResetDescription": "清除過濾器並顯示所有帖子。", "postFilterResetDescription": "清除過濾器並顯示所有帖子。",
"postFilterWithCategory": "查看{}區中的帖子" "postFilterWithCategory": "查看{}區中的帖子",
"databaseSize": "數據庫大小",
"databaseDelete": "刪除數據庫",
"databaseDeleteDescription": "刪除本地數據庫,內容將從服務器重新獲取。",
"databaseDeleted": "本地數據庫已被刪除。"
} }

View File

@ -221,6 +221,25 @@ PODS:
- sqflite_darwin (0.0.4): - sqflite_darwin (0.0.4):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- sqlite3 (3.49.0):
- sqlite3/common (= 3.49.0)
- sqlite3/common (3.49.0)
- sqlite3/dbstatvtab (3.49.0):
- sqlite3/common
- sqlite3/fts5 (3.49.0):
- sqlite3/common
- sqlite3/perf-threadsafe (3.49.0):
- sqlite3/common
- sqlite3/rtree (3.49.0):
- sqlite3/common
- sqlite3_flutter_libs (0.0.1):
- Flutter
- FlutterMacOS
- sqlite3 (~> 3.49.0)
- sqlite3/dbstatvtab
- sqlite3/fts5
- sqlite3/perf-threadsafe
- sqlite3/rtree
- SwiftyGif (5.4.5) - SwiftyGif (5.4.5)
- url_launcher_ios (0.0.1): - url_launcher_ios (0.0.1):
- Flutter - Flutter
@ -268,6 +287,7 @@ DEPENDENCIES:
- share_plus (from `.symlinks/plugins/share_plus/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- video_compress (from `.symlinks/plugins/video_compress/ios`) - video_compress (from `.symlinks/plugins/video_compress/ios`)
- volume_controller (from `.symlinks/plugins/volume_controller/ios`) - volume_controller (from `.symlinks/plugins/volume_controller/ios`)
@ -294,6 +314,7 @@ SPEC REPOS:
- PromisesObjC - PromisesObjC
- SAMKeychain - SAMKeychain
- SDWebImage - SDWebImage
- sqlite3
- SwiftyGif - SwiftyGif
- WebRTC-SDK - WebRTC-SDK
@ -360,6 +381,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin" :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sqflite_darwin: sqflite_darwin:
:path: ".symlinks/plugins/sqflite_darwin/darwin" :path: ".symlinks/plugins/sqflite_darwin/darwin"
sqlite3_flutter_libs:
:path: ".symlinks/plugins/sqlite3_flutter_libs/darwin"
url_launcher_ios: url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios" :path: ".symlinks/plugins/url_launcher_ios/ios"
video_compress: video_compress:
@ -421,6 +444,8 @@ SPEC CHECKSUMS:
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
sqlite3: 4922312598b67e1825c6a6c821296dcbf6783046
sqlite3_flutter_libs: 069c435986dd4b63461aecd68f4b30be4a9e9daa
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
video_compress: fce97e4fb1dfd88175aa07d2ffc8a2f297f87fbe video_compress: fce97e4fb1dfd88175aa07d2ffc8a2f297f87fbe

View File

@ -2,11 +2,12 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:collection/collection.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:drift/drift.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:surface/database/database.dart';
import 'package:surface/providers/database.dart';
import 'package:surface/providers/sn_attachment.dart'; import 'package:surface/providers/sn_attachment.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/user_directory.dart'; import 'package:surface/providers/user_directory.dart';
@ -16,13 +17,13 @@ import 'package:surface/types/websocket.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
class ChatMessageController extends ChangeNotifier { class ChatMessageController extends ChangeNotifier {
static const kChatMessageBoxPrefix = 'nex_chat_messages_';
static const kSingleBatchLoadLimit = 100; static const kSingleBatchLoadLimit = 100;
late final SnNetworkProvider _sn; late final SnNetworkProvider _sn;
late final UserDirectoryProvider _ud; late final UserDirectoryProvider _ud;
late final WebSocketProvider _ws; late final WebSocketProvider _ws;
late final SnAttachmentProvider _attach; late final SnAttachmentProvider _attach;
late final DatabaseProvider _dt;
StreamSubscription? _wsSubscription; StreamSubscription? _wsSubscription;
@ -31,6 +32,7 @@ class ChatMessageController extends ChangeNotifier {
_ud = context.read<UserDirectoryProvider>(); _ud = context.read<UserDirectoryProvider>();
_ws = context.read<WebSocketProvider>(); _ws = context.read<WebSocketProvider>();
_attach = context.read<SnAttachmentProvider>(); _attach = context.read<SnAttachmentProvider>();
_dt = context.read<DatabaseProvider>();
} }
bool isPending = true; bool isPending = true;
@ -38,9 +40,9 @@ class ChatMessageController extends ChangeNotifier {
int? messageTotal; int? messageTotal;
bool get isAllLoaded => messageTotal != null && messages.length >= messageTotal!; bool get isAllLoaded =>
messageTotal != null && messages.length >= messageTotal!;
String? _boxKey;
SnChannel? channel; SnChannel? channel;
SnChannelMember? profile; SnChannelMember? profile;
@ -51,25 +53,17 @@ class ChatMessageController extends ChangeNotifier {
/// Stored as a list of nonce to provide the loading state /// Stored as a list of nonce to provide the loading state
final List<String> unconfirmedMessages = List.empty(growable: true); final List<String> unconfirmedMessages = List.empty(growable: true);
Box<SnChatMessage>? get _box => (_boxKey == null || isPending) ? null : Hive.box<SnChatMessage>(_boxKey!);
final List<SnChannelMember> typingMembers = List.empty(growable: true); final List<SnChannelMember> typingMembers = List.empty(growable: true);
final Map<int, Timer> typingInactiveTimer = {}; final Map<int, Timer> typingInactiveTimer = {};
Future<void> initialize(SnChannel chan) async { Future<void> initialize(SnChannel chan) async {
channel = chan; channel = chan;
// Initialize local data
_boxKey = '$kChatMessageBoxPrefix${chan.id}';
await Hive.openBox<SnChatMessage>(_boxKey!);
// Fetch channel profile // Fetch channel profile
final resp = await _sn.client.get( final resp = await _sn.client.get(
'/cgi/im/channels/${chan.keyPath}/me', '/cgi/im/channels/${chan.keyPath}/me',
); );
profile = SnChannelMember.fromJson( profile = SnChannelMember.fromJson(resp.data);
resp.data as Map<String, dynamic>,
);
_wsSubscription = _ws.pk.stream.listen((event) { _wsSubscription = _ws.pk.stream.listen((event) {
switch (event.method) { switch (event.method) {
@ -87,7 +81,8 @@ class ChatMessageController extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
typingInactiveTimer[member.id]?.cancel(); typingInactiveTimer[member.id]?.cancel();
typingInactiveTimer[member.id] = Timer(const Duration(seconds: 3), () { typingInactiveTimer[member.id] =
Timer(const Duration(seconds: 3), () {
typingMembers.removeWhere((x) => x.id == member.id); typingMembers.removeWhere((x) => x.id == member.id);
typingInactiveTimer.remove(member.id); typingInactiveTimer.remove(member.id);
notifyListeners(); notifyListeners();
@ -129,10 +124,16 @@ class ChatMessageController extends ChangeNotifier {
} }
Future<void> _saveMessageToLocal(Iterable<SnChatMessage> messages) async { Future<void> _saveMessageToLocal(Iterable<SnChatMessage> messages) async {
if (_box == null) return; await _dt.db.snLocalChatMessage.insertAll(
await _box!.putAll({ messages.map(
for (final message in messages) message.id: message, (ele) => SnLocalChatMessageCompanion.insert(
}); id: Value(ele.id),
content: ele,
channelId: channel!.id,
createdAt: Value(ele.createdAt),
),
),
onConflict: DoNothing());
} }
Future<void> _addUnconfirmedMessage(SnChatMessage message) async { Future<void> _addUnconfirmedMessage(SnChatMessage message) async {
@ -184,8 +185,21 @@ class ChatMessageController extends ChangeNotifier {
await _applyMessage(message); await _applyMessage(message);
notifyListeners(); notifyListeners();
if (_box == null) return; if (isCheckedUpdate) {
await _box!.put(message.id, message); await _dt.db.snLocalChatMessage.insertOne(
SnLocalChatMessageCompanion.insert(
id: Value(message.id),
content: message,
channelId: channel!.id,
createdAt: Value(message.createdAt),
),
onConflict: DoUpdate((_) => SnLocalChatMessageCompanion.custom(
content: Constant(jsonEncode(message.toJson())),
)),
);
} else {
incomeStrandedQueue.add(message);
}
} }
Future<void> _applyMessage(SnChatMessage message) async { Future<void> _applyMessage(SnChatMessage message) async {
@ -194,7 +208,8 @@ class ChatMessageController extends ChangeNotifier {
switch (message.type) { switch (message.type) {
case 'messages.edit': case 'messages.edit':
if (message.relatedEventId != null) { if (message.relatedEventId != null) {
final idx = messages.indexWhere((x) => x.id == message.relatedEventId); final idx =
messages.indexWhere((x) => x.id == message.relatedEventId);
if (idx != -1) { if (idx != -1) {
final newBody = message.body; final newBody = message.body;
newBody.remove('related_event'); newBody.remove('related_event');
@ -202,16 +217,24 @@ class ChatMessageController extends ChangeNotifier {
body: newBody, body: newBody,
updatedAt: message.updatedAt, updatedAt: message.updatedAt,
); );
if (_box!.containsKey(message.relatedEventId)) { if (message.relatedEventId != null) {
await _box!.put(message.relatedEventId, messages[idx]); await (_dt.db.snLocalChatMessage.update()
..where((e) => e.id.equals(message.relatedEventId!)))
.write(
SnLocalChatMessageCompanion.custom(
content: Constant(jsonEncode(messages[idx].toJson())),
),
);
} }
} }
} }
case 'messages.delete': case 'messages.delete':
if (message.relatedEventId != null) { if (message.relatedEventId != null) {
messages.removeWhere((x) => x.id == message.relatedEventId); messages.removeWhere((x) => x.id == message.relatedEventId);
if (_box!.containsKey(message.relatedEventId)) { if (message.relatedEventId != null) {
await _box!.delete(message.relatedEventId); await (_dt.db.snLocalChatMessage.delete()
..where((e) => e.id.equals(message.relatedEventId!)))
.go();
} }
} }
} }
@ -233,7 +256,8 @@ class ChatMessageController extends ChangeNotifier {
'algorithm': 'plain', 'algorithm': 'plain',
if (quoteId != null) 'quote_event': quoteId, if (quoteId != null) 'quote_event': quoteId,
if (relatedId != null) 'related_event': relatedId, if (relatedId != null) 'related_event': relatedId,
if (attachments != null && attachments.isNotEmpty) 'attachments': attachments, if (attachments != null && attachments.isNotEmpty)
'attachments': attachments,
}; };
// Mock the message locally // Mock the message locally
@ -287,20 +311,34 @@ class ChatMessageController extends ChangeNotifier {
} }
} }
bool isCheckedUpdate = false;
List<SnChatMessage> incomeStrandedQueue = List.empty(growable: true);
/// Check the local storage is up to date with the server. /// Check the local storage is up to date with the server.
/// If the local storage is not up to date, it will be updated. /// If the local storage is not up to date, it will be updated.
Future<void> checkUpdate() async { Future<void> checkUpdate() async {
if (_box == null) return;
if (_box!.isEmpty) return;
isLoading = true; isLoading = true;
notifyListeners(); notifyListeners();
final mostRecentMessage = await (_dt.db.snLocalChatMessage.select()
..limit(1)
..orderBy([
(e) =>
OrderingTerm(expression: e.createdAt, mode: OrderingMode.desc)
]))
.getSingleOrNull();
if (mostRecentMessage == null) {
// Initial load
await loadMessages(take: 20);
isCheckedUpdate = true;
return;
}
try { try {
final resp = await _sn.client.get( final resp = await _sn.client.get(
'/cgi/im/channels/${channel!.keyPath}/events/update', '/cgi/im/channels/${channel!.keyPath}/events/update',
queryParameters: { queryParameters: {
'pivot': _box!.values.last.id, 'pivot': mostRecentMessage.content.id,
}, },
); );
if (resp.data['up_to_date'] == true) return; if (resp.data['up_to_date'] == true) return;
@ -316,6 +354,12 @@ class ChatMessageController extends ChangeNotifier {
} finally { } finally {
await loadMessages(); await loadMessages();
isLoading = false; isLoading = false;
isCheckedUpdate = true;
_saveMessageToLocal(incomeStrandedQueue).then((_) {
incomeStrandedQueue.clear();
});
notifyListeners(); notifyListeners();
} }
} }
@ -324,13 +368,18 @@ class ChatMessageController extends ChangeNotifier {
/// If it was not found in local storage we will look it up in remote /// If it was not found in local storage we will look it up in remote
Future<SnChatMessage?> getMessage(int id) async { Future<SnChatMessage?> getMessage(int id) async {
SnChatMessage? out; SnChatMessage? out;
if (_box != null && _box!.containsKey(id)) { final local = await (_dt.db.snLocalChatMessage.select()
out = _box!.get(id); ..limit(1)
..where((e) => e.id.equals(id)))
.getSingleOrNull();
if (local != null) {
out = local.content;
} }
if (out == null) { if (out == null) {
try { try {
final resp = await _sn.client.get('/cgi/im/channels/${channel!.keyPath}/events/$id'); final resp = await _sn.client
.get('/cgi/im/channels/${channel!.keyPath}/events/$id');
out = SnChatMessage.fromJson(resp.data); out = SnChatMessage.fromJson(resp.data);
_saveMessageToLocal([out]); _saveMessageToLocal([out]);
} catch (_) { } catch (_) {
@ -364,16 +413,21 @@ class ChatMessageController extends ChangeNotifier {
bool forceLocal = false, bool forceLocal = false,
bool forceRemote = false, bool forceRemote = false,
}) async { }) async {
final localTotal = await _dt.db.snLocalChatMessage
.count(where: (e) => e.channelId.equals(channel!.id))
.getSingle();
late List<SnChatMessage> out; late List<SnChatMessage> out;
if (_box != null && (_box!.length >= take + offset || forceLocal) && !forceRemote) { if ((localTotal >= take + offset || forceLocal) && !forceRemote) {
out = _box!.keys final result = await (_dt.db.snLocalChatMessage.select()
.toList() ..where((e) => e.channelId.equals(channel!.id))
.cast<int>() ..orderBy([
.sorted((a, b) => b.compareTo(a)) (e) =>
.skip(offset) OrderingTerm(expression: e.createdAt, mode: OrderingMode.desc)
.take(take) ])
.map((key) => _box!.get(key)!) ..limit(take, offset: offset))
.toList(); .get();
out = result.map((e) => e.content).toList();
} else { } else {
final resp = await _sn.client.get( final resp = await _sn.client.get(
'/cgi/im/channels/${channel!.keyPath}/events', '/cgi/im/channels/${channel!.keyPath}/events',
@ -408,7 +462,8 @@ class ChatMessageController extends ChangeNotifier {
quoteEvent: quoteEvent, quoteEvent: quoteEvent,
attachments: attachments attachments: attachments
.where( .where(
(ele) => out[i].body['attachments']?.contains(ele?.rid) ?? false, (ele) =>
out[i].body['attachments']?.contains(ele?.rid) ?? false,
) )
.toList(), .toList(),
), ),
@ -416,7 +471,10 @@ class ChatMessageController extends ChangeNotifier {
} }
// Preload sender accounts // Preload sender accounts
final accountId = out.where((ele) => ele.sender.accountId >= 0).map((ele) => ele.sender.accountId).toSet(); final accountId = out
.where((ele) => ele.sender.accountId >= 0)
.map((ele) => ele.sender.accountId)
.toSet();
await _ud.listAccount(accountId); await _ud.listAccount(accountId);
return out; return out;
@ -443,7 +501,6 @@ class ChatMessageController extends ChangeNotifier {
@override @override
void dispose() { void dispose() {
_box?.close();
_wsSubscription?.cancel(); _wsSubscription?.cancel();
super.dispose(); super.dispose();
} }

74
lib/database/chat.dart Normal file
View File

@ -0,0 +1,74 @@
import 'dart:convert';
import 'package:drift/drift.dart';
import 'package:surface/types/chat.dart';
class SnChannelConverter extends TypeConverter<SnChannel, String>
with JsonTypeConverter2<SnChannel, String, Map<String, Object?>> {
const SnChannelConverter();
@override
SnChannel fromSql(String fromDb) {
return fromJson(jsonDecode(fromDb) as Map<String, dynamic>);
}
@override
String toSql(SnChannel value) {
return jsonEncode(toJson(value));
}
@override
SnChannel fromJson(Map<String, Object?> json) {
return SnChannel.fromJson(json);
}
@override
Map<String, Object?> toJson(SnChannel value) {
return value.toJson();
}
}
class SnLocalChatChannel extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get alias => text()();
TextColumn get content => text().map(const SnChannelConverter())();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
}
class SnMessageConverter extends TypeConverter<SnChatMessage, String>
with JsonTypeConverter2<SnChatMessage, String, Map<String, Object?>> {
const SnMessageConverter();
@override
SnChatMessage fromSql(String fromDb) {
return fromJson(jsonDecode(fromDb) as Map<String, dynamic>);
}
@override
String toSql(SnChatMessage value) {
return jsonEncode(toJson(value));
}
@override
SnChatMessage fromJson(Map<String, Object?> json) {
return SnChatMessage.fromJson(json);
}
@override
Map<String, Object?> toJson(SnChatMessage value) {
return value.toJson();
}
}
class SnLocalChatMessage extends Table {
IntColumn get id => integer().autoIncrement()();
IntColumn get channelId => integer()();
TextColumn get content => text().map(const SnMessageConverter())();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
}

View File

@ -0,0 +1,28 @@
import 'package:drift/drift.dart';
import 'package:drift_flutter/drift_flutter.dart';
import 'package:path_provider/path_provider.dart';
import 'package:surface/database/chat.dart';
import 'package:surface/types/chat.dart';
part 'database.g.dart';
@DriftDatabase(tables: [SnLocalChatChannel, SnLocalChatMessage])
class AppDatabase extends _$AppDatabase {
AppDatabase() : super(_openConnection());
@override
int get schemaVersion => 1;
static QueryExecutor _openConnection() {
return driftDatabase(
name: 'solar_network_data',
native: const DriftNativeOptions(
databaseDirectory: getApplicationSupportDirectory,
),
web: DriftWebOptions(
sqlite3Wasm: Uri.parse('sqlite3.wasm'),
driftWorker: Uri.parse('drift_worker.dart.js'),
),
);
}
}

View File

@ -0,0 +1,880 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'database.dart';
// ignore_for_file: type=lint
class $SnLocalChatChannelTable extends SnLocalChatChannel
with TableInfo<$SnLocalChatChannelTable, SnLocalChatChannelData> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
$SnLocalChatChannelTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _idMeta = const VerificationMeta('id');
@override
late final GeneratedColumn<int> id = GeneratedColumn<int>(
'id', aliasedName, false,
hasAutoIncrement: true,
type: DriftSqlType.int,
requiredDuringInsert: false,
defaultConstraints:
GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
static const VerificationMeta _aliasMeta = const VerificationMeta('alias');
@override
late final GeneratedColumn<String> alias = GeneratedColumn<String>(
'alias', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true);
static const VerificationMeta _contentMeta =
const VerificationMeta('content');
@override
late final GeneratedColumnWithTypeConverter<SnChannel, String> content =
GeneratedColumn<String>('content', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true)
.withConverter<SnChannel>($SnLocalChatChannelTable.$convertercontent);
static const VerificationMeta _createdAtMeta =
const VerificationMeta('createdAt');
@override
late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
'created_at', aliasedName, false,
type: DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: currentDateAndTime);
@override
List<GeneratedColumn> get $columns => [id, alias, content, createdAt];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'sn_local_chat_channel';
@override
VerificationContext validateIntegrity(
Insertable<SnLocalChatChannelData> instance,
{bool isInserting = false}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
}
if (data.containsKey('alias')) {
context.handle(
_aliasMeta, alias.isAcceptableOrUnknown(data['alias']!, _aliasMeta));
} else if (isInserting) {
context.missing(_aliasMeta);
}
context.handle(_contentMeta, const VerificationResult.success());
if (data.containsKey('created_at')) {
context.handle(_createdAtMeta,
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => {id};
@override
SnLocalChatChannelData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return SnLocalChatChannelData(
id: attachedDatabase.typeMapping
.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
alias: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}alias'])!,
content: $SnLocalChatChannelTable.$convertercontent.fromSql(
attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}content'])!),
createdAt: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
);
}
@override
$SnLocalChatChannelTable createAlias(String alias) {
return $SnLocalChatChannelTable(attachedDatabase, alias);
}
static JsonTypeConverter2<SnChannel, String, Map<String, Object?>>
$convertercontent = const SnChannelConverter();
}
class SnLocalChatChannelData extends DataClass
implements Insertable<SnLocalChatChannelData> {
final int id;
final String alias;
final SnChannel content;
final DateTime createdAt;
const SnLocalChatChannelData(
{required this.id,
required this.alias,
required this.content,
required this.createdAt});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<int>(id);
map['alias'] = Variable<String>(alias);
{
map['content'] = Variable<String>(
$SnLocalChatChannelTable.$convertercontent.toSql(content));
}
map['created_at'] = Variable<DateTime>(createdAt);
return map;
}
SnLocalChatChannelCompanion toCompanion(bool nullToAbsent) {
return SnLocalChatChannelCompanion(
id: Value(id),
alias: Value(alias),
content: Value(content),
createdAt: Value(createdAt),
);
}
factory SnLocalChatChannelData.fromJson(Map<String, dynamic> json,
{ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return SnLocalChatChannelData(
id: serializer.fromJson<int>(json['id']),
alias: serializer.fromJson<String>(json['alias']),
content: $SnLocalChatChannelTable.$convertercontent
.fromJson(serializer.fromJson<Map<String, Object?>>(json['content'])),
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'alias': serializer.toJson<String>(alias),
'content': serializer.toJson<Map<String, Object?>>(
$SnLocalChatChannelTable.$convertercontent.toJson(content)),
'createdAt': serializer.toJson<DateTime>(createdAt),
};
}
SnLocalChatChannelData copyWith(
{int? id, String? alias, SnChannel? content, DateTime? createdAt}) =>
SnLocalChatChannelData(
id: id ?? this.id,
alias: alias ?? this.alias,
content: content ?? this.content,
createdAt: createdAt ?? this.createdAt,
);
SnLocalChatChannelData copyWithCompanion(SnLocalChatChannelCompanion data) {
return SnLocalChatChannelData(
id: data.id.present ? data.id.value : this.id,
alias: data.alias.present ? data.alias.value : this.alias,
content: data.content.present ? data.content.value : this.content,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
);
}
@override
String toString() {
return (StringBuffer('SnLocalChatChannelData(')
..write('id: $id, ')
..write('alias: $alias, ')
..write('content: $content, ')
..write('createdAt: $createdAt')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(id, alias, content, createdAt);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is SnLocalChatChannelData &&
other.id == this.id &&
other.alias == this.alias &&
other.content == this.content &&
other.createdAt == this.createdAt);
}
class SnLocalChatChannelCompanion
extends UpdateCompanion<SnLocalChatChannelData> {
final Value<int> id;
final Value<String> alias;
final Value<SnChannel> content;
final Value<DateTime> createdAt;
const SnLocalChatChannelCompanion({
this.id = const Value.absent(),
this.alias = const Value.absent(),
this.content = const Value.absent(),
this.createdAt = const Value.absent(),
});
SnLocalChatChannelCompanion.insert({
this.id = const Value.absent(),
required String alias,
required SnChannel content,
this.createdAt = const Value.absent(),
}) : alias = Value(alias),
content = Value(content);
static Insertable<SnLocalChatChannelData> custom({
Expression<int>? id,
Expression<String>? alias,
Expression<String>? content,
Expression<DateTime>? createdAt,
}) {
return RawValuesInsertable({
if (id != null) 'id': id,
if (alias != null) 'alias': alias,
if (content != null) 'content': content,
if (createdAt != null) 'created_at': createdAt,
});
}
SnLocalChatChannelCompanion copyWith(
{Value<int>? id,
Value<String>? alias,
Value<SnChannel>? content,
Value<DateTime>? createdAt}) {
return SnLocalChatChannelCompanion(
id: id ?? this.id,
alias: alias ?? this.alias,
content: content ?? this.content,
createdAt: createdAt ?? this.createdAt,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<int>(id.value);
}
if (alias.present) {
map['alias'] = Variable<String>(alias.value);
}
if (content.present) {
map['content'] = Variable<String>(
$SnLocalChatChannelTable.$convertercontent.toSql(content.value));
}
if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('SnLocalChatChannelCompanion(')
..write('id: $id, ')
..write('alias: $alias, ')
..write('content: $content, ')
..write('createdAt: $createdAt')
..write(')'))
.toString();
}
}
class $SnLocalChatMessageTable extends SnLocalChatMessage
with TableInfo<$SnLocalChatMessageTable, SnLocalChatMessageData> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
$SnLocalChatMessageTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _idMeta = const VerificationMeta('id');
@override
late final GeneratedColumn<int> id = GeneratedColumn<int>(
'id', aliasedName, false,
hasAutoIncrement: true,
type: DriftSqlType.int,
requiredDuringInsert: false,
defaultConstraints:
GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
static const VerificationMeta _channelIdMeta =
const VerificationMeta('channelId');
@override
late final GeneratedColumn<int> channelId = GeneratedColumn<int>(
'channel_id', aliasedName, false,
type: DriftSqlType.int, requiredDuringInsert: true);
static const VerificationMeta _contentMeta =
const VerificationMeta('content');
@override
late final GeneratedColumnWithTypeConverter<SnChatMessage, String> content =
GeneratedColumn<String>('content', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true)
.withConverter<SnChatMessage>(
$SnLocalChatMessageTable.$convertercontent);
static const VerificationMeta _createdAtMeta =
const VerificationMeta('createdAt');
@override
late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
'created_at', aliasedName, false,
type: DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: currentDateAndTime);
@override
List<GeneratedColumn> get $columns => [id, channelId, content, createdAt];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'sn_local_chat_message';
@override
VerificationContext validateIntegrity(
Insertable<SnLocalChatMessageData> instance,
{bool isInserting = false}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
}
if (data.containsKey('channel_id')) {
context.handle(_channelIdMeta,
channelId.isAcceptableOrUnknown(data['channel_id']!, _channelIdMeta));
} else if (isInserting) {
context.missing(_channelIdMeta);
}
context.handle(_contentMeta, const VerificationResult.success());
if (data.containsKey('created_at')) {
context.handle(_createdAtMeta,
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => {id};
@override
SnLocalChatMessageData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return SnLocalChatMessageData(
id: attachedDatabase.typeMapping
.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
channelId: attachedDatabase.typeMapping
.read(DriftSqlType.int, data['${effectivePrefix}channel_id'])!,
content: $SnLocalChatMessageTable.$convertercontent.fromSql(
attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}content'])!),
createdAt: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
);
}
@override
$SnLocalChatMessageTable createAlias(String alias) {
return $SnLocalChatMessageTable(attachedDatabase, alias);
}
static JsonTypeConverter2<SnChatMessage, String, Map<String, Object?>>
$convertercontent = const SnMessageConverter();
}
class SnLocalChatMessageData extends DataClass
implements Insertable<SnLocalChatMessageData> {
final int id;
final int channelId;
final SnChatMessage content;
final DateTime createdAt;
const SnLocalChatMessageData(
{required this.id,
required this.channelId,
required this.content,
required this.createdAt});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<int>(id);
map['channel_id'] = Variable<int>(channelId);
{
map['content'] = Variable<String>(
$SnLocalChatMessageTable.$convertercontent.toSql(content));
}
map['created_at'] = Variable<DateTime>(createdAt);
return map;
}
SnLocalChatMessageCompanion toCompanion(bool nullToAbsent) {
return SnLocalChatMessageCompanion(
id: Value(id),
channelId: Value(channelId),
content: Value(content),
createdAt: Value(createdAt),
);
}
factory SnLocalChatMessageData.fromJson(Map<String, dynamic> json,
{ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return SnLocalChatMessageData(
id: serializer.fromJson<int>(json['id']),
channelId: serializer.fromJson<int>(json['channelId']),
content: $SnLocalChatMessageTable.$convertercontent
.fromJson(serializer.fromJson<Map<String, Object?>>(json['content'])),
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'channelId': serializer.toJson<int>(channelId),
'content': serializer.toJson<Map<String, Object?>>(
$SnLocalChatMessageTable.$convertercontent.toJson(content)),
'createdAt': serializer.toJson<DateTime>(createdAt),
};
}
SnLocalChatMessageData copyWith(
{int? id,
int? channelId,
SnChatMessage? content,
DateTime? createdAt}) =>
SnLocalChatMessageData(
id: id ?? this.id,
channelId: channelId ?? this.channelId,
content: content ?? this.content,
createdAt: createdAt ?? this.createdAt,
);
SnLocalChatMessageData copyWithCompanion(SnLocalChatMessageCompanion data) {
return SnLocalChatMessageData(
id: data.id.present ? data.id.value : this.id,
channelId: data.channelId.present ? data.channelId.value : this.channelId,
content: data.content.present ? data.content.value : this.content,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
);
}
@override
String toString() {
return (StringBuffer('SnLocalChatMessageData(')
..write('id: $id, ')
..write('channelId: $channelId, ')
..write('content: $content, ')
..write('createdAt: $createdAt')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(id, channelId, content, createdAt);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is SnLocalChatMessageData &&
other.id == this.id &&
other.channelId == this.channelId &&
other.content == this.content &&
other.createdAt == this.createdAt);
}
class SnLocalChatMessageCompanion
extends UpdateCompanion<SnLocalChatMessageData> {
final Value<int> id;
final Value<int> channelId;
final Value<SnChatMessage> content;
final Value<DateTime> createdAt;
const SnLocalChatMessageCompanion({
this.id = const Value.absent(),
this.channelId = const Value.absent(),
this.content = const Value.absent(),
this.createdAt = const Value.absent(),
});
SnLocalChatMessageCompanion.insert({
this.id = const Value.absent(),
required int channelId,
required SnChatMessage content,
this.createdAt = const Value.absent(),
}) : channelId = Value(channelId),
content = Value(content);
static Insertable<SnLocalChatMessageData> custom({
Expression<int>? id,
Expression<int>? channelId,
Expression<String>? content,
Expression<DateTime>? createdAt,
}) {
return RawValuesInsertable({
if (id != null) 'id': id,
if (channelId != null) 'channel_id': channelId,
if (content != null) 'content': content,
if (createdAt != null) 'created_at': createdAt,
});
}
SnLocalChatMessageCompanion copyWith(
{Value<int>? id,
Value<int>? channelId,
Value<SnChatMessage>? content,
Value<DateTime>? createdAt}) {
return SnLocalChatMessageCompanion(
id: id ?? this.id,
channelId: channelId ?? this.channelId,
content: content ?? this.content,
createdAt: createdAt ?? this.createdAt,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<int>(id.value);
}
if (channelId.present) {
map['channel_id'] = Variable<int>(channelId.value);
}
if (content.present) {
map['content'] = Variable<String>(
$SnLocalChatMessageTable.$convertercontent.toSql(content.value));
}
if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('SnLocalChatMessageCompanion(')
..write('id: $id, ')
..write('channelId: $channelId, ')
..write('content: $content, ')
..write('createdAt: $createdAt')
..write(')'))
.toString();
}
}
abstract class _$AppDatabase extends GeneratedDatabase {
_$AppDatabase(QueryExecutor e) : super(e);
$AppDatabaseManager get managers => $AppDatabaseManager(this);
late final $SnLocalChatChannelTable snLocalChatChannel =
$SnLocalChatChannelTable(this);
late final $SnLocalChatMessageTable snLocalChatMessage =
$SnLocalChatMessageTable(this);
@override
Iterable<TableInfo<Table, Object?>> get allTables =>
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
@override
List<DatabaseSchemaEntity> get allSchemaEntities =>
[snLocalChatChannel, snLocalChatMessage];
}
typedef $$SnLocalChatChannelTableCreateCompanionBuilder
= SnLocalChatChannelCompanion Function({
Value<int> id,
required String alias,
required SnChannel content,
Value<DateTime> createdAt,
});
typedef $$SnLocalChatChannelTableUpdateCompanionBuilder
= SnLocalChatChannelCompanion Function({
Value<int> id,
Value<String> alias,
Value<SnChannel> content,
Value<DateTime> createdAt,
});
class $$SnLocalChatChannelTableFilterComposer
extends Composer<_$AppDatabase, $SnLocalChatChannelTable> {
$$SnLocalChatChannelTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
ColumnFilters<int> get id => $composableBuilder(
column: $table.id, builder: (column) => ColumnFilters(column));
ColumnFilters<String> get alias => $composableBuilder(
column: $table.alias, builder: (column) => ColumnFilters(column));
ColumnWithTypeConverterFilters<SnChannel, SnChannel, String> get content =>
$composableBuilder(
column: $table.content,
builder: (column) => ColumnWithTypeConverterFilters(column));
ColumnFilters<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt, builder: (column) => ColumnFilters(column));
}
class $$SnLocalChatChannelTableOrderingComposer
extends Composer<_$AppDatabase, $SnLocalChatChannelTable> {
$$SnLocalChatChannelTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
ColumnOrderings<int> get id => $composableBuilder(
column: $table.id, builder: (column) => ColumnOrderings(column));
ColumnOrderings<String> get alias => $composableBuilder(
column: $table.alias, builder: (column) => ColumnOrderings(column));
ColumnOrderings<String> get content => $composableBuilder(
column: $table.content, builder: (column) => ColumnOrderings(column));
ColumnOrderings<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt, builder: (column) => ColumnOrderings(column));
}
class $$SnLocalChatChannelTableAnnotationComposer
extends Composer<_$AppDatabase, $SnLocalChatChannelTable> {
$$SnLocalChatChannelTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
GeneratedColumn<int> get id =>
$composableBuilder(column: $table.id, builder: (column) => column);
GeneratedColumn<String> get alias =>
$composableBuilder(column: $table.alias, builder: (column) => column);
GeneratedColumnWithTypeConverter<SnChannel, String> get content =>
$composableBuilder(column: $table.content, builder: (column) => column);
GeneratedColumn<DateTime> get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column);
}
class $$SnLocalChatChannelTableTableManager extends RootTableManager<
_$AppDatabase,
$SnLocalChatChannelTable,
SnLocalChatChannelData,
$$SnLocalChatChannelTableFilterComposer,
$$SnLocalChatChannelTableOrderingComposer,
$$SnLocalChatChannelTableAnnotationComposer,
$$SnLocalChatChannelTableCreateCompanionBuilder,
$$SnLocalChatChannelTableUpdateCompanionBuilder,
(
SnLocalChatChannelData,
BaseReferences<_$AppDatabase, $SnLocalChatChannelTable,
SnLocalChatChannelData>
),
SnLocalChatChannelData,
PrefetchHooks Function()> {
$$SnLocalChatChannelTableTableManager(
_$AppDatabase db, $SnLocalChatChannelTable table)
: super(TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
$$SnLocalChatChannelTableFilterComposer($db: db, $table: table),
createOrderingComposer: () =>
$$SnLocalChatChannelTableOrderingComposer($db: db, $table: table),
createComputedFieldComposer: () =>
$$SnLocalChatChannelTableAnnotationComposer(
$db: db, $table: table),
updateCompanionCallback: ({
Value<int> id = const Value.absent(),
Value<String> alias = const Value.absent(),
Value<SnChannel> content = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(),
}) =>
SnLocalChatChannelCompanion(
id: id,
alias: alias,
content: content,
createdAt: createdAt,
),
createCompanionCallback: ({
Value<int> id = const Value.absent(),
required String alias,
required SnChannel content,
Value<DateTime> createdAt = const Value.absent(),
}) =>
SnLocalChatChannelCompanion.insert(
id: id,
alias: alias,
content: content,
createdAt: createdAt,
),
withReferenceMapper: (p0) => p0
.map((e) => (e.readTable(table), BaseReferences(db, table, e)))
.toList(),
prefetchHooksCallback: null,
));
}
typedef $$SnLocalChatChannelTableProcessedTableManager = ProcessedTableManager<
_$AppDatabase,
$SnLocalChatChannelTable,
SnLocalChatChannelData,
$$SnLocalChatChannelTableFilterComposer,
$$SnLocalChatChannelTableOrderingComposer,
$$SnLocalChatChannelTableAnnotationComposer,
$$SnLocalChatChannelTableCreateCompanionBuilder,
$$SnLocalChatChannelTableUpdateCompanionBuilder,
(
SnLocalChatChannelData,
BaseReferences<_$AppDatabase, $SnLocalChatChannelTable,
SnLocalChatChannelData>
),
SnLocalChatChannelData,
PrefetchHooks Function()>;
typedef $$SnLocalChatMessageTableCreateCompanionBuilder
= SnLocalChatMessageCompanion Function({
Value<int> id,
required int channelId,
required SnChatMessage content,
Value<DateTime> createdAt,
});
typedef $$SnLocalChatMessageTableUpdateCompanionBuilder
= SnLocalChatMessageCompanion Function({
Value<int> id,
Value<int> channelId,
Value<SnChatMessage> content,
Value<DateTime> createdAt,
});
class $$SnLocalChatMessageTableFilterComposer
extends Composer<_$AppDatabase, $SnLocalChatMessageTable> {
$$SnLocalChatMessageTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
ColumnFilters<int> get id => $composableBuilder(
column: $table.id, builder: (column) => ColumnFilters(column));
ColumnFilters<int> get channelId => $composableBuilder(
column: $table.channelId, builder: (column) => ColumnFilters(column));
ColumnWithTypeConverterFilters<SnChatMessage, SnChatMessage, String>
get content => $composableBuilder(
column: $table.content,
builder: (column) => ColumnWithTypeConverterFilters(column));
ColumnFilters<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt, builder: (column) => ColumnFilters(column));
}
class $$SnLocalChatMessageTableOrderingComposer
extends Composer<_$AppDatabase, $SnLocalChatMessageTable> {
$$SnLocalChatMessageTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
ColumnOrderings<int> get id => $composableBuilder(
column: $table.id, builder: (column) => ColumnOrderings(column));
ColumnOrderings<int> get channelId => $composableBuilder(
column: $table.channelId, builder: (column) => ColumnOrderings(column));
ColumnOrderings<String> get content => $composableBuilder(
column: $table.content, builder: (column) => ColumnOrderings(column));
ColumnOrderings<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt, builder: (column) => ColumnOrderings(column));
}
class $$SnLocalChatMessageTableAnnotationComposer
extends Composer<_$AppDatabase, $SnLocalChatMessageTable> {
$$SnLocalChatMessageTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
GeneratedColumn<int> get id =>
$composableBuilder(column: $table.id, builder: (column) => column);
GeneratedColumn<int> get channelId =>
$composableBuilder(column: $table.channelId, builder: (column) => column);
GeneratedColumnWithTypeConverter<SnChatMessage, String> get content =>
$composableBuilder(column: $table.content, builder: (column) => column);
GeneratedColumn<DateTime> get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column);
}
class $$SnLocalChatMessageTableTableManager extends RootTableManager<
_$AppDatabase,
$SnLocalChatMessageTable,
SnLocalChatMessageData,
$$SnLocalChatMessageTableFilterComposer,
$$SnLocalChatMessageTableOrderingComposer,
$$SnLocalChatMessageTableAnnotationComposer,
$$SnLocalChatMessageTableCreateCompanionBuilder,
$$SnLocalChatMessageTableUpdateCompanionBuilder,
(
SnLocalChatMessageData,
BaseReferences<_$AppDatabase, $SnLocalChatMessageTable,
SnLocalChatMessageData>
),
SnLocalChatMessageData,
PrefetchHooks Function()> {
$$SnLocalChatMessageTableTableManager(
_$AppDatabase db, $SnLocalChatMessageTable table)
: super(TableManagerState(
db: db,
table: table,
createFilteringComposer: () =>
$$SnLocalChatMessageTableFilterComposer($db: db, $table: table),
createOrderingComposer: () =>
$$SnLocalChatMessageTableOrderingComposer($db: db, $table: table),
createComputedFieldComposer: () =>
$$SnLocalChatMessageTableAnnotationComposer(
$db: db, $table: table),
updateCompanionCallback: ({
Value<int> id = const Value.absent(),
Value<int> channelId = const Value.absent(),
Value<SnChatMessage> content = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(),
}) =>
SnLocalChatMessageCompanion(
id: id,
channelId: channelId,
content: content,
createdAt: createdAt,
),
createCompanionCallback: ({
Value<int> id = const Value.absent(),
required int channelId,
required SnChatMessage content,
Value<DateTime> createdAt = const Value.absent(),
}) =>
SnLocalChatMessageCompanion.insert(
id: id,
channelId: channelId,
content: content,
createdAt: createdAt,
),
withReferenceMapper: (p0) => p0
.map((e) => (e.readTable(table), BaseReferences(db, table, e)))
.toList(),
prefetchHooksCallback: null,
));
}
typedef $$SnLocalChatMessageTableProcessedTableManager = ProcessedTableManager<
_$AppDatabase,
$SnLocalChatMessageTable,
SnLocalChatMessageData,
$$SnLocalChatMessageTableFilterComposer,
$$SnLocalChatMessageTableOrderingComposer,
$$SnLocalChatMessageTableAnnotationComposer,
$$SnLocalChatMessageTableCreateCompanionBuilder,
$$SnLocalChatMessageTableUpdateCompanionBuilder,
(
SnLocalChatMessageData,
BaseReferences<_$AppDatabase, $SnLocalChatMessageTable,
SnLocalChatMessageData>
),
SnLocalChatMessageData,
PrefetchHooks Function()>;
class $AppDatabaseManager {
final _$AppDatabase _db;
$AppDatabaseManager(this._db);
$$SnLocalChatChannelTableTableManager get snLocalChatChannel =>
$$SnLocalChatChannelTableTableManager(_db, _db.snLocalChatChannel);
$$SnLocalChatMessageTableTableManager get snLocalChatMessage =>
$$SnLocalChatMessageTableTableManager(_db, _db.snLocalChatMessage);
}

View File

@ -0,0 +1,8 @@
import 'package:drift/wasm.dart';
// Use `dart compile js -O4 ./drift_worker.dart` to compile this file.
// And place it in the web/ directory.
// When compiled with dart2js, this file defines a dedicated or shared web
// worker used by drift.
void main() => WasmDatabase.workerMainForOpen();

View File

@ -13,7 +13,6 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:hotkey_manager/hotkey_manager.dart'; import 'package:hotkey_manager/hotkey_manager.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -24,6 +23,7 @@ import 'package:surface/firebase_options.dart';
import 'package:surface/providers/channel.dart'; import 'package:surface/providers/channel.dart';
import 'package:surface/providers/chat_call.dart'; import 'package:surface/providers/chat_call.dart';
import 'package:surface/providers/config.dart'; import 'package:surface/providers/config.dart';
import 'package:surface/providers/database.dart';
import 'package:surface/providers/link_preview.dart'; import 'package:surface/providers/link_preview.dart';
import 'package:surface/providers/navigation.dart'; import 'package:surface/providers/navigation.dart';
import 'package:surface/providers/notification.dart'; import 'package:surface/providers/notification.dart';
@ -40,8 +40,6 @@ import 'package:surface/providers/userinfo.dart';
import 'package:surface/providers/websocket.dart'; import 'package:surface/providers/websocket.dart';
import 'package:surface/providers/widget.dart'; import 'package:surface/providers/widget.dart';
import 'package:surface/router.dart'; import 'package:surface/router.dart';
import 'package:surface/types/chat.dart';
import 'package:surface/types/realm.dart';
import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy; import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy;
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
import 'package:tray_manager/tray_manager.dart'; import 'package:tray_manager/tray_manager.dart';
@ -82,12 +80,6 @@ void main() async {
await EasyLocalization.ensureInitialized(); await EasyLocalization.ensureInitialized();
await Hive.initFlutter();
Hive.registerAdapter(SnChannelImplAdapter());
Hive.registerAdapter(SnRealmImplAdapter());
Hive.registerAdapter(SnChannelMemberImplAdapter());
Hive.registerAdapter(SnChatMessageImplAdapter());
if (!kIsWeb && !Platform.isLinux) { if (!kIsWeb && !Platform.isLinux) {
await Firebase.initializeApp( await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform, options: DefaultFirebaseOptions.currentPlatform,
@ -114,7 +106,8 @@ void main() async {
} }
if (!kIsWeb && Platform.isAndroid) { if (!kIsWeb && Platform.isAndroid) {
final ImagePickerPlatform imagePickerImplementation = ImagePickerPlatform.instance; final ImagePickerPlatform imagePickerImplementation =
ImagePickerPlatform.instance;
if (imagePickerImplementation is ImagePickerAndroid) { if (imagePickerImplementation is ImagePickerAndroid) {
imagePickerImplementation.useAndroidPhotoPicker = true; imagePickerImplementation.useAndroidPhotoPicker = true;
} }
@ -142,6 +135,9 @@ class SolianApp extends StatelessWidget {
assetLoader: JsonAssetLoader(), assetLoader: JsonAssetLoader(),
child: MultiProvider( child: MultiProvider(
providers: [ providers: [
// Infrastructure layer
Provider(create: (ctx) => DatabaseProvider(ctx)),
// System extensions layer // System extensions layer
Provider(create: (ctx) => HomeWidgetProvider(ctx)), Provider(create: (ctx) => HomeWidgetProvider(ctx)),
@ -230,7 +226,8 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
if (prefs.containsKey('first_boot_time')) { if (prefs.containsKey('first_boot_time')) {
final rawTime = prefs.getString('first_boot_time'); final rawTime = prefs.getString('first_boot_time');
final time = DateTime.tryParse(rawTime ?? ''); final time = DateTime.tryParse(rawTime ?? '');
if (time != null && time.isBefore(DateTime.now().subtract(const Duration(days: 3)))) { if (time != null &&
time.isBefore(DateTime.now().subtract(const Duration(days: 3)))) {
final inAppReview = InAppReview.instance; final inAppReview = InAppReview.instance;
if (prefs.getBool('rating_requested') == true) return; if (prefs.getBool('rating_requested') == true) return;
if (await inAppReview.isAvailable()) { if (await inAppReview.isAvailable()) {
@ -258,13 +255,18 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
).get( ).get(
'https://git.solsynth.dev/api/v1/repos/HyperNet/Surface/tags?page=1&limit=1', 'https://git.solsynth.dev/api/v1/repos/HyperNet/Surface/tags?page=1&limit=1',
); );
final remoteVersionString = (resp.data as List).firstOrNull?['name'] ?? '0.0.0+0'; final remoteVersionString =
(resp.data as List).firstOrNull?['name'] ?? '0.0.0+0';
final remoteVersion = Version.parse(remoteVersionString.split('+').first); final remoteVersion = Version.parse(remoteVersionString.split('+').first);
final localVersion = Version.parse(localVersionString.split('+').first); final localVersion = Version.parse(localVersionString.split('+').first);
final remoteBuildNumber = int.tryParse(remoteVersionString.split('+').last) ?? 0; final remoteBuildNumber =
final localBuildNumber = int.tryParse(localVersionString.split('+').last) ?? 0; int.tryParse(remoteVersionString.split('+').last) ?? 0;
final localBuildNumber =
int.tryParse(localVersionString.split('+').last) ?? 0;
log("[Update] Local: $localVersionString, Remote: $remoteVersionString"); log("[Update] Local: $localVersionString, Remote: $remoteVersionString");
if ((remoteVersion > localVersion || remoteBuildNumber > localBuildNumber) && mounted) { if ((remoteVersion > localVersion ||
remoteBuildNumber > localBuildNumber) &&
mounted) {
final config = context.read<ConfigProvider>(); final config = context.read<ConfigProvider>();
config.setUpdate(remoteVersionString); config.setUpdate(remoteVersionString);
log("[Update] Update available: $remoteVersionString"); log("[Update] Update available: $remoteVersionString");
@ -331,7 +333,9 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
Future<void> _trayInitialization() async { Future<void> _trayInitialization() async {
if (kIsWeb || Platform.isAndroid || Platform.isIOS) return; if (kIsWeb || Platform.isAndroid || Platform.isIOS) return;
final icon = Platform.isWindows ? 'assets/icon/tray-icon.ico' : 'assets/icon/tray-icon.png'; final icon = Platform.isWindows
? 'assets/icon/tray-icon.ico'
: 'assets/icon/tray-icon.png';
final appVersion = await PackageInfo.fromPlatform(); final appVersion = await PackageInfo.fromPlatform();
trayManager.addListener(this); trayManager.addListener(this);

View File

@ -1,7 +1,10 @@
import 'dart:convert';
import 'package:drift/drift.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:surface/controllers/chat_message_controller.dart'; import 'package:surface/database/database.dart';
import 'package:surface/providers/database.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/user_directory.dart'; import 'package:surface/providers/user_directory.dart';
import 'package:surface/types/chat.dart'; import 'package:surface/types/chat.dart';
@ -12,24 +15,32 @@ class ChatChannelProvider extends ChangeNotifier {
late final SnNetworkProvider _sn; late final SnNetworkProvider _sn;
late final UserDirectoryProvider _ud; late final UserDirectoryProvider _ud;
late final DatabaseProvider _dt;
Box<SnChannel>? get _channelBox => Hive.box<SnChannel>(kChatChannelBoxName);
ChatChannelProvider(BuildContext context) { ChatChannelProvider(BuildContext context) {
_sn = context.read<SnNetworkProvider>(); _sn = context.read<SnNetworkProvider>();
_ud = context.read<UserDirectoryProvider>(); _ud = context.read<UserDirectoryProvider>();
_initializeLocalData(); _dt = context.read<DatabaseProvider>();
}
Future<void> _initializeLocalData() async {
await Hive.openBox<SnChannel>(kChatChannelBoxName);
} }
Future<void> _saveChannelToLocal(Iterable<SnChannel> channels) async { Future<void> _saveChannelToLocal(Iterable<SnChannel> channels) async {
if (_channelBox == null) return; await Future.wait(
await _channelBox!.putAll({ channels.map(
for (final channel in channels) channel.key: channel, (ele) => _dt.db.snLocalChatChannel.insertOne(
}); SnLocalChatChannelCompanion.insert(
id: Value(ele.id),
alias: ele.key,
content: ele,
createdAt: Value(ele.createdAt),
),
onConflict: DoUpdate(
(_) => SnLocalChatChannelCompanion.custom(
content: Constant(jsonEncode(ele.toJson())),
),
),
),
),
);
} }
Future<List<SnChannel>> _fetchChannelsFromServer({ Future<List<SnChannel>> _fetchChannelsFromServer({
@ -54,12 +65,13 @@ class ChatChannelProvider extends ChangeNotifier {
/// It will use the local storage as much as possible. /// It will use the local storage as much as possible.
/// The alias should include the scope, formatted as `scope:alias`. /// The alias should include the scope, formatted as `scope:alias`.
Future<SnChannel> getChannel(String key) async { Future<SnChannel> getChannel(String key) async {
if (_channelBox != null) { final local = await (_dt.db.snLocalChatChannel.select()
final local = _channelBox!.get(key); ..where((e) => e.alias.equals(key)))
if (local != null) return local; .getSingleOrNull();
} if (local != null) return local.content;
var resp = await _sn.client.get('/cgi/im/channels/$key'); var resp =
await _sn.client.get('/cgi/im/channels/${key.replaceAll(':', '/')}');
var out = SnChannel.fromJson(resp.data); var out = SnChannel.fromJson(resp.data);
// Preload realm of the channel // Preload realm of the channel
@ -77,8 +89,19 @@ class ChatChannelProvider extends ChangeNotifier {
/// And the second time is when the data was fetched from the server. /// And the second time is when the data was fetched from the server.
/// But there is some exception that will only cause one of them to be emitted. /// But there is some exception that will only cause one of them to be emitted.
/// Like the local storage is broken or the server is down. /// Like the local storage is broken or the server is down.
Stream<List<SnChannel>> fetchChannels() async* { Stream<List<SnChannel>> fetchChannels(
if (_channelBox != null) yield _channelBox!.values.toList(); {bool noRemote = false, bool noLocal = false}) async* {
if (!noLocal) {
final local = await (_dt.db.snLocalChatChannel.select()
..orderBy([
(e) =>
OrderingTerm(expression: e.createdAt, mode: OrderingMode.desc)
]))
.get();
yield local.map((e) => e.content).toList();
}
if (noRemote) return;
var resp = await _sn.client.get('/cgi/id/realms/me/available'); var resp = await _sn.client.get('/cgi/id/realms/me/available');
final realms = List<SnRealm>.from( final realms = List<SnRealm>.from(
@ -120,23 +143,23 @@ class ChatChannelProvider extends ChangeNotifier {
Future<List<SnChatMessage>> getLastMessages( Future<List<SnChatMessage>> getLastMessages(
Iterable<SnChannel> channels, Iterable<SnChannel> channels,
) async { ) async {
final result = List<SnChatMessage>.empty(growable: true); final result = List<Future<SnLocalChatMessageData?>>.empty(growable: true);
for (final channel in channels) { for (final channel in channels) {
final channelBox = await Hive.openBox<SnChatMessage>( final out = (_dt.db.snLocalChatMessage.select()
'${ChatMessageController.kChatMessageBoxPrefix}${channel.id}', ..where((e) => e.channelId.equals(channel.id))
); ..orderBy([
final lastMessage = (e) =>
channelBox.isNotEmpty ? channelBox.values.reduce((a, b) => a.createdAt.isAfter(b.createdAt) ? a : b) : null; OrderingTerm(expression: e.createdAt, mode: OrderingMode.desc)
if (lastMessage != null) result.add(lastMessage); ])
channelBox.close(); ..limit(1))
.getSingleOrNull();
result.add(out);
} }
await _ud.listAccount(result.map((ele) => ele.sender.accountId).toSet()); final out = (await Future.wait(result))
return result; .where((e) => e != null)
} .map((e) => e!.content)
.toList();
@override await _ud.listAccount(out.map((ele) => ele.sender.accountId).toSet());
void dispose() { return out;
_channelBox?.close();
super.dispose();
} }
} }

View File

@ -0,0 +1,31 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:path/path.dart' show join;
import 'package:path_provider/path_provider.dart';
import 'package:surface/database/database.dart';
class DatabaseProvider {
late AppDatabase db;
DatabaseProvider(BuildContext context) {
db = AppDatabase();
}
Future<int> getDatabaseSize() async {
if (kIsWeb) return 0;
final basepath = await getApplicationSupportDirectory();
return await File(join(basepath.path, 'solar_network_data.sqlite'))
.length();
}
Future<void> removeDatabase() async {
if (kIsWeb) return;
final basepath = await getApplicationSupportDirectory();
final file = File(join(basepath.path, 'solar_network_data.sqlite'));
db.close();
await file.delete();
db = AppDatabase();
}
}

View File

@ -4,10 +4,10 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/database.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/userinfo.dart'; import 'package:surface/providers/userinfo.dart';
import 'package:surface/providers/websocket.dart'; import 'package:surface/providers/websocket.dart';
@ -45,7 +45,8 @@ class AccountScreen extends StatelessWidget {
? Stack( ? Stack(
fit: StackFit.expand, fit: StackFit.expand,
children: [ children: [
AutoResizeUniversalImage(sn.getAttachmentUrl(ua.user!.banner), fit: BoxFit.cover), AutoResizeUniversalImage(sn.getAttachmentUrl(ua.user!.banner),
fit: BoxFit.cover),
Positioned( Positioned(
top: 0, top: 0,
left: 0, left: 0,
@ -79,7 +80,9 @@ class AccountScreen extends StatelessWidget {
], ],
), ),
body: SingleChildScrollView( body: SingleChildScrollView(
child: ua.isAuthorized ? _AuthorizedAccountScreen() : _UnauthorizedAccountScreen(), child: ua.isAuthorized
? _AuthorizedAccountScreen()
: _UnauthorizedAccountScreen(),
), ),
); );
} }
@ -115,12 +118,15 @@ class _AuthorizedAccountScreen extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.baseline, crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic, textBaseline: TextBaseline.alphabetic,
children: [ children: [
Text(ua.user!.nick).textStyle(Theme.of(context).textTheme.titleLarge!), Text(ua.user!.nick)
.textStyle(Theme.of(context).textTheme.titleLarge!),
const Gap(4), const Gap(4),
Text('@${ua.user!.name}').textStyle(Theme.of(context).textTheme.bodySmall!), Text('@${ua.user!.name}')
.textStyle(Theme.of(context).textTheme.bodySmall!),
], ],
), ),
Text(ua.user!.description).textStyle(Theme.of(context).textTheme.bodyMedium!), Text(ua.user!.description)
.textStyle(Theme.of(context).textTheme.bodyMedium!),
], ],
), ),
); );
@ -193,8 +199,7 @@ class _AuthorizedAccountScreen extends StatelessWidget {
ua.logoutUser(); ua.logoutUser();
final ws = context.read<WebSocketProvider>(); final ws = context.read<WebSocketProvider>();
ws.disconnect(); ws.disconnect();
await Hive.deleteFromDisk(); context.read<DatabaseProvider>().removeDatabase();
await Hive.initFlutter();
}, },
), ),
], ],
@ -220,7 +225,9 @@ class _UnauthorizedAccountScreen extends StatelessWidget {
child: Icon(Symbols.waving_hand, size: 28), child: Icon(Symbols.waving_hand, size: 28),
), ),
const Gap(8), const Gap(8),
Text('accountIntroTitle').tr().textStyle(Theme.of(context).textTheme.titleLarge!), Text('accountIntroTitle')
.tr()
.textStyle(Theme.of(context).textTheme.titleLarge!),
Text('accountIntroSubtitle').tr(), Text('accountIntroSubtitle').tr(),
], ],
).padding(all: 20), ).padding(all: 20),

View File

@ -6,7 +6,9 @@ import 'package:go_router/go_router.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:surface/providers/channel.dart'; import 'package:surface/providers/channel.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/user_directory.dart'; import 'package:surface/providers/user_directory.dart';
import 'package:surface/providers/userinfo.dart';
import 'package:surface/types/chat.dart'; import 'package:surface/types/chat.dart';
import 'package:surface/widgets/account/account_image.dart'; import 'package:surface/widgets/account/account_image.dart';
import 'package:surface/widgets/account/account_select.dart'; import 'package:surface/widgets/account/account_select.dart';
@ -17,9 +19,6 @@ import 'package:surface/widgets/navigation/app_scaffold.dart';
import 'package:surface/widgets/unauthorized_hint.dart'; import 'package:surface/widgets/unauthorized_hint.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
import '../providers/sn_network.dart';
import '../providers/userinfo.dart';
class ChatScreen extends StatefulWidget { class ChatScreen extends StatefulWidget {
const ChatScreen({super.key}); const ChatScreen({super.key});
@ -35,7 +34,7 @@ class _ChatScreenState extends State<ChatScreen> {
List<SnChannel>? _channels; List<SnChannel>? _channels;
Map<int, SnChatMessage>? _lastMessages; Map<int, SnChatMessage>? _lastMessages;
void _refreshChannels() { void _refreshChannels({bool noRemote = false}) {
final ua = context.read<UserProvider>(); final ua = context.read<UserProvider>();
if (!ua.isAuthorized) { if (!ua.isAuthorized) {
setState(() => _isBusy = false); setState(() => _isBusy = false);
@ -43,12 +42,15 @@ class _ChatScreenState extends State<ChatScreen> {
} }
final chan = context.read<ChatChannelProvider>(); final chan = context.read<ChatChannelProvider>();
chan.fetchChannels().listen((channels) async { chan.fetchChannels(noRemote: noRemote).listen((channels) async {
final lastMessages = await chan.getLastMessages(channels); final lastMessages = await chan.getLastMessages(channels);
_lastMessages = {for (final val in lastMessages) val.channelId: val}; _lastMessages = {for (final val in lastMessages) val.channelId: val};
channels.sort((a, b) { channels.sort((a, b) {
if (_lastMessages!.containsKey(a.id) && _lastMessages!.containsKey(b.id)) { if (_lastMessages!.containsKey(a.id) &&
return _lastMessages![b.id]!.createdAt.compareTo(_lastMessages![a.id]!.createdAt); _lastMessages!.containsKey(b.id)) {
return _lastMessages![b.id]!
.createdAt
.compareTo(_lastMessages![a.id]!.createdAt);
} }
if (_lastMessages!.containsKey(a.id)) return -1; if (_lastMessages!.containsKey(a.id)) return -1;
if (_lastMessages!.containsKey(b.id)) return 1; if (_lastMessages!.containsKey(b.id)) return 1;
@ -86,7 +88,8 @@ class _ChatScreenState extends State<ChatScreen> {
void _newDirectMessage() async { void _newDirectMessage() async {
final user = await showModalBottomSheet( final user = await showModalBottomSheet(
context: context, context: context,
builder: (context) => AccountSelect(title: 'channelNewDirectMessage'.tr()), builder: (context) =>
AccountSelect(title: 'channelNewDirectMessage'.tr()),
); );
if (user == null) return; if (user == null) return;
if (!mounted) return; if (!mounted) return;
@ -98,7 +101,8 @@ class _ChatScreenState extends State<ChatScreen> {
await sn.client.post('/cgi/im/channels/global/dm', data: { await sn.client.post('/cgi/im/channels/global/dm', data: {
'alias': uuid.v4().replaceAll('-', '').substring(0, 12), 'alias': uuid.v4().replaceAll('-', '').substring(0, 12),
'name': 'DM', 'name': 'DM',
'description': 'A direct message channel between @${ua.user?.name} and @${user.name}', 'description':
'A direct message channel between @${ua.user?.name} and @${user.name}',
'related_user': user.id, 'related_user': user.id,
}); });
_fabKey.currentState!.toggle(); _fabKey.currentState!.toggle();
@ -144,20 +148,27 @@ class _ChatScreenState extends State<ChatScreen> {
type: ExpandableFabType.up, type: ExpandableFabType.up,
childrenAnimation: ExpandableFabAnimation.none, childrenAnimation: ExpandableFabAnimation.none,
overlayStyle: ExpandableFabOverlayStyle( overlayStyle: ExpandableFabOverlayStyle(
color: Theme.of(context).colorScheme.surface.withAlpha((255 * 0.5).round()), color: Theme.of(context)
.colorScheme
.surface
.withAlpha((255 * 0.5).round()),
), ),
openButtonBuilder: RotateFloatingActionButtonBuilder( openButtonBuilder: RotateFloatingActionButtonBuilder(
child: const Icon(Symbols.add, size: 28), child: const Icon(Symbols.add, size: 28),
fabSize: ExpandableFabSize.regular, fabSize: ExpandableFabSize.regular,
foregroundColor: Theme.of(context).floatingActionButtonTheme.foregroundColor, foregroundColor:
backgroundColor: Theme.of(context).floatingActionButtonTheme.backgroundColor, Theme.of(context).floatingActionButtonTheme.foregroundColor,
backgroundColor:
Theme.of(context).floatingActionButtonTheme.backgroundColor,
shape: const CircleBorder(), shape: const CircleBorder(),
), ),
closeButtonBuilder: DefaultFloatingActionButtonBuilder( closeButtonBuilder: DefaultFloatingActionButtonBuilder(
child: const Icon(Symbols.close, size: 28), child: const Icon(Symbols.close, size: 28),
fabSize: ExpandableFabSize.regular, fabSize: ExpandableFabSize.regular,
foregroundColor: Theme.of(context).floatingActionButtonTheme.foregroundColor, foregroundColor:
backgroundColor: Theme.of(context).floatingActionButtonTheme.backgroundColor, Theme.of(context).floatingActionButtonTheme.foregroundColor,
backgroundColor:
Theme.of(context).floatingActionButtonTheme.backgroundColor,
shape: const CircleBorder(), shape: const CircleBorder(),
), ),
children: [ children: [
@ -208,13 +219,17 @@ class _ChatScreenState extends State<ChatScreen> {
final lastMessage = _lastMessages?[channel.id]; final lastMessage = _lastMessages?[channel.id];
if (channel.type == 1) { if (channel.type == 1) {
final otherMember = channel.members?.cast<SnChannelMember?>().firstWhere( final otherMember =
channel.members?.cast<SnChannelMember?>().firstWhere(
(ele) => ele?.accountId != ua.user?.id, (ele) => ele?.accountId != ua.user?.id,
orElse: () => null, orElse: () => null,
); );
return ListTile( return ListTile(
title: Text(ud.getAccountFromCache(otherMember?.accountId)?.nick ?? channel.name), title: Text(ud
.getAccountFromCache(otherMember?.accountId)
?.nick ??
channel.name),
subtitle: lastMessage != null subtitle: lastMessage != null
? Text( ? Text(
'${ud.getAccountFromCache(lastMessage.sender.accountId)?.nick}: ${lastMessage.body['text'] ?? 'Unable preview'}', '${ud.getAccountFromCache(lastMessage.sender.accountId)?.nick}: ${lastMessage.body['text'] ?? 'Unable preview'}',
@ -228,9 +243,12 @@ class _ChatScreenState extends State<ChatScreen> {
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
contentPadding: const EdgeInsets.symmetric(horizontal: 16), contentPadding:
const EdgeInsets.symmetric(horizontal: 16),
leading: AccountImage( leading: AccountImage(
content: ud.getAccountFromCache(otherMember?.accountId)?.avatar, content: ud
.getAccountFromCache(otherMember?.accountId)
?.avatar,
), ),
onTap: () { onTap: () {
GoRouter.of(context).pushNamed( GoRouter.of(context).pushNamed(
@ -240,7 +258,7 @@ class _ChatScreenState extends State<ChatScreen> {
'alias': channel.alias, 'alias': channel.alias,
}, },
).then((value) { ).then((value) {
if (mounted) _refreshChannels(); if (mounted) _refreshChannels(noRemote: true);
}); });
}, },
); );
@ -259,7 +277,8 @@ class _ChatScreenState extends State<ChatScreen> {
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
contentPadding: const EdgeInsets.symmetric(horizontal: 16), contentPadding:
const EdgeInsets.symmetric(horizontal: 16),
leading: AccountImage( leading: AccountImage(
content: null, content: null,
fallbackWidget: const Icon(Symbols.chat, size: 20), fallbackWidget: const Icon(Symbols.chat, size: 20),

View File

@ -243,7 +243,12 @@ class _ExploreScreenState extends State<ExploreScreen> with SingleTickerProvider
children: [ children: [
Icon(Symbols.globe, size: 20, color: Theme.of(context).appBarTheme.foregroundColor), Icon(Symbols.globe, size: 20, color: Theme.of(context).appBarTheme.foregroundColor),
const Gap(8), const Gap(8),
Text('postChannelGlobal').tr().textColor(Theme.of(context).appBarTheme.foregroundColor), Flexible(
child: Text(
'postChannelGlobal',
maxLines: 1,
).tr().textColor(Theme.of(context).appBarTheme.foregroundColor),
),
], ],
), ),
), ),
@ -254,7 +259,13 @@ class _ExploreScreenState extends State<ExploreScreen> with SingleTickerProvider
children: [ children: [
Icon(Symbols.group, size: 20, color: Theme.of(context).appBarTheme.foregroundColor), Icon(Symbols.group, size: 20, color: Theme.of(context).appBarTheme.foregroundColor),
const Gap(8), const Gap(8),
Text('postChannelFriends').tr().textColor(Theme.of(context).appBarTheme.foregroundColor), Flexible(
child: Text(
'postChannelFriends',
maxLines: 1,
textAlign: TextAlign.center,
).tr().textColor(Theme.of(context).appBarTheme.foregroundColor),
),
], ],
), ),
), ),
@ -265,7 +276,12 @@ class _ExploreScreenState extends State<ExploreScreen> with SingleTickerProvider
children: [ children: [
Icon(Symbols.subscriptions, size: 20, color: Theme.of(context).appBarTheme.foregroundColor), Icon(Symbols.subscriptions, size: 20, color: Theme.of(context).appBarTheme.foregroundColor),
const Gap(8), const Gap(8),
Text('postChannelFollowing').tr().textColor(Theme.of(context).appBarTheme.foregroundColor), Flexible(
child: Text(
'postChannelFollowing',
maxLines: 1,
).tr().textColor(Theme.of(context).appBarTheme.foregroundColor),
),
], ],
), ),
), ),
@ -276,7 +292,12 @@ class _ExploreScreenState extends State<ExploreScreen> with SingleTickerProvider
children: [ children: [
Icon(Symbols.workspaces, size: 20, color: Theme.of(context).appBarTheme.foregroundColor), Icon(Symbols.workspaces, size: 20, color: Theme.of(context).appBarTheme.foregroundColor),
const Gap(8), const Gap(8),
Text('postChannelRealm').tr().textColor(Theme.of(context).appBarTheme.foregroundColor), Flexible(
child: Text(
'postChannelRealm',
maxLines: 1,
).tr().textColor(Theme.of(context).appBarTheme.foregroundColor),
),
], ],
), ),
), ),

View File

@ -5,8 +5,10 @@ import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart'; import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
@ -14,6 +16,7 @@ import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/config.dart'; import 'package:surface/providers/config.dart';
import 'package:surface/providers/database.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/theme.dart'; import 'package:surface/providers/theme.dart';
import 'package:surface/theme.dart'; import 'package:surface/theme.dart';
@ -67,6 +70,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final sn = context.read<SnNetworkProvider>(); final sn = context.read<SnNetworkProvider>();
final dt = context.read<DatabaseProvider>();
return AppScaffold( return AppScaffold(
appBar: AppBar( appBar: AppBar(
@ -81,7 +85,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('settingsAppearance').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4), Text('settingsAppearance')
.bold()
.fontSize(17)
.tr()
.padding(horizontal: 20, bottom: 4),
ListTile( ListTile(
title: Text('settingsDisplayLanguage').tr(), title: Text('settingsDisplayLanguage').tr(),
subtitle: Text('settingsDisplayLanguageDescription').tr(), subtitle: Text('settingsDisplayLanguageDescription').tr(),
@ -91,15 +99,21 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: DropdownButton2<Locale?>( child: DropdownButton2<Locale?>(
isExpanded: true, isExpanded: true,
items: [ items: [
...EasyLocalization.of(context)!.supportedLocales.mapIndexed((idx, ele) { ...EasyLocalization.of(context)!
.supportedLocales
.mapIndexed((idx, ele) {
return DropdownMenuItem<Locale?>( return DropdownMenuItem<Locale?>(
value: ele, value: ele,
child: Text('${ele.languageCode}-${ele.countryCode}').fontSize(14), child:
Text('${ele.languageCode}-${ele.countryCode}')
.fontSize(14),
); );
}), }),
DropdownMenuItem<Locale?>( DropdownMenuItem<Locale?>(
value: null, value: null,
child: Text('settingsDisplayLanguageSystem').tr().fontSize(14), child: Text('settingsDisplayLanguageSystem')
.tr()
.fontSize(14),
), ),
], ],
value: EasyLocalization.of(context)!.currentLocale, value: EasyLocalization.of(context)!.currentLocale,
@ -132,10 +146,12 @@ class _SettingsScreenState extends State<SettingsScreen> {
leading: const Icon(Symbols.image), leading: const Icon(Symbols.image),
trailing: const Icon(Symbols.chevron_right), trailing: const Icon(Symbols.chevron_right),
onTap: () async { onTap: () async {
final image = await ImagePicker().pickImage(source: ImageSource.gallery); final image = await ImagePicker()
.pickImage(source: ImageSource.gallery);
if (image == null) return; if (image == null) return;
await File(image.path).copy('$_docBasepath/app_background_image'); await File(image.path)
.copy('$_docBasepath/app_background_image');
_prefs.setBool(kAppBackgroundStoreKey, true); _prefs.setBool(kAppBackgroundStoreKey, true);
setState(() {}); setState(() {});
@ -143,7 +159,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
), ),
if (!kIsWeb) if (!kIsWeb)
FutureBuilder<bool>( FutureBuilder<bool>(
future: File('$_docBasepath/app_background_image').exists(), future:
File('$_docBasepath/app_background_image').exists(),
builder: (context, snapshot) { builder: (context, snapshot) {
if (!snapshot.hasData || !snapshot.data!) { if (!snapshot.hasData || !snapshot.data!) {
return const SizedBox.shrink(); return const SizedBox.shrink();
@ -151,12 +168,16 @@ class _SettingsScreenState extends State<SettingsScreen> {
return ListTile( return ListTile(
title: Text('settingsBackgroundImageClear').tr(), title: Text('settingsBackgroundImageClear').tr(),
subtitle: Text('settingsBackgroundImageClearDescription').tr(), subtitle:
contentPadding: const EdgeInsets.symmetric(horizontal: 24), Text('settingsBackgroundImageClearDescription')
.tr(),
contentPadding:
const EdgeInsets.symmetric(horizontal: 24),
leading: const Icon(Symbols.texture), leading: const Icon(Symbols.texture),
trailing: const Icon(Symbols.chevron_right), trailing: const Icon(Symbols.chevron_right),
onTap: () { onTap: () {
File('$_docBasepath/app_background_image').deleteSync(); File('$_docBasepath/app_background_image')
.deleteSync();
_prefs.remove(kAppBackgroundStoreKey); _prefs.remove(kAppBackgroundStoreKey);
setState(() {}); setState(() {});
}, },
@ -186,11 +207,12 @@ class _SettingsScreenState extends State<SettingsScreen> {
contentPadding: const EdgeInsets.symmetric(horizontal: 24), contentPadding: const EdgeInsets.symmetric(horizontal: 24),
trailing: const Icon(Symbols.chevron_right), trailing: const Icon(Symbols.chevron_right),
onTap: () async { onTap: () async {
Color pickerColor = Color(_prefs.getInt(kAppColorSchemeStoreKey) ?? Colors.indigo.value); Color pickerColor = Color(
_prefs.getInt(kAppColorSchemeStoreKey) ??
Colors.indigo.value);
final color = await showDialog<Color?>( final color = await showDialog<Color?>(
context: context, context: context,
builder: (context) => builder: (context) => AlertDialog(
AlertDialog(
content: SingleChildScrollView( content: SingleChildScrollView(
child: ColorPicker( child: ColorPicker(
pickerColor: pickerColor, pickerColor: pickerColor,
@ -248,16 +270,17 @@ class _SettingsScreenState extends State<SettingsScreen> {
], ],
value: _prefs.getInt(kAppColorSchemeStoreKey) == null value: _prefs.getInt(kAppColorSchemeStoreKey) == null
? 1 ? 1
: kColorSchemes.values : kColorSchemes.values.toList().indexWhere((ele) =>
.toList() ele.value ==
.indexWhere((ele) => ele.value == _prefs.getInt(kAppColorSchemeStoreKey)), _prefs.getInt(kAppColorSchemeStoreKey)),
onChanged: (int? value) { onChanged: (int? value) {
if (value != null && value != -1) { if (value != null && value != -1) {
_prefs.setInt(kAppColorSchemeStoreKey, kColorSchemes.values _prefs.setInt(kAppColorSchemeStoreKey,
.elementAt(value) kColorSchemes.values.elementAt(value).value);
.value);
final th = context.read<ThemeProvider>(); final th = context.read<ThemeProvider>();
th.reloadTheme(seedColorOverride: kColorSchemes.values.elementAt(value)); th.reloadTheme(
seedColorOverride:
kColorSchemes.values.elementAt(value));
setState(() {}); setState(() {});
context.showSnackbar('colorSchemeApplied'.tr()); context.showSnackbar('colorSchemeApplied'.tr());
@ -293,7 +316,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
CheckboxListTile( CheckboxListTile(
secondary: const Icon(Symbols.left_panel_close), secondary: const Icon(Symbols.left_panel_close),
title: Text('settingsDrawerPreferCollapse').tr(), title: Text('settingsDrawerPreferCollapse').tr(),
subtitle: Text('settingsDrawerPreferCollapseDescription').tr(), subtitle:
Text('settingsDrawerPreferCollapseDescription').tr(),
contentPadding: const EdgeInsets.only(left: 24, right: 17), contentPadding: const EdgeInsets.only(left: 24, right: 17),
value: _prefs.getBool(kAppDrawerPreferCollapse) ?? false, value: _prefs.getBool(kAppDrawerPreferCollapse) ?? false,
onChanged: (value) { onChanged: (value) {
@ -308,7 +332,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('settingsFeatures').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4), Text('settingsFeatures')
.bold()
.fontSize(17)
.tr()
.padding(horizontal: 20, bottom: 4),
CheckboxListTile( CheckboxListTile(
secondary: const Icon(Symbols.vibration), secondary: const Icon(Symbols.vibration),
contentPadding: const EdgeInsets.only(left: 24, right: 17), contentPadding: const EdgeInsets.only(left: 24, right: 17),
@ -350,7 +378,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('settingsNetwork').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4), Text('settingsNetwork')
.bold()
.fontSize(17)
.tr()
.padding(horizontal: 20, bottom: 4),
TextField( TextField(
controller: _serverUrlController, controller: _serverUrlController,
decoration: InputDecoration( decoration: InputDecoration(
@ -371,7 +403,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
}, },
), ),
), ),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
).padding(horizontal: 16, top: 8, bottom: 4), ).padding(horizontal: 16, top: 8, bottom: 4),
ListTile( ListTile(
title: Text('settingsNetworkServerPreset').tr(), title: Text('settingsNetworkServerPreset').tr(),
@ -383,12 +416,13 @@ class _SettingsScreenState extends State<SettingsScreen> {
isExpanded: true, isExpanded: true,
items: [ items: [
...kNetworkServerDirectory, ...kNetworkServerDirectory,
if (!kNetworkServerDirectory.map((ele) => ele.$2).contains(_serverUrlController.text)) if (!kNetworkServerDirectory
.map((ele) => ele.$2)
.contains(_serverUrlController.text))
('Custom', _serverUrlController.text), ('Custom', _serverUrlController.text),
] ]
.map( .map(
(item) => (item) => DropdownMenuItem<String>(
DropdownMenuItem<String>(
value: item.$2, value: item.$2,
child: Column( child: Column(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
@ -396,7 +430,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(item.$1).fontSize(14), Text(item.$1).fontSize(14),
Text(item.$2, overflow: TextOverflow.ellipsis).fontSize(11) Text(item.$2, overflow: TextOverflow.ellipsis)
.fontSize(11)
], ],
), ),
), ),
@ -442,7 +477,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('settingsPerformance').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4), Text('settingsPerformance')
.bold()
.fontSize(17)
.tr()
.padding(horizontal: 20, bottom: 4),
ListTile( ListTile(
title: Text('settingsImageQuality').tr(), title: Text('settingsImageQuality').tr(),
subtitle: Text('settingsImageQualityDescription').tr(), subtitle: Text('settingsImageQualityDescription').tr(),
@ -450,13 +489,13 @@ class _SettingsScreenState extends State<SettingsScreen> {
leading: const Icon(Symbols.image), leading: const Icon(Symbols.image),
trailing: DropdownButtonHideUnderline( trailing: DropdownButtonHideUnderline(
child: DropdownButton2<FilterQuality>( child: DropdownButton2<FilterQuality>(
value: kImageQualityLevel.values.elementAtOrNull(_prefs.getInt('app_image_quality') ?? 3) ?? value: kImageQualityLevel.values.elementAtOrNull(
_prefs.getInt('app_image_quality') ?? 3) ??
FilterQuality.high, FilterQuality.high,
isExpanded: true, isExpanded: true,
items: kImageQualityLevel.entries items: kImageQualityLevel.entries
.map( .map(
(item) => (item) => DropdownMenuItem<FilterQuality>(
DropdownMenuItem<FilterQuality>(
value: item.value, value: item.value,
child: Text(item.key).tr().fontSize(14), child: Text(item.key).tr().fontSize(14),
), ),
@ -464,7 +503,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
.toList(), .toList(),
onChanged: (FilterQuality? value) { onChanged: (FilterQuality? value) {
if (value == null) return; if (value == null) return;
_prefs.setInt('app_image_quality', kImageQualityLevel.values.toList().indexOf(value)); _prefs.setInt('app_image_quality',
kImageQualityLevel.values.toList().indexOf(value));
setState(() {}); setState(() {});
}, },
buttonStyleData: const ButtonStyleData( buttonStyleData: const ButtonStyleData(
@ -486,7 +526,42 @@ class _SettingsScreenState extends State<SettingsScreen> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('settingsMisc').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4), Text('settingsMisc')
.bold()
.fontSize(17)
.tr()
.padding(horizontal: 20, bottom: 4),
ListTile(
leading: const Icon(Symbols.database),
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
title: Text('databaseSize').tr(),
subtitle: FutureBuilder(
future: dt.getDatabaseSize(),
builder: (context, snapshot) {
if (!snapshot.hasData || kIsWeb) {
return Text('unknown').tr();
}
return Text(
snapshot.data!.formatBytes(),
style: GoogleFonts.robotoMono(),
);
},
),
),
ListTile(
leading: const Icon(Symbols.database_off),
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
title: Text('databaseDelete').tr(),
subtitle: Text('databaseDeleteDescription').tr(),
trailing: const Icon(Symbols.chevron_right),
onTap: () async {
await dt.removeDatabase();
if (!context.mounted) return;
HapticFeedback.heavyImpact();
context.showSnackbar('databaseDeleted'.tr());
setState(() {});
},
),
ListTile( ListTile(
title: Text('settingsMiscAbout').tr(), title: Text('settingsMiscAbout').tr(),
subtitle: Text('settingsMiscAboutDescription').tr(), subtitle: Text('settingsMiscAboutDescription').tr(),

View File

@ -9,7 +9,7 @@ class SnAccount with _$SnAccount {
const SnAccount._(); const SnAccount._();
const factory SnAccount({ const factory SnAccount({
@HiveField(0) required int id, required int id,
required DateTime createdAt, required DateTime createdAt,
required DateTime updatedAt, required DateTime updatedAt,
required DateTime? deletedAt, required DateTime? deletedAt,

View File

@ -58,7 +58,7 @@ abstract class $SnAccountCopyWith<$Res> {
_$SnAccountCopyWithImpl<$Res, SnAccount>; _$SnAccountCopyWithImpl<$Res, SnAccount>;
@useResult @useResult
$Res call( $Res call(
{@HiveField(0) int id, {int id,
DateTime createdAt, DateTime createdAt,
DateTime updatedAt, DateTime updatedAt,
DateTime? deletedAt, DateTime? deletedAt,
@ -226,7 +226,7 @@ abstract class _$$SnAccountImplCopyWith<$Res>
@override @override
@useResult @useResult
$Res call( $Res call(
{@HiveField(0) int id, {int id,
DateTime createdAt, DateTime createdAt,
DateTime updatedAt, DateTime updatedAt,
DateTime? deletedAt, DateTime? deletedAt,
@ -374,7 +374,7 @@ class __$$SnAccountImplCopyWithImpl<$Res>
@JsonSerializable() @JsonSerializable()
class _$SnAccountImpl extends _SnAccount { class _$SnAccountImpl extends _SnAccount {
const _$SnAccountImpl( const _$SnAccountImpl(
{@HiveField(0) required this.id, {required this.id,
required this.createdAt, required this.createdAt,
required this.updatedAt, required this.updatedAt,
required this.deletedAt, required this.deletedAt,
@ -556,7 +556,7 @@ class _$SnAccountImpl extends _SnAccount {
abstract class _SnAccount extends SnAccount { abstract class _SnAccount extends SnAccount {
const factory _SnAccount( const factory _SnAccount(
{@HiveField(0) required final int id, {required final int id,
required final DateTime createdAt, required final DateTime createdAt,
required final DateTime updatedAt, required final DateTime updatedAt,
required final DateTime? deletedAt, required final DateTime? deletedAt,

View File

@ -14,21 +14,21 @@ class SnChannel with _$SnChannel {
@HiveType(typeId: 2) @HiveType(typeId: 2)
const factory SnChannel({ const factory SnChannel({
@HiveField(0) required int id, required int id,
@HiveField(1) required DateTime createdAt, required DateTime createdAt,
@HiveField(2) required DateTime updatedAt, required DateTime updatedAt,
@HiveField(3) required dynamic deletedAt, required dynamic deletedAt,
@HiveField(4) required String alias, required String alias,
@HiveField(5) required String name, required String name,
@HiveField(6) required String description, required String description,
@HiveField(7) required List<SnChannelMember>? members, required List<SnChannelMember>? members,
List<SnChatMessage>? messages, List<SnChatMessage>? messages,
@HiveField(8) required int type, required int type,
@HiveField(9) required int accountId, required int accountId,
@HiveField(10) required SnRealm? realm, required SnRealm? realm,
@HiveField(11) required int? realmId, required int? realmId,
@HiveField(12) required bool isPublic, required bool isPublic,
@HiveField(13) required bool isCommunity, required bool isCommunity,
}) = _SnChannel; }) = _SnChannel;
factory SnChannel.fromJson(Map<String, dynamic> json) => factory SnChannel.fromJson(Map<String, dynamic> json) =>
@ -44,17 +44,17 @@ class SnChannelMember with _$SnChannelMember {
@HiveType(typeId: 3) @HiveType(typeId: 3)
const factory SnChannelMember({ const factory SnChannelMember({
@HiveField(0) required int id, required int id,
@HiveField(1) required DateTime createdAt, required DateTime createdAt,
@HiveField(2) required DateTime updatedAt, required DateTime updatedAt,
@HiveField(3) required DateTime? deletedAt, required DateTime? deletedAt,
@HiveField(4) required int channelId, required int channelId,
@HiveField(5) required int accountId, required int accountId,
@HiveField(6) required String? nick, required String? nick,
@HiveField(7) required SnChannel? channel, required SnChannel? channel,
@HiveField(8) required SnAccount? account, required SnAccount? account,
@Default(0) int notify, @Default(0) int notify,
@HiveField(9) required int powerLevel, required int powerLevel,
dynamic calls, dynamic calls,
dynamic events, dynamic events,
}) = _SnChannelMember; }) = _SnChannelMember;
@ -69,19 +69,19 @@ class SnChatMessage with _$SnChatMessage {
@HiveType(typeId: 4) @HiveType(typeId: 4)
const factory SnChatMessage({ const factory SnChatMessage({
@HiveField(0) required int id, required int id,
@HiveField(1) required DateTime createdAt, required DateTime createdAt,
@HiveField(2) required DateTime updatedAt, required DateTime updatedAt,
@HiveField(3) required DateTime? deletedAt, required DateTime? deletedAt,
@HiveField(4) required String uuid, required String uuid,
@HiveField(5) @Default({}) Map<String, dynamic> body, @Default({}) Map<String, dynamic> body,
@HiveField(6) required String type, required String type,
@HiveField(7) required SnChannel channel, required SnChannel channel,
@HiveField(8) required SnChannelMember sender, required SnChannelMember sender,
@HiveField(9) required int channelId, required int channelId,
@HiveField(10) required int senderId, required int senderId,
@HiveField(11) required int? quoteEventId, required int? quoteEventId,
@HiveField(12) required int? relatedEventId, required int? relatedEventId,
SnChatMessagePreload? preload, SnChatMessagePreload? preload,
}) = _SnChatMessage; }) = _SnChatMessage;

View File

@ -66,21 +66,21 @@ abstract class $SnChannelCopyWith<$Res> {
_$SnChannelCopyWithImpl<$Res, SnChannel>; _$SnChannelCopyWithImpl<$Res, SnChannel>;
@useResult @useResult
$Res call( $Res call(
{@HiveField(0) int id, {int id,
@HiveField(1) DateTime createdAt, DateTime createdAt,
@HiveField(2) DateTime updatedAt, DateTime updatedAt,
@HiveField(3) dynamic deletedAt, dynamic deletedAt,
@HiveField(4) String alias, String alias,
@HiveField(5) String name, String name,
@HiveField(6) String description, String description,
@HiveField(7) List<SnChannelMember>? members, List<SnChannelMember>? members,
List<SnChatMessage>? messages, List<SnChatMessage>? messages,
@HiveField(8) int type, int type,
@HiveField(9) int accountId, int accountId,
@HiveField(10) SnRealm? realm, SnRealm? realm,
@HiveField(11) int? realmId, int? realmId,
@HiveField(12) bool isPublic, bool isPublic,
@HiveField(13) bool isCommunity}); bool isCommunity});
$SnRealmCopyWith<$Res>? get realm; $SnRealmCopyWith<$Res>? get realm;
} }
@ -204,21 +204,21 @@ abstract class _$$SnChannelImplCopyWith<$Res>
@override @override
@useResult @useResult
$Res call( $Res call(
{@HiveField(0) int id, {int id,
@HiveField(1) DateTime createdAt, DateTime createdAt,
@HiveField(2) DateTime updatedAt, DateTime updatedAt,
@HiveField(3) dynamic deletedAt, dynamic deletedAt,
@HiveField(4) String alias, String alias,
@HiveField(5) String name, String name,
@HiveField(6) String description, String description,
@HiveField(7) List<SnChannelMember>? members, List<SnChannelMember>? members,
List<SnChatMessage>? messages, List<SnChatMessage>? messages,
@HiveField(8) int type, int type,
@HiveField(9) int accountId, int accountId,
@HiveField(10) SnRealm? realm, SnRealm? realm,
@HiveField(11) int? realmId, int? realmId,
@HiveField(12) bool isPublic, bool isPublic,
@HiveField(13) bool isCommunity}); bool isCommunity});
@override @override
$SnRealmCopyWith<$Res>? get realm; $SnRealmCopyWith<$Res>? get realm;
@ -323,21 +323,21 @@ class __$$SnChannelImplCopyWithImpl<$Res>
@HiveType(typeId: 2) @HiveType(typeId: 2)
class _$SnChannelImpl extends _SnChannel { class _$SnChannelImpl extends _SnChannel {
const _$SnChannelImpl( const _$SnChannelImpl(
{@HiveField(0) required this.id, {required this.id,
@HiveField(1) required this.createdAt, required this.createdAt,
@HiveField(2) required this.updatedAt, required this.updatedAt,
@HiveField(3) required this.deletedAt, required this.deletedAt,
@HiveField(4) required this.alias, required this.alias,
@HiveField(5) required this.name, required this.name,
@HiveField(6) required this.description, required this.description,
@HiveField(7) required final List<SnChannelMember>? members, required final List<SnChannelMember>? members,
final List<SnChatMessage>? messages, final List<SnChatMessage>? messages,
@HiveField(8) required this.type, required this.type,
@HiveField(9) required this.accountId, required this.accountId,
@HiveField(10) required this.realm, required this.realm,
@HiveField(11) required this.realmId, required this.realmId,
@HiveField(12) required this.isPublic, required this.isPublic,
@HiveField(13) required this.isCommunity}) required this.isCommunity})
: _members = members, : _members = members,
_messages = messages, _messages = messages,
super._(); super._();
@ -477,21 +477,21 @@ class _$SnChannelImpl extends _SnChannel {
abstract class _SnChannel extends SnChannel { abstract class _SnChannel extends SnChannel {
const factory _SnChannel( const factory _SnChannel(
{@HiveField(0) required final int id, {required final int id,
@HiveField(1) required final DateTime createdAt, required final DateTime createdAt,
@HiveField(2) required final DateTime updatedAt, required final DateTime updatedAt,
@HiveField(3) required final dynamic deletedAt, required final dynamic deletedAt,
@HiveField(4) required final String alias, required final String alias,
@HiveField(5) required final String name, required final String name,
@HiveField(6) required final String description, required final String description,
@HiveField(7) required final List<SnChannelMember>? members, required final List<SnChannelMember>? members,
final List<SnChatMessage>? messages, final List<SnChatMessage>? messages,
@HiveField(8) required final int type, required final int type,
@HiveField(9) required final int accountId, required final int accountId,
@HiveField(10) required final SnRealm? realm, required final SnRealm? realm,
@HiveField(11) required final int? realmId, required final int? realmId,
@HiveField(12) required final bool isPublic, required final bool isPublic,
@HiveField(13) required final bool isCommunity}) = _$SnChannelImpl; required final bool isCommunity}) = _$SnChannelImpl;
const _SnChannel._() : super._(); const _SnChannel._() : super._();
factory _SnChannel.fromJson(Map<String, dynamic> json) = factory _SnChannel.fromJson(Map<String, dynamic> json) =
@ -597,17 +597,17 @@ abstract class $SnChannelMemberCopyWith<$Res> {
_$SnChannelMemberCopyWithImpl<$Res, SnChannelMember>; _$SnChannelMemberCopyWithImpl<$Res, SnChannelMember>;
@useResult @useResult
$Res call( $Res call(
{@HiveField(0) int id, {int id,
@HiveField(1) DateTime createdAt, DateTime createdAt,
@HiveField(2) DateTime updatedAt, DateTime updatedAt,
@HiveField(3) DateTime? deletedAt, DateTime? deletedAt,
@HiveField(4) int channelId, int channelId,
@HiveField(5) int accountId, int accountId,
@HiveField(6) String? nick, String? nick,
@HiveField(7) SnChannel? channel, SnChannel? channel,
@HiveField(8) SnAccount? account, SnAccount? account,
int notify, int notify,
@HiveField(9) int powerLevel, int powerLevel,
dynamic calls, dynamic calls,
dynamic events}); dynamic events});
@ -738,17 +738,17 @@ abstract class _$$SnChannelMemberImplCopyWith<$Res>
@override @override
@useResult @useResult
$Res call( $Res call(
{@HiveField(0) int id, {int id,
@HiveField(1) DateTime createdAt, DateTime createdAt,
@HiveField(2) DateTime updatedAt, DateTime updatedAt,
@HiveField(3) DateTime? deletedAt, DateTime? deletedAt,
@HiveField(4) int channelId, int channelId,
@HiveField(5) int accountId, int accountId,
@HiveField(6) String? nick, String? nick,
@HiveField(7) SnChannel? channel, SnChannel? channel,
@HiveField(8) SnAccount? account, SnAccount? account,
int notify, int notify,
@HiveField(9) int powerLevel, int powerLevel,
dynamic calls, dynamic calls,
dynamic events}); dynamic events});
@ -847,17 +847,17 @@ class __$$SnChannelMemberImplCopyWithImpl<$Res>
@HiveType(typeId: 3) @HiveType(typeId: 3)
class _$SnChannelMemberImpl extends _SnChannelMember { class _$SnChannelMemberImpl extends _SnChannelMember {
const _$SnChannelMemberImpl( const _$SnChannelMemberImpl(
{@HiveField(0) required this.id, {required this.id,
@HiveField(1) required this.createdAt, required this.createdAt,
@HiveField(2) required this.updatedAt, required this.updatedAt,
@HiveField(3) required this.deletedAt, required this.deletedAt,
@HiveField(4) required this.channelId, required this.channelId,
@HiveField(5) required this.accountId, required this.accountId,
@HiveField(6) required this.nick, required this.nick,
@HiveField(7) required this.channel, required this.channel,
@HiveField(8) required this.account, required this.account,
this.notify = 0, this.notify = 0,
@HiveField(9) required this.powerLevel, required this.powerLevel,
this.calls, this.calls,
this.events}) this.events})
: super._(); : super._();
@ -971,17 +971,17 @@ class _$SnChannelMemberImpl extends _SnChannelMember {
abstract class _SnChannelMember extends SnChannelMember { abstract class _SnChannelMember extends SnChannelMember {
const factory _SnChannelMember( const factory _SnChannelMember(
{@HiveField(0) required final int id, {required final int id,
@HiveField(1) required final DateTime createdAt, required final DateTime createdAt,
@HiveField(2) required final DateTime updatedAt, required final DateTime updatedAt,
@HiveField(3) required final DateTime? deletedAt, required final DateTime? deletedAt,
@HiveField(4) required final int channelId, required final int channelId,
@HiveField(5) required final int accountId, required final int accountId,
@HiveField(6) required final String? nick, required final String? nick,
@HiveField(7) required final SnChannel? channel, required final SnChannel? channel,
@HiveField(8) required final SnAccount? account, required final SnAccount? account,
final int notify, final int notify,
@HiveField(9) required final int powerLevel, required final int powerLevel,
final dynamic calls, final dynamic calls,
final dynamic events}) = _$SnChannelMemberImpl; final dynamic events}) = _$SnChannelMemberImpl;
const _SnChannelMember._() : super._(); const _SnChannelMember._() : super._();
@ -1085,19 +1085,19 @@ abstract class $SnChatMessageCopyWith<$Res> {
_$SnChatMessageCopyWithImpl<$Res, SnChatMessage>; _$SnChatMessageCopyWithImpl<$Res, SnChatMessage>;
@useResult @useResult
$Res call( $Res call(
{@HiveField(0) int id, {int id,
@HiveField(1) DateTime createdAt, DateTime createdAt,
@HiveField(2) DateTime updatedAt, DateTime updatedAt,
@HiveField(3) DateTime? deletedAt, DateTime? deletedAt,
@HiveField(4) String uuid, String uuid,
@HiveField(5) Map<String, dynamic> body, Map<String, dynamic> body,
@HiveField(6) String type, String type,
@HiveField(7) SnChannel channel, SnChannel channel,
@HiveField(8) SnChannelMember sender, SnChannelMember sender,
@HiveField(9) int channelId, int channelId,
@HiveField(10) int senderId, int senderId,
@HiveField(11) int? quoteEventId, int? quoteEventId,
@HiveField(12) int? relatedEventId, int? relatedEventId,
SnChatMessagePreload? preload}); SnChatMessagePreload? preload});
$SnChannelCopyWith<$Res> get channel; $SnChannelCopyWith<$Res> get channel;
@ -1239,19 +1239,19 @@ abstract class _$$SnChatMessageImplCopyWith<$Res>
@override @override
@useResult @useResult
$Res call( $Res call(
{@HiveField(0) int id, {int id,
@HiveField(1) DateTime createdAt, DateTime createdAt,
@HiveField(2) DateTime updatedAt, DateTime updatedAt,
@HiveField(3) DateTime? deletedAt, DateTime? deletedAt,
@HiveField(4) String uuid, String uuid,
@HiveField(5) Map<String, dynamic> body, Map<String, dynamic> body,
@HiveField(6) String type, String type,
@HiveField(7) SnChannel channel, SnChannel channel,
@HiveField(8) SnChannelMember sender, SnChannelMember sender,
@HiveField(9) int channelId, int channelId,
@HiveField(10) int senderId, int senderId,
@HiveField(11) int? quoteEventId, int? quoteEventId,
@HiveField(12) int? relatedEventId, int? relatedEventId,
SnChatMessagePreload? preload}); SnChatMessagePreload? preload});
@override @override
@ -1356,19 +1356,19 @@ class __$$SnChatMessageImplCopyWithImpl<$Res>
@HiveType(typeId: 4) @HiveType(typeId: 4)
class _$SnChatMessageImpl extends _SnChatMessage { class _$SnChatMessageImpl extends _SnChatMessage {
const _$SnChatMessageImpl( const _$SnChatMessageImpl(
{@HiveField(0) required this.id, {required this.id,
@HiveField(1) required this.createdAt, required this.createdAt,
@HiveField(2) required this.updatedAt, required this.updatedAt,
@HiveField(3) required this.deletedAt, required this.deletedAt,
@HiveField(4) required this.uuid, required this.uuid,
@HiveField(5) final Map<String, dynamic> body = const {}, final Map<String, dynamic> body = const {},
@HiveField(6) required this.type, required this.type,
@HiveField(7) required this.channel, required this.channel,
@HiveField(8) required this.sender, required this.sender,
@HiveField(9) required this.channelId, required this.channelId,
@HiveField(10) required this.senderId, required this.senderId,
@HiveField(11) required this.quoteEventId, required this.quoteEventId,
@HiveField(12) required this.relatedEventId, required this.relatedEventId,
this.preload}) this.preload})
: _body = body, : _body = body,
super._(); super._();
@ -1495,19 +1495,19 @@ class _$SnChatMessageImpl extends _SnChatMessage {
abstract class _SnChatMessage extends SnChatMessage { abstract class _SnChatMessage extends SnChatMessage {
const factory _SnChatMessage( const factory _SnChatMessage(
{@HiveField(0) required final int id, {required final int id,
@HiveField(1) required final DateTime createdAt, required final DateTime createdAt,
@HiveField(2) required final DateTime updatedAt, required final DateTime updatedAt,
@HiveField(3) required final DateTime? deletedAt, required final DateTime? deletedAt,
@HiveField(4) required final String uuid, required final String uuid,
@HiveField(5) final Map<String, dynamic> body, final Map<String, dynamic> body,
@HiveField(6) required final String type, required final String type,
@HiveField(7) required final SnChannel channel, required final SnChannel channel,
@HiveField(8) required final SnChannelMember sender, required final SnChannelMember sender,
@HiveField(9) required final int channelId, required final int channelId,
@HiveField(10) required final int senderId, required final int senderId,
@HiveField(11) required final int? quoteEventId, required final int? quoteEventId,
@HiveField(12) required final int? relatedEventId, required final int? relatedEventId,
final SnChatMessagePreload? preload}) = _$SnChatMessageImpl; final SnChatMessagePreload? preload}) = _$SnChatMessageImpl;
const _SnChatMessage._() : super._(); const _SnChatMessage._() : super._();

View File

@ -29,20 +29,20 @@ class SnRealm with _$SnRealm {
@HiveType(typeId: 1) @HiveType(typeId: 1)
const factory SnRealm({ const factory SnRealm({
@HiveField(0) required int id, required int id,
@HiveField(1) required DateTime createdAt, required DateTime createdAt,
@HiveField(2) required DateTime updatedAt, required DateTime updatedAt,
@HiveField(3) required DateTime? deletedAt, required DateTime? deletedAt,
@HiveField(4) required String alias, required String alias,
@HiveField(5) required String name, required String name,
@HiveField(6) required String description, required String description,
List<SnRealmMember>? members, List<SnRealmMember>? members,
@HiveField(7) required String? avatar, required String? avatar,
@HiveField(8) required String? banner, required String? banner,
@HiveField(9) required Map<String, dynamic>? accessPolicy, required Map<String, dynamic>? accessPolicy,
@HiveField(10) required int accountId, required int accountId,
@HiveField(11) required bool isPublic, required bool isPublic,
@HiveField(12) required bool isCommunity, required bool isCommunity,
@Default(0) int popularity, @Default(0) int popularity,
}) = _SnRealm; }) = _SnRealm;

View File

@ -411,20 +411,20 @@ abstract class $SnRealmCopyWith<$Res> {
_$SnRealmCopyWithImpl<$Res, SnRealm>; _$SnRealmCopyWithImpl<$Res, SnRealm>;
@useResult @useResult
$Res call( $Res call(
{@HiveField(0) int id, {int id,
@HiveField(1) DateTime createdAt, DateTime createdAt,
@HiveField(2) DateTime updatedAt, DateTime updatedAt,
@HiveField(3) DateTime? deletedAt, DateTime? deletedAt,
@HiveField(4) String alias, String alias,
@HiveField(5) String name, String name,
@HiveField(6) String description, String description,
List<SnRealmMember>? members, List<SnRealmMember>? members,
@HiveField(7) String? avatar, String? avatar,
@HiveField(8) String? banner, String? banner,
@HiveField(9) Map<String, dynamic>? accessPolicy, Map<String, dynamic>? accessPolicy,
@HiveField(10) int accountId, int accountId,
@HiveField(11) bool isPublic, bool isPublic,
@HiveField(12) bool isCommunity, bool isCommunity,
int popularity}); int popularity});
} }
@ -532,20 +532,20 @@ abstract class _$$SnRealmImplCopyWith<$Res> implements $SnRealmCopyWith<$Res> {
@override @override
@useResult @useResult
$Res call( $Res call(
{@HiveField(0) int id, {int id,
@HiveField(1) DateTime createdAt, DateTime createdAt,
@HiveField(2) DateTime updatedAt, DateTime updatedAt,
@HiveField(3) DateTime? deletedAt, DateTime? deletedAt,
@HiveField(4) String alias, String alias,
@HiveField(5) String name, String name,
@HiveField(6) String description, String description,
List<SnRealmMember>? members, List<SnRealmMember>? members,
@HiveField(7) String? avatar, String? avatar,
@HiveField(8) String? banner, String? banner,
@HiveField(9) Map<String, dynamic>? accessPolicy, Map<String, dynamic>? accessPolicy,
@HiveField(10) int accountId, int accountId,
@HiveField(11) bool isPublic, bool isPublic,
@HiveField(12) bool isCommunity, bool isCommunity,
int popularity}); int popularity});
} }
@ -648,20 +648,20 @@ class __$$SnRealmImplCopyWithImpl<$Res>
@HiveType(typeId: 1) @HiveType(typeId: 1)
class _$SnRealmImpl extends _SnRealm { class _$SnRealmImpl extends _SnRealm {
const _$SnRealmImpl( const _$SnRealmImpl(
{@HiveField(0) required this.id, {required this.id,
@HiveField(1) required this.createdAt, required this.createdAt,
@HiveField(2) required this.updatedAt, required this.updatedAt,
@HiveField(3) required this.deletedAt, required this.deletedAt,
@HiveField(4) required this.alias, required this.alias,
@HiveField(5) required this.name, required this.name,
@HiveField(6) required this.description, required this.description,
final List<SnRealmMember>? members, final List<SnRealmMember>? members,
@HiveField(7) required this.avatar, required this.avatar,
@HiveField(8) required this.banner, required this.banner,
@HiveField(9) required final Map<String, dynamic>? accessPolicy, required final Map<String, dynamic>? accessPolicy,
@HiveField(10) required this.accountId, required this.accountId,
@HiveField(11) required this.isPublic, required this.isPublic,
@HiveField(12) required this.isCommunity, required this.isCommunity,
this.popularity = 0}) this.popularity = 0})
: _members = members, : _members = members,
_accessPolicy = accessPolicy, _accessPolicy = accessPolicy,
@ -805,20 +805,20 @@ class _$SnRealmImpl extends _SnRealm {
abstract class _SnRealm extends SnRealm { abstract class _SnRealm extends SnRealm {
const factory _SnRealm( const factory _SnRealm(
{@HiveField(0) required final int id, {required final int id,
@HiveField(1) required final DateTime createdAt, required final DateTime createdAt,
@HiveField(2) required final DateTime updatedAt, required final DateTime updatedAt,
@HiveField(3) required final DateTime? deletedAt, required final DateTime? deletedAt,
@HiveField(4) required final String alias, required final String alias,
@HiveField(5) required final String name, required final String name,
@HiveField(6) required final String description, required final String description,
final List<SnRealmMember>? members, final List<SnRealmMember>? members,
@HiveField(7) required final String? avatar, required final String? avatar,
@HiveField(8) required final String? banner, required final String? banner,
@HiveField(9) required final Map<String, dynamic>? accessPolicy, required final Map<String, dynamic>? accessPolicy,
@HiveField(10) required final int accountId, required final int accountId,
@HiveField(11) required final bool isPublic, required final bool isPublic,
@HiveField(12) required final bool isCommunity, required final bool isCommunity,
final int popularity}) = _$SnRealmImpl; final int popularity}) = _$SnRealmImpl;
const _SnRealm._() : super._(); const _SnRealm._() : super._();

View File

@ -15,6 +15,7 @@
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h> #include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
#include <media_kit_video/media_kit_video_plugin.h> #include <media_kit_video/media_kit_video_plugin.h>
#include <pasteboard/pasteboard_plugin.h> #include <pasteboard/pasteboard_plugin.h>
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
#include <tray_manager/tray_manager_plugin.h> #include <tray_manager/tray_manager_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h> #include <url_launcher_linux/url_launcher_plugin.h>
@ -46,6 +47,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) pasteboard_registrar = g_autoptr(FlPluginRegistrar) pasteboard_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin");
pasteboard_plugin_register_with_registrar(pasteboard_registrar); pasteboard_plugin_register_with_registrar(pasteboard_registrar);
g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin");
sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar);
g_autoptr(FlPluginRegistrar) tray_manager_registrar = g_autoptr(FlPluginRegistrar) tray_manager_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "TrayManagerPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "TrayManagerPlugin");
tray_manager_plugin_register_with_registrar(tray_manager_registrar); tray_manager_plugin_register_with_registrar(tray_manager_registrar);

View File

@ -12,6 +12,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
media_kit_libs_linux media_kit_libs_linux
media_kit_video media_kit_video
pasteboard pasteboard
sqlite3_flutter_libs
tray_manager tray_manager
url_launcher_linux url_launcher_linux
) )

View File

@ -30,6 +30,7 @@ import screen_brightness_macos
import share_plus import share_plus
import shared_preferences_foundation import shared_preferences_foundation
import sqflite_darwin import sqflite_darwin
import sqlite3_flutter_libs
import tray_manager import tray_manager
import url_launcher_macos import url_launcher_macos
import video_compress import video_compress
@ -61,6 +62,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin"))
TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin")) TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
VideoCompressPlugin.register(with: registry.registrar(forPlugin: "VideoCompressPlugin")) VideoCompressPlugin.register(with: registry.registrar(forPlugin: "VideoCompressPlugin"))

View File

@ -230,6 +230,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.0" version: "1.3.0"
charcode:
dependency: transitive
description:
name: charcode
sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a
url: "https://pub.dev"
source: hosted
version: "1.4.0"
checked_yaml: checked_yaml:
dependency: transitive dependency: transitive
description: description:
@ -414,6 +422,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.2" version: "1.0.2"
drift:
dependency: "direct main"
description:
name: drift
sha256: "97d5832657d49f26e7a8e07de397ddc63790b039372878d5117af816d0fdb5cb"
url: "https://pub.dev"
source: hosted
version: "2.25.1"
drift_dev:
dependency: "direct dev"
description:
name: drift_dev
sha256: f1db88482dbb016b9bbddddf746d5d0a6938b156ff20e07320052981f97388cc
url: "https://pub.dev"
source: hosted
version: "2.25.2"
drift_flutter:
dependency: "direct main"
description:
name: drift_flutter
sha256: "0cadbf3b8733409a6cf61d18ba2e94e149df81df7de26f48ae0695b48fd71922"
url: "https://pub.dev"
source: hosted
version: "0.2.4"
dropdown_button2: dropdown_button2:
dependency: "direct main" dependency: "direct main"
description: description:
@ -914,30 +946,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.2" version: "2.3.2"
hive:
dependency: "direct main"
description:
name: hive
sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
url: "https://pub.dev"
source: hosted
version: "2.2.3"
hive_flutter:
dependency: "direct main"
description:
name: hive_flutter
sha256: dca1da446b1d808a51689fb5d0c6c9510c0a2ba01e22805d492c73b68e33eecc
url: "https://pub.dev"
source: hosted
version: "1.1.0"
hive_generator:
dependency: "direct dev"
description:
name: hive_generator
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
home_widget: home_widget:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1642,6 +1650,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.1.0" version: "4.1.0"
recase:
dependency: transitive
description:
name: recase
sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213
url: "https://pub.dev"
source: hosted
version: "4.1.0"
receive_sharing_intent: receive_sharing_intent:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1935,6 +1951,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.0" version: "2.4.0"
sqlite3:
dependency: transitive
description:
name: sqlite3
sha256: "32b632dda27d664f85520093ed6f735ae5c49b5b75345afb8b19411bc59bb53d"
url: "https://pub.dev"
source: hosted
version: "2.7.4"
sqlite3_flutter_libs:
dependency: transitive
description:
name: sqlite3_flutter_libs
sha256: "57fafacd815c981735406215966ff7caaa8eab984b094f52e692accefcbd9233"
url: "https://pub.dev"
source: hosted
version: "0.5.30"
sqlparser:
dependency: transitive
description:
name: sqlparser
sha256: "27dd0a9f0c02e22ac0eb42a23df9ea079ce69b52bb4a3b478d64e0ef34a263ee"
url: "https://pub.dev"
source: hosted
version: "0.41.0"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:

View File

@ -72,8 +72,6 @@ dependencies:
collection: ^1.18.0 collection: ^1.18.0
mime: ^2.0.0 mime: ^2.0.0
web_socket_channel: ^3.0.1 web_socket_channel: ^3.0.1
hive: ^2.2.3
hive_flutter: ^1.1.0
swipe_to: ^1.0.6 swipe_to: ^1.0.6
firebase_core: ^3.8.0 firebase_core: ^3.8.0
firebase_messaging: ^15.1.5 firebase_messaging: ^15.1.5
@ -123,6 +121,8 @@ dependencies:
image_picker_android: ^0.8.12+20 image_picker_android: ^0.8.12+20
cached_network_image_platform_interface: ^4.1.1 cached_network_image_platform_interface: ^4.1.1
image_picker_platform_interface: ^2.10.1 image_picker_platform_interface: ^2.10.1
drift: ^2.25.1
drift_flutter: ^0.2.4
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@ -134,13 +134,13 @@ dev_dependencies:
# package. See that file for information about deactivating specific lint # package. See that file for information about deactivating specific lint
# rules and activating additional ones. # rules and activating additional ones.
flutter_lints: ^5.0.0 flutter_lints: ^5.0.0
build_runner: ^2.4.13 build_runner: ^2.4.15
freezed: ^2.5.7 freezed: ^2.5.7
json_serializable: ^6.8.0 json_serializable: ^6.8.0
icons_launcher: ^3.0.0 icons_launcher: ^3.0.0
flutter_native_splash: ^2.4.2 flutter_native_splash: ^2.4.2
hive_generator: ^2.0.1
flutter_launcher_icons: ^0.14.1 flutter_launcher_icons: ^0.14.1
drift_dev: ^2.25.2
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec

13631
web/drift_worker.dart.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,11 +1,11 @@
{ {
"name": "surface", "name": "Solar Network",
"short_name": "surface", "short_name": "Solian",
"start_url": ".", "start_url": ".",
"display": "standalone", "display": "standalone",
"background_color": "#ffffff", "background_color": "#ffffff",
"theme_color": "#ffffff", "theme_color": "#ffffff",
"description": "A new Flutter project.", "description": "The Solar Network is a social network app.",
"orientation": "portrait-primary", "orientation": "portrait-primary",
"prefer_related_applications": false, "prefer_related_applications": false,
"icons": [ "icons": [

BIN
web/sqlite3.wasm Normal file

Binary file not shown.

View File

@ -23,6 +23,7 @@
#include <permission_handler_windows/permission_handler_windows_plugin.h> #include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <screen_brightness_windows/screen_brightness_windows_plugin.h> #include <screen_brightness_windows/screen_brightness_windows_plugin.h>
#include <share_plus/share_plus_windows_plugin_c_api.h> #include <share_plus/share_plus_windows_plugin_c_api.h>
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
#include <tray_manager/tray_manager_plugin.h> #include <tray_manager/tray_manager_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h> #include <url_launcher_windows/url_launcher_windows.h>
@ -61,6 +62,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin")); registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin"));
SharePlusWindowsPluginCApiRegisterWithRegistrar( SharePlusWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
Sqlite3FlutterLibsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin"));
TrayManagerPluginRegisterWithRegistrar( TrayManagerPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("TrayManagerPlugin")); registry->GetRegistrarForPlugin("TrayManagerPlugin"));
UrlLauncherWindowsRegisterWithRegistrar( UrlLauncherWindowsRegisterWithRegistrar(

View File

@ -20,6 +20,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
permission_handler_windows permission_handler_windows
screen_brightness_windows screen_brightness_windows
share_plus share_plus
sqlite3_flutter_libs
tray_manager tray_manager
url_launcher_windows url_launcher_windows
) )