Move theme toggle to profile dropdown

api-accept
Justin 2 years ago
parent 4e7c5b5819
commit 210cee2b7a

@ -23,7 +23,7 @@ import {
FileChooserLogo,
Checkbox,
} from 'soapbox/features/forms';
import ThemeToggle from 'soapbox/features/ui/components/theme_toggle';
import ThemeToggle from 'soapbox/features/ui/components/theme-toggle';
import { isMobile } from 'soapbox/is_mobile';
import { normalizeSoapboxConfig } from 'soapbox/normalizers';

@ -6,8 +6,7 @@ import { Link } from 'react-router-dom';
import { Avatar, Button, Icon } from 'soapbox/components/ui';
import Search from 'soapbox/features/compose/components/search';
import ThemeToggle from 'soapbox/features/ui/components/theme_toggle';
import { useOwnAccount, useSoapboxConfig, useSettings, useFeatures } from 'soapbox/hooks';
import { useOwnAccount, useSoapboxConfig, useSettings } from 'soapbox/hooks';
import { openSidebar } from '../../../actions/sidebar';
@ -19,7 +18,6 @@ const Navbar = () => {
const account = useOwnAccount();
const settings = useSettings();
const features = useFeatures();
const soapboxConfig = useSoapboxConfig();
const singleUserMode = soapboxConfig.get('singleUserMode');
@ -69,11 +67,6 @@ const Navbar = () => {
</div>
<div className='absolute inset-y-0 right-0 flex items-center pr-2 lg:static lg:inset-auto lg:ml-6 lg:pr-0 space-x-3'>
{/* TODO: make this available for everyone when it's ready (possibly in a different place) */}
{(features.darkMode || settings.get('isDeveloper')) && (
<ThemeToggle />
)}
{account ? (
<div className='hidden relative lg:flex items-center'>
<ProfileDropdown account={account}>

@ -7,11 +7,13 @@ import { Link } from 'react-router-dom';
import { logOut, switchAccount } from 'soapbox/actions/auth';
import { fetchOwnAccounts } from 'soapbox/actions/auth';
import { Menu, MenuButton, MenuDivider, MenuItem, MenuLink, MenuList } from 'soapbox/components/ui';
import { useAppSelector } from 'soapbox/hooks';
import { useAppSelector, useFeatures, useSettings } from 'soapbox/hooks';
import { makeGetAccount } from 'soapbox/selectors';
import Account from '../../../components/account';
import ThemeToggle from './theme-toggle';
import type { Account as AccountEntity } from 'soapbox/types/entities';
const messages = defineMessages({
@ -24,9 +26,10 @@ interface IProfileDropdown {
}
type IMenuItem = {
text: string | React.ReactElement | null,
to?: string,
icon?: string,
text: string | React.ReactElement | null
to?: string
toggle?: JSX.Element
icon?: string
action?: (event: React.MouseEvent) => void
}
@ -34,6 +37,8 @@ const getAccount = makeGetAccount();
const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
const dispatch = useDispatch();
const features = useFeatures();
const settings = useSettings();
const intl = useIntl();
const authUsers = useAppSelector((state) => state.auth.get('users'));
@ -73,6 +78,12 @@ const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
}
});
if (features.darkMode || settings.get('isDeveloper')) {
menu.push({ text: null });
menu.push({ text: 'Theme', toggle: <ThemeToggle /> });
}
menu.push({ text: null });
menu.push({
@ -89,7 +100,7 @@ const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
});
return menu;
}, [account, authUsers]);
}, [account, authUsers, features]);
React.useEffect(() => {
fetchOwnAccountThrottled();
@ -103,7 +114,15 @@ const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
<MenuList>
{menu.map((menuItem, idx) => {
if (!menuItem.text) {
if (menuItem.toggle) {
return (
<div className='flex flex-row items-center justify-between px-4 py-1 text-sm text-gray-700 dark:text-gray-400'>
<span>{menuItem.text}</span>
{menuItem.toggle}
</div>
);
} else if (!menuItem.text) {
return <MenuDivider key={idx} />;
} else {
const Comp: any = menuItem.action ? MenuItem : MenuLink;

@ -0,0 +1,67 @@
import React, { useMemo } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { changeSetting } from 'soapbox/actions/settings';
import { Icon } from 'soapbox/components/ui';
import { useSettings } from 'soapbox/hooks';
const messages = defineMessages({
switchToLight: { id: 'tabs_bar.theme_toggle_light', defaultMessage: 'Switch to light theme' },
switchToDark: { id: 'tabs_bar.theme_toggle_dark', defaultMessage: 'Switch to dark theme' },
});
interface IThemeToggle {
showLabel?: boolean,
}
const ThemeToggle = ({ showLabel }: IThemeToggle) => {
const intl = useIntl();
const dispatch = useDispatch();
const themeMode = useSettings().get('themeMode');
const label = intl.formatMessage(themeMode === 'light' ? messages.switchToDark : messages.switchToLight);
const onToggle = (event: React.ChangeEvent<HTMLSelectElement>) => {
dispatch(changeSetting(['themeMode'], event.target.value));
};
const themeIconSrc = useMemo(() => {
switch (themeMode) {
case 'system':
return require('@tabler/icons/icons/device-desktop.svg');
case 'light':
return require('@tabler/icons/icons/sun.svg');
case 'dark':
return require('@tabler/icons/icons/moon.svg');
default:
return null;
}
}, [themeMode]);
return (
<label>
<div className='relative rounded-md shadow-sm'>
<div className='absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none'>
<Icon src={themeIconSrc} className='h-4 w-4 text-gray-400' />
</div>
<select
onChange={onToggle}
defaultValue={themeMode}
className='focus:ring-indigo-500 focus:border-indigo-500 dark:bg-slate-800 dark:border-gray-600 block w-full pl-8 pr-12 sm:text-sm border-gray-300 rounded-md'
>
<option value='system'>System</option>
<option value='light'>Light</option>
<option value='dark'>Dark</option>
</select>
<div className='absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none'>
<Icon src={require('@tabler/icons/icons/chevron-down.svg')} className='h-4 w-4 text-gray-400' />
</div>
</div>
</label>
);
};
export default ThemeToggle;

@ -1,51 +0,0 @@
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import Toggle from 'react-toggle';
import { v4 as uuidv4 } from 'uuid';
import { changeSetting } from 'soapbox/actions/settings';
import { Icon } from 'soapbox/components/ui';
import { useSettings } from 'soapbox/hooks';
const messages = defineMessages({
switchToLight: { id: 'tabs_bar.theme_toggle_light', defaultMessage: 'Switch to light theme' },
switchToDark: { id: 'tabs_bar.theme_toggle_dark', defaultMessage: 'Switch to dark theme' },
});
interface IThemeToggle {
showLabel?: boolean,
}
function ThemeToggle({ showLabel }: IThemeToggle) {
const intl = useIntl();
const dispatch = useDispatch();
const themeMode = useSettings().get('themeMode');
const id = uuidv4();
const label = intl.formatMessage(themeMode === 'light' ? messages.switchToDark : messages.switchToLight);
const onToggle = () => {
const setting = themeMode === 'light' ? 'dark' : 'light';
dispatch(changeSetting(['themeMode'], setting));
};
return (
<div className='theme-toggle'>
<div className='setting-toggle' aria-label={label}>
<Toggle
id={id}
checked={themeMode === 'light'}
icons={{
checked: <Icon className='w-4 h-4' src={require('@tabler/icons/icons/sun.svg')} />,
unchecked: <Icon className='w-4 h-4' src={require('@tabler/icons/icons/moon.svg')} />,
}}
onChange={onToggle}
/>
{showLabel && (<label htmlFor={id} className='setting-toggle__label'>{label}</label>)}
</div>
</div>
);
}
export default ThemeToggle;
Loading…
Cancel
Save