feat: add input to search and use ENS for community invite

fixes #2138
This commit is contained in:
Jonathan Rainville 2021-03-31 15:14:09 -04:00 committed by Iuri Matias
parent 63b3ee3942
commit c42bd1ea78
10 changed files with 235 additions and 66 deletions

View File

@ -12,26 +12,36 @@ ModalPopup {
id: popup id: popup
property string communityId: chatsModel.communities.activeCommunity.id property string communityId: chatsModel.communities.activeCommunity.id
property var pubKeys: []
property var goBack property var goBack
onOpened: { onOpened: {
pubKeys = []; contactFieldAndList.chatKey.text = ""
inviteBtn.enabled = false contactFieldAndList.pubKey = ""
contactList.membersData.clear(); contactFieldAndList.pubKeys = []
// TODO remove friends that are already members contactFieldAndList.ensUsername = ""
getContactListObject(contactList.membersData) contactFieldAndList.chatKey.forceActiveFocus(Qt.MouseFocusReason)
noContactsRect.visible = !profileModel.contacts.list.hasAddedContacts(); contactFieldAndList.existingContacts.visible = profileModel.contacts.list.hasAddedContacts()
contactList.visible = !noContactsRect.visible; contactFieldAndList.noContactsRect.visible = !contactFieldAndList.existingContacts.visible
} }
//% "Invite friends" //% "Invite friends"
title: qsTrId("invite-friends") title: qsTrId("invite-friends")
height: 630
function sendInvites(pubKeys) {
const error = chatsModel.communities.inviteUsersToCommunityById(popup.communityId, JSON.stringify(pubKeys))
if (error) {
console.error('Error inviting', error)
contactFieldAndList.validationError = error
return
}
contactFieldAndList.successMessage = qsTr("Invite successfully sent")
}
Item { Item {
anchors.fill: parent anchors.fill: parent
TextWithLabel { TextWithLabel {
id: shareCommunity id: shareCommunity
anchors.top: parent.top anchors.top: parent.top
@ -51,40 +61,18 @@ ModalPopup {
anchors.rightMargin: -Style.current.padding anchors.rightMargin: -Style.current.padding
} }
StyledText { ContactsListAndSearch {
//% "Contacts" id: contactFieldAndList
text: qsTrId("contacts")
anchors.left: parent.left
anchors.top: sep.bottom anchors.top: sep.bottom
anchors.topMargin: Style.current.smallPadding anchors.topMargin: Style.current.smallPadding
font.pixelSize: 15 anchors.bottom: parent.bottom
font.weight: Font.Thin showCheckbox: true
color: Style.current.secondaryText onUserClicked: function (isContact, pubKey, ensName) {
} if (isContact) {
// those are just added to the list to by added by the bunch
NoFriendsRectangle { return
id: noContactsRect
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
}
ContactList {
id: contactList
selectMode: true
anchors.top: sep.bottom
anchors.topMargin: 100
onItemChecked: function(pubKey, itemChecked) {
var idx = pubKeys.indexOf(pubKey)
if (itemChecked) {
if (idx === -1) {
pubKeys.push(pubKey)
}
} else {
if (idx > -1) {
pubKeys.splice(idx, 1);
}
} }
inviteBtn.enabled = pubKeys.length > 0 sendInvites([pubKey])
} }
} }
} }
@ -111,16 +99,11 @@ ModalPopup {
id: inviteBtn id: inviteBtn
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
enabled: contactFieldAndList.pubKeys.length > 0
//% "Invite" //% "Invite"
text: qsTrId("invite-button") text: qsTrId("invite-button")
onClicked : { onClicked : {
const error = chatsModel.communities.inviteUsersToCommunityById(popup.communityId, JSON.stringify(popup.pubKeys)) sendInvites(contactFieldAndList.pubKeys)
// TODO show error to user also should we show success?
if (error) {
console.error('Error inviting', error)
return
}
popup.close()
} }
} }
} }

View File

@ -157,7 +157,7 @@ ModalPopup {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
} }
PrivateChatPopupExistingContacts { ExistingContacts {
id: existingContacts id: existingContacts
anchors.topMargin: this.height > 0 ? Style.current.xlPadding : 0 anchors.topMargin: this.height > 0 ? Style.current.xlPadding : 0
anchors.top: chatKey.bottom anchors.top: chatKey.bottom
@ -168,7 +168,7 @@ ModalPopup {
expanded: !searchResults.loading && popup.pubKey === "" && !searchResults.showProfileNotFoundMessage expanded: !searchResults.loading && popup.pubKey === "" && !searchResults.showProfileNotFoundMessage
} }
PrivateChatPopupSearchResults { SearchResults {
id: searchResults id: searchResults
anchors.top: existingContacts.visible ? existingContacts.bottom : chatKey.bottom anchors.top: existingContacts.visible ? existingContacts.bottom : chatKey.bottom
anchors.topMargin: Style.current.padding anchors.topMargin: Style.current.padding

View File

@ -9,6 +9,5 @@ GroupChatPopup 1.0 GroupChatPopup.qml
StickerButton 1.0 StickerButton.qml StickerButton 1.0 StickerButton.qml
StickerMarket 1.0 StickerMarket.qml StickerMarket 1.0 StickerMarket.qml
StickersPopup 1.0 StickersPopup.qml StickersPopup 1.0 StickersPopup.qml
InviteFriendsPopup 1.0 InviteFriendsPopup.qml
StickerPackIconWithIndicator 1.0 StickerPackIconWithIndicator.qml StickerPackIconWithIndicator 1.0 StickerPackIconWithIndicator.qml
SuggestedChannels 1.0 SuggestedChannels.qml SuggestedChannels 1.0 SuggestedChannels.qml

View File

@ -4,7 +4,6 @@ import QtQuick.Layouts 1.13
import "../../../../imports" import "../../../../imports"
import "../../../../shared" import "../../../../shared"
import "../../../../shared/status" import "../../../../shared/status"
import "../../Chat/components"
import "./Contacts" import "./Contacts"
Item { Item {
@ -195,7 +194,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
} }
PrivateChatPopupSearchResults { SearchResults {
id: searchResults id: searchResults
anchors.top: addContactSearchInput.bottom anchors.top: addContactSearchInput.bottom
anchors.topMargin: Style.current.xlPadding anchors.topMargin: Style.current.xlPadding

View File

@ -364,6 +364,7 @@ DISTFILES += \
shared/AccountSelector.qml \ shared/AccountSelector.qml \
shared/AddButton.qml \ shared/AddButton.qml \
shared/Address.qml \ shared/Address.qml \
shared/ContactsListAndSearch.qml \
shared/CropCornerRectangle.qml \ shared/CropCornerRectangle.qml \
shared/DelegateModelGeneralized.qml \ shared/DelegateModelGeneralized.qml \
shared/FormGroup.qml \ shared/FormGroup.qml \

View File

@ -0,0 +1,183 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtGraphicalEffects 1.13
import "../imports"
import "../shared/status"
Item {
property string validationError: ""
property string successMessage: ""
property alias chatKey: chatKey
property alias existingContacts: existingContacts
property alias noContactsRect: noContactsRect
property string pubKey : ""
property string ensUsername : ""
property bool showCheckbox: false
signal userClicked(bool isContact, string pubKey, string ensName)
property var pubKeys: ([])
id: root
width: parent.width
property var resolveENS: Backpressure.debounce(root, 500, function (ensName) {
noContactsRect.visible = false
searchResults.loading = true
searchResults.showProfileNotFoundMessage = false
chatsModel.resolveENS(ensName)
});
function validate() {
if (!Utils.isChatKey(chatKey.text) && !Utils.isValidETHNamePrefix(chatKey.text)) {
//% "Enter a valid chat key or ENS username"
validationError = qsTrId("enter-a-valid-chat-key-or-ens-username");
pubKey = ""
ensUsername = "";
} else if (profileModel.profile.pubKey === chatKey.text) {
//% "Can't chat with yourself"
validationError = qsTrId("can-t-chat-with-yourself");
} else {
validationError = ""
}
return validationError === ""
}
Input {
id: chatKey
//% "Enter ENS username or chat key"
placeholderText: qsTrId("enter-contact-code")
Keys.onReleased: {
searchResults.pubKey = ""
if (!validate()) {
searchResults.showProfileNotFoundMessage = false
noContactsRect.visible = false
return;
}
chatKey.text = chatKey.text.trim();
if (Utils.isChatKey(chatKey.text)){
pubKey = chatKey.text;
if (!profileModel.contacts.isAdded(pubKey)) {
searchResults.username = utilsModel.generateAlias(pubKey)
searchResults.userAlias = Utils.compactAddress(pubKey, 4)
searchResults.pubKey = pubKey
}
noContactsRect.visible = false
return;
}
Qt.callLater(resolveENS, chatKey.text)
}
textField.anchors.rightMargin: clearBtn.width + Style.current.padding + 2
Connections {
target: chatsModel
onEnsWasResolved: {
if(chatKey.text == ""){
ensUsername.text = "";
pubKey = "";
} else if(resolvedPubKey == ""){
ensUsername.text = "";
searchResults.pubKey = pubKey = "";
searchResults.showProfileNotFoundMessage = true
} else {
if (profileModel.profile.pubKey === resolvedPubKey) {
//% "Can't chat with yourself"
validationError = qsTrId("can-t-chat-with-yourself");
} else {
searchResults.username = chatsModel.formatENSUsername(chatKey.text)
let userAlias = utilsModel.generateAlias(resolvedPubKey)
userAlias = userAlias.length > 20 ? userAlias.substring(0, 19) + "..." : userAlias
searchResults.userAlias = userAlias + " • " + Utils.compactAddress(resolvedPubKey, 4)
searchResults.pubKey = pubKey = resolvedPubKey;
}
searchResults.showProfileNotFoundMessage = false
}
searchResults.loading = false;
noContactsRect.visible = pubKey === "" && ensUsername.text === "" && !profileModel.contacts.list.hasAddedContacts() && !profileNotFoundMessage.visible
}
}
StatusIconButton {
id: clearBtn
icon.name: "close-icon"
type: "secondary"
visible: chatKey.text !== ""
icon.width: 14
icon.height: 14
width: 14
height: 14
anchors.right: parent.right
anchors.rightMargin: Style.current.padding
anchors.verticalCenter: parent.verticalCenter
onClicked: {
chatKey.text = ""
chatKey.forceActiveFocus(Qt.MouseFocusReason)
searchResults.showProfileNotFoundMessage = false
searchResults.pubKey = pubKey = ""
noContactsRect.visible = false
searchResults.loading = false
}
}
}
StyledText {
id: message
text: validationError || successMessage
visible: validationError !== "" || successMessage !== ""
font.pixelSize: 13
color: !!validationError ? Style.current.danger : Style.current.success
anchors.top: chatKey.bottom
anchors.topMargin: Style.current.smallPadding
anchors.horizontalCenter: parent.horizontalCenter
}
ExistingContacts {
id: existingContacts
anchors.topMargin: this.height > 0 ? Style.current.xlPadding : 0
anchors.top: chatKey.bottom
showCheckbox: root.showCheckbox
filterText: chatKey.text
pubKeys: root.pubKeys
onContactClicked: function (contact) {
if (!contact || typeof contact === "string") {
return
}
const index = root.pubKeys.indexOf(contact.pubKey)
const pubKeysCopy = Object.assign([], root.pubKeys)
if (index === -1) {
pubKeysCopy.push(contact.pubKey)
} else {
pubKeysCopy.splice(index, 1)
}
root.pubKeys = pubKeysCopy
userClicked(true, contact.pubKey, profileModel.contacts.addedContacts.userName(contact.pubKey, contact.name))
}
expanded: !searchResults.loading && pubKey === "" && !searchResults.showProfileNotFoundMessage
}
SearchResults {
id: searchResults
anchors.top: existingContacts.visible ? existingContacts.bottom : chatKey.bottom
anchors.topMargin: Style.current.padding
hasExistingContacts: existingContacts.visible
loading: false
onResultClicked: {
if (!validate()) {
return
}
userClicked(false, pubKey, chatKey.text)
}
onAddToContactsButtonClicked: profileModel.contacts.addContact(pubKey)
}
NoFriendsRectangle {
id: noContactsRect
anchors.top: chatKey.bottom
anchors.topMargin: Style.current.xlPadding * 3
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
}
}

View File

@ -1,10 +1,11 @@
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Controls 2.13 import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13 import QtQuick.Layouts 1.13
import "../../../../imports" import "../imports"
import "../../../../shared" import "./status"
import "../../../../shared/status" // TODO move Contact into shared to get rid of that import
import "./" import "../app/AppLayouts/Chat/components"
import "."
Item { Item {
id: root id: root
@ -12,6 +13,8 @@ Item {
anchors.right: parent.right anchors.right: parent.right
property string filterText: "" property string filterText: ""
property bool expanded: true property bool expanded: true
property bool showCheckbox: false
property var pubKeys: ([])
signal contactClicked(var contact) signal contactClicked(var contact)
function matchesAlias(name, filter) { function matchesAlias(name, filter) {
@ -33,7 +36,8 @@ Item {
id: contactListView id: contactListView
model: profileModel.contacts.list model: profileModel.contacts.list
delegate: Contact { delegate: Contact {
showCheckbox: false showCheckbox: root.showCheckbox
isChecked: root.pubKeys.indexOf(model.pubKey) > -1
pubKey: model.pubKey pubKey: model.pubKey
isContact: model.isContact isContact: model.isContact
isUser: false isUser: false
@ -50,7 +54,6 @@ Item {
} }
} }
} }
} }

View File

@ -1,7 +1,7 @@
import QtQuick 2.12 import QtQuick 2.12
import QtQuick.Controls 2.3 import QtQuick.Controls 2.3
import "../../../../imports" import "../imports"
import "../../../../shared" import "."
ModalPopup { ModalPopup {
id: popup id: popup

View File

@ -1,7 +1,7 @@
import QtQuick 2.13 import QtQuick 2.13
import "../../../../imports" import "../imports"
import "../../../../shared" import "."
import "../../../../shared/status" import "./status"
Item { Item {
id: noContactsRect id: noContactsRect

View File

@ -1,9 +1,10 @@
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Controls 2.13 import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13 import QtQuick.Layouts 1.13
import "../../../../imports" import "../imports"
import "../../../../shared" import "./status"
import "../../../../shared/status" // TODO move Contact into shared to get rid of that import
import "../app/AppLayouts/Chat/components"
import "./" import "./"