diff --git a/ui/component/common/card.jsx b/ui/component/common/card.jsx index 29c3f1a9c..1fb606cdb 100644 --- a/ui/component/common/card.jsx +++ b/ui/component/common/card.jsx @@ -10,6 +10,7 @@ type Props = { title?: string | Node, subtitle?: string | Node, titleActions?: string | Node, + id?: string, body?: string | Node, actions?: string | Node, icon?: string, @@ -30,6 +31,7 @@ export default function Card(props: Props) { title, subtitle, titleActions, + id, body, actions, icon, @@ -53,6 +55,7 @@ export default function Card(props: Props) { className={classnames(className, 'card', { 'card__multi-pane': Boolean(secondPane), })} + id={id} onClick={(e) => { if (onClick) { onClick(); diff --git a/ui/component/common/icon-custom.jsx b/ui/component/common/icon-custom.jsx index 27bcf99fb..a5f0d2d68 100644 --- a/ui/component/common/icon-custom.jsx +++ b/ui/component/common/icon-custom.jsx @@ -2318,6 +2318,23 @@ export const icons = { ), + [ICONS.APPEARANCE]: buildIcon( + + + + + + + + ), + [ICONS.CONTENT]: buildIcon( + + + + + + + ), [ICONS.STAR]: buildIcon( diff --git a/ui/component/page/view.jsx b/ui/component/page/view.jsx index 3951ad603..76867b415 100644 --- a/ui/component/page/view.jsx +++ b/ui/component/page/view.jsx @@ -23,6 +23,7 @@ type Props = { isUpgradeAvailable: boolean, authPage: boolean, filePage: boolean, + settingsPage?: boolean, noHeader: boolean, noFooter: boolean, noSideNavigation: boolean, @@ -45,6 +46,7 @@ function Page(props: Props) { children, className, filePage = false, + settingsPage, authPage = false, fullWidthPage = false, noHeader = false, @@ -114,6 +116,7 @@ function Page(props: Props) { 'main--full-width': fullWidthPage, 'main--auth-page': authPage, 'main--file-page': filePage, + 'main--settings-page': settingsPage, 'main--markdown': isMarkdown, 'main--theater-mode': isOnFilePage && videoTheaterMode && !livestream, 'main--livestream': livestream && !chatDisabled, diff --git a/ui/component/settingAccount/view.jsx b/ui/component/settingAccount/view.jsx index 6033f4446..4dce9bd1a 100644 --- a/ui/component/settingAccount/view.jsx +++ b/ui/component/settingAccount/view.jsx @@ -1,6 +1,7 @@ // @flow import * as ICONS from 'constants/icons'; import * as PAGES from 'constants/pages'; +import { SETTINGS_GRP } from 'constants/settings'; import React from 'react'; import Button from 'component/button'; import Card from 'component/common/card'; @@ -37,6 +38,7 @@ export default function SettingAccount(props: Props) { return ( any, + icon: string, + extra?: Node, +}; + +export default function SettingsSideNavigation() { + const sidebarOpen = true; + const isMediumScreen = useIsMediumScreen(); + + const SIDE_LINKS: Array = [ + { + title: 'Appearance', + link: `/$/${PAGES.SETTINGS}#appearance`, + icon: ICONS.APPEARANCE, + }, + { + title: 'Account', + link: `/$/${PAGES.SETTINGS}#account`, + icon: ICONS.ACCOUNT, + }, + { + title: 'Content settings', + link: `/$/${PAGES.SETTINGS}#content`, + icon: ICONS.CONTENT, + }, + { + title: 'System', + link: `/$/${PAGES.SETTINGS}#system`, + icon: ICONS.SETTINGS, + }, + ]; + + const isAbsolute = isMediumScreen; + const microNavigation = !sidebarOpen || isMediumScreen; + + if (isMediumScreen) { + // I think it's ok to hide it for now on medium/small screens given that + // we are using a scrolling Settings Page that displays everything. If we + // really need this, most likely we can display it as a Tab at the top + // of the page. + return null; + } + + return ( +
+ + + {isMediumScreen && sidebarOpen && ( + <> + + + )} +
+ ); +} diff --git a/ui/constants/icons.js b/ui/constants/icons.js index db057e8f1..55110cefe 100644 --- a/ui/constants/icons.js +++ b/ui/constants/icons.js @@ -163,5 +163,7 @@ export const STACK = 'stack'; export const TIME = 'time'; export const GLOBE = 'globe'; export const RSS = 'rss'; +export const APPEARANCE = 'Appearance'; +export const CONTENT = 'Content'; export const STAR = 'star'; export const MUSIC = 'MusicCategory'; diff --git a/ui/constants/settings.js b/ui/constants/settings.js index ca0e48c31..aadb632a0 100644 --- a/ui/constants/settings.js +++ b/ui/constants/settings.js @@ -26,3 +26,10 @@ export const ENABLE_SYNC = 'enable_sync'; export const TO_TRAY_WHEN_CLOSED = 'to_tray_when_closed'; export const ENABLE_PUBLISH_PREVIEW = 'enable-publish-preview'; export const DESKTOP_WINDOW_ZOOM = 'desktop_window_zoom'; + +export const SETTINGS_GRP = { + APPEARANCE: 'appearance', + ACCOUNT: 'account', + CONTENT: 'content', + SYSTEM: 'system', +}; diff --git a/ui/page/settings/view.jsx b/ui/page/settings/view.jsx index 4f2b98d59..0752896aa 100644 --- a/ui/page/settings/view.jsx +++ b/ui/page/settings/view.jsx @@ -8,6 +8,7 @@ import Page from 'component/page'; import SettingAccount from 'component/settingAccount'; import SettingAppearance from 'component/settingAppearance'; import SettingContent from 'component/settingContent'; +import SettingsSideNavigation from 'component/settingsSideNavigation'; import SettingSystem from 'component/settingSystem'; import SettingUnauthenticated from 'component/settingUnauthenticated'; import Yrbl from 'component/yrbl'; @@ -40,37 +41,52 @@ class SettingsPage extends React.PureComponent { const noDaemonSettings = !daemonSettings || Object.keys(daemonSettings).length === 0; return ( - - {!isAuthenticated && IS_WEB && ( - <> - -
- -
- } - /> - - - )} + + - {!IS_WEB && noDaemonSettings ? ( -
-
{__('Failed to load settings.')}
-
- ) : ( -
- - - - -
- )} +
+ {!isAuthenticated && IS_WEB && ( + <> + +
+ +
+ } + /> +
+ + )} + + {!IS_WEB && noDaemonSettings ? ( +
+
{__('Failed to load settings.')}
+
+ ) : ( +
+ + + + +
+ )} +
); } diff --git a/ui/scss/component/_main.scss b/ui/scss/component/_main.scss index e4814d29a..f70bcff0c 100644 --- a/ui/scss/component/_main.scss +++ b/ui/scss/component/_main.scss @@ -221,6 +221,23 @@ } } +.main--settings-page { + width: 100%; + max-width: 70rem; + margin-left: auto; + margin-right: auto; + margin-top: var(--spacing-m); + padding: 0 var(--spacing-m); + display: flex; + flex-direction: row; + align-items: flex-start; + position: relative; + + @media (min-width: $breakpoint-small) { + width: 80%; + } +} + .main--markdown { flex-direction: column; }