style: Apply team linter in stream chat persistence (#331)

* lint(persistence): Apply team linter

Signed-off-by: Sahil Kumar <[email protected]>

* style: flutter format

Signed-off-by: Sahil Kumar <[email protected]>
This commit is contained in:
Sahil Kumar
2021-03-15 10:23:44 +01:00
committed by GitHub
parent a8443a98b0
commit 8ec54f3834
30 changed files with 813 additions and 772 deletions
@@ -0,0 +1,147 @@
analyzer:
exclude:
- lib/**/*.g.dart
- lib/**/*.freezed.dart
- example/*
- test/*
linter:
rules:
- always_use_package_imports
- avoid_empty_else
- avoid_relative_lib_imports
- avoid_slow_async_io
- avoid_types_as_parameter_names
- cancel_subscriptions
- close_sinks
- control_flow_in_finally
- diagnostic_describe_all_properties
- empty_statements
- hash_and_equals
- invariant_booleans
- iterable_contains_unrelated_type
- list_remove_unrelated_type
- literal_only_boolean_expressions
- no_adjacent_strings_in_list
- no_duplicate_case_values
- no_logic_in_create_state
- prefer_void_to_null
- test_types_in_equals
- throw_in_finally
- unnecessary_statements
- unrelated_type_equality_checks
- omit_local_variable_types
- use_key_in_widget_constructors
- valid_regexps
- always_declare_return_types
- always_put_required_named_parameters_first
- always_require_non_null_named_parameters
- annotate_overrides
- avoid_bool_literals_in_conditional_expressions
- avoid_catching_errors
- avoid_init_to_null
- avoid_null_checks_in_equality_operators
- avoid_positional_boolean_parameters
- avoid_private_typedef_functions
- avoid_redundant_argument_values
- avoid_return_types_on_setters
- avoid_returning_null
- avoid_returning_null_for_void
- avoid_shadowing_type_parameters
- avoid_single_cascade_in_expression_statements
- avoid_unnecessary_containers
- avoid_unused_constructor_parameters
- await_only_futures
- camel_case_extensions
- camel_case_types
- cascade_invocations
- constant_identifier_names
- curly_braces_in_flow_control_structures
- directives_ordering
- empty_catches
- empty_constructor_bodies
- exhaustive_cases
- file_names
- implementation_imports
- join_return_with_assignment
- leading_newlines_in_multiline_strings
- library_names
- library_prefixes
- lines_longer_than_80_chars
- missing_whitespace_between_adjacent_strings
- non_constant_identifier_names
- null_closures
- one_member_abstracts
- only_throw_errors
- package_api_docs
- package_prefixed_library_names
- parameter_assignments
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
- prefer_asserts_with_message
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
- prefer_constructors_over_static_methods
- prefer_contains
- prefer_equal_for_default_values
- prefer_expression_function_bodies
- prefer_final_fields
- prefer_final_in_for_each
- prefer_final_locals
- prefer_function_declarations_over_variables
- prefer_generic_function_type_aliases
- prefer_if_elements_to_conditional_expressions
- prefer_if_null_operators
- prefer_initializing_formals
- prefer_inlined_adds
- prefer_int_literals
- prefer_interpolation_to_compose_strings
- prefer_is_empty
- prefer_is_not_empty
- prefer_is_not_operator
- prefer_null_aware_operators
- prefer_single_quotes
- prefer_spread_collections
- prefer_typing_uninitialized_variables
- provide_deprecation_message
- public_member_api_docs
- recursive_getters
- sized_box_for_whitespace
- slash_for_doc_comments
- sort_child_properties_last
- sort_constructors_first
- sort_unnamed_constructors_first
- type_annotate_public_apis
- type_init_formals
- unnecessary_await_in_return
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_getters_setters
- unnecessary_lambdas
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_in_if_null_operators
- unnecessary_nullable_for_final_variable_declarations
- unnecessary_parenthesis
- unnecessary_raw_strings
- unnecessary_string_escapes
- unnecessary_string_interpolations
- unnecessary_this
- use_is_even_rather_than_modulo
- use_late_for_private_fields_and_variables
- use_rethrow_when_possible
- use_setters_to_change_properties
- use_to_and_as_if_applicable
- package_names
- sort_pub_dependencies
# To be added when null-safe:
# - cast_nullable_to_non_nullable
#- unnecessary_null_checks
# - tighten_type_of_initializing_formals
# - null_check_on_nullable_type_parameter
@@ -6,7 +6,7 @@ import 'package:moor/moor.dart';
/// by the sqlite backend. /// by the sqlite backend.
class ListConverter<T> extends TypeConverter<List<T>, String> { class ListConverter<T> extends TypeConverter<List<T>, String> {
@override @override
List<T> mapToDart(fromDb) { List<T> mapToDart(String fromDb) {
if (fromDb == null) { if (fromDb == null) {
return null; return null;
} }
@@ -14,7 +14,7 @@ class ListConverter<T> extends TypeConverter<List<T>, String> {
} }
@override @override
String mapToSql(value) { String mapToSql(List<T> value) {
if (value == null) { if (value == null) {
return null; return null;
} }
@@ -6,7 +6,7 @@ import 'package:moor/moor.dart';
/// by the sqlite backend. /// by the sqlite backend.
class MapConverter<T> extends TypeConverter<Map<String, T>, String> { class MapConverter<T> extends TypeConverter<Map<String, T>, String> {
@override @override
Map<String, T> mapToDart(fromDb) { Map<String, T> mapToDart(String fromDb) {
if (fromDb == null) { if (fromDb == null) {
return null; return null;
} }
@@ -14,7 +14,7 @@ class MapConverter<T> extends TypeConverter<Map<String, T>, String> {
} }
@override @override
String mapToSql(value) { String mapToSql(Map<String, T> value) {
if (value == null) { if (value == null) {
return null; return null;
} }
@@ -3,7 +3,7 @@ import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'package:stream_chat_persistence/src/entity/channels.dart'; import 'package:stream_chat_persistence/src/entity/channels.dart';
import 'package:stream_chat_persistence/src/entity/users.dart'; import 'package:stream_chat_persistence/src/entity/users.dart';
import '../mapper/mapper.dart'; import 'package:stream_chat_persistence/src/mapper/mapper.dart';
part 'channel_dao.g.dart'; part 'channel_dao.g.dart';
@@ -15,15 +15,14 @@ class ChannelDao extends DatabaseAccessor<MoorChatDatabase>
ChannelDao(MoorChatDatabase db) : super(db); ChannelDao(MoorChatDatabase db) : super(db);
/// Get channel by cid /// Get channel by cid
Future<ChannelModel> getChannelByCid(String cid) async { Future<ChannelModel> getChannelByCid(String cid) async =>
return (select(channels)..where((c) => c.cid.equals(cid))).join([ (select(channels)..where((c) => c.cid.equals(cid))).join([
leftOuterJoin(users, channels.createdById.equalsExp(users.id)), leftOuterJoin(users, channels.createdById.equalsExp(users.id)),
]).map((rows) { ]).map((rows) {
final channel = rows.readTable(channels); final channel = rows.readTable(channels);
final createdBy = rows.readTable(users); final createdBy = rows.readTable(users);
return channel.toChannelModel(createdBy: createdBy?.toUser()); return channel.toChannelModel(createdBy: createdBy?.toUser());
}).getSingle(); }).getSingle();
}
/// Delete all channels by matching cid in [cids] /// Delete all channels by matching cid in [cids]
/// ///
@@ -31,27 +30,22 @@ class ChannelDao extends DatabaseAccessor<MoorChatDatabase>
/// 1. Channel Reads /// 1. Channel Reads
/// 2. Channel Members /// 2. Channel Members
/// 3. Channel Messages -> Messages Reactions /// 3. Channel Messages -> Messages Reactions
Future<void> deleteChannelByCids(List<String> cids) async { Future<void> deleteChannelByCids(List<String> cids) async =>
return (delete(channels)..where((tbl) => tbl.cid.isIn(cids))).go(); (delete(channels)..where((tbl) => tbl.cid.isIn(cids))).go();
}
/// Get the channel cids saved in the storage /// Get the channel cids saved in the storage
Future<List<String>> get cids { Future<List<String>> get cids => (select(channels)
return (select(channels) ..orderBy([(c) => OrderingTerm.desc(c.lastMessageAt)])
..orderBy([(c) => OrderingTerm.desc(c.lastMessageAt)]) ..limit(250))
..limit(250)) .map((c) => c.cid)
.map((c) => c.cid) .get();
.get();
}
/// Updates all the channels using the new [channelList] data /// Updates all the channels using the new [channelList] data
Future<void> updateChannels(List<ChannelModel> channelList) { Future<void> updateChannels(List<ChannelModel> channelList) => batch(
return batch( (it) => it.insertAll(
(it) => it.insertAll( channels,
channels, channelList.map((c) => c.toEntity()).toList(),
channelList.map((c) => c.toEntity()).toList(), mode: InsertMode.insertOrReplace,
mode: InsertMode.insertOrReplace, ),
), );
);
}
} }
@@ -7,7 +7,7 @@ import 'package:stream_chat_persistence/src/entity/channel_queries.dart';
import 'package:stream_chat_persistence/src/entity/channels.dart'; import 'package:stream_chat_persistence/src/entity/channels.dart';
import 'package:stream_chat_persistence/src/entity/users.dart'; import 'package:stream_chat_persistence/src/entity/users.dart';
import '../mapper/mapper.dart'; import 'package:stream_chat_persistence/src/mapper/mapper.dart';
part 'channel_query_dao.g.dart'; part 'channel_query_dao.g.dart';
@@ -31,31 +31,31 @@ class ChannelQueryDao extends DatabaseAccessor<MoorChatDatabase>
/// the list of matching rows will be deleted /// the list of matching rows will be deleted
Future<void> updateChannelQueries( Future<void> updateChannelQueries(
Map<String, dynamic> filter, Map<String, dynamic> filter,
List<String> cids, List<String> cids, {
bool clearQueryCache, bool clearQueryCache,
) async { }) async =>
return transaction(() async { transaction(() async {
final hash = _computeHash(filter); final hash = _computeHash(filter);
if (clearQueryCache) { if (clearQueryCache) {
await batch((it) {
it.deleteWhere<ChannelQueries, ChannelQueryEntity>(
channelQueries,
(c) => c.queryHash.equals(hash),
);
});
}
await batch((it) { await batch((it) {
it.deleteWhere<ChannelQueries, ChannelQueryEntity>( it.insertAll(
channelQueries, channelQueries,
(c) => c.queryHash.equals(hash), cids
.map((cid) =>
ChannelQueryEntity(queryHash: hash, channelCid: cid))
.toList(),
mode: InsertMode.insertOrReplace,
); );
}); });
}
await batch((it) {
it.insertAll(
channelQueries,
cids.map((cid) {
return ChannelQueryEntity(queryHash: hash, channelCid: cid);
}).toList(),
mode: InsertMode.insertOrReplace,
);
}); });
});
}
/// Get list of channels by filter, sort and paginationParams /// Get list of channels by filter, sort and paginationParams
Future<List<ChannelModel>> getChannels({ Future<List<ChannelModel>> getChannels({
@@ -70,7 +70,7 @@ class ChannelQueryDao extends DatabaseAccessor<MoorChatDatabase>
); );
} }
return true; return true;
}()); }(), '');
final hash = _computeHash(filter); final hash = _computeHash(filter);
final cachedChannelCids = await (select(channelQueries) final cachedChannelCids = await (select(channelQueries)
@@ -89,10 +89,11 @@ class ChannelQueryDao extends DatabaseAccessor<MoorChatDatabase>
})).get(); })).get();
final possibleSortingFields = cachedChannels.fold<List<String>>( final possibleSortingFields = cachedChannels.fold<List<String>>(
ChannelModel.topLevelFields, (previousValue, element) { ChannelModel.topLevelFields,
return {...previousValue, ...element.extraData.keys}.toList(); (previousValue, element) =>
}); {...previousValue, ...element.extraData.keys}.toList());
// ignore: parameter_assignments
sort = sort sort = sort
?.where((s) => possibleSortingFields.contains(s.field)) ?.where((s) => possibleSortingFields.contains(s.field))
?.toList(growable: false); ?.toList(growable: false);
@@ -4,7 +4,7 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'package:stream_chat_persistence/src/entity/connection_events.dart'; import 'package:stream_chat_persistence/src/entity/connection_events.dart';
import 'package:stream_chat_persistence/src/entity/users.dart'; import 'package:stream_chat_persistence/src/entity/users.dart';
import '../mapper/mapper.dart'; import 'package:stream_chat_persistence/src/mapper/mapper.dart';
part 'connection_event_dao.g.dart'; part 'connection_event_dao.g.dart';
@@ -16,43 +16,38 @@ class ConnectionEventDao extends DatabaseAccessor<MoorChatDatabase>
ConnectionEventDao(MoorChatDatabase db) : super(db); ConnectionEventDao(MoorChatDatabase db) : super(db);
/// Get the latest stored connection event /// Get the latest stored connection event
Future<Event> get connectionEvent { Future<Event> get connectionEvent => select(connectionEvents)
return select(connectionEvents).map((eventEntity) { .map((eventEntity) => eventEntity.toEvent())
return eventEntity.toEvent(); .getSingle();
}).getSingle();
}
/// Get the latest stored lastSyncAt /// Get the latest stored lastSyncAt
Future<DateTime> get lastSyncAt { Future<DateTime> get lastSyncAt =>
return select(connectionEvents).getSingle().then((r) => r?.lastSyncAt); select(connectionEvents).getSingle().then((r) => r?.lastSyncAt);
}
/// Update stored connection event with latest data /// Update stored connection event with latest data
Future<void> updateConnectionEvent(Event event) async { Future<void> updateConnectionEvent(Event event) async =>
return transaction(() async { transaction(() async {
final connectionInfo = await select(connectionEvents).getSingle(); final connectionInfo = await select(connectionEvents).getSingle();
await into(connectionEvents).insert( await into(connectionEvents).insert(
ConnectionEventEntity( ConnectionEventEntity(
id: 1, id: 1,
lastSyncAt: connectionInfo?.lastSyncAt, lastSyncAt: connectionInfo?.lastSyncAt,
lastEventAt: event.createdAt ?? connectionInfo?.lastEventAt, lastEventAt: event.createdAt ?? connectionInfo?.lastEventAt,
totalUnreadCount: totalUnreadCount:
event.totalUnreadCount ?? connectionInfo?.totalUnreadCount, event.totalUnreadCount ?? connectionInfo?.totalUnreadCount,
ownUser: event.me?.toJson() ?? connectionInfo?.ownUser, ownUser: event.me?.toJson() ?? connectionInfo?.ownUser,
unreadChannels: unreadChannels:
event.unreadChannels ?? connectionInfo?.unreadChannels, event.unreadChannels ?? connectionInfo?.unreadChannels,
), ),
mode: InsertMode.insertOrReplace, mode: InsertMode.insertOrReplace,
); );
}); });
}
/// Update stored lastSyncAt with latest data /// Update stored lastSyncAt with latest data
Future<int> updateLastSyncAt(DateTime lastSyncAt) async { Future<int> updateLastSyncAt(DateTime lastSyncAt) async =>
return (update(connectionEvents)..where((tbl) => tbl.id.equals(1))).write( (update(connectionEvents)..where((tbl) => tbl.id.equals(1))).write(
ConnectionEventsCompanion( ConnectionEventsCompanion(
lastSyncAt: Value(lastSyncAt), lastSyncAt: Value(lastSyncAt),
), ),
); );
}
} }
@@ -1,9 +1,9 @@
export 'user_dao.dart';
export 'channel_dao.dart'; export 'channel_dao.dart';
export 'channel_query_dao.dart';
export 'connection_event_dao.dart';
export 'member_dao.dart';
export 'message_dao.dart'; export 'message_dao.dart';
export 'pinned_message_dao.dart'; export 'pinned_message_dao.dart';
export 'member_dao.dart';
export 'connection_event_dao.dart';
export 'reaction_dao.dart'; export 'reaction_dao.dart';
export 'read_dao.dart'; export 'read_dao.dart';
export 'channel_query_dao.dart'; export 'user_dao.dart';
@@ -5,7 +5,7 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'package:stream_chat_persistence/src/entity/members.dart'; import 'package:stream_chat_persistence/src/entity/members.dart';
import 'package:stream_chat_persistence/src/entity/users.dart'; import 'package:stream_chat_persistence/src/entity/users.dart';
import '../mapper/mapper.dart'; import 'package:stream_chat_persistence/src/mapper/mapper.dart';
part 'member_dao.g.dart'; part 'member_dao.g.dart';
@@ -17,37 +17,33 @@ class MemberDao extends DatabaseAccessor<MoorChatDatabase>
MemberDao(MoorChatDatabase db) : super(db); MemberDao(MoorChatDatabase db) : super(db);
/// Get all members where [Members.channelCid] matches [cid] /// Get all members where [Members.channelCid] matches [cid]
Future<List<Member>> getMembersByCid(String cid) async { Future<List<Member>> getMembersByCid(String cid) async =>
return (select(members).join([ (select(members).join([
leftOuterJoin(users, members.userId.equalsExp(users.id)), leftOuterJoin(users, members.userId.equalsExp(users.id)),
]) ])
..where(members.channelCid.equals(cid)) ..where(members.channelCid.equals(cid))
..orderBy([OrderingTerm.asc(members.createdAt)])) ..orderBy([OrderingTerm.asc(members.createdAt)]))
.map((row) { .map((row) {
final userEntity = row.readTable(users); final userEntity = row.readTable(users);
final memberEntity = row.readTable(members); final memberEntity = row.readTable(members);
return memberEntity.toMember(user: userEntity?.toUser()); return memberEntity.toMember(user: userEntity?.toUser());
}).get(); }).get();
}
/// Updates all the members using the new [memberList] data /// Updates all the members using the new [memberList] data
Future<void> updateMembers(String cid, List<Member> memberList) async { Future<void> updateMembers(String cid, List<Member> memberList) async =>
return batch( batch(
(it) => it.insertAll( (it) => it.insertAll(
members, members,
memberList.map((m) => m.toEntity(cid: cid)).toList(), memberList.map((m) => m.toEntity(cid: cid)).toList(),
mode: InsertMode.insertOrReplace, mode: InsertMode.insertOrReplace,
), ),
); );
}
/// Deletes all the members whose [Members.channelCid] is present in [cids] /// Deletes all the members whose [Members.channelCid] is present in [cids]
Future<void> deleteMemberByCids(List<String> cids) async { Future<void> deleteMemberByCids(List<String> cids) async => batch((it) {
return batch((it) { it.deleteWhere<Members, MemberEntity>(
it.deleteWhere<Members, MemberEntity>( members,
members, (m) => m.channelCid.isIn(cids),
(m) => m.channelCid.isIn(cids), );
); });
});
}
} }
@@ -4,7 +4,7 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'package:stream_chat_persistence/src/entity/messages.dart'; import 'package:stream_chat_persistence/src/entity/messages.dart';
import 'package:stream_chat_persistence/src/entity/users.dart'; import 'package:stream_chat_persistence/src/entity/users.dart';
import '../mapper/mapper.dart'; import 'package:stream_chat_persistence/src/mapper/mapper.dart';
part 'message_dao.g.dart'; part 'message_dao.g.dart';
@@ -25,17 +25,15 @@ class MessageDao extends DatabaseAccessor<MoorChatDatabase>
/// ///
/// This will automatically delete the following linked records /// This will automatically delete the following linked records
/// 1. Message Reactions /// 1. Message Reactions
Future<void> deleteMessageByIds(List<String> messageIds) { Future<void> deleteMessageByIds(List<String> messageIds) =>
return (delete(messages)..where((tbl) => tbl.id.isIn(messageIds))).go(); (delete(messages)..where((tbl) => tbl.id.isIn(messageIds))).go();
}
/// Removes all the messages by matching [Messages.channelCid] in [cids] /// Removes all the messages by matching [Messages.channelCid] in [cids]
/// ///
/// This will automatically delete the following linked records /// This will automatically delete the following linked records
/// 1. Message Reactions /// 1. Message Reactions
Future<void> deleteMessageByCids(List<String> cids) async { Future<void> deleteMessageByCids(List<String> cids) async =>
return (delete(messages)..where((tbl) => tbl.channelCid.isIn(cids))).go(); (delete(messages)..where((tbl) => tbl.channelCid.isIn(cids))).go();
}
Future<Message> _messageFromJoinRow(TypedResult rows) async { Future<Message> _messageFromJoinRow(TypedResult rows) async {
final userEntity = rows.readTable(_users); final userEntity = rows.readTable(_users);
@@ -60,31 +58,29 @@ class MessageDao extends DatabaseAccessor<MoorChatDatabase>
} }
/// Returns a single message by matching the [Messages.id] with [id] /// Returns a single message by matching the [Messages.id] with [id]
Future<Message> getMessageById(String id) async { Future<Message> getMessageById(String id) async =>
return await (select(messages).join([ await (select(messages).join([
leftOuterJoin(_users, messages.userId.equalsExp(_users.id)), leftOuterJoin(_users, messages.userId.equalsExp(_users.id)),
leftOuterJoin( leftOuterJoin(_pinnedByUsers,
_pinnedByUsers, messages.pinnedByUserId.equalsExp(_pinnedByUsers.id)), messages.pinnedByUserId.equalsExp(_pinnedByUsers.id)),
]) ])
..where(messages.id.equals(id))) ..where(messages.id.equals(id)))
.map(_messageFromJoinRow) .map(_messageFromJoinRow)
.getSingle(); .getSingle();
}
/// Returns all the messages of a particular thread by matching /// Returns all the messages of a particular thread by matching
/// [Messages.channelCid] with [cid] /// [Messages.channelCid] with [cid]
Future<List<Message>> getThreadMessages(String cid) async { Future<List<Message>> getThreadMessages(String cid) async =>
return Future.wait(await (select(messages).join([ Future.wait(await (select(messages).join([
leftOuterJoin(users, messages.userId.equalsExp(_users.id)), leftOuterJoin(users, messages.userId.equalsExp(_users.id)),
leftOuterJoin( leftOuterJoin(_pinnedByUsers,
_pinnedByUsers, messages.pinnedByUserId.equalsExp(_pinnedByUsers.id)), messages.pinnedByUserId.equalsExp(_pinnedByUsers.id)),
]) ])
..where(messages.channelCid.equals(cid)) ..where(messages.channelCid.equals(cid))
..where(isNotNull(messages.parentId)) ..where(isNotNull(messages.parentId))
..orderBy([OrderingTerm.asc(messages.createdAt)])) ..orderBy([OrderingTerm.asc(messages.createdAt)]))
.map(_messageFromJoinRow) .map(_messageFromJoinRow)
.get()); .get());
}
/// Returns all the messages of a particular thread by matching /// Returns all the messages of a particular thread by matching
/// [Messages.parentId] with [parentId] /// [Messages.parentId] with [parentId]
@@ -4,7 +4,7 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'package:stream_chat_persistence/src/entity/pinned_messages.dart'; import 'package:stream_chat_persistence/src/entity/pinned_messages.dart';
import 'package:stream_chat_persistence/src/entity/users.dart'; import 'package:stream_chat_persistence/src/entity/users.dart';
import '../mapper/mapper.dart'; import 'package:stream_chat_persistence/src/mapper/mapper.dart';
part 'pinned_message_dao.g.dart'; part 'pinned_message_dao.g.dart';
@@ -25,19 +25,15 @@ class PinnedMessageDao extends DatabaseAccessor<MoorChatDatabase>
/// ///
/// This will automatically delete the following linked records /// This will automatically delete the following linked records
/// 1. Message Reactions /// 1. Message Reactions
Future<void> deleteMessageByIds(List<String> messageIds) { Future<void> deleteMessageByIds(List<String> messageIds) =>
return (delete(pinnedMessages)..where((tbl) => tbl.id.isIn(messageIds))) (delete(pinnedMessages)..where((tbl) => tbl.id.isIn(messageIds))).go();
.go();
}
/// Removes all the messages by matching [PinnedMessages.channelCid] in [cids] /// Removes all the messages by matching [PinnedMessages.channelCid] in [cids]
/// ///
/// This will automatically delete the following linked records /// This will automatically delete the following linked records
/// 1. Message Reactions /// 1. Message Reactions
Future<void> deleteMessageByCids(List<String> cids) async { Future<void> deleteMessageByCids(List<String> cids) async =>
return (delete(pinnedMessages)..where((tbl) => tbl.channelCid.isIn(cids))) (delete(pinnedMessages)..where((tbl) => tbl.channelCid.isIn(cids))).go();
.go();
}
Future<Message> _messageFromJoinRow(TypedResult rows) async { Future<Message> _messageFromJoinRow(TypedResult rows) async {
final userEntity = rows.readTable(users); final userEntity = rows.readTable(users);
@@ -62,31 +58,29 @@ class PinnedMessageDao extends DatabaseAccessor<MoorChatDatabase>
} }
/// Returns a single message by matching the [PinnedMessages.id] with [id] /// Returns a single message by matching the [PinnedMessages.id] with [id]
Future<Message> getMessageById(String id) async { Future<Message> getMessageById(String id) async =>
return await (select(pinnedMessages).join([ await (select(pinnedMessages).join([
leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)), leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)),
leftOuterJoin(_pinnedByUsers, leftOuterJoin(_pinnedByUsers,
pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id)), pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id)),
]) ])
..where(pinnedMessages.id.equals(id))) ..where(pinnedMessages.id.equals(id)))
.map(_messageFromJoinRow) .map(_messageFromJoinRow)
.getSingle(); .getSingle();
}
/// Returns all the messages of a particular thread by matching /// Returns all the messages of a particular thread by matching
/// [PinnedMessages.channelCid] with [cid] /// [PinnedMessages.channelCid] with [cid]
Future<List<Message>> getThreadMessages(String cid) async { Future<List<Message>> getThreadMessages(String cid) async =>
return Future.wait(await (select(pinnedMessages).join([ Future.wait(await (select(pinnedMessages).join([
leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)), leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)),
leftOuterJoin(_pinnedByUsers, leftOuterJoin(_pinnedByUsers,
pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id)), pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id)),
]) ])
..where(pinnedMessages.channelCid.equals(cid)) ..where(pinnedMessages.channelCid.equals(cid))
..where(isNotNull(pinnedMessages.parentId)) ..where(isNotNull(pinnedMessages.parentId))
..orderBy([OrderingTerm.asc(pinnedMessages.createdAt)])) ..orderBy([OrderingTerm.asc(pinnedMessages.createdAt)]))
.map(_messageFromJoinRow) .map(_messageFromJoinRow)
.get()); .get());
}
/// Returns all the messages of a particular thread by matching /// Returns all the messages of a particular thread by matching
/// [PinnedMessages.parentId] with [parentId] /// [PinnedMessages.parentId] with [parentId]
@@ -3,7 +3,7 @@ import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'package:stream_chat_persistence/src/entity/reactions.dart'; import 'package:stream_chat_persistence/src/entity/reactions.dart';
import 'package:stream_chat_persistence/src/entity/users.dart'; import 'package:stream_chat_persistence/src/entity/users.dart';
import '../mapper/mapper.dart'; import 'package:stream_chat_persistence/src/mapper/mapper.dart';
part 'reaction_dao.g.dart'; part 'reaction_dao.g.dart';
@@ -16,18 +16,17 @@ class ReactionDao extends DatabaseAccessor<MoorChatDatabase>
/// Returns all the reactions of a particular message by matching /// Returns all the reactions of a particular message by matching
/// [Reactions.messageId] with [messageId] /// [Reactions.messageId] with [messageId]
Future<List<Reaction>> getReactions(String messageId) { Future<List<Reaction>> getReactions(String messageId) =>
return (select(reactions).join([ (select(reactions).join([
leftOuterJoin(users, reactions.userId.equalsExp(users.id)), leftOuterJoin(users, reactions.userId.equalsExp(users.id)),
]) ])
..where(reactions.messageId.equals(messageId)) ..where(reactions.messageId.equals(messageId))
..orderBy([OrderingTerm.asc(reactions.createdAt)])) ..orderBy([OrderingTerm.asc(reactions.createdAt)]))
.map((rows) { .map((rows) {
final userEntity = rows.readTable(users); final userEntity = rows.readTable(users);
final reactionEntity = rows.readTable(reactions); final reactionEntity = rows.readTable(reactions);
return reactionEntity.toReaction(user: userEntity?.toUser()); return reactionEntity.toReaction(user: userEntity?.toUser());
}).get(); }).get();
}
/// Returns all the reactions of a particular message /// Returns all the reactions of a particular message
/// added by a particular user by matching /// added by a particular user by matching
@@ -42,23 +41,21 @@ class ReactionDao extends DatabaseAccessor<MoorChatDatabase>
} }
/// Updates the reactions data with the new [reactionList] data /// Updates the reactions data with the new [reactionList] data
Future<void> updateReactions(List<Reaction> reactionList) { Future<void> updateReactions(List<Reaction> reactionList) => batch((it) {
return batch((it) { it.insertAll(
it.insertAll( reactions,
reactions, reactionList.map((r) => r.toEntity()).toList(),
reactionList.map((r) => r.toEntity()).toList(), mode: InsertMode.insertOrReplace,
mode: InsertMode.insertOrReplace, );
); });
});
}
/// Deletes all the reactions whose [Reactions.messageId] is present in [messageIds] /// Deletes all the reactions whose [Reactions.messageId] is
Future<void> deleteReactionsByMessageIds(List<String> messageIds) { /// present in [messageIds]
return batch((it) { Future<void> deleteReactionsByMessageIds(List<String> messageIds) =>
it.deleteWhere<Reactions, ReactionEntity>( batch((it) {
reactions, it.deleteWhere<Reactions, ReactionEntity>(
(r) => r.messageId.isIn(messageIds), reactions,
); (r) => r.messageId.isIn(messageIds),
}); );
} });
} }
@@ -3,7 +3,7 @@ import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'package:stream_chat_persistence/src/entity/reads.dart'; import 'package:stream_chat_persistence/src/entity/reads.dart';
import 'package:stream_chat_persistence/src/entity/users.dart'; import 'package:stream_chat_persistence/src/entity/users.dart';
import '../mapper/mapper.dart'; import 'package:stream_chat_persistence/src/mapper/mapper.dart';
part 'read_dao.g.dart'; part 'read_dao.g.dart';
@@ -14,30 +14,26 @@ class ReadDao extends DatabaseAccessor<MoorChatDatabase> with _$ReadDaoMixin {
ReadDao(MoorChatDatabase db) : super(db); ReadDao(MoorChatDatabase db) : super(db);
/// Get all reads where [Reads.channelCid] matches [cid] /// Get all reads where [Reads.channelCid] matches [cid]
Future<List<Read>> getReadsByCid(String cid) async { Future<List<Read>> getReadsByCid(String cid) async => (select(reads).join([
return (select(reads).join([ leftOuterJoin(users, reads.userId.equalsExp(users.id)),
leftOuterJoin(users, reads.userId.equalsExp(users.id)), ])
]) ..where(reads.channelCid.equals(cid))
..where(reads.channelCid.equals(cid)) ..orderBy([
..orderBy([ OrderingTerm.asc(reads.lastRead),
OrderingTerm.asc(reads.lastRead), ]))
])) .map((row) {
.map((row) { final userEntity = row.readTable(users);
final userEntity = row.readTable(users); final readEntity = row.readTable(reads);
final readEntity = row.readTable(reads); return readEntity.toRead(user: userEntity?.toUser());
return readEntity.toRead(user: userEntity?.toUser()); }).get();
}).get();
}
/// Updates the read data of a particular channel with /// Updates the read data of a particular channel with
/// the new [readList] data /// the new [readList] data
Future<void> updateReads(String cid, List<Read> readList) { Future<void> updateReads(String cid, List<Read> readList) => batch(
return batch( (it) => it.insertAll(
(it) => it.insertAll( reads,
reads, readList.map((r) => r.toEntity(cid: cid)).toList(),
readList.map((r) => r.toEntity(cid: cid)).toList(), mode: InsertMode.insertOrReplace,
mode: InsertMode.insertOrReplace, ),
), );
);
}
} }
@@ -2,7 +2,7 @@ import 'package:moor/moor.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'package:stream_chat_persistence/src/entity/users.dart'; import 'package:stream_chat_persistence/src/entity/users.dart';
import '../mapper/user_mapper.dart'; import 'package:stream_chat_persistence/src/mapper/user_mapper.dart';
part 'user_dao.g.dart'; part 'user_dao.g.dart';
@@ -13,13 +13,11 @@ class UserDao extends DatabaseAccessor<MoorChatDatabase> with _$UserDaoMixin {
UserDao(MoorChatDatabase db) : super(db); UserDao(MoorChatDatabase db) : super(db);
/// Updates the users data with the new [userList] data /// Updates the users data with the new [userList] data
Future<void> updateUsers(List<User> userList) { Future<void> updateUsers(List<User> userList) => batch(
return batch( (it) => it.insertAll(
(it) => it.insertAll( users,
users, userList.map((u) => u.toEntity()).toList(),
userList.map((u) => u.toEntity()).toList(), mode: InsertMode.insertOrReplace,
mode: InsertMode.insertOrReplace, ),
), );
);
}
} }
@@ -13,15 +13,12 @@ LazyDatabase _openConnection(
String userId, { String userId, {
bool logStatements = false, bool logStatements = false,
bool persistOnDisk = true, bool persistOnDisk = true,
}) { }) =>
return LazyDatabase(() async { LazyDatabase(() async => SharedDB.constructDatabase(
return await SharedDB.constructDatabase( userId,
userId, logStatements: logStatements,
logStatements: logStatements, persistOnDisk: persistOnDisk,
persistOnDisk: persistOnDisk, ));
);
});
}
/// A chat database implemented using moor /// A chat database implemented using moor
@UseMoor(tables: [ @UseMoor(tables: [
@@ -9,7 +9,7 @@ import 'package:path_provider/path_provider.dart';
import 'package:stream_chat_persistence/src/stream_chat_persistence_client.dart'; import 'package:stream_chat_persistence/src/stream_chat_persistence_client.dart';
import 'package:stream_chat_persistence/stream_chat_persistence.dart'; import 'package:stream_chat_persistence/stream_chat_persistence.dart';
import '../moor_chat_database.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
/// A Helper class to construct new instances of [MoorChatDatabase] specifically /// A Helper class to construct new instances of [MoorChatDatabase] specifically
/// for native platform applications /// for native platform applications
@@ -40,12 +40,10 @@ class SharedDB {
} }
static void _startBackground(_IsolateStartRequest request) { static void _startBackground(_IsolateStartRequest request) {
final executor = LazyDatabase(() async { final executor = LazyDatabase(() async => VmDatabase(
return VmDatabase( File(request.targetPath),
File(request.targetPath), logStatements: request.logStatements,
logStatements: request.logStatements, ));
);
});
final moorIsolate = MoorIsolate.inCurrent( final moorIsolate = MoorIsolate.inCurrent(
() => DatabaseConnection.fromExecutor(executor), () => DatabaseConnection.fromExecutor(executor),
); );
@@ -69,7 +67,7 @@ class SharedDB {
), ),
); );
return (await receivePort.first as MoorIsolate); return await receivePort.first as MoorIsolate;
} }
/// Returns a new instance of [MoorChatDatabase] using the factory constructor /// Returns a new instance of [MoorChatDatabase] using the factory constructor
@@ -84,7 +82,7 @@ class SharedDB {
return MoorChatDatabase.connect( return MoorChatDatabase.connect(
userId, userId,
DatabaseConnection.delayed(Future(() async { DatabaseConnection.delayed(Future(() async {
MoorIsolate isolate = await _createMoorIsolate( final isolate = await _createMoorIsolate(
dbName, dbName,
logStatements: logStatements, logStatements: logStatements,
); );
@@ -95,13 +93,13 @@ class SharedDB {
} }
class _IsolateStartRequest { class _IsolateStartRequest {
final SendPort sendMoorIsolate;
final String targetPath;
final bool logStatements;
const _IsolateStartRequest( const _IsolateStartRequest(
this.sendMoorIsolate, this.sendMoorIsolate,
this.targetPath, { this.targetPath, {
this.logStatements = false, this.logStatements = false,
}); });
final SendPort sendMoorIsolate;
final String targetPath;
final bool logStatements;
} }
@@ -12,7 +12,8 @@ class SharedDB {
bool logStatements = false, bool logStatements = false,
bool persistOnDisk = true, bool persistOnDisk = true,
}) { }) {
throw 'Unsupported Platform'; throw UnsupportedError(
'No implementation of the constructDatabase api provided');
} }
/// Return a new instance of moor chat database. /// Return a new instance of moor chat database.
@@ -22,6 +23,7 @@ class SharedDB {
String userId, { String userId, {
bool logStatements = false, bool logStatements = false,
}) { }) {
throw 'Unsupported Platform'; throw UnsupportedError(
'No implementation of the constructMoorChatDatabase api provided');
} }
} }
@@ -1,7 +1,7 @@
import 'package:moor/moor_web.dart'; import 'package:moor/moor_web.dart';
import 'package:stream_chat_persistence/src/stream_chat_persistence_client.dart'; import 'package:stream_chat_persistence/src/stream_chat_persistence_client.dart';
import '../moor_chat_database.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
/// A Helper class to construct new instances of [MoorChatDatabase] specifically /// A Helper class to construct new instances of [MoorChatDatabase] specifically
/// for Web applications /// for Web applications
@@ -1,9 +1,9 @@
export 'channel_queries.dart';
export 'channels.dart'; export 'channels.dart';
export 'connection_events.dart';
export 'members.dart';
export 'messages.dart'; export 'messages.dart';
export 'pinned_messages.dart'; export 'pinned_messages.dart';
export 'reactions.dart'; export 'reactions.dart';
export 'users.dart';
export 'members.dart';
export 'reads.dart'; export 'reads.dart';
export 'channel_queries.dart'; export 'users.dart';
export 'connection_events.dart';
@@ -1,6 +1,6 @@
import 'package:moor/moor.dart'; import 'package:moor/moor.dart';
import 'messages.dart'; import 'package:stream_chat_persistence/src/entity/messages.dart';
/// Represents a [PinnedMessages] table in [MoorChatDatabase]. /// Represents a [PinnedMessages] table in [MoorChatDatabase].
@DataClassName('PinnedMessageEntity') @DataClassName('PinnedMessageEntity')
@@ -29,34 +29,31 @@ extension ChannelEntityX on ChannelEntity {
List<Read> reads, List<Read> reads,
List<Message> messages, List<Message> messages,
List<Message> pinnedMessages, List<Message> pinnedMessages,
}) { }) =>
return ChannelState( ChannelState(
members: members, members: members,
read: reads, read: reads,
messages: messages, messages: messages,
pinnedMessages: pinnedMessages, pinnedMessages: pinnedMessages,
channel: toChannelModel(createdBy: createdBy), channel: toChannelModel(createdBy: createdBy),
); );
}
} }
/// Useful mapping functions for [ChannelModel] /// Useful mapping functions for [ChannelModel]
extension ChannelModelX on ChannelModel { extension ChannelModelX on ChannelModel {
/// Maps a [ChannelModel] into [ChannelEntity] /// Maps a [ChannelModel] into [ChannelEntity]
ChannelEntity toEntity() { ChannelEntity toEntity() => ChannelEntity(
return ChannelEntity( id: id,
id: id, type: type,
type: type, cid: cid,
cid: cid, config: config.toJson(),
config: config.toJson(), frozen: frozen,
frozen: frozen, lastMessageAt: lastMessageAt,
lastMessageAt: lastMessageAt, createdAt: createdAt,
createdAt: createdAt, updatedAt: updatedAt,
updatedAt: updatedAt, deletedAt: deletedAt,
deletedAt: deletedAt, memberCount: memberCount,
memberCount: memberCount, createdById: createdBy.id,
createdById: createdBy.id, extraData: extraData,
extraData: extraData, );
);
}
} }
@@ -4,11 +4,9 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
/// Useful mapping functions for [ConnectionEventEntity] /// Useful mapping functions for [ConnectionEventEntity]
extension ConnectionEventX on ConnectionEventEntity { extension ConnectionEventX on ConnectionEventEntity {
/// Maps a [ConnectionEventEntity] into [Event] /// Maps a [ConnectionEventEntity] into [Event]
Event toEvent() { Event toEvent() => Event(
return Event( me: ownUser != null ? OwnUser.fromJson(ownUser) : null,
me: ownUser != null ? OwnUser.fromJson(ownUser) : null, totalUnreadCount: totalUnreadCount,
totalUnreadCount: totalUnreadCount, unreadChannels: unreadChannels,
unreadChannels: unreadChannels, );
);
}
} }
@@ -1,8 +1,8 @@
export 'user_mapper.dart';
export 'reaction_mapper.dart';
export 'channel_mapper.dart'; export 'channel_mapper.dart';
export 'event_mapper.dart'; export 'event_mapper.dart';
export 'member_mapper.dart'; export 'member_mapper.dart';
export 'read_mapper.dart';
export 'message_mapper.dart'; export 'message_mapper.dart';
export 'pinned_message_mapper.dart'; export 'pinned_message_mapper.dart';
export 'reaction_mapper.dart';
export 'read_mapper.dart';
export 'user_mapper.dart';
@@ -4,39 +4,35 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
/// Useful mapping functions for [MemberEntity] /// Useful mapping functions for [MemberEntity]
extension MemberEntityX on MemberEntity { extension MemberEntityX on MemberEntity {
/// Maps a [MemberEntity] into [Member] /// Maps a [MemberEntity] into [Member]
Member toMember({User user}) { Member toMember({User user}) => Member(
return Member( user: user,
user: user, userId: userId,
userId: userId, banned: banned,
banned: banned, shadowBanned: shadowBanned,
shadowBanned: shadowBanned, updatedAt: updatedAt,
updatedAt: updatedAt, createdAt: createdAt,
createdAt: createdAt, role: role,
role: role, inviteAcceptedAt: inviteAcceptedAt,
inviteAcceptedAt: inviteAcceptedAt, invited: invited,
invited: invited, inviteRejectedAt: inviteRejectedAt,
inviteRejectedAt: inviteRejectedAt, isModerator: isModerator,
isModerator: isModerator, );
);
}
} }
/// Useful mapping functions for [Member] /// Useful mapping functions for [Member]
extension MemberX on Member { extension MemberX on Member {
/// Maps a [Member] into [MemberEntity] /// Maps a [Member] into [MemberEntity]
MemberEntity toEntity({String cid}) { MemberEntity toEntity({String cid}) => MemberEntity(
return MemberEntity( userId: user?.id,
userId: user?.id, banned: banned,
banned: banned, shadowBanned: shadowBanned,
shadowBanned: shadowBanned, channelCid: cid,
channelCid: cid, createdAt: createdAt,
createdAt: createdAt, isModerator: isModerator,
isModerator: isModerator, inviteRejectedAt: inviteRejectedAt,
inviteRejectedAt: inviteRejectedAt, invited: invited,
invited: invited, inviteAcceptedAt: inviteAcceptedAt,
inviteAcceptedAt: inviteAcceptedAt, role: role,
role: role, updatedAt: updatedAt,
updatedAt: updatedAt, );
);
}
} }
@@ -12,70 +12,66 @@ extension MessageEntityX on MessageEntity {
List<Reaction> latestReactions, List<Reaction> latestReactions,
List<Reaction> ownReactions, List<Reaction> ownReactions,
Message quotedMessage, Message quotedMessage,
}) { }) =>
return Message( Message(
shadowed: shadowed, shadowed: shadowed,
latestReactions: latestReactions, latestReactions: latestReactions,
ownReactions: ownReactions, ownReactions: ownReactions,
attachments: attachments?.map((it) { attachments: attachments?.map((it) {
final json = jsonDecode(it); final json = jsonDecode(it);
return Attachment.fromData(json); return Attachment.fromData(json);
})?.toList(), })?.toList(),
createdAt: createdAt, createdAt: createdAt,
extraData: extraData, extraData: extraData,
updatedAt: updatedAt, updatedAt: updatedAt,
id: id, id: id,
type: type, type: type,
status: status, status: status,
command: command, command: command,
parentId: parentId, parentId: parentId,
quotedMessageId: quotedMessageId, quotedMessageId: quotedMessageId,
quotedMessage: quotedMessage, quotedMessage: quotedMessage,
reactionCounts: reactionCounts, reactionCounts: reactionCounts,
reactionScores: reactionScores, reactionScores: reactionScores,
replyCount: replyCount, replyCount: replyCount,
showInChannel: showInChannel, showInChannel: showInChannel,
text: messageText, text: messageText,
user: user, user: user,
deletedAt: deletedAt, deletedAt: deletedAt,
pinned: pinned, pinned: pinned,
pinnedAt: pinnedAt, pinnedAt: pinnedAt,
pinExpires: pinExpires, pinExpires: pinExpires,
pinnedBy: pinnedBy, pinnedBy: pinnedBy,
); );
}
} }
/// Useful mapping functions for [Message] /// Useful mapping functions for [Message]
extension MessageX on Message { extension MessageX on Message {
/// Maps a [Message] into [MessageEntity] /// Maps a [Message] into [MessageEntity]
MessageEntity toEntity({String cid}) { MessageEntity toEntity({String cid}) => MessageEntity(
return MessageEntity( id: id,
id: id, attachments:
attachments: attachments?.map((it) { attachments?.map((it) => jsonEncode(it.toData()))?.toList(),
return jsonEncode(it.toData()); channelCid: cid,
})?.toList(), type: type,
channelCid: cid, parentId: parentId,
type: type, quotedMessageId: quotedMessageId,
parentId: parentId, command: command,
quotedMessageId: quotedMessageId, createdAt: createdAt,
command: command, shadowed: shadowed,
createdAt: createdAt, showInChannel: showInChannel,
shadowed: shadowed, replyCount: replyCount,
showInChannel: showInChannel, reactionScores: reactionScores,
replyCount: replyCount, reactionCounts: reactionCounts,
reactionScores: reactionScores, status: status,
reactionCounts: reactionCounts, updatedAt: updatedAt,
status: status, extraData: extraData,
updatedAt: updatedAt, userId: user?.id,
extraData: extraData, deletedAt: deletedAt,
userId: user?.id, messageText: text,
deletedAt: deletedAt, pinned: pinned,
messageText: text, pinnedAt: pinnedAt,
pinned: pinned, pinExpires: pinExpires,
pinnedAt: pinnedAt, pinnedByUserId: pinnedBy?.id,
pinExpires: pinExpires, );
pinnedByUserId: pinnedBy?.id,
);
}
} }
@@ -12,70 +12,66 @@ extension PinnedMessageEntityX on PinnedMessageEntity {
List<Reaction> latestReactions, List<Reaction> latestReactions,
List<Reaction> ownReactions, List<Reaction> ownReactions,
Message quotedMessage, Message quotedMessage,
}) { }) =>
return Message( Message(
shadowed: shadowed, shadowed: shadowed,
latestReactions: latestReactions, latestReactions: latestReactions,
ownReactions: ownReactions, ownReactions: ownReactions,
attachments: attachments?.map((it) { attachments: attachments?.map((it) {
final json = jsonDecode(it); final json = jsonDecode(it);
return Attachment.fromData(json); return Attachment.fromData(json);
})?.toList(), })?.toList(),
createdAt: createdAt, createdAt: createdAt,
extraData: extraData, extraData: extraData,
updatedAt: updatedAt, updatedAt: updatedAt,
id: id, id: id,
type: type, type: type,
status: status, status: status,
command: command, command: command,
parentId: parentId, parentId: parentId,
quotedMessageId: quotedMessageId, quotedMessageId: quotedMessageId,
quotedMessage: quotedMessage, quotedMessage: quotedMessage,
reactionCounts: reactionCounts, reactionCounts: reactionCounts,
reactionScores: reactionScores, reactionScores: reactionScores,
replyCount: replyCount, replyCount: replyCount,
showInChannel: showInChannel, showInChannel: showInChannel,
text: messageText, text: messageText,
user: user, user: user,
deletedAt: deletedAt, deletedAt: deletedAt,
pinned: pinned, pinned: pinned,
pinnedAt: pinnedAt, pinnedAt: pinnedAt,
pinExpires: pinExpires, pinExpires: pinExpires,
pinnedBy: pinnedBy, pinnedBy: pinnedBy,
); );
}
} }
/// Useful mapping functions for [Message] /// Useful mapping functions for [Message]
extension PMessageX on Message { extension PMessageX on Message {
/// Maps a [Message] into [PinnedMessageEntity] /// Maps a [Message] into [PinnedMessageEntity]
PinnedMessageEntity toPinnedEntity({String cid}) { PinnedMessageEntity toPinnedEntity({String cid}) => PinnedMessageEntity(
return PinnedMessageEntity( id: id,
id: id, attachments:
attachments: attachments?.map((it) { attachments?.map((it) => jsonEncode(it.toData()))?.toList(),
return jsonEncode(it.toData()); channelCid: cid,
})?.toList(), type: type,
channelCid: cid, parentId: parentId,
type: type, quotedMessageId: quotedMessageId,
parentId: parentId, command: command,
quotedMessageId: quotedMessageId, createdAt: createdAt,
command: command, shadowed: shadowed,
createdAt: createdAt, showInChannel: showInChannel,
shadowed: shadowed, replyCount: replyCount,
showInChannel: showInChannel, reactionScores: reactionScores,
replyCount: replyCount, reactionCounts: reactionCounts,
reactionScores: reactionScores, status: status,
reactionCounts: reactionCounts, updatedAt: updatedAt,
status: status, extraData: extraData,
updatedAt: updatedAt, userId: user?.id,
extraData: extraData, deletedAt: deletedAt,
userId: user?.id, messageText: text,
deletedAt: deletedAt, pinned: pinned,
messageText: text, pinnedAt: pinnedAt,
pinned: pinned, pinExpires: pinExpires,
pinnedAt: pinnedAt, pinnedByUserId: pinnedBy?.id,
pinExpires: pinExpires, );
pinnedByUserId: pinnedBy?.id,
);
}
} }
@@ -4,30 +4,26 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
/// Useful mapping functions for [ReactionEntity] /// Useful mapping functions for [ReactionEntity]
extension ReactionEntityX on ReactionEntity { extension ReactionEntityX on ReactionEntity {
/// Maps a [ReactionEntity] into [Reaction] /// Maps a [ReactionEntity] into [Reaction]
Reaction toReaction({User user}) { Reaction toReaction({User user}) => Reaction(
return Reaction( extraData: extraData,
extraData: extraData, type: type,
type: type, createdAt: createdAt,
createdAt: createdAt, userId: userId,
userId: userId, user: user,
user: user, messageId: messageId,
messageId: messageId, score: score,
score: score, );
);
}
} }
/// Useful mapping functions for [Reaction] /// Useful mapping functions for [Reaction]
extension ReactionX on Reaction { extension ReactionX on Reaction {
/// Maps a [Reaction] into [ReactionEntity] /// Maps a [Reaction] into [ReactionEntity]
ReactionEntity toEntity() { ReactionEntity toEntity() => ReactionEntity(
return ReactionEntity( extraData: extraData,
extraData: extraData, type: type,
type: type, createdAt: createdAt,
createdAt: createdAt, userId: userId,
userId: userId, messageId: messageId,
messageId: messageId, score: score,
score: score, );
);
}
} }
@@ -4,24 +4,20 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
/// Useful mapping functions for [ReadEntity] /// Useful mapping functions for [ReadEntity]
extension ReadEntityX on ReadEntity { extension ReadEntityX on ReadEntity {
/// Maps a [ReadEntity] into [Read] /// Maps a [ReadEntity] into [Read]
Read toRead({User user}) { Read toRead({User user}) => Read(
return Read( user: user,
user: user, lastRead: lastRead,
lastRead: lastRead, unreadMessages: unreadMessages,
unreadMessages: unreadMessages, );
);
}
} }
/// Useful mapping functions for [Read] /// Useful mapping functions for [Read]
extension ReadX on Read { extension ReadX on Read {
/// Maps a [Read] into [ReadEntity] /// Maps a [Read] into [ReadEntity]
ReadEntity toEntity({String cid}) { ReadEntity toEntity({String cid}) => ReadEntity(
return ReadEntity( lastRead: lastRead,
lastRead: lastRead, userId: user?.id,
userId: user?.id, channelCid: cid,
channelCid: cid, unreadMessages: unreadMessages,
unreadMessages: unreadMessages, );
);
}
} }
@@ -4,33 +4,29 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
/// Useful mapping functions for [UserEntity] /// Useful mapping functions for [UserEntity]
extension UserEntityX on UserEntity { extension UserEntityX on UserEntity {
/// Maps a [UserEntity] into [User] /// Maps a [UserEntity] into [User]
User toUser() { User toUser() => User(
return User( id: id,
id: id, updatedAt: updatedAt,
updatedAt: updatedAt, role: role,
role: role, online: online,
online: online, lastActive: lastActive,
lastActive: lastActive, extraData: extraData,
extraData: extraData, banned: banned,
banned: banned, createdAt: createdAt,
createdAt: createdAt, );
);
}
} }
/// Useful mapping functions for [User] /// Useful mapping functions for [User]
extension UserX on User { extension UserX on User {
/// Maps a [User] into [UserEntity] /// Maps a [User] into [UserEntity]
UserEntity toEntity() { UserEntity toEntity() => UserEntity(
return UserEntity( id: id,
id: id, role: role,
role: role, createdAt: createdAt,
createdAt: createdAt, updatedAt: updatedAt,
updatedAt: updatedAt, lastActive: lastActive,
lastActive: lastActive, online: online,
online: online, banned: banned,
banned: banned, extraData: extraData,
extraData: extraData, );
);
}
} }
@@ -3,8 +3,8 @@ import 'package:meta/meta.dart';
import 'package:mutex/mutex.dart'; import 'package:mutex/mutex.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
import 'db/shared/shared_db.dart'; import 'package:stream_chat_persistence/src/db/shared/shared_db.dart';
/// Various connection modes on which [StreamChatPersistenceClient] can work /// Various connection modes on which [StreamChatPersistenceClient] can work
enum ConnectionMode { enum ConnectionMode {
@@ -15,7 +15,7 @@ enum ConnectionMode {
background, background,
} }
final levelEmojiMapper = { final _levelEmojiMapper = {
Level.INFO: '', Level.INFO: '',
Level.WARNING: '⚠️', Level.WARNING: '⚠️',
Level.SEVERE: '🚨', Level.SEVERE: '🚨',
@@ -29,8 +29,8 @@ class StreamChatPersistenceClient extends ChatPersistenceClient {
ConnectionMode connectionMode = ConnectionMode.regular, ConnectionMode connectionMode = ConnectionMode.regular,
Level logLevel = Level.WARNING, Level logLevel = Level.WARNING,
LogHandlerFunction logHandlerFunction, LogHandlerFunction logHandlerFunction,
}) : assert(connectionMode != null), }) : assert(connectionMode != null, 'ConnectionMode cannot be null'),
assert(logLevel != null), assert(logLevel != null, 'LogLevel cannot be null'),
_connectionMode = connectionMode, _connectionMode = connectionMode,
_logger = Logger.detached('💽')..level = logLevel { _logger = Logger.detached('💽')..level = logLevel {
_logger.onRecord.listen(logHandlerFunction ?? _defaultLogHandler); _logger.onRecord.listen(logHandlerFunction ?? _defaultLogHandler);
@@ -56,8 +56,10 @@ class StreamChatPersistenceClient extends ChatPersistenceClient {
///``` ///```
LogHandlerFunction logHandlerFunction; LogHandlerFunction logHandlerFunction;
/// [MoorChatDatabase] instance used by this client.
@visibleForTesting @visibleForTesting
MoorChatDatabase db; MoorChatDatabase db;
final Logger _logger; final Logger _logger;
final ConnectionMode _connectionMode; final ConnectionMode _connectionMode;
final _mutex = ReadWriteMutex(); final _mutex = ReadWriteMutex();
@@ -65,13 +67,13 @@ class StreamChatPersistenceClient extends ChatPersistenceClient {
void _defaultLogHandler(LogRecord record) { void _defaultLogHandler(LogRecord record) {
print( print(
'(${record.time}) ' '(${record.time}) '
'${levelEmojiMapper[record.level] ?? record.level.name} ' '${_levelEmojiMapper[record.level] ?? record.level.name} '
'${record.loggerName} ${record.message}', '${record.loggerName} ${record.message}',
); );
if (record.stackTrace != null) print(record.stackTrace); if (record.stackTrace != null) print(record.stackTrace);
} }
Future<T> readProtected<T>(Future<T> Function() f) async { Future<T> _readProtected<T>(Future<T> Function() f) async {
T ret; T ret;
await _mutex.protectRead(() async { await _mutex.protectRead(() async {
if (db == null) { if (db == null) {
@@ -103,299 +105,256 @@ class StreamChatPersistenceClient extends ChatPersistenceClient {
} }
@override @override
Future<Event> getConnectionInfo() { Future<Event> getConnectionInfo() => _readProtected(() {
return readProtected(() { _logger.info('getConnectionInfo');
_logger.info('getConnectionInfo'); return db.connectionEventDao.connectionEvent;
return db.connectionEventDao.connectionEvent; });
});
}
@override @override
Future<void> updateConnectionInfo(Event event) { Future<void> updateConnectionInfo(Event event) => _readProtected(() {
return readProtected(() { _logger.info('updateConnectionInfo');
_logger.info('updateConnectionInfo'); return db.connectionEventDao.updateConnectionEvent(event);
return db.connectionEventDao.updateConnectionEvent(event); });
});
}
@override @override
Future<void> updateLastSyncAt(DateTime lastSyncAt) { Future<void> updateLastSyncAt(DateTime lastSyncAt) => _readProtected(() {
return readProtected(() { _logger.info('updateLastSyncAt');
_logger.info('updateLastSyncAt'); return db.connectionEventDao.updateLastSyncAt(lastSyncAt);
return db.connectionEventDao.updateLastSyncAt(lastSyncAt); });
});
}
@override @override
Future<DateTime> getLastSyncAt() { Future<DateTime> getLastSyncAt() => _readProtected(() {
return readProtected(() { _logger.info('getLastSyncAt');
_logger.info('getLastSyncAt'); return db.connectionEventDao.lastSyncAt;
return db.connectionEventDao.lastSyncAt; });
});
}
@override @override
Future<void> deleteChannels(List<String> cids) { Future<void> deleteChannels(List<String> cids) => _readProtected(() {
return readProtected(() { _logger.info('deleteChannels');
_logger.info('deleteChannels'); return db.channelDao.deleteChannelByCids(cids);
return db.channelDao.deleteChannelByCids(cids); });
});
}
@override @override
Future<List<String>> getChannelCids() { Future<List<String>> getChannelCids() => _readProtected(() {
return readProtected(() { _logger.info('getChannelCids');
_logger.info('getChannelCids'); return db.channelDao.cids;
return db.channelDao.cids; });
});
}
@override @override
Future<void> deleteMessageByIds(List<String> messageIds) { Future<void> deleteMessageByIds(List<String> messageIds) =>
return readProtected(() { _readProtected(() {
_logger.info('deleteMessageByIds'); _logger.info('deleteMessageByIds');
return db.messageDao.deleteMessageByIds(messageIds); return db.messageDao.deleteMessageByIds(messageIds);
}); });
}
@override @override
Future<void> deletePinnedMessageByIds(List<String> messageIds) { Future<void> deletePinnedMessageByIds(List<String> messageIds) =>
return readProtected(() { _readProtected(() {
_logger.info('deletePinnedMessageByIds'); _logger.info('deletePinnedMessageByIds');
return db.pinnedMessageDao.deleteMessageByIds(messageIds); return db.pinnedMessageDao.deleteMessageByIds(messageIds);
}); });
}
@override @override
Future<void> deleteMessageByCids(List<String> cids) { Future<void> deleteMessageByCids(List<String> cids) => _readProtected(() {
return readProtected(() { _logger.info('deleteMessageByCids');
_logger.info('deleteMessageByCids'); return db.messageDao.deleteMessageByCids(cids);
return db.messageDao.deleteMessageByCids(cids); });
});
}
@override @override
Future<void> deletePinnedMessageByCids(List<String> cids) { Future<void> deletePinnedMessageByCids(List<String> cids) =>
return readProtected(() { _readProtected(() {
_logger.info('deletePinnedMessageByCids'); _logger.info('deletePinnedMessageByCids');
return db.pinnedMessageDao.deleteMessageByCids(cids); return db.pinnedMessageDao.deleteMessageByCids(cids);
}); });
}
@override @override
Future<List<Member>> getMembersByCid(String cid) { Future<List<Member>> getMembersByCid(String cid) => _readProtected(() {
return readProtected(() { _logger.info('getMembersByCid');
_logger.info('getMembersByCid'); return db.memberDao.getMembersByCid(cid);
return db.memberDao.getMembersByCid(cid); });
});
}
@override @override
Future<ChannelModel> getChannelByCid(String cid) { Future<ChannelModel> getChannelByCid(String cid) => _readProtected(() {
return readProtected(() { _logger.info('getChannelByCid');
_logger.info('getChannelByCid'); return db.channelDao.getChannelByCid(cid);
return db.channelDao.getChannelByCid(cid); });
});
}
@override @override
Future<List<Message>> getMessagesByCid( Future<List<Message>> getMessagesByCid(
String cid, { String cid, {
PaginationParams messagePagination, PaginationParams messagePagination,
}) { }) =>
return readProtected(() { _readProtected(() {
_logger.info('getMessagesByCid'); _logger.info('getMessagesByCid');
return db.messageDao.getMessagesByCid( return db.messageDao.getMessagesByCid(
cid, cid,
messagePagination: messagePagination, messagePagination: messagePagination,
); );
}); });
}
@override @override
Future<List<Message>> getPinnedMessagesByCid( Future<List<Message>> getPinnedMessagesByCid(
String cid, { String cid, {
PaginationParams messagePagination, PaginationParams messagePagination,
}) { }) =>
return readProtected(() { _readProtected(() {
_logger.info('getPinnedMessagesByCid'); _logger.info('getPinnedMessagesByCid');
return db.pinnedMessageDao.getMessagesByCid( return db.pinnedMessageDao.getMessagesByCid(
cid, cid,
messagePagination: messagePagination, messagePagination: messagePagination,
); );
}); });
}
@override @override
Future<List<Read>> getReadsByCid(String cid) { Future<List<Read>> getReadsByCid(String cid) => _readProtected(() {
return readProtected(() { _logger.info('getReadsByCid');
_logger.info('getReadsByCid'); return db.readDao.getReadsByCid(cid);
return db.readDao.getReadsByCid(cid); });
});
}
@override @override
Future<Map<String, List<Message>>> getChannelThreads(String cid) async { Future<Map<String, List<Message>>> getChannelThreads(String cid) async =>
return readProtected(() async { _readProtected(() async {
_logger.info('getChannelThreads'); _logger.info('getChannelThreads');
final messages = await db.messageDao.getThreadMessages(cid); final messages = await db.messageDao.getThreadMessages(cid);
final messageByParentIdDictionary = <String, List<Message>>{}; final messageByParentIdDictionary = <String, List<Message>>{};
for (final message in messages) { for (final message in messages) {
final parentId = message.parentId; final parentId = message.parentId;
messageByParentIdDictionary[parentId] = [ messageByParentIdDictionary[parentId] = [
...messageByParentIdDictionary[parentId] ?? [], ...messageByParentIdDictionary[parentId] ?? [],
message message
]; ];
} }
return messageByParentIdDictionary; return messageByParentIdDictionary;
}); });
}
@override @override
Future<List<Message>> getReplies( Future<List<Message>> getReplies(
String parentId, { String parentId, {
PaginationParams options, PaginationParams options,
}) { }) =>
return readProtected(() async { _readProtected(() async {
_logger.info('getReplies'); _logger.info('getReplies');
return db.messageDao.getThreadMessagesByParentId( return db.messageDao.getThreadMessagesByParentId(
parentId, parentId,
options: options, options: options,
); );
}); });
}
@override @override
Future<List<ChannelState>> getChannelStates({ Future<List<ChannelState>> getChannelStates({
Map<String, dynamic> filter, Map<String, dynamic> filter,
List<SortOption<ChannelModel>> sort = const [], List<SortOption<ChannelModel>> sort = const [],
PaginationParams paginationParams, PaginationParams paginationParams,
}) async { }) async =>
return readProtected(() async { _readProtected(() async {
_logger.info('getChannelStates'); _logger.info('getChannelStates');
final channels = await db.channelQueryDao.getChannels( final channels = await db.channelQueryDao.getChannels(
filter: filter, filter: filter,
sort: sort, sort: sort,
paginationParams: paginationParams, paginationParams: paginationParams,
); );
return Future.wait(channels.map((e) => getChannelStateByCid(e.cid))); return Future.wait(channels.map((e) => getChannelStateByCid(e.cid)));
}); });
}
@override @override
Future<void> updateChannelQueries( Future<void> updateChannelQueries(
Map<String, dynamic> filter, Map<String, dynamic> filter,
List<String> cids, List<String> cids,
bool clearQueryCache, bool clearQueryCache,
) { ) =>
return readProtected(() async { _readProtected(() async {
_logger.info('updateChannelQueries'); _logger.info('updateChannelQueries');
return db.channelQueryDao.updateChannelQueries( return db.channelQueryDao.updateChannelQueries(
filter, filter,
cids, cids,
clearQueryCache, clearQueryCache: clearQueryCache,
); );
});
}
@override
Future<void> updateChannels(List<ChannelModel> channels) {
return readProtected(() async {
_logger.info('updateChannels');
return db.channelDao.updateChannels(channels);
});
}
@override
Future<void> updateMembers(String cid, List<Member> members) {
return readProtected(() async {
_logger.info('updateMembers');
return db.memberDao.updateMembers(cid, members);
});
}
@override
Future<void> updateMessages(String cid, List<Message> messages) {
return readProtected(() async {
_logger.info('updateMessages');
return db.messageDao.updateMessages(cid, messages);
});
}
@override
Future<void> updatePinnedMessages(String cid, List<Message> messages) {
return readProtected(() async {
_logger.info('updatePinnedMessages');
return db.pinnedMessageDao.updateMessages(cid, messages);
});
}
@override
Future<void> updateReactions(List<Reaction> reactions) {
return readProtected(() async {
_logger.info('updateReactions');
return db.reactionDao.updateReactions(reactions);
});
}
@override
Future<void> updateReads(String cid, List<Read> reads) {
return readProtected(() async {
_logger.info('updateReads');
return db.readDao.updateReads(cid, reads);
});
}
@override
Future<void> updateUsers(List<User> users) {
return readProtected(() async {
_logger.info('updateUsers');
return db.userDao.updateUsers(users);
});
}
@override
Future<void> deleteReactionsByMessageId(List<String> messageIds) {
return readProtected(() async {
_logger.info('deleteReactionsByMessageId');
return db.reactionDao.deleteReactionsByMessageIds(messageIds);
});
}
@override
Future<void> deleteMembersByCids(List<String> cids) {
return readProtected(() async {
_logger.info('deleteMembersByCids');
return db.memberDao.deleteMemberByCids(cids);
});
}
@override
Future<void> updateChannelStates(List<ChannelState> channelStates) {
return readProtected(() async {
return db.transaction(() async {
await super.updateChannelStates(channelStates);
}); });
});
}
@override @override
Future<void> disconnect({bool flush = false}) async { Future<void> updateChannels(List<ChannelModel> channels) =>
return _mutex.protectWrite(() async { _readProtected(() async {
_logger.info('disconnect'); _logger.info('updateChannels');
if (db != null) { return db.channelDao.updateChannels(channels);
_logger.info('Disconnecting'); });
if (flush) {
_logger.info('Flushing'); @override
await db.batch((batch) { Future<void> updateMembers(String cid, List<Member> members) =>
db.allTables.forEach((table) { _readProtected(() async {
db.delete(table).go(); _logger.info('updateMembers');
return db.memberDao.updateMembers(cid, members);
});
@override
Future<void> updateMessages(String cid, List<Message> messages) =>
_readProtected(() async {
_logger.info('updateMessages');
return db.messageDao.updateMessages(cid, messages);
});
@override
Future<void> updatePinnedMessages(String cid, List<Message> messages) =>
_readProtected(() async {
_logger.info('updatePinnedMessages');
return db.pinnedMessageDao.updateMessages(cid, messages);
});
@override
Future<void> updateReactions(List<Reaction> reactions) =>
_readProtected(() async {
_logger.info('updateReactions');
return db.reactionDao.updateReactions(reactions);
});
@override
Future<void> updateReads(String cid, List<Read> reads) =>
_readProtected(() async {
_logger.info('updateReads');
return db.readDao.updateReads(cid, reads);
});
@override
Future<void> updateUsers(List<User> users) => _readProtected(() async {
_logger.info('updateUsers');
return db.userDao.updateUsers(users);
});
@override
Future<void> deleteReactionsByMessageId(List<String> messageIds) =>
_readProtected(() async {
_logger.info('deleteReactionsByMessageId');
return db.reactionDao.deleteReactionsByMessageIds(messageIds);
});
@override
Future<void> deleteMembersByCids(List<String> cids) =>
_readProtected(() async {
_logger.info('deleteMembersByCids');
return db.memberDao.deleteMemberByCids(cids);
});
@override
Future<void> updateChannelStates(List<ChannelState> channelStates) =>
_readProtected(() async => db.transaction(() async {
await super.updateChannelStates(channelStates);
}));
@override
Future<void> disconnect({bool flush = false}) async =>
_mutex.protectWrite(() async {
_logger.info('disconnect');
if (db != null) {
_logger.info('Disconnecting');
if (flush) {
_logger.info('Flushing');
await db.batch((batch) {
db.allTables.forEach((table) {
db.delete(table).go();
});
}); });
}); }
await db.disconnect();
db = null;
} }
await db.disconnect(); });
db = null;
}
});
}
} }