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 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 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, }; } }