Customizable fonts
This commit is contained in:
10
src/App.tsx
10
src/App.tsx
@@ -19,6 +19,7 @@ import { Loading, MainLayout } from './components';
|
||||
import '@mantine/core/styles.css';
|
||||
import '@mantine/dates/styles.css';
|
||||
import '@mantine/notifications/styles.css';
|
||||
import 'react-fontpicker-ts/dist/index.css';
|
||||
|
||||
import '@gfazioli/mantine-text-animate/styles.css';
|
||||
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
||||
@@ -54,7 +55,7 @@ function App() {
|
||||
search: colorsTuple(theme.searchColor),
|
||||
},
|
||||
white: theme?.backgroundColor,
|
||||
fontFamily: '-apple-system,Jost,BlinkMacSystemFont,Helvetica Neue,Roboto,sans-serif',
|
||||
fontFamily: `${personalData?.[0]?.defaultFont},Helvetica Neue,Roboto,system-ui,sans-serif`,
|
||||
autoContrast: true,
|
||||
});
|
||||
const mantineTheme = mergeMantineTheme(DEFAULT_THEME, overrides);
|
||||
@@ -81,7 +82,12 @@ function App() {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (allThemes && themeName && personalData && isThemePlaylistsFetched) {
|
||||
if (
|
||||
allThemes &&
|
||||
themeName &&
|
||||
Object.keys(personalData).length > 0 &&
|
||||
isThemePlaylistsFetched
|
||||
) {
|
||||
if (currentThemePlaylist === 'All') {
|
||||
if (themeName === 'random') {
|
||||
handleThemeChange(allThemes, false);
|
||||
|
||||
@@ -12,6 +12,7 @@ import { useDebouncedValue } from '@mantine/hooks';
|
||||
import { SettingsHeader } from 'components';
|
||||
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import FontPicker from 'react-fontpicker-ts';
|
||||
import { cardClasses, switchClasses, textClasses, textInputClasses } from 'styles';
|
||||
import type { PersonalData } from 'types';
|
||||
|
||||
@@ -20,25 +21,44 @@ const PersonalizationSettings = () => {
|
||||
const { data: personalData, isFetching: personalDataIsFetching } = useQueryAPI({
|
||||
endpoint: endpoint,
|
||||
});
|
||||
const [personal, setPersonal] = useState<PersonalData>(personalData?.[0] || {});
|
||||
const personal: PersonalData = personalData?.[0] || {};
|
||||
const [wolEnabled, setWolEnabled] = useState<boolean>(personal?.enableWol === 1);
|
||||
const [guacamoleEnabled, setGuacamoleEnabled] = useState<boolean>(
|
||||
personal?.enableGuacamole === 1,
|
||||
);
|
||||
const [defaultFont, setDefaultFont] = useState<string>(personal?.defaultFont);
|
||||
const [update, setUpdate] = useState<any>({});
|
||||
const [debounced] = useDebouncedValue(update, 1000);
|
||||
const { mutateAsync } = useMutationAPI();
|
||||
const { isMobile, theme } = useCommonHooks();
|
||||
|
||||
useEffect(() => {
|
||||
if (Object.keys(debounced).length) {
|
||||
setPersonal(update);
|
||||
mutateAsync({ method: 'PATCH', endpoint: endpoint, body: debounced });
|
||||
setUpdate({});
|
||||
}
|
||||
}, [debounced]);
|
||||
|
||||
useEffect(() => {
|
||||
if (update !== personal && Object.keys(update).length) {
|
||||
setPersonal(update);
|
||||
if (Object.keys(personal).length > 0) {
|
||||
setWolEnabled(personal.enableWol === 1);
|
||||
setGuacamoleEnabled(personal.enableGuacamole === 1);
|
||||
setDefaultFont(personal.defaultFont);
|
||||
}
|
||||
}, [update]);
|
||||
}, [personal]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
Object.keys(personal).length > 0 &&
|
||||
defaultFont &&
|
||||
defaultFont !== personal.defaultFont
|
||||
) {
|
||||
setUpdate({
|
||||
...personal,
|
||||
defaultFont: defaultFont,
|
||||
});
|
||||
}
|
||||
}, [defaultFont]);
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<SettingsHeader
|
||||
@@ -76,163 +96,229 @@ const PersonalizationSettings = () => {
|
||||
}}
|
||||
/>
|
||||
<Grid w={'75%'}>
|
||||
<GridCol span={isMobile ? 12 : 4}>
|
||||
<Card className={cardClasses.settingsItem} classNames={cardClasses} pt={0}>
|
||||
<TextInput
|
||||
label={'First Name'}
|
||||
className={textInputClasses.settingsItem}
|
||||
classNames={textInputClasses}
|
||||
defaultValue={personal?.firstName}
|
||||
onChange={(event) => {
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
firstName: event.currentTarget.value,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
styles={
|
||||
theme.textShadowsEnabled
|
||||
? {
|
||||
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
</GridCol>
|
||||
<GridCol span={isMobile ? 12 : 4}>
|
||||
<Card className={cardClasses.settingsItem} classNames={cardClasses} pt={0}>
|
||||
<TextInput
|
||||
label={'Last Name'}
|
||||
className={textInputClasses.settingsItem}
|
||||
classNames={textInputClasses}
|
||||
defaultValue={personal?.lastName}
|
||||
onChange={(event) => {
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
lastName: event.currentTarget.value,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
styles={
|
||||
theme.textShadowsEnabled
|
||||
? {
|
||||
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
</GridCol>
|
||||
<GridCol span={isMobile ? 12 : 4}>
|
||||
<Card className={cardClasses.settingsItem} classNames={cardClasses} pt={0}>
|
||||
<TextInput
|
||||
label={'Email'}
|
||||
className={textInputClasses.settingsItem}
|
||||
classNames={textInputClasses}
|
||||
defaultValue={personal?.email}
|
||||
onChange={(event) => {
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
email: event.currentTarget.value,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
styles={
|
||||
theme.textShadowsEnabled
|
||||
? {
|
||||
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
</GridCol>
|
||||
<GridCol span={12}>
|
||||
<Card className={cardClasses.settingsItem} classNames={cardClasses} pt={0}>
|
||||
<TextInput
|
||||
label={'Physical Address'}
|
||||
className={textInputClasses.settingsItem}
|
||||
classNames={textInputClasses}
|
||||
defaultValue={personal?.physicalAddress}
|
||||
onChange={(event) => {
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
physicalAddress: event.currentTarget.value,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
styles={
|
||||
theme.textShadowsEnabled
|
||||
? {
|
||||
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
</GridCol>
|
||||
{Object.keys(personal).length > 0 && (
|
||||
<>
|
||||
<GridCol span={isMobile ? 12 : 4}>
|
||||
<Card
|
||||
className={cardClasses.settingsItem}
|
||||
classNames={cardClasses}
|
||||
pt={0}
|
||||
>
|
||||
<TextInput
|
||||
label={'First Name'}
|
||||
className={textInputClasses.settingsItem}
|
||||
classNames={textInputClasses}
|
||||
defaultValue={personal?.firstName}
|
||||
onChange={(event) => {
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
firstName: event.currentTarget.value,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
styles={
|
||||
theme.textShadowsEnabled
|
||||
? {
|
||||
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
</GridCol>
|
||||
<GridCol span={isMobile ? 12 : 4}>
|
||||
<Card
|
||||
className={cardClasses.settingsItem}
|
||||
classNames={cardClasses}
|
||||
pt={0}
|
||||
>
|
||||
<TextInput
|
||||
label={'Last Name'}
|
||||
className={textInputClasses.settingsItem}
|
||||
classNames={textInputClasses}
|
||||
defaultValue={personal?.lastName}
|
||||
onChange={(event) => {
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
lastName: event.currentTarget.value,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
styles={
|
||||
theme.textShadowsEnabled
|
||||
? {
|
||||
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
</GridCol>
|
||||
<GridCol span={isMobile ? 12 : 4}>
|
||||
<Card
|
||||
className={cardClasses.settingsItem}
|
||||
classNames={cardClasses}
|
||||
pt={0}
|
||||
>
|
||||
<TextInput
|
||||
label={'Email'}
|
||||
className={textInputClasses.settingsItem}
|
||||
classNames={textInputClasses}
|
||||
defaultValue={personal?.email}
|
||||
onChange={(event) => {
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
email: event.currentTarget.value,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
styles={
|
||||
theme.textShadowsEnabled
|
||||
? {
|
||||
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
</GridCol>
|
||||
<GridCol span={12}>
|
||||
<Card
|
||||
className={cardClasses.settingsItem}
|
||||
classNames={cardClasses}
|
||||
pt={0}
|
||||
>
|
||||
<TextInput
|
||||
label={'Nickname'}
|
||||
className={textInputClasses.settingsItem}
|
||||
classNames={textInputClasses}
|
||||
defaultValue={personal?.nickname || ''}
|
||||
onChange={(event) => {
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
nickname: event.currentTarget.value,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
styles={
|
||||
theme.textShadowsEnabled
|
||||
? {
|
||||
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
</GridCol>
|
||||
<GridCol span={12}>
|
||||
<Card
|
||||
className={cardClasses.settingsItem}
|
||||
classNames={cardClasses}
|
||||
pt={0}
|
||||
>
|
||||
<TextInput
|
||||
label={'Physical Address'}
|
||||
className={textInputClasses.settingsItem}
|
||||
classNames={textInputClasses}
|
||||
defaultValue={personal?.physicalAddress}
|
||||
onChange={(event) => {
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
physicalAddress: event.currentTarget.value,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
styles={
|
||||
theme.textShadowsEnabled
|
||||
? {
|
||||
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
</GridCol>
|
||||
<GridCol span={12}>
|
||||
<Card
|
||||
w={'100%'}
|
||||
className={cardClasses.settingsItem}
|
||||
classNames={cardClasses}
|
||||
style={{ overflow: 'visible' }}
|
||||
>
|
||||
<Stack w={'100%'} px={16}>
|
||||
<Text
|
||||
top={-12}
|
||||
className={textClasses.currentTheme}
|
||||
classNames={textClasses}
|
||||
>
|
||||
Extras
|
||||
</Text>
|
||||
<Text>Font Select</Text>
|
||||
{defaultFont && (
|
||||
<FontPicker
|
||||
autoLoad
|
||||
defaultValue={defaultFont || 'Jost'}
|
||||
value={(font: string) => setDefaultFont(font)}
|
||||
/>
|
||||
)}
|
||||
<Switch
|
||||
label={'Enable Wake on LAN'}
|
||||
className={switchClasses.default}
|
||||
classNames={switchClasses}
|
||||
checked={wolEnabled}
|
||||
onChange={(event) => {
|
||||
setWolEnabled(!wolEnabled);
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
enableWol: event.target.checked ? 1 : 0,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
/>
|
||||
<Switch
|
||||
label={'Enable Guacamole Clients'}
|
||||
className={switchClasses.default}
|
||||
classNames={switchClasses}
|
||||
checked={guacamoleEnabled}
|
||||
onChange={(event) => {
|
||||
setGuacamoleEnabled(!guacamoleEnabled);
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
enableGuacamole: event.target.checked ? 1 : 0,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
/>
|
||||
{personal?.enableGuacamole === 1 && (
|
||||
<TextInput
|
||||
label={'Guacamole Host'}
|
||||
className={textInputClasses.settingsItem}
|
||||
classNames={textInputClasses}
|
||||
defaultValue={personal?.guacamoleHost}
|
||||
onChange={(event) => {
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
guacamoleHost: event.currentTarget.value,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
styles={
|
||||
theme.textShadowsEnabled
|
||||
? {
|
||||
label: {
|
||||
boxShadow: '2px 2px var(--mantine-color-border-0)',
|
||||
},
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
</Card>
|
||||
</GridCol>
|
||||
</>
|
||||
)}
|
||||
</Grid>
|
||||
<Card
|
||||
w={'75%'}
|
||||
className={cardClasses.settingsItem}
|
||||
classNames={cardClasses}
|
||||
style={{ overflow: 'visible' }}
|
||||
>
|
||||
<Stack w={'100%'} px={16}>
|
||||
<Text top={-12} className={textClasses.currentTheme} classNames={textClasses}>
|
||||
Extras
|
||||
</Text>
|
||||
<Switch
|
||||
label={'Enable Wake on LAN'}
|
||||
className={switchClasses.default}
|
||||
classNames={switchClasses}
|
||||
checked={personal?.enableWol === 1}
|
||||
onChange={(event) => {
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
enableWol: event.target.checked ? 1 : 0,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
/>
|
||||
<Switch
|
||||
label={'Enable Guacamole Clients'}
|
||||
className={switchClasses.default}
|
||||
classNames={switchClasses}
|
||||
checked={personal?.enableGuacamole === 1}
|
||||
onChange={(event) => {
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
enableGuacamole: event.target.checked ? 1 : 0,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
/>
|
||||
{personal?.enableGuacamole === 1 && (
|
||||
<TextInput
|
||||
label={'Guacamole Host'}
|
||||
className={textInputClasses.settingsItem}
|
||||
classNames={textInputClasses}
|
||||
defaultValue={personal?.guacamoleHost}
|
||||
onChange={(event) => {
|
||||
const updatedPersonal = {
|
||||
...personal,
|
||||
guacamoleHost: event.currentTarget.value,
|
||||
};
|
||||
setUpdate(updatedPersonal);
|
||||
}}
|
||||
styles={
|
||||
theme.textShadowsEnabled
|
||||
? {
|
||||
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
</Card>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
.dateTime {
|
||||
&.root {
|
||||
display: flex;
|
||||
font-family: -apple-system, Jost, BlinkMacSystemFont, Helvetica Neue, Roboto,
|
||||
sans-serif !important;
|
||||
font-size: 1.75rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -78,7 +76,6 @@
|
||||
font-size: .6875rem;
|
||||
font-style: normal;
|
||||
color: var(--mantine-color-primary-0);
|
||||
font-family: "Jost", sans-serif;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@@ -6,4 +6,6 @@ export interface PersonalData {
|
||||
enableWol: number;
|
||||
enableGuacamole: number;
|
||||
guacamoleHost?: string;
|
||||
nickname?: string;
|
||||
defaultFont: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user