171 lines
5.4 KiB
Dart
171 lines
5.4 KiB
Dart
import 'dart:typed_data';
|
||
import 'dart:io';
|
||
|
||
enum MessageStatus { encrypting, sending, sent, delivered, read, failed }
|
||
|
||
enum MessageType { text, image, video, file, videoNote, voiceNote }
|
||
|
||
class MessageModel {
|
||
final int? id; // server id (null пока не подтверждено сервером)
|
||
final int? tempId; // client temp id (для сопоставления ack)
|
||
final int senderId;
|
||
final int receiverId;
|
||
final String text; // текст для UI (у нас уже расшифрованный)
|
||
final DateTime createdAt;
|
||
final bool isMe;
|
||
final MessageStatus status;
|
||
final int? replyToId; // ID сообщения, на которое отвечают
|
||
final String? replyToText; // текст сообщения, на которое отвечают (для отображения)
|
||
final DateTime? editedAt;
|
||
final File? localFile;
|
||
final MessageType messageType;
|
||
final String? fileId;
|
||
final String? encryptedFileKey;
|
||
final String? fileName;
|
||
final int? fileSize;
|
||
|
||
MessageModel({
|
||
this.id,
|
||
this.tempId,
|
||
required this.senderId,
|
||
required this.receiverId,
|
||
required this.text,
|
||
required this.createdAt,
|
||
required this.isMe,
|
||
this.status = MessageStatus.sent,
|
||
this.replyToId,
|
||
this.replyToText,
|
||
this.editedAt,
|
||
this.localFile,
|
||
this.messageType = MessageType.text,
|
||
this.fileId,
|
||
this.encryptedFileKey,
|
||
this.fileName,
|
||
this.fileSize
|
||
});
|
||
|
||
MessageModel copyWith({
|
||
int? id,
|
||
int? tempId,
|
||
int? senderId,
|
||
int? receiverId,
|
||
String? text,
|
||
DateTime? createdAt,
|
||
bool? isMe,
|
||
MessageStatus? status,
|
||
int? replyToId,
|
||
String? replyToText,
|
||
DateTime? editedAt,
|
||
File? localFile,
|
||
MessageType? messageType,
|
||
String? fileId,
|
||
String? encryptedFileKey,
|
||
String? fileName,
|
||
int? fileSize,
|
||
}) {
|
||
return MessageModel(
|
||
id: id ?? this.id,
|
||
tempId: tempId ?? this.tempId,
|
||
senderId: senderId ?? this.senderId,
|
||
receiverId: receiverId ?? this.receiverId,
|
||
text: text ?? this.text,
|
||
createdAt: createdAt ?? this.createdAt,
|
||
isMe: isMe ?? this.isMe,
|
||
status: status ?? this.status,
|
||
replyToId: replyToId ?? this.replyToId,
|
||
replyToText: replyToText ?? this.replyToText,
|
||
editedAt: editedAt ?? this.editedAt,
|
||
localFile: localFile ?? this.localFile,
|
||
messageType: messageType ?? this.messageType,
|
||
fileId: fileId ?? this.fileId,
|
||
encryptedFileKey: encryptedFileKey ?? this.encryptedFileKey,
|
||
fileName: fileName ?? this.fileName,
|
||
fileSize: fileSize ?? this.fileSize,
|
||
);
|
||
}
|
||
|
||
factory MessageModel.fromJson(Map<String, dynamic> json, int currentUserId) {
|
||
final senderId = int.parse(json['sender_id'].toString());
|
||
final receiverId = int.parse((json['receiver_id'] ?? json['recipient_id']).toString());
|
||
final createdAtRaw = (json['created_at'] ?? json['timestamp']).toString();
|
||
final messageTypeStr = json['message_type']?.toString() ?? 'text';
|
||
|
||
return MessageModel(
|
||
id: json['id'] == null ? null : int.tryParse(json['id'].toString()),
|
||
tempId: json['temp_id'] == null ? null : int.tryParse(json['temp_id'].toString()),
|
||
senderId: senderId,
|
||
receiverId: receiverId,
|
||
text: (json['text'] ?? json['content'] ?? '').toString(),
|
||
createdAt: DateTime.tryParse(createdAtRaw) ?? DateTime.now(),
|
||
isMe: senderId == currentUserId,
|
||
status: MessageStatus.sent,
|
||
replyToId: json['reply_to_id'] == null ? null : int.tryParse(json['reply_to_id'].toString()),
|
||
replyToText: json['reply_to_text'] == null ? null : json['reply_to_text'].toString(),
|
||
editedAt: json['edited_at'] == null ? null : DateTime.tryParse(json['edited_at'].toString()),
|
||
messageType: MessageModel.parseMessageType(messageTypeStr),
|
||
fileId: json['file_id']?.toString(),
|
||
encryptedFileKey: json['encrypted_key']?.toString(),
|
||
fileName: json['file_name']?.toString(),
|
||
fileSize: json['file_size'] == null ? null : int.tryParse(json['file_size'].toString()),
|
||
);
|
||
}
|
||
|
||
static MessageType parseMessageType(String typeStr) {
|
||
switch (typeStr.toLowerCase()) {
|
||
case 'image':
|
||
return MessageType.image;
|
||
case 'video':
|
||
return MessageType.video;
|
||
case 'file':
|
||
return MessageType.file;
|
||
case 'video_note':
|
||
case 'videonote':
|
||
return MessageType.videoNote;
|
||
case 'voice_note':
|
||
case 'voicenote':
|
||
return MessageType.voiceNote;
|
||
case 'text':
|
||
default:
|
||
return MessageType.text;
|
||
}
|
||
}
|
||
|
||
|
||
static String getMediaPreview(MessageType type) {
|
||
switch (type) {
|
||
case MessageType.videoNote:
|
||
return '[Кружок]';
|
||
case MessageType.voiceNote:
|
||
return '[Голосовое]';
|
||
case MessageType.image:
|
||
return '[Фото]';
|
||
case MessageType.video:
|
||
return '[Видео]';
|
||
case MessageType.file:
|
||
return '[Файл]';
|
||
case MessageType.text:
|
||
return '';
|
||
}
|
||
}
|
||
|
||
Map<String, dynamic> toJson() {
|
||
return {
|
||
'id': id,
|
||
'temp_id': tempId,
|
||
'sender_id': senderId,
|
||
'receiver_id': receiverId,
|
||
'text': text,
|
||
'created_at': createdAt.toIso8601String(),
|
||
'status': status.name,
|
||
'reply_to_id': replyToId,
|
||
'reply_to_text': replyToText,
|
||
'edited_at': editedAt?.toIso8601String(),
|
||
'message_type': messageType.name,
|
||
'file_id': fileId,
|
||
'encrypted_key': encryptedFileKey,
|
||
'file_name': fileName,
|
||
'file_size': fileSize,
|
||
};
|
||
}
|
||
}
|