From 9b8c6aa6735e31135f4067663471526c15e7c25b Mon Sep 17 00:00:00 2001 From: Mykhailo Prakhov Date: Fri, 1 Mar 2024 12:24:09 +0100 Subject: [PATCH] feat: ban/unban/kick system and ephemeral notifications --- src/app/core/notifications/details.nim | 19 ++-- .../notifications/notifications_manager.nim | 94 +++++++++++-------- src/app/global/global_events.nim | 26 +++-- .../main/ephemeral_notification_item.nim | 16 +++- .../main/ephemeral_notification_model.nim | 4 +- src/app/modules/main/module.nim | 63 ++++++++++--- src/app_service/common/types.nim | 1 + .../service/community/dto/community.nim | 5 +- src/app_service/service/community/service.nim | 37 ++++++-- ui/imports/utils/Constants.qml | 3 +- 10 files changed, 184 insertions(+), 84 deletions(-) diff --git a/src/app/core/notifications/details.nim b/src/app/core/notifications/details.nim index 7969527bef..8ce1777b81 100644 --- a/src/app/core/notifications/details.nim +++ b/src/app/core/notifications/details.nim @@ -23,7 +23,10 @@ type CommunityTokenPermissionDeleted, CommunityTokenPermissionCreationFailed, CommunityTokenPermissionUpdateFailed, - CommunityTokenPermissionDeletionFailed + CommunityTokenPermissionDeletionFailed, + CommunityMemberKicked, + CommunityMemberBanned, + CommunityMemberUnbanned NotificationDetails* = object notificationType*: NotificationType # the default value is `UnknownNotification` @@ -41,14 +44,14 @@ proc isEmpty*(self: NotificationDetails): bool = proc toNotificationDetails*(jsonObj: JsonNode): NotificationDetails = var notificationType: int - if (not (jsonObj.getProp("notificationType", notificationType) and + if (not (jsonObj.getProp("notificationType", notificationType) and jsonObj.getProp("sectionId", result.sectionId) and - jsonObj.getProp("isCommunitySection", result.isCommunitySection) and - jsonObj.getProp("sectionActive", result.sectionActive) and - jsonObj.getProp("chatId", result.chatId) and - jsonObj.getProp("chatActive", result.chatActive) and - jsonObj.getProp("isOneToOne", result.isOneToOne) and - jsonObj.getProp("isGroupChat", result.isGroupChat) and + jsonObj.getProp("isCommunitySection", result.isCommunitySection) and + jsonObj.getProp("sectionActive", result.sectionActive) and + jsonObj.getProp("chatId", result.chatId) and + jsonObj.getProp("chatActive", result.chatActive) and + jsonObj.getProp("isOneToOne", result.isOneToOne) and + jsonObj.getProp("isGroupChat", result.isGroupChat) and jsonObj.getProp("messageId", result.messageId))): return NotificationDetails() diff --git a/src/app/core/notifications/notifications_manager.nim b/src/app/core/notifications/notifications_manager.nim index 881e8823fb..c66ace7487 100644 --- a/src/app/core/notifications/notifications_manager.nim +++ b/src/app/core/notifications/notifications_manager.nim @@ -63,21 +63,21 @@ QtObject: self.soundManager = newStatusSoundManager() signalConnect(self.osNotification, "notificationClicked(QString)", self, "onOSNotificationClicked(QString)", 2) - signalConnect(singletonInstance.globalEvents, "showTestNotification(QString, QString)", + signalConnect(singletonInstance.globalEvents, "showTestNotification(QString, QString)", self, "onShowTestNotification(QString, QString)", 2) - signalConnect(singletonInstance.globalEvents, "showMessageNotification(QString, QString, QString, bool, bool, QString, bool, QString, int, bool, bool)", + signalConnect(singletonInstance.globalEvents, "showMessageNotification(QString, QString, QString, bool, bool, QString, bool, QString, int, bool, bool)", self, "onShowMessageNotification(QString, QString, QString, bool, bool, QString, bool, QString, int, bool, bool)", 2) - signalConnect(singletonInstance.globalEvents, "showNewContactRequestNotification(QString, QString, QString)", + signalConnect(singletonInstance.globalEvents, "showNewContactRequestNotification(QString, QString, QString)", self, "onShowNewContactRequestNotification(QString, QString, QString)", 2) signalConnect(singletonInstance.globalEvents, "showAcceptedContactRequest(QString, QString, QString)", self, "onShowAcceptedContactRequest(QString, QString, QString)", 2) signalConnect(singletonInstance.globalEvents, "showContactRemoved(QString, QString, QString)", self, "onShowContactRemoved(QString, QString, QString)", 2) - signalConnect(singletonInstance.globalEvents, "newCommunityMembershipRequestNotification(QString, QString, QString)", + signalConnect(singletonInstance.globalEvents, "newCommunityMembershipRequestNotification(QString, QString, QString)", self, "onNewCommunityMembershipRequestNotification(QString, QString, QString)", 2) - signalConnect(singletonInstance.globalEvents, "myRequestToJoinCommunityAcccepted(QString, QString, QString)", + signalConnect(singletonInstance.globalEvents, "myRequestToJoinCommunityAcccepted(QString, QString, QString)", self, "onMyRequestToJoinCommunityAcccepted(QString, QString, QString)", 2) - signalConnect(singletonInstance.globalEvents, "myRequestToJoinCommunityRejected(QString, QString, QString)", + signalConnect(singletonInstance.globalEvents, "myRequestToJoinCommunityRejected(QString, QString, QString)", self, "onMyRequestToJoinCommunityRejected(QString, QString, QString)", 2) signalConnect(singletonInstance.globalEvents, "meMentionedIconBadgeNotification(int)", self, "onMeMentionedIconBadgeNotification(int)", 2) @@ -88,12 +88,16 @@ QtObject: signalConnect(singletonInstance.globalEvents, "showCommunityTokenPermissionUpdateFailedNotification(QString, QString, QString)", self, "onShowCommunityTokenPermissionUpdateFailedNotification(QString, QString, QString)", 2) signalConnect(singletonInstance.globalEvents, "showCommunityTokenPermissionDeletionFailedNotification(QString, QString, QString)", self, "onShowCommunityTokenPermissionDeletionFailedNotification(QString, QString, QString)", 2) + signalConnect(singletonInstance.globalEvents, "showCommunityMemberKickedNotification(QString, QString, QString)", self, "onShowCommunityMemberKickedNotification(QString, QString, QString)", 2) + signalConnect(singletonInstance.globalEvents, "showCommunityMemberBannedNotification(QString, QString, QString)", self, "onShowCommunityMemberBannedNotification(QString, QString, QString)", 2) + signalConnect(singletonInstance.globalEvents, "showCommunityMemberUnbannedNotification(QString, QString, QString)", self, "onShowCommunityMemberUnbannedNotification(QString, QString, QString)", 2) + self.notificationSetUp = true proc init*(self: NotificationsManager) = self.events.once(FAKE_LOADING_SCREEN_FINISHED) do(e:Args): self.onAppReady() - + proc showOSNotification(self: NotificationsManager, title: string, message: string, identifier: string) = if defined(windows): let data = NotificationArgs(title: title, message: message) @@ -107,7 +111,7 @@ QtObject: ## This slot is called once user clicks OS notificaiton bubble, "identifier" ## contains data which uniquely define that notification. debug "OS notification clicked", identifier=identifier - + # Make the app the top most window. app_makeItActive(singletonInstance.engine) @@ -119,7 +123,7 @@ QtObject: info "Test notification was clicked" return - if(details.notificationType == NotificationType.NewMessage or + if(details.notificationType == NotificationType.NewMessage or details.notificationType == NotificationType.NewMessageWithPersonalMention or details.notificationType == NotificationType.NewMessageWithGlobalMention): let data = ActiveSectionChatArgs(sectionId: details.sectionId, chatId: details.chatId, messageId: details.messageId) @@ -131,15 +135,15 @@ QtObject: let details = NotificationDetails(notificationType: NotificationType.TestNotification) self.processNotification(title, message, details) - proc onShowMessageNotification(self: NotificationsManager, title: string, message: string, sectionId: string, - isCommunitySection: bool, isSectionActive: bool, chatId: string, isChatActive: bool, messageId: string, + proc onShowMessageNotification(self: NotificationsManager, title: string, message: string, sectionId: string, + isCommunitySection: bool, isSectionActive: bool, chatId: string, isChatActive: bool, messageId: string, notificationType: int, isOneToOne: bool, isGroupChat: bool) {.slot.} = let details = NotificationDetails( notificationType: notificationType.NotificationType, - sectionId: sectionId, + sectionId: sectionId, isCommunitySection: isCommunitySection, sectionActive: isSectionActive, - chatId: chatId, + chatId: chatId, chatActive: isChatActive, isOneToOne: isOneToOne, isGroupChat: isGroupChat, @@ -170,12 +174,12 @@ QtObject: let details = NotificationDetails(notificationType: NotificationType.CommunityTokenPermissionDeletionFailed, sectionId: sectionId, isCommunitySection: true) self.processNotification(title, message, details) - proc onShowNewContactRequestNotification*(self: NotificationsManager, title: string, message: string, + proc onShowNewContactRequestNotification*(self: NotificationsManager, title: string, message: string, sectionId: string) {.slot.} = let details = NotificationDetails(notificationType: NotificationType.NewContactRequest, sectionId: sectionId) self.processNotification(title, message, details) - proc onShowAcceptedContactRequest*(self: NotificationsManager, title: string, message: string, + proc onShowAcceptedContactRequest*(self: NotificationsManager, title: string, message: string, sectionId: string) {.slot.} = let details = NotificationDetails(notificationType: NotificationType.AcceptedContactRequest, sectionId: sectionId) self.processNotification(title, message, details) @@ -185,26 +189,38 @@ QtObject: let details = NotificationDetails(notificationType: NotificationType.ContactRemoved, sectionId: sectionId) self.processNotification(title, message, details) - proc onNewCommunityMembershipRequestNotification*(self: NotificationsManager, title: string, message: string, + proc onNewCommunityMembershipRequestNotification*(self: NotificationsManager, title: string, message: string, sectionId: string) {.slot.} = let details = NotificationDetails(notificationType: NotificationType.JoinCommunityRequest, sectionId: sectionId) self.processNotification(title, message, details) - proc onMyRequestToJoinCommunityAcccepted*(self: NotificationsManager, title: string, message: string, + proc onMyRequestToJoinCommunityAcccepted*(self: NotificationsManager, title: string, message: string, sectionId: string) {.slot.} = - let details = NotificationDetails(notificationType: NotificationType.MyRequestToJoinCommunityAccepted, + let details = NotificationDetails(notificationType: NotificationType.MyRequestToJoinCommunityAccepted, sectionId: sectionId) self.processNotification(title, message, details) - proc onMyRequestToJoinCommunityRejected*(self: NotificationsManager, title: string, message: string, + proc onMyRequestToJoinCommunityRejected*(self: NotificationsManager, title: string, message: string, sectionId: string) {.slot.} = - let details = NotificationDetails(notificationType: NotificationType.MyRequestToJoinCommunityRejected, + let details = NotificationDetails(notificationType: NotificationType.MyRequestToJoinCommunityRejected, sectionId: sectionId) self.processNotification(title, message, details) proc onMeMentionedIconBadgeNotification(self: NotificationsManager, allMentions: int) {.slot.} = self.osNotification.showIconBadgeNotification(allMentions) + proc onShowCommunityMemberKickedNotification*(self: NotificationsManager, title: string, message: string, sectionId: string) {.slot.} = + let details = NotificationDetails(notificationType: NotificationType.CommunityMemberKicked, sectionId: sectionId, isCommunitySection: true) + self.processNotification(title, message, details) + + proc onShowCommunityMemberBannedNotification*(self: NotificationsManager, title: string, message: string, sectionId: string) {.slot.} = + let details = NotificationDetails(notificationType: NotificationType.CommunityMemberBanned, sectionId: sectionId, isCommunitySection: true) + self.processNotification(title, message, details) + + proc onShowCommunityMemberUnbannedNotification*(self: NotificationsManager, title: string, message: string, sectionId: string) {.slot.} = + let details = NotificationDetails(notificationType: NotificationType.CommunityMemberUnbanned, sectionId: sectionId, isCommunitySection: true) + self.processNotification(title, message, details) + proc notificationCheck(self: NotificationsManager, title: string, message: string, details: NotificationDetails, notificationWay: string) = var data = NotificationArgs(title: title, message: message, details: details) @@ -213,32 +229,32 @@ QtObject: debug "Add AC notification", title=title, message=message self.events.emit(SIGNAL_ADD_NOTIFICATION_TO_ACTIVITY_CENTER, data) - # An exemption from the diagrams, at least for now, is that we don't need to implement the "Badge Check" block here, + # An exemption from the diagrams, at least for now, is that we don't need to implement the "Badge Check" block here, # cause that's already handled in appropriate modules. let appIsActive = app_isActive(singletonInstance.engine) - if details.notificationType == NotificationType.NewMessage or + if details.notificationType == NotificationType.NewMessage or details.notificationType == NotificationType.NewMessageWithPersonalMention or details.notificationType == NotificationType.NewMessageWithGlobalMention or - details.notificationType == NotificationType.NewContactRequest or + details.notificationType == NotificationType.NewContactRequest or details.notificationType == NotificationType.ContactRemoved or details.notificationType == NotificationType.IdentityVerificationRequest: if notificationWay == VALUE_NOTIF_DELIVER_QUIETLY: return - if (details.notificationType == NotificationType.NewMessage or + if (details.notificationType == NotificationType.NewMessage or details.notificationType == NotificationType.NewMessageWithPersonalMention or details.notificationType == NotificationType.NewMessageWithGlobalMention) and - details.sectionActive and + details.sectionActive and details.chatActive and appIsActive: return if appIsActive: debug "Add APP notification", title=title, message=message self.events.emit(SIGNAL_DISPLAY_APP_NOTIFICATION, data) - + if not appIsActive or details.notificationType == NotificationType.TestNotification: # Check anonymity level if(self.settingsService.getNotificationMessagePreview() == PREVIEW_ANONYMOUS): @@ -249,18 +265,18 @@ QtObject: let identifier = $(details.toJsonNode()) debug "Add OS notification", title=data.title, message=data.message, identifier=identifier - self.showOSNotification(data.title, data.message, identifier) - + self.showOSNotification(data.title, data.message, identifier) + if self.settingsService.getNotificationSoundsEnabled(): self.soundManager.setPlayerVolume(self.settingsService.getNotificationVolume()) self.soundManager.playSound(NOTIFICATION_SOUND) proc processNotification(self: NotificationsManager, title: string, message: string, details: NotificationDetails) = - ## This is the main method which need to be called to process an event according to the preferences set in the + ## This is the main method which need to be called to process an event according to the preferences set in the ## "Notifications & Sounds" panel of the "Settings" section. - ## + ## ## This method determines whether a notification need to be displayed or not, what notification to display, whether - ## to display App or OS notification and/or add notification to Activity Center, what level of anonymous to apply, + ## to display App or OS notification and/or add notification to Activity Center, what level of anonymous to apply, ## whether to play sound or not and so... # The flow used here follows these diagrams: @@ -274,7 +290,7 @@ QtObject: if(details.notificationType == NotificationType.NewContactRequest): if(self.settingsService.getNotifSettingContactRequests() != VALUE_NOTIF_TURN_OFF): self.notificationCheck(title, message, details, self.settingsService.getNotifSettingContactRequests()) - return + return # In case of identity verification request elif(details.notificationType == NotificationType.IdentityVerificationRequest): @@ -283,7 +299,7 @@ QtObject: return # In case of new message (regardless it's message with mention or not) - elif(details.notificationType == NotificationType.NewMessage or + elif(details.notificationType == NotificationType.NewMessage or details.notificationType == NotificationType.NewMessageWithPersonalMention or details.notificationType == NotificationType.NewMessageWithGlobalMention): if(self.settingsService.getNotifSettingAllMessages() != VALUE_NOTIF_TURN_OFF): @@ -296,17 +312,17 @@ QtObject: if(exemptions.muteAllMessages): return - if(details.notificationType == NotificationType.NewMessageWithPersonalMention and + if(details.notificationType == NotificationType.NewMessageWithPersonalMention and exemptions.personalMentions != VALUE_NOTIF_TURN_OFF): self.notificationCheck(title, message, details, exemptions.personalMentions) return - if(details.notificationType == NotificationType.NewMessageWithGlobalMention and + if(details.notificationType == NotificationType.NewMessageWithGlobalMention and exemptions.globalMentions != VALUE_NOTIF_TURN_OFF): self.notificationCheck(title, message, details, exemptions.globalMentions) return - if(details.notificationType == NotificationType.NewMessage and + if(details.notificationType == NotificationType.NewMessage and exemptions.otherMessages != VALUE_NOTIF_TURN_OFF): self.notificationCheck(title, message, details, exemptions.otherMessages) return @@ -320,16 +336,16 @@ QtObject: (details.isGroupChat and details.notificationType != NotificationType.NewMessageWithPersonalMention): return - if(details.notificationType == NotificationType.NewMessageWithPersonalMention and + if(details.notificationType == NotificationType.NewMessageWithPersonalMention and self.settingsService.getNotifSettingPersonalMentions() != VALUE_NOTIF_TURN_OFF): self.notificationCheck(title, message, details, self.settingsService.getNotifSettingPersonalMentions()) return - if(details.notificationType == NotificationType.NewMessageWithGlobalMention and + if(details.notificationType == NotificationType.NewMessageWithGlobalMention and self.settingsService.getNotifSettingGlobalMentions() != VALUE_NOTIF_TURN_OFF): self.notificationCheck(title, message, details, self.settingsService.getNotifSettingGlobalMentions()) return - + if(details.notificationType == NotificationType.NewMessage): if(details.isOneToOne and self.settingsService.getNotifSettingOneToOneChats() != VALUE_NOTIF_TURN_OFF): diff --git a/src/app/global/global_events.nim b/src/app/global/global_events.nim index c2d4126f0c..930de02a6a 100644 --- a/src/app/global/global_events.nim +++ b/src/app/global/global_events.nim @@ -14,7 +14,7 @@ QtObject: result.setup proc showTestNotification*(self: GlobalEvents, title: string, message: string) {.signal.} - + proc showCommunityTokenPermissionCreatedNotification*(self: GlobalEvents, sectionId: string, title: string, message: string) {.signal.} proc showCommunityTokenPermissionUpdatedNotification*(self: GlobalEvents, sectionId: string, title: string, message: string) {.signal.} @@ -27,10 +27,10 @@ QtObject: proc showCommunityTokenPermissionDeletionFailedNotification*(self: GlobalEvents, sectionId: string, title: string, message: string) {.signal.} - proc showMessageNotification*(self: GlobalEvents, title: string, message: string, sectionId: string, - isCommunitySection: bool, isSectionActive: bool, chatId: string, isChatActive: bool, messageId: string, + proc showMessageNotification*(self: GlobalEvents, title: string, message: string, sectionId: string, + isCommunitySection: bool, isSectionActive: bool, chatId: string, isChatActive: bool, messageId: string, notificationType: int, isOneToOne: bool, isGroupChat: bool) {.signal.} - + proc showNewContactRequestNotification*(self: GlobalEvents, title: string, message: string, sectionId: string) {.signal.} @@ -39,14 +39,20 @@ QtObject: proc showContactRemoved*(self: GlobalEvents, title: string, message: string, sectionId: string) {.signal.} - - proc newCommunityMembershipRequestNotification*(self: GlobalEvents, title: string, message: string, - sectionId: string) {.signal.} - - proc myRequestToJoinCommunityAcccepted*(self: GlobalEvents, title: string, message: string, + + proc newCommunityMembershipRequestNotification*(self: GlobalEvents, title: string, message: string, sectionId: string) {.signal.} - proc myRequestToJoinCommunityRejected*(self: GlobalEvents, title: string, message: string, + proc myRequestToJoinCommunityAcccepted*(self: GlobalEvents, title: string, message: string, + sectionId: string) {.signal.} + + proc myRequestToJoinCommunityRejected*(self: GlobalEvents, title: string, message: string, sectionId: string) {.signal.} proc meMentionedIconBadgeNotification*(self: GlobalEvents, allMentions: int) {.signal.} + + proc showCommunityMemberKickedNotification*(self: GlobalEvents, sectionId: string, title: string, message: string) {.signal.} + + proc showCommunityMemberBannedNotification*(self: GlobalEvents, sectionId: string, title: string, message: string) {.signal.} + + proc showCommunityMemberUnbannedNotification*(self: GlobalEvents, sectionId: string, title: string, message: string) {.signal.} \ No newline at end of file diff --git a/src/app/modules/main/ephemeral_notification_item.nim b/src/app/modules/main/ephemeral_notification_item.nim index ddba9e18d7..96f442c86a 100644 --- a/src/app/modules/main/ephemeral_notification_item.nim +++ b/src/app/modules/main/ephemeral_notification_item.nim @@ -6,6 +6,14 @@ type Success Danger +type EphemeralActionType* {.pure} = enum + None = 0 + NavigateToCommunityAdmin + OpenFinaliseOwnershipPopup + OpenSendModalPopup + ViewTransactionDetails + OpenFirstCommunityTokenPopup + type Item* = object id: int64 @@ -19,7 +27,7 @@ type loading: bool ephNotifType: EphemeralNotificationType url: string - actionType: int + actionType: EphemeralActionType actionData: string details: NotificationDetails @@ -33,7 +41,7 @@ proc initItem*(id: int64, loading = false, ephNotifType = EphemeralNotificationType.Default, url = "", - actionType = 0, # It means, no action enabled + actionType = EphemeralActionType.None, # It means, no action enabled actionData = "", details: NotificationDetails): Item = result = Item() @@ -63,7 +71,7 @@ proc title*(self: Item): string = proc durationInMs*(self: Item): int = self.durationInMs - + proc subTitle*(self: Item): string = self.subTitle @@ -85,7 +93,7 @@ proc ephNotifType*(self: Item): EphemeralNotificationType = proc url*(self: Item): string = self.url -proc actionType*(self: Item): int = +proc actionType*(self: Item): EphemeralActionType = self.actionType proc actionData*(self: Item): string = diff --git a/src/app/modules/main/ephemeral_notification_model.nim b/src/app/modules/main/ephemeral_notification_model.nim index 7b83b27599..84e1692539 100644 --- a/src/app/modules/main/ephemeral_notification_model.nim +++ b/src/app/modules/main/ephemeral_notification_model.nim @@ -84,7 +84,7 @@ QtObject: of ModelRole.Url: result = newQVariant(item.url) of ModelRole.ActionType: - result = newQVariant(item.actionType) + result = newQVariant(item.actionType.int) of ModelRole.ActionData: result = newQVariant(item.actionData) @@ -93,7 +93,7 @@ QtObject: if(self.items[i].id == id): return i return -1 - + proc getItemWithId*(self: Model, id: int64): Item = let ind = self.findIndexById(id) if(ind == -1): diff --git a/src/app/modules/main/module.nim b/src/app/modules/main/module.nim index a291ded1d6..6ae8e194be 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -822,7 +822,10 @@ method setActiveSection*[T](self: Module[T], item: SectionItem, skipSavingInSett method setActiveSectionById*[T](self: Module[T], id: string) = let item = self.view.model().getItemById(id) - self.setActiveSection(item) + if item.isEmpty(): + discard self.communitiesModule.spectateCommunity(id) + else: + self.setActiveSection(item) proc notifySubModulesAboutChange[T](self: Module[T], sectionId: string) = for cModule in self.channelGroupModules.values: @@ -1231,9 +1234,36 @@ method onAcceptRequestToJoinSuccess*[T](self: Module[T], communityId: string, me item.updatePendingRequestLoadingState(memberKey, false) method onMembershipStatusUpdated*[T](self: Module[T], communityId: string, memberPubkey: string, status: MembershipRequestState) = - let item = self.view.model().getItemById(communityId) - if item.id != "": - item.updateMembershipStatus(memberPubkey, status) + let myPublicKey = singletonInstance.userProfile.getPubKey() + let communityDto = self.controller.getCommunityById(communityId) + + if myPublicKey == memberPubkey: + case status: + of MembershipRequestState.Banned: + singletonInstance.globalEvents.showCommunityMemberBannedNotification(fmt "You've been banned from {communityDto.name}", "", communityId) + of MembershipRequestState.Kicked: + singletonInstance.globalEvents.showCommunityMemberKickedNotification(fmt "You were kicked from {communityDto.name}", "", communityId) + of MembershipRequestState.Unbanned: + singletonInstance.globalEvents.showCommunityMemberUnbannedNotification(fmt "You were unbanned from {communityDto.name}", "", communityId) + else: + discard + elif communityDto.isControlNode: + let (contactName, _, _) = self.controller.getContactNameAndImage(memberPubkey) + let item = self.view.model().getItemById(communityId) + if item.id != "": + item.updateMembershipStatus(memberPubkey, status) + + case status: + of MembershipRequestState.Banned: + self.displayEphemeralNotification(fmt "{contactName} was banned from {communityDto.name}", "" , "checkmark-circle", false, EphemeralNotificationType.Success.int, "") + + of MembershipRequestState.Kicked: + self.displayEphemeralNotification(fmt "{contactName} was kicked from {communityDto.name}", "" , "checkmark-circle", false, EphemeralNotificationType.Success.int, "") + + of MembershipRequestState.Unbanned: + self.displayEphemeralNotification(fmt "{contactName} unbanned from {communityDto.name}", "" , "checkmark-circle", false, EphemeralNotificationType.Success.int, "") + else: + discard method calculateProfileSectionHasNotification*[T](self: Module[T]): bool = return not self.controller.isMnemonicBackedUp() @@ -1280,7 +1310,7 @@ method displayEphemeralNotification*[T](self: Module[T], title: string, subTitle finalEphNotifType = EphemeralNotificationType.Danger let item = ephemeral_notification_item.initItem(id, title, TOAST_MESSAGE_VISIBILITY_DURATION_IN_MS, subTitle, "", icon, "", - loading, finalEphNotifType, url, 0, "", details) + loading, finalEphNotifType, url, EphemeralActionType.None, "", details) self.view.ephemeralNotificationModel().addItem(item) # TO UNIFY with the one above. @@ -1296,7 +1326,7 @@ method displayEphemeralWithActionNotification*[T](self: Module[T], title: string finalEphNotifType = EphemeralNotificationType.Danger let item = ephemeral_notification_item.initItem(id, title, TOAST_MESSAGE_VISIBILITY_DURATION_IN_MS, subTitle, "", icon, iconColor, - loading, finalEphNotifType, "", actionType, actionData, details) + loading, finalEphNotifType, "", EphemeralActionType(actionType), actionData, details) self.view.ephemeralNotificationModel().addItem(item) # TO UNIFY with the one above. @@ -1313,11 +1343,11 @@ method displayEphemeralImageWithActionNotification*[T](self: Module[T], title: s let item = ephemeral_notification_item.initItem(id, title, TOAST_MESSAGE_VISIBILITY_DURATION_IN_MS, subTitle, image, "", "", false, - finalEphNotifType, "", actionType, actionData, details) + finalEphNotifType, "", EphemeralActionType(actionType), actionData, details) self.view.ephemeralNotificationModel().addItem(item) method displayEphemeralNotification*[T](self: Module[T], title: string, subTitle: string, details: NotificationDetails) = - if(details.notificationType == NotificationType.NewMessage or + if details.notificationType == NotificationType.NewMessage or details.notificationType == NotificationType.NewMessageWithPersonalMention or details.notificationType == NotificationType.CommunityTokenPermissionCreated or details.notificationType == NotificationType.CommunityTokenPermissionUpdated or @@ -1325,17 +1355,26 @@ method displayEphemeralNotification*[T](self: Module[T], title: string, subTitle details.notificationType == NotificationType.CommunityTokenPermissionCreationFailed or details.notificationType == NotificationType.CommunityTokenPermissionUpdateFailed or details.notificationType == NotificationType.CommunityTokenPermissionDeletionFailed or - details.notificationType == NotificationType.NewMessageWithGlobalMention): + details.notificationType == NotificationType.NewMessageWithGlobalMention: self.displayEphemeralNotification(title, subTitle, "", false, EphemeralNotificationType.Default.int, "", details) - elif(details.notificationType == NotificationType.NewContactRequest or + elif details.notificationType == NotificationType.NewContactRequest or details.notificationType == NotificationType.IdentityVerificationRequest or - details.notificationType == NotificationType.ContactRemoved): + details.notificationType == NotificationType.ContactRemoved: self.displayEphemeralNotification(title, subTitle, "contact", false, EphemeralNotificationType.Default.int, "", details) - elif(details.notificationType == NotificationType.AcceptedContactRequest): + elif details.notificationType == NotificationType.AcceptedContactRequest: self.displayEphemeralNotification(title, subTitle, "checkmark-circle", false, EphemeralNotificationType.Success.int, "", details) + elif details.notificationType == NotificationType.CommunityMemberKicked: + self.displayEphemeralNotification(title, subTitle, "communities", false, EphemeralNotificationType.Danger.int, "", details) + + elif details.notificationType == NotificationType.CommunityMemberBanned: + self.displayEphemeralNotification(title, subTitle, "communities", false, EphemeralNotificationType.Danger.int, "", details) + + elif details.notificationType == NotificationType.CommunityMemberUnbanned: + self.displayEphemeralWithActionNotification(title, "Visit community" , "communities", "", false, EphemeralNotificationType.Success.int, EphemeralActionType.NavigateToCommunityAdmin.int, details.sectionId) + method removeEphemeralNotification*[T](self: Module[T], id: int64) = self.view.ephemeralNotificationModel().removeItemWithId(id) diff --git a/src/app_service/common/types.nim b/src/app_service/common/types.nim index e66ac48a7a..ad83b77418 100644 --- a/src/app_service/common/types.nim +++ b/src/app_service/common/types.nim @@ -71,6 +71,7 @@ type MembershipRequestState* {.pure} = enum UnbannedPending = 9, KickedPending = 10, AwaitingAddress = 11, + Unbanned = 12, type ContractTransactionStatus* {.pure.} = enum diff --git a/src/app_service/service/community/dto/community.nim b/src/app_service/service/community/dto/community.nim index 4dc3d870e9..56c20da6d4 100644 --- a/src/app_service/service/community/dto/community.nim +++ b/src/app_service/service/community/dto/community.nim @@ -41,6 +41,8 @@ type BanPending, UnbanPending, KickPending + Unbanned, + Kicked type CommunityMembershipRequestDto* = object id*: string @@ -479,7 +481,8 @@ proc toMembershipRequestState*(state: CommunityMemberPendingBanOrKick): Membersh return MembershipRequestState.UnbannedPending of CommunityMemberPendingBanOrKick.KickPending: return MembershipRequestState.KickedPending - return MembershipRequestState.None + else: + return MembershipRequestState.None proc toCommunitySettingsDto*(jsonObj: JsonNode): CommunitySettingsDto = result = CommunitySettingsDto() diff --git a/src/app_service/service/community/service.nim b/src/app_service/service/community/service.nim index 07404fa191..e0e11c811b 100644 --- a/src/app_service/service/community/service.nim +++ b/src/app_service/service/community/service.nim @@ -731,11 +731,31 @@ QtObject: self.events.emit(SIGNAL_COMMUNITY_JOINED, CommunityArgs(community: community, fromUserAction: false)) self.events.emit(SIGNAL_COMMUNITIES_UPDATE, CommunitiesArgs(communities: @[community])) + if wasJoined and not community.joined and not community.isMember: - if not community.spectated: - self.events.emit(SIGNAL_COMMUNITY_LEFT, CommunityIdArgs(communityId: community.id)) - else: + # If we were kicked due to ownership change - we will stay in a spectate mode + if community.spectated: self.events.emit(SIGNAL_COMMUNITY_KICKED, CommunityArgs(community: community)) + else: + # We were kicked or banned, leave the community + self.events.emit(SIGNAL_COMMUNITY_LEFT, CommunityIdArgs(communityId: community.id)) + var status = MembershipRequestState.Kicked + if community.pendingAndBannedMembers.hasKey(myPublicKey) and + community.pendingAndBannedMembers[myPublicKey] == CommunityMemberPendingBanOrKick.Banned: + status = MembershipRequestState.Banned + + self.events.emit(SIGNAL_COMMUNITY_MEMBER_STATUS_CHANGED, CommunityMemberStatusUpdatedArgs( + communityId: community.id, + memberPubkey: myPublicKey, + status: status)) + + # Check if we were unbanned + if not wasJoined and not community.joined and prevCommunity.pendingAndBannedMembers.hasKey(myPublicKey) and not + community.pendingAndBannedMembers.hasKey(myPublicKey): + self.events.emit(SIGNAL_COMMUNITY_MEMBER_STATUS_CHANGED, CommunityMemberStatusUpdatedArgs( + communityId: community.id, + memberPubkey: myPublicKey, + status: MembershipRequestState.Unbanned)) except Exception as e: error "Error handling community updates", msg = e.msg @@ -2133,10 +2153,13 @@ QtObject: var status: MembershipRequestState = MembershipRequestState.None if community.pendingAndBannedMembers.hasKey(memberPubkey): status = community.pendingAndBannedMembers[memberPubkey].toMembershipRequestState() - else: - for member in community.members: - if member.id == memberPubkey: - status = MembershipRequestState.Accepted + + if status == MembershipRequestState.None: + let prevCommunity = self.communities[community.id] + if prevCommunity.pendingAndBannedMembers.hasKey(memberPubkey): + status = MembershipRequestState.Unbanned + elif len(prevCommunity.members) > len(community.members): + status = MembershipRequestState.Kicked self.events.emit(SIGNAL_COMMUNITY_MEMBER_STATUS_CHANGED, CommunityMemberStatusUpdatedArgs( communityId: community.id, diff --git a/ui/imports/utils/Constants.qml b/ui/imports/utils/Constants.qml index 3fdf5e12f3..0844a69488 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -1229,7 +1229,8 @@ QtObject { BannedPending, UnbannedPending, KickedPending, - AwaitingAddress + AwaitingAddress, + Unbanned } readonly property QtObject walletAccountColors: QtObject {