diff --git a/src/app/modules/main/chat_section/chat_content/users/module.nim b/src/app/modules/main/chat_section/chat_content/users/module.nim index 5f70a87022..5ebd9ac27d 100644 --- a/src/app/modules/main/chat_section/chat_content/users/module.nim +++ b/src/app/modules/main/chat_section/chat_content/users/module.nim @@ -196,7 +196,7 @@ method onChatMemberUpdated*(self: Module, publicKey: string, memberRole: MemberR return let contactDetails = self.controller.getContactDetails(publicKey) let isMe = publicKey == singletonInstance.userProfile.getPubKey() - self.view.model().updateItem( + discard self.view.model().updateItem( pubKey = publicKey, displayName = contactDetails.dto.displayName, ensName = contactDetails.dto.name, diff --git a/src/app/modules/main/chat_section/controller.nim b/src/app/modules/main/chat_section/controller.nim index d264d56f8c..b099adfa3b 100644 --- a/src/app/modules/main/chat_section/controller.nim +++ b/src/app/modules/main/chat_section/controller.nim @@ -410,7 +410,7 @@ proc init*(self: Controller) = self.delegate.updateRequestToJoinState(RequestToJoinState.Requested) self.events.on(SIGNAL_REQUEST_TO_JOIN_COMMUNITY_CANCELED) do(e:Args): - let args = community_service.CommunityIdArgs(e) + let args = community_service.CanceledCommunityRequestArgs(e) if args.communityId == self.sectionId: self.delegate.updateRequestToJoinState(RequestToJoinState.None) diff --git a/src/app/modules/main/communities/models/pending_request_item.nim b/src/app/modules/main/communities/models/pending_request_item.nim deleted file mode 100644 index c6d7c63bbe..0000000000 --- a/src/app/modules/main/communities/models/pending_request_item.nim +++ /dev/null @@ -1,54 +0,0 @@ -import stew/shims/strformat - -type - PendingRequestItem* = ref object - id: string - publicKey: string - chatId: string - communityId: string - state: int - our: string - -proc initItem*( - id: string, - publicKey: string, - chatId: string, - communityId: string, - state: int, - our: string - ): PendingRequestItem = - result = PendingRequestItem() - result.id = id - result.publicKey = publicKey - result.chatId = chatId - result.communityId = communityId - result.state = state - result.our = our - -proc id*(self: PendingRequestItem): string = - self.id - -proc pubKey*(self: PendingRequestItem): string = - self.publicKey - -proc chatId*(self: PendingRequestItem): string = - self.chatId - -proc communityId*(self: PendingRequestItem): string = - self.communityId - -proc state*(self: PendingRequestItem): int = - self.state - -proc our*(self: PendingRequestItem): string = - self.our - -proc `$`*(self: PendingRequestItem): string = - result = fmt"""PendingRequestItem( - id: {self.id}, - publicKey: {$self.publicKey}, - chatId: {$self.chatId}, - communityId: {$self.communityId}, - state: {$self.state}, - our: {$self.our}, - ]""" \ No newline at end of file diff --git a/src/app/modules/main/communities/models/pending_request_model.nim b/src/app/modules/main/communities/models/pending_request_model.nim deleted file mode 100644 index 8cd17235a4..0000000000 --- a/src/app/modules/main/communities/models/pending_request_model.nim +++ /dev/null @@ -1,120 +0,0 @@ -import NimQml, Tables -import pending_request_item - -type - ModelRole {.pure.} = enum - Id = UserRole + 1 - PubKey - ChatId - CommunityId - State - Our - -QtObject: - type PendingRequestModel* = ref object of QAbstractListModel - items*: seq[PendingRequestItem] - - proc setup(self: PendingRequestModel) = - self.QAbstractListModel.setup - - proc delete(self: PendingRequestModel) = - self.items = @[] - self.QAbstractListModel.delete - - proc newPendingRequestModel*(): PendingRequestModel = - new(result, delete) - result.setup - - proc countChanged(self: PendingRequestModel) {.signal.} - - proc setItems*(self: PendingRequestModel, items: seq[PendingRequestItem]) = - self.beginResetModel() - self.items = items - self.endResetModel() - self.countChanged() - - proc getCount(self: PendingRequestModel): int {.slot.} = - self.items.len - QtProperty[int] count: - read = getCount - notify = countChanged - - method rowCount(self: PendingRequestModel, index: QModelIndex = nil): int = - return self.items.len - - method roleNames(self: PendingRequestModel): Table[int, string] = - { - ModelRole.Id.int:"id", - ModelRole.PubKey.int:"pubKey", - ModelRole.ChatId.int:"chatId", - ModelRole.CommunityId.int:"communityId", - ModelRole.State.int:"state", - ModelRole.Our.int:"our" - }.toTable - - method data(self: PendingRequestModel, index: QModelIndex, role: int): QVariant = - if not index.isValid: - return - if index.row < 0 or index.row >= self.items.len: - return - let item = self.items[index.row] - let enumRole = role.ModelRole - - case enumRole: - of ModelRole.Id: - result = newQVariant(item.id) - of ModelRole.PubKey: - result = newQVariant(item.pubKey) - of ModelRole.ChatId: - result = newQVariant(item.chatId) - of ModelRole.CommunityId: - result = newQVariant(item.communityId) - of ModelRole.State: - result = newQVariant(item.state) - of ModelRole.Our: - result = newQVariant(item.our) - - proc findIndexById(self: PendingRequestModel, id: string): int = - for i in 0 ..< self.items.len: - if(self.items[i].id == id): - return i - return -1 - - proc addItems*(self: PendingRequestModel, items: seq[PendingRequestItem]) = - if(items.len == 0): - return - - let parentModelIndex = newQModelIndex() - defer: parentModelIndex.delete - - let first = self.items.len - let last = first + items.len - 1 - self.beginInsertRows(parentModelIndex, first, last) - self.items.add(items) - self.endInsertRows() - self.countChanged() - - proc addItem*(self: PendingRequestModel, item: PendingRequestItem) = - let parentModelIndex = newQModelIndex() - defer: parentModelIndex.delete - - self.beginInsertRows(parentModelIndex, self.items.len, self.items.len) - self.items.add(item) - self.endInsertRows() - self.countChanged() - - proc containsItemWithId*(self: PendingRequestModel, id: string): bool = - return self.findIndexById(id) != -1 - - proc removeItemWithId*(self: PendingRequestModel, id: string) = - let ind = self.findIndexById(id) - if(ind == -1): - return - - let parentModelIndex = newQModelIndex() - defer: parentModelIndex.delete - - self.beginRemoveRows(parentModelIndex, ind, ind) - self.items.delete(ind) - self.endRemoveRows() - self.countChanged() diff --git a/src/app/modules/main/communities/module.nim b/src/app/modules/main/communities/module.nim index dad638c7e7..cc10838645 100644 --- a/src/app/modules/main/communities/module.nim +++ b/src/app/modules/main/communities/module.nim @@ -319,8 +319,7 @@ method navigateToCommunity*(self: Module, communityId: string) = self.delegate.setActiveSectionById(communityId) method communityEdited*(self: Module, community: CommunityDto) = - self.view.model().editItem(self.getCommunityItem(community)) - self.view.communityChanged(community.id) + self.view.updateItem(self.getCommunityItem(community)) method setCuratedCommunities*(self: Module, curatedCommunities: seq[CommunityDto]) = for community in curatedCommunities: diff --git a/src/app/modules/main/communities/view.nim b/src/app/modules/main/communities/view.nim index 8e39516844..9b7034b8a5 100644 --- a/src/app/modules/main/communities/view.nim +++ b/src/app/modules/main/communities/view.nim @@ -315,7 +315,7 @@ QtObject: self.model.addItem(item) self.communityAdded(item.id) - proc updateItem(self: View, item: SectionItem) = + proc updateItem*(self: View, item: SectionItem) = self.model.editItem(item) self.communityChanged(item.id) diff --git a/src/app/modules/main/controller.nim b/src/app/modules/main/controller.nim index 3cf34e2e5a..311a394bc2 100644 --- a/src/app/modules/main/controller.nim +++ b/src/app/modules/main/controller.nim @@ -309,6 +309,10 @@ proc init*(self: Controller) = var args = CommunityRequestArgs(e) self.delegate.newCommunityMembershipRequestReceived(args.communityRequest) + self.events.on(SIGNAL_REQUEST_TO_JOIN_COMMUNITY_CANCELED) do(e:Args): + let args = community_service.CanceledCommunityRequestArgs(e) + self.delegate.communityMembershipRequestCanceled(args.communityId, args.requestId, args.pubKey) + self.events.on(SIGNAL_NEW_REQUEST_TO_JOIN_COMMUNITY_ACCEPTED) do(e: Args): var args = CommunityRequestArgs(e) self.delegate.communityMemberRevealedAccountsAdded(args.communityRequest) @@ -496,6 +500,13 @@ proc init*(self: Controller) = self.events.on(SIGNAL_WALLET_ACCOUNT_NETWORK_ENABLED_UPDATED) do(e: Args): self.delegate.onAppNetworkChanged() + self.events.on(SIGNAL_CONTACT_UPDATED) do(e: Args): + let args = ContactArgs(e) + self.delegate.contactUpdated(args.contactId) + + self.events.on(SIGNAL_LOGGEDIN_USER_NAME_CHANGED) do(e: Args): + self.delegate.contactUpdated(singletonInstance.userProfile.getPubKey()) + proc isConnected*(self: Controller): bool = return self.nodeService.isConnected() diff --git a/src/app/modules/main/io_interface.nim b/src/app/modules/main/io_interface.nim index 70b3d3684b..ceaaacac6f 100644 --- a/src/app/modules/main/io_interface.nim +++ b/src/app/modules/main/io_interface.nim @@ -219,6 +219,9 @@ method newCommunityMembershipRequestReceived*(self: AccessInterface, membershipR {.base.} = raise newException(ValueError, "No implementation available") +method communityMembershipRequestCanceled*(self: AccessInterface, communityId: string, requestId: string, pubKey: string) {.base.} = + raise newException(ValueError, "No implementation available") + method meMentionedCountChanged*(self: AccessInterface, allMentions: int) {.base.} = raise newException(ValueError, "No implementation available") @@ -433,6 +436,9 @@ method startTokenHoldersManagement*(self: AccessInterface, communityId: string, method stopTokenHoldersManagement*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") +method contactUpdated*(self: AccessInterface, contactId: string) {.base.} = + raise newException(ValueError, "No implementation available") + # This way (using concepts) is used only for the modules managed by AppController type DelegateInterface* = concept c diff --git a/src/app/modules/main/module.nim b/src/app/modules/main/module.nim index 7f681c93c1..c827d4d07e 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -1,8 +1,7 @@ -import NimQml, tables, json, sugar, sequtils, stew/shims/strformat, marshal, times, chronicles, stint, browsers, strutils +import NimQml, tables, json, sequtils, stew/shims/strformat, marshal, times, chronicles, stint, browsers, strutils import io_interface, view, controller, chat_search_item, chat_search_model import ephemeral_notification_item, ephemeral_notification_model -import ./communities/models/[pending_request_item, pending_request_model] import ../shared_models/[user_item, member_item, member_model, section_item, section_model, section_details] import ../shared_models/[color_hash_item, color_hash_model] import ../shared_modules/keycard_popup/module as keycard_shared_module @@ -127,7 +126,7 @@ method calculateProfileSectionHasNotification*[T](self: Module[T]): bool proc switchToContactOrDisplayUserProfile[T](self: Module[T], publicKey: string) method activateStatusDeepLink*[T](self: Module[T], statusDeepLink: string) proc checkIfWeHaveNotifications[T](self: Module[T]) - +proc createMemberItem[T](self: Module[T], memberId: string, requestId: string, state: MembershipRequestState, role: MemberRole, airdropAddress: string = ""): MemberItem proc newModule*[T]( delegate: T, @@ -342,7 +341,6 @@ proc createCommunitySectionItem[T](self: Module[T], communityDetails: CommunityD let hasNotification = unviewedCount > 0 or notificationsCount > 0 let active = self.getActiveSectionId() == communityDetails.id # We must pass on if the current item section is currently active to keep that property as it is - # Add members who were kicked from the community after the ownership change for auto-rejoin after they share addresses var members = communityDetails.members for requestForAutoRejoin in communityDetails.waitingForSharedAddressesRequestsToJoin: @@ -408,15 +406,6 @@ proc createCommunitySectionItem[T](self: Module[T], communityDetails: CommunityD airdropAddress, ) ), - # pendingRequestsToJoin - communityDetails.pendingRequestsToJoin.map(x => pending_request_item.initItem( - x.id, - x.publicKey, - x.chatId, - x.communityId, - x.state, - x.our - )), communityDetails.settings.historyArchiveSupportEnabled, communityDetails.adminSettings.pinMessageAllMembersEnabled, bannedMembers, @@ -1102,7 +1091,7 @@ method communityLeft*[T](self: Module[T], communityId: string) = method communityEdited*[T]( self: Module[T], community: CommunityDto) = - if(not self.chatSectionModules.contains(community.id)): + if not self.chatSectionModules.contains(community.id): return var communitySectionItem = self.createCommunitySectionItem(community, isEdit = true) # We need to calculate the unread counts because the community update doesn't come with it @@ -1367,7 +1356,17 @@ method newCommunityMembershipRequestReceived*[T](self: Module[T], membershipRequ let (contactName, _, _) = self.controller.getContactNameAndImage(membershipRequest.publicKey) let community = self.controller.getCommunityById(membershipRequest.communityId) singletonInstance.globalEvents.newCommunityMembershipRequestNotification("New membership request", - fmt "{contactName} asks to join {community.name}", community.id) + fmt "{contactName} asks to join {community.name}", community.id) + + self.view.model().addPendingMember(membershipRequest.communityId, self.createMemberItem( + membershipRequest.publicKey, + membershipRequest.id, + MembershipRequestState(membershipRequest.state), + MemberRole.None, + )) + +method communityMembershipRequestCanceled*[T](self: Module[T], communityId: string, requestId: string, pubKey: string) = + self.view.model().removePendingMember(communityId, pubKey) method meMentionedCountChanged*[T](self: Module[T], allMentions: int) = singletonInstance.globalEvents.meMentionedIconBadgeNotification(allMentions) @@ -1725,7 +1724,7 @@ method updateRequestToJoinState*[T](self: Module[T], sectionId: string, requestT if sectionId in self.chatSectionModules: self.chatSectionModules[sectionId].updateRequestToJoinState(requestToJoinState) -proc createMemberItem*[T]( +proc createMemberItem[T]( self: Module[T], memberId: string, requestId: string, @@ -1754,4 +1753,20 @@ proc createMemberItem*[T]( airdropAddress = airdropAddress, ) +method contactUpdated*[T](self: Module[T], contactId: string) = + let contactDetails = self.controller.getContactDetails(contactId) + let isMe = contactId == singletonInstance.userProfile.getPubKey() + self.view.model().updateMemberItemInSections( + pubKey = contactId, + displayName = contactDetails.dto.displayName, + ensName = contactDetails.dto.name, + isEnsVerified = contactDetails.dto.ensVerified, + localNickname = contactDetails.dto.localNickname, + alias = contactDetails.dto.alias, + icon = contactDetails.icon, + isContact = contactDetails.dto.isContact, + isVerified = not isMe and contactDetails.dto.isContactVerified(), + isUntrustworthy = contactDetails.dto.trustStatus == TrustStatus.Untrustworthy, + ) + {.pop.} diff --git a/src/app/modules/main/profile_section/notifications/item.nim b/src/app/modules/main/profile_section/notifications/item.nim index 79cc0d3ac0..cced0973b3 100644 --- a/src/app/modules/main/profile_section/notifications/item.nim +++ b/src/app/modules/main/profile_section/notifications/item.nim @@ -19,9 +19,15 @@ type globalMentions: string otherMessages: string -proc initItem*(id, name, image, color: string, joinedTimestamp: int64, itemType: Type, muteAllMessages = false, - personalMentions = VALUE_NOTIF_SEND_ALERTS, globalMentions = VALUE_NOTIF_SEND_ALERTS, - otherMessages = VALUE_NOTIF_TURN_OFF): Item = +proc initItem*( + id, name, image, color: string, + joinedTimestamp: int64, + itemType: Type, + muteAllMessages = false, + personalMentions = VALUE_NOTIF_SEND_ALERTS, + globalMentions = VALUE_NOTIF_SEND_ALERTS, + otherMessages = VALUE_NOTIF_TURN_OFF, + ): Item = result = Item() result.id = id result.name = name @@ -46,9 +52,15 @@ proc `name=`*(self: Item, value: string) = proc image*(self: Item): string = return self.image +proc `image=`*(self: Item, value: string) = + self.image = value + proc color*(self: Item): string = self.color +proc `color=`*(self: Item, value: string) = + self.color = value + proc joinedTimestamp*(self: Item): int64 = return self.joinedTimestamp diff --git a/src/app/modules/main/profile_section/notifications/model.nim b/src/app/modules/main/profile_section/notifications/model.nim index c7728b45c9..1fe3e3eed3 100644 --- a/src/app/modules/main/profile_section/notifications/model.nim +++ b/src/app/modules/main/profile_section/notifications/model.nim @@ -2,6 +2,7 @@ import NimQml, Tables import item import ../../../../../app_service/service/settings/dto/settings +import ../../../shared_models/model_utils type ModelRole {.pure.} = enum @@ -57,10 +58,10 @@ QtObject: }.toTable method data(self: Model, index: QModelIndex, role: int): QVariant = - if (not index.isValid): + if not index.isValid: return - if (index.row < 0 or index.row >= self.items.len): + if index.row < 0 or index.row >= self.items.len: return let item = self.items[index.row] @@ -108,7 +109,7 @@ QtObject: self.countChanged() proc setItems*(self: Model, items: seq[Item]) = - if(items.len == 0): + if items.len == 0: return let parentModelIndex = newQModelIndex() @@ -129,7 +130,7 @@ QtObject: proc removeItemById*(self: Model, id: string) = let ind = self.findIndexForItemId(id) - if(ind == -1): + if ind == -1: return let parentModelIndex = newQModelIndex() @@ -150,24 +151,48 @@ QtObject: let ind = self.findIndexForItemId(id) if(ind == -1): return + + var roles: seq[int] = @[] - self.items[ind].muteAllMessages = muteAllMessages - self.items[ind].personalMentions = personalMentions - self.items[ind].globalMentions = globalMentions - self.items[ind].otherMessages = otherMessages + updateRole(muteAllMessages, MuteAllMessages) + updateRole(personalMentions, PersonalMentions) + updateRole(globalMentions, GlobalMentions) + updateRole(otherMessages, OtherMessages) + + if roles.len == 0: + return + + roles.add(ModelRole.Customized.int) let index = self.createIndex(ind, 0, nil) defer: index.delete - self.dataChanged(index, index, @[ModelRole.MuteAllMessages.int, ModelRole.PersonalMentions.int, - ModelRole.GlobalMentions.int, ModelRole.OtherMessages.int, ModelRole.Customized.int]) + self.dataChanged(index, index, roles) proc updateName*(self: Model, id: string, name: string) = let ind = self.findIndexForItemId(id) - if(ind == -1): + if ind == -1 or self.items[ind].name == name: return self.items[ind].name = name let index = self.createIndex(ind, 0, nil) defer: index.delete - self.dataChanged(index, index, @[ModelRole.Name.int]) \ No newline at end of file + self.dataChanged(index, index, @[ModelRole.Name.int]) + + proc updateItem*(self: Model, id, name, image, color: string) = + let ind = self.findIndexForItemId(id) + if ind == -1: + return + + var roles: seq[int] = @[] + + updateRole(name, Name) + updateRole(image, Image) + updateRole(color, Color) + + if roles.len == 0: + return + + let index = self.createIndex(ind, 0, nil) + defer: index.delete + self.dataChanged(index, index, roles) \ No newline at end of file diff --git a/src/app/modules/main/profile_section/notifications/module.nim b/src/app/modules/main/profile_section/notifications/module.nim index 21e2ab4931..f7dabbfb6a 100644 --- a/src/app/modules/main/profile_section/notifications/module.nim +++ b/src/app/modules/main/profile_section/notifications/module.nim @@ -129,27 +129,29 @@ method addCommunity*(self: Module, communityDto: CommunityDto) = self.view.exemptionsModel().addItem(item) method editCommunity*(self: Module, communityDto: CommunityDto) = - self.view.exemptionsModel().removeItemById(communityDto.id) - let item = self.createItem(communityDto.id, communityDto.name, communityDto.images.thumbnail, communityDto.color, - joinedTimestamp = 0, item.Type.Community) - self.view.exemptionsModel().addItem(item) + self.view.exemptionsModel().updateItem( + communityDto.id, + communityDto.name, + communityDto.images.thumbnail, + communityDto.color, + ) method removeItemWithId*(self: Module, itemId: string) = - if(self.controller.removeNotifSettingExemptions(itemId)): + if self.controller.removeNotifSettingExemptions(itemId): self.view.exemptionsModel().removeItemById(itemId) method addChat*(self: Module, chatDto: ChatDto) = if chatDto.chatType != ChatType.OneToOne and chatDto.chatType != ChatType.PrivateGroupChat: return let ind = self.view.exemptionsModel().findIndexForItemId(chatDto.id) - if(ind != -1): + if ind != -1: return let item = self.createChatItem(chatDto) self.view.exemptionsModel().addItem(item) method addChat*(self: Module, itemId: string) = let ind = self.view.exemptionsModel().findIndexForItemId(itemId) - if(ind != -1): + if ind != -1: return let chatDto = self.controller.getChatDetails(itemId) if chatDto.chatType != ChatType.OneToOne and chatDto.chatType != ChatType.PrivateGroupChat: diff --git a/src/app/modules/main/view.nim b/src/app/modules/main/view.nim index 25dc778791..fc1b9d19b8 100644 --- a/src/app/modules/main/view.nim +++ b/src/app/modules/main/view.nim @@ -73,7 +73,7 @@ QtObject: proc editItem*(self: View, item: SectionItem) = self.model.editItem(item) - if (self.activeSection.getId() == item.id): + if self.activeSection.getId() == item.id: self.activeSectionSet(item) proc model*(self: View): SectionModel = @@ -164,7 +164,6 @@ QtObject: proc activeSectionSet*(self: View, item: SectionItem) = self.activeSection.setActiveSectionData(item) - self.activeSectionChanged() proc setNthEnabledSectionActive*(self: View, nth: int) {.slot.} = let item = self.model.getNthEnabledItem(nth) diff --git a/src/app/modules/shared_models/member_model.nim b/src/app/modules/shared_models/member_model.nim index 4aa0990c04..51315cc283 100644 --- a/src/app/modules/shared_models/member_model.nim +++ b/src/app/modules/shared_models/member_model.nim @@ -1,11 +1,11 @@ import NimQml, Tables, stew/shims/strformat, sequtils, sugar - # TODO: use generics to remove duplication between user_model and member_model import ../../../app_service/common/types import ../../../app_service/service/contacts/dto/contacts import member_item import contacts_utils +import model_utils type ModelRole {.pure.} = enum @@ -74,7 +74,7 @@ QtObject: read = getCount notify = countChanged - method rowCount(self: Model, index: QModelIndex = nil): int = + method rowCount*(self: Model, index: QModelIndex = nil): int = return self.items.len method roleNames(self: Model): Table[int, string] = @@ -174,6 +174,44 @@ QtObject: self.endInsertRows() self.countChanged() + proc findIndexForMember(self: Model, pubKey: string): int = + for i in 0 ..< self.items.len: + if(self.items[i].pubKey == pubKey): + return i + + return -1 + + proc getMemberItem*(self: Model, pubKey: string): MemberItem = + let ind = self.findIndexForMember(pubKey) + if ind != -1: + return self.items[ind] + + proc removeItemWithIndex(self: Model, index: int) = + let parentModelIndex = newQModelIndex() + defer: parentModelIndex.delete + + self.beginRemoveRows(parentModelIndex, index, index) + self.items.delete(index) + self.endRemoveRows() + self.countChanged() + + proc removeAllItems(self: Model) = + if self.items.len <= 0: + return + + self.beginResetModel() + self.items = @[] + self.endResetModel() + self.countChanged() + + # TODO: rename me to removeItemByPubkey + proc removeItemById*(self: Model, pubKey: string) = + let ind = self.findIndexForMember(pubKey) + if ind == -1: + return + + self.removeItemWithIndex(ind) + proc addItems*(self: Model, items: seq[MemberItem]) = if items.len == 0: return @@ -189,22 +227,6 @@ QtObject: self.endInsertRows() self.countChanged() - proc findIndexForMember(self: Model, pubKey: string): int = - for i in 0 ..< self.items.len: - if(self.items[i].pubKey == pubKey): - return i - - return -1 - - proc removeItemWithIndex(self: Model, index: int) = - let parentModelIndex = newQModelIndex() - defer: parentModelIndex.delete - - self.beginRemoveRows(parentModelIndex, index, index) - self.items.delete(index) - self.endRemoveRows() - self.countChanged() - proc isContactWithIdAdded*(self: Model, id: string): bool = return self.findIndexForMember(id) != -1 @@ -219,17 +241,9 @@ QtObject: resolvePreferredDisplayName(self.items[ind].localNickname, self.items[ind].ensName, self.items[ind].displayName, self.items[ind].alias) != resolvePreferredDisplayName(localNickname, ensName, displayName, self.items[ind].alias) - if self.items[ind].displayName != displayName: - self.items[ind].displayName = displayName - roles.add(ModelRole.DisplayName.int) - - if self.items[ind].ensName != ensName: - self.items[ind].ensName = ensName - roles.add(ModelRole.EnsName.int) - - if self.items[ind].localNickname != localNickname: - self.items[ind].localNickname = localNickname - roles.add(ModelRole.LocalNickname.int) + updateRole(displayName, DisplayName) + updateRole(ensName, EnsName) + updateRole(localNickname, LocalNickname) if roles.len == 0: return @@ -269,7 +283,8 @@ QtObject: memberRole: MemberRole, joined: bool, isUntrustworthy: bool, - ) = + callDataChanged: bool = true, + ): seq[int] = let ind = self.findIndexForMember(pubKey) if ind == -1: return @@ -278,51 +293,19 @@ QtObject: let preferredDisplayNameChanged = resolvePreferredDisplayName(self.items[ind].localNickname, self.items[ind].ensName, self.items[ind].displayName, self.items[ind].alias) != - resolvePreferredDisplayName(localNickname, ensName, displayName, alias) + resolvePreferredDisplayName(localNickname, ensName, displayName, self.items[ind].alias) - if self.items[ind].displayName != displayName: - self.items[ind].displayName = displayName - roles.add(ModelRole.DisplayName.int) - - if self.items[ind].ensName != ensName: - self.items[ind].ensName = ensName - roles.add(ModelRole.EnsName.int) - - if self.items[ind].localNickname != localNickname: - self.items[ind].localNickname = localNickname - roles.add(ModelRole.LocalNickname.int) - - if self.items[ind].isEnsVerified != isEnsVerified: - self.items[ind].isEnsVerified = isEnsVerified - roles.add(ModelRole.IsEnsVerified.int) - - if self.items[ind].alias != alias: - self.items[ind].alias = alias - roles.add(ModelRole.Alias.int) - - if self.items[ind].icon != icon: - self.items[ind].icon = icon - roles.add(ModelRole.Icon.int) - - if self.items[ind].isContact != isContact: - self.items[ind].isContact = isContact - roles.add(ModelRole.IsContact.int) - - if self.items[ind].isVerified != isVerified: - self.items[ind].isVerified = isVerified - roles.add(ModelRole.IsVerified.int) - - if self.items[ind].memberRole != memberRole: - self.items[ind].memberRole = memberRole - roles.add(ModelRole.MemberRole.int) - - if self.items[ind].joined != joined: - self.items[ind].joined = joined - roles.add(ModelRole.Joined.int) - - if self.items[ind].isUntrustworthy != isUntrustworthy: - self.items[ind].isUntrustworthy = isUntrustworthy - roles.add(ModelRole.IsUntrustworthy.int) + updateRole(displayName, DisplayName) + updateRole(ensName, EnsName) + updateRole(localNickname, LocalNickname) + updateRole(isEnsVerified, IsEnsVerified) + updateRole(alias, Alias) + updateRole(icon, Icon) + updateRole(isContact, IsContact) + updateRole(isVerified, IsVerified) + updateRole(memberRole, MemberRole) + updateRole(joined, Joined) + updateRole(isUntrustworthy, IsUntrustworthy) if preferredDisplayNameChanged: roles.add(ModelRole.PreferredDisplayName.int) @@ -330,9 +313,89 @@ QtObject: if roles.len == 0: return - let index = self.createIndex(ind, 0, nil) - defer: index.delete - self.dataChanged(index, index, roles) + if callDataChanged: + let index = self.createIndex(ind, 0, nil) + defer: index.delete + self.dataChanged(index, index, roles) + + return roles + + proc updateItems*(self: Model, items: seq[MemberItem]) = + var startIndex = -1 + var endIndex = -1 + var allRoles: seq[int] = @[] + for item in items: + let itemIndex = self.findIndexForMember(item.pubKey) + if itemIndex == -1: + continue + let roles = self.updateItem( + item.pubKey, + item.displayName, + item.ensName, + item.isEnsVerified, + item.localNickname, + item.alias, + item.icon, + item.isContact, + item.isVerified, + item.memberRole, + item.joined, + item.isUntrustworthy, + callDataChanged = false, + ) + + if roles.len > 0: + if startIndex == -1: + startIndex = itemIndex + endIndex = itemIndex + allRoles = concat(allRoles, roles) + + if allRoles.len == 0: + return + + let startModelIndex = self.createIndex(startIndex, 0, nil) + let endModelIndex = self.createIndex(endIndex, 0, nil) + defer: startModelIndex.delete + defer: endModelIndex.delete + self.dataChanged(startModelIndex, endModelIndex, allRoles) + + + proc updateToTheseItems*(self: Model, items: seq[MemberItem]) = + if items.len == 0: + self.removeAllItems() + return + + # Check for removals + var itemsToRemove: seq[string] = @[] + for oldItem in self.items: + var found = false + for newItem in items: + if oldItem.pubKey == newItem.pubKey: + found = true + break + if not found: + itemsToRemove.add(oldItem.pubKey) + + for itemToRemove in itemsToRemove: + self.removeItemById(itemToRemove) + + var itemsToAdd: seq[MemberItem] = @[] + var itemsToUpdate: seq[MemberItem] = @[] + + for item in items: + let ind = self.findIndexForMember(item.pubKey) + if ind == -1: + # Item does not exist, we add it + itemsToAdd.add(item) + continue + + itemsToUpdate.add(item) + + if itemsToUpdate.len > 0: + self.updateItems(itemsToUpdate) + + if itemsToAdd.len > 0: + self.addItems(itemsToAdd) proc updateItem*( self: Model, @@ -351,7 +414,7 @@ QtObject: if ind == -1: return - self.updateItem( + discard self.updateItem( pubKey, displayName, ensName, @@ -403,14 +466,6 @@ QtObject: return self.items[idx].airdropAddress -# TODO: rename me to removeItemByPubkey - proc removeItemById*(self: Model, pubKey: string) = - let ind = self.findIndexForMember(pubKey) - if ind == -1: - return - - self.removeItemWithIndex(ind) - # TODO: rename me to getItemsAsPubkeys proc getItemIds*(self: Model): seq[string] = return self.items.map(i => i.pubKey) diff --git a/src/app/modules/shared_models/model_utils.nim b/src/app/modules/shared_models/model_utils.nim new file mode 100644 index 0000000000..ebb598827a --- /dev/null +++ b/src/app/modules/shared_models/model_utils.nim @@ -0,0 +1,20 @@ +import std/macros + +# Macro that simplifies checking and updating values in a model +# IMPORTANT: + # The model's items need to be in a `seq` called `items` + # A `seq[string]` named `roles` needs to exist + # The index of the item being checked must be named `ind` +macro updateRole*(propertyName: untyped, roleName: untyped): untyped = + quote do: + if self.items[ind].`propertyName` != `propertyName`: + self.items[ind].`propertyName` = `propertyName` + roles.add(ModelRole.`roleName`.int) + +# Same thing as updateRole where you have a value to set that is not the same **exact** name as the propertyName +# Eg: updateRoleWithValue(name, Name, item.name) +macro updateRoleWithValue*(propertyName: untyped, roleName: untyped, value: untyped): untyped = + quote do: + if self.items[ind].`propertyName` != `value`: + self.items[ind].`propertyName` = `value` + roles.add(ModelRole.`roleName`.int) \ No newline at end of file diff --git a/src/app/modules/shared_models/section_details.nim b/src/app/modules/shared_models/section_details.nim index 5ed69e2b5f..cfa65cdc48 100644 --- a/src/app/modules/shared_models/section_details.nim +++ b/src/app/modules/shared_models/section_details.nim @@ -17,25 +17,50 @@ QtObject: new(result, delete) result.setup - proc setActiveSectionData*(self: SectionDetails, item: SectionItem) = - self.id = item.id - self.sectionType = item.sectionType - self.joined = item.joined - + proc idChanged*(self: SectionDetails) {.signal.} proc getId*(self: SectionDetails): string {.slot.} = return self.id QtProperty[string] id: read = getId + notify = idChanged + proc sectionTypeChanged*(self: SectionDetails) {.signal.} proc getSectionType(self: SectionDetails): int {.slot.} = return self.sectionType.int QtProperty[int] sectionType: read = getSectionType + notify = sectionTypeChanged + proc joinedChanged*(self: SectionDetails) {.signal.} proc getJoined(self: SectionDetails): bool {.slot.} = return self.joined QtProperty[bool] joined: - read = getJoined \ No newline at end of file + read = getJoined + notify = joinedChanged + + proc setActiveSectionData*(self: SectionDetails, item: SectionItem) = + var idChanged = false + var joinedChanged = false + var sectionTypeChanged = false + + if self.id != item.id: + self.id = item.id + idChanged = true + + if self.joined != item.joined: + self.joined = item.joined + joinedChanged = true + + if self.sectionType != item.sectionType: + self.sectionType = item.sectionType + sectionTypeChanged = true + + if idChanged: + self.idChanged() + if joinedChanged: + self.joinedChanged() + if sectionTypeChanged: + self.sectionTypeChanged() diff --git a/src/app/modules/shared_models/section_item.nim b/src/app/modules/shared_models/section_item.nim index 0bf1ec6d4e..667c951931 100644 --- a/src/app/modules/shared_models/section_item.nim +++ b/src/app/modules/shared_models/section_item.nim @@ -1,6 +1,5 @@ import stew/shims/strformat, stint import ./member_model, ./member_item -import ../main/communities/models/[pending_request_item, pending_request_model] import ../main/communities/tokens/models/token_model as community_tokens_model import ../main/communities/tokens/models/token_item @@ -48,7 +47,6 @@ type ensOnly: bool muted: bool membersModel: member_model.Model - pendingRequestsToJoinModel: PendingRequestModel historyArchiveSupportEnabled: bool pinMessageAllMembersEnabled: bool bannedMembersModel: member_model.Model @@ -89,7 +87,6 @@ proc initItem*( ensOnly = false, muted = false, members: seq[MemberItem] = @[], - pendingRequestsToJoin: seq[PendingRequestItem] = @[], historyArchiveSupportEnabled = false, pinMessageAllMembersEnabled = false, bannedMembers: seq[MemberItem] = @[], @@ -130,8 +127,6 @@ proc initItem*( result.muted = muted result.membersModel = newModel() result.membersModel.setItems(members) - result.pendingRequestsToJoinModel = newPendingRequestModel() - result.pendingRequestsToJoinModel.setItems(pendingRequestsToJoin) result.historyArchiveSupportEnabled = historyArchiveSupportEnabled result.pinMessageAllMembersEnabled = pinMessageAllMembersEnabled result.bannedMembersModel = newModel() @@ -199,6 +194,9 @@ proc sectionType*(self: SectionItem): SectionType {.inline.} = proc name*(self: SectionItem): string {.inline.} = self.name +proc `name=`*(self: var SectionItem, value: string) {.inline.} = + self.name = value + proc memberRole*(self: SectionItem): MemberRole {.inline.} = self.memberRole @@ -208,30 +206,57 @@ proc `memberRole=`*(self: var SectionItem, value: MemberRole) {.inline.} = proc isControlNode*(self: SectionItem): bool {.inline.} = self.isControlNode +proc `isControlNode=`*(self: var SectionItem, value: bool) {.inline.} = + self.isControlNode = value + proc description*(self: SectionItem): string {.inline.} = self.description +proc `description=`*(self: var SectionItem, value: string) {.inline.} = + self.description = value + proc introMessage*(self: SectionItem): string {.inline.} = self.introMessage +proc `introMessage=`*(self: var SectionItem, value: string) {.inline.} = + self.introMessage = value + proc outroMessage*(self: SectionItem): string {.inline.} = self.outroMessage +proc `outroMessage=`*(self: var SectionItem, value: string) {.inline.} = + self.outroMessage = value + proc image*(self: SectionItem): string {.inline.} = self.image +proc `image=`*(self: var SectionItem, value: string) {.inline.} = + self.image = value + proc bannerImageData*(self: SectionItem): string {.inline.} = self.bannerImageData +proc `bannerImageData=`*(self: var SectionItem, value: string) {.inline.} = + self.bannerImageData = value + proc icon*(self: SectionItem): string {.inline.} = self.icon +proc `icon=`*(self: var SectionItem, value: string) {.inline.} = + self.icon = value + proc color*(self: SectionItem): string {.inline.} = self.color +proc `color=`*(self: var SectionItem, value: string) {.inline.} = + self.color = value + proc tags*(self: SectionItem): string {.inline.} = self.tags +proc `tags=`*(self: var SectionItem, value: string) {.inline.} = + self.tags = value + proc hasNotification*(self: SectionItem): bool {.inline.} = self.hasNotification @@ -265,27 +290,51 @@ proc `enabled=`*(self: var SectionItem, value: bool) {.inline.} = proc joined*(self: SectionItem): bool {.inline.} = self.joined +proc `joined=`*(self: var SectionItem, value: bool) {.inline.} = + self.joined = value + proc canJoin*(self: SectionItem): bool {.inline.} = self.canJoin +proc `canJoin=`*(self: var SectionItem, value: bool) {.inline.} = + self.canJoin = value + proc spectated*(self: SectionItem): bool {.inline.} = self.spectated +proc `spectated=`*(self: var SectionItem, value: bool) {.inline.} = + self.spectated = value + proc canRequestAccess*(self: SectionItem): bool {.inline.} = self.canRequestAccess +proc `canRequestAccess=`*(self: var SectionItem, value: bool) {.inline.} = + self.canRequestAccess = value + proc canManageUsers*(self: SectionItem): bool {.inline.} = self.canManageUsers +proc `canManageUsers=`*(self: var SectionItem, value: bool) {.inline.} = + self.canManageUsers = value + proc isMember*(self: SectionItem): bool {.inline.} = self.isMember +proc `isMember=`*(self: var SectionItem, value: bool) {.inline.} = + self.isMember = value + proc access*(self: SectionItem): int {.inline.} = self.access +proc `access=`*(self: var SectionItem, value: int) {.inline.} = + self.access = value + proc ensOnly*(self: SectionItem): bool {.inline.} = self.ensOnly +proc `ensOnly=`*(self: var SectionItem, value: bool) {.inline.} = + self.ensOnly = value + proc muted*(self: SectionItem): bool {.inline.} = self.muted @@ -331,21 +380,30 @@ proc declinedMemberRequests*(self: SectionItem): member_model.Model {.inline.} = proc isPendingOwnershipRequest*(self: SectionItem): bool {.inline.} = self.isPendingOwnershipRequest +proc `isPendingOwnershipRequest=`*(self: var SectionItem, value: bool) {.inline.} = + self.isPendingOwnershipRequest = value + proc setIsPendingOwnershipRequest*(self: var SectionItem, isPending: bool) {.inline.} = self.isPendingOwnershipRequest = isPending -proc pendingRequestsToJoin*(self: SectionItem): PendingRequestModel {.inline.} = - self.pendingRequestsToJoinModel - proc historyArchiveSupportEnabled*(self: SectionItem): bool {.inline.} = self.historyArchiveSupportEnabled +proc `historyArchiveSupportEnabled=`*(self: var SectionItem, value: bool) {.inline.} = + self.historyArchiveSupportEnabled = value + proc pinMessageAllMembersEnabled*(self: SectionItem): bool {.inline.} = self.pinMessageAllMembersEnabled +proc `pinMessageAllMembersEnabled=`*(self: var SectionItem, value: bool) {.inline.} = + self.pinMessageAllMembersEnabled = value + proc encrypted*(self: SectionItem): bool {.inline.} = self.encrypted +proc `encrypted=`*(self: var SectionItem, value: bool) {.inline.} = + self.encrypted = value + proc appendCommunityToken*(self: SectionItem, item: TokenItem) {.inline.} = self.communityTokensModel.appendItem(item) diff --git a/src/app/modules/shared_models/section_model.nim b/src/app/modules/shared_models/section_model.nim index 2d85c82f46..29f8a4deb9 100644 --- a/src/app/modules/shared_models/section_model.nim +++ b/src/app/modules/shared_models/section_model.nim @@ -2,8 +2,9 @@ import NimQml, Tables, strutils, stew/shims/strformat import json -import section_item, member_model +import section_item, member_model, member_item import ../main/communities/tokens/models/[token_item, token_model] +import model_utils type ModelRole {.pure.} = enum @@ -34,7 +35,6 @@ type EnsOnly Muted MembersModel - PendingRequestsToJoinModel HistoryArchiveSupportEnabled PinMessageAllMembersEnabled BannedMembersModel @@ -111,7 +111,6 @@ QtObject: ModelRole.EnsOnly.int:"ensOnly", ModelRole.Muted.int:"muted", ModelRole.MembersModel.int:"members", - ModelRole.PendingRequestsToJoinModel.int:"pendingRequestsToJoin", ModelRole.HistoryArchiveSupportEnabled.int:"historyArchiveSupportEnabled", ModelRole.PinMessageAllMembersEnabled.int:"pinMessageAllMembersEnabled", ModelRole.BannedMembersModel.int:"bannedMembers", @@ -191,8 +190,6 @@ QtObject: result = newQVariant(item.muted) of ModelRole.MembersModel: result = newQVariant(item.members) - of ModelRole.PendingRequestsToJoinModel: - result = newQVariant(item.pendingRequestsToJoin) of ModelRole.HistoryArchiveSupportEnabled: result = newQVariant(item.historyArchiveSupportEnabled) of ModelRole.PinMessageAllMembersEnabled: @@ -283,9 +280,11 @@ QtObject: self.countChanged() proc setMuted*(self: SectionModel, id: string, muted: bool) = - let index = self.getItemIndex(id) - if (index == -1): + if index == -1: + return + + if self.items[index].muted == muted: return self.items[index].muted = muted @@ -294,14 +293,119 @@ QtObject: self.dataChanged(dataIndex, dataIndex, @[ModelRole.Muted.int]) proc editItem*(self: SectionModel, item: SectionItem) = - let index = self.getItemIndex(item.id) - if (index == -1): + let ind = self.getItemIndex(item.id) + if ind == -1: + return + + var roles: seq[int] = @[] + + updateRoleWithValue(name, Name, item.name) + updateRoleWithValue(memberRole, MemberRole, item.memberRole) + updateRoleWithValue(isControlNode, IsControlNode, item.isControlNode) + updateRoleWithValue(description, Description, item.description) + updateRoleWithValue(introMessage, IntroMessage, item.introMessage) + updateRoleWithValue(outroMessage, OutroMessage, item.outroMessage) + updateRoleWithValue(image, Image, item.image) + updateRoleWithValue(bannerImageData, BannerImageData, item.bannerImageData) + updateRoleWithValue(icon, Icon, item.icon) + updateRoleWithValue(color, Color, item.color) + updateRoleWithValue(tags, Tags, item.tags) + updateRoleWithValue(hasNotification, HasNotification, item.hasNotification) + updateRoleWithValue(notificationsCount, NotificationsCount, item.notificationsCount) + updateRoleWithValue(active, Active, item.active) + updateRoleWithValue(enabled, Enabled, item.enabled) + updateRoleWithValue(joined, Joined, item.joined) + updateRoleWithValue(spectated, Spectated, item.spectated) + updateRoleWithValue(isMember, IsMember, item.isMember) + updateRoleWithValue(isMember, IsMember, item.isMember) + updateRoleWithValue(canManageUsers, CanManageUsers, item.canManageUsers) + updateRoleWithValue(canRequestAccess, CanRequestAccess, item.canRequestAccess) + updateRoleWithValue(access, Access, item.access) + updateRoleWithValue(ensOnly, EnsOnly, item.ensOnly) + updateRoleWithValue(muted, Muted, item.muted) + updateRoleWithValue(historyArchiveSupportEnabled, HistoryArchiveSupportEnabled, item.historyArchiveSupportEnabled) + updateRoleWithValue(pinMessageAllMembersEnabled, PinMessageAllMembersEnabled, item.pinMessageAllMembersEnabled) + updateRoleWithValue(encrypted, Encrypted, item.encrypted) + updateRoleWithValue(pubsubTopic, PubsubTopic, item.pubsubTopic) + updateRoleWithValue(pubsubTopicKey, PubsubTopicKey, item.pubsubTopicKey) + updateRoleWithValue(shardIndex, ShardIndex, item.shardIndex) + updateRoleWithValue(isPendingOwnershipRequest, IsPendingOwnershipRequest, item.isPendingOwnershipRequest) + + self.items[ind].members.updateToTheseItems(item.members.getItems()) + self.items[ind].bannedMembers.updateToTheseItems(item.bannedMembers.getItems()) + self.items[ind].pendingMemberRequests.updateToTheseItems(item.pendingMemberRequests.getItems()) + self.items[ind].declinedMemberRequests.updateToTheseItems(item.declinedMemberRequests.getItems()) + + if roles.len == 0: return - self.items[index] = item - let dataIndex = self.createIndex(index, 0, nil) + let dataIndex = self.createIndex(ind, 0, nil) defer: dataIndex.delete - self.dataChanged(dataIndex, dataIndex) + self.dataChanged(dataIndex, dataIndex, roles) + + proc updateMemberItemInSections*( + self: SectionModel, + pubKey: string, + displayName: string, + ensName: string, + isEnsVerified: bool, + localNickname: string, + alias: string, + icon: string, + isContact: bool, + isVerified: bool, + isUntrustworthy: bool, + ) = + for item in self.items: + # TODO refactor to use only one model https://github.com/status-im/status-desktop/issues/16433 + item.members.updateItem( + pubKey, + displayName, + ensName, + isEnsVerified, + localNickname, + alias, + icon, + isContact, + isVerified, + isUntrustworthy, + ) + item.bannedMembers.updateItem( + pubKey, + displayName, + ensName, + isEnsVerified, + localNickname, + alias, + icon, + isContact, + isVerified, + isUntrustworthy, + ) + item.pendingMemberRequests.updateItem( + pubKey, + displayName, + ensName, + isEnsVerified, + localNickname, + alias, + icon, + isContact, + isVerified, + isUntrustworthy, + ) + item.declinedMemberRequests.updateItem( + pubKey, + displayName, + ensName, + isEnsVerified, + localNickname, + alias, + icon, + isContact, + isVerified, + isUntrustworthy, + ) proc getNthEnabledItem*(self: SectionModel, nth: int): SectionItem = if nth >= 0 and nth < self.items.len: @@ -330,13 +434,13 @@ QtObject: proc setActiveSection*(self: SectionModel, id: string) = for i in 0 ..< self.items.len: - if(self.items[i].active): + if self.items[i].active: let index = self.createIndex(i, 0, nil) defer: index.delete self.items[i].active = false self.dataChanged(index, index, @[ModelRole.Active.int]) - if(self.items[i].id == id): + if self.items[i].id == id: let index = self.createIndex(i, 0, nil) defer: index.delete self.items[i].active = true @@ -348,9 +452,11 @@ QtObject: proc notificationsCountChanged*(self: SectionModel) {.signal.} proc enableDisableSection(self: SectionModel, sectionType: SectionType, value: bool) = - if(sectionType != SectionType.Community): + if sectionType != SectionType.Community: for i in 0 ..< self.items.len: - if(self.items[i].sectionType == sectionType): + if self.items[i].sectionType == sectionType: + if self.items[i].enabled == value: + continue let index = self.createIndex(i, 0, nil) defer: index.delete self.items[i].enabled = value @@ -359,9 +465,9 @@ QtObject: var topInd = -1 var bottomInd = -1 for i in 0 ..< self.items.len: - if(self.items[i].sectionType == sectionType): + if self.items[i].sectionType == sectionType: self.items[i].enabled = value - if(topInd == -1): + if topInd == -1: topInd = i bottomInd = i @@ -393,21 +499,31 @@ QtObject: proc updateIsPendingOwnershipRequest*(self: SectionModel, id: string, isPending: bool) = for i in 0 ..< self.items.len: - if(self.items[i].id == id): + if self.items[i].id == id: let index = self.createIndex(i, 0, nil) defer: index.delete + + if self.items[i].isPendingOwnershipRequest == isPending: + return + self.items[i].setIsPendingOwnershipRequest(isPending) self.dataChanged(index, index, @[ModelRole.IsPendingOwnershipRequest.int]) return proc updateNotifications*(self: SectionModel, id: string, hasNotification: bool, notificationsCount: int) = - for i in 0 ..< self.items.len: - if(self.items[i].id == id): - let index = self.createIndex(i, 0, nil) + for ind in 0 ..< self.items.len: + if self.items[ind].id == id: + var roles: seq[int] = @[] + + updateRole(hasNotification, HasNotification) + updateRole(notificationsCount, NotificationsCount) + + if roles.len == 0: + return + + let index = self.createIndex(ind, 0, nil) defer: index.delete - self.items[i].hasNotification = hasNotification - self.items[i].notificationsCount = notificationsCount - self.dataChanged(index, index, @[ModelRole.HasNotification.int, ModelRole.NotificationsCount.int]) + self.dataChanged(index, index, roles) self.notificationsCountChanged() return @@ -423,7 +539,6 @@ QtObject: let index = self.createIndex(i, 0, nil) defer: index.delete self.items[i].appendCommunityToken(item) - self.dataChanged(index, index, @[ModelRole.CommunityTokensModel.int]) return proc getSectionNameById*(self: SectionModel, sectionId: string): string {.slot.} = @@ -476,9 +591,23 @@ QtObject: for pubkey, airdropAddress in communityMembersAirdropAddress.pairs: self.items[index].members.setAirdropAddress(pubkey, airdropAddress) - proc setTokenItems*(self: SectionModel, id: string, communityTokensItems: seq[TokenItem]) = + proc setTokenItems*(self: SectionModel, id: string, communityTokensItems: seq[TokenItem]) = let index = self.getItemIndex(id) if (index == -1): return self.items[index].communityTokens.setItems(communityTokensItems) + + proc addPendingMember*(self: SectionModel, communityId: string, memberItem: MemberItem) = + let i = self.getItemIndex(communityId) + if i == -1: + return + + self.items[i].pendingMemberRequests.addItem(memberItem) + + proc removePendingMember*(self: SectionModel, communityId: string, memberId: string) = + let i = self.getItemIndex(communityId) + if i == -1: + return + + self.items[i].pendingMemberRequests.removeItemById(memberId) diff --git a/src/app/modules/shared_models/user_model.nim b/src/app/modules/shared_models/user_model.nim index a671e70f1a..fe95839b3e 100644 --- a/src/app/modules/shared_models/user_model.nim +++ b/src/app/modules/shared_models/user_model.nim @@ -3,6 +3,7 @@ import user_item import ../../../app_service/common/types import contacts_utils +import model_utils type ModelRole {.pure.} = enum @@ -272,17 +273,9 @@ QtObject: resolvePreferredDisplayName(self.items[ind].localNickname, self.items[ind].ensName, self.items[ind].displayName, self.items[ind].alias) != resolvePreferredDisplayName(localNickname, ensName, displayName, self.items[ind].alias) - if self.items[ind].displayName != displayName: - self.items[ind].displayName = displayName - roles.add(ModelRole.DisplayName.int) - - if self.items[ind].ensName != ensName: - self.items[ind].ensName = ensName - roles.add(ModelRole.EnsName.int) - - if self.items[ind].localNickname != localNickname: - self.items[ind].localNickname = localNickname - roles.add(ModelRole.LocalNickname.int) + updateRole(displayName, DisplayName) + updateRole(ensName, EnsName) + updateRole(localNickname, LocalNickname) if preferredDisplayNameChanged: roles.add(ModelRole.PreferredDisplayName.int) @@ -319,7 +312,7 @@ QtObject: isUntrustworthy: bool = false, ) = let ind = self.findIndexByPubKey(pubKey) - if(ind == -1): + if ind == -1: return var roles: seq[int] = @[] @@ -328,33 +321,12 @@ QtObject: resolvePreferredDisplayName(self.items[ind].localNickname, self.items[ind].ensName, self.items[ind].displayName, self.items[ind].alias) != resolvePreferredDisplayName(localNickname, ensName, displayName, alias) - if self.items[ind].displayName != displayName: - self.items[ind].displayName = displayName - roles.add(ModelRole.DisplayName.int) - - if self.items[ind].ensName != ensName: - self.items[ind].ensName = ensName - roles.add(ModelRole.EnsName.int) - - if self.items[ind].isEnsVerified != isEnsVerified: - self.items[ind].isEnsVerified = isEnsVerified - roles.add(ModelRole.IsEnsVerified.int) - - if self.items[ind].localNickname != localNickname: - self.items[ind].localNickname = localNickname - roles.add(ModelRole.LocalNickname.int) - - if self.items[ind].alias != alias: - self.items[ind].alias = alias - roles.add(ModelRole.Alias.int) - - if self.items[ind].icon != icon: - self.items[ind].icon = icon - roles.add(ModelRole.Icon.int) - - if self.items[ind].isUntrustworthy != isUntrustworthy: - self.items[ind].isUntrustworthy = isUntrustworthy - roles.add(ModelRole.IsUntrustworthy.int) + updateRole(displayName, DisplayName) + updateRole(ensName, EnsName) + updateRole(localNickname, LocalNickname) + updateRole(alias, Alias) + updateRole(icon, Icon) + updateRole(isUntrustworthy, IsUntrustworthy) if preferredDisplayNameChanged: roles.add(ModelRole.PreferredDisplayName.int) diff --git a/src/app_service/service/community/dto/community.nim b/src/app_service/service/community/dto/community.nim index 69b5b8b410..9fbdec2db2 100644 --- a/src/app_service/service/community/dto/community.nim +++ b/src/app_service/service/community/dto/community.nim @@ -1,6 +1,6 @@ {.used.} -import json, sequtils, sugar, tables, strutils, json_serialization +import json, sequtils, sugar, tables, strutils, json_serialization, strformat import ../../../../backend/communities include ../../../common/json_utils @@ -64,6 +64,17 @@ type CommunityMembershipRequestDto* = object our*: string #FIXME: should be bool revealedAccounts*: seq[RevealedAccount] +proc `$`*(self: CommunityMembershipRequestDto): string = + return fmt"""CommunityMembershipRequestDto( + id:{self.id}, + publicKey:{self.publicKey}, + chatId:{self.chatId}, + communityId:{self.communityId}, + state:{self.state}, + our:{self.our}, + revealedAccounts.count:{self.revealedAccounts.len}, + """ + type CommunitySettingsDto* = object id*: string historyArchiveSupportEnabled*: bool diff --git a/src/app_service/service/community/service.nim b/src/app_service/service/community/service.nim index dd2936a3de..86df15a974 100644 --- a/src/app_service/service/community/service.nim +++ b/src/app_service/service/community/service.nim @@ -55,6 +55,11 @@ type CommunityRequestArgs* = ref object of Args communityRequest*: CommunityMembershipRequestDto + CanceledCommunityRequestArgs* = ref object of Args + communityId*: string + requestId*: string + pubKey*: string + CommunityRequestFailedArgs* = ref object of Args communityId*: string error*: string @@ -1976,6 +1981,14 @@ QtObject: # If the state is now declined, add to the declined requests if newState == RequestToJoinType.Declined: community.declinedRequestsToJoin.add(community.pendingRequestsToJoin[indexPending]) + elif newState == RequestToJoinType.Canceled: + self.events.emit(SIGNAL_REQUEST_TO_JOIN_COMMUNITY_CANCELED, + CanceledCommunityRequestArgs( + communityId: communityId, + requestId: requestId, + pubKey: community.pendingRequestsToJoin[indexPending].publicKey, + ) + ) # If the state is no longer pending, delete the request community.pendingRequestsToJoin.delete(indexPending) @@ -2013,9 +2026,15 @@ QtObject: error "error while cancel membership request ", msg return + self.events.emit(SIGNAL_REQUEST_TO_JOIN_COMMUNITY_CANCELED, + CanceledCommunityRequestArgs( + communityId: communityId, + requestId: myPendingRequest.id, + pubKey: community.pendingRequestsToJoin[i].publicKey, + ) + ) community.pendingRequestsToJoin.delete(i) self.communities[communityId] = community - self.events.emit(SIGNAL_REQUEST_TO_JOIN_COMMUNITY_CANCELED, CommunityIdArgs(communityId: communityId)) checkAndEmitACNotificationsFromResponse(self.events, response.result{"activityCenterNotifications"}) return diff --git a/test/nim/member_model_test.nim b/test/nim/member_model_test.nim new file mode 100644 index 0000000000..c118e6c87a --- /dev/null +++ b/test/nim/member_model_test.nim @@ -0,0 +1,128 @@ +import unittest + +import app/modules/shared_models/[member_model, member_item] +import app_service/common/types + +proc createTestMemberItem(pubKey: string): MemberItem = + return initMemberItem( + pubKey = pubKey, + displayName = "", + ensName = "", + isEnsVerified = false, + localNickname = "", + alias = "", + icon = "", + colorId = 0, + isVerified = false, + ) + +let memberA = createTestMemberItem("0xa") +let memberB = createTestMemberItem("0xb") +let memberC = createTestMemberItem("0xc") +let memberD = createTestMemberItem("0xd") +let memberE = createTestMemberItem("0xe") + +suite "empty member model": + let model = newModel() + + test "initial size": + require(model.rowCount() == 0) + +suite "updating member items": + setup: + let model = newModel() + model.addItems(@[memberA, memberB, memberC]) + check(model.rowCount() == 3) + + test "update only display name": + let updatedRoles = model.updateItem( + pubkey = "0xa", + displayName = "newName", + ensName = "", + isEnsVerified = false, + localNickname = "", + alias = "", + icon = "", + isContact = false, + isVerified = false, + memberRole = MemberRole.None, + joined = false, + isUntrustworthy = false, + callDataChanged = false, + ) + # Two updated roles, because preferredDisplayName gets updated too + check(updatedRoles.len() == 2) + let item = model.getMemberItem("0xa") + check(item.displayName == "newName") + + test "update two properties not related to name": + let updatedRoles = model.updateItem( + pubkey = "0xb", + displayName = "", + ensName = "", + isEnsVerified = false, + localNickname = "", + alias = "", + icon = "icon", + isContact = true, + isVerified = false, + memberRole = MemberRole.None, + joined = false, + isUntrustworthy = false, + callDataChanged = false, + ) + check(updatedRoles.len() == 2) + let item = model.getMemberItem("0xb") + check(item.icon == "icon") + check(item.isContact == true) + + test "update two items at the same time": + let memberACopy = memberA + memberACopy.displayName = "bob" + + let memberBCopy = memberB + memberBCopy.displayName = "alice" + + model.updateItems(@[memberACopy, memberBCopy]) + let itemA = model.getMemberItem("0xa") + check(itemA.displayName == "bob") + model.updateItems(@[memberACopy, memberBCopy]) + let itemB = model.getMemberItem("0xb") + check(itemB.displayName == "alice") + + test "remove an item using updateToTheseItems": + model.updateToTheseItems(@[memberA, memberB]) + check(model.rowCount == 2) + + test "add an item using updateToTheseItems": + model.updateToTheseItems(@[memberA, memberB, memberD]) + check(model.rowCount == 3) + + test "add an item and update another using updateToTheseItems": + let memberACopy = memberA + memberACopy.displayName = "roger" + model.updateToTheseItems(@[memberACopy, memberB, memberD, memberE]) + check(model.rowCount == 4) + let itemA = model.getMemberItem("0xa") + check(itemA.displayName == "roger") + + test "add an item, remove one and update another using updateToTheseItems": + let memberACopy = memberA + memberACopy.displayName = "brandon" + let memberCCopy = memberC + memberCCopy.displayName = "kurt" + let memberDCopy = memberD + memberDCopy.displayName = "amanda" + let memberECopy = memberE + memberECopy.displayName = "gina" + + model.updateToTheseItems(@[memberACopy, memberCCopy, memberDCopy, memberECopy]) + check(model.rowCount == 4) + let itemA = model.getMemberItem("0xa") + check(itemA.displayName == "brandon") + let itemC = model.getMemberItem("0xc") + check(itemC.displayName == "kurt") + let itemD = model.getMemberItem("0xd") + check(itemD.displayName == "amanda") + let itemE = model.getMemberItem("0xe") + check(itemE.displayName == "gina")