import 'package:stream_chat/src/api/requests.dart'; import 'package:stream_chat/src/models/channel_model.dart'; import 'package:stream_chat/src/models/channel_state.dart'; import 'package:stream_chat/src/models/event.dart'; import 'package:stream_chat/src/models/member.dart'; import 'package:stream_chat/src/models/message.dart'; import 'package:stream_chat/src/models/reaction.dart'; import 'package:stream_chat/src/models/read.dart'; import 'package:stream_chat/src/models/user.dart'; /// A simple client used for persisting chat data locally. abstract class ChatPersistenceClient { /// Creates a new connection to the client Future connect(String userId); /// Closes the client connection /// If [flush] is true, the data will also be deleted Future disconnect({bool flush = false}); /// Get stored replies by messageId Future> getReplies( String parentId, { PaginationParams options, }); /// Get stored connection event Future getConnectionInfo(); /// Get stored lastSyncAt Future getLastSyncAt(); /// Update stored connection event Future updateConnectionInfo(Event event); /// Update stored lastSyncAt Future updateLastSyncAt(DateTime lastSyncAt); /// Get the channel cids saved in the offline storage Future> getChannelCids(); /// Get stored [ChannelModel]s by providing channel [cid] Future getChannelByCid(String cid); /// Get stored channel [Member]s by providing channel [cid] Future> getMembersByCid(String cid); /// Get stored channel [Read]s by providing channel [cid] Future> getReadsByCid(String cid); /// Get stored [Message]s by providing channel [cid] /// /// Optionally, you can [messagePagination] /// for filtering out messages Future> getMessagesByCid( String cid, { PaginationParams messagePagination, }); /// Get [ChannelState] data by providing channel [cid] Future getChannelStateByCid( String cid, { PaginationParams messagePagination, }) async { final members = await getMembersByCid(cid); final reads = await getReadsByCid(cid); final channel = await getChannelByCid(cid); final messages = await getMessagesByCid( cid, messagePagination: messagePagination, ); return ChannelState( members: members, read: reads, messages: messages, channel: channel, ); } /// Get all the stored [ChannelState]s /// /// Optionally, pass [filter], [sort], [paginationParams] /// for filtering out states. Future> getChannelStates({ Map filter, List sort = const [], PaginationParams paginationParams, }); /// Update list of channel queries. /// /// If [clearQueryCache] is true before the insert /// the list of matching rows will be deleted Future updateChannelQueries( Map filter, List cids, bool clearQueryCache, ); /// Remove a message by [messageId] Future deleteMessageById(String messageId) { return deleteMessageByIds([messageId]); } /// Remove a message by [messageIds] Future deleteMessageByIds(List messageIds); /// Remove a message by channel [cid] Future deleteMessageByCid(String cid) { return deleteMessageByCids([cid]); } /// Remove a message by message [cids] Future deleteMessageByCids(List cids); /// Remove a channel by [cid] Future deleteChannels(List cids); /// Updates the message data of a particular channel [cid] with /// the new [messages] data Future updateMessages(String cid, List messages); /// Returns all the threads by parent message of a particular channel by /// providing channel [cid] Future>> getChannelThreads(String cid); /// Updates all the channels using the new [channels] data. Future updateChannels(List channels); /// Updates all the members of a particular channle [cid] /// with the new [members] data Future updateMembers(String cid, List members); /// Updates the read data of a particular channel [cid] with /// the new [reads] data Future updateReads(String cid, List reads); /// Updates the users data with the new [users] data Future updateUsers(List users); /// Updates the reactions data with the new [reactions] data Future updateReactions(List reactions); /// Deletes all the reactions by [messageIds] Future deleteReactionsByMessageId(List messageIds); /// Deletes all the members by channel [cids] Future deleteMembersByCids(List cids); /// Update the channel state data using [channelState] Future updateChannelState(ChannelState channelState) { return updateChannelStates([channelState]); } /// Update list of channel states Future updateChannelStates(List channelStates) async { final channels = channelStates.map((it) { return it.channel; }).where((it) => it != null); final reactions = channelStates.expand((it) => it.messages).expand((it) { return [ ...it.ownReactions.where((r) => r.userId != null), ...it.latestReactions.where((r) => r.userId != null) ]; }).where((it) => it != null); final users = channelStates .map((cs) => [ cs.channel?.createdBy, ...cs.messages?.map((m) { return [ m.user, ...m.latestReactions?.map((r) => r.user), ...m.ownReactions?.map((r) => r.user), ]; })?.expand((v) => v), ...cs.read?.map((r) => r.user), ...cs.members?.map((m) => m.user), ]) .expand((it) => it) .where((it) => it != null); final updateMessagesFuture = channelStates.map((it) { final cid = it.channel.cid; final messages = it.messages.where((it) => it != null); return updateMessages(cid, messages.toList(growable: false)); }).toList(growable: false); final updateReadsFuture = channelStates.map((it) { final cid = it.channel.cid; final reads = it.read.where((it) => it != null); return updateReads(cid, reads.toList(growable: false)); }).toList(growable: false); final updateMembersFuture = channelStates.map((it) { final cid = it.channel.cid; final members = it.members.where((it) => it != null); return updateMembers(cid, members.toList(growable: false)); }).toList(growable: false); final deleteReactions = deleteReactionsByMessageId(channelStates .expand((it) => it.messages) .map((m) => m.id) .toList(growable: false)); final deleteMembers = deleteMembersByCids( channelStates.map((it) => it.channel.cid).toList(growable: false), ); await Future.wait([ deleteReactions, deleteMembers, ]); await Future.wait([ ...updateMessagesFuture, ...updateReadsFuture, ...updateMembersFuture, updateUsers(users.toList(growable: false)), updateChannels(channels.toList(growable: false)), updateReactions(reactions.toList(growable: false)), ]); } }