mirror of
https://github.com/status-im/status-app.git
synced 2026-08-27 15:11:20 +00:00
feat(nav): move Settings from the primary nav bar to the profile menu
Closes part of #21861
This commit is contained in:
@@ -248,7 +248,6 @@ SplitView {
|
||||
}
|
||||
|
||||
thirdpartyServicesEnabled: ctrlThirdPartyServices.checked
|
||||
profileSectionHasNotification: ctrlSettingsHasNotification.checked
|
||||
|
||||
onItemActivated: function (sectionType, sectionId) {
|
||||
logs.logEvent("onItemActivated", ["sectionType", "sectionId"], arguments)
|
||||
|
||||
@@ -57,7 +57,6 @@ Item {
|
||||
return ["👨🏻🍼", "🏃🏿♂️", "🌇", "🤶🏿", "🏮"]
|
||||
}
|
||||
|
||||
profileSectionHasNotification: false
|
||||
thirdpartyServicesEnabled: true
|
||||
|
||||
acVisible: false
|
||||
@@ -193,10 +192,8 @@ Item {
|
||||
verify(!!walletBtn)
|
||||
tryCompare(walletBtn, "visible", true)
|
||||
|
||||
// Check for Settings button
|
||||
const settingsBtn = findChild(controlUnderTest, "Settings-navbar")
|
||||
verify(!!settingsBtn)
|
||||
tryCompare(settingsBtn, "visible", true)
|
||||
// Settings is not a nav item anymore, it lives in the profile menu
|
||||
verify(!findChild(controlUnderTest, "Settings-navbar"))
|
||||
}
|
||||
|
||||
function test_section_button_click() {
|
||||
@@ -217,16 +214,9 @@ Item {
|
||||
verify(!!walletBtn)
|
||||
tryCompare(walletBtn, "checked", true)
|
||||
|
||||
// verify the Settings button is not checked
|
||||
const settingsBtn = findChild(controlUnderTest, "Settings-navbar")
|
||||
verify(!!settingsBtn)
|
||||
tryCompare(settingsBtn, "checked", false)
|
||||
|
||||
// simulate changing the active section from outside (via mock model update)
|
||||
sidebarAdaptor.sectionsModel.setActiveSection("id3") // "id" of Constants.appSection.profile
|
||||
|
||||
// verify that Settings is active, Wallet is not
|
||||
tryCompare(settingsBtn, "checked", true)
|
||||
tryCompare(walletBtn, "checked", false)
|
||||
}
|
||||
|
||||
@@ -297,17 +287,6 @@ Item {
|
||||
verify(true) // Basic validation
|
||||
}
|
||||
|
||||
function test_profile_section_notification() {
|
||||
controlUnderTest.profileSectionHasNotification = true
|
||||
|
||||
const settingsBtn = findChild(controlUnderTest, "Settings-navbar")
|
||||
verify(!!settingsBtn)
|
||||
|
||||
// Settings button should show notification when profileSectionHasNotification is true
|
||||
tryCompare(settingsBtn, "showBadge", true)
|
||||
tryCompare(settingsBtn, "badgeVisible", true)
|
||||
}
|
||||
|
||||
function test_community_buttons_have_object_name() {
|
||||
// Look for community buttons with specific objectName
|
||||
const communityBtn = findChild(controlUnderTest, "CommunityNavBarButton_Status.app")
|
||||
|
||||
@@ -18,6 +18,7 @@ class OnlineIdentifier(QObject):
|
||||
self.automatic_button = Button(names.userContextmenu_AutomaticButton)
|
||||
self.view_my_profile_button = Button(names.userContextMenu_ViewMyProfileAction)
|
||||
self.copy_link_to_profile_button = QObject(names.userContextMenu_CopyLinkToProfile)
|
||||
self.settings_button = Button(names.userContextMenu_SettingsAction)
|
||||
self.user_name_text_label = TextLabel(names.userStatusDisplayName)
|
||||
|
||||
@property
|
||||
|
||||
@@ -45,7 +45,6 @@ class MainLeftPanel(QObject):
|
||||
self.messages_button = Button(names.chatButton)
|
||||
self.communities_portal_button = Button(names.communitiesPortalButton)
|
||||
self.community_template_button = Button(names.statusCommunityMainNavBarListView_CommunityNavBarButton)
|
||||
self.settings_button = Button(names.settingsGearButton)
|
||||
self.wallet_button = Button(names.mainWalletButton)
|
||||
self.activity_center_button = Button(names.activityCenterButton)
|
||||
self.market_button = Button(names.marketButton)
|
||||
@@ -91,10 +90,10 @@ class MainLeftPanel(QObject):
|
||||
def open_messages_screen(self):
|
||||
return self.messages_button
|
||||
|
||||
@allure.step('Click Gear button and open Settings screen')
|
||||
@open_with_retries(SettingsScreen, attempts=3, delay=3.0)
|
||||
def open_settings(self):
|
||||
return self.settings_button
|
||||
@allure.step('Open Settings screen from the profile context menu')
|
||||
def open_settings(self) -> SettingsScreen:
|
||||
self.open_online_identifier().settings_button.click()
|
||||
return SettingsScreen().wait_until_appears()
|
||||
|
||||
@allure.step('Click Activity center button and open Activity center panel')
|
||||
@open_with_retries(ActivityCenter)
|
||||
|
||||
@@ -59,12 +59,11 @@ chatButton = {"container": primaryNavSidebar, "objectName": "Messages-navbar", "
|
||||
"visible": True}
|
||||
marketButton = {"container": primaryNavSidebar, "objectName": "Market-navbar", "type": "RegularSectionButton", "visible": True}
|
||||
|
||||
# Second half of left main panel: communities button, settings button
|
||||
# Second half of left main panel: communities button
|
||||
communitiesSettingsNavBarList = {"container": statusDesktop_mainWindow, "objectName": "statusMainNavBarListView",
|
||||
"type": "ListView", "visible": True}
|
||||
communitiesPortalButton = {"container": primaryNavSidebar, "objectName": "Communities Portal-navbar",
|
||||
"type": "PrimaryNavSidebarButton", "visible": True}
|
||||
settingsGearButton = {"container": primaryNavSidebar, "objectName": "Settings-navbar", "type": "BottomSectionButton", "visible": True}
|
||||
activityCenterButton = {"container": statusDesktop_mainWindow, "objectName": "Activity Center-navbar", "type": "PrimaryNavSidebarButton", "visible": True}
|
||||
|
||||
# Online identifier
|
||||
@@ -177,6 +176,9 @@ userContextMenu_ViewMyProfileAction = {"container": statusDesktop_mainWindow_ove
|
||||
userContextMenu_CopyLinkToProfile = {"container": statusDesktop_mainWindow_overlay,
|
||||
"objectName": "userStatusCopyLinkAction", "type": "StatusMenuItem",
|
||||
"visible": True}
|
||||
userContextMenu_SettingsAction = {"container": statusDesktop_mainWindow_overlay,
|
||||
"objectName": "userStatusSettingsAction", "type": "StatusMenuItem",
|
||||
"visible": True}
|
||||
userStatusDisplayName = {"container": onlineIdentifier, "objectName": "userStatusDisplayName",
|
||||
"type": "StatusBaseText", "visible": True}
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ class DeviceContext:
|
||||
max_taps = 5
|
||||
for tap_attempt in range(1, max_taps + 1):
|
||||
if not app.is_element_visible(
|
||||
app_locators.LEFT_NAV_SETTINGS, timeout=1
|
||||
app_locators.PROFILE_NAV_BUTTON, timeout=1
|
||||
):
|
||||
self.logger.info(
|
||||
"Drawer closed after %d tap(s)", tap_attempt - 1
|
||||
|
||||
@@ -16,9 +16,6 @@ class AppLocators(BaseLocators):
|
||||
LEFT_NAV_COMMUNITIES = BaseLocators.xpath(
|
||||
"//*[contains(@resource-id, 'Communities Portal-navbar')]"
|
||||
)
|
||||
LEFT_NAV_SETTINGS = BaseLocators.xpath(
|
||||
"//*[contains(@resource-id, 'Settings-navbar')]"
|
||||
)
|
||||
|
||||
# Home dock (visible only on Home)
|
||||
HOME_DOCK_CONTAINER = BaseLocators.xpath(
|
||||
@@ -45,6 +42,9 @@ class AppLocators(BaseLocators):
|
||||
SHARE_PROFILE_ACTION = BaseLocators.xpath(
|
||||
"//*[contains(@resource-id,'userStatusShareProfileAction')]"
|
||||
)
|
||||
SETTINGS_ACTION = BaseLocators.xpath(
|
||||
"//*[contains(@resource-id,'userStatusSettingsAction')]"
|
||||
)
|
||||
PROFILE_MENU_CONTAINER = BaseLocators.xpath(
|
||||
"//*[contains(@resource-id,'ProfileMenu')]"
|
||||
)
|
||||
|
||||
@@ -24,7 +24,6 @@ class App(BasePage):
|
||||
"market": self.locators.LEFT_NAV_MARKET,
|
||||
"messaging": self.locators.LEFT_NAV_MESSAGES,
|
||||
"communities": self.locators.LEFT_NAV_COMMUNITIES,
|
||||
"settings": self.locators.LEFT_NAV_SETTINGS,
|
||||
}
|
||||
for name, locator in mapping.items():
|
||||
el = self.find_element_safe(locator, timeout=1)
|
||||
@@ -149,7 +148,7 @@ class App(BasePage):
|
||||
the app's QML picks layout based on width, so wide tablets in portrait
|
||||
still get the always-visible side-nav.
|
||||
"""
|
||||
if self.is_element_visible(self.locators.LEFT_NAV_SETTINGS, timeout=2):
|
||||
if self.is_element_visible(self.locators.PROFILE_NAV_BUTTON, timeout=2):
|
||||
return True
|
||||
|
||||
from utils.screen_identity import dismiss_introduce_yourself
|
||||
@@ -164,11 +163,11 @@ class App(BasePage):
|
||||
return True
|
||||
|
||||
if self.is_element_visible(self.locators.LEFT_NAV_ANY, timeout=1):
|
||||
return self.is_element_visible(self.locators.LEFT_NAV_SETTINGS, timeout=5)
|
||||
return self.is_element_visible(self.locators.PROFILE_NAV_BUTTON, timeout=5)
|
||||
|
||||
# Phase 1: unwind deep navigation stack via back button
|
||||
for _ in range(5):
|
||||
if self.is_element_visible(self.locators.LEFT_NAV_SETTINGS, timeout=1):
|
||||
if self.is_element_visible(self.locators.PROFILE_NAV_BUTTON, timeout=1):
|
||||
return True
|
||||
if not self.is_element_visible(
|
||||
self.locators.TOOLBAR_BACK_BUTTON, timeout=1
|
||||
@@ -176,7 +175,7 @@ class App(BasePage):
|
||||
break
|
||||
self.safe_click(self.locators.TOOLBAR_BACK_BUTTON, timeout=2)
|
||||
|
||||
if self.is_element_visible(self.locators.LEFT_NAV_SETTINGS, timeout=1):
|
||||
if self.is_element_visible(self.locators.PROFILE_NAV_BUTTON, timeout=1):
|
||||
return True
|
||||
|
||||
# Phase 2: drag the drawer handle to open the nav drawer
|
||||
@@ -185,7 +184,7 @@ class App(BasePage):
|
||||
break
|
||||
self.logger.debug("Nav drawer open attempt %d did not reveal nav", attempt + 1)
|
||||
|
||||
return self.is_element_visible(self.locators.LEFT_NAV_SETTINGS, timeout=5)
|
||||
return self.is_element_visible(self.locators.PROFILE_NAV_BUTTON, timeout=5)
|
||||
|
||||
# Locator for the drawer swipe-indicator handle visible in portrait mode.
|
||||
NAV_DRAWER_HANDLE = (
|
||||
@@ -269,16 +268,36 @@ class App(BasePage):
|
||||
return False
|
||||
|
||||
def click_settings_button(self) -> bool:
|
||||
self.logger.info("Clicking Settings button")
|
||||
if self.active_section() == "settings":
|
||||
self.logger.info("Opening Settings from the profile menu")
|
||||
from utils.screen_identity import SCREEN_ANCHORS
|
||||
if self.is_element_visible(SCREEN_ANCHORS["settings"], timeout=1):
|
||||
self.logger.info("Already in Settings section — skipping nav")
|
||||
return True
|
||||
from utils.screen_identity import SCREEN_ANCHORS
|
||||
return self._click_drawer_nav_with_verify(
|
||||
nav_locator=self.locators.LEFT_NAV_SETTINGS,
|
||||
landmark_locator=SCREEN_ANCHORS["settings"],
|
||||
nav_name="Settings",
|
||||
)
|
||||
|
||||
for attempt in range(1, 4):
|
||||
if not self._ensure_main_nav_visible():
|
||||
self.logger.warning(
|
||||
"Nav not visible on Settings attempt %d", attempt
|
||||
)
|
||||
continue
|
||||
if not self.safe_click(self.locators.PROFILE_NAV_BUTTON, timeout=5):
|
||||
self.logger.warning(
|
||||
"Profile nav button tap failed on Settings attempt %d", attempt
|
||||
)
|
||||
continue
|
||||
if not self.safe_click(self.locators.SETTINGS_ACTION, timeout=5):
|
||||
self.logger.warning(
|
||||
"Settings menu item tap failed on attempt %d", attempt
|
||||
)
|
||||
continue
|
||||
if self.is_element_visible(SCREEN_ANCHORS["settings"], timeout=15):
|
||||
return True
|
||||
self.logger.warning(
|
||||
"Settings screen anchor not visible after attempt %d", attempt
|
||||
)
|
||||
|
||||
self.dump_page_source("settings_nav_failed")
|
||||
return False
|
||||
|
||||
def _click_drawer_nav_with_verify(
|
||||
self,
|
||||
|
||||
@@ -1046,6 +1046,14 @@ Item {
|
||||
homePageLoader.item.focusSearch()
|
||||
}
|
||||
|
||||
function openSettingsRoot() {
|
||||
profileLoader.settingsSubSubsection = -1
|
||||
profileLoader.settingsSubsection = appMain.isPortraitMode ? -1 : Constants.settingsSubsection.profile
|
||||
appMain.rootStore.setActiveSectionBySectionType(Constants.appSection.profile)
|
||||
if (profileLoader.item)
|
||||
profileLoader.item.currentIndex = StatusSectionLayout.LeftPanel
|
||||
}
|
||||
|
||||
function maybeDisplayIntroduceYourselfPopup() {
|
||||
if (!appMainLocalSettings.introduceYourselfPopupSeen
|
||||
&& appMain.ownContactDetails?.displayName === "") {
|
||||
@@ -2623,17 +2631,6 @@ Item {
|
||||
|
||||
communityPopupMenu: communityContextMenuComponent
|
||||
|
||||
profileSectionHasNotification: {
|
||||
if (contactsModelAdaptor?.pendingReceivedRequestContacts?.ModelCount.count > 0) // pending contact request
|
||||
return true
|
||||
if (appMain.mainReady
|
||||
&& !appMain.privacyStore?.mnemonicBackedUp
|
||||
&& !appMain.profileStore?.userDeclinedBackupBanner) // seedphrase not backed up (removed)
|
||||
return true
|
||||
if (d.syncingBadgeCount > 0) // sync entries
|
||||
return true
|
||||
return false
|
||||
}
|
||||
thirdpartyServicesEnabled: appMain.rootStore.thirdpartyServicesEnabled
|
||||
profileLoading: !appMain.mainReady
|
||||
|
||||
@@ -2644,6 +2641,7 @@ Item {
|
||||
onSetCurrentUserStatusRequested: status => appMain.rootStore.setCurrentUserStatus(status)
|
||||
onViewProfileRequested: pubKey => Global.openProfilePopup(pubKey)
|
||||
onShareOwnProfileRequested: Global.shareProfileDialogRequested(appMain.ownContactDetails.publicKey)
|
||||
onSettingsRequested: d.openSettingsRoot()
|
||||
|
||||
onItemActivated: function(sectionType, sectionId) {
|
||||
// Ensure Activity Center Panel is closed when manual navigation done
|
||||
|
||||
@@ -83,10 +83,6 @@ SQUtils.QObject {
|
||||
roleName: "sectionType"
|
||||
value: Constants.appSection.qrCodeScanner
|
||||
}
|
||||
ValueFilter {
|
||||
roleName: "sectionType"
|
||||
value: Constants.appSection.profile
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,6 @@ Control {
|
||||
|
||||
property Component communityPopupMenu // required property var model
|
||||
|
||||
required property bool profileSectionHasNotification
|
||||
required property bool thirdpartyServicesEnabled
|
||||
property bool profileLoading: false
|
||||
|
||||
@@ -74,6 +73,7 @@ Control {
|
||||
signal activityCenterRequested(bool shouldShow)
|
||||
signal viewProfileRequested(string pubKey)
|
||||
signal shareOwnProfileRequested
|
||||
signal settingsRequested
|
||||
signal setCurrentUserStatusRequested(int status)
|
||||
|
||||
Component.onCompleted: d.snapToMode()
|
||||
@@ -293,6 +293,7 @@ Control {
|
||||
onSetCurrentUserStatusRequested: (status) => root.setCurrentUserStatusRequested(status)
|
||||
onViewProfileRequested: (pubKey) => root.viewProfileRequested(pubKey)
|
||||
onShareOwnProfileRequested: root.shareOwnProfileRequested()
|
||||
onSettingsRequested: root.settingsRequested()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -437,11 +438,7 @@ Control {
|
||||
}
|
||||
|
||||
component BottomSectionButton: RegularSectionButton {
|
||||
showBadge: {
|
||||
if (model.sectionType === Constants.appSection.profile)
|
||||
return root.profileSectionHasNotification
|
||||
return model.hasNotification
|
||||
}
|
||||
showBadge: model.hasNotification
|
||||
}
|
||||
|
||||
component SidebarListView: ListView {
|
||||
|
||||
@@ -27,6 +27,7 @@ StatusIconTabButton {
|
||||
|
||||
signal viewProfileRequested(string pubKey)
|
||||
signal shareOwnProfileRequested
|
||||
signal settingsRequested
|
||||
signal setCurrentUserStatusRequested(int status)
|
||||
|
||||
name: root.name
|
||||
@@ -100,6 +101,7 @@ StatusIconTabButton {
|
||||
|
||||
onViewProfileRequested: root.viewProfileRequested(root.pubKey)
|
||||
onShareOwnProfileRequested: root.shareOwnProfileRequested()
|
||||
onSettingsRequested: root.settingsRequested()
|
||||
onCopyLinkRequested: ClipboardUtils.setText(root.getLinkToProfileFn(root.pubKey))
|
||||
onSetCurrentUserStatusRequested: (status) => root.setCurrentUserStatusRequested(status)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ StatusDropdown {
|
||||
signal viewProfileRequested
|
||||
signal copyLinkRequested
|
||||
signal shareOwnProfileRequested
|
||||
signal settingsRequested
|
||||
signal setCurrentUserStatusRequested(int status)
|
||||
|
||||
implicitWidth: 400
|
||||
@@ -142,6 +143,18 @@ StatusDropdown {
|
||||
}
|
||||
}
|
||||
|
||||
ActionWrapper {
|
||||
action: StatusAction {
|
||||
objectName: "userStatusSettingsAction"
|
||||
text: qsTr("Settings")
|
||||
icon.name: "settings"
|
||||
onTriggered: {
|
||||
root.settingsRequested()
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusMenuSeparator {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user