fix(ui,shared,service): Use a UUID as a unique message key
This commit is contained in:
parent
865846af9a
commit
301ff664a8
@ -108,9 +108,7 @@ files:
|
||||
implements:
|
||||
- JsonImplementation
|
||||
attributes:
|
||||
key:
|
||||
type: MessageKey
|
||||
deserialise: true
|
||||
id: String
|
||||
progress: double?
|
||||
# Triggered by [RosterService] if we receive a roster push.
|
||||
- name: RosterDiffEvent
|
||||
@ -559,26 +557,21 @@ files:
|
||||
implements:
|
||||
- JsonImplementation
|
||||
attributes:
|
||||
sid: String
|
||||
conversationJid: String
|
||||
id: String
|
||||
sendMarker: bool
|
||||
- name: AddReactionToMessageCommand
|
||||
extends: BackgroundCommand
|
||||
implements:
|
||||
- JsonImplementation
|
||||
attributes:
|
||||
key:
|
||||
type: MessageKey
|
||||
deserialise: true
|
||||
id: String
|
||||
emoji: String
|
||||
- name: RemoveReactionFromMessageCommand
|
||||
extends: BackgroundCommand
|
||||
implements:
|
||||
- JsonImplementation
|
||||
attributes:
|
||||
key:
|
||||
type: MessageKey
|
||||
deserialise: true
|
||||
id: String
|
||||
emoji: String
|
||||
- name: MarkOmemoDeviceAsVerifiedCommand
|
||||
extends: BackgroundCommand
|
||||
@ -652,9 +645,7 @@ files:
|
||||
implements:
|
||||
- JsonImplementation
|
||||
attributes:
|
||||
key:
|
||||
type: MessageKey
|
||||
deserialise: true
|
||||
id: String
|
||||
- name: RequestAvatarForJidCommand
|
||||
extends: BackgroundCommand
|
||||
implements:
|
||||
|
@ -84,9 +84,8 @@ class ConversationService {
|
||||
|
||||
Message? lastMessage;
|
||||
if (c['lastMessageId'] != null) {
|
||||
lastMessage = await GetIt.I.get<MessageService>().getMessageBySid(
|
||||
lastMessage = await GetIt.I.get<MessageService>().getMessageById(
|
||||
c['lastMessageId']! as String,
|
||||
jid,
|
||||
accountJid,
|
||||
queryReactionPreview: false,
|
||||
);
|
||||
@ -173,7 +172,7 @@ class ConversationService {
|
||||
final c = <String, dynamic>{};
|
||||
|
||||
if (lastMessage != null) {
|
||||
c['lastMessageId'] = lastMessage.sid;
|
||||
c['lastMessageId'] = lastMessage.id;
|
||||
}
|
||||
if (lastChangeTimestamp != null) {
|
||||
c['lastChangeTimestamp'] = lastChangeTimestamp;
|
||||
|
@ -2,6 +2,7 @@ import 'package:get_it/get_it.dart';
|
||||
import 'package:moxxyv2/service/database/constants.dart';
|
||||
import 'package:moxxyv2/service/xmpp_state.dart';
|
||||
import 'package:sqflite_sqlcipher/sqflite.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
extension MaybeGet<K, V> on Map<K, V> {
|
||||
V? maybeGet(K? key) {
|
||||
@ -65,6 +66,7 @@ Future<void> upgradeFromV45ToV46(Database db) async {
|
||||
await db.execute(
|
||||
'''
|
||||
CREATE TABLE ${messagesTable}_new (
|
||||
id TEXT PRIMARY KEY,
|
||||
accountJid TEXT NOT NULL,
|
||||
sender TEXT NOT NULL,
|
||||
body TEXT,
|
||||
@ -79,7 +81,7 @@ Future<void> upgradeFromV45ToV46(Database db) async {
|
||||
displayed INTEGER,
|
||||
acked INTEGER,
|
||||
originId TEXT,
|
||||
quote_sid TEXT,
|
||||
quote_id TEXT,
|
||||
file_metadata_id TEXT,
|
||||
isDownloading INTEGER NOT NULL,
|
||||
isUploading INTEGER NOT NULL,
|
||||
@ -89,30 +91,24 @@ Future<void> upgradeFromV45ToV46(Database db) async {
|
||||
stickerPackId TEXT,
|
||||
pseudoMessageType INTEGER,
|
||||
pseudoMessageData TEXT,
|
||||
PRIMARY KEY (accountJid, sender, sid, timestamp, conversationJid),
|
||||
CONSTRAINT fk_quote
|
||||
FOREIGN KEY (accountJid, quote_sid)
|
||||
REFERENCES $messagesTable (accountJid, sid)
|
||||
FOREIGN KEY (accountJid, quote_id)
|
||||
REFERENCES $messagesTable (accountJid, id)
|
||||
CONSTRAINT fk_file_metadata
|
||||
FOREIGN KEY (file_metadata_id)
|
||||
REFERENCES $fileMetadataTable (id)
|
||||
)''',
|
||||
);
|
||||
// Build up the message map
|
||||
/// Message's old id attribute -> Message's sid attribute.
|
||||
/// Message's old id attribute -> Message's new UUID attribute.
|
||||
const uuid = Uuid();
|
||||
final messageMap = <int, String>{};
|
||||
final conversationMap = <int, String>{};
|
||||
final timestampMap = <int, int>{};
|
||||
final senderMap = <int, String>{};
|
||||
|
||||
|
||||
if (migrateRows) {
|
||||
final messages = await db.query(messagesTable);
|
||||
for (final message in messages) {
|
||||
messageMap[message['id']! as int] = message['sid']! as String;
|
||||
conversationMap[message['id']! as int] =
|
||||
message['conversationJid']! as String;
|
||||
timestampMap[message['id']! as int] = message['timestamp']! as int;
|
||||
senderMap[message['id']! as int] = message['sender']! as String;
|
||||
messageMap[message['id']! as int] = uuid.v4();
|
||||
}
|
||||
// Then migrate messages
|
||||
for (final message in messages) {
|
||||
@ -121,7 +117,8 @@ Future<void> upgradeFromV45ToV46(Database db) async {
|
||||
..remove('id')
|
||||
..remove('quote_id'),
|
||||
'accountJid': accountJid,
|
||||
'quote_sid': messageMap.maybeGet(message['quote_id'] as int?)
|
||||
'quote_id': messageMap.maybeGet(message['quote_id'] as int?),
|
||||
'id': messageMap[message['id']! as int]!,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -156,7 +153,7 @@ Future<void> upgradeFromV45ToV46(Database db) async {
|
||||
PRIMARY KEY (jid, accountJid),
|
||||
CONSTRAINT fk_last_message
|
||||
FOREIGN KEY (accountJid, lastMessageId)
|
||||
REFERENCES $messagesTable (accountJid, sid),
|
||||
REFERENCES $messagesTable (accountJid, id),
|
||||
CONSTRAINT fk_contact_id
|
||||
FOREIGN KEY (contactId)
|
||||
REFERENCES $contactsTable (id)
|
||||
@ -188,17 +185,14 @@ Future<void> upgradeFromV45ToV46(Database db) async {
|
||||
await db.execute(
|
||||
'''
|
||||
CREATE TABLE ${reactionsTable}_new (
|
||||
accountJid TEXT NOT NULL,
|
||||
message_timestamp INTEGER NOT NULL,
|
||||
message_sender TEXT NOT NULL,
|
||||
senderJid TEXT NOT NULL,
|
||||
emoji TEXT NOT NULL,
|
||||
message_sid TEXT NOT NULL,
|
||||
conversationJid TEXT NOT NULL,
|
||||
PRIMARY KEY (accountJid, senderJid, emoji, message_sid, conversationJid, message_timestamp, message_sender),
|
||||
accountJid TEXT NOT NULL,
|
||||
message_id TEXT NOT NULL,
|
||||
senderJid TEXT NOT NULL,
|
||||
emoji TEXT NOT NULL,
|
||||
PRIMARY KEY (accountJid, senderJid, emoji, message_id),
|
||||
CONSTRAINT fk_message
|
||||
FOREIGN KEY (accountJid, message_sid, conversationJid, message_timestamp, message_sender)
|
||||
REFERENCES $messagesTable (accountJid, sid, conversationJid, timestamp, sender)
|
||||
FOREIGN KEY (accountJid, message_id)
|
||||
REFERENCES $messagesTable (accountJid, id)
|
||||
ON DELETE CASCADE
|
||||
)''',
|
||||
);
|
||||
@ -207,12 +201,10 @@ Future<void> upgradeFromV45ToV46(Database db) async {
|
||||
await db.insert(
|
||||
'${reactionsTable}_new',
|
||||
{
|
||||
...Map.from(reaction)..remove('message_id'),
|
||||
'message_sid': messageMap.maybeGet(reaction['message_id']! as int),
|
||||
'conversationJid': conversationMap[reaction['message_id']! as int],
|
||||
...Map.from(reaction)
|
||||
..remove('message_id'),
|
||||
'message_id': messageMap.maybeGet(reaction['message_id']! as int),
|
||||
'accountJid': accountJid,
|
||||
'message_timestamp': timestampMap[reaction['message_id']! as int],
|
||||
'message_sender': senderMap[reaction['message_id']! as int],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -669,8 +669,7 @@ Future<void> performRequestDownload(
|
||||
final accountJid = await GetIt.I.get<XmppStateService>().getAccountJid();
|
||||
|
||||
final message = await ms.updateMessage(
|
||||
command.message.sid,
|
||||
command.message.conversationJid,
|
||||
command.message.id,
|
||||
accountJid,
|
||||
isDownloading: true,
|
||||
);
|
||||
@ -687,7 +686,9 @@ Future<void> performRequestDownload(
|
||||
|
||||
await srv.downloadFile(
|
||||
FileDownloadJob(
|
||||
message.messageKey,
|
||||
message.id,
|
||||
message.conversationJid,
|
||||
accountJid,
|
||||
MediaFileLocation(
|
||||
fileMetadata.sourceUrls!,
|
||||
fileMetadata.filename,
|
||||
@ -702,7 +703,6 @@ Future<void> performRequestDownload(
|
||||
fileMetadata.ciphertextHashes,
|
||||
null,
|
||||
),
|
||||
accountJid,
|
||||
message.fileMetadata!.id,
|
||||
message.fileMetadata!.plaintextHashes?.isNotEmpty ?? false,
|
||||
mimeGuess,
|
||||
@ -1056,8 +1056,7 @@ Future<void> performMarkConversationAsRead(
|
||||
|
||||
if (conversation.lastMessage != null) {
|
||||
await GetIt.I.get<MessageService>().markMessageAsRead(
|
||||
conversation.lastMessage!.sid,
|
||||
conversation.lastMessage!.conversationJid,
|
||||
conversation.lastMessage!.id,
|
||||
accountJid,
|
||||
conversation.type != ConversationType.note,
|
||||
);
|
||||
@ -1077,8 +1076,7 @@ Future<void> performMarkMessageAsRead(
|
||||
}) async {
|
||||
final accountJid = await GetIt.I.get<XmppStateService>().getAccountJid();
|
||||
await GetIt.I.get<MessageService>().markMessageAsRead(
|
||||
command.sid,
|
||||
command.conversationJid,
|
||||
command.id,
|
||||
accountJid,
|
||||
command.sendMarker,
|
||||
);
|
||||
@ -1091,8 +1089,7 @@ Future<void> performAddMessageReaction(
|
||||
final accountJid = await GetIt.I.get<XmppStateService>().getAccountJid();
|
||||
final rs = GetIt.I.get<ReactionsService>();
|
||||
final msg = await rs.addNewReaction(
|
||||
command.key.sid,
|
||||
command.key.conversationJid,
|
||||
command.id,
|
||||
accountJid,
|
||||
accountJid,
|
||||
command.emoji,
|
||||
@ -1108,19 +1105,18 @@ Future<void> performAddMessageReaction(
|
||||
),
|
||||
);
|
||||
|
||||
if (command.key.conversationJid != '') {
|
||||
if (msg.conversationJid != '') {
|
||||
// Send the reaction
|
||||
final manager = GetIt.I
|
||||
.get<XmppConnection>()
|
||||
.getManagerById<MessageManager>(messageManager)!;
|
||||
await manager.sendMessage(
|
||||
JID.fromString(command.key.conversationJid),
|
||||
JID.fromString(msg.conversationJid),
|
||||
TypedMap<StanzaHandlerExtension>.fromList([
|
||||
MessageReactionsData(
|
||||
msg.originId ?? msg.sid,
|
||||
await rs.getReactionsForMessageByJid(
|
||||
command.key.sid,
|
||||
command.key.conversationJid,
|
||||
command.id,
|
||||
accountJid,
|
||||
accountJid,
|
||||
),
|
||||
@ -1140,8 +1136,7 @@ Future<void> performRemoveMessageReaction(
|
||||
final accountJid = await GetIt.I.get<XmppStateService>().getAccountJid();
|
||||
final rs = GetIt.I.get<ReactionsService>();
|
||||
final msg = await rs.removeReaction(
|
||||
command.key.sid,
|
||||
command.key.conversationJid,
|
||||
command.id,
|
||||
accountJid,
|
||||
accountJid,
|
||||
command.emoji,
|
||||
@ -1158,19 +1153,18 @@ Future<void> performRemoveMessageReaction(
|
||||
);
|
||||
|
||||
|
||||
if (command.key.conversationJid != '') {
|
||||
if (msg.conversationJid != '') {
|
||||
// Send the reaction
|
||||
final manager = GetIt.I
|
||||
.get<XmppConnection>()
|
||||
.getManagerById<MessageManager>(messageManager)!;
|
||||
await manager.sendMessage(
|
||||
JID.fromString(command.key.conversationJid),
|
||||
JID.fromString(msg.conversationJid),
|
||||
TypedMap<StanzaHandlerExtension>.fromList([
|
||||
MessageReactionsData(
|
||||
msg.originId ?? msg.sid,
|
||||
await rs.getReactionsForMessageByJid(
|
||||
command.key.sid,
|
||||
command.key.conversationJid,
|
||||
command.id,
|
||||
accountJid,
|
||||
accountJid,
|
||||
),
|
||||
@ -1395,8 +1389,7 @@ Future<void> performGetReactions(
|
||||
final accountJid = await GetIt.I.get<XmppStateService>().getAccountJid();
|
||||
final reactionsRaw =
|
||||
await GetIt.I.get<ReactionsService>().getReactionsForMessage(
|
||||
command.key.sid,
|
||||
command.key.conversationJid,
|
||||
command.id,
|
||||
accountJid,
|
||||
);
|
||||
final reactionsMap = <String, List<String>>{};
|
||||
|
@ -144,8 +144,7 @@ class HttpFileTransferService {
|
||||
for (final recipient in job.recipients) {
|
||||
final m = job.messageMap[recipient]!;
|
||||
final msg = await ms.updateMessage(
|
||||
m.sid,
|
||||
m.conversationJid,
|
||||
m.id,
|
||||
job.accountJid,
|
||||
errorType: error,
|
||||
isUploading: false,
|
||||
@ -155,7 +154,7 @@ class HttpFileTransferService {
|
||||
// Update the conversation list
|
||||
final conversation =
|
||||
await cs.getConversationByJid(recipient, job.accountJid);
|
||||
if (conversation?.lastMessage?.sid == msg.sid) {
|
||||
if (conversation?.lastMessage?.id == msg.id) {
|
||||
final newConversation = conversation!.copyWith(
|
||||
lastMessage: msg,
|
||||
);
|
||||
@ -217,7 +216,6 @@ class HttpFileTransferService {
|
||||
}
|
||||
final slot = slotResult.get<HttpFileUploadSlot>();
|
||||
|
||||
final messageKey = job.messageMap.values.first.messageKey;
|
||||
final uploadStatusCode = await client.uploadFile(
|
||||
Uri.parse(slot.putUrl),
|
||||
slot.headers,
|
||||
@ -229,7 +227,7 @@ class HttpFileTransferService {
|
||||
final progress = current.toDouble() / total.toDouble();
|
||||
sendEvent(
|
||||
ProgressEvent(
|
||||
key: messageKey,
|
||||
id: job.messageMap[job.recipients.first]!.id,
|
||||
progress: progress == 1 ? 0.99 : progress,
|
||||
),
|
||||
);
|
||||
@ -328,8 +326,7 @@ class HttpFileTransferService {
|
||||
// Notify UI of upload completion
|
||||
final m = job.messageMap[recipient]!;
|
||||
var msg = await ms.updateMessage(
|
||||
m.sid,
|
||||
m.conversationJid,
|
||||
m.id,
|
||||
job.accountJid,
|
||||
errorType: null,
|
||||
isUploading: false,
|
||||
@ -338,10 +335,9 @@ class HttpFileTransferService {
|
||||
// TODO(Unknown): Maybe batch those two together?
|
||||
final oldSid = msg.sid;
|
||||
msg = await ms.updateMessage(
|
||||
msg.sid,
|
||||
msg.conversationJid,
|
||||
msg.id,
|
||||
job.accountJid,
|
||||
newSid: uuid.v4(),
|
||||
sid: uuid.v4(),
|
||||
originId: uuid.v4(),
|
||||
);
|
||||
sendEvent(MessageUpdatedEvent(message: msg));
|
||||
@ -407,8 +403,7 @@ class HttpFileTransferService {
|
||||
|
||||
// Notify UI of download failure
|
||||
final msg = await ms.updateMessage(
|
||||
job.messageKey.sid,
|
||||
job.messageKey.conversationJid,
|
||||
job.messageId,
|
||||
job.accountJid,
|
||||
errorType: error,
|
||||
isDownloading: false,
|
||||
@ -450,7 +445,7 @@ class HttpFileTransferService {
|
||||
final progress = current.toDouble() / total.toDouble();
|
||||
sendEvent(
|
||||
ProgressEvent(
|
||||
key: job.messageKey,
|
||||
id: job.messageId,
|
||||
progress: progress == 1 ? 0.99 : progress,
|
||||
),
|
||||
);
|
||||
@ -476,7 +471,7 @@ class HttpFileTransferService {
|
||||
// The file was downloaded and is now being decrypted
|
||||
sendEvent(
|
||||
ProgressEvent(
|
||||
key: job.messageKey,
|
||||
id: job.messageId,
|
||||
),
|
||||
);
|
||||
|
||||
@ -590,7 +585,7 @@ class HttpFileTransferService {
|
||||
|
||||
final cs = GetIt.I.get<ConversationService>();
|
||||
final conversation = (await cs.getConversationByJid(
|
||||
job.messageKey.conversationJid,
|
||||
job.conversationJid,
|
||||
job.accountJid,
|
||||
))!;
|
||||
|
||||
@ -603,8 +598,7 @@ class HttpFileTransferService {
|
||||
}
|
||||
|
||||
final msg = await GetIt.I.get<MessageService>().updateMessage(
|
||||
job.messageKey.sid,
|
||||
job.messageKey.conversationJid,
|
||||
job.messageId,
|
||||
job.accountJid,
|
||||
fileMetadata: metadata,
|
||||
isFileUploadNotification: false,
|
||||
@ -615,7 +609,7 @@ class HttpFileTransferService {
|
||||
sendEvent(MessageUpdatedEvent(message: msg));
|
||||
|
||||
final updatedConversation = conversation.copyWith(
|
||||
lastMessage: conversation.lastMessage?.sid == job.messageKey.sid
|
||||
lastMessage: conversation.lastMessage?.id == job.messageId
|
||||
? msg
|
||||
: conversation.lastMessage,
|
||||
);
|
||||
|
@ -55,24 +55,29 @@ class FileUploadJob {
|
||||
@immutable
|
||||
class FileDownloadJob {
|
||||
const FileDownloadJob(
|
||||
this.messageKey,
|
||||
this.location,
|
||||
this.messageId,
|
||||
this.conversationJid,
|
||||
this.accountJid,
|
||||
this.location,
|
||||
this.metadataId,
|
||||
this.createMetadataHashes,
|
||||
this.mimeGuess, {
|
||||
this.shouldShowNotification = true,
|
||||
});
|
||||
|
||||
/// The message key.
|
||||
final MessageKey messageKey;
|
||||
/// The message id.
|
||||
final String messageId;
|
||||
|
||||
/// The JID of the conversation we're downloading the file in.
|
||||
final String conversationJid;
|
||||
|
||||
/// The associated account.
|
||||
final String accountJid;
|
||||
|
||||
|
||||
/// The location where the file can be found.
|
||||
final MediaFileLocation location;
|
||||
|
||||
/// The associated account
|
||||
final String accountJid;
|
||||
|
||||
/// The id of the file metadata describing the file.
|
||||
final String metadataId;
|
||||
|
||||
@ -89,8 +94,9 @@ class FileDownloadJob {
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is FileDownloadJob &&
|
||||
messageId == other.messageId &&
|
||||
conversationJid == other.conversationJid &&
|
||||
location == other.location &&
|
||||
messageKey == other.messageKey &&
|
||||
accountJid == other.accountJid &&
|
||||
metadataId == other.metadataId &&
|
||||
mimeGuess == other.mimeGuess &&
|
||||
@ -99,8 +105,9 @@ class FileDownloadJob {
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
conversationJid.hashCode ^
|
||||
messageId.hashCode ^
|
||||
location.hashCode ^
|
||||
messageKey.hashCode ^
|
||||
metadataId.hashCode ^
|
||||
mimeGuess.hashCode ^
|
||||
shouldShowNotification.hashCode;
|
||||
|
@ -17,11 +17,15 @@ import 'package:moxxyv2/shared/events.dart';
|
||||
import 'package:moxxyv2/shared/models/file_metadata.dart';
|
||||
import 'package:moxxyv2/shared/models/message.dart';
|
||||
import 'package:moxxyv2/shared/warning_types.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class MessageService {
|
||||
/// Logger
|
||||
final Logger _log = Logger('MessageService');
|
||||
|
||||
/// UUID instance for message ids.
|
||||
final _uuid = const Uuid();
|
||||
|
||||
Future<Message> _parseMessage(
|
||||
Map<String, Object?> rawMessage,
|
||||
String accountJid,
|
||||
@ -46,28 +50,33 @@ class MessageService {
|
||||
fm,
|
||||
queryReactionPreview
|
||||
? await GetIt.I.get<ReactionsService>().getPreviewReactionsForMessage(
|
||||
rawMessage['sid']! as String,
|
||||
rawMessage['conversationJid']! as String,
|
||||
rawMessage['id']! as String,
|
||||
accountJid,
|
||||
)
|
||||
: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// Queries the database for a message with a stanza id of [sid] inside
|
||||
/// the conversation [conversationJid] in the context of the account
|
||||
/// Queries the database for a message with a stanza id of [id] inside
|
||||
/// the conversation [conversationJid], if specified, in the context of the account
|
||||
/// [accountJid].
|
||||
Future<Message?> getMessageBySid(
|
||||
String sid,
|
||||
String conversationJid,
|
||||
Future<Message?> getMessageById(
|
||||
String id,
|
||||
String accountJid, {
|
||||
String? conversationJid,
|
||||
bool queryReactionPreview = true,
|
||||
}) async {
|
||||
final db = GetIt.I.get<DatabaseService>().database;
|
||||
final messagesRaw = await db.query(
|
||||
messagesTable,
|
||||
where: 'sid = ? AND conversationJid = ? AND accountJid = ?',
|
||||
whereArgs: [sid, conversationJid, accountJid],
|
||||
where: conversationJid != null
|
||||
? 'id = ? AND accountJid = ? AND conversationJid = ?'
|
||||
: 'id = ? AND accountJid = ?',
|
||||
whereArgs: [
|
||||
id,
|
||||
accountJid,
|
||||
if (conversationJid != null) conversationJid,
|
||||
],
|
||||
limit: 1,
|
||||
);
|
||||
|
||||
@ -77,22 +86,53 @@ class MessageService {
|
||||
}
|
||||
|
||||
/// Queries the database for a message with a stanza id of [originId] inside
|
||||
/// the conversation [conversationJid] in the context of the account
|
||||
/// the conversation [conversationJid], if specified, in the context of the account
|
||||
/// [accountJid].
|
||||
Future<Message?> getMessageByOriginId(
|
||||
String originId,
|
||||
String conversationJid,
|
||||
String accountJid, {
|
||||
String? conversationJid,
|
||||
bool queryReactionPreview = true,
|
||||
}) async {
|
||||
final db = GetIt.I.get<DatabaseService>().database;
|
||||
final messagesRaw = await db.query(
|
||||
messagesTable,
|
||||
where: 'conversationJid = ? AND accountJid = ? AND originId = ?',
|
||||
where: conversationJid != null
|
||||
? 'accountJid = ? AND originId = ? AND conversationJid = ?'
|
||||
: 'accountJid = ? AND originId = ?',
|
||||
whereArgs: [
|
||||
conversationJid,
|
||||
accountJid,
|
||||
originId,
|
||||
if (conversationJid != null) conversationJid,
|
||||
],
|
||||
limit: 1,
|
||||
);
|
||||
|
||||
if (messagesRaw.isEmpty) return null;
|
||||
|
||||
// TODO(PapaTutuWawa): Load the quoted message
|
||||
return _parseMessage(messagesRaw.first, accountJid, queryReactionPreview);
|
||||
}
|
||||
|
||||
/// Query the database for the message with a stanza id of [sid] in the context of [accountJid].
|
||||
/// If [conversationJid] is specified, then the message must also be within the conversation with
|
||||
/// [conversationJid].
|
||||
Future<Message?> getMessageByStanzaId(
|
||||
String sid,
|
||||
String accountJid, {
|
||||
String? conversationJid,
|
||||
bool queryReactionPreview = true,
|
||||
}) async {
|
||||
final db = GetIt.I.get<DatabaseService>().database;
|
||||
final messagesRaw = await db.query(
|
||||
messagesTable,
|
||||
where: conversationJid != null
|
||||
? 'accountJid = ? AND sid = ? AND conversationJid = ?'
|
||||
: 'accountJid = ? AND sid = ?',
|
||||
whereArgs: [
|
||||
accountJid,
|
||||
sid,
|
||||
if (conversationJid != null) conversationJid,
|
||||
],
|
||||
limit: 1,
|
||||
);
|
||||
@ -135,7 +175,7 @@ SELECT
|
||||
quote.displayed AS quote_displayed,
|
||||
quote.acked AS quote_acked,
|
||||
quote.originId AS quote_originId,
|
||||
quote.quote_sid AS quote_quote_sid,
|
||||
quote.quote_id AS quote_quote_id,
|
||||
quote.file_metadata_id AS quote_file_metadata_id,
|
||||
quote.isDownloading AS quote_isDownloading,
|
||||
quote.isUploading AS quote_isUploading,
|
||||
@ -162,7 +202,7 @@ SELECT
|
||||
fm.size as fm_size
|
||||
FROM (SELECT * FROM $messagesTable WHERE $query ORDER BY timestamp DESC LIMIT $messagePaginationSize) AS msg
|
||||
LEFT JOIN $fileMetadataTable fm ON msg.file_metadata_id = fm.id
|
||||
LEFT JOIN $messagesTable quote ON msg.quote_sid = quote.sid;
|
||||
LEFT JOIN $messagesTable quote ON msg.quote_id = quote.id;
|
||||
''',
|
||||
[
|
||||
jid,
|
||||
@ -178,7 +218,7 @@ FROM (SELECT * FROM $messagesTable WHERE $query ORDER BY timestamp DESC LIMIT $m
|
||||
}
|
||||
|
||||
Message? quotes;
|
||||
if (m['quote_sid'] != null) {
|
||||
if (m['quote_id'] != null) {
|
||||
final rawQuote = getPrefixedSubMap(m, 'quote_');
|
||||
|
||||
FileMetadata? quoteFm;
|
||||
@ -209,8 +249,7 @@ FROM (SELECT * FROM $messagesTable WHERE $query ORDER BY timestamp DESC LIMIT $m
|
||||
quotes,
|
||||
fm,
|
||||
await GetIt.I.get<ReactionsService>().getPreviewReactionsForMessage(
|
||||
m['sid']! as String,
|
||||
jid,
|
||||
m['id']! as String,
|
||||
accountJid,
|
||||
),
|
||||
),
|
||||
@ -296,8 +335,7 @@ FROM
|
||||
getPrefixedSubMap(m, 'fm_'),
|
||||
),
|
||||
await GetIt.I.get<ReactionsService>().getPreviewReactionsForMessage(
|
||||
m['sid']! as String,
|
||||
m['conversationJid']! as String,
|
||||
m['id']! as String,
|
||||
accountJid,
|
||||
),
|
||||
),
|
||||
@ -333,6 +371,7 @@ FROM
|
||||
}) async {
|
||||
final db = GetIt.I.get<DatabaseService>().database;
|
||||
var message = Message(
|
||||
_uuid.v4(),
|
||||
accountJid,
|
||||
sender,
|
||||
body,
|
||||
@ -358,7 +397,7 @@ FROM
|
||||
|
||||
if (quoteId != null) {
|
||||
final quotes =
|
||||
await getMessageBySid(quoteId, conversationJid, accountJid);
|
||||
await getMessageById(quoteId, accountJid);
|
||||
if (quotes == null) {
|
||||
_log.warning('Failed to add quote for message with id $quoteId');
|
||||
} else {
|
||||
@ -372,10 +411,9 @@ FROM
|
||||
|
||||
/// Wrapper around [DatabaseService]'s updateMessage that updates the cache
|
||||
Future<Message> updateMessage(
|
||||
String sid,
|
||||
String conversationJid,
|
||||
String id,
|
||||
String accountJid, {
|
||||
String? newSid,
|
||||
String? sid,
|
||||
Object? body = notSpecified,
|
||||
bool? received,
|
||||
bool? displayed,
|
||||
@ -432,22 +470,21 @@ FROM
|
||||
if (isEdited != null) {
|
||||
m['isEdited'] = boolToInt(isEdited);
|
||||
}
|
||||
if (newSid != null) {
|
||||
m['sid'] = newSid;
|
||||
if (sid != null) {
|
||||
m['sid'] = sid;
|
||||
}
|
||||
|
||||
final updatedMessage = await db.updateAndReturn(
|
||||
messagesTable,
|
||||
m,
|
||||
where: 'sid = ? AND conversationJid = ? AND accountJid = ?',
|
||||
whereArgs: [sid, conversationJid, accountJid],
|
||||
where: 'id = ? AND accountJid = ?',
|
||||
whereArgs: [id, accountJid],
|
||||
);
|
||||
|
||||
Message? quotes;
|
||||
if (updatedMessage['quote_sid'] != null) {
|
||||
quotes = await getMessageBySid(
|
||||
updatedMessage['quote_sid']! as String,
|
||||
updatedMessage['conversationJid']! as String,
|
||||
if (updatedMessage['quote_id'] != null) {
|
||||
quotes = await getMessageById(
|
||||
updatedMessage['quote_id']! as String,
|
||||
accountJid,
|
||||
queryReactionPreview: false,
|
||||
);
|
||||
@ -471,10 +508,9 @@ FROM
|
||||
updatedMessage,
|
||||
quotes,
|
||||
metadata,
|
||||
// TODO: How should this work with reactions?
|
||||
await GetIt.I
|
||||
.get<ReactionsService>()
|
||||
.getPreviewReactionsForMessage(sid, conversationJid, accountJid),
|
||||
.getPreviewReactionsForMessage(id, accountJid),
|
||||
);
|
||||
|
||||
return msg;
|
||||
@ -500,7 +536,6 @@ FROM
|
||||
) async {
|
||||
final msg = await getMessageByOriginId(
|
||||
originId,
|
||||
conversationJid,
|
||||
accountJid,
|
||||
queryReactionPreview: false,
|
||||
);
|
||||
@ -524,8 +559,7 @@ FROM
|
||||
|
||||
final isMedia = msg.isMedia;
|
||||
final retractedMessage = await updateMessage(
|
||||
msg.sid,
|
||||
msg.conversationJid,
|
||||
msg.id,
|
||||
accountJid,
|
||||
warningType: null,
|
||||
errorType: null,
|
||||
@ -539,7 +573,7 @@ FROM
|
||||
final conversation =
|
||||
await cs.getConversationByJid(conversationJid, accountJid);
|
||||
if (conversation != null) {
|
||||
if (conversation.lastMessage?.sid == msg.sid) {
|
||||
if (conversation.lastMessage?.id == msg.id) {
|
||||
final newConversation = conversation.copyWith(
|
||||
lastMessage: retractedMessage,
|
||||
);
|
||||
@ -565,19 +599,17 @@ FROM
|
||||
}
|
||||
}
|
||||
|
||||
/// Marks the message with the stanza id [sid] as displayed and sends an
|
||||
/// Marks the message with the message id [id] as displayed and sends an
|
||||
/// [MessageUpdatedEvent] to the UI. if [sendChatMarker] is true, then
|
||||
/// a Chat Marker with <displayed /> is sent to the message's
|
||||
/// conversationJid attribute.
|
||||
Future<Message> markMessageAsRead(
|
||||
String sid,
|
||||
String converationJid,
|
||||
String id,
|
||||
String accountJid,
|
||||
bool sendChatMarker,
|
||||
) async {
|
||||
final newMessage = await updateMessage(
|
||||
sid,
|
||||
converationJid,
|
||||
id,
|
||||
accountJid,
|
||||
displayed: true,
|
||||
);
|
||||
|
@ -1,4 +1,3 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
@ -27,7 +26,7 @@ const _warningChannelKey = 'warning_channel';
|
||||
|
||||
/// Message payload keys.
|
||||
const _conversationJidKey = 'conversationJid';
|
||||
const _messageKeyKey = 'key';
|
||||
const _messageIdKey = 'message_id';
|
||||
const _conversationTitleKey = 'title';
|
||||
const _conversationAvatarKey = 'avatarPath';
|
||||
|
||||
@ -50,13 +49,9 @@ class NotificationsService {
|
||||
);
|
||||
} else if (event.type == NotificationEventType.markAsRead) {
|
||||
final accountJid = await GetIt.I.get<XmppStateService>().getAccountJid();
|
||||
final messageKey = modelm.MessageKey.fromJson(
|
||||
jsonDecode(event.extra![_messageKeyKey]!) as Map<String, Object>,
|
||||
);
|
||||
// Mark the message as read
|
||||
await GetIt.I.get<MessageService>().markMessageAsRead(
|
||||
messageKey.sid,
|
||||
messageKey.conversationJid,
|
||||
event.extra![_messageIdKey]!,
|
||||
accountJid,
|
||||
// [XmppService.sendReadMarker] will check whether the *SHOULD* send
|
||||
// the marker, i.e. if the privacy settings allow it.
|
||||
@ -301,7 +296,7 @@ class NotificationsService {
|
||||
isGroupchat: c.isGroupchat,
|
||||
extra: {
|
||||
_conversationJidKey: c.jid,
|
||||
_messageKeyKey: jsonEncode(m.messageKey),
|
||||
_messageIdKey: m.id,
|
||||
_conversationTitleKey: await c.titleWithOptionalContactService,
|
||||
_conversationAvatarKey: await c.avatarPathWithOptionalContactService,
|
||||
},
|
||||
@ -351,7 +346,7 @@ class NotificationsService {
|
||||
isGroupchat: c.isGroupchat,
|
||||
extra: {
|
||||
_conversationJidKey: c.jid,
|
||||
_messageKeyKey: jsonEncode(m.messageKey),
|
||||
_messageIdKey: m.id,
|
||||
_conversationTitleKey: await c.titleWithOptionalContactService,
|
||||
_conversationAvatarKey: await c.avatarPathWithOptionalContactService,
|
||||
},
|
||||
|
@ -21,17 +21,16 @@ class ReactionWrapper {
|
||||
class ReactionsService {
|
||||
final Logger _log = Logger('ReactionsService');
|
||||
|
||||
/// Query the database for 6 distinct emoji reactions associated with the message sid
|
||||
/// [sid].
|
||||
/// Query the database for 6 distinct emoji reactions associated with the message id
|
||||
/// [id].
|
||||
Future<List<String>> getPreviewReactionsForMessage(
|
||||
String sid,
|
||||
String conversationJid,
|
||||
String id,
|
||||
String accountJid,
|
||||
) async {
|
||||
final reactions = await GetIt.I.get<DatabaseService>().database.query(
|
||||
reactionsTable,
|
||||
where: 'message_sid = ? AND conversationJid = ? AND accountJid = ?',
|
||||
whereArgs: [sid, conversationJid, accountJid],
|
||||
where: 'message_id = ? AND accountJid = ?',
|
||||
whereArgs: [id, accountJid],
|
||||
columns: ['emoji'],
|
||||
distinct: true,
|
||||
limit: 6,
|
||||
@ -41,62 +40,58 @@ class ReactionsService {
|
||||
}
|
||||
|
||||
Future<List<Reaction>> getReactionsForMessage(
|
||||
String sid,
|
||||
String conversationJid,
|
||||
String id,
|
||||
String accountJid,
|
||||
) async {
|
||||
final reactions = await GetIt.I.get<DatabaseService>().database.query(
|
||||
reactionsTable,
|
||||
where: 'message_sid = ? AND conversationJid = ? AND accountJid = ?',
|
||||
whereArgs: [sid, conversationJid, accountJid],
|
||||
where: 'message_id = ? AND accountJid = ?',
|
||||
whereArgs: [id, accountJid],
|
||||
);
|
||||
|
||||
return reactions.map(Reaction.fromJson).toList();
|
||||
}
|
||||
|
||||
Future<List<String>> getReactionsForMessageByJid(
|
||||
String sid,
|
||||
String conversationJid,
|
||||
String id,
|
||||
String accountJid,
|
||||
String jid,
|
||||
) async {
|
||||
final reactions = await GetIt.I.get<DatabaseService>().database.query(
|
||||
reactionsTable,
|
||||
where:
|
||||
'message_sid = ? AND conversationJid = ? AND accountJid = ? AND senderJid = ?',
|
||||
whereArgs: [sid, conversationJid, accountJid, jid],
|
||||
'message_id = ? AND accountJid = ? AND senderJid = ?',
|
||||
whereArgs: [id, accountJid, jid],
|
||||
);
|
||||
|
||||
return reactions.map((r) => r['emoji']! as String).toList();
|
||||
}
|
||||
|
||||
Future<int> _countReactions(
|
||||
String sid,
|
||||
String conversationJid,
|
||||
String id,
|
||||
String accountJid,
|
||||
String emoji,
|
||||
) async {
|
||||
return GetIt.I.get<DatabaseService>().database.count(
|
||||
reactionsTable,
|
||||
'message_sid = ? AND conversationJid = ? AND accountJid = ? AND emoji = ?',
|
||||
[sid, conversationJid, accountJid, emoji],
|
||||
'message_id = ? AND accountJid = ? AND emoji = ?',
|
||||
[id, accountJid, emoji],
|
||||
);
|
||||
}
|
||||
|
||||
/// Adds a new reaction [emoji], if possible, to [sid] and returns the
|
||||
/// Adds a new reaction [emoji], if possible, to the message with id [id] and returns the
|
||||
/// new message reaction preview.
|
||||
Future<Message?> addNewReaction(
|
||||
String sid,
|
||||
String conversationJid,
|
||||
String id,
|
||||
String accountJid,
|
||||
String senderJid,
|
||||
String emoji,
|
||||
) async {
|
||||
final ms = GetIt.I.get<MessageService>();
|
||||
var msg = await ms.getMessageBySid(sid, conversationJid, accountJid);
|
||||
var msg = await ms.getMessageById(id, accountJid);
|
||||
if (msg == null) {
|
||||
_log.warning(
|
||||
'Failed to get message ($sid, $conversationJid, $accountJid)',
|
||||
'Failed to get message ($id, $accountJid)',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@ -105,11 +100,8 @@ class ReactionsService {
|
||||
await GetIt.I.get<DatabaseService>().database.insert(
|
||||
reactionsTable,
|
||||
Reaction(
|
||||
sid,
|
||||
conversationJid,
|
||||
id,
|
||||
accountJid,
|
||||
msg.timestamp,
|
||||
msg.sender,
|
||||
senderJid,
|
||||
emoji,
|
||||
).toJson(),
|
||||
@ -129,17 +121,16 @@ class ReactionsService {
|
||||
}
|
||||
|
||||
Future<Message?> removeReaction(
|
||||
String sid,
|
||||
String conversationJid,
|
||||
String id,
|
||||
String accountJid,
|
||||
String senderJid,
|
||||
String emoji,
|
||||
) async {
|
||||
final ms = GetIt.I.get<MessageService>();
|
||||
final msg = await ms.getMessageBySid(sid, conversationJid, accountJid);
|
||||
final msg = await ms.getMessageById(id, accountJid);
|
||||
if (msg == null) {
|
||||
_log.warning(
|
||||
'Failed to get message ($sid, $conversationJid, $accountJid)',
|
||||
'Failed to get message ($id, $accountJid)',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@ -147,17 +138,16 @@ class ReactionsService {
|
||||
await GetIt.I.get<DatabaseService>().database.delete(
|
||||
reactionsTable,
|
||||
where:
|
||||
'message_sid = ? AND conversationJid = ? AND accountJid = ? AND emoji = ? AND senderJid = ?',
|
||||
'message_id = ? AND accountJid = ? AND emoji = ? AND senderJid = ?',
|
||||
whereArgs: [
|
||||
sid,
|
||||
conversationJid,
|
||||
id,
|
||||
accountJid,
|
||||
emoji,
|
||||
(await GetIt.I.get<XmppStateService>().getXmppState()).jid,
|
||||
],
|
||||
);
|
||||
final count =
|
||||
await _countReactions(sid, conversationJid, accountJid, emoji);
|
||||
await _countReactions(id, accountJid, emoji);
|
||||
|
||||
if (count > 0) {
|
||||
return msg;
|
||||
@ -177,7 +167,7 @@ class ReactionsService {
|
||||
) async {
|
||||
// Get all reactions know for this message
|
||||
final allReactions =
|
||||
await getReactionsForMessage(msg.sid, msg.conversationJid, accountJid);
|
||||
await getReactionsForMessage(msg.id, accountJid);
|
||||
final userEmojis =
|
||||
allReactions.where((r) => r.senderJid == senderJid).map((r) => r.emoji);
|
||||
final removedReactions = userEmojis.where((e) => !emojis.contains(e));
|
||||
@ -189,8 +179,8 @@ class ReactionsService {
|
||||
final rows = await db.delete(
|
||||
reactionsTable,
|
||||
where:
|
||||
'message_sid = ? AND conversationJid = ? AND accountJid = ? AND senderJid = ? AND emoji = ?',
|
||||
whereArgs: [msg.sid, msg.conversationJid, accountJid, senderJid, emoji],
|
||||
'message_id = ? AND accountJid = ? AND senderJid = ? AND emoji = ?',
|
||||
whereArgs: [msg.id, accountJid, senderJid, emoji],
|
||||
);
|
||||
assert(rows == 1, 'Only one row should be removed');
|
||||
}
|
||||
@ -199,11 +189,8 @@ class ReactionsService {
|
||||
await db.insert(
|
||||
reactionsTable,
|
||||
Reaction(
|
||||
msg.sid,
|
||||
msg.conversationJid,
|
||||
msg.id,
|
||||
accountJid,
|
||||
msg.timestamp,
|
||||
msg.sender,
|
||||
senderJid,
|
||||
emoji,
|
||||
).toJson(),
|
||||
@ -212,8 +199,7 @@ class ReactionsService {
|
||||
|
||||
final newMessage = msg.copyWith(
|
||||
reactionsPreview: await getPreviewReactionsForMessage(
|
||||
msg.sid,
|
||||
msg.conversationJid,
|
||||
msg.id,
|
||||
accountJid,
|
||||
),
|
||||
);
|
||||
|
@ -144,14 +144,14 @@ class XmppService {
|
||||
String? getCurrentlyOpenedChatJid() => _currentlyOpenedChatJid;
|
||||
|
||||
/// Sends a message correction to [recipient] regarding the message with stanza id
|
||||
/// [oldId]. The old message's body gets corrected to [newBody]. [sid] is the message's
|
||||
/// stanza id. [chatState] can be optionally specified to also include a chat state
|
||||
/// [oldId]. The old message's body gets corrected to [newBody]. [id] is the message's
|
||||
/// id. [chatState] can be optionally specified to also include a chat state
|
||||
/// in the message.
|
||||
///
|
||||
/// This function handles updating the message and optionally the corresponding
|
||||
/// conversation.
|
||||
Future<void> sendMessageCorrection(
|
||||
String sid,
|
||||
String id,
|
||||
String conversationJid,
|
||||
String accountJid,
|
||||
String newBody,
|
||||
@ -166,8 +166,7 @@ class XmppService {
|
||||
|
||||
// Update the database
|
||||
final msg = await ms.updateMessage(
|
||||
sid,
|
||||
conversationJid,
|
||||
id,
|
||||
accountJid,
|
||||
isEdited: true,
|
||||
body: newBody,
|
||||
@ -178,7 +177,7 @@ class XmppService {
|
||||
recipient,
|
||||
accountJid,
|
||||
update: (c) async {
|
||||
if (c.lastMessage?.sid == sid) {
|
||||
if (c.lastMessage?.id == id) {
|
||||
return cs.updateConversation(
|
||||
c.jid,
|
||||
accountJid,
|
||||
@ -944,9 +943,8 @@ class XmppService {
|
||||
final cs = GetIt.I.get<ConversationService>();
|
||||
final sender = event.from.toBare().toString();
|
||||
final accountJid = await GetIt.I.get<XmppStateService>().getAccountJid();
|
||||
final dbMsg = await ms.getMessageBySid(
|
||||
final dbMsg = await ms.getMessageByOriginId(
|
||||
event.id,
|
||||
sender,
|
||||
accountJid,
|
||||
queryReactionPreview: false,
|
||||
);
|
||||
@ -958,8 +956,7 @@ class XmppService {
|
||||
}
|
||||
|
||||
final msg = await ms.updateMessage(
|
||||
dbMsg.sid,
|
||||
sender,
|
||||
dbMsg.id,
|
||||
accountJid,
|
||||
received: true,
|
||||
);
|
||||
@ -967,7 +964,7 @@ class XmppService {
|
||||
|
||||
// Update the conversation
|
||||
final conv = await cs.getConversationByJid(sender, accountJid);
|
||||
if (conv != null && conv.lastMessage?.sid == msg.sid) {
|
||||
if (conv != null && conv.lastMessage?.id == msg.id) {
|
||||
final newConv = conv.copyWith(lastMessage: msg);
|
||||
cs.setConversation(newConv);
|
||||
_log.finest('Updating conversation');
|
||||
@ -983,15 +980,14 @@ class XmppService {
|
||||
final sender = event.from.toBare().toString();
|
||||
final accountJid = await GetIt.I.get<XmppStateService>().getAccountJid();
|
||||
// TODO(Unknown): With groupchats, we should use the groupchat assigned stanza-id
|
||||
final dbMsg = await ms.getMessageBySid(event.id, sender, accountJid);
|
||||
final dbMsg = await ms.getMessageByOriginId(event.id, accountJid);
|
||||
if (dbMsg == null) {
|
||||
_log.warning('Did not find the message in the database!');
|
||||
return;
|
||||
}
|
||||
|
||||
final msg = await ms.updateMessage(
|
||||
dbMsg.sid,
|
||||
sender,
|
||||
dbMsg.id,
|
||||
accountJid,
|
||||
received: dbMsg.received ||
|
||||
event.type == ChatMarker.received ||
|
||||
@ -1005,7 +1001,7 @@ class XmppService {
|
||||
|
||||
// Update the conversation
|
||||
final conv = await cs.getConversationByJid(sender, accountJid);
|
||||
if (conv != null && conv.lastMessage?.sid == msg.sid) {
|
||||
if (conv != null && conv.lastMessage?.id == msg.id) {
|
||||
final newConv = conv.copyWith(lastMessage: msg);
|
||||
cs.setConversation(newConv);
|
||||
_log.finest('Updating conversation');
|
||||
@ -1156,9 +1152,8 @@ class XmppService {
|
||||
}
|
||||
|
||||
final ms = GetIt.I.get<MessageService>();
|
||||
final msg = await ms.getMessageBySid(
|
||||
final msg = await ms.getMessageByStanzaId(
|
||||
event.id!,
|
||||
event.from.toBare().toString(),
|
||||
accountJid,
|
||||
);
|
||||
|
||||
@ -1177,8 +1172,7 @@ class XmppService {
|
||||
}
|
||||
|
||||
final newMsg = await ms.updateMessage(
|
||||
msg.sid,
|
||||
msg.conversationJid,
|
||||
msg.id,
|
||||
accountJid,
|
||||
errorType: error,
|
||||
);
|
||||
@ -1201,8 +1195,7 @@ class XmppService {
|
||||
final cs = GetIt.I.get<ConversationService>();
|
||||
|
||||
final correctionId = event.extensions.get<LastMessageCorrectionData>()!.id;
|
||||
final msg = await ms.getMessageBySid(
|
||||
conversationJid,
|
||||
final msg = await ms.getMessageByOriginId(
|
||||
correctionId,
|
||||
accountJid,
|
||||
);
|
||||
@ -1231,8 +1224,7 @@ class XmppService {
|
||||
|
||||
// TODO(Unknown): Should we null-check here?
|
||||
final newMsg = await ms.updateMessage(
|
||||
msg.sid,
|
||||
msg.conversationJid,
|
||||
msg.id,
|
||||
accountJid,
|
||||
body: event.extensions.get<MessageBodyData>()!.body,
|
||||
isEdited: true,
|
||||
@ -1240,7 +1232,7 @@ class XmppService {
|
||||
sendEvent(MessageUpdatedEvent(message: newMsg));
|
||||
|
||||
final conv = await cs.getConversationByJid(msg.conversationJid, accountJid);
|
||||
if (conv != null && conv.lastMessage?.sid == msg.sid) {
|
||||
if (conv != null && conv.lastMessage?.id == msg.id) {
|
||||
final newConv = conv.copyWith(
|
||||
lastMessage: newMsg,
|
||||
);
|
||||
@ -1257,9 +1249,8 @@ class XmppService {
|
||||
final ms = GetIt.I.get<MessageService>();
|
||||
// TODO(Unknown): Once we support groupchats, we need to instead query by the stanza-id
|
||||
final reactions = event.extensions.get<MessageReactionsData>()!;
|
||||
final msg = await ms.getMessageBySid(
|
||||
final msg = await ms.getMessageByOriginId(
|
||||
reactions.messageId,
|
||||
conversationJid,
|
||||
accountJid,
|
||||
queryReactionPreview: false,
|
||||
);
|
||||
@ -1494,16 +1485,16 @@ class XmppService {
|
||||
(metadata.size != null &&
|
||||
metadata.size! < prefs.maximumAutoDownloadSize * 1000000)) {
|
||||
message = await ms.updateMessage(
|
||||
message.sid,
|
||||
message.conversationJid,
|
||||
message.id,
|
||||
accountJid,
|
||||
isDownloading: true,
|
||||
);
|
||||
await fts.downloadFile(
|
||||
FileDownloadJob(
|
||||
message.messageKey,
|
||||
embeddedFile,
|
||||
message.id,
|
||||
message.conversationJid,
|
||||
accountJid,
|
||||
embeddedFile,
|
||||
message.fileMetadata!.id,
|
||||
// If we did not retrieve the file, then we were not able to find it using
|
||||
// hashes.
|
||||
@ -1620,8 +1611,7 @@ class XmppService {
|
||||
// Mark the file as downlading when it includes a File Upload Notification
|
||||
if (fun != null) {
|
||||
message = await ms.updateMessage(
|
||||
message.sid,
|
||||
message.conversationJid,
|
||||
message.id,
|
||||
accountJid,
|
||||
isDownloading: true,
|
||||
);
|
||||
@ -1641,7 +1631,7 @@ class XmppService {
|
||||
final replacementId =
|
||||
event.extensions.get<FileUploadNotificationReplacementData>()!.id;
|
||||
var message =
|
||||
await ms.getMessageBySid(replacementId, conversationJid, accountJid);
|
||||
await ms.getMessageByOriginId(replacementId, accountJid);
|
||||
if (message == null) {
|
||||
_log.warning(
|
||||
'Received a FileUploadNotification replacement for unknown message',
|
||||
@ -1681,13 +1671,12 @@ class XmppService {
|
||||
|
||||
final oldFileMetadata = message.fileMetadata;
|
||||
message = await ms.updateMessage(
|
||||
message.sid,
|
||||
message.conversationJid,
|
||||
message.id,
|
||||
accountJid,
|
||||
fileMetadata: fileMetadata ?? notSpecified,
|
||||
isFileUploadNotification: false,
|
||||
isDownloading: shouldDownload,
|
||||
newSid: event.id,
|
||||
sid: event.id,
|
||||
originId: event.extensions.get<StableIdData>()?.originId,
|
||||
);
|
||||
|
||||
@ -1705,9 +1694,10 @@ class XmppService {
|
||||
_log.finest('Advertised file MIME: ${_getMimeGuess(event)}');
|
||||
await GetIt.I.get<HttpFileTransferService>().downloadFile(
|
||||
FileDownloadJob(
|
||||
message.messageKey,
|
||||
embeddedFile,
|
||||
message.id,
|
||||
message.conversationJid,
|
||||
accountJid,
|
||||
embeddedFile,
|
||||
oldFileMetadata!.id,
|
||||
// If [fileMetadata] is null, then we were not able to find the file metadata
|
||||
// using hashes and thus have to create hash pointers.
|
||||
@ -1740,12 +1730,11 @@ class XmppService {
|
||||
final ms = GetIt.I.get<MessageService>();
|
||||
final cs = GetIt.I.get<ConversationService>();
|
||||
final accountJid = await GetIt.I.get<XmppStateService>().getAccountJid();
|
||||
final msg = await ms.getMessageBySid(jid, event.stanza.id!, accountJid);
|
||||
final msg = await ms.getMessageByStanzaId(event.stanza.id!, accountJid);
|
||||
if (msg != null) {
|
||||
// Ack the message
|
||||
final newMsg = await ms.updateMessage(
|
||||
msg.sid,
|
||||
msg.conversationJid,
|
||||
msg.id,
|
||||
accountJid,
|
||||
acked: true,
|
||||
);
|
||||
@ -1753,7 +1742,7 @@ class XmppService {
|
||||
|
||||
// Ack the conversation
|
||||
final conv = await cs.getConversationByJid(jid, accountJid);
|
||||
if (conv != null && conv.lastMessage?.sid == newMsg.sid) {
|
||||
if (conv != null && conv.lastMessage?.id == newMsg.id) {
|
||||
final newConv = conv.copyWith(lastMessage: msg);
|
||||
cs.setConversation(newConv);
|
||||
sendEvent(ConversationUpdatedEvent(conversation: newConv));
|
||||
@ -1798,12 +1787,9 @@ class XmppService {
|
||||
if (event.data.stanza.tag != 'message') return;
|
||||
|
||||
final accountJid = await GetIt.I.get<XmppStateService>().getAccountJid();
|
||||
final conversationJid =
|
||||
JID.fromString(event.data.stanza.to!).toBare().toString();
|
||||
final ms = GetIt.I.get<MessageService>();
|
||||
final message = await ms.getMessageBySid(
|
||||
final message = await ms.getMessageByStanzaId(
|
||||
event.data.stanza.id!,
|
||||
conversationJid,
|
||||
accountJid,
|
||||
);
|
||||
|
||||
@ -1816,8 +1802,7 @@ class XmppService {
|
||||
|
||||
_log.finest('Cancel reason: ${event.data.cancelReason}');
|
||||
final newMessage = await ms.updateMessage(
|
||||
message.sid,
|
||||
conversationJid,
|
||||
message.id,
|
||||
accountJid,
|
||||
errorType: MessageErrorType.fromException(event.data.cancelReason),
|
||||
);
|
||||
|
@ -197,7 +197,7 @@ class Conversation with _$Conversation {
|
||||
'open': boolToInt(open),
|
||||
'muted': boolToInt(muted),
|
||||
'encrypted': boolToInt(encrypted),
|
||||
'lastMessageId': lastMessage?.sid,
|
||||
'lastMessageId': lastMessage?.id,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -9,57 +9,6 @@ import 'package:moxxyv2/shared/warning_types.dart';
|
||||
part 'message.freezed.dart';
|
||||
part 'message.g.dart';
|
||||
|
||||
/// A composite key to replace the old incrementing integer id attribute.
|
||||
/// Somewhat mimicks the message table's primary key.
|
||||
@immutable
|
||||
class MessageKey {
|
||||
const MessageKey(
|
||||
this.sender,
|
||||
this.conversationJid,
|
||||
this.sid,
|
||||
this.timestamp,
|
||||
);
|
||||
|
||||
factory MessageKey.fromJson(Map<String, dynamic> json) {
|
||||
final map = json.cast<String, Object>();
|
||||
return MessageKey(
|
||||
map['sender']! as String,
|
||||
map['conversationJid']! as String,
|
||||
map['sid']! as String,
|
||||
map['timestamp']! as int,
|
||||
);
|
||||
}
|
||||
|
||||
final String conversationJid;
|
||||
final String sender;
|
||||
final String sid;
|
||||
final int timestamp;
|
||||
|
||||
Map<String, Object> toJson() => {
|
||||
'conversationJid': conversationJid,
|
||||
'sender': sender,
|
||||
'sid': sid,
|
||||
'timestamp': timestamp,
|
||||
};
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is MessageKey &&
|
||||
other.conversationJid == conversationJid &&
|
||||
other.sender == sender &&
|
||||
other.sid == sid &&
|
||||
other.timestamp == timestamp;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => sender.hashCode ^ sid.hashCode ^ timestamp.hashCode;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MessageKey($sender, $sid, $timestamp)';
|
||||
}
|
||||
}
|
||||
|
||||
enum PseudoMessageType {
|
||||
/// Indicates that a new device was created in the chat.
|
||||
newDevice(1),
|
||||
@ -102,6 +51,9 @@ class PseudoMessageTypeConverter extends JsonConverter<PseudoMessageType, int> {
|
||||
@freezed
|
||||
class Message with _$Message {
|
||||
factory Message(
|
||||
// The message id (Moxxy-generated UUID).
|
||||
String id,
|
||||
|
||||
/// The JID of the account that sent or received the message.
|
||||
String accountJid,
|
||||
|
||||
@ -231,7 +183,7 @@ class Message with _$Message {
|
||||
'encrypted': boolToInt(encrypted),
|
||||
'file_metadata_id': fileMetadata?.id,
|
||||
// NOTE: Message.quote_id is a foreign-key
|
||||
'quote_sid': quotes?.sid,
|
||||
'quote_id': quotes?.id,
|
||||
'isDownloading': boolToInt(isDownloading),
|
||||
'isUploading': boolToInt(isUploading),
|
||||
'isRetracted': boolToInt(isRetracted),
|
||||
@ -343,12 +295,4 @@ class Message with _$Message {
|
||||
|
||||
/// The JID of the sender in moxxmpp's format.
|
||||
JID get senderJid => JID.fromString(sender);
|
||||
|
||||
/// A "unique" key for a message.
|
||||
MessageKey get messageKey => MessageKey(
|
||||
sender,
|
||||
conversationJid,
|
||||
sid,
|
||||
timestamp,
|
||||
);
|
||||
}
|
||||
|
@ -8,22 +8,11 @@ class Reaction with _$Reaction {
|
||||
factory Reaction(
|
||||
// This is valid in combination with freezed
|
||||
// ignore: invalid_annotation_target
|
||||
@JsonKey(name: 'message_sid') String messageSid,
|
||||
|
||||
/// The JID of the conversation this reaction is in.
|
||||
String conversationJid,
|
||||
@JsonKey(name: 'message_id') String messageId,
|
||||
|
||||
// The account JID of the attached message.
|
||||
String accountJid,
|
||||
|
||||
// The timestamp of the referenced message. Required for database reasons.
|
||||
// ignore: invalid_annotation_target
|
||||
@JsonKey(name: 'message_timestamp') int timestamp,
|
||||
|
||||
// The sender of the referenced message. Required for database reasons.
|
||||
// ignore: invalid_annotation_target
|
||||
@JsonKey(name: 'message_sender') String messageSender,
|
||||
|
||||
// The sender of the reaction.
|
||||
String senderJid,
|
||||
|
||||
|
@ -287,7 +287,7 @@ class BidirectionalConversationController
|
||||
if (newMessage.timestamp < cache.first.timestamp) return;
|
||||
|
||||
replaceItem(
|
||||
(msg) => msg.sid == newMessage.sid && msg.sender == newMessage.sender,
|
||||
(msg) => msg.id == newMessage.id,
|
||||
newMessage,
|
||||
);
|
||||
}
|
||||
@ -352,9 +352,7 @@ class BidirectionalConversationController
|
||||
if (!hasNewerData) {
|
||||
if (wasEditing) {
|
||||
foundMessage = replaceItem(
|
||||
(message) =>
|
||||
message.sid == result.message.sid &&
|
||||
message.sender == result.message.sender,
|
||||
(message) => message.id == result.message.id,
|
||||
result.message,
|
||||
);
|
||||
} else {
|
||||
|
@ -140,7 +140,7 @@ Future<void> onRosterPush(RosterDiffEvent event, {dynamic extra}) async {
|
||||
}
|
||||
|
||||
Future<void> onProgress(ProgressEvent event, {dynamic extra}) async {
|
||||
GetIt.I.get<UIProgressService>().onProgress(event.key, event.progress);
|
||||
GetIt.I.get<UIProgressService>().onProgress(event.id, event.progress);
|
||||
}
|
||||
|
||||
Future<void> onSelfAvatarChanged(
|
||||
|
@ -53,9 +53,7 @@ int getMessageMenuOptionCount(
|
||||
message.isReactable,
|
||||
message.canRetract(sentBySelf),
|
||||
// TODO(Unknown): Remove this and just allow us to correct any message
|
||||
message.canEdit(sentBySelf) &&
|
||||
lastMessage?.sid == message.sid &&
|
||||
lastMessage?.sender == message.sender,
|
||||
message.canEdit(sentBySelf) && lastMessage?.id == message.id,
|
||||
message.errorMenuVisible,
|
||||
message.hasWarning,
|
||||
message.isCopyable,
|
||||
@ -105,7 +103,7 @@ class ConversationPageState extends State<ConversationPage>
|
||||
late final Animation<double> _scrollToBottomAnimation;
|
||||
late final StreamSubscription<bool> _scrolledToBottomStateSubscription;
|
||||
|
||||
final Map<MessageKey, GlobalKey> _messageKeys = {};
|
||||
final Map<String, GlobalKey> _messageKeys = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -279,11 +277,11 @@ class ConversationPageState extends State<ConversationPage>
|
||||
|
||||
// Give each bubble its own animation and animation controller
|
||||
GlobalKey key;
|
||||
if (!_messageKeys.containsKey(item.messageKey)) {
|
||||
if (!_messageKeys.containsKey(item.id)) {
|
||||
key = GlobalKey();
|
||||
_messageKeys[item.messageKey] = key;
|
||||
_messageKeys[item.id] = key;
|
||||
} else {
|
||||
key = _messageKeys[item.messageKey]!;
|
||||
key = _messageKeys[item.id]!;
|
||||
}
|
||||
|
||||
final bubble = RawChatBubble(
|
||||
|
@ -198,7 +198,7 @@ class SelectedMessageContextMenu extends StatelessWidget {
|
||||
.getDataSender()
|
||||
.sendData(
|
||||
AddReactionToMessageCommand(
|
||||
key: message.messageKey,
|
||||
id: message.id,
|
||||
emoji: emoji,
|
||||
),
|
||||
awaitable: false,
|
||||
|
@ -131,6 +131,7 @@ class NewConversationPage extends StatelessWidget {
|
||||
'',
|
||||
item.title,
|
||||
Message(
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
item.jid,
|
||||
|
@ -1,27 +1,24 @@
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:moxxyv2/shared/models/message.dart';
|
||||
|
||||
typedef UIProgressCallback = void Function(double?);
|
||||
|
||||
/// This class handles download progress notifications from the backend and relays them
|
||||
/// to the correct ChatBubble instance so that it can update itself.
|
||||
class UIProgressService {
|
||||
UIProgressService()
|
||||
: _callbacks = {},
|
||||
_log = Logger('UIProgressService');
|
||||
/// Logger.
|
||||
final Logger _log = Logger('UIProgressService');
|
||||
|
||||
final Logger _log;
|
||||
// Database message id -> callback function
|
||||
final Map<MessageKey, UIProgressCallback> _callbacks;
|
||||
final Map<String, UIProgressCallback> _callbacks = {};
|
||||
|
||||
void registerCallback(MessageKey key, UIProgressCallback callback) {
|
||||
_log.finest('Registering callback for $key');
|
||||
_callbacks[key] = callback;
|
||||
void registerCallback(String id, UIProgressCallback callback) {
|
||||
_log.finest('Registering callback for $id');
|
||||
_callbacks[id] = callback;
|
||||
}
|
||||
|
||||
void unregisterCallback(MessageKey key) {
|
||||
_log.finest('Unregistering callback for $key');
|
||||
_callbacks.remove(key);
|
||||
void unregisterCallback(String id) {
|
||||
_log.finest('Unregistering callback for $id');
|
||||
_callbacks.remove(id);
|
||||
}
|
||||
|
||||
void unregisterAll() {
|
||||
@ -29,15 +26,15 @@ class UIProgressService {
|
||||
_callbacks.clear();
|
||||
}
|
||||
|
||||
void onProgress(MessageKey key, double? progress) {
|
||||
if (_callbacks.containsKey(key)) {
|
||||
void onProgress(String id, double? progress) {
|
||||
if (_callbacks.containsKey(id)) {
|
||||
if (progress == 1.0) {
|
||||
unregisterCallback(key);
|
||||
unregisterCallback(id);
|
||||
} else {
|
||||
_callbacks[key]!(progress);
|
||||
_callbacks[id]!(progress);
|
||||
}
|
||||
} else {
|
||||
_log.warning('Received progress callback for unregistered key $key');
|
||||
_log.warning('Received progress callback for unregistered key $id');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,15 +13,15 @@ class UIReadMarkerService {
|
||||
final Logger _log = Logger('UIReadMarkerService');
|
||||
|
||||
/// The cache of messages we already processed.
|
||||
final Map<MessageKey, bool> _messages = {};
|
||||
final Map<String, bool> _messages = {};
|
||||
|
||||
/// Checks if we should send a read marker for [message]. If we should, tells
|
||||
/// the backend to actually send it.
|
||||
void handleMarker(Message message) {
|
||||
if (_messages.containsKey(message.messageKey)) return;
|
||||
if (_messages.containsKey(message.id)) return;
|
||||
|
||||
// Make sure we don't reach here anymore.
|
||||
_messages[message.messageKey] = true;
|
||||
_messages[message.id] = true;
|
||||
|
||||
// Only send this for messages we have not yet marked as read.
|
||||
if (message.displayed) return;
|
||||
@ -29,12 +29,10 @@ class UIReadMarkerService {
|
||||
// Check if we should send markers.
|
||||
if (!GetIt.I.get<PreferencesBloc>().state.sendChatMarkers) return;
|
||||
|
||||
final id = message.originId ?? message.sid;
|
||||
_log.finest('Sending chat marker for ${message.conversationJid}:$id');
|
||||
_log.finest('Sending chat marker for ${message.id}');
|
||||
MoxplatformPlugin.handler.getDataSender().sendData(
|
||||
MarkMessageAsReadCommand(
|
||||
sid: message.sid,
|
||||
conversationJid: message.conversationJid,
|
||||
id: message.id,
|
||||
sendMarker: true,
|
||||
),
|
||||
awaitable: false,
|
||||
|
@ -33,7 +33,7 @@ class _AudioWidget extends StatelessWidget {
|
||||
this.onChanged,
|
||||
this.duration,
|
||||
this.position,
|
||||
this.messageKey,
|
||||
this.messageId,
|
||||
);
|
||||
final double maxWidth;
|
||||
final bool isDownloading;
|
||||
@ -42,14 +42,14 @@ class _AudioWidget extends StatelessWidget {
|
||||
final double? duration;
|
||||
final double? position;
|
||||
final Widget? icon;
|
||||
final MessageKey messageKey;
|
||||
final String messageId;
|
||||
|
||||
Widget _getLeftWidget() {
|
||||
if (isDownloading) {
|
||||
return SizedBox(
|
||||
width: 48,
|
||||
height: 48,
|
||||
child: ProgressWidget(messageKey),
|
||||
child: ProgressWidget(messageId),
|
||||
);
|
||||
}
|
||||
|
||||
@ -174,7 +174,7 @@ class AudioChatState extends State<AudioChatWidget> {
|
||||
(_) {},
|
||||
null,
|
||||
null,
|
||||
widget.message.messageKey,
|
||||
widget.message.id,
|
||||
),
|
||||
MessageBubbleBottom(widget.message, widget.sent),
|
||||
widget.radius,
|
||||
@ -192,7 +192,7 @@ class AudioChatState extends State<AudioChatWidget> {
|
||||
(_) {},
|
||||
null,
|
||||
null,
|
||||
widget.message.messageKey,
|
||||
widget.message.id,
|
||||
),
|
||||
MessageBubbleBottom(widget.message, widget.sent),
|
||||
widget.radius,
|
||||
@ -237,7 +237,7 @@ class AudioChatState extends State<AudioChatWidget> {
|
||||
},
|
||||
_duration,
|
||||
_position,
|
||||
widget.message.messageKey,
|
||||
widget.message.id,
|
||||
),
|
||||
MessageBubbleBottom(
|
||||
widget.message,
|
||||
|
@ -147,7 +147,7 @@ class FileChatWidget extends StatelessWidget {
|
||||
maxWidth,
|
||||
sent,
|
||||
mimeType: message.fileMetadata!.filename,
|
||||
downloadButton: ProgressWidget(message.messageKey),
|
||||
downloadButton: ProgressWidget(message.id),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ class ImageChatWidget extends StatelessWidget {
|
||||
Image.file(File(message.fileMetadata!.path!)),
|
||||
MessageBubbleBottom(message, sent),
|
||||
radius,
|
||||
extra: ProgressWidget(message.messageKey),
|
||||
extra: ProgressWidget(message.id),
|
||||
);
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ class ImageChatWidget extends StatelessWidget {
|
||||
),
|
||||
MessageBubbleBottom(message, sent),
|
||||
radius,
|
||||
extra: ProgressWidget(message.messageKey),
|
||||
extra: ProgressWidget(message.id),
|
||||
);
|
||||
} else {
|
||||
return FileChatBaseWidget(
|
||||
@ -60,7 +60,7 @@ class ImageChatWidget extends StatelessWidget {
|
||||
maxWidth,
|
||||
sent,
|
||||
mimeType: message.fileMetadata!.mimeType,
|
||||
downloadButton: ProgressWidget(message.messageKey),
|
||||
downloadButton: ProgressWidget(message.id),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ class VideoChatWidget extends StatelessWidget {
|
||||
),
|
||||
MessageBubbleBottom(message, sent),
|
||||
radius,
|
||||
extra: ProgressWidget(message.messageKey),
|
||||
extra: ProgressWidget(message.id),
|
||||
);
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ class VideoChatWidget extends StatelessWidget {
|
||||
),
|
||||
MessageBubbleBottom(message, sent),
|
||||
radius,
|
||||
extra: ProgressWidget(message.messageKey),
|
||||
extra: ProgressWidget(message.id),
|
||||
);
|
||||
} else {
|
||||
return FileChatBaseWidget(
|
||||
@ -71,7 +71,7 @@ class VideoChatWidget extends StatelessWidget {
|
||||
maxWidth,
|
||||
sent,
|
||||
mimeType: message.fileMetadata!.mimeType,
|
||||
downloadButton: ProgressWidget(message.messageKey),
|
||||
downloadButton: ProgressWidget(message.id),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:moxxyv2/shared/models/message.dart';
|
||||
import 'package:moxxyv2/ui/constants.dart';
|
||||
import 'package:moxxyv2/ui/service/progress.dart';
|
||||
|
||||
@ -9,8 +8,8 @@ import 'package:moxxyv2/ui/service/progress.dart';
|
||||
// we, for example, use blurhash, then we compute the image from the blurhash on every
|
||||
// update.
|
||||
class ProgressWidget extends StatefulWidget {
|
||||
const ProgressWidget(this.messageKey, {super.key});
|
||||
final MessageKey messageKey;
|
||||
const ProgressWidget(this.messageId, {super.key});
|
||||
final String messageId;
|
||||
|
||||
@override
|
||||
ProgressWidgetState createState() => ProgressWidgetState();
|
||||
@ -31,7 +30,7 @@ class ProgressWidgetState extends State<ProgressWidget> {
|
||||
// Register against the DownloadService
|
||||
GetIt.I
|
||||
.get<UIProgressService>()
|
||||
.registerCallback(widget.messageKey, _onProgressUpdate);
|
||||
.registerCallback(widget.messageId, _onProgressUpdate);
|
||||
|
||||
super.initState();
|
||||
}
|
||||
@ -39,7 +38,7 @@ class ProgressWidgetState extends State<ProgressWidget> {
|
||||
@override
|
||||
void dispose() {
|
||||
// Unregister
|
||||
GetIt.I.get<UIProgressService>().unregisterCallback(widget.messageKey);
|
||||
GetIt.I.get<UIProgressService>().unregisterCallback(widget.messageId);
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import 'package:moxplatform/moxplatform.dart';
|
||||
import 'package:moxxyv2/i18n/strings.g.dart';
|
||||
import 'package:moxxyv2/shared/commands.dart';
|
||||
import 'package:moxxyv2/shared/events.dart';
|
||||
import 'package:moxxyv2/shared/models/message.dart';
|
||||
import 'package:moxxyv2/shared/models/reaction_group.dart';
|
||||
import 'package:moxxyv2/ui/bloc/conversations_bloc.dart';
|
||||
import 'package:moxxyv2/ui/helpers.dart';
|
||||
@ -39,17 +38,17 @@ List<ReactionGroup> ensureReactionGroupOrder(
|
||||
/// Displays the reactions to a message and allows modifying the reactions.
|
||||
/// When created, fetches the reactions from the ReactionService.
|
||||
class ReactionList extends StatelessWidget {
|
||||
const ReactionList(this.messageKey, {super.key});
|
||||
const ReactionList(this.messageId, {super.key});
|
||||
|
||||
/// The database identifier of the message to fetch reactions of.
|
||||
final MessageKey messageKey;
|
||||
final String messageId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<BackgroundEvent?>(
|
||||
future: MoxplatformPlugin.handler.getDataSender().sendData(
|
||||
GetReactionsForMessageCommand(
|
||||
key: messageKey,
|
||||
id: messageId,
|
||||
),
|
||||
) as Future<BackgroundEvent?>,
|
||||
builder: (context, snapshot) {
|
||||
@ -104,7 +103,7 @@ class ReactionList extends StatelessWidget {
|
||||
.getDataSender()
|
||||
.sendData(
|
||||
AddReactionToMessageCommand(
|
||||
key: messageKey,
|
||||
id: messageId,
|
||||
emoji: emoji,
|
||||
),
|
||||
awaitable: false,
|
||||
@ -116,7 +115,7 @@ class ReactionList extends StatelessWidget {
|
||||
? (emoji) async {
|
||||
await MoxplatformPlugin.handler.getDataSender().sendData(
|
||||
RemoveReactionFromMessageCommand(
|
||||
key: messageKey,
|
||||
id: messageId,
|
||||
emoji: emoji,
|
||||
),
|
||||
awaitable: false,
|
||||
|
@ -33,7 +33,7 @@ class ReactionsPreview extends StatelessWidget {
|
||||
),
|
||||
builder: (context) {
|
||||
return ReactionList(
|
||||
message.messageKey,
|
||||
message.id,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user