Files
stream-chat-flutter/packages/stream_chat/lib/src/models/message.dart
T

434 lines
12 KiB
Dart

import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:stream_chat/src/models/attachment.dart';
import 'package:stream_chat/src/models/reaction.dart';
import 'package:stream_chat/src/models/serialization.dart';
import 'package:stream_chat/src/models/user.dart';
import 'package:uuid/uuid.dart';
part 'message.g.dart';
class _PinExpires {
const _PinExpires();
}
const _pinExpires = _PinExpires();
/// Enum defining the status of a sending message
enum MessageSendingStatus {
/// Message is being sent
sending,
/// Message is being updated
updating,
/// Message is being deleted
deleting,
/// Message failed to send
failed,
/// Message failed to updated
// ignore: constant_identifier_names
failed_update,
/// Message failed to delete
// ignore: constant_identifier_names
failed_delete,
/// Message correctly sent
sent,
}
/// The class that contains the information about a message
@JsonSerializable()
class Message extends Equatable {
/// Constructor used for json serialization
Message({
String? id,
this.text,
this.type = 'regular',
this.attachments = const [],
this.mentionedUsers = const [],
this.silent = false,
this.shadowed = false,
this.reactionCounts,
this.reactionScores,
this.latestReactions,
this.ownReactions,
this.parentId,
this.quotedMessage,
this.quotedMessageId,
this.replyCount = 0,
this.threadParticipants,
this.showInChannel,
this.command,
DateTime? createdAt,
DateTime? updatedAt,
this.user,
this.pinned = false,
this.pinnedAt,
DateTime? pinExpires,
this.pinnedBy,
this.extraData = const {},
this.deletedAt,
this.status = MessageSendingStatus.sent,
this.skipPush = false,
}) : id = id ?? const Uuid().v4(),
pinExpires = pinExpires?.toUtc(),
createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now();
/// Create a new instance from a json
factory Message.fromJson(Map<String, dynamic> json) => _$MessageFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
/// The message ID. This is either created by Stream or set client side when
/// the message is added.
final String id;
/// The text of this message
final String? text;
/// The status of a sending message
@JsonKey(ignore: true)
final MessageSendingStatus status;
/// The message type
@JsonKey(
includeIfNull: false,
toJson: Serialization.readOnly,
defaultValue: 'regular',
)
final String type;
/// The list of attachments, either provided by the user or generated from a
/// command or as a result of URL scraping.
@JsonKey(
includeIfNull: false,
defaultValue: [],
)
final List<Attachment> attachments;
/// The list of user mentioned in the message
@JsonKey(
toJson: Serialization.userIds,
defaultValue: [],
)
final List<User> mentionedUsers;
/// A map describing the count of number of every reaction
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final Map<String, int>? reactionCounts;
/// A map describing the count of score of every reaction
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final Map<String, int>? reactionScores;
/// The latest reactions to the message created by any user.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final List<Reaction>? latestReactions;
/// The reactions added to the message by the current user.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final List<Reaction>? ownReactions;
/// The ID of the parent message, if the message is a thread reply.
final String? parentId;
/// A quoted reply message
@JsonKey(toJson: Serialization.readOnly)
final Message? quotedMessage;
/// The ID of the quoted message, if the message is a quoted reply.
final String? quotedMessageId;
/// Reserved field indicating the number of replies for this message.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final int? replyCount;
/// Reserved field indicating the thread participants for this message.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final List<User>? threadParticipants;
/// Check if this message needs to show in the channel.
final bool? showInChannel;
/// If true the message is silent
@JsonKey(defaultValue: false)
final bool silent;
/// If true the message will not send a push notification
@JsonKey(defaultValue: false)
final bool skipPush;
/// If true the message is shadowed
@JsonKey(
includeIfNull: false,
toJson: Serialization.readOnly,
defaultValue: false,
)
final bool shadowed;
/// A used command name.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final String? command;
/// Reserved field indicating when the message was created.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime createdAt;
/// Reserved field indicating when the message was updated last time.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime updatedAt;
/// User who sent the message
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final User? user;
/// If true the message is pinned
@JsonKey(defaultValue: false)
final bool pinned;
/// Reserved field indicating when the message was pinned
@JsonKey(toJson: Serialization.readOnly)
final DateTime? pinnedAt;
/// Reserved field indicating when the message will expire
///
/// if `null` message has no expiry
final DateTime? pinExpires;
/// Reserved field indicating who pinned the message
@JsonKey(toJson: Serialization.readOnly)
final User? pinnedBy;
/// Message custom extraData
@JsonKey(
includeIfNull: false,
defaultValue: {},
)
final Map<String, dynamic> extraData;
/// True if the message is a system info
bool get isSystem => type == 'system';
/// True if the message has been deleted
bool get isDeleted => type == 'deleted';
/// True if the message is ephemeral
bool get isEphemeral => type == 'ephemeral';
/// Reserved field indicating when the message was deleted.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime? deletedAt;
/// Known top level fields.
/// Useful for [Serialization] methods.
static const topLevelFields = [
'id',
'text',
'type',
'silent',
'attachments',
'latest_reactions',
'shadowed',
'own_reactions',
'mentioned_users',
'reaction_counts',
'reaction_scores',
'silent',
'parent_id',
'quoted_message',
'quoted_message_id',
'reply_count',
'thread_participants',
'show_in_channel',
'command',
'created_at',
'updated_at',
'deleted_at',
'user',
'pinned',
'pinned_at',
'pin_expires',
'pinned_by',
'skip_push',
];
/// Serialize to json
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$MessageToJson(this),
);
/// Creates a copy of [Message] with specified attributes overridden.
Message copyWith({
String? id,
String? text,
String? type,
List<Attachment>? attachments,
List<User>? mentionedUsers,
Map<String, int>? reactionCounts,
Map<String, int>? reactionScores,
List<Reaction>? latestReactions,
List<Reaction>? ownReactions,
String? parentId,
Message? quotedMessage,
String? quotedMessageId,
int? replyCount,
List<User>? threadParticipants,
bool? showInChannel,
bool? shadowed,
bool? silent,
String? command,
DateTime? createdAt,
DateTime? updatedAt,
DateTime? deletedAt,
User? user,
bool? pinned,
DateTime? pinnedAt,
Object? pinExpires = _pinExpires,
User? pinnedBy,
Map<String, dynamic>? extraData,
MessageSendingStatus? status,
bool? skipPush,
}) {
assert(() {
if (pinExpires is! DateTime &&
pinExpires != null &&
pinExpires is! _PinExpires) {
throw ArgumentError('`pinExpires` can only be set as DateTime or null');
}
return true;
}(), 'Validate type for pinExpires');
return Message(
id: id ?? this.id,
text: text ?? this.text,
type: type ?? this.type,
attachments: attachments ?? this.attachments,
mentionedUsers: mentionedUsers ?? this.mentionedUsers,
reactionCounts: reactionCounts ?? this.reactionCounts,
reactionScores: reactionScores ?? this.reactionScores,
latestReactions: latestReactions ?? this.latestReactions,
ownReactions: ownReactions ?? this.ownReactions,
parentId: parentId ?? this.parentId,
quotedMessage: quotedMessage ?? this.quotedMessage,
quotedMessageId: quotedMessageId ?? this.quotedMessageId,
replyCount: replyCount ?? this.replyCount,
threadParticipants: threadParticipants ?? this.threadParticipants,
showInChannel: showInChannel ?? this.showInChannel,
command: command ?? this.command,
createdAt: createdAt ?? this.createdAt,
silent: silent ?? this.silent,
extraData: extraData ?? this.extraData,
user: user ?? this.user,
shadowed: shadowed ?? this.shadowed,
updatedAt: updatedAt ?? this.updatedAt,
deletedAt: deletedAt ?? this.deletedAt,
status: status ?? this.status,
pinned: pinned ?? this.pinned,
pinnedAt: pinnedAt ?? this.pinnedAt,
pinnedBy: pinnedBy ?? this.pinnedBy,
pinExpires:
pinExpires == _pinExpires ? this.pinExpires : pinExpires as DateTime?,
skipPush: skipPush ?? this.skipPush,
);
}
/// Returns a new [Message] that is a combination of this message and the
/// given [other] message.
Message merge(Message other) => copyWith(
id: other.id,
text: other.text,
type: other.type,
attachments: other.attachments,
mentionedUsers: other.mentionedUsers,
reactionCounts: other.reactionCounts,
reactionScores: other.reactionScores,
latestReactions: other.latestReactions,
ownReactions: other.ownReactions,
parentId: other.parentId,
quotedMessage: other.quotedMessage,
quotedMessageId: other.quotedMessageId,
replyCount: other.replyCount,
threadParticipants: other.threadParticipants,
showInChannel: other.showInChannel,
command: other.command,
createdAt: other.createdAt,
silent: other.silent,
extraData: other.extraData,
user: other.user,
shadowed: other.shadowed,
updatedAt: other.updatedAt,
deletedAt: other.deletedAt,
status: other.status,
pinned: other.pinned,
pinnedAt: other.pinnedAt,
pinExpires: other.pinExpires,
pinnedBy: other.pinnedBy,
);
@override
List<Object?> get props => [
id,
text,
type,
attachments,
mentionedUsers,
reactionCounts,
reactionScores,
latestReactions,
ownReactions,
parentId,
quotedMessage,
quotedMessageId,
replyCount,
threadParticipants,
showInChannel,
shadowed,
silent,
command,
createdAt,
updatedAt,
deletedAt,
user,
pinned,
pinnedAt,
pinExpires,
pinnedBy,
extraData,
status,
skipPush,
];
}
/// A translated message
/// It has an additional property called [i18n]
@JsonSerializable()
class TranslatedMessage extends Message {
/// Constructor used for json serialization
TranslatedMessage(this.i18n) : super();
/// Create a new instance from a json
factory TranslatedMessage.fromJson(Map<String, dynamic> json) =>
_$TranslatedMessageFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields),
);
/// A Map of
final Map<String, String>? i18n;
/// Known top level fields.
/// Useful for [Serialization] methods.
static final topLevelFields = [
'i18n',
...Message.topLevelFields,
];
/// Serialize to json
@override
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$TranslatedMessageToJson(this),
);
}