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