Feat/request payment cards 16737 (#16740)

* feat: Payment Request cards

* feat: Card fixes
This commit is contained in:
Cuteivist 2024-12-03 09:36:04 +01:00 committed by GitHub
parent 9490dbb57e
commit 7998b0640f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 492 additions and 9 deletions

View File

@ -32,6 +32,7 @@ SplitView {
width: parent.width width: parent.width
imagePreviewArray: ["https://picsum.photos/200/300?random=1", "https://picsum.photos/200/300?random=1"] imagePreviewArray: ["https://picsum.photos/200/300?random=1", "https://picsum.photos/200/300?random=1"]
linkPreviewModel: showLinkPreviewSettings ? emptyModel : mockedLinkPreviewModel linkPreviewModel: showLinkPreviewSettings ? emptyModel : mockedLinkPreviewModel
paymentRequestModel: mockedPaymentRequestModel
showLinkPreviewSettings: !linkPreviewEnabledSwitch.checked showLinkPreviewSettings: !linkPreviewEnabledSwitch.checked
visible: hasContent visible: hasContent
@ -82,6 +83,10 @@ SplitView {
LinkPreviewModel { LinkPreviewModel {
id: mockedLinkPreviewModel id: mockedLinkPreviewModel
} }
PaymentRequestModel {
id: mockedPaymentRequestModel
}
} }
// category: Panels // category: Panels

View File

@ -13,6 +13,10 @@ SplitView {
id: mockedLinkPreviewModel id: mockedLinkPreviewModel
} }
PaymentRequestModel {
id: mockedPaymentRequestModel
}
Pane { Pane {
id: messageViewWrapper id: messageViewWrapper
SplitView.fillWidth: true SplitView.fillWidth: true
@ -27,6 +31,10 @@ SplitView {
playAnimations: true playAnimations: true
linkPreviewModel: mockedLinkPreviewModel linkPreviewModel: mockedLinkPreviewModel
gifLinks: [ "https://media.tenor.com/qN_ytiwLh24AAAAC/cold.gif" ] gifLinks: [ "https://media.tenor.com/qN_ytiwLh24AAAAC/cold.gif" ]
paymentRequestModel: mockedPaymentRequestModel
areTestNetworksEnabled: false
senderName: "Alice"
gifUnfurlingEnabled: false gifUnfurlingEnabled: false
canAskToUnfurlGifs: true canAskToUnfurlGifs: true
@ -70,6 +78,11 @@ SplitView {
checked: linksMessageView.isOnline checked: linksMessageView.isOnline
onToggled: linksMessageView.isOnline = !linksMessageView.isOnline onToggled: linksMessageView.isOnline = !linksMessageView.isOnline
} }
CheckBox {
text: qsTr("Testnet enabled")
checked: linksMessageView.areTestNetworksEnabled
onToggled: linksMessageView.areTestNetworksEnabled = !linksMessageView.areTestNetworksEnabled
}
} }
} }
} }

View File

@ -17,6 +17,8 @@ SplitView {
QtObject { QtObject {
id: d id: d
readonly property var exampleAlbum: [ModelsData.banners.coinbase, ModelsData.icons.status]
readonly property var messagesModel: ListModel { readonly property var messagesModel: ListModel {
ListElement { ListElement {
timestamp: 1656937930123 timestamp: 1656937930123
@ -152,6 +154,17 @@ SplitView {
trustIndicator: StatusContactVerificationIcons.TrustedType.None trustIndicator: StatusContactVerificationIcons.TrustedType.None
outgoingStatus: StatusMessage.OutgoingStatus.Delivered outgoingStatus: StatusMessage.OutgoingStatus.Delivered
} }
ListElement {
timestamp: 1667937830123
senderId: "zq123456790"
senderDisplayName: "Alice"
contentType: StatusMessage.ContentType.Image
message: "This message contains images"
isContact: true
isAReply: false
trustIndicator: StatusContactVerificationIcons.TrustedType.None
outgoingStatus: StatusMessage.OutgoingStatus.Delivered
}
} }
readonly property var colorHash: ListModel { readonly property var colorHash: ListModel {
ListElement { colorId: 13; segmentLength: 5 } ListElement { colorId: 13; segmentLength: 5 }
@ -202,6 +215,8 @@ SplitView {
colorId: index colorId: index
colorHash: d.colorHash colorHash: d.colorHash
} }
album: model.contentType === StatusMessage.ContentType.Image ? d.exampleAlbum : []
albumCount: model.contentType === StatusMessage.ContentType.Image ? d.exampleAlbum.length : 0
} }
replyDetails { replyDetails {
@ -222,6 +237,7 @@ SplitView {
onReplyMessageClicked: logs.logEvent("StatusMessage::replyMessageClicked") onReplyMessageClicked: logs.logEvent("StatusMessage::replyMessageClicked")
onResendClicked: logs.logEvent("StatusMessage::resendClicked") onResendClicked: logs.logEvent("StatusMessage::resendClicked")
onLinkActivated: logs.logEvent("StatusMessage::linkActivated" + link) onLinkActivated: logs.logEvent("StatusMessage::linkActivated" + link)
onImageClicked: logs.logEvent("StatusMessage::imageClicked")
} }
} }
} }

View File

@ -35,7 +35,7 @@ Item {
property StatusMessage controlUnderTest: null property StatusMessage controlUnderTest: null
TestCase { TestCase {
name: "TokenSelectorView" name: "StatusMessage"
when: windowShown when: windowShown
function init() { function init() {

View File

@ -0,0 +1,18 @@
import QtQuick 2.15
ListModel {
id: root
ListElement {
symbol: "WBTC"
amount: "0.00017"
address: "0x7F47C2e18a4BBf5487E6fb082eC2D9Ab0E6d7240"
chainId: 1 // main
}
ListElement {
symbol: "ETH"
amount: "12345.6789"
address: "0x7F47C2e98a4BBf5487E6fb082eC2D9Ab0E6d8881"
chainId: 10 // Opti
}
}

View File

@ -23,6 +23,7 @@ TokensBySymbolModel 1.0 TokensBySymbolModel.qml
CommunitiesModel 1.0 CommunitiesModel.qml CommunitiesModel 1.0 CommunitiesModel.qml
OnRampProvidersModel 1.0 OnRampProvidersModel.qml OnRampProvidersModel 1.0 OnRampProvidersModel.qml
SwapTransactionRoutes 1.0 SwapTransactionRoutes.qml SwapTransactionRoutes 1.0 SwapTransactionRoutes.qml
PaymentRequestModel 1.0 PaymentRequestModel.qml
singleton ModelsData 1.0 ModelsData.qml singleton ModelsData 1.0 ModelsData.qml
singleton NetworksModel 1.0 NetworksModel.qml singleton NetworksModel 1.0 NetworksModel.qml

View File

@ -49,6 +49,7 @@ Control {
property string messageAttachments: "" property string messageAttachments: ""
property var reactionIcons: [] property var reactionIcons: []
property var linkPreviewModel property var linkPreviewModel
property var paymentRequestModel
property var gifLinks property var gifLinks
property string messageId: "" property string messageId: ""
@ -312,6 +313,7 @@ Control {
anchors.right: parent.right anchors.right: parent.right
visible: active visible: active
sourceComponent: StatusTextMessage { sourceComponent: StatusTextMessage {
objectName: "StatusMessage_textMessage"
messageDetails: root.messageDetails messageDetails: root.messageDetails
isEdited: root.isEdited isEdited: root.isEdited
allowShowMore: !root.isInPinnedPopup allowShowMore: !root.isInPinnedPopup
@ -326,6 +328,7 @@ Control {
Loader { Loader {
active: true active: true
sourceComponent: StatusMessageImageAlbum { sourceComponent: StatusMessageImageAlbum {
objectName: "StatusMessage_imageAlbum"
width: messageLayout.width width: messageLayout.width
album: root.messageDetails.albumCount > 0 ? root.messageDetails.album : [root.messageDetails.messageContent] album: root.messageDetails.albumCount > 0 ? root.messageDetails.album : [root.messageDetails.messageContent]
albumCount: root.messageDetails.albumCount > 0 ? root.messageDetails.albumCount : 1 albumCount: root.messageDetails.albumCount > 0 ? root.messageDetails.albumCount : 1
@ -375,7 +378,8 @@ Control {
Layout.preferredHeight: implicitHeight Layout.preferredHeight: implicitHeight
active: !root.editMode && active: !root.editMode &&
((!!root.linkPreviewModel && root.linkPreviewModel.count > 0) ((!!root.linkPreviewModel && root.linkPreviewModel.count > 0)
|| (!!root.gifLinks && root.gifLinks.length > 0)) || (!!root.gifLinks && root.gifLinks.length > 0)
|| (!!root.paymentRequestModel && root.paymentRequestModel.ModelCount.count > 0))
visible: active visible: active
} }
Loader { Loader {

View File

@ -24,6 +24,7 @@ RowLayout {
delegate: Loader { delegate: Loader {
active: true active: true
objectName: "album_image_loader_" + index
readonly property bool imageLoaded: index < root.album.length readonly property bool imageLoaded: index < root.album.length
readonly property string imagePath: imageLoaded ? root.album[index] : "" readonly property string imagePath: imageLoaded ? root.album[index] : ""
sourceComponent: imageLoaded ? imageComponent : imagePlaceholderComponent sourceComponent: imageLoaded ? imageComponent : imagePlaceholderComponent

View File

@ -8043,6 +8043,7 @@
<file>assets/png/chat/chat@2x.png</file> <file>assets/png/chat/chat@2x.png</file>
<file>assets/png/chat/chat@3x.png</file> <file>assets/png/chat/chat@3x.png</file>
<file>assets/png/chat/wave.png</file> <file>assets/png/chat/wave.png</file>
<file>assets/png/chat/request_payment_banner.png</file>
<file>assets/png/keycard/authenticate.png</file> <file>assets/png/keycard/authenticate.png</file>
<file>assets/png/keycard/biometrics-fail.png</file> <file>assets/png/keycard/biometrics-fail.png</file>
<file>assets/png/keycard/biometrics-success.png</file> <file>assets/png/keycard/biometrics-success.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -200,6 +200,14 @@ Item {
// Call later to make sure activeUsersStore and activeMessagesStore bindings are updated // Call later to make sure activeUsersStore and activeMessagesStore bindings are updated
Qt.callLater(d.restoreInputState, preservedText) Qt.callLater(d.restoreInputState, preservedText)
} }
function formatBalance(amount, symbol) {
let asset = ModelUtils.getByKey(WalletStore.RootStore.tokensStore.flatTokensModel, "symbol", symbol)
if (!asset)
return "0"
const num = AmountsArithmetic.toNumber(amount, asset.decimals)
return root.rootStore.currencyStore.formatCurrencyAmount(num, symbol, {noSynbol: true})
}
} }
EmptyChatPanel { EmptyChatPanel {
@ -238,11 +246,13 @@ Item {
utilsStore: root.utilsStore utilsStore: root.utilsStore
rootStore: root.rootStore rootStore: root.rootStore
contactsStore: root.contactsStore contactsStore: root.contactsStore
formatBalance: d.formatBalance
emojiPopup: root.emojiPopup emojiPopup: root.emojiPopup
stickersPopup: root.stickersPopup stickersPopup: root.stickersPopup
stickersLoaded: root.stickersLoaded stickersLoaded: root.stickersLoaded
isBlocked: model.blocked isBlocked: model.blocked
sendViaPersonalChatEnabled: root.sendViaPersonalChatEnabled sendViaPersonalChatEnabled: root.sendViaPersonalChatEnabled
areTestNetworksEnabled: root.areTestNetworksEnabled
onOpenStickerPackPopup: { onOpenStickerPackPopup: {
root.openStickerPackPopup(stickerPackId) root.openStickerPackPopup(stickerPackId)
} }
@ -294,6 +304,8 @@ Item {
sharedStore: root.sharedRootStore sharedStore: root.sharedRootStore
linkPreviewModel: !!d.activeChatContentModule ? d.activeChatContentModule.inputAreaModule.linkPreviewModel : null linkPreviewModel: !!d.activeChatContentModule ? d.activeChatContentModule.inputAreaModule.linkPreviewModel : null
paymentRequestModel: !!d.activeChatContentModule ? d.activeChatContentModule.inputAreaModule.paymentRequestModel : null
formatBalance: d.formatBalance
urlsList: d.urlsList urlsList: d.urlsList
askToEnableLinkPreview: { askToEnableLinkPreview: {
if(!d.activeChatContentModule || !d.activeChatContentModule.inputAreaModule || !d.activeChatContentModule.inputAreaModule.preservedProperties) if(!d.activeChatContentModule || !d.activeChatContentModule.inputAreaModule || !d.activeChatContentModule.inputAreaModule.preservedProperties)
@ -391,6 +403,7 @@ Item {
d.activeChatContentModule.inputAreaModule.setLinkPreviewEnabledForCurrentMessage(false) d.activeChatContentModule.inputAreaModule.setLinkPreviewEnabledForCurrentMessage(false)
} }
onDismissLinkPreview: (index) => d.activeChatContentModule.inputAreaModule.removeLinkPreviewData(index) onDismissLinkPreview: (index) => d.activeChatContentModule.inputAreaModule.removeLinkPreviewData(index)
onRemovePaymentRequestPreview: (index) => d.activeChatContentModule.inputAreaModule.removePaymentRequestPreviewData(index)
} }
ChatPermissionQualificationPanel { ChatPermissionQualificationPanel {

View File

@ -38,8 +38,10 @@ ColumnLayout {
property ContactsStore contactsStore property ContactsStore contactsStore
property string chatId property string chatId
property int chatType: Constants.chatType.unknown property int chatType: Constants.chatType.unknown
property var formatBalance
readonly property alias chatMessagesLoader: chatMessagesLoader readonly property alias chatMessagesLoader: chatMessagesLoader
property bool areTestNetworksEnabled
property var emojiPopup property var emojiPopup
property var stickersPopup property var stickersPopup
@ -94,6 +96,7 @@ ColumnLayout {
rootStore: root.rootStore rootStore: root.rootStore
contactsStore: root.contactsStore contactsStore: root.contactsStore
messageStore: root.messageStore messageStore: root.messageStore
formatBalance: root.formatBalance
emojiPopup: root.emojiPopup emojiPopup: root.emojiPopup
stickersPopup: root.stickersPopup stickersPopup: root.stickersPopup
usersStore: root.usersStore usersStore: root.usersStore
@ -103,6 +106,7 @@ ColumnLayout {
isChatBlocked: root.isBlocked || !root.isUserAllowedToSendMessage isChatBlocked: root.isBlocked || !root.isUserAllowedToSendMessage
channelEmoji: !chatContentModule ? "" : (chatContentModule.chatDetails.emoji || "") channelEmoji: !chatContentModule ? "" : (chatContentModule.chatDetails.emoji || "")
sendViaPersonalChatEnabled: root.sendViaPersonalChatEnabled sendViaPersonalChatEnabled: root.sendViaPersonalChatEnabled
areTestNetworksEnabled: root.areTestNetworksEnabled
onShowReplyArea: (messageId, senderId) => { onShowReplyArea: (messageId, senderId) => {
root.showReplyArea(messageId) root.showReplyArea(messageId)
} }

View File

@ -39,9 +39,11 @@ Item {
property UsersStore usersStore property UsersStore usersStore
property ContactsStore contactsStore property ContactsStore contactsStore
property string channelEmoji property string channelEmoji
property var formatBalance
property var emojiPopup property var emojiPopup
property var stickersPopup property var stickersPopup
property bool areTestNetworksEnabled
property string chatId: "" property string chatId: ""
property bool stickersLoaded: false property bool stickersLoaded: false
@ -283,10 +285,12 @@ Item {
stickersPopup: root.stickersPopup stickersPopup: root.stickersPopup
chatLogView: ListView.view chatLogView: ListView.view
chatContentModule: root.chatContentModule chatContentModule: root.chatContentModule
formatBalance: root.formatBalance
isChatBlocked: root.isChatBlocked isChatBlocked: root.isChatBlocked
sendViaPersonalChatEnabled: root.sendViaPersonalChatEnabled sendViaPersonalChatEnabled: root.sendViaPersonalChatEnabled
areTestNetworksEnabled: root.areTestNetworksEnabled
chatId: root.chatId chatId: root.chatId
messageId: model.id messageId: model.id
@ -326,6 +330,7 @@ Item {
deletedByContactColorHash: model.deletedByContactColorHash deletedByContactColorHash: model.deletedByContactColorHash
linkPreviewModel: model.linkPreviewModel linkPreviewModel: model.linkPreviewModel
links: model.links links: model.links
paymentRequestModel: model.paymentRequestModel
messageAttachments: model.messageAttachments messageAttachments: model.messageAttachments
transactionParams: model.transactionParameters transactionParams: model.transactionParameters
hasMention: model.mentioned hasMention: model.mentioned

View File

@ -708,6 +708,21 @@ Item {
sendModal.open(address) sendModal.open(address)
} }
function onPaymentRequestClicked(receiverAddress: string, symbol: string, amount: string, chainId: int) {
if (!!symbol) {
sendModal.preSelectedHoldingID = symbol
sendModal.preSelectedHoldingType = Constants.TokenType.ERC20
}
if (!!amount) {
sendModal.preDefinedRawAmountToSend = amount
}
if (!!chainId) {
sendModal.preSelectedChainId = chainId
}
sendModal.open(receiverAddress)
}
function onSwitchToCommunity(communityId: string) { function onSwitchToCommunity(communityId: string) {
appMain.communitiesStore.setActiveCommunity(communityId) appMain.communitiesStore.setActiveCommunity(communityId)
} }
@ -1875,6 +1890,7 @@ Item {
property int preSelectedHoldingType: Constants.TokenType.Unknown property int preSelectedHoldingType: Constants.TokenType.Unknown
property int preSelectedSendType: Constants.SendType.Unknown property int preSelectedSendType: Constants.SendType.Unknown
property string preDefinedAmountToSend property string preDefinedAmountToSend
property string preDefinedRawAmountToSend
property int preSelectedChainId: 0 property int preSelectedChainId: 0
property bool onlyAssets: false property bool onlyAssets: false
@ -1903,6 +1919,7 @@ Item {
sendModal.preSelectedAccountAddress = "" sendModal.preSelectedAccountAddress = ""
sendModal.preSelectedRecipient = undefined sendModal.preSelectedRecipient = undefined
sendModal.preDefinedAmountToSend = "" sendModal.preDefinedAmountToSend = ""
sendModal.preDefinedRawAmountToSend = ""
sendModal.preSelectedChainId = 0 sendModal.preSelectedChainId = 0
sendModal.stickersPackId = "" sendModal.stickersPackId = ""
@ -1929,6 +1946,9 @@ Item {
if (sendModal.preDefinedAmountToSend != "") { if (sendModal.preDefinedAmountToSend != "") {
item.preDefinedAmountToSend = sendModal.preDefinedAmountToSend item.preDefinedAmountToSend = sendModal.preDefinedAmountToSend
} }
if (sendModal.preDefinedRawAmountToSend != "") {
item.preDefinedRawAmountToSend = sendModal.preDefinedRawAmountToSend
}
if (!!sendModal.preSelectedChainId) { if (!!sendModal.preSelectedChainId) {
item.preSelectedChainId = sendModal.preSelectedChainId item.preSelectedChainId = sendModal.preSelectedChainId
} }

View File

@ -32,15 +32,25 @@ Control {
*/ */
required property var linkPreviewModel required property var linkPreviewModel
required property bool showLinkPreviewSettings required property bool showLinkPreviewSettings
/*
Expected roles:
string symbol
string amount
*/
required property var paymentRequestModel
property var formatBalance: null
readonly property alias hoveredUrl: d.hoveredUrl readonly property alias hoveredUrl: d.hoveredUrl
readonly property bool hasContent: imagePreviewArray.length > 0 || showLinkPreviewSettings || linkPreviewRepeater.count > 0 readonly property bool hasContent: imagePreviewArray.length > 0 || showLinkPreviewSettings || linkPreviewRepeater.count > 0 || paymentRequestRepeater.count > 0
signal imageRemoved(int index) signal imageRemoved(int index)
signal imageClicked(var chatImage) signal imageClicked(var chatImage)
signal linkReload(string link) signal linkReload(string link)
signal linkClicked(string link) signal linkClicked(string link)
signal removePaymentRequestPreview(int index)
signal enableLinkPreview() signal enableLinkPreview()
signal enableLinkPreviewForThisMessage() signal enableLinkPreviewForThisMessage()
signal disableLinkPreview() signal disableLinkPreview()
@ -96,6 +106,21 @@ Control {
onImageRemoved: root.imageRemoved(index) onImageRemoved: root.imageRemoved(index)
visible: !!imagePreviewArray && imagePreviewArray.length > 0 visible: !!imagePreviewArray && imagePreviewArray.length > 0
} }
Repeater {
id: paymentRequestRepeater
model: root.paymentRequestModel
delegate: PaymentRequestMiniCardDelegate {
required property var model
amount: {
if (!root.formatBalance)
return model.amount
return root.formatBalance(model.amount, model.symbol)
}
symbol: model.symbol
onClose: root.removePaymentRequestPreview(model.index)
}
}
Repeater { Repeater {
id: linkPreviewRepeater id: linkPreviewRepeater
model: d.filteredModel model: d.filteredModel

View File

@ -0,0 +1,112 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import utils 1.0
CalloutCard {
id: root
required property string amount
required property string symbol
readonly property bool containsMouse: mouseArea.hovered || closeButton.hovered
signal close()
implicitWidth:260
implicitHeight: 64
verticalPadding: 15
horizontalPadding: 12
borderColor: Theme.palette.directColor7
backgroundColor: root.containsMouse ? Theme.palette.directColor7 : Theme.palette.background
contentItem: Item {
implicitHeight: layout.implicitHeight
implicitWidth: layout.implicitWidth
RowLayout {
id: layout
anchors.fill: parent
spacing: 16
StatusRoundIcon {
id: favIcon
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.preferredWidth: 36
Layout.preferredHeight: 36
asset.width: 24
asset.height: 24
asset.bgColor: Theme.palette.directColor7
asset.bgHeight: 36
asset.bgWidth: 36
asset.color: Theme.palette.primaryColor1
asset.name: Theme.svg("send")
StatusSmartIdenticon {
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.right
asset.width: 16
asset.height: 16
asset.bgColor: root.containsMouse ? Theme.palette.transparent : Theme.palette.background
asset.bgHeight: 20
asset.bgWidth: 20
asset.isImage: true
asset.name: Constants.tokenIcon(root.symbol)
}
}
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
StatusBaseText {
Layout.fillWidth: true
Layout.fillHeight: true
text: qsTr("Payment request")
font.pixelSize: Theme.additionalTextSize
font.weight: Font.Medium
}
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: true
StatusBaseText {
Layout.maximumWidth: parent.width * 0.8
Layout.fillHeight: true
font.pixelSize: Theme.tertiaryTextFontSize
color: Theme.palette.baseColor1
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
text: root.amount
}
StatusBaseText {
Layout.fillHeight: true
font.pixelSize: Theme.tertiaryTextFontSize
color: Theme.palette.baseColor1
verticalAlignment: Text.AlignVCenter
text: root.symbol
}
}
}
StatusFlatButton {
id: closeButton
icon.name: "close"
size: StatusBaseButton.Size.Small
hoverColor: Theme.palette.directColor8
textColor: Theme.palette.directColor1
onClicked: root.close()
}
}
}
HoverHandler {
id: mouseArea
target: background
cursorShape: Qt.PointingHandCursor
}
}

View File

@ -10,6 +10,7 @@ LinkPreviewCard 1.0 LinkPreviewCard.qml
LinkPreviewMiniCard 1.0 LinkPreviewMiniCard.qml LinkPreviewMiniCard 1.0 LinkPreviewMiniCard.qml
LinkPreviewSettingsCard 1.0 LinkPreviewSettingsCard.qml LinkPreviewSettingsCard 1.0 LinkPreviewSettingsCard.qml
LinkPreviewSettingsCardMenu 1.0 LinkPreviewSettingsCardMenu.qml LinkPreviewSettingsCardMenu 1.0 LinkPreviewSettingsCardMenu.qml
PaymentRequestMiniCardDelegate 1.0 PaymentRequestMiniCardDelegate.qml
MessageMouseArea 1.0 MessageMouseArea.qml MessageMouseArea 1.0 MessageMouseArea.qml
MessageReactionsRow 1.0 MessageReactionsRow.qml MessageReactionsRow 1.0 MessageReactionsRow.qml
ProfileHeader 1.0 ProfileHeader.qml ProfileHeader 1.0 ProfileHeader.qml

View File

@ -0,0 +1,155 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15
import StatusQ.Core 0.1
import QtGraphicalEffects 1.15
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import shared.controls.chat 1.0
import utils 1.0
CalloutCard {
id: root
required property string amount
required property string symbol
required property string address
required property bool areTestNetworksEnabled
property string senderName
property string senderThumbnailImage
property int senderColorId
property bool highlight: false
signal clicked(var mouse)
implicitHeight: 187
implicitWidth: 305 + 2 * borderWidth
borderWidth: 2
hoverEnabled: true
dropShadow: d.highlight
borderColor: d.highlight ? Theme.palette.background : Theme.palette.border
padding: 12
Behavior on borderColor {
ColorAnimation { duration: 200 }
}
QtObject {
id: d
readonly property bool highlight: (root.highlight || root.hovered) && isAvailable
readonly property bool isAvailable: !root.areTestNetworksEnabled
}
contentItem: ColumnLayout {
spacing: 4
Rectangle {
Layout.fillHeight: true
Layout.fillWidth: true
radius: 8
color: Theme.palette.primaryColor3
clip: true
border.width: 1
border.color: Theme.palette.primaryColor2
StatusImage {
anchors.fill: parent
asynchronous: true
source: Theme.png("chat/request_payment_banner")
}
Row {
id: iconRow
spacing: -8
anchors.centerIn: parent
StatusRoundedImage {
id: symbolImage
anchors.verticalCenter: parent.verticalCenter
image.source: Constants.tokenIcon(root.symbol)
width: 44
height: width
image.layer.enabled: true
image.layer.effect: OpacityMask {
id: mask
invert: true
maskSource: Item {
width: mask.width + 2
height: mask.height + 2
Rectangle {
anchors.centerIn: parent
anchors.horizontalCenterOffset: symbolImage.width + iconRow.spacing - 2
width: parent.width
height: width
radius: width / 2
}
}
}
}
StatusSmartIdenticon {
width: symbolImage.width
height: symbolImage.height
asset.width: symbolImage.width
asset.height: symbolImage.height
asset.isImage: !!root.senderThumbnailImage
asset.name: root.senderThumbnailImage
asset.isLetterIdenticon: root.senderThumbnailImage === ""
asset.color: Theme.palette.userCustomizationColors[root.senderColorId]
asset.charactersLen: 2
name: root.senderName
}
}
}
Item {
Layout.fillWidth: true
Layout.preferredHeight: 4
}
StatusBaseText {
Layout.fillWidth: true
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
text: qsTr("Send %1 %2 to %3").arg(root.amount).arg(root.symbol).arg(Utils.compactAddress(root.address.toLowerCase(), 4))
font.pixelSize: Theme.additionalTextSize
font.weight: Font.Medium
}
StatusBaseText {
Layout.fillWidth: true
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
font.pixelSize: Theme.tertiaryTextFontSize
color: Theme.palette.baseColor1
verticalAlignment: Text.AlignVCenter
text: qsTr("Requested by %1").arg(root.senderName)
}
}
StatusToolTip {
text: qsTr("Not available in the testnet mode")
visible: !d.isAvailable && root.hovered
y: -height
}
MouseArea {
id: ma
anchors.fill: root
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if (!d.isAvailable)
return
root.clicked(mouse)
}
}
}

View File

@ -2,4 +2,5 @@ ContactListItemDelegate 1.0 ContactListItemDelegate.qml
LinkPreviewCardDelegate 1.0 LinkPreviewCardDelegate.qml LinkPreviewCardDelegate 1.0 LinkPreviewCardDelegate.qml
LinkPreviewGifDelegate 1.0 LinkPreviewGifDelegate.qml LinkPreviewGifDelegate 1.0 LinkPreviewGifDelegate.qml
LinkPreviewMiniCardDelegate 1.0 LinkPreviewMiniCardDelegate.qml LinkPreviewMiniCardDelegate 1.0 LinkPreviewMiniCardDelegate.qml
PaymentRequestCardDelegate 1.0 PaymentRequestCardDelegate.qml
InfoCard 1.0 InfoCard.qml InfoCard 1.0 InfoCard.qml

View File

@ -41,6 +41,7 @@ StatusDialog {
property alias preSelectedRecipientType: recipientInputLoader.selectedRecipientType property alias preSelectedRecipientType: recipientInputLoader.selectedRecipientType
property string preDefinedAmountToSend property string preDefinedAmountToSend
property string preDefinedRawAmountToSend
property int preSelectedChainId: 0 property int preSelectedChainId: 0
property string stickersPackId property string stickersPackId
@ -99,8 +100,8 @@ StatusDialog {
return Constants.NoError return Constants.NoError
} }
readonly property double maxFiatBalance: isSelectedHoldingValidAsset ? selectedHolding.currencyBalance : 0 readonly property double maxFiatBalance: isSelectedHoldingValidAsset && !!selectedHolding.currencyBalance ? selectedHolding.currencyBalance : 0
readonly property double maxCryptoBalance: isSelectedHoldingValidAsset ? selectedHolding.currentBalance : 0 readonly property double maxCryptoBalance: isSelectedHoldingValidAsset && !!selectedHolding.currencyBalance ? selectedHolding.currentBalance : 0
readonly property double maxInputBalance: amountToSend.fiatMode ? maxFiatBalance : maxCryptoBalance readonly property double maxInputBalance: amountToSend.fiatMode ? maxFiatBalance : maxCryptoBalance
readonly property string tokenSymbol: !!d.selectedHolding && !!d.selectedHolding.symbol ? d.selectedHolding.symbol: "" readonly property string tokenSymbol: !!d.selectedHolding && !!d.selectedHolding.symbol ? d.selectedHolding.symbol: ""
@ -234,13 +235,24 @@ StatusDialog {
if (popup.preSelectedHoldingType === Constants.TokenType.Native if (popup.preSelectedHoldingType === Constants.TokenType.Native
|| popup.preSelectedHoldingType === Constants.TokenType.ERC20) { || popup.preSelectedHoldingType === Constants.TokenType.ERC20) {
const entry = SQUtils.ModelUtils.getByKey( let iconSource = ""
let entry = SQUtils.ModelUtils.getByKey(
assetsAdaptor.outputAssetsModel, "tokensKey", assetsAdaptor.outputAssetsModel, "tokensKey",
popup.preSelectedHoldingID) popup.preSelectedHoldingID)
if (entry) {
iconSource = entry.iconSource
} else {
entry = SQUtils.ModelUtils.getByKey(
popup.store.walletAssetStore.renamedTokensBySymbolModel, "tokensKey",
popup.preSelectedHoldingID)
iconSource = Constants.tokenIcon(entry.symbol)
}
d.selectedHoldingType = Constants.TokenType.ERC20 d.selectedHoldingType = Constants.TokenType.ERC20
d.selectedHolding = entry d.selectedHolding = entry
holdingSelector.setSelection(entry.symbol, entry.iconSource, holdingSelector.setSelection(entry.symbol, iconSource,
popup.preSelectedHoldingID) popup.preSelectedHoldingID)
holdingSelector.selectedItem = entry holdingSelector.selectedItem = entry
} else { } else {
@ -260,6 +272,7 @@ StatusDialog {
holdingSelector.currentTab = TokenSelectorPanel.Tabs.Collectibles holdingSelector.currentTab = TokenSelectorPanel.Tabs.Collectibles
} }
} }
if(!!popup.preDefinedAmountToSend) { if(!!popup.preDefinedAmountToSend) {
// TODO: At this stage the number should not be localized. However // TODO: At this stage the number should not be localized. However
// in many places when initializing popup the number is provided // in many places when initializing popup the number is provided
@ -267,10 +280,13 @@ StatusDialog {
// number consistently. Only the displaying component should apply // number consistently. Only the displaying component should apply
// final localized formatting. // final localized formatting.
const delocalized = popup.preDefinedAmountToSend.replace(LocaleUtils.userInputLocale.decimalPoint, ".") const delocalized = popup.preDefinedAmountToSend.replace(LocaleUtils.userInputLocale.decimalPoint, ".")
amountToSend.setValue(delocalized) amountToSend.setValue(delocalized)
} }
if (!!popup.preDefinedRawAmountToSend) {
amountToSend.setRawValue(popup.preDefinedRawAmountToSend)
}
if (!!popup.preSelectedChainId) { if (!!popup.preSelectedChainId) {
popup.preDefinedAmountToSend = popup.preDefinedAmountToSend.replace(Qt.locale().decimalPoint, '.') popup.preDefinedAmountToSend = popup.preDefinedAmountToSend.replace(Qt.locale().decimalPoint, '.')
store.updateRoutePreferredChains(popup.preSelectedChainId) store.updateRoutePreferredChains(popup.preSelectedChainId)

View File

@ -107,6 +107,22 @@ Control {
textField.text = d.localize(trimmed) textField.text = d.localize(trimmed)
} }
function setRawValue(valueString) {
if (!valueString)
valueString = "0"
if (d.fiatMode) {
setValue(valueString)
return
}
const divisor = SQUtils.AmountsArithmetic.fromExponent(root.multiplierIndex)
const stringNumber = SQUtils.AmountsArithmetic.div(SQUtils.AmountsArithmetic.fromString(valueString), divisor).toFixed(root.multiplierIndex)
const trimmed = d.removeDecimalTrailingZeros(stringNumber)
textField.text = d.localize(trimmed)
}
function clear() { function clear() {
textField.clear() textField.clear()
} }

View File

@ -37,6 +37,7 @@ Rectangle {
signal disableLinkPreview() signal disableLinkPreview()
signal dismissLinkPreviewSettings() signal dismissLinkPreviewSettings()
signal dismissLinkPreview(int index) signal dismissLinkPreview(int index)
signal removePaymentRequestPreview(int index)
property var usersModel property var usersModel
property SharedStores.RootStore sharedStore property SharedStores.RootStore sharedStore
@ -67,6 +68,9 @@ Rectangle {
property var fileUrlsAndSources: [] property var fileUrlsAndSources: []
property var linkPreviewModel: null property var linkPreviewModel: null
property var paymentRequestModel: null
property var formatBalance: null
property var urlsList: [] property var urlsList: []
@ -1224,6 +1228,8 @@ Rectangle {
topPadding: 12 topPadding: 12
imagePreviewArray: control.fileUrlsAndSources imagePreviewArray: control.fileUrlsAndSources
linkPreviewModel: control.linkPreviewModel linkPreviewModel: control.linkPreviewModel
paymentRequestModel: control.paymentRequestModel
formatBalance: control.formatBalance
showLinkPreviewSettings: control.askToEnableLinkPreview showLinkPreviewSettings: control.askToEnableLinkPreview
onImageRemoved: (index) => { onImageRemoved: (index) => {
//Just do a copy and replace the whole thing because it's a plain JS array and thre's no signal when a single item is removed //Just do a copy and replace the whole thing because it's a plain JS array and thre's no signal when a single item is removed
@ -1242,6 +1248,7 @@ Rectangle {
onDisableLinkPreview: () => control.disableLinkPreview() onDisableLinkPreview: () => control.disableLinkPreview()
onDismissLinkPreviewSettings: () => control.dismissLinkPreviewSettings() onDismissLinkPreviewSettings: () => control.dismissLinkPreviewSettings()
onDismissLinkPreview: (index) => control.dismissLinkPreview(index) onDismissLinkPreview: (index) => control.dismissLinkPreview(index)
onRemovePaymentRequestPreview: (index) => control.removePaymentRequestPreview(index)
} }
RowLayout { RowLayout {

View File

@ -6,6 +6,7 @@ import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1 import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import StatusQ.Components 0.1 import StatusQ.Components 0.1
import StatusQ.Core.Utils 0.1 as SQUtils
import shared.controls 1.0 import shared.controls 1.0
import shared.panels 1.0 import shared.panels 1.0
@ -25,15 +26,26 @@ Flow {
required property var linkPreviewModel required property var linkPreviewModel
required property var gifLinks required property var gifLinks
required property var paymentRequestModel
required property bool gifUnfurlingEnabled required property bool gifUnfurlingEnabled
required property bool canAskToUnfurlGifs required property bool canAskToUnfurlGifs
required property bool areTestNetworksEnabled
property var formatBalance: null
property string senderName
property string senderThumbnailImage
property int senderColorId
readonly property alias hoveredLink: linksRepeater.hoveredUrl readonly property alias hoveredLink: linksRepeater.hoveredUrl
property string highlightLink: "" property string highlightLink: ""
signal imageClicked(var image, var mouse, string imageSource, string url) signal imageClicked(var image, var mouse, string imageSource, string url)
signal openContextMenu(var item, string url, string domain) signal openContextMenu(var item, string url, string domain)
signal setNeverAskAboutUnfurlingAgain(bool neverAskAgain) signal setNeverAskAboutUnfurlingAgain(bool neverAskAgain)
signal paymentRequestClicked(int index)
function resetLocalAskAboutUnfurling() { function resetLocalAskAboutUnfurling() {
d.localAskAboutUnfurling = true d.localAskAboutUnfurling = true
@ -56,9 +68,30 @@ Flow {
sourceComponent: enableLinkComponent sourceComponent: enableLinkComponent
} }
Repeater {
id: paymentRequestRepeater
model: root.paymentRequestModel
delegate: PaymentRequestCardDelegate {
required property var model
objectName: "PaymentRequestDelegate_" + model.index
areTestNetworksEnabled: root.areTestNetworksEnabled
amount: {
if (!root.formatBalance)
return model.amount
return root.formatBalance(model.amount, model.symbol)
}
symbol: model.symbol
address: model.receiver
senderName: root.senderName
senderThumbnailImage: root.senderThumbnailImage
senderColorId: root.senderColorId
onClicked: root.paymentRequestClicked(model.index)
}
}
Repeater { Repeater {
id: tempRepeater id: tempRepeater
visible: root.canAskToUnfurlGifs visible: root.cankToUnfurlGifs
model: root.gifUnfurlingEnabled ? gifLinks : [] model: root.gifUnfurlingEnabled ? gifLinks : []
delegate: LinkPreviewGifDelegate { delegate: LinkPreviewGifDelegate {

View File

@ -70,9 +70,11 @@ Loader {
property string messagePinnedBy: "" property string messagePinnedBy: ""
property var reactionsModel: [] property var reactionsModel: []
property var linkPreviewModel property var linkPreviewModel
property var paymentRequestModel
property string messageAttachments: "" property string messageAttachments: ""
property var transactionParams property var transactionParams
property var emojiReactionsModel property var emojiReactionsModel
property var formatBalance
// These 2 properties can be dropped when the new unfurling flow supports GIFs // These 2 properties can be dropped when the new unfurling flow supports GIFs
property var links property var links
@ -137,6 +139,8 @@ Loader {
property bool sendViaPersonalChatEnabled property bool sendViaPersonalChatEnabled
property bool areTestNetworksEnabled
property bool stickersLoaded: false property bool stickersLoaded: false
property string sticker property string sticker
property int stickerPack: -1 property int stickerPack: -1
@ -720,6 +724,7 @@ Loader {
resendError: root.resendError resendError: root.resendError
reactionsModel: root.reactionsModel reactionsModel: root.reactionsModel
linkPreviewModel: root.linkPreviewModel linkPreviewModel: root.linkPreviewModel
paymentRequestModel: root.paymentRequestModel
gifLinks: root.gifLinks gifLinks: root.gifLinks
showHeader: root.shouldRepeatHeader || dateGroupLabel.visible || isAReply || showHeader: root.shouldRepeatHeader || dateGroupLabel.visible || isAReply ||
@ -973,9 +978,15 @@ Loader {
linkPreviewModel: root.linkPreviewModel linkPreviewModel: root.linkPreviewModel
gifLinks: root.gifLinks gifLinks: root.gifLinks
senderName: root.senderDisplayName
senderThumbnailImage: root.senderIcon || ""
senderColorId: Utils.colorIdForPubkey(root.senderId)
paymentRequestModel: root.paymentRequestModel
playAnimations: root.Window.active && root.messageStore.isChatActive playAnimations: root.Window.active && root.messageStore.isChatActive
isOnline: root.rootStore.mainModuleInst.isOnline isOnline: root.rootStore.mainModuleInst.isOnline
highlightLink: delegate.hoveredLink highlightLink: delegate.hoveredLink
areTestNetworksEnabled: root.areTestNetworksEnabled
formatBalance: root.formatBalance
onImageClicked: (image, mouse, imageSource, url) => { onImageClicked: (image, mouse, imageSource, url) => {
d.onImageClicked(image, mouse, imageSource, url) d.onImageClicked(image, mouse, imageSource, url)
} }
@ -986,6 +997,10 @@ Loader {
gifUnfurlingEnabled: root.sharedRootStore.gifUnfurlingEnabled gifUnfurlingEnabled: root.sharedRootStore.gifUnfurlingEnabled
canAskToUnfurlGifs: !root.sharedRootStore.neverAskAboutUnfurlingAgain canAskToUnfurlGifs: !root.sharedRootStore.neverAskAboutUnfurlingAgain
onSetNeverAskAboutUnfurlingAgain: root.sharedRootStore.setNeverAskAboutUnfurlingAgain(neverAskAgain) onSetNeverAskAboutUnfurlingAgain: root.sharedRootStore.setNeverAskAboutUnfurlingAgain(neverAskAgain)
onPaymentRequestClicked: (index) => {
const request = StatusQUtils.ModelUtils.get(paymentRequestModel, index)
Global.paymentRequestClicked(request.receiver, request.symbol, request.amount, request.chainId)
}
Component.onCompleted: { Component.onCompleted: {
root.messageStore.messageModule.forceLinkPreviewsLocalData(root.messageId) root.messageStore.messageModule.forceLinkPreviewsLocalData(root.messageId)

View File

@ -70,6 +70,7 @@ QtObject {
signal appSectionBySectionTypeChanged(int sectionType, int subsection, int subSubsection, var data) signal appSectionBySectionTypeChanged(int sectionType, int subsection, int subSubsection, var data)
signal openSendModal(string address) signal openSendModal(string address)
signal paymentRequestClicked(string receiverAddress, string symbol, string amount, int chainId)
signal switchToCommunity(string communityId) signal switchToCommunity(string communityId)
signal switchToCommunitySettings(string communityId) signal switchToCommunitySettings(string communityId)
signal switchToCommunityChannelsView(string communityId) signal switchToCommunityChannelsView(string communityId)