Skip to content

Commit 5c7ede7

Browse files
committed
Issue 15: add contacts UI and send/receive integration
1 parent 8f376a6 commit 5c7ede7

File tree

7 files changed

+1033
-2
lines changed

7 files changed

+1033
-2
lines changed

doc/test-automation-selectors.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ It supports the parity backlog Definition of Done (`DefinitionOfDone.md`) requir
2121

2222
## Current Examples
2323

24-
- Receive flow: `receiveAmountInput`, `receiveLabelInput`, `receiveCreateAddressButton`, `receiveCopySelectedUriButton`, `receiveQrImage`
25-
- Send flow: `sendAddressInput`, `sendContinueButton`, `sendResultPopup`
24+
- Receive flow: `receiveAmountInput`, `receiveLabelInput`, `receiveContactSelectButton`, `receiveContactsPopup`, `receiveContactsSearchInput`, `receiveContactRow`, `receiveContactSelectFirstActionButton`, `receiveContactUseButton`, `receiveCreateAddressButton`, `receiveCopySelectedUriButton`, `receiveQrImage`
25+
- Send flow: `sendAddressInput`, `sendOpenContactsButton`, `sendContactsPopup`, `sendContactsSearchInput`, `sendContactLabelInput`, `sendContactAddressInput`, `sendContactSaveButton`, `sendContactDeleteButton`, `sendContactUseButton`, `sendContinueButton`, `sendResultPopup`
2626
- Wallet tabs/pages: `walletSendPage`, `walletRequestPaymentPage`, `activityListView`
2727
- Onboarding storage flow: `onboardingStorageAmountDetailedSettingsButton`, `storageSettingsPruneTargetInput`, `storageLocationDefaultOption`
2828
- RPC console flow: `aboutDeveloperOptionsItem`, `developerRpcConsoleItem`, `rpcConsoleInput`, `rpcConsoleSubmitButton`, `rpcConsoleOutputText`

qml/pages/wallet/RequestPayment.qml

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,30 @@ Page {
199199
}
200200
}
201201

202+
RowLayout {
203+
Layout.fillWidth: true
204+
spacing: 10
205+
206+
ContinueButton {
207+
objectName: "receiveContactSelectButton"
208+
text: qsTr("Use contact label")
209+
enabled: root.wallet && root.wallet.addressBook
210+
onClicked: {
211+
if (root.wallet && root.wallet.addressBook && receiveContactsPopup.openForSelection) {
212+
receiveContactsPopup.openForSelection()
213+
}
214+
}
215+
}
216+
217+
CoreText {
218+
Layout.fillWidth: true
219+
text: qsTr("Contact updates are reflected here for faster request labeling.")
220+
color: Theme.color.neutral7
221+
font.pixelSize: 14
222+
wrapMode: Text.WordWrap
223+
}
224+
}
225+
202226
Separator {
203227
Layout.fillWidth: true
204228
}
@@ -465,4 +489,174 @@ Page {
465489
}
466490
}
467491
}
492+
493+
Popup {
494+
id: receiveContactsPopup
495+
objectName: "receiveContactsPopup"
496+
modal: true
497+
closePolicy: Popup.CloseOnPressOutside
498+
anchors.centerIn: parent
499+
width: 560
500+
height: 470
501+
502+
property string selectedLabel: ""
503+
504+
function openForSelection() {
505+
receiveContactSearchInput.text = ""
506+
selectedLabel = ""
507+
if (root.wallet && root.wallet.addressBook && root.wallet.addressBook.refresh) {
508+
root.wallet.addressBook.refresh()
509+
}
510+
open()
511+
}
512+
513+
function searchMatches(contactLabel, contactAddress) {
514+
const query = receiveContactSearchInput.text.trim().toLowerCase()
515+
if (query.length === 0) {
516+
return true
517+
}
518+
519+
const normalizedLabel = (contactLabel || "").toLowerCase()
520+
const normalizedAddress = (contactAddress || "").toLowerCase()
521+
return normalizedLabel.indexOf(query) !== -1 || normalizedAddress.indexOf(query) !== -1
522+
}
523+
524+
background: Rectangle {
525+
color: Theme.color.neutral0
526+
border.color: Theme.color.neutral4
527+
border.width: 1
528+
radius: 8
529+
}
530+
531+
contentItem: ColumnLayout {
532+
spacing: 10
533+
534+
Header {
535+
header: qsTr("Contact labels")
536+
headerBold: true
537+
description: qsTr("Choose an existing contact label for this request.")
538+
}
539+
540+
CoreTextField {
541+
id: receiveContactSearchInput
542+
objectName: "receiveContactsSearchInput"
543+
Layout.fillWidth: true
544+
placeholderText: qsTr("Search contacts")
545+
onTextChanged: receiveContactsPopup.selectedLabel = ""
546+
}
547+
548+
ScrollView {
549+
Layout.fillWidth: true
550+
Layout.preferredHeight: 260
551+
clip: true
552+
553+
ListView {
554+
id: receiveContactsListView
555+
objectName: "receiveContactsList"
556+
width: parent.width
557+
spacing: 6
558+
model: root.wallet && root.wallet.addressBook ? root.wallet.addressBook : null
559+
560+
delegate: Rectangle {
561+
required property string address
562+
required property string label
563+
required property string purpose
564+
565+
readonly property bool filteredOut: !receiveContactsPopup.searchMatches(label, address)
566+
567+
objectName: "receiveContactRow"
568+
width: receiveContactsListView.width
569+
height: filteredOut ? 0 : 52
570+
visible: !filteredOut
571+
radius: 6
572+
color: receiveContactsPopup.selectedLabel === (label !== "" ? label : address)
573+
? Theme.color.orangeLight2
574+
: Theme.color.neutral2
575+
576+
RowLayout {
577+
anchors.fill: parent
578+
anchors.margins: 8
579+
spacing: 8
580+
581+
CoreText {
582+
objectName: "receiveContactRowLabel"
583+
Layout.fillWidth: true
584+
text: label !== "" ? label : address
585+
elide: Text.ElideRight
586+
bold: true
587+
font.pixelSize: 15
588+
}
589+
590+
CoreText {
591+
text: purpose
592+
color: Theme.color.neutral7
593+
font.pixelSize: 12
594+
}
595+
}
596+
597+
onVisibleChanged: {
598+
if (visible && receiveContactsPopup.selectedLabel.length === 0) {
599+
receiveContactsPopup.selectedLabel = label !== "" ? label : address
600+
}
601+
}
602+
603+
Component.onCompleted: {
604+
if (visible && receiveContactsPopup.selectedLabel.length === 0) {
605+
receiveContactsPopup.selectedLabel = label !== "" ? label : address
606+
}
607+
}
608+
609+
MouseArea {
610+
anchors.fill: parent
611+
onClicked: {
612+
receiveContactsPopup.selectedLabel = label !== "" ? label : address
613+
}
614+
}
615+
}
616+
}
617+
}
618+
619+
Button {
620+
objectName: "receiveContactSelectFirstActionButton"
621+
visible: false
622+
onClicked: {
623+
for (const child of receiveContactsListView.contentItem.children) {
624+
if (child && child.visible && child.label !== undefined && child.address !== undefined) {
625+
receiveContactsPopup.selectedLabel = child.label !== "" ? child.label : child.address
626+
return
627+
}
628+
}
629+
receiveContactsPopup.selectedLabel = ""
630+
}
631+
}
632+
633+
RowLayout {
634+
Layout.fillWidth: true
635+
spacing: 10
636+
637+
OutlineButton {
638+
objectName: "receiveContactsCloseButton"
639+
Layout.fillWidth: true
640+
text: qsTr("Close")
641+
onClicked: receiveContactsPopup.close()
642+
}
643+
644+
ContinueButton {
645+
objectName: "receiveContactUseButton"
646+
Layout.fillWidth: true
647+
text: qsTr("Use label")
648+
enabled: root.request && (receiveContactsPopup.selectedLabel.length > 0 || receiveContactSearchInput.text.trim().length > 0)
649+
onClicked: {
650+
if (root.request) {
651+
const fallbackLabel = receiveContactSearchInput.text.trim()
652+
root.request.label = receiveContactsPopup.selectedLabel.length > 0
653+
? receiveContactsPopup.selectedLabel
654+
: fallbackLabel
655+
}
656+
receiveContactsPopup.close()
657+
}
658+
}
659+
}
660+
}
661+
}
468662
}

0 commit comments

Comments
 (0)