From 20eb5c6b7cb4bf0e0afb70e1a0df5d6424c9399d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enik=C5=91=20Pusztai?= Date: Fri, 20 Dec 2019 15:03:52 +0100 Subject: [PATCH 1/7] themeswitcher wip --- .../src/components/appbar/DesktopAppBar.tsx | 4 +- .../src/components/appbar/DesktopNavMenu.tsx | 127 ++++++++++++++++++ apps/sensenet/src/localization/default.ts | 5 + apps/sensenet/src/localization/hungarian.ts | 5 + 4 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 apps/sensenet/src/components/appbar/DesktopNavMenu.tsx diff --git a/apps/sensenet/src/components/appbar/DesktopAppBar.tsx b/apps/sensenet/src/components/appbar/DesktopAppBar.tsx index 8ad70e716..e8b3d9710 100644 --- a/apps/sensenet/src/components/appbar/DesktopAppBar.tsx +++ b/apps/sensenet/src/components/appbar/DesktopAppBar.tsx @@ -7,6 +7,7 @@ import { ResponsiveContext, ResponsivePersonalSetttings } from '../../context' import { useCommandPalette, useTheme } from '../../hooks' import { CommandPalette } from '../command-palette/CommandPalette' import { RepositorySelector } from '../RepositorySelector' +import { DesktopNavMenu } from './DesktopNavMenu' export const DesktopAppBar: React.FunctionComponent<{ openDrawer?: () => void }> = props => { const device = useContext(ResponsiveContext) @@ -16,7 +17,7 @@ export const DesktopAppBar: React.FunctionComponent<{ openDrawer?: () => void }> const commandPalette = useCommandPalette() return ( - +
void }>
{personalSettings.commandPalette.enabled ? :
} + ) diff --git a/apps/sensenet/src/components/appbar/DesktopNavMenu.tsx b/apps/sensenet/src/components/appbar/DesktopNavMenu.tsx new file mode 100644 index 000000000..e3b576545 --- /dev/null +++ b/apps/sensenet/src/components/appbar/DesktopNavMenu.tsx @@ -0,0 +1,127 @@ +import React from 'react' +import ClickAwayListener from '@material-ui/core/ClickAwayListener' +import Grow from '@material-ui/core/Grow' +import Paper from '@material-ui/core/Paper' +import Popper from '@material-ui/core/Popper' +import MenuItem from '@material-ui/core/MenuItem' +import MenuList from '@material-ui/core/MenuList' +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles' +import { IconButton, ListItemIcon, ListItemText } from '@material-ui/core' +import KeyboardArrowDown from '@material-ui/icons/KeyboardArrowDown' +import { useRepository, useSession } from '@sensenet/hooks-react' +import { NavLink } from 'react-router-dom' +import { UserAvatar } from '../UserAvatar' +import { useLocalization } from '../../hooks' +import { useDialog } from '../dialogs' + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + height: '100%', + padding: '8px 16px', + background: theme.palette.background.paper, + alignItems: 'center', + }, + paper: { + marginRight: theme.spacing(2), + }, + }), +) + +export const DesktopNavMenu: React.FunctionComponent = props => { + const classes = useStyles() + const { openDialog } = useDialog() + const [open, setOpen] = React.useState(false) + const anchorRef = React.useRef(null) + const session = useSession() + const repo = useRepository() + const localization = useLocalization() + + const handleToggle = () => { + setOpen(prevOpen => !prevOpen) + } + + const handleClose = (event: React.MouseEvent) => { + if (anchorRef.current && anchorRef.current.contains(event.target as HTMLElement)) { + return + } + + setOpen(false) + } + + function handleListKeyDown(event: React.KeyboardEvent) { + if (event.key === 'Tab') { + event.preventDefault() + setOpen(false) + } + } + + const prevOpen = React.useRef(open) + React.useEffect(() => { + if (prevOpen.current === true && open === false) { + anchorRef.current!.focus() + } + + prevOpen.current = open + }, [open]) + + const logout = () => { + openDialog({ name: 'logout', props: { userToLogout: session.currentUser } }) + } + + return ( +
+ + + + + + {({ TransitionProps, placement }) => ( + + + + + + + + + + + + {localization.topMenu.changeRepo} + + + {localization.topMenu.personalSettings} + + {localization.topMenu.logout} + + + + + )} + +
+ ) +} diff --git a/apps/sensenet/src/localization/default.ts b/apps/sensenet/src/localization/default.ts index 9b4effce2..cefab4dc8 100644 --- a/apps/sensenet/src/localization/default.ts +++ b/apps/sensenet/src/localization/default.ts @@ -261,6 +261,11 @@ const values = { saveSuccessNotification: `The changes of '{0}' has been saved`, saveFailedNotification: `Failed to save changes of content '{0}'`, }, + topMenu: { + changeRepo: 'Change repository', + personalSettings: 'Personal settings', + logout: 'Logout', + }, navigationCommandProvider: { personalSettingsPrimary: 'Personal Settings', personalSettingsSecondary: 'Edit your personal settings', diff --git a/apps/sensenet/src/localization/hungarian.ts b/apps/sensenet/src/localization/hungarian.ts index 456630a74..11619013b 100644 --- a/apps/sensenet/src/localization/hungarian.ts +++ b/apps/sensenet/src/localization/hungarian.ts @@ -104,6 +104,11 @@ const values: DeepPartial = { notLoggedIn: 'Nincs bejelentkezve', typeToFilter: 'Kezdj írni a szűréshez...', }, + topMenu: { + changeRepo: 'Másik repository', + personalSettings: 'Személyes beállítások', + logout: 'Kijelentkezés', + }, } export default values From 94a6f2d8dcbfaa694371e4907699133ec5e92750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enik=C5=91=20Pusztai?= Date: Sun, 22 Dec 2019 18:10:42 +0100 Subject: [PATCH 2/7] switcher has been implemented --- .../src/components/appbar/DesktopAppBar.tsx | 6 +- .../src/components/appbar/DesktopNavMenu.tsx | 108 +++++++++++++++--- .../src/components/drawer/PermanentDrawer.tsx | 108 +++--------------- apps/sensenet/src/components/login/Login.tsx | 3 + apps/sensenet/src/context/ThemeProvider.tsx | 27 +++-- apps/sensenet/src/hooks/use-theme-service.ts | 9 ++ apps/sensenet/src/localization/default.ts | 2 +- .../sensenet/src/services/PersonalSettings.ts | 13 ++- apps/sensenet/src/services/ThemeService.ts | 28 +++++ apps/sensenet/src/services/index.ts | 1 + 10 files changed, 182 insertions(+), 123 deletions(-) create mode 100644 apps/sensenet/src/hooks/use-theme-service.ts create mode 100644 apps/sensenet/src/services/ThemeService.ts diff --git a/apps/sensenet/src/components/appbar/DesktopAppBar.tsx b/apps/sensenet/src/components/appbar/DesktopAppBar.tsx index e8b3d9710..f20c4584b 100644 --- a/apps/sensenet/src/components/appbar/DesktopAppBar.tsx +++ b/apps/sensenet/src/components/appbar/DesktopAppBar.tsx @@ -17,8 +17,10 @@ export const DesktopAppBar: React.FunctionComponent<{ openDrawer?: () => void }> const commandPalette = useCommandPalette() return ( - - + +
+ createStyles({ + root: { + width: 28, + height: 16, + padding: 0, + display: 'flex', + }, + switchBase: { + padding: 2, + color: theme.palette.common.white, + opacity: '1', + '&$checked': { + transform: 'translateX(12px)', + color: theme.palette.common.black, + '& + $track': { + opacity: 1, + backgroundColor: theme.palette.common.white, + }, + }, + }, + thumb: { + width: 12, + height: 12, + boxShadow: 'none', + }, + track: { + opacity: 1, + }, + checked: {}, + }), +)(Switch) const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -29,14 +71,24 @@ const useStyles = makeStyles((theme: Theme) => }), ) -export const DesktopNavMenu: React.FunctionComponent = props => { +export const DesktopNavMenu: React.FunctionComponent = () => { + const themeService = useThemeService() + const injector = useInjector() const classes = useStyles() + const theme = useTheme() + const service = injector.getInstance(PersonalSettings) const { openDialog } = useDialog() const [open, setOpen] = React.useState(false) const anchorRef = React.useRef(null) const session = useSession() const repo = useRepository() const localization = useLocalization() + const [pageTheme, setPageTheme] = useState<'dark' | 'light' | undefined>(themeService.currentTheme.getValue()) + + useEffect(() => { + service.setPersonalSettingsTheme(pageTheme) + themeService.currentTheme.setValue(pageTheme) + }, [pageTheme, service, themeService.currentTheme]) const handleToggle = () => { setOpen(prevOpen => !prevOpen) @@ -66,8 +118,13 @@ export const DesktopNavMenu: React.FunctionComponent = props => { prevOpen.current = open }, [open]) - const logout = () => { + const logout = (event: React.MouseEvent) => { openDialog({ name: 'logout', props: { userToLogout: session.currentUser } }) + handleClose(event) + } + + const switchTheme = () => (event: React.ChangeEvent) => { + setPageTheme(event.target.checked ? 'dark' : 'light') } return ( @@ -82,9 +139,8 @@ export const DesktopNavMenu: React.FunctionComponent = props => { @@ -110,12 +166,30 @@ export const DesktopNavMenu: React.FunctionComponent = props => { /> - {localization.topMenu.changeRepo} + + {localization.topMenu.changeRepo}{' '} + - - {localization.topMenu.personalSettings} + + + {localization.topMenu.personalSettings} + - {localization.topMenu.logout} + + {localization.topMenu.logout} + + + + + + {pageTheme === 'dark' ? 'Light theme' : 'Dark theme'} + + + + + + + diff --git a/apps/sensenet/src/components/drawer/PermanentDrawer.tsx b/apps/sensenet/src/components/drawer/PermanentDrawer.tsx index 6d1048ed7..43534bd8c 100644 --- a/apps/sensenet/src/components/drawer/PermanentDrawer.tsx +++ b/apps/sensenet/src/components/drawer/PermanentDrawer.tsx @@ -1,48 +1,26 @@ -import IconButton from '@material-ui/core/IconButton' import List from '@material-ui/core/List' import ListItem from '@material-ui/core/ListItem' import ListItemIcon from '@material-ui/core/ListItemIcon' -import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction' import ListItemText from '@material-ui/core/ListItemText' import Paper from '@material-ui/core/Paper' import Tooltip from '@material-ui/core/Tooltip' -import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft' -import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight' -import Settings from '@material-ui/icons/Settings' -import { PathHelper } from '@sensenet/client-utils' -import React, { useContext, useEffect, useState } from 'react' +import { Close, Menu } from '@material-ui/icons' +import React, { useContext, useState } from 'react' import { withRouter } from 'react-router' -import { Link, matchPath, NavLink, RouteComponentProps } from 'react-router-dom' -import { useRepository, useSession } from '@sensenet/hooks-react' -import { ResponsiveContext, ResponsivePersonalSetttings } from '../../context' -import { useDrawerItems, useLocalization, usePersonalSettings, useTheme } from '../../hooks' -import { LogoutButton } from '../LogoutButton' -import { UserAvatar } from '../UserAvatar' +import { matchPath, NavLink, RouteComponentProps } from 'react-router-dom' +import { useRepository } from '@sensenet/hooks-react' +import { ResponsivePersonalSetttings } from '../../context' +import { useDrawerItems, useLocalization, useTheme } from '../../hooks' const PermanentDrawer: React.FunctionComponent = props => { const settings = useContext(ResponsivePersonalSetttings) - const personalSettings = usePersonalSettings() const theme = useTheme() - const session = useSession() const repo = useRepository() - const device = useContext(ResponsiveContext) const [opened, setOpened] = useState(settings.drawer.type === 'permanent') const items = useDrawerItems() const localization = useLocalization().drawer - const [currentRepoEntry, setCurrentRepoEntry] = useState( - personalSettings.repositories.find(r => r.url === PathHelper.trimSlashes(repo.configuration.repositoryUrl)), - ) - - useEffect( - () => - setCurrentRepoEntry( - personalSettings.repositories.find(r => r.url === PathHelper.trimSlashes(repo.configuration.repositoryUrl)), - ), - [personalSettings, repo], - ) - if (!settings.drawer.enabled) { return null } @@ -63,6 +41,15 @@ const PermanentDrawer: React.FunctionComponent = props => { transition: 'width 100ms ease-in-out', }}>
+
+ {settings.drawer.type === 'mini-variant' ? ( + setOpened(!opened)} key="expandcollapse"> + + {opened ? : } + + + ) : null} +
{items.map((item, index) => { const isActive = matchPath(props.location.pathname, `/:repositoryId${item.url}`) return isActive ? ( @@ -100,71 +87,6 @@ const PermanentDrawer: React.FunctionComponent = props => { ) })}
-
- {opened ? ( - - - - - - - - {device === 'mobile' ? null : ( - - - - - - )} - - - - - ) : ( - <> - - - - {localization.personalSettingsTitle}
{localization.personalSettingsSecondaryText} - - } - placement="right"> - - - -
-
-
- - - )} - - {settings.drawer.type === 'mini-variant' ? ( - setOpened(!opened)} key="expandcollapse"> - - {opened ? : } - - {opened ? : null} - - ) : null} -
) diff --git a/apps/sensenet/src/components/login/Login.tsx b/apps/sensenet/src/components/login/Login.tsx index 96e5471af..25beffc95 100644 --- a/apps/sensenet/src/components/login/Login.tsx +++ b/apps/sensenet/src/components/login/Login.tsx @@ -11,6 +11,7 @@ import { useHistory, useLocation, useRouteMatch } from 'react-router' import { useLocalization, useTheme } from '../../hooks' import { PersonalSettings, PersonalSettingsType } from '../../services/PersonalSettings' import { UserAvatar } from '../UserAvatar' +import { useThemeService } from '../../hooks/use-theme-service' import { DemoUser, InfoBox } from './info-box' const useStyles = makeStyles((theme: Theme) => @@ -25,6 +26,7 @@ const useStyles = makeStyles((theme: Theme) => ) export const Login = () => { + const themeService = useThemeService() const injector = useInjector() const history = useHistory() const match = useRouteMatch() @@ -41,6 +43,7 @@ export const Login = () => { const repositories: PersonalSettingsType['repositories'] = personalSettings.repositories || [] const existingRepo = repositories.find(r => r.url === repo.configuration.repositoryUrl) + themeService.currentTheme.setValue(personalSettings.theme) const [userName, setUserName] = useState((existingRepo && existingRepo.loginName) || '') const [password, setPassword] = useState('') diff --git a/apps/sensenet/src/context/ThemeProvider.tsx b/apps/sensenet/src/context/ThemeProvider.tsx index fda9772ec..f29c27651 100644 --- a/apps/sensenet/src/context/ThemeProvider.tsx +++ b/apps/sensenet/src/context/ThemeProvider.tsx @@ -1,10 +1,12 @@ -import createMuiTheme, { ThemeOptions } from '@material-ui/core/styles/createMuiTheme' +import createMuiTheme, { Theme, ThemeOptions } from '@material-ui/core/styles/createMuiTheme' import { MuiThemeProvider } from '@material-ui/core/styles' -import React, { useContext } from 'react' -import { ResponsivePersonalSetttings } from './ResponsiveContextProvider' +import React, { useEffect, useState } from 'react' +import { useInjector } from '@sensenet/hooks-react/src' +import { PersonalSettings } from '../services' +import { useThemeService } from '../hooks/use-theme-service' import { ThemeContext } from './ThemeContext' -const mergeThemes = (options: ThemeOptions, type: 'light' | 'dark') => +const mergeThemes = (options: ThemeOptions, type: 'light' | 'dark' | undefined) => createMuiTheme({ ...options, palette: { @@ -14,11 +16,20 @@ const mergeThemes = (options: ThemeOptions, type: 'light' | 'dark') => }) export const ThemeProvider: React.FunctionComponent<{ theme: ThemeOptions }> = props => { - const ps = useContext(ResponsivePersonalSetttings) - const theme = mergeThemes(props.theme, ps.theme) + const injector = useInjector() + const service = injector.getInstance(PersonalSettings) + const settings = service.userValue.getValue() + const [pageTheme, setPageTheme] = useState(mergeThemes(props.theme, settings.theme)) + const themeService = useThemeService() + + useEffect(() => { + themeService.currentTheme.subscribe(currentTheme => setPageTheme(mergeThemes(props.theme, currentTheme))) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + return ( - - {props.children} + + {props.children} ) } diff --git a/apps/sensenet/src/hooks/use-theme-service.ts b/apps/sensenet/src/hooks/use-theme-service.ts new file mode 100644 index 000000000..7ef9ac1fc --- /dev/null +++ b/apps/sensenet/src/hooks/use-theme-service.ts @@ -0,0 +1,9 @@ +import { useState } from 'react' +import { useInjector } from '@sensenet/hooks-react' +import { ThemeService } from '../services/ThemeService' + +export const useThemeService = () => { + const injector = useInjector() + const [themeService] = useState(injector.getInstance(ThemeService)) + return themeService +} diff --git a/apps/sensenet/src/localization/default.ts b/apps/sensenet/src/localization/default.ts index cefab4dc8..edf3e1c0d 100644 --- a/apps/sensenet/src/localization/default.ts +++ b/apps/sensenet/src/localization/default.ts @@ -264,7 +264,7 @@ const values = { topMenu: { changeRepo: 'Change repository', personalSettings: 'Personal settings', - logout: 'Logout', + logout: 'Log out', }, navigationCommandProvider: { personalSettingsPrimary: 'Personal Settings', diff --git a/apps/sensenet/src/services/PersonalSettings.ts b/apps/sensenet/src/services/PersonalSettings.ts index 73f13daa0..3b42cfdd0 100644 --- a/apps/sensenet/src/services/PersonalSettings.ts +++ b/apps/sensenet/src/services/PersonalSettings.ts @@ -6,7 +6,6 @@ import { BrowseType } from '../components/content' const settingsKey = `SN-APP-USER-SETTINGS` export interface UiSettings { - theme: 'dark' | 'light' content: { browseType: typeof BrowseType[number] fields: Array @@ -150,6 +149,7 @@ export type PersonalSettingsType = PlatformDependent & { sendLogWithCrashReports: boolean logLevel: Array language: 'default' | 'hungarian' + theme: 'light' | 'dark' } export const defaultSettings: PersonalSettingsType = { @@ -284,7 +284,6 @@ export const defaultSettings: PersonalSettingsType = { ], }, default: { - theme: 'dark', content: { browseType: 'explorer', fields: ['DisplayName', 'Locked', 'CreatedBy', 'Actions'], @@ -339,6 +338,7 @@ export const defaultSettings: PersonalSettingsType = { eventLogSize: 500, sendLogWithCrashReports: true, logLevel: ['Information', 'Warning', 'Error', 'Fatal'], + theme: 'light', } @Injectable({ lifetime: 'singleton' }) @@ -406,4 +406,13 @@ export class PersonalSettings { this.effectiveValue.setValue(deepMerge(defaultSettings, settings)) localStorage.setItem(`${settingsKey}`, JSON.stringify(settings)) } + + public setPersonalSettingsTheme(theme: 'dark' | 'light' | undefined) { + const currentValuesCopy: Partial = this.userValue.getValue() + currentValuesCopy.theme = theme ? theme : 'light' + + this.userValue.setValue(currentValuesCopy) + this.effectiveValue.setValue(deepMerge(defaultSettings, currentValuesCopy)) + localStorage.setItem(`${settingsKey}`, JSON.stringify(currentValuesCopy)) + } } diff --git a/apps/sensenet/src/services/ThemeService.ts b/apps/sensenet/src/services/ThemeService.ts new file mode 100644 index 000000000..3e417dc91 --- /dev/null +++ b/apps/sensenet/src/services/ThemeService.ts @@ -0,0 +1,28 @@ +import { Disposable, Injectable, Injector, ObservableValue, ScopedLogger } from '@sensenet/client-utils' + +/** + * A context service to get/set the active theme + */ +@Injectable({ lifetime: 'singleton' }) +export class ThemeService implements Disposable { + public currentTheme = new ObservableValue<'dark' | 'light' | undefined>() + + private logger: ScopedLogger + + public async dispose() { + this.currentTheme.dispose() + } + + constructor(injector: Injector) { + this.logger = injector.logger.withScope('SelectionService') + + this.currentTheme.subscribe(currentTheme => + this.logger.verbose({ + message: currentTheme ? `Current theme changed to ${currentTheme}` : `Current theme set to None`, + data: { + relatedContent: currentTheme, + }, + }), + ) + } +} diff --git a/apps/sensenet/src/services/index.ts b/apps/sensenet/src/services/index.ts index 6108bb185..a9312c705 100644 --- a/apps/sensenet/src/services/index.ts +++ b/apps/sensenet/src/services/index.ts @@ -8,5 +8,6 @@ export * from './LocalizationService' export * from './PersonalSettings' export * from './RepositoryManager' export * from './SelectionService' +export * from './ThemeService' export * from './UploadTracker' export * from './request-counter-service' From 021e215a0eafcaa875784eb61b658693b1f7c6ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enik=C5=91=20Pusztai?= Date: Tue, 7 Jan 2020 17:14:20 +0100 Subject: [PATCH 3/7] change repo was duplicated --- apps/sensenet/src/components/appbar/DesktopNavMenu.tsx | 5 ----- apps/sensenet/src/localization/default.ts | 1 - apps/sensenet/src/localization/hungarian.ts | 1 - 3 files changed, 7 deletions(-) diff --git a/apps/sensenet/src/components/appbar/DesktopNavMenu.tsx b/apps/sensenet/src/components/appbar/DesktopNavMenu.tsx index 0c47681cf..ab74c6b11 100644 --- a/apps/sensenet/src/components/appbar/DesktopNavMenu.tsx +++ b/apps/sensenet/src/components/appbar/DesktopNavMenu.tsx @@ -165,11 +165,6 @@ export const DesktopNavMenu: React.FunctionComponent = () => { primary={`${session.currentUser.DisplayName || session.currentUser.Name} user`} /> - - - {localization.topMenu.changeRepo}{' '} - - {localization.topMenu.personalSettings} diff --git a/apps/sensenet/src/localization/default.ts b/apps/sensenet/src/localization/default.ts index edf3e1c0d..3da44bb0d 100644 --- a/apps/sensenet/src/localization/default.ts +++ b/apps/sensenet/src/localization/default.ts @@ -262,7 +262,6 @@ const values = { saveFailedNotification: `Failed to save changes of content '{0}'`, }, topMenu: { - changeRepo: 'Change repository', personalSettings: 'Personal settings', logout: 'Log out', }, diff --git a/apps/sensenet/src/localization/hungarian.ts b/apps/sensenet/src/localization/hungarian.ts index 11619013b..9ff18c02b 100644 --- a/apps/sensenet/src/localization/hungarian.ts +++ b/apps/sensenet/src/localization/hungarian.ts @@ -105,7 +105,6 @@ const values: DeepPartial = { typeToFilter: 'Kezdj írni a szűréshez...', }, topMenu: { - changeRepo: 'Másik repository', personalSettings: 'Személyes beállítások', logout: 'Kijelentkezés', }, From 76832a20ce68cebef515fe4972b5abb5e89f51ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enik=C5=91=20Pusztai?= Date: Wed, 8 Jan 2020 14:28:03 +0100 Subject: [PATCH 4/7] Refactor: remove themeService and setPersonalSettingsTheme --- .../src/components/appbar/DesktopNavMenu.tsx | 20 +++++-------- apps/sensenet/src/components/login/Login.tsx | 3 -- apps/sensenet/src/context/ThemeProvider.tsx | 9 +++--- apps/sensenet/src/hooks/use-theme-service.ts | 9 ------ .../sensenet/src/services/PersonalSettings.ts | 9 ------ apps/sensenet/src/services/ThemeService.ts | 28 ------------------- apps/sensenet/src/services/index.ts | 1 - 7 files changed, 11 insertions(+), 68 deletions(-) delete mode 100644 apps/sensenet/src/hooks/use-theme-service.ts delete mode 100644 apps/sensenet/src/services/ThemeService.ts diff --git a/apps/sensenet/src/components/appbar/DesktopNavMenu.tsx b/apps/sensenet/src/components/appbar/DesktopNavMenu.tsx index f9431724c..eb730c7eb 100644 --- a/apps/sensenet/src/components/appbar/DesktopNavMenu.tsx +++ b/apps/sensenet/src/components/appbar/DesktopNavMenu.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react' +import React from 'react' import ClickAwayListener from '@material-ui/core/ClickAwayListener' import { Grid, @@ -18,10 +18,9 @@ import KeyboardArrowDown from '@material-ui/icons/KeyboardArrowDown' import { useInjector, useRepository, useSession } from '@sensenet/hooks-react' import { NavLink } from 'react-router-dom' import { UserAvatar } from '../UserAvatar' -import { useLocalization } from '../../hooks' +import { useLocalization, usePersonalSettings } from '../../hooks' import { useDialog } from '../dialogs' import { PersonalSettings } from '../../services' -import { useThemeService } from '../../hooks/use-theme-service' const AntSwitch = withStyles((theme: Theme) => createStyles({ @@ -72,7 +71,7 @@ const useStyles = makeStyles((theme: Theme) => ) export const DesktopNavMenu: React.FunctionComponent = () => { - const themeService = useThemeService() + const personalSettings = usePersonalSettings() const injector = useInjector() const classes = useStyles() const theme = useTheme() @@ -83,12 +82,6 @@ export const DesktopNavMenu: React.FunctionComponent = () => { const session = useSession() const repo = useRepository() const localization = useLocalization() - const [pageTheme, setPageTheme] = useState<'dark' | 'light' | undefined>(themeService.currentTheme.getValue()) - - useEffect(() => { - service.setPersonalSettingsTheme(pageTheme) - themeService.currentTheme.setValue(pageTheme) - }, [pageTheme, service, themeService.currentTheme]) const handleToggle = () => { setOpen(prevOpen => !prevOpen) @@ -124,7 +117,8 @@ export const DesktopNavMenu: React.FunctionComponent = () => { } const switchTheme = () => (event: React.ChangeEvent) => { - setPageTheme(event.target.checked ? 'dark' : 'light') + const settings = service.userValue.getValue() + service.setPersonalSettingsValue({ ...settings, theme: event.target.checked ? 'dark' : 'light' }) } return ( @@ -177,10 +171,10 @@ export const DesktopNavMenu: React.FunctionComponent = () => { - {pageTheme === 'dark' ? 'Light theme' : 'Dark theme'} + {personalSettings.theme === 'dark' ? 'Light theme' : 'Dark theme'} - + diff --git a/apps/sensenet/src/components/login/Login.tsx b/apps/sensenet/src/components/login/Login.tsx index 25beffc95..96e5471af 100644 --- a/apps/sensenet/src/components/login/Login.tsx +++ b/apps/sensenet/src/components/login/Login.tsx @@ -11,7 +11,6 @@ import { useHistory, useLocation, useRouteMatch } from 'react-router' import { useLocalization, useTheme } from '../../hooks' import { PersonalSettings, PersonalSettingsType } from '../../services/PersonalSettings' import { UserAvatar } from '../UserAvatar' -import { useThemeService } from '../../hooks/use-theme-service' import { DemoUser, InfoBox } from './info-box' const useStyles = makeStyles((theme: Theme) => @@ -26,7 +25,6 @@ const useStyles = makeStyles((theme: Theme) => ) export const Login = () => { - const themeService = useThemeService() const injector = useInjector() const history = useHistory() const match = useRouteMatch() @@ -43,7 +41,6 @@ export const Login = () => { const repositories: PersonalSettingsType['repositories'] = personalSettings.repositories || [] const existingRepo = repositories.find(r => r.url === repo.configuration.repositoryUrl) - themeService.currentTheme.setValue(personalSettings.theme) const [userName, setUserName] = useState((existingRepo && existingRepo.loginName) || '') const [password, setPassword] = useState('') diff --git a/apps/sensenet/src/context/ThemeProvider.tsx b/apps/sensenet/src/context/ThemeProvider.tsx index f29c27651..6252e2e4e 100644 --- a/apps/sensenet/src/context/ThemeProvider.tsx +++ b/apps/sensenet/src/context/ThemeProvider.tsx @@ -3,7 +3,7 @@ import { MuiThemeProvider } from '@material-ui/core/styles' import React, { useEffect, useState } from 'react' import { useInjector } from '@sensenet/hooks-react/src' import { PersonalSettings } from '../services' -import { useThemeService } from '../hooks/use-theme-service' +import { usePersonalSettings } from '../hooks' import { ThemeContext } from './ThemeContext' const mergeThemes = (options: ThemeOptions, type: 'light' | 'dark' | undefined) => @@ -20,12 +20,11 @@ export const ThemeProvider: React.FunctionComponent<{ theme: ThemeOptions }> = p const service = injector.getInstance(PersonalSettings) const settings = service.userValue.getValue() const [pageTheme, setPageTheme] = useState(mergeThemes(props.theme, settings.theme)) - const themeService = useThemeService() + const personalSettings = usePersonalSettings() useEffect(() => { - themeService.currentTheme.subscribe(currentTheme => setPageTheme(mergeThemes(props.theme, currentTheme))) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) + setPageTheme(mergeThemes(props.theme, personalSettings.theme)) + }, [personalSettings.theme, props.theme]) return ( diff --git a/apps/sensenet/src/hooks/use-theme-service.ts b/apps/sensenet/src/hooks/use-theme-service.ts deleted file mode 100644 index 7ef9ac1fc..000000000 --- a/apps/sensenet/src/hooks/use-theme-service.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { useState } from 'react' -import { useInjector } from '@sensenet/hooks-react' -import { ThemeService } from '../services/ThemeService' - -export const useThemeService = () => { - const injector = useInjector() - const [themeService] = useState(injector.getInstance(ThemeService)) - return themeService -} diff --git a/apps/sensenet/src/services/PersonalSettings.ts b/apps/sensenet/src/services/PersonalSettings.ts index dcee10704..5bbb4e7d9 100644 --- a/apps/sensenet/src/services/PersonalSettings.ts +++ b/apps/sensenet/src/services/PersonalSettings.ts @@ -410,13 +410,4 @@ export class PersonalSettings { this.effectiveValue.setValue(deepMerge(defaultSettings, settings)) localStorage.setItem(`${settingsKey}`, JSON.stringify(settings)) } - - public setPersonalSettingsTheme(theme: 'dark' | 'light' | undefined) { - const currentValuesCopy: Partial = this.userValue.getValue() - currentValuesCopy.theme = theme ? theme : 'light' - - this.userValue.setValue(currentValuesCopy) - this.effectiveValue.setValue(deepMerge(defaultSettings, currentValuesCopy)) - localStorage.setItem(`${settingsKey}`, JSON.stringify(currentValuesCopy)) - } } diff --git a/apps/sensenet/src/services/ThemeService.ts b/apps/sensenet/src/services/ThemeService.ts deleted file mode 100644 index 3e417dc91..000000000 --- a/apps/sensenet/src/services/ThemeService.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Disposable, Injectable, Injector, ObservableValue, ScopedLogger } from '@sensenet/client-utils' - -/** - * A context service to get/set the active theme - */ -@Injectable({ lifetime: 'singleton' }) -export class ThemeService implements Disposable { - public currentTheme = new ObservableValue<'dark' | 'light' | undefined>() - - private logger: ScopedLogger - - public async dispose() { - this.currentTheme.dispose() - } - - constructor(injector: Injector) { - this.logger = injector.logger.withScope('SelectionService') - - this.currentTheme.subscribe(currentTheme => - this.logger.verbose({ - message: currentTheme ? `Current theme changed to ${currentTheme}` : `Current theme set to None`, - data: { - relatedContent: currentTheme, - }, - }), - ) - } -} diff --git a/apps/sensenet/src/services/index.ts b/apps/sensenet/src/services/index.ts index a9312c705..6108bb185 100644 --- a/apps/sensenet/src/services/index.ts +++ b/apps/sensenet/src/services/index.ts @@ -8,6 +8,5 @@ export * from './LocalizationService' export * from './PersonalSettings' export * from './RepositoryManager' export * from './SelectionService' -export * from './ThemeService' export * from './UploadTracker' export * from './request-counter-service' From 75a68c0f80510ff3e2131249759cabf8b898b160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enik=C5=91=20Pusztai?= Date: Wed, 8 Jan 2020 15:15:58 +0100 Subject: [PATCH 5/7] AntSwitch has been moved into a new component --- apps/sensenet/src/components/ant-switch.ts | 35 ++++++++++++++++++ .../src/components/appbar/DesktopNavMenu.tsx | 37 +------------------ 2 files changed, 37 insertions(+), 35 deletions(-) create mode 100644 apps/sensenet/src/components/ant-switch.ts diff --git a/apps/sensenet/src/components/ant-switch.ts b/apps/sensenet/src/components/ant-switch.ts new file mode 100644 index 000000000..9799e9c39 --- /dev/null +++ b/apps/sensenet/src/components/ant-switch.ts @@ -0,0 +1,35 @@ +import { createStyles, Theme, withStyles } from '@material-ui/core/styles' +import { Switch } from '@material-ui/core' + +export const AntSwitch = withStyles((theme: Theme) => + createStyles({ + root: { + width: 28, + height: 16, + padding: 0, + display: 'flex', + }, + switchBase: { + padding: 2, + color: theme.palette.common.white, + opacity: '1', + '&$checked': { + transform: 'translateX(12px)', + color: theme.palette.common.black, + '& + $track': { + opacity: 1, + backgroundColor: theme.palette.common.white, + }, + }, + }, + thumb: { + width: 12, + height: 12, + boxShadow: 'none', + }, + track: { + opacity: 1, + }, + checked: {}, + }), +)(Switch) diff --git a/apps/sensenet/src/components/appbar/DesktopNavMenu.tsx b/apps/sensenet/src/components/appbar/DesktopNavMenu.tsx index eb730c7eb..17ff2dcae 100644 --- a/apps/sensenet/src/components/appbar/DesktopNavMenu.tsx +++ b/apps/sensenet/src/components/appbar/DesktopNavMenu.tsx @@ -10,10 +10,9 @@ import { MenuList, Paper, Popper, - Switch, Typography, } from '@material-ui/core' -import { createStyles, makeStyles, Theme, useTheme, withStyles } from '@material-ui/core/styles' +import { createStyles, makeStyles, Theme, useTheme } from '@material-ui/core/styles' import KeyboardArrowDown from '@material-ui/icons/KeyboardArrowDown' import { useInjector, useRepository, useSession } from '@sensenet/hooks-react' import { NavLink } from 'react-router-dom' @@ -21,39 +20,7 @@ import { UserAvatar } from '../UserAvatar' import { useLocalization, usePersonalSettings } from '../../hooks' import { useDialog } from '../dialogs' import { PersonalSettings } from '../../services' - -const AntSwitch = withStyles((theme: Theme) => - createStyles({ - root: { - width: 28, - height: 16, - padding: 0, - display: 'flex', - }, - switchBase: { - padding: 2, - color: theme.palette.common.white, - opacity: '1', - '&$checked': { - transform: 'translateX(12px)', - color: theme.palette.common.black, - '& + $track': { - opacity: 1, - backgroundColor: theme.palette.common.white, - }, - }, - }, - thumb: { - width: 12, - height: 12, - boxShadow: 'none', - }, - track: { - opacity: 1, - }, - checked: {}, - }), -)(Switch) +import { AntSwitch } from '../../components/ant-switch' const useStyles = makeStyles((theme: Theme) => createStyles({ From cfef07c059d87197944c9360d98a21185ea11eb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enik=C5=91=20Pusztai?= Date: Fri, 10 Jan 2020 21:51:02 +0100 Subject: [PATCH 6/7] remove injector and other unneccesary variables from ThemeProvider --- apps/sensenet/src/context/ThemeProvider.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/apps/sensenet/src/context/ThemeProvider.tsx b/apps/sensenet/src/context/ThemeProvider.tsx index 6252e2e4e..32d0cafb5 100644 --- a/apps/sensenet/src/context/ThemeProvider.tsx +++ b/apps/sensenet/src/context/ThemeProvider.tsx @@ -1,8 +1,6 @@ import createMuiTheme, { Theme, ThemeOptions } from '@material-ui/core/styles/createMuiTheme' import { MuiThemeProvider } from '@material-ui/core/styles' import React, { useEffect, useState } from 'react' -import { useInjector } from '@sensenet/hooks-react/src' -import { PersonalSettings } from '../services' import { usePersonalSettings } from '../hooks' import { ThemeContext } from './ThemeContext' @@ -16,11 +14,8 @@ const mergeThemes = (options: ThemeOptions, type: 'light' | 'dark' | undefined) }) export const ThemeProvider: React.FunctionComponent<{ theme: ThemeOptions }> = props => { - const injector = useInjector() - const service = injector.getInstance(PersonalSettings) - const settings = service.userValue.getValue() - const [pageTheme, setPageTheme] = useState(mergeThemes(props.theme, settings.theme)) const personalSettings = usePersonalSettings() + const [pageTheme, setPageTheme] = useState(mergeThemes(props.theme, personalSettings.theme)) useEffect(() => { setPageTheme(mergeThemes(props.theme, personalSettings.theme)) From e0ba9f9d60db08bdecb4ccc2a36bb0af1b793432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enik=C5=91=20Pusztai?= Date: Mon, 13 Jan 2020 14:35:04 +0100 Subject: [PATCH 7/7] themeswitcher has been fixed --- apps/sensenet/src/components/theme.ts | 2 +- apps/sensenet/src/context/ThemeProvider.tsx | 46 ++++++++++++++------- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/apps/sensenet/src/components/theme.ts b/apps/sensenet/src/components/theme.ts index 402d8a5e5..2b5265d5d 100644 --- a/apps/sensenet/src/components/theme.ts +++ b/apps/sensenet/src/components/theme.ts @@ -3,7 +3,7 @@ import zIndex from '@material-ui/core/styles/zIndex' const theme: ThemeOptions = { palette: { - type: 'dark', + type: 'light', secondary: { light: '#90caf9', main: '#1976d2', diff --git a/apps/sensenet/src/context/ThemeProvider.tsx b/apps/sensenet/src/context/ThemeProvider.tsx index 32d0cafb5..227f2c107 100644 --- a/apps/sensenet/src/context/ThemeProvider.tsx +++ b/apps/sensenet/src/context/ThemeProvider.tsx @@ -1,29 +1,43 @@ -import createMuiTheme, { Theme, ThemeOptions } from '@material-ui/core/styles/createMuiTheme' +import createMuiTheme, { ThemeOptions } from '@material-ui/core/styles/createMuiTheme' import { MuiThemeProvider } from '@material-ui/core/styles' -import React, { useEffect, useState } from 'react' +import React, { useEffect, useMemo, useState } from 'react' +import useMediaQuery from '@material-ui/core/useMediaQuery' +import { useInjector } from '@sensenet/hooks-react' import { usePersonalSettings } from '../hooks' +import { PersonalSettings } from '../services' import { ThemeContext } from './ThemeContext' -const mergeThemes = (options: ThemeOptions, type: 'light' | 'dark' | undefined) => - createMuiTheme({ - ...options, - palette: { - ...options.palette, - type: type === 'light' ? 'light' : 'dark', - }, - }) - export const ThemeProvider: React.FunctionComponent<{ theme: ThemeOptions }> = props => { + const preferredType = useMediaQuery('(prefers-color-scheme: dark)') ? 'dark' : 'light' const personalSettings = usePersonalSettings() - const [pageTheme, setPageTheme] = useState(mergeThemes(props.theme, personalSettings.theme)) + const di = useInjector() + const settingsService = di.getInstance(PersonalSettings) + const [pageTheme, setPageTheme] = useState<'light' | 'dark'>(preferredType) + + useEffect(() => { + setPageTheme(preferredType) + const userValue = settingsService.userValue.getValue() + settingsService.setPersonalSettingsValue({ ...userValue, theme: preferredType }) + }, [preferredType, settingsService]) + + const theme = useMemo(() => { + const nextTheme = createMuiTheme({ + palette: { + ...props.theme.palette, + type: pageTheme, + }, + }) + + return nextTheme + }, [pageTheme, props.theme.palette]) useEffect(() => { - setPageTheme(mergeThemes(props.theme, personalSettings.theme)) - }, [personalSettings.theme, props.theme]) + setPageTheme(personalSettings.theme) + }, [personalSettings.theme]) return ( - - {props.children} + + {props.children} ) }