feat(messages): add deleted system message that tells who deleted

Fixes #11712
This commit is contained in:
Jonathan Rainville 2023-11-22 15:07:38 -05:00
parent 90570b53da
commit d66540d74f
16 changed files with 410 additions and 267 deletions

View File

@ -130,6 +130,9 @@ proc createMessageItemFromDto(self: Module, message: MessageDto, communityId: st
contactDetails.dto.ensVerified,
message.discordMessage,
resendError = "",
message.deleted,
message.deletedBy,
deletedByContactDetails = ContactDetails(),
message.mentioned,
message.quotedMessage.`from`,
message.quotedMessage.text,

View File

@ -180,7 +180,7 @@ proc init*(self: Controller) =
let args = MessageDeletedArgs(e)
if(self.chatId != args.chatId):
return
self.delegate.onMessageDeleted(args.messageId)
self.delegate.onMessageDeleted(args.messageId, args.deletedBy)
self.events.on(SIGNAL_MESSAGE_EDITED) do(e: Args):
let args = MessageEditedArgs(e)

View File

@ -117,7 +117,7 @@ method getNumberOfPinnedMessages*(self: AccessInterface): int {.base.} =
method deleteMessage*(self: AccessInterface, messageId: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onMessageDeleted*(self: AccessInterface, messageId: string) {.base.} =
method onMessageDeleted*(self: AccessInterface, messageId, deletedBy: string) {.base.} =
raise newException(ValueError, "No implementation available")
method editMessage*(self: AccessInterface, messageId: string, contentType: int, updatedMsg: string) {.base.} =

View File

@ -67,6 +67,7 @@ proc setChatDetails(self: Module, chatDetails: ChatDto)
proc updateItemsByAlbum(self: Module, items: var seq[Item], message: MessageDto): bool
proc updateLinkPreviewsContacts(self: Module, item: Item, requestFromMailserver: bool)
proc updateLinkPreviewsCommunities(self: Module, item: Item, requestFromMailserver: bool)
proc currentUserWalletContainsAddress(self: Module, address: string): bool
method delete*(self: Module) =
self.controller.delete
@ -94,6 +95,171 @@ method viewDidLoad*(self: Module) =
method getModuleAsVariant*(self: Module): QVariant =
return self.viewVariant
# TODO: Fetch the message from status-go. The generated albumIdToImagesMap is built on the messages received and does not account for
# older messages for which the images would not be found. Ticket https://github.com/status-im/status-desktop/issues/12821
proc generateAlbumIdToImageMap(self: Module, messages: seq[MessageDto]): Table[string, seq[string]] =
var albumIdToImagesMap = initTable[string, seq[string]]()
for message in messages:
if message.albumId in albumIdToImagesMap:
albumIdToImagesMap[message.albumId].add(message.image)
else:
albumIdToImagesMap[message.albumId] = @[message.image]
return albumIdToImagesMap
proc setQuotedMessageImages(self: Module, message: MessageDto, items: var seq[Item], albumIdToImagesMap: Table[string, seq[string]]) =
for i in 0 ..< items.len:
let item = items[i]
var quotedMessageAlbumMessageImages = item.quotedMessageAlbumMessageImages
if message.id != item.responseToMessageWithId:
continue
if message.albumId notin albumIdToImagesMap:
continue
quotedMessageAlbumMessageImages = albumIdToImagesMap[message.albumId]
item.quotedMessageAlbumMessageImages = quotedMessageAlbumMessageImages
item.quotedMessageAlbumImagesCount = quotedMessageAlbumMessageImages.len
items[i] = item
proc createMessageItemsFromMessageDtos(self: Module, messages: seq[MessageDto], reactions: seq[ReactionDto] = @[]): seq[Item] =
var albumIdToImagesMap = self.generateAlbumIdToImageMap(messages)
for message in messages:
# https://github.com/status-im/status-desktop/issues/7632 will introduce deleteFroMe feature.
# Now we just skip deleted messages
if message.deletedForMe:
continue
# Add image to album if album already exists
if (message.contentType == ContentType.Image and len(message.albumId) != 0):
if (self.view.model().updateAlbumIfExists(message.albumId, message.image, message.id)):
continue
self.setQuotedMessageImages(message, result, albumIdToImagesMap)
if (self.updateItemsByAlbum(result, message)):
continue
let sender = self.controller.getContactDetails(message.`from`)
var quotedMessageAuthorDetails = ContactDetails()
if message.quotedMessage.`from` != "":
if(message.`from` == message.quotedMessage.`from`):
quotedMessageAuthorDetails = sender
else:
quotedMessageAuthorDetails = self.controller.getContactDetails(message.quotedMessage.`from`)
var deletedByContactDetails = ContactDetails()
if message.deletedBy != "":
if(message.`from` == message.deletedBy):
deletedByContactDetails = sender
else:
deletedByContactDetails = self.controller.getContactDetails(message.deletedBy)
# remove a message which has replace parameters filled
let index = self.view.model().findIndexForMessageId(message.replace)
if(index != -1):
self.view.model().removeItem(message.replace)
var communityChats: seq[ChatDto]
communityChats = self.controller.getCommunityDetails().chats
var renderedMessageText = self.controller.getRenderedText(message.parsedText, communityChats)
var transactionContract = message.transactionParameters.contract
var transactionValue = message.transactionParameters.value
var isCurrentUser = sender.isCurrentUser
if(message.contentType == ContentType.Transaction):
(transactionContract, transactionValue) = self.controller.getTransactionDetails(message)
if message.transactionParameters.fromAddress != "":
isCurrentUser = self.currentUserWalletContainsAddress(message.transactionParameters.fromAddress)
var item = initItem(
message.id,
message.communityId,
message.responseTo,
message.`from`,
sender.defaultDisplayName,
sender.optionalName,
sender.icon,
sender.colorHash,
(isCurrentUser and message.contentType != ContentType.DiscordMessage),
sender.dto.added,
message.outgoingStatus,
renderedMessageText,
message.text,
message.parsedText,
message.image,
message.containsContactMentions(),
message.seen,
timestamp = message.timestamp,
clock = message.clock,
message.contentType,
message.messageType,
message.contactRequestState,
sticker = message.sticker.url,
message.sticker.pack,
message.links,
message.linkPreviews,
newTransactionParametersItem(message.transactionParameters.id,
message.transactionParameters.fromAddress,
message.transactionParameters.address,
transactionContract,
transactionValue,
message.transactionParameters.transactionHash,
message.transactionParameters.commandState,
message.transactionParameters.signature),
message.mentionedUsersPks(),
sender.dto.trustStatus,
sender.dto.ensVerified,
message.discordMessage,
resendError = "",
message.deleted,
message.deletedBy,
deletedByContactDetails,
message.mentioned,
message.quotedMessage.`from`,
message.quotedMessage.text,
self.controller.getRenderedText(message.quotedMessage.parsedText, communityChats),
message.quotedMessage.contentType,
message.quotedMessage.deleted,
message.quotedMessage.discordMessage,
quotedMessageAuthorDetails,
message.quotedMessage.albumImages,
message.quotedMessage.albumImagesCount,
message.albumId,
if (len(message.albumId) == 0): @[] else: @[message.image],
if (len(message.albumId) == 0): @[] else: @[message.id],
message.albumImagesCount,
)
self.updateLinkPreviewsContacts(item, requestFromMailserver = item.seen)
self.updateLinkPreviewsCommunities(item, requestFromMailserver = item.seen)
for r in reactions:
if(r.messageId == message.id):
var emojiIdAsEnum: EmojiId
if(message_reaction_item.toEmojiIdAsEnum(r.emojiId, emojiIdAsEnum)):
let userWhoAddedThisReaction = self.controller.getContactById(r.`from`)
let didIReactWithThisEmoji = userWhoAddedThisReaction.id == singletonInstance.userProfile.getPubKey()
item.addReaction(emojiIdAsEnum, didIReactWithThisEmoji, userWhoAddedThisReaction.id,
userWhoAddedThisReaction.userDefaultDisplayName(), r.id)
else:
error "wrong emoji id found when loading messages", methodName="newMessagesLoaded"
if message.editedAt != 0:
item.isEdited = true
if(message.contentType == ContentType.Gap):
item.gapFrom = message.gapParameters.`from`
item.gapTo = message.gapParameters.to
result.add(item)
proc createFetchMoreMessagesItem(self: Module): Item =
let chatDto = self.controller.getChatDetails()
result = initItem(
@ -129,6 +295,9 @@ proc createFetchMoreMessagesItem(self: Module): Item =
senderEnsVerified = false,
DiscordMessage(),
resendError = "",
deleted = false,
deletedBy = "",
deletedByContactDetails = ContactDetails(),
mentioned = false,
quotedMessageFrom = "",
quotedMessageText = "",
@ -191,6 +360,9 @@ proc createChatIdentifierItem(self: Module): Item =
senderEnsVerified = false,
DiscordMessage(),
resendError = "",
deleted = false,
deletedBy = "",
deletedByContactDetails = ContactDetails(),
mentioned = false,
quotedMessageFrom = "",
quotedMessageText = "",
@ -236,157 +408,9 @@ method reevaluateViewLoadingState*(self: Module) =
self.firstUnseenMessageState.fetching or
self.view.getMessageSearchOngoing())
# TODO: Fetch the message from status-go. The generated albumIdToImagesMap is built on the messages received and does not account for
# older messages for which the images would not be found. Ticket https://github.com/status-im/status-desktop/issues/12821
proc generateAlbumIdToImageMap(self: Module, messages: seq[MessageDto]): Table[string, seq[string]] =
var albumIdToImagesMap = initTable[string, seq[string]]()
for message in messages:
if message.albumId in albumIdToImagesMap:
albumIdToImagesMap[message.albumId].add(message.image)
else:
albumIdToImagesMap[message.albumId] = @[message.image]
return albumIdToImagesMap
proc setQuotedMessageImages(self: Module, message: MessageDto, items: var seq[Item], albumIdToImagesMap: Table[string, seq[string]]) =
for i in 0 ..< items.len:
let item = items[i]
var quotedMessageAlbumMessageImages = item.quotedMessageAlbumMessageImages
if message.id != item.responseToMessageWithId:
continue
if message.albumId notin albumIdToImagesMap:
continue
quotedMessageAlbumMessageImages = albumIdToImagesMap[message.albumId]
item.quotedMessageAlbumMessageImages = quotedMessageAlbumMessageImages
item.quotedMessageAlbumImagesCount = quotedMessageAlbumMessageImages.len
items[i] = item
method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: seq[ReactionDto]) =
var viewItems: seq[Item]
var albumIdToImagesMap = self.generateAlbumIdToImageMap(messages)
if(messages.len > 0):
for message in messages:
# https://github.com/status-im/status-desktop/issues/7632 will introduce deleteFroMe feature.
# Now we just skip deleted messages
if message.deleted or message.deletedForMe:
continue
let sender = self.controller.getContactDetails(message.`from`)
var quotedMessageAuthorDetails = ContactDetails()
if message.quotedMessage.`from` != "":
if(message.`from` == message.quotedMessage.`from`):
quotedMessageAuthorDetails = sender
else:
quotedMessageAuthorDetails = self.controller.getContactDetails(message.quotedMessage.`from`)
var communityChats: seq[ChatDto]
communityChats = self.controller.getCommunityDetails().chats
var renderedMessageText = self.controller.getRenderedText(message.parsedText, communityChats)
# Add image to album if album already exists
if (message.contentType == ContentType.Image and len(message.albumId) != 0):
if (self.view.model().updateAlbumIfExists(message.albumId, message.image, message.id)):
continue
self.setQuotedMessageImages(message, viewItems, albumIdToImagesMap)
if (self.updateItemsByAlbum(viewItems, message)):
continue
var transactionContract = message.transactionParameters.contract
var transactionValue = message.transactionParameters.value
var isCurrentUser = sender.isCurrentUser
if(message.contentType == ContentType.Transaction):
(transactionContract, transactionValue) = self.controller.getTransactionDetails(message)
if message.transactionParameters.fromAddress != "":
isCurrentUser = self.currentUserWalletContainsAddress(message.transactionParameters.fromAddress)
var item = initItem(
message.id,
message.communityId,
message.responseTo,
message.`from`,
sender.defaultDisplayName,
sender.optionalName,
sender.icon,
sender.colorHash,
(isCurrentUser and message.contentType != ContentType.DiscordMessage),
sender.dto.added,
message.outgoingStatus,
renderedMessageText,
message.text,
message.parsedText,
message.image,
message.containsContactMentions(),
message.seen,
timestamp = message.timestamp,
clock = message.clock,
message.contentType,
message.messageType,
message.contactRequestState,
sticker = message.sticker.url,
message.sticker.pack,
message.links,
message.linkPreviews,
newTransactionParametersItem(message.transactionParameters.id,
message.transactionParameters.fromAddress,
message.transactionParameters.address,
transactionContract,
transactionValue,
message.transactionParameters.transactionHash,
message.transactionParameters.commandState,
message.transactionParameters.signature),
message.mentionedUsersPks(),
sender.dto.trustStatus,
sender.dto.ensVerified,
message.discordMessage,
resendError = "",
message.mentioned,
message.quotedMessage.`from`,
message.quotedMessage.text,
self.controller.getRenderedText(message.quotedMessage.parsedText, communityChats),
message.quotedMessage.contentType,
message.quotedMessage.deleted,
message.quotedMessage.discordMessage,
quotedMessageAuthorDetails,
message.quotedMessage.albumImages,
message.quotedMessage.albumImagesCount,
message.albumId,
if (len(message.albumId) == 0): @[] else: @[message.image],
if (len(message.albumId) == 0): @[] else: @[message.id],
message.albumImagesCount,
)
self.updateLinkPreviewsContacts(item, requestFromMailserver = item.seen)
self.updateLinkPreviewsCommunities(item, requestFromMailserver = item.seen)
for r in reactions:
if(r.messageId == message.id):
var emojiIdAsEnum: EmojiId
if(message_reaction_item.toEmojiIdAsEnum(r.emojiId, emojiIdAsEnum)):
let userWhoAddedThisReaction = self.controller.getContactById(r.`from`)
let didIReactWithThisEmoji = userWhoAddedThisReaction.id == singletonInstance.userProfile.getPubKey()
item.addReaction(emojiIdAsEnum, didIReactWithThisEmoji, userWhoAddedThisReaction.id,
userWhoAddedThisReaction.userDefaultDisplayName(), r.id)
else:
error "wrong emoji id found when loading messages", methodName="newMessagesLoaded"
if message.editedAt != 0:
item.isEdited = true
if(message.contentType == ContentType.Gap):
item.gapFrom = message.gapParameters.`from`
item.gapTo = message.gapParameters.to
# messages are sorted from the most recent to the least recent one
viewItems.add(item)
var viewItems = self.createMessageItemsFromMessageDtos(messages, reactions)
if self.controller.getChatDetails().hasMoreMessagesToRequest():
viewItems.add(self.createFetchMoreMessagesItem())
@ -408,104 +432,7 @@ method newPinnedMessagesLoaded*(self: Module, pinnedMessages: seq[PinnedMessageD
self.onPinMessage(p.message.id, p.pinnedBy)
method messagesAdded*(self: Module, messages: seq[MessageDto]) =
var items: seq[Item]
for message in messages:
let sender = self.controller.getContactDetails(message.`from`)
let communityChats = self.controller.getCommunityDetails().chats
var quotedMessageAuthorDetails = ContactDetails()
if message.quotedMessage.`from` != "":
if(message.`from` == message.quotedMessage.`from`):
quotedMessageAuthorDetails = sender
else:
quotedMessageAuthorDetails = self.controller.getContactDetails(message.quotedMessage.`from`)
let renderedMessageText = self.controller.getRenderedText(message.parsedText, communityChats)
var transactionContract = message.transactionParameters.contract
var transactionValue = message.transactionParameters.value
var isCurrentUser = sender.isCurrentUser
if message.contentType == ContentType.Transaction:
(transactionContract, transactionValue) = self.controller.getTransactionDetails(message)
if message.transactionParameters.fromAddress != "":
isCurrentUser = self.currentUserWalletContainsAddress(message.transactionParameters.fromAddress)
# remove a message which has replace parameters filled
let index = self.view.model().findIndexForMessageId(message.replace)
if(index != -1):
self.view.model().removeItem(message.replace)
# https://github.com/status-im/status-desktop/issues/7632 will introduce deleteFroMe feature.
# Now we just skip deleted messages
if message.deleted or message.deletedForMe:
continue
# Add image to album if album already exists
if (message.contentType == ContentType.Image and len(message.albumId) != 0):
if (self.view.model().updateAlbumIfExists(message.albumId, message.image, message.id)):
continue
if (self.updateItemsByAlbum(items, message)):
continue
var item = initItem(
message.id,
message.communityId,
message.responseTo,
message.`from`,
sender.defaultDisplayName,
sender.optionalName,
sender.icon,
sender.colorHash,
(isCurrentUser and message.contentType != ContentType.DiscordMessage),
sender.dto.added,
message.outgoingStatus,
renderedMessageText,
message.text,
message.parsedText,
message.image,
message.containsContactMentions(),
message.seen,
timestamp = message.timestamp,
clock = message.clock,
message.contentType,
message.messageType,
message.contactRequestState,
sticker = message.sticker.url,
message.sticker.pack,
message.links,
message.linkPreviews,
newTransactionParametersItem(message.transactionParameters.id,
message.transactionParameters.fromAddress,
message.transactionParameters.address,
transactionContract,
transactionValue,
message.transactionParameters.transactionHash,
message.transactionParameters.commandState,
message.transactionParameters.signature),
message.mentionedUsersPks,
sender.dto.trustStatus,
sender.dto.ensVerified,
message.discordMessage,
resendError = "",
message.mentioned,
message.quotedMessage.`from`,
message.quotedMessage.text,
self.controller.getRenderedText(message.quotedMessage.parsedText, communityChats),
message.quotedMessage.contentType,
message.quotedMessage.deleted,
message.quotedMessage.discordMessage,
quotedMessageAuthorDetails,
message.quotedMessage.albumImages,
message.quotedMessage.albumImagesCount,
message.albumId,
if (len(message.albumId) == 0): @[] else: @[message.image],
if (len(message.albumId) == 0): @[] else: @[message.id],
message.albumImagesCount,
)
items.add(item)
self.updateLinkPreviewsContacts(item, requestFromMailserver = item.seen)
self.updateLinkPreviewsCommunities(item, requestFromMailserver = item.seen)
let items = self.createMessageItemsFromMessageDtos(messages)
self.view.model().insertItemsBasedOnClock(items)
@ -654,8 +581,16 @@ method updateContactDetails*(self: Module, contactId: string) =
method deleteMessage*(self: Module, messageId: string) =
self.controller.deleteMessage(messageId)
method onMessageDeleted*(self: Module, messageId: string) =
self.view.model().removeItem(messageId)
method onMessageDeleted*(self: Module, messageId, deletedBy: string) =
var deletedByValue = deletedBy
if deletedBy == "":
# deletedBy is empty if it was deleted by the sender
let messageItem = self.view.model().getItemWithMessageId(messageId)
if messageItem.id == "":
return
deletedByValue = messageItem.senderId
var deletedByContactDetails = self.controller.getContactDetails(deletedByValue)
self.view.model().messageDeleted(messageId, deletedByValue, deletedByContactDetails)
method editMessage*(self: Module, messageId: string, contentType: int, updatedMsg: string) =
self.controller.editMessage(messageId, contentType, updatedMsg)

View File

@ -211,6 +211,9 @@ proc buildPinnedMessageItem(self: Module, message: MessageDto, actionInitiatedBy
contactDetails.dto.ensVerified,
message.discordMessage,
resendError = "",
message.deleted,
message.deletedBy,
deletedByContactDetails = ContactDetails(),
message.mentioned,
message.quotedMessage.`from`,
message.quotedMessage.text,

View File

@ -42,6 +42,9 @@ type
pinnedBy: string
editMode: bool
isEdited: bool
deleted: bool
deletedBy: string
deletedByContactDetails: ContactDetails
links: seq[string]
linkPreviewModel: link_preview_model.Model
transactionParameters: TransactionParametersItem
@ -99,6 +102,9 @@ proc initItem*(
senderEnsVerified: bool,
discordMessage: DiscordMessage,
resendError: string,
deleted: bool,
deletedBy: string,
deletedByContactDetails: ContactDetails,
mentioned: bool,
quotedMessageFrom: string,
quotedMessageText: string,
@ -143,6 +149,9 @@ proc initItem*(
result.stickerPack = stickerPack
result.editMode = false
result.isEdited = false
result.deleted = deleted
result.deletedBy = deletedBy
result.deletedByContactDetails = deletedByContactDetails
result.links = links
result.linkPreviewModel = newLinkPreviewModel(linkPreviews)
result.transactionParameters = transactionParameters
@ -230,6 +239,9 @@ proc initNewMessagesMarkerItem*(clock, timestamp: int64): Item =
senderEnsVerified = false,
discordMessage = DiscordMessage(),
resendError = "",
deleted = false,
deletedBy = "",
deletedByContactDetails = ContactDetails(),
mentioned = false,
quotedMessageFrom = "",
quotedMessageText = "",
@ -272,6 +284,8 @@ proc `$`*(self: Item): string =
messageReactions: [{$self.reactionsModel}],
editMode:{$self.editMode},
isEdited:{$self.isEdited},
deleted:{self.deleted},
deletedBy:{$self.deletedBy},
links:{$self.links},
transactionParameters:{$self.transactionParameters},
mentionedUsersPks:{$self.mentionedUsersPks},
@ -500,6 +514,8 @@ proc toJsonNode*(self: Item): JsonNode =
"pinnedBy": self.pinnedBy,
"editMode": self.editMode,
"isEdited": self.isEdited,
"deleted": self.deleted,
"deletedBy": self.deletedBy,
"links": self.links,
"mentionedUsersPks": self.mentionedUsersPks,
"senderEnsVerified": self.senderEnsVerified,
@ -532,6 +548,23 @@ proc isEdited*(self: Item): bool {.inline.} =
proc `isEdited=`*(self: Item, value: bool) {.inline.} =
self.isEdited = value
proc deleted*(self: Item): bool {.inline.} =
self.deleted
proc `deleted=`*(self: Item, value: bool) {.inline.} =
self.deleted = value
proc deletedBy*(self: Item): string {.inline.} =
self.deletedBy
proc deletedByContactDetails*(self: Item): ContactDetails {.inline.} =
self.deletedByContactDetails
proc `deletedByContactDetails=`*(self: Item, value: ContactDetails) {.inline.} =
self.deletedByContactDetails = value
proc `deletedBy=`*(self: Item, value: string) {.inline.} =
self.deletedBy = value
proc gapFrom*(self: Item): int64 {.inline.} =
self.gapFrom
@ -586,7 +619,7 @@ proc quotedMessageAuthorAvatar*(self: Item): string {.inline.} =
proc `quotedMessageAuthorAvatar=`*(self: Item, value: string) {.inline.} =
self.quotedMessageAuthorAvatar = value
proc quotedMessageAuthorDetails*(self: Item): ContactDetails {.inline.} =
self.quotedMessageAuthorDetails
proc `quotedMessageAuthorDetails=`*(self: Item, value: ContactDetails) {.inline.} =

View File

@ -12,6 +12,7 @@ type
PrevMsgIndex
PrevMsgSenderId
PrevMsgContentType
PrevMsgDeleted
NextMsgIndex
NextMsgTimestamp
CommunityId
@ -42,6 +43,11 @@ type
Reactions
EditMode
IsEdited
Deleted
DeletedBy
DeletedByContactDisplayName
DeletedByContactIcon
DeletedByContactColorHash
Links
LinkPreviewModel
TransactionParameters
@ -111,6 +117,7 @@ QtObject:
ModelRole.PrevMsgIndex.int:"prevMsgIndex",
ModelRole.PrevMsgSenderId.int:"prevMsgSenderId",
ModelRole.PrevMsgContentType.int:"prevMsgContentType",
ModelRole.PrevMsgDeleted.int:"prevMsgDeleted",
ModelRole.NextMsgIndex.int:"nextMsgIndex",
ModelRole.NextMsgTimestamp.int:"nextMsgTimestamp",
ModelRole.CommunityId.int:"communityId",
@ -142,6 +149,11 @@ QtObject:
ModelRole.Reactions.int:"reactions",
ModelRole.EditMode.int: "editMode",
ModelRole.IsEdited.int: "isEdited",
ModelRole.Deleted.int: "deleted",
ModelRole.DeletedBy.int: "deletedBy",
ModelRole.DeletedByContactDisplayName.int: "deletedByContactDisplayName",
ModelRole.DeletedByContactIcon.int: "deletedByContactIcon",
ModelRole.DeletedByContactColorHash.int: "deletedByContactColorHash",
ModelRole.Links.int: "links",
ModelRole.LinkPreviewModel.int: "linkPreviewModel",
ModelRole.TransactionParameters.int: "transactionParameters",
@ -197,6 +209,12 @@ QtObject:
result = newQVariant(prevItem.contentType.int)
else:
result = newQVariant(ContentType.Unknown.int)
of ModelRole.PrevMsgDeleted:
if (index.row + 1 < self.items.len):
let prevItem = self.items[index.row + 1]
result = newQVariant(prevItem.deleted)
else:
result = newQVariant(false)
of ModelRole.PrevMsgIndex:
result = newQVariant(index.row + 1)
of ModelRole.NextMsgIndex:
@ -293,6 +311,16 @@ QtObject:
result = newQVariant(item.editMode)
of ModelRole.IsEdited:
result = newQVariant(item.isEdited)
of ModelRole.Deleted:
result = newQVariant(item.deleted)
of ModelRole.DeletedBy:
result = newQVariant(item.deletedBy)
of ModelRole.DeletedByContactDisplayName:
result = newQVariant(item.deletedByContactDetails.dto.userDefaultDisplayName())
of ModelRole.DeletedByContactIcon:
result = newQVariant(item.deletedByContactDetails.dto.image.thumbnail)
of ModelRole.DeletedByContactColorHash:
result = newQVariant(item.deletedByContactDetails.colorHash)
of ModelRole.Links:
result = newQVariant(item.links.join(" "))
of ModelRole.LinkPreviewModel:
@ -425,7 +453,7 @@ QtObject:
self.insertItemsBasedOnClock(@[item])
# Replied message was deleted
proc updateMessagesWithResponseTo(self: Model, messageId: string) =
proc updateMessagesWhenQuotedMessageDeleted(self: Model, messageId: string) =
for i in 0 ..< self.items.len:
if(self.items[i].responseToMessageWithId == messageId):
let ind = self.createIndex(i, 0, nil)
@ -469,7 +497,33 @@ QtObject:
self.updateItemAtIndex(ind + 1)
self.countChanged()
self.updateMessagesWithResponseTo(messageId)
self.updateMessagesWhenQuotedMessageDeleted(messageId)
proc messageDeleted*(self: Model, messageId: string, deletedBy: string, deletedByContactDetails: ContactDetails) =
let i = self.findIndexForMessageId(messageId)
if(i == -1):
return
let ind = self.createIndex(i, 0, nil)
defer: ind.delete
var item = self.items[i]
item.messageText = ""
item.unparsedText = ""
item.deleted = true
item.deletedBy = deletedBy
item.deletedByContactDetails = deletedByContactDetails
self.dataChanged(ind, ind, @[
ModelRole.MessageText.int,
ModelRole.UnparsedText.int,
ModelRole.Deleted.int,
ModelRole.DeletedBy.int,
ModelRole.DeletedByContactDisplayName.int,
ModelRole.DeletedByContactIcon.int,
ModelRole.DeletedByContactColorHash.int,
])
self.updateMessagesWhenQuotedMessageDeleted(messageId)
proc getLastItemFrom*(self: Model, pubkey: string): Item =
# last item == first time since we process messages in reverse order

View File

@ -118,6 +118,7 @@ type MessageDto* = object
linkPreviews*: seq[LinkPreview]
editedAt*: int
deleted*: bool
deletedBy*: string
deletedForMe*: bool
transactionParameters*: TransactionParameters
mentioned*: bool
@ -237,6 +238,10 @@ proc toMessageDto*(jsonObj: JsonNode): MessageDto =
discard jsonObj.getProp("albumImagesCount", result.albumImagesCount)
discard jsonObj.getProp("editedAt", result.editedAt)
discard jsonObj.getProp("deleted", result.deleted)
discard jsonObj.getProp("deletedBy", result.deletedBy)
if result.deleted and result.deletedBy == "":
# The message was deleted by the sender itself
result.deletedBy = result.`from`
discard jsonObj.getProp("deletedForMe", result.deletedForMe)
discard jsonObj.getProp("mentioned", result.mentioned)
discard jsonObj.getProp("replied", result.replied)

View File

@ -7,8 +7,10 @@ include ../../../common/json_utils
type RemovedMessageDto* = object
chatId*: string
messageId*: string
deletedBy*: string
proc toRemovedMessageDto*(jsonObj: JsonNode): RemovedMessageDto =
result = RemovedMessageDto()
discard jsonObj.getProp("chatId", result.chatId)
discard jsonObj.getProp("messageId", result.messageId)
discard jsonObj.getProp("deletedBy", result.deletedBy)

View File

@ -106,6 +106,7 @@ type
MessageDeletedArgs* = ref object of Args
chatId*: string
messageId*: string
deletedBy*: string
MessageDeliveredArgs* = ref object of Args
chatId*: string
@ -347,7 +348,7 @@ QtObject:
proc handleDeletedMessagesUpdate(self: Service, deletedMessages: seq[RemovedMessageDto]) =
for dm in deletedMessages:
let data = MessageDeletedArgs(chatId: dm.chatId, messageId: dm.messageId)
let data = MessageDeletedArgs(chatId: dm.chatId, messageId: dm.messageId, deletedBy: dm.deletedBy)
self.events.emit(SIGNAL_MESSAGE_DELETION, data)
proc handleEmojiReactionsUpdate(self: Service, emojiReactions: seq[ReactionDto]) =
@ -505,9 +506,11 @@ QtObject:
if(responseObj.getProp("reactions", reactionsArr)):
reactions = map(reactionsArr.getElems(), proc(x: JsonNode): ReactionDto = x.toReactionDto())
let data = MessagesLoadedArgs(chatId: chatId,
messages: messages,
reactions: reactions)
let data = MessagesLoadedArgs(
chatId: chatId,
messages: messages,
reactions: reactions,
)
self.events.emit(SIGNAL_MESSAGES_LOADED, data)
@ -998,7 +1001,11 @@ proc deleteMessage*(self: Service, messageId: string) =
error "error: ", procName="deleteMessage", errDesription = "there is no set chat id or message id in response"
return
let data = MessageDeletedArgs(chatId: chat_Id, messageId: message_Id)
let data = MessageDeletedArgs(
chatId: chat_Id,
messageId: message_Id,
deletedBy: singletonInstance.userProfile.getPubKey(),
)
self.events.emit(SIGNAL_MESSAGE_DELETION, data)
except Exception as e:

View File

@ -42,6 +42,9 @@ proc createTestMessageItem(id: string, clock: int64): Item =
senderEnsVerified = false,
discordMessage = DiscordMessage(),
resendError = "",
deleted = false,
deletedBy = "",
deletedByContactDetails = ContactDetails(),
mentioned = false,
quotedMessageFrom = "",
quotedMessageText = "",

View File

@ -62,6 +62,7 @@ StatusSortableColumnHeader 0.1 StatusSortableColumnHeader.qml
StatusStepper 0.1 StatusStepper.qml
StatusSyncDeviceDelegate 0.1 StatusSyncDeviceDelegate.qml
StatusTagSelector 0.1 StatusTagSelector.qml
StatusTimeStampLabel 0.1 StatusTimeStampLabel.qml
StatusToastMessage 0.1 StatusToastMessage.qml
StatusToolBar 0.1 StatusToolBar.qml
StatusVideo 0.1 StatusVideo.qml

View File

@ -9,7 +9,7 @@
<file>StatusQ/Components/private/statusMessage/StatusPinMessageDetails.qml</file>
<file>StatusQ/Components/private/statusMessage/StatusSticker.qml</file>
<file>StatusQ/Components/private/statusMessage/StatusTextMessage.qml</file>
<file>StatusQ/Components/private/statusMessage/StatusTimeStampLabel.qml</file>
<file>StatusQ/Components/StatusTimeStampLabel.qml</file>
<file>StatusQ/Components/qmldir</file>
<file>StatusQ/Components/private/qmldir</file>
<file>StatusQ/Components/StatusAddress.qml</file>

View File

@ -296,6 +296,11 @@ Item {
editModeOn: model.editMode
onEditModeOnChanged: root.editModeChanged(editModeOn)
isEdited: model.isEdited
deleted: model.deleted
deletedBy: model.deletedBy
deletedByContactDisplayName: model.deletedByContactDisplayName
deletedByContactIcon: model.deletedByContactIcon
deletedByContactColorHash: model.deletedByContactColorHash
linkPreviewModel: model.linkPreviewModel
links: model.links
messageAttachments: model.messageAttachments
@ -322,12 +327,13 @@ Item {
// Also one important thing here is that messages are set in descending order
// in terms of `timestamp` of a message, that means a message with the most
// recent time is added at index 0.
prevMessageIndex: prevMsgIndex
prevMessageTimestamp: prevMsgTimestamp
prevMessageSenderId: prevMsgSenderId
prevMessageContentType: prevMsgContentType
nextMessageIndex: nextMsgIndex
nextMessageTimestamp: nextMsgTimestamp
prevMessageIndex: model.prevMsgIndex
prevMessageTimestamp: model.prevMsgTimestamp
prevMessageSenderId: model.prevMsgSenderId
prevMessageContentType: model.prevMsgContentType
prevMessageDeleted: model.prevMsgDeleted
nextMessageIndex: model.nextMsgIndex
nextMessageTimestamp: model.nextMsgTimestamp
onOpenStickerPackPopup: {
root.openStickerPackPopup(stickerPackId);

View File

@ -103,6 +103,7 @@ Loader {
property int prevMessageIndex: -1
property int prevMessageContentType: prevMessageAsJsonObj ? prevMessageAsJsonObj.contentType : Constants.messageContentType.unknownContentType
property bool prevMessageDeleted: false
property double prevMessageTimestamp: prevMessageAsJsonObj ? prevMessageAsJsonObj.timestamp : 0
property string prevMessageSenderId: prevMessageAsJsonObj ? prevMessageAsJsonObj.senderId : ""
property var prevMessageAsJsonObj
@ -113,6 +114,12 @@ Loader {
property bool editModeOn: false
property bool isEdited: false
property bool deleted: false
property string deletedBy: ""
property string deletedByContactDisplayName: ""
property string deletedByContactIcon: ""
property string deletedByContactColorHash: ""
property bool shouldRepeatHeader: d.getShouldRepeatHeader(messageTimestamp, prevMessageTimestamp, messageOutgoingStatus)
property bool hasMention: false
@ -202,6 +209,9 @@ Loader {
asynchronous: true
sourceComponent: {
if (root.deleted) {
return deletedMessageComponent
}
switch(messageContentType) {
case Constants.messageContentType.chatIdentifier:
return channelIdentifierComponent
@ -448,6 +458,87 @@ Loader {
}
}
Component {
id: deletedMessageComponent
ColumnLayout {
RowLayout {
id: deletedMessage
height: 40
Layout.fillWidth: true
Layout.topMargin: 8
Layout.bottomMargin: 8
Layout.leftMargin: 16
spacing: 8
readonly property int smartIconSize: 20
readonly property int colorId: Utils.colorIdForPubkey(root.deletedBy)
readonly property var messageDetails: StatusMessageDetails {
sender.profileImage {
width: deletedMessage.smartIconSize
height: deletedMessage.smartIconSize
assetSettings: StatusAssetSettings {
width: deletedMessage.smartIconSize
height: deletedMessage.smartIconSize
name: root.deletedByContactIcon || ""
isLetterIdenticon: name === ""
imgIsIdenticon: false
charactersLen: 1
color: Theme.palette.userCustomizationColors[deletedMessage.colorId]
letterSize: 14
}
name: root.deletedByContactIcon || ""
pubkey: root.deletedBy
colorId: deletedMessage.colorId
colorHash: root.deletedByContactColorHash
showRing: true
}
}
Rectangle {
Layout.preferredWidth: deletedMessage.height
Layout.preferredHeight: deletedMessage.height
radius: width / 2
color: Theme.palette.dangerColor3
Layout.alignment: Qt.AlignVCenter
StatusIcon {
anchors.centerIn: parent
width: 24
height: 24
icon: "delete"
color: Theme.palette.dangerColor1
}
}
StatusSmartIdenticon {
id: profileImage
Layout.preferredWidth: deletedMessage.smartIconSize
Layout.preferredHeight: deletedMessage.smartIconSize
Layout.alignment: Qt.AlignVCenter
visible: true
name: root.deletedByContactDisplayName
asset: deletedMessage.messageDetails.sender.profileImage.assetSettings
ringSettings: deletedMessage.messageDetails.sender.profileImage.ringSettings
}
StatusBaseText {
text: qsTr("<b>%1</b> deleted this message").arg(root.deletedByContactDisplayName)
Layout.alignment: Qt.AlignVCenter
}
StatusTimeStampLabel {
Layout.alignment: Qt.AlignVCenter
timestamp: root.messageTimestamp
showFullTimestamp: false
}
}
}
}
Component {
id: messageComponent
@ -539,7 +630,7 @@ Loader {
root.prevMessageContentType === Constants.messageContentType.systemMessageMutualEventSent ||
root.prevMessageContentType === Constants.messageContentType.systemMessageMutualEventAccepted ||
root.prevMessageContentType === Constants.messageContentType.systemMessageMutualEventRemoved ||
root.senderId !== root.prevMessageSenderId
root.senderId !== root.prevMessageSenderId || root.prevMessageDeleted
isActiveMessage: d.isMessageActive
topPadding: showHeader ? Style.current.halfPadding : 0
bottomPadding: showHeader && d.nextMessageHasHeader() ? Style.current.halfPadding : 2