✨ Local message history db
This commit is contained in:
50
lib/providers/message/helper.dart
Normal file
50
lib/providers/message/helper.dart
Normal file
@ -0,0 +1,50 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:solian/models/channel.dart';
|
||||
import 'package:solian/models/message.dart';
|
||||
import 'package:solian/models/pagination.dart';
|
||||
import 'package:solian/providers/auth.dart';
|
||||
import 'package:solian/providers/message/history.dart';
|
||||
|
||||
Future<MessageHistoryDb> createHistoryDb() async {
|
||||
return await $FloorMessageHistoryDb
|
||||
.databaseBuilder('messaging_data.dart')
|
||||
.build();
|
||||
}
|
||||
|
||||
extension MessageHistoryHelper on MessageHistoryDb {
|
||||
receiveMessage(Message remote) async {
|
||||
await localMessages.insert(LocalMessage(
|
||||
remote.id,
|
||||
remote,
|
||||
remote.channelId,
|
||||
));
|
||||
}
|
||||
|
||||
syncMessages(Channel channel, {String? scope}) async {
|
||||
final AuthProvider auth = Get.find();
|
||||
if (!await auth.isAuthorized) return;
|
||||
|
||||
final client = auth.configureClient('messaging');
|
||||
|
||||
final resp = await client
|
||||
.get('/api/channels/$scope/${channel.alias}/messages?take=10&offset=0');
|
||||
|
||||
if (resp.statusCode != 200) {
|
||||
throw Exception(resp.bodyString);
|
||||
}
|
||||
|
||||
// TODO Continue sync until the last message / the message exists / sync limitation
|
||||
|
||||
final PaginationResult result = PaginationResult.fromJson(resp.body);
|
||||
final parsed = result.data?.map((e) => Message.fromJson(e)).toList();
|
||||
if (parsed != null) {
|
||||
await localMessages.insertBulk(
|
||||
parsed.map((x) => LocalMessage(x.id, x, x.channelId)).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<LocalMessage>> listMessages(Channel channel) async {
|
||||
return await localMessages.findAllByChannel(channel.id);
|
||||
}
|
||||
}
|
51
lib/providers/message/history.dart
Normal file
51
lib/providers/message/history.dart
Normal file
@ -0,0 +1,51 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:floor/floor.dart';
|
||||
import 'package:solian/models/message.dart';
|
||||
import 'package:sqflite/sqflite.dart' as sqflite;
|
||||
|
||||
part 'history.g.dart';
|
||||
|
||||
@entity
|
||||
class LocalMessage {
|
||||
@primaryKey
|
||||
final int id;
|
||||
|
||||
final Message data;
|
||||
final int channelId;
|
||||
|
||||
LocalMessage(this.id, this.data, this.channelId);
|
||||
}
|
||||
|
||||
class RemoteMessageConverter extends TypeConverter<Message, String> {
|
||||
@override
|
||||
Message decode(String databaseValue) {
|
||||
return Message.fromJson(jsonDecode(databaseValue));
|
||||
}
|
||||
|
||||
@override
|
||||
String encode(Message value) {
|
||||
return jsonEncode(value.toJson());
|
||||
}
|
||||
}
|
||||
|
||||
@dao
|
||||
abstract class LocalMessageDao {
|
||||
@Query('SELECT * FROM LocalMessage WHERE channelId = :channelId')
|
||||
Future<List<LocalMessage>> findAllByChannel(int channelId);
|
||||
|
||||
@Insert(onConflict: OnConflictStrategy.replace)
|
||||
Future<void> insert(LocalMessage m);
|
||||
|
||||
@Insert(onConflict: OnConflictStrategy.replace)
|
||||
Future<void> insertBulk(List<LocalMessage> m);
|
||||
|
||||
@Query('DELETE * FROM LocalMessage')
|
||||
Future<void> wipeLocalMessages();
|
||||
}
|
||||
|
||||
@TypeConverters([RemoteMessageConverter])
|
||||
@Database(version: 1, entities: [LocalMessage])
|
||||
abstract class MessageHistoryDb extends FloorDatabase {
|
||||
LocalMessageDao get localMessages;
|
||||
}
|
165
lib/providers/message/history.g.dart
Normal file
165
lib/providers/message/history.g.dart
Normal file
@ -0,0 +1,165 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'history.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FloorGenerator
|
||||
// **************************************************************************
|
||||
|
||||
abstract class $MessageHistoryDbBuilderContract {
|
||||
/// Adds migrations to the builder.
|
||||
$MessageHistoryDbBuilderContract addMigrations(List<Migration> migrations);
|
||||
|
||||
/// Adds a database [Callback] to the builder.
|
||||
$MessageHistoryDbBuilderContract addCallback(Callback callback);
|
||||
|
||||
/// Creates the database and initializes it.
|
||||
Future<MessageHistoryDb> build();
|
||||
}
|
||||
|
||||
// ignore: avoid_classes_with_only_static_members
|
||||
class $FloorMessageHistoryDb {
|
||||
/// Creates a database builder for a persistent database.
|
||||
/// Once a database is built, you should keep a reference to it and re-use it.
|
||||
static $MessageHistoryDbBuilderContract databaseBuilder(String name) =>
|
||||
_$MessageHistoryDbBuilder(name);
|
||||
|
||||
/// Creates a database builder for an in memory database.
|
||||
/// Information stored in an in memory database disappears when the process is killed.
|
||||
/// Once a database is built, you should keep a reference to it and re-use it.
|
||||
static $MessageHistoryDbBuilderContract inMemoryDatabaseBuilder() =>
|
||||
_$MessageHistoryDbBuilder(null);
|
||||
}
|
||||
|
||||
class _$MessageHistoryDbBuilder implements $MessageHistoryDbBuilderContract {
|
||||
_$MessageHistoryDbBuilder(this.name);
|
||||
|
||||
final String? name;
|
||||
|
||||
final List<Migration> _migrations = [];
|
||||
|
||||
Callback? _callback;
|
||||
|
||||
@override
|
||||
$MessageHistoryDbBuilderContract addMigrations(List<Migration> migrations) {
|
||||
_migrations.addAll(migrations);
|
||||
return this;
|
||||
}
|
||||
|
||||
@override
|
||||
$MessageHistoryDbBuilderContract addCallback(Callback callback) {
|
||||
_callback = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<MessageHistoryDb> build() async {
|
||||
final path = name != null
|
||||
? await sqfliteDatabaseFactory.getDatabasePath(name!)
|
||||
: ':memory:';
|
||||
final database = _$MessageHistoryDb();
|
||||
database.database = await database.open(
|
||||
path,
|
||||
_migrations,
|
||||
_callback,
|
||||
);
|
||||
return database;
|
||||
}
|
||||
}
|
||||
|
||||
class _$MessageHistoryDb extends MessageHistoryDb {
|
||||
_$MessageHistoryDb([StreamController<String>? listener]) {
|
||||
changeListener = listener ?? StreamController<String>.broadcast();
|
||||
}
|
||||
|
||||
LocalMessageDao? _localMessagesInstance;
|
||||
|
||||
Future<sqflite.Database> open(
|
||||
String path,
|
||||
List<Migration> migrations, [
|
||||
Callback? callback,
|
||||
]) async {
|
||||
final databaseOptions = sqflite.OpenDatabaseOptions(
|
||||
version: 1,
|
||||
onConfigure: (database) async {
|
||||
await database.execute('PRAGMA foreign_keys = ON');
|
||||
await callback?.onConfigure?.call(database);
|
||||
},
|
||||
onOpen: (database) async {
|
||||
await callback?.onOpen?.call(database);
|
||||
},
|
||||
onUpgrade: (database, startVersion, endVersion) async {
|
||||
await MigrationAdapter.runMigrations(
|
||||
database, startVersion, endVersion, migrations);
|
||||
|
||||
await callback?.onUpgrade?.call(database, startVersion, endVersion);
|
||||
},
|
||||
onCreate: (database, version) async {
|
||||
await database.execute(
|
||||
'CREATE TABLE IF NOT EXISTS `LocalMessage` (`id` INTEGER NOT NULL, `data` TEXT NOT NULL, `channelId` INTEGER NOT NULL, PRIMARY KEY (`id`))');
|
||||
|
||||
await callback?.onCreate?.call(database, version);
|
||||
},
|
||||
);
|
||||
return sqfliteDatabaseFactory.openDatabase(path, options: databaseOptions);
|
||||
}
|
||||
|
||||
@override
|
||||
LocalMessageDao get localMessages {
|
||||
return _localMessagesInstance ??=
|
||||
_$LocalMessageDao(database, changeListener);
|
||||
}
|
||||
}
|
||||
|
||||
class _$LocalMessageDao extends LocalMessageDao {
|
||||
_$LocalMessageDao(
|
||||
this.database,
|
||||
this.changeListener,
|
||||
) : _queryAdapter = QueryAdapter(database),
|
||||
_localMessageInsertionAdapter = InsertionAdapter(
|
||||
database,
|
||||
'LocalMessage',
|
||||
(LocalMessage item) => <String, Object?>{
|
||||
'id': item.id,
|
||||
'data': _remoteMessageConverter.encode(item.data),
|
||||
'channelId': item.channelId
|
||||
});
|
||||
|
||||
final sqflite.DatabaseExecutor database;
|
||||
|
||||
final StreamController<String> changeListener;
|
||||
|
||||
final QueryAdapter _queryAdapter;
|
||||
|
||||
final InsertionAdapter<LocalMessage> _localMessageInsertionAdapter;
|
||||
|
||||
@override
|
||||
Future<List<LocalMessage>> findAllByChannel(int channelId) async {
|
||||
return _queryAdapter.queryList(
|
||||
'SELECT * FROM LocalMessage WHERE channelId = ?1',
|
||||
mapper: (Map<String, Object?> row) => LocalMessage(
|
||||
row['id'] as int,
|
||||
_remoteMessageConverter.decode(row['data'] as String),
|
||||
row['channelId'] as int),
|
||||
arguments: [channelId]);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> wipeLocalMessages() async {
|
||||
await _queryAdapter.queryNoReturn('DELETE * FROM LocalMessage');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> insert(LocalMessage m) async {
|
||||
await _localMessageInsertionAdapter.insert(m, OnConflictStrategy.replace);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> insertBulk(List<LocalMessage> m) async {
|
||||
await _localMessageInsertionAdapter.insertList(
|
||||
m, OnConflictStrategy.replace);
|
||||
}
|
||||
}
|
||||
|
||||
// ignore_for_file: unused_element
|
||||
final _remoteMessageConverter = RemoteMessageConverter();
|
Reference in New Issue
Block a user