@@ -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