feat(@desktop/chat): Displaying new type of chat message - bridge message

Show discord user as a message sender.
Show discord avatar next to user name.
Show "Bridged from Discord" label.
Open adjusted profile context menu.

Issue #13098
This commit is contained in:
Michal Iskierko 2024-01-05 16:34:20 +01:00 committed by Michał Iskierko
parent eee8ec8e32
commit 0a18dda176
19 changed files with 137 additions and 22 deletions

View File

@ -147,6 +147,7 @@ proc createMessageItemFromDto(self: Module, message: MessageDto, communityId: st
imagesAlbum, imagesAlbum,
albumMessageIds, albumMessageIds,
message.albumImagesCount, message.albumImagesCount,
message.bridgeMessage,
)) ))
method convertToItems*( method convertToItems*(

View File

@ -206,6 +206,7 @@ proc createMessageItemsFromMessageDtos(self: Module, messages: seq[MessageDto],
if (len(message.albumId) == 0): @[] else: @[message.image], if (len(message.albumId) == 0): @[] else: @[message.image],
if (len(message.albumId) == 0): @[] else: @[message.id], if (len(message.albumId) == 0): @[] else: @[message.id],
message.albumImagesCount, message.albumImagesCount,
message.bridgeMessage,
) )
self.updateLinkPreviewsContacts(item, requestFromMailserver = item.seen) self.updateLinkPreviewsContacts(item, requestFromMailserver = item.seen)
@ -284,6 +285,7 @@ proc createFetchMoreMessagesItem(self: Module): Item =
albumMessageImages = @[], albumMessageImages = @[],
albumMessageIds = @[], albumMessageIds = @[],
albumImagesCount = 0, albumImagesCount = 0,
BridgeMessage(),
) )
proc createChatIdentifierItem(self: Module): Item = proc createChatIdentifierItem(self: Module): Item =
@ -349,6 +351,7 @@ proc createChatIdentifierItem(self: Module): Item =
albumMessageImages = @[], albumMessageImages = @[],
albumMessageIds = @[], albumMessageIds = @[],
albumImagesCount = 0, albumImagesCount = 0,
bridgeMessage = BridgeMessage(),
) )
proc checkIfMessageLoadedAndScrollToItIfItIs(self: Module) = proc checkIfMessageLoadedAndScrollToItIfItIs(self: Module) =

View File

@ -228,6 +228,7 @@ proc buildPinnedMessageItem(self: Module, message: MessageDto, actionInitiatedBy
if (len(message.albumId) == 0): @[] else: @[message.image], if (len(message.albumId) == 0): @[] else: @[message.image],
if (len(message.albumId) == 0): @[] else: @[message.id], if (len(message.albumId) == 0): @[] else: @[message.id],
message.albumImagesCount, message.albumImagesCount,
message.bridgeMessage,
) )
item.pinned = true item.pinned = true
item.pinnedBy = actionInitiatedBy item.pinnedBy = actionInitiatedBy

View File

@ -70,6 +70,7 @@ type
albumMessageImages: seq[string] albumMessageImages: seq[string]
albumMessageIds: seq[string] albumMessageIds: seq[string]
albumImagesCount: int albumImagesCount: int
bridgeName: string
proc initItem*( proc initItem*(
id, id,
@ -121,6 +122,7 @@ proc initItem*(
albumMessageImages: seq[string], albumMessageImages: seq[string],
albumMessageIds: seq[string], albumMessageIds: seq[string],
albumImagesCount: int, albumImagesCount: int,
bridgeMessage: BridgeMessage,
): Item = ): Item =
result = Item() result = Item()
result.id = id result.id = id
@ -208,6 +210,13 @@ proc initItem*(
if attachment.contentType.contains("image"): if attachment.contentType.contains("image"):
result.messageAttachments.add(attachment.localUrl) result.messageAttachments.add(attachment.localUrl)
if contentType == ContentType.BridgeMessage:
result.messageText = bridgeMessage.content
result.unparsedText = bridgeMessage.content
result.senderDisplayName = bridgeMessage.userName
result.senderIcon = bridgeMessage.userAvatar
result.bridgeName = bridgeMessage.bridgeName
proc initNewMessagesMarkerItem*(clock, timestamp: int64): Item = proc initNewMessagesMarkerItem*(clock, timestamp: int64): Item =
return initItem( return initItem(
id = "", id = "",
@ -259,6 +268,7 @@ proc initNewMessagesMarkerItem*(clock, timestamp: int64): Item =
albumMessageImages = @[], albumMessageImages = @[],
albumMessageIds = @[], albumMessageIds = @[],
albumImagesCount = 0, albumImagesCount = 0,
bridgeMessage = BridgeMessage(),
) )
proc `$`*(self: Item): string = proc `$`*(self: Item): string =
@ -404,6 +414,9 @@ proc `albumMessageIds=`*(self: Item, value: seq[string]) {.inline.} =
proc albumImagesCount*(self: Item): int {.inline.} = proc albumImagesCount*(self: Item): int {.inline.} =
self.albumImagesCount self.albumImagesCount
proc bridgeName*(self: Item): string {.inline.} =
self.bridgeName
proc messageContainsMentions*(self: Item): bool {.inline.} = proc messageContainsMentions*(self: Item): bool {.inline.} =
self.messageContainsMentions self.messageContainsMentions
@ -542,6 +555,7 @@ proc toJsonNode*(self: Item): JsonNode =
"albumMessageImages": self.albumMessageImages, "albumMessageImages": self.albumMessageImages,
"albumMessageIds": self.albumMessageIds, "albumMessageIds": self.albumMessageIds,
"albumImagesCount": self.albumImagesCount, "albumImagesCount": self.albumImagesCount,
"bridgeName": self.bridgeName
} }
proc editMode*(self: Item): bool {.inline.} = proc editMode*(self: Item): bool {.inline.} =

View File

@ -73,6 +73,7 @@ type
QuotedMessageAlbumImagesCount QuotedMessageAlbumImagesCount
AlbumMessageImages AlbumMessageImages
AlbumImagesCount AlbumImagesCount
BridgeName
QtObject: QtObject:
type type
@ -178,6 +179,7 @@ QtObject:
ModelRole.QuotedMessageAlbumImagesCount.int: "quotedMessageAlbumImagesCount", ModelRole.QuotedMessageAlbumImagesCount.int: "quotedMessageAlbumImagesCount",
ModelRole.AlbumMessageImages.int: "albumMessageImages", ModelRole.AlbumMessageImages.int: "albumMessageImages",
ModelRole.AlbumImagesCount.int: "albumImagesCount", ModelRole.AlbumImagesCount.int: "albumImagesCount",
ModelRole.BridgeName.int: "bridgeName",
}.toTable }.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant = method data(self: Model, index: QModelIndex, role: int): QVariant =
@ -350,6 +352,8 @@ QtObject:
result = newQVariant(item.albumMessageImages.join(" ")) result = newQVariant(item.albumMessageImages.join(" "))
of ModelRole.AlbumImagesCount: of ModelRole.AlbumImagesCount:
result = newQVariant(item.albumImagesCount) result = newQVariant(item.albumImagesCount)
of ModelRole.BridgeName:
result = newQVariant(item.bridgeName)
proc updateItemAtIndex(self: Model, index: int) = proc updateItemAtIndex(self: Model, index: int) =
let ind = self.createIndex(index, 0, nil) let ind = self.createIndex(index, 0, nil)

View File

@ -22,6 +22,7 @@ type
SystemMessageMutualEventSent = 15 SystemMessageMutualEventSent = 15
SystemMessageMutualEventAccepted = 16 SystemMessageMutualEventAccepted = 16
SystemMessageMutualEventRemoved = 17 SystemMessageMutualEventRemoved = 17
BridgeMessage = 18
proc toContentType*(value: int): ContentType = proc toContentType*(value: int): ContentType =
try: try:

View File

@ -56,6 +56,14 @@ type DiscordMessage* = object
author*: DiscordMessageAuthor author*: DiscordMessageAuthor
attachments*: seq[DiscordMessageAttachment] attachments*: seq[DiscordMessageAttachment]
type BridgeMessage* = object
bridgeName*: string
userName*: string
userAvatar*: string
userId*: string
content*: string
messageId*: string
parentMessageId*: string
type QuotedMessage* = object type QuotedMessage* = object
`from`*: string `from`*: string
@ -95,6 +103,7 @@ type MessageDto* = object
outgoingStatus*: string outgoingStatus*: string
quotedMessage*: QuotedMessage quotedMessage*: QuotedMessage
discordMessage*: DiscordMessage discordMessage*: DiscordMessage
bridgeMessage*: BridgeMessage
rtl*: bool rtl*: bool
parsedText*: seq[ParsedText] parsedText*: seq[ParsedText]
lineCount*: int lineCount*: int
@ -170,6 +179,16 @@ proc toDiscordMessage*(jsonObj: JsonNode): DiscordMessage =
for attachment in attachmentsArr: for attachment in attachmentsArr:
result.attachments.add(toDiscordMessageAttachment(attachment)) result.attachments.add(toDiscordMessageAttachment(attachment))
proc toBridgeMessage*(jsonObj: JsonNode): BridgeMessage =
result = BridgeMessage()
discard jsonObj.getProp("userName", result.userName)
discard jsonObj.getProp("bridgeName", result.bridgeName)
discard jsonObj.getProp("userAvatar", result.userAvatar)
discard jsonObj.getProp("userID", result.userId)
discard jsonObj.getProp("content", result.content)
discard jsonObj.getProp("messageID", result.messageId)
discard jsonObj.getProp("parentMessageID", result.parentMessageId)
proc toQuotedMessage*(jsonObj: JsonNode): QuotedMessage = proc toQuotedMessage*(jsonObj: JsonNode): QuotedMessage =
result = QuotedMessage() result = QuotedMessage()
var contentType: int var contentType: int
@ -263,6 +282,10 @@ proc toMessageDto*(jsonObj: JsonNode): MessageDto =
if(jsonObj.getProp("discordMessage", discordMessageObj)): if(jsonObj.getProp("discordMessage", discordMessageObj)):
result.discordMessage = toDiscordMessage(discordMessageObj) result.discordMessage = toDiscordMessage(discordMessageObj)
var bridgeMessageObj: JsonNode
if(jsonObj.getProp("bridgeMessage", bridgeMessageObj)):
result.bridgeMessage = toBridgeMessage(bridgeMessageObj)
var stickerObj: JsonNode var stickerObj: JsonNode
if(jsonObj.getProp("sticker", stickerObj)): if(jsonObj.getProp("sticker", stickerObj)):
result.sticker = toSticker(stickerObj) result.sticker = toSticker(stickerObj)

View File

@ -59,6 +59,7 @@ proc createTestMessageItem(id: string, clock: int64): Item =
albumMessageImages = @[], albumMessageImages = @[],
albumMessageIds = @[], albumMessageIds = @[],
albumImagesCount = 0, albumImagesCount = 0,
bridgeMessage = BridgeMessage(),
) )
let message0_chatIdentifier = createTestMessageItem("chat-identifier", -2) let message0_chatIdentifier = createTestMessageItem("chat-identifier", -2)

View File

@ -25,7 +25,8 @@ Control {
SystemMessagePinnedMessage = 14, SystemMessagePinnedMessage = 14,
SystemMessageMutualEventSent = 15, SystemMessageMutualEventSent = 15,
SystemMessageMutualEventAccepted = 16, SystemMessageMutualEventAccepted = 16,
SystemMessageMutualEventRemoved = 17 SystemMessageMutualEventRemoved = 17,
BridgeMessage = 18
} }
property list<Item> quickActions property list<Item> quickActions
@ -221,6 +222,8 @@ Control {
name: root.messageDetails.sender.displayName name: root.messageDetails.sender.displayName
asset: root.messageDetails.sender.profileImage.assetSettings asset: root.messageDetails.sender.profileImage.assetSettings
ringSettings: root.messageDetails.sender.profileImage.ringSettings ringSettings: root.messageDetails.sender.profileImage.ringSettings
bridgeBadge.visible: root.messageDetails.contentType === StatusMessage.ContentType.BridgeMessage
bridgeBadge.image.source: root.messageDetails.sender.badgeImage
MouseArea { MouseArea {
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
@ -267,7 +270,8 @@ Control {
&& ((root.messageDetails.contentType === StatusMessage.ContentType.Text) || && ((root.messageDetails.contentType === StatusMessage.ContentType.Text) ||
(root.messageDetails.contentType === StatusMessage.ContentType.Emoji) || (root.messageDetails.contentType === StatusMessage.ContentType.Emoji) ||
(root.messageDetails.contentType === StatusMessage.ContentType.DiscordMessage) || (root.messageDetails.contentType === StatusMessage.ContentType.DiscordMessage) ||
(root.messageDetails.contentType === StatusMessage.ContentType.Invitation))) (root.messageDetails.contentType === StatusMessage.ContentType.Invitation) ||
(root.messageDetails.contentType === StatusMessage.ContentType.BridgeMessage)))
visible: active visible: active
sourceComponent: StatusTextMessage { sourceComponent: StatusTextMessage {
objectName: "StatusMessage_textMessage" objectName: "StatusMessage_textMessage"

View File

@ -12,6 +12,8 @@ QtObject {
property bool isContact: false property bool isContact: false
property int trustIndicator: StatusContactVerificationIcons.TrustedType.None property int trustIndicator: StatusContactVerificationIcons.TrustedType.None
property string badgeImage: ""
property StatusProfileImageSettings profileImage: StatusProfileImageSettings { property StatusProfileImageSettings profileImage: StatusProfileImageSettings {
pubkey: root.id pubkey: root.id
showRing: !root.isEnsVerified showRing: !root.isEnsVerified

View File

@ -12,6 +12,8 @@ Loader {
// Badge color properties must be set if badgeItem.visible = true // Badge color properties must be set if badgeItem.visible = true
property alias badge: statusBadge property alias badge: statusBadge
property alias bridgeBadge: bridgeBadge
property StatusAssetSettings asset: StatusAssetSettings { property StatusAssetSettings asset: StatusAssetSettings {
width: 40 width: 40
height: 40 height: 40
@ -33,7 +35,7 @@ Loader {
property bool loading: false property bool loading: false
property bool hoverEnabled: false property bool hoverEnabled: false
readonly property bool hovered: (sourceComponent == roundedIcon && item) ? readonly property bool hovered: (sourceComponent === roundedIcon && item) ?
item.hovered : false item.hovered : false
signal clicked(var mouse) signal clicked(var mouse)
@ -140,6 +142,20 @@ Loader {
z: root.dZ z: root.dZ
} }
StatusRoundedImage {
id: bridgeBadge
visible: false
anchors.bottom: root.bottom
anchors.right: root.right
anchors.rightMargin: -border.width
anchors.bottomMargin: -border.width
implicitHeight: 20
implicitWidth: 20
border.width: 3
border.color: Theme.palette.statusBadge.foregroundColor
z: root.dZ
}
Component { Component {
id: loadingComp id: loadingComp
LoadingComponent { LoadingComponent {

View File

@ -111,6 +111,7 @@ StatusDialog {
quotedMessageAuthorDetailsEnsVerified: model.quotedMessageAuthorEnsVerified quotedMessageAuthorDetailsEnsVerified: model.quotedMessageAuthorEnsVerified
quotedMessageAuthorDetailsIsContact: model.quotedMessageAuthorIsContact quotedMessageAuthorDetailsIsContact: model.quotedMessageAuthorIsContact
quotedMessageAuthorDetailsColorHash: model.quotedMessageAuthorColorHash quotedMessageAuthorDetailsColorHash: model.quotedMessageAuthorColorHash
bridgeName: model.bridgeName
// This is possible since we have all data loaded before we load qml. // This is possible since we have all data loaded before we load qml.
// When we fetch messages to fulfill a gap we have to set them at once. // When we fetch messages to fulfill a gap we have to set them at once.

View File

@ -325,6 +325,7 @@ Item {
quotedMessageAuthorDetailsColorHash: model.quotedMessageAuthorColorHash quotedMessageAuthorDetailsColorHash: model.quotedMessageAuthorColorHash
quotedMessageAlbumMessageImages: model.quotedMessageAlbumMessageImages.split(" ") quotedMessageAlbumMessageImages: model.quotedMessageAlbumMessageImages.split(" ")
quotedMessageAlbumImagesCount: model.quotedMessageAlbumImagesCount quotedMessageAlbumImagesCount: model.quotedMessageAlbumImagesCount
bridgeName: model.bridgeName
gapFrom: model.gapFrom gapFrom: model.gapFrom
gapTo: model.gapTo gapTo: model.gapTo

View File

@ -0,0 +1,11 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="16" height="16" rx="8" fill="#5865F2"/>
<g clip-path="url(#clip0_50821_248568)">
<path d="M11.471 4.66999C10.814 4.35248 10.1204 4.12724 9.40789 4C9.31039 4.18397 9.22218 4.37324 9.14361 4.56704C8.3847 4.44633 7.61293 4.44633 6.85401 4.56704C6.7754 4.37326 6.68719 4.18399 6.58974 4C5.87681 4.12831 5.18275 4.35409 4.52508 4.67165C3.21944 6.71067 2.8655 8.69904 3.04247 10.6592C3.80708 11.2555 4.66291 11.709 5.57275 12C5.77761 11.7092 5.9589 11.4006 6.11467 11.0776C5.8188 10.961 5.53324 10.8171 5.26128 10.6476C5.33285 10.5928 5.40285 10.5363 5.4705 10.4815C6.26183 10.8743 7.12552 11.078 7.99999 11.078C8.87446 11.078 9.73816 10.8743 10.5295 10.4815C10.5979 10.5405 10.6679 10.5969 10.7387 10.6476C10.4662 10.8174 10.1801 10.9615 9.88374 11.0785C10.0393 11.4013 10.2206 11.7096 10.4257 12C11.3363 11.7102 12.1928 11.2569 12.9575 10.66C13.1652 8.38688 12.6028 6.41677 11.471 4.66999ZM6.33883 9.45372C5.84567 9.45372 5.43825 8.98132 5.43825 8.40017C5.43825 7.81901 5.83152 7.34247 6.33726 7.34247C6.843 7.34247 7.24728 7.81901 7.23863 8.40017C7.22997 8.98132 6.84143 9.45372 6.33883 9.45372ZM9.66115 9.45372C9.16721 9.45372 8.76136 8.98132 8.76136 8.40017C8.76136 7.81901 9.15462 7.34247 9.66115 7.34247C10.1677 7.34247 10.5688 7.81901 10.5602 8.40017C10.5515 8.98132 10.1637 9.45372 9.66115 9.45372Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_50821_248568">
<rect width="10" height="8" fill="white" transform="translate(3 4)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -40,6 +40,7 @@ Item {
property bool editButtonVisible: displayNamePlusIconsVisible property bool editButtonVisible: displayNamePlusIconsVisible
property bool loading: false property bool loading: false
readonly property bool compact: root.imageSize === ProfileHeader.ImageSize.Compact readonly property bool compact: root.imageSize === ProfileHeader.ImageSize.Compact
property bool isBridgedAccount: false
signal clicked() signal clicked()
signal editClicked() signal editClicked()
@ -119,6 +120,7 @@ Item {
imageHeight: imageWidth imageHeight: imageWidth
ensVerified: root.userIsEnsVerified ensVerified: root.userIsEnsVerified
loading: root.loading loading: root.loading
isBridgedAccount: root.isBridgedAccount
} }
StatusRoundButton { StatusRoundButton {
@ -206,7 +208,7 @@ Item {
StatusContactVerificationIcons { StatusContactVerificationIcons {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
visible: !root.isCurrentUser visible: !root.isCurrentUser && !root.isBridgedAccount
isContact: root.isContact isContact: root.isContact
trustIndicator: root.trustStatus trustIndicator: root.trustStatus
} }
@ -235,7 +237,7 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
visible: root.pubkeyVisible visible: root.pubkeyVisible
text: Utils.getElidedCompressedPk(pubkey) text: root.isBridgedAccount ? qsTr("Bridged from Discord") : Utils.getElidedCompressedPk(pubkey)
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
font.pixelSize: 13 font.pixelSize: 13
color: Style.current.secondaryText color: Style.current.secondaryText
@ -243,7 +245,7 @@ Item {
RowLayout { RowLayout {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
visible: root.pubkeyVisibleWithCopy visible: root.pubkeyVisibleWithCopy && !root.isBridgedAccount
StyledText { StyledText {
id: txtChatKey id: txtChatKey
text: qsTr("Chatkey:%1...").arg(pubkey.substring(0, 32)) text: qsTr("Chatkey:%1...").arg(pubkey.substring(0, 32))
@ -265,7 +267,7 @@ Item {
EmojiHash { EmojiHash {
id: emojiHash id: emojiHash
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
visible: root.emojiHashVisible visible: root.emojiHashVisible && !root.isBridgedAccount
compact: root.compact compact: root.compact
publicKey: root.pubkey publicKey: root.pubkey
} }

View File

@ -16,11 +16,12 @@ Loader {
property string name property string name
property string pubkey property string pubkey
property string image property string image
property bool showRing: !ensVerified property bool showRing: !ensVerified && !root.isBridgedAccount
property bool interactive: true property bool interactive: true
property bool disabled: false property bool disabled: false
property bool ensVerified: false property bool ensVerified: false
property bool loading: false property bool loading: false
property bool isBridgedAccount: false
property int colorId: Utils.colorIdForPubkey(pubkey) property int colorId: Utils.colorIdForPubkey(pubkey)
property var colorHash: Utils.getColorHashAsJson(pubkey, ensVerified) property var colorHash: Utils.getColorHashAsJson(pubkey, ensVerified)
@ -41,6 +42,8 @@ Loader {
ringSpecModel: root.showRing ? root.colorHash : undefined ringSpecModel: root.showRing ? root.colorHash : undefined
} }
loading: root.loading loading: root.loading
bridgeBadge.visible: root.isBridgedAccount
bridgeBadge.image.source: Style.svg("discord-bridge")
Loader { Loader {
anchors.fill: parent anchors.fill: parent

View File

@ -128,13 +128,15 @@ Loader {
property bool stickersLoaded: false property bool stickersLoaded: false
property string sticker property string sticker
property int stickerPack: -1 property int stickerPack: -1
property string bridgeName: ""
property bool isEmoji: messageContentType === Constants.messageContentType.emojiType property bool isEmoji: messageContentType === Constants.messageContentType.emojiType
property bool isImage: messageContentType === Constants.messageContentType.imageType || (isDiscordMessage && messageImage != "") property bool isImage: messageContentType === Constants.messageContentType.imageType || (isDiscordMessage && messageImage != "")
property bool isAudio: messageContentType === Constants.messageContentType.audioType property bool isAudio: messageContentType === Constants.messageContentType.audioType
property bool isSticker: messageContentType === Constants.messageContentType.stickerType property bool isSticker: messageContentType === Constants.messageContentType.stickerType
property bool isDiscordMessage: messageContentType === Constants.messageContentType.discordMessageType property bool isDiscordMessage: messageContentType === Constants.messageContentType.discordMessageType
property bool isText: messageContentType === Constants.messageContentType.messageType || messageContentType === Constants.messageContentType.contactRequestType || isDiscordMessage property bool isBridgeMessage: messageContentType === Constants.messageContentType.bridgeMessageType
property bool isText: messageContentType === Constants.messageContentType.messageType || messageContentType === Constants.messageContentType.contactRequestType || isDiscordMessage || isBridgeMessage
property bool isMessage: isEmoji || isImage || isSticker || isText || isAudio property bool isMessage: isEmoji || isImage || isSticker || isText || isAudio
|| messageContentType === Constants.messageContentType.communityInviteType || messageContentType === Constants.messageContentType.transactionType || messageContentType === Constants.messageContentType.communityInviteType || messageContentType === Constants.messageContentType.transactionType
@ -152,6 +154,7 @@ Loader {
selectedUserPublicKey: isReply ? quotedMessageFrom : root.senderId, selectedUserPublicKey: isReply ? quotedMessageFrom : root.senderId,
selectedUserDisplayName: isReply ? quotedMessageAuthorDetailsDisplayName : root.senderDisplayName, selectedUserDisplayName: isReply ? quotedMessageAuthorDetailsDisplayName : root.senderDisplayName,
selectedUserIcon: isReply ? quotedMessageAuthorDetailsThumbnailImage : root.senderIcon, selectedUserIcon: isReply ? quotedMessageAuthorDetailsThumbnailImage : root.senderIcon,
isBridgedAccount: root.isBridgeMessage
} }
Global.openMenu(profileContextMenuComponent, sender, params) Global.openMenu(profileContextMenuComponent, sender, params)
@ -239,6 +242,7 @@ Loader {
case Constants.messageContentType.communityInviteType: case Constants.messageContentType.communityInviteType:
case Constants.messageContentType.discordMessageType: case Constants.messageContentType.discordMessageType:
case Constants.messageContentType.contactRequestType: case Constants.messageContentType.contactRequestType:
case Constants.messageContentType.bridgeMessageType:
return messageComponent return messageComponent
case Constants.messageContentType.unknownContentType: case Constants.messageContentType.unknownContentType:
// NOTE: We could display smth like "unknown message type, please upgrade Status to see it". // NOTE: We could display smth like "unknown message type, please upgrade Status to see it".
@ -296,6 +300,8 @@ Loader {
return StatusMessage.ContentType.Invitation; return StatusMessage.ContentType.Invitation;
case Constants.messageContentType.discordMessageType: case Constants.messageContentType.discordMessageType:
return StatusMessage.ContentType.DiscordMessage; return StatusMessage.ContentType.DiscordMessage;
case Constants.messageContentType.bridgeMessageType:
return StatusMessage.ContentType.BridgeMessage;
case Constants.messageContentType.systemMessagePinnedMessage: case Constants.messageContentType.systemMessagePinnedMessage:
return StatusMessage.ContentType.SystemMessagePinnedMessage; return StatusMessage.ContentType.SystemMessagePinnedMessage;
case Constants.messageContentType.systemMessageMutualEventSent: case Constants.messageContentType.systemMessageMutualEventSent:
@ -333,6 +339,11 @@ Loader {
break; break;
} }
} }
function correctBridgeNameCapitalization(bridgeName) {
return (bridgeName === "discord") ? "Discord" : bridgeName
}
} }
Component { Component {
@ -724,7 +735,14 @@ Loader {
messageDetails: StatusMessageDetails { messageDetails: StatusMessageDetails {
contentType: delegate.contentType contentType: delegate.contentType
messageOriginInfo: isDiscordMessage ? qsTr("Imported from discord") : "" messageOriginInfo: {
if (isDiscordMessage) {
return qsTr("Imported from discord")
} else if (isBridgeMessage) {
return qsTr("Bridged from %1").arg(d.correctBridgeNameCapitalization(root.bridgeName))
}
return ""
}
messageText: root.messageText messageText: root.messageText
messageContent: { messageContent: {
switch (delegate.contentType) switch (delegate.contentType)
@ -747,9 +765,9 @@ Loader {
sender.id: root.senderIsEnsVerified ? "" : Utils.getCompressedPk(root.senderId) sender.id: root.senderIsEnsVerified ? "" : Utils.getCompressedPk(root.senderId)
sender.displayName: root.senderDisplayName sender.displayName: root.senderDisplayName
sender.secondaryName: root.senderOptionalName sender.secondaryName: root.senderOptionalName
sender.isEnsVerified: root.senderIsEnsVerified sender.isEnsVerified: root.isBridgeMessage ? false : root.senderIsEnsVerified
sender.isContact: root.senderIsAdded sender.isContact: root.isBridgeMessage ? false : root.senderIsAdded
sender.trustIndicator: root.senderTrustStatus sender.trustIndicator: root.isBridgeMessage ? StatusContactVerificationIcons.TrustedType.None: root.senderTrustStatus
sender.profileImage { sender.profileImage {
width: 40 width: 40
height: 40 height: 40
@ -757,8 +775,9 @@ Loader {
pubkey: root.senderId pubkey: root.senderId
colorId: Utils.colorIdForPubkey(root.senderId) colorId: Utils.colorIdForPubkey(root.senderId)
colorHash: root.senderColorHash colorHash: root.senderColorHash
showRing: !root.isDiscordMessage && !root.senderIsEnsVerified showRing: !root.isDiscordMessage && !root.senderIsEnsVerified && !root.isBridgeMessage
} }
sender.badgeImage: Style.svg("discord-bridge")
} }
replyDetails: StatusMessageDetails { replyDetails: StatusMessageDetails {

View File

@ -25,6 +25,8 @@ StatusMenu {
property string selectedUserDisplayName: "" property string selectedUserDisplayName: ""
property string selectedUserIcon: "" property string selectedUserIcon: ""
property bool isBridgedAccount: false
readonly property bool isMe: { readonly property bool isMe: {
return root.selectedUserPublicKey === root.store.contactsStore.myPublicKey; return root.selectedUserPublicKey === root.store.contactsStore.myPublicKey;
} }
@ -104,15 +106,18 @@ StatusMenu {
isContact: root.isContact isContact: root.isContact
isCurrentUser: root.isMe isCurrentUser: root.isMe
userIsEnsVerified: (!!contactDetails && contactDetails.ensVerified) || false userIsEnsVerified: (!!contactDetails && contactDetails.ensVerified) || false
isBridgedAccount: root.isBridgedAccount
} }
StatusMenuSeparator { StatusMenuSeparator {
topPadding: root.topPadding topPadding: root.topPadding
visible: !root.isBridgedAccount
} }
ViewProfileMenuItem { ViewProfileMenuItem {
id: viewProfileAction id: viewProfileAction
objectName: "viewProfile_StatusItem" objectName: "viewProfile_StatusItem"
enabled: !root.isBridgedAccount
onTriggered: { onTriggered: {
root.openProfileClicked(root.selectedUserPublicKey) root.openProfileClicked(root.selectedUserPublicKey)
root.close() root.close()
@ -122,7 +127,7 @@ StatusMenu {
SendMessageMenuItem { SendMessageMenuItem {
id: sendMessageMenuItem id: sendMessageMenuItem
objectName: "sendMessage_StatusItem" objectName: "sendMessage_StatusItem"
enabled: root.isContact && !root.isBlockedContact enabled: root.isContact && !root.isBlockedContact && !root.isBridgedAccount
onTriggered: { onTriggered: {
root.createOneToOneChat("", root.selectedUserPublicKey, "") root.createOneToOneChat("", root.selectedUserPublicKey, "")
root.close() root.close()
@ -133,7 +138,7 @@ StatusMenu {
id: sendContactRequestMenuItem id: sendContactRequestMenuItem
objectName: "sendContactRequest_StatusItem" objectName: "sendContactRequest_StatusItem"
enabled: !root.isMe && !root.isContact enabled: !root.isMe && !root.isContact
&& !root.isBlockedContact && !root.hasPendingContactRequest && !root.isBlockedContact && !root.hasPendingContactRequest && !root.isBridgedAccount
onTriggered: { onTriggered: {
Global.openContactRequestPopup(root.selectedUserPublicKey, null) Global.openContactRequestPopup(root.selectedUserPublicKey, null)
root.close() root.close()
@ -149,6 +154,7 @@ StatusMenu {
&& !root.isBlockedContact && !root.isBlockedContact
&& root.outgoingVerificationStatus === Constants.verificationStatus.unverified && root.outgoingVerificationStatus === Constants.verificationStatus.unverified
&& !root.hasActiveReceivedVerificationRequestFrom && !root.hasActiveReceivedVerificationRequestFrom
&& !root.isBridgedAccount
onTriggered: { onTriggered: {
Global.openSendIDRequestPopup(root.selectedUserPublicKey, null) Global.openSendIDRequestPopup(root.selectedUserPublicKey, null)
root.close() root.close()
@ -167,6 +173,7 @@ StatusMenu {
&& !root.isBlockedContact && !root.isTrusted && !root.isBlockedContact && !root.isTrusted
&& (root.hasActiveReceivedVerificationRequestFrom && (root.hasActiveReceivedVerificationRequestFrom
|| root.isVerificationRequestSent) || root.isVerificationRequestSent)
&& !root.isBridgedAccount
onTriggered: { onTriggered: {
if (hasActiveReceivedVerificationRequestFrom) { if (hasActiveReceivedVerificationRequestFrom) {
Global.openIncomingIDRequestPopup(root.selectedUserPublicKey, null) Global.openIncomingIDRequestPopup(root.selectedUserPublicKey, null)
@ -183,7 +190,7 @@ StatusMenu {
objectName: "rename_StatusItem" objectName: "rename_StatusItem"
text: qsTr("Rename") text: qsTr("Rename")
icon.name: "edit_pencil" icon.name: "edit_pencil"
enabled: !root.isMe enabled: !root.isMe && !root.isBridgedAccount
onTriggered: { onTriggered: {
Global.openNicknamePopupRequested(root.selectedUserPublicKey, contactDetails.localNickname, Global.openNicknamePopupRequested(root.selectedUserPublicKey, contactDetails.localNickname,
"%1 (%2)".arg(root.selectedUserDisplayName).arg(Utils.getElidedCompressedPk(root.selectedUserPublicKey))) "%1 (%2)".arg(root.selectedUserDisplayName).arg(Utils.getElidedCompressedPk(root.selectedUserPublicKey)))
@ -196,7 +203,7 @@ StatusMenu {
objectName: "unblock_StatusItem" objectName: "unblock_StatusItem"
text: qsTr("Unblock User") text: qsTr("Unblock User")
icon.name: "remove-circle" icon.name: "remove-circle"
enabled: !root.isMe && root.isBlockedContact enabled: !root.isMe && root.isBlockedContact && !root.isBridgedAccount
onTriggered: Global.unblockContactRequested(root.selectedUserPublicKey, root.selectedUserDisplayName) onTriggered: Global.unblockContactRequested(root.selectedUserPublicKey, root.selectedUserDisplayName)
} }
@ -212,7 +219,7 @@ StatusMenu {
text: qsTr("Mark as Untrustworthy") text: qsTr("Mark as Untrustworthy")
icon.name: "warning" icon.name: "warning"
type: StatusAction.Type.Danger type: StatusAction.Type.Danger
enabled: !root.isMe && root.userTrustIsUnknown enabled: !root.isMe && root.userTrustIsUnknown && !root.isBridgedAccount
onTriggered: root.store.contactsStore.markUntrustworthy(root.selectedUserPublicKey) onTriggered: root.store.contactsStore.markUntrustworthy(root.selectedUserPublicKey)
} }
@ -221,7 +228,7 @@ StatusMenu {
objectName: "removeUntrustworthy_StatusItem" objectName: "removeUntrustworthy_StatusItem"
text: qsTr("Remove Untrustworthy Mark") text: qsTr("Remove Untrustworthy Mark")
icon.name: "warning" icon.name: "warning"
enabled: !root.isMe && root.userIsUntrustworthy enabled: !root.isMe && root.userIsUntrustworthy && !root.isBridgedAccount
onTriggered: root.store.contactsStore.removeTrustStatus(root.selectedUserPublicKey) onTriggered: root.store.contactsStore.removeTrustStatus(root.selectedUserPublicKey)
} }
@ -230,7 +237,7 @@ StatusMenu {
objectName: "removeContact_StatusItem" objectName: "removeContact_StatusItem"
icon.name: "remove-contact" icon.name: "remove-contact"
type: StatusAction.Type.Danger type: StatusAction.Type.Danger
enabled: root.isContact && !root.isBlockedContact && !root.hasPendingContactRequest enabled: root.isContact && !root.isBlockedContact && !root.hasPendingContactRequest && !root.isBridgedAccount
onTriggered: { onTriggered: {
Global.removeContactRequested(root.selectedUserDisplayName, root.selectedUserPublicKey) Global.removeContactRequested(root.selectedUserDisplayName, root.selectedUserPublicKey)
root.close() root.close()
@ -243,7 +250,7 @@ StatusMenu {
text: qsTr("Block User") text: qsTr("Block User")
icon.name: "cancel" icon.name: "cancel"
type: StatusAction.Type.Danger type: StatusAction.Type.Danger
enabled: !root.isMe && !root.isBlockedContact enabled: !root.isMe && !root.isBlockedContact && !root.isBridgedAccount
onTriggered: Global.blockContactRequested(root.selectedUserPublicKey, root.selectedUserDisplayName) onTriggered: Global.blockContactRequested(root.selectedUserPublicKey, root.selectedUserDisplayName)
} }

View File

@ -457,6 +457,7 @@ QtObject {
readonly property int systemMessageMutualEventSent: 15 readonly property int systemMessageMutualEventSent: 15
readonly property int systemMessageMutualEventAccepted: 16 readonly property int systemMessageMutualEventAccepted: 16
readonly property int systemMessageMutualEventRemoved: 17 readonly property int systemMessageMutualEventRemoved: 17
readonly property int bridgeMessageType: 18
} }
readonly property QtObject messageModelRoles: QtObject { readonly property QtObject messageModelRoles: QtObject {