feat: add chat command bubbles for received txs

This commit is contained in:
Jonathan Rainville 2020-09-03 16:43:08 -04:00 committed by Iuri Matias
parent 4e801c5336
commit 60492b4db1
24 changed files with 1909 additions and 192 deletions

View File

@ -24,6 +24,10 @@ proc handleChatEvents(self: ChatController) =
var evArgs = ChatUpdateArgs(e)
self.view.updateChats(evArgs.chats, false)
self.status.events.on("messageDeleted") do(e: Args):
var evArgs = MessageArgs(e)
self.view.deleteMessage(evArgs.channel, evArgs.id)
self.status.events.on("chatHistoryCleared") do(e: Args):
var args = ChannelArgs(e)
self.view.clearMessages(args.chat.id)

View File

@ -488,6 +488,9 @@ QtObject:
self.currentSuggestions.setNewData(self.status.contacts.getContacts())
self.calculateUnreadMessages()
proc deleteMessage*(self: ChatsView, channelId: string, messageId: string) =
self.messageList[channelId].deleteMessage(messageId)
proc renameGroup*(self: ChatsView, newName: string) {.slot.} =
self.status.chat.renameGroup(self.activeChannel.id, newName)
@ -570,5 +573,15 @@ QtObject:
if (self.chats.chats.len == 0): return false
let selectedChannel = self.chats.getChannel(channelIndex)
if (selectedChannel == nil): return false
result = selectedChannel.muted
result = selectedChannel.muted
### Chat commands functions ###
proc acceptRequestAddressForTransaction*(self: ChatsView, messageId: string , address: string) {.slot.} =
self.status.chat.acceptRequestAddressForTransaction(messageId, address)
proc declineRequestAddressForTransaction*(self: ChatsView, messageId: string) {.slot.} =
self.status.chat.declineRequestAddressForTransaction(messageId)
proc declineRequestTransaction*(self: ChatsView, messageId: string) {.slot.} =
self.status.chat.declineRequestTransaction(messageId)

View File

@ -1,4 +1,4 @@
import NimQml, Tables, sets
import NimQml, Tables, sets, json
import ../../../status/status
import ../../../status/accounts
import ../../../status/chat
@ -31,6 +31,7 @@ type
Audio = UserRole + 20
AudioDurationMs = UserRole + 21
EmojiReactions = UserRole + 22
CommandParameters = UserRole + 23
QtObject:
type
@ -69,6 +70,14 @@ QtObject:
result.status = status
result.setup
proc deleteMessage*(self: ChatMessageList, messageId: string) =
let messageIndex = self.messageIndex[messageId]
self.beginRemoveRows(newQModelIndex(), messageIndex, messageIndex)
self.messages.delete(messageIndex)
self.messageIndex.del(messageId)
self.messageReactions.del(messageId)
self.endRemoveRows()
proc resetTimeOut*(self: ChatMessageList, messageId: string) =
if not self.messageIndex.hasKey(messageId): return
let msgIdx = self.messageIndex[messageId]
@ -126,6 +135,17 @@ QtObject:
of ChatMessageRoles.Audio: result = newQVariant(message.audio)
of ChatMessageRoles.AudioDurationMs: result = newQVariant(message.audioDurationMs)
of ChatMessageRoles.EmojiReactions: result = newQVariant(self.getReactions(message.id))
# Pass the command parameters as a JSON string
of ChatMessageRoles.CommandParameters: result = newQVariant($(%*{
"id": message.commandParameters.id,
"fromAddress": message.commandParameters.fromAddress,
"address": message.commandParameters.address,
"contract": message.commandParameters.contract,
"value": message.commandParameters.value,
"transactionHash": message.commandParameters.transactionHash,
"commandState": message.commandParameters.commandState,
"signature": message.commandParameters.signature
}))
method roleNames(self: ChatMessageList): Table[int, string] =
{
@ -150,7 +170,8 @@ QtObject:
ChatMessageRoles.Image.int: "image",
ChatMessageRoles.Audio.int: "audio",
ChatMessageRoles.AudioDurationMs.int: "audioDurationMs",
ChatMessageRoles.EmojiReactions.int: "emojiReactions"
ChatMessageRoles.EmojiReactions.int: "emojiReactions",
ChatMessageRoles.CommandParameters.int: "commandParameters"
}.toTable
proc getMessageIndex(self: ChatMessageList, messageId: string): int {.slot.} =

View File

@ -2,6 +2,7 @@ import eventemitter, json, strutils, sequtils, tables, chronicles, sugar, times
import libstatus/contracts as status_contracts
import libstatus/chat as status_chat
import libstatus/mailservers as status_mailservers
import libstatus/chatCommands as status_chat_commands
import libstatus/stickers as status_stickers
import libstatus/types
import mailservers
@ -265,19 +266,20 @@ proc clearHistory*(self: ChatModel, chatId: string) =
proc setActiveChannel*(self: ChatModel, chatId: string) =
self.events.emit("activeChannelChanged", ChatIdArg(chatId: chatId))
proc sendMessage*(self: ChatModel, chatId: string, msg: string, replyTo: string = "", contentType: int = ContentType.Message.int) =
var response = status_chat.sendChatMessage(chatId, msg, replyTo, contentType)
var (chats, messages) = self.processChatUpdate(parseJson(response))
proc processMessageUpdateAfterSend(self: ChatModel, response: string): (seq[Chat], seq[Message]) =
result = self.processChatUpdate(parseJson(response))
var (chats, messages) = result
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats, contacts: @[]))
for msg in messages:
self.events.emit("sendingMessage", MessageArgs(id: msg.id, channel: msg.chatId))
proc sendMessage*(self: ChatModel, chatId: string, msg: string, replyTo: string = "", contentType: int = ContentType.Message.int) =
var response = status_chat.sendChatMessage(chatId, msg, replyTo, contentType)
discard self.processMessageUpdateAfterSend(response)
proc sendImage*(self: ChatModel, chatId: string, image: string) =
var response = status_chat.sendImageMessage(chatId, image)
var (chats, messages) = self.processChatUpdate(parseJson(response))
self.events.emit("chatUpdate", ChatUpdateArgs(messages: messages, chats: chats, contacts: @[]))
for msg in messages:
self.events.emit("sendingMessage", MessageArgs(id: msg.id, channel: msg.chatId))
discard self.processMessageUpdateAfterSend(response)
proc addStickerToRecent*(self: ChatModel, sticker: Sticker, save: bool = false) =
self.recentStickers.insert(sticker, 0)
@ -396,3 +398,20 @@ proc muteChat*(self: ChatModel, chatId: string) =
proc unmuteChat*(self: ChatModel, chatId: string) =
discard status_chat.unmuteChat(chatId)
proc processUpdateForTransaction*(self: ChatModel, messageId: string, response: string) =
var (chats, messages) = self.processMessageUpdateAfterSend(response)
self.events.emit("messageDeleted", MessageArgs(id: messageId, channel: chats[0].id))
proc acceptRequestAddressForTransaction*(self: ChatModel, messageId: string, address: string) =
let response = status_chat_commands.acceptRequestAddressForTransaction(messageId, address)
self.processUpdateForTransaction(messageId, response)
proc declineRequestAddressForTransaction*(self: ChatModel, messageId: string) =
let response = status_chat_commands.declineRequestAddressForTransaction(messageId)
self.processUpdateForTransaction(messageId, response)
proc declineRequestTransaction*(self: ChatModel, messageId: string) =
let response = status_chat_commands.declineRequestTransaction(messageId)
self.processUpdateForTransaction(messageId, response)

View File

@ -19,11 +19,21 @@ type TextItem* = object
literal*: string
destination*: string
type CommandParameters* = object
id*: string
fromAddress*: string
address*: string
contract*: string
value*: string
transactionHash*: string
commandState*: int
signature*: string
type Message* = object
alias*: string
chatId*: string
clock*: int
# commandParameters*: # ???
commandParameters*: CommandParameters
contentType*: ContentType
ensName*: string
fromAuthor*: string
@ -32,7 +42,7 @@ type Message* = object
lineCount*: int
localChatId*: string
messageType*: string # ???
parsedText*: seq[TextItem]
parsedText*: seq[TextItem]
# quotedMessage: # ???
replace*: string # ???
responseTo*: string

View File

@ -58,7 +58,6 @@ proc chatMessages*(chatId: string, cursor: string = ""): (string, seq[Message])
cursorVal = newJString(cursor)
let rpcResult = parseJson(callPrivateRPC("chatMessages".prefix, %* [chatId, cursorVal, 20]))["result"]
if rpcResult["messages"].kind != JNull:
for jsonMsg in rpcResult["messages"]:
messages.add(jsonMsg.toMessage)

View File

@ -0,0 +1,11 @@
import json, chronicles
import core, utils
proc acceptRequestAddressForTransaction*(messageId: string, address: string): string =
result = callPrivateRPC("acceptRequestAddressForTransaction".prefix, %* [messageId, address])
proc declineRequestAddressForTransaction*(messageId: string): string =
result = callPrivateRPC("declineRequestAddressForTransaction".prefix, %* [messageId])
proc declineRequestTransaction*(messageId: string): string =
result = callPrivateRPC("declineRequestTransaction".prefix, %* [messageId])

View File

@ -198,6 +198,19 @@ proc toMessage*(jsonMsg: JsonNode): Message =
if message.contentType == ContentType.Sticker:
message.stickerHash = jsonMsg["sticker"]["hash"].getStr
.join(" ")
if message.contentType == ContentType.Transaction:
message.commandParameters = CommandParameters(
id: jsonMsg["commandParameters"]["id"].getStr,
fromAddress: jsonMsg["commandParameters"]["from"].getStr,
address: jsonMsg["commandParameters"]["address"].getStr,
contract: jsonMsg["commandParameters"]["contract"].getStr,
value: jsonMsg["commandParameters"]["value"].getStr,
transactionHash: jsonMsg["commandParameters"]["transactionHash"].getStr,
commandState: jsonMsg["commandParameters"]["commandState"].getInt,
signature: jsonMsg["commandParameters"]["signature"].getStr
)
result = message

View File

@ -136,7 +136,6 @@ proc updateAccount*(self: WalletModel, address: string) =
self.events.emit("accountsUpdated", Args())
proc getTotalFiatBalance*(self: WalletModel): string =
var newBalance = 0.0
fmt"{self.totalBalance:.2f} {self.defaultCurrency}"
proc convertValue*(self: WalletModel, balance: string, fromCurrency: string, toCurrency: string): float =

View File

@ -61,6 +61,8 @@ Rectangle {
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: root.onClicked
onClicked: {
root.onClicked()
}
}
}

View File

@ -3,51 +3,76 @@ import QtQuick.Controls 2.13
import QtGraphicalEffects 1.13
import "../../../../../imports"
import "../../../../../shared"
import "../../../Wallet"
Popup {
id: root
width: buttonRow.width
height: buttonRow.height
padding: 0
margins: 0
id: root
width: buttonRow.width
height: buttonRow.height
padding: 0
margins: 0
background: Rectangle {
color: Style.current.background
radius: Style.current.radius
border.width: 0
layer.enabled: true
layer.effect: DropShadow{
verticalOffset: 3
radius: 8
samples: 15
fast: true
cached: true
color: "#22000000"
}
}
background: Rectangle {
color: Style.current.background
radius: Style.current.radius
border.width: 0
layer.enabled: true
layer.effect: DropShadow {
verticalOffset: 3
radius: 8
samples: 15
fast: true
cached: true
color: "#22000000"
}
}
Row {
id: buttonRow
anchors.left: parent.left
anchors.leftMargin: 0
anchors.top: parent.top
anchors.topMargin: 0
padding: Style.current.halfPadding
spacing: Style.current.halfPadding
Row {
id: buttonRow
anchors.left: parent.left
anchors.leftMargin: 0
anchors.top: parent.top
anchors.topMargin: 0
padding: Style.current.halfPadding
spacing: Style.current.halfPadding
ChatCommandButton {
iconColor: Style.current.purple
iconSource: "../../../../img/send.svg"
//% "Send transaction"
text: qsTrId("send-transaction")
}
ChatCommandButton {
iconColor: Style.current.purple
iconSource: "../../../../img/send.svg"
//% "Send transaction"
text: qsTrId("send-transaction")
onClicked: function () {
sendModal.selectedRecipient = {
address: "0x9ce0056c5fc6bb9459a4dcfa35eaad8c1fee5ce9",
identicon: chatsModel.activeChannel.identicon,
name: chatsModel.activeChannel.name,
type: RecipientSelector.Type.Contact
}
sendModal.open()
}
}
ChatCommandButton {
iconColor: Style.current.orange
iconSource: "../../../../img/send.svg"
rotatedImage: true
//% "Request transaction"
text: qsTrId("request-transaction")
}
}
ChatCommandButton {
iconColor: Style.current.orange
iconSource: "../../../../img/send.svg"
rotatedImage: true
//% "Request transaction"
text: qsTrId("request-transaction")
}
SendModal {
id: sendModal
onOpened: {
walletModel.getGasPricePredictions()
}
selectedRecipient: {
return {
address: "0x9ce0056c5fc6bb9459a4dcfa35eaad8c1fee5ce9",
identicon: chatsModel.activeChannel.identicon,
name: chatsModel.activeChannel.name,
type: RecipientSelector.Type.Contact
}
}
}
}
}

View File

@ -79,6 +79,8 @@ Item {
return fetchMoreMessagesButtonComponent
case Constants.systemMessagePrivateGroupType:
return privateGroupHeaderComponent
case Constants.transactionType:
return transactionBubble
default:
return appSettings.compactMode ? compactMessageComponent : messageComponent
}
@ -174,6 +176,12 @@ Item {
clickMessage: messageItem.clickMessage
}
}
// Transaction bubble
Component {
id: transactionBubble
TransactionBubble {}
}
}
/*##^##

View File

@ -2,123 +2,214 @@ import QtQuick 2.3
import "../../../../../shared"
import "../../../../../imports"
import "./TransactionComponents"
import "../../../Wallet/data"
Rectangle {
property string tokenAmount: "100"
property string token: "SNT"
property string fiatValue: "10 USD"
property bool outgoing: true
property string state: "addressReceived"
property int timestamp: 1598454756329
Item {
property var commandParametersObject: {
try {
var result = JSON.parse(commandParameters)
return result
} catch (e) {
console.error('Error parsing command parameters')
console.error('JSON:', commandParameters)
console.error('Error:', e)
return {
id: "",
from: "",
address: "",
contract: "",
value: "",
transactionHash: "",
commandState: 1,
signature: null
}
}
}
property var token: {
if (commandParametersObject.contract === "") {
return {
symbol: "ETH",
name: "Ethereum",
address: "0x0000000000000000000000000000000000000000",
decimals: 18,
hasIcon: true
}
}
let count = walletModel.defaultTokenList.items.count
for (let i = 0; i < count; i++) {
let token = walletModel.defaultTokenList.items.get(i)
if (commandParametersObject.contract === token.address) {
return token
}
}
return {}
}
property string tokenAmount: {
if (!commandParametersObject.value) {
return "0"
}
// Divide the Wei value by 10^decimals
var divModResult = Utils.newBigNumber(commandParametersObject.value)
.divmod(Utils.newBigNumber(10)
.pow(token.decimals))
// Make a string with the quotient and the remainder
var str = divModResult.quotient.toString() + "." + divModResult.remainder.toString()
// Remove the useless zeros at the satrt and the end, but keep at least one before the dot
return str.replace(/^(0*)([0-9\.]+?)(0*)$/g, function (match, firstZeros, whatWeKeep, secondZeros) {
if (whatWeKeep.startsWith('.')) {
whatWeKeep = "0" + whatWeKeep
}
return whatWeKeep
})
}
property string tokenSymbol: token.symbol
property string fiatValue: {
if (!tokenAmount || !token.symbol) {
return "0"
}
var defaultFiatSymbol = walletModel.defaultCurrency
return walletModel.getFiatValue(tokenAmount, token.symbol, defaultFiatSymbol) + " " + defaultFiatSymbol.toUpperCase()
}
property int state: commandParametersObject.commandState
property bool outgoing: {
switch (root.state) {
case Constants.pending:
case Constants.confirmed:
case Constants.transactionRequested:
case Constants.addressRequested: return isCurrentUser
case Constants.declined:
case Constants.transactionDeclined:
case Constants.addressReceived: return !isCurrentUser
default: return false
}
}
property int innerMargin: 12
id: root
width: 170
height: childrenRect.height
radius: 16
color: Style.current.background
border.color: Style.current.border
border.width: 1
anchors.left: parent.left
anchors.leftMargin: appSettings.compactMode ? Style.current.padding : 48
width: rectangleBubble.width
height: rectangleBubble.height
StyledText {
id: title
color: Style.current.secondaryText
text: outgoing ? qsTr("↑ Outgoing transaction") : qsTr("↓ Incoming transaction")
font.weight: Font.Medium
anchors.top: parent.top
anchors.topMargin: Style.current.halfPadding
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: 13
}
Item {
id: valueContainer
height: tokenText.height + fiatText.height
anchors.top: title.bottom
anchors.topMargin: 4
anchors.right: parent.right
anchors.rightMargin: 12
anchors.left: parent.left
anchors.leftMargin: 12
Image {
id: tokenImage
source: `../../../../img/tokens/${root.token}.png`
width: 24
height: 24
anchors.verticalCenter: parent.verticalCenter
}
Rectangle {
id: rectangleBubble
width: (bubbleLoader.active ? bubbleLoader.width : valueContainer.width)
+ timeText.width + 3 * root.innerMargin
height: childrenRect.height + root.innerMargin
radius: 16
color: Style.current.background
border.color: Style.current.border
border.width: 1
StyledText {
id: tokenText
color: Style.current.text
text: `${root.tokenAmount} ${root.token}`
anchors.left: tokenImage.right
anchors.leftMargin: Style.current.halfPadding
font.pixelSize: 22
}
StyledText {
id: fiatText
id: title
color: Style.current.secondaryText
text: root.fiatValue
anchors.top: tokenText.bottom
anchors.left: tokenText.left
text: outgoing ? qsTr("↑ Outgoing transaction") : qsTr("↓ Incoming transaction")
font.weight: Font.Medium
anchors.top: parent.top
anchors.topMargin: Style.current.halfPadding
anchors.left: parent.left
anchors.leftMargin: root.innerMargin
font.pixelSize: 13
}
}
Loader {
id: bubbleLoader
active: root.state !== Constants.addressRequested || !outgoing
sourceComponent: stateBubbleComponent
anchors.top: valueContainer.bottom
anchors.topMargin: Style.current.halfPadding
width: parent.width
height: item.height + 12
}
Item {
id: valueContainer
width: childrenRect.width
height: tokenText.height + fiatText.height
anchors.top: title.bottom
anchors.topMargin: 4
anchors.left: parent.left
anchors.leftMargin: root.innerMargin
Component {
id: stateBubbleComponent
Image {
id: tokenImage
source: `../../../../img/tokens/${root.tokenSymbol}.png`
width: 24
height: 24
anchors.verticalCenter: parent.verticalCenter
}
StateBubble {
state: root.state
StyledText {
id: tokenText
color: Style.current.textColor
text: `${root.tokenAmount} ${root.tokenSymbol}`
anchors.left: tokenImage.right
anchors.leftMargin: Style.current.halfPadding
font.pixelSize: 22
}
StyledText {
id: fiatText
color: Style.current.secondaryText
text: root.fiatValue
anchors.top: tokenText.bottom
anchors.left: tokenText.left
font.pixelSize: 13
}
}
Loader {
id: bubbleLoader
active: isCurrentUser || (!isCurrentUser && !(root.state === Constants.addressRequested || root.state === Constants.transactionRequested))
sourceComponent: stateBubbleComponent
anchors.top: valueContainer.bottom
anchors.topMargin: Style.current.halfPadding
anchors.left: parent.left
anchors.leftMargin: root.innerMargin
}
Component {
id: stateBubbleComponent
StateBubble {
state: root.state
outgoing: root.outgoing
}
}
Loader {
id: buttonsLoader
active: (root.state === Constants.addressRequested && !root.outgoing) ||
(root.state === Constants.addressReceived && root.outgoing) ||
(root.state === Constants.transactionRequested && !root.outgoing)
sourceComponent: root.outgoing ? signAndSendComponent : acceptTransactionComponent
anchors.top: bubbleLoader.active ? bubbleLoader.bottom : valueContainer.bottom
anchors.topMargin: bubbleLoader.active ? root.innerMargin : 20
width: parent.width
}
Component {
id: acceptTransactionComponent
AcceptTransaction {
state: root.state
}
}
Component {
id: signAndSendComponent
SendTransactionButton {}
}
StyledText {
id: timeText
color: Style.current.secondaryText
text: Utils.formatTime(timestamp)
anchors.left: bubbleLoader.active ? bubbleLoader.right : undefined
anchors.leftMargin: bubbleLoader.active ? 13 : 0
anchors.right: bubbleLoader.active ? undefined : parent.right
anchors.rightMargin: bubbleLoader.active ? 0 : root.innerMargin
anchors.bottom: bubbleLoader.active ? bubbleLoader.bottom : buttonsLoader.top
anchors.bottomMargin: bubbleLoader.active ? -root.innerMargin : 7
font.pixelSize: 10
}
}
Loader {
id: buttonsLoader
active: (root.state === Constants.addressRequested && !root.outgoing) ||
(root.state === Constants.addressReceived && root.outgoing)
sourceComponent: root.outgoing ? signAndSendComponent : acceptTransactionComponent
anchors.top: bubbleLoader.active ? bubbleLoader.bottom : valueContainer.bottom
anchors.topMargin: bubbleLoader.active ? 0 : Style.current.halfPadding
width: parent.width
height: item.height
}
Component {
id: acceptTransactionComponent
AcceptTransaction {}
}
Component {
id: signAndSendComponent
SendTransactionButton {}
}
StyledText {
id: timeText
color: Style.current.secondaryText
text: Utils.formatTime(root.timestamp)
anchors.bottom: parent.bottom
anchors.bottomMargin: 9
anchors.right: parent.right
anchors.rightMargin: 12
font.pixelSize: 10
}
}
/*##^##

View File

@ -3,6 +3,8 @@ import "../../../../../../shared"
import "../../../../../../imports"
Item {
property int state: Constants.addressRequested
width: parent.width
height: childrenRect.height
@ -13,14 +15,13 @@ Item {
StyledText {
id: acceptText
color: Style.current.blue
text: qsTr("Accept and share address")
text: root.state === Constants.addressRequested ? qsTr("Accept and share address") : qsTr("Accept and send")
padding: Style.current.halfPadding
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
font.weight: Font.Medium
anchors.right: parent.right
anchors.left: parent.left
bottomPadding: Style.current.halfPadding
topPadding: Style.current.halfPadding
anchors.top: separator1.bottom
font.pixelSize: 15
@ -28,7 +29,12 @@ Item {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
console.log('Accept')
if (root.state === Constants.addressRequested) {
// TODO get address from a modal instead
chatsModel.acceptRequestAddressForTransaction(messageId, walletModel.getDefaultAccount())
} else if (root.state === Constants.transactionRequested) {
console.log('Accept and send')
}
}
}
}
@ -48,8 +54,7 @@ Item {
font.weight: Font.Medium
anchors.right: parent.right
anchors.left: parent.left
bottomPadding: Style.current.padding
topPadding: Style.current.padding
padding: Style.current.halfPadding
anchors.top: separator2.bottom
font.pixelSize: 15
@ -57,7 +62,12 @@ Item {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
console.log('Decline')
if (root.state === Constants.addressRequested) {
chatsModel.declineRequestAddressForTransaction(messageId)
} else if (root.state === Constants.transactionRequested) {
chatsModel.declineRequestTransaction(messageId)
}
}
}
}

View File

@ -4,7 +4,7 @@ import "../../../../../../imports"
Item {
width: parent.width
height: childrenRect.height
height: childrenRect.height + Style.current.halfPadding
Separator {
id: separator
@ -19,9 +19,8 @@ Item {
font.weight: Font.Medium
anchors.right: parent.right
anchors.left: parent.left
bottomPadding: Style.current.halfPadding
topPadding: Style.current.halfPadding
anchors.top: separator1.bottom
anchors.top: separator.bottom
font.pixelSize: 15
MouseArea {

View File

@ -4,11 +4,12 @@ import "../../../../../../shared"
import "../../../../../../imports"
Rectangle {
property string state: Constants.pending
property int state: Constants.pending
property bool outgoing: true
id: root
width: childrenRect.width + 12
height: childrenRect.height
width: childrenRect.width + 24
height: 28
border.width: 1
border.color: Style.current.border
radius: 24
@ -19,11 +20,10 @@ Rectangle {
switch (root.state) {
case Constants.pending:
case Constants.addressReceived:
case Constants.shared:
case Constants.transactionRequested:
case Constants.addressRequested: return "../../../../../img/dotsLoadings.svg"
case Constants.confirmed: return "../../../../../img/check.svg"
case Constants.unknown:
case Constants.failure:
case Constants.transactionDeclined:
case Constants.declined: return "../../../../../img/exclamation.svg"
default: return ""
}
@ -38,7 +38,7 @@ Rectangle {
ColorOverlay {
anchors.fill: stateImage
source: stateImage
color: state == Constants.confirmed ? Style.current.transparent : Style.current.text
color: state === Constants.confirmed ? Style.current.transparent : Style.current.textColor
}
StyledText {
@ -48,7 +48,7 @@ Rectangle {
return Style.current.danger
}
if (root.state === Constants.confirmed || root.state === Constants.declined) {
return Style.current.text
return Style.current.textColor
}
return Style.current.secondaryText
@ -59,9 +59,10 @@ Rectangle {
case Constants.confirmed: return qsTr("Confirmed")
case Constants.unknown: return qsTr("Unknown token")
case Constants.addressRequested: return qsTr("Address requested")
case Constants.addressReceived: return qsTr("Address received")
case Constants.transactionRequested: return qsTr("Waiting to accept")
case Constants.addressReceived: return (!root.outgoing ? qsTr("Address shared") : qsTr("Address received"))
case Constants.transactionDeclined:
case Constants.declined: return qsTr("Transaction declined")
case Constants.shared: return qsTr("Shared Other Account")
case Constants.failure: return qsTr("Failure")
default: return qsTr("Unknown state")
}
@ -69,8 +70,7 @@ Rectangle {
font.weight: Font.Medium
anchors.left: stateImage.right
anchors.leftMargin: 4
bottomPadding: Style.current.halfPadding
topPadding: Style.current.halfPadding
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 13
}
}

View File

@ -14,6 +14,12 @@ ModalPopup {
title: qsTrId("command-button-send")
height: 504
property var selectedRecipient
onSelectedRecipientChanged: {
selectRecipient.selectedRecipient = this.selectedRecipient
selectRecipient.readOnly = !!this.selectedRecipient && !!this.selectedRecipient.address
}
property MessageDialog sendingError: MessageDialog {
id: sendingError
title: qsTr("Error sending the transaction")
@ -94,6 +100,8 @@ ModalPopup {
accounts: walletModel.accounts
contacts: profileModel.addedContacts
label: qsTr("Recipient")
readOnly: !!root.selectedRecipient && !!root.selectedRecipient.address
selectedRecipient: root.selectedRecipient
anchors.top: separator.bottom
anchors.topMargin: 10
width: stack.width

View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>

File diff suppressed because it is too large Load Diff

View File

@ -25,14 +25,13 @@ QtObject {
readonly property string generatedWalletType: "generated"
// Transaction states
readonly property string pending: "pending"
readonly property string confirmed: "confirmed"
readonly property string unknown: "unknown"
readonly property string addressRequested: "addressRequested"
readonly property string addressReceived: "addressReceived"
readonly property string declined: "declined"
readonly property string shared: "shared"
readonly property string failure: "failure"
readonly property int addressRequested: 1
readonly property int declined: 2
readonly property int addressReceived: 3
readonly property int transactionRequested: 4
readonly property int transactionDeclined: 5
readonly property int pending: 6
readonly property int confirmed: 7
readonly property var accountColors: [
"#9B832F",

View File

@ -2,8 +2,14 @@ pragma Singleton
import QtQuick 2.13
import "../shared/xss.js" as XSS
import "./BigNumber/bignumber.js" as BigNumber
QtObject {
function newBigNumber(number) {
// See here for docs: https://github.com/peterolson/BigInteger.js
return BigNumber.bigInt(number)
}
function isHex(value) {
return /^(-0x|0x)?[0-9a-fA-F]*$/i.test(value)
}

View File

@ -167,6 +167,7 @@ DISTFILES += \
app/AppLayouts/Wallet/components/collectiblesComponents/CollectiblesModal.qml \
app/AppLayouts/Wallet/components/collectiblesComponents/CollectiblesModalContent.qml \
app/AppLayouts/Wallet/components/collectiblesComponents/collectiblesData.js \
app/AppLayouts/Wallet/data/Tokens.qml \
fonts/InterStatus/InterStatus-Black.otf \
fonts/InterStatus/InterStatus-BlackItalic.otf \
fonts/InterStatus/InterStatus-Bold.otf \

View File

@ -18,6 +18,7 @@ Item {
property int iconHeight: 24
property int iconWidth: 24
property bool copyToClipboard: false
property bool readOnly: false
readonly property bool hasIcon: icon.toString() !== ""
readonly property var forceActiveFocus: function () {
@ -65,7 +66,7 @@ Item {
if (!!validationError) {
return Style.current.danger
}
if (inputValue.focus) {
if (!inputBox.readOnly && inputValue.focus) {
return Style.current.inputBorderFocus
}
return Style.current.transparent
@ -87,6 +88,7 @@ Item {
leftPadding: inputBox.hasIcon ? iconWidth + 20 : Style.current.padding
selectByMouse: true
font.pixelSize: fontPixelSize
readOnly: inputBox.readOnly
background: Rectangle {
color: Style.current.transparent
}

View File

@ -13,7 +13,7 @@ Item {
property alias label: txtLabel.text
// If supplied, additional info will be displayed top-right in danger colour (red)
property alias additionalInfo: txtAddlInfo.text
property var selectedRecipient: { }
property var selectedRecipient
property bool readOnly: false
height: (readOnly ? inpReadOnly.height : inpAddress.height) + txtLabel.height
//% "Invalid ethereum address"
@ -106,7 +106,7 @@ Item {
textField.verticalAlignment: TextField.AlignVCenter
textField.font.pixelSize: 15
textField.color: Style.current.secondaryText
textField.readOnly: true
readOnly: true
validationErrorAlignment: TextEdit.AlignRight
validationErrorTopMargin: 8
customHeight: 56