feat(persistence): add pinned message reactions table
This commit is contained in:
@@ -4,6 +4,7 @@ export 'connection_event_dao.dart';
|
||||
export 'member_dao.dart';
|
||||
export 'message_dao.dart';
|
||||
export 'pinned_message_dao.dart';
|
||||
export 'pinned_message_reaction_dao.dart';
|
||||
export 'reaction_dao.dart';
|
||||
export 'read_dao.dart';
|
||||
export 'user_dao.dart';
|
||||
|
||||
@@ -39,8 +39,10 @@ class PinnedMessageDao extends DatabaseAccessor<MoorChatDatabase>
|
||||
final userEntity = rows.readTableOrNull(users);
|
||||
final pinnedByEntity = rows.readTableOrNull(_pinnedByUsers);
|
||||
final msgEntity = rows.readTable(pinnedMessages);
|
||||
final latestReactions = await _db.reactionDao.getReactions(msgEntity.id);
|
||||
final ownReactions = await _db.reactionDao.getReactionsByUserId(
|
||||
final latestReactions =
|
||||
await _db.pinnedMessageReactionDao.getReactions(msgEntity.id);
|
||||
final ownReactions =
|
||||
await _db.pinnedMessageReactionDao.getReactionsByUserId(
|
||||
msgEntity.id,
|
||||
_db.userId,
|
||||
);
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
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_message_reactions.dart';
|
||||
import 'package:stream_chat_persistence/src/entity/users.dart';
|
||||
import 'package:stream_chat_persistence/src/mapper/mapper.dart';
|
||||
|
||||
part 'pinned_message_reaction_dao.g.dart';
|
||||
|
||||
/// The Data Access Object for operations in [PinnedMessageReactions] table.
|
||||
@UseDao(tables: [PinnedMessageReactions, Users])
|
||||
class PinnedMessageReactionDao extends DatabaseAccessor<MoorChatDatabase>
|
||||
with _$PinnedMessageReactionDaoMixin {
|
||||
/// Creates a new reaction dao instance
|
||||
PinnedMessageReactionDao(MoorChatDatabase db) : super(db);
|
||||
|
||||
/// Returns all the reactions of a particular message by matching
|
||||
/// [Reactions.messageId] with [messageId]
|
||||
Future<List<Reaction>> getReactions(String messageId) =>
|
||||
(select(pinnedMessageReactions).join([
|
||||
leftOuterJoin(users, pinnedMessageReactions.userId.equalsExp(users.id)),
|
||||
])
|
||||
..where(pinnedMessageReactions.messageId.equals(messageId))
|
||||
..orderBy([OrderingTerm.asc(pinnedMessageReactions.createdAt)]))
|
||||
.map((rows) {
|
||||
final userEntity = rows.readTableOrNull(users);
|
||||
final reactionEntity = rows.readTable(pinnedMessageReactions);
|
||||
return reactionEntity.toReaction(user: userEntity?.toUser());
|
||||
}).get();
|
||||
|
||||
/// Returns all the reactions of a particular message
|
||||
/// added by a particular user by matching
|
||||
/// [Reactions.messageId] with [messageId] and
|
||||
/// [Reactions.userId] with [userId]
|
||||
Future<List<Reaction>> getReactionsByUserId(
|
||||
String messageId,
|
||||
String userId,
|
||||
) async {
|
||||
final reactions = await getReactions(messageId);
|
||||
return reactions.where((it) => it.userId == userId).toList();
|
||||
}
|
||||
|
||||
/// Updates the reactions data with the new [reactionList] data
|
||||
Future<void> updateReactions(List<Reaction> reactionList) => batch((it) {
|
||||
it.insertAllOnConflictUpdate(
|
||||
pinnedMessageReactions,
|
||||
reactionList.map((r) => r.toPinnedEntity()).toList(),
|
||||
);
|
||||
});
|
||||
|
||||
/// Deletes all the reactions whose [Reactions.messageId] is
|
||||
/// present in [messageIds]
|
||||
Future<void> deleteReactionsByMessageIds(List<String> messageIds) =>
|
||||
batch((it) {
|
||||
it.deleteWhere<PinnedMessageReactions, PinnedMessageReactionEntity>(
|
||||
pinnedMessageReactions,
|
||||
(r) => r.messageId.isIn(messageIds),
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'pinned_message_reaction_dao.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// DaoGenerator
|
||||
// **************************************************************************
|
||||
|
||||
mixin _$PinnedMessageReactionDaoMixin on DatabaseAccessor<MoorChatDatabase> {
|
||||
$PinnedMessageReactionsTable get pinnedMessageReactions =>
|
||||
attachedDatabase.pinnedMessageReactions;
|
||||
$UsersTable get users => attachedDatabase.users;
|
||||
}
|
||||
@@ -14,6 +14,7 @@ part 'moor_chat_database.g.dart';
|
||||
Channels,
|
||||
Messages,
|
||||
PinnedMessages,
|
||||
PinnedMessageReactions,
|
||||
Reactions,
|
||||
Users,
|
||||
Members,
|
||||
@@ -25,6 +26,7 @@ part 'moor_chat_database.g.dart';
|
||||
ChannelDao,
|
||||
MessageDao,
|
||||
PinnedMessageDao,
|
||||
PinnedMessageReactionDao,
|
||||
MemberDao,
|
||||
ReactionDao,
|
||||
ReadDao,
|
||||
|
||||
@@ -2769,6 +2769,340 @@ class $PinnedMessagesTable extends PinnedMessages
|
||||
MapConverter<Object?>();
|
||||
}
|
||||
|
||||
class PinnedMessageReactionEntity extends DataClass
|
||||
implements Insertable<PinnedMessageReactionEntity> {
|
||||
/// The id of the user that sent the reaction
|
||||
final String userId;
|
||||
|
||||
/// The messageId to which the reaction belongs
|
||||
final String messageId;
|
||||
|
||||
/// The type of the reaction
|
||||
final String type;
|
||||
|
||||
/// The DateTime on which the reaction is created
|
||||
final DateTime createdAt;
|
||||
|
||||
/// The score of the reaction (ie. number of reactions sent)
|
||||
final int score;
|
||||
|
||||
/// Reaction custom extraData
|
||||
final Map<String, Object?>? extraData;
|
||||
PinnedMessageReactionEntity(
|
||||
{required this.userId,
|
||||
required this.messageId,
|
||||
required this.type,
|
||||
required this.createdAt,
|
||||
required this.score,
|
||||
this.extraData});
|
||||
factory PinnedMessageReactionEntity.fromData(
|
||||
Map<String, dynamic> data, GeneratedDatabase db,
|
||||
{String? prefix}) {
|
||||
final effectivePrefix = prefix ?? '';
|
||||
return PinnedMessageReactionEntity(
|
||||
userId: const StringType()
|
||||
.mapFromDatabaseResponse(data['${effectivePrefix}user_id'])!,
|
||||
messageId: const StringType()
|
||||
.mapFromDatabaseResponse(data['${effectivePrefix}message_id'])!,
|
||||
type: const StringType()
|
||||
.mapFromDatabaseResponse(data['${effectivePrefix}type'])!,
|
||||
createdAt: const DateTimeType()
|
||||
.mapFromDatabaseResponse(data['${effectivePrefix}created_at'])!,
|
||||
score: const IntType()
|
||||
.mapFromDatabaseResponse(data['${effectivePrefix}score'])!,
|
||||
extraData: $PinnedMessageReactionsTable.$converter0.mapToDart(
|
||||
const StringType()
|
||||
.mapFromDatabaseResponse(data['${effectivePrefix}extra_data'])),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, Expression>{};
|
||||
map['user_id'] = Variable<String>(userId);
|
||||
map['message_id'] = Variable<String>(messageId);
|
||||
map['type'] = Variable<String>(type);
|
||||
map['created_at'] = Variable<DateTime>(createdAt);
|
||||
map['score'] = Variable<int>(score);
|
||||
if (!nullToAbsent || extraData != null) {
|
||||
final converter = $PinnedMessageReactionsTable.$converter0;
|
||||
map['extra_data'] = Variable<String?>(converter.mapToSql(extraData));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
factory PinnedMessageReactionEntity.fromJson(Map<String, dynamic> json,
|
||||
{ValueSerializer? serializer}) {
|
||||
serializer ??= moorRuntimeOptions.defaultSerializer;
|
||||
return PinnedMessageReactionEntity(
|
||||
userId: serializer.fromJson<String>(json['userId']),
|
||||
messageId: serializer.fromJson<String>(json['messageId']),
|
||||
type: serializer.fromJson<String>(json['type']),
|
||||
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
|
||||
score: serializer.fromJson<int>(json['score']),
|
||||
extraData: serializer.fromJson<Map<String, Object?>?>(json['extraData']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
|
||||
serializer ??= moorRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'userId': serializer.toJson<String>(userId),
|
||||
'messageId': serializer.toJson<String>(messageId),
|
||||
'type': serializer.toJson<String>(type),
|
||||
'createdAt': serializer.toJson<DateTime>(createdAt),
|
||||
'score': serializer.toJson<int>(score),
|
||||
'extraData': serializer.toJson<Map<String, Object?>?>(extraData),
|
||||
};
|
||||
}
|
||||
|
||||
PinnedMessageReactionEntity copyWith(
|
||||
{String? userId,
|
||||
String? messageId,
|
||||
String? type,
|
||||
DateTime? createdAt,
|
||||
int? score,
|
||||
Value<Map<String, Object?>?> extraData = const Value.absent()}) =>
|
||||
PinnedMessageReactionEntity(
|
||||
userId: userId ?? this.userId,
|
||||
messageId: messageId ?? this.messageId,
|
||||
type: type ?? this.type,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
score: score ?? this.score,
|
||||
extraData: extraData.present ? extraData.value : this.extraData,
|
||||
);
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('PinnedMessageReactionEntity(')
|
||||
..write('userId: $userId, ')
|
||||
..write('messageId: $messageId, ')
|
||||
..write('type: $type, ')
|
||||
..write('createdAt: $createdAt, ')
|
||||
..write('score: $score, ')
|
||||
..write('extraData: $extraData')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => $mrjf($mrjc(
|
||||
userId.hashCode,
|
||||
$mrjc(
|
||||
messageId.hashCode,
|
||||
$mrjc(
|
||||
type.hashCode,
|
||||
$mrjc(createdAt.hashCode,
|
||||
$mrjc(score.hashCode, extraData.hashCode))))));
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is PinnedMessageReactionEntity &&
|
||||
other.userId == this.userId &&
|
||||
other.messageId == this.messageId &&
|
||||
other.type == this.type &&
|
||||
other.createdAt == this.createdAt &&
|
||||
other.score == this.score &&
|
||||
other.extraData == this.extraData);
|
||||
}
|
||||
|
||||
class PinnedMessageReactionsCompanion
|
||||
extends UpdateCompanion<PinnedMessageReactionEntity> {
|
||||
final Value<String> userId;
|
||||
final Value<String> messageId;
|
||||
final Value<String> type;
|
||||
final Value<DateTime> createdAt;
|
||||
final Value<int> score;
|
||||
final Value<Map<String, Object?>?> extraData;
|
||||
const PinnedMessageReactionsCompanion({
|
||||
this.userId = const Value.absent(),
|
||||
this.messageId = const Value.absent(),
|
||||
this.type = const Value.absent(),
|
||||
this.createdAt = const Value.absent(),
|
||||
this.score = const Value.absent(),
|
||||
this.extraData = const Value.absent(),
|
||||
});
|
||||
PinnedMessageReactionsCompanion.insert({
|
||||
required String userId,
|
||||
required String messageId,
|
||||
required String type,
|
||||
this.createdAt = const Value.absent(),
|
||||
this.score = const Value.absent(),
|
||||
this.extraData = const Value.absent(),
|
||||
}) : userId = Value(userId),
|
||||
messageId = Value(messageId),
|
||||
type = Value(type);
|
||||
static Insertable<PinnedMessageReactionEntity> custom({
|
||||
Expression<String>? userId,
|
||||
Expression<String>? messageId,
|
||||
Expression<String>? type,
|
||||
Expression<DateTime>? createdAt,
|
||||
Expression<int>? score,
|
||||
Expression<Map<String, Object?>?>? extraData,
|
||||
}) {
|
||||
return RawValuesInsertable({
|
||||
if (userId != null) 'user_id': userId,
|
||||
if (messageId != null) 'message_id': messageId,
|
||||
if (type != null) 'type': type,
|
||||
if (createdAt != null) 'created_at': createdAt,
|
||||
if (score != null) 'score': score,
|
||||
if (extraData != null) 'extra_data': extraData,
|
||||
});
|
||||
}
|
||||
|
||||
PinnedMessageReactionsCompanion copyWith(
|
||||
{Value<String>? userId,
|
||||
Value<String>? messageId,
|
||||
Value<String>? type,
|
||||
Value<DateTime>? createdAt,
|
||||
Value<int>? score,
|
||||
Value<Map<String, Object?>?>? extraData}) {
|
||||
return PinnedMessageReactionsCompanion(
|
||||
userId: userId ?? this.userId,
|
||||
messageId: messageId ?? this.messageId,
|
||||
type: type ?? this.type,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
score: score ?? this.score,
|
||||
extraData: extraData ?? this.extraData,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, Expression>{};
|
||||
if (userId.present) {
|
||||
map['user_id'] = Variable<String>(userId.value);
|
||||
}
|
||||
if (messageId.present) {
|
||||
map['message_id'] = Variable<String>(messageId.value);
|
||||
}
|
||||
if (type.present) {
|
||||
map['type'] = Variable<String>(type.value);
|
||||
}
|
||||
if (createdAt.present) {
|
||||
map['created_at'] = Variable<DateTime>(createdAt.value);
|
||||
}
|
||||
if (score.present) {
|
||||
map['score'] = Variable<int>(score.value);
|
||||
}
|
||||
if (extraData.present) {
|
||||
final converter = $PinnedMessageReactionsTable.$converter0;
|
||||
map['extra_data'] =
|
||||
Variable<String?>(converter.mapToSql(extraData.value));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('PinnedMessageReactionsCompanion(')
|
||||
..write('userId: $userId, ')
|
||||
..write('messageId: $messageId, ')
|
||||
..write('type: $type, ')
|
||||
..write('createdAt: $createdAt, ')
|
||||
..write('score: $score, ')
|
||||
..write('extraData: $extraData')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
class $PinnedMessageReactionsTable extends PinnedMessageReactions
|
||||
with TableInfo<$PinnedMessageReactionsTable, PinnedMessageReactionEntity> {
|
||||
final GeneratedDatabase _db;
|
||||
final String? _alias;
|
||||
$PinnedMessageReactionsTable(this._db, [this._alias]);
|
||||
final VerificationMeta _userIdMeta = const VerificationMeta('userId');
|
||||
late final GeneratedColumn<String?> userId = GeneratedColumn<String?>(
|
||||
'user_id', aliasedName, false,
|
||||
typeName: 'TEXT', requiredDuringInsert: true);
|
||||
final VerificationMeta _messageIdMeta = const VerificationMeta('messageId');
|
||||
late final GeneratedColumn<String?> messageId = GeneratedColumn<String?>(
|
||||
'message_id', aliasedName, false,
|
||||
typeName: 'TEXT',
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'REFERENCES pinned_messages(id) ON DELETE CASCADE');
|
||||
final VerificationMeta _typeMeta = const VerificationMeta('type');
|
||||
late final GeneratedColumn<String?> type = GeneratedColumn<String?>(
|
||||
'type', aliasedName, false,
|
||||
typeName: 'TEXT', requiredDuringInsert: true);
|
||||
final VerificationMeta _createdAtMeta = const VerificationMeta('createdAt');
|
||||
late final GeneratedColumn<DateTime?> createdAt = GeneratedColumn<DateTime?>(
|
||||
'created_at', aliasedName, false,
|
||||
typeName: 'INTEGER',
|
||||
requiredDuringInsert: false,
|
||||
defaultValue: currentDateAndTime);
|
||||
final VerificationMeta _scoreMeta = const VerificationMeta('score');
|
||||
late final GeneratedColumn<int?> score = GeneratedColumn<int?>(
|
||||
'score', aliasedName, false,
|
||||
typeName: 'INTEGER',
|
||||
requiredDuringInsert: false,
|
||||
defaultValue: const Constant(0));
|
||||
final VerificationMeta _extraDataMeta = const VerificationMeta('extraData');
|
||||
late final GeneratedColumnWithTypeConverter<Map<String, Object?>, String?>
|
||||
extraData = GeneratedColumn<String?>('extra_data', aliasedName, true,
|
||||
typeName: 'TEXT', requiredDuringInsert: false)
|
||||
.withConverter<Map<String, Object?>>(
|
||||
$PinnedMessageReactionsTable.$converter0);
|
||||
@override
|
||||
List<GeneratedColumn> get $columns =>
|
||||
[userId, messageId, type, createdAt, score, extraData];
|
||||
@override
|
||||
String get aliasedName => _alias ?? 'pinned_message_reactions';
|
||||
@override
|
||||
String get actualTableName => 'pinned_message_reactions';
|
||||
@override
|
||||
VerificationContext validateIntegrity(
|
||||
Insertable<PinnedMessageReactionEntity> instance,
|
||||
{bool isInserting = false}) {
|
||||
final context = VerificationContext();
|
||||
final data = instance.toColumns(true);
|
||||
if (data.containsKey('user_id')) {
|
||||
context.handle(_userIdMeta,
|
||||
userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_userIdMeta);
|
||||
}
|
||||
if (data.containsKey('message_id')) {
|
||||
context.handle(_messageIdMeta,
|
||||
messageId.isAcceptableOrUnknown(data['message_id']!, _messageIdMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_messageIdMeta);
|
||||
}
|
||||
if (data.containsKey('type')) {
|
||||
context.handle(
|
||||
_typeMeta, type.isAcceptableOrUnknown(data['type']!, _typeMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_typeMeta);
|
||||
}
|
||||
if (data.containsKey('created_at')) {
|
||||
context.handle(_createdAtMeta,
|
||||
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
|
||||
}
|
||||
if (data.containsKey('score')) {
|
||||
context.handle(
|
||||
_scoreMeta, score.isAcceptableOrUnknown(data['score']!, _scoreMeta));
|
||||
}
|
||||
context.handle(_extraDataMeta, const VerificationResult.success());
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<GeneratedColumn> get $primaryKey => {messageId, type, userId};
|
||||
@override
|
||||
PinnedMessageReactionEntity map(Map<String, dynamic> data,
|
||||
{String? tablePrefix}) {
|
||||
return PinnedMessageReactionEntity.fromData(data, _db,
|
||||
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||
}
|
||||
|
||||
@override
|
||||
$PinnedMessageReactionsTable createAlias(String alias) {
|
||||
return $PinnedMessageReactionsTable(_db, alias);
|
||||
}
|
||||
|
||||
static TypeConverter<Map<String, Object?>, String> $converter0 =
|
||||
MapConverter<Object?>();
|
||||
}
|
||||
|
||||
class ReactionEntity extends DataClass implements Insertable<ReactionEntity> {
|
||||
/// The id of the user that sent the reaction
|
||||
final String userId;
|
||||
@@ -4897,6 +5231,8 @@ abstract class _$MoorChatDatabase extends GeneratedDatabase {
|
||||
late final $ChannelsTable channels = $ChannelsTable(this);
|
||||
late final $MessagesTable messages = $MessagesTable(this);
|
||||
late final $PinnedMessagesTable pinnedMessages = $PinnedMessagesTable(this);
|
||||
late final $PinnedMessageReactionsTable pinnedMessageReactions =
|
||||
$PinnedMessageReactionsTable(this);
|
||||
late final $ReactionsTable reactions = $ReactionsTable(this);
|
||||
late final $UsersTable users = $UsersTable(this);
|
||||
late final $MembersTable members = $MembersTable(this);
|
||||
@@ -4909,6 +5245,8 @@ abstract class _$MoorChatDatabase extends GeneratedDatabase {
|
||||
late final MessageDao messageDao = MessageDao(this as MoorChatDatabase);
|
||||
late final PinnedMessageDao pinnedMessageDao =
|
||||
PinnedMessageDao(this as MoorChatDatabase);
|
||||
late final PinnedMessageReactionDao pinnedMessageReactionDao =
|
||||
PinnedMessageReactionDao(this as MoorChatDatabase);
|
||||
late final MemberDao memberDao = MemberDao(this as MoorChatDatabase);
|
||||
late final ReactionDao reactionDao = ReactionDao(this as MoorChatDatabase);
|
||||
late final ReadDao readDao = ReadDao(this as MoorChatDatabase);
|
||||
@@ -4923,6 +5261,7 @@ abstract class _$MoorChatDatabase extends GeneratedDatabase {
|
||||
channels,
|
||||
messages,
|
||||
pinnedMessages,
|
||||
pinnedMessageReactions,
|
||||
reactions,
|
||||
users,
|
||||
members,
|
||||
|
||||
@@ -3,6 +3,7 @@ export 'channels.dart';
|
||||
export 'connection_events.dart';
|
||||
export 'members.dart';
|
||||
export 'messages.dart';
|
||||
export 'pinned_message_reactions.dart';
|
||||
export 'pinned_messages.dart';
|
||||
export 'reactions.dart';
|
||||
export 'reads.dart';
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// coverage:ignore-file
|
||||
import 'package:moor/moor.dart';
|
||||
|
||||
import 'package:stream_chat_persistence/src/entity/reactions.dart';
|
||||
|
||||
/// Represents a [PinnedMessageReactions] table in [MoorChatDatabase].
|
||||
@DataClassName('PinnedMessageReactionEntity')
|
||||
class PinnedMessageReactions extends Reactions {
|
||||
/// The messageId to which the reaction belongs
|
||||
@override
|
||||
TextColumn get messageId => text()
|
||||
.customConstraint('REFERENCES pinned_messages(id) ON DELETE CASCADE')();
|
||||
}
|
||||
@@ -3,6 +3,7 @@ export 'event_mapper.dart';
|
||||
export 'member_mapper.dart';
|
||||
export 'message_mapper.dart';
|
||||
export 'pinned_message_mapper.dart';
|
||||
export 'pinned_message_reaction_mapper.dart';
|
||||
export 'reaction_mapper.dart';
|
||||
export 'read_mapper.dart';
|
||||
export 'user_mapper.dart';
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import 'package:stream_chat/stream_chat.dart';
|
||||
import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
|
||||
|
||||
/// Useful mapping functions for [PinnedMessageReactionEntity]
|
||||
extension PinnedMessageReactionEntityX on PinnedMessageReactionEntity {
|
||||
/// Maps a [PinnedMessageReactionEntity] into [Reaction]
|
||||
Reaction toReaction({User? user}) => Reaction(
|
||||
extraData: extraData ?? {},
|
||||
type: type,
|
||||
createdAt: createdAt,
|
||||
userId: userId,
|
||||
user: user,
|
||||
messageId: messageId,
|
||||
score: score,
|
||||
);
|
||||
}
|
||||
|
||||
/// Useful mapping functions for [Reaction]
|
||||
extension PReactionX on Reaction {
|
||||
/// Maps a [Reaction] into [ReactionEntity]
|
||||
PinnedMessageReactionEntity toPinnedEntity() => PinnedMessageReactionEntity(
|
||||
extraData: extraData,
|
||||
type: type,
|
||||
createdAt: createdAt,
|
||||
userId: userId!,
|
||||
messageId: messageId!,
|
||||
score: score,
|
||||
);
|
||||
}
|
||||
@@ -318,6 +318,15 @@ class StreamChatPersistenceClient extends ChatPersistenceClient {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updatePinnedMessageReactions(List<Reaction> reactions) {
|
||||
assert(_debugIsConnected, '');
|
||||
_logger.info('updatePinnedMessageReactions');
|
||||
return _readProtected(
|
||||
() => db!.pinnedMessageReactionDao.updateReactions(reactions),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateReactions(List<Reaction> reactions) {
|
||||
assert(_debugIsConnected, '');
|
||||
@@ -339,6 +348,18 @@ class StreamChatPersistenceClient extends ChatPersistenceClient {
|
||||
return _readProtected(() => db!.userDao.updateUsers(users));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deletePinnedMessageReactionsByMessageId(
|
||||
List<String> messageIds,
|
||||
) {
|
||||
assert(_debugIsConnected, '');
|
||||
_logger.info('deletePinnedMessageReactionsByMessageId');
|
||||
return _readProtected(
|
||||
() =>
|
||||
db!.pinnedMessageReactionDao.deleteReactionsByMessageIds(messageIds),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteReactionsByMessageId(List<String> messageIds) {
|
||||
assert(_debugIsConnected, '');
|
||||
|
||||
@@ -34,6 +34,12 @@ class MockChatDatabase extends Mock implements MoorChatDatabase {
|
||||
@override
|
||||
ReactionDao get reactionDao => _reactionDao ??= MockReactionDao();
|
||||
|
||||
PinnedMessageReactionDao? _pinnedMessageReactionDao;
|
||||
|
||||
@override
|
||||
PinnedMessageReactionDao get pinnedMessageReactionDao =>
|
||||
_pinnedMessageReactionDao ??= MockPinnedMessageReactionDao();
|
||||
|
||||
ReadDao? _readDao;
|
||||
|
||||
@override
|
||||
@@ -70,6 +76,9 @@ class MockMemberDao extends Mock implements MemberDao {}
|
||||
|
||||
class MockReactionDao extends Mock implements ReactionDao {}
|
||||
|
||||
class MockPinnedMessageReactionDao extends Mock
|
||||
implements PinnedMessageReactionDao {}
|
||||
|
||||
class MockReadDao extends Mock implements ReadDao {}
|
||||
|
||||
class MockChannelQueryDao extends Mock implements ChannelQueryDao {}
|
||||
|
||||
@@ -92,7 +92,7 @@ void main() {
|
||||
await database.userDao.updateUsers(users);
|
||||
await database.channelDao.updateChannels(channels);
|
||||
await pinnedMessageDao.updateMessages(cid, allMessages);
|
||||
await database.reactionDao.updateReactions([reaction]);
|
||||
await database.pinnedMessageReactionDao.updateReactions([reaction]);
|
||||
return allMessages;
|
||||
}
|
||||
|
||||
@@ -109,7 +109,8 @@ void main() {
|
||||
final firstMessageId = messages.first.id;
|
||||
|
||||
// Fetched reactions list should have one reaction for given message id
|
||||
final reactions = await database.reactionDao.getReactions(firstMessageId);
|
||||
final reactions =
|
||||
await database.pinnedMessageReactionDao.getReactions(firstMessageId);
|
||||
expect(reactions.length, 1);
|
||||
|
||||
// Deleting 2 messages from DB
|
||||
@@ -124,7 +125,7 @@ void main() {
|
||||
|
||||
// Reaction for the first message should be deleted too
|
||||
final newReactions =
|
||||
await database.reactionDao.getReactions(firstMessageId);
|
||||
await database.pinnedMessageReactionDao.getReactions(firstMessageId);
|
||||
expect(newReactions, isEmpty);
|
||||
});
|
||||
|
||||
@@ -147,8 +148,8 @@ void main() {
|
||||
|
||||
// Fetched reactions list should have one reaction for given message id
|
||||
final cid1firstMessageId = cid1Messages.first.id;
|
||||
final cid1Reactions =
|
||||
await database.reactionDao.getReactions(cid1firstMessageId);
|
||||
final cid1Reactions = await database.pinnedMessageReactionDao
|
||||
.getReactions(cid1firstMessageId);
|
||||
expect(cid1Reactions.length, 1);
|
||||
|
||||
// Deleting all the messages of cid1
|
||||
@@ -163,8 +164,8 @@ void main() {
|
||||
expect(cid2FetchedMessages, isNotEmpty);
|
||||
|
||||
// Reaction for the first message should be deleted too
|
||||
final cid1FetchedReactions =
|
||||
await database.reactionDao.getReactions(cid1firstMessageId);
|
||||
final cid1FetchedReactions = await database.pinnedMessageReactionDao
|
||||
.getReactions(cid1firstMessageId);
|
||||
expect(cid1FetchedReactions, isEmpty);
|
||||
},
|
||||
);
|
||||
@@ -184,12 +185,12 @@ void main() {
|
||||
|
||||
// Fetched reactions list should have one reaction for given message id
|
||||
final cid1FirstMessageId = cid1Messages.first.id;
|
||||
final cid1Reactions =
|
||||
await database.reactionDao.getReactions(cid1FirstMessageId);
|
||||
final cid1Reactions = await database.pinnedMessageReactionDao
|
||||
.getReactions(cid1FirstMessageId);
|
||||
expect(cid1Reactions.length, 1);
|
||||
final cid2FirstMessageId = cid2Messages.first.id;
|
||||
final cid2Reactions =
|
||||
await database.reactionDao.getReactions(cid2FirstMessageId);
|
||||
final cid2Reactions = await database.pinnedMessageReactionDao
|
||||
.getReactions(cid2FirstMessageId);
|
||||
expect(cid2Reactions.length, 1);
|
||||
|
||||
// Deleting all the messages of cid1
|
||||
@@ -204,11 +205,11 @@ void main() {
|
||||
expect(cid2FetchedMessages, isEmpty);
|
||||
|
||||
// Reaction for the first message should be deleted too
|
||||
final cid1FetchedReactions =
|
||||
await database.reactionDao.getReactions(cid1FirstMessageId);
|
||||
final cid1FetchedReactions = await database.pinnedMessageReactionDao
|
||||
.getReactions(cid1FirstMessageId);
|
||||
expect(cid1FetchedReactions, isEmpty);
|
||||
final cid2FetchedReactions =
|
||||
await database.reactionDao.getReactions(cid2FirstMessageId);
|
||||
final cid2FetchedReactions = await database.pinnedMessageReactionDao
|
||||
.getReactions(cid2FirstMessageId);
|
||||
expect(cid2FetchedReactions, isEmpty);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:stream_chat/stream_chat.dart';
|
||||
import 'package:stream_chat_persistence/src/dao/pinned_message_reaction_dao.dart';
|
||||
import 'package:stream_chat_persistence/src/db/moor_chat_database.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../../stream_chat_persistence_client_test.dart';
|
||||
|
||||
void main() {
|
||||
late PinnedMessageReactionDao pinnedMessageReactionDao;
|
||||
late MoorChatDatabase database;
|
||||
|
||||
setUp(() {
|
||||
database = testDatabaseProvider('testUserId');
|
||||
pinnedMessageReactionDao = database.pinnedMessageReactionDao;
|
||||
});
|
||||
|
||||
Future<List<Reaction>> _prepareReactionData(
|
||||
String messageId, {
|
||||
String? userId,
|
||||
int count = 3,
|
||||
}) async {
|
||||
const cid = 'test:Cid';
|
||||
final channels = [ChannelModel(cid: cid)];
|
||||
final users = List.generate(count, (index) => User(id: 'testUserId$index'));
|
||||
final message = Message(
|
||||
id: messageId,
|
||||
type: 'testType',
|
||||
user: users.first,
|
||||
createdAt: DateTime.now(),
|
||||
shadowed: math.Random().nextBool(),
|
||||
showInChannel: math.Random().nextBool(),
|
||||
replyCount: 3,
|
||||
updatedAt: DateTime.now(),
|
||||
extraData: const {'extra_test_field': 'extraTestData'},
|
||||
text: 'Dummy text',
|
||||
pinned: math.Random().nextBool(),
|
||||
pinnedAt: DateTime.now(),
|
||||
pinnedBy: users.first,
|
||||
);
|
||||
final reactions = List.generate(
|
||||
count,
|
||||
(index) => Reaction(
|
||||
type: 'testType$index',
|
||||
createdAt: DateTime.now(),
|
||||
userId: userId ?? users[index].id,
|
||||
messageId: message.id,
|
||||
score: count + 3,
|
||||
extraData: {'extra_test_field': 'extraTestData'},
|
||||
),
|
||||
);
|
||||
|
||||
await database.userDao.updateUsers(users);
|
||||
await database.channelDao.updateChannels(channels);
|
||||
await database.pinnedMessageDao.updateMessages(cid, [message]);
|
||||
await pinnedMessageReactionDao.updateReactions(reactions);
|
||||
|
||||
return reactions;
|
||||
}
|
||||
|
||||
test('getReactions', () async {
|
||||
const messageId = 'testMessageId';
|
||||
|
||||
// Should be empty initially
|
||||
final reactions = await pinnedMessageReactionDao.getReactions(messageId);
|
||||
expect(reactions, isEmpty);
|
||||
|
||||
// Adding sample reactions
|
||||
final insertedReactions = await _prepareReactionData(messageId);
|
||||
expect(insertedReactions, isNotEmpty);
|
||||
|
||||
// Fetched reaction length should match inserted reactions length.
|
||||
// Every reaction messageId should match the provided messageId.
|
||||
final fetchedReactions =
|
||||
await pinnedMessageReactionDao.getReactions(messageId);
|
||||
expect(fetchedReactions.length, insertedReactions.length);
|
||||
expect(fetchedReactions.every((it) => it.messageId == messageId), true);
|
||||
});
|
||||
|
||||
test('getReactionsByUserId', () async {
|
||||
const messageId = 'testMessageId';
|
||||
const userId = 'testUserId';
|
||||
|
||||
// Should be empty initially
|
||||
final reactions =
|
||||
await pinnedMessageReactionDao.getReactionsByUserId(messageId, userId);
|
||||
expect(reactions, isEmpty);
|
||||
|
||||
// Adding sample reactions
|
||||
final insertedReactions =
|
||||
await _prepareReactionData(messageId, userId: userId);
|
||||
expect(insertedReactions, isNotEmpty);
|
||||
|
||||
// Fetched reaction length should match inserted reactions length.
|
||||
// Every reaction messageId should match the provided messageId.
|
||||
// Every reaction userId should match the provided userId.
|
||||
final fetchedReactions =
|
||||
await pinnedMessageReactionDao.getReactionsByUserId(messageId, userId);
|
||||
expect(fetchedReactions.length, insertedReactions.length);
|
||||
expect(fetchedReactions.every((it) => it.messageId == messageId), true);
|
||||
expect(fetchedReactions.every((it) => it.userId == userId), true);
|
||||
});
|
||||
|
||||
test('updateReactions', () async {
|
||||
const messageId = 'testMessageId';
|
||||
|
||||
// Preparing test data
|
||||
final reactions = await _prepareReactionData(messageId);
|
||||
|
||||
// Modifying one of the reaction and also adding one new
|
||||
final copyReaction = reactions.first.copyWith(score: 33);
|
||||
final newReaction = Reaction(
|
||||
type: 'testType3',
|
||||
createdAt: DateTime.now(),
|
||||
userId: 'testUserId3',
|
||||
messageId: messageId,
|
||||
score: 30,
|
||||
extraData: {'extra_test_field': 'extraTestData'},
|
||||
);
|
||||
|
||||
await pinnedMessageReactionDao.updateReactions([copyReaction, newReaction]);
|
||||
|
||||
// Fetched reaction length should be one more than inserted reactions.
|
||||
// copyReaction `score` modified field should be 33.
|
||||
// Fetched reactions should contain the newReaction.
|
||||
final fetchedReactions =
|
||||
await pinnedMessageReactionDao.getReactions(messageId);
|
||||
expect(fetchedReactions.length, reactions.length + 1);
|
||||
expect(
|
||||
fetchedReactions
|
||||
.firstWhere((it) =>
|
||||
it.userId == copyReaction.userId && it.type == copyReaction.type)
|
||||
.score,
|
||||
33,
|
||||
);
|
||||
expect(
|
||||
fetchedReactions
|
||||
.where((it) =>
|
||||
it.userId == newReaction.userId && it.type == newReaction.type)
|
||||
.isNotEmpty,
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
group('deleteReactionsByMessageIds', () {
|
||||
const messageId1 = 'testMessageId1';
|
||||
const messageId2 = 'testMessageId2';
|
||||
test('should delete all the reactions of first message', () async {
|
||||
// Preparing test data
|
||||
final insertedReactions1 = await _prepareReactionData(messageId1);
|
||||
final insertedReactions2 = await _prepareReactionData(messageId2);
|
||||
|
||||
// Fetched reaction list length should match
|
||||
// the inserted reactions list length
|
||||
final reactions1 =
|
||||
await pinnedMessageReactionDao.getReactions(messageId1);
|
||||
final reactions2 =
|
||||
await pinnedMessageReactionDao.getReactions(messageId2);
|
||||
expect(reactions1.length, insertedReactions1.length);
|
||||
expect(reactions2.length, insertedReactions2.length);
|
||||
|
||||
// Deleting all the reactions of messageId1
|
||||
await pinnedMessageReactionDao.deleteReactionsByMessageIds([messageId1]);
|
||||
|
||||
// Fetched reactions length of only messageId1 should be empty
|
||||
final fetchedReactions1 =
|
||||
await pinnedMessageReactionDao.getReactions(messageId1);
|
||||
final fetchedReactions2 =
|
||||
await pinnedMessageReactionDao.getReactions(messageId2);
|
||||
expect(fetchedReactions1, isEmpty);
|
||||
expect(fetchedReactions2, isNotEmpty);
|
||||
});
|
||||
test('should delete all the messages of both message', () async {
|
||||
// Preparing test data
|
||||
final insertedReactions1 = await _prepareReactionData(messageId1);
|
||||
final insertedReactions2 = await _prepareReactionData(messageId2);
|
||||
|
||||
// Fetched reaction list length should match
|
||||
// the inserted reactions list length
|
||||
final reactions1 =
|
||||
await pinnedMessageReactionDao.getReactions(messageId1);
|
||||
final reactions2 =
|
||||
await pinnedMessageReactionDao.getReactions(messageId2);
|
||||
expect(reactions1.length, insertedReactions1.length);
|
||||
expect(reactions2.length, insertedReactions2.length);
|
||||
|
||||
// Deleting all the reactions of messageId1 and messageId2
|
||||
await pinnedMessageReactionDao
|
||||
.deleteReactionsByMessageIds([messageId1, messageId2]);
|
||||
|
||||
// Fetched reactions length of both messages should be empty
|
||||
final fetchedReactions1 =
|
||||
await pinnedMessageReactionDao.getReactions(messageId1);
|
||||
final fetchedReactions2 =
|
||||
await pinnedMessageReactionDao.getReactions(messageId2);
|
||||
expect(fetchedReactions1, isEmpty);
|
||||
expect(fetchedReactions2, isEmpty);
|
||||
});
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await database.disconnect();
|
||||
});
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
import 'package:test/test.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/mapper/pinned_message_reaction_mapper.dart';
|
||||
|
||||
import '../utils/date_matcher.dart';
|
||||
|
||||
void main() {
|
||||
test('toReaction should map the entity into Reaction', () {
|
||||
final user = User(id: 'testUserId');
|
||||
final message = Message(id: 'testMessageId');
|
||||
final entity = PinnedMessageReactionEntity(
|
||||
userId: user.id,
|
||||
messageId: message.id,
|
||||
type: 'haha',
|
||||
score: 33,
|
||||
createdAt: DateTime.now(),
|
||||
extraData: {'extra_test_data': 'extraData'},
|
||||
);
|
||||
|
||||
final reaction = entity.toReaction(user: user);
|
||||
expect(reaction, isA<Reaction>());
|
||||
expect(reaction.userId, entity.userId);
|
||||
expect(reaction.messageId, entity.messageId);
|
||||
expect(reaction.type, entity.type);
|
||||
expect(reaction.score, entity.score);
|
||||
expect(reaction.createdAt, isSameDateAs(entity.createdAt));
|
||||
expect(reaction.extraData, entity.extraData);
|
||||
});
|
||||
|
||||
test('toEntity should map reaction into PinnedMessageReactionEntity', () {
|
||||
final user = User(id: 'testUserId');
|
||||
final message = Message(id: 'testMessageId');
|
||||
final reaction = Reaction(
|
||||
userId: user.id,
|
||||
messageId: message.id,
|
||||
type: 'haha',
|
||||
score: 33,
|
||||
createdAt: DateTime.now(),
|
||||
extraData: {'extra_test_data': 'extraData'},
|
||||
);
|
||||
|
||||
final entity = reaction.toPinnedEntity();
|
||||
expect(entity, isA<PinnedMessageReactionEntity>());
|
||||
expect(entity.userId, reaction.userId);
|
||||
expect(entity.messageId, reaction.messageId);
|
||||
expect(entity.type, reaction.type);
|
||||
expect(entity.score, reaction.score);
|
||||
expect(entity.createdAt, isSameDateAs(reaction.createdAt));
|
||||
expect(entity.extraData, reaction.extraData);
|
||||
});
|
||||
}
|
||||
@@ -502,6 +502,21 @@ void main() {
|
||||
.called(1);
|
||||
});
|
||||
|
||||
test('updatePinnedMessageReactions', () async {
|
||||
final reactions = List.generate(
|
||||
3,
|
||||
(index) => Reaction(type: 'testType$index'),
|
||||
);
|
||||
when(() =>
|
||||
mockDatabase.pinnedMessageReactionDao.updateReactions(reactions))
|
||||
.thenAnswer((_) => Future.value());
|
||||
|
||||
await client.updatePinnedMessageReactions(reactions);
|
||||
verify(() =>
|
||||
mockDatabase.pinnedMessageReactionDao.updateReactions(reactions))
|
||||
.called(1);
|
||||
});
|
||||
|
||||
test('deleteReactionsByMessageId', () async {
|
||||
final messageIds = <String>[];
|
||||
when(() =>
|
||||
@@ -514,6 +529,17 @@ void main() {
|
||||
.called(1);
|
||||
});
|
||||
|
||||
test('deletePinnedMessageReactionsByMessageId', () async {
|
||||
final messageIds = <String>[];
|
||||
when(() => mockDatabase.pinnedMessageReactionDao
|
||||
.deleteReactionsByMessageIds(messageIds))
|
||||
.thenAnswer((_) => Future.value());
|
||||
|
||||
await client.deletePinnedMessageReactionsByMessageId(messageIds);
|
||||
verify(() => mockDatabase.pinnedMessageReactionDao
|
||||
.deleteReactionsByMessageIds(messageIds)).called(1);
|
||||
});
|
||||
|
||||
test('deleteMembersByCids', () async {
|
||||
final cids = <String>[];
|
||||
when(() => mockDatabase.memberDao.deleteMemberByCids(cids))
|
||||
|
||||
Reference in New Issue
Block a user