feat: add contact requests and handling of them
This commit is contained in:
parent
03addd4ea9
commit
436cb42eae
|
@ -74,7 +74,7 @@ QtObject:
|
|||
proc pendingRequestsToJoinForCommunity*(self: CommunitiesView, communityId: string): seq[CommunityMembershipRequest] =
|
||||
result = self.status.chat.pendingRequestsToJoinForCommunity(communityId)
|
||||
|
||||
proc membershipRequestPushed*(self: CommunitiesView, communityName: string, pubKey: string) {.signal.}
|
||||
proc membershipRequestPushed*(self: CommunitiesView, communityId: string, communityName: string, pubKey: string) {.signal.}
|
||||
|
||||
proc addMembershipRequests*(self: CommunitiesView, membershipRequests: seq[CommunityMembershipRequest]) =
|
||||
var communityId: string
|
||||
|
@ -87,7 +87,7 @@ QtObject:
|
|||
let alreadyPresentRequestIdx = community.membershipRequests.findIndexById(request.id)
|
||||
if (alreadyPresentRequestIdx == -1):
|
||||
community.membershipRequests.add(request)
|
||||
self.membershipRequestPushed(community.name, request.publicKey)
|
||||
self.membershipRequestPushed(community.id, community.name, request.publicKey)
|
||||
else:
|
||||
community.membershipRequests[alreadyPresentRequestIdx] = request
|
||||
self.joinedCommunityList.replaceCommunity(community)
|
||||
|
@ -171,7 +171,7 @@ QtObject:
|
|||
error "Error joining the community", msg = e.msg
|
||||
result = fmt"Error joining the community: {e.msg}"
|
||||
|
||||
proc membershipRequestChanged*(self: CommunitiesView, communityName: string, accepted: bool) {.signal.}
|
||||
proc membershipRequestChanged*(self: CommunitiesView, communityId: string, communityName: string, accepted: bool) {.signal.}
|
||||
|
||||
proc communityAdded*(self: CommunitiesView, communityId: string) {.signal.}
|
||||
|
||||
|
@ -200,7 +200,7 @@ QtObject:
|
|||
var i = 0
|
||||
for communityRequest in self.myCommunityRequests:
|
||||
if (communityRequest.communityId == community.id):
|
||||
self.membershipRequestChanged(community.name, true)
|
||||
self.membershipRequestChanged(community.id, community.name, true)
|
||||
self.myCommunityRequests.delete(i, i)
|
||||
break
|
||||
i = i + 1
|
||||
|
|
|
@ -49,6 +49,8 @@ QtObject:
|
|||
proc activeChanged*(self: CommunityItemView) {.signal.}
|
||||
|
||||
proc setActive*(self: CommunityItemView, value: bool) {.slot.} =
|
||||
if (self.active == value):
|
||||
return
|
||||
self.active = value
|
||||
self.status.events.emit("communityActiveChanged", CommunityActiveChangedArgs(active: value))
|
||||
self.activeChanged()
|
||||
|
|
|
@ -120,6 +120,7 @@ proc init*(self: ProfileController, account: Account) =
|
|||
# TODO: view should react to model changes
|
||||
self.status.chat.updateContacts(msgData.contacts)
|
||||
self.view.contacts.updateContactList(msgData.contacts)
|
||||
self.view.contacts.notifyOnNewContactRequests(msgData.contacts)
|
||||
if msgData.installations.len > 0:
|
||||
self.view.devices.addDevices(msgData.installations)
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ type
|
|||
LocalNickname = UserRole + 9
|
||||
ThumbnailImage = UserRole + 10
|
||||
LargeImage = UserRole + 11
|
||||
RequestReceived = UserRole + 12
|
||||
|
||||
QtObject:
|
||||
type ContactList* = ref object of QAbstractListModel
|
||||
|
@ -38,6 +39,15 @@ QtObject:
|
|||
method rowCount(self: ContactList, index: QModelIndex = nil): int =
|
||||
return self.contacts.len
|
||||
|
||||
proc countChanged*(self: ContactList) {.signal.}
|
||||
|
||||
proc count*(self: ContactList): int {.slot.} =
|
||||
self.contacts.len
|
||||
|
||||
QtProperty[int] count:
|
||||
read = count
|
||||
notify = countChanged
|
||||
|
||||
proc userName*(self: ContactList, pubKey: string, defaultValue: string = ""): string {.slot.} =
|
||||
for contact in self.contacts:
|
||||
if(contact.id != pubKey): continue
|
||||
|
@ -66,6 +76,7 @@ QtObject:
|
|||
of "localNickname": result = $contact.localNickname
|
||||
of "thumbnailImage": result = $contact.identityImage.thumbnail
|
||||
of "largeImage": result = $contact.identityImage.large
|
||||
of "requestReceived": result = $contact.requestReceived()
|
||||
|
||||
method data(self: ContactList, index: QModelIndex, role: int): QVariant =
|
||||
if not index.isValid:
|
||||
|
@ -85,6 +96,7 @@ QtObject:
|
|||
of ContactRoles.LocalNickname: result = newQVariant(contact.localNickname)
|
||||
of ContactRoles.ThumbnailImage: result = newQVariant(contact.identityImage.thumbnail)
|
||||
of ContactRoles.LargeImage: result = newQVariant(contact.identityImage.large)
|
||||
of ContactRoles.RequestReceived: result = newQVariant(contact.requestReceived())
|
||||
|
||||
method roleNames(self: ContactList): Table[int, string] =
|
||||
{
|
||||
|
@ -98,13 +110,15 @@ QtObject:
|
|||
ContactRoles.LocalNickname.int:"localNickname",
|
||||
ContactRoles.EnsVerified.int:"ensVerified",
|
||||
ContactRoles.ThumbnailImage.int:"thumbnailImage",
|
||||
ContactRoles.LargeImage.int:"largeImage"
|
||||
ContactRoles.LargeImage.int:"largeImage",
|
||||
ContactRoles.RequestReceived.int:"requestReceived"
|
||||
}.toTable
|
||||
|
||||
proc addContactToList*(self: ContactList, contact: Profile) =
|
||||
self.beginInsertRows(newQModelIndex(), self.contacts.len, self.contacts.len)
|
||||
self.contacts.add(contact)
|
||||
self.endInsertRows()
|
||||
self.countChanged()
|
||||
|
||||
proc hasAddedContacts(self: ContactList): bool {.slot.} =
|
||||
for c in self.contacts:
|
||||
|
@ -134,3 +148,4 @@ QtObject:
|
|||
self.beginResetModel()
|
||||
self.contacts = contactList
|
||||
self.endResetModel()
|
||||
self.countChanged()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import NimQml, chronicles, sequtils, sugar, strutils
|
||||
import NimQml, chronicles, sequtils, sugar, strutils, json
|
||||
import ../../../status/libstatus/utils as status_utils
|
||||
import ../../../status/status
|
||||
import ../../../status/chat/chat
|
||||
|
@ -34,6 +34,7 @@ QtObject:
|
|||
type ContactsView* = ref object of QObject
|
||||
status: Status
|
||||
contactList*: ContactList
|
||||
contactRequests*: ContactList
|
||||
addedContacts*: ContactList
|
||||
blockedContacts*: ContactList
|
||||
contactToAdd*: Profile
|
||||
|
@ -44,6 +45,7 @@ QtObject:
|
|||
proc delete*(self: ContactsView) =
|
||||
self.contactList.delete
|
||||
self.addedContacts.delete
|
||||
self.contactRequests.delete
|
||||
self.blockedContacts.delete
|
||||
self.QObject.delete
|
||||
|
||||
|
@ -51,6 +53,7 @@ QtObject:
|
|||
new(result, delete)
|
||||
result.status = status
|
||||
result.contactList = newContactList()
|
||||
result.contactRequests = newContactList()
|
||||
result.addedContacts = newContactList()
|
||||
result.blockedContacts = newContactList()
|
||||
result.contactToAdd = Profile(
|
||||
|
@ -63,10 +66,12 @@ QtObject:
|
|||
proc updateContactList*(self: ContactsView, contacts: seq[Profile]) =
|
||||
for contact in contacts:
|
||||
self.contactList.updateContact(contact)
|
||||
if contact.systemTags.contains(":contact/added"):
|
||||
self.addedContacts.updateContact(contact)
|
||||
if contact.systemTags.contains(":contact/blocked"):
|
||||
self.blockedContacts.updateContact(contact)
|
||||
if contact.systemTags.contains(contactAdded):
|
||||
self.addedContacts.updateContact(contact)
|
||||
if contact.systemTags.contains(contactBlocked):
|
||||
self.blockedContacts.updateContact(contact)
|
||||
if contact.systemTags.contains(contactRequest) and not contact.systemTags.contains(contactAdded) and not contact.systemTags.contains(contactBlocked):
|
||||
self.contactRequests.updateContact(contact)
|
||||
|
||||
proc contactListChanged*(self: ContactsView) {.signal.}
|
||||
|
||||
|
@ -75,10 +80,18 @@ QtObject:
|
|||
|
||||
proc setContactList*(self: ContactsView, contactList: seq[Profile]) =
|
||||
self.contactList.setNewData(contactList)
|
||||
self.addedContacts.setNewData(contactList.filter(c => c.systemTags.contains(":contact/added")))
|
||||
self.blockedContacts.setNewData(contactList.filter(c => c.systemTags.contains(":contact/blocked")))
|
||||
self.addedContacts.setNewData(contactList.filter(c => c.systemTags.contains(contactAdded)))
|
||||
self.blockedContacts.setNewData(contactList.filter(c => c.systemTags.contains(contactBlocked)))
|
||||
self.contactRequests.setNewData(contactList.filter(c => c.systemTags.contains(contactRequest) and not c.systemTags.contains(contactAdded) and not c.systemTags.contains(contactBlocked)))
|
||||
self.contactListChanged()
|
||||
|
||||
proc contactRequestAdded*(self: ContactsView, name: string, address: string) {.signal.}
|
||||
|
||||
proc notifyOnNewContactRequests*(self: ContactsView, contacts: seq[Profile]) =
|
||||
for contact in contacts:
|
||||
if contact.systemTags.contains(contactRequest) and not contact.systemTags.contains(contactAdded) and not contact.systemTags.contains(contactBlocked):
|
||||
self.contactRequestAdded(status_ens.userNameOrAlias(contact), contact.address)
|
||||
|
||||
QtProperty[QVariant] list:
|
||||
read = getContactList
|
||||
write = setContactList
|
||||
|
@ -104,6 +117,13 @@ QtObject:
|
|||
return true
|
||||
return false
|
||||
|
||||
proc getContactRequests(self: ContactsView): QVariant {.slot.} =
|
||||
return newQVariant(self.contactRequests)
|
||||
|
||||
QtProperty[QVariant] contactRequests:
|
||||
read = getContactRequests
|
||||
notify = contactListChanged
|
||||
|
||||
proc contactToAddChanged*(self: ContactsView) {.signal.}
|
||||
|
||||
proc getContactToAddUsername(self: ContactsView): QVariant {.slot.} =
|
||||
|
@ -129,6 +149,10 @@ QtObject:
|
|||
if id == "": return false
|
||||
self.status.contacts.isAdded(id)
|
||||
|
||||
proc contactRequestReceived*(self: ContactsView, id: string): bool {.slot.} =
|
||||
if id == "": return false
|
||||
self.status.contacts.contactRequestReceived(id)
|
||||
|
||||
proc lookupContact*(self: ContactsView, value: string) {.slot.} =
|
||||
if value == "":
|
||||
return
|
||||
|
@ -164,6 +188,19 @@ QtObject:
|
|||
self.status.chat.join(status_utils.getTimelineChatId(publicKey), ChatType.Profile, "", publicKey)
|
||||
self.contactChanged(publicKey, true)
|
||||
|
||||
proc rejectContactRequest*(self: ContactsView, publicKey: string) {.slot.} =
|
||||
self.status.contacts.rejectContactRequest(publicKey)
|
||||
|
||||
proc rejectContactRequests*(self: ContactsView, publicKeysJSON: string) {.slot.} =
|
||||
let publicKeys = publicKeysJSON.parseJson
|
||||
for pubkey in publicKeys:
|
||||
self.rejectContactRequest(pubkey.getStr)
|
||||
|
||||
proc acceptContactRequests*(self: ContactsView, publicKeysJSON: string) {.slot.} =
|
||||
let publicKeys = publicKeysJSON.parseJson
|
||||
for pubkey in publicKeys:
|
||||
discard self.addContact(pubkey.getStr)
|
||||
|
||||
proc changeContactNickname*(self: ContactsView, publicKey: string, nickname: string) {.slot.} =
|
||||
var nicknameToSet = nickname
|
||||
if (nicknameToSet == ""):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import json, sequtils, sugar
|
||||
import json, sequtils, sugar, chronicles
|
||||
import libstatus/contacts as status_contacts
|
||||
import libstatus/accounts as status_accounts
|
||||
import libstatus/chat as status_chat
|
||||
|
@ -33,13 +33,17 @@ proc getContactByID*(self: ContactModel, id: string): Profile =
|
|||
|
||||
proc blockContact*(self: ContactModel, id: string): string =
|
||||
var contact = self.getContactByID(id)
|
||||
contact.systemTags.add(":contact/blocked")
|
||||
contact.systemTags.add(contactBlocked)
|
||||
let index = contact.systemTags.find(contactAdded)
|
||||
if (index > -1):
|
||||
contact.systemTags.delete(index)
|
||||
discard status_contacts.blockContact(contact)
|
||||
self.events.emit("contactBlocked", Args())
|
||||
|
||||
|
||||
proc unblockContact*(self: ContactModel, id: string): string =
|
||||
var contact = self.getContactByID(id)
|
||||
contact.systemTags.delete(contact.systemTags.find(":contact/blocked"))
|
||||
contact.systemTags.delete(contact.systemTags.find(contactBlocked))
|
||||
discard status_contacts.saveContact(contact.id, contact.ensVerified, contact.ensName, contact.alias, contact.identicon, contact.identityImage.thumbnail, contact.systemTags, contact.localNickname)
|
||||
self.events.emit("contactUnblocked", Args())
|
||||
|
||||
|
@ -47,7 +51,7 @@ proc getAllContacts*(): seq[Profile] =
|
|||
result = map(status_contacts.getContacts().getElems(), proc(x: JsonNode): Profile = x.toProfileModel())
|
||||
|
||||
proc getAddedContacts*(): seq[Profile] =
|
||||
result = getAllContacts().filter(c => c.systemTags.contains(":contact/added"))
|
||||
result = getAllContacts().filter(c => c.systemTags.contains(contactAdded))
|
||||
|
||||
proc getContacts*(self: ContactModel): seq[Profile] =
|
||||
result = getAllContacts()
|
||||
|
@ -89,10 +93,16 @@ proc setNickName*(self: ContactModel, id: string, localNickname: string): string
|
|||
|
||||
proc addContact*(self: ContactModel, id: string): string =
|
||||
var contact = self.getOrCreateContact(id)
|
||||
let updating = contact.systemTags.contains(":contact/added")
|
||||
|
||||
let updating = contact.systemTags.contains(contactAdded)
|
||||
if not updating:
|
||||
contact.systemTags.add(":contact/added")
|
||||
contact.systemTags.add(contactAdded)
|
||||
discard status_chat.createProfileChat(contact.id)
|
||||
else:
|
||||
let index = contact.systemTags.find(contactBlocked)
|
||||
if (index > -1):
|
||||
contact.systemTags.delete(index)
|
||||
|
||||
var thumbnail = ""
|
||||
if contact.identityImage != nil:
|
||||
thumbnail = contact.identityImage.thumbnail
|
||||
|
@ -117,7 +127,7 @@ proc addContact*(self: ContactModel, id: string): string =
|
|||
|
||||
proc removeContact*(self: ContactModel, id: string) =
|
||||
let contact = self.getContactByID(id)
|
||||
contact.systemTags.delete(contact.systemTags.find(":contact/added"))
|
||||
contact.systemTags.delete(contact.systemTags.find(contactAdded))
|
||||
|
||||
var thumbnail = ""
|
||||
if contact.identityImage != nil:
|
||||
|
@ -129,4 +139,20 @@ proc removeContact*(self: ContactModel, id: string) =
|
|||
proc isAdded*(self: ContactModel, id: string): bool =
|
||||
var contact = self.getContactByID(id)
|
||||
if contact.isNil: return false
|
||||
contact.systemTags.contains(":contact/added")
|
||||
contact.systemTags.contains(contactAdded)
|
||||
|
||||
proc contactRequestReceived*(self: ContactModel, id: string): bool =
|
||||
var contact = self.getContactByID(id)
|
||||
if contact.isNil: return false
|
||||
contact.systemTags.contains(contactRequest)
|
||||
|
||||
proc rejectContactRequest*(self: ContactModel, id: string) =
|
||||
let contact = self.getContactByID(id)
|
||||
contact.systemTags.delete(contact.systemTags.find(contactRequest))
|
||||
|
||||
var thumbnail = ""
|
||||
if contact.identityImage != nil:
|
||||
thumbnail = contact.identityImage.thumbnail
|
||||
|
||||
discard status_contacts.saveContact(contact.id, contact.ensVerified, contact.ensName, contact.alias, contact.identicon, thumbnail, contact.systemTags, contact.localNickname)
|
||||
self.events.emit("contactRemoved", Args())
|
||||
|
|
|
@ -8,11 +8,19 @@ type Profile* = ref object
|
|||
appearance*: int
|
||||
systemTags*: seq[string]
|
||||
|
||||
|
||||
const contactAdded* = ":contact/added"
|
||||
const contactBlocked* = ":contact/blocked"
|
||||
const contactRequest* = ":contact/request-received"
|
||||
|
||||
proc isContact*(self: Profile): bool =
|
||||
result = self.systemTags.contains(":contact/added") and not self.systemTags.contains(":contact/blocked")
|
||||
result = self.systemTags.contains(contactAdded) and not self.systemTags.contains(":contact/blocked")
|
||||
|
||||
proc isBlocked*(self: Profile): bool =
|
||||
result = self.systemTags.contains(":contact/blocked")
|
||||
result = self.systemTags.contains(contactBlocked)
|
||||
|
||||
proc requestReceived*(self: Profile): bool =
|
||||
result = self.systemTags.contains(contactRequest)
|
||||
|
||||
proc toProfileModel*(account: Account): Profile =
|
||||
result = Profile(
|
||||
|
|
|
@ -29,13 +29,17 @@ StackLayout {
|
|||
property var doNotShowAddToContactBannerToThose: ([])
|
||||
|
||||
property var onActivated: function () {
|
||||
chatInput.textInput.forceActiveFocus(Qt.MouseFocusReason)
|
||||
inputArea.chatInput.textInput.forceActiveFocus(Qt.MouseFocusReason)
|
||||
}
|
||||
|
||||
property string activeChatId: chatsModel.activeChannel.id
|
||||
property bool isBlocked: profileModel.contacts.isContactBlocked(activeChatId)
|
||||
property bool isContact: profileModel.contacts.isAdded(activeChatId)
|
||||
|
||||
property alias input: chatInput
|
||||
property alias input: inputArea.chatInput
|
||||
|
||||
property string currentNotificationChatId
|
||||
property string currentNotificationCommunityId
|
||||
|
||||
property string hoveredMessage
|
||||
property string activeMessage
|
||||
|
@ -57,7 +61,7 @@ StackLayout {
|
|||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
chatInput.textInput.forceActiveFocus(Qt.MouseFocusReason)
|
||||
inputArea.chatInput.textInput.forceActiveFocus(Qt.MouseFocusReason)
|
||||
}
|
||||
|
||||
Layout.fillHeight: true
|
||||
|
@ -95,12 +99,12 @@ StackLayout {
|
|||
identicon: chatsModel.messageList.getMessageData(i, "identicon"),
|
||||
localNickname: chatsModel.messageList.getMessageData(i, "localName")
|
||||
})
|
||||
chatInput.suggestionsList.append(suggestionsObj[suggestionsObj.length - 1]);
|
||||
inputArea.chatInput.suggestionsList.append(suggestionsObj[suggestionsObj.length - 1]);
|
||||
idMap[contactAddr] = true;
|
||||
}
|
||||
|
||||
function populateSuggestions() {
|
||||
chatInput.suggestionsList.clear()
|
||||
inputArea.chatInput.suggestionsList.clear()
|
||||
const len = chatsModel.suggestionList.rowCount()
|
||||
|
||||
idMap = {}
|
||||
|
@ -116,7 +120,7 @@ StackLayout {
|
|||
localNickname: chatsModel.suggestionList.rowData(i, "localNickname")
|
||||
})
|
||||
|
||||
chatInput.suggestionsList.append(suggestionsObj[suggestionsObj.length - 1]);
|
||||
inputArea.chatInput.suggestionsList.append(suggestionsObj[suggestionsObj.length - 1]);
|
||||
idMap[contactAddr] = true;
|
||||
}
|
||||
const len2 = chatsModel.messageList.rowCount();
|
||||
|
@ -135,7 +139,7 @@ StackLayout {
|
|||
let message = chatsModel.messageList.getMessageData(replyMessageIndex, "message")
|
||||
let identicon = chatsModel.messageList.getMessageData(replyMessageIndex, "identicon")
|
||||
|
||||
chatInput.showReplyArea(userName, message, identicon)
|
||||
inputArea.chatInput.showReplyArea(userName, message, identicon)
|
||||
}
|
||||
|
||||
function requestAddressForTransaction(address, amount, tokenAddress, tokenDecimals = 18) {
|
||||
|
@ -165,6 +169,25 @@ StackLayout {
|
|||
}
|
||||
}
|
||||
|
||||
function clickOnNotification() {
|
||||
applicationWindow.show()
|
||||
applicationWindow.raise()
|
||||
applicationWindow.requestActivate()
|
||||
appMain.changeAppSection(Constants.chat)
|
||||
if (currentNotificationChatId) {
|
||||
chatsModel.setActiveChannel(currentNotificationChatId)
|
||||
} else if (currentNotificationCommunityId) {
|
||||
chatsModel.communities.setActiveCommunity(currentNotificationCommunityId)
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: systemTray
|
||||
onMessageClicked: function () {
|
||||
clickOnNotification()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
}
|
||||
|
@ -222,54 +245,9 @@ StackLayout {
|
|||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
visible: chatsModel.activeChannel.chatType === Constants.chatTypeOneToOne &&
|
||||
!profileModel.contacts.isAdded(activeChatId) &&
|
||||
!doNotShowAddToContactBannerToThose.includes(activeChatId)
|
||||
AddToContactBanner {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
height: 36
|
||||
|
||||
SVGImage {
|
||||
source: "../../img/plusSign.svg"
|
||||
anchors.right: addToContactsTxt.left
|
||||
anchors.rightMargin: Style.current.smallPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
layer.enabled: true
|
||||
layer.effect: ColorOverlay { color: addToContactsTxt.color }
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: addToContactsTxt
|
||||
text: qsTr("Add to contacts")
|
||||
color: Style.current.primary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
Separator {
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: profileModel.contacts.addContact(activeChatId)
|
||||
}
|
||||
|
||||
StatusIconButton {
|
||||
id: closeBtn
|
||||
icon.name: "close"
|
||||
onClicked: {
|
||||
const newArray = Object.assign([], doNotShowAddToContactBannerToThose)
|
||||
newArray.push(activeChatId)
|
||||
doNotShowAddToContactBannerToThose = newArray
|
||||
}
|
||||
width: 20
|
||||
height: 20
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.halfPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
StackLayout {
|
||||
|
@ -306,8 +284,8 @@ StackLayout {
|
|||
Connections {
|
||||
target: chatsModel
|
||||
onActiveChannelChanged: {
|
||||
chatInput.suggestions.hide();
|
||||
chatInput.textInput.forceActiveFocus(Qt.MouseFocusReason)
|
||||
inputArea.chatInput.suggestions.hide();
|
||||
inputArea.chatInput.textInput.forceActiveFocus(Qt.MouseFocusReason)
|
||||
populateSuggestions();
|
||||
}
|
||||
onMessagePushed: {
|
||||
|
@ -322,97 +300,17 @@ StackLayout {
|
|||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
ChatRequestMessage {
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: Style.current.bigPadding
|
||||
}
|
||||
|
||||
InputArea {
|
||||
id: inputArea
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: parent.width
|
||||
height: chatInput.height
|
||||
Layout.preferredHeight: height
|
||||
color: "transparent"
|
||||
|
||||
Connections {
|
||||
target: chatsModel
|
||||
onLoadingMessagesChanged:
|
||||
if(value){
|
||||
loadingMessagesIndicator.active = true
|
||||
} else {
|
||||
timer.setTimeout(function(){
|
||||
loadingMessagesIndicator.active = false;
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: loadingMessagesIndicator
|
||||
active: chatsModel.loadingMessages
|
||||
sourceComponent: loadingIndicator
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: chatInput.top
|
||||
anchors.rightMargin: Style.current.padding
|
||||
anchors.bottomMargin: Style.current.padding
|
||||
}
|
||||
|
||||
Component {
|
||||
id: loadingIndicator
|
||||
LoadingAnimation {}
|
||||
}
|
||||
|
||||
StatusChatInput {
|
||||
id: chatInput
|
||||
visible: {
|
||||
const community = chatsModel.communities.activeCommunity
|
||||
if (chatsModel.activeChannel.chatType === Constants.chatTypePrivateGroupChat) {
|
||||
return chatsModel.activeChannel.isMember
|
||||
}
|
||||
return !community.active ||
|
||||
community.access === Constants.communityChatPublicAccess ||
|
||||
community.admin ||
|
||||
chatsModel.activeChannel.canPost
|
||||
}
|
||||
enabled: !isBlocked
|
||||
chatInputPlaceholder: isBlocked ?
|
||||
//% "This user has been blocked."
|
||||
qsTrId("this-user-has-been-blocked-") :
|
||||
//% "Type a message."
|
||||
qsTrId("type-a-message-")
|
||||
anchors.bottom: parent.bottom
|
||||
recentStickers: chatsModel.stickers.recent
|
||||
stickerPackList: chatsModel.stickers.stickerPacks
|
||||
chatType: chatsModel.activeChannel.chatType
|
||||
onSendTransactionCommandButtonClicked: {
|
||||
if (chatsModel.activeChannel.ensVerified) {
|
||||
txModalLoader.sourceComponent = cmpSendTransactionWithEns
|
||||
} else {
|
||||
txModalLoader.sourceComponent = cmpSendTransactionNoEns
|
||||
}
|
||||
txModalLoader.item.open()
|
||||
}
|
||||
onReceiveTransactionCommandButtonClicked: {
|
||||
txModalLoader.sourceComponent = cmpReceiveTransaction
|
||||
txModalLoader.item.open()
|
||||
}
|
||||
onStickerSelected: {
|
||||
chatsModel.stickers.send(hashId, packId)
|
||||
}
|
||||
onSendMessage: {
|
||||
if (chatInput.fileUrls.length > 0){
|
||||
chatsModel.sendImages(JSON.stringify(fileUrls));
|
||||
}
|
||||
let msg = chatsModel.plainText(Emoji.deparse(chatInput.textInput.text))
|
||||
if (msg.length > 0){
|
||||
msg = chatInput.interpretMessage(msg)
|
||||
chatsModel.sendMessage(msg, chatInput.isReply ? SelectedMessage.messageId : "", Utils.isOnlyEmoji(msg) ? Constants.emojiType : Constants.messageType, false, JSON.stringify(suggestionsObj));
|
||||
if(event) event.accepted = true
|
||||
sendMessageSound.stop();
|
||||
Qt.callLater(sendMessageSound.play);
|
||||
|
||||
chatInput.textInput.clear();
|
||||
chatInput.textInput.textFormat = TextEdit.PlainText;
|
||||
chatInput.textInput.textFormat = TextEdit.RichText;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import QtQuick 2.13
|
||||
import QtGraphicalEffects 1.13
|
||||
import "../../../../../imports"
|
||||
import "../../../../../shared"
|
||||
import "../../../../../shared/status"
|
||||
|
||||
Item {
|
||||
visible: chatsModel.activeChannel.chatType === Constants.chatTypeOneToOne &&
|
||||
!isContact &&
|
||||
!doNotShowAddToContactBannerToThose.includes(activeChatId)
|
||||
height: 36
|
||||
|
||||
SVGImage {
|
||||
source: "../../../../img/plusSign.svg"
|
||||
anchors.right: addToContactsTxt.left
|
||||
anchors.rightMargin: Style.current.smallPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
layer.enabled: true
|
||||
layer.effect: ColorOverlay { color: addToContactsTxt.color }
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: addToContactsTxt
|
||||
text: qsTr("Add to contacts")
|
||||
color: Style.current.primary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
Separator {
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: profileModel.contacts.addContact(activeChatId)
|
||||
}
|
||||
|
||||
StatusIconButton {
|
||||
id: closeBtn
|
||||
icon.name: "close"
|
||||
onClicked: {
|
||||
const newArray = Object.assign([], doNotShowAddToContactBannerToThose)
|
||||
newArray.push(activeChatId)
|
||||
doNotShowAddToContactBannerToThose = newArray
|
||||
}
|
||||
width: 20
|
||||
height: 20
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.halfPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import QtQuick 2.13
|
||||
import "../../../../../imports"
|
||||
import "../../../../../shared"
|
||||
import "../../../../../shared/status"
|
||||
|
||||
Item {
|
||||
visible: chatsModel.activeChannel.chatType === Constants.chatTypeOneToOne && !isContact
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
|
||||
Image {
|
||||
id: waveImg
|
||||
source: "../../../../img/wave.png"
|
||||
width: 80
|
||||
height: 80
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: contactText1
|
||||
text: qsTr("You need to be mutual contacts with this person for them to receive your messages")
|
||||
anchors.top: waveImg.bottom
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
anchors.topMargin: Style.current.padding
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width / 1.3
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: contactText2
|
||||
text: qsTr("Just click this button to add them as contact. They will receive a notification all once they accept you as contact as well, you'll be able to chat")
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
anchors.top: contactText1.bottom
|
||||
anchors.topMargin: 2
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width / 1.3
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
text: qsTr("Add to contacts")
|
||||
anchors.top: contactText2.bottom
|
||||
anchors.topMargin: Style.current.smallPadding
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
onClicked: profileModel.contacts.addContact(activeChatId)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
import QtQuick 2.13
|
||||
import "../../../../../imports"
|
||||
import "../../../../../shared"
|
||||
import "../../../../../shared/status"
|
||||
import "../../components"
|
||||
|
||||
Item {
|
||||
property alias chatInput: chatInput
|
||||
|
||||
id: inputArea
|
||||
height: chatInput.height
|
||||
|
||||
Connections {
|
||||
target: chatsModel
|
||||
onLoadingMessagesChanged:
|
||||
if(value){
|
||||
loadingMessagesIndicator.active = true
|
||||
} else {
|
||||
timer.setTimeout(function(){
|
||||
loadingMessagesIndicator.active = false;
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: loadingMessagesIndicator
|
||||
active: chatsModel.loadingMessages
|
||||
sourceComponent: loadingIndicator
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: chatInput.top
|
||||
anchors.rightMargin: Style.current.padding
|
||||
anchors.bottomMargin: Style.current.padding
|
||||
}
|
||||
|
||||
Component {
|
||||
id: loadingIndicator
|
||||
LoadingAnimation {}
|
||||
}
|
||||
|
||||
StatusChatInput {
|
||||
id: chatInput
|
||||
visible: {
|
||||
const community = chatsModel.communities.activeCommunity
|
||||
if (chatsModel.activeChannel.chatType === Constants.chatTypePrivateGroupChat) {
|
||||
return chatsModel.activeChannel.isMember
|
||||
}
|
||||
return !community.active ||
|
||||
community.access === Constants.communityChatPublicAccess ||
|
||||
community.admin ||
|
||||
chatsModel.activeChannel.canPost
|
||||
}
|
||||
enabled: !isBlocked
|
||||
chatInputPlaceholder: isBlocked ?
|
||||
//% "This user has been blocked."
|
||||
qsTrId("this-user-has-been-blocked-") :
|
||||
//% "Type a message."
|
||||
qsTrId("type-a-message-")
|
||||
anchors.bottom: parent.bottom
|
||||
recentStickers: chatsModel.stickers.recent
|
||||
stickerPackList: chatsModel.stickers.stickerPacks
|
||||
chatType: chatsModel.activeChannel.chatType
|
||||
onSendTransactionCommandButtonClicked: {
|
||||
if (chatsModel.activeChannel.ensVerified) {
|
||||
txModalLoader.sourceComponent = cmpSendTransactionWithEns
|
||||
} else {
|
||||
txModalLoader.sourceComponent = cmpSendTransactionNoEns
|
||||
}
|
||||
txModalLoader.item.open()
|
||||
}
|
||||
onReceiveTransactionCommandButtonClicked: {
|
||||
txModalLoader.sourceComponent = cmpReceiveTransaction
|
||||
txModalLoader.item.open()
|
||||
}
|
||||
onStickerSelected: {
|
||||
chatsModel.stickers.send(hashId, packId)
|
||||
}
|
||||
onSendMessage: {
|
||||
if (chatInput.fileUrls.length > 0){
|
||||
chatsModel.sendImages(JSON.stringify(fileUrls));
|
||||
}
|
||||
let msg = chatsModel.plainText(Emoji.deparse(chatInput.textInput.text))
|
||||
if (msg.length > 0){
|
||||
msg = chatInput.interpretMessage(msg)
|
||||
chatsModel.sendMessage(msg, chatInput.isReply ? SelectedMessage.messageId : "", Utils.isOnlyEmoji(msg) ? Constants.emojiType : Constants.messageType, false, JSON.stringify(suggestionsObj));
|
||||
if(event) event.accepted = true
|
||||
sendMessageSound.stop();
|
||||
Qt.callLater(sendMessageSound.play);
|
||||
|
||||
chatInput.textInput.clear();
|
||||
chatInput.textInput.textFormat = TextEdit.PlainText;
|
||||
chatInput.textInput.textFormat = TextEdit.RichText;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import QtQml.Models 2.13
|
|||
import QtGraphicalEffects 1.13
|
||||
import QtQuick.Dialogs 1.3
|
||||
import "../../../../shared"
|
||||
import "../../../../shared/status"
|
||||
import "../../../../imports"
|
||||
import "../components"
|
||||
import "./samples/"
|
||||
|
@ -31,8 +32,6 @@ ScrollView {
|
|||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
|
||||
ListView {
|
||||
property string currentNotificationChatId
|
||||
|
||||
id: chatLogView
|
||||
anchors.fill: parent
|
||||
anchors.bottomMargin: Style.current.bigPadding
|
||||
|
@ -47,23 +46,33 @@ ScrollView {
|
|||
verticalLayoutDirection: ListView.BottomToTop
|
||||
|
||||
// This header and Connections is to create an invisible padding so that the chat identifier is at the top
|
||||
// The Connections is necessary, because doing the check inside teh ehader created a binding loop (the contentHeight includes the header height
|
||||
// The Connections is necessary, because doing the check inside the header created a binding loop (the contentHeight includes the header height
|
||||
// If the content height is smaller than the full height, we "show" the padding so that the chat identifier is at the top, otherwise we disable the Connections
|
||||
header: Item {
|
||||
height: 0
|
||||
width: chatLogView.width
|
||||
}
|
||||
function checkHeaderHeight() {
|
||||
if (!chatLogView.headerItem) {
|
||||
return
|
||||
}
|
||||
|
||||
if (chatLogView.contentItem.height - chatLogView.headerItem.height < chatLogView.height) {
|
||||
chatLogView.headerItem.height = chatLogView.height - (chatLogView.contentItem.height - chatLogView.headerItem.height) - 36
|
||||
} else {
|
||||
chatLogView.headerItem.height = 0
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
id: contentHeightConnection
|
||||
enabled: true
|
||||
target: chatLogView
|
||||
onContentHeightChanged: {
|
||||
if (chatLogView.contentItem.height - chatLogView.headerItem.height < chatLogView.height) {
|
||||
chatLogView.headerItem.height = chatLogView.height - (chatLogView.contentItem.height - chatLogView.headerItem.height) - 36
|
||||
} else {
|
||||
chatLogView.headerItem.height = 0
|
||||
contentHeightConnection.enabled = false
|
||||
}
|
||||
chatLogView.checkHeaderHeight()
|
||||
}
|
||||
onHeightChanged: {
|
||||
chatLogView.checkHeaderHeight()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,14 +157,6 @@ ScrollView {
|
|||
return true
|
||||
}
|
||||
|
||||
function clickOnNotification(chatId) {
|
||||
applicationWindow.show()
|
||||
applicationWindow.raise()
|
||||
applicationWindow.requestActivate()
|
||||
chatsModel.setActiveChannel(chatId)
|
||||
appMain.changeAppSection(Constants.chat)
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: chatsModel
|
||||
onMessagesLoaded: {
|
||||
|
@ -191,7 +192,8 @@ ScrollView {
|
|||
return
|
||||
}
|
||||
|
||||
chatLogView.currentNotificationChatId = chatId
|
||||
chatColumnLayout.currentNotificationChatId = chatId
|
||||
chatColumnLayout.currentNotificationCommunityId = null
|
||||
|
||||
let name;
|
||||
if (appSettings.notificationMessagePreviewSetting === Constants.notificationPreviewAnonymous) {
|
||||
|
@ -223,7 +225,7 @@ ScrollView {
|
|||
SystemTrayIcon.NoIcon,
|
||||
Constants.notificationPopupTTL)
|
||||
} else {
|
||||
notificationWindow.notifyUser(chatId, name, message, chatType, identicon, chatLogView.clickOnNotification)
|
||||
notificationWindow.notifyUser(chatId, name, message, chatType, identicon, chatColumnLayout.clickOnNotification)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -232,7 +234,9 @@ ScrollView {
|
|||
Connections {
|
||||
target: chatsModel.communities
|
||||
|
||||
onMembershipRequestChanged: function (communityName, accepted) {
|
||||
onMembershipRequestChanged: function (communityId, communityName, accepted) {
|
||||
chatColumnLayout.currentNotificationChatId = null
|
||||
chatColumnLayout.currentNotificationCommunityId = communityId
|
||||
systemTray.showMessage("Status",
|
||||
accepted ? qsTr("You have been accepted into the ‘%1’ community").arg(communityName) :
|
||||
qsTr("Your request to join the ‘%1’ community was declined").arg(communityName),
|
||||
|
@ -240,7 +244,9 @@ ScrollView {
|
|||
Constants.notificationPopupTTL)
|
||||
}
|
||||
|
||||
onMembershipRequestPushed: function (communityName, pubKey) {
|
||||
onMembershipRequestPushed: function (communityId, communityName, pubKey) {
|
||||
chatColumnLayout.currentNotificationChatId = null
|
||||
chatColumnLayout.currentNotificationCommunityId = communityId
|
||||
systemTray.showMessage(qsTr("New membership request"),
|
||||
qsTr("%1 asks to join ‘%2’").arg(Utils.getDisplayName(pubKey)).arg(communityName),
|
||||
SystemTrayIcon.NoIcon,
|
||||
|
@ -248,13 +254,6 @@ ScrollView {
|
|||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: systemTray
|
||||
onMessageClicked: {
|
||||
chatLogView.clickOnNotification(chatLogView.currentNotificationChatId)
|
||||
}
|
||||
}
|
||||
|
||||
property var loadMsgs : Backpressure.oneInTime(chatLogView, 500, function() {
|
||||
if(loadingMessages) return;
|
||||
loadingMessages = true;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import QtQuick 2.13
|
||||
import Qt.labs.platform 1.1
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
|
||||
import "../../../imports"
|
||||
import "../../../shared"
|
||||
import "../../../shared/status"
|
||||
import "./components"
|
||||
import "./ContactsColumn"
|
||||
import "./CommunityComponents"
|
||||
|
@ -90,6 +92,15 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: contactRequestsPopup
|
||||
ContactRequestsPopup {
|
||||
onClosed: {
|
||||
destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SearchBox {
|
||||
id: searchBox
|
||||
anchors.top: title.bottom
|
||||
|
@ -108,9 +119,37 @@ Rectangle {
|
|||
anchors.topMargin: Style.current.padding
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: profileModel.contacts
|
||||
onContactRequestAdded: {
|
||||
systemTray.showMessage(qsTr("New contact request"),
|
||||
qsTr("%1 requests to become contacts").arg(Utils.removeStatusEns(name)),
|
||||
SystemTrayIcon.NoIcon,
|
||||
Constants.notificationPopupTTL)
|
||||
}
|
||||
}
|
||||
|
||||
StatusSettingsLineButton {
|
||||
property int nbRequests: profileModel.contacts.contactRequests.count
|
||||
|
||||
id: contactRequest
|
||||
anchors.top: searchBox.bottom
|
||||
anchors.topMargin: visible ? Style.current.padding : 0
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.halfPadding
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.halfPadding
|
||||
visible: nbRequests > 0
|
||||
height: visible ? implicitHeight : 0
|
||||
text: qsTr("Contact requests")
|
||||
isBadge: true
|
||||
badgeText: nbRequests.toString()
|
||||
onClicked: openPopup(contactRequestsPopup)
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: chatGroupsContainer
|
||||
anchors.top: searchBox.bottom
|
||||
anchors.top: contactRequest.bottom
|
||||
anchors.topMargin: Style.current.padding
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import QtGraphicalEffects 1.13
|
||||
import "../../../../imports"
|
||||
import "../../../../shared"
|
||||
import "../../../../shared/status"
|
||||
import "../../Profile/Sections/Contacts"
|
||||
|
||||
ModalPopup {
|
||||
id: popup
|
||||
|
||||
|
||||
title: qsTr("Contact requests")
|
||||
|
||||
ListView {
|
||||
id: contactList
|
||||
|
||||
property Component profilePopupComponent: ProfilePopup {
|
||||
id: profilePopup
|
||||
onClosed: destroy()
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: -Style.current.halfPadding
|
||||
anchors.rightMargin: -Style.current.halfPadding
|
||||
|
||||
model: profileModel.contacts.contactRequests
|
||||
clip: true
|
||||
|
||||
delegate: ContactRequest {
|
||||
name: Utils.removeStatusEns(model.name)
|
||||
address: model.address
|
||||
localNickname: model.localNickname
|
||||
identicon: model.thumbnailImage || model.identicon
|
||||
profileClick: function (showFooter, userName, fromAuthor, identicon, textParam, nickName) {
|
||||
var popup = profilePopupComponent.createObject(contactList);
|
||||
popup.openPopup(showFooter, userName, fromAuthor, identicon, textParam, nickName);
|
||||
}
|
||||
onBlockContactActionTriggered: {
|
||||
blockContactConfirmationDialog.contactName = name
|
||||
blockContactConfirmationDialog.contactAddress = address
|
||||
blockContactConfirmationDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: Item {
|
||||
width: parent.width
|
||||
height: children[0].height
|
||||
|
||||
BlockContactConfirmationDialog {
|
||||
id: blockContactConfirmationDialog
|
||||
onBlockButtonClicked: {
|
||||
profileModel.contacts.blockContact(blockContactConfirmationDialog.contactAddress)
|
||||
blockContactConfirmationDialog.close()
|
||||
}
|
||||
}
|
||||
|
||||
ConfirmationDialog {
|
||||
id: declineAllDialog
|
||||
title: qsTr("Decline all contacts")
|
||||
confirmationText: qsTr("Are you sure you want to decline all these contact requests")
|
||||
onConfirmButtonClicked: {
|
||||
const pubkeys = []
|
||||
for (let i = 0; i < contactList.count; i++) {
|
||||
pubkeys.push(contactList.itemAtIndex(i).address)
|
||||
}
|
||||
profileModel.contacts.rejectContactRequests(JSON.stringify(pubkeys))
|
||||
declineAllDialog.close()
|
||||
}
|
||||
}
|
||||
|
||||
ConfirmationDialog {
|
||||
id: acceptAllDialog
|
||||
title: qsTr("Accept all contacts")
|
||||
confirmationText: qsTr("Are you sure you want to accept all these contact requests")
|
||||
onConfirmButtonClicked: {
|
||||
const pubkeys = []
|
||||
for (let i = 0; i < contactList.count; i++) {
|
||||
pubkeys.push(contactList.itemAtIndex(i).address)
|
||||
}
|
||||
profileModel.contacts.acceptContactRequests(JSON.stringify(pubkeys))
|
||||
acceptAllDialog.close()
|
||||
}
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
id: blockBtn
|
||||
anchors.right: addToContactsButton.left
|
||||
anchors.rightMargin: Style.current.padding
|
||||
anchors.bottom: parent.bottom
|
||||
type: "warn"
|
||||
text: qsTr("Decline all")
|
||||
onClicked: declineAllDialog.open()
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
id: addToContactsButton
|
||||
anchors.right: parent.right
|
||||
text: qsTr("Accept all")
|
||||
anchors.bottom: parent.bottom
|
||||
onClicked: acceptAllDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -61,7 +61,7 @@ ListView {
|
|||
// TODO: Make ConfirmationDialog a dynamic component on a future refactor
|
||||
ConfirmationDialog {
|
||||
id: removeContactConfirmationDialog
|
||||
title: qsTrId("remove-contact")
|
||||
title: qsTr("Remove contact")
|
||||
//% "Are you sure you want to remove this contact?"
|
||||
confirmationText: qsTrId("are-you-sure-you-want-to-remove-this-contact-")
|
||||
onConfirmButtonClicked: {
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import "../../../../../imports"
|
||||
import "../../../../../shared"
|
||||
import "../../../../../shared/status"
|
||||
|
||||
Rectangle {
|
||||
property string name
|
||||
property string address
|
||||
property string identicon
|
||||
property string localNickname
|
||||
property var profileClick: function() {}
|
||||
signal blockContactActionTriggered(name: string, address: string)
|
||||
property bool isHovered: false
|
||||
id: container
|
||||
|
||||
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
|
||||
|
||||
StatusImageIdenticon {
|
||||
id: accountImage
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
source: identicon
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: usernameText
|
||||
text: name
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: 17
|
||||
anchors.top: accountImage.top
|
||||
anchors.topMargin: Style.current.smallPadding
|
||||
anchors.left: accountImage.right
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.right: declineBtn.left
|
||||
anchors.rightMargin: Style.current.padding
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
onClicked: {
|
||||
if (mouse.button === Qt.RightButton) {
|
||||
contactContextMenu.popup()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
onHoveredChanged: container.isHovered = hovered
|
||||
}
|
||||
|
||||
StatusIconButton {
|
||||
id: declineBtn
|
||||
icon.name: "close"
|
||||
onClicked: profileModel.contacts.rejectContactRequest(container.address)
|
||||
width: 32
|
||||
height: 32
|
||||
padding: 6
|
||||
iconColor: Style.current.danger
|
||||
hoveredIconColor: Style.current.danger
|
||||
highlightedBackgroundColor: Utils.setColorAlpha(Style.current.danger, 0.1)
|
||||
anchors.right: acceptBtn.left
|
||||
anchors.rightMargin: Style.current.halfPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StatusIconButton {
|
||||
id: acceptBtn
|
||||
icon.name: "check-circle"
|
||||
onClicked: profileModel.contacts.addContact(container.address)
|
||||
width: 32
|
||||
height: 32
|
||||
padding: 6
|
||||
iconColor: Style.current.success
|
||||
hoveredIconColor: Style.current.success
|
||||
highlightedBackgroundColor: Utils.setColorAlpha(Style.current.success, 0.1)
|
||||
anchors.right: menuButton.left
|
||||
anchors.rightMargin: Style.current.halfPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StatusContextMenuButton {
|
||||
property int iconSize: 14
|
||||
id: menuButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.current.padding
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: {
|
||||
contactContextMenu.popup()
|
||||
}
|
||||
|
||||
PopupMenu {
|
||||
id: contactContextMenu
|
||||
hasArrow: false
|
||||
Action {
|
||||
icon.source: "../../../../img/profileActive.svg"
|
||||
icon.width: menuButton.iconSize
|
||||
icon.height: menuButton.iconSize
|
||||
//% "View Profile"
|
||||
text: qsTrId("view-profile")
|
||||
onTriggered: profileClick(true, name, address, identicon, "", localNickname)
|
||||
enabled: true
|
||||
}
|
||||
Separator {}
|
||||
Action {
|
||||
icon.source: "../../../../img/block-icon.svg"
|
||||
icon.width: menuButton.iconSize
|
||||
icon.height: menuButton.iconSize
|
||||
icon.color: Style.current.danger
|
||||
text: qsTr("Decline and block")
|
||||
onTriggered: container.blockContactActionTriggered(name, address)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 9.7 KiB |
|
@ -96,10 +96,13 @@ DISTFILES += \
|
|||
app/AppLayouts/Browser/FavoritesBar.qml \
|
||||
app/AppLayouts/Browser/FavoritesList.qml \
|
||||
app/AppLayouts/Browser/components/BookmarkButton.qml \
|
||||
app/AppLayouts/Chat/ChatColumn/ChatComponents/AddToContactBanner.qml \
|
||||
app/AppLayouts/Chat/ChatColumn/ChatComponents/ChatCommandButton.qml \
|
||||
app/AppLayouts/Chat/ChatColumn/ChatComponents/ChatCommandModal.qml \
|
||||
app/AppLayouts/Chat/ChatColumn/ChatComponents/ChatCommandsPopup.qml \
|
||||
app/AppLayouts/Chat/ChatColumn/ChatComponents/ChatInputButton.qml \
|
||||
app/AppLayouts/Chat/ChatColumn/ChatComponents/ChatRequestMessage.qml \
|
||||
app/AppLayouts/Chat/ChatColumn/ChatComponents/InputArea.qml \
|
||||
app/AppLayouts/Chat/ChatColumn/ChatComponents/RequestModal.qml \
|
||||
app/AppLayouts/Chat/ChatColumn/ChatComponents/SignTransactionModal.qml \
|
||||
app/AppLayouts/Chat/ChatColumn/CompactMessage.qml \
|
||||
|
@ -156,6 +159,7 @@ DISTFILES += \
|
|||
app/AppLayouts/Chat/components/CommunitiesPopup.qml \
|
||||
app/AppLayouts/Chat/components/CommunityDetailPopup.qml \
|
||||
app/AppLayouts/Chat/components/ContactList.qml \
|
||||
app/AppLayouts/Chat/components/ContactRequestsPopup.qml \
|
||||
app/AppLayouts/Chat/components/CreateCommunityPopup.qml \
|
||||
app/AppLayouts/Chat/components/EmojiCategoryButton.qml \
|
||||
app/AppLayouts/Chat/components/EmojiPopup.qml \
|
||||
|
@ -175,6 +179,7 @@ DISTFILES += \
|
|||
app/AppLayouts/Profile/LeftTab/components/MenuButton.qml \
|
||||
app/AppLayouts/Chat/data/EmojiReactions.qml \
|
||||
app/AppLayouts/Profile/Sections/AppearanceContainer.qml \
|
||||
app/AppLayouts/Profile/Sections/Contacts/ContactRequest.qml \
|
||||
app/AppLayouts/Profile/Sections/NetworksModal.qml \
|
||||
app/AppLayouts/Profile/Sections/BackupSeedModal.qml \
|
||||
app/AppLayouts/Profile/Sections/BrowserContainer.qml \
|
||||
|
|
|
@ -119,19 +119,22 @@ Item {
|
|||
text: {
|
||||
switch(root.realChatType){
|
||||
//% "Public chat"
|
||||
case Constants.chatTypePublic: return qsTrId("public-chat")
|
||||
case Constants.chatTypeOneToOne: return (profileModel.contacts.isAdded(root.chatId) ?
|
||||
//% "Contact"
|
||||
qsTrId("chat-is-a-contact") :
|
||||
//% "Not a contact"
|
||||
qsTrId("chat-is-not-a-contact"))
|
||||
case Constants.chatTypePrivateGroupChat:
|
||||
let cnt = chatsModel.activeChannel.members.rowCount();
|
||||
//% "%1 members"
|
||||
if(cnt > 1) return qsTrId("%1-members").arg(cnt);
|
||||
//% "1 member"
|
||||
return qsTrId("1-member");
|
||||
default: return "...";
|
||||
case Constants.chatTypePublic: return qsTrId("public-chat")
|
||||
case Constants.chatTypeOneToOne: return (profileModel.contacts.isAdded(root.chatId) ?
|
||||
profileModel.contacts.contactRequestReceived(root.chatId) ?
|
||||
//% "Contact"
|
||||
qsTrId("chat-is-a-contact") :
|
||||
qsTr("Contact request pending") :
|
||||
|
||||
//% "Not a contact"
|
||||
qsTrId("chat-is-not-a-contact"))
|
||||
case Constants.chatTypePrivateGroupChat:
|
||||
let cnt = chatsModel.activeChannel.members.rowCount();
|
||||
//% "%1 members"
|
||||
if(cnt > 1) return qsTrId("%1-members").arg(cnt);
|
||||
//% "1 member"
|
||||
return qsTrId("1-member");
|
||||
default: return "...";
|
||||
}
|
||||
}
|
||||
font.pixelSize: 12
|
||||
|
|
Loading…
Reference in New Issue