diff --git a/storybook/PagesModel.qml b/storybook/PagesModel.qml index 63a5c9d6e2..59639a316b 100644 --- a/storybook/PagesModel.qml +++ b/storybook/PagesModel.qml @@ -181,6 +181,10 @@ ListModel { title: "EditSettingsPanel" section: "Panels" } + ListElement { + title: "OverviewSettingsFooter" + section: "Panels" + } ListElement { title: "BurnTokensPopup" section: "Popups" diff --git a/storybook/figma.json b/storybook/figma.json index 492615c040..514ed0fd1f 100644 --- a/storybook/figma.json +++ b/storybook/figma.json @@ -227,5 +227,8 @@ ], "EditSettingsPanel": [ "https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba⎜Desktop?node-id=3132%3A383870&mode=dev" + ], + "OverviewSettingsFooter": [ + "https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba⎜Desktop?type=design&node-id=31171-629792&mode=design&t=IAlt2Frp5gx0yPAn-0" ] } diff --git a/storybook/pages/OverviewSettingsFooterPage.qml b/storybook/pages/OverviewSettingsFooterPage.qml new file mode 100644 index 0000000000..6d5bc5da31 --- /dev/null +++ b/storybook/pages/OverviewSettingsFooterPage.qml @@ -0,0 +1,58 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +import AppLayouts.Communities.panels 1.0 + +import utils 1.0 + +SplitView { + id: root + + Item { + id: wrapper + SplitView.fillWidth: true + SplitView.fillHeight: true + OverviewSettingsFooter { + id: footer + width: parent.width + anchors.centerIn: parent + isControlNode: controlNodeSwitch.checked + communityName: "Socks" + } + } + + Pane { + SplitView.preferredWidth: 300 + SplitView.fillHeight: true + + ColumnLayout { + Switch { + id: controlNodeSwitch + text: "Control node on/off" + checked: true + } + + ColumnLayout { + Label { + Layout.fillWidth: true + text: "Login type::" + } + + RadioButton { + checked: true + text: qsTr("Password") + onCheckedChanged: if(checked) footer.loginType = Constants.LoginType.Password + } + RadioButton { + text: qsTr("Biometrics") + onCheckedChanged: if(checked) footer.loginType = Constants.LoginType.Biometrics + } + RadioButton { + text: qsTr("Keycard") + onCheckedChanged: if(checked) footer.loginType = Constants.LoginType.Keycard + } + } + } + } +} diff --git a/test/ui-test/src/screens/StatusCommunityScreen.py b/test/ui-test/src/screens/StatusCommunityScreen.py index 122c3cfb9d..dfa5fa14bb 100644 --- a/test/ui-test/src/screens/StatusCommunityScreen.py +++ b/test/ui-test/src/screens/StatusCommunityScreen.py @@ -111,14 +111,7 @@ class CreateOrEditCommunityCategoryPopup(Enum): COMMUNITY_CATEGORY_LIST: str = "createOrEditCommunityCategoryChannelList_ListView" COMMUNITY_CATEGORY_LIST_ITEM_PLACEHOLDER: str = "createOrEditCommunityCategoryChannelList_ListItem_Placeholder" COMMUNITY_CATEGORY_BUTTON: str = "createOrEditCommunityCategoryBtn_StatusButton" - MODAL_CLOSE_BUTTON = "modal_Close_Button" - -class CommunityOverviewScreenComponents(Enum): - # Constants definitions: - COMMUNITY_PRIVATE_KEY_LENGHT_UI = 35 # length of community PK on the Transfer Ownership popup - # Components: - COMMUNITY_OVERVIEW_BACK_UP_BUTTON ="communityOverview_Back_up_StatusButton" - COMMUNITY_OVERVIEW_AIRDROP_TOKENS_BUTTON="communityOverview_Airdrop_Tokens_StatusButton" + MODAL_CLOSE_BUTTON = "modal_Close_Button" class StatusCommunityScreen: @@ -575,18 +568,4 @@ class StatusCommunityScreen: assert BaseElement(str(CommunityWelcomeScreenComponents.ADD_NEW_ITEM_BUTTON.value)).is_enabled button_title = get_obj(CommunityWelcomeScreenComponents.ADD_NEW_ITEM_BUTTON.value).text verify_equals(option, str(button_title)) - - def verify_community_private_key(self): - Button(CommunityOverviewScreenComponents.COMMUNITY_OVERVIEW_BACK_UP_BUTTON.value).click() - transferOwnershipPopup = BackUpCommunityPrivateKeyPopup().wait_until_appears() - transferOwnershipPopup.copy_community_private_key() - community_private_key = transferOwnershipPopup.private_key - assert len(community_private_key) == (CommunityOverviewScreenComponents.COMMUNITY_PRIVATE_KEY_LENGHT_UI.value), f"Current key length: {len(community_private_key)}" - assert community_private_key.startswith("0x"), f"Current private key does not start with 0x: {community_private_key}" - - def open_airdrops_from_overview(self): - Button(CommunityOverviewScreenComponents.COMMUNITY_OVERVIEW_AIRDROP_TOKENS_BUTTON.value).click() - welcome_screen_title = get_obj(CommunityWelcomeScreenComponents.WELCOME_SCREEN_TITLE.value).text - ref_value = CommunityWelcomeScreenComponents.WELCOME_SCREEN_TITLE_OPTION_AIRDROPS.value - assert welcome_screen_title == ref_value, f"Current screen title: {welcome_screen_title}, expected: {ref_value}" \ No newline at end of file diff --git a/test/ui-test/testSuites/suite_communities/shared/scripts/community_names.py b/test/ui-test/testSuites/suite_communities/shared/scripts/community_names.py index d339a463af..eb6195eff3 100644 --- a/test/ui-test/testSuites/suite_communities/shared/scripts/community_names.py +++ b/test/ui-test/testSuites/suite_communities/shared/scripts/community_names.py @@ -85,13 +85,6 @@ communitySettings_EditCommunity_ColorPicker_Button = {"container": communitySett communitySettings_ColorPanel_HexColor_Input = {"container": statusDesktop_mainWindow_overlay, "objectName": "communityColorPanelHexInput", "type": "TextEdit", "visible": True} communitySettings_SaveColor_Button = {"container": statusDesktop_mainWindow_overlay, "objectName": "communityColorPanelSelectColorButton", "type": "StatusButton", "visible": True} -# Community Overview -communityOverview_Back_up_Banner = {"container": statusDesktop_mainWindow, "objectName": "backUpBanner", "type": "BannerPanel", "visible": True} -communityOverview_Back_up_StatusButton = {"container": communityOverview_Back_up_Banner, "objectName": "communityBannerButton", "type": "StatusButton", "visible": True} -communityOverview_Airdrop_Tokens_Banner = {"container": statusDesktop_mainWindow, "objectName": "airdropBanner", "type": "BannerPanel", "visible": True} -communityOverview_Airdrop_Tokens_StatusButton = {"container": communityOverview_Airdrop_Tokens_Banner, "objectName": "communityBannerButton", "type": "StatusButton", "visible": True} -transferOwnerShipTextEdit = {"container": statusDesktop_mainWindow_overlay, "id": "edit", "type": "TextEdit", "unnamed": 1, "visible": True} - # Community transfer ownership copyCommunityPrivateKeyButton = {"container": statusDesktop_mainWindow, "objectName": "copyCommunityPrivateKeyButton", "type": "StatusButton", "visible": True} diff --git a/test/ui-test/testSuites/suite_communities/shared/steps/communitySteps.py b/test/ui-test/testSuites/suite_communities/shared/steps/communitySteps.py index 603828a56a..8a8e88c8a5 100644 --- a/test/ui-test/testSuites/suite_communities/shared/steps/communitySteps.py +++ b/test/ui-test/testSuites/suite_communities/shared/steps/communitySteps.py @@ -267,14 +267,6 @@ def step(context, option:str, list): @Then("\"|any|\" button is present") def step (context, action_button_name): _statusCommunityScreen.verify_action_button_enabled(action_button_name) - -@Then("the user is able to open Back up modal and copy private key") -def step(context): - _statusCommunityScreen.verify_community_private_key() - -@Then("the user is able to click Airdrop Tokens button and navigate to Airdrops screen") -def step(context): - _statusCommunityScreen.open_airdrops_from_overview() ########################################################################### ### COMMON methods used in different steps given/when/then region: diff --git a/test/ui-test/testSuites/suite_communities/tst_communityManageOverview/test.feature b/test/ui-test/testSuites/suite_communities/tst_communityManageOverview/test.feature index 91b58d8f77..c078dbed11 100644 --- a/test/ui-test/testSuites/suite_communities/tst_communityManageOverview/test.feature +++ b/test/ui-test/testSuites/suite_communities/tst_communityManageOverview/test.feature @@ -4,33 +4,22 @@ Feature: Community -> Manage Community -> Overview page -Background: - Given A first time user lands on the status desktop and generates new key - And the user signs up with username "tester123" and password "TesTEr16843/!@00" - And the user lands on the signed in app - And the user opens the community portal section - And the user lands on the community portal section - And the user creates a community named "Test-Community", with description "My community description", intro "Community Intro" and outro "Community Outro" - And the user lands on the community named "Test-Community" + Background: + Given A first time user lands on the status desktop and generates new key + And the user signs up with username "tester123" and password "TesTEr16843/!@00" + And the user lands on the signed in app + And the user opens the community portal section + And the user lands on the community portal section + And the user creates a community named "Test-Community", with description "My community description", intro "Community Intro" and outro "Community Outro" + And the user lands on the community named "Test-Community" -Scenario: Community admin is able to back up community key from community overview page - When "Manage Community" is clicked in the community sidebar - And "Overview" section is selected - Then the user is able to open Back up modal and copy private key - -Scenario: Community admin is able to navigate to Airdrops page from Overview screen - When "Manage Community" is clicked in the community sidebar - And "Overview" section is selected - Then the user is able to click Airdrop Tokens button and navigate to Airdrops screen - - - Scenario Outline: Manage community -> Overview: community admin edits the community name, description and color - When the admin renames the community to "" and description to "" and color to "" - Then the community overview name is "" - And the community overview description is "" - And the community overview color is "" - When the admin goes back to the community - Then the user lands on the community named "" - Examples: - | new_community_name | new_community_description | new_community_color | - | myCommunityNamedChanged | Cool new description 123 | #ff0000 | \ No newline at end of file + Scenario Outline: Manage community -> Overview: community admin edits the community name, description and color + When the admin renames the community to "" and description to "" and color to "" + Then the community overview name is "" + And the community overview description is "" + And the community overview color is "" + When the admin goes back to the community + Then the user lands on the community named "" + Examples: + | new_community_name | new_community_description | new_community_color | + | myCommunityNamedChanged | Cool new description 123 | #ff0000 | \ No newline at end of file diff --git a/ui/StatusQ/src/assets.qrc b/ui/StatusQ/src/assets.qrc index 20ad760c20..6fd71584c2 100644 --- a/ui/StatusQ/src/assets.qrc +++ b/ui/StatusQ/src/assets.qrc @@ -185,6 +185,7 @@ assets/img/icons/ETH.png assets/img/icons/exchange.svg assets/img/icons/external.svg + assets/img/icons/external-link.svg assets/img/icons/face-id.svg assets/img/icons/face-sad.svg assets/img/icons/favourite.svg diff --git a/ui/StatusQ/src/assets/img/icons/external-link.svg b/ui/StatusQ/src/assets/img/icons/external-link.svg new file mode 100644 index 0000000000..d30228d68d --- /dev/null +++ b/ui/StatusQ/src/assets/img/icons/external-link.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/ui/app/AppLayouts/Communities/panels/JoinPermissionsOverlayPanel.qml b/ui/app/AppLayouts/Communities/panels/JoinPermissionsOverlayPanel.qml index f673d6ffb3..38679e9b39 100644 --- a/ui/app/AppLayouts/Communities/panels/JoinPermissionsOverlayPanel.qml +++ b/ui/app/AppLayouts/Communities/panels/JoinPermissionsOverlayPanel.qml @@ -61,11 +61,6 @@ Control { return root.joinCommunity ? (root.requiresRequest ? d.communityRevealAddressWithRequestText : d.communityRevealAddressText) : d.channelRevealAddressText } - function getRevealAddressIcon() { - if(root.loginType == Constants.LoginType.Password) return "password" - return root.loginType == Constants.LoginType.Biometrics ? "touch-id" : "keycard" - } - function filterPermissions(model) { return !!model && (model.tokenCriteriaMet || !model.isPrivate) } @@ -153,7 +148,7 @@ Control { Layout.alignment: Qt.AlignHCenter visible: !root.showOnlyPanels && !root.isJoinRequestRejected && root.requiresRequest text: root.isInvitationPending ? d.getInvitationPendingText() : d.getRevealAddressText() - icon.name: root.isInvitationPending ? "" : d.getRevealAddressIcon() + icon.name: root.isInvitationPending ? "" : Constants.authenticationIconByType[root.loginType] font.pixelSize: 13 enabled: root.requirementsMet || d.communityPermissionsModel.count == 0 onClicked: root.isInvitationPending ? root.invitationPendingClicked() : root.revealAddressClicked() diff --git a/ui/app/AppLayouts/Communities/panels/OverviewSettingsFooter.qml b/ui/app/AppLayouts/Communities/panels/OverviewSettingsFooter.qml new file mode 100644 index 0000000000..f1ed11aeb9 --- /dev/null +++ b/ui/app/AppLayouts/Communities/panels/OverviewSettingsFooter.qml @@ -0,0 +1,133 @@ +import QtQuick 2.15 +import QtQml 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +import StatusQ.Components 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 + +import utils 1.0 + +Control { + id: root + property bool isControlNode: true + property int loginType: Constants.LoginType.Password + property string communityName: "" + + signal primaryButtonClicked + signal secondaryButtonClicked + + QtObject { + id: d + + readonly property real verticalBreakPoint: 950 + readonly property bool twoRowsLayout: contentItem.width <= verticalBreakPoint + + property string paragraphTitle + property string paragraphSubtitle + property string primaryButtonText + property string primaryButtonIcon + property string secondaryButtonText + property string secondaryButtonIcon + property string indicatorBgColor + property string indicatorColor + } + + contentItem: GridLayout { + id: mainGrid + columnSpacing: 16 + rowSpacing: 16 + + StatusRoundIcon { + id: icon + Layout.row: 0 + Layout.column: 0 + color: d.indicatorBgColor + asset.color: d.indicatorColor + asset.name: "desktop" + } + + ColumnLayout { + id: paragraph + Layout.row: 0 + Layout.column: 1 + Layout.columnSpan: d.twoRowsLayout ? 2 : 1 + Layout.fillWidth: true + spacing: 4 + StatusBaseText { + id: title + Layout.fillWidth: true + text: d.paragraphTitle + font.pixelSize: 15 + font.bold: true + color: Theme.palette.directColor1 + wrapMode: Text.WordWrap + } + + StatusBaseText { + id: subtitle + Layout.fillWidth: true + text: d.paragraphSubtitle + font.pixelSize: 15 + color: Theme.palette.baseColor1 + wrapMode: Text.WordWrap + } + } + + Item { + Layout.fillWidth: true + Layout.row: 0 + Layout.column: 3 + } + + RowLayout { + Layout.row: d.twoRowsLayout ? 1 : 0 + Layout.column: d.twoRowsLayout ? 1 : 4 + Layout.alignment: Qt.AlignLeft + + StatusFlatButton { + size: StatusBaseButton.Size.Small + text: d.secondaryButtonText + icon.name: d.secondaryButtonIcon + onClicked: root.secondaryButtonClicked() + } + + StatusButton { + size: StatusBaseButton.Size.Small + text: d.primaryButtonText + icon.name: d.primaryButtonIcon + onClicked: root.primaryButtonClicked() + } + } + } + + // Behavior + states: [ + State { + name: "isControlNode" + when: root.isControlNode + PropertyChanges { target: d; indicatorBgColor: Theme.palette.successColor2 } + PropertyChanges { target: d; indicatorColor: Theme.palette.successColor1 } + PropertyChanges { target: d; paragraphTitle: qsTr("This device is currently the control node for the %1 Community").arg(root.communityName) } + PropertyChanges { target: d; paragraphSubtitle: qsTr("For your Community to function correctly keep this device online with Status running as much as possible.") } + PropertyChanges { target: d; primaryButtonText: qsTr("Move control node") } + PropertyChanges { target: d; primaryButtonIcon: Constants.authenticationIconByType[root.loginType] } + PropertyChanges { target: d; secondaryButtonText: qsTr("Learn more") } + PropertyChanges { target: d; secondaryButtonIcon: "external-link" } + }, + State { + name: "isNotControlNode" + when: !root.isControlNode + PropertyChanges { target: d; indicatorBgColor: Theme.palette.primaryColor3 } + PropertyChanges { target: d; indicatorColor: Theme.palette.primaryColor1 } + PropertyChanges { target: d; paragraphTitle: qsTr("Make this device the control node for the %1 Community").arg(root.communityName) } + PropertyChanges { target: d; paragraphSubtitle: qsTr("You will need to input the Community private key. Ensure this is a device you can keep online with Status running.") } + PropertyChanges { target: d; primaryButtonText: qsTr("Make this device the control node") } + PropertyChanges { target: d; primaryButtonIcon: "" } + PropertyChanges { target: d; secondaryButtonText: qsTr("Learn more") } + PropertyChanges { target: d; secondaryButtonIcon: "external-link" } + } + ] +} diff --git a/ui/app/AppLayouts/Communities/panels/OverviewSettingsPanel.qml b/ui/app/AppLayouts/Communities/panels/OverviewSettingsPanel.qml index 5b788c463c..5fee094a16 100644 --- a/ui/app/AppLayouts/Communities/panels/OverviewSettingsPanel.qml +++ b/ui/app/AppLayouts/Communities/panels/OverviewSettingsPanel.qml @@ -35,6 +35,7 @@ StackLayout { property bool editable: false property bool owned: false + property int loginType: Constants.LoginType.Password function navigateBack() { if (editSettingsPanelLoader.item.dirty) @@ -138,40 +139,19 @@ StackLayout { Item { Layout.fillHeight: true } + } - RowLayout { - BannerPanel { - objectName: "invitePeopleBanner" - text: qsTr("Welcome to your community!") - buttonText: qsTr("Invite new people") - icon.name: "invite-users" - onButtonClicked: root.inviteNewPeopleClicked() - } - Item { - Layout.fillWidth: true - } - BannerPanel { - objectName: "airdropBanner" - visible: root.owned - text: qsTr("Try an airdrop to reward your community for engagement!") - buttonText: qsTr("Airdrop Tokens") - icon.name: "airdrop" - onButtonClicked: root.airdropTokensClicked() - } - - Item { - Layout.fillWidth: true - } - - BannerPanel { - objectName: "backUpBanner" - visible: root.owned - text: qsTr("Back up community key") - buttonText: qsTr("Back up") - icon.name: "objects" - onButtonClicked: root.backUpClicked() - } - } + footer: OverviewSettingsFooter { + rightPadding: 64 + leftPadding: 64 + bottomPadding: 50 + loginType: root.loginType + communityName: root.name + //TODO connect to backend + isControlNode: root.owned + onPrimaryButtonClicked: isControlNode = !isControlNode + //TODO update once the domain changes + onSecondaryButtonClicked: Global.openLink(Constants.statusHelpLinkPrefix + "en/status-communities/about-the-control-node-in-status-communities") } } diff --git a/ui/app/AppLayouts/Communities/panels/qmldir b/ui/app/AppLayouts/Communities/panels/qmldir index e86e874cba..97101da706 100644 --- a/ui/app/AppLayouts/Communities/panels/qmldir +++ b/ui/app/AppLayouts/Communities/panels/qmldir @@ -14,6 +14,7 @@ JoinPermissionsOverlayPanel 1.0 JoinPermissionsOverlayPanel.qml MembersSettingsPanel 1.0 MembersSettingsPanel.qml MintTokensFooterPanel 1.0 MintTokensFooterPanel.qml MintTokensSettingsPanel 1.0 MintTokensSettingsPanel.qml +OverviewSettingsFooter 1.0 OverviewSettingsFooter.qml OverviewSettingsPanel 1.0 OverviewSettingsPanel.qml PermissionConflictWarningPanel 1.0 PermissionConflictWarningPanel.qml PermissionQualificationPanel 1.0 PermissionQualificationPanel.qml diff --git a/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml b/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml index 46c5dcdfd6..72c9c35d31 100644 --- a/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml +++ b/ui/app/AppLayouts/Communities/views/CommunitySettingsView.qml @@ -169,6 +169,7 @@ StatusSectionLayout { pinMessagesEnabled: root.community.pinMessageAllMembersEnabled editable: true owned: root.community.memberRole === Constants.memberRole.owner + loginType: root.rootStore.loginType onEdited: { const error = root.chatCommunitySectionModule.editCommunity( diff --git a/ui/imports/utils/Constants.qml b/ui/imports/utils/Constants.qml index 42ae60527d..69daa1d74f 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -848,6 +848,7 @@ QtObject { readonly property string communityLinkPrefix: externalStatusLinkWithHttps + '/c/' readonly property string userLinkPrefix: externalStatusLinkWithHttps + '/u/' readonly property string statusLinkPrefix: 'https://status.im/' + readonly property string statusHelpLinkPrefix: `https://help.status.im/` readonly property int maxUploadFiles: 5 readonly property double maxUploadFilesizeMB: 10 @@ -914,6 +915,12 @@ QtObject { Biometrics, Keycard } + // Needs to match the enum above + readonly property var authenticationIconByType: [ + "password", + "touch-id", + "keycard", + ] enum ComputeFeeErrorCode { Success,