diff --git a/fluxer_app/src/features/app/components/dialogs/components/DesktopSettingsView.tsx b/fluxer_app/src/features/app/components/dialogs/components/DesktopSettingsView.tsx index fbf8f3d24..a6e7e3297 100644 --- a/fluxer_app/src/features/app/components/dialogs/components/DesktopSettingsView.tsx +++ b/fluxer_app/src/features/app/components/dialogs/components/DesktopSettingsView.tsx @@ -54,6 +54,7 @@ import { getSectionIdsForTab, getSectionsForTab, getUserSettingsTabLabel, + PRIVACY_REVIEW_NEEDED_DESCRIPTOR, tabHasMultipleLinkableSections, USER_SETTINGS_LABEL_DESCRIPTOR, } from '@app/features/user/components/settings_utils/SettingsConstants'; @@ -79,6 +80,7 @@ import {buildUserSettingsDeepLink} from '@app/features/user/components/settings_ import {useSettingsContentKey} from '@app/features/user/hooks/useSettingsContentKey'; import {useUnsavedChangesFlash} from '@app/features/user/hooks/useUnsavedChangesFlash'; import {ScrollSpyProvider, useScrollSpyContext} from '@app/features/user/state/ScrollSpyContext'; +import UserSettings from '@app/features/user/state/UserSettings'; import Users from '@app/features/user/state/Users'; import { collapseSettingsTreeTab, @@ -380,6 +382,7 @@ export const DesktopSettingsView: React.FC = observer( const activeTabPanelId = selectedTab ? `settings-tabpanel-${selectedTab}` : undefined; const activeTabId = selectedTab ? `settings-tab-${selectedTab}` : undefined; const shouldShowWhatsNew = hasWhatsNewEntries(); + const privacySetupPending = UserSettings.isPrivacySetupPending(); const scrollKey = useMemo(() => { const subtabKey = contentKey ?? initialSubtab ?? 'root'; if (isSearchActive) { @@ -585,6 +588,11 @@ export const DesktopSettingsView: React.FC = observer( } selected={isSelected} + attentionLabel={ + tab.type === 'privacy_safety' && privacySetupPending + ? i18n._(PRIVACY_REVIEW_NEEDED_DESCRIPTOR) + : undefined + } onClick={() => handleTabSelect(tab.type)} onRequestContentFocus={focusContentPanel} id={tabId} diff --git a/fluxer_app/src/features/app/components/dialogs/components/MobileSettingsView.tsx b/fluxer_app/src/features/app/components/dialogs/components/MobileSettingsView.tsx index 23106ea4d..e1f0e4d58 100644 --- a/fluxer_app/src/features/app/components/dialogs/components/MobileSettingsView.tsx +++ b/fluxer_app/src/features/app/components/dialogs/components/MobileSettingsView.tsx @@ -21,6 +21,7 @@ import * as ModalCommands from '@app/features/ui/commands/ModalCommands'; import {modal} from '@app/features/ui/commands/ModalCommands'; import * as TextCopyCommands from '@app/features/ui/commands/TextCopyCommands'; import {MentionBadgeAnimated} from '@app/features/ui/components/MentionBadge'; +import {NotificationDot} from '@app/features/ui/components/NotificationDot'; import {Scroller, type ScrollerHandle} from '@app/features/ui/components/Scroller'; import {Spinner} from '@app/features/ui/components/Spinner'; import {StatusAwareAvatar} from '@app/features/ui/components/StatusAwareAvatar'; @@ -31,6 +32,7 @@ import type {SettingsTab} from '@app/features/user/components/settings_utils/Set import { getCategoryLabel, getUserSettingsTabLabel, + PRIVACY_REVIEW_NEEDED_DESCRIPTOR, } from '@app/features/user/components/settings_utils/SettingsConstants'; import {isSettingsItemNew} from '@app/features/user/components/settings_utils/SettingsMetadata'; import { @@ -44,6 +46,7 @@ import {buildUserSettingsDeepLink} from '@app/features/user/components/settings_ import type {MobileNavigationState} from '@app/features/user/hooks/useMobileNavigation'; import {useSettingsContentKey} from '@app/features/user/hooks/useSettingsContentKey'; import {useUnsavedChangesFlash} from '@app/features/user/hooks/useUnsavedChangesFlash'; +import UserSettings from '@app/features/user/state/UserSettings'; import Users from '@app/features/user/state/Users'; import {msg} from '@lingui/core/macro'; import {useLingui} from '@lingui/react/macro'; @@ -137,9 +140,10 @@ interface PressableSettingsItemProps { tab: SettingsTab; onSelect: () => void; badge?: React.ReactNode; + attention?: string; } -const PressableSettingsItem: React.FC = observer(({tab, onSelect, badge}) => { +const PressableSettingsItem: React.FC = observer(({tab, onSelect, badge, attention}) => { const {isPressed, pressableProps} = usePressable(); const {handleLongPressCopy, consumeSuppressedClick} = useSettingsPageDeepLinkLongPress(tab.type); const handleClick = useCallback( @@ -187,6 +191,9 @@ const PressableSettingsItem: React.FC = observer(({t {badge} + {attention && ( + + )} getAdvancedSettingItems().some((item) => isSettingsItemNew(item, Date.now(), currentUser?.createdAt ?? null)), @@ -432,6 +440,11 @@ const MobileSettingsList = observer( tab={tab} onSelect={() => onTabSelect(tab.type, tab.label)} badge={badge} + attention={ + tab.type === 'privacy_safety' && privacySetupPending + ? i18n._(PRIVACY_REVIEW_NEEDED_DESCRIPTOR) + : undefined + } data-flx="app.mobile-settings-view.mobile-settings-list.pressable-settings-item.tab-select" /> {(!isLastTab || isLastCategory) && ( diff --git a/fluxer_app/src/features/app/components/dialogs/shared/SettingsModalLayout.module.css b/fluxer_app/src/features/app/components/dialogs/shared/SettingsModalLayout.module.css index 9c786f5eb..44762caa0 100644 --- a/fluxer_app/src/features/app/components/dialogs/shared/SettingsModalLayout.module.css +++ b/fluxer_app/src/features/app/components/dialogs/shared/SettingsModalLayout.module.css @@ -406,9 +406,30 @@ border: 0; } +.sidebarItemTrailing { + position: relative; + display: flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + margin-inline-start: 0.25rem; + min-width: 1rem; + min-height: 1rem; +} + +.sidebarItemAttentionDot { + position: absolute; +} + +.sidebarItem[data-expandable='true'].sidebarItemSelected .sidebarItemAttentionDot, +.sidebarItem[data-expandable='true'][aria-expanded='true'] .sidebarItemAttentionDot, +.sidebarItem[data-expandable='true']:hover .sidebarItemAttentionDot, +.sidebarItem[data-expandable='true'].sidebarItemFocused .sidebarItemAttentionDot { + visibility: hidden; +} + .sidebarItemChevron { flex-shrink: 0; - margin-inline-start: 0.25rem; color: var(--text-tertiary); opacity: 0; transform: rotate(0deg); diff --git a/fluxer_app/src/features/app/components/dialogs/shared/SettingsModalLayout.tsx b/fluxer_app/src/features/app/components/dialogs/shared/SettingsModalLayout.tsx index a24a23fa2..322a95dca 100644 --- a/fluxer_app/src/features/app/components/dialogs/shared/SettingsModalLayout.tsx +++ b/fluxer_app/src/features/app/components/dialogs/shared/SettingsModalLayout.tsx @@ -4,6 +4,7 @@ import Accessibility from '@app/features/accessibility/state/Accessibility'; import styles from '@app/features/app/components/dialogs/shared/SettingsModalLayout.module.css'; import {SETTINGS_SECTIONS_DESCRIPTOR} from '@app/features/i18n/utils/CommonMessageDescriptors'; import {remFromPx} from '@app/features/theme/layout/RemFromPx'; +import {NotificationDot} from '@app/features/ui/components/NotificationDot'; import {Scroller, type ScrollerHandle} from '@app/features/ui/components/Scroller'; import FocusRing from '@app/features/ui/focus_ring/FocusRing'; import { @@ -292,6 +293,7 @@ export const SettingsModalSidebarItem: React.FC = expandableId, sectionsGroupId, toggleOnSelectedClick = true, + attentionLabel, }) => { const {tabIndex, buttonRef} = useSettingsModalSidebarItemLogic({selected}); const focusContentAfterActivationRef = useRef(false); @@ -354,14 +356,28 @@ export const SettingsModalSidebarItem: React.FC = > {label} - {expandable && ( -