fix(ChatMessagesView): Usage of `StatusMessage` WIP
This commit is contained in:
parent
75ec2750b3
commit
42a1cf995c
|
@ -75,7 +75,7 @@ proc createMessageItemFromDto(self: Module, message: MessageDto, chatDetails: Ch
|
|||
chatDetails.communityId, # we don't received community id via `activityCenterNotifications` api call
|
||||
message.responseTo,
|
||||
message.`from`,
|
||||
contactDetails.displayName,
|
||||
contactDetails.details.displayName,
|
||||
contactDetails.details.localNickname,
|
||||
contactDetails.icon,
|
||||
contactDetails.isCurrentUser,
|
||||
|
@ -93,7 +93,8 @@ proc createMessageItemFromDto(self: Module, message: MessageDto, chatDetails: Ch
|
|||
message.links,
|
||||
newTransactionParametersItem("","","","","","",-1,""),
|
||||
message.mentionedUsersPks,
|
||||
contactDetails.details.trustStatus
|
||||
contactDetails.details.trustStatus,
|
||||
contactDetails.details.ensVerified
|
||||
))
|
||||
|
||||
method convertToItems*(
|
||||
|
|
|
@ -91,10 +91,11 @@ proc createFetchMoreMessagesItem(self: Module): Item =
|
|||
messageType = -1,
|
||||
sticker = "",
|
||||
stickerPack = -1,
|
||||
@[],
|
||||
newTransactionParametersItem("","","","","","",-1,""),
|
||||
@[],
|
||||
TrustStatus.Unknown
|
||||
links = @[],
|
||||
transactionParameters = newTransactionParametersItem("","","","","","",-1,""),
|
||||
mentionedUsersPks = @[],
|
||||
senderTrustStatus = TrustStatus.Unknown,
|
||||
senderEnsVerified = false
|
||||
)
|
||||
|
||||
proc createChatIdentifierItem(self: Module): Item =
|
||||
|
@ -128,10 +129,11 @@ proc createChatIdentifierItem(self: Module): Item =
|
|||
messageType = -1,
|
||||
sticker = "",
|
||||
stickerPack = -1,
|
||||
@[],
|
||||
newTransactionParametersItem("","","","","","",-1,""),
|
||||
@[],
|
||||
TrustStatus.Unknown
|
||||
links = @[],
|
||||
transactionParameters = newTransactionParametersItem("","","","","","",-1,""),
|
||||
mentionedUsersPks = @[],
|
||||
senderTrustStatus = TrustStatus.Unknown,
|
||||
senderEnsVerified = false
|
||||
)
|
||||
|
||||
proc checkIfMessageLoadedAndScrollToItIfItIs(self: Module): bool =
|
||||
|
@ -177,7 +179,7 @@ method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: se
|
|||
m.communityId,
|
||||
m.responseTo,
|
||||
m.`from`,
|
||||
sender.displayName,
|
||||
sender.details.displayName,
|
||||
sender.details.localNickname,
|
||||
sender.icon,
|
||||
isCurrentUser,
|
||||
|
@ -203,6 +205,7 @@ method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: se
|
|||
m.transactionParameters.signature),
|
||||
m.mentionedUsersPks(),
|
||||
sender.details.trustStatus,
|
||||
sender.details.ensVerified
|
||||
)
|
||||
|
||||
for r in reactions:
|
||||
|
@ -265,7 +268,7 @@ method messageAdded*(self: Module, message: MessageDto) =
|
|||
message.communityId,
|
||||
message.responseTo,
|
||||
message.`from`,
|
||||
sender.displayName,
|
||||
sender.details.displayName,
|
||||
sender.details.localNickname,
|
||||
sender.icon,
|
||||
isCurrentUser,
|
||||
|
@ -291,6 +294,7 @@ method messageAdded*(self: Module, message: MessageDto) =
|
|||
message.transactionParameters.signature),
|
||||
message.mentionedUsersPks,
|
||||
sender.details.trustStatus,
|
||||
sender.details.ensVerified
|
||||
)
|
||||
|
||||
self.view.model().insertItemBasedOnTimestamp(item)
|
||||
|
@ -399,7 +403,7 @@ method updateContactDetails*(self: Module, contactId: string) =
|
|||
let updatedContact = self.controller.getContactDetails(contactId)
|
||||
for item in self.view.model().modelContactUpdateIterator(contactId):
|
||||
if(item.senderId == contactId):
|
||||
item.senderDisplayName = updatedContact.displayName
|
||||
item.senderDisplayName = updatedContact.details.displayName
|
||||
item.senderLocalName = updatedContact.details.localNickname
|
||||
item.senderIcon = updatedContact.icon
|
||||
item.senderIsAdded = updatedContact.details.added
|
||||
|
|
|
@ -167,7 +167,7 @@ proc buildPinnedMessageItem(self: Module, messageId: string, actionInitiatedBy:
|
|||
m.communityId,
|
||||
m.responseTo,
|
||||
m.`from`,
|
||||
contactDetails.displayName,
|
||||
contactDetails.details.displayName,
|
||||
contactDetails.details.localNickname,
|
||||
contactDetails.icon,
|
||||
isCurrentUser,
|
||||
|
@ -193,6 +193,7 @@ proc buildPinnedMessageItem(self: Module, messageId: string, actionInitiatedBy:
|
|||
m.transactionParameters.signature),
|
||||
m.mentionedUsersPks,
|
||||
contactDetails.details.trustStatus,
|
||||
contactDetails.details.ensVerified
|
||||
)
|
||||
item.pinned = true
|
||||
item.pinnedBy = actionInitiatedBy
|
||||
|
@ -318,8 +319,9 @@ method onContactDetailsUpdated*(self: Module, contactId: string) =
|
|||
let updatedContact = self.controller.getContactDetails(contactId)
|
||||
for item in self.view.pinnedModel().modelContactUpdateIterator(contactId):
|
||||
if(item.senderId == contactId):
|
||||
item.senderDisplayName = updatedContact.displayName
|
||||
item.senderDisplayName = updatedContact.details.displayName
|
||||
item.senderLocalName = updatedContact.details.localNickname
|
||||
item.senderEnsVerified = updatedContact.details.ensVerified
|
||||
item.senderIcon = updatedContact.icon
|
||||
item.senderTrustStatus = updatedContact.details.trustStatus
|
||||
if(item.messageContainsMentions):
|
||||
|
@ -329,7 +331,7 @@ method onContactDetailsUpdated*(self: Module, contactId: string) =
|
|||
item.messageContainsMentions = m.containsContactMentions()
|
||||
|
||||
if(self.controller.getMyChatId() == contactId):
|
||||
self.view.updateChatDetailsNameAndIcon(updatedContact.displayName, updatedContact.icon)
|
||||
self.view.updateChatDetailsNameAndIcon(updatedContact.details.displayName, updatedContact.icon)
|
||||
self.view.updateTrustStatus(updatedContact.details.trustStatus == TrustStatus.Untrustworthy)
|
||||
|
||||
method onNotificationsUpdated*(self: Module, hasUnreadMessages: bool, notificationCount: int) =
|
||||
|
|
|
@ -75,7 +75,7 @@ method onNewMessagesLoaded*(self: Module, messages: seq[MessageDto]) =
|
|||
let status = toOnlineStatus(statusUpdateDto.statusType)
|
||||
self.view.model().addItem(initMemberItem(
|
||||
pubKey = m.`from`,
|
||||
displayName = contactDetails.displayName,
|
||||
displayName = contactDetails.details.displayName,
|
||||
ensName = contactDetails.details.name, # is it correct?
|
||||
localNickname = contactDetails.details.localNickname,
|
||||
alias = contactDetails.details.alias,
|
||||
|
@ -132,7 +132,7 @@ method addChatMember*(self: Module, member: ChatMember) =
|
|||
let isMe = member.id == singletonInstance.userProfile.getPubKey()
|
||||
let contactDetails = self.controller.getContactDetails(member.id)
|
||||
var status = OnlineStatus.Online
|
||||
var displayName = contactDetails.displayName
|
||||
var displayName = contactDetails.details.displayName
|
||||
if (isMe):
|
||||
let currentUserStatus = intToEnum(singletonInstance.userProfile.getCurrentUserStatus(), StatusType.Unknown)
|
||||
status = toOnlineStatus(currentUserStatus)
|
||||
|
@ -142,7 +142,7 @@ method addChatMember*(self: Module, member: ChatMember) =
|
|||
|
||||
self.view.model().addItem(initMemberItem(
|
||||
pubKey = member.id,
|
||||
displayName = displayName,
|
||||
displayName = contactDetails.details.displayName,
|
||||
ensName = contactDetails.details.name,
|
||||
localNickname = contactDetails.details.localNickname,
|
||||
alias = contactDetails.details.alias,
|
||||
|
@ -180,7 +180,7 @@ method onChatMemberUpdated*(self: Module, publicKey: string, admin: bool, joined
|
|||
let contactDetails = self.controller.getContactDetails(publicKey)
|
||||
self.view.model().updateItem(
|
||||
pubKey = publicKey,
|
||||
displayName = contactDetails.displayName,
|
||||
displayName = contactDetails.details.displayName,
|
||||
ensName = contactDetails.details.name,
|
||||
localNickname = contactDetails.details.localNickname,
|
||||
alias = contactDetails.details.alias,
|
||||
|
|
|
@ -65,7 +65,7 @@ proc createChatItem(self: Module, chatDto: ChatDto): Item =
|
|||
var itemType = item.Type.GroupChat
|
||||
if(chatDto.chatType == ChatType.OneToOne):
|
||||
let contactDetails = self.controller.getContactDetails(chatDto.id)
|
||||
chatName = contactDetails.displayName
|
||||
chatName = contactDetails.details.displayName
|
||||
chatImage = contactDetails.icon
|
||||
itemType = item.Type.OneToOneChat
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ type
|
|||
transactionParameters: TransactionParametersItem
|
||||
mentionedUsersPks: seq[string]
|
||||
senderTrustStatus: TrustStatus
|
||||
senderEnsVerified: bool
|
||||
|
||||
proc initItem*(
|
||||
id,
|
||||
|
@ -61,7 +62,8 @@ proc initItem*(
|
|||
links: seq[string],
|
||||
transactionParameters: TransactionParametersItem,
|
||||
mentionedUsersPks: seq[string],
|
||||
senderTrustStatus: TrustStatus
|
||||
senderTrustStatus: TrustStatus,
|
||||
senderEnsVerified: bool
|
||||
): Item =
|
||||
result = Item()
|
||||
result.id = id
|
||||
|
@ -93,6 +95,7 @@ proc initItem*(
|
|||
result.gapFrom = 0
|
||||
result.gapTo = 0
|
||||
result.senderTrustStatus = senderTrustStatus
|
||||
result.senderEnsVerified = senderEnsVerified
|
||||
|
||||
proc `$`*(self: Item): string =
|
||||
result = fmt"""Item(
|
||||
|
@ -120,6 +123,7 @@ proc `$`*(self: Item): string =
|
|||
transactionParameters:{$self.transactionParameters},
|
||||
mentionedUsersPks:{$self.mentionedUsersPks},
|
||||
senderTrustStatus:{$self.senderTrustStatus},
|
||||
senderEnsVerified: {self.senderEnsVerified},
|
||||
)"""
|
||||
|
||||
proc id*(self: Item): string {.inline.} =
|
||||
|
@ -167,6 +171,12 @@ proc senderTrustStatus*(self: Item): TrustStatus {.inline.} =
|
|||
proc `senderTrustStatus=`*(self: Item, value: TrustStatus) {.inline.} =
|
||||
self.senderTrustStatus = value
|
||||
|
||||
proc senderEnsVerified*(self: Item): bool {.inline.} =
|
||||
self.senderEnsVerified
|
||||
|
||||
proc `senderEnsVerified=`*(self: Item, value: bool) {.inline.} =
|
||||
self.senderEnsVerified = value
|
||||
|
||||
proc outgoingStatus*(self: Item): string {.inline.} =
|
||||
self.outgoingStatus
|
||||
|
||||
|
@ -274,7 +284,8 @@ proc toJsonNode*(self: Item): JsonNode =
|
|||
"editMode": self.editMode,
|
||||
"isEdited": self.isEdited,
|
||||
"links": self.links,
|
||||
"mentionedUsersPks": self.mentionedUsersPks
|
||||
"mentionedUsersPks": self.mentionedUsersPks,
|
||||
"senderEnsVerified": self.senderEnsVerified
|
||||
}
|
||||
|
||||
proc editMode*(self: Item): bool {.inline.} =
|
||||
|
|
|
@ -49,6 +49,10 @@ QtObject:
|
|||
QtProperty[string] senderLocalName:
|
||||
read = senderLocalName
|
||||
|
||||
proc senderEnsVerified*(self: MessageItem): bool {.slot.} = result = ?.self.messageItem.senderEnsVerified
|
||||
QtProperty[bool] senderEnsVerified:
|
||||
read = senderEnsVerified
|
||||
|
||||
proc amISender*(self: MessageItem): bool {.slot.} = result = ?.self.messageItem.amISender
|
||||
QtProperty[bool] amISender:
|
||||
read = amISender
|
||||
|
|
|
@ -38,6 +38,7 @@ type
|
|||
TransactionParameters
|
||||
MentionedUsersPks
|
||||
SenderTrustStatus
|
||||
SenderEnsVerified
|
||||
|
||||
QtObject:
|
||||
type
|
||||
|
@ -71,7 +72,7 @@ QtObject:
|
|||
proc countChanged(self: Model) {.signal.}
|
||||
proc getCount(self: Model): int {.slot.} =
|
||||
self.items.len
|
||||
QtProperty[int] count:
|
||||
QtProperty[int]count:
|
||||
read = getCount
|
||||
notify = countChanged
|
||||
|
||||
|
@ -112,7 +113,8 @@ QtObject:
|
|||
ModelRole.Links.int: "links",
|
||||
ModelRole.TransactionParameters.int: "transactionParameters",
|
||||
ModelRole.MentionedUsersPks.int: "mentionedUsersPks",
|
||||
ModelRole.SenderTrustStatus.int: "senderTrustStatus"
|
||||
ModelRole.SenderTrustStatus.int: "senderTrustStatus",
|
||||
ModelRole.SenderEnsVerified.int: "senderEnsVerified"
|
||||
}.toTable
|
||||
|
||||
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
||||
|
@ -205,6 +207,8 @@ QtObject:
|
|||
}))
|
||||
of ModelRole.MentionedUsersPks:
|
||||
result = newQVariant(item.mentionedUsersPks.join(" "))
|
||||
of ModelRole.SenderEnsVerified:
|
||||
result = newQVariant(item.senderEnsVerified)
|
||||
|
||||
proc updateItemAtIndex(self: Model, index: int) =
|
||||
let ind = self.createIndex(index, 0, nil)
|
||||
|
@ -393,8 +397,12 @@ QtObject:
|
|||
|
||||
var roles: seq[int]
|
||||
if(self.items[i].senderId == contactId):
|
||||
roles = @[ModelRole.SenderDisplayName.int, ModelRole.SenderLocalName.int,
|
||||
ModelRole.SenderIcon.int, ModelRole.SenderIsAdded.int, ModelRole.SenderTrustStatus.int]
|
||||
roles = @[ModelRole.SenderDisplayName.int,
|
||||
ModelRole.SenderLocalName.int,
|
||||
ModelRole.SenderIcon.int,
|
||||
ModelRole.SenderIsAdded.int,
|
||||
ModelRole.SenderTrustStatus.int,
|
||||
ModelRole.SenderEnsVerified.int]
|
||||
if(self.items[i].pinnedBy == contactId):
|
||||
roles.add(ModelRole.PinnedBy.int)
|
||||
if(self.items[i].messageContainsMentions):
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 9f6f9905e988bb98689a4157dc7ad1fd42ea88c2
|
||||
Subproject commit 6c107b5760fe33ef2fea3cee53a8039610d1024d
|
|
@ -50,8 +50,8 @@ Item {
|
|||
StyledText {
|
||||
id: contactInfo
|
||||
text: realChatType !== Constants.chatType.publicChat ?
|
||||
StatusQUtils.Emoji.parse(Utils.removeStatusEns(Utils.filterXSS(name))) :
|
||||
"#" + Utils.filterXSS(name)
|
||||
StatusQUtils.Emoji.parse(Utils.removeStatusEns(StatusQUtils.Utils.filterXSS(name))) :
|
||||
"#" + StatusQUtils.Utils.filterXSS(name)
|
||||
anchors.left: contactImage.right
|
||||
anchors.leftMargin: 4
|
||||
color: textColor
|
||||
|
|
|
@ -24,7 +24,7 @@ Item {
|
|||
}
|
||||
|
||||
StyledTextEdit {
|
||||
text: Utils.getReplyMessageStyle(StatusQUtils.Emoji.parse(Utils.linkifyAndXSS(repliedMessageContent), StatusQUtils.Emoji.size.small), false)
|
||||
text: Utils.getReplyMessageStyle(StatusQUtils.Emoji.parse(StatusQUtils.Utils.linkifyAndXSS(repliedMessageContent), StatusQUtils.Emoji.size.small), false)
|
||||
textFormat: Text.RichText
|
||||
height: 18
|
||||
width: implicitWidth > 300 ? 300 : implicitWidth
|
||||
|
|
|
@ -10,11 +10,11 @@ StyledText {
|
|||
color: Style.current.secondaryText
|
||||
text: Utils.formatShortTime(timestamp)
|
||||
font.pixelSize: Style.current.asideTextFontSize
|
||||
property string timestamp
|
||||
property int timestamp
|
||||
|
||||
StatusQ.StatusToolTip {
|
||||
visible: hhandler.hovered
|
||||
text: Utils.formatLongDateTime(parseInt(chatTime.timestamp, 10), RootStore.accountSensitiveSettings.isDDMMYYDateFormat, RootStore.accountSensitiveSettings.is24hTimeFormat)
|
||||
text: Utils.formatLongDateTime(chatTime.timestamp, RootStore.accountSensitiveSettings.isDDMMYYDateFormat, RootStore.accountSensitiveSettings.is24hTimeFormat)
|
||||
maxWidth: 350
|
||||
}
|
||||
|
||||
|
|
|
@ -101,14 +101,12 @@ Item {
|
|||
if (mouse.button === Qt.RightButton) {
|
||||
// Set parent, X & Y positions for the messageContextMenu
|
||||
messageContextMenu.parent = this
|
||||
messageContextMenu.setXPosition = function() { return 0; }
|
||||
messageContextMenu.setYPosition = function() { return mouse.y + (Style.current.halfPadding/2); }
|
||||
messageContextMenu.isProfile = true
|
||||
messageContextMenu.myPublicKey = userProfile.pubKey
|
||||
messageContextMenu.selectedUserPublicKey = model.pubKey
|
||||
messageContextMenu.selectedUserDisplayName = model.displayName
|
||||
messageContextMenu.selectedUserIcon = image.source
|
||||
messageContextMenu.popup()
|
||||
messageContextMenu.popup(4, 4)
|
||||
} else if (mouse.button === Qt.LeftButton && !!messageContextMenu) {
|
||||
Global.openProfilePopup(model.pubKey);
|
||||
}
|
||||
|
|
|
@ -98,13 +98,16 @@ ModalPopup {
|
|||
|
||||
delegate: Item {
|
||||
id: messageDelegate
|
||||
property var listView: ListView.view
|
||||
width: parent.width
|
||||
|
||||
width: ListView.view.width
|
||||
height: messageItem.height
|
||||
|
||||
MessageView {
|
||||
id: messageItem
|
||||
store: popup.store
|
||||
|
||||
width: parent.width
|
||||
|
||||
rootStore: popup.store
|
||||
messageStore: popup.messageStore
|
||||
messageContextMenu: msgContextMenu
|
||||
|
||||
|
@ -113,9 +116,10 @@ ModalPopup {
|
|||
senderId: model.senderId
|
||||
senderDisplayName: model.senderDisplayName
|
||||
senderLocalName: model.senderLocalName
|
||||
senderEnsName: model.senderEnsVerified ? model.senderDisplayName : ""
|
||||
senderIcon: model.senderIcon
|
||||
amISender: model.amISender
|
||||
message: model.messageText
|
||||
messageText: model.messageText
|
||||
messageImage: model.messageImage
|
||||
messageTimestamp: model.timestamp
|
||||
messageOutgoingStatus: model.outgoingStatus
|
||||
|
@ -125,7 +129,6 @@ ModalPopup {
|
|||
reactionsModel: model.reactions
|
||||
senderTrustStatus: model.senderTrustStatus
|
||||
linkUrls: model.links
|
||||
isInPinnedPopup: true
|
||||
transactionParams: model.transactionParameters
|
||||
|
||||
// This is possible since we have all data loaded before we load qml.
|
||||
|
@ -136,7 +139,8 @@ ModalPopup {
|
|||
nextMessageAsJsonObj: popup.messageStore? popup.messageStore.getMessageByIndexAsJson(index + 1) : {}
|
||||
|
||||
// Additional params
|
||||
forceHoverHandler: !popup.messageToPin
|
||||
isInPinnedPopup: true
|
||||
disableHover: !!popup.messageToPin
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
|
|
|
@ -4,6 +4,7 @@ import QtQuick.Dialogs 1.3
|
|||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Controls.Validators 0.1
|
||||
|
@ -220,9 +221,9 @@ StatusModal {
|
|||
|
||||
let error = ""
|
||||
if (isEdit) {
|
||||
error = root.store.editCommunityCategory(root.categoryId, Utils.filterXSS(root.contentItem.categoryName.input.text), JSON.stringify(channels));
|
||||
error = root.store.editCommunityCategory(root.categoryId, StatusQUtils.Utils.filterXSS(root.contentItem.categoryName.input.text), JSON.stringify(channels));
|
||||
} else {
|
||||
error = root.store.createCommunityCategory(Utils.filterXSS(root.contentItem.categoryName.input.text), JSON.stringify(channels));
|
||||
error = root.store.createCommunityCategory(StatusQUtils.Utils.filterXSS(root.contentItem.categoryName.input.text), JSON.stringify(channels));
|
||||
}
|
||||
|
||||
if (error) {
|
||||
|
|
|
@ -303,14 +303,14 @@ StatusDialog {
|
|||
|
||||
if (!isEdit) {
|
||||
//scrollView.communityColor.color.toString().toUpperCase()
|
||||
root.createCommunityChannel(Utils.filterXSS(nameInput.input.text),
|
||||
Utils.filterXSS(descriptionTextArea.text),
|
||||
root.createCommunityChannel(StatusQUtils.Utils.filterXSS(nameInput.input.text),
|
||||
StatusQUtils.Utils.filterXSS(descriptionTextArea.text),
|
||||
emoji,
|
||||
colorDialog.color.toString().toUpperCase(),
|
||||
root.categoryId)
|
||||
} else {
|
||||
root.editCommunityChannel(Utils.filterXSS(nameInput.input.text),
|
||||
Utils.filterXSS(descriptionTextArea.text),
|
||||
root.editCommunityChannel(StatusQUtils.Utils.filterXSS(nameInput.input.text),
|
||||
StatusQUtils.Utils.filterXSS(descriptionTextArea.text),
|
||||
emoji,
|
||||
colorDialog.color.toString().toUpperCase(),
|
||||
root.categoryId)
|
||||
|
|
|
@ -4,10 +4,10 @@ import QtQuick.Layouts 1.14
|
|||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Core.Utils 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
|
||||
import utils 1.0
|
||||
import shared.controls 1.0
|
||||
import shared 1.0
|
||||
|
||||
|
|
|
@ -135,49 +135,6 @@ QtObject {
|
|||
return messageModule.toggleReaction(messageId, emojiId)
|
||||
}
|
||||
|
||||
function lastTwoItems(nodes) {
|
||||
return nodes.join(qsTr(" and "));
|
||||
}
|
||||
|
||||
function showReactionAuthors(jsonArrayOfUsersReactedWithThisEmoji, emojiId) {
|
||||
let listOfUsers = JSON.parse(jsonArrayOfUsersReactedWithThisEmoji)
|
||||
if (listOfUsers.error) {
|
||||
console.error("error parsing users who reacted to a message, error: ", obj.error)
|
||||
return
|
||||
}
|
||||
|
||||
let tooltip
|
||||
if (listOfUsers.length === 1) {
|
||||
tooltip = listOfUsers[0]
|
||||
} else if (listOfUsers.length === 2) {
|
||||
tooltip = lastTwoItems(listOfUsers);
|
||||
} else {
|
||||
var leftNode = [];
|
||||
var rightNode = [];
|
||||
const maxReactions = 12
|
||||
let maximum = Math.min(maxReactions, listOfUsers.length)
|
||||
|
||||
if (listOfUsers.length > maxReactions) {
|
||||
leftNode = listOfUsers.slice(0, maxReactions);
|
||||
rightNode = listOfUsers.slice(maxReactions, listOfUsers.length);
|
||||
return (rightNode.length === 1) ?
|
||||
lastTwoItems([leftNode.join(", "), rightNode[0]]) :
|
||||
lastTwoItems([leftNode.join(", "), qsTr("%1 more").arg(rightNode.length)]);
|
||||
}
|
||||
|
||||
leftNode = listOfUsers.slice(0, maximum - 1);
|
||||
rightNode = listOfUsers.slice(maximum - 1, listOfUsers.length);
|
||||
tooltip = lastTwoItems([leftNode.join(", "), rightNode[0]])
|
||||
}
|
||||
|
||||
tooltip += qsTr(" reacted with ");
|
||||
let emojiHtml = StatusQUtils.Emoji.getEmojiFromId(emojiId);
|
||||
if (emojiHtml) {
|
||||
tooltip += emojiHtml;
|
||||
}
|
||||
return tooltip
|
||||
}
|
||||
|
||||
function deleteMessage(messageId) {
|
||||
if(!messageModule)
|
||||
return
|
||||
|
|
|
@ -12,6 +12,7 @@ import "../controls"
|
|||
import "../panels"
|
||||
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
Item {
|
||||
|
@ -20,22 +21,26 @@ Item {
|
|||
height: childrenRect.height + dateGroupLbl.anchors.topMargin
|
||||
property var store
|
||||
property int previousNotificationIndex
|
||||
property string previousNotificationTimestamp
|
||||
property int previousNotificationTimestamp
|
||||
property bool hideReadNotifications: false
|
||||
property bool acCurrentFilterAll: false
|
||||
|
||||
DateGroup {
|
||||
StatusDateGroupLabel {
|
||||
id: dateGroupLbl
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Style.current.halfPadding
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding
|
||||
height: visible ? implicitHeight : 0
|
||||
visible: text !== ""
|
||||
previousMessageIndex: root.previousNotificationIndex
|
||||
previousMessageTimestamp: root.previousNotificationTimestamp
|
||||
messageTimestamp: model.timestamp
|
||||
isActivityCenterMessage: true
|
||||
height: visible ? implicitHeight : 0
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: groupRequestContent
|
||||
property string timestamp: model.timestamp
|
||||
property int timestamp: model.timestamp
|
||||
|
||||
visible: {
|
||||
if (hideReadNotifications && model.read) {
|
||||
|
|
|
@ -124,11 +124,11 @@ Item {
|
|||
MessageView {
|
||||
id: notificationMessage
|
||||
anchors.right: undefined
|
||||
store: root.store
|
||||
rootStore: root.store
|
||||
messageStore: root.store.messageStore
|
||||
messageId: model.id
|
||||
senderDisplayName: model.message.senderDisplayName
|
||||
message: model.message.messageText
|
||||
messageText: model.message.messageText
|
||||
responseToMessageWithId: model.message.responseToMessageWithId
|
||||
senderId: model.message.senderId
|
||||
senderLocalName: model.message.senderLocalName
|
||||
|
@ -140,10 +140,10 @@ Item {
|
|||
messageContentType: model.message.contentType
|
||||
senderTrustStatus: model.message.senderTrustStatus
|
||||
activityCenterMessage: true
|
||||
read: model.read
|
||||
activityCenterMessageRead: model.read
|
||||
onImageClicked: Global.openImagePopup(image, root.messageContextMenu)
|
||||
scrollToBottom: null
|
||||
clickMessage: function (isProfileClick) {
|
||||
messageClickHandler: {
|
||||
if (isProfileClick) {
|
||||
return Global.openProfilePopup(model.message.senderId);
|
||||
}
|
||||
|
|
|
@ -91,11 +91,10 @@ ColumnLayout {
|
|||
case Constants.chatType.publicChat:
|
||||
return qsTr("Public chat")
|
||||
case Constants.chatType.privateGroupChat:
|
||||
let cnt = root.usersStore.usersModule.model.count
|
||||
if(cnt > 1) return qsTr("%1 members").arg(cnt);
|
||||
return qsTr("1 member");
|
||||
const cnt = root.usersStore.usersModule.model.count
|
||||
return qsTr("%n members(s)", "", cnt)
|
||||
case Constants.chatType.communityChat:
|
||||
return Utils.linkifyAndXSS(chatContentModule.chatDetails.description).trim()
|
||||
return StatusQUtils.Utils.linkifyAndXSS(chatContentModule.chatDetails.description).trim()
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
@ -161,10 +160,11 @@ ColumnLayout {
|
|||
Component {
|
||||
id: contactsSelector
|
||||
GroupChatPanel {
|
||||
sectionModule: root.chatSectionModule
|
||||
sectionModule: chatSectionModule
|
||||
chatContentModule: root.chatContentModule
|
||||
rootStore: root.rootStore
|
||||
maxHeight: root.height
|
||||
|
||||
onPanelClosed: topBar.toolbarComponent = statusChatInfoButton
|
||||
}
|
||||
}
|
||||
|
@ -429,15 +429,16 @@ ColumnLayout {
|
|||
id: chatMessages
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
store: root.rootStore
|
||||
chatContentModule: root.chatContentModule
|
||||
rootStore: root.rootStore
|
||||
contactsStore: root.contactsStore
|
||||
messageContextMenuInst: contextmenu
|
||||
messageContextMenu: contextmenu
|
||||
messageStore: messageStore
|
||||
emojiPopup: root.emojiPopup
|
||||
usersStore: root.usersStore
|
||||
stickersLoaded: root.stickersLoaded
|
||||
isChatBlocked: root.isBlocked
|
||||
channelEmoji: chatContentModule.chatDetails.emoji || ""
|
||||
channelEmoji: !chatContentModule ? "" : (chatContentModule.chatDetails.emoji || "")
|
||||
isActiveChannel: root.isActiveChannel
|
||||
onShowReplyArea: {
|
||||
let obj = messageStore.getMessageByIdAsJson(messageId)
|
||||
|
@ -455,9 +456,9 @@ ColumnLayout {
|
|||
id: inputArea
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: parent.width
|
||||
height: chatInput.height
|
||||
Layout.preferredHeight: height
|
||||
Layout.preferredHeight: chatInput.implicitHeight
|
||||
+ chatInput.anchors.topMargin
|
||||
+ chatInput.anchors.bottomMargin
|
||||
|
||||
Loader {
|
||||
id: loadingMessagesIndicator
|
||||
|
@ -474,6 +475,10 @@ ColumnLayout {
|
|||
|
||||
StatusChatInput {
|
||||
id: chatInput
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.current.smallPadding
|
||||
|
||||
store: root.rootStore
|
||||
usersStore: root.usersStore
|
||||
|
||||
|
|
|
@ -19,12 +19,18 @@ import shared.status 1.0
|
|||
import shared.controls 1.0
|
||||
import shared.views.chat 1.0
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Components 0.1
|
||||
|
||||
import "../controls"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property var store
|
||||
property var chatContentModule
|
||||
property var rootStore
|
||||
property var messageStore
|
||||
property var usersStore
|
||||
property var contactsStore
|
||||
|
@ -37,7 +43,7 @@ Item {
|
|||
property bool isChatBlocked: false
|
||||
property bool isActiveChannel: false
|
||||
|
||||
property var messageContextMenuInst
|
||||
property var messageContextMenu
|
||||
|
||||
property real scrollY: chatLogView.visibleArea.yPosition * chatLogView.contentHeight
|
||||
property int newMessages: 0
|
||||
|
@ -67,23 +73,23 @@ Item {
|
|||
}
|
||||
|
||||
// Not Refactored Yet
|
||||
// onNewMessagePushed: {
|
||||
// if (!chatLogView.scrollToBottom()) {
|
||||
// newMessages++
|
||||
// }
|
||||
// }
|
||||
// onNewMessagePushed: {
|
||||
// if (!chatLogView.scrollToBottom()) {
|
||||
// newMessages++
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
Item {
|
||||
id: loadingMessagesIndicator
|
||||
visible: root.store.loadingHistoryMessagesInProgress
|
||||
visible: root.rootStore.loadingHistoryMessagesInProgress
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
height: visible? 20 : 0
|
||||
width: parent.width
|
||||
|
||||
Loader {
|
||||
active: root.store.loadingHistoryMessagesInProgress
|
||||
active: root.rootStore.loadingHistoryMessagesInProgress
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
sourceComponent: Component {
|
||||
|
@ -105,14 +111,6 @@ Item {
|
|||
spacing: 0
|
||||
verticalLayoutDirection: ListView.BottomToTop
|
||||
|
||||
// This header and Connections is to create an invisible padding so that the chat identifier is at the top
|
||||
// The Connections is necessary, because doing the check inside the header created a binding loop (the contentHeight includes the header height
|
||||
// If the content height is smaller than the full height, we "show" the padding so that the chat identifier is at the top, otherwise we disable the Connections
|
||||
header: Item {
|
||||
height: 0
|
||||
width: chatLogView.width
|
||||
}
|
||||
|
||||
function checkHeaderHeight() {
|
||||
if (!chatLogView.headerItem) {
|
||||
return
|
||||
|
@ -125,30 +123,70 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
function scrollToBottom(force, caller) {
|
||||
if (!force && !chatLogView.atYEnd) {
|
||||
// User has scrolled up, we don't want to scroll back
|
||||
return false
|
||||
}
|
||||
if (caller && caller !== chatLogView.itemAtIndex(chatLogView.count - 1)) {
|
||||
// If we have a caller, only accept its request if it's the last message
|
||||
return false
|
||||
}
|
||||
// Call this twice and with a timer since the first scroll to bottom might have happened before some stuff loads
|
||||
// meaning that the scroll will not actually be at the bottom on switch
|
||||
// Add a small delay because images, even though they say they say they are loaed, they aren't shown yet
|
||||
Qt.callLater(chatLogView.positionViewAtBeginning)
|
||||
timer.setTimeout(function() {
|
||||
Qt.callLater(chatLogView.positionViewAtBeginning)
|
||||
}, 100);
|
||||
return true
|
||||
}
|
||||
|
||||
model: messageStore.messagesModel
|
||||
|
||||
Component.onCompleted: chatLogView.scrollToBottom(true)
|
||||
|
||||
onContentYChanged: {
|
||||
scrollDownButton.visible = contentHeight - (scrollY + height) > 400
|
||||
let loadMore = scrollDownButton.visible && scrollY < 500
|
||||
if(loadMore){
|
||||
messageStore.loadMoreMessages()
|
||||
}
|
||||
}
|
||||
|
||||
ScrollBar.vertical: StatusScrollBar {
|
||||
visible: chatLogView.visibleArea.heightRatio < 1
|
||||
}
|
||||
|
||||
// Connections {
|
||||
// id: contentHeightConnection
|
||||
// enabled: true
|
||||
// target: chatLogView
|
||||
// onContentHeightChanged: {
|
||||
// chatLogView.checkHeaderHeight()
|
||||
// }
|
||||
// onHeightChanged: {
|
||||
// chatLogView.checkHeaderHeight()
|
||||
// }
|
||||
// }
|
||||
// This header and Connections is to create an invisible padding so that the chat identifier is at the top
|
||||
// The Connections is necessary, because doing the check inside the header created a binding loop (the contentHeight includes the header height
|
||||
// If the content height is smaller than the full height, we "show" the padding so that the chat identifier is at the top, otherwise we disable the Connections
|
||||
header: Item {
|
||||
height: 0
|
||||
width: chatLogView.width
|
||||
}
|
||||
|
||||
// Connections {
|
||||
// id: contentHeightConnection
|
||||
// enabled: true
|
||||
// target: chatLogView
|
||||
// onContentHeightChanged: {
|
||||
// chatLogView.checkHeaderHeight()
|
||||
// }
|
||||
// onHeightChanged: {
|
||||
// chatLogView.checkHeaderHeight()
|
||||
// }
|
||||
// }
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
}
|
||||
|
||||
Button {
|
||||
id: scrollDownButton
|
||||
|
||||
readonly property int buttonPadding: 5
|
||||
|
||||
id: scrollDownButton
|
||||
visible: false
|
||||
height: 32
|
||||
width: nbMessages.width + arrowImage.width + 2 * Style.current.halfPadding + (nbMessages.visible ? scrollDownButton.buttonPadding : 0)
|
||||
|
@ -160,6 +198,7 @@ Item {
|
|||
border.width: 0
|
||||
radius: 16
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
newMessages = 0
|
||||
scrollDownButton.visible = false
|
||||
|
@ -202,50 +241,23 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
function scrollToBottom(force, caller) {
|
||||
if (!force && !chatLogView.atYEnd) {
|
||||
// User has scrolled up, we don't want to scroll back
|
||||
return false
|
||||
}
|
||||
if (caller && caller !== chatLogView.itemAtIndex(chatLogView.count - 1)) {
|
||||
// If we have a caller, only accept its request if it's the last message
|
||||
return false
|
||||
}
|
||||
// Call this twice and with a timer since the first scroll to bottom might have happened before some stuff loads
|
||||
// meaning that the scroll will not actually be at the bottom on switch
|
||||
// Add a small delay because images, even though they say they say they are loaed, they aren't shown yet
|
||||
Qt.callLater(chatLogView.positionViewAtBeginning)
|
||||
timer.setTimeout(function() {
|
||||
Qt.callLater(chatLogView.positionViewAtBeginning)
|
||||
}, 100);
|
||||
return true
|
||||
}
|
||||
|
||||
// Connections {
|
||||
// Connections {
|
||||
// Not Refactored Yet
|
||||
// target: root.store.chatsModelInst
|
||||
// target: root.rootStore.chatsModelInst
|
||||
|
||||
// onAppReady: {
|
||||
// chatLogView.scrollToBottom(true)
|
||||
// }
|
||||
// }
|
||||
|
||||
onContentYChanged: {
|
||||
scrollDownButton.visible = contentHeight - (scrollY + height) > 400
|
||||
let loadMore = scrollDownButton.visible && scrollY < 500
|
||||
if(loadMore){
|
||||
messageStore.loadMoreMessages()
|
||||
}
|
||||
}
|
||||
|
||||
model: messageStore.messagesModel
|
||||
|
||||
Component.onCompleted: chatLogView.scrollToBottom(true)
|
||||
// onAppReady: {
|
||||
// chatLogView.scrollToBottom(true)
|
||||
// }
|
||||
// }
|
||||
|
||||
delegate: MessageView {
|
||||
id: msgDelegate
|
||||
|
||||
width: ListView.view.width
|
||||
height: implicitHeight
|
||||
|
||||
objectName: "chatMessageViewDelegate"
|
||||
store: root.store
|
||||
rootStore: root.rootStore
|
||||
messageStore: root.messageStore
|
||||
usersStore: root.usersStore
|
||||
contactsStore: root.contactsStore
|
||||
|
@ -255,7 +267,7 @@ Item {
|
|||
|
||||
isActiveChannel: root.isActiveChannel
|
||||
isChatBlocked: root.isChatBlocked
|
||||
messageContextMenu: messageContextMenuInst
|
||||
messageContextMenu: root.messageContextMenu
|
||||
|
||||
itemIndex: index
|
||||
messageId: model.id
|
||||
|
@ -264,10 +276,11 @@ Item {
|
|||
senderId: model.senderId
|
||||
senderDisplayName: model.senderDisplayName
|
||||
senderLocalName: model.senderLocalName
|
||||
senderEnsName: model.senderEnsVerified ? model.senderDisplayName : ""
|
||||
senderIcon: model.senderIcon
|
||||
senderIsAdded: model.senderIsAdded
|
||||
amISender: model.amISender
|
||||
message: model.messageText
|
||||
messageText: model.messageText
|
||||
messageImage: model.messageImage
|
||||
messageTimestamp: model.timestamp
|
||||
messageOutgoingStatus: model.outgoingStatus
|
||||
|
@ -282,6 +295,7 @@ Item {
|
|||
isEdited: model.isEdited
|
||||
linkUrls: model.links
|
||||
transactionParams: model.transactionParameters
|
||||
hasMention: model.mentionedUsersPks.split(" ").includes(root.rootStore.userProfileInst.pubKey)
|
||||
|
||||
gapFrom: model.gapFrom
|
||||
gapTo: model.gapTo
|
||||
|
@ -296,6 +310,7 @@ Item {
|
|||
prevMsgTimestamp: model.prevMsgTimestamp
|
||||
nextMessageIndex: model.nextMsgIndex
|
||||
nextMessageAsJsonObj: messageStore.getMessageByIndexAsJson(model.nextMsgIndex)
|
||||
|
||||
onOpenStickerPackPopup: {
|
||||
root.openStickerPackPopup(stickerPackId);
|
||||
}
|
||||
|
@ -304,7 +319,7 @@ Item {
|
|||
root.showReplyArea(messageId, author)
|
||||
}
|
||||
|
||||
onImageClicked: Global.openImagePopup(image, messageContextMenuInst)
|
||||
onImageClicked: Global.openImagePopup(image, messageContextMenu)
|
||||
|
||||
stickersLoaded: root.stickersLoaded
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import shared.popups 1.0
|
|||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
||||
import StatusQ.Layout 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
|
@ -148,10 +149,10 @@ StatusAppTwoPanelLayout {
|
|||
|
||||
onEdited: {
|
||||
const error = root.chatCommunitySectionModule.editCommunity(
|
||||
Utils.filterXSS(item.name),
|
||||
Utils.filterXSS(item.description),
|
||||
Utils.filterXSS(item.introMessage),
|
||||
Utils.filterXSS(item.outroMessage),
|
||||
StatusQUtils.Utils.filterXSS(item.name),
|
||||
StatusQUtils.Utils.filterXSS(item.description),
|
||||
StatusQUtils.Utils.filterXSS(item.introMessage),
|
||||
StatusQUtils.Utils.filterXSS(item.outroMessage),
|
||||
item.options.requestToJoinEnabled ? Constants.communityChatOnRequestAccess : Constants.communityChatPublicAccess,
|
||||
item.color.toString().toUpperCase(),
|
||||
item.selectedTags,
|
||||
|
|
|
@ -9,6 +9,7 @@ import shared.popups 1.0
|
|||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Controls.Validators 0.1
|
||||
|
@ -160,10 +161,10 @@ StatusStackModal {
|
|||
|
||||
function createCommunity() {
|
||||
const error = store.createCommunity({
|
||||
name: Utils.filterXSS(nameInput.input.text),
|
||||
description: Utils.filterXSS(descriptionTextInput.input.text),
|
||||
introMessage: Utils.filterXSS(introMessageInput.input.text),
|
||||
outroMessage: Utils.filterXSS(outroMessageInput.input.text),
|
||||
name: StatusQUtils.Utils.filterXSS(nameInput.input.text),
|
||||
description: StatusQUtils.Utils.filterXSS(descriptionTextInput.input.text),
|
||||
introMessage: StatusQUtils.Utils.filterXSS(introMessageInput.input.text),
|
||||
outroMessage: StatusQUtils.Utils.filterXSS(outroMessageInput.input.text),
|
||||
color: colorPicker.color.toString().toUpperCase(),
|
||||
tags: communityTagsPicker.selectedTags,
|
||||
image: {
|
||||
|
|
|
@ -56,23 +56,25 @@ SettingsContentBase {
|
|||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: paceholderMessage.height + paceholderMessage.anchors.margins*2
|
||||
height: placeholderMessage.implicitHeight +
|
||||
placeholderMessage.anchors.leftMargin +
|
||||
placeholderMessage.anchors.rightMargin
|
||||
radius: Style.current.radius
|
||||
border.color: Style.current.border
|
||||
color: Style.current.transparent
|
||||
|
||||
MessageView {
|
||||
id: paceholderMessage
|
||||
id: placeholderMessage
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Style.current.smallPadding
|
||||
isMessage: true
|
||||
shouldRepeatHeader: true
|
||||
messageTimestamp:Date.now()
|
||||
messageTimestamp: Date.now()
|
||||
senderDisplayName: "@vitalik"
|
||||
senderIcon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAb0lEQVR4Ae3UQQqAIBRF0Wj9ba9Bq6l5JBQqfn/ngDMH3YS3AAB/tO3H+XRG3b9bR/+gVoREI2RapVXpfd5+X5oXERKNkHS+rk3tOpWkeREh0QiZVu91ql2zNC8iJBoh0yqtSqt1slpCghICANDPBc0ESPh0bHkHAAAAAElFTkSuQmCC"
|
||||
message: qsTr("Blockchains will drop search costs, causing a kind of decomposition that allows you to have markets of entities that are horizontally segregated and vertically segregated.")
|
||||
messageText: qsTr("Blockchains will drop search costs, causing a kind of decomposition that allows you to have markets of entities that are horizontally segregated and vertically segregated.")
|
||||
messageContentType: Constants.messageContentType.messageType
|
||||
placeholderMessage: true
|
||||
}
|
||||
|
|
|
@ -130,56 +130,4 @@ QtObject {
|
|||
return []
|
||||
}
|
||||
}
|
||||
property var clickMessage: function(isProfileClick, isSticker = false, isImage = false, image = null, isEmoji = false, hideEmojiPicker = false, isReply = false, isRightClickOnImage = false, imageSource = "") {
|
||||
if (placeholderMessage || activityCenterMessage) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!isProfileClick) {
|
||||
SelectedMessage.set(messageId, fromAuthor);
|
||||
}
|
||||
|
||||
messageContextMenu.messageId = messageId
|
||||
messageContextMenu.contentType = contentType
|
||||
messageContextMenu.linkUrls = linkUrls;
|
||||
messageContextMenu.isProfile = !!isProfileClick;
|
||||
messageContextMenu.isCurrentUser = isCurrentUser
|
||||
messageContextMenu.isText = isText
|
||||
messageContextMenu.isSticker = isSticker;
|
||||
messageContextMenu.isEmoji = isEmoji;
|
||||
messageContextMenu.hideEmojiPicker = hideEmojiPicker;
|
||||
messageContextMenu.pinnedMessage = pinnedMessage;
|
||||
messageContextMenu.isCurrentUser = isCurrentUser;
|
||||
messageContextMenu.isRightClickOnImage = isRightClickOnImage
|
||||
messageContextMenu.imageSource = imageSource
|
||||
messageContextMenu.onClickEdit = function() {isEdit = true}
|
||||
|
||||
//TODO remove dynamic scoping
|
||||
if (isReply) {
|
||||
let nickname = appMain.getUserNickname(repliedMessageAuthor)
|
||||
messageContextMenu.show(repliedMessageAuthor, repliedMessageAuthorPubkey, repliedMessageUserImage, plainText, nickname, emojiReactionsModel);
|
||||
} else {
|
||||
let nickname = appMain.getUserNickname(fromAuthor)
|
||||
messageContextMenu.show(userName, fromAuthor, profileImageSource, plainText, nickname, emojiReactionsModel);
|
||||
}
|
||||
|
||||
messageContextMenu.x = messageContextMenu.setXPosition()
|
||||
messageContextMenu.y = messageContextMenu.setYPosition()
|
||||
}
|
||||
|
||||
function setHovered(messageId, hovered) {
|
||||
if (hovered) {
|
||||
hoveredMessage = messageId;
|
||||
} else if (hoveredMessage === messageId) {
|
||||
hoveredMessage = "";
|
||||
}
|
||||
}
|
||||
|
||||
function setMessageActive(messageId, active) {
|
||||
if (active) {
|
||||
activeMessage = messageId;
|
||||
} else if (activeMessage === messageId) {
|
||||
activeMessage = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ Rectangle {
|
|||
}
|
||||
StyledText {
|
||||
id: timeValue
|
||||
text: Utils.formatLongDateTime(parseInt(timestamp) * 1000, RootStore.accountSensitiveSettings.isDDMMYYDateFormat, RootStore.accountSensitiveSettings.is24hTimeFormat)
|
||||
text: Utils.formatLongDateTime(timestamp * 1000, RootStore.accountSensitiveSettings.isDDMMYYDateFormat, RootStore.accountSensitiveSettings.is24hTimeFormat)
|
||||
font.pixelSize: Style.current.primaryTextFontSize
|
||||
anchors.rightMargin: Style.current.smallPadding
|
||||
}
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
import QtQuick 2.3
|
||||
|
||||
import shared 1.0
|
||||
import shared.panels 1.0
|
||||
import utils 1.0
|
||||
|
||||
StyledText {
|
||||
property bool isActivityCenterMessage: false
|
||||
property int previousMessageIndex: -1
|
||||
property string previousMessageTimestamp
|
||||
property string messageTimestamp
|
||||
|
||||
id: dateGroupLbl
|
||||
font.pixelSize: 13
|
||||
color: Style.current.secondaryText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.horizontalCenter: isActivityCenterMessage ? undefined : parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: visible ? (isActivityCenterMessage ? Style.current.halfPadding : 20) : 0
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: isActivityCenterMessage ? Style.current.padding : 0
|
||||
|
||||
text: {
|
||||
if (previousMessageIndex === -1) return ""; // identifier
|
||||
|
||||
let now = new Date()
|
||||
let yesterday = new Date()
|
||||
yesterday.setDate(now.getDate()-1)
|
||||
|
||||
let currentMsgDate = new Date(parseInt(messageTimestamp, 10));
|
||||
let prevMsgDate = previousMessageTimestamp === "" ? undefined : new Date(parseInt(previousMessageTimestamp, 10));
|
||||
|
||||
if (!!prevMsgDate && currentMsgDate.getDay() === prevMsgDate.getDay()) {
|
||||
return ""
|
||||
}
|
||||
|
||||
if (now.toDateString() === currentMsgDate.toDateString()) {
|
||||
return qsTr("Today")
|
||||
} else if (yesterday.toDateString() === currentMsgDate.toDateString()) {
|
||||
return qsTr("Yesterday")
|
||||
} else {
|
||||
const monthNames = [
|
||||
qsTr("January"),
|
||||
qsTr("February"),
|
||||
qsTr("March"),
|
||||
qsTr("April"),
|
||||
qsTr("May"),
|
||||
qsTr("June"),
|
||||
qsTr("July"),
|
||||
qsTr("August"),
|
||||
qsTr("September"),
|
||||
qsTr("October"),
|
||||
qsTr("November"),
|
||||
qsTr("December")
|
||||
];
|
||||
return monthNames[currentMsgDate.getMonth()] + ", " + currentMsgDate.getDate()
|
||||
}
|
||||
}
|
||||
visible: text !== ""
|
||||
}
|
|
@ -8,19 +8,18 @@ import utils 1.0
|
|||
|
||||
Item {
|
||||
id: root
|
||||
height: childrenRect.height + Style.current.smallPadding * 2
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
property int nextMessageIndex
|
||||
property string nextMsgTimestamp
|
||||
property double nextMsgTimestamp
|
||||
|
||||
signal clicked()
|
||||
signal timerTriggered()
|
||||
|
||||
implicitHeight: childrenRect.height + Style.current.smallPadding * 2
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
readonly property string formattedDate: nextMessageIndex > -1 ? Utils.formatLongDate(nextMsgTimestamp * 1, RootStore.accountSensitiveSettings.isDDMMYYDateFormat) :
|
||||
readonly property string formattedDate: nextMessageIndex > -1 ? Utils.formatLongDate(nextMsgTimestamp, RootStore.accountSensitiveSettings.isDDMMYYDateFormat) :
|
||||
Utils.formatLongDate(undefined, RootStore.accountSensitiveSettings.isDDMMYYDateFormat)
|
||||
}
|
||||
|
||||
|
|
|
@ -12,9 +12,7 @@ Item {
|
|||
|
||||
signal clicked()
|
||||
|
||||
height: childrenRect.height + Style.current.smallPadding * 2
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: childrenRect.height + Style.current.smallPadding * 2
|
||||
|
||||
Separator {
|
||||
id: sep1
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
|
||||
import utils 1.0
|
||||
import shared 1.0
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
z: 50
|
||||
enabled: !placeholderMessage
|
||||
//TODO remove dynamic scoping
|
||||
// property bool isSticker: false
|
||||
// property bool placeholderMessage: false
|
||||
property bool isHovered: false
|
||||
property bool stickersLoaded: false
|
||||
property bool isMessageActive
|
||||
property bool isActivityCenterMessage: false
|
||||
property var messageContextMenu
|
||||
property var messageContextMenuParent
|
||||
signal openStickerPackPopup()
|
||||
signal setMessageActive(string messageId, bool active)
|
||||
signal clickMessage(bool isProfileClick, bool isSticker, bool isImage)
|
||||
|
||||
cursorShape: !enabled ? Qt.PointingHandCursor : undefined
|
||||
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
onClicked: {
|
||||
if (isActivityCenterMessage) {
|
||||
mouseArea.clickMessage(false, isSticker, false)
|
||||
return
|
||||
}
|
||||
if (mouse.button === Qt.RightButton) {
|
||||
if (!!mouseArea.messageContextMenu) {
|
||||
// Set parent, X & Y positions for the messageContextMenu
|
||||
messageContextMenu.parent = messageContextMenuParent;
|
||||
messageContextMenu.setXPosition = function() { return (mouse.x)};
|
||||
messageContextMenu.setYPosition = function() { return (mouse.y)};
|
||||
}
|
||||
mouseArea.clickMessage(false, isSticker, false)
|
||||
if (typeof isMessageActive !== "undefined") {
|
||||
setMessageActive(messageId, true)
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (mouse.button === Qt.LeftButton && isSticker && stickersLoaded) {
|
||||
if (isHovered) {
|
||||
isHovered = false;
|
||||
}
|
||||
openStickerPackPopup();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
import QtQuick 2.3
|
||||
|
||||
import shared 1.0
|
||||
import shared.panels 1.0
|
||||
import utils 1.0
|
||||
|
||||
StyledText {
|
||||
id: retryLbl
|
||||
color: Style.current.red
|
||||
text: qsTr("Resend")
|
||||
font.pixelSize: Style.current.tertiaryTextFontSize
|
||||
visible: isCurrentUser && (timeout || isExpired)
|
||||
property bool isCurrentUser: false
|
||||
property bool isExpired: false
|
||||
property bool timeout: false
|
||||
signal clicked()
|
||||
MouseArea {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
retryLbl.clicked();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,7 +18,6 @@ Loader {
|
|||
property string image
|
||||
property bool showRing: true
|
||||
property bool interactive: true
|
||||
property var messageContextMenu
|
||||
|
||||
property int colorId: Utils.colorIdForPubkey(pubkey)
|
||||
property var colorHash: Utils.getColorHashAsJson(pubkey)
|
||||
|
@ -51,12 +50,6 @@ Loader {
|
|||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
if (!!root.messageContextMenu) {
|
||||
// Set parent, X & Y positions for the messageContextMenu
|
||||
root.messageContextMenu.parent = root
|
||||
root.messageContextMenu.setXPosition = function() { return root.width + 4 }
|
||||
root.messageContextMenu.setYPosition = function() { return 0 }
|
||||
}
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ Item {
|
|||
width: chatName.width + (ensOrAlias.visible ? ensOrAlias.width + ensOrAlias.anchors.leftMargin : 0)
|
||||
property alias label: chatName
|
||||
|
||||
property var messageContextMenu
|
||||
property string displayName
|
||||
property string localName
|
||||
property bool amISender
|
||||
|
@ -41,12 +40,6 @@ Item {
|
|||
root.isHovered = false
|
||||
}
|
||||
onClicked: {
|
||||
if (!!root.messageContextMenu) {
|
||||
// Set parent, X & Y positions for the messageContextMenu
|
||||
root.messageContextMenu.parent = root
|
||||
root.messageContextMenu.setXPosition = function() { return 0}
|
||||
root.messageContextMenu.setYPosition = function() { return root.height + 4}
|
||||
}
|
||||
root.clickMessage(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,111 +0,0 @@
|
|||
import QtQuick 2.3
|
||||
import QtMultimedia 5.14
|
||||
import shared 1.0
|
||||
import shared.panels 1.0
|
||||
import shared.stores 1.0
|
||||
|
||||
import utils 1.0
|
||||
|
||||
Item {
|
||||
property string audioSource: ""
|
||||
|
||||
height: 20
|
||||
width: 350
|
||||
|
||||
Audio {
|
||||
id: audioMessage
|
||||
source: audioSource
|
||||
store: RootStore
|
||||
notifyInterval: 150
|
||||
}
|
||||
|
||||
SVGImage {
|
||||
id: playButton
|
||||
source: audioMessage.playbackState == Audio.PlayingState ? Style.svg("icon-pause") : Style.svg("icon-play")
|
||||
width: 15
|
||||
height: 15
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
MouseArea {
|
||||
id: playArea
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
if(audioMessage.playbackState === Audio.PlayingState){
|
||||
audioMessage.pause();
|
||||
} else {
|
||||
audioMessage.play();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
height: 2
|
||||
width: 300
|
||||
color: Style.current.grey
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: playButton.right
|
||||
anchors.leftMargin: 20
|
||||
Rectangle {
|
||||
id: progress
|
||||
height: 2
|
||||
width: {
|
||||
if(audioMessage.duration === 0) return 0;
|
||||
if(audioMessage.playbackState === Audio.StoppedState) return 0;
|
||||
return parent.width * audioMessage.position / audioMessage.duration;
|
||||
}
|
||||
color: Style.current.black
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: handle
|
||||
width: 10
|
||||
height: 10
|
||||
color: Style.current.black
|
||||
radius: 10
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: progress.width
|
||||
state: "default"
|
||||
|
||||
states: State {
|
||||
name: "pressed"
|
||||
when: handleMouseArea.pressed
|
||||
PropertyChanges {
|
||||
target: handle;
|
||||
scale: 1.2
|
||||
}
|
||||
}
|
||||
transitions: Transition {
|
||||
NumberAnimation {
|
||||
properties: "scale";
|
||||
duration: 100;
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: handleMouseArea
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
drag.target: parent
|
||||
drag.axis: Drag.XAxis
|
||||
drag.minimumX: 0
|
||||
drag.maximumX: parent.parent.width
|
||||
onPressed: {
|
||||
handle.state = "pressed"
|
||||
if(audioMessage.playbackState === Audio.PlayingState){
|
||||
audioMessage.pause();
|
||||
}
|
||||
}
|
||||
onReleased: {
|
||||
handle.state = "default"
|
||||
audioMessage.seek(audioMessage.duration * handle.x / parent.parent.width);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,213 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtGraphicalEffects 1.13
|
||||
|
||||
import StatusQ.Controls 0.1
|
||||
|
||||
import utils 1.0
|
||||
import shared.popups 1.0
|
||||
|
||||
Rectangle {
|
||||
id: buttonsContainer
|
||||
property bool parentIsHovered: false
|
||||
property bool isChatBlocked: false
|
||||
property int containerMargin: 2
|
||||
property int contentType: 2
|
||||
property bool isCurrentUser: false
|
||||
property bool isMessageActive: false
|
||||
property var messageContextMenu
|
||||
property bool isInPinnedPopup: false
|
||||
property bool activityCenterMsg
|
||||
property bool placeholderMsg
|
||||
property string fromAuthor
|
||||
property bool editBtnActive: false
|
||||
property bool pinButtonActive: false
|
||||
property bool deleteButtonActive: false
|
||||
property bool pinnedMessage: false
|
||||
property bool canPin: false
|
||||
signal replyClicked(string messageId, string author)
|
||||
signal hoverChanged(bool hovered)
|
||||
signal setMessageActive(string messageId, bool active)
|
||||
signal clickMessage(bool isProfileClick, bool isSticker, bool isImage, var image, bool isEmoji, bool hideEmojiPicker)
|
||||
|
||||
visible: !buttonsContainer.isChatBlocked &&
|
||||
!buttonsContainer.placeholderMsg && !buttonsContainer.activityCenterMsg &&
|
||||
(buttonsContainer.parentIsHovered || isMessageActive)
|
||||
&& contentType !== Constants.messageContentType.transactionType
|
||||
width: buttonRow.width + buttonsContainer.containerMargin * 2
|
||||
height: 36
|
||||
radius: Style.current.radius
|
||||
color: Style.current.modalBackground
|
||||
z: 52
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: DropShadow {
|
||||
width: buttonsContainer.width
|
||||
height: buttonsContainer.height
|
||||
x: buttonsContainer.x
|
||||
y: buttonsContainer.y + 10
|
||||
visible: buttonsContainer.visible
|
||||
source: buttonsContainer
|
||||
horizontalOffset: 0
|
||||
verticalOffset: 2
|
||||
radius: 10
|
||||
samples: 15
|
||||
color: "#22000000"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: buttonsContainer
|
||||
acceptedButtons: Qt.NoButton
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
buttonsContainer.hoverChanged(true)
|
||||
}
|
||||
onExited: {
|
||||
buttonsContainer.hoverChanged(false)
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttonRow
|
||||
spacing: buttonsContainer.containerMargin
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: buttonsContainer.containerMargin
|
||||
anchors.verticalCenter: buttonsContainer.verticalCenter
|
||||
height: parent.height - 2 * buttonsContainer.containerMargin
|
||||
|
||||
Loader {
|
||||
active: !buttonsContainer.isInPinnedPopup
|
||||
sourceComponent: StatusFlatRoundButton {
|
||||
id: emojiBtn
|
||||
width: 32
|
||||
height: 32
|
||||
icon.name: "reaction-b"
|
||||
type: StatusFlatRoundButton.Type.Tertiary
|
||||
tooltip.text: qsTr("Add reaction")
|
||||
onClicked: {
|
||||
setMessageActive(messageId, true)
|
||||
// Set parent, X & Y positions for the messageContextMenu
|
||||
buttonsContainer.messageContextMenu.parent = buttonsContainer
|
||||
buttonsContainer.messageContextMenu.setXPosition = function() { return (-Math.abs(buttonsContainer.width - buttonsContainer.messageContextMenu.emojiContainer.width))}
|
||||
buttonsContainer.messageContextMenu.setYPosition = function() { return (-buttonsContainer.messageContextMenu.height - 4)}
|
||||
buttonsContainer.clickMessage(false, false, false, null, true, false)
|
||||
}
|
||||
onHoveredChanged: buttonsContainer.hoverChanged(this.hovered)
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: !buttonsContainer.isInPinnedPopup
|
||||
sourceComponent: StatusFlatRoundButton {
|
||||
id: replyBtn
|
||||
objectName: "replyToMessageButton"
|
||||
width: 32
|
||||
height: 32
|
||||
icon.name: "reply"
|
||||
type: StatusFlatRoundButton.Type.Tertiary
|
||||
tooltip.text: qsTr("Reply")
|
||||
onClicked: {
|
||||
buttonsContainer.replyClicked(messageId, fromAuthor);
|
||||
if (messageContextMenu.closeParentPopup) {
|
||||
messageContextMenu.closeParentPopup()
|
||||
}
|
||||
}
|
||||
onHoveredChanged: buttonsContainer.hoverChanged(this.hovered)
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: buttonsContainer.editBtnActive && !buttonsContainer.isInPinnedPopup
|
||||
sourceComponent: StatusFlatRoundButton {
|
||||
id: editButton
|
||||
width: 32
|
||||
height: 32
|
||||
icon.source: Style.svg("edit-message")
|
||||
type: StatusFlatRoundButton.Type.Tertiary
|
||||
tooltip.text: qsTr("Edit")
|
||||
onClicked: messageStore.setEditModeOn(messageId)
|
||||
onHoveredChanged: buttonsContainer.hoverChanged(editButton.hovered)
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: buttonsContainer.pinButtonActive
|
||||
sourceComponent: StatusFlatRoundButton {
|
||||
id: pinButton
|
||||
width: 32
|
||||
height: 32
|
||||
icon.name: buttonsContainer.pinnedMessage ? "unpin" : "pin"
|
||||
type: StatusFlatRoundButton.Type.Tertiary
|
||||
tooltip.text: buttonsContainer.pinnedMessage ? qsTr("Unpin") : qsTr("Pin")
|
||||
onHoveredChanged: buttonsContainer.hoverChanged(pinButton.hovered)
|
||||
onClicked: {
|
||||
if (buttonsContainer.pinnedMessage) {
|
||||
messageStore.unpinMessage(messageId)
|
||||
return;
|
||||
}
|
||||
|
||||
if (buttonsContainer.canPin) {
|
||||
messageStore.pinMessage(messageId)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!chatContentModule) {
|
||||
console.warn("error on open pinned messages limit reached from message context menu - chat content module is not set")
|
||||
return;
|
||||
}
|
||||
|
||||
Global.openPopup(pinnedMessagesPopupComponent, {
|
||||
store: rootStore,
|
||||
messageStore: messageStore,
|
||||
pinnedMessagesModel: chatContentModule.pinnedMessagesModel,
|
||||
messageToPin: buttonsContainer.messageId
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: buttonsContainer.deleteButtonActive && !buttonsContainer.isInPinnedPopup
|
||||
sourceComponent: StatusFlatRoundButton {
|
||||
id: deleteButton
|
||||
objectName: "chatDeleteMessageButton"
|
||||
width: 32
|
||||
height: 32
|
||||
type: StatusFlatRoundButton.Type.Tertiary
|
||||
icon.name: "delete"
|
||||
tooltip.text: qsTr("Delete")
|
||||
onHoveredChanged: buttonsContainer.hoverChanged(deleteButton.hovered)
|
||||
onClicked: {
|
||||
if (!localAccountSensitiveSettings.showDeleteMessageWarning) {
|
||||
messageStore.deleteMessage(messageId)
|
||||
}
|
||||
else {
|
||||
Global.openPopup(deleteMessageConfirmationDialogComponent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: deleteMessageConfirmationDialogComponent
|
||||
|
||||
ConfirmationDialog {
|
||||
confirmButtonObjectName: "chatButtonsPanelConfirmDeleteMessageButton"
|
||||
header.title: qsTr("Confirm deleting this message")
|
||||
confirmationText: qsTr("Are you sure you want to delete this message? Be aware that other clients are not guaranteed to delete the message as well.")
|
||||
height: 260
|
||||
checkbox.visible: true
|
||||
executeConfirm: function () {
|
||||
if (checkbox.checked) {
|
||||
localAccountSensitiveSettings.showDeleteMessageWarning = false
|
||||
}
|
||||
|
||||
close()
|
||||
messageStore.deleteMessage(messageId)
|
||||
}
|
||||
onClosed: {
|
||||
destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,192 +0,0 @@
|
|||
import QtQuick 2.14
|
||||
import QtQuick.Shapes 1.13
|
||||
import QtGraphicalEffects 1.13
|
||||
|
||||
import utils 1.0
|
||||
import shared.controls 1.0
|
||||
import shared 1.0
|
||||
import shared.panels 1.0
|
||||
import shared.status 1.0
|
||||
import shared.controls.chat 1.0
|
||||
|
||||
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
||||
|
||||
Loader {
|
||||
id: root
|
||||
|
||||
property bool amISenderOfTheRepliedMessage
|
||||
property int repliedMessageContentType
|
||||
property string repliedMessageSenderIcon
|
||||
property bool repliedMessageIsEdited
|
||||
property string repliedMessageSender
|
||||
property string repliedMessageSenderPubkey
|
||||
property bool repliedMessageSenderIsAdded
|
||||
property string repliedMessageContent
|
||||
property string repliedMessageImage
|
||||
property bool isCurrentUser: false
|
||||
property int nameMargin: 6
|
||||
property int textFieldWidth: item ? item.textField.width : 0
|
||||
property int textFieldImplicitWidth: 0
|
||||
property int authorWidth: item ? item.authorMetrics.width : 0
|
||||
property bool longReply: false
|
||||
property color elementsColor: amISenderOfTheRepliedMessage ? Style.current.chatReplyCurrentUser : Style.current.secondaryText
|
||||
property var container
|
||||
property int chatHorizontalPadding
|
||||
property string stickerData
|
||||
|
||||
signal clickMessage(bool isProfileClick, bool isSticker, bool isImage, var image, bool isEmoji, bool hideEmojiPicker, bool isReply)
|
||||
signal scrollToBottom(bool isit, var container)
|
||||
|
||||
sourceComponent: Component {
|
||||
Item {
|
||||
property alias textField: lblReplyMessage
|
||||
property alias authorMetrics: txtAuthorMetrics
|
||||
|
||||
id: chatReply
|
||||
// childrenRect.height shows a binding loop for some reason, so we use heights instead
|
||||
height: {
|
||||
const h = userImage.height + 4
|
||||
if (repliedMessageContentType === Constants.messageContentType.imageType) {
|
||||
return h + imgReplyImage.height
|
||||
}
|
||||
if (repliedMessageContentType === Constants.messageContentType.stickerType) {
|
||||
return h + stickerLoader.height
|
||||
}
|
||||
return h + lblReplyMessage.height
|
||||
}
|
||||
width: parent.width
|
||||
clip: true
|
||||
|
||||
TextMetrics {
|
||||
id: txtAuthorMetrics
|
||||
font: lblReplyAuthor.font
|
||||
text: lblReplyAuthor.text
|
||||
}
|
||||
|
||||
Shape {
|
||||
id: replyCorner
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 20 - 1
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Style.current.smallPadding
|
||||
width: 20
|
||||
height: parent.height - anchors.topMargin
|
||||
asynchronous: true
|
||||
antialiasing: true
|
||||
|
||||
ShapePath {
|
||||
id: capTest
|
||||
|
||||
strokeColor: Utils.setColorAlpha(root.elementsColor, 0.4)
|
||||
strokeWidth: 3
|
||||
fillColor: "transparent"
|
||||
|
||||
capStyle: ShapePath.RoundCap
|
||||
joinStyle: ShapePath.RoundJoin
|
||||
|
||||
startX: 20
|
||||
startY: 0
|
||||
PathLine { x: 10; y: 0 }
|
||||
PathArc {
|
||||
x: 0; y: 10
|
||||
radiusX: 13
|
||||
radiusY: 13
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
PathLine { x: 0; y: chatReply.height - replyCorner.anchors.topMargin }
|
||||
}
|
||||
}
|
||||
|
||||
UserImage {
|
||||
id: userImage
|
||||
anchors.left: replyCorner.right
|
||||
anchors.leftMargin: Style.current.halfPadding
|
||||
|
||||
imageHeight: 20
|
||||
imageWidth: 20
|
||||
active: true
|
||||
|
||||
name: repliedMessageSender
|
||||
pubkey: repliedMessageSenderPubkey
|
||||
image: repliedMessageSenderIcon
|
||||
|
||||
onClicked: root.clickMessage(true, false, false, null, false, false, true)
|
||||
}
|
||||
|
||||
StyledTextEdit {
|
||||
id: lblReplyAuthor
|
||||
text: repliedMessageSender
|
||||
color: root.elementsColor
|
||||
readOnly: true
|
||||
font.pixelSize: Style.current.secondaryTextFontSize
|
||||
selectByMouse: true
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: userImage.verticalCenter
|
||||
anchors.left: userImage.right
|
||||
anchors.leftMargin: 5
|
||||
}
|
||||
|
||||
StatusChatImage {
|
||||
id: imgReplyImage
|
||||
visible: repliedMessageContentType === Constants.messageContentType.imageType
|
||||
imageWidth: 50
|
||||
imageSource: repliedMessageImage
|
||||
anchors.top: lblReplyAuthor.bottom
|
||||
anchors.topMargin: nameMargin
|
||||
anchors.left: userImage.left
|
||||
chatHorizontalPadding: 0
|
||||
container: root.container
|
||||
allCornersRounded: true
|
||||
playing: false
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: stickerLoader
|
||||
active: repliedMessageContentType === Constants.messageContentType.stickerType
|
||||
anchors.top: lblReplyAuthor.bottom
|
||||
anchors.topMargin: nameMargin
|
||||
anchors.left: userImage.left
|
||||
sourceComponent: Component {
|
||||
StatusSticker {
|
||||
id: stickerId
|
||||
imageHeight: 56
|
||||
imageWidth: 56
|
||||
stickerData: root.stickerData
|
||||
contentType: repliedMessageContentType
|
||||
onLoaded: {
|
||||
scrollToBottom(true, root.container)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledTextEdit {
|
||||
id: lblReplyMessage
|
||||
visible: repliedMessageContentType !== Constants.messageContentType.imageType && repliedMessageContentType !== Constants.messageContentType.stickerType
|
||||
Component.onCompleted: textFieldImplicitWidth = implicitWidth
|
||||
anchors.top: lblReplyAuthor.bottom
|
||||
anchors.topMargin: nameMargin
|
||||
text: {
|
||||
if (repliedMessageIsEdited){
|
||||
let index = repliedMessageContent.length - 4
|
||||
return Utils.getReplyMessageStyle(StatusQUtils.Emoji.parse(Utils.linkifyAndXSS(repliedMessageContent.slice(0, index) + Constants.editLabel + repliedMessageContent.slice(index)), StatusQUtils.Emoji.size.small), amISenderOfTheRepliedMessage)
|
||||
} else {
|
||||
return Utils.getReplyMessageStyle(StatusQUtils.Emoji.parse(Utils.linkifyAndXSS(repliedMessageContent), StatusQUtils.Emoji.size.small), amISenderOfTheRepliedMessage)
|
||||
}
|
||||
}
|
||||
textFormat: Text.RichText
|
||||
color: root.elementsColor
|
||||
readOnly: true
|
||||
selectByMouse: true
|
||||
font.pixelSize: Style.current.additionalTextSize
|
||||
font.weight: Font.Medium
|
||||
anchors.left: userImage.left
|
||||
width: root.longReply ? parent.width : implicitWidth
|
||||
height: 20
|
||||
clip: true
|
||||
z: 51
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,192 +0,0 @@
|
|||
import QtQuick 2.3
|
||||
import QtQuick.Controls 2.13
|
||||
import QtGraphicalEffects 1.13
|
||||
|
||||
import shared 1.0
|
||||
import shared.panels 1.0
|
||||
|
||||
import StatusQ.Controls 0.1 as StatusQ
|
||||
import utils 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
height: 20
|
||||
width: childrenRect.width
|
||||
|
||||
property int imageMargin: 4
|
||||
signal addEmojiClicked()
|
||||
signal hoverChanged(bool hovered)
|
||||
signal toggleReaction(int emojiID)
|
||||
signal setMessageActive(string messageId, bool active)
|
||||
|
||||
property var store
|
||||
property bool isCurrentUser
|
||||
property var emojiReactionsModel
|
||||
property bool isMessageActive
|
||||
|
||||
Row {
|
||||
spacing: root.imageMargin
|
||||
|
||||
Repeater {
|
||||
id: reactionRepeater
|
||||
width: childrenRect.width
|
||||
model: root.emojiReactionsModel
|
||||
|
||||
Rectangle {
|
||||
property bool isHovered: false
|
||||
|
||||
id: emojiContainer
|
||||
width: emojiImage.width + emojiCount.width + (root.imageMargin * 2) + + 8
|
||||
height: 20
|
||||
radius: 10
|
||||
color: model.didIReactWithThisEmoji ?
|
||||
(isHovered ? Style.current.emojiReactionActiveBackgroundHovered : Style.current.secondaryBackground) :
|
||||
(isHovered ? Style.current.emojiReactionBackgroundHovered : Style.current.emojiReactionBackground)
|
||||
|
||||
StatusQ.StatusToolTip {
|
||||
visible: mouseArea.containsMouse
|
||||
maxWidth: 400
|
||||
text: root.store.showReactionAuthors(model.jsonArrayOfUsersReactedWithThisEmoji, model.emojiId)
|
||||
}
|
||||
|
||||
// Rounded corner to cover one corner
|
||||
Rectangle {
|
||||
color: parent.color
|
||||
width: 10
|
||||
height: 10
|
||||
anchors.top: parent.top
|
||||
anchors.left: !root.isCurrentUser? parent.left : undefined
|
||||
anchors.leftMargin: 0
|
||||
anchors.right: !root.isCurrentUser? undefined : parent.right
|
||||
anchors.rightMargin: 0
|
||||
radius: 2
|
||||
z: -1
|
||||
}
|
||||
|
||||
// This is a workaround to get a "border" around the rectangle including the weird rectangle
|
||||
Loader {
|
||||
active: model.didIReactWithThisEmoji
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: -1
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: -1
|
||||
z: -2
|
||||
|
||||
sourceComponent: Component {
|
||||
Rectangle {
|
||||
width: emojiContainer.width + 2
|
||||
height: emojiContainer.height + 2
|
||||
radius: emojiContainer.radius
|
||||
color: Style.current.primary
|
||||
|
||||
Rectangle {
|
||||
color: parent.color
|
||||
width: 10
|
||||
height: 10
|
||||
anchors.top: parent.top
|
||||
anchors.left: !root.isCurrentUser? parent.left : undefined
|
||||
anchors.leftMargin: 0
|
||||
anchors.right: !root.isCurrentUser? undefined : parent.right
|
||||
anchors.rightMargin: 0
|
||||
radius: 2
|
||||
z: -1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SVGImage {
|
||||
id: emojiImage
|
||||
width: 15
|
||||
height: 15
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: {
|
||||
switch (model.emojiId) {
|
||||
case 1: return Style.svg("emojiReactions/heart")
|
||||
case 2: return Style.svg("emojiReactions/thumbsUp")
|
||||
case 3: return Style.svg("emojiReactions/thumbsDown")
|
||||
case 4: return Style.svg("emojiReactions/laughing")
|
||||
case 5: return Style.svg("emojiReactions/sad")
|
||||
case 6: return Style.svg("emojiReactions/angry")
|
||||
default: return ""
|
||||
}
|
||||
}
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.imageMargin
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: emojiCount
|
||||
text: model.numberOfReactions
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: emojiImage.right
|
||||
anchors.leftMargin: root.imageMargin
|
||||
font.pixelSize: 12
|
||||
color: model.didIReactWithThisEmoji ? Style.current.textColorTertiary : Style.current.textColor
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: {
|
||||
root.hoverChanged(true)
|
||||
emojiContainer.isHovered = true
|
||||
}
|
||||
onExited: {
|
||||
root.hoverChanged(false)
|
||||
emojiContainer.isHovered = false
|
||||
}
|
||||
onClicked: {
|
||||
toggleReaction(model.emojiId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: addEmojiBtn.width + addEmojiBtn.anchors.leftMargin // there is more margin between the button and the emojis than between each emoji
|
||||
height: addEmojiBtn.height
|
||||
|
||||
SVGImage {
|
||||
property bool isHovered: false
|
||||
|
||||
id: addEmojiBtn
|
||||
source: Style.svg("emoji")
|
||||
width: 16.5
|
||||
height: 16.5
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 2.5
|
||||
|
||||
}
|
||||
|
||||
ColorOverlay {
|
||||
anchors.fill: addEmojiBtn
|
||||
antialiasing: true
|
||||
source: addEmojiBtn
|
||||
color: addEmojiBtn.isHovered ? Style.current.primary : Style.current.secondaryText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: addEmojiBtn
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
onEntered: addEmojiBtn.isHovered = true
|
||||
onExited: addEmojiBtn.isHovered = false
|
||||
onClicked: {
|
||||
if (typeof root.isMessageActive !== "undefined") {
|
||||
setMessageActive(messageId, true);
|
||||
}
|
||||
root.addEmojiClicked();
|
||||
}
|
||||
}
|
||||
|
||||
StatusQ.StatusToolTip {
|
||||
visible: addEmojiBtn.isHovered
|
||||
text: qsTr("Add reaction")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -63,12 +63,14 @@ StatusModal {
|
|||
id: verificationMessage
|
||||
anchors.top: description.bottom
|
||||
anchors.topMargin: Style.current.padding
|
||||
width: parent.width
|
||||
isMessage: true
|
||||
shouldRepeatHeader: true
|
||||
messageTimestamp: root.messageTimestamp
|
||||
senderId: root.senderPublicKey
|
||||
senderDisplayName: root.senderDisplayName
|
||||
senderIcon: root.senderIcon
|
||||
message: root.challengeText
|
||||
messageText: root.challengeText
|
||||
messageContentType: Constants.messageContentType.messageType
|
||||
placeholderMessage: true
|
||||
}
|
||||
|
@ -93,12 +95,14 @@ StatusModal {
|
|||
id: responseMessage
|
||||
visible: !!root.responseText
|
||||
anchors.top: verificationMessage.bottom
|
||||
width: parent.width
|
||||
isMessage: true
|
||||
shouldRepeatHeader: true
|
||||
messageTimestamp: root.responseTimestamp
|
||||
senderId: root.senderPublicKey
|
||||
senderDisplayName: userProfile.name
|
||||
senderIcon: userProfile.icon
|
||||
message: root.responseText
|
||||
messageText: root.responseText
|
||||
messageContentType: Constants.messageContentType.messageType
|
||||
placeholderMessage: true
|
||||
}
|
||||
|
|
|
@ -4,4 +4,3 @@ DelegateModelGeneralized 1.0 DelegateModelGeneralized.qml
|
|||
LoadingAnimation 1.0 LoadingAnimation.qml
|
||||
MacTrafficLights 1.0 MacTrafficLights.qml
|
||||
NumberPolyFill 1.0 polyfill.number.toLocaleString.js
|
||||
XSS 1.0 xss.js
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -10,10 +10,12 @@ import StatusQ.Core.Utils 0.1 as StatusQUtils
|
|||
|
||||
Rectangle {
|
||||
id: root
|
||||
height: (root.contentType === Constants.messageContentType.imageType) ?
|
||||
replyToUsername.height + imageThumbnail.height + Style.current.padding :
|
||||
(root.contentType === Constants.messageContentType.stickerType) ?
|
||||
replyToUsername.height + stickerThumbnail.height + Style.current.padding : 50
|
||||
implicitHeight: (root.contentType === Constants.messageContentType.imageType)
|
||||
? replyToUsername.height + imageThumbnail.height + Style.current.padding
|
||||
: (root.contentType === Constants.messageContentType.stickerType)
|
||||
? replyToUsername.height + stickerThumbnail.height + Style.current.padding
|
||||
: 50
|
||||
|
||||
color: Style.current.replyBackground
|
||||
radius: 16
|
||||
clip: true
|
||||
|
@ -61,7 +63,7 @@ Rectangle {
|
|||
|
||||
StyledText {
|
||||
id: replyText
|
||||
text: Utils.getMessageWithStyle(StatusQUtils.Emoji.parse(Utils.linkifyAndXSS(message)), false)
|
||||
text: StatusQUtils.Utils.getMessageWithStyle(StatusQUtils.Emoji.parse(StatusQUtils.Utils.linkifyAndXSS(message)), false)
|
||||
anchors.fill: parent
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: 13
|
||||
|
|
|
@ -233,7 +233,7 @@ Popup {
|
|||
|
||||
RowLayout {
|
||||
id: stickersRowLayout
|
||||
width: scrollView.availableWidth
|
||||
width: inputScrollView.availableWidth
|
||||
spacing: Style.current.padding
|
||||
|
||||
Repeater {
|
||||
|
|
|
@ -13,6 +13,7 @@ import shared.panels 1.0
|
|||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
|
@ -228,13 +229,13 @@ Rectangle {
|
|||
subTitle: {
|
||||
let user = ""
|
||||
if (isCurrentUser) {
|
||||
user = root.profileStore.ensName !== "" ? root.profileStore.ensName : Utils.elideText(root.profileStore.pubkey, 5)
|
||||
user = root.profileStore.ensName !== "" ? root.profileStore.ensName : StatusQUtils.Utils.elideText(root.profileStore.pubkey, 5)
|
||||
} else if (userIsEnsVerified) {
|
||||
user = userEnsName
|
||||
}
|
||||
|
||||
if (user === ""){
|
||||
user = Utils.elideText(userPublicKey, 5)
|
||||
user = StatusQUtils.Utils.elideText(userPublicKey, 5)
|
||||
}
|
||||
return Constants.userLinkPrefix + user;
|
||||
}
|
||||
|
@ -370,9 +371,10 @@ Rectangle {
|
|||
isMessage: true
|
||||
shouldRepeatHeader: true
|
||||
messageTimestamp: root.verificationRequestedAt
|
||||
senderId: profileStore.pubkey
|
||||
senderDisplayName: userProfile.name
|
||||
senderIcon: userProfile.icon
|
||||
message: root.verificationChallenge
|
||||
messageText: root.verificationChallenge
|
||||
messageContentType: Constants.messageContentType.messageType
|
||||
placeholderMessage: true
|
||||
}
|
||||
|
@ -380,13 +382,14 @@ Rectangle {
|
|||
MessageView {
|
||||
id: responseMessage
|
||||
visible: root.showVerificationPendingSection && !!root.verificationResponse
|
||||
width: parent.width
|
||||
Layout.fillWidth: true
|
||||
isMessage: true
|
||||
shouldRepeatHeader: true
|
||||
messageTimestamp: root.verificationRepliedAt
|
||||
senderId: root.userPublicKey
|
||||
senderDisplayName: root.verificationResponseDisplayName
|
||||
senderIcon: root.verificationResponseIcon
|
||||
message: root.verificationResponse
|
||||
messageText: root.verificationResponse
|
||||
messageContentType: Constants.messageContentType.messageType
|
||||
placeholderMessage: true
|
||||
}
|
||||
|
|
|
@ -122,16 +122,16 @@ Item {
|
|||
}
|
||||
|
||||
text: {
|
||||
if (contentType === Constants.messageContentType.stickerType) return "";
|
||||
let msg = Utils.linkifyAndXSS(message);
|
||||
if (contentType === Constants.messageContentType.stickerType)
|
||||
return "";
|
||||
let msg = StatusQUtils.Utils.linkifyAndXSS(message);
|
||||
if (isEmoji)
|
||||
return StatusQUtils.Emoji.parse(msg, StatusQUtils.Emoji.size.middle, StatusQUtils.Emoji.format.png);
|
||||
if (isEdited) {
|
||||
let index = msg.endsWith("code>") ? msg.length : msg.length - 4
|
||||
return Utils.getMessageWithStyle(StatusQUtils.Emoji.parse(msg.slice(0, index) + Constants.editLabel + msg.slice(index)), isCurrentUser, hoveredLink)
|
||||
return StatusQUtils.Utils.getMessageWithStyle(StatusQUtils.Emoji.parse(msg.slice(0, index) + Constants.editLabel + msg.slice(index)), isCurrentUser, hoveredLink)
|
||||
}
|
||||
return Utils.getMessageWithStyle(StatusQUtils.Emoji.parse(msg), isCurrentUser, hoveredLink)
|
||||
|
||||
return StatusQUtils.Utils.getMessageWithStyle(StatusQUtils.Emoji.parse(msg), isCurrentUser, hoveredLink)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,805 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtGraphicalEffects 1.13
|
||||
|
||||
import utils 1.0
|
||||
import shared.panels 1.0
|
||||
import shared.status 1.0
|
||||
import shared.controls 1.0
|
||||
import shared.panels.chat 1.0
|
||||
import shared.views.chat 1.0
|
||||
import shared.controls.chat 1.0
|
||||
|
||||
import StatusQ.Controls 0.1 as StatusQControls
|
||||
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
||||
import StatusQ.Components 0.1
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property var store
|
||||
property var messageStore
|
||||
property var usersStore
|
||||
property var contactsStore
|
||||
|
||||
property var chatLogView
|
||||
property var emojiPopup
|
||||
|
||||
property var messageContextMenu
|
||||
property var container
|
||||
property int contentType
|
||||
property bool isChatBlocked: false
|
||||
property bool isActiveChannel: false
|
||||
property int senderTrustStatus
|
||||
|
||||
property int chatHorizontalPadding: Style.current.halfPadding
|
||||
property int chatVerticalPadding: 7
|
||||
property bool headerRepeatCondition: (authorCurrentMsg !== authorPrevMsg ||
|
||||
shouldRepeatHeader || dateGroupLbl.visible || chatReply.active)
|
||||
property bool stickersLoaded: false
|
||||
property string sticker
|
||||
property int stickerPack
|
||||
property bool isMessageActive: false
|
||||
property bool amISender: false
|
||||
property string senderIcon: ""
|
||||
property bool isHovered: false
|
||||
property bool isInPinnedPopup: false
|
||||
property bool pinnedMessage: false
|
||||
property bool canPin: false
|
||||
property string communityId
|
||||
property bool editModeOn: false
|
||||
property string linkUrls: ""
|
||||
|
||||
property string message: ""
|
||||
|
||||
property int prevMessageIndex
|
||||
property string prevMsgTimestamp
|
||||
property string messageTimestamp
|
||||
|
||||
property var transactionParams
|
||||
|
||||
signal openStickerPackPopup(string stickerPackId)
|
||||
signal addEmoji(bool isProfileClick, bool isSticker, bool isImage , var image, bool isEmoji, bool hideEmojiPicker)
|
||||
signal clickMessage(bool isProfileClick, bool isSticker, bool isImage, var image, bool isEmoji, bool hideEmojiPicker, bool isReply, bool isRightClickOnImage, string imageSource)
|
||||
signal replyClicked(string messageId, string author)
|
||||
signal imageClicked(var image)
|
||||
|
||||
|
||||
function setMessageActive(messageId, active) {
|
||||
if (active) {
|
||||
activeMessage = messageId;
|
||||
} else if (activeMessage === messageId) {
|
||||
activeMessage = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
width: parent.width
|
||||
height: messageContainer.height + messageContainer.anchors.topMargin
|
||||
+ (dateGroupLbl.visible ? dateGroupLbl.height + dateGroupLbl.anchors.topMargin : 0)
|
||||
|
||||
Connections {
|
||||
target: !!root.messageStore && root.messageStore.messageModule ?
|
||||
root.messageStore.messageModule : null
|
||||
enabled: !!root.messageStore && !!root.messageStore.messageModule && responseTo !== ""
|
||||
onRefreshAMessageUserRespondedTo: {
|
||||
if(msgId === messageId)
|
||||
chatReply.resetOriginalMessage()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: ensureMessageFullyVisibleTimer
|
||||
interval: 1
|
||||
onTriggered: {
|
||||
chatLogView.positionViewAtIndex(ListView.currentIndex, ListView.Contain)
|
||||
}
|
||||
}
|
||||
|
||||
MessageMouseArea {
|
||||
enabled: !root.isChatBlocked && !placeholderMessage && !isImage
|
||||
anchors.fill: messageContainer
|
||||
acceptedButtons: activityCenterMessage ? Qt.LeftButton : Qt.RightButton
|
||||
messageContextMenu: root.messageContextMenu
|
||||
messageContextMenuParent: root
|
||||
isHovered: root.isHovered
|
||||
isMessageActive: root.isMessageActive
|
||||
isActivityCenterMessage: activityCenterMessage
|
||||
stickersLoaded: root.stickersLoaded
|
||||
onClickMessage: {
|
||||
root.clickMessage(isProfileClick, isSticker, isImage, null, isEmoji, false, false, false, "");
|
||||
}
|
||||
}
|
||||
|
||||
ChatButtonsPanel {
|
||||
contentType: messageContentType
|
||||
parentIsHovered: !editModeOn && isHovered
|
||||
isChatBlocked: root.isChatBlocked
|
||||
onHoverChanged: {
|
||||
hovered && setHovered(messageId, hovered)
|
||||
}
|
||||
onSetMessageActive: {
|
||||
root.setMessageActive(messageId, active)
|
||||
}
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 20
|
||||
anchors.top: messageContainer.top
|
||||
// This is not exactly like the design because the hover becomes messed up with the buttons on top of another Message
|
||||
anchors.topMargin: -Style.current.halfPadding
|
||||
messageContextMenu: root.messageContextMenu
|
||||
isInPinnedPopup: root.isInPinnedPopup
|
||||
fromAuthor: senderId
|
||||
editBtnActive: isText && !editModeOn && root.amISender
|
||||
pinButtonActive: {
|
||||
if (!root.messageStore)
|
||||
return false
|
||||
|
||||
const chatType = root.messageStore.getChatType();
|
||||
const amIChatAdmin = root.messageStore.amIChatAdmin();
|
||||
const pinMessageAllowedForMembers = root.messageStore.pinMessageAllowedForMembers()
|
||||
|
||||
return chatType === Constants.chatType.oneToOne ||
|
||||
chatType === Constants.chatType.privateGroupChat && amIChatAdmin ||
|
||||
chatType === Constants.chatType.communityChat && (amIChatAdmin || pinMessageAllowedForMembers);
|
||||
|
||||
}
|
||||
deleteButtonActive: {
|
||||
if (!root.messageStore)
|
||||
return false;
|
||||
const isMyMessage = senderId !== "" && senderId === userProfile.pubKey;
|
||||
const chatType = root.messageStore.getChatType();
|
||||
return isMyMessage &&
|
||||
(contentType === Constants.messageContentType.messageType ||
|
||||
contentType === Constants.messageContentType.stickerType ||
|
||||
contentType === Constants.messageContentType.emojiType ||
|
||||
contentType === Constants.messageContentType.imageType ||
|
||||
contentType === Constants.messageContentType.audioType);
|
||||
}
|
||||
pinnedMessage: root.pinnedMessage
|
||||
canPin: root.canPin
|
||||
|
||||
activityCenterMsg: activityCenterMessage
|
||||
placeholderMsg: placeholderMessage
|
||||
onClickMessage: {
|
||||
root.clickMessage(isProfileClick, isSticker, isImage, image, isEmoji, hideEmojiPicker, false, false, "");
|
||||
}
|
||||
onReplyClicked: {
|
||||
root.replyClicked(messageId, author)
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
enabled: isHovered || isMessageActive
|
||||
target: typeof root.messageContextMenu !== "undefined" ? root.messageContextMenu : null
|
||||
onOpened: root.setMessageActive(messageId, true)
|
||||
onClosed: root.setMessageActive(messageId, false)
|
||||
}
|
||||
|
||||
DateGroup {
|
||||
id: dateGroupLbl
|
||||
previousMessageIndex: root.prevMessageIndex
|
||||
previousMessageTimestamp: root.prevMsgTimestamp
|
||||
messageTimestamp: root.messageTimestamp
|
||||
isActivityCenterMessage: activityCenterMessage
|
||||
}
|
||||
|
||||
function startMessageFoundAnimation() {
|
||||
messageFoundAnimation.start();
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: messageFoundAnimation
|
||||
PauseAnimation {
|
||||
duration: 600
|
||||
}
|
||||
NumberAnimation {
|
||||
target: highlightRect
|
||||
property: "opacity"
|
||||
to: 1.0
|
||||
duration: 1500
|
||||
}
|
||||
PauseAnimation {
|
||||
duration: 1000
|
||||
}
|
||||
NumberAnimation {
|
||||
target: highlightRect
|
||||
property: "opacity"
|
||||
to: 0.0
|
||||
duration: 1500
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: highlightRect
|
||||
anchors.fill: messageContainer
|
||||
opacity: 0
|
||||
visible: (opacity > 0.001)
|
||||
color: Style.current.backgroundHoverLight
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: messageContainer
|
||||
|
||||
property alias messageContent: messageContent
|
||||
|
||||
anchors.top: dateGroupLbl.visible ? dateGroupLbl.bottom : parent.top
|
||||
anchors.topMargin: dateGroupLbl.visible ? (activityCenterMessage ? 4 : Style.current.padding) : 0
|
||||
height: childrenRect.height
|
||||
+ (chatName.visible || emojiReactionLoader.active ? Style.current.halfPadding : 0)
|
||||
+ (chatName.visible && emojiReactionLoader.active ? Style.current.padding : 0)
|
||||
+ (!chatName.visible && chatImageContent.active ? 6 : 0)
|
||||
+ (emojiReactionLoader.active ? emojiReactionLoader.height: 0)
|
||||
+ (retry.visible && !chatTime.visible ? Style.current.smallPadding : 0)
|
||||
+ (pinnedRectangleLoader.active ? Style.current.smallPadding : 0)
|
||||
+ (editModeOn ? 25 : 0)
|
||||
+ (!chatName.visible ? 6 : 0)
|
||||
width: parent.width
|
||||
|
||||
color: {
|
||||
if (editModeOn) {
|
||||
return Style.current.backgroundHoverLight
|
||||
}
|
||||
|
||||
if (activityCenterMessage) {
|
||||
return read ? Style.current.transparent : Utils.setColorAlpha(Style.current.blue, 0.1)
|
||||
}
|
||||
|
||||
if (placeholderMessage) {
|
||||
return Style.current.transparent
|
||||
}
|
||||
|
||||
if (pinnedMessage) {
|
||||
return isHovered || isMessageActive ? Style.current.pinnedMessageBackgroundHovered : Style.current.pinnedMessageBackground
|
||||
}
|
||||
|
||||
return isHovered || isMessageActive ? (hasMention ? Style.current.mentionMessageHoverColor : Style.current.backgroundHoverLight) :
|
||||
(hasMention ? Style.current.mentionMessageColor : Style.current.transparent)
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: pinnedRectangleLoader
|
||||
active: !editModeOn && pinnedMessage
|
||||
anchors.left: chatName.left
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: active ? Style.current.halfPadding : 0
|
||||
|
||||
sourceComponent: Component {
|
||||
Rectangle {
|
||||
id: pinnedRectangle
|
||||
height: 24
|
||||
width: childrenRect.width + Style.current.smallPadding
|
||||
color: Style.current.pinnedRectangleBackground
|
||||
radius: 12
|
||||
|
||||
SVGImage {
|
||||
id: pinImage
|
||||
source: Style.svg("pin")
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 3
|
||||
width: 16
|
||||
height: 16
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
ColorOverlay {
|
||||
anchors.fill: parent
|
||||
source: parent
|
||||
color: Style.current.pinnedMessageBorder
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: qsTr("Pinned by %1").arg(Utils.getContactDetailsAsJson(messagePinnedBy).displayName)
|
||||
anchors.left: pinImage.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pixelSize: 13
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Not Refactored Yet
|
||||
// Connections {
|
||||
// enabled: !!rootStore
|
||||
// target: enabled ? rootStore.chatsModelInst.messageView : null
|
||||
// onMessageEdited: {
|
||||
// if(chatReply.item)
|
||||
// chatReply.item.messageEdited(editedMessageId, editedMessageContent)
|
||||
// }
|
||||
// }
|
||||
|
||||
ChatReplyPanel {
|
||||
id: chatReply
|
||||
anchors.top: pinnedRectangleLoader.active ? pinnedRectangleLoader.bottom : parent.top
|
||||
anchors.topMargin: active ? 4 : 0
|
||||
anchors.left: chatImage.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.padding
|
||||
isCurrentUser: root.amISender
|
||||
longReply: active && textFieldImplicitWidth > width
|
||||
container: root.container
|
||||
chatHorizontalPadding: chatHorizontalPadding
|
||||
active: responseTo !== "" && !activityCenterMessage
|
||||
|
||||
function resetOriginalMessage() {
|
||||
if(!root.messageStore)
|
||||
return
|
||||
let obj = root.messageStore.getMessageByIdAsJson(responseTo)
|
||||
if(!obj)
|
||||
return
|
||||
|
||||
amISenderOfTheRepliedMessage = obj.amISender
|
||||
repliedMessageContentType = obj.contentType
|
||||
repliedMessageSenderIcon = obj.senderIcon
|
||||
// TODO: not sure about is edited at the moment
|
||||
repliedMessageIsEdited = false
|
||||
repliedMessageSender = obj.senderDisplayName
|
||||
repliedMessageSenderPubkey = obj.senderId
|
||||
repliedMessageSenderIsAdded = obj.senderIsAdded
|
||||
repliedMessageContent = obj.messageText
|
||||
repliedMessageImage = obj.messageImage
|
||||
stickerData = obj.sticker
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
resetOriginalMessage()
|
||||
}
|
||||
|
||||
onScrollToBottom: {
|
||||
// Not Refactored Yet
|
||||
// messageStore.scrollToBottom(isit, root.container);
|
||||
}
|
||||
|
||||
onClickMessage: {
|
||||
root.clickMessage(isProfileClick, isSticker, isImage, image, isEmoji, hideEmojiPicker, isReply, false, "")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
UserImage {
|
||||
id: chatImage
|
||||
|
||||
active: isMessage && headerRepeatCondition
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.top: chatReply.active ? chatReply.bottom :
|
||||
pinnedRectangleLoader.active ? pinnedRectangleLoader.bottom : parent.top
|
||||
anchors.topMargin: chatReply.active || pinnedRectangleLoader.active ? 4 : Style.current.smallPadding
|
||||
|
||||
image: root.senderIcon
|
||||
pubkey: senderId
|
||||
name: senderDisplayName
|
||||
messageContextMenu: root.messageContextMenu
|
||||
|
||||
onClicked: root.clickMessage(true, false, false, null, false, false, false, false, "")
|
||||
}
|
||||
|
||||
UsernameLabel {
|
||||
id: chatName
|
||||
visible: !editModeOn && isMessage && headerRepeatCondition
|
||||
anchors.leftMargin: chatHorizontalPadding
|
||||
anchors.top: chatImage.top
|
||||
anchors.left: chatImage.right
|
||||
messageContextMenu: root.messageContextMenu
|
||||
displayName: senderDisplayName
|
||||
localName: senderLocalName
|
||||
amISender: root.amISender
|
||||
onClickMessage: {
|
||||
root.clickMessage(true, false, false, null, false, false, false, false, "")
|
||||
}
|
||||
}
|
||||
|
||||
VerificationLabel {
|
||||
id: trustStatus
|
||||
anchors.left: chatName.right
|
||||
anchors.leftMargin: visible ? 4 : 0
|
||||
anchors.bottom: chatName.bottom
|
||||
anchors.bottomMargin: 4
|
||||
visible: !root.amISender && chatName.visible
|
||||
trustStatus: senderTrustStatus
|
||||
}
|
||||
|
||||
ChatTimePanel {
|
||||
id: chatTime
|
||||
visible: !editModeOn && headerRepeatCondition
|
||||
anchors.verticalCenter: chatName.verticalCenter
|
||||
anchors.left: trustStatus.right
|
||||
anchors.leftMargin: 4
|
||||
color: Style.current.secondaryText
|
||||
timestamp: root.messageTimestamp
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: editMessageLoader
|
||||
active: editModeOn
|
||||
anchors.top: chatReply.active ? chatReply.bottom : parent.top
|
||||
anchors.left: chatImage.right
|
||||
anchors.leftMargin: chatHorizontalPadding
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: chatHorizontalPadding
|
||||
height: (item !== null && typeof(item)!== 'undefined')? item.height: 0
|
||||
sourceComponent: Item {
|
||||
id: editText
|
||||
height: childrenRect.height
|
||||
|
||||
property bool suggestionsOpened: false
|
||||
Keys.onEscapePressed: {
|
||||
if (!suggestionsOpened) {
|
||||
cancelBtn.clicked()
|
||||
}
|
||||
suggestionsOpened = false
|
||||
}
|
||||
|
||||
StatusChatInput {
|
||||
id: editTextInput
|
||||
|
||||
store: root.store
|
||||
usersStore: root.usersStore
|
||||
|
||||
chatInputPlaceholder: qsTr("Pinned by %1")
|
||||
chatType: messageStore.getChatType()
|
||||
isEdit: true
|
||||
emojiPopup: root.emojiPopup
|
||||
messageContextMenu: root.messageContextMenu
|
||||
onSendMessage: {
|
||||
saveBtn.clicked(null)
|
||||
}
|
||||
suggestions.onVisibleChanged: {
|
||||
if (suggestions.visible) {
|
||||
editText.suggestionsOpened = true
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
let mentionsMap = new Map()
|
||||
let index = 0
|
||||
while (true) {
|
||||
index = message.indexOf("<a href=", index)
|
||||
if (index < 0) {
|
||||
break
|
||||
}
|
||||
let startIndex = index
|
||||
let endIndex = message.indexOf("</a>", index) + 4
|
||||
if (endIndex < 0) {
|
||||
index += 8 // "<a href="
|
||||
continue
|
||||
}
|
||||
let addrIndex = message.indexOf("0x", index + 8)
|
||||
if (addrIndex < 0) {
|
||||
index += 8 // "<a href="
|
||||
continue
|
||||
}
|
||||
let addrEndIndex = message.indexOf("\"", addrIndex)
|
||||
if (addrEndIndex < 0) {
|
||||
index += 8 // "<a href="
|
||||
continue
|
||||
}
|
||||
const mentionLink = message.substring(startIndex, endIndex)
|
||||
const linkTag = message.substring(index, endIndex)
|
||||
const linkText = linkTag.replace(/(<([^>]+)>)/ig,"").trim()
|
||||
const atSymbol = linkText.startsWith("@") ? '' : '@'
|
||||
const mentionTag = Constants.mentionSpanTag + atSymbol + linkText + '</span> '
|
||||
mentionsMap.set(mentionLink, mentionTag)
|
||||
index += linkTag.length
|
||||
}
|
||||
|
||||
var text = message
|
||||
for (let [key, value] of mentionsMap) {
|
||||
text = text.replace(new RegExp(key, 'g'), value)
|
||||
}
|
||||
editTextInput.textInput.text = text
|
||||
editTextInput.textInput.cursorPosition = editTextInput.textInput.length
|
||||
}
|
||||
}
|
||||
|
||||
StatusQControls.StatusFlatButton {
|
||||
id: cancelBtn
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.halfPadding
|
||||
anchors.top: editTextInput.bottom
|
||||
text: qsTr("Cancel")
|
||||
onClicked: {
|
||||
messageStore.setEditModeOff(messageId)
|
||||
editTextInput.textInput.text = StatusQUtils.Emoji.parse(message)
|
||||
ensureMessageFullyVisibleTimer.start()
|
||||
}
|
||||
}
|
||||
|
||||
StatusQControls.StatusButton {
|
||||
id: saveBtn
|
||||
anchors.left: cancelBtn.right
|
||||
anchors.leftMargin: Style.current.halfPadding
|
||||
anchors.top: editTextInput.bottom
|
||||
text: qsTr("Save")
|
||||
enabled: editTextInput.textInput.text.trim().length > 0
|
||||
onClicked: {
|
||||
let msg = rootStore.plainText(StatusQUtils.Emoji.deparse(editTextInput.textInput.text))
|
||||
if (msg.length > 0){
|
||||
msg = messageStore.interpretMessage(msg)
|
||||
messageStore.setEditModeOff(messageId)
|
||||
messageStore.editMessage(messageId, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: messageContent
|
||||
height: childrenRect.height + (isEmoji ? 2 : 0)
|
||||
anchors.top: chatName.visible ? chatName.bottom :
|
||||
chatReply.active ? chatReply.bottom :
|
||||
pinnedRectangleLoader.active ? pinnedRectangleLoader.bottom : parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: chatImage.imageWidth + Style.current.padding + root.chatHorizontalPadding
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: root.chatHorizontalPadding
|
||||
anchors.topMargin: (!chatName.visible || !chatReply.active || !pinnedRectangleLoader.active) ? 4 : 0
|
||||
visible: !editModeOn
|
||||
|
||||
ChatTextView {
|
||||
id: chatText
|
||||
readonly property int leftPadding: chatImage.anchors.leftMargin + chatImage.width + chatHorizontalPadding
|
||||
visible: isText || isEmoji || (isImage && root.message !== "<p>Update to latest version to see a nice image here!</p>")
|
||||
|
||||
message: Utils.removeGifUrls(root.message)
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: isEmoji ? 2 : 0
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
textField.rightPadding: Style.current.bigPadding
|
||||
onLinkActivated: {
|
||||
if (activityCenterMessage) {
|
||||
root.clickMessage(false, isSticker, false, null, false, false, false, false, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: chatImageContent
|
||||
active: isImage
|
||||
anchors.top: chatText.visible ? chatText.bottom : parent.top
|
||||
anchors.topMargin: active ? 6 : 0
|
||||
z: 51
|
||||
sourceComponent: Component {
|
||||
StatusChatImage {
|
||||
playing: root.messageStore.playAnimation
|
||||
imageSource: messageImage
|
||||
imageWidth: 200
|
||||
isActiveChannel: root.isActiveChannel
|
||||
onClicked: {
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
root.imageClicked(image)
|
||||
}
|
||||
else if (mouse.button === Qt.RightButton) {
|
||||
// Set parent, X & Y positions for the messageContextMenu
|
||||
root.messageContextMenu.parent = root
|
||||
|
||||
const mappedPos = root.mapFromItem(
|
||||
this,
|
||||
mouse.x + Style.current.smallPadding,
|
||||
mouse.y - Style.current.smallPadding)
|
||||
|
||||
root.messageContextMenu.setXPosition = function() { return mappedPos.x }
|
||||
root.messageContextMenu.setYPosition = function() { return mappedPos.y }
|
||||
root.clickMessage(false, false, true, image, false, true, false, true, imageSource)
|
||||
}
|
||||
}
|
||||
container: root.container
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: stickerLoader
|
||||
active: contentType === Constants.messageContentType.stickerType
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: active ? Style.current.halfPadding : 0
|
||||
sourceComponent: Component {
|
||||
Rectangle {
|
||||
id: stickerContainer
|
||||
color: Style.current.transparent
|
||||
border.color: isHovered ? Qt.darker(Style.current.border, 1.1) : Style.current.border
|
||||
border.width: 1
|
||||
radius: 16
|
||||
width: stickerId.width + 2 * chatVerticalPadding
|
||||
height: stickerId.height + 2 * chatVerticalPadding
|
||||
|
||||
StatusSticker {
|
||||
id: stickerId
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: chatVerticalPadding
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: chatVerticalPadding
|
||||
contentType: root.contentType
|
||||
stickerData: root.sticker
|
||||
onLoaded: {
|
||||
if(!root.messageStore)
|
||||
return
|
||||
// Not refactored yet
|
||||
// root.messageStore.scrollToBottom(true, root.container)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MessageMouseArea {
|
||||
id: messageMouseArea
|
||||
anchors.fill: stickerLoader.active ? stickerLoader : chatText
|
||||
z: activityCenterMessage ? chatText.z + 1 : chatText.z -1
|
||||
enabled: !root.isChatBlocked && !placeholderMessage
|
||||
messageContextMenu: root.messageContextMenu
|
||||
messageContextMenuParent: root
|
||||
isHovered: root.isHovered
|
||||
isMessageActive: root.isMessageActive
|
||||
isActivityCenterMessage: activityCenterMessage
|
||||
stickersLoaded: root.stickersLoaded
|
||||
onClickMessage: {
|
||||
root.clickMessage(isProfileClick, isSticker, isImage, null, false, false, false, false, "");
|
||||
}
|
||||
onOpenStickerPackPopup: {
|
||||
root.openStickerPackPopup(root.stickerPack);
|
||||
}
|
||||
|
||||
onSetMessageActive: {
|
||||
root.setMessageActive(messageId, active);
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: linksLoader
|
||||
active: !!linkUrls
|
||||
height: item ? item.height : 0
|
||||
anchors.top: chatText.bottom
|
||||
anchors.topMargin: active ? Style.current.halfPadding : 0
|
||||
|
||||
sourceComponent: Component {
|
||||
LinksMessageView {
|
||||
linkUrls: root.linkUrls
|
||||
container: root.container
|
||||
messageStore: root.messageStore
|
||||
store: root.store
|
||||
isCurrentUser: root.amISender
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: audioPlayerLoader
|
||||
active: isAudio
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: active ? Style.current.halfPadding : 0
|
||||
|
||||
sourceComponent: Component {
|
||||
AudioPlayerPanel {
|
||||
audioSource: audio
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: transactionBubbleLoader
|
||||
active: contentType === Constants.messageContentType.transactionType
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: active ? (chatName.visible ? 4 : 6) : 0
|
||||
sourceComponent: Component {
|
||||
TransactionBubbleView {
|
||||
transactionParams: root.transactionParams
|
||||
store: root.store
|
||||
contactsStore: root.contactsStore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: contentType === Constants.messageContentType.communityInviteType
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: active ? 8 : 0
|
||||
sourceComponent: Component {
|
||||
id: invitationBubble
|
||||
InvitationBubbleView {
|
||||
store: root.store
|
||||
communityId: root.communityId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Retry {
|
||||
id: retry
|
||||
height: visible ? implicitHeight : 0
|
||||
anchors.left: chatTime.visible ? chatTime.right : messageContent.left
|
||||
anchors.leftMargin: chatTime.visible ? chatHorizontalPadding : 0
|
||||
anchors.top: chatTime.visible ? chatTime.top : messageContent.bottom
|
||||
anchors.topMargin: chatTime.visible ? 0 : -4
|
||||
anchors.bottom: chatTime.visible ? chatTime.bottom : undefined
|
||||
isCurrentUser: root.amISender
|
||||
isExpired: isExpired
|
||||
timeout: timeout
|
||||
onClicked: {
|
||||
// Not Refactored Yet
|
||||
// rootStore.chatsModelInst.messageView.resendMessage(chatId, messageId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: !activityCenterMessage && (hasMention || pinnedMessage)
|
||||
height: messageContainer.height
|
||||
anchors.left: messageContainer.left
|
||||
anchors.top: messageContainer.top
|
||||
|
||||
sourceComponent: Component {
|
||||
Rectangle {
|
||||
id: mentionBorder
|
||||
color: pinnedMessage ? Style.current.pinnedMessageBorder : Style.current.mentionColor
|
||||
width: 2
|
||||
height: parent.height
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
enabled: !activityCenterMessage && !chatLogView.flickingVertically &&
|
||||
(forceHoverHandler || (typeof root.messageContextMenu !== "undefined" && typeof Global.profilePopupOpened !== "undefined" &&
|
||||
!root.messageContextMenu.opened && !Global.profilePopupOpened && !Global.popupOpened))
|
||||
onHoveredChanged: {
|
||||
setHovered(messageId, hovered);
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: emojiReactionLoader
|
||||
active: reactionsModel.count > 0
|
||||
anchors.bottom: messageContainer.bottom
|
||||
anchors.bottomMargin: Style.current.halfPadding
|
||||
anchors.left: messageContainer.left
|
||||
anchors.leftMargin: messageContainer.messageContent.anchors.leftMargin
|
||||
|
||||
sourceComponent: Component {
|
||||
EmojiReactionsPanel {
|
||||
id: emojiRect
|
||||
store: root.messageStore
|
||||
emojiReactionsModel: reactionsModel
|
||||
onHoverChanged: {
|
||||
setHovered(messageId, hovered)
|
||||
}
|
||||
isMessageActive: isMessageActive
|
||||
isCurrentUser: root.amISender
|
||||
onAddEmojiClicked: {
|
||||
if(root.isChatBlocked)
|
||||
return
|
||||
|
||||
// First set parent, X & Y positions for the messageContextMenu
|
||||
root.messageContextMenu.parent = emojiRect
|
||||
root.messageContextMenu.setXPosition = function() { return (root.messageContextMenu.parent.x + root.messageContextMenu.parent.width + 4) }
|
||||
root.messageContextMenu.setYPosition = function() { return (-root.messageContextMenu.height - 4) }
|
||||
|
||||
// Second, add emoji that also triggers setXYPosition methods / open popup:
|
||||
root.addEmoji(false, false, false, null, true, false);
|
||||
}
|
||||
|
||||
onToggleReaction: {
|
||||
if(root.isChatBlocked)
|
||||
return
|
||||
|
||||
if(!root.messageStore)
|
||||
{
|
||||
console.error("reaction cannot be toggled, message store is not valid")
|
||||
return
|
||||
}
|
||||
|
||||
root.messageStore.toggleReaction(messageId, emojiID)
|
||||
}
|
||||
|
||||
onSetMessageActive: {
|
||||
root.setMessageActive(messageId, active);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -102,9 +102,6 @@ StatusPopupMenu {
|
|||
readonly property bool userTrustIsUnknown: d.contactDetails && d.contactDetails.trustStatus === Constants.trustStatus.unknown
|
||||
readonly property bool userIsUntrustworthy: d.contactDetails && d.contactDetails.trustStatus === Constants.trustStatus.untrustworthy
|
||||
|
||||
property var setXPosition: function() {return 0}
|
||||
property var setYPosition: function() {return 0}
|
||||
|
||||
property var emojiReactionsReactedByUser: []
|
||||
|
||||
signal openProfileClicked(string publicKey, string state)
|
||||
|
@ -142,14 +139,6 @@ StatusPopupMenu {
|
|||
d.contactDetails = {}
|
||||
}
|
||||
|
||||
onHeightChanged: { root.y = setYPosition(); }
|
||||
onWidthChanged: { root.x = setXPosition(); }
|
||||
onOpened: {
|
||||
// Trigger x and y position:
|
||||
x = setXPosition()
|
||||
y = setYPosition()
|
||||
}
|
||||
|
||||
width: Math.max(emojiContainer.visible ? emojiContainer.width : 0,
|
||||
(root.isRightClickOnImage && !root.pinnedPopup) ? 176 : 230)
|
||||
|
||||
|
@ -202,7 +191,8 @@ StatusPopupMenu {
|
|||
displayName: root.selectedUserDisplayName
|
||||
pubkey: root.selectedUserPublicKey
|
||||
icon: root.selectedUserIcon
|
||||
trustStatus: d.contactDetails.trustStatus
|
||||
trustStatus: d.contactDetails && d.contactDetails.trustStatus ? d.contactDetails.trustStatus
|
||||
: Constants.trustStatus.unknown
|
||||
isContact: root.isMyMutualContact
|
||||
isCurrentUser: root.isMe
|
||||
}
|
||||
|
|
|
@ -1,38 +1,24 @@
|
|||
import QtQuick 2.13
|
||||
|
||||
import StatusQ.Components 0.1
|
||||
|
||||
import utils 1.0
|
||||
import shared.panels 1.0
|
||||
import shared.status 1.0
|
||||
import shared.controls 1.0
|
||||
import shared.popups 1.0
|
||||
import shared.panels.chat 1.0
|
||||
import shared.views.chat 1.0
|
||||
import shared.controls.chat 1.0
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Components 0.1
|
||||
|
||||
Loader {
|
||||
id: root
|
||||
|
||||
width: parent.width
|
||||
z: (typeof chatLogView === "undefined") ? 1 : (chatLogView.count - index)
|
||||
|
||||
sourceComponent: {
|
||||
switch(contentType) {
|
||||
case Constants.messageContentType.chatIdentifier:
|
||||
return channelIdentifierComponent
|
||||
case Constants.messageContentType.fetchMoreMessagesButton:
|
||||
return fetchMoreMessagesButtonComponent
|
||||
case Constants.messageContentType.systemMessagePrivateGroupType:
|
||||
return privateGroupHeaderComponent
|
||||
case Constants.messageContentType.gapType:
|
||||
return gapComponent
|
||||
default:
|
||||
return compactMessageComponent
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
property var store
|
||||
property var rootStore
|
||||
property var messageStore
|
||||
property var usersStore
|
||||
property var contactsStore
|
||||
|
@ -54,30 +40,37 @@ Loader {
|
|||
property string senderId: ""
|
||||
property string senderDisplayName: ""
|
||||
property string senderLocalName: ""
|
||||
property string senderEnsName: ""
|
||||
property string senderIcon: ""
|
||||
property bool amISender: false
|
||||
property bool senderIsAdded: false
|
||||
property int senderTrustStatus: Constants.trustStatus.unknown
|
||||
readonly property string senderIconToShow: {
|
||||
if ((!senderIsAdded &&
|
||||
Global.privacyModuleInst.profilePicturesVisibility !==
|
||||
Constants.profilePicturesVisibility.everyone)) {
|
||||
Global.privacyModuleInst.profilePicturesVisibility !==
|
||||
Constants.profilePicturesVisibility.everyone)) {
|
||||
return ""
|
||||
}
|
||||
return senderIcon
|
||||
}
|
||||
property string message: ""
|
||||
property string messageText: ""
|
||||
property string messageImage: ""
|
||||
property string messageTimestamp: ""
|
||||
property double messageTimestamp: 0 // We use double, because QML's int is too small
|
||||
property string messageOutgoingStatus: ""
|
||||
property int messageContentType: 1
|
||||
property bool pinnedMessage: false
|
||||
property string messagePinnedBy: ""
|
||||
property var reactionsModel: []
|
||||
property string linkUrls: ""
|
||||
property bool isInPinnedPopup: false // The pinned popup limits the number of buttons shown
|
||||
property var transactionParams
|
||||
|
||||
// External behavior changers
|
||||
property bool isInPinnedPopup: false // The pinned popup limits the number of buttons shown
|
||||
property bool disableHover: false // Used to force the HoverHandler to be active (useful for messages in popups)
|
||||
property bool placeholderMessage: false
|
||||
property bool activityCenterMessage: false
|
||||
property bool activityCenterMessageRead: true
|
||||
|
||||
property int gapFrom: 0
|
||||
property int gapTo: 0
|
||||
|
||||
|
@ -86,94 +79,63 @@ Loader {
|
|||
property int nextMessageIndex: -1
|
||||
property var nextMessageAsJsonObj
|
||||
|
||||
property string hoveredMessage
|
||||
property string activeMessage
|
||||
property bool isHovered: typeof hoveredMessage !== "undefined" && hoveredMessage === messageId
|
||||
property bool isMessageActive: typeof activeMessage !== "undefined" && activeMessage === messageId
|
||||
|
||||
property bool editModeOn: false
|
||||
property bool isEdited: false
|
||||
|
||||
function setHovered(messageId, hovered) {
|
||||
if (hovered) {
|
||||
hoveredMessage = messageId;
|
||||
} else if (hoveredMessage === messageId) {
|
||||
hoveredMessage = "";
|
||||
}
|
||||
}
|
||||
property string responseTo: responseToMessageWithId
|
||||
|
||||
// Legacy
|
||||
property string responseTo: responseToMessageWithId
|
||||
property bool isCurrentUser: amISender
|
||||
property int contentType: messageContentType
|
||||
property string timestamp: messageTimestamp
|
||||
property string displayUserName: senderDisplayName
|
||||
property string outgoingStatus: messageOutgoingStatus
|
||||
property string authorCurrentMsg: senderId
|
||||
property string authorPrevMsg: {
|
||||
if(!prevMessageAsJsonObj ||
|
||||
// The system message for private groups appear as created by the group host, but it shouldn't
|
||||
prevMessageAsJsonObj.contentType === Constants.messageContentType.systemMessagePrivateGroupType) {
|
||||
// The system message for private groups appear as created by the group host, but it shouldn't
|
||||
prevMessageAsJsonObj.contentType === Constants.messageContentType.systemMessagePrivateGroupType) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return prevMessageAsJsonObj.senderId
|
||||
}
|
||||
property string prevMsgTimestamp: {
|
||||
if(!prevMessageAsJsonObj)
|
||||
return ""
|
||||
property double prevMsgTimestamp: prevMessageAsJsonObj ? prevMessageAsJsonObj.timestamp : 0
|
||||
property double nextMsgTimestamp: nextMessageAsJsonObj ? nextMessageAsJsonObj.timestamp : 0
|
||||
|
||||
return prevMessageAsJsonObj.timestamp
|
||||
}
|
||||
property string nextMsgTimestamp: {
|
||||
if(!nextMessageAsJsonObj)
|
||||
return ""
|
||||
property bool shouldRepeatHeader: ((messageTimestamp - prevMsgTimestamp) / 60 / 1000) > Constants.repeatHeaderInterval
|
||||
|
||||
return nextMessageAsJsonObj.timestamp
|
||||
}
|
||||
|
||||
property bool shouldRepeatHeader: ((parseInt(timestamp, 10) - parseInt(prevMsgTimestamp, 10)) / 60 / 1000) > Constants.repeatHeaderInterval
|
||||
|
||||
//////////////////////////////////////
|
||||
//TODO CHECCK - REMOVE
|
||||
property string plainText: "That's right. We're friends... Of justice, that is."
|
||||
property string emojiReactions: ""
|
||||
property bool timeout: false
|
||||
property bool hasMention: false
|
||||
property bool placeholderMessage: false
|
||||
property bool activityCenterMessage: false
|
||||
property bool read: true
|
||||
property bool forceHoverHandler: false // Used to force the HoverHandler to be active (useful for messages in popups)
|
||||
property string replaces: ""
|
||||
property bool isEdited: false
|
||||
property bool stickersLoaded: false
|
||||
//////////////////////////////////////
|
||||
|
||||
|
||||
property string sticker: "Qme8vJtyrEHxABcSVGPF95PtozDgUyfr1xGjePmFdZgk9v"
|
||||
property int stickerPack: -1
|
||||
property bool isEmoji: contentType === Constants.messageContentType.emojiType
|
||||
property bool isImage: contentType === Constants.messageContentType.imageType
|
||||
property bool isAudio: contentType === Constants.messageContentType.audioType
|
||||
property bool isStatusMessage: contentType === Constants.messageContentType.systemMessagePrivateGroupType
|
||||
property bool isSticker: contentType === Constants.messageContentType.stickerType
|
||||
property bool isText: contentType === Constants.messageContentType.messageType || contentType === Constants.messageContentType.editType
|
||||
property bool isMessage: isEmoji || isImage || isSticker || isText || isAudio
|
||||
|| contentType === Constants.messageContentType.communityInviteType || contentType === Constants.messageContentType.transactionType
|
||||
|
||||
property bool isExpired: (outgoingStatus === "sending" && (Math.floor(timestamp) + 180000) < Date.now())
|
||||
property bool isEmoji: messageContentType === Constants.messageContentType.emojiType
|
||||
property bool isImage: messageContentType === Constants.messageContentType.imageType
|
||||
property bool isAudio: messageContentType === Constants.messageContentType.audioType
|
||||
property bool isStatusMessage: messageContentType === Constants.messageContentType.systemMessagePrivateGroupType
|
||||
property bool isSticker: messageContentType === Constants.messageContentType.stickerType
|
||||
property bool isText: messageContentType === Constants.messageContentType.messageType || messageContentType === Constants.messageContentType.editType
|
||||
property bool isMessage: isEmoji || isImage || isSticker || isText || isAudio
|
||||
|| messageContentType === Constants.messageContentType.communityInviteType || messageContentType === Constants.messageContentType.transactionType
|
||||
|
||||
property bool isExpired: (outgoingStatus === "sending" && (Math.floor(messageTimestamp) + 180000) < Date.now())
|
||||
property int statusAgeEpoch: 0
|
||||
|
||||
signal imageClicked(var image)
|
||||
property var scrollToBottom: function () {}
|
||||
|
||||
property var clickMessage: function(isProfileClick,
|
||||
isSticker = false,
|
||||
isImage = false,
|
||||
image = null,
|
||||
isEmoji = false,
|
||||
hideEmojiPicker = false,
|
||||
isReply = false,
|
||||
isRightClickOnImage = false,
|
||||
imageSource = "") {
|
||||
// WARNING: To much arguments here. Create an object argument.
|
||||
property var messageClickHandler: function(sender, point,
|
||||
isProfileClick,
|
||||
isSticker = false,
|
||||
isImage = false,
|
||||
image = null,
|
||||
isEmoji = false,
|
||||
hideEmojiPicker = false,
|
||||
isReply = false,
|
||||
isRightClickOnImage = false,
|
||||
imageSource = "") {
|
||||
|
||||
if (placeholderMessage || activityCenterMessage) {
|
||||
return
|
||||
|
@ -188,7 +150,7 @@ Loader {
|
|||
messageContextMenu.messageSenderId = root.senderId
|
||||
messageContextMenu.messageContentType = root.messageContentType
|
||||
messageContextMenu.pinnedMessage = root.pinnedMessage
|
||||
messageContextMenu.canPin = messageStore.getNumberOfPinnedMessages() < Constants.maxNumberOfPins
|
||||
messageContextMenu.canPin = d.canPin
|
||||
|
||||
messageContextMenu.selectedUserPublicKey = root.senderId
|
||||
messageContextMenu.selectedUserDisplayName = root.senderDisplayName
|
||||
|
@ -202,7 +164,7 @@ Loader {
|
|||
messageContextMenu.isSticker = isSticker
|
||||
messageContextMenu.hideEmojiPicker = hideEmojiPicker
|
||||
|
||||
if(isReply){
|
||||
if (isReply){
|
||||
let obj = messageStore.getMessageByIdAsJson(responseTo)
|
||||
if(!obj)
|
||||
return
|
||||
|
@ -213,15 +175,16 @@ Loader {
|
|||
messageContextMenu.selectedUserIcon = obj.senderIcon
|
||||
}
|
||||
|
||||
messageContextMenu.popup()
|
||||
messageContextMenu.parent = sender;
|
||||
messageContextMenu.popup(point);
|
||||
}
|
||||
|
||||
signal showReplyArea(string messageId, string author)
|
||||
|
||||
|
||||
// function showReactionAuthors(fromAccounts, emojiId) {
|
||||
// return root.rootStore.showReactionAuthors(fromAccounts, emojiId)
|
||||
// }
|
||||
// function showReactionAuthors(fromAccounts, emojiId) {
|
||||
// return root.rootStore.showReactionAuthors(fromAccounts, emojiId)
|
||||
// }
|
||||
|
||||
function startMessageFoundAnimation() {
|
||||
root.item.startMessageFoundAnimation();
|
||||
|
@ -231,23 +194,76 @@ Loader {
|
|||
|
||||
signal openStickerPackPopup(string stickerPackId)
|
||||
// Not Refactored Yet
|
||||
// Connections {
|
||||
// enabled: (!placeholderMessage && !!root.rootStore)
|
||||
// target: !!root.rootStore ? root.rootStore.allContacts : null
|
||||
// onContactChanged: {
|
||||
// if (pubkey === fromAuthor) {
|
||||
// const img = appMain.getProfileImage(userPubKey, isCurrentUser, useLargeImage)
|
||||
// if (img) {
|
||||
// profileImageSource = img
|
||||
// }
|
||||
// } else if (replyMessageIndex > -1 && pubkey === repliedMessageAuthorPubkey) {
|
||||
// const imgReply = appMain.getProfileImage(repliedMessageAuthorPubkey, repliedMessageAuthorIsCurrentUser, false)
|
||||
// if (imgReply) {
|
||||
// repliedMessageUserImage = imgReply
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Connections {
|
||||
// enabled: (!placeholderMessage && !!root.rootStore)
|
||||
// target: !!root.rootStore ? root.rootStore.allContacts : null
|
||||
// onContactChanged: {
|
||||
// if (pubkey === fromAuthor) {
|
||||
// const img = appMain.getProfileImage(userPubKey, isCurrentUser, useLargeImage)
|
||||
// if (img) {
|
||||
// profileImageSource = img
|
||||
// }
|
||||
// } else if (replyMessageIndex > -1 && pubkey === repliedMessageAuthorPubkey) {
|
||||
// const imgReply = appMain.getProfileImage(repliedMessageAuthorPubkey, repliedMessageAuthorIsCurrentUser, false)
|
||||
// if (imgReply) {
|
||||
// repliedMessageUserImage = imgReply
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
z: (typeof chatLogView === "undefined") ? 1 : (chatLogView.count - index)
|
||||
|
||||
sourceComponent: {
|
||||
switch(messageContentType) {
|
||||
case Constants.messageContentType.chatIdentifier:
|
||||
return channelIdentifierComponent
|
||||
case Constants.messageContentType.fetchMoreMessagesButton:
|
||||
return fetchMoreMessagesButtonComponent
|
||||
case Constants.messageContentType.systemMessagePrivateGroupType:
|
||||
return privateGroupHeaderComponent
|
||||
case Constants.messageContentType.gapType:
|
||||
return gapComponent
|
||||
default:
|
||||
return messageComponent
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
readonly property bool canPin: !!messageStore &&
|
||||
messageStore.getNumberOfPinnedMessages() < Constants.maxNumberOfPins
|
||||
readonly property int chatButtonSize: 32
|
||||
|
||||
property string activeMessage
|
||||
readonly property bool isMessageActive: typeof activeMessage !== "undefined" && activeMessage === messageId
|
||||
|
||||
function setMessageActive(messageId, active) {
|
||||
|
||||
// TODO: Is argument messageId actually needed?
|
||||
// It was probably used with dynamic scoping,
|
||||
// but not this method can be moved to private `d`.
|
||||
// Probably that it was done this way, because `MessageView` is reused as delegate.
|
||||
|
||||
if (active) {
|
||||
d.activeMessage = messageId;
|
||||
return;
|
||||
}
|
||||
if (d.activeMessage === messageId) {
|
||||
d.activeMessage = "";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
enabled: d.isMessageActive
|
||||
target: root.messageContextMenu
|
||||
onClosed: {
|
||||
d.setMessageActive(root.messageId, false)
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: gapComponent
|
||||
|
@ -293,18 +309,18 @@ Loader {
|
|||
wrapMode: Text.Wrap
|
||||
text: {
|
||||
return `<html>`+
|
||||
`<head>`+
|
||||
`<style type="text/css">`+
|
||||
`a {`+
|
||||
`<head>`+
|
||||
`<style type="text/css">`+
|
||||
`a {`+
|
||||
`color: ${Style.current.textColor};`+
|
||||
`text-decoration: none;`+
|
||||
`}`+
|
||||
`</style>`+
|
||||
`</head>`+
|
||||
`<body>`+
|
||||
`${message}`+
|
||||
`</body>`+
|
||||
`</html>`;
|
||||
`}`+
|
||||
`</style>`+
|
||||
`</head>`+
|
||||
`<body>`+
|
||||
`${messageText}`+
|
||||
`</body>`+
|
||||
`</html>`;
|
||||
}
|
||||
visible: isStatusMessage
|
||||
font.pixelSize: 14
|
||||
|
@ -318,60 +334,465 @@ Loader {
|
|||
}
|
||||
|
||||
Component {
|
||||
id: compactMessageComponent
|
||||
id: messageComponent
|
||||
|
||||
CompactMessageView {
|
||||
container: root
|
||||
store: root.store
|
||||
message: root.message
|
||||
messageStore: root.messageStore
|
||||
usersStore: root.usersStore
|
||||
contactsStore: root.contactsStore
|
||||
messageContextMenu: root.messageContextMenu
|
||||
contentType: root.messageContentType
|
||||
isChatBlocked: root.isChatBlocked
|
||||
isActiveChannel: root.isActiveChannel
|
||||
emojiPopup: root.emojiPopup
|
||||
senderTrustStatus: root.senderTrustStatus
|
||||
chatLogView: root.chatLogView
|
||||
StatusMessage {
|
||||
id: delegate
|
||||
|
||||
prevMessageIndex: root.prevMessageIndex
|
||||
prevMsgTimestamp: root.prevMsgTimestamp
|
||||
messageTimestamp: root.messageTimestamp
|
||||
|
||||
communityId: root.communityId
|
||||
stickersLoaded: root.stickersLoaded
|
||||
sticker: root.sticker
|
||||
stickerPack: root.stickerPack
|
||||
isMessageActive: root.isMessageActive
|
||||
senderIcon: root.senderIconToShow
|
||||
amISender: root.amISender
|
||||
isHovered: root.isHovered
|
||||
editModeOn: root.editModeOn
|
||||
linkUrls: root.linkUrls
|
||||
isInPinnedPopup: root.isInPinnedPopup
|
||||
pinnedMessage: root.pinnedMessage
|
||||
canPin: !!messageStore && messageStore.getNumberOfPinnedMessages() < Constants.maxNumberOfPins
|
||||
|
||||
transactionParams: root.transactionParams
|
||||
|
||||
onAddEmoji: {
|
||||
root.clickMessage(isProfileClick, isSticker, isImage , image, isEmoji, hideEmojiPicker)
|
||||
function convertContentType(value) {
|
||||
switch (value) {
|
||||
case Constants.messageContentType.messageType:
|
||||
return StatusMessage.ContentType.Text;
|
||||
case Constants.messageContentType.stickerType:
|
||||
return StatusMessage.ContentType.Sticker;
|
||||
case Constants.messageContentType.emojiType:
|
||||
return StatusMessage.ContentType.Emoji;
|
||||
case Constants.messageContentType.transactionType:
|
||||
return StatusMessage.ContentType.Transaction;
|
||||
case Constants.messageContentType.imageType:
|
||||
return StatusMessage.ContentType.Image;
|
||||
case Constants.messageContentType.audioType:
|
||||
return StatusMessage.ContentType.Audio;
|
||||
case Constants.messageContentType.communityInviteType:
|
||||
return StatusMessage.ContentType.Invitation;
|
||||
case Constants.messageContentType.fetchMoreMessagesButton:
|
||||
case Constants.messageContentType.chatIdentifier:
|
||||
case Constants.messageContentType.unknownContentType:
|
||||
case Constants.messageContentType.statusType:
|
||||
case Constants.messageContentType.systemMessagePrivateGroupType:
|
||||
case Constants.messageContentType.gapType:
|
||||
case Constants.messageContentType.editType:
|
||||
default:
|
||||
return StatusMessage.ContentType.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
onClickMessage: {
|
||||
root.clickMessage(isProfileClick, isSticker, isImage, image, isEmoji, hideEmojiPicker, isReply, isRightClickOnImage, imageSource)
|
||||
readonly property int contentType: convertContentType(root.messageContentType)
|
||||
readonly property bool isReply: root.responseTo !== ""
|
||||
readonly property var replyMessage: root.messageStore && isReply ? root.messageStore.getMessageByIdAsJson(root.responseTo) : null
|
||||
readonly property string replySenderId: replyMessage ? replyMessage.senderId : ""
|
||||
|
||||
function editCompletedHandler(newMessageText) {
|
||||
const message = root.rootStore.plainText(StatusQUtils.Emoji.deparse(newMessageText))
|
||||
if (message.length <= 0)
|
||||
return;
|
||||
|
||||
const interpretedMessage = root.messageStore.interpretMessage(message)
|
||||
root.messageStore.setEditModeOff(root.messageId)
|
||||
root.messageStore.editMessage(root.messageId, interpretedMessage)
|
||||
}
|
||||
|
||||
onOpenStickerPackPopup: {
|
||||
root.openStickerPackPopup(stickerPackId);
|
||||
audioMessageInfoText: qsTr("Audio Message")
|
||||
cancelButtonText: qsTr("Cancel")
|
||||
saveButtonText: qsTr("Save")
|
||||
loadingImageText: qsTr("Loading image...")
|
||||
errorLoadingImageText: qsTr("Error loading the image")
|
||||
resendText: qsTr("Resend")
|
||||
pinnedMsgInfoText: qsTr("Pinned by")
|
||||
reactionIcons: [
|
||||
Style.svg("emojiReactions/heart"),
|
||||
Style.svg("emojiReactions/thumbsUp"),
|
||||
Style.svg("emojiReactions/thumbsDown"),
|
||||
Style.svg("emojiReactions/laughing"),
|
||||
Style.svg("emojiReactions/sad"),
|
||||
Style.svg("emojiReactions/angry"),
|
||||
]
|
||||
|
||||
timestamp: root.messageTimestamp
|
||||
editMode: root.editModeOn
|
||||
isAReply: delegate.isReply
|
||||
isEdited: root.isEdited
|
||||
hasMention: root.hasMention
|
||||
isPinned: root.pinnedMessage
|
||||
pinnedBy: root.pinnedMessage ? Utils.getContactDetailsAsJson(root.messagePinnedBy).displayName : ""
|
||||
hasExpired: root.isExpired
|
||||
reactionsModel: root.reactionsModel
|
||||
|
||||
previousMessageIndex: root.prevMessageIndex
|
||||
previousMessageTimestamp: root.prevMsgTimestamp
|
||||
|
||||
showHeader: root.authorCurrentMsg !== root.authorPrevMsg ||
|
||||
root.shouldRepeatHeader || dateGroupVisible || isAReply
|
||||
isActiveMessage: d.isMessageActive
|
||||
|
||||
disableHover: root.disableHover ||
|
||||
(root.chatLogView && root.chatLogView.flickingVertically) ||
|
||||
activityCenterMessage ||
|
||||
root.messageContextMenu.opened ||
|
||||
!!Global.profilePopupOpened ||
|
||||
!!Global.popupOpened
|
||||
|
||||
hideQuickActions: root.isChatBlocked ||
|
||||
root.placeholderMessage ||
|
||||
root.activityCenterMessage
|
||||
|
||||
overrideBackground: root.activityCenterMessage || root.placeholderMessage
|
||||
overrideBackgroundColor: {
|
||||
if (root.activityCenterMessage && root.activityCenterMessageRead)
|
||||
return Utils.setColorAlpha(Style.current.blue, 0.1);
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
onReplyClicked: {
|
||||
root.showReplyArea(messageId, author)
|
||||
|
||||
timestampString: Utils.formatShortTime(timestamp,
|
||||
localAccountSensitiveSettings.is24hTimeFormat)
|
||||
|
||||
timestampTooltipString: Utils.formatLongDateTime(timestamp,
|
||||
localAccountSensitiveSettings.isDDMMYYDateFormat,
|
||||
localAccountSensitiveSettings.is24hTimeFormat);
|
||||
|
||||
onEditCancelled: {
|
||||
root.messageStore.setEditModeOff(root.messageId)
|
||||
}
|
||||
|
||||
onImageClicked: root.imageClicked(image)
|
||||
onEditCompleted: {
|
||||
delegate.editCompletedHandler(newMsgText)
|
||||
}
|
||||
|
||||
onImageClicked: {
|
||||
switch (mouse.button) {
|
||||
case Qt.LeftButton:
|
||||
root.imageClicked(image, mouse);
|
||||
break;
|
||||
case Qt.RightButton:
|
||||
root.messageClickHandler(image, Qt.point(mouse.x, mouse.y), false, false, true, image, false, true, false, true, imageSource)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onLinkActivated: {
|
||||
if (link.startsWith('//')) {
|
||||
const pubkey = link.replace("//", "");
|
||||
Global.openProfilePopup(pubkey)
|
||||
return;
|
||||
}
|
||||
|
||||
Global.openLink(link)
|
||||
}
|
||||
|
||||
onProfilePictureClicked: {
|
||||
d.setMessageActive(root.messageId, true);
|
||||
root.messageClickHandler(sender, Qt.point(mouse.x, mouse.y), true);
|
||||
}
|
||||
|
||||
onReplyProfileClicked: {
|
||||
d.setMessageActive(root.messageId, true);
|
||||
root.messageClickHandler(sender, Qt.point(mouse.x, mouse.y), true, false, false, null, false, false, true);
|
||||
}
|
||||
|
||||
onSenderNameClicked: {
|
||||
d.setMessageActive(root.messageId, true);
|
||||
root.messageClickHandler(sender, Qt.point(mouse.x, mouse.y), true);
|
||||
}
|
||||
|
||||
onToggleReactionClicked: {
|
||||
if (root.isChatBlocked)
|
||||
return
|
||||
|
||||
if (!root.messageStore) {
|
||||
console.error("Reaction can not be toggled, message store is not valid")
|
||||
return
|
||||
}
|
||||
|
||||
root.messageStore.toggleReaction(root.messageId, emojiId)
|
||||
}
|
||||
|
||||
onAddReactionClicked: {
|
||||
if (root.isChatBlocked)
|
||||
return;
|
||||
|
||||
d.setMessageActive(root.messageId, true);
|
||||
root.messageClickHandler(sender, Qt.point(mouse.x, mouse.y), false, false, false, null, true, false);
|
||||
}
|
||||
|
||||
onStickerClicked: {
|
||||
root.openStickerPackPopup(root.stickerPack);
|
||||
}
|
||||
|
||||
mouseArea {
|
||||
acceptedButtons: root.activityCenterMessage ? Qt.LeftButton : Qt.RightButton
|
||||
enabled: !root.isChatBlocked &&
|
||||
!root.placeholderMessage &&
|
||||
delegate.contentType !== StatusMessage.ContentType.Image
|
||||
onClicked: {
|
||||
d.setMessageActive(root.messageId, true);
|
||||
root.messageClickHandler(this, Qt.point(mouse.x, mouse.y),
|
||||
false, false, false, null, root.isEmoji, false, false, false, "");
|
||||
}
|
||||
}
|
||||
|
||||
messageDetails: StatusMessageDetails {
|
||||
contentType: delegate.contentType
|
||||
messageText: root.messageText
|
||||
messageContent: {
|
||||
switch (delegate.contentType)
|
||||
{
|
||||
case StatusMessage.ContentType.Sticker:
|
||||
return root.sticker;
|
||||
case StatusMessage.ContentType.Image:
|
||||
return root.messageImage;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
amISender: root.amISender
|
||||
sender.id: root.senderId
|
||||
sender.userName: root.senderDisplayName
|
||||
sender.localName: root.senderLocalName
|
||||
sender.ensName: root.senderEnsName
|
||||
sender.isContact: root.senderIsAdded
|
||||
sender.trustIndicator: root.senderTrustStatus
|
||||
sender.profileImage {
|
||||
width: 40
|
||||
height: 40
|
||||
pubkey: root.senderId
|
||||
source: root.senderIcon || ""
|
||||
colorId: Utils.colorIdForPubkey(root.senderId)
|
||||
colorHash: Utils.getColorHashAsJson(root.senderId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
replyDetails: StatusMessageDetails {
|
||||
messageText: delegate.replyMessage ? delegate.replyMessage.messageText : ""
|
||||
contentType: delegate.replyMessage ? delegate.convertContentType(delegate.replyMessage.contentType) : 0
|
||||
messageContent: {
|
||||
if (!delegate.replyMessage)
|
||||
return "";
|
||||
switch (contentType) {
|
||||
case StatusMessage.ContentType.Sticker:
|
||||
return delegate.replyMessage.sticker;
|
||||
case StatusMessage.ContentType.Image:
|
||||
return delegate.replyMessage.messageImage;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
amISender: delegate.replyMessage && delegate.replyMessage.amISender
|
||||
sender.id: delegate.replyMessage ? delegate.replyMessage.senderId : ""
|
||||
sender.isContact: delegate.replyMessage && delegate.replyMessage.senderIsAdded
|
||||
sender.userName: delegate.replyMessage ? delegate.replyMessage.senderDisplayName: ""
|
||||
sender.ensName: delegate.replyMessage && delegate.replyMessage.ensVerified ? delegate.replyMessage.senderDisplayName : ""
|
||||
sender.localName: delegate.replyMessage ? delegate.replyMessage.senderLocalName : ""
|
||||
sender.profileImage {
|
||||
width: 20
|
||||
height: 20
|
||||
pubkey: delegate.replySenderId
|
||||
source: delegate.replyMessage ? delegate.replyMessage.senderIcon: ""
|
||||
colorId: Utils.colorIdForPubkey(delegate.replySenderId)
|
||||
colorHash: Utils.getColorHashAsJson(delegate.replySenderId)
|
||||
}
|
||||
}
|
||||
|
||||
statusChatInput: StatusChatInput {
|
||||
id: editTextInput
|
||||
|
||||
readonly property string messageText: editTextInput.textInput.text
|
||||
|
||||
// TODO: Move this property and Escape handler to StatusChatInput
|
||||
property bool suggestionsOpened: false
|
||||
|
||||
width: parent.width
|
||||
|
||||
Keys.onEscapePressed: {
|
||||
if (!suggestionsOpened) {
|
||||
delegate.editCancelled()
|
||||
}
|
||||
suggestionsOpened = false
|
||||
}
|
||||
|
||||
store: root.rootStore
|
||||
usersStore: root.usersStore
|
||||
emojiPopup: root.emojiPopup
|
||||
messageContextMenu: root.messageContextMenu
|
||||
|
||||
chatType: root.messageStore.getChatType()
|
||||
isEdit: true
|
||||
|
||||
onSendMessage: {
|
||||
delegate.editCompletedHandler(editTextInput.textInput.text)
|
||||
}
|
||||
|
||||
suggestions.onVisibleChanged: {
|
||||
if (suggestions.visible) {
|
||||
suggestionsOpened = true
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
parseMessage(root.messageText);
|
||||
}
|
||||
}
|
||||
|
||||
linksComponent: Component {
|
||||
LinksMessageView {
|
||||
linkUrls: root.linkUrls
|
||||
container: root
|
||||
messageStore: root.messageStore
|
||||
store: root.rootStore
|
||||
isCurrentUser: root.amISender
|
||||
}
|
||||
}
|
||||
|
||||
transcationComponent: Component {
|
||||
TransactionBubbleView {
|
||||
transactionParams: root.transactionParams
|
||||
store: root.rootStore
|
||||
contactsStore: root.contactsStore
|
||||
}
|
||||
}
|
||||
|
||||
invitationComponent: Component {
|
||||
InvitationBubbleView {
|
||||
store: root.rootStore
|
||||
communityId: root.communityId
|
||||
}
|
||||
}
|
||||
|
||||
quickActions: [
|
||||
Loader {
|
||||
active: !root.isInPinnedPopup
|
||||
sourceComponent: StatusFlatRoundButton {
|
||||
width: d.chatButtonSize
|
||||
height: d.chatButtonSize
|
||||
icon.name: "reaction-b"
|
||||
type: StatusFlatRoundButton.Type.Tertiary
|
||||
tooltip.text: qsTr("Add reaction")
|
||||
onClicked: {
|
||||
d.setMessageActive(root.messageId, true)
|
||||
root.messageClickHandler(this, Qt.point(mouse.x, mouse.y), false, false, false, null, true, false)
|
||||
}
|
||||
}
|
||||
},
|
||||
Loader {
|
||||
active: !root.isInPinnedPopup
|
||||
sourceComponent: StatusFlatRoundButton {
|
||||
width: d.chatButtonSize
|
||||
height: d.chatButtonSize
|
||||
icon.name: "reply"
|
||||
type: StatusFlatRoundButton.Type.Tertiary
|
||||
tooltip.text: qsTr("Reply")
|
||||
onClicked: {
|
||||
root.showReplyArea(root.messageId, root.senderId)
|
||||
if (messageContextMenu.closeParentPopup) {
|
||||
messageContextMenu.closeParentPopup()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Loader {
|
||||
active: !root.isInPinnedPopup && root.isText && !root.editModeOn && root.amISender
|
||||
visible: active
|
||||
sourceComponent: StatusFlatRoundButton {
|
||||
width: d.chatButtonSize
|
||||
height: d.chatButtonSize
|
||||
icon.name: "edit_pencil"
|
||||
type: StatusFlatRoundButton.Type.Tertiary
|
||||
tooltip.text: qsTr("Edit")
|
||||
onClicked: {
|
||||
root.messageStore.setEditModeOn(root.messageId)
|
||||
}
|
||||
}
|
||||
},
|
||||
Loader {
|
||||
active: {
|
||||
if (!root.messageStore)
|
||||
return false
|
||||
|
||||
const chatType = root.messageStore.getChatType();
|
||||
const amIChatAdmin = root.messageStore.amIChatAdmin();
|
||||
const pinMessageAllowedForMembers = root.messageStore.pinMessageAllowedForMembers()
|
||||
|
||||
return chatType === Constants.chatType.oneToOne ||
|
||||
chatType === Constants.chatType.privateGroupChat && amIChatAdmin ||
|
||||
chatType === Constants.chatType.communityChat && (amIChatAdmin || pinMessageAllowedForMembers);
|
||||
|
||||
}
|
||||
sourceComponent: StatusFlatRoundButton {
|
||||
width: d.chatButtonSize
|
||||
height: d.chatButtonSize
|
||||
icon.name: root.pinnedMessage ? "unpin" : "pin"
|
||||
type: StatusFlatRoundButton.Type.Tertiary
|
||||
tooltip.text: root.pinnedMessage ? qsTr("Unpin") : qsTr("Pin")
|
||||
onClicked: {
|
||||
if (root.pinnedMessage) {
|
||||
messageStore.unpinMessage(root.messageId)
|
||||
return;
|
||||
}
|
||||
|
||||
if (d.canPin) {
|
||||
messageStore.pinMessage(root.messageId)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!chatContentModule) {
|
||||
console.warn("error on open pinned messages limit reached from message context menu - chat content module is not set")
|
||||
return;
|
||||
}
|
||||
|
||||
Global.openPopup(pinnedMessagesPopupComponent, {
|
||||
store: root.rootStore,
|
||||
messageStore: messageStore,
|
||||
pinnedMessagesModel: chatContentModule.pinnedMessagesModel,
|
||||
messageToPin: root.messageId
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
Loader {
|
||||
active: {
|
||||
if (root.isInPinnedPopup)
|
||||
return false;
|
||||
if (!root.messageStore)
|
||||
return false;
|
||||
const isMyMessage = senderId !== "" && senderId === userProfile.pubKey;
|
||||
const chatType = root.messageStore.getChatType();
|
||||
return isMyMessage &&
|
||||
(messageContentType === Constants.messageContentType.messageType ||
|
||||
messageContentType === Constants.messageContentType.stickerType ||
|
||||
messageContentType === Constants.messageContentType.emojiType ||
|
||||
messageContentType === Constants.messageContentType.imageType ||
|
||||
messageContentType === Constants.messageContentType.audioType);
|
||||
}
|
||||
sourceComponent: StatusFlatRoundButton {
|
||||
width: d.chatButtonSize
|
||||
height: d.chatButtonSize
|
||||
icon.name: "delete"
|
||||
type: StatusFlatRoundButton.Type.Tertiary
|
||||
tooltip.text: qsTr("Delete")
|
||||
onClicked: {
|
||||
if (!localAccountSensitiveSettings.showDeleteMessageWarning) {
|
||||
messageStore.deleteMessage(root.messageId)
|
||||
}
|
||||
else {
|
||||
Global.openPopup(deleteMessageConfirmationDialogComponent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: deleteMessageConfirmationDialogComponent
|
||||
|
||||
ConfirmationDialog {
|
||||
header.title: qsTrId("Confirm deleting this message")
|
||||
confirmationText: qsTrId("Are you sure you want to delete this message? Be aware that other clients are not guaranteed to delete the message as well.")
|
||||
height: 260
|
||||
checkbox.visible: true
|
||||
executeConfirm: function () {
|
||||
if (checkbox.checked) {
|
||||
localAccountSensitiveSettings.showDeleteMessageWarning = false
|
||||
}
|
||||
|
||||
close()
|
||||
messageStore.deleteMessage(root.messageId)
|
||||
}
|
||||
onClosed: {
|
||||
destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -250,7 +250,7 @@ Item {
|
|||
StyledText {
|
||||
id: timeText
|
||||
color: Style.current.secondaryText
|
||||
text: Utils.formatShortTime(timestamp, RootStore.accountSensitiveSettings.is24hTimeFormat)
|
||||
text: Utils.formatShortTime(messageTimestamp, RootStore.accountSensitiveSettings.is24hTimeFormat)
|
||||
anchors.left: bubbleLoader.active ? bubbleLoader.right : undefined
|
||||
anchors.leftMargin: bubbleLoader.active ? 13 : 0
|
||||
anchors.right: bubbleLoader.active ? undefined : parent.right
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -478,6 +478,11 @@ QtObject {
|
|||
readonly property string ens_connected: "connected"
|
||||
readonly property string ens_connected_dkey: "connected-different-key"
|
||||
|
||||
readonly property string storeToKeychainValueStore: "store"
|
||||
readonly property string storeToKeychainValueNotNow: "notNow"
|
||||
readonly property string storeToKeychainValueNever: "never"
|
||||
|
||||
// WARNING: Remove later. Moved to StatusQ.
|
||||
readonly property string editLabel: ` <span class="isEdited">` + qsTr("(edited)") + `</span>`
|
||||
|
||||
readonly property string newBookmark: " "
|
||||
|
|
|
@ -4,6 +4,7 @@ import QtQuick 2.13
|
|||
|
||||
import shared 1.0
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
||||
|
||||
QtObject {
|
||||
property var globalUtilsInst: globalUtils
|
||||
|
@ -56,55 +57,6 @@ QtObject {
|
|||
return Style.current.accountColors[colorIndex]
|
||||
}
|
||||
|
||||
function getMessageWithStyle(msg, isCurrentUser, hoveredLink = "") {
|
||||
return `<style type="text/css">` +
|
||||
`img, a, del, code, blockquote { margin: 0; padding: 0; }` +
|
||||
`code {` +
|
||||
`font-family: ${Style.current.fontCodeRegular.name};` +
|
||||
`font-weight: 400;` +
|
||||
`font-size: ${Style.current.secondaryTextFontSize};` +
|
||||
`padding: 2px 4px;` +
|
||||
`border-radius: 4px;` +
|
||||
`background-color: ${Style.current.codeBackground};` +
|
||||
`color: ${Style.current.black};` +
|
||||
`white-space: pre;` +
|
||||
`}` +
|
||||
`p {` +
|
||||
`line-height: 22px;` +
|
||||
`}` +
|
||||
`a {` +
|
||||
`color: ${Style.current.linkColor};` +
|
||||
`}` +
|
||||
`a.mention {` +
|
||||
`color: ${Style.current.mentionColor};` +
|
||||
`background-color: ${Style.current.mentionBgColor};` +
|
||||
`text-decoration: none;` +
|
||||
`padding: 0px 2px;` +
|
||||
`}` +
|
||||
(hoveredLink !== "" ? `a.mention[href="${hoveredLink}"] { background-color: ${Style.current.mentionBgHoverColor}; }` : ``) +
|
||||
`del {` +
|
||||
`text-decoration: line-through;` +
|
||||
`}` +
|
||||
`table.blockquote td {` +
|
||||
`padding-left: 10px;` +
|
||||
`color: ${isCurrentUser ? Style.current.chatReplyCurrentUser : Style.current.secondaryText};` +
|
||||
`}` +
|
||||
`table.blockquote td.quoteline {` +
|
||||
`background-color: ${isCurrentUser ? Style.current.chatReplyCurrentUser : Style.current.secondaryText};` +
|
||||
`height: 100%;` +
|
||||
`padding-left: 0;` +
|
||||
`}` +
|
||||
`.emoji {` +
|
||||
`vertical-align: bottom;` +
|
||||
`}` +
|
||||
`span.isEdited {` +
|
||||
`color: ${Style.current.secondaryText};` +
|
||||
`margin-left: 5px` +
|
||||
`}` +
|
||||
`</style>` +
|
||||
`${msg}`
|
||||
}
|
||||
|
||||
function getReplyMessageStyle(msg, isCurrentUser) {
|
||||
return `<style type="text/css">`+
|
||||
`a {`+
|
||||
|
@ -147,22 +99,6 @@ QtObject {
|
|||
return addr.substring(0, 2 + numberOfChars) + "..." + addr.substring(addr.length - numberOfChars);
|
||||
}
|
||||
|
||||
function linkifyAndXSS(inputText) {
|
||||
//URLs starting with http://, https://, or ftp://
|
||||
var replacePattern1 = /(\b(https?|ftp|statusim):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
|
||||
var replacedText = inputText.replace(replacePattern1, "<a href='$1'>$1</a>");
|
||||
|
||||
//URLs starting with "www." (without // before it, or it'd re-link the ones done above).
|
||||
var replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
|
||||
replacedText = replacedText.replace(replacePattern2, "$1<a href='http://$2'>$2</a>");
|
||||
|
||||
return XSS.filterXSS(replacedText)
|
||||
}
|
||||
|
||||
function filterXSS(inputText) {
|
||||
return XSS.filterXSS(inputText)
|
||||
}
|
||||
|
||||
function toLocaleString(val, locale, options) {
|
||||
return NumberPolyFill.toLocaleString(val, locale, options)
|
||||
}
|
||||
|
@ -656,11 +592,7 @@ QtObject {
|
|||
return ""
|
||||
}
|
||||
let compressedPk = getCompressedPk(publicKey)
|
||||
return elideText(compressedPk, 6, 3)
|
||||
}
|
||||
|
||||
function elideText(text, leftCharsCount, rightCharsCount = leftCharsCount) {
|
||||
return text.substr(0, leftCharsCount) + "..." + text.substr(text.length - rightCharsCount)
|
||||
return StatusQUtils.Utils.elideText(compressedPk, 6, 3)
|
||||
}
|
||||
|
||||
function getTimeDifference(d1, d2) {
|
||||
|
|
Loading…
Reference in New Issue