From 149ff69310d1e94f8196d2310c33a391d5e13477 Mon Sep 17 00:00:00 2001 From: Sale Djenic Date: Thu, 24 Mar 2022 21:55:22 +0100 Subject: [PATCH] fix(@desktop/contacts): contacts updated to match more closely to requested design --- .../chat_section/chat_content/controller.nim | 4 +- .../main/chat_section/chat_content/module.nim | 2 +- .../modules/main/chat_section/controller.nim | 4 +- src/app/modules/main/chat_section/module.nim | 27 +-- src/app/modules/main/controller.nim | 4 +- src/app/modules/main/module.nim | 2 +- .../profile_section/contacts/controller.nim | 14 +- .../profile_section/contacts/io_interface.nim | 5 + .../main/profile_section/contacts/module.nim | 116 +++++---- .../main/profile_section/contacts/view.nim | 98 ++++++-- .../modules/shared_models/contacts_item.nim | 30 ++- .../modules/shared_models/contacts_model.nim | 20 +- src/app_service/service/chat/service.nim | 2 +- .../service/contacts/dto/contacts.nim | 35 ++- src/app_service/service/contacts/service.nim | 75 ++++-- src/app_service/service/message/service.nim | 2 +- .../Profile/panels/ContactPanel.qml | 58 ++++- .../Profile/panels/ContactsListPanel.qml | 172 +++++++++++--- .../Profile/stores/ContactsStore.qml | 27 +-- .../AppLayouts/Profile/views/ContactsView.qml | 223 +++++++++++++----- .../shared/controls/StatusTabButton.qml | 7 +- ui/imports/utils/Constants.qml | 17 ++ 22 files changed, 711 insertions(+), 233 deletions(-) diff --git a/src/app/modules/main/chat_section/chat_content/controller.nim b/src/app/modules/main/chat_section/chat_content/controller.nim index 3c59ee61fa..129bcb07fe 100644 --- a/src/app/modules/main/chat_section/chat_content/controller.nim +++ b/src/app/modules/main/chat_section/chat_content/controller.nim @@ -149,8 +149,8 @@ proc getMessageDetails*(self: Controller, messageId: string): proc isUsersListAvailable*(self: Controller): bool = return self.isUsersListAvailable -proc getMyAddedContacts*(self: Controller): seq[ContactsDto] = - return self.contactService.getAddedContacts() +proc getMyMutualContacts*(self: Controller): seq[ContactsDto] = + return self.contactService.getContactsByGroup(ContactsGroup.MyMutualContacts) proc muteChat*(self: Controller) = self.chatService.muteChat(self.chatId) diff --git a/src/app/modules/main/chat_section/chat_content/module.nim b/src/app/modules/main/chat_section/chat_content/module.nim index 47de642d36..c860854656 100644 --- a/src/app/modules/main/chat_section/chat_content/module.nim +++ b/src/app/modules/main/chat_section/chat_content/module.nim @@ -234,7 +234,7 @@ method getMyChatId*(self: Module): string = self.controller.getMyChatId() method isMyContact*(self: Module, contactId: string): bool = - self.controller.getMyAddedContacts().filter(x => x.id == contactId).len > 0 + self.controller.getMyMutualContacts().filter(x => x.id == contactId).len > 0 method muteChat*(self: Module) = self.controller.muteChat() diff --git a/src/app/modules/main/chat_section/controller.nim b/src/app/modules/main/chat_section/controller.nim index b24e92d294..5fbbb3a951 100644 --- a/src/app/modules/main/chat_section/controller.nim +++ b/src/app/modules/main/chat_section/controller.nim @@ -267,8 +267,8 @@ proc clearChatHistory*(self: Controller, chatId: string) = proc getCurrentFleet*(self: Controller): string = return self.settingsService.getFleetAsString() -proc getContacts*(self: Controller): seq[ContactsDto] = - return self.contactService.getContacts() +proc getContacts*(self: Controller, group: ContactsGroup): seq[ContactsDto] = + return self.contactService.getContactsByGroup(group) proc getContactDetails*(self: Controller, id: string): ContactDetails = return self.contactService.getContactDetails(id) diff --git a/src/app/modules/main/chat_section/module.nim b/src/app/modules/main/chat_section/module.nim index 4fa53a0195..d9e5b4d05a 100644 --- a/src/app/modules/main/chat_section/module.nim +++ b/src/app/modules/main/chat_section/module.nim @@ -222,39 +222,36 @@ proc createItemFromPublicKey(self: Module, publicKey: string): contacts_item.Ite contactDetails.displayName, contactDetails.icon, contactDetails.isIdenticon, - contactDetails.details.isContact(), + contactDetails.details.isMutualContact(), contactDetails.details.isBlocked(), - contactDetails.details.requestReceived() + contactDetails.details.isContactVerified(), + contactDetails.details.isContactUntrustworthy() ) proc initContactRequestsModel(self: Module) = var contactsWhoAddedMe: seq[contacts_item.Item] - let contacts = self.controller.getContacts() + let contacts = self.controller.getContacts(ContactsGroup.IncomingPendingContactRequests) for c in contacts: - if(c.requestReceived() and not c.isContact() and not c.isBlocked()): - let item = self.createItemFromPublicKey(c.id) - contactsWhoAddedMe.add(item) + let item = self.createItemFromPublicKey(c.id) + contactsWhoAddedMe.add(item) self.view.contactRequestsModel().addItems(contactsWhoAddedMe) proc convertPubKeysToJson(self: Module, pubKeys: string): seq[string] = return map(parseJson(pubKeys).getElems(), proc(x:JsonNode):string = x.getStr) - method initListOfMyContacts*(self: Module, pubKeys: string) = var myContacts: seq[contacts_item.Item] - let contacts = self.controller.getContacts() + let contacts = self.controller.getContacts(ContactsGroup.MyMutualContacts) for c in contacts: - if(c.isContact() and not c.isBlocked() and not pubKeys.contains(c.id)): - let item = self.createItemFromPublicKey(c.id) - myContacts.add(item) + let item = self.createItemFromPublicKey(c.id) + myContacts.add(item) self.view.listOfMyContacts().addItems(myContacts) method clearListOfMyContacts*(self: Module) = self.view.listOfMyContacts().clear() - method load*(self: Module, events: EventEmitter, settingsService: settings_service.Service, contactService: contact_service.Service, @@ -595,11 +592,11 @@ method getCurrentFleet*(self: Module): string = method acceptContactRequest*(self: Module, publicKey: string) = self.controller.addContact(publicKey) - self.createOneToOneChat(communityID = "" , publicKey, ensName = "") method onContactAccepted*(self: Module, publicKey: string) = self.view.contactRequestsModel().removeItemWithPubKey(publicKey) self.updateParentBadgeNotifications() + self.createOneToOneChat(communityID = "" , publicKey, ensName = "") method acceptAllContactRequests*(self: Module) = let pubKeys = self.view.contactRequestsModel().getPublicKeys() @@ -630,8 +627,8 @@ method onContactUnblocked*(self: Module, publicKey: string) = method onContactDetailsUpdated*(self: Module, publicKey: string) = let contactDetails = self.controller.getContactDetails(publicKey) - if (contactDetails.details.requestReceived() and - not contactDetails.details.isContact()and + if (contactDetails.details.isContactRequestReceived() and + not contactDetails.details.isContactRequestSent() and not contactDetails.details.isBlocked() and not self.view.contactRequestsModel().containsItemWithPubKey(publicKey)): let item = self.createItemFromPublicKey(publicKey) diff --git a/src/app/modules/main/controller.nim b/src/app/modules/main/controller.nim index abe7402f24..11d2e26d03 100644 --- a/src/app/modules/main/controller.nim +++ b/src/app/modules/main/controller.nim @@ -265,8 +265,8 @@ proc setUserStatus*(self: Controller, status: bool) = proc getContact*(self: Controller, id: string): ContactsDto = return self.contactsService.getContactById(id) -proc getContacts*(self: Controller): seq[ContactsDto] = - return self.contactsService.getContacts() +proc getContacts*(self: Controller, group: ContactsGroup): seq[ContactsDto] = + return self.contactsService.getContactsByGroup(group) proc getContactNameAndImage*(self: Controller, contactId: string): tuple[name: string, image: string, isIdenticon: bool] = diff --git a/src/app/modules/main/module.nim b/src/app/modules/main/module.nim index 59517932b0..152fb1ffd6 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -261,7 +261,7 @@ method load*[T]( var activeSectionId = singletonInstance.localAccountSensitiveSettings.getActiveSection() # Chat Section - let receivedContactRequests = self.controller.getContacts().filter(x => x.requestReceived() and not x.isContact() and not x.isBlocked()) + let receivedContactRequests = self.controller.getContacts(ContactsGroup.IncomingPendingContactRequests) let (unviewedCount, mentionsCount) = self.controller.getNumOfNotificaitonsForChat() let notificationsCount = mentionsCount + receivedContactRequests.len let hasNotification = unviewedCount > 0 or notificationsCount > 0 diff --git a/src/app/modules/main/profile_section/contacts/controller.nim b/src/app/modules/main/profile_section/contacts/controller.nim index fcdb264a3c..b4726d54c0 100644 --- a/src/app/modules/main/profile_section/contacts/controller.nim +++ b/src/app/modules/main/profile_section/contacts/controller.nim @@ -37,6 +37,10 @@ proc init*(self: Controller) = var args = ContactArgs(e) self.delegate.contactRemoved(args.contactId) + self.events.on(SIGNAL_CONTACT_REJECTION_REMOVED) do(e: Args): + var args = ContactArgs(e) + self.delegate.contactRequestRejectionRemoved(args.contactId) + self.events.on(SIGNAL_CONTACT_NICKNAME_CHANGED) do(e: Args): var args = ContactArgs(e) self.delegate.contactNicknameChanged(args.contactId) @@ -45,8 +49,8 @@ proc init*(self: Controller) = var args = ContactArgs(e) self.delegate.contactUpdated(args.contactId) -proc getContacts*(self: Controller): seq[ContactsDto] = - return self.contactsService.getContacts() +proc getContacts*(self: Controller, group: ContactsGroup): seq[ContactsDto] = + return self.contactsService.getContactsByGroup(group) proc getContact*(self: Controller, id: string): ContactsDto = return self.contactsService.getContactById(id) @@ -69,3 +73,9 @@ proc removeContact*(self: Controller, publicKey: string) = proc changeContactNickname*(self: Controller, publicKey: string, nickname: string) = self.contactsService.changeContactNickname(publicKey, nickname) + +proc rejectContactRequest*(self: Controller, publicKey: string) = + self.contactsService.rejectContactRequest(publicKey) + +proc removeContactRequestRejection*(self: Controller, publicKey: string) = + self.contactsService.removeContactRequestRejection(publicKey) \ No newline at end of file diff --git a/src/app/modules/main/profile_section/contacts/io_interface.nim b/src/app/modules/main/profile_section/contacts/io_interface.nim index d1bed62e62..5b54d49767 100644 --- a/src/app/modules/main/profile_section/contacts/io_interface.nim +++ b/src/app/modules/main/profile_section/contacts/io_interface.nim @@ -46,6 +46,8 @@ method blockContact*(self: AccessInterface, publicKey: string) {.base.} = method removeContact*(self: AccessInterface, publicKey: string) {.base.} = raise newException(ValueError, "No implementation available") +method removeContactRequestRejection*(self: AccessInterface, publicKey: string) {.base.} = + raise newException(ValueError, "No implementation available") # Controller Delegate Interface @@ -66,3 +68,6 @@ method contactNicknameChanged*(self: AccessInterface, publicKey: string) {.base. method contactUpdated*(self: AccessInterface, publicKey: string) {.base.} = raise newException(ValueError, "No implementation available") + +method contactRequestRejectionRemoved*(self: AccessInterface, publicKey: string) {.base.} = + raise newException(ValueError, "No implementation available") \ No newline at end of file diff --git a/src/app/modules/main/profile_section/contacts/module.nim b/src/app/modules/main/profile_section/contacts/module.nim index 141c0df78e..c9d4fac692 100644 --- a/src/app/modules/main/profile_section/contacts/module.nim +++ b/src/app/modules/main/profile_section/contacts/module.nim @@ -40,22 +40,17 @@ proc createItemFromPublicKey(self: Module, publicKey: string): Item = let contact = self.controller.getContact(publicKey) let (name, image, isIdenticon) = self.controller.getContactNameAndImage(contact.id) - return initItem(contact.id, name, image, isIdenticon, contact.isContact(), contact.isBlocked(), - contact.requestReceived()) + return initItem(contact.id, name, image, isIdenticon, contact.isMutualContact(), contact.isBlocked(), + contact.isContactVerified(), contact.isContactUntrustworthy()) -proc initModels(self: Module) = - var myContacts: seq[Item] - var blockedContacts: seq[Item] - let contacts = self.controller.getContacts() +proc buildModel(self: Module, model: Model, group: ContactsGroup) = + var items: seq[Item] + let contacts = self.controller.getContacts(group) for c in contacts: let item = self.createItemFromPublicKey(c.id) - if(item.isContact() and c.id != singletonInstance.userProfile.getPubKey()): - myContacts.add(item) - if(item.isBlocked()): - blockedContacts.add(item) + items.add(item) - self.view.myContactsModel().addItems(myContacts) - self.view.blockedContactsModel().addItems(blockedContacts) + model.addItems(items) method load*(self: Module) = self.controller.init() @@ -65,7 +60,13 @@ method isLoaded*(self: Module): bool = return self.moduleLoaded method viewDidLoad*(self: Module) = - self.initModels() + self.buildModel(self.view.myMutualContactsModel(), ContactsGroup.MyMutualContacts) + self.buildModel(self.view.blockedContactsModel(), ContactsGroup.BlockedContacts) + self.buildModel(self.view.receivedContactRequestsModel(), ContactsGroup.IncomingPendingContactRequests) + self.buildModel(self.view.sentContactRequestsModel(), ContactsGroup.OutgoingPendingContactRequests) + self.buildModel(self.view.receivedButRejectedContactRequestsModel(), ContactsGroup.IncomingRejectedContactRequests) + self.buildModel(self.view.sentButRejectedContactRequestsModel(), ContactsGroup.IncomingRejectedContactRequests) + self.moduleLoaded = true self.delegate.contactsModuleDidLoad() @@ -75,34 +76,8 @@ method getModuleAsVariant*(self: Module): QVariant = method addContact*(self: Module, publicKey: string) = self.controller.addContact(publicKey) -method contactAdded*(self: Module, publicKey: string) = - let item = self.createItemFromPublicKey(publicKey) - self.view.myContactsModel().addItem(item) - self.view.blockedContactsModel().removeItemWithPubKey(publicKey) - -method contactBlocked*(self: Module, publicKey: string) = - let item = self.createItemFromPublicKey(publicKey) - self.view.myContactsModel().removeItemWithPubKey(publicKey) - self.view.blockedContactsModel().addItem(item) - -method contactUnblocked*(self: Module, publicKey: string) = - let item = self.createItemFromPublicKey(publicKey) - self.view.myContactsModel().addItem(item) - self.view.blockedContactsModel().removeItemWithPubKey(publicKey) - -method contactRemoved*(self: Module, publicKey: string) = - self.view.myContactsModel().removeItemWithPubKey(publicKey) - self.view.blockedContactsModel().removeItemWithPubKey(publicKey) - -method contactNicknameChanged*(self: Module, publicKey: string) = - let (name, _, _) = self.controller.getContactNameAndImage(publicKey) - self.view.myContactsModel().updateName(publicKey, name) - self.view.blockedContactsModel().updateName(publicKey, name) - -method contactUpdated*(self: Module, publicKey: string) = - let item = self.createItemFromPublicKey(publicKey) - self.view.myContactsModel().updateItem(item) - self.view.blockedContactsModel().updateItem(item) +method rejectContactRequest*(self: Module, publicKey: string) = + self.controller.rejectContactRequest(publicKey) method unblockContact*(self: Module, publicKey: string) = self.controller.unblockContact(publicKey) @@ -115,3 +90,62 @@ method removeContact*(self: Module, publicKey: string) = method changeContactNickname*(self: Module, publicKey: string, nickname: string) = self.controller.changeContactNickname(publicKey, nickname) + +method removeContactRequestRejection*(self: Module, publicKey: string) = + self.controller.removeContactRequestRejection(publicKey) + +proc addItemToAppropriateModel(self: Module, item: Item) = + if(item.isBlocked()): + self.view.blockedContactsModel().addItem(item) + elif(item.isMutualContact()): + self.view.myMutualContactsModel().addItem(item) + else: + let contact = self.controller.getContact(item.pubKey()) + if(contact.isContactRequestReceived() and not contact.isContactRequestSent() and not contact.isReceivedContactRequestRejected()): + self.view.receivedContactRequestsModel().addItem(item) + elif(contact.isContactRequestSent() and not contact.isContactRequestReceived() and not contact.isSentContactRequestRejected()): + self.view.sentContactRequestsModel().addItem(item) + elif(contact.isContactRequestReceived() and contact.isReceivedContactRequestRejected()): + self.view.receivedButRejectedContactRequestsModel().addItem(item) + elif(contact.isContactRequestSent() and contact.isSentContactRequestRejected()): + self.view.sentButRejectedContactRequestsModel().addItem(item) + +proc removeItemWithPubKeyFromAllModels(self: Module, publicKey: string) = + self.view.myMutualContactsModel().removeItemWithPubKey(publicKey) + self.view.receivedContactRequestsModel().removeItemWithPubKey(publicKey) + self.view.sentContactRequestsModel().removeItemWithPubKey(publicKey) + self.view.receivedButRejectedContactRequestsModel().removeItemWithPubKey(publicKey) + self.view.sentButRejectedContactRequestsModel().removeItemWithPubKey(publicKey) + self.view.blockedContactsModel().removeItemWithPubKey(publicKey) + +method removeIfExistsAndAddToAppropriateModel*(self: Module, publicKey: string) = + self.removeItemWithPubKeyFromAllModels(publicKey) + let item = self.createItemFromPublicKey(publicKey) + self.addItemToAppropriateModel(item) + +method contactAdded*(self: Module, publicKey: string) = + self.removeIfExistsAndAddToAppropriateModel(publicKey) + +method contactBlocked*(self: Module, publicKey: string) = + self.removeIfExistsAndAddToAppropriateModel(publicKey) + +method contactUnblocked*(self: Module, publicKey: string) = + self.removeIfExistsAndAddToAppropriateModel(publicKey) + +method contactRemoved*(self: Module, publicKey: string) = + self.removeIfExistsAndAddToAppropriateModel(publicKey) + +method contactRequestRejectionRemoved*(self: Module, publicKey: string) = + self.removeIfExistsAndAddToAppropriateModel(publicKey) + +method contactUpdated*(self: Module, publicKey: string) = + self.removeIfExistsAndAddToAppropriateModel(publicKey) + +method contactNicknameChanged*(self: Module, publicKey: string) = + let (name, _, _) = self.controller.getContactNameAndImage(publicKey) + self.view.myMutualContactsModel().updateName(publicKey, name) + self.view.receivedContactRequestsModel().updateName(publicKey, name) + self.view.sentContactRequestsModel().updateName(publicKey, name) + self.view.receivedButRejectedContactRequestsModel().updateName(publicKey, name) + self.view.sentButRejectedContactRequestsModel().updateName(publicKey, name) + self.view.blockedContactsModel().updateName(publicKey, name) \ No newline at end of file diff --git a/src/app/modules/main/profile_section/contacts/view.nim b/src/app/modules/main/profile_section/contacts/view.nim index 80af61d325..5aa7641fc6 100644 --- a/src/app/modules/main/profile_section/contacts/view.nim +++ b/src/app/modules/main/profile_section/contacts/view.nim @@ -7,42 +7,78 @@ QtObject: type View* = ref object of QObject delegate: io_interface.AccessInterface - myContactsModel: Model - myContactsModelVariant: QVariant + myMutualContactsModel: Model + myMutualContactsModelVariant: QVariant blockedContactsModel: Model blockedContactsModelVariant: QVariant + receivedContactRequestsModel: Model + receivedContactRequestsModelVariant: QVariant + sentContactRequestsModel: Model + sentContactRequestsModelVariant: QVariant + receivedButRejectedContactRequestsModel: Model + receivedButRejectedContactRequestsModelVariant: QVariant + sentButRejectedContactRequestsModel: Model + sentButRejectedContactRequestsModelVariant: QVariant proc delete*(self: View) = - self.myContactsModel.delete - self.myContactsModelVariant.delete + self.myMutualContactsModel.delete + self.myMutualContactsModelVariant.delete self.blockedContactsModel.delete self.blockedContactsModelVariant.delete + self.receivedContactRequestsModel.delete + self.receivedContactRequestsModelVariant.delete + self.sentContactRequestsModel.delete + self.sentContactRequestsModelVariant.delete + self.receivedButRejectedContactRequestsModel.delete + self.receivedButRejectedContactRequestsModelVariant.delete + self.sentButRejectedContactRequestsModel.delete + self.sentButRejectedContactRequestsModelVariant.delete self.QObject.delete proc newView*(delegate: io_interface.AccessInterface): View = new(result, delete) result.QObject.setup result.delegate = delegate - result.myContactsModel = newModel() - result.myContactsModelVariant = newQVariant(result.myContactsModel) + result.myMutualContactsModel = newModel() + result.myMutualContactsModelVariant = newQVariant(result.myMutualContactsModel) result.blockedContactsModel = newModel() result.blockedContactsModelVariant = newQVariant(result.blockedContactsModel) + result.receivedContactRequestsModel = newModel() + result.receivedContactRequestsModelVariant = newQVariant(result.receivedContactRequestsModel) + result.sentContactRequestsModel = newModel() + result.sentContactRequestsModelVariant = newQVariant(result.sentContactRequestsModel) + result.receivedButRejectedContactRequestsModel = newModel() + result.receivedButRejectedContactRequestsModelVariant = newQVariant(result.receivedButRejectedContactRequestsModel) + result.sentButRejectedContactRequestsModel = newModel() + result.sentButRejectedContactRequestsModelVariant = newQVariant(result.sentButRejectedContactRequestsModel) proc load*(self: View) = self.delegate.viewDidLoad() - proc myContactsModel*(self: View): Model = - return self.myContactsModel + proc myMutualContactsModel*(self: View): Model = + return self.myMutualContactsModel proc blockedContactsModel*(self: View): Model = return self.blockedContactsModel - proc myContactsModelChanged(self: View) {.signal.} - proc getMyContactsModel(self: View): QVariant {.slot.} = - return self.myContactsModelVariant - QtProperty[QVariant] myContactsModel: - read = getMyContactsModel - notify = myContactsModelChanged + proc receivedContactRequestsModel*(self: View): Model = + return self.receivedContactRequestsModel + + proc sentContactRequestsModel*(self: View): Model = + return self.sentContactRequestsModel + + proc receivedButRejectedContactRequestsModel*(self: View): Model = + return self.receivedButRejectedContactRequestsModel + + proc sentButRejectedContactRequestsModel*(self: View): Model = + return self.sentButRejectedContactRequestsModel + + proc myMutualContactsModelChanged(self: View) {.signal.} + proc getMyMutualContactsModel(self: View): QVariant {.slot.} = + return self.myMutualContactsModelVariant + QtProperty[QVariant] myMutualContactsModel: + read = getMyMutualContactsModel + notify = myMutualContactsModelChanged proc blockedContactsModelChanged(self: View) {.signal.} proc getBlockedContactsModel(self: View): QVariant {.slot.} = @@ -51,9 +87,40 @@ QtObject: read = getBlockedContactsModel notify = blockedContactsModelChanged + proc receivedContactRequestsModelChanged(self: View) {.signal.} + proc getReceivedContactRequestsModel(self: View): QVariant {.slot.} = + return self.receivedContactRequestsModelVariant + QtProperty[QVariant] receivedContactRequestsModel: + read = getReceivedContactRequestsModel + notify = receivedContactRequestsModelChanged + + proc sentContactRequestsModelChanged(self: View) {.signal.} + proc getSentContactRequestsModel(self: View): QVariant {.slot.} = + return self.sentContactRequestsModelVariant + QtProperty[QVariant] sentContactRequestsModel: + read = getSentContactRequestsModel + notify = sentContactRequestsModelChanged + + proc receivedButRejectedContactRequestsModelChanged(self: View) {.signal.} + proc getReceivedButRejectedContactRequestsModel(self: View): QVariant {.slot.} = + return self.receivedButRejectedContactRequestsModelVariant + QtProperty[QVariant] receivedButRejectedContactRequestsModel: + read = getReceivedButRejectedContactRequestsModel + notify = receivedButRejectedContactRequestsModelChanged + + proc sentButRejectedContactRequestsModelChanged(self: View) {.signal.} + proc getSentButRejectedContactRequestsModel(self: View): QVariant {.slot.} = + return self.sentButRejectedContactRequestsModelVariant + QtProperty[QVariant] sentButRejectedContactRequestsModel: + read = getSentButRejectedContactRequestsModel + notify = sentButRejectedContactRequestsModelChanged + proc addContact*(self: View, publicKey: string) {.slot.} = self.delegate.addContact(publicKey) + proc rejectContactRequest*(self: View, publicKey: string) {.slot.} = + self.delegate.rejectContactRequest(publicKey) + proc changeContactNickname*(self: View, publicKey: string, nickname: string) {.slot.} = self.delegate.changeContactNickname(publicKey, nickname) @@ -65,3 +132,6 @@ QtObject: proc removeContact*(self: View, publicKey: string) {.slot.} = self.delegate.removeContact(publicKey) + + proc removeContactRequestRejection*(self: View, publicKey: string) {.slot.} = + self.delegate.removeContactRequestRejection(publicKey) \ No newline at end of file diff --git a/src/app/modules/shared_models/contacts_item.nim b/src/app/modules/shared_models/contacts_item.nim index 44322129e8..20643ce0e3 100644 --- a/src/app/modules/shared_models/contacts_item.nim +++ b/src/app/modules/shared_models/contacts_item.nim @@ -1,22 +1,34 @@ +type + ContactVerificationState* {.pure.} = enum + NotMarked = 0 + Verified + Untrustworthy + type Item* = ref object pubKey: string name: string icon: string isIdenticon: bool - isContact: bool isBlocked: bool - requestReceived: bool + isMutualContact: bool + verificationState: ContactVerificationState -proc initItem*(pubKey, name, icon: string, isIdenticon, isContact, isBlocked, requestReceived: bool): Item = +proc initItem*(pubKey, name, icon: string, isIdenticon, isMutualContact, isBlocked: bool, + isContactVerified, isContactUntrustworthy: bool): Item = result = Item() result.pubKey = pubKey result.name = name result.icon = icon result.isIdenticon = isIdenticon - result.isContact = isContact + result.isMutualContact = isMutualContact result.isBlocked = isBlocked - result.requestReceived = requestReceived + if(isContactVerified): + result.verificationState = ContactVerificationState.Verified + elif(isContactUntrustworthy): + result.verificationState = ContactVerificationState.Untrustworthy + else: + result.verificationState = ContactVerificationState.NotMarked proc pubKey*(self: Item): string = self.pubKey @@ -33,11 +45,11 @@ proc icon*(self: Item): string = proc isIdenticon*(self: Item): bool = self.isIdenticon -proc isContact*(self: Item): bool = - self.isContact +proc isMutualContact*(self: Item): bool = + self.isMutualContact proc isBlocked*(self: Item): bool = self.isBlocked -proc requestReceived*(self: Item): bool = - self.requestReceived +proc verificationState*(self: Item): ContactVerificationState = + self.verificationState \ No newline at end of file diff --git a/src/app/modules/shared_models/contacts_model.nim b/src/app/modules/shared_models/contacts_model.nim index 5191c20bae..899d32c516 100644 --- a/src/app/modules/shared_models/contacts_model.nim +++ b/src/app/modules/shared_models/contacts_model.nim @@ -7,9 +7,9 @@ type Name Icon IsIdenticon - IsContact + IsMutualContact IsBlocked - RequestReceived + VerificationState QtObject: type Model* = ref object of QAbstractListModel @@ -42,9 +42,9 @@ QtObject: ModelRole.Name.int:"name", ModelRole.Icon.int:"icon", ModelRole.IsIdenticon.int:"isIdenticon", - ModelRole.IsContact.int:"isContact", + ModelRole.IsMutualContact.int:"isMutualContact", ModelRole.IsBlocked.int:"isBlocked", - ModelRole.RequestReceived.int:"requestReceived" + ModelRole.VerificationState.int:"verificationState" }.toTable method data(self: Model, index: QModelIndex, role: int): QVariant = @@ -64,12 +64,12 @@ QtObject: result = newQVariant(item.icon) of ModelRole.IsIdenticon: result = newQVariant(item.isIdenticon) - of ModelRole.IsContact: - result = newQVariant(item.isContact) + of ModelRole.IsMutualContact: + result = newQVariant(item.isMutualContact) of ModelRole.IsBlocked: result = newQVariant(item.isBlocked) - of ModelRole.RequestReceived: - result = newQVariant(item.requestReceived) + of ModelRole.VerificationState: + result = newQVariant(item.verificationState.int) proc findIndexByPubKey(self: Model, pubKey: string): int = for i in 0 ..< self.items.len: @@ -130,9 +130,9 @@ QtObject: ModelRole.Name.int, ModelRole.Icon.int, ModelRole.IsIdenticon.int, - ModelRole.IsContact.int, + ModelRole.IsMutualContact.int, ModelRole.IsBlocked.int, - ModelRole.RequestReceived.int + ModelRole.VerificationState.int ] ) diff --git a/src/app_service/service/chat/service.nim b/src/app_service/service/chat/service.nim index 56b8901216..66e3a74289 100644 --- a/src/app_service/service/chat/service.nim +++ b/src/app_service/service/chat/service.nim @@ -294,7 +294,7 @@ QtObject: preferredUsername: string = "", communityId: string = "") = try: - let allKnownContacts = self.contactService.getContacts() + let allKnownContacts = self.contactService.getContactsByGroup(ContactsGroup.AllKnownContacts) let processedMsg = message_common.replaceMentionsWithPubKeys(allKnownContacts, msg) let response = status_chat.sendChatMessage( diff --git a/src/app_service/service/contacts/dto/contacts.nim b/src/app_service/service/contacts/dto/contacts.nim index 77e156fa69..7f28cd46ea 100644 --- a/src/app_service/service/contacts/dto/contacts.nim +++ b/src/app_service/service/contacts/dto/contacts.nim @@ -97,11 +97,36 @@ proc userNameOrAlias*(contact: ContactsDto): string = else: result = contact.alias -proc isContact*(self: ContactsDto): bool = - result = self.added +proc isContactRequestReceived*(self: ContactsDto): bool = + return self.hasAddedUs + +proc isContactRequestSent*(self: ContactsDto): bool = + return self.added + +proc isSentContactRequestRejected*(self: ContactsDto): bool = + # TODO not implemented in `status-go` yet + # We don't have this prop for now. + return false + +proc isReceivedContactRequestRejected*(self: ContactsDto): bool = + # We need to check this. + return self.removed proc isBlocked*(self: ContactsDto): bool = - result = self.blocked + return self.blocked -proc requestReceived*(self: ContactsDto): bool = - result = self.hasAddedUs +proc isMutualContact*(self: ContactsDto): bool = + # TODO not implemented in `status-go` yet + # But for now we consider that contact is mutual contact if I added him and he added me. + return self.hasAddedUs and self.added + +proc isContactVerified*(self: ContactsDto): bool = + # TODO not implemented in `status-go` yet + return false + +proc isContactUntrustworthy*(self: ContactsDto): bool = + # TODO not implemented in `status-go` yet + return false + +proc isContactMarked*(self: ContactsDto): bool = + return self.isContactVerified() or self.isContactUntrustworthy() \ No newline at end of file diff --git a/src/app_service/service/contacts/service.nim b/src/app_service/service/contacts/service.nim index 3a59c109b7..2071695fbf 100644 --- a/src/app_service/service/contacts/service.nim +++ b/src/app_service/service/contacts/service.nim @@ -48,11 +48,21 @@ const SIGNAL_CONTACT_ADDED* = "contactAdded" const SIGNAL_CONTACT_BLOCKED* = "contactBlocked" const SIGNAL_CONTACT_UNBLOCKED* = "contactUnblocked" const SIGNAL_CONTACT_REMOVED* = "contactRemoved" +const SIGNAL_CONTACT_REJECTION_REMOVED* = "contactRejectionRemoved" const SIGNAL_CONTACT_NICKNAME_CHANGED* = "contactNicknameChanged" const SIGNAL_CONTACTS_STATUS_UPDATED* = "contactsStatusUpdated" const SIGNAL_CONTACT_UPDATED* = "contactUpdated" const SIGNAL_LOGGEDIN_USER_IMAGE_CHANGED* = "loggedInUserImageChanged" +type + ContactsGroup* {.pure.} = enum + AllKnownContacts + MyMutualContacts + IncomingPendingContactRequests + OutgoingPendingContactRequests + IncomingRejectedContactRequests + OutgoingRejectedContactRequests + BlockedContacts QtObject: type Service* = ref object of QObject @@ -152,18 +162,44 @@ QtObject: let data = Args() self.events.emit(SIGNAL_LOGGEDIN_USER_IMAGE_CHANGED, data) - proc getContacts*(self: Service): seq[ContactsDto] = - return toSeq(self.contacts.values) - - proc getAddedContacts*(self: Service): seq[ContactsDto] = - return self.getContacts().filter(x => x.isContact()) - - proc getBlockedContacts*(self: Service): seq[ContactsDto] = - return self.getContacts().filter(x => x.isBlocked()) - - proc getContactsWhoAddedMe*(self: Service): seq[ContactsDto] = - return self.getContacts().filter(x => x.requestReceived()) - + proc getContactsByGroup*(self: Service, group: ContactsGroup): seq[ContactsDto] = + # Having this logic here we ensure that the same contact group in each part of the app will have the same list + # of contacts. Be sure when you change any condition here. + let myPubKey = singletonInstance.userProfile.getPubKey() + let contacts = toSeq(self.contacts.values) + if (group == ContactsGroup.IncomingPendingContactRequests): + return contacts.filter(x => x.id != myPubKey and + x.isContactRequestReceived() and + not x.isContactRequestSent() and + not x.isReceivedContactRequestRejected() and + not x.isBlocked()) + elif (group == ContactsGroup.OutgoingPendingContactRequests): + return contacts.filter(x => x.id != myPubKey and + x.isContactRequestSent() and + not x.isContactRequestReceived() and + not x.isSentContactRequestRejected() and + not x.isBlocked()) + elif (group == ContactsGroup.IncomingRejectedContactRequests): + return contacts.filter(x => x.id != myPubKey and + x.isContactRequestReceived() and + x.isReceivedContactRequestRejected() and + not x.isBlocked()) + elif (group == ContactsGroup.OutgoingRejectedContactRequests): + return contacts.filter(x => x.id != myPubKey and + x.isContactRequestSent() and + x.isSentContactRequestRejected() and + not x.isBlocked()) + elif (group == ContactsGroup.BlockedContacts): + return contacts.filter(x => x.id != myPubKey and + x.isBlocked()) + elif (group == ContactsGroup.MyMutualContacts): + # we need to revise this when we introduce "identity verification" feature + return contacts.filter(x => x.id != myPubKey and + x.isMutualContact() and + not x.isBlocked()) + elif (group == ContactsGroup.AllKnownContacts): + return contacts + proc fetchContact(self: Service, id: string): ContactsDto = try: let response = status_contacts.getContactByID(id) @@ -280,7 +316,7 @@ QtObject: proc rejectContactRequest*(self: Service, publicKey: string) = var contact = self.getContactById(publicKey) - contact.hasAddedUs = false + contact.removed = true let response = status_contacts.rejectContactRequest(contact.id) if(not response.error.isNil): @@ -290,6 +326,16 @@ QtObject: self.saveContact(contact) self.events.emit(SIGNAL_CONTACT_REMOVED, ContactArgs(contactId: contact.id)) + proc removeContactRequestRejection*(self: Service, publicKey: string) = + var contact = self.getContactById(publicKey) + contact.removed = false + + # When we know what flags or what `status-go` end point we need to call, we should add + # that call here. + + self.saveContact(contact) + self.events.emit(SIGNAL_CONTACT_REJECTION_REMOVED, ContactArgs(contactId: contact.id)) + proc changeContactNickname*(self: Service, publicKey: string, nickname: string) = var contact = self.getContactById(publicKey) contact.localNickname = nickname @@ -329,8 +375,7 @@ QtObject: proc removeContact*(self: Service, publicKey: string) = var contact = self.getContactById(publicKey) - contact.added = false - contact.hasAddedUs = false + contact.removed = true let response = status_contacts.removeContact(contact.id) if(not response.error.isNil): diff --git a/src/app_service/service/message/service.nim b/src/app_service/service/message/service.nim index 555498ab14..7807bf4b5e 100644 --- a/src/app_service/service/message/service.nim +++ b/src/app_service/service/message/service.nim @@ -683,7 +683,7 @@ proc deleteMessage*(self: Service, messageId: string) = proc editMessage*(self: Service, messageId: string, msg: string) = try: - let allKnownContacts = self.contactService.getContacts() + let allKnownContacts = self.contactService.getContactsByGroup(ContactsGroup.AllKnownContacts) let processedMsg = message_common.replaceMentionsWithPubKeys(allKnownContacts, msg) let response = status_go.editMessage(messageId, processedMsg) diff --git a/ui/app/AppLayouts/Profile/panels/ContactPanel.qml b/ui/app/AppLayouts/Profile/panels/ContactPanel.qml index 462a5dc1ed..91653af262 100644 --- a/ui/app/AppLayouts/Profile/panels/ContactPanel.qml +++ b/ui/app/AppLayouts/Profile/panels/ContactPanel.qml @@ -18,7 +18,7 @@ import shared.controls.chat 1.0 StatusListItem { id: container width: parent.width - visible: container.isContact && (searchStr == "" || container.name.includes(searchStr)) + visible: container.isMutualContact && (container.searchStr == "" || container.name.includes(container.searchStr)) height: visible ? implicitHeight : 0 title: container.name image.source: container.icon @@ -27,27 +27,75 @@ StatusListItem { property string publicKey: "0x04d8c07dd137bd1b73a6f51df148b4f77ddaa11209d36e43d8344c0a7d6db1cad6085f27cfb75dd3ae21d86ceffebe4cf8a35b9ce8d26baa19dc264efe6d8f221b" property string icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=" property bool isIdenticon - - property bool isContact: true + property bool isMutualContact: false property bool isBlocked: false + property int verificationState: Constants.contactVerificationState.notMarked + property string searchStr: "" property bool showSendMessageButton: false + property bool showRejectContactRequestButton: false + property bool showAcceptContactRequestButton: false + property bool showRemoveRejectionButton: false + property string contactText: "" + property bool contactTextClickable: false signal openProfilePopup(string publicKey) signal openChangeNicknamePopup(string publicKey) signal sendMessageActionTriggered(string publicKey) + signal acceptContactRequest(string publicKey) + signal rejectContactRequest(string publicKey) + signal removeRejection(string publicKey) + signal textClicked(string publicKey) components: [ StatusFlatRoundButton { visible: showSendMessageButton - id: sendMessageBtn width: visible ? 32 : 0 height: visible ? 32 : 0 icon.name: "chat" type: StatusFlatRoundButton.Type.Secondary onClicked: container.sendMessageActionTriggered(container.publicKey) }, + StatusFlatRoundButton { + visible: showRejectContactRequestButton + width: visible ? 32 : 0 + height: visible ? 32 : 0 + icon.name: "close-circle" + icon.color: Style.current.danger + onClicked: container.rejectContactRequest(container.publicKey) + }, + StatusFlatRoundButton { + visible: showAcceptContactRequestButton + width: visible ? 32 : 0 + height: visible ? 32 : 0 + icon.name: "checkmark-circle" + icon.color: Style.current.success + onClicked: container.acceptContactRequest(container.publicKey) + }, + StatusFlatRoundButton { + visible: showRemoveRejectionButton + width: visible ? 32 : 0 + height: visible ? 32 : 0 + icon.name: "cancel" + icon.color: Style.current.danger + onClicked: container.removeRejection(container.publicKey) + }, + StatusBaseText { + text: container.contactText + anchors.verticalCenter: parent.verticalCenter + color: container.contactTextClickable? Theme.palette.directColor1 : Theme.palette.baseColor1 + + MouseArea { + anchors.fill: parent + enabled: container.contactTextClickable + cursorShape: sensor.enabled && containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor + hoverEnabled: true + onClicked: { + container.textClicked(container.publicKey) + } + } + }, StatusFlatRoundButton { id: menuButton width: 32 @@ -101,7 +149,7 @@ StatusListItem { container.sendMessageActionTriggered(container.publicKey) menuButton.highlighted = false } - enabled: !container.isBlocked + enabled: container.isMutualContact } StatusMenuItem { diff --git a/ui/app/AppLayouts/Profile/panels/ContactsListPanel.qml b/ui/app/AppLayouts/Profile/panels/ContactsListPanel.qml index cdb708882b..1262ecc891 100644 --- a/ui/app/AppLayouts/Profile/panels/ContactsListPanel.qml +++ b/ui/app/AppLayouts/Profile/panels/ContactsListPanel.qml @@ -1,57 +1,163 @@ import QtQuick 2.13 import QtQuick.Controls 2.13 import QtQuick.Layouts 1.13 +import QtQml.Models 2.13 import utils 1.0 import shared 1.0 import shared.popups 1.0 +import shared.panels 1.0 import "../../Chat/popups" import "." -ListView { - id: contactList - property var contactsModel +Item { + id: contactListRoot - property string searchStr: "" + property var contactsModel + property int panelUsage: Constants.contactsPanelUsage.unknownPosition + + property string title: "" property string searchString: "" property string lowerCaseSearchString: searchString.toLowerCase() - property string contactToRemove: "" - property bool hideBlocked: false - property bool showSendMessageButton - signal contactClicked(var contact) - signal openProfilePopup(var contact) - signal sendMessageActionTriggered(var contact) - signal openChangeNicknamePopup(var contact) + signal contactClicked(string publicKey) + signal openProfilePopup(string publicKey) + signal sendMessageActionTriggered(string publicKey) + signal openChangeNicknamePopup(string publicKey) + signal acceptContactRequest(string publicKey) + signal rejectContactRequest(string publicKey) + signal removeRejection(string publicKey) + signal textClicked(string publicKey) - width: parent.width + visible: contactsList.count > 0 - model: contactList.contactsModel + StyledText { + id: title + anchors.top: parent.top + anchors.left: parent.left + visible: contactListRoot.title !== "" + text: contactListRoot.title + font.weight: Font.Medium + font.pixelSize: 15 + color: Style.current.secondaryText + } - delegate: ContactPanel { - id: panelDelegate - name: model.name - publicKey: model.pubKey - icon: model.icon - isIdenticon: model.isIdenticon - isContact: model.isContact - isBlocked: model.isBlocked - showSendMessageButton: contactList.showSendMessageButton + DelegateModel { + id: delegateModel - onClicked: contactList.contactClicked(model) - onOpenProfilePopup: contactList.openProfilePopup(model) - onSendMessageActionTriggered: contactList.sendMessageActionTriggered(model) - onOpenChangeNicknamePopup: contactList.openChangeNicknamePopup(model) + function update() { + var visible = []; + for (var i = 0; i < items.count; ++i) { + var item = items.get(i); + if (panelUsage === Constants.contactsPanelUsage.verifiedMutualContacts) { + if(item.model.verificationState === Constants.contactVerificationState.verified) + visible.push(item); + } + else if (panelUsage === Constants.contactsPanelUsage.mutualContacts) { + if(item.model.verificationState !== Constants.contactVerificationState.verified) + visible.push(item); + } + else { + visible.push(item); + } + } - visible: { - if (hideBlocked && model.isBlocked) { - return false - } + for (i = 0; i < visible.length; ++i) { + item = visible[i]; + item.inVisible = true; + if (item.visibleIndex !== i) { + visibleItems.move(item.visibleIndex, i, 1); + } + } + } - return searchString === "" || - panelDelegate.name.toLowerCase().includes(lowerCaseSearchString) || - panelDelegate.publicKey.toLowerCase().includes(lowerCaseSearchString) + model: contactListRoot.contactsModel + + groups: [DelegateModelGroup { + id: visibleItems + name: "visible" + includeByDefault: false + }] + + filterOnGroup: "visible" + items.onChanged: update() + delegate: contactPanelComponent + } + + ListView { + id: contactsList + anchors.top: title.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + model: delegateModel + } + + Component { + id: contactPanelComponent + + ContactPanel { + id: panelDelegate + name: model.name + publicKey: model.pubKey + icon: model.icon + isIdenticon: model.isIdenticon + isMutualContact: model.isMutualContact + isBlocked: model.isBlocked + verificationState: model.verificationState + + searchStr: contactListRoot.searchString + + showSendMessageButton: model.isMutualContact + showRejectContactRequestButton: { + if (contactListRoot.panelUsage === Constants.contactsPanelUsage.receivedContactRequest) { + return true + } + + return false + } + showAcceptContactRequestButton: { + if (contactListRoot.panelUsage === Constants.contactsPanelUsage.receivedContactRequest) { + return true + } + + return false + } + showRemoveRejectionButton: { + if (contactListRoot.panelUsage === Constants.contactsPanelUsage.rejectedReceivedContactRequest) { + return true + } + + return false + } + contactText: { + if (contactListRoot.panelUsage === Constants.contactsPanelUsage.sentContactRequest) { + return qsTr("Contact Request Sent") + } + else if (contactListRoot.panelUsage === Constants.contactsPanelUsage.rejectedSentContactRequest) { + return qsTr("Contact Request Rejected") + } + + return "" + } + contactTextClickable: { + return false + } + + onClicked: contactListRoot.contactClicked(model.pubKey) + onOpenProfilePopup: contactListRoot.openProfilePopup(publicKey) + onSendMessageActionTriggered: contactListRoot.sendMessageActionTriggered(publicKey) + onOpenChangeNicknamePopup: contactListRoot.openChangeNicknamePopup(publicKey) + onAcceptContactRequest: contactListRoot.acceptContactRequest(publicKey) + onRejectContactRequest: contactListRoot.rejectContactRequest(publicKey) + onRemoveRejection: contactListRoot.removeRejection(publicKey) + onTextClicked: contactListRoot.textClicked(publicKey) + + visible: searchString === "" || + panelDelegate.name.toLowerCase().includes(lowerCaseSearchString) || + panelDelegate.publicKey.toLowerCase().includes(lowerCaseSearchString) } } } diff --git a/ui/app/AppLayouts/Profile/stores/ContactsStore.qml b/ui/app/AppLayouts/Profile/stores/ContactsStore.qml index 306a536d19..308a833189 100644 --- a/ui/app/AppLayouts/Profile/stores/ContactsStore.qml +++ b/ui/app/AppLayouts/Profile/stores/ContactsStore.qml @@ -10,13 +10,13 @@ QtObject { property var mainModuleInst: mainModule property string myPublicKey: userProfile.pubKey - property var myContactsModel: contactsModule.myContactsModel - property var blockedContactsModel: contactsModule.blockedContactsModel - // TODO move contact requests back to the contacts module since we need them in the Profile - // also, having them in the chat section creates some waste, since no community has it - property var chatSectionModule: mainModule.getChatSectionModule() - property var contactRequestsModel: chatSectionModule.contactRequestsModel + property var myContactsModel: contactsModule.myMutualContactsModel + property var blockedContactsModel: contactsModule.blockedContactsModel + property var receivedContactRequestsModel: contactsModule.receivedContactRequestsModel + property var sentContactRequestsModel: contactsModule.sentContactRequestsModel + property var receivedButRejectedContactRequestsModel: contactsModule.receivedButRejectedContactRequestsModel + property var sentButRejectedContactRequestsModel: contactsModule.sentButRejectedContactRequestsModel function resolveENS(value) { root.mainModuleInst.resolveENS(value, "") @@ -32,8 +32,7 @@ QtObject { function joinPrivateChat(pubKey) { Global.changeAppSectionBySectionType(Constants.appSection.chat) - let chatCommunitySectionModule = root.mainModuleInst.getChatSectionModule() - chatCommunitySectionModule.createOneToOneChat("", pubKey, "") + root.contactsModule.addContact(pubKey) } function addContact(pubKey) { @@ -57,18 +56,14 @@ QtObject { } function acceptContactRequest(pubKey) { - chatSectionModule.acceptContactRequest(pubKey) - } - - function acceptAllContactRequests() { - chatSectionModule.acceptAllContactRequests() + root.contactsModule.addContact(pubKey) } function rejectContactRequest(pubKey) { - chatSectionModule.rejectContactRequest(pubKey) + root.contactsModule.rejectContactRequest(pubKey) } - function rejectAllContactRequests() { - chatSectionModule.rejectAllContactRequests() + function removeContactRequestRejection(pubKey) { + root.contactsModule.removeContactRequestRejection(pubKey) } } diff --git a/ui/app/AppLayouts/Profile/views/ContactsView.qml b/ui/app/AppLayouts/Profile/views/ContactsView.qml index 61cb3fbf10..f201f9373b 100644 --- a/ui/app/AppLayouts/Profile/views/ContactsView.qml +++ b/ui/app/AppLayouts/Profile/views/ContactsView.qml @@ -49,7 +49,7 @@ Item { anchors.left: parent.left anchors.leftMargin: -40 onClicked: Global.changeAppSectionBySectionType(Constants.appSection.profile, - Constants.settingsSubsection.messaging) + Constants.settingsSubsection.messaging) } RowLayout { @@ -95,24 +95,28 @@ Item { } StatusTabButton { id: contactsBtn - anchors.top: parent.top + addToWidth: Style.current.bigPadding btnText: qsTr("Contacts") } StatusTabButton { id: pendingRequestsBtn - enabled: root.contactsStore.contactRequestsModel.count > 0 - anchors.left: contactsBtn.right - anchors.top: parent.top - anchors.leftMargin: Style.current.bigPadding + addToWidth: Style.current.bigPadding + enabled: root.contactsStore.receivedContactRequestsModel.count > 0 || + root.contactsStore.sentContactRequestsModel.count > 0 btnText: qsTr("Pending Requests") badge.value: contactList.count } + StatusTabButton { + id: rejectedRequestsBtn + addToWidth: Style.current.bigPadding + enabled: root.contactsStore.receivedButRejectedContactRequestsModel.count > 0 || + root.contactsStore.sentButRejectedContactRequestsModel.count > 0 + btnText: qsTr("Rejected Requests") + } StatusTabButton { id: blockedBtn + addToWidth: Style.current.bigPadding enabled: root.contactsStore.blockedContactsModel.count > 0 - anchors.left: pendingRequestsBtn.right - anchors.leftMargin: Style.current.bigPadding - anchors.top: parent.top btnText: qsTr("Blocked") } } @@ -127,34 +131,59 @@ Item { // CONTACTS Item { - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding - height: parent.height + Layout.fillWidth: true + Layout.fillHeight: true - ContactsListPanel { - id: contactListView + ColumnLayout { anchors.fill: parent - contactsModel: root.contactsStore.myContactsModel - clip: true - hideBlocked: true - searchString: searchBox.text - showSendMessageButton: true - onContactClicked: { - root.contactsStore.joinPrivateChat(contact.pubKey) + ContactsListPanel { + Layout.fillWidth: true + Layout.preferredHeight: parent.height * 0.5 + contactsModel: root.contactsStore.myContactsModel + clip: true + title: qsTr("Identity Verified Contacts") + searchString: searchBox.text + panelUsage: Constants.contactsPanelUsage.verifiedMutualContacts + + onOpenProfilePopup: { + Global.openProfilePopup(publicKey) + } + + onSendMessageActionTriggered: { + root.contactsStore.joinPrivateChat(publicKey) + } + + onOpenChangeNicknamePopup: { + Global.openProfilePopup(publicKey, null, true) + } } - onOpenProfilePopup: { - Global.openProfilePopup(contact.pubKey) + ContactsListPanel { + Layout.fillWidth: true + Layout.preferredHeight: parent.height * 0.5 + contactsModel: root.contactsStore.myContactsModel + clip: true + title: qsTr("Contacts") + searchString: searchBox.text + panelUsage: Constants.contactsPanelUsage.mutualContacts + + onOpenProfilePopup: { + Global.openProfilePopup(publicKey) + } + + onSendMessageActionTriggered: { + root.contactsStore.joinPrivateChat(publicKey) + } + + onOpenChangeNicknamePopup: { + Global.openProfilePopup(publicKey, null, true) + } } - onSendMessageActionTriggered: { - root.contactsStore.joinPrivateChat(contact.pubKey) - } - onOpenChangeNicknamePopup: { - Global.openProfilePopup(contact.pubKey, null, true) + Item { + Layout.fillWidth: true + Layout.fillHeight: true } } @@ -163,56 +192,136 @@ Item { //% "You don’t have any contacts yet" text: qsTrId("you-don-t-have-any-contacts-yet") width: parent.width - anchors.verticalCenter: parent.verticalCenter + anchors.centerIn: parent } } // PENDING REQUESTS Item { - ListView { - id: contactList + Layout.fillWidth: true + Layout.fillHeight: true + ColumnLayout { anchors.fill: parent - anchors.leftMargin: -Style.current.halfPadding - anchors.rightMargin: -Style.current.halfPadding - model: root.contactsStore.contactRequestsModel - clip: true + ContactsListPanel { + Layout.fillWidth: true + Layout.preferredHeight: parent.height * 0.5 + clip: true + title: qsTr("Received") + searchString: searchBox.text + contactsModel: root.contactsStore.receivedContactRequestsModel + panelUsage: Constants.contactsPanelUsage.receivedContactRequest - delegate: ContactRequestPanel { - contactName: model.name - contactIcon: model.icon - contactIconIsIdenticon: model.isIdenticon onOpenProfilePopup: { - Global.openProfilePopup(model.pubKey) + Global.openProfilePopup(publicKey) } - onBlockContactActionTriggered: { - blockContactConfirmationDialog.contactName = model.name - blockContactConfirmationDialog.contactAddress = model.pubKey - blockContactConfirmationDialog.open() + + onOpenChangeNicknamePopup: { + Global.openProfilePopup(publicKey, null, true) } - onAcceptClicked: { - root.contactsStore.acceptContactRequest(model.pubKey) + + onAcceptContactRequest: { + root.contactsStore.acceptContactRequest(publicKey) } - onDeclineClicked: { - root.contactsStore.rejectContactRequest(model.pubKey) + + onRejectContactRequest: { + root.contactsStore.rejectContactRequest(publicKey) } } + + ContactsListPanel { + Layout.fillWidth: true + Layout.preferredHeight: parent.height * 0.5 + clip: true + title: qsTr("Sent") + searchString: searchBox.text + contactsModel: root.contactsStore.sentContactRequestsModel + panelUsage: Constants.contactsPanelUsage.sentContactRequest + + onOpenProfilePopup: { + Global.openProfilePopup(publicKey) + } + + onOpenChangeNicknamePopup: { + Global.openProfilePopup(publicKey, null, true) + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + } + } + + // REJECTED REQUESTS + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + ColumnLayout { + anchors.fill: parent + + ContactsListPanel { + Layout.fillWidth: true + Layout.preferredHeight: parent.height * 0.5 + clip: true + title: qsTr("Received") + searchString: searchBox.text + contactsModel: root.contactsStore.receivedButRejectedContactRequestsModel + panelUsage: Constants.contactsPanelUsage.rejectedReceivedContactRequest + + onOpenProfilePopup: { + Global.openProfilePopup(publicKey) + } + + onOpenChangeNicknamePopup: { + Global.openProfilePopup(publicKey, null, true) + } + + onRemoveRejection: { + root.contactsStore.removeContactRequestRejection(publicKey) + } + } + + ContactsListPanel { + Layout.fillWidth: true + Layout.preferredHeight: parent.height * 0.5 + clip: true + title: qsTr("Sent") + searchString: searchBox.text + contactsModel: root.contactsStore.sentButRejectedContactRequestsModel + panelUsage: Constants.contactsPanelUsage.rejectedSentContactRequest + + onOpenProfilePopup: { + Global.openProfilePopup(publicKey) + } + + onOpenChangeNicknamePopup: { + Global.openProfilePopup(publicKey, null, true) + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } } } // BLOCKED ContactsListPanel { - anchors.left: parent.left - anchors.leftMargin: -Style.current.padding - anchors.right: parent.right - anchors.rightMargin: -Style.current.padding - height: parent.height + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + searchString: searchBox.text contactsModel: root.contactsStore.blockedContactsModel + panelUsage: Constants.contactsPanelUsage.blockedContacts onOpenProfilePopup: { - Global.openProfilePopup(contact.pubKey) + Global.openProfilePopup(publicKey) } } } @@ -245,7 +354,7 @@ Item { confirmationText: qsTrId("are-you-sure-you-want-to-remove-this-contact-") onConfirmButtonClicked: { if (Utils.getContactDetailsAsJson(removeContactConfirmationDialog.value).isContact) { - root.contactsStore.removeContact(removeContactConfirmationDialog.value); + root.contactsStore.removeContact(removeContactConfirmationDialog.value); } removeContactConfirmationDialog.close() } diff --git a/ui/imports/shared/controls/StatusTabButton.qml b/ui/imports/shared/controls/StatusTabButton.qml index 35d31cb631..174e652fc4 100644 --- a/ui/imports/shared/controls/StatusTabButton.qml +++ b/ui/imports/shared/controls/StatusTabButton.qml @@ -10,11 +10,15 @@ import StatusQ.Components 0.1 TabButton { property string btnText: "Default Button" + property int addToWidth: 0 + property alias badge: statusBadge id: tabButton width: tabBtnText.width + - (statusBadge.visible ? statusBadge.width + statusBadge.anchors.leftMargin : 0) + (statusBadge.visible ? statusBadge.width + statusBadge.anchors.leftMargin : 0) + + addToWidth + height: tabBtnText.height + 11 text: "" padding: 0 @@ -25,6 +29,7 @@ TabButton { StyledText { id: tabBtnText + anchors.horizontalCenter: parent.horizontalCenter text: btnText font.weight: Font.Medium font.pixelSize: 15 diff --git a/ui/imports/utils/Constants.qml b/ui/imports/utils/Constants.qml index 40ec6c4731..0387c41df7 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -91,6 +91,23 @@ QtObject { readonly property int noOne: 3 } + readonly property QtObject contactVerificationState: QtObject { + readonly property int notMarked: 0 + readonly property int verified: 1 + readonly property int untrustworthy: 2 + } + + readonly property QtObject contactsPanelUsage: QtObject { + readonly property int unknownPosition: -1 + readonly property int mutualContacts: 0 + readonly property int verifiedMutualContacts: 1 + readonly property int sentContactRequest: 2 + readonly property int receivedContactRequest: 3 + readonly property int rejectedSentContactRequest: 4 + readonly property int rejectedReceivedContactRequest: 5 + readonly property int blockedContacts: 6 + } + readonly property int communityImported: 0 readonly property int communityImportingInProgress: 1 readonly property int communityImportingError: 2