feat(desktop): New invite to community popup with message

This commit is contained in:
MishkaRogachev 2022-07-25 18:07:19 +03:00 committed by Mikhail Rogachev
parent 0418979e9c
commit e67d649c46
25 changed files with 293 additions and 143 deletions

View File

@ -454,8 +454,8 @@ method unmuteCategory*(self: Controller, categoryId: string) =
proc setCommunityMuted*(self: Controller, muted: bool) =
self.communityService.setCommunityMuted(self.sectionId, muted)
proc inviteUsersToCommunity*(self: Controller, pubKeys: string): string =
result = self.communityService.inviteUsersToCommunityById(self.sectionId, pubKeys)
proc inviteUsersToCommunity*(self: Controller, pubKeys: string, inviteMessage: string): string =
result = self.communityService.inviteUsersToCommunityById(self.sectionId, pubKeys, inviteMessage)
proc reorderCommunityCategories*(self: Controller, categoryId: string, position: int) =
self.communityService.reorderCommunityCategories(self.sectionId, categoryId, position)

View File

@ -284,7 +284,7 @@ method exportCommunity*(self: AccessInterface): string {.base.} =
method setCommunityMuted*(self: AccessInterface, muted: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method inviteUsersToCommunity*(self: AccessInterface, pubKeysJSON: string): string {.base.} =
method inviteUsersToCommunity*(self: AccessInterface, pubKeysJSON: string, inviteMessage: string): string {.base.} =
raise newException(ValueError, "No implementation available")
method createCommunityCategory*(self: AccessInterface, name: string, channels: seq[string]) {.base.} =

View File

@ -792,8 +792,8 @@ method exportCommunity*(self: Module): string =
method setCommunityMuted*(self: Module, muted: bool) =
self.controller.setCommunityMuted(muted)
method inviteUsersToCommunity*(self: Module, pubKeysJSON: string): string =
result = self.controller.inviteUsersToCommunity(pubKeysJSON)
method inviteUsersToCommunity*(self: Module, pubKeysJSON: string, inviteMessage: string): string =
result = self.controller.inviteUsersToCommunity(pubKeysJSON, inviteMessage)
method prepareEditCategoryModel*(self: Module, categoryId: string) =
self.view.editCategoryChannelsModel().clearItems()

View File

@ -274,8 +274,8 @@ QtObject:
proc setCommunityMuted*(self: View, muted: bool) {.slot.} =
self.delegate.setCommunityMuted(muted)
proc inviteUsersToCommunity*(self: View, pubKeysJSON: string): string {.slot.} =
result = self.delegate.inviteUsersToCommunity(pubKeysJSON)
proc inviteUsersToCommunity*(self: View, pubKeysJSON: string, inviteMessage: string): string {.slot.} =
result = self.delegate.inviteUsersToCommunity(pubKeysJSON, inviteMessage)
proc createCommunityCategory*(self: View, name: string, channels: string) {.slot.} =
let channelsSeq = map(parseJson(channels).getElems(), proc(x:JsonNode):string = x.getStr())

View File

@ -17,8 +17,8 @@ proc newController*(delegate: io_interface.AccessInterface,
proc delete*(self: Controller) =
discard
proc inviteUsersToCommunity*(self: Controller, communityID: string, pubKeys: string): string =
result = self.communityService.inviteUsersToCommunityById(communityID, pubKeys)
proc inviteUsersToCommunity*(self: Controller, communityID: string, pubKeys: string, inviteMessage: string): string =
result = self.communityService.inviteUsersToCommunityById(communityID, pubKeys, inviteMessage)
proc leaveCommunity*(self: Controller, communityID: string) =
self.communityService.leaveCommunity(communityID)

View File

@ -22,7 +22,7 @@ method getModuleAsVariant*(self: AccessInterface): QVariant {.base.} =
method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method inviteUsersToCommunity*(self: AccessInterface, communityID: string, pubKeysJSON: string): string {.base.} =
method inviteUsersToCommunity*(self: AccessInterface, communityID: string, pubKeysJSON: string, inviteMessage: string): string {.base.} =
raise newException(ValueError, "No implementation available")
method leaveCommunity*(self: AccessInterface, communityID: string) {.base.} =

View File

@ -42,8 +42,8 @@ method viewDidLoad*(self: Module) =
method getModuleAsVariant*(self: Module): QVariant =
return self.viewVariant
method inviteUsersToCommunity*(self: Module, communityID: string, pubKeysJSON: string): string =
result = self.controller.inviteUsersToCommunity(communityID, pubKeysJSON)
method inviteUsersToCommunity*(self: Module, communityID: string, pubKeysJSON: string, inviteMessage: string): string =
result = self.controller.inviteUsersToCommunity(communityID, pubKeysJSON, inviteMessage)
method leaveCommunity*(self: Module, communityID: string) =
self.controller.leaveCommunity(communityID)

View File

@ -19,8 +19,8 @@ QtObject:
proc load*(self: View) =
self.delegate.viewDidLoad()
method inviteUsersToCommunity*(self: View, communityID: string, pubKeysJSON: string): string {.slot.} =
result = self.delegate.inviteUsersToCommunity(communityID, pubKeysJSON)
method inviteUsersToCommunity*(self: View, communityID: string, pubKeysJSON: string, inviteMessage: string): string {.slot.} =
result = self.delegate.inviteUsersToCommunity(communityID, pubKeysJSON, inviteMessage)
method leaveCommunity*(self: View, communityID: string) {.slot.} =
self.delegate.leaveCommunity(communityID)

View File

@ -1069,7 +1069,7 @@ QtObject:
except Exception as e:
error "Error declining request to join community", msg = e.msg
proc inviteUsersToCommunityById*(self: Service, communityId: string, pubKeysJson: string): string =
proc inviteUsersToCommunityById*(self: Service, communityId: string, pubKeysJson: string, inviteMessage: string): string =
try:
let pubKeysParsed = pubKeysJson.parseJson
var pubKeys: seq[string] = @[]
@ -1077,7 +1077,7 @@ QtObject:
pubKeys.add(pubKey.getStr)
# We no longer send invites, but merely share the community so
# users can request access (with automatic acception)
let response = status_go.shareCommunityToUsers(communityId, pubKeys)
let response = status_go.shareCommunityToUsers(communityId, pubKeys, inviteMessage)
discard self.chatService.processMessageUpdateAfterSend(response)
except Exception as e:
error "Error sharing community", msg = e.msg

View File

@ -274,10 +274,11 @@ proc inviteUsersToCommunity*(communityId: string, pubKeys: seq[string]): RpcResp
"users": pubKeys
}])
proc shareCommunityToUsers*(communityId: string, pubKeys: seq[string]): RpcResponse[JsonNode] {.raises: [Exception].} =
proc shareCommunityToUsers*(communityId: string, pubKeys: seq[string], inviteMessage: string): RpcResponse[JsonNode] {.raises: [Exception].} =
return callPrivateRPC("shareCommunity".prefix, %*[{
"communityId": communityId,
"users": pubKeys
"users": pubKeys,
"inviteMessage": inviteMessage
}])
proc getCommunitiesSettings*(): RpcResponse[JsonNode] {.raises: [Exception].} =

View File

@ -33,8 +33,6 @@ Rectangle {
visible: isVisible && (isContact || isUser)
height: visible ? 64 : 0
anchors.right: parent.right
anchors.left: parent.left
border.width: 0
radius: Style.current.radius
color: isHovered ? Style.current.backgroundHover : Style.current.transparent

View File

@ -1,18 +1,18 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Dialogs 1.3
import QtQuick.Layouts 1.13
import QtGraphicalEffects 1.13
import QtQuick 2.14
import QtQuick.Layouts 1.4
import utils 1.0
import shared.controls 1.0
import shared 1.0
import shared.panels 1.0
import shared.status 1.0
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Popups 0.1
Column {
import utils 1.0
import shared 1.0
import shared.controls 1.0
import shared.panels 1.0
import shared.views 1.0
import shared.status 1.0
ColumnLayout {
id: root
property string headerTitle: ""
@ -20,7 +20,57 @@ Column {
property var rootStore
property var contactsStore
property var community
property alias contactListSearch: contactFieldAndList
property var pubKeys: ([])
spacing: Style.current.padding
StyledText {
id: headline
text: qsTr("Contacts")
font.pixelSize: Style.current.primaryTextFontSize
color: Style.current.secondaryText
}
StatusInput {
id: filterInput
placeholderText: qsTr("Search contacts")
input.icon.name: "search"
input.clearable: true
Layout.fillWidth: true
}
ExistingContacts {
id: existingContacts
contactsStore: root.contactsStore
community: root.community
hideCommunityMembers: true
showCheckbox: true
filterText: filterInput.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
}
Layout.rightMargin: -Style.current.padding
Layout.fillWidth: true
Layout.fillHeight: true
}
StatusModalDivider {
bottomPadding: Style.current.padding
Layout.fillWidth: true
}
StatusDescriptionListItem {
title: qsTr("Share community")
@ -32,31 +82,5 @@ Column {
root.rootStore.copyToClipboard(link)
tooltip.visible = !tooltip.visible
}
width: parent.width
}
StatusModalDivider {
bottomPadding: Style.current.padding
}
StyledText {
id: headline
text: qsTr("Contacts")
font.pixelSize: Style.current.primaryTextFontSize
color: Style.current.secondaryText
anchors.left: parent.left
anchors.leftMargin: Style.current.padding
}
ContactsListAndSearch {
id: contactFieldAndList
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width - 32
contactsStore: root.contactsStore
community: root.community
showCheckbox: true
hideCommunityMembers: true
showSearch: false
rootStore: root.rootStore
}
}

View File

@ -0,0 +1,53 @@
import QtQuick 2.14
import QtQuick.Layouts 1.4
import StatusQ.Core 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Popups 0.1
import utils 1.0
import shared 1.0
import shared.views 1.0
import shared.status 1.0
ColumnLayout {
id: root
property var pubKeys: ([])
property var rootStore
property var contactsStore
property alias inviteMessage: messageInput.text
spacing: Style.current.padding
QtObject {
id: d
readonly property int maxMsgLength: 140
readonly property int msgHeight: 108
}
StatusInput {
id: messageInput
label: qsTr("Invitation Message")
charLimit: d.maxMsgLength
placeholderText: qsTr("The message a contact will get with community invitation")
input.multiline: true
input.implicitHeight: d.msgHeight
input.verticalAlignment: TextEdit.AlignTop
Layout.minimumHeight: 150 // TODO: implicitHeight is not calculated well from input.implicitHeight
Layout.fillWidth: true
}
PickedContacts {
id: existingContacts
enabled: false
contactsStore: root.contactsStore
pubKeys: root.pubKeys
Layout.fillWidth: true
Layout.fillHeight: true
}
}

View File

@ -90,13 +90,6 @@ StatusModal {
community: root.community
contactsStore: root.contactsStore
rootStore: root.store
contactListSearch.chatKey.text: ""
contactListSearch.pubKey: ""
contactListSearch.pubKeys: []
contactListSearch.ensUsername: ""
contactListSearch.existingContacts.visible: root.hasAddedContacts
contactListSearch.noContactsRect.visible: !contactListSearch.existingContacts.visible
}
}
}
@ -117,9 +110,9 @@ StatusModal {
text: qsTr("Invite")
visible: root.contentItem.depth > 2
height: !visible ? 0 : implicitHeight
enabled: root.contentItem.currentItem.contactListSearch !== undefined && root.contentItem.currentItem.contactListSearch.pubKeys.length > 0
enabled: root.contentItem.currentItem !== undefined && root.contentItem.currentItem.pubKeys.length > 0
onClicked: {
root.contentItem.currentItem.sendInvites(root.contentItem.currentItem.contactListSearch.pubKeys)
root.contentItem.currentItem.sendInvites(root.contentItem.currentItem.pubKeys, "") // NOTE: empty message
root.contentItem.pop()
}
}

View File

@ -1,76 +1,90 @@
import QtQuick 2.12
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import QtQml.Models 2.3
import QtQuick 2.14
import QtQuick.Layouts 1.4
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Popups 0.1
import utils 1.0
import shared.panels 1.0
import "../../views"
import "../../panels/communities"
StatusModal {
id: popup
StatusStackModal {
id: root
property var rootStore
property var contactsStore
property var community
property var communitySectionModule
property bool hasAddedContacts
signal sendInvites(var pubKeys)
property var pubKeys: ([])
property string inviteMessage: ""
property string validationError: ""
property string successMessage: ""
onOpened: {
contentItem.community = community;
contentItem.contactListSearch.chatKey.text = "";
contentItem.contactListSearch.pubKey = "";
contentItem.contactListSearch.pubKeys = [];
contentItem.contactListSearch.ensUsername = "";
contentItem.contactListSearch.chatKey.forceActiveFocus(Qt.MouseFocusReason);
contentItem.contactListSearch.existingContacts.visible = hasAddedContacts;
contentItem.contactListSearch.noContactsRect.visible = !contentItem.contactListSearch.existingContacts.visible;
}
margins: 32
height: 550
header.title: qsTr("Invite friends")
signal sendInvites(var pubKeys, string inviteMessage)
function processInviteResult(error) {
if (error) {
console.error('Error inviting', error)
contactFieldAndList.validationError = error
return
console.error('Error inviting', error);
root.validationError = error;
} else {
root.validationError = "";
root.successMessage = qsTr("Invite successfully sent");
}
popup.contentItem.contactListSearch.successMessage = qsTr("Invite successfully sent")
}
contentItem: CommunityProfilePopupInviteFriendsPanel {
id: contactFieldAndList
rootStore: popup.rootStore
contactsStore: popup.contactsStore
community: popup.community
onOpened: {
root.pubKeys = [];
root.successMessage = "";
root.validationError = "";
}
leftButtons: [
StatusBackButton {
onClicked: {
popup.close()
}
}
]
stackTitle: qsTr("Invite Contacts to %1").arg(community.name)
width: 640
height: 700
rightButtons: [
StatusButton {
enabled: popup.contentItem.contactListSearch.pubKeys.length > 0
text: qsTr("Invite")
onClicked : {
popup.sendInvites(popup.contentItem.contactListSearch.pubKeys)
}
nextButton: StatusButton {
text: qsTr("Next")
enabled: root.pubKeys.length
onClicked: {
root.currentIndex++;
}
}
finishButton: StatusButton {
enabled: root.pubKeys.length > 0
text: qsTr("Send Invites")
onClicked: {
root.sendInvites(root.pubKeys, root.inviteMessage);
root.close();
}
}
subHeaderItem: StyledText {
text: root.validationError || root.successMessage
visible: root.validationError !== "" || root.successMessage !== ""
font.pixelSize: 13
color: !!root.validationError ? Style.current.danger : Style.current.success
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: visible ? contentHeight : 0
}
stackItems: [
CommunityProfilePopupInviteFriendsPanel {
width: parent.width
rootStore: root.rootStore
contactsStore: root.contactsStore
community: root.community
onPubKeysChanged: root.pubKeys = pubKeys
},
CommunityProfilePopupInviteMessagePanel {
width: parent.width
contactsStore: root.contactsStore
pubKeys: root.pubKeys
onInviteMessageChanged: root.inviteMessage = inviteMessage
}
]
}

View File

@ -229,13 +229,13 @@ StatusAppTwoPanelLayout {
anchors.centerIn: parent
rootStore: root.rootStore
contactsStore: root.rootStore.contactsStore
onClosed: {
destroy()
onClosed: () => {
destroy();
}
onSendInvites: {
const error = root.chatCommunitySectionModule.inviteUsersToCommunity(JSON.stringify(pubKeys))
processInviteResult(error)
onSendInvites: (pubKeys, inviteMessage) => {
const error = root.communitySectionModule.inviteUsersToCommunity(JSON.stringify(pubKeys), inviteMessage);
processInviteResult(error);
}
}
}

View File

@ -92,14 +92,14 @@ SettingsContentBase {
anchors.centerIn: parent
rootStore: root.rootStore
contactsStore: root.contactStore
onClosed: {
destroy()
onClosed: () => {
destroy();
}
onSendInvites: {
const error = root.profileSectionStore.communitiesProfileModule.inviteUsersToCommunity(
community.id, JSON.stringify(pubKeys))
processInviteResult(error)
community.id, JSON.stringify(pubKeys), inviteMessage);
processInviteResult(error);
}
}

View File

@ -719,13 +719,13 @@ Item {
anchors.centerIn: parent
rootStore: appMain.rootStore
contactsStore: appMain.rootStore.contactStore
onClosed: {
destroy()
onClosed: () => {
destroy();
}
onSendInvites: {
const error = communitySectionModule.inviteUsersToCommunity(JSON.stringify(pubKeys))
processInviteResult(error)
onSendInvites: (pubKeys, inviteMessage) => {
const error = communitySectionModule.inviteUsersToCommunity(JSON.stringify(pubKeys), inviteMessage);
processInviteResult(error);
}
}
}

View File

@ -2,11 +2,12 @@ import QtQuick 2.14
import QtQuick.Layouts 1.4
import QtGraphicalEffects 1.14
import utils 1.0
import shared.stores 1.0
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import utils 1.0
import shared.stores 1.0
import "../"
import "../views"
import "../panels"
@ -65,6 +66,7 @@ Item {
ColumnLayout {
id: column
anchors.fill: parent
anchors.rightMargin: Style.current.bigPadding
spacing: Style.current.smallPadding
Input {
@ -225,13 +227,18 @@ Item {
onAddToContactsButtonClicked: {
root.contactsStore.addContact(pubKey)
}
Layout.fillWidth: true
Layout.rightMargin: Style.current.padding
}
Item {
Layout.fillHeight: true
}
Layout.fillWidth: true
}
NoFriendsRectangle {
id: noContactsRect
visible: showContactList
visible: showContactList && existingContacts.count === 0
anchors.centerIn: parent
rootStore: root.rootStore
}

View File

@ -50,8 +50,6 @@ Item {
signal textEdited(string inputValue)
signal keyPressed(var event)
anchors.right: parent.right
anchors.left: parent.left
implicitHeight: inputRectangle.height +
(hasLabel ? inputLabel.height + labelMargin : 0) +
(!keepHeight &&!!validationError ? (validationErrorText.height + validationErrorTopMargin) : 0)

View File

@ -1,6 +1,6 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import StatusQ.Core 0.1
@ -22,6 +22,8 @@ Item {
property bool hideCommunityMembers: false
property var pubKeys: ([])
readonly property alias count: contactListView.count
signal contactClicked(var contact)
function matchesAlias(name, filter) {
@ -29,12 +31,13 @@ Item {
return parts.some(p => p.startsWith(filter))
}
implicitWidth: contactListView.implicitWidth
implicitWidth: contactListView.implicitWidth + contactListView.margins
implicitHeight: visible ? Math.min(contactListView.contentHeight, (expanded ? 320 : 192)) : 0
StatusListView {
id: contactListView
anchors.fill: parent
rightMargin: 0
spacing: 0
model: root.contactsStore.myContactsModel
@ -48,6 +51,9 @@ Item {
name: model.displayName
image: model.icon
isVisible: {
if (isChecked)
return true;
return model.isContact && !model.isBlocked && (root.filterText === "" ||
root.matchesAlias(model.alias.toLowerCase(), root.filterText.toLowerCase()) ||
model.displayName.toLowerCase().includes(root.filterText.toLowerCase()) ||
@ -63,5 +69,3 @@ Item {
}
}
}

View File

@ -10,8 +10,8 @@ import "../popups"
Item {
id: noContactsRect
width: 260
height: visible ? 120 : 0
implicitWidth: 260
implicitHeight: visible ? 120 : 0
property string text: qsTr("You dont have any contacts yet. Invite your friends to start chatting.")
property alias textColor: noContacts.color

View File

@ -0,0 +1,57 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import StatusQ.Core 0.1
import utils 1.0
import shared.status 1.0
import shared.stores 1.0
// TODO move Contact into shared to get rid of that import
import AppLayouts.Chat.controls 1.0
import SortFilterProxyModel 0.2
Item {
id: root
property var contactsStore
property var pubKeys: ([])
readonly property alias count: contactGridView.count
signal contactClicked(var contact)
function matchesAlias(name, filter) {
let parts = name.split(" ")
return parts.some(p => p.startsWith(filter))
}
implicitWidth: contactGridView.implicitWidth + contactGridView.margins
implicitHeight: visible ? contactGridView.contentHeight : 0
StatusGridView {
id: contactGridView
anchors.fill: parent
rightMargin: 0
cellWidth: parent.width / 2
model: SortFilterProxyModel {
sourceModel: root.contactsStore.myContactsModel
filters: [
ExpressionFilter { expression: root.pubKeys.indexOf(model.pubKey) > -1 }
]
}
delegate: Contact {
width: contactGridView.cellWidth
showCheckbox: false
pubKey: model.pubKey
isContact: model.isContact
isUser: false
name: model.displayName
image: model.icon
}
}
}

View File

@ -1,5 +1,6 @@
EnsResolver 1.0 EnsResolver.qml
ExistingContacts 1.0 ExistingContacts.qml
PickedContacts 1.0 PickedContacts.qml
NoFriendsRectangle 1.0 NoFriendsRectangle.qml
SearchResults 1.0 SearchResults.qml
TransactionPreview 1.0 TransactionPreview.qml

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit 2edcf0e3606dd54b469053080cbe951169cbc813
Subproject commit 081974da1e4ee03f912fef85a37fc1d13580ad8c