From 789a01cf09e557d45a95b10952ef2cc042293284 Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Tue, 22 Aug 2023 14:04:58 -0400 Subject: [PATCH] feat(shared-addresses): add loading state for checking permissions Fixes #11893 --- .../modules/main/communities/controller.nim | 8 +++++ .../modules/main/communities/io_interface.nim | 6 ++++ src/app/modules/main/communities/module.nim | 25 +++++++++++++ src/app/modules/main/communities/view.nim | 35 +++++++++++++------ ui/app/AppLayouts/Chat/ChatLayout.qml | 1 + ui/app/AppLayouts/Chat/stores/RootStore.qml | 2 ++ .../panels/JoinPermissionsOverlayPanel.qml | 19 ++-------- .../panels/RequirementsCheckPendingLoader.qml | 22 ++++++++++++ .../panels/SharedAddressesPanel.qml | 12 +++++++ ui/app/AppLayouts/Communities/panels/qmldir | 1 + .../popups/SharedAddressesPopup.qml | 3 ++ .../Communities/stores/CommunitiesStore.qml | 1 + .../Communities/views/CommunityColumnView.qml | 1 + .../Profile/views/CommunitiesView.qml | 1 + ui/app/AppLayouts/stores/RootStore.qml | 1 + ui/app/mainui/Popups.qml | 2 ++ .../shared/popups/CommunityIntroDialog.qml | 3 ++ 17 files changed, 116 insertions(+), 27 deletions(-) create mode 100644 ui/app/AppLayouts/Communities/panels/RequirementsCheckPendingLoader.qml diff --git a/src/app/modules/main/communities/controller.nim b/src/app/modules/main/communities/controller.nim index 242ef193ba..91ae8cd947 100644 --- a/src/app/modules/main/communities/controller.nim +++ b/src/app/modules/main/communities/controller.nim @@ -198,6 +198,14 @@ proc init*(self: Controller) = self.delegate.callbackFromAuthentication(authenticated) self.tmpAuthenticationWithCallbackInProgress = false + self.events.on(SIGNAL_CHECK_PERMISSIONS_TO_JOIN_FAILED) do(e: Args): + let args = CheckPermissionsToJoinFailedArgs(e) + self.delegate.onCommunityCheckPermissionsToJoinFailed(args.communityId, args.error) + + self.events.on(SIGNAL_CHECK_ALL_CHANNELS_PERMISSIONS_FAILED) do(e: Args): + let args = CheckChannelsPermissionsErrorArgs(e) + self.delegate.onCommunityCheckAllChannelPermissionsFailed(args.communityId, args.error) + proc getCommunityTags*(self: Controller): string = result = self.communityService.getCommunityTags() diff --git a/src/app/modules/main/communities/io_interface.nim b/src/app/modules/main/communities/io_interface.nim index 6747d8547c..2ca04a06ee 100644 --- a/src/app/modules/main/communities/io_interface.nim +++ b/src/app/modules/main/communities/io_interface.nim @@ -217,3 +217,9 @@ method onCommunityCheckPermissionsToJoinResponse*(self: AccessInterface, communi method onCommunityCheckAllChannelsPermissionsResponse*(self: AccessInterface, communityId: string, checkChannelPermissionsResponse: CheckAllChannelsPermissionsResponseDto) {.base.} = raise newException(ValueError, "No implementation available") + +method onCommunityCheckPermissionsToJoinFailed*(self: AccessInterface, communityId: string, ValueErrorerror: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method onCommunityCheckAllChannelPermissionsFailed*(self: AccessInterface, communityId: string, ValueErrorerror: string) {.base.} = + raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/main/communities/module.nim b/src/app/modules/main/communities/module.nim index cdd9c25731..d8526a96cb 100644 --- a/src/app/modules/main/communities/module.nim +++ b/src/app/modules/main/communities/module.nim @@ -44,6 +44,8 @@ type moduleLoaded: bool curatedCommunitiesLoaded: bool communityTokensModule: community_tokens_module.AccessInterface + checkingPermissionToJoinInProgress: bool + checkingAllChannelPermissionsInProgress: bool # Forward declaration method setCommunityTags*(self: Module, communityTags: string) @@ -79,6 +81,8 @@ proc newModule*( result.communityTokensModule = community_tokens_module.newCommunityTokensModule(result, events, communityTokensService, transactionService, networksService, communityService) result.moduleLoaded = false result.curatedCommunitiesLoaded = false + result.checkingPermissionToJoinInProgress = false + result.checkingAllChannelPermissionsInProgress = false method delete*(self: Module) = self.view.delete @@ -541,6 +545,7 @@ method getCommunityPublicKeyFromPrivateKey*(self: Module, communityPrivateKey: s method checkPermissions*(self: Module, communityId: string, sharedAddresses: seq[string]) = self.controller.asyncCheckPermissionsToJoin(communityId, sharedAddresses) self.controller.asyncCheckAllChannelsPermissions(communityId, sharedAddresses) + self.view.setCheckingPermissionsInProgress(inProgress = true) method prepareTokenModelForCommunity*(self: Module, communityId: string) = let community = self.controller.getCommunityById(communityId) @@ -595,12 +600,32 @@ proc applyPermissionResponse*(self: Module, communityId: string, permissions: Ta ) self.view.spectatedCommunityPermissionModel.updateItem(id, updatedTokenPermissionItem) +proc updateCheckingPermissionsInProgressIfNeeded(self: Module, inProgress = false) = + if self.checkingPermissionToJoinInProgress != self.checkingAllChannelPermissionsInProgress: + # Wait until both join and channel permissions have returned to update the loading + return + self.view.setCheckingPermissionsInProgress(inProgress) + +method onCommunityCheckPermissionsToJoinFailed*(self: Module, communityId: string, error: string) = + # TODO show error + self.checkingPermissionToJoinInProgress = false + self.updateCheckingPermissionsInProgressIfNeeded(inProgress = false) + +method onCommunityCheckAllChannelPermissionsFailed*(self: Module, communityId: string, error: string) = + # TODO show error + self.checkingAllChannelPermissionsInProgress = false + self.updateCheckingPermissionsInProgressIfNeeded(inProgress = false) + method onCommunityCheckPermissionsToJoinResponse*(self: Module, communityId: string, checkPermissionsToJoinResponse: CheckPermissionsToJoinResponseDto) = self.applyPermissionResponse(communityId, checkPermissionsToJoinResponse.permissions) + self.checkingPermissionToJoinInProgress = false + self.updateCheckingPermissionsInProgressIfNeeded(inProgress = false) method onCommunityCheckAllChannelsPermissionsResponse*(self: Module, communityId: string, checkChannelPermissionsResponse: CheckAllChannelsPermissionsResponseDto) = + self.checkingAllChannelPermissionsInProgress = false + self.updateCheckingPermissionsInProgressIfNeeded(inProgress = false) for _, channelPermissionResponse in checkChannelPermissionsResponse.channels: self.applyPermissionResponse( communityId, diff --git a/src/app/modules/main/communities/view.nim b/src/app/modules/main/communities/view.nim index f8a7d3429a..b3fc7d1831 100644 --- a/src/app/modules/main/communities/view.nim +++ b/src/app/modules/main/communities/view.nim @@ -50,6 +50,7 @@ QtObject: discordImportCommunityImage: string discordImportHasCommunityImage: bool downloadingCommunityHistoryArchives: bool + checkingPermissionsInProgress: bool proc delete*(self: View) = self.model.delete @@ -142,7 +143,7 @@ QtObject: proc downloadingCommunityHistoryArchivesChanged*(self: View) {.signal.} - proc setDownloadingCommunityHistoryArchives*(self: View, flag: bool) {.slot.} = + proc setDownloadingCommunityHistoryArchives*(self: View, flag: bool) = if (self.downloadingCommunityHistoryArchives == flag): return self.downloadingCommunityHistoryArchives = flag self.downloadingCommunityHistoryArchivesChanged() @@ -156,7 +157,7 @@ QtObject: proc discordImportHasCommunityImageChanged*(self: View) {.signal.} - proc setDiscordImportHasCommunityImage*(self: View, hasImage: bool) {.slot.} = + proc setDiscordImportHasCommunityImage*(self: View, hasImage: bool) = if (self.discordImportHasCommunityImage == hasImage): return self.discordImportHasCommunityImage = hasImage self.discordImportHasCommunityImageChanged() @@ -170,7 +171,7 @@ QtObject: proc discordImportWarningsCountChanged*(self: View) {.signal.} - proc setDiscordImportWarningsCount*(self: View, count: int) {.slot.} = + proc setDiscordImportWarningsCount*(self: View, count: int) = if (self.discordImportWarningsCount == count): return self.discordImportWarningsCount = count self.discordImportWarningsCountChanged() @@ -182,7 +183,7 @@ QtObject: read = getDiscordImportWarningsCount notify = discordImportWarningsCountChanged - proc setDiscordImportErrorsCount*(self: View, count: int) {.slot.} = + proc setDiscordImportErrorsCount*(self: View, count: int) = if (self.discordImportErrorsCount == count): return self.discordImportErrorsCount = count self.discordImportErrorsCountChanged() @@ -196,7 +197,7 @@ QtObject: proc discordImportProgressChanged*(self: View) {.signal.} - proc setDiscordImportProgress*(self: View, value: int) {.slot.} = + proc setDiscordImportProgress*(self: View, value: int) = if (self.discordImportProgress == value): return self.discordImportProgress = value self.discordImportProgressChanged() @@ -210,7 +211,7 @@ QtObject: proc discordImportInProgressChanged*(self: View) {.signal.} - proc setDiscordImportInProgress*(self: View, value: bool) {.slot.} = + proc setDiscordImportInProgress*(self: View, value: bool) = if (self.discordImportInProgress == value): return self.discordImportInProgress = value self.discordImportInProgressChanged() @@ -224,7 +225,7 @@ QtObject: proc discordImportCancelledChanged*(self: View) {.signal.} - proc setDiscordImportCancelled*(self: View, value: bool) {.slot.} = + proc setDiscordImportCancelled*(self: View, value: bool) = if (self.discordImportCancelled == value): return self.discordImportCancelled = value self.discordImportCancelledChanged() @@ -238,7 +239,7 @@ QtObject: proc discordImportProgressStoppedChanged*(self: View) {.signal.} - proc setDiscordImportProgressStopped*(self: View, stopped: bool) {.slot.} = + proc setDiscordImportProgressStopped*(self: View, stopped: bool) = if (self.discordImportProgressStopped == stopped): return self.discordImportProgressStopped = stopped self.discordImportProgressStoppedChanged() @@ -252,7 +253,7 @@ QtObject: proc discordImportProgressTotalChunksCountChanged*(self: View) {.signal.} - proc setDiscordImportProgressTotalChunksCount*(self: View, count: int) {.slot.} = + proc setDiscordImportProgressTotalChunksCount*(self: View, count: int) = if (self.discordImportProgressTotalChunksCount == count): return self.discordImportProgressTotalChunksCount = count self.discordImportProgressTotalChunksCountChanged() @@ -266,7 +267,7 @@ QtObject: proc discordImportProgressCurrentChunkChanged*(self: View) {.signal.} - proc setDiscordImportProgressCurrentChunk*(self: View, count: int) {.slot.} = + proc setDiscordImportProgressCurrentChunk*(self: View, count: int) = if (self.discordImportProgressCurrentChunk == count): return self.discordImportProgressCurrentChunk = count self.discordImportProgressCurrentChunkChanged() @@ -669,3 +670,17 @@ QtObject: proc getCommunityPublicKeyFromPrivateKey*(self: View, communityPrivateKey: string): string {.slot.} = result = self.delegate.getCommunityPublicKeyFromPrivateKey(communityPrivateKey) + + proc checkingPermissionsInProgressChanged*(self: View) {.signal.} + + proc setCheckingPermissionsInProgress*(self: View, inProgress: bool) = + if (self.checkingPermissionsInProgress == inProgress): return + self.checkingPermissionsInProgress = inProgress + self.checkingPermissionsInProgressChanged() + + proc getCheckingPermissionsInProgress*(self: View): bool {.slot.} = + return self.checkingPermissionsInProgress + + QtProperty[bool] requirementsCheckPending: + read = getCheckingPermissionsInProgress + notify = checkingPermissionsInProgressChanged \ No newline at end of file diff --git a/ui/app/AppLayouts/Chat/ChatLayout.qml b/ui/app/AppLayouts/Chat/ChatLayout.qml index f20d679608..5624ff0fe3 100644 --- a/ui/app/AppLayouts/Chat/ChatLayout.qml +++ b/ui/app/AppLayouts/Chat/ChatLayout.qml @@ -192,6 +192,7 @@ StackLayout { loginType: root.rootStore.loginType walletAccountsModel: WalletStore.RootStore.nonWatchAccounts + requirementsCheckPending: root.rootStore.requirementsCheckPending permissionsModel: { root.rootStore.prepareTokenModelForCommunity(communityIntroDialog.communityId) return root.rootStore.permissionsModel diff --git a/ui/app/AppLayouts/Chat/stores/RootStore.qml b/ui/app/AppLayouts/Chat/stores/RootStore.qml index 07b1cc809b..91f32c69ec 100644 --- a/ui/app/AppLayouts/Chat/stores/RootStore.qml +++ b/ui/app/AppLayouts/Chat/stores/RootStore.qml @@ -60,6 +60,8 @@ QtObject { root.communitiesModuleInst.prepareTokenModelForCommunity(publicKey) } + readonly property bool requirementsCheckPending: root.communitiesModuleInst.requirementsCheckPending + readonly property var permissionsModel: !!root.communitiesModuleInst.spectatedCommunityPermissionModel ? root.communitiesModuleInst.spectatedCommunityPermissionModel : null diff --git a/ui/app/AppLayouts/Communities/panels/JoinPermissionsOverlayPanel.qml b/ui/app/AppLayouts/Communities/panels/JoinPermissionsOverlayPanel.qml index b2e036030a..529ff33952 100644 --- a/ui/app/AppLayouts/Communities/panels/JoinPermissionsOverlayPanel.qml +++ b/ui/app/AppLayouts/Communities/panels/JoinPermissionsOverlayPanel.qml @@ -169,24 +169,9 @@ Control { color: Theme.palette.dangerColor1 } - StatusBaseText { - id: requirementsCheckPendingText - - Layout.alignment: Qt.AlignHCenter + RequirementsCheckPendingLoader { visible: root.requirementsCheckPending - - text: qsTr("Requirements check pending...") - - color: Theme.palette.dangerColor1 - - SequentialAnimation { - id: blinkingAnimation - - loops: Animation.Infinite - running: requirementsCheckPendingText.visible - NumberAnimation { target: requirementsCheckPendingText; property: "opacity"; to: 0; duration: 1500;} - NumberAnimation { target: requirementsCheckPendingText; property: "opacity"; to: 1; duration: 1500;} - } + Layout.alignment: Qt.AlignHCenter } } } diff --git a/ui/app/AppLayouts/Communities/panels/RequirementsCheckPendingLoader.qml b/ui/app/AppLayouts/Communities/panels/RequirementsCheckPendingLoader.qml new file mode 100644 index 0000000000..b5d1dbe8ea --- /dev/null +++ b/ui/app/AppLayouts/Communities/panels/RequirementsCheckPendingLoader.qml @@ -0,0 +1,22 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.15 + +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 + +StatusBaseText { + id: root + + text: qsTr("Requirements check pending...") + + color: Theme.palette.dangerColor1 + + SequentialAnimation { + id: blinkingAnimation + + loops: Animation.Infinite + running: root.visible + NumberAnimation { target: root; property: "opacity"; to: 0; duration: 1500;} + NumberAnimation { target: root; property: "opacity"; to: 1; duration: 1500;} + } +} diff --git a/ui/app/AppLayouts/Communities/panels/SharedAddressesPanel.qml b/ui/app/AppLayouts/Communities/panels/SharedAddressesPanel.qml index a6997c763f..519814fb67 100644 --- a/ui/app/AppLayouts/Communities/panels/SharedAddressesPanel.qml +++ b/ui/app/AppLayouts/Communities/panels/SharedAddressesPanel.qml @@ -26,6 +26,8 @@ Control { property bool isEditMode + property bool requirementsCheckPending: false + required property string communityName required property string communityIcon property int loginType: Constants.LoginType.Password @@ -174,6 +176,16 @@ Control { } } + RequirementsCheckPendingLoader { + visible: root.requirementsCheckPending + Layout.alignment: Qt.AlignHCenter + } + + Item { + Layout.fillWidth: true + Layout.preferredHeight: Style.current.padding + } + // divider with top rounded corners + drop shadow Rectangle { Layout.fillWidth: true diff --git a/ui/app/AppLayouts/Communities/panels/qmldir b/ui/app/AppLayouts/Communities/panels/qmldir index 5d795481f4..656c1ba2d7 100644 --- a/ui/app/AppLayouts/Communities/panels/qmldir +++ b/ui/app/AppLayouts/Communities/panels/qmldir @@ -27,6 +27,7 @@ PrivilegedTokenArtworkPanel 1.0 PrivilegedTokenArtworkPanel.qml ProfilePopupInviteFriendsPanel 1.0 ProfilePopupInviteFriendsPanel.qml ProfilePopupInviteMessagePanel 1.0 ProfilePopupInviteMessagePanel.qml ProfilePopupOverviewPanel 1.0 ProfilePopupOverviewPanel.qml +RequirementsCheckPendingLoader 1.0 RequirementsCheckPendingLoader.qml SharedAddressesPanel 1.0 SharedAddressesPanel.qml SortableTokenHoldersList 1.0 SortableTokenHoldersList.qml SortableTokenHoldersPanel 1.0 SortableTokenHoldersPanel.qml diff --git a/ui/app/AppLayouts/Communities/popups/SharedAddressesPopup.qml b/ui/app/AppLayouts/Communities/popups/SharedAddressesPopup.qml index ab18b83304..171a2a9b81 100644 --- a/ui/app/AppLayouts/Communities/popups/SharedAddressesPopup.qml +++ b/ui/app/AppLayouts/Communities/popups/SharedAddressesPopup.qml @@ -11,6 +11,8 @@ StatusDialog { property bool isEditMode + property bool requirementsCheckPending + required property string communityName required property string communityIcon property int loginType: Constants.LoginType.Password @@ -34,6 +36,7 @@ StatusDialog { contentItem: SharedAddressesPanel { id: panel isEditMode: root.isEditMode + requirementsCheckPending: root.requirementsCheckPending communityName: root.communityName communityIcon: root.communityIcon loginType: root.loginType diff --git a/ui/app/AppLayouts/Communities/stores/CommunitiesStore.qml b/ui/app/AppLayouts/Communities/stores/CommunitiesStore.qml index ea103642ab..91a0fef04a 100644 --- a/ui/app/AppLayouts/Communities/stores/CommunitiesStore.qml +++ b/ui/app/AppLayouts/Communities/stores/CommunitiesStore.qml @@ -110,6 +110,7 @@ QtObject { } property var communitiesList: communitiesModuleInst.model + readonly property bool requirementsCheckPending: communitiesModuleInst.requirementsCheckPending function spectateCommunity(publicKey) { root.communitiesModuleInst.spectateCommunity(publicKey, ""); diff --git a/ui/app/AppLayouts/Communities/views/CommunityColumnView.qml b/ui/app/AppLayouts/Communities/views/CommunityColumnView.qml index 830a90e87f..ab1217ab02 100644 --- a/ui/app/AppLayouts/Communities/views/CommunityColumnView.qml +++ b/ui/app/AppLayouts/Communities/views/CommunityColumnView.qml @@ -120,6 +120,7 @@ Item { CommunityIntroDialog { isInvitationPending: joinCommunityButton.invitationPending + requirementsCheckPending: root.store.requirementsCheckPending name: communityData.name introMessage: communityData.introMessage imageSrc: communityData.image diff --git a/ui/app/AppLayouts/Profile/views/CommunitiesView.qml b/ui/app/AppLayouts/Profile/views/CommunitiesView.qml index fbe69ddfbc..c1337ade76 100644 --- a/ui/app/AppLayouts/Profile/views/CommunitiesView.qml +++ b/ui/app/AppLayouts/Profile/views/CommunitiesView.qml @@ -234,6 +234,7 @@ SettingsContentBase { loginType: chatStore.loginType walletAccountsModel: WalletStore.RootStore.nonWatchAccounts + requirementsCheckPending: root.rootStore.requirementsCheckPending permissionsModel: { root.rootStore.prepareTokenModelForCommunity(communityIntroDialog.communityId) return root.rootStore.permissionsModel diff --git a/ui/app/AppLayouts/stores/RootStore.qml b/ui/app/AppLayouts/stores/RootStore.qml index 40eb090f18..19d65ee519 100644 --- a/ui/app/AppLayouts/stores/RootStore.qml +++ b/ui/app/AppLayouts/stores/RootStore.qml @@ -15,6 +15,7 @@ QtObject { property var aboutModuleInst: aboutModule property var communitiesModuleInst: communitiesModule property bool newVersionAvailable: false + readonly property bool requirementsCheckPending: communitiesModuleInst.requirementsCheckPending property string latestVersion property string downloadURL diff --git a/ui/app/mainui/Popups.qml b/ui/app/mainui/Popups.qml index 0711817a58..3a6fbee70d 100644 --- a/ui/app/mainui/Popups.qml +++ b/ui/app/mainui/Popups.qml @@ -523,6 +523,7 @@ QtObject { id: communityIntroDialog property string communityId loginType: root.rootStore.loginType + requirementsCheckPending: root.rootStore.requirementsCheckPending walletAccountsModel: root.rootStore.walletAccountsModel permissionsModel: { root.rootStore.prepareTokenModelForCommunity(communityIntroDialog.communityId) @@ -708,6 +709,7 @@ QtObject { communityName: chatStore.sectionDetails.name communityIcon: chatStore.sectionDetails.image + requirementsCheckPending: root.rootStore.requirementsCheckPending // FIXME get these from the community settings (from the initial "join" call) //selectedSharedAddresses: [???] //selectedAirdropAddress: "???" diff --git a/ui/imports/shared/popups/CommunityIntroDialog.qml b/ui/imports/shared/popups/CommunityIntroDialog.qml index 6f39a4e8d8..3a052dbc15 100644 --- a/ui/imports/shared/popups/CommunityIntroDialog.qml +++ b/ui/imports/shared/popups/CommunityIntroDialog.qml @@ -30,6 +30,8 @@ StatusStackModal { required property var assetsModel required property var collectiblesModel + required property bool requirementsCheckPending + signal joined(string airdropAddress, var sharedAddresses) signal cancelMembershipRequest() signal sharedAddressesUpdated(var sharedAddresses) @@ -102,6 +104,7 @@ StatusStackModal { communityName: root.name communityIcon: root.imageSrc loginType: root.loginType + requirementsCheckPending: root.requirementsCheckPending walletAccountsModel: SortFilterProxyModel { sourceModel: root.walletAccountsModel sorters: [