146 lines
4.9 KiB
Dart
146 lines
4.9 KiB
Dart
import 'dart:convert';
|
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
import 'package:moxxyv2/service/database/helpers.dart';
|
|
import 'package:moxxyv2/shared/error_types.dart';
|
|
import 'package:moxxyv2/shared/helpers.dart';
|
|
import 'package:moxxyv2/shared/warning_types.dart';
|
|
|
|
part 'message.freezed.dart';
|
|
part 'message.g.dart';
|
|
|
|
Map<String, String>? _optionalJsonDecode(String? data) {
|
|
if (data == null) return null;
|
|
|
|
return jsonDecode(data) as Map<String, String>;
|
|
}
|
|
|
|
String? _optionalJsonEncode(Map<String, String>? data) {
|
|
if (data == null) return null;
|
|
|
|
return jsonEncode(data);
|
|
}
|
|
|
|
@freezed
|
|
class Message with _$Message {
|
|
// NOTE: id is the database id of the message
|
|
// NOTE: isMedia is for telling the UI that this message contains the URL for media but the path is not yet available
|
|
// NOTE: srcUrl is the Url that a file has been or can be downloaded from
|
|
|
|
factory Message(
|
|
String sender,
|
|
String body,
|
|
int timestamp,
|
|
String sid,
|
|
int id,
|
|
String conversationJid,
|
|
bool isMedia,
|
|
bool isFileUploadNotification,
|
|
bool encrypted,
|
|
{
|
|
int? errorType,
|
|
int? warningType,
|
|
String? mediaUrl,
|
|
@Default(false) bool isDownloading,
|
|
@Default(false) bool isUploading,
|
|
String? mediaType,
|
|
String? thumbnailData,
|
|
int? mediaWidth,
|
|
int? mediaHeight,
|
|
String? srcUrl,
|
|
String? key,
|
|
String? iv,
|
|
String? encryptionScheme,
|
|
@Default(false) bool received,
|
|
@Default(false) bool displayed,
|
|
@Default(false) bool acked,
|
|
@Default(false) bool isRetracted,
|
|
String? originId,
|
|
Message? quotes,
|
|
String? filename,
|
|
Map<String, String>? plaintextHashes,
|
|
Map<String, String>? ciphertextHashes,
|
|
int? mediaSize,
|
|
}
|
|
) = _Message;
|
|
|
|
const Message._();
|
|
|
|
/// JSON
|
|
factory Message.fromJson(Map<String, dynamic> json) => _$MessageFromJson(json);
|
|
|
|
factory Message.fromDatabaseJson(Map<String, dynamic> json, Message? quotes) {
|
|
return Message.fromJson({
|
|
...json,
|
|
'received': intToBool(json['received']! as int),
|
|
'displayed': intToBool(json['displayed']! as int),
|
|
'acked': intToBool(json['acked']! as int),
|
|
'isMedia': intToBool(json['isMedia']! as int),
|
|
'isFileUploadNotification': intToBool(json['isFileUploadNotification']! as int),
|
|
'encrypted': intToBool(json['encrypted']! as int),
|
|
'plaintextHashes': _optionalJsonDecode(json['plaintextHashes'] as String?),
|
|
'ciphertextHashes': _optionalJsonDecode(json['ciphertextHashes'] as String?),
|
|
'isDownloading': intToBool(json['isDownloading']! as int),
|
|
'isUploading': intToBool(json['isUploading']! as int),
|
|
'isRetracted': intToBool(json['isRetracted']! as int),
|
|
}).copyWith(quotes: quotes);
|
|
}
|
|
|
|
Map<String, dynamic> toDatabaseJson() {
|
|
final map = toJson()
|
|
..remove('id')
|
|
..remove('quotes');
|
|
|
|
return {
|
|
...map,
|
|
'isMedia': boolToInt(isMedia),
|
|
'isFileUploadNotification': boolToInt(isFileUploadNotification),
|
|
'received': boolToInt(received),
|
|
'displayed': boolToInt(displayed),
|
|
'acked': boolToInt(acked),
|
|
'encrypted': boolToInt(encrypted),
|
|
// NOTE: Message.quote_id is a foreign-key
|
|
'quote_id': quotes?.id,
|
|
'plaintextHashes': _optionalJsonEncode(plaintextHashes),
|
|
'ciphertextHashes': _optionalJsonEncode(ciphertextHashes),
|
|
'isDownloading': boolToInt(isDownloading),
|
|
'isUploading': boolToInt(isUploading),
|
|
'isRetracted': boolToInt(isRetracted),
|
|
};
|
|
}
|
|
|
|
/// Returns true if the message is an error. If not, then returns false.
|
|
bool isError() {
|
|
return errorType != null && errorType != noError;
|
|
}
|
|
|
|
/// Returns true if the message is a warning. If not, then returns false.
|
|
bool isWarning() {
|
|
return warningType != null && warningType != noWarning;
|
|
}
|
|
|
|
/// Returns a representative emoji for a message. Its primary purpose is
|
|
/// to provide a universal fallback for quoted media messages.
|
|
String get messageEmoji {
|
|
return mimeTypeToEmoji(mediaType, addTypeName: false);
|
|
}
|
|
|
|
/// Returns true if the message can be quoted. False if not.
|
|
bool get isQuotable => !isError() && !isRetracted && !isFileUploadNotification && !isUploading && !isDownloading;
|
|
|
|
/// Returns true if the message can be retracted. False if not.
|
|
/// [sentBySelf] asks whether or not the message was sent by us (the current Jid).
|
|
bool canRetract(bool sentBySelf) {
|
|
return originId != null && sentBySelf && !isFileUploadNotification && !isUploading && !isDownloading;
|
|
}
|
|
|
|
/// Returns true if the message can be edited. False if not.
|
|
/// [sentBySelf] asks whether or not the message was sent by us (the current Jid).
|
|
bool canEdit(bool sentBySelf) {
|
|
return sentBySelf && !isMedia && !isFileUploadNotification && !isUploading && !isDownloading;
|
|
}
|
|
|
|
/// Returns true if the message can open the selection menu by longpressing. False if
|
|
/// not.
|
|
bool get isLongpressable => !isRetracted;
|
|
}
|