Basic messages & loading

This commit is contained in:
LittleSheep 2025-05-03 13:15:41 +08:00
parent 63ec82891f
commit b2c31bcf13
19 changed files with 2484 additions and 6 deletions

View File

@ -75,5 +75,6 @@
"editChatRoom": "Edit a Room", "editChatRoom": "Edit a Room",
"chat": "Chat", "chat": "Chat",
"chatMessageHint": "Message in {}", "chatMessageHint": "Message in {}",
"chatDirectMessageHint": "Message to {}" "chatDirectMessageHint": "Message to {}",
"loading": "Loading..."
} }

View File

@ -142,6 +142,28 @@ PODS:
- sqflite_darwin (0.0.4): - sqflite_darwin (0.0.4):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- sqlite3 (3.49.1):
- sqlite3/common (= 3.49.1)
- sqlite3/common (3.49.1)
- sqlite3/dbstatvtab (3.49.1):
- sqlite3/common
- sqlite3/fts5 (3.49.1):
- sqlite3/common
- sqlite3/math (3.49.1):
- sqlite3/common
- sqlite3/perf-threadsafe (3.49.1):
- sqlite3/common
- sqlite3/rtree (3.49.1):
- sqlite3/common
- sqlite3_flutter_libs (0.0.1):
- Flutter
- FlutterMacOS
- sqlite3 (~> 3.49.1)
- sqlite3/dbstatvtab
- sqlite3/fts5
- sqlite3/math
- sqlite3/perf-threadsafe
- sqlite3/rtree
- super_native_extensions (0.0.1): - super_native_extensions (0.0.1):
- Flutter - Flutter
- SwiftyGif (5.4.5) - SwiftyGif (5.4.5)
@ -172,6 +194,7 @@ DEPENDENCIES:
- quill_native_bridge_ios (from `.symlinks/plugins/quill_native_bridge_ios/ios`) - quill_native_bridge_ios (from `.symlinks/plugins/quill_native_bridge_ios/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`)
- super_native_extensions (from `.symlinks/plugins/super_native_extensions/ios`) - super_native_extensions (from `.symlinks/plugins/super_native_extensions/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- volume_controller (from `.symlinks/plugins/volume_controller/ios`) - volume_controller (from `.symlinks/plugins/volume_controller/ios`)
@ -194,6 +217,7 @@ SPEC REPOS:
- PromisesObjC - PromisesObjC
- SAMKeychain - SAMKeychain
- SDWebImage - SDWebImage
- sqlite3
- SwiftyGif - SwiftyGif
EXTERNAL SOURCES: EXTERNAL SOURCES:
@ -233,6 +257,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"
super_native_extensions: super_native_extensions:
:path: ".symlinks/plugins/super_native_extensions/ios" :path: ".symlinks/plugins/super_native_extensions/ios"
url_launcher_ios: url_launcher_ios:
@ -276,6 +302,8 @@ SPEC CHECKSUMS:
SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868 SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
sqlite3: fc1400008a9b3525f5914ed715a5d1af0b8f4983
sqlite3_flutter_libs: f6acaa2172e6bb3e2e70c771661905080e8ebcf2
super_native_extensions: b763c02dc3a8fd078389f410bf15149179020cb4 super_native_extensions: b763c02dc3a8fd078389f410bf15149179020cb4
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d url_launcher_ios: 694010445543906933d732453a59da0a173ae33d

View File

@ -0,0 +1,81 @@
import 'dart:convert';
import 'dart:io';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:island/database/message.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
part 'drift_db.g.dart';
// Define the database
@DriftDatabase(tables: [ChatMessages])
class AppDatabase extends _$AppDatabase {
AppDatabase() : super(_openConnection());
@override
int get schemaVersion => 1;
// Methods for chat messages
Future<List<ChatMessage>> getMessagesForRoom(
int roomId, {
int offset = 0,
int limit = 20,
}) {
return (select(chatMessages)
..where((m) => m.roomId.equals(roomId))
..orderBy([(m) => OrderingTerm.desc(m.createdAt)])
..limit(limit, offset: offset))
.get();
}
Future<int> saveMessage(ChatMessagesCompanion message) {
return into(chatMessages).insert(message, mode: InsertMode.insertOrReplace);
}
Future<int> updateMessageStatus(String id, MessageStatus status) {
return (update(chatMessages)..where(
(m) => m.id.equals(id),
)).write(ChatMessagesCompanion(status: Value(status)));
}
Future<int> deleteMessage(String id) {
return (delete(chatMessages)..where((m) => m.id.equals(id))).go();
}
// Convert between Drift and model objects
ChatMessagesCompanion messageToCompanion(LocalChatMessage message) {
return ChatMessagesCompanion(
id: Value(message.id),
roomId: Value(message.roomId),
senderId: Value(message.senderId),
content: Value(message.toRemoteMessage().content),
nonce: Value(message.nonce),
data: Value(jsonEncode(message.data)),
createdAt: Value(message.createdAt),
status: Value(message.status),
);
}
LocalChatMessage companionToMessage(ChatMessage dbMessage) {
final data = jsonDecode(dbMessage.data);
return LocalChatMessage(
id: dbMessage.id,
roomId: dbMessage.roomId,
senderId: dbMessage.senderId,
data: data,
createdAt: dbMessage.createdAt,
status: dbMessage.status,
nonce: dbMessage.nonce,
);
}
}
// Helper to open the database connection
LazyDatabase _openConnection() {
return LazyDatabase(() async {
final dbFolder = await getApplicationDocumentsDirectory();
final file = File(p.join(dbFolder.path, 'island_chat.sqlite'));
return NativeDatabase(file);
});
}

View File

@ -0,0 +1,807 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'drift_db.dart';
// ignore_for_file: type=lint
class $ChatMessagesTable extends ChatMessages
with TableInfo<$ChatMessagesTable, ChatMessage> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
$ChatMessagesTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _idMeta = const VerificationMeta('id');
@override
late final GeneratedColumn<String> id = GeneratedColumn<String>(
'id',
aliasedName,
false,
type: DriftSqlType.string,
requiredDuringInsert: true,
);
static const VerificationMeta _roomIdMeta = const VerificationMeta('roomId');
@override
late final GeneratedColumn<int> roomId = GeneratedColumn<int>(
'room_id',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: true,
);
static const VerificationMeta _senderIdMeta = const VerificationMeta(
'senderId',
);
@override
late final GeneratedColumn<String> senderId = GeneratedColumn<String>(
'sender_id',
aliasedName,
false,
type: DriftSqlType.string,
requiredDuringInsert: true,
);
static const VerificationMeta _contentMeta = const VerificationMeta(
'content',
);
@override
late final GeneratedColumn<String> content = GeneratedColumn<String>(
'content',
aliasedName,
true,
type: DriftSqlType.string,
requiredDuringInsert: false,
);
static const VerificationMeta _nonceMeta = const VerificationMeta('nonce');
@override
late final GeneratedColumn<String> nonce = GeneratedColumn<String>(
'nonce',
aliasedName,
true,
type: DriftSqlType.string,
requiredDuringInsert: false,
);
static const VerificationMeta _dataMeta = const VerificationMeta('data');
@override
late final GeneratedColumn<String> data = GeneratedColumn<String>(
'data',
aliasedName,
false,
type: DriftSqlType.string,
requiredDuringInsert: true,
);
static const VerificationMeta _createdAtMeta = const VerificationMeta(
'createdAt',
);
@override
late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
'created_at',
aliasedName,
false,
type: DriftSqlType.dateTime,
requiredDuringInsert: true,
);
@override
late final GeneratedColumnWithTypeConverter<MessageStatus, int> status =
GeneratedColumn<int>(
'status',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: true,
).withConverter<MessageStatus>($ChatMessagesTable.$converterstatus);
@override
List<GeneratedColumn> get $columns => [
id,
roomId,
senderId,
content,
nonce,
data,
createdAt,
status,
];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'chat_messages';
@override
VerificationContext validateIntegrity(
Insertable<ChatMessage> instance, {
bool isInserting = false,
}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
} else if (isInserting) {
context.missing(_idMeta);
}
if (data.containsKey('room_id')) {
context.handle(
_roomIdMeta,
roomId.isAcceptableOrUnknown(data['room_id']!, _roomIdMeta),
);
} else if (isInserting) {
context.missing(_roomIdMeta);
}
if (data.containsKey('sender_id')) {
context.handle(
_senderIdMeta,
senderId.isAcceptableOrUnknown(data['sender_id']!, _senderIdMeta),
);
} else if (isInserting) {
context.missing(_senderIdMeta);
}
if (data.containsKey('content')) {
context.handle(
_contentMeta,
content.isAcceptableOrUnknown(data['content']!, _contentMeta),
);
}
if (data.containsKey('nonce')) {
context.handle(
_nonceMeta,
nonce.isAcceptableOrUnknown(data['nonce']!, _nonceMeta),
);
}
if (data.containsKey('data')) {
context.handle(
_dataMeta,
this.data.isAcceptableOrUnknown(data['data']!, _dataMeta),
);
} else if (isInserting) {
context.missing(_dataMeta);
}
if (data.containsKey('created_at')) {
context.handle(
_createdAtMeta,
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta),
);
} else if (isInserting) {
context.missing(_createdAtMeta);
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => {id};
@override
ChatMessage map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return ChatMessage(
id:
attachedDatabase.typeMapping.read(
DriftSqlType.string,
data['${effectivePrefix}id'],
)!,
roomId:
attachedDatabase.typeMapping.read(
DriftSqlType.int,
data['${effectivePrefix}room_id'],
)!,
senderId:
attachedDatabase.typeMapping.read(
DriftSqlType.string,
data['${effectivePrefix}sender_id'],
)!,
content: attachedDatabase.typeMapping.read(
DriftSqlType.string,
data['${effectivePrefix}content'],
),
nonce: attachedDatabase.typeMapping.read(
DriftSqlType.string,
data['${effectivePrefix}nonce'],
),
data:
attachedDatabase.typeMapping.read(
DriftSqlType.string,
data['${effectivePrefix}data'],
)!,
createdAt:
attachedDatabase.typeMapping.read(
DriftSqlType.dateTime,
data['${effectivePrefix}created_at'],
)!,
status: $ChatMessagesTable.$converterstatus.fromSql(
attachedDatabase.typeMapping.read(
DriftSqlType.int,
data['${effectivePrefix}status'],
)!,
),
);
}
@override
$ChatMessagesTable createAlias(String alias) {
return $ChatMessagesTable(attachedDatabase, alias);
}
static JsonTypeConverter2<MessageStatus, int, int> $converterstatus =
const EnumIndexConverter<MessageStatus>(MessageStatus.values);
}
class ChatMessage extends DataClass implements Insertable<ChatMessage> {
final String id;
final int roomId;
final String senderId;
final String? content;
final String? nonce;
final String data;
final DateTime createdAt;
final MessageStatus status;
const ChatMessage({
required this.id,
required this.roomId,
required this.senderId,
this.content,
this.nonce,
required this.data,
required this.createdAt,
required this.status,
});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<String>(id);
map['room_id'] = Variable<int>(roomId);
map['sender_id'] = Variable<String>(senderId);
if (!nullToAbsent || content != null) {
map['content'] = Variable<String>(content);
}
if (!nullToAbsent || nonce != null) {
map['nonce'] = Variable<String>(nonce);
}
map['data'] = Variable<String>(data);
map['created_at'] = Variable<DateTime>(createdAt);
{
map['status'] = Variable<int>(
$ChatMessagesTable.$converterstatus.toSql(status),
);
}
return map;
}
ChatMessagesCompanion toCompanion(bool nullToAbsent) {
return ChatMessagesCompanion(
id: Value(id),
roomId: Value(roomId),
senderId: Value(senderId),
content:
content == null && nullToAbsent
? const Value.absent()
: Value(content),
nonce:
nonce == null && nullToAbsent ? const Value.absent() : Value(nonce),
data: Value(data),
createdAt: Value(createdAt),
status: Value(status),
);
}
factory ChatMessage.fromJson(
Map<String, dynamic> json, {
ValueSerializer? serializer,
}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return ChatMessage(
id: serializer.fromJson<String>(json['id']),
roomId: serializer.fromJson<int>(json['roomId']),
senderId: serializer.fromJson<String>(json['senderId']),
content: serializer.fromJson<String?>(json['content']),
nonce: serializer.fromJson<String?>(json['nonce']),
data: serializer.fromJson<String>(json['data']),
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
status: $ChatMessagesTable.$converterstatus.fromJson(
serializer.fromJson<int>(json['status']),
),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<String>(id),
'roomId': serializer.toJson<int>(roomId),
'senderId': serializer.toJson<String>(senderId),
'content': serializer.toJson<String?>(content),
'nonce': serializer.toJson<String?>(nonce),
'data': serializer.toJson<String>(data),
'createdAt': serializer.toJson<DateTime>(createdAt),
'status': serializer.toJson<int>(
$ChatMessagesTable.$converterstatus.toJson(status),
),
};
}
ChatMessage copyWith({
String? id,
int? roomId,
String? senderId,
Value<String?> content = const Value.absent(),
Value<String?> nonce = const Value.absent(),
String? data,
DateTime? createdAt,
MessageStatus? status,
}) => ChatMessage(
id: id ?? this.id,
roomId: roomId ?? this.roomId,
senderId: senderId ?? this.senderId,
content: content.present ? content.value : this.content,
nonce: nonce.present ? nonce.value : this.nonce,
data: data ?? this.data,
createdAt: createdAt ?? this.createdAt,
status: status ?? this.status,
);
ChatMessage copyWithCompanion(ChatMessagesCompanion data) {
return ChatMessage(
id: data.id.present ? data.id.value : this.id,
roomId: data.roomId.present ? data.roomId.value : this.roomId,
senderId: data.senderId.present ? data.senderId.value : this.senderId,
content: data.content.present ? data.content.value : this.content,
nonce: data.nonce.present ? data.nonce.value : this.nonce,
data: data.data.present ? data.data.value : this.data,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
status: data.status.present ? data.status.value : this.status,
);
}
@override
String toString() {
return (StringBuffer('ChatMessage(')
..write('id: $id, ')
..write('roomId: $roomId, ')
..write('senderId: $senderId, ')
..write('content: $content, ')
..write('nonce: $nonce, ')
..write('data: $data, ')
..write('createdAt: $createdAt, ')
..write('status: $status')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(
id,
roomId,
senderId,
content,
nonce,
data,
createdAt,
status,
);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is ChatMessage &&
other.id == this.id &&
other.roomId == this.roomId &&
other.senderId == this.senderId &&
other.content == this.content &&
other.nonce == this.nonce &&
other.data == this.data &&
other.createdAt == this.createdAt &&
other.status == this.status);
}
class ChatMessagesCompanion extends UpdateCompanion<ChatMessage> {
final Value<String> id;
final Value<int> roomId;
final Value<String> senderId;
final Value<String?> content;
final Value<String?> nonce;
final Value<String> data;
final Value<DateTime> createdAt;
final Value<MessageStatus> status;
final Value<int> rowid;
const ChatMessagesCompanion({
this.id = const Value.absent(),
this.roomId = const Value.absent(),
this.senderId = const Value.absent(),
this.content = const Value.absent(),
this.nonce = const Value.absent(),
this.data = const Value.absent(),
this.createdAt = const Value.absent(),
this.status = const Value.absent(),
this.rowid = const Value.absent(),
});
ChatMessagesCompanion.insert({
required String id,
required int roomId,
required String senderId,
this.content = const Value.absent(),
this.nonce = const Value.absent(),
required String data,
required DateTime createdAt,
required MessageStatus status,
this.rowid = const Value.absent(),
}) : id = Value(id),
roomId = Value(roomId),
senderId = Value(senderId),
data = Value(data),
createdAt = Value(createdAt),
status = Value(status);
static Insertable<ChatMessage> custom({
Expression<String>? id,
Expression<int>? roomId,
Expression<String>? senderId,
Expression<String>? content,
Expression<String>? nonce,
Expression<String>? data,
Expression<DateTime>? createdAt,
Expression<int>? status,
Expression<int>? rowid,
}) {
return RawValuesInsertable({
if (id != null) 'id': id,
if (roomId != null) 'room_id': roomId,
if (senderId != null) 'sender_id': senderId,
if (content != null) 'content': content,
if (nonce != null) 'nonce': nonce,
if (data != null) 'data': data,
if (createdAt != null) 'created_at': createdAt,
if (status != null) 'status': status,
if (rowid != null) 'rowid': rowid,
});
}
ChatMessagesCompanion copyWith({
Value<String>? id,
Value<int>? roomId,
Value<String>? senderId,
Value<String?>? content,
Value<String?>? nonce,
Value<String>? data,
Value<DateTime>? createdAt,
Value<MessageStatus>? status,
Value<int>? rowid,
}) {
return ChatMessagesCompanion(
id: id ?? this.id,
roomId: roomId ?? this.roomId,
senderId: senderId ?? this.senderId,
content: content ?? this.content,
nonce: nonce ?? this.nonce,
data: data ?? this.data,
createdAt: createdAt ?? this.createdAt,
status: status ?? this.status,
rowid: rowid ?? this.rowid,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<String>(id.value);
}
if (roomId.present) {
map['room_id'] = Variable<int>(roomId.value);
}
if (senderId.present) {
map['sender_id'] = Variable<String>(senderId.value);
}
if (content.present) {
map['content'] = Variable<String>(content.value);
}
if (nonce.present) {
map['nonce'] = Variable<String>(nonce.value);
}
if (data.present) {
map['data'] = Variable<String>(data.value);
}
if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value);
}
if (status.present) {
map['status'] = Variable<int>(
$ChatMessagesTable.$converterstatus.toSql(status.value),
);
}
if (rowid.present) {
map['rowid'] = Variable<int>(rowid.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('ChatMessagesCompanion(')
..write('id: $id, ')
..write('roomId: $roomId, ')
..write('senderId: $senderId, ')
..write('content: $content, ')
..write('nonce: $nonce, ')
..write('data: $data, ')
..write('createdAt: $createdAt, ')
..write('status: $status, ')
..write('rowid: $rowid')
..write(')'))
.toString();
}
}
abstract class _$AppDatabase extends GeneratedDatabase {
_$AppDatabase(QueryExecutor e) : super(e);
$AppDatabaseManager get managers => $AppDatabaseManager(this);
late final $ChatMessagesTable chatMessages = $ChatMessagesTable(this);
@override
Iterable<TableInfo<Table, Object?>> get allTables =>
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
@override
List<DatabaseSchemaEntity> get allSchemaEntities => [chatMessages];
}
typedef $$ChatMessagesTableCreateCompanionBuilder =
ChatMessagesCompanion Function({
required String id,
required int roomId,
required String senderId,
Value<String?> content,
Value<String?> nonce,
required String data,
required DateTime createdAt,
required MessageStatus status,
Value<int> rowid,
});
typedef $$ChatMessagesTableUpdateCompanionBuilder =
ChatMessagesCompanion Function({
Value<String> id,
Value<int> roomId,
Value<String> senderId,
Value<String?> content,
Value<String?> nonce,
Value<String> data,
Value<DateTime> createdAt,
Value<MessageStatus> status,
Value<int> rowid,
});
class $$ChatMessagesTableFilterComposer
extends Composer<_$AppDatabase, $ChatMessagesTable> {
$$ChatMessagesTableFilterComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
ColumnFilters<String> get id => $composableBuilder(
column: $table.id,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<int> get roomId => $composableBuilder(
column: $table.roomId,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<String> get senderId => $composableBuilder(
column: $table.senderId,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<String> get content => $composableBuilder(
column: $table.content,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<String> get nonce => $composableBuilder(
column: $table.nonce,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<String> get data => $composableBuilder(
column: $table.data,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => ColumnFilters(column),
);
ColumnWithTypeConverterFilters<MessageStatus, MessageStatus, int>
get status => $composableBuilder(
column: $table.status,
builder: (column) => ColumnWithTypeConverterFilters(column),
);
}
class $$ChatMessagesTableOrderingComposer
extends Composer<_$AppDatabase, $ChatMessagesTable> {
$$ChatMessagesTableOrderingComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
ColumnOrderings<String> get id => $composableBuilder(
column: $table.id,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<int> get roomId => $composableBuilder(
column: $table.roomId,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<String> get senderId => $composableBuilder(
column: $table.senderId,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<String> get content => $composableBuilder(
column: $table.content,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<String> get nonce => $composableBuilder(
column: $table.nonce,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<String> get data => $composableBuilder(
column: $table.data,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<DateTime> get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<int> get status => $composableBuilder(
column: $table.status,
builder: (column) => ColumnOrderings(column),
);
}
class $$ChatMessagesTableAnnotationComposer
extends Composer<_$AppDatabase, $ChatMessagesTable> {
$$ChatMessagesTableAnnotationComposer({
required super.$db,
required super.$table,
super.joinBuilder,
super.$addJoinBuilderToRootComposer,
super.$removeJoinBuilderFromRootComposer,
});
GeneratedColumn<String> get id =>
$composableBuilder(column: $table.id, builder: (column) => column);
GeneratedColumn<int> get roomId =>
$composableBuilder(column: $table.roomId, builder: (column) => column);
GeneratedColumn<String> get senderId =>
$composableBuilder(column: $table.senderId, builder: (column) => column);
GeneratedColumn<String> get content =>
$composableBuilder(column: $table.content, builder: (column) => column);
GeneratedColumn<String> get nonce =>
$composableBuilder(column: $table.nonce, builder: (column) => column);
GeneratedColumn<String> get data =>
$composableBuilder(column: $table.data, builder: (column) => column);
GeneratedColumn<DateTime> get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column);
GeneratedColumnWithTypeConverter<MessageStatus, int> get status =>
$composableBuilder(column: $table.status, builder: (column) => column);
}
class $$ChatMessagesTableTableManager
extends
RootTableManager<
_$AppDatabase,
$ChatMessagesTable,
ChatMessage,
$$ChatMessagesTableFilterComposer,
$$ChatMessagesTableOrderingComposer,
$$ChatMessagesTableAnnotationComposer,
$$ChatMessagesTableCreateCompanionBuilder,
$$ChatMessagesTableUpdateCompanionBuilder,
(
ChatMessage,
BaseReferences<_$AppDatabase, $ChatMessagesTable, ChatMessage>,
),
ChatMessage,
PrefetchHooks Function()
> {
$$ChatMessagesTableTableManager(_$AppDatabase db, $ChatMessagesTable table)
: super(
TableManagerState(
db: db,
table: table,
createFilteringComposer:
() => $$ChatMessagesTableFilterComposer($db: db, $table: table),
createOrderingComposer:
() => $$ChatMessagesTableOrderingComposer($db: db, $table: table),
createComputedFieldComposer:
() =>
$$ChatMessagesTableAnnotationComposer($db: db, $table: table),
updateCompanionCallback:
({
Value<String> id = const Value.absent(),
Value<int> roomId = const Value.absent(),
Value<String> senderId = const Value.absent(),
Value<String?> content = const Value.absent(),
Value<String?> nonce = const Value.absent(),
Value<String> data = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(),
Value<MessageStatus> status = const Value.absent(),
Value<int> rowid = const Value.absent(),
}) => ChatMessagesCompanion(
id: id,
roomId: roomId,
senderId: senderId,
content: content,
nonce: nonce,
data: data,
createdAt: createdAt,
status: status,
rowid: rowid,
),
createCompanionCallback:
({
required String id,
required int roomId,
required String senderId,
Value<String?> content = const Value.absent(),
Value<String?> nonce = const Value.absent(),
required String data,
required DateTime createdAt,
required MessageStatus status,
Value<int> rowid = const Value.absent(),
}) => ChatMessagesCompanion.insert(
id: id,
roomId: roomId,
senderId: senderId,
content: content,
nonce: nonce,
data: data,
createdAt: createdAt,
status: status,
rowid: rowid,
),
withReferenceMapper:
(p0) =>
p0
.map(
(e) => (
e.readTable(table),
BaseReferences(db, table, e),
),
)
.toList(),
prefetchHooksCallback: null,
),
);
}
typedef $$ChatMessagesTableProcessedTableManager =
ProcessedTableManager<
_$AppDatabase,
$ChatMessagesTable,
ChatMessage,
$$ChatMessagesTableFilterComposer,
$$ChatMessagesTableOrderingComposer,
$$ChatMessagesTableAnnotationComposer,
$$ChatMessagesTableCreateCompanionBuilder,
$$ChatMessagesTableUpdateCompanionBuilder,
(
ChatMessage,
BaseReferences<_$AppDatabase, $ChatMessagesTable, ChatMessage>,
),
ChatMessage,
PrefetchHooks Function()
>;
class $AppDatabaseManager {
final _$AppDatabase _db;
$AppDatabaseManager(this._db);
$$ChatMessagesTableTableManager get chatMessages =>
$$ChatMessagesTableTableManager(_db, _db.chatMessages);
}

58
lib/database/message.dart Normal file
View File

@ -0,0 +1,58 @@
import 'package:drift/drift.dart';
import 'package:island/models/chat.dart';
class ChatMessages extends Table {
TextColumn get id => text()();
IntColumn get roomId => integer()();
TextColumn get senderId => text()();
TextColumn get content => text().nullable()();
TextColumn get nonce => text().nullable()();
TextColumn get data => text()();
DateTimeColumn get createdAt => dateTime()();
IntColumn get status => intEnum<MessageStatus>()();
@override
Set<Column> get primaryKey => {id};
}
class LocalChatMessage {
final String id;
final int roomId;
final String senderId;
final Map<String, dynamic> data;
final DateTime createdAt;
MessageStatus status;
final String? nonce;
LocalChatMessage({
required this.id,
required this.roomId,
required this.senderId,
required this.data,
required this.createdAt,
required this.status,
this.nonce,
});
SnChatMessage toRemoteMessage() {
return SnChatMessage.fromJson(data);
}
static LocalChatMessage fromRemoteMessage(
SnChatMessage message,
MessageStatus status, {
String? nonce,
}) {
return LocalChatMessage(
id: message.id,
roomId: message.chatRoomId,
senderId: message.senderId,
data: message.toJson(),
createdAt: message.createdAt,
status: status,
nonce: nonce ?? message.nonce,
);
}
}
enum MessageStatus { pending, sent, failed }

View File

@ -0,0 +1,263 @@
import 'package:dio/dio.dart';
import 'package:island/database/drift_db.dart';
import 'package:island/database/message.dart';
import 'package:island/models/chat.dart';
import 'package:island/models/file.dart';
import 'package:uuid/uuid.dart';
class MessageRepository {
final SnChat room;
final Dio _apiClient;
final AppDatabase _database;
SnChatMember? _identity;
final Map<String, LocalChatMessage> _pendingMessages = {};
MessageRepository(this.room, this._apiClient, this._database) {
initialize();
}
bool initialized = false;
Future<void> initialize() async {
if (initialized) return;
try {
final response = await _apiClient.get('/chat/${room.id}/members/me');
_identity = SnChatMember.fromJson(response.data);
initialized = true;
} catch (e) {
rethrow;
}
}
Future<List<LocalChatMessage>> listMessages({
int offset = 0,
int take = 20,
}) async {
try {
final localMessages = await _getCachedMessages(
room.id,
offset: offset,
take: take,
);
if (offset == 0) {
// Always fetch latest messages in background if we're loading the first page
_fetchAndCacheMessages(room.id, offset: offset, take: take);
if (localMessages.isNotEmpty) {
return localMessages;
}
}
return await _fetchAndCacheMessages(room.id, offset: offset, take: take);
} catch (e) {
// If API fails but we have local messages, return them
final localMessages = await _getCachedMessages(
room.id,
offset: offset,
take: take,
);
if (localMessages.isNotEmpty) {
return localMessages;
}
rethrow;
}
}
Future<List<LocalChatMessage>> _getCachedMessages(
int roomId, {
int offset = 0,
int take = 20,
}) async {
// Get messages from local database
final dbMessages = await _database.getMessagesForRoom(
roomId,
offset: offset,
limit: take,
);
final dbLocalMessages =
dbMessages.map(_database.companionToMessage).toList();
// Combine with pending messages
final pendingForRoom =
_pendingMessages.values.where((msg) => msg.roomId == roomId).toList();
// Sort by timestamp descending (newest first)
final allMessages = [...pendingForRoom, ...dbLocalMessages];
allMessages.sort((a, b) => b.createdAt.compareTo(a.createdAt));
// Apply pagination
if (offset >= allMessages.length) {
return [];
}
final end =
(offset + take) > allMessages.length
? allMessages.length
: (offset + take);
return allMessages.sublist(offset, end);
}
Future<List<LocalChatMessage>> _fetchAndCacheMessages(
int roomId, {
int offset = 0,
int take = 20,
}) async {
final response = await _apiClient.get(
'/chat/$roomId/messages',
queryParameters: {'offset': offset, 'take': take},
);
final total = int.parse(response.headers.value('X-Total') ?? '0');
final List<dynamic> data = response.data;
final messages =
data.map((json) {
final remoteMessage = SnChatMessage.fromJson(json);
return LocalChatMessage.fromRemoteMessage(
remoteMessage,
MessageStatus.sent,
);
}).toList();
for (final message in messages) {
await _database.saveMessage(_database.messageToCompanion(message));
if (message.nonce != null) {
_pendingMessages.removeWhere(
(_, pendingMsg) => pendingMsg.nonce == message.nonce,
);
}
}
return messages;
}
Future<LocalChatMessage> sendMessage(
int roomId,
String content, {
List<SnCloudFile>? attachments,
Map<String, dynamic>? meta,
}) async {
if (!initialized) {
throw UnsupportedError(
"The message repository is not ready for send message.",
);
}
// Generate a unique nonce for this message
final nonce = const Uuid().v4();
// Create a local message with pending status
final mockMessage = SnChatMessage(
id: 'pending_$nonce',
chatRoomId: roomId,
senderId: _identity!.id,
content: content,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
nonce: nonce,
sender: _identity!,
);
final localMessage = LocalChatMessage.fromRemoteMessage(
mockMessage,
MessageStatus.pending,
);
// Store in memory and database
_pendingMessages[localMessage.id] = localMessage;
await _database.saveMessage(_database.messageToCompanion(localMessage));
try {
// Send to server
final response = await _apiClient.post(
'/chat/$roomId/messages',
data: {
'content': content,
'attachments_id': attachments,
'meta': meta,
'nonce': nonce,
},
);
// Update with server response
final remoteMessage = SnChatMessage.fromJson(response.data);
final updatedMessage = LocalChatMessage.fromRemoteMessage(
remoteMessage,
MessageStatus.sent,
);
// Remove from pending and update in database
_pendingMessages.remove(localMessage.id);
await _database.deleteMessage(localMessage.id);
await _database.saveMessage(_database.messageToCompanion(updatedMessage));
return updatedMessage;
} catch (e) {
// Update status to failed
localMessage.status = MessageStatus.failed;
_pendingMessages[localMessage.id] = localMessage;
await _database.updateMessageStatus(
localMessage.id,
MessageStatus.failed,
);
rethrow;
}
}
Future<LocalChatMessage> retryMessage(String pendingMessageId) async {
final message = _pendingMessages[pendingMessageId];
if (message == null) {
throw Exception('Message not found');
}
// Update status back to pending
message.status = MessageStatus.pending;
_pendingMessages[pendingMessageId] = message;
await _database.updateMessageStatus(
pendingMessageId,
MessageStatus.pending,
);
try {
// Send to server
var remoteMessage = message.toRemoteMessage();
final response = await _apiClient.post(
'/chat/${message.roomId}/messages',
data: {
'content': remoteMessage.content,
'attachments_id': remoteMessage.attachments,
'meta': remoteMessage.meta,
'nonce': message.nonce,
},
);
// Update with server response
remoteMessage = SnChatMessage.fromJson(response.data);
final updatedMessage = LocalChatMessage.fromRemoteMessage(
remoteMessage,
MessageStatus.sent,
);
// Remove from pending and update in database
_pendingMessages.remove(pendingMessageId);
await _database.deleteMessage(pendingMessageId);
await _database.saveMessage(_database.messageToCompanion(updatedMessage));
return updatedMessage;
} catch (e) {
// Update status to failed
message.status = MessageStatus.failed;
_pendingMessages[pendingMessageId] = message;
await _database.updateMessageStatus(
pendingMessageId,
MessageStatus.failed,
);
rethrow;
}
}
}

View File

@ -24,3 +24,68 @@ abstract class SnChat with _$SnChat {
factory SnChat.fromJson(Map<String, dynamic> json) => _$SnChatFromJson(json); factory SnChat.fromJson(Map<String, dynamic> json) => _$SnChatFromJson(json);
} }
@freezed
abstract class SnChatMessage with _$SnChatMessage {
const factory SnChatMessage({
required DateTime createdAt,
required DateTime updatedAt,
DateTime? deletedAt,
required String id,
String? content,
String? nonce,
@Default({}) Map<String, dynamic> meta,
@Default([]) List<String> membersMetioned,
DateTime? editedAt,
@Default([]) List<SnCloudFile> attachments,
@Default([]) List<SnChatReaction> reactions,
String? repliedMessageId,
SnChatMessage? repliedMessage,
String? forwardedMessageId,
SnChatMessage? forwardedMessage,
required String senderId,
required SnChatMember sender,
required int chatRoomId,
}) = _SnChatMessage;
factory SnChatMessage.fromJson(Map<String, dynamic> json) =>
_$SnChatMessageFromJson(json);
}
@freezed
abstract class SnChatReaction with _$SnChatReaction {
const factory SnChatReaction({
required DateTime createdAt,
required DateTime updatedAt,
required DateTime? deletedAt,
required String id,
required String messageId,
required String senderId,
required SnChatMember sender,
required String symbol,
required int attitude,
}) = _SnChatReaction;
factory SnChatReaction.fromJson(Map<String, dynamic> json) =>
_$SnChatReactionFromJson(json);
}
@freezed
abstract class SnChatMember with _$SnChatMember {
const factory SnChatMember({
required DateTime createdAt,
required DateTime updatedAt,
required DateTime? deletedAt,
required String id,
required int chatRoomId,
required int accountId,
required String? nick,
required int role,
required int notify,
required DateTime? joinedAt,
required bool isBot,
}) = _SnChatMember;
factory SnChatMember.fromJson(Map<String, dynamic> json) =>
_$SnChatMemberFromJson(json);
}

View File

@ -250,4 +250,616 @@ $SnRealmCopyWith<$Res>? get realm {
} }
} }
/// @nodoc
mixin _$SnChatMessage {
DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; String get id; String? get content; String? get nonce; Map<String, dynamic> get meta; List<String> get membersMetioned; DateTime? get editedAt; List<SnCloudFile> get attachments; List<SnChatReaction> get reactions; String? get repliedMessageId; SnChatMessage? get repliedMessage; String? get forwardedMessageId; SnChatMessage? get forwardedMessage; String get senderId; SnChatMember get sender; int get chatRoomId;
/// Create a copy of SnChatMessage
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$SnChatMessageCopyWith<SnChatMessage> get copyWith => _$SnChatMessageCopyWithImpl<SnChatMessage>(this as SnChatMessage, _$identity);
/// Serializes this SnChatMessage to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnChatMessage&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.id, id) || other.id == id)&&(identical(other.content, content) || other.content == content)&&(identical(other.nonce, nonce) || other.nonce == nonce)&&const DeepCollectionEquality().equals(other.meta, meta)&&const DeepCollectionEquality().equals(other.membersMetioned, membersMetioned)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&const DeepCollectionEquality().equals(other.attachments, attachments)&&const DeepCollectionEquality().equals(other.reactions, reactions)&&(identical(other.repliedMessageId, repliedMessageId) || other.repliedMessageId == repliedMessageId)&&(identical(other.repliedMessage, repliedMessage) || other.repliedMessage == repliedMessage)&&(identical(other.forwardedMessageId, forwardedMessageId) || other.forwardedMessageId == forwardedMessageId)&&(identical(other.forwardedMessage, forwardedMessage) || other.forwardedMessage == forwardedMessage)&&(identical(other.senderId, senderId) || other.senderId == senderId)&&(identical(other.sender, sender) || other.sender == sender)&&(identical(other.chatRoomId, chatRoomId) || other.chatRoomId == chatRoomId));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,createdAt,updatedAt,deletedAt,id,content,nonce,const DeepCollectionEquality().hash(meta),const DeepCollectionEquality().hash(membersMetioned),editedAt,const DeepCollectionEquality().hash(attachments),const DeepCollectionEquality().hash(reactions),repliedMessageId,repliedMessage,forwardedMessageId,forwardedMessage,senderId,sender,chatRoomId);
@override
String toString() {
return 'SnChatMessage(createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, id: $id, content: $content, nonce: $nonce, meta: $meta, membersMetioned: $membersMetioned, editedAt: $editedAt, attachments: $attachments, reactions: $reactions, repliedMessageId: $repliedMessageId, repliedMessage: $repliedMessage, forwardedMessageId: $forwardedMessageId, forwardedMessage: $forwardedMessage, senderId: $senderId, sender: $sender, chatRoomId: $chatRoomId)';
}
}
/// @nodoc
abstract mixin class $SnChatMessageCopyWith<$Res> {
factory $SnChatMessageCopyWith(SnChatMessage value, $Res Function(SnChatMessage) _then) = _$SnChatMessageCopyWithImpl;
@useResult
$Res call({
DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String? content, String? nonce, Map<String, dynamic> meta, List<String> membersMetioned, DateTime? editedAt, List<SnCloudFile> attachments, List<SnChatReaction> reactions, String? repliedMessageId, SnChatMessage? repliedMessage, String? forwardedMessageId, SnChatMessage? forwardedMessage, String senderId, SnChatMember sender, int chatRoomId
});
$SnChatMessageCopyWith<$Res>? get repliedMessage;$SnChatMessageCopyWith<$Res>? get forwardedMessage;$SnChatMemberCopyWith<$Res> get sender;
}
/// @nodoc
class _$SnChatMessageCopyWithImpl<$Res>
implements $SnChatMessageCopyWith<$Res> {
_$SnChatMessageCopyWithImpl(this._self, this._then);
final SnChatMessage _self;
final $Res Function(SnChatMessage) _then;
/// Create a copy of SnChatMessage
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? id = null,Object? content = freezed,Object? nonce = freezed,Object? meta = null,Object? membersMetioned = null,Object? editedAt = freezed,Object? attachments = null,Object? reactions = null,Object? repliedMessageId = freezed,Object? repliedMessage = freezed,Object? forwardedMessageId = freezed,Object? forwardedMessage = freezed,Object? senderId = null,Object? sender = null,Object? chatRoomId = null,}) {
return _then(_self.copyWith(
createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,content: freezed == content ? _self.content : content // ignore: cast_nullable_to_non_nullable
as String?,nonce: freezed == nonce ? _self.nonce : nonce // ignore: cast_nullable_to_non_nullable
as String?,meta: null == meta ? _self.meta : meta // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,membersMetioned: null == membersMetioned ? _self.membersMetioned : membersMetioned // ignore: cast_nullable_to_non_nullable
as List<String>,editedAt: freezed == editedAt ? _self.editedAt : editedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,attachments: null == attachments ? _self.attachments : attachments // ignore: cast_nullable_to_non_nullable
as List<SnCloudFile>,reactions: null == reactions ? _self.reactions : reactions // ignore: cast_nullable_to_non_nullable
as List<SnChatReaction>,repliedMessageId: freezed == repliedMessageId ? _self.repliedMessageId : repliedMessageId // ignore: cast_nullable_to_non_nullable
as String?,repliedMessage: freezed == repliedMessage ? _self.repliedMessage : repliedMessage // ignore: cast_nullable_to_non_nullable
as SnChatMessage?,forwardedMessageId: freezed == forwardedMessageId ? _self.forwardedMessageId : forwardedMessageId // ignore: cast_nullable_to_non_nullable
as String?,forwardedMessage: freezed == forwardedMessage ? _self.forwardedMessage : forwardedMessage // ignore: cast_nullable_to_non_nullable
as SnChatMessage?,senderId: null == senderId ? _self.senderId : senderId // ignore: cast_nullable_to_non_nullable
as String,sender: null == sender ? _self.sender : sender // ignore: cast_nullable_to_non_nullable
as SnChatMember,chatRoomId: null == chatRoomId ? _self.chatRoomId : chatRoomId // ignore: cast_nullable_to_non_nullable
as int,
));
}
/// Create a copy of SnChatMessage
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnChatMessageCopyWith<$Res>? get repliedMessage {
if (_self.repliedMessage == null) {
return null;
}
return $SnChatMessageCopyWith<$Res>(_self.repliedMessage!, (value) {
return _then(_self.copyWith(repliedMessage: value));
});
}/// Create a copy of SnChatMessage
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnChatMessageCopyWith<$Res>? get forwardedMessage {
if (_self.forwardedMessage == null) {
return null;
}
return $SnChatMessageCopyWith<$Res>(_self.forwardedMessage!, (value) {
return _then(_self.copyWith(forwardedMessage: value));
});
}/// Create a copy of SnChatMessage
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnChatMemberCopyWith<$Res> get sender {
return $SnChatMemberCopyWith<$Res>(_self.sender, (value) {
return _then(_self.copyWith(sender: value));
});
}
}
/// @nodoc
@JsonSerializable()
class _SnChatMessage implements SnChatMessage {
const _SnChatMessage({required this.createdAt, required this.updatedAt, this.deletedAt, required this.id, this.content, this.nonce, final Map<String, dynamic> meta = const {}, final List<String> membersMetioned = const [], this.editedAt, final List<SnCloudFile> attachments = const [], final List<SnChatReaction> reactions = const [], this.repliedMessageId, this.repliedMessage, this.forwardedMessageId, this.forwardedMessage, required this.senderId, required this.sender, required this.chatRoomId}): _meta = meta,_membersMetioned = membersMetioned,_attachments = attachments,_reactions = reactions;
factory _SnChatMessage.fromJson(Map<String, dynamic> json) => _$SnChatMessageFromJson(json);
@override final DateTime createdAt;
@override final DateTime updatedAt;
@override final DateTime? deletedAt;
@override final String id;
@override final String? content;
@override final String? nonce;
final Map<String, dynamic> _meta;
@override@JsonKey() Map<String, dynamic> get meta {
if (_meta is EqualUnmodifiableMapView) return _meta;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_meta);
}
final List<String> _membersMetioned;
@override@JsonKey() List<String> get membersMetioned {
if (_membersMetioned is EqualUnmodifiableListView) return _membersMetioned;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_membersMetioned);
}
@override final DateTime? editedAt;
final List<SnCloudFile> _attachments;
@override@JsonKey() List<SnCloudFile> get attachments {
if (_attachments is EqualUnmodifiableListView) return _attachments;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_attachments);
}
final List<SnChatReaction> _reactions;
@override@JsonKey() List<SnChatReaction> get reactions {
if (_reactions is EqualUnmodifiableListView) return _reactions;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_reactions);
}
@override final String? repliedMessageId;
@override final SnChatMessage? repliedMessage;
@override final String? forwardedMessageId;
@override final SnChatMessage? forwardedMessage;
@override final String senderId;
@override final SnChatMember sender;
@override final int chatRoomId;
/// Create a copy of SnChatMessage
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$SnChatMessageCopyWith<_SnChatMessage> get copyWith => __$SnChatMessageCopyWithImpl<_SnChatMessage>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$SnChatMessageToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnChatMessage&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.id, id) || other.id == id)&&(identical(other.content, content) || other.content == content)&&(identical(other.nonce, nonce) || other.nonce == nonce)&&const DeepCollectionEquality().equals(other._meta, _meta)&&const DeepCollectionEquality().equals(other._membersMetioned, _membersMetioned)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&const DeepCollectionEquality().equals(other._attachments, _attachments)&&const DeepCollectionEquality().equals(other._reactions, _reactions)&&(identical(other.repliedMessageId, repliedMessageId) || other.repliedMessageId == repliedMessageId)&&(identical(other.repliedMessage, repliedMessage) || other.repliedMessage == repliedMessage)&&(identical(other.forwardedMessageId, forwardedMessageId) || other.forwardedMessageId == forwardedMessageId)&&(identical(other.forwardedMessage, forwardedMessage) || other.forwardedMessage == forwardedMessage)&&(identical(other.senderId, senderId) || other.senderId == senderId)&&(identical(other.sender, sender) || other.sender == sender)&&(identical(other.chatRoomId, chatRoomId) || other.chatRoomId == chatRoomId));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,createdAt,updatedAt,deletedAt,id,content,nonce,const DeepCollectionEquality().hash(_meta),const DeepCollectionEquality().hash(_membersMetioned),editedAt,const DeepCollectionEquality().hash(_attachments),const DeepCollectionEquality().hash(_reactions),repliedMessageId,repliedMessage,forwardedMessageId,forwardedMessage,senderId,sender,chatRoomId);
@override
String toString() {
return 'SnChatMessage(createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, id: $id, content: $content, nonce: $nonce, meta: $meta, membersMetioned: $membersMetioned, editedAt: $editedAt, attachments: $attachments, reactions: $reactions, repliedMessageId: $repliedMessageId, repliedMessage: $repliedMessage, forwardedMessageId: $forwardedMessageId, forwardedMessage: $forwardedMessage, senderId: $senderId, sender: $sender, chatRoomId: $chatRoomId)';
}
}
/// @nodoc
abstract mixin class _$SnChatMessageCopyWith<$Res> implements $SnChatMessageCopyWith<$Res> {
factory _$SnChatMessageCopyWith(_SnChatMessage value, $Res Function(_SnChatMessage) _then) = __$SnChatMessageCopyWithImpl;
@override @useResult
$Res call({
DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String? content, String? nonce, Map<String, dynamic> meta, List<String> membersMetioned, DateTime? editedAt, List<SnCloudFile> attachments, List<SnChatReaction> reactions, String? repliedMessageId, SnChatMessage? repliedMessage, String? forwardedMessageId, SnChatMessage? forwardedMessage, String senderId, SnChatMember sender, int chatRoomId
});
@override $SnChatMessageCopyWith<$Res>? get repliedMessage;@override $SnChatMessageCopyWith<$Res>? get forwardedMessage;@override $SnChatMemberCopyWith<$Res> get sender;
}
/// @nodoc
class __$SnChatMessageCopyWithImpl<$Res>
implements _$SnChatMessageCopyWith<$Res> {
__$SnChatMessageCopyWithImpl(this._self, this._then);
final _SnChatMessage _self;
final $Res Function(_SnChatMessage) _then;
/// Create a copy of SnChatMessage
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? id = null,Object? content = freezed,Object? nonce = freezed,Object? meta = null,Object? membersMetioned = null,Object? editedAt = freezed,Object? attachments = null,Object? reactions = null,Object? repliedMessageId = freezed,Object? repliedMessage = freezed,Object? forwardedMessageId = freezed,Object? forwardedMessage = freezed,Object? senderId = null,Object? sender = null,Object? chatRoomId = null,}) {
return _then(_SnChatMessage(
createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,content: freezed == content ? _self.content : content // ignore: cast_nullable_to_non_nullable
as String?,nonce: freezed == nonce ? _self.nonce : nonce // ignore: cast_nullable_to_non_nullable
as String?,meta: null == meta ? _self._meta : meta // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,membersMetioned: null == membersMetioned ? _self._membersMetioned : membersMetioned // ignore: cast_nullable_to_non_nullable
as List<String>,editedAt: freezed == editedAt ? _self.editedAt : editedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,attachments: null == attachments ? _self._attachments : attachments // ignore: cast_nullable_to_non_nullable
as List<SnCloudFile>,reactions: null == reactions ? _self._reactions : reactions // ignore: cast_nullable_to_non_nullable
as List<SnChatReaction>,repliedMessageId: freezed == repliedMessageId ? _self.repliedMessageId : repliedMessageId // ignore: cast_nullable_to_non_nullable
as String?,repliedMessage: freezed == repliedMessage ? _self.repliedMessage : repliedMessage // ignore: cast_nullable_to_non_nullable
as SnChatMessage?,forwardedMessageId: freezed == forwardedMessageId ? _self.forwardedMessageId : forwardedMessageId // ignore: cast_nullable_to_non_nullable
as String?,forwardedMessage: freezed == forwardedMessage ? _self.forwardedMessage : forwardedMessage // ignore: cast_nullable_to_non_nullable
as SnChatMessage?,senderId: null == senderId ? _self.senderId : senderId // ignore: cast_nullable_to_non_nullable
as String,sender: null == sender ? _self.sender : sender // ignore: cast_nullable_to_non_nullable
as SnChatMember,chatRoomId: null == chatRoomId ? _self.chatRoomId : chatRoomId // ignore: cast_nullable_to_non_nullable
as int,
));
}
/// Create a copy of SnChatMessage
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnChatMessageCopyWith<$Res>? get repliedMessage {
if (_self.repliedMessage == null) {
return null;
}
return $SnChatMessageCopyWith<$Res>(_self.repliedMessage!, (value) {
return _then(_self.copyWith(repliedMessage: value));
});
}/// Create a copy of SnChatMessage
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnChatMessageCopyWith<$Res>? get forwardedMessage {
if (_self.forwardedMessage == null) {
return null;
}
return $SnChatMessageCopyWith<$Res>(_self.forwardedMessage!, (value) {
return _then(_self.copyWith(forwardedMessage: value));
});
}/// Create a copy of SnChatMessage
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnChatMemberCopyWith<$Res> get sender {
return $SnChatMemberCopyWith<$Res>(_self.sender, (value) {
return _then(_self.copyWith(sender: value));
});
}
}
/// @nodoc
mixin _$SnChatReaction {
DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; String get id; String get messageId; String get senderId; SnChatMember get sender; String get symbol; int get attitude;
/// Create a copy of SnChatReaction
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$SnChatReactionCopyWith<SnChatReaction> get copyWith => _$SnChatReactionCopyWithImpl<SnChatReaction>(this as SnChatReaction, _$identity);
/// Serializes this SnChatReaction to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnChatReaction&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.id, id) || other.id == id)&&(identical(other.messageId, messageId) || other.messageId == messageId)&&(identical(other.senderId, senderId) || other.senderId == senderId)&&(identical(other.sender, sender) || other.sender == sender)&&(identical(other.symbol, symbol) || other.symbol == symbol)&&(identical(other.attitude, attitude) || other.attitude == attitude));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,createdAt,updatedAt,deletedAt,id,messageId,senderId,sender,symbol,attitude);
@override
String toString() {
return 'SnChatReaction(createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, id: $id, messageId: $messageId, senderId: $senderId, sender: $sender, symbol: $symbol, attitude: $attitude)';
}
}
/// @nodoc
abstract mixin class $SnChatReactionCopyWith<$Res> {
factory $SnChatReactionCopyWith(SnChatReaction value, $Res Function(SnChatReaction) _then) = _$SnChatReactionCopyWithImpl;
@useResult
$Res call({
DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String messageId, String senderId, SnChatMember sender, String symbol, int attitude
});
$SnChatMemberCopyWith<$Res> get sender;
}
/// @nodoc
class _$SnChatReactionCopyWithImpl<$Res>
implements $SnChatReactionCopyWith<$Res> {
_$SnChatReactionCopyWithImpl(this._self, this._then);
final SnChatReaction _self;
final $Res Function(SnChatReaction) _then;
/// Create a copy of SnChatReaction
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? id = null,Object? messageId = null,Object? senderId = null,Object? sender = null,Object? symbol = null,Object? attitude = null,}) {
return _then(_self.copyWith(
createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,messageId: null == messageId ? _self.messageId : messageId // ignore: cast_nullable_to_non_nullable
as String,senderId: null == senderId ? _self.senderId : senderId // ignore: cast_nullable_to_non_nullable
as String,sender: null == sender ? _self.sender : sender // ignore: cast_nullable_to_non_nullable
as SnChatMember,symbol: null == symbol ? _self.symbol : symbol // ignore: cast_nullable_to_non_nullable
as String,attitude: null == attitude ? _self.attitude : attitude // ignore: cast_nullable_to_non_nullable
as int,
));
}
/// Create a copy of SnChatReaction
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnChatMemberCopyWith<$Res> get sender {
return $SnChatMemberCopyWith<$Res>(_self.sender, (value) {
return _then(_self.copyWith(sender: value));
});
}
}
/// @nodoc
@JsonSerializable()
class _SnChatReaction implements SnChatReaction {
const _SnChatReaction({required this.createdAt, required this.updatedAt, required this.deletedAt, required this.id, required this.messageId, required this.senderId, required this.sender, required this.symbol, required this.attitude});
factory _SnChatReaction.fromJson(Map<String, dynamic> json) => _$SnChatReactionFromJson(json);
@override final DateTime createdAt;
@override final DateTime updatedAt;
@override final DateTime? deletedAt;
@override final String id;
@override final String messageId;
@override final String senderId;
@override final SnChatMember sender;
@override final String symbol;
@override final int attitude;
/// Create a copy of SnChatReaction
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$SnChatReactionCopyWith<_SnChatReaction> get copyWith => __$SnChatReactionCopyWithImpl<_SnChatReaction>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$SnChatReactionToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnChatReaction&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.id, id) || other.id == id)&&(identical(other.messageId, messageId) || other.messageId == messageId)&&(identical(other.senderId, senderId) || other.senderId == senderId)&&(identical(other.sender, sender) || other.sender == sender)&&(identical(other.symbol, symbol) || other.symbol == symbol)&&(identical(other.attitude, attitude) || other.attitude == attitude));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,createdAt,updatedAt,deletedAt,id,messageId,senderId,sender,symbol,attitude);
@override
String toString() {
return 'SnChatReaction(createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, id: $id, messageId: $messageId, senderId: $senderId, sender: $sender, symbol: $symbol, attitude: $attitude)';
}
}
/// @nodoc
abstract mixin class _$SnChatReactionCopyWith<$Res> implements $SnChatReactionCopyWith<$Res> {
factory _$SnChatReactionCopyWith(_SnChatReaction value, $Res Function(_SnChatReaction) _then) = __$SnChatReactionCopyWithImpl;
@override @useResult
$Res call({
DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String messageId, String senderId, SnChatMember sender, String symbol, int attitude
});
@override $SnChatMemberCopyWith<$Res> get sender;
}
/// @nodoc
class __$SnChatReactionCopyWithImpl<$Res>
implements _$SnChatReactionCopyWith<$Res> {
__$SnChatReactionCopyWithImpl(this._self, this._then);
final _SnChatReaction _self;
final $Res Function(_SnChatReaction) _then;
/// Create a copy of SnChatReaction
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? id = null,Object? messageId = null,Object? senderId = null,Object? sender = null,Object? symbol = null,Object? attitude = null,}) {
return _then(_SnChatReaction(
createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,messageId: null == messageId ? _self.messageId : messageId // ignore: cast_nullable_to_non_nullable
as String,senderId: null == senderId ? _self.senderId : senderId // ignore: cast_nullable_to_non_nullable
as String,sender: null == sender ? _self.sender : sender // ignore: cast_nullable_to_non_nullable
as SnChatMember,symbol: null == symbol ? _self.symbol : symbol // ignore: cast_nullable_to_non_nullable
as String,attitude: null == attitude ? _self.attitude : attitude // ignore: cast_nullable_to_non_nullable
as int,
));
}
/// Create a copy of SnChatReaction
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnChatMemberCopyWith<$Res> get sender {
return $SnChatMemberCopyWith<$Res>(_self.sender, (value) {
return _then(_self.copyWith(sender: value));
});
}
}
/// @nodoc
mixin _$SnChatMember {
DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; String get id; int get chatRoomId; int get accountId; String? get nick; int get role; int get notify; DateTime? get joinedAt; bool get isBot;
/// Create a copy of SnChatMember
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$SnChatMemberCopyWith<SnChatMember> get copyWith => _$SnChatMemberCopyWithImpl<SnChatMember>(this as SnChatMember, _$identity);
/// Serializes this SnChatMember to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnChatMember&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.id, id) || other.id == id)&&(identical(other.chatRoomId, chatRoomId) || other.chatRoomId == chatRoomId)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.role, role) || other.role == role)&&(identical(other.notify, notify) || other.notify == notify)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt)&&(identical(other.isBot, isBot) || other.isBot == isBot));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,createdAt,updatedAt,deletedAt,id,chatRoomId,accountId,nick,role,notify,joinedAt,isBot);
@override
String toString() {
return 'SnChatMember(createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, id: $id, chatRoomId: $chatRoomId, accountId: $accountId, nick: $nick, role: $role, notify: $notify, joinedAt: $joinedAt, isBot: $isBot)';
}
}
/// @nodoc
abstract mixin class $SnChatMemberCopyWith<$Res> {
factory $SnChatMemberCopyWith(SnChatMember value, $Res Function(SnChatMember) _then) = _$SnChatMemberCopyWithImpl;
@useResult
$Res call({
DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, int chatRoomId, int accountId, String? nick, int role, int notify, DateTime? joinedAt, bool isBot
});
}
/// @nodoc
class _$SnChatMemberCopyWithImpl<$Res>
implements $SnChatMemberCopyWith<$Res> {
_$SnChatMemberCopyWithImpl(this._self, this._then);
final SnChatMember _self;
final $Res Function(SnChatMember) _then;
/// Create a copy of SnChatMember
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? id = null,Object? chatRoomId = null,Object? accountId = null,Object? nick = freezed,Object? role = null,Object? notify = null,Object? joinedAt = freezed,Object? isBot = null,}) {
return _then(_self.copyWith(
createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,chatRoomId: null == chatRoomId ? _self.chatRoomId : chatRoomId // ignore: cast_nullable_to_non_nullable
as int,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
as int,nick: freezed == nick ? _self.nick : nick // ignore: cast_nullable_to_non_nullable
as String?,role: null == role ? _self.role : role // ignore: cast_nullable_to_non_nullable
as int,notify: null == notify ? _self.notify : notify // ignore: cast_nullable_to_non_nullable
as int,joinedAt: freezed == joinedAt ? _self.joinedAt : joinedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,isBot: null == isBot ? _self.isBot : isBot // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// @nodoc
@JsonSerializable()
class _SnChatMember implements SnChatMember {
const _SnChatMember({required this.createdAt, required this.updatedAt, required this.deletedAt, required this.id, required this.chatRoomId, required this.accountId, required this.nick, required this.role, required this.notify, required this.joinedAt, required this.isBot});
factory _SnChatMember.fromJson(Map<String, dynamic> json) => _$SnChatMemberFromJson(json);
@override final DateTime createdAt;
@override final DateTime updatedAt;
@override final DateTime? deletedAt;
@override final String id;
@override final int chatRoomId;
@override final int accountId;
@override final String? nick;
@override final int role;
@override final int notify;
@override final DateTime? joinedAt;
@override final bool isBot;
/// Create a copy of SnChatMember
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$SnChatMemberCopyWith<_SnChatMember> get copyWith => __$SnChatMemberCopyWithImpl<_SnChatMember>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$SnChatMemberToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnChatMember&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.id, id) || other.id == id)&&(identical(other.chatRoomId, chatRoomId) || other.chatRoomId == chatRoomId)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.nick, nick) || other.nick == nick)&&(identical(other.role, role) || other.role == role)&&(identical(other.notify, notify) || other.notify == notify)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt)&&(identical(other.isBot, isBot) || other.isBot == isBot));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,createdAt,updatedAt,deletedAt,id,chatRoomId,accountId,nick,role,notify,joinedAt,isBot);
@override
String toString() {
return 'SnChatMember(createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, id: $id, chatRoomId: $chatRoomId, accountId: $accountId, nick: $nick, role: $role, notify: $notify, joinedAt: $joinedAt, isBot: $isBot)';
}
}
/// @nodoc
abstract mixin class _$SnChatMemberCopyWith<$Res> implements $SnChatMemberCopyWith<$Res> {
factory _$SnChatMemberCopyWith(_SnChatMember value, $Res Function(_SnChatMember) _then) = __$SnChatMemberCopyWithImpl;
@override @useResult
$Res call({
DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, int chatRoomId, int accountId, String? nick, int role, int notify, DateTime? joinedAt, bool isBot
});
}
/// @nodoc
class __$SnChatMemberCopyWithImpl<$Res>
implements _$SnChatMemberCopyWith<$Res> {
__$SnChatMemberCopyWithImpl(this._self, this._then);
final _SnChatMember _self;
final $Res Function(_SnChatMember) _then;
/// Create a copy of SnChatMember
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? id = null,Object? chatRoomId = null,Object? accountId = null,Object? nick = freezed,Object? role = null,Object? notify = null,Object? joinedAt = freezed,Object? isBot = null,}) {
return _then(_SnChatMember(
createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,chatRoomId: null == chatRoomId ? _self.chatRoomId : chatRoomId // ignore: cast_nullable_to_non_nullable
as int,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
as int,nick: freezed == nick ? _self.nick : nick // ignore: cast_nullable_to_non_nullable
as String?,role: null == role ? _self.role : role // ignore: cast_nullable_to_non_nullable
as int,notify: null == notify ? _self.notify : notify // ignore: cast_nullable_to_non_nullable
as int,joinedAt: freezed == joinedAt ? _self.joinedAt : joinedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,isBot: null == isBot ? _self.isBot : isBot // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
// dart format on // dart format on

View File

@ -47,3 +47,140 @@ Map<String, dynamic> _$SnChatToJson(_SnChat instance) => <String, dynamic>{
'updated_at': instance.updatedAt.toIso8601String(), 'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(), 'deleted_at': instance.deletedAt?.toIso8601String(),
}; };
_SnChatMessage _$SnChatMessageFromJson(Map<String, dynamic> json) =>
_SnChatMessage(
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
deletedAt:
json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
id: json['id'] as String,
content: json['content'] as String?,
nonce: json['nonce'] as String?,
meta: json['meta'] as Map<String, dynamic>? ?? const {},
membersMetioned:
(json['members_metioned'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
editedAt:
json['edited_at'] == null
? null
: DateTime.parse(json['edited_at'] as String),
attachments:
(json['attachments'] as List<dynamic>?)
?.map((e) => SnCloudFile.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
reactions:
(json['reactions'] as List<dynamic>?)
?.map((e) => SnChatReaction.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
repliedMessageId: json['replied_message_id'] as String?,
repliedMessage:
json['replied_message'] == null
? null
: SnChatMessage.fromJson(
json['replied_message'] as Map<String, dynamic>,
),
forwardedMessageId: json['forwarded_message_id'] as String?,
forwardedMessage:
json['forwarded_message'] == null
? null
: SnChatMessage.fromJson(
json['forwarded_message'] as Map<String, dynamic>,
),
senderId: json['sender_id'] as String,
sender: SnChatMember.fromJson(json['sender'] as Map<String, dynamic>),
chatRoomId: (json['chat_room_id'] as num).toInt(),
);
Map<String, dynamic> _$SnChatMessageToJson(_SnChatMessage instance) =>
<String, dynamic>{
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(),
'id': instance.id,
'content': instance.content,
'nonce': instance.nonce,
'meta': instance.meta,
'members_metioned': instance.membersMetioned,
'edited_at': instance.editedAt?.toIso8601String(),
'attachments': instance.attachments.map((e) => e.toJson()).toList(),
'reactions': instance.reactions.map((e) => e.toJson()).toList(),
'replied_message_id': instance.repliedMessageId,
'replied_message': instance.repliedMessage?.toJson(),
'forwarded_message_id': instance.forwardedMessageId,
'forwarded_message': instance.forwardedMessage?.toJson(),
'sender_id': instance.senderId,
'sender': instance.sender.toJson(),
'chat_room_id': instance.chatRoomId,
};
_SnChatReaction _$SnChatReactionFromJson(Map<String, dynamic> json) =>
_SnChatReaction(
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
deletedAt:
json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
id: json['id'] as String,
messageId: json['message_id'] as String,
senderId: json['sender_id'] as String,
sender: SnChatMember.fromJson(json['sender'] as Map<String, dynamic>),
symbol: json['symbol'] as String,
attitude: (json['attitude'] as num).toInt(),
);
Map<String, dynamic> _$SnChatReactionToJson(_SnChatReaction instance) =>
<String, dynamic>{
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(),
'id': instance.id,
'message_id': instance.messageId,
'sender_id': instance.senderId,
'sender': instance.sender.toJson(),
'symbol': instance.symbol,
'attitude': instance.attitude,
};
_SnChatMember _$SnChatMemberFromJson(Map<String, dynamic> json) =>
_SnChatMember(
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
deletedAt:
json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
id: json['id'] as String,
chatRoomId: (json['chat_room_id'] as num).toInt(),
accountId: (json['account_id'] as num).toInt(),
nick: json['nick'] as String?,
role: (json['role'] as num).toInt(),
notify: (json['notify'] as num).toInt(),
joinedAt:
json['joined_at'] == null
? null
: DateTime.parse(json['joined_at'] as String),
isBot: json['is_bot'] as bool,
);
Map<String, dynamic> _$SnChatMemberToJson(_SnChatMember instance) =>
<String, dynamic>{
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(),
'id': instance.id,
'chat_room_id': instance.chatRoomId,
'account_id': instance.accountId,
'nick': instance.nick,
'role': instance.role,
'notify': instance.notify,
'joined_at': instance.joinedAt?.toIso8601String(),
'is_bot': instance.isBot,
};

100
lib/pods/message.dart Normal file
View File

@ -0,0 +1,100 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/database/drift_db.dart';
import 'package:island/database/message.dart';
import 'package:island/database/message_repository.dart';
import 'package:island/models/chat.dart';
import 'package:island/pods/network.dart';
import 'package:island/widgets/alert.dart';
// Global database instance
final databaseProvider = Provider<AppDatabase>((ref) {
final db = AppDatabase();
ref.onDispose(() => db.close());
return db;
});
final messageRepositoryProvider =
FutureProvider.family<MessageRepository, SnChat>((ref, chat) async {
final apiClient = ref.watch(apiClientProvider);
final database = ref.watch(databaseProvider);
return MessageRepository(chat, apiClient, database);
});
final chatMessagesProvider =
FutureProvider.family<List<LocalChatMessage>, SnChat>((ref, room) async {
final repository = await ref.watch(
messageRepositoryProvider(room).future,
);
return repository.listMessages();
});
class ChatMessageNotifier
extends StateNotifier<AsyncValue<List<LocalChatMessage>>> {
final MessageRepository _repository;
final int roomId;
int _currentOffset = 0;
final int _pageSize = 20;
bool _hasMore = true;
ChatMessageNotifier(this._repository, this.roomId)
: super(const AsyncValue.loading()) {
loadInitial();
}
Future<void> loadInitial() async {
state = const AsyncValue.loading();
try {
final messages = await _repository.listMessages(
offset: 0,
take: _pageSize,
);
_currentOffset = messages.length;
_hasMore = messages.length >= _pageSize;
state = AsyncValue.data(messages);
} catch (e, stack) {
state = AsyncValue.error(e, stack);
}
}
Future<void> loadMore() async {
if (!_hasMore) return;
try {
final newMessages = await _repository.listMessages(
offset: _currentOffset,
take: _pageSize,
);
if (newMessages.isEmpty) {
_hasMore = false;
return;
}
_currentOffset += newMessages.length;
_hasMore = newMessages.length >= _pageSize;
state = AsyncValue.data([...state.value ?? [], ...newMessages]);
} catch (err) {
showErrorAlert(err);
}
}
Future<void> sendMessage(String content) async {
try {
final message = await _repository.sendMessage(roomId, content);
final currentMessages = state.value ?? [];
state = AsyncValue.data([message, ...currentMessages]);
} catch (err) {
showErrorAlert(err);
}
}
bool get hasMore => _hasMore;
}
final chatMessageNotifierProvider = StateNotifierProvider.family<
ChatMessageNotifier,
AsyncValue<List<LocalChatMessage>>,
MessageRepository
>((ref, repository) => ChatMessageNotifier(repository, repository.room.id));

View File

@ -4,11 +4,123 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/database/message.dart';
import 'package:island/database/message_repository.dart';
import 'package:island/pods/message.dart';
import 'package:island/pods/network.dart';
import 'package:island/widgets/alert.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/cloud_files.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'chat.dart'; import 'chat.dart';
final messageRepositoryProvider = FutureProvider.family<MessageRepository, int>(
(ref, roomId) async {
final room = ref.watch(chatroomProvider(roomId)).value;
final apiClient = ref.watch(apiClientProvider);
final database = ref.watch(databaseProvider);
return MessageRepository(room!, apiClient, database);
},
);
// Provider for messages with pagination
final messagesProvider = StateNotifierProvider.family<
MessagesNotifier,
AsyncValue<List<LocalChatMessage>>,
int
>((ref, roomId) => MessagesNotifier(ref, roomId));
class MessagesNotifier
extends StateNotifier<AsyncValue<List<LocalChatMessage>>> {
final Ref _ref;
final int _roomId;
int _currentPage = 0;
static const int _pageSize = 20;
bool _hasMore = true;
MessagesNotifier(this._ref, this._roomId)
: super(const AsyncValue.loading()) {
loadInitial();
}
Future<void> loadInitial() async {
try {
final repository = await _ref.read(
messageRepositoryProvider(_roomId).future,
);
final messages = await repository.listMessages(
offset: 0,
take: _pageSize,
);
state = AsyncValue.data(messages);
_currentPage = 0;
_hasMore = messages.length == _pageSize;
} catch (e, stack) {
state = AsyncValue.error(e, stack);
}
}
Future<void> loadMore() async {
if (!_hasMore || state is AsyncLoading) return;
try {
final currentMessages = state.value ?? [];
_currentPage++;
final repository = await _ref.read(
messageRepositoryProvider(_roomId).future,
);
final newMessages = await repository.listMessages(
offset: _currentPage * _pageSize,
take: _pageSize,
);
if (newMessages.isEmpty || newMessages.length < _pageSize) {
_hasMore = false;
}
state = AsyncValue.data([...currentMessages, ...newMessages]);
} catch (err) {
showErrorAlert(err);
_currentPage--;
}
}
Future<void> sendMessage(String content) async {
try {
final repository = await _ref.read(
messageRepositoryProvider(_roomId).future,
);
final message = await repository.sendMessage(_roomId, content);
// Add the new message to the list
final currentMessages = state.value ?? [];
state = AsyncValue.data([message, ...currentMessages]);
} catch (err) {
showErrorAlert(err);
}
}
Future<void> retryMessage(String pendingMessageId) async {
try {
final repository = await _ref.read(
messageRepositoryProvider(_roomId).future,
);
final updatedMessage = await repository.retryMessage(pendingMessageId);
// Update the message in the list
final currentMessages = state.value ?? [];
final index = currentMessages.indexWhere((m) => m.id == pendingMessageId);
if (index >= 0) {
final newList = [...currentMessages];
newList[index] = updatedMessage;
state = AsyncValue.data(newList);
}
} catch (err) {
showErrorAlert(err);
}
}
}
@RoutePage() @RoutePage()
class ChatRoomScreen extends HookConsumerWidget { class ChatRoomScreen extends HookConsumerWidget {
final int id; final int id;
@ -17,8 +129,24 @@ class ChatRoomScreen extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final chatRoom = ref.watch(chatroomProvider(id)); final chatRoom = ref.watch(chatroomProvider(id));
final messages = ref.watch(messagesProvider(id));
final messagesNotifier = ref.read(messagesProvider(id).notifier);
final messageController = useTextEditingController(); final messageController = useTextEditingController();
final scrollController = useScrollController();
// Add scroll listener for pagination
useEffect(() {
void onScroll() {
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 200) {
messagesNotifier.loadMore();
}
}
scrollController.addListener(onScroll);
return () => scrollController.removeListener(onScroll);
}, [scrollController]);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
@ -59,10 +187,34 @@ class ChatRoomScreen extends HookConsumerWidget {
body: Column( body: Column(
children: [ children: [
Expanded( Expanded(
child: chatRoom.when( child: messages.when(
data: (room) => SizedBox.expand(), data:
(messageList) =>
messageList.isEmpty
? Center(child: Text('No messages yet'.tr()))
: ListView.builder(
controller: scrollController,
reverse: true, // Show newest messages at the bottom
itemCount: messageList.length,
itemBuilder: (context, index) {
final message = messageList[index];
return MessageBubble(message: message);
},
),
loading: () => const Center(child: CircularProgressIndicator()), loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => Center(child: Text('Error: $error')), error:
(error, stack) => Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Error: $error'),
ElevatedButton(
onPressed: () => messagesNotifier.loadInitial(),
child: Text('Retry'.tr()),
),
],
),
),
), ),
), ),
Material( Material(
@ -105,7 +257,14 @@ class ChatRoomScreen extends HookConsumerWidget {
IconButton( IconButton(
icon: const Icon(Icons.send), icon: const Icon(Icons.send),
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
onPressed: () {}, onPressed: () {
if (messageController.text.trim().isNotEmpty) {
messagesNotifier.sendMessage(
messageController.text.trim(),
);
messageController.clear();
}
},
), ),
], ],
).padding(bottom: MediaQuery.of(context).padding.bottom), ).padding(bottom: MediaQuery.of(context).padding.bottom),
@ -116,3 +275,99 @@ class ChatRoomScreen extends HookConsumerWidget {
); );
} }
} }
class MessageBubble extends StatelessWidget {
final LocalChatMessage message;
const MessageBubble({Key? key, required this.message}) : super(key: key);
@override
Widget build(BuildContext context) {
final isCurrentUser =
message.senderId == 'current_user_id'; // Replace with actual check
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
child: Row(
mainAxisAlignment:
isCurrentUser ? MainAxisAlignment.end : MainAxisAlignment.start,
children: [
if (!isCurrentUser)
CircleAvatar(
radius: 16,
child: Text(message.senderId[0].toUpperCase()),
),
const Gap(8),
Flexible(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color:
isCurrentUser
? Theme.of(context).colorScheme.primary.withOpacity(0.8)
: Colors.grey.shade200,
borderRadius: BorderRadius.circular(16),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
message.toRemoteMessage().content ?? '',
style: TextStyle(
color: isCurrentUser ? Colors.white : Colors.black,
),
),
const Gap(4),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
DateFormat.Hm().format(message.createdAt),
style: TextStyle(
fontSize: 10,
color:
isCurrentUser ? Colors.white70 : Colors.black54,
),
),
const Gap(4),
if (isCurrentUser)
_buildStatusIcon(context, message.status),
],
),
],
),
),
),
const Gap(8),
if (isCurrentUser)
const SizedBox(width: 32), // Balance with avatar on the other side
],
),
);
}
Widget _buildStatusIcon(BuildContext context, MessageStatus status) {
switch (status) {
case MessageStatus.pending:
return const Icon(Icons.access_time, size: 12, color: Colors.white70);
case MessageStatus.sent:
return const Icon(Icons.check, size: 12, color: Colors.white70);
case MessageStatus.failed:
return Consumer(
builder:
(context, ref, _) => GestureDetector(
onTap: () {
ref
.read(messagesProvider(message.roomId).notifier)
.retryMessage(message.id);
},
child: const Icon(
Icons.error_outline,
size: 12,
color: Colors.red,
),
),
);
}
}
}

View File

@ -13,6 +13,7 @@
#include <irondash_engine_context/irondash_engine_context_plugin.h> #include <irondash_engine_context/irondash_engine_context_plugin.h>
#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 <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
#include <super_native_extensions/super_native_extensions_plugin.h> #include <super_native_extensions/super_native_extensions_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h> #include <url_launcher_linux/url_launcher_plugin.h>
@ -38,6 +39,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) media_kit_video_registrar = g_autoptr(FlPluginRegistrar) media_kit_video_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin");
media_kit_video_plugin_register_with_registrar(media_kit_video_registrar); media_kit_video_plugin_register_with_registrar(media_kit_video_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) super_native_extensions_registrar = g_autoptr(FlPluginRegistrar) super_native_extensions_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "SuperNativeExtensionsPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "SuperNativeExtensionsPlugin");
super_native_extensions_plugin_register_with_registrar(super_native_extensions_registrar); super_native_extensions_plugin_register_with_registrar(super_native_extensions_registrar);

View File

@ -10,6 +10,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
irondash_engine_context irondash_engine_context
media_kit_libs_linux media_kit_libs_linux
media_kit_video media_kit_video
sqlite3_flutter_libs
super_native_extensions super_native_extensions
url_launcher_linux url_launcher_linux
) )

View File

@ -22,6 +22,7 @@ import path_provider_foundation
import quill_native_bridge_macos import quill_native_bridge_macos
import shared_preferences_foundation import shared_preferences_foundation
import sqflite_darwin import sqflite_darwin
import sqlite3_flutter_libs
import super_native_extensions import super_native_extensions
import url_launcher_macos import url_launcher_macos
import volume_controller import volume_controller
@ -45,6 +46,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
QuillNativeBridgePlugin.register(with: registry.registrar(forPlugin: "QuillNativeBridgePlugin")) QuillNativeBridgePlugin.register(with: registry.registrar(forPlugin: "QuillNativeBridgePlugin"))
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"))
SuperNativeExtensionsPlugin.register(with: registry.registrar(forPlugin: "SuperNativeExtensionsPlugin")) SuperNativeExtensionsPlugin.register(with: registry.registrar(forPlugin: "SuperNativeExtensionsPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
VolumeControllerPlugin.register(with: registry.registrar(forPlugin: "VolumeControllerPlugin")) VolumeControllerPlugin.register(with: registry.registrar(forPlugin: "VolumeControllerPlugin"))

View File

@ -425,6 +425,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.1.1"
drift:
dependency: "direct main"
description:
name: drift
sha256: "14a61af39d4584faf1d73b5b35e4b758a43008cf4c0fdb0576ec8e7032c0d9a5"
url: "https://pub.dev"
source: hosted
version: "2.26.0"
drift_dev:
dependency: "direct dev"
description:
name: drift_dev
sha256: "0d3f8b33b76cf1c6a82ee34d9511c40957549c4674b8f1688609e6d6c7306588"
url: "https://pub.dev"
source: hosted
version: "2.26.0"
drift_flutter:
dependency: "direct main"
description:
name: drift_flutter
sha256: "0cadbf3b8733409a6cf61d18ba2e94e149df81df7de26f48ae0695b48fd71922"
url: "https://pub.dev"
source: hosted
version: "0.2.4"
easy_localization: easy_localization:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1270,7 +1294,7 @@ packages:
source: hosted source: hosted
version: "3.2.0" version: "3.2.0"
path: path:
dependency: transitive dependency: "direct main"
description: description:
name: path name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
@ -1477,6 +1501,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.2.2" version: "3.2.2"
recase:
dependency: transitive
description:
name: recase
sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213
url: "https://pub.dev"
source: hosted
version: "4.1.0"
responsive_framework: responsive_framework:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1714,6 +1746,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: "310af39c40dd0bb2058538333c9d9840a2725ae0b9f77e4fd09ad6696aa8f66e"
url: "https://pub.dev"
source: hosted
version: "2.7.5"
sqlite3_flutter_libs:
dependency: transitive
description:
name: sqlite3_flutter_libs
sha256: "1a96b59227828d9eb1463191d684b37a27d66ee5ed7597fcf42eee6452c88a14"
url: "https://pub.dev"
source: hosted
version: "0.5.32"
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

@ -85,6 +85,9 @@ dependencies:
web_socket_channel: ^3.0.3 web_socket_channel: ^3.0.3
flutter_quill: ^11.4.0 flutter_quill: ^11.4.0
material_symbols_icons: ^4.2815.0 material_symbols_icons: ^4.2815.0
drift: ^2.26.0
drift_flutter: ^0.2.4
path: ^1.9.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@ -103,6 +106,7 @@ dev_dependencies:
riverpod_generator: ^2.6.5 riverpod_generator: ^2.6.5
custom_lint: ^0.7.5 custom_lint: ^0.7.5
riverpod_lint: ^2.6.5 riverpod_lint: ^2.6.5
drift_dev: ^2.26.0
# 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

View File

@ -15,6 +15,7 @@
#include <irondash_engine_context/irondash_engine_context_plugin_c_api.h> #include <irondash_engine_context/irondash_engine_context_plugin_c_api.h>
#include <media_kit_libs_windows_video/media_kit_libs_windows_video_plugin_c_api.h> #include <media_kit_libs_windows_video/media_kit_libs_windows_video_plugin_c_api.h>
#include <media_kit_video/media_kit_video_plugin_c_api.h> #include <media_kit_video/media_kit_video_plugin_c_api.h>
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
#include <super_native_extensions/super_native_extensions_plugin_c_api.h> #include <super_native_extensions/super_native_extensions_plugin_c_api.h>
#include <url_launcher_windows/url_launcher_windows.h> #include <url_launcher_windows/url_launcher_windows.h>
#include <volume_controller/volume_controller_plugin_c_api.h> #include <volume_controller/volume_controller_plugin_c_api.h>
@ -38,6 +39,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi")); registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi"));
MediaKitVideoPluginCApiRegisterWithRegistrar( MediaKitVideoPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("MediaKitVideoPluginCApi")); registry->GetRegistrarForPlugin("MediaKitVideoPluginCApi"));
Sqlite3FlutterLibsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin"));
SuperNativeExtensionsPluginCApiRegisterWithRegistrar( SuperNativeExtensionsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SuperNativeExtensionsPluginCApi")); registry->GetRegistrarForPlugin("SuperNativeExtensionsPluginCApi"));
UrlLauncherWindowsRegisterWithRegistrar( UrlLauncherWindowsRegisterWithRegistrar(

View File

@ -12,6 +12,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
irondash_engine_context irondash_engine_context
media_kit_libs_windows_video media_kit_libs_windows_video
media_kit_video media_kit_video
sqlite3_flutter_libs
super_native_extensions super_native_extensions
url_launcher_windows url_launcher_windows
volume_controller volume_controller