class Message { final String id; final String content; final String? author; final String? groupId; final String converseId; final bool hasRecall; final Map? meta; final DateTime? createdAt; final DateTime? updatedAt; Message({ required this.id, required this.content, this.author, this.groupId, required this.converseId, this.hasRecall = false, this.meta, this.createdAt, this.updatedAt, }); factory Message.fromJson(Map json) { return Message( id: (json['_id'] ?? json['id'] ?? '').toString(), content: json['content'] ?? '', author: json['author']?.toString(), groupId: json['groupId']?.toString(), converseId: (json['converseId'] ?? '').toString(), hasRecall: json['hasRecall'] ?? false, meta: json['meta'], createdAt: json['createdAt'] != null ? DateTime.parse(json['createdAt'].toString()) : null, updatedAt: json['updatedAt'] != null ? DateTime.parse(json['updatedAt'].toString()) : null, ); } Map toJson() { return { '_id': id, 'content': content, 'author': author, 'groupId': groupId, 'converseId': converseId, 'hasRecall': hasRecall, 'meta': meta, 'createdAt': createdAt?.toIso8601String(), 'updatedAt': updatedAt?.toIso8601String(), }; } /// Determine message type from meta MessageType get type { if (hasRecall) return MessageType.recalled; final metaType = meta?['type']; if (metaType == 'image' || metaType == 'file') { return MessageType.file; } if (metaType == 'system') { return MessageType.system; } return MessageType.text; } /// Convenience alias for author (sender ID) String get senderId => author ?? ''; /// For display purposes - get sender name from meta if available String get senderName => meta?['nickname']?.toString() ?? ''; } enum MessageType { text, file, system, recalled, }