From cf6ae8761ed532e9bd5a5a4dff1dabbbbf443c2c Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Mon, 22 Feb 2021 17:39:23 +0530 Subject: [PATCH 01/41] [Persistence] Add support for pinned messages Signed-off-by: Sahil Kumar --- .../lib/src/db/chat_persistence_client.dart | 39 + .../lib/src/dao/channel_query_dao.dart | 2 + .../lib/src/dao/dao.dart | 1 + .../lib/src/dao/message_dao.dart | 24 +- .../lib/src/dao/pinned_message_dao.dart | 169 ++ .../lib/src/dao/pinned_message_dao.g.dart | 12 + .../lib/src/db/moor_chat_database.dart | 4 +- .../lib/src/db/moor_chat_database.g.dart | 1375 ++++++++++++++++- .../lib/src/entity/entity.dart | 1 + .../lib/src/entity/messages.dart | 12 + .../lib/src/entity/pinned_messages.dart | 7 + .../lib/src/mapper/channel_mapper.dart | 2 + .../lib/src/mapper/mapper.dart | 1 + .../lib/src/mapper/message_mapper.dart | 9 + .../lib/src/mapper/pinned_message_mapper.dart | 81 + .../src/stream_chat_persistence_client.dart | 26 + packages/stream_chat_persistence/pubspec.yaml | 3 +- 17 files changed, 1759 insertions(+), 9 deletions(-) create mode 100644 packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.dart create mode 100644 packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.g.dart create mode 100644 packages/stream_chat_persistence/lib/src/entity/pinned_messages.dart create mode 100644 packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart diff --git a/packages/stream_chat/lib/src/db/chat_persistence_client.dart b/packages/stream_chat/lib/src/db/chat_persistence_client.dart index ac65783d1..9c31eca93 100644 --- a/packages/stream_chat/lib/src/db/chat_persistence_client.dart +++ b/packages/stream_chat/lib/src/db/chat_persistence_client.dart @@ -56,10 +56,17 @@ abstract class ChatPersistenceClient { PaginationParams messagePagination, }); + /// Get stored pinned [Message]s by providing channel [cid] + Future> getPinnedMessagesByCid( + String cid, { + PaginationParams messagePagination, + }); + /// Get [ChannelState] data by providing channel [cid] Future getChannelStateByCid( String cid, { PaginationParams messagePagination, + PaginationParams pinnedMessagePagination, }) async { final members = await getMembersByCid(cid); final reads = await getReadsByCid(cid); @@ -68,10 +75,15 @@ abstract class ChatPersistenceClient { cid, messagePagination: messagePagination, ); + final pinnedMessages = await getPinnedMessagesByCid( + cid, + messagePagination: pinnedMessagePagination, + ); return ChannelState( members: members, read: reads, messages: messages, + pinnedMessages: pinnedMessages, channel: channel, ); } @@ -101,17 +113,33 @@ abstract class ChatPersistenceClient { return deleteMessageByIds([messageId]); } + /// Remove a pinned message by [messageId] + Future deletePinnedMessageById(String messageId) { + return deletePinnedMessageByIds([messageId]); + } + /// Remove a message by [messageIds] Future deleteMessageByIds(List messageIds); + /// Remove a pinned message by [messageIds] + Future deletePinnedMessageByIds(List messageIds); + /// Remove a message by channel [cid] Future deleteMessageByCid(String cid) { return deleteMessageByCids([cid]); } + /// Remove a pinned message by channel [cid] + Future deletePinnedMessageByCid(String cid) { + return deletePinnedMessageByCids([cid]); + } + /// Remove a message by message [cids] Future deleteMessageByCids(List cids); + /// Remove a pinned message by message [cids] + Future deletePinnedMessageByCids(List cids); + /// Remove a channel by [cid] Future deleteChannels(List cids); @@ -119,6 +147,10 @@ abstract class ChatPersistenceClient { /// the new [messages] data Future updateMessages(String cid, List messages); + /// Updates the pinned message data of a particular channel [cid] with + /// the new [messages] data + Future updatePinnedMessages(String cid, List messages); + /// Returns all the threads by parent message of a particular channel by /// providing channel [cid] Future>> getChannelThreads(String cid); @@ -204,6 +236,12 @@ abstract class ChatPersistenceClient { return updateMessages(cid, messages.toList(growable: false)); }).toList(growable: false); + final updatePinnedMessagesFuture = channelStates.map((it) { + final cid = it.channel.cid; + final messages = it.pinnedMessages.where((it) => it != null); + return updatePinnedMessages(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) ?? []; @@ -218,6 +256,7 @@ abstract class ChatPersistenceClient { await Future.wait([ ...updateMessagesFuture, + ...updatePinnedMessagesFuture, ...updateReadsFuture, ...updateMembersFuture, updateUsers(users.toList(growable: false)), diff --git a/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart b/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart index 1492420c4..83743debf 100644 --- a/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart @@ -101,12 +101,14 @@ class ChannelQueryDao extends DatabaseAccessor final members = await _db.memberDao.getMembersByCid(cid); final reads = await _db.readDao.getReadsByCid(cid); final messages = await _db.messageDao.getMessagesByCid(cid); + final pinnedMessages = await _db.pinnedMessageDao.getMessagesByCid(cid); return channelEntity.toChannelState( createdBy: userEntity?.toUser(), members: members, reads: reads, messages: messages, + pinnedMessages: pinnedMessages, ); }).get(); })); diff --git a/packages/stream_chat_persistence/lib/src/dao/dao.dart b/packages/stream_chat_persistence/lib/src/dao/dao.dart index 6f1e8221f..319536306 100644 --- a/packages/stream_chat_persistence/lib/src/dao/dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/dao.dart @@ -1,6 +1,7 @@ export 'user_dao.dart'; export 'channel_dao.dart'; export 'message_dao.dart'; +export 'pinned_message_dao.dart'; export 'member_dao.dart'; export 'connection_event_dao.dart'; export 'reaction_dao.dart'; diff --git a/packages/stream_chat_persistence/lib/src/dao/message_dao.dart b/packages/stream_chat_persistence/lib/src/dao/message_dao.dart index c207ae208..01e5c889c 100644 --- a/packages/stream_chat_persistence/lib/src/dao/message_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/message_dao.dart @@ -17,6 +17,10 @@ class MessageDao extends DatabaseAccessor final MoorChatDatabase _db; + $UsersTable get _users => alias(users, 'users'); + + $UsersTable get _pinnedByUsers => alias(users, 'pinnedByUsers'); + /// Removes all the messages by matching [Messages.id] in [messageIds] /// /// This will automatically delete the following linked records @@ -34,7 +38,8 @@ class MessageDao extends DatabaseAccessor } Future _messageFromJoinRow(TypedResult rows) async { - final userEntity = rows.readTable(users); + final userEntity = rows.readTable(_users); + final pinnedByEntity = rows.readTable(_pinnedByUsers); final msgEntity = rows.readTable(messages); final latestReactions = await _db.reactionDao.getReactions(msgEntity.id); final ownReactions = await _db.reactionDao.getReactionsByUserId( @@ -47,6 +52,7 @@ class MessageDao extends DatabaseAccessor } return msgEntity.toMessage( user: userEntity?.toUser(), + pinnedBy: pinnedByEntity?.toUser(), latestReactions: latestReactions, ownReactions: ownReactions, quotedMessage: quotedMessage, @@ -56,7 +62,9 @@ class MessageDao extends DatabaseAccessor /// Returns a single message by matching the [Messages.id] with [id] Future getMessageById(String id) async { return await (select(messages).join([ - leftOuterJoin(users, messages.userId.equalsExp(users.id)), + leftOuterJoin(_users, messages.userId.equalsExp(_users.id)), + leftOuterJoin( + _pinnedByUsers, messages.pinnedByUserId.equalsExp(_pinnedByUsers.id)), ]) ..where(messages.id.equals(id))) .map(_messageFromJoinRow) @@ -67,7 +75,9 @@ class MessageDao extends DatabaseAccessor /// [Messages.channelCid] with [cid] Future> getThreadMessages(String cid) async { return Future.wait(await (select(messages).join([ - leftOuterJoin(users, messages.userId.equalsExp(users.id)), + leftOuterJoin(users, messages.userId.equalsExp(_users.id)), + leftOuterJoin( + _pinnedByUsers, messages.pinnedByUserId.equalsExp(_pinnedByUsers.id)), ]) ..where(messages.channelCid.equals(cid)) ..where(isNotNull(messages.parentId)) @@ -83,7 +93,9 @@ class MessageDao extends DatabaseAccessor PaginationParams options, }) async { final msgList = await Future.wait(await (select(messages).join([ - innerJoin(users, messages.userId.equalsExp(users.id)), + innerJoin(_users, messages.userId.equalsExp(_users.id)), + innerJoin( + _pinnedByUsers, messages.pinnedByUserId.equalsExp(_pinnedByUsers.id)), ]) ..where(messages.parentId.equals(parentId)) ..orderBy([OrderingTerm.asc(messages.createdAt)])) @@ -104,7 +116,9 @@ class MessageDao extends DatabaseAccessor PaginationParams messagePagination, }) async { final msgList = await Future.wait(await (select(messages).join([ - leftOuterJoin(users, messages.userId.equalsExp(users.id)), + leftOuterJoin(_users, messages.userId.equalsExp(_users.id)), + leftOuterJoin( + _pinnedByUsers, messages.pinnedByUserId.equalsExp(_pinnedByUsers.id)), ]) ..where(messages.channelCid.equals(cid)) ..where( diff --git a/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.dart b/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.dart new file mode 100644 index 000000000..edf8e4384 --- /dev/null +++ b/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.dart @@ -0,0 +1,169 @@ +import 'package:moor/moor.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/entity/pinned_messages.dart'; +import 'package:stream_chat_persistence/src/entity/users.dart'; + +import '../mapper/mapper.dart'; + +part 'pinned_message_dao.g.dart'; + +/// The Data Access Object for operations in [Messages] table. +@UseDao(tables: [PinnedMessages, Users]) +class PinnedMessageDao extends DatabaseAccessor + with _$PinnedMessageDaoMixin { + /// Creates a new message dao instance + PinnedMessageDao(this._db) : super(_db); + + final MoorChatDatabase _db; + + $UsersTable get _users => alias(users, 'users'); + + $UsersTable get _pinnedByUsers => alias(users, 'pinnedByUsers'); + + /// Removes all the messages by matching [PinnedMessages.id] in [messageIds] + /// + /// This will automatically delete the following linked records + /// 1. Message Reactions + Future deleteMessageByIds(List messageIds) { + return (delete(pinnedMessages)..where((tbl) => tbl.id.isIn(messageIds))) + .go(); + } + + /// Removes all the messages by matching [PinnedMessages.channelCid] in [cids] + /// + /// This will automatically delete the following linked records + /// 1. Message Reactions + Future deleteMessageByCids(List cids) async { + return (delete(pinnedMessages)..where((tbl) => tbl.channelCid.isIn(cids))) + .go(); + } + + Future _messageFromJoinRow(TypedResult rows) async { + final userEntity = rows.readTable(users); + final pinnedByEntity = rows.readTable(_pinnedByUsers); + final msgEntity = rows.readTable(pinnedMessages); + final latestReactions = await _db.reactionDao.getReactions(msgEntity.id); + final ownReactions = await _db.reactionDao.getReactionsByUserId( + msgEntity.id, + _db.userId, + ); + Message quotedMessage; + if (msgEntity.quotedMessageId != null) { + quotedMessage = await getMessageById(msgEntity.quotedMessageId); + } + return msgEntity.toMessage( + user: userEntity?.toUser(), + pinnedBy: pinnedByEntity?.toUser(), + latestReactions: latestReactions, + ownReactions: ownReactions, + quotedMessage: quotedMessage, + ); + } + + /// Returns a single message by matching the [PinnedMessages.id] with [id] + Future getMessageById(String id) async { + return await (select(pinnedMessages).join([ + leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)), + leftOuterJoin(_pinnedByUsers, + pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id)), + ]) + ..where(pinnedMessages.id.equals(id))) + .map(_messageFromJoinRow) + .getSingle(); + } + + /// Returns all the messages of a particular thread by matching + /// [PinnedMessages.channelCid] with [cid] + Future> getThreadMessages(String cid) async { + return Future.wait(await (select(pinnedMessages).join([ + leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)), + leftOuterJoin(_pinnedByUsers, + pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id)), + ]) + ..where(pinnedMessages.channelCid.equals(cid)) + ..where(isNotNull(pinnedMessages.parentId)) + ..orderBy([OrderingTerm.asc(pinnedMessages.createdAt)])) + .map(_messageFromJoinRow) + .get()); + } + + /// Returns all the messages of a particular thread by matching + /// [PinnedMessages.parentId] with [parentId] + Future> getThreadMessagesByParentId( + String parentId, { + PaginationParams options, + }) async { + final msgList = await Future.wait(await (select(pinnedMessages).join([ + innerJoin(_users, pinnedMessages.userId.equalsExp(_users.id)), + innerJoin(_pinnedByUsers, + pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id)), + ]) + ..where(pinnedMessages.parentId.equals(parentId)) + ..orderBy([OrderingTerm.asc(pinnedMessages.createdAt)])) + .map(_messageFromJoinRow) + .get()); + + if (options?.lessThan != null) { + final lessThanIndex = msgList.indexWhere((m) => m.id == options.lessThan); + msgList.removeRange(lessThanIndex, msgList.length); + } + return msgList; + } + + /// Returns all the messages of a channel by matching + /// [PinnedMessages.channelCid] with [parentId] + Future> getMessagesByCid( + String cid, { + PaginationParams messagePagination, + }) async { + final msgList = await Future.wait(await (select(pinnedMessages).join([ + leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)), + leftOuterJoin(_pinnedByUsers, + pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id)), + ]) + ..where(pinnedMessages.channelCid.equals(cid)) + ..where(isNull(pinnedMessages.parentId) | + pinnedMessages.showInChannel.equals(true)) + ..orderBy([OrderingTerm.asc(pinnedMessages.createdAt)])) + .map(_messageFromJoinRow) + .get()); + + if (messagePagination?.lessThan != null) { + final lessThanIndex = msgList.indexWhere( + (m) => m.id == messagePagination.lessThan, + ); + if (lessThanIndex != -1) { + msgList.removeRange(lessThanIndex, msgList.length); + } + } + if (messagePagination?.greaterThanOrEqual != null) { + final greaterThanIndex = msgList.indexWhere( + (m) => m.id == messagePagination.greaterThanOrEqual, + ); + if (greaterThanIndex != -1) { + msgList.removeRange(0, greaterThanIndex); + } + } + if (messagePagination?.limit != null) { + return msgList.take(messagePagination.limit).toList(); + } + return msgList; + } + + /// Updates the message data of a particular channel with + /// the new [messageList] data + Future updateMessages(String cid, List messageList) async { + if (messageList == null) { + return; + } + + return batch((batch) { + batch.insertAll( + pinnedMessages, + messageList.map((it) => it.toPinnedEntity(cid: cid)).toList(), + mode: InsertMode.insertOrReplace, + ); + }); + } +} diff --git a/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.g.dart b/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.g.dart new file mode 100644 index 000000000..e5e078509 --- /dev/null +++ b/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.g.dart @@ -0,0 +1,12 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'pinned_message_dao.dart'; + +// ************************************************************************** +// DaoGenerator +// ************************************************************************** + +mixin _$PinnedMessageDaoMixin on DatabaseAccessor { + $PinnedMessagesTable get pinnedMessages => attachedDatabase.pinnedMessages; + $UsersTable get users => attachedDatabase.users; +} diff --git a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart index 4230f2787..eae1abc69 100644 --- a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart +++ b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart @@ -25,6 +25,7 @@ LazyDatabase _openConnection( @UseMoor(tables: [ Channels, Messages, + PinnedMessages, Reactions, Users, Members, @@ -35,6 +36,7 @@ LazyDatabase _openConnection( UserDao, ChannelDao, MessageDao, + PinnedMessageDao, MemberDao, ReactionDao, ReadDao, @@ -67,7 +69,7 @@ class MoorChatDatabase extends _$MoorChatDatabase { // you should bump this number whenever you change or add a table definition. @override - int get schemaVersion => 1; + int get schemaVersion => 2; @override MigrationStrategy get migration => MigrationStrategy( diff --git a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart index 0ff6e9f55..b83b16da0 100644 --- a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart +++ b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart @@ -673,6 +673,10 @@ class MessageEntity extends DataClass implements Insertable { final DateTime updatedAt; final DateTime deletedAt; final String userId; + final bool pinned; + final DateTime pinnedAt; + final DateTime pinExpires; + final String pinnedByUserId; final String channelCid; final Map extraData; MessageEntity( @@ -694,6 +698,10 @@ class MessageEntity extends DataClass implements Insertable { this.updatedAt, this.deletedAt, this.userId, + @required this.pinned, + this.pinnedAt, + this.pinExpires, + this.pinnedByUserId, this.channelCid, this.extraData}); factory MessageEntity.fromData( @@ -739,6 +747,14 @@ class MessageEntity extends DataClass implements Insertable { .mapFromDatabaseResponse(data['${effectivePrefix}deleted_at']), userId: stringType.mapFromDatabaseResponse(data['${effectivePrefix}user_id']), + pinned: + boolType.mapFromDatabaseResponse(data['${effectivePrefix}pinned']), + pinnedAt: dateTimeType + .mapFromDatabaseResponse(data['${effectivePrefix}pinned_at']), + pinExpires: dateTimeType + .mapFromDatabaseResponse(data['${effectivePrefix}pin_expires']), + pinnedByUserId: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}pinned_by_user_id']), channelCid: stringType .mapFromDatabaseResponse(data['${effectivePrefix}channel_cid']), extraData: $MessagesTable.$converter5.mapToDart(stringType @@ -810,6 +826,18 @@ class MessageEntity extends DataClass implements Insertable { if (!nullToAbsent || userId != null) { map['user_id'] = Variable(userId); } + if (!nullToAbsent || pinned != null) { + map['pinned'] = Variable(pinned); + } + if (!nullToAbsent || pinnedAt != null) { + map['pinned_at'] = Variable(pinnedAt); + } + if (!nullToAbsent || pinExpires != null) { + map['pin_expires'] = Variable(pinExpires); + } + if (!nullToAbsent || pinnedByUserId != null) { + map['pinned_by_user_id'] = Variable(pinnedByUserId); + } if (!nullToAbsent || channelCid != null) { map['channel_cid'] = Variable(channelCid); } @@ -844,6 +872,10 @@ class MessageEntity extends DataClass implements Insertable { updatedAt: serializer.fromJson(json['updatedAt']), deletedAt: serializer.fromJson(json['deletedAt']), userId: serializer.fromJson(json['userId']), + pinned: serializer.fromJson(json['pinned']), + pinnedAt: serializer.fromJson(json['pinnedAt']), + pinExpires: serializer.fromJson(json['pinExpires']), + pinnedByUserId: serializer.fromJson(json['pinnedByUserId']), channelCid: serializer.fromJson(json['channelCid']), extraData: serializer.fromJson>(json['extraData']), ); @@ -870,6 +902,10 @@ class MessageEntity extends DataClass implements Insertable { 'updatedAt': serializer.toJson(updatedAt), 'deletedAt': serializer.toJson(deletedAt), 'userId': serializer.toJson(userId), + 'pinned': serializer.toJson(pinned), + 'pinnedAt': serializer.toJson(pinnedAt), + 'pinExpires': serializer.toJson(pinExpires), + 'pinnedByUserId': serializer.toJson(pinnedByUserId), 'channelCid': serializer.toJson(channelCid), 'extraData': serializer.toJson>(extraData), }; @@ -894,6 +930,10 @@ class MessageEntity extends DataClass implements Insertable { Value updatedAt = const Value.absent(), Value deletedAt = const Value.absent(), Value userId = const Value.absent(), + bool pinned, + Value pinnedAt = const Value.absent(), + Value pinExpires = const Value.absent(), + Value pinnedByUserId = const Value.absent(), Value channelCid = const Value.absent(), Value> extraData = const Value.absent()}) => MessageEntity( @@ -921,6 +961,11 @@ class MessageEntity extends DataClass implements Insertable { updatedAt: updatedAt.present ? updatedAt.value : this.updatedAt, deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, userId: userId.present ? userId.value : this.userId, + pinned: pinned ?? this.pinned, + pinnedAt: pinnedAt.present ? pinnedAt.value : this.pinnedAt, + pinExpires: pinExpires.present ? pinExpires.value : this.pinExpires, + pinnedByUserId: + pinnedByUserId.present ? pinnedByUserId.value : this.pinnedByUserId, channelCid: channelCid.present ? channelCid.value : this.channelCid, extraData: extraData.present ? extraData.value : this.extraData, ); @@ -945,6 +990,10 @@ class MessageEntity extends DataClass implements Insertable { ..write('updatedAt: $updatedAt, ') ..write('deletedAt: $deletedAt, ') ..write('userId: $userId, ') + ..write('pinned: $pinned, ') + ..write('pinnedAt: $pinnedAt, ') + ..write('pinExpires: $pinExpires, ') + ..write('pinnedByUserId: $pinnedByUserId, ') ..write('channelCid: $channelCid, ') ..write('extraData: $extraData') ..write(')')) @@ -993,8 +1042,8 @@ class MessageEntity extends DataClass implements Insertable { userId .hashCode, $mrjc( - channelCid.hashCode, - extraData.hashCode)))))))))))))))))))); + pinned.hashCode, + $mrjc(pinnedAt.hashCode, $mrjc(pinExpires.hashCode, $mrjc(pinnedByUserId.hashCode, $mrjc(channelCid.hashCode, extraData.hashCode)))))))))))))))))))))))); @override bool operator ==(dynamic other) => identical(this, other) || @@ -1017,6 +1066,10 @@ class MessageEntity extends DataClass implements Insertable { other.updatedAt == this.updatedAt && other.deletedAt == this.deletedAt && other.userId == this.userId && + other.pinned == this.pinned && + other.pinnedAt == this.pinnedAt && + other.pinExpires == this.pinExpires && + other.pinnedByUserId == this.pinnedByUserId && other.channelCid == this.channelCid && other.extraData == this.extraData); } @@ -1040,6 +1093,10 @@ class MessagesCompanion extends UpdateCompanion { final Value updatedAt; final Value deletedAt; final Value userId; + final Value pinned; + final Value pinnedAt; + final Value pinExpires; + final Value pinnedByUserId; final Value channelCid; final Value> extraData; const MessagesCompanion({ @@ -1061,6 +1118,10 @@ class MessagesCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), this.deletedAt = const Value.absent(), this.userId = const Value.absent(), + this.pinned = const Value.absent(), + this.pinnedAt = const Value.absent(), + this.pinExpires = const Value.absent(), + this.pinnedByUserId = const Value.absent(), this.channelCid = const Value.absent(), this.extraData = const Value.absent(), }); @@ -1083,6 +1144,10 @@ class MessagesCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), this.deletedAt = const Value.absent(), this.userId = const Value.absent(), + this.pinned = const Value.absent(), + this.pinnedAt = const Value.absent(), + this.pinExpires = const Value.absent(), + this.pinnedByUserId = const Value.absent(), this.channelCid = const Value.absent(), this.extraData = const Value.absent(), }) : id = Value(id), @@ -1106,6 +1171,10 @@ class MessagesCompanion extends UpdateCompanion { Expression updatedAt, Expression deletedAt, Expression userId, + Expression pinned, + Expression pinnedAt, + Expression pinExpires, + Expression pinnedByUserId, Expression channelCid, Expression extraData, }) { @@ -1128,6 +1197,10 @@ class MessagesCompanion extends UpdateCompanion { if (updatedAt != null) 'updated_at': updatedAt, if (deletedAt != null) 'deleted_at': deletedAt, if (userId != null) 'user_id': userId, + if (pinned != null) 'pinned': pinned, + if (pinnedAt != null) 'pinned_at': pinnedAt, + if (pinExpires != null) 'pin_expires': pinExpires, + if (pinnedByUserId != null) 'pinned_by_user_id': pinnedByUserId, if (channelCid != null) 'channel_cid': channelCid, if (extraData != null) 'extra_data': extraData, }); @@ -1152,6 +1225,10 @@ class MessagesCompanion extends UpdateCompanion { Value updatedAt, Value deletedAt, Value userId, + Value pinned, + Value pinnedAt, + Value pinExpires, + Value pinnedByUserId, Value channelCid, Value> extraData}) { return MessagesCompanion( @@ -1173,6 +1250,10 @@ class MessagesCompanion extends UpdateCompanion { updatedAt: updatedAt ?? this.updatedAt, deletedAt: deletedAt ?? this.deletedAt, userId: userId ?? this.userId, + pinned: pinned ?? this.pinned, + pinnedAt: pinnedAt ?? this.pinnedAt, + pinExpires: pinExpires ?? this.pinExpires, + pinnedByUserId: pinnedByUserId ?? this.pinnedByUserId, channelCid: channelCid ?? this.channelCid, extraData: extraData ?? this.extraData, ); @@ -1244,6 +1325,18 @@ class MessagesCompanion extends UpdateCompanion { if (userId.present) { map['user_id'] = Variable(userId.value); } + if (pinned.present) { + map['pinned'] = Variable(pinned.value); + } + if (pinnedAt.present) { + map['pinned_at'] = Variable(pinnedAt.value); + } + if (pinExpires.present) { + map['pin_expires'] = Variable(pinExpires.value); + } + if (pinnedByUserId.present) { + map['pinned_by_user_id'] = Variable(pinnedByUserId.value); + } if (channelCid.present) { map['channel_cid'] = Variable(channelCid.value); } @@ -1275,6 +1368,10 @@ class MessagesCompanion extends UpdateCompanion { ..write('updatedAt: $updatedAt, ') ..write('deletedAt: $deletedAt, ') ..write('userId: $userId, ') + ..write('pinned: $pinned, ') + ..write('pinnedAt: $pinnedAt, ') + ..write('pinExpires: $pinExpires, ') + ..write('pinnedByUserId: $pinnedByUserId, ') ..write('channelCid: $channelCid, ') ..write('extraData: $extraData') ..write(')')) @@ -1517,6 +1614,54 @@ class $MessagesTable extends Messages ); } + final VerificationMeta _pinnedMeta = const VerificationMeta('pinned'); + GeneratedBoolColumn _pinned; + @override + GeneratedBoolColumn get pinned => _pinned ??= _constructPinned(); + GeneratedBoolColumn _constructPinned() { + return GeneratedBoolColumn('pinned', $tableName, false, + defaultValue: const Constant(false)); + } + + final VerificationMeta _pinnedAtMeta = const VerificationMeta('pinnedAt'); + GeneratedDateTimeColumn _pinnedAt; + @override + GeneratedDateTimeColumn get pinnedAt => _pinnedAt ??= _constructPinnedAt(); + GeneratedDateTimeColumn _constructPinnedAt() { + return GeneratedDateTimeColumn( + 'pinned_at', + $tableName, + true, + ); + } + + final VerificationMeta _pinExpiresMeta = const VerificationMeta('pinExpires'); + GeneratedDateTimeColumn _pinExpires; + @override + GeneratedDateTimeColumn get pinExpires => + _pinExpires ??= _constructPinExpires(); + GeneratedDateTimeColumn _constructPinExpires() { + return GeneratedDateTimeColumn( + 'pin_expires', + $tableName, + true, + ); + } + + final VerificationMeta _pinnedByUserIdMeta = + const VerificationMeta('pinnedByUserId'); + GeneratedTextColumn _pinnedByUserId; + @override + GeneratedTextColumn get pinnedByUserId => + _pinnedByUserId ??= _constructPinnedByUserId(); + GeneratedTextColumn _constructPinnedByUserId() { + return GeneratedTextColumn( + 'pinned_by_user_id', + $tableName, + true, + ); + } + final VerificationMeta _channelCidMeta = const VerificationMeta('channelCid'); GeneratedTextColumn _channelCid; @override @@ -1559,6 +1704,10 @@ class $MessagesTable extends Messages updatedAt, deletedAt, userId, + pinned, + pinnedAt, + pinExpires, + pinnedByUserId, channelCid, extraData ]; @@ -1641,6 +1790,26 @@ class $MessagesTable extends Messages context.handle(_userIdMeta, userId.isAcceptableOrUnknown(data['user_id'], _userIdMeta)); } + if (data.containsKey('pinned')) { + context.handle(_pinnedMeta, + pinned.isAcceptableOrUnknown(data['pinned'], _pinnedMeta)); + } + if (data.containsKey('pinned_at')) { + context.handle(_pinnedAtMeta, + pinnedAt.isAcceptableOrUnknown(data['pinned_at'], _pinnedAtMeta)); + } + if (data.containsKey('pin_expires')) { + context.handle( + _pinExpiresMeta, + pinExpires.isAcceptableOrUnknown( + data['pin_expires'], _pinExpiresMeta)); + } + if (data.containsKey('pinned_by_user_id')) { + context.handle( + _pinnedByUserIdMeta, + pinnedByUserId.isAcceptableOrUnknown( + data['pinned_by_user_id'], _pinnedByUserIdMeta)); + } if (data.containsKey('channel_cid')) { context.handle( _channelCidMeta, @@ -1678,6 +1847,1201 @@ class $MessagesTable extends Messages MapConverter(); } +class PinnedMessageEntity extends DataClass + implements Insertable { + final String id; + final String messageText; + final List attachments; + final MessageSendingStatus status; + final String type; + final List mentionedUsers; + final Map reactionCounts; + final Map reactionScores; + final String parentId; + final String quotedMessageId; + final int replyCount; + final bool showInChannel; + final bool shadowed; + final String command; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime deletedAt; + final String userId; + final bool pinned; + final DateTime pinnedAt; + final DateTime pinExpires; + final String pinnedByUserId; + final String channelCid; + final Map extraData; + PinnedMessageEntity( + {@required this.id, + this.messageText, + this.attachments, + this.status, + this.type, + this.mentionedUsers, + this.reactionCounts, + this.reactionScores, + this.parentId, + this.quotedMessageId, + this.replyCount, + this.showInChannel, + this.shadowed, + this.command, + @required this.createdAt, + this.updatedAt, + this.deletedAt, + this.userId, + @required this.pinned, + this.pinnedAt, + this.pinExpires, + this.pinnedByUserId, + this.channelCid, + this.extraData}); + factory PinnedMessageEntity.fromData( + Map data, GeneratedDatabase db, + {String prefix}) { + final effectivePrefix = prefix ?? ''; + final stringType = db.typeSystem.forDartType(); + final intType = db.typeSystem.forDartType(); + final boolType = db.typeSystem.forDartType(); + final dateTimeType = db.typeSystem.forDartType(); + return PinnedMessageEntity( + id: stringType.mapFromDatabaseResponse(data['${effectivePrefix}id']), + messageText: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}message_text']), + attachments: $PinnedMessagesTable.$converter0.mapToDart(stringType + .mapFromDatabaseResponse(data['${effectivePrefix}attachments'])), + status: $PinnedMessagesTable.$converter1.mapToDart( + intType.mapFromDatabaseResponse(data['${effectivePrefix}status'])), + type: stringType.mapFromDatabaseResponse(data['${effectivePrefix}type']), + mentionedUsers: $PinnedMessagesTable.$converter2.mapToDart(stringType + .mapFromDatabaseResponse(data['${effectivePrefix}mentioned_users'])), + reactionCounts: $PinnedMessagesTable.$converter3.mapToDart(stringType + .mapFromDatabaseResponse(data['${effectivePrefix}reaction_counts'])), + reactionScores: $PinnedMessagesTable.$converter4.mapToDart(stringType + .mapFromDatabaseResponse(data['${effectivePrefix}reaction_scores'])), + parentId: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}parent_id']), + quotedMessageId: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}quoted_message_id']), + replyCount: intType + .mapFromDatabaseResponse(data['${effectivePrefix}reply_count']), + showInChannel: boolType + .mapFromDatabaseResponse(data['${effectivePrefix}show_in_channel']), + shadowed: + boolType.mapFromDatabaseResponse(data['${effectivePrefix}shadowed']), + command: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}command']), + createdAt: dateTimeType + .mapFromDatabaseResponse(data['${effectivePrefix}created_at']), + updatedAt: dateTimeType + .mapFromDatabaseResponse(data['${effectivePrefix}updated_at']), + deletedAt: dateTimeType + .mapFromDatabaseResponse(data['${effectivePrefix}deleted_at']), + userId: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}user_id']), + pinned: + boolType.mapFromDatabaseResponse(data['${effectivePrefix}pinned']), + pinnedAt: dateTimeType + .mapFromDatabaseResponse(data['${effectivePrefix}pinned_at']), + pinExpires: dateTimeType + .mapFromDatabaseResponse(data['${effectivePrefix}pin_expires']), + pinnedByUserId: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}pinned_by_user_id']), + channelCid: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}channel_cid']), + extraData: $PinnedMessagesTable.$converter5.mapToDart(stringType + .mapFromDatabaseResponse(data['${effectivePrefix}extra_data'])), + ); + } + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || id != null) { + map['id'] = Variable(id); + } + if (!nullToAbsent || messageText != null) { + map['message_text'] = Variable(messageText); + } + if (!nullToAbsent || attachments != null) { + final converter = $PinnedMessagesTable.$converter0; + map['attachments'] = Variable(converter.mapToSql(attachments)); + } + if (!nullToAbsent || status != null) { + final converter = $PinnedMessagesTable.$converter1; + map['status'] = Variable(converter.mapToSql(status)); + } + if (!nullToAbsent || type != null) { + map['type'] = Variable(type); + } + if (!nullToAbsent || mentionedUsers != null) { + final converter = $PinnedMessagesTable.$converter2; + map['mentioned_users'] = + Variable(converter.mapToSql(mentionedUsers)); + } + if (!nullToAbsent || reactionCounts != null) { + final converter = $PinnedMessagesTable.$converter3; + map['reaction_counts'] = + Variable(converter.mapToSql(reactionCounts)); + } + if (!nullToAbsent || reactionScores != null) { + final converter = $PinnedMessagesTable.$converter4; + map['reaction_scores'] = + Variable(converter.mapToSql(reactionScores)); + } + if (!nullToAbsent || parentId != null) { + map['parent_id'] = Variable(parentId); + } + if (!nullToAbsent || quotedMessageId != null) { + map['quoted_message_id'] = Variable(quotedMessageId); + } + if (!nullToAbsent || replyCount != null) { + map['reply_count'] = Variable(replyCount); + } + if (!nullToAbsent || showInChannel != null) { + map['show_in_channel'] = Variable(showInChannel); + } + if (!nullToAbsent || shadowed != null) { + map['shadowed'] = Variable(shadowed); + } + if (!nullToAbsent || command != null) { + map['command'] = Variable(command); + } + if (!nullToAbsent || createdAt != null) { + map['created_at'] = Variable(createdAt); + } + if (!nullToAbsent || updatedAt != null) { + map['updated_at'] = Variable(updatedAt); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || userId != null) { + map['user_id'] = Variable(userId); + } + if (!nullToAbsent || pinned != null) { + map['pinned'] = Variable(pinned); + } + if (!nullToAbsent || pinnedAt != null) { + map['pinned_at'] = Variable(pinnedAt); + } + if (!nullToAbsent || pinExpires != null) { + map['pin_expires'] = Variable(pinExpires); + } + if (!nullToAbsent || pinnedByUserId != null) { + map['pinned_by_user_id'] = Variable(pinnedByUserId); + } + if (!nullToAbsent || channelCid != null) { + map['channel_cid'] = Variable(channelCid); + } + if (!nullToAbsent || extraData != null) { + final converter = $PinnedMessagesTable.$converter5; + map['extra_data'] = Variable(converter.mapToSql(extraData)); + } + return map; + } + + factory PinnedMessageEntity.fromJson(Map json, + {ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return PinnedMessageEntity( + id: serializer.fromJson(json['id']), + messageText: serializer.fromJson(json['messageText']), + attachments: serializer.fromJson>(json['attachments']), + status: serializer.fromJson(json['status']), + type: serializer.fromJson(json['type']), + mentionedUsers: serializer.fromJson>(json['mentionedUsers']), + reactionCounts: + serializer.fromJson>(json['reactionCounts']), + reactionScores: + serializer.fromJson>(json['reactionScores']), + parentId: serializer.fromJson(json['parentId']), + quotedMessageId: serializer.fromJson(json['quotedMessageId']), + replyCount: serializer.fromJson(json['replyCount']), + showInChannel: serializer.fromJson(json['showInChannel']), + shadowed: serializer.fromJson(json['shadowed']), + command: serializer.fromJson(json['command']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + userId: serializer.fromJson(json['userId']), + pinned: serializer.fromJson(json['pinned']), + pinnedAt: serializer.fromJson(json['pinnedAt']), + pinExpires: serializer.fromJson(json['pinExpires']), + pinnedByUserId: serializer.fromJson(json['pinnedByUserId']), + channelCid: serializer.fromJson(json['channelCid']), + extraData: serializer.fromJson>(json['extraData']), + ); + } + @override + Map toJson({ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'messageText': serializer.toJson(messageText), + 'attachments': serializer.toJson>(attachments), + 'status': serializer.toJson(status), + 'type': serializer.toJson(type), + 'mentionedUsers': serializer.toJson>(mentionedUsers), + 'reactionCounts': serializer.toJson>(reactionCounts), + 'reactionScores': serializer.toJson>(reactionScores), + 'parentId': serializer.toJson(parentId), + 'quotedMessageId': serializer.toJson(quotedMessageId), + 'replyCount': serializer.toJson(replyCount), + 'showInChannel': serializer.toJson(showInChannel), + 'shadowed': serializer.toJson(shadowed), + 'command': serializer.toJson(command), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'userId': serializer.toJson(userId), + 'pinned': serializer.toJson(pinned), + 'pinnedAt': serializer.toJson(pinnedAt), + 'pinExpires': serializer.toJson(pinExpires), + 'pinnedByUserId': serializer.toJson(pinnedByUserId), + 'channelCid': serializer.toJson(channelCid), + 'extraData': serializer.toJson>(extraData), + }; + } + + PinnedMessageEntity copyWith( + {String id, + Value messageText = const Value.absent(), + Value> attachments = const Value.absent(), + Value status = const Value.absent(), + Value type = const Value.absent(), + Value> mentionedUsers = const Value.absent(), + Value> reactionCounts = const Value.absent(), + Value> reactionScores = const Value.absent(), + Value parentId = const Value.absent(), + Value quotedMessageId = const Value.absent(), + Value replyCount = const Value.absent(), + Value showInChannel = const Value.absent(), + Value shadowed = const Value.absent(), + Value command = const Value.absent(), + DateTime createdAt, + Value updatedAt = const Value.absent(), + Value deletedAt = const Value.absent(), + Value userId = const Value.absent(), + bool pinned, + Value pinnedAt = const Value.absent(), + Value pinExpires = const Value.absent(), + Value pinnedByUserId = const Value.absent(), + Value channelCid = const Value.absent(), + Value> extraData = const Value.absent()}) => + PinnedMessageEntity( + id: id ?? this.id, + messageText: messageText.present ? messageText.value : this.messageText, + attachments: attachments.present ? attachments.value : this.attachments, + status: status.present ? status.value : this.status, + type: type.present ? type.value : this.type, + mentionedUsers: + mentionedUsers.present ? mentionedUsers.value : this.mentionedUsers, + reactionCounts: + reactionCounts.present ? reactionCounts.value : this.reactionCounts, + reactionScores: + reactionScores.present ? reactionScores.value : this.reactionScores, + parentId: parentId.present ? parentId.value : this.parentId, + quotedMessageId: quotedMessageId.present + ? quotedMessageId.value + : this.quotedMessageId, + replyCount: replyCount.present ? replyCount.value : this.replyCount, + showInChannel: + showInChannel.present ? showInChannel.value : this.showInChannel, + shadowed: shadowed.present ? shadowed.value : this.shadowed, + command: command.present ? command.value : this.command, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt.present ? updatedAt.value : this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + userId: userId.present ? userId.value : this.userId, + pinned: pinned ?? this.pinned, + pinnedAt: pinnedAt.present ? pinnedAt.value : this.pinnedAt, + pinExpires: pinExpires.present ? pinExpires.value : this.pinExpires, + pinnedByUserId: + pinnedByUserId.present ? pinnedByUserId.value : this.pinnedByUserId, + channelCid: channelCid.present ? channelCid.value : this.channelCid, + extraData: extraData.present ? extraData.value : this.extraData, + ); + @override + String toString() { + return (StringBuffer('PinnedMessageEntity(') + ..write('id: $id, ') + ..write('messageText: $messageText, ') + ..write('attachments: $attachments, ') + ..write('status: $status, ') + ..write('type: $type, ') + ..write('mentionedUsers: $mentionedUsers, ') + ..write('reactionCounts: $reactionCounts, ') + ..write('reactionScores: $reactionScores, ') + ..write('parentId: $parentId, ') + ..write('quotedMessageId: $quotedMessageId, ') + ..write('replyCount: $replyCount, ') + ..write('showInChannel: $showInChannel, ') + ..write('shadowed: $shadowed, ') + ..write('command: $command, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('userId: $userId, ') + ..write('pinned: $pinned, ') + ..write('pinnedAt: $pinnedAt, ') + ..write('pinExpires: $pinExpires, ') + ..write('pinnedByUserId: $pinnedByUserId, ') + ..write('channelCid: $channelCid, ') + ..write('extraData: $extraData') + ..write(')')) + .toString(); + } + + @override + int get hashCode => $mrjf($mrjc( + id.hashCode, + $mrjc( + messageText.hashCode, + $mrjc( + attachments.hashCode, + $mrjc( + status.hashCode, + $mrjc( + type.hashCode, + $mrjc( + mentionedUsers.hashCode, + $mrjc( + reactionCounts.hashCode, + $mrjc( + reactionScores.hashCode, + $mrjc( + parentId.hashCode, + $mrjc( + quotedMessageId.hashCode, + $mrjc( + replyCount.hashCode, + $mrjc( + showInChannel.hashCode, + $mrjc( + shadowed.hashCode, + $mrjc( + command.hashCode, + $mrjc( + createdAt + .hashCode, + $mrjc( + updatedAt + .hashCode, + $mrjc( + deletedAt + .hashCode, + $mrjc( + userId + .hashCode, + $mrjc( + pinned.hashCode, + $mrjc(pinnedAt.hashCode, $mrjc(pinExpires.hashCode, $mrjc(pinnedByUserId.hashCode, $mrjc(channelCid.hashCode, extraData.hashCode)))))))))))))))))))))))); + @override + bool operator ==(dynamic other) => + identical(this, other) || + (other is PinnedMessageEntity && + other.id == this.id && + other.messageText == this.messageText && + other.attachments == this.attachments && + other.status == this.status && + other.type == this.type && + other.mentionedUsers == this.mentionedUsers && + other.reactionCounts == this.reactionCounts && + other.reactionScores == this.reactionScores && + other.parentId == this.parentId && + other.quotedMessageId == this.quotedMessageId && + other.replyCount == this.replyCount && + other.showInChannel == this.showInChannel && + other.shadowed == this.shadowed && + other.command == this.command && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.userId == this.userId && + other.pinned == this.pinned && + other.pinnedAt == this.pinnedAt && + other.pinExpires == this.pinExpires && + other.pinnedByUserId == this.pinnedByUserId && + other.channelCid == this.channelCid && + other.extraData == this.extraData); +} + +class PinnedMessagesCompanion extends UpdateCompanion { + final Value id; + final Value messageText; + final Value> attachments; + final Value status; + final Value type; + final Value> mentionedUsers; + final Value> reactionCounts; + final Value> reactionScores; + final Value parentId; + final Value quotedMessageId; + final Value replyCount; + final Value showInChannel; + final Value shadowed; + final Value command; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value userId; + final Value pinned; + final Value pinnedAt; + final Value pinExpires; + final Value pinnedByUserId; + final Value channelCid; + final Value> extraData; + const PinnedMessagesCompanion({ + this.id = const Value.absent(), + this.messageText = const Value.absent(), + this.attachments = const Value.absent(), + this.status = const Value.absent(), + this.type = const Value.absent(), + this.mentionedUsers = const Value.absent(), + this.reactionCounts = const Value.absent(), + this.reactionScores = const Value.absent(), + this.parentId = const Value.absent(), + this.quotedMessageId = const Value.absent(), + this.replyCount = const Value.absent(), + this.showInChannel = const Value.absent(), + this.shadowed = const Value.absent(), + this.command = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.userId = const Value.absent(), + this.pinned = const Value.absent(), + this.pinnedAt = const Value.absent(), + this.pinExpires = const Value.absent(), + this.pinnedByUserId = const Value.absent(), + this.channelCid = const Value.absent(), + this.extraData = const Value.absent(), + }); + PinnedMessagesCompanion.insert({ + @required String id, + this.messageText = const Value.absent(), + this.attachments = const Value.absent(), + this.status = const Value.absent(), + this.type = const Value.absent(), + this.mentionedUsers = const Value.absent(), + this.reactionCounts = const Value.absent(), + this.reactionScores = const Value.absent(), + this.parentId = const Value.absent(), + this.quotedMessageId = const Value.absent(), + this.replyCount = const Value.absent(), + this.showInChannel = const Value.absent(), + this.shadowed = const Value.absent(), + this.command = const Value.absent(), + @required DateTime createdAt, + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.userId = const Value.absent(), + this.pinned = const Value.absent(), + this.pinnedAt = const Value.absent(), + this.pinExpires = const Value.absent(), + this.pinnedByUserId = const Value.absent(), + this.channelCid = const Value.absent(), + this.extraData = const Value.absent(), + }) : id = Value(id), + createdAt = Value(createdAt); + static Insertable custom({ + Expression id, + Expression messageText, + Expression attachments, + Expression status, + Expression type, + Expression mentionedUsers, + Expression reactionCounts, + Expression reactionScores, + Expression parentId, + Expression quotedMessageId, + Expression replyCount, + Expression showInChannel, + Expression shadowed, + Expression command, + Expression createdAt, + Expression updatedAt, + Expression deletedAt, + Expression userId, + Expression pinned, + Expression pinnedAt, + Expression pinExpires, + Expression pinnedByUserId, + Expression channelCid, + Expression extraData, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (messageText != null) 'message_text': messageText, + if (attachments != null) 'attachments': attachments, + if (status != null) 'status': status, + if (type != null) 'type': type, + if (mentionedUsers != null) 'mentioned_users': mentionedUsers, + if (reactionCounts != null) 'reaction_counts': reactionCounts, + if (reactionScores != null) 'reaction_scores': reactionScores, + if (parentId != null) 'parent_id': parentId, + if (quotedMessageId != null) 'quoted_message_id': quotedMessageId, + if (replyCount != null) 'reply_count': replyCount, + if (showInChannel != null) 'show_in_channel': showInChannel, + if (shadowed != null) 'shadowed': shadowed, + if (command != null) 'command': command, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (userId != null) 'user_id': userId, + if (pinned != null) 'pinned': pinned, + if (pinnedAt != null) 'pinned_at': pinnedAt, + if (pinExpires != null) 'pin_expires': pinExpires, + if (pinnedByUserId != null) 'pinned_by_user_id': pinnedByUserId, + if (channelCid != null) 'channel_cid': channelCid, + if (extraData != null) 'extra_data': extraData, + }); + } + + PinnedMessagesCompanion copyWith( + {Value id, + Value messageText, + Value> attachments, + Value status, + Value type, + Value> mentionedUsers, + Value> reactionCounts, + Value> reactionScores, + Value parentId, + Value quotedMessageId, + Value replyCount, + Value showInChannel, + Value shadowed, + Value command, + Value createdAt, + Value updatedAt, + Value deletedAt, + Value userId, + Value pinned, + Value pinnedAt, + Value pinExpires, + Value pinnedByUserId, + Value channelCid, + Value> extraData}) { + return PinnedMessagesCompanion( + id: id ?? this.id, + messageText: messageText ?? this.messageText, + attachments: attachments ?? this.attachments, + status: status ?? this.status, + type: type ?? this.type, + mentionedUsers: mentionedUsers ?? this.mentionedUsers, + reactionCounts: reactionCounts ?? this.reactionCounts, + reactionScores: reactionScores ?? this.reactionScores, + parentId: parentId ?? this.parentId, + quotedMessageId: quotedMessageId ?? this.quotedMessageId, + replyCount: replyCount ?? this.replyCount, + showInChannel: showInChannel ?? this.showInChannel, + shadowed: shadowed ?? this.shadowed, + command: command ?? this.command, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + userId: userId ?? this.userId, + pinned: pinned ?? this.pinned, + pinnedAt: pinnedAt ?? this.pinnedAt, + pinExpires: pinExpires ?? this.pinExpires, + pinnedByUserId: pinnedByUserId ?? this.pinnedByUserId, + channelCid: channelCid ?? this.channelCid, + extraData: extraData ?? this.extraData, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (messageText.present) { + map['message_text'] = Variable(messageText.value); + } + if (attachments.present) { + final converter = $PinnedMessagesTable.$converter0; + map['attachments'] = + Variable(converter.mapToSql(attachments.value)); + } + if (status.present) { + final converter = $PinnedMessagesTable.$converter1; + map['status'] = Variable(converter.mapToSql(status.value)); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (mentionedUsers.present) { + final converter = $PinnedMessagesTable.$converter2; + map['mentioned_users'] = + Variable(converter.mapToSql(mentionedUsers.value)); + } + if (reactionCounts.present) { + final converter = $PinnedMessagesTable.$converter3; + map['reaction_counts'] = + Variable(converter.mapToSql(reactionCounts.value)); + } + if (reactionScores.present) { + final converter = $PinnedMessagesTable.$converter4; + map['reaction_scores'] = + Variable(converter.mapToSql(reactionScores.value)); + } + if (parentId.present) { + map['parent_id'] = Variable(parentId.value); + } + if (quotedMessageId.present) { + map['quoted_message_id'] = Variable(quotedMessageId.value); + } + if (replyCount.present) { + map['reply_count'] = Variable(replyCount.value); + } + if (showInChannel.present) { + map['show_in_channel'] = Variable(showInChannel.value); + } + if (shadowed.present) { + map['shadowed'] = Variable(shadowed.value); + } + if (command.present) { + map['command'] = Variable(command.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (pinned.present) { + map['pinned'] = Variable(pinned.value); + } + if (pinnedAt.present) { + map['pinned_at'] = Variable(pinnedAt.value); + } + if (pinExpires.present) { + map['pin_expires'] = Variable(pinExpires.value); + } + if (pinnedByUserId.present) { + map['pinned_by_user_id'] = Variable(pinnedByUserId.value); + } + if (channelCid.present) { + map['channel_cid'] = Variable(channelCid.value); + } + if (extraData.present) { + final converter = $PinnedMessagesTable.$converter5; + map['extra_data'] = Variable(converter.mapToSql(extraData.value)); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PinnedMessagesCompanion(') + ..write('id: $id, ') + ..write('messageText: $messageText, ') + ..write('attachments: $attachments, ') + ..write('status: $status, ') + ..write('type: $type, ') + ..write('mentionedUsers: $mentionedUsers, ') + ..write('reactionCounts: $reactionCounts, ') + ..write('reactionScores: $reactionScores, ') + ..write('parentId: $parentId, ') + ..write('quotedMessageId: $quotedMessageId, ') + ..write('replyCount: $replyCount, ') + ..write('showInChannel: $showInChannel, ') + ..write('shadowed: $shadowed, ') + ..write('command: $command, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('userId: $userId, ') + ..write('pinned: $pinned, ') + ..write('pinnedAt: $pinnedAt, ') + ..write('pinExpires: $pinExpires, ') + ..write('pinnedByUserId: $pinnedByUserId, ') + ..write('channelCid: $channelCid, ') + ..write('extraData: $extraData') + ..write(')')) + .toString(); + } +} + +class $PinnedMessagesTable extends PinnedMessages + with TableInfo<$PinnedMessagesTable, PinnedMessageEntity> { + final GeneratedDatabase _db; + final String _alias; + $PinnedMessagesTable(this._db, [this._alias]); + final VerificationMeta _idMeta = const VerificationMeta('id'); + GeneratedTextColumn _id; + @override + GeneratedTextColumn get id => _id ??= _constructId(); + GeneratedTextColumn _constructId() { + return GeneratedTextColumn( + 'id', + $tableName, + false, + ); + } + + final VerificationMeta _messageTextMeta = + const VerificationMeta('messageText'); + GeneratedTextColumn _messageText; + @override + GeneratedTextColumn get messageText => + _messageText ??= _constructMessageText(); + GeneratedTextColumn _constructMessageText() { + return GeneratedTextColumn( + 'message_text', + $tableName, + true, + ); + } + + final VerificationMeta _attachmentsMeta = + const VerificationMeta('attachments'); + GeneratedTextColumn _attachments; + @override + GeneratedTextColumn get attachments => + _attachments ??= _constructAttachments(); + GeneratedTextColumn _constructAttachments() { + return GeneratedTextColumn( + 'attachments', + $tableName, + true, + ); + } + + final VerificationMeta _statusMeta = const VerificationMeta('status'); + GeneratedIntColumn _status; + @override + GeneratedIntColumn get status => _status ??= _constructStatus(); + GeneratedIntColumn _constructStatus() { + return GeneratedIntColumn( + 'status', + $tableName, + true, + ); + } + + final VerificationMeta _typeMeta = const VerificationMeta('type'); + GeneratedTextColumn _type; + @override + GeneratedTextColumn get type => _type ??= _constructType(); + GeneratedTextColumn _constructType() { + return GeneratedTextColumn( + 'type', + $tableName, + true, + ); + } + + final VerificationMeta _mentionedUsersMeta = + const VerificationMeta('mentionedUsers'); + GeneratedTextColumn _mentionedUsers; + @override + GeneratedTextColumn get mentionedUsers => + _mentionedUsers ??= _constructMentionedUsers(); + GeneratedTextColumn _constructMentionedUsers() { + return GeneratedTextColumn( + 'mentioned_users', + $tableName, + true, + ); + } + + final VerificationMeta _reactionCountsMeta = + const VerificationMeta('reactionCounts'); + GeneratedTextColumn _reactionCounts; + @override + GeneratedTextColumn get reactionCounts => + _reactionCounts ??= _constructReactionCounts(); + GeneratedTextColumn _constructReactionCounts() { + return GeneratedTextColumn( + 'reaction_counts', + $tableName, + true, + ); + } + + final VerificationMeta _reactionScoresMeta = + const VerificationMeta('reactionScores'); + GeneratedTextColumn _reactionScores; + @override + GeneratedTextColumn get reactionScores => + _reactionScores ??= _constructReactionScores(); + GeneratedTextColumn _constructReactionScores() { + return GeneratedTextColumn( + 'reaction_scores', + $tableName, + true, + ); + } + + final VerificationMeta _parentIdMeta = const VerificationMeta('parentId'); + GeneratedTextColumn _parentId; + @override + GeneratedTextColumn get parentId => _parentId ??= _constructParentId(); + GeneratedTextColumn _constructParentId() { + return GeneratedTextColumn( + 'parent_id', + $tableName, + true, + ); + } + + final VerificationMeta _quotedMessageIdMeta = + const VerificationMeta('quotedMessageId'); + GeneratedTextColumn _quotedMessageId; + @override + GeneratedTextColumn get quotedMessageId => + _quotedMessageId ??= _constructQuotedMessageId(); + GeneratedTextColumn _constructQuotedMessageId() { + return GeneratedTextColumn( + 'quoted_message_id', + $tableName, + true, + ); + } + + final VerificationMeta _replyCountMeta = const VerificationMeta('replyCount'); + GeneratedIntColumn _replyCount; + @override + GeneratedIntColumn get replyCount => _replyCount ??= _constructReplyCount(); + GeneratedIntColumn _constructReplyCount() { + return GeneratedIntColumn( + 'reply_count', + $tableName, + true, + ); + } + + final VerificationMeta _showInChannelMeta = + const VerificationMeta('showInChannel'); + GeneratedBoolColumn _showInChannel; + @override + GeneratedBoolColumn get showInChannel => + _showInChannel ??= _constructShowInChannel(); + GeneratedBoolColumn _constructShowInChannel() { + return GeneratedBoolColumn( + 'show_in_channel', + $tableName, + true, + ); + } + + final VerificationMeta _shadowedMeta = const VerificationMeta('shadowed'); + GeneratedBoolColumn _shadowed; + @override + GeneratedBoolColumn get shadowed => _shadowed ??= _constructShadowed(); + GeneratedBoolColumn _constructShadowed() { + return GeneratedBoolColumn( + 'shadowed', + $tableName, + true, + ); + } + + final VerificationMeta _commandMeta = const VerificationMeta('command'); + GeneratedTextColumn _command; + @override + GeneratedTextColumn get command => _command ??= _constructCommand(); + GeneratedTextColumn _constructCommand() { + return GeneratedTextColumn( + 'command', + $tableName, + true, + ); + } + + final VerificationMeta _createdAtMeta = const VerificationMeta('createdAt'); + GeneratedDateTimeColumn _createdAt; + @override + GeneratedDateTimeColumn get createdAt => _createdAt ??= _constructCreatedAt(); + GeneratedDateTimeColumn _constructCreatedAt() { + return GeneratedDateTimeColumn( + 'created_at', + $tableName, + false, + ); + } + + final VerificationMeta _updatedAtMeta = const VerificationMeta('updatedAt'); + GeneratedDateTimeColumn _updatedAt; + @override + GeneratedDateTimeColumn get updatedAt => _updatedAt ??= _constructUpdatedAt(); + GeneratedDateTimeColumn _constructUpdatedAt() { + return GeneratedDateTimeColumn( + 'updated_at', + $tableName, + true, + ); + } + + final VerificationMeta _deletedAtMeta = const VerificationMeta('deletedAt'); + GeneratedDateTimeColumn _deletedAt; + @override + GeneratedDateTimeColumn get deletedAt => _deletedAt ??= _constructDeletedAt(); + GeneratedDateTimeColumn _constructDeletedAt() { + return GeneratedDateTimeColumn( + 'deleted_at', + $tableName, + true, + ); + } + + final VerificationMeta _userIdMeta = const VerificationMeta('userId'); + GeneratedTextColumn _userId; + @override + GeneratedTextColumn get userId => _userId ??= _constructUserId(); + GeneratedTextColumn _constructUserId() { + return GeneratedTextColumn( + 'user_id', + $tableName, + true, + ); + } + + final VerificationMeta _pinnedMeta = const VerificationMeta('pinned'); + GeneratedBoolColumn _pinned; + @override + GeneratedBoolColumn get pinned => _pinned ??= _constructPinned(); + GeneratedBoolColumn _constructPinned() { + return GeneratedBoolColumn('pinned', $tableName, false, + defaultValue: const Constant(false)); + } + + final VerificationMeta _pinnedAtMeta = const VerificationMeta('pinnedAt'); + GeneratedDateTimeColumn _pinnedAt; + @override + GeneratedDateTimeColumn get pinnedAt => _pinnedAt ??= _constructPinnedAt(); + GeneratedDateTimeColumn _constructPinnedAt() { + return GeneratedDateTimeColumn( + 'pinned_at', + $tableName, + true, + ); + } + + final VerificationMeta _pinExpiresMeta = const VerificationMeta('pinExpires'); + GeneratedDateTimeColumn _pinExpires; + @override + GeneratedDateTimeColumn get pinExpires => + _pinExpires ??= _constructPinExpires(); + GeneratedDateTimeColumn _constructPinExpires() { + return GeneratedDateTimeColumn( + 'pin_expires', + $tableName, + true, + ); + } + + final VerificationMeta _pinnedByUserIdMeta = + const VerificationMeta('pinnedByUserId'); + GeneratedTextColumn _pinnedByUserId; + @override + GeneratedTextColumn get pinnedByUserId => + _pinnedByUserId ??= _constructPinnedByUserId(); + GeneratedTextColumn _constructPinnedByUserId() { + return GeneratedTextColumn( + 'pinned_by_user_id', + $tableName, + true, + ); + } + + final VerificationMeta _channelCidMeta = const VerificationMeta('channelCid'); + GeneratedTextColumn _channelCid; + @override + GeneratedTextColumn get channelCid => _channelCid ??= _constructChannelCid(); + GeneratedTextColumn _constructChannelCid() { + return GeneratedTextColumn('channel_cid', $tableName, true, + $customConstraints: + 'NULLABLE REFERENCES channels(cid) ON DELETE CASCADE'); + } + + final VerificationMeta _extraDataMeta = const VerificationMeta('extraData'); + GeneratedTextColumn _extraData; + @override + GeneratedTextColumn get extraData => _extraData ??= _constructExtraData(); + GeneratedTextColumn _constructExtraData() { + return GeneratedTextColumn( + 'extra_data', + $tableName, + true, + ); + } + + @override + List get $columns => [ + id, + messageText, + attachments, + status, + type, + mentionedUsers, + reactionCounts, + reactionScores, + parentId, + quotedMessageId, + replyCount, + showInChannel, + shadowed, + command, + createdAt, + updatedAt, + deletedAt, + userId, + pinned, + pinnedAt, + pinExpires, + pinnedByUserId, + channelCid, + extraData + ]; + @override + $PinnedMessagesTable get asDslTable => this; + @override + String get $tableName => _alias ?? 'pinned_messages'; + @override + final String actualTableName = 'pinned_messages'; + @override + VerificationContext validateIntegrity( + Insertable 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('message_text')) { + context.handle( + _messageTextMeta, + messageText.isAcceptableOrUnknown( + data['message_text'], _messageTextMeta)); + } + context.handle(_attachmentsMeta, const VerificationResult.success()); + context.handle(_statusMeta, const VerificationResult.success()); + if (data.containsKey('type')) { + context.handle( + _typeMeta, type.isAcceptableOrUnknown(data['type'], _typeMeta)); + } + context.handle(_mentionedUsersMeta, const VerificationResult.success()); + context.handle(_reactionCountsMeta, const VerificationResult.success()); + context.handle(_reactionScoresMeta, const VerificationResult.success()); + if (data.containsKey('parent_id')) { + context.handle(_parentIdMeta, + parentId.isAcceptableOrUnknown(data['parent_id'], _parentIdMeta)); + } + if (data.containsKey('quoted_message_id')) { + context.handle( + _quotedMessageIdMeta, + quotedMessageId.isAcceptableOrUnknown( + data['quoted_message_id'], _quotedMessageIdMeta)); + } + if (data.containsKey('reply_count')) { + context.handle( + _replyCountMeta, + replyCount.isAcceptableOrUnknown( + data['reply_count'], _replyCountMeta)); + } + if (data.containsKey('show_in_channel')) { + context.handle( + _showInChannelMeta, + showInChannel.isAcceptableOrUnknown( + data['show_in_channel'], _showInChannelMeta)); + } + if (data.containsKey('shadowed')) { + context.handle(_shadowedMeta, + shadowed.isAcceptableOrUnknown(data['shadowed'], _shadowedMeta)); + } + if (data.containsKey('command')) { + context.handle(_commandMeta, + command.isAcceptableOrUnknown(data['command'], _commandMeta)); + } + if (data.containsKey('created_at')) { + context.handle(_createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at'], _createdAtMeta)); + } else if (isInserting) { + context.missing(_createdAtMeta); + } + if (data.containsKey('updated_at')) { + context.handle(_updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at'], _updatedAtMeta)); + } + if (data.containsKey('deleted_at')) { + context.handle(_deletedAtMeta, + deletedAt.isAcceptableOrUnknown(data['deleted_at'], _deletedAtMeta)); + } + if (data.containsKey('user_id')) { + context.handle(_userIdMeta, + userId.isAcceptableOrUnknown(data['user_id'], _userIdMeta)); + } + if (data.containsKey('pinned')) { + context.handle(_pinnedMeta, + pinned.isAcceptableOrUnknown(data['pinned'], _pinnedMeta)); + } + if (data.containsKey('pinned_at')) { + context.handle(_pinnedAtMeta, + pinnedAt.isAcceptableOrUnknown(data['pinned_at'], _pinnedAtMeta)); + } + if (data.containsKey('pin_expires')) { + context.handle( + _pinExpiresMeta, + pinExpires.isAcceptableOrUnknown( + data['pin_expires'], _pinExpiresMeta)); + } + if (data.containsKey('pinned_by_user_id')) { + context.handle( + _pinnedByUserIdMeta, + pinnedByUserId.isAcceptableOrUnknown( + data['pinned_by_user_id'], _pinnedByUserIdMeta)); + } + if (data.containsKey('channel_cid')) { + context.handle( + _channelCidMeta, + channelCid.isAcceptableOrUnknown( + data['channel_cid'], _channelCidMeta)); + } + context.handle(_extraDataMeta, const VerificationResult.success()); + return context; + } + + @override + Set get $primaryKey => {id}; + @override + PinnedMessageEntity map(Map data, {String tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; + return PinnedMessageEntity.fromData(data, _db, prefix: effectivePrefix); + } + + @override + $PinnedMessagesTable createAlias(String alias) { + return $PinnedMessagesTable(_db, alias); + } + + static TypeConverter, String> $converter0 = + ListConverter(); + static TypeConverter $converter1 = + MessageSendingStatusConverter(); + static TypeConverter, String> $converter2 = + ListConverter(); + static TypeConverter, String> $converter3 = + MapConverter(); + static TypeConverter, String> $converter4 = + MapConverter(); + static TypeConverter, String> $converter5 = + MapConverter(); +} + class ReactionEntity extends DataClass implements Insertable { final String userId; final String messageId; @@ -3979,6 +5343,9 @@ abstract class _$MoorChatDatabase extends GeneratedDatabase { $ChannelsTable get channels => _channels ??= $ChannelsTable(this); $MessagesTable _messages; $MessagesTable get messages => _messages ??= $MessagesTable(this); + $PinnedMessagesTable _pinnedMessages; + $PinnedMessagesTable get pinnedMessages => + _pinnedMessages ??= $PinnedMessagesTable(this); $ReactionsTable _reactions; $ReactionsTable get reactions => _reactions ??= $ReactionsTable(this); $UsersTable _users; @@ -4001,6 +5368,9 @@ abstract class _$MoorChatDatabase extends GeneratedDatabase { MessageDao _messageDao; MessageDao get messageDao => _messageDao ??= MessageDao(this as MoorChatDatabase); + PinnedMessageDao _pinnedMessageDao; + PinnedMessageDao get pinnedMessageDao => + _pinnedMessageDao ??= PinnedMessageDao(this as MoorChatDatabase); MemberDao _memberDao; MemberDao get memberDao => _memberDao ??= MemberDao(this as MoorChatDatabase); ReactionDao _reactionDao; @@ -4020,6 +5390,7 @@ abstract class _$MoorChatDatabase extends GeneratedDatabase { List get allSchemaEntities => [ channels, messages, + pinnedMessages, reactions, users, members, diff --git a/packages/stream_chat_persistence/lib/src/entity/entity.dart b/packages/stream_chat_persistence/lib/src/entity/entity.dart index 8751f538d..ca9d04cb4 100644 --- a/packages/stream_chat_persistence/lib/src/entity/entity.dart +++ b/packages/stream_chat_persistence/lib/src/entity/entity.dart @@ -1,5 +1,6 @@ export 'channels.dart'; export 'messages.dart'; +export 'pinned_messages.dart'; export 'reactions.dart'; export 'users.dart'; export 'members.dart'; diff --git a/packages/stream_chat_persistence/lib/src/entity/messages.dart b/packages/stream_chat_persistence/lib/src/entity/messages.dart index 8a151ae69..0350d9de6 100644 --- a/packages/stream_chat_persistence/lib/src/entity/messages.dart +++ b/packages/stream_chat_persistence/lib/src/entity/messages.dart @@ -64,6 +64,18 @@ class Messages extends Table { /// Id of the User who sent the message TextColumn get userId => text().nullable()(); + /// Whether the message is pinned or not + BoolColumn get pinned => boolean().withDefault(const Constant(false))(); + + /// The DateTime at which the message was pinned + DateTimeColumn get pinnedAt => dateTime().nullable()(); + + /// The DateTime on which the message pin expires + DateTimeColumn get pinExpires => dateTime().nullable()(); + + /// Id of the User who pinned the message + TextColumn get pinnedByUserId => text().nullable()(); + /// The channel cid of which this message is part of TextColumn get channelCid => text().nullable().customConstraint( 'NULLABLE REFERENCES channels(cid) ON DELETE CASCADE')(); diff --git a/packages/stream_chat_persistence/lib/src/entity/pinned_messages.dart b/packages/stream_chat_persistence/lib/src/entity/pinned_messages.dart new file mode 100644 index 000000000..f6956f54e --- /dev/null +++ b/packages/stream_chat_persistence/lib/src/entity/pinned_messages.dart @@ -0,0 +1,7 @@ +import 'package:moor/moor.dart'; + +import 'messages.dart'; + +/// Represents a [PinnedMessages] table in [MoorChatDatabase]. +@DataClassName('PinnedMessageEntity') +class PinnedMessages extends Messages {} diff --git a/packages/stream_chat_persistence/lib/src/mapper/channel_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/channel_mapper.dart index 7d0e152f7..3e2520fb6 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/channel_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/channel_mapper.dart @@ -28,11 +28,13 @@ extension ChannelEntityX on ChannelEntity { List members, List reads, List messages, + List pinnedMessages, }) { return ChannelState( members: members, read: reads, messages: messages, + pinnedMessages: pinnedMessages, channel: toChannelModel(createdBy: createdBy), ); } diff --git a/packages/stream_chat_persistence/lib/src/mapper/mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/mapper.dart index 8d19729db..cdc31e057 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/mapper.dart @@ -5,3 +5,4 @@ export 'event_mapper.dart'; export 'member_mapper.dart'; export 'read_mapper.dart'; export 'message_mapper.dart'; +export 'pinned_message_mapper.dart'; diff --git a/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart index c8bda4e64..5acf1a84f 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart @@ -8,6 +8,7 @@ extension MessageEntityX on MessageEntity { /// Maps a [MessageEntity] into [Message] Message toMessage({ User user, + User pinnedBy, List latestReactions, List ownReactions, Message quotedMessage, @@ -37,6 +38,10 @@ extension MessageEntityX on MessageEntity { text: messageText, user: user, deletedAt: deletedAt, + pinned: pinned, + pinnedAt: pinnedAt, + pinExpires: pinExpires, + pinnedBy: pinnedBy, ); } } @@ -67,6 +72,10 @@ extension MessageX on Message { userId: user?.id, deletedAt: deletedAt, messageText: text, + pinned: pinned, + pinnedAt: pinnedAt, + pinExpires: pinExpires, + pinnedByUserId: pinnedBy?.id, ); } } diff --git a/packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart new file mode 100644 index 000000000..1abe896fe --- /dev/null +++ b/packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart @@ -0,0 +1,81 @@ +import 'dart:convert'; + +import 'package:stream_chat/stream_chat.dart'; +import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; + +/// Useful mapping functions for [PinnedMessageEntity] +extension PinnedMessageEntityX on PinnedMessageEntity { + /// Maps a [PinnedMessageEntity] into [Message] + Message toMessage({ + User user, + User pinnedBy, + List latestReactions, + List ownReactions, + Message quotedMessage, + }) { + return Message( + shadowed: shadowed, + latestReactions: latestReactions, + ownReactions: ownReactions, + attachments: attachments?.map((it) { + final json = jsonDecode(it); + return Attachment.fromData(json); + })?.toList(), + createdAt: createdAt, + extraData: extraData, + updatedAt: updatedAt, + id: id, + type: type, + status: status, + command: command, + parentId: parentId, + quotedMessageId: quotedMessageId, + quotedMessage: quotedMessage, + reactionCounts: reactionCounts, + reactionScores: reactionScores, + replyCount: replyCount, + showInChannel: showInChannel, + text: messageText, + user: user, + deletedAt: deletedAt, + pinned: pinned, + pinnedAt: pinnedAt, + pinExpires: pinExpires, + pinnedBy: pinnedBy, + ); + } +} + +/// Useful mapping functions for [Message] +extension PMessageX on Message { + /// Maps a [Message] into [PinnedMessageEntity] + PinnedMessageEntity toPinnedEntity({String cid}) { + return PinnedMessageEntity( + id: id, + attachments: attachments?.map((it) { + return jsonEncode(it.toData()); + })?.toList(), + channelCid: cid, + type: type, + parentId: parentId, + quotedMessageId: quotedMessageId, + command: command, + createdAt: createdAt, + shadowed: shadowed, + showInChannel: showInChannel, + replyCount: replyCount, + reactionScores: reactionScores, + reactionCounts: reactionCounts, + status: status, + updatedAt: updatedAt, + extraData: extraData, + userId: user?.id, + deletedAt: deletedAt, + messageText: text, + pinned: pinned, + pinnedAt: pinnedAt, + pinExpires: pinExpires, + pinnedByUserId: pinnedBy?.id, + ); + } +} diff --git a/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart b/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart index 0962c9ac2..50d171111 100644 --- a/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart +++ b/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart @@ -81,11 +81,21 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { return _db.messageDao.deleteMessageByIds(messageIds); } + @override + Future deletePinnedMessageByIds(List messageIds) { + return _db.pinnedMessageDao.deleteMessageByIds(messageIds); + } + @override Future deleteMessageByCids(List cids) { return _db.messageDao.deleteMessageByCids(cids); } + @override + Future deletePinnedMessageByCids(List cids) { + return _db.pinnedMessageDao.deleteMessageByCids(cids); + } + @override Future> getMembersByCid(String cid) { return _db.memberDao.getMembersByCid(cid); @@ -107,6 +117,17 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { ); } + @override + Future> getPinnedMessagesByCid( + String cid, { + PaginationParams messagePagination, + }) { + return _db.pinnedMessageDao.getMessagesByCid( + cid, + messagePagination: messagePagination, + ); + } + @override Future> getReadsByCid(String cid) { return _db.readDao.getReadsByCid(cid); @@ -178,6 +199,11 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { return _db.messageDao.updateMessages(cid, messages); } + @override + Future updatePinnedMessages(String cid, List messages) { + return _db.pinnedMessageDao.updateMessages(cid, messages); + } + @override Future updateReactions(List reactions) { return _db.reactionDao.updateReactions(reactions); diff --git a/packages/stream_chat_persistence/pubspec.yaml b/packages/stream_chat_persistence/pubspec.yaml index 0517b02ed..9551507e3 100644 --- a/packages/stream_chat_persistence/pubspec.yaml +++ b/packages/stream_chat_persistence/pubspec.yaml @@ -13,7 +13,8 @@ dependencies: path: ^1.7.0 path_provider: ^1.6.27 sqlite3_flutter_libs: ^0.4.0+1 - stream_chat: ^1.2.0-beta + stream_chat: + path: ../stream_chat dev_dependencies: test: ^1.15.7 From 6c501d875ba248c04b8a6d07fe998e6136fa5afb Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Mon, 22 Feb 2021 19:46:30 +0530 Subject: [PATCH 02/41] [LLC -> Channel] Expose channel pinned messages in the form of getter and a stream Signed-off-by: Sahil Kumar --- packages/stream_chat/lib/src/api/channel.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/api/channel.dart index e7a389227..03be0abf1 100644 --- a/packages/stream_chat/lib/src/api/channel.dart +++ b/packages/stream_chat/lib/src/api/channel.dart @@ -1502,6 +1502,13 @@ class ChannelClientState { Stream> get messagesStream => channelStateStream.map((cs) => cs.messages); + /// Channel pinned message list + List get pinnedMessages => _channelState.pinnedMessages; + + /// Channel pinned message list as a stream + Stream> get pinnedMessagesStream => + channelStateStream.map((cs) => cs.pinnedMessages); + /// Get channel last message Message get lastMessage => _channelState.messages?.isNotEmpty == true ? _channelState.messages.last From 750c5305f46e9af977aab687bd889d1bf0449eb4 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 23 Feb 2021 13:19:31 +0100 Subject: [PATCH 03/41] update pinned messages --- packages/stream_chat/lib/src/api/channel.dart | 40 +- .../ios/Runner.xcodeproj/project.pbxproj | 563 ++++++++++++++++++ .../contents.xcworkspacedata | 3 + .../example/pubspec.yaml | 3 +- 4 files changed, 603 insertions(+), 6 deletions(-) create mode 100644 packages/stream_chat_persistence/example/ios/Runner.xcodeproj/project.pbxproj diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/api/channel.dart index 03be0abf1..76611438c 100644 --- a/packages/stream_chat/lib/src/api/channel.dart +++ b/packages/stream_chat/lib/src/api/channel.dart @@ -41,6 +41,7 @@ class Channel { state = ChannelClientState(this, channelState); _initializedCompleter.complete(true); _startCleaning(); + _startCleaningPinnedMessages(); _client.logger.info('New Channel instance initialized created'); } @@ -845,6 +846,7 @@ class Channel { _initializedCompleter.complete(true); } _startCleaning(); + _startCleaningPinnedMessages(); } /// Stop watching the channel @@ -1185,13 +1187,12 @@ class Channel { } Timer _cleaningTimer; - void _startCleaning() { if (config?.typingEvents == false) { return; } - _cleaningTimer = Timer.periodic(Duration(milliseconds: 500), (_) { + _cleaningTimer = Timer.periodic(Duration(seconds: 1), (_) { final now = DateTime.now(); if (_lastTypingEvent != null && @@ -1203,8 +1204,22 @@ class Channel { }); } + Timer _pinnedMessagesTimer; + void _startCleaningPinnedMessages() { + _pinnedMessagesTimer = Timer.periodic(Duration(seconds: 30), (_) { + final now = DateTime.now(); + final messageExpired = state.channelState.pinnedMessages + .any((m) => m.pinExpires.isBefore(now)); + if (messageExpired) { + state._channelState = + state._channelState.copyWith(pinnedMessages: state.pinnedMessages); + } + }); + } + /// Call this method to dispose the channel client void dispose() { + _pinnedMessagesTimer.cancel(); _cleaningTimer.cancel(); state.dispose(); } @@ -1413,6 +1428,15 @@ class ChannelClientState { ..removeWhere((it) => it.userId != userId), ); addMessage(message); + + if (message.pinned == true) { + _channelState = _channelState.copyWith( + pinnedMessages: [ + ..._channelState.pinnedMessages ?? [], + message, + ], + ); + } })); } @@ -1503,11 +1527,12 @@ class ChannelClientState { channelStateStream.map((cs) => cs.messages); /// Channel pinned message list - List get pinnedMessages => _channelState.pinnedMessages; + List get pinnedMessages => + _channelState.pinnedMessages?.where(_pinIsValid())?.toList(); /// Channel pinned message list as a stream - Stream> get pinnedMessagesStream => - channelStateStream.map((cs) => cs.pinnedMessages); + Stream> get pinnedMessagesStream => channelStateStream + .map((cs) => cs.pinnedMessages?.where(_pinIsValid())?.toList()); /// Get channel last message Message get lastMessage => _channelState.messages?.isNotEmpty == true @@ -1769,3 +1794,8 @@ class ChannelClientState { _typingEventsController.close(); } } + +bool Function(Message) _pinIsValid() { + final now = DateTime.now(); + return (Message m) => m.pinExpires.isAfter(now); +} diff --git a/packages/stream_chat_persistence/example/ios/Runner.xcodeproj/project.pbxproj b/packages/stream_chat_persistence/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..261aa5a82 --- /dev/null +++ b/packages/stream_chat_persistence/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,563 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 0E9B23A7BA08E142FA5000CF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D76F8024ABE1070895D659BA /* Pods_Runner.framework */; }; + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 3F6A054EEDAF06BCF649C130 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 6FE1ECA061EBB001F6BCE8B7 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D76F8024ABE1070895D659BA /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + EC2A45E9198C1011BED23834 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0E9B23A7BA08E142FA5000CF /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 04AAB960E493BD92262BBF82 /* Frameworks */ = { + isa = PBXGroup; + children = ( + D76F8024ABE1070895D659BA /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 8559384DCD98ED6067CEF8CB /* Pods */ = { + isa = PBXGroup; + children = ( + 3F6A054EEDAF06BCF649C130 /* Pods-Runner.debug.xcconfig */, + EC2A45E9198C1011BED23834 /* Pods-Runner.release.xcconfig */, + 6FE1ECA061EBB001F6BCE8B7 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 8559384DCD98ED6067CEF8CB /* Pods */, + 04AAB960E493BD92262BBF82 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + C1EE41B94EADE099F7AF3A1C /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + BD38DACC9A0AD429D9ED9939 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + BD38DACC9A0AD429D9ED9939 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + C1EE41B94EADE099F7AF3A1C /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/stream_chat_persistence/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/stream_chat_persistence/example/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16e..21a3cc14c 100644 --- a/packages/stream_chat_persistence/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/packages/stream_chat_persistence/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/packages/stream_chat_persistence/example/pubspec.yaml b/packages/stream_chat_persistence/example/pubspec.yaml index 4298e23a5..5008566b0 100644 --- a/packages/stream_chat_persistence/example/pubspec.yaml +++ b/packages/stream_chat_persistence/example/pubspec.yaml @@ -11,7 +11,8 @@ dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.0 - stream_chat: ^1.1.0-beta + stream_chat: + path: ../../stream_chat stream_chat_persistence: path: ../ From b66a16c7f113ae79bffda30a25aa3a5e72534f3b Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 23 Feb 2021 13:37:13 +0100 Subject: [PATCH 04/41] clear expired pinned messages --- packages/stream_chat/lib/src/api/channel.dart | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/api/channel.dart index 76611438c..024f87243 100644 --- a/packages/stream_chat/lib/src/api/channel.dart +++ b/packages/stream_chat/lib/src/api/channel.dart @@ -1208,11 +1208,21 @@ class Channel { void _startCleaningPinnedMessages() { _pinnedMessagesTimer = Timer.periodic(Duration(seconds: 30), (_) { final now = DateTime.now(); - final messageExpired = state.channelState.pinnedMessages - .any((m) => m.pinExpires.isBefore(now)); - if (messageExpired) { - state._channelState = - state._channelState.copyWith(pinnedMessages: state.pinnedMessages); + final expiredMessages = state.channelState.pinnedMessages + ?.where((m) => m.pinExpires?.isBefore(now) == true) + ?.toList() ?? + []; + if (expiredMessages.isNotEmpty) { + expiredMessages.forEach((m) => state.addMessage(m.copyWith( + pinExpires: null, + pinned: false, + pinnedAt: null, + pinnedBy: null, + ))); + + state._channelState = state._channelState.copyWith( + pinnedMessages: state.pinnedMessages.where(_pinIsValid()).toList(), + ); } }); } @@ -1527,12 +1537,11 @@ class ChannelClientState { channelStateStream.map((cs) => cs.messages); /// Channel pinned message list - List get pinnedMessages => - _channelState.pinnedMessages?.where(_pinIsValid())?.toList(); + List get pinnedMessages => _channelState.pinnedMessages?.toList(); /// Channel pinned message list as a stream - Stream> get pinnedMessagesStream => channelStateStream - .map((cs) => cs.pinnedMessages?.where(_pinIsValid())?.toList()); + Stream> get pinnedMessagesStream => + channelStateStream.map((cs) => cs.pinnedMessages?.toList()); /// Get channel last message Message get lastMessage => _channelState.messages?.isNotEmpty == true From e0e25e7caf6085985003fcbe325d72189b3c8afe Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 23 Feb 2021 17:42:52 +0100 Subject: [PATCH 05/41] move cleaning logic to channel state --- packages/stream_chat/lib/src/api/channel.dart | 95 +++++++++---------- 1 file changed, 47 insertions(+), 48 deletions(-) diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/api/channel.dart index 024f87243..758d03172 100644 --- a/packages/stream_chat/lib/src/api/channel.dart +++ b/packages/stream_chat/lib/src/api/channel.dart @@ -40,9 +40,6 @@ class Channel { state = ChannelClientState(this, channelState); _initializedCompleter.complete(true); - _startCleaning(); - _startCleaningPinnedMessages(); - _client.logger.info('New Channel instance initialized created'); } @@ -845,8 +842,6 @@ class Channel { if (!_initializedCompleter.isCompleted) { _initializedCompleter.complete(true); } - _startCleaning(); - _startCleaningPinnedMessages(); } /// Stop watching the channel @@ -1186,51 +1181,8 @@ class Channel { )); } - Timer _cleaningTimer; - void _startCleaning() { - if (config?.typingEvents == false) { - return; - } - - _cleaningTimer = Timer.periodic(Duration(seconds: 1), (_) { - final now = DateTime.now(); - - if (_lastTypingEvent != null && - now.difference(_lastTypingEvent).inSeconds > 1) { - stopTyping(); - } - - state._clean(); - }); - } - - Timer _pinnedMessagesTimer; - void _startCleaningPinnedMessages() { - _pinnedMessagesTimer = Timer.periodic(Duration(seconds: 30), (_) { - final now = DateTime.now(); - final expiredMessages = state.channelState.pinnedMessages - ?.where((m) => m.pinExpires?.isBefore(now) == true) - ?.toList() ?? - []; - if (expiredMessages.isNotEmpty) { - expiredMessages.forEach((m) => state.addMessage(m.copyWith( - pinExpires: null, - pinned: false, - pinnedAt: null, - pinnedBy: null, - ))); - - state._channelState = state._channelState.copyWith( - pinnedMessages: state.pinnedMessages.where(_pinIsValid()).toList(), - ); - } - }); - } - /// Call this method to dispose the channel client void dispose() { - _pinnedMessagesTimer.cancel(); - _cleaningTimer.cancel(); state.dispose(); } @@ -1281,6 +1233,10 @@ class ChannelClientState { _computeInitialUnread(); + _startCleaning(); + + _startCleaningPinnedMessages(); + _channel._client.chatPersistenceClient ?.getChannelThreads(_channel.cid) ?.then((threads) { @@ -1777,6 +1733,47 @@ class ChannelClientState { })); } + Timer _cleaningTimer; + void _startCleaning() { + if (_channel.config?.typingEvents == false) { + return; + } + + _cleaningTimer = Timer.periodic(Duration(seconds: 1), (_) { + final now = DateTime.now(); + + if (_channel._lastTypingEvent != null && + now.difference(_channel._lastTypingEvent).inSeconds > 1) { + _channel.stopTyping(); + } + + _clean(); + }); + } + + Timer _pinnedMessagesTimer; + void _startCleaningPinnedMessages() { + _pinnedMessagesTimer = Timer.periodic(Duration(seconds: 30), (_) { + final now = DateTime.now(); + final expiredMessages = channelState.pinnedMessages + ?.where((m) => m.pinExpires?.isBefore(now) == true) + ?.toList() ?? + []; + if (expiredMessages.isNotEmpty) { + expiredMessages.forEach((m) => addMessage(m.copyWith( + pinExpires: null, + pinned: false, + pinnedAt: null, + pinnedBy: null, + ))); + + _channelState = _channelState.copyWith( + pinnedMessages: pinnedMessages.where(_pinIsValid()).toList(), + ); + } + }); + } + void _clean() { final now = DateTime.now(); _typings.forEach((user, lastTypingEvent) { @@ -1800,6 +1797,8 @@ class ChannelClientState { _channelStateController.close(); _isUpToDateController.close(); _threadsController.close(); + _cleaningTimer.cancel(); + _pinnedMessagesTimer.cancel(); _typingEventsController.close(); } } From 36d0c74a0da2b3c6fbde35e63c9c865daf79d8d0 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 24 Feb 2021 10:17:33 +0100 Subject: [PATCH 06/41] use updatechannelstate --- packages/stream_chat/lib/src/api/channel.dart | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/api/channel.dart index 758d03172..a72312350 100644 --- a/packages/stream_chat/lib/src/api/channel.dart +++ b/packages/stream_chat/lib/src/api/channel.dart @@ -1655,6 +1655,7 @@ class ChannelClientState { watcherCount: updatedState.watcherCount, members: newMembers, read: newReads, + pinnedMessages: updatedState.pinnedMessages, ); } @@ -1755,21 +1756,24 @@ class ChannelClientState { void _startCleaningPinnedMessages() { _pinnedMessagesTimer = Timer.periodic(Duration(seconds: 30), (_) { final now = DateTime.now(); - final expiredMessages = channelState.pinnedMessages + var expiredMessages = channelState.pinnedMessages ?.where((m) => m.pinExpires?.isBefore(now) == true) ?.toList() ?? []; if (expiredMessages.isNotEmpty) { - expiredMessages.forEach((m) => addMessage(m.copyWith( - pinExpires: null, - pinned: false, - pinnedAt: null, - pinnedBy: null, - ))); + expiredMessages = expiredMessages + .map((m) => m.copyWith( + pinExpires: null, + pinned: false, + pinnedAt: null, + pinnedBy: null, + )) + .toList(); - _channelState = _channelState.copyWith( + updateChannelState(_channelState.copyWith( pinnedMessages: pinnedMessages.where(_pinIsValid()).toList(), - ); + messages: expiredMessages, + )); } }); } From 950a2347b23b57e14363bc24e880696fb6019dd9 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 24 Feb 2021 10:17:44 +0100 Subject: [PATCH 07/41] update core readme --- .../example/lib/main.dart | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/packages/stream_chat_persistence/example/lib/main.dart b/packages/stream_chat_persistence/example/lib/main.dart index b356d96b7..6c614cc76 100644 --- a/packages/stream_chat_persistence/example/lib/main.dart +++ b/packages/stream_chat_persistence/example/lib/main.dart @@ -5,7 +5,10 @@ import 'package:stream_chat_persistence/stream_chat_persistence.dart'; Future main() async { /// Create a new instance of [StreamChatClient] passing the apikey obtained from your /// project dashboard. - final client = StreamChatClient('b67pax5b2wdq'); + final client = StreamChatClient( + 'b67pax5b2wdq', + logLevel: Level.INFO, + ); WidgetsFlutterBinding.ensureInitialized(); @@ -175,6 +178,15 @@ class _MessageViewState extends State { Widget build(BuildContext context) { return Column( children: [ + StreamBuilder>( + initialData: widget.channel.state.pinnedMessages, + stream: widget.channel.state.pinnedMessagesStream, + builder: (context, snap) { + return Column( + children: snap.data?.map((p) => Text(p.text))?.toList() ?? [], + ); + }, + ), Expanded( child: ListView.builder( controller: _scrollController, @@ -183,11 +195,17 @@ class _MessageViewState extends State { itemBuilder: (BuildContext context, int index) { final item = _messages[index]; if (item.user.id == widget.channel.client.uid) { - return Align( - alignment: Alignment.centerRight, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text(item.text), + return GestureDetector( + onLongPress: () { + print('pinning'); + widget.channel.pinMessage(item, 120); + }, + child: Align( + alignment: Alignment.centerRight, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(item.text), + ), ), ); } else { From 925817383fbfa24ad0918d319be0c79b6a5a3ba7 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 24 Feb 2021 10:48:04 +0100 Subject: [PATCH 08/41] fix example --- packages/stream_chat_flutter_core/README.md | 2 +- .../example/lib/main.dart | 30 ++++--------------- 2 files changed, 7 insertions(+), 25 deletions(-) diff --git a/packages/stream_chat_flutter_core/README.md b/packages/stream_chat_flutter_core/README.md index d2443713c..1fcf000b8 100644 --- a/packages/stream_chat_flutter_core/README.md +++ b/packages/stream_chat_flutter_core/README.md @@ -3,7 +3,7 @@ > The official Flutter core components for Stream Chat, a service for > building chat applications. -[![Pub](https://img.shields.io/pub/v/stream_chat_flutter.svg)](https://pub.dartlang.org/packages/stream_chat_flutter) +[![Pub](https://img.shields.io/pub/v/stream_chat_flutter_core.svg)](https://pub.dartlang.org/packages/stream_chat_flutter_core) ![](https://img.shields.io/badge/platform-flutter%20%7C%20flutter%20web-ff69b4.svg?style=flat-square) [![Gitter](https://badges.gitter.im/GetStream/stream-chat-flutter.svg)](https://gitter.im/GetStream/stream-chat-flutter?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) ![CI](https://github.com/GetStream/stream-chat-flutter/workflows/stream_flutter_workflow/badge.svg?branch=master) diff --git a/packages/stream_chat_persistence/example/lib/main.dart b/packages/stream_chat_persistence/example/lib/main.dart index 6c614cc76..b356d96b7 100644 --- a/packages/stream_chat_persistence/example/lib/main.dart +++ b/packages/stream_chat_persistence/example/lib/main.dart @@ -5,10 +5,7 @@ import 'package:stream_chat_persistence/stream_chat_persistence.dart'; Future main() async { /// Create a new instance of [StreamChatClient] passing the apikey obtained from your /// project dashboard. - final client = StreamChatClient( - 'b67pax5b2wdq', - logLevel: Level.INFO, - ); + final client = StreamChatClient('b67pax5b2wdq'); WidgetsFlutterBinding.ensureInitialized(); @@ -178,15 +175,6 @@ class _MessageViewState extends State { Widget build(BuildContext context) { return Column( children: [ - StreamBuilder>( - initialData: widget.channel.state.pinnedMessages, - stream: widget.channel.state.pinnedMessagesStream, - builder: (context, snap) { - return Column( - children: snap.data?.map((p) => Text(p.text))?.toList() ?? [], - ); - }, - ), Expanded( child: ListView.builder( controller: _scrollController, @@ -195,17 +183,11 @@ class _MessageViewState extends State { itemBuilder: (BuildContext context, int index) { final item = _messages[index]; if (item.user.id == widget.channel.client.uid) { - return GestureDetector( - onLongPress: () { - print('pinning'); - widget.channel.pinMessage(item, 120); - }, - child: Align( - alignment: Alignment.centerRight, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text(item.text), - ), + return Align( + alignment: Alignment.centerRight, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(item.text), ), ); } else { From 61598891e65b6a70f960b3097de5300a5477a14e Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 24 Feb 2021 11:46:32 +0100 Subject: [PATCH 09/41] update action --- .github/workflows/stream_flutter_workflow.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/stream_flutter_workflow.yml b/.github/workflows/stream_flutter_workflow.yml index 4c8882d72..9e898edac 100644 --- a/.github/workflows/stream_flutter_workflow.yml +++ b/.github/workflows/stream_flutter_workflow.yml @@ -13,6 +13,7 @@ on: jobs: analyze: + if: github.base_ref == 'master' timeout-minutes: 15 runs-on: ubuntu-latest steps: From 6b553a026bbd9b34bd63a9aaf2a7d0c51b11e181 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Wed, 24 Feb 2021 17:07:14 +0530 Subject: [PATCH 10/41] feat: Exposed customAttachmentBuilders through MessageListView --- packages/stream_chat_flutter/lib/src/message_list_view.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/stream_chat_flutter/lib/src/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view.dart index 1c5c8061f..e2f59e9cd 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -130,6 +130,7 @@ class MessageListView extends StatefulWidget { this.emptyBuilder, this.messageListBuilder, this.errorWidgetBuilder, + this.customAttachmentBuilders, }) : super(key: key); /// Function used to build a custom message widget @@ -203,6 +204,9 @@ class MessageListView extends StatefulWidget { /// of a connection failure. final ErrorBuilder errorWidgetBuilder; + /// Attachment builders for the default message widget + final Map customAttachmentBuilders; + @override _MessageListViewState createState() => _MessageListViewState(); } @@ -798,6 +802,7 @@ class _MessageListViewState extends State { break; } }, + customAttachmentBuilders: widget.customAttachmentBuilders, ); } @@ -958,6 +963,7 @@ class _MessageListViewState extends State { break; } }, + customAttachmentBuilders: widget.customAttachmentBuilders, ); if (!message.isDeleted && From 3d83808a59507267c8372e99d25bed5e1343d654 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Wed, 24 Feb 2021 17:10:09 +0530 Subject: [PATCH 11/41] feat: Exposed customAttachmentBuilders through MessageListView --- packages/stream_chat_flutter/lib/src/message_list_view.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat_flutter/lib/src/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view.dart index e2f59e9cd..505c4e0fa 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -205,6 +205,7 @@ class MessageListView extends StatefulWidget { final ErrorBuilder errorWidgetBuilder; /// Attachment builders for the default message widget + /// Please change this in the [MessageWidget] if you are using a custom implementation final Map customAttachmentBuilders; @override From 3f139d2eb6c30348159b3608e3d51e3fb5826529 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Thu, 25 Feb 2021 12:21:46 +0530 Subject: [PATCH 12/41] fix: Keyboard now closes when a command is enabled --- packages/stream_chat_flutter/lib/src/message_input.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 26feba698..e383173c0 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -2008,6 +2008,7 @@ class MessageInputState extends State { if (_commandEnabled) { text = '/${_chosenCommand.name} ' + text; + FocusScope.of(context).unfocus(); } final attachments = [..._attachments.values]; From 8e64f9d3831a326f782373fdc99b63998e00a218 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Thu, 25 Feb 2021 13:39:29 +0530 Subject: [PATCH 13/41] fix: Commands overflow bug --- .../lib/src/message_input.dart | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 26feba698..52223e7ed 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -417,19 +417,21 @@ class MessageInputState extends State { ), splashRadius: 24, ), - secondChild: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - if (!widget.disableAttachments) _buildAttachmentButton(), - if (widget.editMessage == null && - StreamChannel.of(context) - .channel - ?.config - ?.commands - ?.isNotEmpty == - true) - _buildCommandButton(), - ].insertBetween(const SizedBox(width: 8)), + secondChild: FittedBox( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + if (!widget.disableAttachments) _buildAttachmentButton(), + if (widget.editMessage == null && + StreamChannel.of(context) + .channel + ?.config + ?.commands + ?.isNotEmpty == + true) + _buildCommandButton(), + ].insertBetween(const SizedBox(width: 8)), + ), ), duration: Duration(milliseconds: 300), alignment: Alignment.center, From 949e83c6a99488e4a36f44e322d08d785bdf9059 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Thu, 25 Feb 2021 14:05:08 +0530 Subject: [PATCH 14/41] feat: Added message input theme and send button animation duration --- .../lib/src/message_input.dart | 3 +- .../lib/src/stream_chat_theme.dart | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index 26feba698..c6da99fc9 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -392,7 +392,8 @@ class MessageInputState extends State { : CrossFadeState.showSecond, firstChild: _buildSendButton(context), secondChild: _buildIdleSendButton(context), - duration: Duration(milliseconds: 300), + duration: + StreamChatTheme.of(context).messageInputTheme.sendAnimationDuration, alignment: Alignment.center, ), ); diff --git a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart index c2f3ee7ea..9b246ace4 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart @@ -60,6 +60,9 @@ class StreamChatThemeData { /// Theme of other users messages final MessageTheme otherMessageTheme; + /// Theme of other users messages + final MessageInputTheme messageInputTheme; + /// The widget that will be built when the channel image is unavailable final Widget Function(BuildContext, Channel) defaultChannelImage; @@ -80,6 +83,7 @@ class StreamChatThemeData { this.channelTheme, this.otherMessageTheme, this.ownMessageTheme, + this.messageInputTheme, this.defaultChannelImage, this.defaultUserImage, this.primaryIconTheme, @@ -108,6 +112,7 @@ class StreamChatThemeData { ChannelTheme channelTheme, MessageTheme ownMessageTheme, MessageTheme otherMessageTheme, + MessageInputTheme messageInputTheme, Widget Function(BuildContext, Channel) defaultChannelImage, Widget Function(BuildContext, User) defaultUserImage, IconThemeData primaryIconTheme, @@ -123,6 +128,7 @@ class StreamChatThemeData { channelTheme: channelTheme ?? this.channelTheme, ownMessageTheme: ownMessageTheme ?? this.ownMessageTheme, otherMessageTheme: otherMessageTheme ?? this.otherMessageTheme, + messageInputTheme: messageInputTheme ?? this.messageInputTheme, reactionIcons: reactionIcons ?? this.reactionIcons, ); @@ -143,6 +149,8 @@ class StreamChatThemeData { other.ownMessageTheme, otherMessageTheme: otherMessageTheme?.merge(other.otherMessageTheme) ?? other.otherMessageTheme, + messageInputTheme: messageInputTheme?.merge(other.messageInputTheme) ?? + other.messageInputTheme, reactionIcons: other.reactionIcons, ); } @@ -248,6 +256,9 @@ class StreamChatThemeData { ), ), ), + messageInputTheme: MessageInputTheme( + sendAnimationDuration: Duration(milliseconds: 7000), + ), reactionIcons: [ ReactionIcon( type: 'love', @@ -873,6 +884,27 @@ class ChannelHeaderTheme { } } +class MessageInputTheme { + final Duration sendAnimationDuration; + + const MessageInputTheme({ + this.sendAnimationDuration, + }); + + MessageInputTheme copyWith({Duration sendAnimationDuration}) => + MessageInputTheme( + sendAnimationDuration: + sendAnimationDuration ?? this.sendAnimationDuration, + ); + + MessageInputTheme merge(MessageInputTheme other) { + if (other == null) return this; + return copyWith( + sendAnimationDuration: other.sendAnimationDuration, + ); + } +} + class Effect { final double sigmaX; final double sigmaY; From 03553ea3cdd84224e01986cb21c6d75b3a3b7f22 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Thu, 25 Feb 2021 14:06:27 +0530 Subject: [PATCH 15/41] fix: corrected duration --- packages/stream_chat_flutter/lib/src/stream_chat_theme.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart index 9b246ace4..3c0ed8224 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart @@ -257,7 +257,7 @@ class StreamChatThemeData { ), ), messageInputTheme: MessageInputTheme( - sendAnimationDuration: Duration(milliseconds: 7000), + sendAnimationDuration: Duration(milliseconds: 300), ), reactionIcons: [ ReactionIcon( From 0b69d41328e40cfc42382ae73ffcd60cd22d0fc3 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Thu, 25 Feb 2021 14:18:08 +0530 Subject: [PATCH 16/41] fix: channel header text style --- packages/stream_chat_flutter/lib/src/stream_chat_theme.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart index c2f3ee7ea..29988a86a 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart @@ -195,10 +195,7 @@ class StreamChatThemeData { ), ), color: colorTheme.white, - title: TextStyle( - fontSize: 14, - color: colorTheme.black, - ), + title: textTheme.headlineBold, lastMessageAt: TextStyle( fontSize: 11, color: colorTheme.black.withOpacity(.5), From 492727754c86bffb64568ce60bacba1847046983 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Thu, 25 Feb 2021 14:47:04 +0530 Subject: [PATCH 17/41] fix: Delete now deletes a single image --- .../stream_chat_flutter/lib/src/image_actions_modal.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/image_actions_modal.dart b/packages/stream_chat_flutter/lib/src/image_actions_modal.dart index 3c4a7c73c..ec9abd93d 100644 --- a/packages/stream_chat_flutter/lib/src/image_actions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/image_actions_modal.dart @@ -103,11 +103,11 @@ class ImageActionsModal extends StatelessWidget { color: StreamChatTheme.of(context).colorTheme.accentRed, ), () { + var channel = StreamChannel.of(context).channel; + channel.updateMessage( + message..attachments.removeAt(currentIndex)); Navigator.pop(context); Navigator.pop(context); - StreamChannel.of(context) - .channel - .deleteMessage(message); }, color: StreamChatTheme.of(context).colorTheme.accentRed, ), From 59c8dca60ea3b93158d209153141cdcee0ec419c Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Thu, 25 Feb 2021 14:55:15 +0530 Subject: [PATCH 18/41] fix: Delete now deletes a single image --- .../lib/src/image_actions_modal.dart | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/image_actions_modal.dart b/packages/stream_chat_flutter/lib/src/image_actions_modal.dart index 3c4a7c73c..7d004d377 100644 --- a/packages/stream_chat_flutter/lib/src/image_actions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/image_actions_modal.dart @@ -103,11 +103,20 @@ class ImageActionsModal extends StatelessWidget { color: StreamChatTheme.of(context).colorTheme.accentRed, ), () { - Navigator.pop(context); - Navigator.pop(context); - StreamChannel.of(context) - .channel - .deleteMessage(message); + if (message.attachments.length > 1 || + message.text.isNotEmpty) { + var channel = StreamChannel.of(context).channel; + channel.updateMessage( + message..attachments.removeAt(currentIndex)); + Navigator.pop(context); + Navigator.pop(context); + } else { + var channel = StreamChannel.of(context).channel; + channel.deleteMessage(message).then((value) { + Navigator.pop(context); + Navigator.pop(context); + }); + } }, color: StreamChatTheme.of(context).colorTheme.accentRed, ), From 75cdaaf06253073c87191d88d8197943c25352af Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Thu, 25 Feb 2021 10:43:54 +0100 Subject: [PATCH 19/41] add platform type to userAgent header --- packages/stream_chat/lib/src/client.dart | 4 +- .../platform_detector/platform_detector.dart | 44 +++++++++++++++++++ .../platform_detector_io.dart | 11 +++++ .../platform_detector_web.dart | 3 ++ 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 packages/stream_chat/lib/src/platform_detector/platform_detector.dart create mode 100644 packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart create mode 100644 packages/stream_chat/lib/src/platform_detector/platform_detector_web.dart diff --git a/packages/stream_chat/lib/src/client.dart b/packages/stream_chat/lib/src/client.dart index 8b8d7c112..65e77a0d8 100644 --- a/packages/stream_chat/lib/src/client.dart +++ b/packages/stream_chat/lib/src/client.dart @@ -10,6 +10,7 @@ import 'package:stream_chat/src/api/retry_policy.dart'; import 'package:stream_chat/src/event_type.dart'; import 'package:stream_chat/src/models/attachment_file.dart'; import 'package:stream_chat/src/models/own_user.dart'; +import 'package:stream_chat/src/platform_detector/platform_detector.dart'; import 'package:stream_chat/version.dart'; import 'package:uuid/uuid.dart'; @@ -888,9 +889,8 @@ class StreamChatClient { String get _authType => _anonymous ? 'anonymous' : 'jwt'; - // TODO: get the right version of the lib from the build toolchain String get _userAgent => - 'stream-chat-dart-client-${PACKAGE_VERSION.split('+')[0]}'; + 'stream-chat-dart-client-${Platform.name}-${PACKAGE_VERSION.split('+')[0]}'; Map get _commonQueryParams => { 'user_id': state.user?.id, diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector.dart b/packages/stream_chat/lib/src/platform_detector/platform_detector.dart new file mode 100644 index 000000000..6fbb8f5fb --- /dev/null +++ b/packages/stream_chat/lib/src/platform_detector/platform_detector.dart @@ -0,0 +1,44 @@ +import 'platform_detector_web.dart' if (dart.library.io) 'src/platform_io.dart'; + +enum PlatformType { + Android, + Ios, + Web, + MacOS, + Windows, + Linux, + Fuchsia, +} + +abstract class Platform { + static bool get isAndroid => currentPlatform == PlatformType.Android; + static bool get isIos => currentPlatform == PlatformType.Ios; + static bool get isWeb => currentPlatform == PlatformType.Web; + static bool get isMacOS => currentPlatform == PlatformType.MacOS; + static bool get isWindows => currentPlatform == PlatformType.Windows; + static bool get isLinux => currentPlatform == PlatformType.Linux; + static bool get isFuchsia => currentPlatform == PlatformType.Fuchsia; + + static String get name { + switch (currentPlatform) { + case PlatformType.Android: + return 'android'; + case PlatformType.Ios: + return 'ios'; + case PlatformType.Web: + return 'web'; + case PlatformType.MacOS: + return 'macos'; + case PlatformType.Windows: + return 'windows'; + case PlatformType.Linux: + return 'linux'; + case PlatformType.Fuchsia: + return 'fuchsia'; + default: + return ''; + } + } + + PlatformType get type => currentPlatform; +} diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart b/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart new file mode 100644 index 000000000..39b27e32e --- /dev/null +++ b/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart @@ -0,0 +1,11 @@ +import 'dart:io'; +import 'platform_detector.dart'; + +PlatformType get currentPlatform { + if (Platform.isWindows) return PlatformType.Windows; + if (Platform.isFuchsia) return PlatformType.Fuchsia; + if (Platform.isMacOS) return PlatformType.MacOS; + if (Platform.isLinux) return PlatformType.Linux; + if (Platform.isIos) return PlatformType.Ios; + return PlatformType.Android; +} diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector_web.dart b/packages/stream_chat/lib/src/platform_detector/platform_detector_web.dart new file mode 100644 index 000000000..b1613afb2 --- /dev/null +++ b/packages/stream_chat/lib/src/platform_detector/platform_detector_web.dart @@ -0,0 +1,3 @@ +import 'platform_detector.dart'; + +PlatformType get currentPlatform => PlatformType.Web; From 628ec27b77b3b10bbb7ae7ae95a6772b847e9f3a Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Thu, 25 Feb 2021 11:19:56 +0100 Subject: [PATCH 20/41] fix stackoverflow bug --- .../example/ios/Flutter/Debug.xcconfig | 1 - .../example/ios/Flutter/Release.xcconfig | 1 - .../ios/Runner.xcodeproj/project.pbxproj | 495 ++++++++++++++++++ packages/stream_chat/example/lib/main.dart | 5 +- packages/stream_chat/lib/src/client.dart | 2 +- .../platform_detector/platform_detector.dart | 7 +- .../platform_detector_io.dart | 3 +- .../platform_detector_stub.dart | 5 + .../src/platform_detector/platform_type.dart | 9 + 9 files changed, 521 insertions(+), 7 deletions(-) create mode 100644 packages/stream_chat/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 packages/stream_chat/lib/src/platform_detector/platform_detector_stub.dart create mode 100644 packages/stream_chat/lib/src/platform_detector/platform_type.dart diff --git a/packages/stream_chat/example/ios/Flutter/Debug.xcconfig b/packages/stream_chat/example/ios/Flutter/Debug.xcconfig index e8efba114..592ceee85 100644 --- a/packages/stream_chat/example/ios/Flutter/Debug.xcconfig +++ b/packages/stream_chat/example/ios/Flutter/Debug.xcconfig @@ -1,2 +1 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/packages/stream_chat/example/ios/Flutter/Release.xcconfig b/packages/stream_chat/example/ios/Flutter/Release.xcconfig index 399e9340e..592ceee85 100644 --- a/packages/stream_chat/example/ios/Flutter/Release.xcconfig +++ b/packages/stream_chat/example/ios/Flutter/Release.xcconfig @@ -1,2 +1 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/packages/stream_chat/example/ios/Runner.xcodeproj/project.pbxproj b/packages/stream_chat/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..1aec2aa07 --- /dev/null +++ b/packages/stream_chat/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,495 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/stream_chat/example/lib/main.dart b/packages/stream_chat/example/lib/main.dart index 1261d3fea..2d0a529fa 100644 --- a/packages/stream_chat/example/lib/main.dart +++ b/packages/stream_chat/example/lib/main.dart @@ -4,7 +4,10 @@ import 'package:stream_chat/stream_chat.dart'; Future main() async { /// Create a new instance of [StreamChatClient] passing the apikey obtained from your /// project dashboard. - final client = StreamChatClient('b67pax5b2wdq'); + final client = StreamChatClient( + 'b67pax5b2wdq', + logLevel: Level.INFO, + ); /// Set the current user. In a production scenario, this should be done using /// a backend to generate a user token using our server SDK. diff --git a/packages/stream_chat/lib/src/client.dart b/packages/stream_chat/lib/src/client.dart index 65e77a0d8..a13025396 100644 --- a/packages/stream_chat/lib/src/client.dart +++ b/packages/stream_chat/lib/src/client.dart @@ -890,7 +890,7 @@ class StreamChatClient { String get _authType => _anonymous ? 'anonymous' : 'jwt'; String get _userAgent => - 'stream-chat-dart-client-${Platform.name}-${PACKAGE_VERSION.split('+')[0]}'; + 'stream-chat-dart-client-${CurrentPlatform.name}-${PACKAGE_VERSION.split('+')[0]}'; Map get _commonQueryParams => { 'user_id': state.user?.id, diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector.dart b/packages/stream_chat/lib/src/platform_detector/platform_detector.dart index 6fbb8f5fb..c3cf9f745 100644 --- a/packages/stream_chat/lib/src/platform_detector/platform_detector.dart +++ b/packages/stream_chat/lib/src/platform_detector/platform_detector.dart @@ -1,4 +1,6 @@ -import 'platform_detector_web.dart' if (dart.library.io) 'src/platform_io.dart'; +import 'platform_detector_stub.dart' + if (dart.library.html) 'platform_detector_web.dart' + if (dart.library.io) 'platform_detector_io.dart'; enum PlatformType { Android, @@ -10,7 +12,8 @@ enum PlatformType { Fuchsia, } -abstract class Platform { +class CurrentPlatform { + CurrentPlatform._(); static bool get isAndroid => currentPlatform == PlatformType.Android; static bool get isIos => currentPlatform == PlatformType.Ios; static bool get isWeb => currentPlatform == PlatformType.Web; diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart b/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart index 39b27e32e..7339b683d 100644 --- a/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart +++ b/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart @@ -1,11 +1,12 @@ import 'dart:io'; import 'platform_detector.dart'; +//Override default method, to provide .io access PlatformType get currentPlatform { if (Platform.isWindows) return PlatformType.Windows; if (Platform.isFuchsia) return PlatformType.Fuchsia; if (Platform.isMacOS) return PlatformType.MacOS; if (Platform.isLinux) return PlatformType.Linux; - if (Platform.isIos) return PlatformType.Ios; + if (Platform.isIOS) return PlatformType.Ios; return PlatformType.Android; } diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector_stub.dart b/packages/stream_chat/lib/src/platform_detector/platform_detector_stub.dart new file mode 100644 index 000000000..4469fc46a --- /dev/null +++ b/packages/stream_chat/lib/src/platform_detector/platform_detector_stub.dart @@ -0,0 +1,5 @@ +import 'platform_detector.dart'; + +PlatformType get currentPlatform { + throw UnimplementedError(); +} diff --git a/packages/stream_chat/lib/src/platform_detector/platform_type.dart b/packages/stream_chat/lib/src/platform_detector/platform_type.dart new file mode 100644 index 000000000..985d540c5 --- /dev/null +++ b/packages/stream_chat/lib/src/platform_detector/platform_type.dart @@ -0,0 +1,9 @@ +enum PlatformType { + Android, + Ios, + Web, + MacOS, + Windows, + Linux, + Fuchsia, +} From 209e0b17387c14ca03cbb83b7d8c28f76f309502 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Thu, 25 Feb 2021 11:20:31 +0100 Subject: [PATCH 21/41] cleanup --- .../lib/src/platform_detector/platform_detector_io.dart | 1 - .../lib/src/platform_detector/platform_type.dart | 9 --------- 2 files changed, 10 deletions(-) delete mode 100644 packages/stream_chat/lib/src/platform_detector/platform_type.dart diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart b/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart index 7339b683d..6cca45b6e 100644 --- a/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart +++ b/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'platform_detector.dart'; -//Override default method, to provide .io access PlatformType get currentPlatform { if (Platform.isWindows) return PlatformType.Windows; if (Platform.isFuchsia) return PlatformType.Fuchsia; diff --git a/packages/stream_chat/lib/src/platform_detector/platform_type.dart b/packages/stream_chat/lib/src/platform_detector/platform_type.dart deleted file mode 100644 index 985d540c5..000000000 --- a/packages/stream_chat/lib/src/platform_detector/platform_type.dart +++ /dev/null @@ -1,9 +0,0 @@ -enum PlatformType { - Android, - Ios, - Web, - MacOS, - Windows, - Linux, - Fuchsia, -} From d030a97167ac3727f9d7ae9549967cc23f111c57 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 25 Feb 2021 19:32:05 +0530 Subject: [PATCH 22/41] [UI-Kit -> ImageActionsModal] Fix delete image Signed-off-by: Sahil Kumar --- .../lib/src/image_actions_modal.dart | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/image_actions_modal.dart b/packages/stream_chat_flutter/lib/src/image_actions_modal.dart index 7d004d377..90d730647 100644 --- a/packages/stream_chat_flutter/lib/src/image_actions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/image_actions_modal.dart @@ -103,15 +103,20 @@ class ImageActionsModal extends StatelessWidget { color: StreamChatTheme.of(context).colorTheme.accentRed, ), () { + final channel = StreamChannel.of(context).channel; if (message.attachments.length > 1 || message.text.isNotEmpty) { - var channel = StreamChannel.of(context).channel; - channel.updateMessage( - message..attachments.removeAt(currentIndex)); + final remainingAttachments = [...message.attachments] + ..removeAt(currentIndex); + channel.updateMessage(message.copyWith( + attachments: remainingAttachments.map((e) { + return e.copyWith( + uploadState: UploadState.success()); + }).toList(), + )); Navigator.pop(context); Navigator.pop(context); } else { - var channel = StreamChannel.of(context).channel; channel.deleteMessage(message).then((value) { Navigator.pop(context); Navigator.pop(context); From f0efec30a3f21b4fbcf251cd2628fc786150bacf Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Thu, 25 Feb 2021 15:16:41 +0100 Subject: [PATCH 23/41] review fix --- .../platform_detector/platform_detector.dart | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector.dart b/packages/stream_chat/lib/src/platform_detector/platform_detector.dart index c3cf9f745..8f601501c 100644 --- a/packages/stream_chat/lib/src/platform_detector/platform_detector.dart +++ b/packages/stream_chat/lib/src/platform_detector/platform_detector.dart @@ -14,16 +14,16 @@ enum PlatformType { class CurrentPlatform { CurrentPlatform._(); - static bool get isAndroid => currentPlatform == PlatformType.Android; - static bool get isIos => currentPlatform == PlatformType.Ios; - static bool get isWeb => currentPlatform == PlatformType.Web; - static bool get isMacOS => currentPlatform == PlatformType.MacOS; - static bool get isWindows => currentPlatform == PlatformType.Windows; - static bool get isLinux => currentPlatform == PlatformType.Linux; - static bool get isFuchsia => currentPlatform == PlatformType.Fuchsia; + static bool get isAndroid => type == PlatformType.Android; + static bool get isIos => type == PlatformType.Ios; + static bool get isWeb => type == PlatformType.Web; + static bool get isMacOS => type == PlatformType.MacOS; + static bool get isWindows => type == PlatformType.Windows; + static bool get isLinux => type == PlatformType.Linux; + static bool get isFuchsia => type == PlatformType.Fuchsia; static String get name { - switch (currentPlatform) { + switch (type) { case PlatformType.Android: return 'android'; case PlatformType.Ios: @@ -43,5 +43,5 @@ class CurrentPlatform { } } - PlatformType get type => currentPlatform; + static PlatformType get type => currentPlatform; } From ee61fcdc7269f5edb91ebf4f93281e11368c47f5 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Thu, 25 Feb 2021 16:36:29 +0100 Subject: [PATCH 24/41] fix inputbackground --- packages/stream_chat_flutter/lib/src/stream_chat_theme.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart index c2f3ee7ea..83b51dde2 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart @@ -204,7 +204,7 @@ class StreamChatThemeData { color: colorTheme.black.withOpacity(.5), ), ), - inputBackground: colorTheme.white.withAlpha(12), + inputBackground: colorTheme.white, ), ownMessageTheme: MessageTheme( messageAuthor: textTheme.footnote.copyWith(color: colorTheme.grey), From feb0eb151e91c3f8347d430e9f49113b5b0bb5e3 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Fri, 26 Feb 2021 13:14:48 +0530 Subject: [PATCH 25/41] fix: Rolled back changes from other PR --- .../stream_chat_flutter/lib/src/image_actions_modal.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/image_actions_modal.dart b/packages/stream_chat_flutter/lib/src/image_actions_modal.dart index ec9abd93d..3c4a7c73c 100644 --- a/packages/stream_chat_flutter/lib/src/image_actions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/image_actions_modal.dart @@ -103,11 +103,11 @@ class ImageActionsModal extends StatelessWidget { color: StreamChatTheme.of(context).colorTheme.accentRed, ), () { - var channel = StreamChannel.of(context).channel; - channel.updateMessage( - message..attachments.removeAt(currentIndex)); Navigator.pop(context); Navigator.pop(context); + StreamChannel.of(context) + .channel + .deleteMessage(message); }, color: StreamChatTheme.of(context).colorTheme.accentRed, ), From 7db34db7ca91356af4cc66ba24014d7e9c7a16a5 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Fri, 26 Feb 2021 13:38:14 +0530 Subject: [PATCH 26/41] feat: Shifted fields from channel to input theme --- .../lib/src/message_actions_modal.dart | 3 +- .../lib/src/message_input.dart | 18 ++-- .../lib/src/stream_chat_theme.dart | 86 ++++++++++--------- 3 files changed, 57 insertions(+), 50 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal.dart index af05d74ce..d914afba5 100644 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/message_actions_modal.dart @@ -596,7 +596,8 @@ class _MessageActionsModalState extends State { elevation: 2, clipBehavior: Clip.hardEdge, isScrollControlled: true, - backgroundColor: StreamChatTheme.of(context).channelTheme.inputBackground, + backgroundColor: + StreamChatTheme.of(context).messageInputTheme.inputBackground, shape: RoundedRectangleBorder( borderRadius: BorderRadius.only( topLeft: Radius.circular(16), diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index c6da99fc9..3032f1f5b 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -235,7 +235,7 @@ class MessageInputState extends State { @override Widget build(BuildContext context) { Widget child = Container( - color: StreamChatTheme.of(context).channelTheme.inputBackground, + color: StreamChatTheme.of(context).messageInputTheme.inputBackground, child: SafeArea( child: GestureDetector( onPanUpdate: (details) { @@ -1684,8 +1684,10 @@ class MessageInputState extends State { return IconButton( icon: StreamSvgIcon.lightning( color: _commandsOverlay != null - ? StreamChatTheme.of(context).channelTheme.actionButtonColor - : StreamChatTheme.of(context).channelTheme.actionButtonIdleColor, + ? StreamChatTheme.of(context).messageInputTheme.actionButtonColor + : StreamChatTheme.of(context) + .messageInputTheme + .actionButtonIdleColor, ), padding: const EdgeInsets.all(0), constraints: BoxConstraints.tightFor( @@ -1722,8 +1724,10 @@ class MessageInputState extends State { return IconButton( icon: StreamSvgIcon.attach( color: _openFilePickerSection - ? StreamChatTheme.of(context).channelTheme.actionButtonColor - : StreamChatTheme.of(context).channelTheme.actionButtonIdleColor, + ? StreamChatTheme.of(context).messageInputTheme.actionButtonColor + : StreamChatTheme.of(context) + .messageInputTheme + .actionButtonIdleColor, ), padding: const EdgeInsets.all(0), constraints: BoxConstraints.tightFor( @@ -1962,7 +1966,7 @@ class MessageInputState extends State { Widget _buildIdleSendButton(BuildContext context) { return StreamSvgIcon( assetName: _getIdleSendIcon(), - color: StreamChatTheme.of(context).channelTheme.sendButtonIdleColor, + color: StreamChatTheme.of(context).messageInputTheme.sendButtonIdleColor, ); } @@ -1977,7 +1981,7 @@ class MessageInputState extends State { ), icon: StreamSvgIcon( assetName: _getSendIcon(), - color: StreamChatTheme.of(context).channelTheme.sendButtonColor, + color: StreamChatTheme.of(context).messageInputTheme.sendButtonColor, ), ); } diff --git a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart index 3c0ed8224..9ad2b7c53 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart @@ -190,10 +190,6 @@ class StreamChatThemeData { ), indicatorIconSize: 16.0), channelTheme: ChannelTheme( - actionButtonColor: colorTheme.accentBlue, - actionButtonIdleColor: colorTheme.grey, - sendButtonColor: colorTheme.accentBlue, - sendButtonIdleColor: colorTheme.greyGainsboro, channelHeaderTheme: ChannelHeaderTheme( avatarTheme: AvatarTheme( borderRadius: BorderRadius.circular(20), @@ -212,7 +208,6 @@ class StreamChatThemeData { color: colorTheme.black.withOpacity(.5), ), ), - inputBackground: colorTheme.white.withAlpha(12), ), ownMessageTheme: MessageTheme( messageAuthor: textTheme.footnote.copyWith(color: colorTheme.grey), @@ -258,6 +253,11 @@ class StreamChatThemeData { ), messageInputTheme: MessageInputTheme( sendAnimationDuration: Duration(milliseconds: 300), + actionButtonColor: colorTheme.accentBlue, + actionButtonIdleColor: colorTheme.grey, + sendButtonColor: colorTheme.accentBlue, + sendButtonIdleColor: colorTheme.greyGainsboro, + inputBackground: colorTheme.white.withAlpha(12), ), reactionIcons: [ ReactionIcon( @@ -639,47 +639,16 @@ class ChannelTheme { /// Theme of the [ChannelHeader] widget final ChannelHeaderTheme channelHeaderTheme; - /// Background color of [MessageInput] send button - final Color sendButtonColor; - - /// Background color of [MessageInput] action buttons - final Color actionButtonColor; - - /// Background color of [MessageInput] send button - final Color sendButtonIdleColor; - - /// Background color of [MessageInput] action buttons - final Color actionButtonIdleColor; - - /// Background color of [MessageInput] - final Color inputBackground; - ChannelTheme({ this.channelHeaderTheme, - this.actionButtonColor, - this.sendButtonColor, - this.actionButtonIdleColor, - this.sendButtonIdleColor, - this.inputBackground, }); /// Creates a copy of [ChannelTheme] with specified attributes overridden. ChannelTheme copyWith({ ChannelHeaderTheme channelHeaderTheme, - Color inputBackground, - Color actionButtonColor, - Color sendButtonColor, - Color actionButtonIdleColor, - Color sendButtonIdleColor, }) => ChannelTheme( channelHeaderTheme: channelHeaderTheme ?? this.channelHeaderTheme, - inputBackground: inputBackground ?? this.inputBackground, - actionButtonColor: actionButtonColor ?? this.actionButtonColor, - sendButtonColor: sendButtonColor ?? this.sendButtonColor, - actionButtonIdleColor: - actionButtonIdleColor ?? this.actionButtonIdleColor, - sendButtonIdleColor: sendButtonIdleColor ?? this.sendButtonIdleColor, ); ChannelTheme merge(ChannelTheme other) { @@ -687,11 +656,6 @@ class ChannelTheme { return copyWith( channelHeaderTheme: channelHeaderTheme?.merge(other.channelHeaderTheme) ?? other.channelHeaderTheme, - inputBackground: other.inputBackground, - actionButtonColor: other.actionButtonColor, - actionButtonIdleColor: other.actionButtonIdleColor, - sendButtonColor: other.sendButtonColor, - sendButtonIdleColor: other.sendButtonIdleColor, ); } } @@ -887,20 +851,58 @@ class ChannelHeaderTheme { class MessageInputTheme { final Duration sendAnimationDuration; + /// Background color of [MessageInput] send button + final Color sendButtonColor; + + /// Background color of [MessageInput] action buttons + final Color actionButtonColor; + + /// Background color of [MessageInput] send button + final Color sendButtonIdleColor; + + /// Background color of [MessageInput] action buttons + final Color actionButtonIdleColor; + + /// Background color of [MessageInput] + final Color inputBackground; + const MessageInputTheme({ this.sendAnimationDuration, + this.actionButtonColor, + this.sendButtonColor, + this.actionButtonIdleColor, + this.sendButtonIdleColor, + this.inputBackground, }); - MessageInputTheme copyWith({Duration sendAnimationDuration}) => + MessageInputTheme copyWith({ + Duration sendAnimationDuration, + Color inputBackground, + Color actionButtonColor, + Color sendButtonColor, + Color actionButtonIdleColor, + Color sendButtonIdleColor, + }) => MessageInputTheme( sendAnimationDuration: sendAnimationDuration ?? this.sendAnimationDuration, + inputBackground: inputBackground ?? this.inputBackground, + actionButtonColor: actionButtonColor ?? this.actionButtonColor, + sendButtonColor: sendButtonColor ?? this.sendButtonColor, + actionButtonIdleColor: + actionButtonIdleColor ?? this.actionButtonIdleColor, + sendButtonIdleColor: sendButtonIdleColor ?? this.sendButtonIdleColor, ); MessageInputTheme merge(MessageInputTheme other) { if (other == null) return this; return copyWith( sendAnimationDuration: other.sendAnimationDuration, + inputBackground: other.inputBackground, + actionButtonColor: other.actionButtonColor, + actionButtonIdleColor: other.actionButtonIdleColor, + sendButtonColor: other.sendButtonColor, + sendButtonIdleColor: other.sendButtonIdleColor, ); } } From f812fbbdc51fe09592230c17d16a59936fd2789c Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Fri, 26 Feb 2021 13:23:24 +0100 Subject: [PATCH 27/41] default to success and hide indicators if not needed --- packages/stream_chat/lib/src/models/attachment.dart | 11 ++++++++--- .../attachment/attachment_upload_state_builder.dart | 4 +++- .../lib/src/attachment/file_attachment.dart | 12 ++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/packages/stream_chat/lib/src/models/attachment.dart b/packages/stream_chat/lib/src/models/attachment.dart index cc55aa712..e2e81cd93 100644 --- a/packages/stream_chat/lib/src/models/attachment.dart +++ b/packages/stream_chat/lib/src/models/attachment.dart @@ -58,7 +58,7 @@ class Attachment { final AttachmentFile file; /// The current upload state of the attachment - final UploadState uploadState; + UploadState uploadState; /// Map of custom channel extraData @JsonKey(includeIfNull: false) @@ -123,10 +123,15 @@ class Attachment { this.actions, this.extraData, this.file, - this.uploadState, + UploadState uploadState, }) : id = id ?? Uuid().v4(), title = title ?? file?.name, - localUri = file?.path != null ? Uri.parse(file.path) : null; + localUri = file?.path != null ? Uri.parse(file.path) : null { + this.uploadState = uploadState ?? + ((assetUrl != null || imageUrl != null) + ? UploadState.success() + : UploadState.preparing()); + } /// Create a new instance from a json factory Attachment.fromJson(Map json) { diff --git a/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart b/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart index fb65fd12f..2425b9668 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart @@ -27,7 +27,9 @@ class AttachmentUploadStateBuilder extends StatelessWidget { @override Widget build(BuildContext context) { - if (attachment.uploadState == null) return Offstage(); + if (message.status == null || message.status == MessageSendingStatus.sent) { + return Offstage(); + } final messageId = message.id; final attachmentId = attachment.id; diff --git a/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart index ee6b10c8b..96d1cff2c 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart @@ -227,6 +227,18 @@ class FileAttachment extends AttachmentWidget { }, ); + if (message.status == null || message.status == MessageSendingStatus.sent) { + trailingWidget = IconButton( + icon: StreamSvgIcon.cloudDownload(color: theme.colorTheme.black), + padding: const EdgeInsets.all(8), + visualDensity: VisualDensity.compact, + splashRadius: 16, + onPressed: () { + launchURL(context, attachment.assetUrl); + }, + ); + } + return Material( type: MaterialType.transparency, child: trailingWidget, From 9f5cc16dca08b7fd4dc199f206979dc9f86c57a2 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Fri, 26 Feb 2021 13:32:44 +0100 Subject: [PATCH 28/41] fix file attachment --- .../lib/src/attachment/file_attachment.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart index 96d1cff2c..f7fbc277b 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart @@ -227,7 +227,9 @@ class FileAttachment extends AttachmentWidget { }, ); - if (message.status == null || message.status == MessageSendingStatus.sent) { + if (message != null && + (message.status == null || + message.status == MessageSendingStatus.sent)) { trailingWidget = IconButton( icon: StreamSvgIcon.cloudDownload(color: theme.colorTheme.black), padding: const EdgeInsets.all(8), @@ -253,6 +255,9 @@ class FileAttachment extends AttachmentWidget { ); return attachment.uploadState?.when( preparing: () { + if (message == null) { + return Text('${fileSize(size, 2)}', style: textStyle); + } return UploadProgressIndicator( uploaded: 0, total: double.maxFinite.toInt(), From 114182d50f23385a1e8c0f0cf94f65e53ab43ca5 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Fri, 26 Feb 2021 13:34:28 +0100 Subject: [PATCH 29/41] fix delete image --- .../stream_chat_flutter/lib/src/image_actions_modal.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/image_actions_modal.dart b/packages/stream_chat_flutter/lib/src/image_actions_modal.dart index 90d730647..1304b90d8 100644 --- a/packages/stream_chat_flutter/lib/src/image_actions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/image_actions_modal.dart @@ -109,10 +109,7 @@ class ImageActionsModal extends StatelessWidget { final remainingAttachments = [...message.attachments] ..removeAt(currentIndex); channel.updateMessage(message.copyWith( - attachments: remainingAttachments.map((e) { - return e.copyWith( - uploadState: UploadState.success()); - }).toList(), + attachments: remainingAttachments, )); Navigator.pop(context); Navigator.pop(context); From 623d48e32bfb3d08fd70168fcb1e16f695a98033 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 26 Feb 2021 18:17:43 +0530 Subject: [PATCH 30/41] Add custom sort support for offline channels Signed-off-by: Sahil Kumar --- packages/stream_chat/lib/src/api/channel.dart | 4 +- .../stream_chat/lib/src/api/requests.dart | 14 +- packages/stream_chat/lib/src/client.dart | 235 ++++++++---------- .../lib/src/db/chat_persistence_client.dart | 30 +-- .../lib/src/models/channel_state.dart | 2 +- packages/stream_chat/test/version_test.dart | 120 ++++++++- .../lib/src/channel_info.dart | 5 +- .../lib/src/channel_list_view.dart | 168 +++---------- .../lib/src/channel_list_core.dart | 79 ++---- .../lib/src/channels_bloc.dart | 22 +- .../example/pubspec.yaml | 2 +- .../lib/src/dao/channel_query_dao.dart | 140 ++++++----- .../src/stream_chat_persistence_client.dart | 7 +- 13 files changed, 390 insertions(+), 438 deletions(-) diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/api/channel.dart index a72312350..1f5da4666 100644 --- a/packages/stream_chat/lib/src/api/channel.dart +++ b/packages/stream_chat/lib/src/api/channel.dart @@ -1245,7 +1245,9 @@ class ChannelClientState { _channel._client.chatPersistenceClient ?.getChannelStateByCid(_channel.cid) ?.then((state) { - updateChannelState(state); + // Replacing the persistence state members with the latest `channelState.members` + // as they may have changes over the time. + updateChannelState(state.copyWith(members: channelState.members)); retryFailedMessages(); }); }); diff --git a/packages/stream_chat/lib/src/api/requests.dart b/packages/stream_chat/lib/src/api/requests.dart index fdf1f34fb..414113de8 100644 --- a/packages/stream_chat/lib/src/api/requests.dart +++ b/packages/stream_chat/lib/src/api/requests.dart @@ -4,7 +4,7 @@ part 'requests.g.dart'; /// Sorting options @JsonSerializable(createFactory: false) -class SortOption { +class SortOption { /// Ascending order static const ASC = 1; @@ -17,6 +17,10 @@ class SortOption { /// A sorting direction final int direction; + /// Sorting field Comparator required for offline sorting + @JsonKey(ignore: true) + final Comparator comparator; + /// Creates a new SortOption instance /// /// For example: @@ -24,7 +28,11 @@ class SortOption { /// // Sort channels by the last message date: /// final sorting = SortOption("last_message_at") /// ``` - const SortOption(this.field, {this.direction = DESC}); + const SortOption( + this.field, { + this.direction = DESC, + this.comparator, + }); /// Serialize model to json Map toJson() => _$SortOptionToJson(this); @@ -67,7 +75,7 @@ class PaginationParams { /// ``` const PaginationParams({ this.limit = 10, - this.offset, + this.offset = 0, this.greaterThan, this.greaterThanOrEqual, this.lessThan, diff --git a/packages/stream_chat/lib/src/client.dart b/packages/stream_chat/lib/src/client.dart index a13025396..55212b895 100644 --- a/packages/stream_chat/lib/src/client.dart +++ b/packages/stream_chat/lib/src/client.dart @@ -9,6 +9,7 @@ import 'package:rxdart/rxdart.dart'; import 'package:stream_chat/src/api/retry_policy.dart'; import 'package:stream_chat/src/event_type.dart'; import 'package:stream_chat/src/models/attachment_file.dart'; +import 'package:stream_chat/src/models/channel_model.dart'; import 'package:stream_chat/src/models/own_user.dart'; import 'package:stream_chat/src/platform_detector/platform_detector.dart'; import 'package:stream_chat/version.dart'; @@ -22,6 +23,7 @@ import 'api/responses.dart'; import 'api/websocket.dart'; import 'db/chat_persistence_client.dart'; import 'exceptions.dart'; +import 'models/channel_state.dart'; import 'models/event.dart'; import 'models/message.dart'; import 'models/user.dart'; @@ -489,7 +491,7 @@ class StreamChatClient { if (status == ConnectionStatus.connected && state.channels?.isNotEmpty == true) { - unawaited(queryChannels(filter: { + unawaited(_queryChannels(filter: { 'cid': { '\$in': state.channels.keys.toList(), }, @@ -578,15 +580,15 @@ class StreamChatClient { final _queryChannelsStreams = >>{}; /// Requests channels with a given query. - Future> queryChannels({ + Stream> queryChannels({ Map filter, - List sort, + List> sort, Map options, PaginationParams paginationParams = const PaginationParams(limit: 10), int messageLimit, - bool onlyOffline = false, + bool preferOffline = true, bool waitForConnect = true, - }) async { + }) async* { if (waitForConnect) { if (_connectCompleter != null && !_connectCompleter.isCompleted) { logger.info('awaiting connection completer'); @@ -598,7 +600,7 @@ class StreamChatClient { if (persistenceEnabled) { logger.warning( '$errorMessage\nTrying to retrieve channels from the offline storage.'); - onlyOffline = true; + preferOffline = true; } else { throw Exception(errorMessage); } @@ -606,34 +608,41 @@ class StreamChatClient { } final hash = base64.encode(utf8.encode( - '$filter${_asMap(sort)}$options${paginationParams?.toJson()}$messageLimit$onlyOffline')); + '$filter${_asMap(sort)}$options${paginationParams?.toJson()}$messageLimit$preferOffline', + )); + if (_queryChannelsStreams.containsKey(hash)) { - return _queryChannelsStreams[hash]; - } + yield await _queryChannelsStreams[hash]; + } else { + if (true) { + final channels = await _queryChannelsOffline( + filter: filter, + sort: sort, + paginationParams: paginationParams, + ); + if (channels.isNotEmpty) yield channels; + } - final newQueryChannelsStream = _doQueryChannels( - filter: filter, - sort: sort, - options: options, - paginationParams: paginationParams, - messageLimit: messageLimit, - onlyOffline: onlyOffline, - ).whenComplete(() { - _queryChannelsStreams.remove(hash); - }); + final newQueryChannelsFuture = _queryChannels( + filter: filter, + sort: sort, + options: options, + paginationParams: paginationParams, + messageLimit: messageLimit, + ); - _queryChannelsStreams[hash] = newQueryChannelsStream; + _queryChannelsStreams[hash] = newQueryChannelsFuture; - return newQueryChannelsStream; + yield await newQueryChannelsFuture; + } } - Future> _doQueryChannels({ + Future> _queryChannels({ @required Map filter, - @required List sort, - @required Map options, - @required int messageLimit, + List> sort, + Map options, + int messageLimit, PaginationParams paginationParams = const PaginationParams(limit: 10), - bool onlyOffline = false, }) async { logger.info('Query channel start'); final defaultOptions = { @@ -661,86 +670,85 @@ class StreamChatClient { payload.addAll(paginationParams.toJson()); } - if (onlyOffline) { - return _queryChannelsOffline( - filter: filter, - sort: sort, - paginationParams: paginationParams, - ); - } + final response = await get( + '/channels', + queryParameters: { + 'payload': jsonEncode(payload), + }, + ); - try { - final response = await get( - '/channels', - queryParameters: { - 'payload': jsonEncode(payload), - }, - ); + final res = decode( + response.data, + QueryChannelsResponse.fromJson, + ); - final res = decode( - response.data, - QueryChannelsResponse.fromJson, - ); + if (res.channels?.isEmpty == true && (paginationParams?.offset ?? 0) == 0) { + logger.warning('''We could not find any channel for this query. + Please make sure to take a look at the Flutter tutorial: https://getstream.io/chat/flutter/tutorial + If your application already has users and channels, you might need to adjust your query channel as explained in the docs https://getstream.io/chat/docs/query_channels/?language=dart'''); + return []; + } - final users = res.channels - ?.expand((channel) => channel.members.map((member) => member.user)) - ?.toList(); + final channels = res.channels; - if (users != null) { - state._updateUsers(users); - } + final users = channels + .expand((it) => it.members) + .map((it) => it.user) + .toList(growable: false); - logger.info('Got ${res.channels?.length} channels from api'); + state._updateUsers(users); - if (res.channels?.isEmpty != false && - (paginationParams?.offset ?? 0) == 0) { - logger.warning('''We could not find any channel for this query. - Please make sure to take a look at the Flutter tutorial: https://getstream.io/chat/flutter/tutorial - If your application already has users and channels, you might need to adjust your query channel as explained in the docs https://getstream.io/chat/docs/query_channels/?language=dart'''); - } + logger.info('Got ${res.channels?.length} channels from api'); - final newChannels = Map.from(state.channels ?? {}); - final channels = []; - - if (res.channels != null) { - for (final channelState in res.channels) { - final channel = newChannels[channelState.channel.cid]; - if (channel != null) { - channel.state?.updateChannelState(channelState); - channels.add(channel); - } else { - final newChannel = Channel.fromState(this, channelState); - await chatPersistenceClient - ?.updateChannelState(newChannel.state.channelState); - newChannel.state?.updateChannelState(channelState); - newChannels[newChannel.cid] = newChannel; - channels.add(newChannel); - } - } - } + final updateData = _mapChannelStateToChannel(channels); - state.channels = newChannels; + await chatPersistenceClient?.updateChannelQueries( + filter, + channels.map((c) => c.channel.cid).toList(), + paginationParams?.offset == null || paginationParams.offset == 0, + ); - await chatPersistenceClient?.updateChannelQueries( - filter, - res.channels.map((c) => c.channel.cid).toList(), - paginationParams?.offset == null || paginationParams.offset == 0, - ); + state.channels = updateData.key; + return updateData.value; + } - return channels; - } catch (e) { - if (!persistenceEnabled) { - rethrow; + Future> _queryChannelsOffline({ + @required Map filter, + @required List> sort, + PaginationParams paginationParams = const PaginationParams(limit: 10), + }) async { + final offlineChannels = await chatPersistenceClient?.getChannelStates( + filter: filter, + sort: sort, + paginationParams: paginationParams, + ); + final updatedData = _mapChannelStateToChannel(offlineChannels); + state.channels = updatedData.key; + return updatedData.value; + } + + MapEntry, List> _mapChannelStateToChannel( + List channelStates, + ) { + final channels = {...state.channels ?? {}}; + final newChannels = []; + if (channelStates != null) { + for (final channelState in channelStates) { + final channel = channels[channelState.channel.cid]; + if (channel != null) { + channel.state?.updateChannelState(channelState); + newChannels.add(channel); + } else { + final newChannel = Channel.fromState(this, channelState); + channels[newChannel.cid] = newChannel; + newChannels.add(newChannel); + } } - return _queryChannelsOffline( - filter: filter, - sort: sort, - paginationParams: paginationParams, - ); } + return MapEntry(channels, newChannels); } - dynamic _parseError(DioError error) { + Object _parseError(DioError error) { if (error.type == DioErrorType.RESPONSE) { final apiError = ApiError(error.response?.data, error.response?.statusCode); @@ -751,39 +759,6 @@ class StreamChatClient { return error; } - Future> _queryChannelsOffline({ - @required Map filter, - @required List sort, - PaginationParams paginationParams = const PaginationParams(limit: 10), - }) async { - final offlineChannels = await chatPersistenceClient?.getChannelStates( - filter: filter, - sort: sort, - paginationParams: paginationParams, - ) ?? - []; - final newChannels = Map.from(state.channels ?? {}); - logger.info('Got ${offlineChannels.length} channels from storage'); - final channels = offlineChannels.map((channelState) { - final channel = newChannels[channelState.channel.cid]; - if (channel != null) { - channel.state?.updateChannelState(channelState); - return channel; - } else { - final newChannel = Channel.fromState(this, channelState); - chatPersistenceClient - ?.updateChannelState(newChannel.state.channelState); - newChannels[newChannel.cid] = newChannel; - return newChannel; - } - }).toList(); - - if (channels.isNotEmpty) { - state.channels = newChannels; - } - return channels; - } - /// Handy method to make http GET request with error parsing. Future> get( String path, { @@ -1448,18 +1423,16 @@ class ClientState { _userController.add(user); } - void _updateUsers(List users) { - users?.forEach(_updateUser); - } - - void _updateUser(User user) { + void _updateUsers(List userList) { final newUsers = { ...users ?? {}, - user.id: user, + for (var user in userList) user.id: user, }; _usersController.add(newUsers); } + void _updateUser(User user) => _updateUsers([user]); + /// The current user OwnUser get user => _userController.value; diff --git a/packages/stream_chat/lib/src/db/chat_persistence_client.dart b/packages/stream_chat/lib/src/db/chat_persistence_client.dart index 9c31eca93..e95dadb25 100644 --- a/packages/stream_chat/lib/src/db/chat_persistence_client.dart +++ b/packages/stream_chat/lib/src/db/chat_persistence_client.dart @@ -68,23 +68,19 @@ abstract class ChatPersistenceClient { PaginationParams messagePagination, PaginationParams pinnedMessagePagination, }) async { - final members = await getMembersByCid(cid); - final reads = await getReadsByCid(cid); - final channel = await getChannelByCid(cid); - final messages = await getMessagesByCid( - cid, - messagePagination: messagePagination, - ); - final pinnedMessages = await getPinnedMessagesByCid( - cid, - messagePagination: pinnedMessagePagination, - ); + final data = await Future.wait([ + getMembersByCid(cid), + getReadsByCid(cid), + getChannelByCid(cid), + getMessagesByCid(cid, messagePagination: messagePagination), + getPinnedMessagesByCid(cid,messagePagination: pinnedMessagePagination), + ]); return ChannelState( - members: members, - read: reads, - messages: messages, - pinnedMessages: pinnedMessages, - channel: channel, + members: data[0], + read: data[1], + channel: data[2], + messages: data[3], + pinnedMessages: data[4], ); } @@ -94,7 +90,7 @@ abstract class ChatPersistenceClient { /// for filtering out states. Future> getChannelStates({ Map filter, - List sort = const [], + List> sort = const [], PaginationParams paginationParams, }); diff --git a/packages/stream_chat/lib/src/models/channel_state.dart b/packages/stream_chat/lib/src/models/channel_state.dart index 36d68a0c8..c2a3d48c5 100644 --- a/packages/stream_chat/lib/src/models/channel_state.dart +++ b/packages/stream_chat/lib/src/models/channel_state.dart @@ -8,7 +8,7 @@ import 'message.dart'; part 'channel_state.g.dart'; -/// The class that contains the information about a command +/// The class that contains the information about a channel @JsonSerializable() class ChannelState { /// The channel to which this state belongs diff --git a/packages/stream_chat/test/version_test.dart b/packages/stream_chat/test/version_test.dart index ada2a85c8..f5b2a2c0a 100644 --- a/packages/stream_chat/test/version_test.dart +++ b/packages/stream_chat/test/version_test.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:rxdart/rxdart.dart'; import 'package:test/test.dart'; import 'package:stream_chat/version.dart'; @@ -10,14 +11,113 @@ void prepareTest() { } } -void main() { - prepareTest(); - test('stream chat version matches pubspec', () { - final String pubspecPath = '${Directory.current.path}/pubspec.yaml'; - final String pubspec = File(pubspecPath).readAsStringSync(); - final RegExp regex = RegExp('version:\s*(.*)'); - final RegExpMatch match = regex.firstMatch(pubspec); - expect(match, isNotNull); - expect(PACKAGE_VERSION, match.group(1).trim()); - }); +// void main() { +// prepareTest(); +// test('stream chat version matches pubspec', () { +// final String pubspecPath = '${Directory.current.path}/pubspec.yaml'; +// final String pubspec = File(pubspecPath).readAsStringSync(); +// final RegExp regex = RegExp('version:\s*(.*)'); +// final RegExpMatch match = regex.firstMatch(pubspec); +// expect(match, isNotNull); +// expect(PACKAGE_VERSION, match.group(1).trim()); +// }); +// } + +void main() async { + var items = [ + ABC('Sahil', 22, 62.0), + ABC('Devraj', 23, 76.0), + ABC('Harsh', 18, 48.0), + ABC('Harsh', 17, 88.0), + ABC('Harsh', 17, 48.0), + ABC('Devraj', 23, 74.0, { + 'Test': 'Sahil', + }), + ABC('Devraj', 12, 76.0, { + 'Test': 'Avni', + }), + ]; + + var comparators = [ + (ABC a, ABC b) { + // if (a.extraData == null) return -1; + // if (b.extraData == null) return 1; + // if (a.extraData == null && b.extraData == null) return 0; + var aa = (a.extraData ?? {})['Test'] as String; + var bb = (b.extraData ?? {})['Test'] as String; + return aa.compareTo(bb); + }, + // (ABC a, ABC b) => a.name.compareTo(b.name), + // (ABC a, ABC b) => a.age.compareTo(b.age), + // (ABC a, ABC b) => a.weight.compareTo(b.weight), + ]; + + // for (var comp in comparators.reversed) { + // items.sort(comp); + // } + + // items.sort(comparators[last]) + + Stream getLaugh2() { + if (true) { + return Stream.value('HEHO'); + } else { + return getLaugh2(); + } + } + + Stream getLaugh() async* { + yield 'HAHA'; + await Future.delayed(const Duration(seconds: 3)); + yield 'HOHO'; + await Future.delayed(const Duration(seconds: 3)); + yield 'HEHE'; + } + + // final stream = BehaviorSubject.seeded('HAHA'); + // + // stream.('HOHO'); + + await for (var value in getLaugh2()) { + print(value); + } + + // stream.add('HEHE'); + + // compare(ABC a, ABC b) { + // int result; + // for (final comparator in comparators) { + // try { + // result = comparator(a, b); + // } catch (e) { + // result = 0; + // } + // if (result != 0) return result; + // } + // return 0; + // } + // + // items.sort(compare); + // + // print(items); +} + +class ABC { + final String name; + final int age; + final double weight; + final Map extraData; + + const ABC(this.name, this.age, this.weight, [this.extraData]); + + @override + String toString() { + return ''' + \n + Name : $name, + Age : $age, + Weight : $weight, + ExtraData : $extraData, + '''; + } } diff --git a/packages/stream_chat_flutter/lib/src/channel_info.dart b/packages/stream_chat_flutter/lib/src/channel_info.dart index 4244c910b..53a621a03 100644 --- a/packages/stream_chat_flutter/lib/src/channel_info.dart +++ b/packages/stream_chat_flutter/lib/src/channel_info.dart @@ -49,8 +49,11 @@ class ChannelInfo extends StatelessWidget { var alternativeWidget; if (channel.memberCount != null && channel.memberCount > 2) { + var text = '${channel.memberCount} Members'; + final watcherCount = channel.state.watcherCount ?? 0; + if (watcherCount > 0) text += ' $watcherCount Online'; alternativeWidget = Text( - '${channel.memberCount} Members, ${channel.state.watcherCount} Online', + text, style: StreamChatTheme.of(context) .channelTheme .channelHeaderTheme diff --git a/packages/stream_chat_flutter/lib/src/channel_list_view.dart b/packages/stream_chat_flutter/lib/src/channel_list_view.dart index 90b40be93..7f9818a71 100644 --- a/packages/stream_chat_flutter/lib/src/channel_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/channel_list_view.dart @@ -99,7 +99,7 @@ class ChannelListView extends StatefulWidget { /// Sorting is based on field and direction, multiple sorting options can be provided. /// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count. /// Direction can be ascending or descending. - final List sort; + final List> sort; /// Pagination parameters /// limit: the number of channels to return (max is 30) @@ -159,54 +159,42 @@ class ChannelListView extends StatefulWidget { _ChannelListViewState createState() => _ChannelListViewState(); } -class _ChannelListViewState extends State - with WidgetsBindingObserver { - final ScrollController _scrollController = ScrollController(); - final SlidableController _slideController = SlidableController(); - final ChannelListController _channelListController = ChannelListController(); +class _ChannelListViewState extends State { + final _slideController = SlidableController(); + + final _channelListController = ChannelListController(); @override Widget build(BuildContext context) { - var child = ChannelListCore( - channelListController: _channelListController, - listBuilder: widget.listBuilder ?? - (context, list) { - return _buildListView(list); - }, - emptyBuilder: widget.emptyBuilder ?? - (BuildContext context) { - return _buildEmptyWidget(); - }, - errorBuilder: widget.errorBuilder ?? - (BuildContext context, dynamic error) { - return _buildErrorWidget(context); - }, - loadingBuilder: widget.loadingBuilder ?? - (BuildContext context) { - return _buildLoadingWidget(); - }, + Widget child = ChannelListCore( pagination: widget.pagination, options: widget.options, sort: widget.sort, filter: widget.filter, + channelListController: _channelListController, + listBuilder: widget.listBuilder ?? _buildListView, + emptyBuilder: widget.emptyBuilder ?? _buildEmptyWidget, + errorBuilder: widget.errorBuilder ?? _buildErrorWidget, + loadingBuilder: widget.loadingBuilder ?? _buildLoadingWidget, ); - if (!widget.pullToRefresh) { - return child; - } else { - return RefreshIndicator( - onRefresh: () async { - _channelListController.loadData(); - }, + if (widget.pullToRefresh) { + child = RefreshIndicator( + onRefresh: () async => _channelListController.loadData(), child: child, ); } + + return LazyLoadScrollView( + onEndOfPage: () async { + _channelListController.paginateData(); + }, + child: child, + ); } - Widget _buildListView( - List channels, - ) { - var child; + Widget _buildListView(BuildContext context, List channels) { + Widget child; if (channels.isNotEmpty) { if (widget.crossAxisCount > 1) { @@ -216,7 +204,6 @@ class _ChannelListViewState extends State crossAxisCount: widget.crossAxisCount), itemCount: channels.length, physics: AlwaysScrollableScrollPhysics(), - controller: _scrollController, itemBuilder: (context, index) { return _gridItemBuilder(context, index, channels); }, @@ -236,18 +223,17 @@ class _ChannelListViewState extends State itemBuilder: (context, index) { return _listItemBuilder(context, index, channels); }, - controller: _scrollController, ); } } return AnimatedSwitcher( child: child, - duration: Duration(milliseconds: 500), + duration: const Duration(milliseconds: 500), ); } - Widget _buildEmptyWidget() { + Widget _buildEmptyWidget(BuildContext context) { return LayoutBuilder( builder: (context, viewportConstraints) { return SingleChildScrollView( @@ -326,7 +312,7 @@ class _ChannelListViewState extends State ); } - Widget _buildLoadingWidget() { + Widget _buildLoadingWidget(BuildContext context) { return ListView( padding: widget.padding, physics: AlwaysScrollableScrollPhysics(), @@ -341,13 +327,13 @@ class _ChannelListViewState extends State return _separatorBuilder(context, i); } } - return _buildLoadingItem(); + return _buildLoadingItem(context); }, ), ); } - Shimmer _buildLoadingItem() { + Shimmer _buildLoadingItem(BuildContext context) { if (widget.crossAxisCount > 1) { return Shimmer.fromColors( baseColor: StreamChatTheme.of(context).colorTheme.greyGainsboro, @@ -443,9 +429,7 @@ class _ChannelListViewState extends State } } - Widget _buildErrorWidget( - BuildContext context, - ) { + Widget _buildErrorWidget(BuildContext context, Object error) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -572,23 +556,13 @@ class _ChannelListViewState extends State ], child: Container( color: StreamChatTheme.of(context).colorTheme.whiteSnow, - child: widget.channelPreviewBuilder != null - ? widget.channelPreviewBuilder( - context, - channel, - ) - : ChannelPreview( - onLongPress: widget.onChannelLongPress, - channel: channel, - onImageTap: widget.onImageTap != null - ? () { - widget.onImageTap(channel); - } - : null, - onTap: (channel) { - onTap(channel, widget.channelWidget); - }, - ), + child: widget.channelPreviewBuilder?.call(context, channel) ?? + ChannelPreview( + onLongPress: widget.onChannelLongPress, + channel: channel, + onImageTap: widget.onImageTap?.call(channel), + onTap: (channel) => onTap(channel, widget.channelWidget), + ), ), ); }, @@ -618,9 +592,7 @@ class _ChannelListViewState extends State width: 64, height: 64, ), - onTap: () { - widget.onChannelTap(channel, null); - }, + onTap: () => widget.onChannelTap(channel, null), ), SizedBox(height: 7), Padding( @@ -680,70 +652,4 @@ class _ChannelListViewState extends State color: effect.color.withOpacity(effect.alpha ?? 1.0), ); } - - void _listenChannelPagination(ChannelsBlocState channelsProvider) { - if (_scrollController.position.maxScrollExtent == - _scrollController.offset && - _scrollController.offset != 0) { - _channelListController.paginateData(); - } - } - - StreamSubscription _subscription; - - @override - void initState() { - super.initState(); - - WidgetsBinding.instance.addObserver(this); - - final channelsBloc = ChannelsBloc.of(context); - channelsBloc.queryChannels( - filter: widget.filter, - sortOptions: widget.sort, - paginationParams: widget.pagination, - options: widget.options, - ); - - _scrollController.addListener(() { - channelsBloc.queryChannelsLoading.first.then((loading) { - if (!loading) { - _listenChannelPagination(channelsBloc); - } - }); - }); - - final client = StreamChat.of(context).client; - - _subscription = client - .on( - EventType.connectionRecovered, - EventType.notificationAddedToChannel, - EventType.notificationMessageNew, - EventType.channelVisible, - ) - .listen((event) { - _channelListController.loadData(); - }); - } - - @override - void didUpdateWidget(ChannelListView oldWidget) { - super.didUpdateWidget(oldWidget); - - if (widget.filter?.toString() != oldWidget.filter?.toString() || - jsonEncode(widget.sort) != jsonEncode(oldWidget.sort) || - widget.pagination?.toJson()?.toString() != - oldWidget.pagination?.toJson()?.toString() || - widget.options?.toString() != oldWidget.options?.toString()) { - _channelListController.loadData(); - } - } - - @override - void dispose() { - _subscription.cancel(); - WidgetsBinding.instance.removeObserver(this); - super.dispose(); - } } diff --git a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart index 18a8e477a..acc9f8024 100644 --- a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart @@ -106,7 +106,7 @@ class ChannelListCore extends StatefulWidget { /// Sorting is based on field and direction, multiple sorting options can be provided. /// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count. /// Direction can be ascending or descending. - final List sort; + final List> sort; /// Pagination parameters /// limit: the number of channels to return (max is 30) @@ -118,8 +118,7 @@ class ChannelListCore extends StatefulWidget { _ChannelListCoreState createState() => _ChannelListCoreState(); } -class _ChannelListCoreState extends State - with WidgetsBindingObserver { +class _ChannelListCoreState extends State { @override Widget build(BuildContext context) { final channelsBloc = ChannelsBloc.of(context); @@ -133,34 +132,21 @@ class _ChannelListCoreState extends State return StreamBuilder>( stream: channelsBlocState.channelsStream, builder: (context, snapshot) { - var child; if (snapshot.hasError) { - child = _buildErrorWidget( - snapshot, - context, - channelsBlocState, - ); - } else if (!snapshot.hasData) { - child = _buildLoadingWidget(); - } else { - final channels = snapshot.data; - - child = widget.emptyBuilder(context); - - if (channels.isNotEmpty) { - return widget.listBuilder(context, channels); - } + return _buildErrorWidget(snapshot, context, channelsBlocState); } - - return child; + if (!snapshot.hasData) { + return widget.loadingBuilder(context); + } + final channels = snapshot.data; + if (channels.isEmpty) { + return widget.emptyBuilder(context); + } + return widget.listBuilder(context, channels); }, ); } - Widget _buildLoadingWidget() { - return widget.loadingBuilder(context); - } - Widget _buildErrorWidget( AsyncSnapshot> snapshot, BuildContext context, @@ -197,39 +183,21 @@ class _ChannelListCoreState extends State ); } - StreamSubscription _subscription; + StreamSubscription _subscription; @override void initState() { super.initState(); - - WidgetsBinding.instance.addObserver(this); - - final channelsBloc = ChannelsBloc.of(context); - channelsBloc.queryChannels( - filter: widget.filter, - sortOptions: widget.sort, - paginationParams: widget.pagination, - options: widget.options, - ); - + loadData(); final client = StreamChatCore.of(context).client; - _subscription = client .on( - EventType.connectionRecovered, - EventType.notificationAddedToChannel, - EventType.notificationMessageNew, - EventType.channelVisible, - ) - .listen((event) { - channelsBloc.queryChannels( - filter: widget.filter, - sortOptions: widget.sort, - paginationParams: widget.pagination, - options: widget.options, - ); - }); + EventType.connectionRecovered, + EventType.notificationAddedToChannel, + EventType.notificationMessageNew, + EventType.channelVisible, + ) + .listen((event) => loadData()); if (widget.channelListController != null) { widget.channelListController.loadData = loadData; @@ -246,20 +214,13 @@ class _ChannelListCoreState extends State widget.pagination?.toJson()?.toString() != oldWidget.pagination?.toJson()?.toString() || widget.options?.toString() != oldWidget.options?.toString()) { - final channelsBloc = ChannelsBloc.of(context); - channelsBloc.queryChannels( - filter: widget.filter, - sortOptions: widget.sort, - paginationParams: widget.pagination, - options: widget.options, - ); + loadData(); } } @override void dispose() { _subscription.cancel(); - WidgetsBinding.instance.removeObserver(this); super.dispose(); } } diff --git a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart index 7009234b7..836e57c8f 100644 --- a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart @@ -84,7 +84,7 @@ class ChannelsBlocState extends State /// Calls [client.queryChannels] updating [queryChannelsLoading] stream Future queryChannels({ Map filter, - List sortOptions, + List> sortOptions, PaginationParams paginationParams, Map options, bool onlyOffline = false, @@ -102,21 +102,21 @@ class ChannelsBlocState extends State paginationParams.offset == null || paginationParams.offset == 0; final oldChannels = List.from(channels ?? []); - final _channels = await client.queryChannels( + await for (final channels in client.queryChannels( filter: filter, sort: sortOptions, options: options, paginationParams: paginationParams, - onlyOffline: onlyOffline, - ); - - if (clear) { - _channelsController.add(_channels); - } else { - final l = oldChannels + _channels; - _channelsController.add(l); + preferOffline: onlyOffline, + )) { + if (clear) { + _channelsController.add(channels); + } else { + final l = oldChannels + channels; + _channelsController.add(l); + } + _queryChannelsLoadingController.sink.add(false); } - _queryChannelsLoadingController.sink.add(false); } catch (err, stackTrace) { print(err); print(stackTrace); diff --git a/packages/stream_chat_persistence/example/pubspec.yaml b/packages/stream_chat_persistence/example/pubspec.yaml index 5008566b0..2586bea65 100644 --- a/packages/stream_chat_persistence/example/pubspec.yaml +++ b/packages/stream_chat_persistence/example/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.0 - stream_chat: + stream_chat: path: ../../stream_chat stream_chat_persistence: path: ../ diff --git a/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart b/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart index 83743debf..7d4af560f 100644 --- a/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart @@ -15,9 +15,7 @@ part 'channel_query_dao.g.dart'; class ChannelQueryDao extends DatabaseAccessor with _$ChannelQueryDaoMixin { /// Creates a new channel query dao instance - ChannelQueryDao(this._db) : super(_db); - - final MoorChatDatabase _db; + ChannelQueryDao(MoorChatDatabase db) : super(db); String _computeHash(Map filter) { if (filter == null) { @@ -37,19 +35,19 @@ class ChannelQueryDao extends DatabaseAccessor ) async { final hash = _computeHash(filter); if (clearQueryCache) { - await (delete(channelQueries) - ..where((query) => query.queryHash.equals(hash))) - .go(); + await batch((it) { + it.deleteWhere( + channelQueries, + (c) => c.queryHash.equals(hash), + ); + }); } - return batch((batch) { - batch.insertAll( + return batch((it) { + it.insertAll( channelQueries, cids.map((cid) { - return ChannelQueryEntity( - queryHash: hash, - channelCid: cid, - ); + return ChannelQueryEntity(queryHash: hash, channelCid: cid); }).toList(), mode: InsertMode.insertOrReplace, ); @@ -57,70 +55,74 @@ class ChannelQueryDao extends DatabaseAccessor } /// Get list of channels by filter, sort and paginationParams - Future> getChannelStates({ + Future> getChannels({ Map filter, - List sort = const [], + List> sort = const [], PaginationParams paginationParams, }) async { + assert(() { + if (sort != null && sort.any((it) => it.comparator == null)) { + throw ArgumentError( + 'SortOption requires a comparator in order to sort', + ); + } + return true; + }()); + final hash = _computeHash(filter); - final cachedChannels = await Future.wait(await (select(channelQueries) + final cachedChannelCids = await (select(channelQueries) ..where((c) => c.queryHash.equals(hash))) - .get() - .then((channelQueries) { - final cids = channelQueries.map((c) => c.channelCid).toList(); - final query = select(channels)..where((c) => c.cid.isIn(cids)); - - sort = sort - ?.where((s) => ChannelModel.topLevelFields.contains(s.field)) - ?.toList(); - - if (sort != null && sort.isNotEmpty) { - query.orderBy(sort.map((s) { - final orderExpression = CustomExpression('channels.${s.field}'); - return (c) => OrderingTerm( - expression: orderExpression, - mode: s.direction == 1 ? OrderingMode.asc : OrderingMode.desc, - ); - }).toList()); - } + .map((c) => c.channelCid) + .get(); + + final query = select(channels)..where((c) => c.cid.isIn(cachedChannelCids)); + + final cachedChannels = await (query.join([ + leftOuterJoin(users, channels.createdById.equalsExp(users.id)), + ]).map((row) { + final createdByEntity = row.readTable(users); + final channelEntity = row.readTable(channels); + return channelEntity.toChannelModel(createdBy: createdByEntity?.toUser()); + })).get(); + + final possibleSortingFields = cachedChannels.fold>( + ChannelModel.topLevelFields, (previousValue, element) { + return {...previousValue, ...element.extraData.keys}.toList(); + }); - if (paginationParams != null) { - query.limit( - paginationParams.limit ?? 10, - offset: paginationParams.offset, - ); - } + sort = sort + ?.where((s) => possibleSortingFields.contains(s.field)) + ?.toList(growable: false); + + Comparator chainedComparator = (a, b) { + final dateA = a.lastMessageAt ?? a.createdAt; + final dateB = b.lastMessageAt ?? b.createdAt; + return dateB.compareTo(dateA); + }; + + if (sort != null && sort.isNotEmpty) { + chainedComparator = (a, b) { + int result; + for (final comparator in sort.map((it) => it.comparator)) { + try { + result = comparator(a, b); + } catch (e) { + result = 0; + } + if (result != 0) return result; + } + return 0; + }; + } - return query.join([ - leftOuterJoin(users, channels.createdById.equalsExp(users.id)), - ]).map((row) async { - final userEntity = row.readTable(users); - final channelEntity = row.readTable(channels); - - final cid = channelEntity.cid; - final members = await _db.memberDao.getMembersByCid(cid); - final reads = await _db.readDao.getReadsByCid(cid); - final messages = await _db.messageDao.getMessagesByCid(cid); - final pinnedMessages = await _db.pinnedMessageDao.getMessagesByCid(cid); - - return channelEntity.toChannelState( - createdBy: userEntity?.toUser(), - members: members, - reads: reads, - messages: messages, - pinnedMessages: pinnedMessages, - ); - }).get(); - })); - - if (sort?.isEmpty != false && cachedChannels?.isNotEmpty == true) { - cachedChannels - .sort((a, b) => b.channel.updatedAt.compareTo(a.channel.updatedAt)); - cachedChannels.sort((a, b) { - final dateA = a.channel.lastMessageAt ?? a.channel.createdAt; - final dateB = b.channel.lastMessageAt ?? b.channel.createdAt; - return dateB.compareTo(dateA); - }); + cachedChannels.sort(chainedComparator); + + if (paginationParams?.offset != null) { + cachedChannels.removeRange(0, paginationParams.offset); + } + + if (paginationParams?.limit != null) { + return cachedChannels.take(paginationParams.limit).toList(); } return cachedChannels; diff --git a/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart b/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart index 50d171111..e6be01ec4 100644 --- a/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart +++ b/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart @@ -161,14 +161,15 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { @override Future> getChannelStates({ Map filter, - List sort = const [], + List> sort = const [], PaginationParams paginationParams, - }) { - return _db.channelQueryDao.getChannelStates( + }) async { + final channels = await _db.channelQueryDao.getChannels( filter: filter, sort: sort, paginationParams: paginationParams, ); + return Future.wait(channels.map((e) => getChannelStateByCid(e.cid))); } @override From 771076c29c63363aac2ce19f69714eba9786bb8c Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 26 Feb 2021 18:35:26 +0530 Subject: [PATCH 31/41] [LLC -> Client] Expose `queryChannelsOnline` and `queryChannelsOffline` Signed-off-by: Sahil Kumar --- packages/stream_chat/lib/src/client.dart | 12 +++++++----- .../lib/src/db/chat_persistence_client.dart | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/stream_chat/lib/src/client.dart b/packages/stream_chat/lib/src/client.dart index 55212b895..aa93e480a 100644 --- a/packages/stream_chat/lib/src/client.dart +++ b/packages/stream_chat/lib/src/client.dart @@ -491,7 +491,7 @@ class StreamChatClient { if (status == ConnectionStatus.connected && state.channels?.isNotEmpty == true) { - unawaited(_queryChannels(filter: { + unawaited(queryChannelsOnline(filter: { 'cid': { '\$in': state.channels.keys.toList(), }, @@ -615,7 +615,7 @@ class StreamChatClient { yield await _queryChannelsStreams[hash]; } else { if (true) { - final channels = await _queryChannelsOffline( + final channels = await queryChannelsOffline( filter: filter, sort: sort, paginationParams: paginationParams, @@ -623,7 +623,7 @@ class StreamChatClient { if (channels.isNotEmpty) yield channels; } - final newQueryChannelsFuture = _queryChannels( + final newQueryChannelsFuture = queryChannelsOnline( filter: filter, sort: sort, options: options, @@ -637,7 +637,8 @@ class StreamChatClient { } } - Future> _queryChannels({ + /// Requests channels with a given query from the API. + Future> queryChannelsOnline({ @required Map filter, List> sort, Map options, @@ -712,7 +713,8 @@ class StreamChatClient { return updateData.value; } - Future> _queryChannelsOffline({ + /// Requests channels with a given query from the Persistence client. + Future> queryChannelsOffline({ @required Map filter, @required List> sort, PaginationParams paginationParams = const PaginationParams(limit: 10), diff --git a/packages/stream_chat/lib/src/db/chat_persistence_client.dart b/packages/stream_chat/lib/src/db/chat_persistence_client.dart index e95dadb25..da99f34e4 100644 --- a/packages/stream_chat/lib/src/db/chat_persistence_client.dart +++ b/packages/stream_chat/lib/src/db/chat_persistence_client.dart @@ -73,7 +73,7 @@ abstract class ChatPersistenceClient { getReadsByCid(cid), getChannelByCid(cid), getMessagesByCid(cid, messagePagination: messagePagination), - getPinnedMessagesByCid(cid,messagePagination: pinnedMessagePagination), + getPinnedMessagesByCid(cid, messagePagination: pinnedMessagePagination), ]); return ChannelState( members: data[0], From 81c2a514fbe939eff4dbed16b57381f35dd76f2b Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 26 Feb 2021 18:46:51 +0530 Subject: [PATCH 32/41] [LLC -> Client] Change queryChannel `preferOffline` default value to false Signed-off-by: Sahil Kumar --- packages/stream_chat/lib/src/client.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/stream_chat/lib/src/client.dart b/packages/stream_chat/lib/src/client.dart index aa93e480a..782b7056e 100644 --- a/packages/stream_chat/lib/src/client.dart +++ b/packages/stream_chat/lib/src/client.dart @@ -586,7 +586,7 @@ class StreamChatClient { Map options, PaginationParams paginationParams = const PaginationParams(limit: 10), int messageLimit, - bool preferOffline = true, + bool preferOffline = false, bool waitForConnect = true, }) async* { if (waitForConnect) { @@ -614,7 +614,7 @@ class StreamChatClient { if (_queryChannelsStreams.containsKey(hash)) { yield await _queryChannelsStreams[hash]; } else { - if (true) { + if (preferOffline) { final channels = await queryChannelsOffline( filter: filter, sort: sort, From 46410ede5020f2a9f87596004871db438e4a3a16 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 26 Feb 2021 18:48:46 +0530 Subject: [PATCH 33/41] [UI-Kit -> ChannelListCore] Replace ChannelListController function signature with `AsyncCallback` for better refreshIndicator support. Signed-off-by: Sahil Kumar --- .../lib/src/channel_list_view.dart | 10 +++------- .../lib/src/channel_list_core.dart | 14 ++++++-------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/channel_list_view.dart b/packages/stream_chat_flutter/lib/src/channel_list_view.dart index 7f9818a71..9edd39ff6 100644 --- a/packages/stream_chat_flutter/lib/src/channel_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/channel_list_view.dart @@ -180,15 +180,13 @@ class _ChannelListViewState extends State { if (widget.pullToRefresh) { child = RefreshIndicator( - onRefresh: () async => _channelListController.loadData(), + onRefresh: () => _channelListController.loadData(), child: child, ); } return LazyLoadScrollView( - onEndOfPage: () async { - _channelListController.paginateData(); - }, + onEndOfPage: () => _channelListController.paginateData(), child: child, ); } @@ -451,9 +449,7 @@ class _ChannelListViewState extends State { style: Theme.of(context).textTheme.headline6, ), FlatButton( - onPressed: () { - _channelListController.loadData(); - }, + onPressed: () => _channelListController.loadData(), child: Text('Retry'), ), ], diff --git a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart index acc9f8024..816bb052c 100644 --- a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart @@ -159,10 +159,9 @@ class _ChannelListCoreState extends State { return widget.errorBuilder(context, snapshot.error); } - void loadData() { + Future loadData() { final channelsBloc = ChannelsBloc.of(context); - - channelsBloc.queryChannels( + return channelsBloc.queryChannels( filter: widget.filter, sortOptions: widget.sort, paginationParams: widget.pagination, @@ -170,10 +169,9 @@ class _ChannelListCoreState extends State { ); } - void paginateData() { + Future paginateData() { final channelsBloc = ChannelsBloc.of(context); - - channelsBloc.queryChannels( + return channelsBloc.queryChannels( filter: widget.filter, sortOptions: widget.sort, paginationParams: widget.pagination.copyWith( @@ -229,10 +227,10 @@ class _ChannelListCoreState extends State { class ChannelListController { /// This function calls Stream's servers to load a list of channels. If there is existing data, /// calling this function causes a reload. - VoidCallback loadData; + AsyncCallback loadData; /// This function is used to load another page of data. Note, [loadData] should be /// used to populate the initial page of data. Calling [paginateData] performs a query /// to load subsequent pages. - VoidCallback paginateData; + AsyncCallback paginateData; } From 4368327139c26e428b4e182b1eb4c032d86ff61f Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 26 Feb 2021 18:52:29 +0530 Subject: [PATCH 34/41] [LLC] Remove unwanted testing code Signed-off-by: Sahil Kumar --- packages/stream_chat/test/version_test.dart | 119 ++------------------ 1 file changed, 10 insertions(+), 109 deletions(-) diff --git a/packages/stream_chat/test/version_test.dart b/packages/stream_chat/test/version_test.dart index f5b2a2c0a..55b1818a8 100644 --- a/packages/stream_chat/test/version_test.dart +++ b/packages/stream_chat/test/version_test.dart @@ -11,113 +11,14 @@ void prepareTest() { } } -// void main() { -// prepareTest(); -// test('stream chat version matches pubspec', () { -// final String pubspecPath = '${Directory.current.path}/pubspec.yaml'; -// final String pubspec = File(pubspecPath).readAsStringSync(); -// final RegExp regex = RegExp('version:\s*(.*)'); -// final RegExpMatch match = regex.firstMatch(pubspec); -// expect(match, isNotNull); -// expect(PACKAGE_VERSION, match.group(1).trim()); -// }); -// } - -void main() async { - var items = [ - ABC('Sahil', 22, 62.0), - ABC('Devraj', 23, 76.0), - ABC('Harsh', 18, 48.0), - ABC('Harsh', 17, 88.0), - ABC('Harsh', 17, 48.0), - ABC('Devraj', 23, 74.0, { - 'Test': 'Sahil', - }), - ABC('Devraj', 12, 76.0, { - 'Test': 'Avni', - }), - ]; - - var comparators = [ - (ABC a, ABC b) { - // if (a.extraData == null) return -1; - // if (b.extraData == null) return 1; - // if (a.extraData == null && b.extraData == null) return 0; - var aa = (a.extraData ?? {})['Test'] as String; - var bb = (b.extraData ?? {})['Test'] as String; - return aa.compareTo(bb); - }, - // (ABC a, ABC b) => a.name.compareTo(b.name), - // (ABC a, ABC b) => a.age.compareTo(b.age), - // (ABC a, ABC b) => a.weight.compareTo(b.weight), - ]; - - // for (var comp in comparators.reversed) { - // items.sort(comp); - // } - - // items.sort(comparators[last]) - - Stream getLaugh2() { - if (true) { - return Stream.value('HEHO'); - } else { - return getLaugh2(); - } - } - - Stream getLaugh() async* { - yield 'HAHA'; - await Future.delayed(const Duration(seconds: 3)); - yield 'HOHO'; - await Future.delayed(const Duration(seconds: 3)); - yield 'HEHE'; - } - - // final stream = BehaviorSubject.seeded('HAHA'); - // - // stream.('HOHO'); - - await for (var value in getLaugh2()) { - print(value); - } - - // stream.add('HEHE'); - - // compare(ABC a, ABC b) { - // int result; - // for (final comparator in comparators) { - // try { - // result = comparator(a, b); - // } catch (e) { - // result = 0; - // } - // if (result != 0) return result; - // } - // return 0; - // } - // - // items.sort(compare); - // - // print(items); -} - -class ABC { - final String name; - final int age; - final double weight; - final Map extraData; - - const ABC(this.name, this.age, this.weight, [this.extraData]); - - @override - String toString() { - return ''' - \n - Name : $name, - Age : $age, - Weight : $weight, - ExtraData : $extraData, - '''; - } +void main() { + prepareTest(); + test('stream chat version matches pubspec', () { + final String pubspecPath = '${Directory.current.path}/pubspec.yaml'; + final String pubspec = File(pubspecPath).readAsStringSync(); + final RegExp regex = RegExp('version:\s*(.*)'); + final RegExpMatch match = regex.firstMatch(pubspec); + expect(match, isNotNull); + expect(PACKAGE_VERSION, match.group(1).trim()); + }); } From d0abad3a3b697267539835efa58dce6cbd8aa3e3 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 26 Feb 2021 19:33:14 +0530 Subject: [PATCH 35/41] [LLC] Fix tests and minor refactoring Signed-off-by: Sahil Kumar --- packages/stream_chat/lib/src/client.dart | 34 ++++++++----------- .../stream_chat/test/src/client_test.dart | 17 ++++++---- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/packages/stream_chat/lib/src/client.dart b/packages/stream_chat/lib/src/client.dart index 782b7056e..2c36f4f61 100644 --- a/packages/stream_chat/lib/src/client.dart +++ b/packages/stream_chat/lib/src/client.dart @@ -589,24 +589,6 @@ class StreamChatClient { bool preferOffline = false, bool waitForConnect = true, }) async* { - if (waitForConnect) { - if (_connectCompleter != null && !_connectCompleter.isCompleted) { - logger.info('awaiting connection completer'); - await _connectCompleter.future; - } - if (wsConnectionStatus != ConnectionStatus.connected) { - final errorMessage = - 'You cannot use queryChannels without an active connection. Please call `connectUser` to connect the client.'; - if (persistenceEnabled) { - logger.warning( - '$errorMessage\nTrying to retrieve channels from the offline storage.'); - preferOffline = true; - } else { - throw Exception(errorMessage); - } - } - } - final hash = base64.encode(utf8.encode( '$filter${_asMap(sort)}$options${paginationParams?.toJson()}$messageLimit$preferOffline', )); @@ -644,7 +626,21 @@ class StreamChatClient { Map options, int messageLimit, PaginationParams paginationParams = const PaginationParams(limit: 10), + bool waitForConnect = true, }) async { + if (waitForConnect) { + if (_connectCompleter != null && !_connectCompleter.isCompleted) { + logger.info('awaiting connection completer'); + await _connectCompleter.future; + } + if (wsConnectionStatus != ConnectionStatus.connected) { + throw Exception( + 'You cannot use queryChannels without an active connection.' + ' Please call `connectUser` to connect the client.', + ); + } + } + logger.info('Query channel start'); final defaultOptions = { 'state': true, @@ -683,7 +679,7 @@ class StreamChatClient { QueryChannelsResponse.fromJson, ); - if (res.channels?.isEmpty == true && (paginationParams?.offset ?? 0) == 0) { + if ((res.channels ?? []).isEmpty && (paginationParams?.offset ?? 0) == 0) { logger.warning('''We could not find any channel for this query. Please make sure to take a look at the Flutter tutorial: https://getstream.io/chat/flutter/tutorial If your application already has users and channels, you might need to adjust your query channel as explained in the docs https://getstream.io/chat/docs/query_channels/?language=dart'''); diff --git a/packages/stream_chat/test/src/client_test.dart b/packages/stream_chat/test/src/client_test.dart index b38185b57..1cdddbf0b 100644 --- a/packages/stream_chat/test/src/client_test.dart +++ b/packages/stream_chat/test/src/client_test.dart @@ -10,6 +10,7 @@ import 'package:stream_chat/src/client.dart'; import 'package:stream_chat/src/exceptions.dart'; import 'package:stream_chat/src/models/message.dart'; import 'package:stream_chat/src/models/user.dart'; +import 'package:stream_chat/src/models/channel_model.dart'; import 'package:test/test.dart'; class MockDio extends Mock implements DioForNative {} @@ -86,7 +87,7 @@ void main() { }); }); - group('queryChannels', () { + group('queryChannelsOnline', () { test('should pass right default parameters', () async { final mockDio = MockDio(); @@ -106,13 +107,14 @@ void main() { "watch": true, "presence": false, "limit": 10, + "offset": 0, }), }; when(mockDio.get('/channels', queryParameters: queryParams)) .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); - await client.queryChannels(waitForConnect: false); + await client.queryChannelsOnline(filter: null, waitForConnect: false); verify(mockDio.get('/channels', queryParameters: queryParams)) .called(1); @@ -134,7 +136,7 @@ void main() { "\$in": ["test"], }, }; - final sortOptions = []; + final sortOptions = >[]; final options = {"state": false, "watch": false, "presence": true}; final paginationParams = PaginationParams( limit: 10, @@ -152,10 +154,10 @@ void main() { when(mockDio.get('/channels', queryParameters: queryParams)) .thenAnswer((_) async { - return Response(data: '{}', statusCode: 200); + return Response(data: '{"channels":[]}', statusCode: 200); }); - await client.queryChannels( + await client.queryChannelsOnline( filter: queryFilter, sort: sortOptions, options: options, @@ -229,6 +231,7 @@ void main() { 'query': query, 'sort': sortOptions, 'limit': 10, + 'offset': 0, }), }; @@ -346,7 +349,7 @@ void main() { }; when(mockDio.get('/users', queryParameters: queryParams)) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + .thenAnswer((_) async => Response(data: '{"users":[]}', statusCode: 200)); await client.queryUsers(); @@ -382,7 +385,7 @@ void main() { when(mockDio.get('/users', queryParameters: queryParams)) .thenAnswer((_) async { - return Response(data: '{}', statusCode: 200); + return Response(data: '{"users":[]}', statusCode: 200); }); await client.queryUsers( From 81ba72bdcc22e752355dbd3dcd6a98ef792faa6b Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 26 Feb 2021 19:36:31 +0530 Subject: [PATCH 36/41] [LLC] Fix tests Signed-off-by: Sahil Kumar --- packages/stream_chat/test/src/api/requests_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stream_chat/test/src/api/requests_test.dart b/packages/stream_chat/test/src/api/requests_test.dart index 6f7324ed6..de8ddaf88 100644 --- a/packages/stream_chat/test/src/api/requests_test.dart +++ b/packages/stream_chat/test/src/api/requests_test.dart @@ -12,7 +12,7 @@ void main() { test('PaginationParams', () { final option = PaginationParams(); final j = option.toJson(); - expect(j, {'limit': 10}); + expect(j, {'limit': 10, 'offset': 0}); }); }); } From 3b8b0aa9fd241674cccfd3f13cd43ac1702ba4fe Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Fri, 26 Feb 2021 19:37:04 +0530 Subject: [PATCH 37/41] flutter format Signed-off-by: Sahil Kumar --- packages/stream_chat/test/src/client_test.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/stream_chat/test/src/client_test.dart b/packages/stream_chat/test/src/client_test.dart index 1cdddbf0b..e70724a3a 100644 --- a/packages/stream_chat/test/src/client_test.dart +++ b/packages/stream_chat/test/src/client_test.dart @@ -349,7 +349,8 @@ void main() { }; when(mockDio.get('/users', queryParameters: queryParams)) - .thenAnswer((_) async => Response(data: '{"users":[]}', statusCode: 200)); + .thenAnswer( + (_) async => Response(data: '{"users":[]}', statusCode: 200)); await client.queryUsers(); From 7a1c470997a9a44f4e902dd70e36fb65d4af78cf Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Fri, 26 Feb 2021 16:21:09 +0100 Subject: [PATCH 38/41] bump version --- packages/stream_chat/lib/version.dart | 2 +- packages/stream_chat/pubspec.yaml | 2 +- packages/stream_chat_flutter/example/pubspec.yaml | 3 +-- packages/stream_chat_flutter/pubspec.yaml | 4 ++-- packages/stream_chat_flutter_core/pubspec.yaml | 4 ++-- packages/stream_chat_persistence/example/pubspec.yaml | 3 +-- packages/stream_chat_persistence/pubspec.yaml | 5 ++--- 7 files changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/stream_chat/lib/version.dart b/packages/stream_chat/lib/version.dart index 6cd8c4bcf..7eef07949 100644 --- a/packages/stream_chat/lib/version.dart +++ b/packages/stream_chat/lib/version.dart @@ -2,4 +2,4 @@ import 'package:stream_chat/src/client.dart'; /// Current package version /// Used in [StreamChatClient] to build the `x-stream-client` header -const PACKAGE_VERSION = '1.2.0-beta'; +const PACKAGE_VERSION = '1.3.0-beta'; diff --git a/packages/stream_chat/pubspec.yaml b/packages/stream_chat/pubspec.yaml index a625f95b2..63ea95a90 100644 --- a/packages/stream_chat/pubspec.yaml +++ b/packages/stream_chat/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat homepage: https://getstream.io/ description: The official Dart client for Stream Chat, a service for building chat applications. -version: 1.2.0-beta +version: 1.3.0-beta repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues diff --git a/packages/stream_chat_flutter/example/pubspec.yaml b/packages/stream_chat_flutter/example/pubspec.yaml index 7a1863b01..4d32fb58c 100644 --- a/packages/stream_chat_flutter/example/pubspec.yaml +++ b/packages/stream_chat_flutter/example/pubspec.yaml @@ -25,8 +25,7 @@ dependencies: sdk: flutter stream_chat_flutter: path: ../ - stream_chat_persistence: - path: ../../stream_chat_persistence + stream_chat_persistence: ^1.3.0-beta # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index 8fcf99a79..36b561dac 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat_flutter homepage: https://github.com/GetStream/stream-chat-flutter description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter. -version: 1.2.0-beta +version: 1.3.0-beta repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues @@ -11,7 +11,7 @@ environment: dependencies: flutter: sdk: flutter - stream_chat_flutter_core: ^1.2.0-beta + stream_chat_flutter_core: ^1.3.0-beta flutter_app_badger: ^1.1.2 photo_view: ^0.10.3 rxdart: ^0.25.0 diff --git a/packages/stream_chat_flutter_core/pubspec.yaml b/packages/stream_chat_flutter_core/pubspec.yaml index e9ce119b7..2896ece37 100644 --- a/packages/stream_chat_flutter_core/pubspec.yaml +++ b/packages/stream_chat_flutter_core/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat_flutter_core homepage: https://github.com/GetStream/stream-chat-flutter description: Stream Chat official Flutter SDK Core. Build your own chat experience using Dart and Flutter. -version: 1.2.0-beta +version: 1.3.0-beta repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues @@ -10,7 +10,7 @@ environment: flutter: ">=1.17.0" dependencies: - stream_chat: ^1.2.0-beta + stream_chat: ^1.3.0-beta flutter: sdk: flutter rxdart: ^0.25.0 diff --git a/packages/stream_chat_persistence/example/pubspec.yaml b/packages/stream_chat_persistence/example/pubspec.yaml index 2586bea65..f70d9d93d 100644 --- a/packages/stream_chat_persistence/example/pubspec.yaml +++ b/packages/stream_chat_persistence/example/pubspec.yaml @@ -11,8 +11,7 @@ dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.0 - stream_chat: - path: ../../stream_chat + stream_chat: ^1.3.0 stream_chat_persistence: path: ../ diff --git a/packages/stream_chat_persistence/pubspec.yaml b/packages/stream_chat_persistence/pubspec.yaml index 9551507e3..bd32d8f35 100644 --- a/packages/stream_chat_persistence/pubspec.yaml +++ b/packages/stream_chat_persistence/pubspec.yaml @@ -1,7 +1,7 @@ name: stream_chat_persistence homepage: https://github.com/GetStream/stream-chat-flutter description: Official Stream Chat Persistence library. Build your own chat experience using Dart and Flutter. -version: 1.2.0-beta +version: 1.3.0-beta repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues @@ -13,8 +13,7 @@ dependencies: path: ^1.7.0 path_provider: ^1.6.27 sqlite3_flutter_libs: ^0.4.0+1 - stream_chat: - path: ../stream_chat + stream_chat: ^1.3.0-beta dev_dependencies: test: ^1.15.7 From b8e29cd88cb11d01045a7edfd7fd8ad6e3fe53c1 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Fri, 26 Feb 2021 16:25:08 +0100 Subject: [PATCH 39/41] update changelogs --- packages/stream_chat/CHANGELOG.md | 7 +++++++ packages/stream_chat_flutter/CHANGELOG.md | 11 ++++++++++- packages/stream_chat_flutter_core/CHANGELOG.md | 5 +++++ packages/stream_chat_persistence/CHANGELOG.md | 4 ++++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 676dbec4c..b0d0c6585 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.3.0-beta + +- Save pinned messages in offline storage +- Minor fixes +- `StreamClient.QueryChannels` now returns a Stream and fetches the channels from storage before calling the api +- Added `StreamClient.QueryChannelsOnline` and `StreamClient.QueryChannelsOffline` to fetch channels only from online or offline + ## 1.2.0-beta - 🛑 **BREAKING** Changed signature of `StreamClient.search` method diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 1e04d3cc6..57cc011d0 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,7 +1,16 @@ +## 1.3.0-beta + +- Added `MessageInputTheme` +- Fixed overflow in `MessageInput` animation +- Delete only image on imagegallery +- Close keyboard after sending a command +- Exposed `customAttachmentBuilders` through `MessageListView` +- Update `stream_chat_core` dependency + ## 1.2.0-beta - Minor fixes -- Update stream_chat_core dependency +- Update `stream_chat_core` dependency ## 1.1.1-beta diff --git a/packages/stream_chat_flutter_core/CHANGELOG.md b/packages/stream_chat_flutter_core/CHANGELOG.md index 737fe5cc2..d9abfe213 100644 --- a/packages/stream_chat_flutter_core/CHANGELOG.md +++ b/packages/stream_chat_flutter_core/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.3.0-beta + +* Update llc dependency +* Minor fixes + ## 1.2.0-beta * Update llc dependency diff --git a/packages/stream_chat_persistence/CHANGELOG.md b/packages/stream_chat_persistence/CHANGELOG.md index c214c008f..fab0e8741 100644 --- a/packages/stream_chat_persistence/CHANGELOG.md +++ b/packages/stream_chat_persistence/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.3.0-beta + +* Update llc dependency + ## 1.2.0-beta * Update llc dependency From a65df2662607608e8429777af08ab35b95720e38 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Fri, 26 Feb 2021 17:12:03 +0100 Subject: [PATCH 40/41] fix lint --- .../platform_detector/platform_detector.dart | 31 +++++++++++++++++++ .../platform_detector_io.dart | 1 + .../platform_detector_stub.dart | 1 + .../platform_detector_web.dart | 1 + .../lib/src/channel_list_view.dart | 3 -- 5 files changed, 34 insertions(+), 3 deletions(-) diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector.dart b/packages/stream_chat/lib/src/platform_detector/platform_detector.dart index 8f601501c..32748256b 100644 --- a/packages/stream_chat/lib/src/platform_detector/platform_detector.dart +++ b/packages/stream_chat/lib/src/platform_detector/platform_detector.dart @@ -2,26 +2,56 @@ import 'platform_detector_stub.dart' if (dart.library.html) 'platform_detector_web.dart' if (dart.library.io) 'platform_detector_io.dart'; +/// Possible platforms enum PlatformType { + /// Android, + + /// Ios, + + /// Web, + + /// MacOS, + + /// Windows, + + /// Linux, + + /// Fuchsia, } +/// Utility class that provides information on the current platform class CurrentPlatform { CurrentPlatform._(); + + /// True if the app is running on android static bool get isAndroid => type == PlatformType.Android; + + /// True if the app is running on ios static bool get isIos => type == PlatformType.Ios; + + /// True if the app is running on web static bool get isWeb => type == PlatformType.Web; + + /// True if the app is running on macos static bool get isMacOS => type == PlatformType.MacOS; + + /// True if the app is running on windows static bool get isWindows => type == PlatformType.Windows; + + /// True if the app is running on linux static bool get isLinux => type == PlatformType.Linux; + + /// True if the app is running on fuchsia static bool get isFuchsia => type == PlatformType.Fuchsia; + /// Returns a string version of the platform static String get name { switch (type) { case PlatformType.Android: @@ -43,5 +73,6 @@ class CurrentPlatform { } } + /// Get current platform type static PlatformType get type => currentPlatform; } diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart b/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart index 6cca45b6e..df74f5d88 100644 --- a/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart +++ b/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'platform_detector.dart'; +/// Version running on native systems PlatformType get currentPlatform { if (Platform.isWindows) return PlatformType.Windows; if (Platform.isFuchsia) return PlatformType.Fuchsia; diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector_stub.dart b/packages/stream_chat/lib/src/platform_detector/platform_detector_stub.dart index 4469fc46a..f9143deb1 100644 --- a/packages/stream_chat/lib/src/platform_detector/platform_detector_stub.dart +++ b/packages/stream_chat/lib/src/platform_detector/platform_detector_stub.dart @@ -1,5 +1,6 @@ import 'platform_detector.dart'; +/// Stub implementation PlatformType get currentPlatform { throw UnimplementedError(); } diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector_web.dart b/packages/stream_chat/lib/src/platform_detector/platform_detector_web.dart index b1613afb2..4274bf719 100644 --- a/packages/stream_chat/lib/src/platform_detector/platform_detector_web.dart +++ b/packages/stream_chat/lib/src/platform_detector/platform_detector_web.dart @@ -1,3 +1,4 @@ import 'platform_detector.dart'; +/// Version running on web PlatformType get currentPlatform => PlatformType.Web; diff --git a/packages/stream_chat_flutter/lib/src/channel_list_view.dart b/packages/stream_chat_flutter/lib/src/channel_list_view.dart index 9edd39ff6..3bab76b2b 100644 --- a/packages/stream_chat_flutter/lib/src/channel_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/channel_list_view.dart @@ -1,6 +1,3 @@ -import 'dart:async'; -import 'dart:convert'; - import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; From 009b66826ba0a3671a13ed68ad6ac550223ead6b Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Fri, 26 Feb 2021 17:17:28 +0100 Subject: [PATCH 41/41] add MessageInputTheme basic docs --- packages/stream_chat_flutter/lib/src/stream_chat_theme.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart index 338e2603c..aa4f51f0d 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart @@ -845,7 +845,9 @@ class ChannelHeaderTheme { } } +/// Defines the theme dedicated to the [MessageInput] widget class MessageInputTheme { + /// Duration of the [MessageInput] send button animation final Duration sendAnimationDuration; /// Background color of [MessageInput] send button @@ -863,6 +865,7 @@ class MessageInputTheme { /// Background color of [MessageInput] final Color inputBackground; + /// Returns a new [MessageInputTheme] const MessageInputTheme({ this.sendAnimationDuration, this.actionButtonColor, @@ -872,6 +875,7 @@ class MessageInputTheme { this.inputBackground, }); + /// Returns a new [MessageInputTheme] replacing some of its properties MessageInputTheme copyWith({ Duration sendAnimationDuration, Color inputBackground, @@ -891,6 +895,7 @@ class MessageInputTheme { sendButtonIdleColor: sendButtonIdleColor ?? this.sendButtonIdleColor, ); + /// Merges [this] [MessageInputTheme] with the [other] MessageInputTheme merge(MessageInputTheme other) { if (other == null) return this; return copyWith(