Refactor settings management and add mutation error handling
All checks were successful
Deploy to production / build (push) Successful in 18s
All checks were successful
Deploy to production / build (push) Successful in 18s
This commit is contained in:
60
src/App.tsx
60
src/App.tsx
@@ -37,17 +37,11 @@ function App() {
|
||||
const { data: themePlaylists, isFetched: isThemePlaylistsFetched } = useQueryAPI({
|
||||
endpoint: 'api/themePlaylists',
|
||||
});
|
||||
const { data: personalData, isFetched: isPersonalDataFetched } = useQueryAPI({
|
||||
const { data: personalData } = useQueryAPI({
|
||||
endpoint: 'api/personalData',
|
||||
});
|
||||
const {
|
||||
theme,
|
||||
setTheme,
|
||||
themeName,
|
||||
setThemeName,
|
||||
handleThemeChange,
|
||||
currentThemePlaylist,
|
||||
} = useCommonHooks();
|
||||
const { settings, updateSetting, theme, setTheme, handleThemeChange } =
|
||||
useCommonHooks();
|
||||
const [loaded, setLoaded] = useState<boolean>(false);
|
||||
const overrides = createTheme({
|
||||
primaryColor: 'primary',
|
||||
@@ -68,22 +62,23 @@ function App() {
|
||||
'shift + t',
|
||||
() => {
|
||||
const themeList = JSON.parse(
|
||||
themePlaylists?.find((t: ThemePlaylist) => t?.name === currentThemePlaylist)
|
||||
?.themeList ?? '[]',
|
||||
themePlaylists?.find(
|
||||
(t: ThemePlaylist) => t?.name === settings.currentThemePlaylist,
|
||||
)?.themeList ?? '[]',
|
||||
);
|
||||
if (themeList.length > 0 && currentThemePlaylist !== 'Time of Day') {
|
||||
if (themeList.length > 0 && settings.currentThemePlaylist !== 'Time of Day') {
|
||||
const filteredThemes = allThemes.filter((t: Theme) => themeList.includes(t.id));
|
||||
handleThemeChange(filteredThemes, false);
|
||||
} else if (currentThemePlaylist === 'All') {
|
||||
} else if (settings.currentThemePlaylist === 'All') {
|
||||
handleThemeChange(allThemes, true);
|
||||
} else if (currentThemePlaylist === 'Backgrounds') {
|
||||
} else if (settings.currentThemePlaylist === 'Backgrounds') {
|
||||
handleThemeChange(
|
||||
allThemes.filter(
|
||||
(a: any) => !a.backgroundImage.toLowerCase().includes('none'),
|
||||
),
|
||||
false,
|
||||
);
|
||||
} else if (currentThemePlaylist === 'No Backgrounds') {
|
||||
} else if (settings.currentThemePlaylist === 'No Backgrounds') {
|
||||
handleThemeChange(
|
||||
allThemes.filter((a: any) =>
|
||||
a.backgroundImage.toLowerCase().includes('none'),
|
||||
@@ -119,22 +114,26 @@ function App() {
|
||||
useEffect(() => {
|
||||
if (
|
||||
allThemes &&
|
||||
themeName &&
|
||||
settings.theme &&
|
||||
personalData &&
|
||||
Object.keys(personalData).length > 0 &&
|
||||
isThemePlaylistsFetched
|
||||
) {
|
||||
if (currentThemePlaylist === 'All') {
|
||||
if (themeName === 'random') {
|
||||
Object.keys(personalData[0]).forEach((key: any) => {
|
||||
updateSetting(key, personalData[0][key]);
|
||||
});
|
||||
if (settings.currentThemePlaylist === 'All') {
|
||||
if (settings.theme === 'random') {
|
||||
handleThemeChange(allThemes, false);
|
||||
} else {
|
||||
const temp = allThemes.find((t: Theme) => t.name === themeName);
|
||||
const temp = allThemes.find((t: Theme) => t.name === settings.theme);
|
||||
setTheme(temp);
|
||||
}
|
||||
} else if (currentThemePlaylist === 'Time of Day') {
|
||||
} else if (settings.currentThemePlaylist === 'Time of Day') {
|
||||
const themeList = JSON.parse(
|
||||
themePlaylists?.find((t: ThemePlaylist) => t.name === currentThemePlaylist)
|
||||
.themeList,
|
||||
themePlaylists?.find(
|
||||
(t: ThemePlaylist) => t.name === settings.currentThemePlaylist,
|
||||
).themeList,
|
||||
);
|
||||
setTheme(
|
||||
allThemes.find(
|
||||
@@ -142,34 +141,35 @@ function App() {
|
||||
t.id === Number(themeList[Math.floor(new Date().getHours() / 6)]),
|
||||
),
|
||||
);
|
||||
} else if (currentThemePlaylist === 'Backgrounds') {
|
||||
} else if (settings.currentThemePlaylist === 'Backgrounds') {
|
||||
handleThemeChange(
|
||||
allThemes.filter((a: any) => !a.backgroundImage.toLowerCase().includes('none')),
|
||||
false,
|
||||
);
|
||||
} else if (currentThemePlaylist === 'No Backgrounds') {
|
||||
} else if (settings.currentThemePlaylist === 'No Backgrounds') {
|
||||
handleThemeChange(
|
||||
allThemes.filter((a: any) => a.backgroundImage.toLowerCase().includes('none')),
|
||||
false,
|
||||
);
|
||||
} else {
|
||||
const themeList = JSON.parse(
|
||||
themePlaylists?.find((t: ThemePlaylist) => t.name === currentThemePlaylist)
|
||||
.themeList,
|
||||
themePlaylists?.find(
|
||||
(t: ThemePlaylist) => t.name === settings.currentThemePlaylist,
|
||||
).themeList,
|
||||
);
|
||||
const filteredThemes = allThemes.filter((t: Theme) => themeList.includes(t.id));
|
||||
setThemeName('random');
|
||||
updateSetting('theme', 'random');
|
||||
handleThemeChange(filteredThemes, false);
|
||||
}
|
||||
setLoaded(true);
|
||||
}
|
||||
}, [
|
||||
isAllThemesFetched,
|
||||
themeName,
|
||||
settings.theme,
|
||||
allThemes,
|
||||
currentThemePlaylist,
|
||||
settings.currentThemePlaylist,
|
||||
isThemePlaylistsFetched,
|
||||
isPersonalDataFetched,
|
||||
personalData,
|
||||
]);
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -19,7 +19,7 @@ export function WeatherModal({
|
||||
handleResetSearch: () => void;
|
||||
closeWeatherModal: () => void;
|
||||
}) {
|
||||
const { isMobile, currentUser } = useCommonHooks();
|
||||
const { isMobile, settings } = useCommonHooks();
|
||||
const [selectedDay, setSelectedDay] = useState<any>(undefined);
|
||||
const [selectedHourly, setSelectedHourly] = useState<any>(undefined);
|
||||
|
||||
@@ -79,7 +79,7 @@ export function WeatherModal({
|
||||
Weather Forecast for{' '}
|
||||
</Text>
|
||||
<Text fz={'1.125rem'} fw={600}>
|
||||
{currentUser.physicalAddress}
|
||||
{settings.physicalAddress}
|
||||
</Text>
|
||||
</Group>
|
||||
</Modal.Title>
|
||||
|
||||
@@ -306,23 +306,53 @@ export function useMutationAPI() {
|
||||
body: object | undefined | null;
|
||||
}) => callEndpoint(method, endpoint, body),
|
||||
onSuccess: (_, { endpoint }) => {
|
||||
queryClient.refetchQueries({
|
||||
queryKey: ['GET', endpoint],
|
||||
});
|
||||
queryClient
|
||||
.refetchQueries({
|
||||
queryKey: ['GET', endpoint],
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
endpoint === 'api/bookmarkOrder' &&
|
||||
queryClient.refetchQueries({ queryKey: ['GET', 'api/bookmarks'] });
|
||||
queryClient
|
||||
.refetchQueries({ queryKey: ['GET', 'api/bookmarks'] })
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
endpoint === 'api/bookmarkCategoryOrder' &&
|
||||
queryClient.refetchQueries({ queryKey: ['GET', 'api/bookmarkCategories'] });
|
||||
queryClient
|
||||
.refetchQueries({ queryKey: ['GET', 'api/bookmarkCategories'] })
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
endpoint === 'api/applicationOrder' &&
|
||||
queryClient.refetchQueries({ queryKey: ['GET', 'api/applications'] });
|
||||
queryClient
|
||||
.refetchQueries({ queryKey: ['GET', 'api/applications'] })
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
endpoint === 'api/applications' &&
|
||||
queryClient.refetchQueries({ queryKey: ['GET', 'api/enabledApplications'] });
|
||||
queryClient
|
||||
.refetchQueries({ queryKey: ['GET', 'api/enabledApplications'] })
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
endpoint === 'api/bookmarks' &&
|
||||
queryClient.refetchQueries({ queryKey: ['GET', 'api/themes'] });
|
||||
queryClient.refetchQueries({ queryKey: ['GET', 'api/themes'] }).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
endpoint === 'api/providers' &&
|
||||
queryClient.refetchQueries({ queryKey: ['GET', 'api/providers/default'] });
|
||||
queryClient
|
||||
.refetchQueries({ queryKey: ['GET', 'api/providers/default'] })
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
endpoint === 'api/personalData' &&
|
||||
queryClient.refetchQueries({ queryKey: ['GET', 'api/personalData'] });
|
||||
queryClient
|
||||
.refetchQueries({ queryKey: ['GET', 'api/personalData'] })
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -426,9 +456,21 @@ export function useFileUploadAPI() {
|
||||
}) => callEndpoint(method, endpoint, body),
|
||||
onMutate: () => {},
|
||||
onSuccess: (_variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['GET', 'api/backgrounds'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['GET', 'api/themes'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['GET', 'api/themePlaylists'] });
|
||||
queryClient
|
||||
.invalidateQueries({ queryKey: ['GET', 'api/backgrounds'] })
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
queryClient
|
||||
.invalidateQueries({ queryKey: ['GET', 'api/themes'] })
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
queryClient
|
||||
.invalidateQueries({ queryKey: ['GET', 'api/themePlaylists'] })
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,126 +2,73 @@ import { useLocalStorage } from '@mantine/hooks';
|
||||
import { appTheme } from 'contexts';
|
||||
import { useAtom } from 'jotai';
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { Location, Theme, User } from 'types';
|
||||
import type { Settings, Theme } from 'types';
|
||||
import { useMutationAPI } from './useAPI';
|
||||
|
||||
const initialSettings: Settings = {
|
||||
theme: 'random',
|
||||
appFilter: ['All'],
|
||||
appSort: 'A>Z',
|
||||
appDensity: 6,
|
||||
appScale: 1,
|
||||
bookmarkSort: 'A>Z',
|
||||
bookmarkFilter: ['All'],
|
||||
bookmarkDensity: 6,
|
||||
bookmarkScale: 1,
|
||||
currentThemePlaylist: 'All',
|
||||
defaultProviderLabel: 'Google Search',
|
||||
defaultProviderURL: 'https://www.google.com/search?q=',
|
||||
defaultProviderPrefix: '/g',
|
||||
defaultProviderIcon: 'FaGoogle',
|
||||
timeFormat: '12h',
|
||||
firstName: 'First',
|
||||
lastName: 'Last',
|
||||
nickname: 'Boss',
|
||||
email: 'triggerUser@default.com',
|
||||
physicalAddress: '1510 Polk St, Houston, TX 77002',
|
||||
defaultFont: 'Jost',
|
||||
enableWol: 0,
|
||||
enableGuacamole: 0,
|
||||
guacamoleHost: 'https://domain.example.com/guacamole',
|
||||
lat: 29.750950134386066,
|
||||
lon: -95.36220654860034,
|
||||
};
|
||||
|
||||
export function useCommonHooks() {
|
||||
const { mutateAsync } = useMutationAPI();
|
||||
const isMobile = globalThis.matchMedia('(max-width: 600px)').matches;
|
||||
const [theme, setTheme] = useAtom<Theme>(appTheme);
|
||||
const [themeName, setThemeName] = useLocalStorage<string>({
|
||||
key: 'theme',
|
||||
defaultValue: 'random',
|
||||
});
|
||||
const [appFilter, setAppFilter] = useLocalStorage<string[]>({
|
||||
key: 'appFilter',
|
||||
defaultValue: ['All'],
|
||||
});
|
||||
const [appSort, setAppSort] = useLocalStorage<string>({
|
||||
key: 'appSort',
|
||||
defaultValue: 'A>Z',
|
||||
});
|
||||
const [appDensity, setAppDensity] = useLocalStorage<number>({
|
||||
key: 'appDensity',
|
||||
defaultValue: 6,
|
||||
});
|
||||
const [appScale, setAppScale] = useLocalStorage<number>({
|
||||
key: 'appScale',
|
||||
defaultValue: 1,
|
||||
});
|
||||
const [bookmarkSort, setBookmarkSort] = useLocalStorage<string>({
|
||||
key: 'bookmarkSort',
|
||||
defaultValue: 'A>Z',
|
||||
});
|
||||
const [bookmarkFilter, setBookmarkFilter] = useLocalStorage<string[]>({
|
||||
key: 'bookmarkFilter',
|
||||
defaultValue: ['All'],
|
||||
});
|
||||
const [bookmarkDensity, setBookmarkDensity] = useLocalStorage<number>({
|
||||
key: 'bookmarkDensity',
|
||||
defaultValue: 6,
|
||||
});
|
||||
const [bookmarkScale, setBookmarkScale] = useLocalStorage<number>({
|
||||
key: 'bookmarkScale',
|
||||
defaultValue: 1,
|
||||
});
|
||||
const [currentTheme, setCurrentTheme] = useLocalStorage<string>({
|
||||
key: 'theme',
|
||||
defaultValue: 'random',
|
||||
});
|
||||
const [currentThemePlaylist, setCurrentThemePlaylist] = useLocalStorage<string>({
|
||||
key: 'themePlaylist',
|
||||
defaultValue: 'All',
|
||||
});
|
||||
const [defaultProviderLabel, setDefaultProviderLabel] = useLocalStorage<string>({
|
||||
key: 'providerLabel',
|
||||
defaultValue: 'Google Search',
|
||||
});
|
||||
const [defaultProviderURL, setDefaultProviderURL] = useLocalStorage<string>({
|
||||
key: 'providerURL',
|
||||
defaultValue: 'https://www.google.com/search?q=',
|
||||
});
|
||||
const [defaultProviderPrefix, setDefaultProviderPrefix] = useLocalStorage<string>({
|
||||
key: 'providerPrefix',
|
||||
defaultValue: '/g',
|
||||
});
|
||||
const [defaultProviderIcon, setDefaultProviderIcon] = useLocalStorage<string>({
|
||||
key: 'providerIcon',
|
||||
defaultValue: 'FaGoogle',
|
||||
});
|
||||
const [timeFormat, setTimeFormat] = useLocalStorage<string>({
|
||||
key: 'timeFormat',
|
||||
defaultValue: '12h',
|
||||
|
||||
const [settings, setSettings] = useLocalStorage<Settings>({
|
||||
key: 'trigger_settings',
|
||||
defaultValue: initialSettings,
|
||||
});
|
||||
|
||||
// Helper to update specific keys without wiping the rest of the object
|
||||
const updateSetting = <K extends keyof Settings>(key: K, value: Settings[K]) => {
|
||||
setSettings((prev) => ({ ...prev, [key]: value }));
|
||||
};
|
||||
|
||||
const [searchValue, setSearchValue] = useState<string>('');
|
||||
const [providerLabel, setProviderLabel] = useState(defaultProviderLabel);
|
||||
const [providerURL, setProviderURL] = useState(defaultProviderURL);
|
||||
const [providerPrefix, setProviderPrefix] = useState(defaultProviderPrefix);
|
||||
const [providerIcon, setProviderIcon] = useState(defaultProviderIcon);
|
||||
const [providerLabel, setProviderLabel] = useState(settings.defaultProviderLabel);
|
||||
const [providerURL, setProviderURL] = useState(settings.defaultProviderURL);
|
||||
const [providerPrefix, setProviderPrefix] = useState(settings.defaultProviderPrefix);
|
||||
const [providerIcon, setProviderIcon] = useState(settings.defaultProviderIcon);
|
||||
|
||||
useEffect(() => {
|
||||
setProviderLabel(defaultProviderLabel);
|
||||
setProviderURL(defaultProviderURL);
|
||||
setProviderPrefix(defaultProviderPrefix);
|
||||
setProviderIcon(defaultProviderIcon);
|
||||
setProviderLabel(settings.defaultProviderLabel);
|
||||
setProviderURL(settings.defaultProviderURL);
|
||||
setProviderPrefix(settings.defaultProviderPrefix);
|
||||
setProviderIcon(settings.defaultProviderIcon);
|
||||
}, [
|
||||
defaultProviderLabel,
|
||||
defaultProviderURL,
|
||||
defaultProviderPrefix,
|
||||
defaultProviderIcon,
|
||||
settings.defaultProviderLabel,
|
||||
settings.defaultProviderURL,
|
||||
settings.defaultProviderPrefix,
|
||||
settings.defaultProviderIcon,
|
||||
]);
|
||||
|
||||
const [currentUser, setCurrentUser] = useLocalStorage<User>({
|
||||
key: 'user',
|
||||
defaultValue: {
|
||||
firstName: 'First',
|
||||
lastName: 'Last',
|
||||
nickname: 'Boss',
|
||||
email: 'triggerUser@default.com',
|
||||
physicalAddress: '1510 Polk St, Houston, TX 77002',
|
||||
defaultFont: 'Jost',
|
||||
enableWol: 0,
|
||||
enableGuacamole: 0,
|
||||
guacamoleHost: 'https://domain.example.com/guacamole',
|
||||
},
|
||||
});
|
||||
const [currentLocation, setCurrentLocation] = useLocalStorage<Location>({
|
||||
key: 'location',
|
||||
defaultValue: {
|
||||
lat: 29.750950134386066,
|
||||
lon: -95.36220654860034,
|
||||
},
|
||||
});
|
||||
|
||||
const urlPattern = new RegExp(
|
||||
String.raw`^(https?:\/\/)?` +
|
||||
String.raw`((([a-z\d]([a-z\d-]*[a-z\d])*)\.)+[a-z]{2,}|` +
|
||||
String.raw`((\d{1,3}\.){3}\d{1,3}))` +
|
||||
String.raw`(\:\d+)?(\/[-a-z\d%_.~+]*)*` +
|
||||
String.raw`(\?[;&a-z\d%_.~+=-]*)?` +
|
||||
String.raw`(\#[-a-z\d_]*)?$`,
|
||||
'i',
|
||||
);
|
||||
const urlPattern =
|
||||
/^(https?:\/\/)?([\w.-]+)\.([a-z]{2,})(:\d+)?(\/[-a-z\d%_.~+]*)*(\?[;&a-z\d%_.~+=-]*)?(#[-a-z\d_]*)?$/i;
|
||||
|
||||
const textShadowStyle =
|
||||
'1px 1px 1px color-mix(in srgb, var(--mantine-color-accent-0), transparent 50%)';
|
||||
@@ -139,22 +86,24 @@ export function useCommonHooks() {
|
||||
const enabled = themes
|
||||
.filter((t: Theme) => t.randomEnabled === 1)
|
||||
.filter((t: Theme) => t.name !== theme.name);
|
||||
if (enabled.length === 0 && currentThemePlaylist === 'All') {
|
||||
if (enabled.length === 0 && settings.currentThemePlaylist === 'All') {
|
||||
setTheme(themes[0]);
|
||||
save && setThemeName(themes[0].name);
|
||||
} else if (currentThemePlaylist === 'All') {
|
||||
if (save) updateSetting('theme', themes[0].name);
|
||||
} else if (settings.currentThemePlaylist === 'All') {
|
||||
const index = Math.floor(Math.random() * enabled.length);
|
||||
setTheme(enabled[index]);
|
||||
save && setThemeName(enabled[index].name);
|
||||
if (save) updateSetting('theme', enabled[index].name);
|
||||
} else {
|
||||
const index = Math.floor(Math.random() * themes.length);
|
||||
setTheme(themes[index]);
|
||||
save && setThemeName(themes[index].name);
|
||||
if (save) updateSetting('theme', themes[index].name);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = (item: any, endpoint: string) => {
|
||||
mutateAsync({ method: 'DELETE', endpoint: endpoint, body: item });
|
||||
mutateAsync({ method: 'DELETE', endpoint: endpoint, body: item }).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
const iconFilter = (selectedTheme?: Theme) => {
|
||||
@@ -178,7 +127,6 @@ export function useCommonHooks() {
|
||||
const getObjectDiff = (original: any, current: any) => {
|
||||
const changes: any = {};
|
||||
|
||||
// Check current object's properties
|
||||
for (const [key, value] of Object.entries(current)) {
|
||||
if (!(key in original)) {
|
||||
changes[key] = {
|
||||
@@ -191,7 +139,6 @@ export function useCommonHooks() {
|
||||
const originalValue = original[key];
|
||||
const currentValue = value;
|
||||
|
||||
// Handle different types of comparisons
|
||||
if (
|
||||
originalValue !== currentValue &&
|
||||
String(originalValue) !== String(currentValue) &&
|
||||
@@ -204,7 +151,6 @@ export function useCommonHooks() {
|
||||
}
|
||||
}
|
||||
|
||||
// Check for removed properties
|
||||
for (const key of Object.keys(original)) {
|
||||
if (!(key in current)) {
|
||||
changes[key] = {
|
||||
@@ -218,6 +164,8 @@ export function useCommonHooks() {
|
||||
};
|
||||
|
||||
return {
|
||||
settings,
|
||||
updateSetting,
|
||||
isMobile,
|
||||
theme,
|
||||
setTheme,
|
||||
@@ -228,28 +176,6 @@ export function useCommonHooks() {
|
||||
getObjectDiff,
|
||||
handleThemeChange,
|
||||
handleDelete,
|
||||
themeName,
|
||||
setThemeName,
|
||||
appFilter,
|
||||
setAppFilter,
|
||||
appSort,
|
||||
setAppSort,
|
||||
appDensity,
|
||||
setAppDensity,
|
||||
appScale,
|
||||
setAppScale,
|
||||
bookmarkSort,
|
||||
setBookmarkSort,
|
||||
bookmarkFilter,
|
||||
setBookmarkFilter,
|
||||
bookmarkDensity,
|
||||
setBookmarkDensity,
|
||||
bookmarkScale,
|
||||
setBookmarkScale,
|
||||
currentTheme,
|
||||
setCurrentTheme,
|
||||
currentThemePlaylist,
|
||||
setCurrentThemePlaylist,
|
||||
searchValue,
|
||||
setSearchValue,
|
||||
providerLabel,
|
||||
@@ -260,20 +186,6 @@ export function useCommonHooks() {
|
||||
setProviderPrefix,
|
||||
providerIcon,
|
||||
setProviderIcon,
|
||||
defaultProviderLabel,
|
||||
setDefaultProviderLabel,
|
||||
defaultProviderURL,
|
||||
setDefaultProviderURL,
|
||||
defaultProviderPrefix,
|
||||
setDefaultProviderPrefix,
|
||||
defaultProviderIcon,
|
||||
setDefaultProviderIcon,
|
||||
timeFormat,
|
||||
setTimeFormat,
|
||||
currentUser,
|
||||
setCurrentUser,
|
||||
currentLocation,
|
||||
setCurrentLocation,
|
||||
textShadowStyle,
|
||||
largeTextShadowStyle,
|
||||
};
|
||||
|
||||
@@ -15,14 +15,7 @@ import {
|
||||
} from './components';
|
||||
|
||||
export function Homepage() {
|
||||
const {
|
||||
appFilter,
|
||||
bookmarkFilter,
|
||||
isMobile,
|
||||
setCurrentUser,
|
||||
currentLocation,
|
||||
setCurrentLocation,
|
||||
} = useCommonHooks();
|
||||
const { isMobile, settings, updateSetting } = useCommonHooks();
|
||||
const dateOptions: Intl.DateTimeFormatOptions = isMobile
|
||||
? {}
|
||||
: {
|
||||
@@ -34,7 +27,7 @@ export function Homepage() {
|
||||
|
||||
const date = new Date().toLocaleDateString('en-US', dateOptions);
|
||||
|
||||
const { data: personalData, isFetched: personalIsFetched } = useQueryAPI({
|
||||
const { data: personalData } = useQueryAPI({
|
||||
endpoint: 'api/personalData',
|
||||
});
|
||||
const { data: messageData } = useQueryAPI({
|
||||
@@ -44,8 +37,8 @@ export function Homepage() {
|
||||
address: personalData?.[0]?.physicalAddress,
|
||||
});
|
||||
const { data: meteoWeather } = useOpenMeteoAPI({
|
||||
lat: weatherMetadata?.[0].lat ?? currentLocation.lat,
|
||||
lon: weatherMetadata?.[0].lon ?? currentLocation.lon,
|
||||
lat: weatherMetadata?.[0].lat ?? settings.lat,
|
||||
lon: weatherMetadata?.[0].lon ?? settings.lon,
|
||||
});
|
||||
|
||||
const personal: PersonalData = personalData?.[0];
|
||||
@@ -59,11 +52,6 @@ export function Homepage() {
|
||||
const [weatherModalOpened, { open: openWeatherModal, close: closeWeatherModal }] =
|
||||
useDisclosure(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (personalData && personalIsFetched) {
|
||||
setCurrentUser(personalData[0]);
|
||||
}
|
||||
}, [personalData, personalIsFetched]);
|
||||
useEffect(() => {
|
||||
if (personal) {
|
||||
setDisplayedGreeting(() => {
|
||||
@@ -87,21 +75,23 @@ export function Homepage() {
|
||||
useEffect(() => {
|
||||
if (meteoWeather) {
|
||||
setMeteoCurrent(meteoWeather);
|
||||
setCurrentLocation({
|
||||
lat: weatherMetadata?.[0].lat,
|
||||
lon: weatherMetadata?.[0].lon,
|
||||
});
|
||||
updateSetting('lat', weatherMetadata?.[0].lat);
|
||||
updateSetting('lon', weatherMetadata?.[0].lon);
|
||||
}
|
||||
}, [meteoWeather]);
|
||||
|
||||
return (
|
||||
<Stack
|
||||
gap={0}
|
||||
h={appFilter.length > 0 || bookmarkFilter.length > 0 ? undefined : '100%'}
|
||||
h={
|
||||
settings.appFilter.length > 0 || settings.bookmarkFilter.length > 0
|
||||
? undefined
|
||||
: '100%'
|
||||
}
|
||||
className={stackClasses.containerStack}
|
||||
justify='center'
|
||||
>
|
||||
{appFilter.length > 0 || bookmarkFilter.length > 0 ? (
|
||||
{settings.appFilter.length > 0 || settings.bookmarkFilter.length > 0 ? (
|
||||
<>
|
||||
<Search
|
||||
current={meteoCurrent}
|
||||
@@ -120,8 +110,8 @@ export function Homepage() {
|
||||
current={meteoCurrent}
|
||||
openWeatherModal={openWeatherModal}
|
||||
/>
|
||||
<Applications contentType={appFilter} />
|
||||
<Bookmarks contentType={bookmarkFilter} />
|
||||
<Applications contentType={settings.appFilter} />
|
||||
<Bookmarks contentType={settings.bookmarkFilter} />
|
||||
</>
|
||||
) : (
|
||||
<Box h={'100%'} w={'100%'}>
|
||||
|
||||
@@ -8,10 +8,10 @@ export function Applications({ contentType }: { contentType: string[] }) {
|
||||
const { data: applicationData } = useQueryAPI({
|
||||
endpoint: 'api/enabledApplications',
|
||||
});
|
||||
const { appSort, appDensity, appScale, theme, textShadowStyle } = useCommonHooks();
|
||||
const { settings, theme, textShadowStyle } = useCommonHooks();
|
||||
const applications: Application[] = applicationData
|
||||
?.sort((a: Application, b: Application) => {
|
||||
switch (appSort) {
|
||||
switch (settings.appSort) {
|
||||
case 'A>Z':
|
||||
return a.name.toLocaleLowerCase() > b.name.toLocaleLowerCase() ? 1 : -1;
|
||||
case 'Z>A':
|
||||
@@ -54,7 +54,7 @@ export function Applications({ contentType }: { contentType: string[] }) {
|
||||
style={{
|
||||
gap: '.25rem',
|
||||
display: 'grid',
|
||||
gridTemplateColumns: `repeat(${appDensity}, 1fr)`,
|
||||
gridTemplateColumns: `repeat(${settings.appDensity}, 1fr)`,
|
||||
}}
|
||||
>
|
||||
{applications?.map((application: Application) => {
|
||||
@@ -65,7 +65,7 @@ export function Applications({ contentType }: { contentType: string[] }) {
|
||||
url={application.url}
|
||||
icon={application.icon}
|
||||
enabled={application.enabled}
|
||||
scale={appScale}
|
||||
scale={settings.appScale}
|
||||
linksActive
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -11,15 +11,14 @@ export function Bookmarks({ contentType }: { contentType: string[] }) {
|
||||
const { data: groupData, isFetched: groupsIsFetched } = useQueryAPI({
|
||||
endpoint: 'api/bookmarkCategories',
|
||||
});
|
||||
const { bookmarkSort, bookmarkDensity, bookmarkScale, theme, textShadowStyle } =
|
||||
useCommonHooks();
|
||||
const { settings, theme, textShadowStyle } = useCommonHooks();
|
||||
const groupList: BookmarkGroup[] = groupData?.map((category: BookmarkGroup) => {
|
||||
return category;
|
||||
});
|
||||
const bookmarks: Bookmark[] = bookmarkData || [];
|
||||
const groups: BookmarkGroup[] = groupList
|
||||
?.toSorted((a: BookmarkGroup, b: BookmarkGroup) => {
|
||||
switch (bookmarkSort) {
|
||||
switch (settings.bookmarkSort) {
|
||||
case 'A>Z':
|
||||
return a.categoryName.toLocaleLowerCase() > b.categoryName.toLocaleLowerCase()
|
||||
? 1
|
||||
@@ -71,7 +70,7 @@ export function Bookmarks({ contentType }: { contentType: string[] }) {
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridRowGap: '0px',
|
||||
gridTemplateColumns: `repeat(${bookmarkDensity}, 1fr)`,
|
||||
gridTemplateColumns: `repeat(${settings.bookmarkDensity}, 1fr)`,
|
||||
}}
|
||||
>
|
||||
{groups
|
||||
@@ -88,7 +87,7 @@ export function Bookmarks({ contentType }: { contentType: string[] }) {
|
||||
(bookmark: Bookmark) =>
|
||||
bookmark.category === category.categoryName,
|
||||
)}
|
||||
bookmarkScale={bookmarkScale}
|
||||
bookmarkScale={settings.bookmarkScale}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -18,13 +18,12 @@ export function Header({
|
||||
const [time, setTime] = useState(new Date());
|
||||
const {
|
||||
isMobile,
|
||||
currentUser,
|
||||
fixDigits,
|
||||
theme,
|
||||
textShadowStyle,
|
||||
largeTextShadowStyle,
|
||||
timeFormat,
|
||||
setTimeFormat,
|
||||
settings,
|
||||
updateSetting,
|
||||
} = useCommonHooks();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -51,12 +50,12 @@ export function Header({
|
||||
}}
|
||||
>
|
||||
{greet
|
||||
?.replace('$firstName', currentUser.firstName)
|
||||
.replace('$lastName', currentUser.lastName)
|
||||
.replace('$nickname', currentUser.nickname)
|
||||
.replace('$^firstName', currentUser.firstName.toUpperCase())
|
||||
.replace('$^lastName', currentUser.lastName.toUpperCase())
|
||||
.replace('$^nickname', currentUser.nickname?.toUpperCase())}
|
||||
?.replace('$firstName', settings.firstName)
|
||||
.replace('$lastName', settings.lastName)
|
||||
.replace('$nickname', settings.nickname)
|
||||
.replace('$^firstName', settings.firstName.toUpperCase())
|
||||
.replace('$^lastName', settings.lastName.toUpperCase())
|
||||
.replace('$^nickname', settings.nickname?.toUpperCase())}
|
||||
</Text>
|
||||
</Stack>
|
||||
<Stack
|
||||
@@ -123,10 +122,12 @@ export function Header({
|
||||
textShadow: theme.textShadowsEnabled ? textShadowStyle : undefined,
|
||||
}}
|
||||
onClick={() =>
|
||||
timeFormat === '12h' ? setTimeFormat('24h') : setTimeFormat('12h')
|
||||
settings.timeFormat === '12h'
|
||||
? updateSetting('timeFormat', '24h')
|
||||
: updateSetting('timeFormat', '12h')
|
||||
}
|
||||
>
|
||||
{timeFormat === '12h'
|
||||
{settings.timeFormat === '12h'
|
||||
? `${
|
||||
time.getHours() === 0
|
||||
? 12
|
||||
|
||||
@@ -13,13 +13,12 @@ export function LargeHeader({
|
||||
const [time, setTime] = useState(new Date());
|
||||
const {
|
||||
isMobile,
|
||||
currentUser,
|
||||
fixDigits,
|
||||
theme,
|
||||
textShadowStyle,
|
||||
largeTextShadowStyle,
|
||||
timeFormat,
|
||||
setTimeFormat,
|
||||
settings,
|
||||
updateSetting,
|
||||
} = useCommonHooks();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -40,12 +39,12 @@ export function LargeHeader({
|
||||
}}
|
||||
>
|
||||
{greet
|
||||
?.replace('$firstName', currentUser.firstName)
|
||||
.replace('$lastName', currentUser.lastName)
|
||||
.replace('$nickname', currentUser.nickname)
|
||||
.replace('$^firstName', currentUser.firstName.toUpperCase())
|
||||
.replace('$^lastName', currentUser.lastName.toUpperCase())
|
||||
.replace('$^nickname', currentUser.nickname?.toUpperCase())}
|
||||
?.replace('$firstName', settings.firstName)
|
||||
.replace('$lastName', settings.lastName)
|
||||
.replace('$nickname', settings.nickname)
|
||||
.replace('$^firstName', settings.firstName.toUpperCase())
|
||||
.replace('$^lastName', settings.lastName.toUpperCase())
|
||||
.replace('$^nickname', settings.nickname?.toUpperCase())}
|
||||
</Text>
|
||||
<Text
|
||||
fz={'2.25rem'}
|
||||
@@ -69,10 +68,12 @@ export function LargeHeader({
|
||||
zIndex: 1,
|
||||
}}
|
||||
onClick={() =>
|
||||
timeFormat === '12h' ? setTimeFormat('24h') : setTimeFormat('12h')
|
||||
settings.timeFormat === '12h'
|
||||
? updateSetting('timeFormat', '24h')
|
||||
: updateSetting('timeFormat', '12h')
|
||||
}
|
||||
>
|
||||
{timeFormat === '12h'
|
||||
{settings.timeFormat === '12h'
|
||||
? `${
|
||||
time.getHours() === 0
|
||||
? 12
|
||||
|
||||
@@ -63,14 +63,8 @@ export function Search({
|
||||
setProviderIcon,
|
||||
searchValue,
|
||||
setSearchValue,
|
||||
defaultProviderURL,
|
||||
setDefaultProviderURL,
|
||||
defaultProviderLabel,
|
||||
setDefaultProviderLabel,
|
||||
defaultProviderPrefix,
|
||||
setDefaultProviderPrefix,
|
||||
defaultProviderIcon,
|
||||
setDefaultProviderIcon,
|
||||
settings,
|
||||
updateSetting,
|
||||
} = useCommonHooks();
|
||||
const { ref, height } = useElementSize();
|
||||
const { mutateAsync } = useMutationAPI();
|
||||
@@ -146,8 +140,8 @@ export function Search({
|
||||
setSearchValue('');
|
||||
setListIndex(-1);
|
||||
setHistoryMultiplier(1);
|
||||
setProviderLabel(defaultProviderLabel);
|
||||
setProviderURL(defaultProviderURL);
|
||||
setProviderLabel(settings.defaultProviderLabel);
|
||||
setProviderURL(settings.defaultProviderURL);
|
||||
};
|
||||
|
||||
const handleGuacamoleClientLaunchItem = ({ option }: { option: any }) => {
|
||||
@@ -176,9 +170,13 @@ export function Search({
|
||||
method: 'POST',
|
||||
endpoint: searchEndpoint,
|
||||
body: { prefix: providerPrefix, provider: providerLabel, searchTerm: subtext },
|
||||
}).then(() => {
|
||||
open(`${providerURL}${subtext}`, '_self');
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
open(`${providerURL}${subtext}`, '_self');
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
} else if (providerLabel === 'Show weather') {
|
||||
setSearchValue('');
|
||||
closeDropdown();
|
||||
@@ -211,9 +209,13 @@ export function Search({
|
||||
method: 'POST',
|
||||
endpoint: searchEndpoint,
|
||||
body: { prefix: providerPrefix, provider: providerLabel, searchTerm: subtext },
|
||||
}).then(() => {
|
||||
open(`${providerURL}${subtext}`, '_self');
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
open(`${providerURL}${subtext}`, '_self');
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
} else {
|
||||
open(`${providerURL}${subtext}`, '_self');
|
||||
}
|
||||
@@ -372,10 +374,10 @@ export function Search({
|
||||
setProviderIcon('');
|
||||
}
|
||||
} else {
|
||||
setProviderLabel(defaultProviderLabel);
|
||||
setProviderURL(defaultProviderURL);
|
||||
setProviderPrefix(defaultProviderPrefix);
|
||||
setProviderIcon(defaultProviderIcon);
|
||||
setProviderLabel(settings.defaultProviderLabel);
|
||||
setProviderURL(settings.defaultProviderURL);
|
||||
setProviderPrefix(settings.defaultProviderPrefix);
|
||||
setProviderIcon(settings.defaultProviderIcon);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -460,12 +462,16 @@ export function Search({
|
||||
|
||||
useEffect(() => {
|
||||
if (defaultProviderData && defaultProviderDataIsFetched) {
|
||||
setDefaultProviderURL(
|
||||
updateSetting(
|
||||
'defaultProviderURL',
|
||||
defaultProviderData[0]?.url ?? 'https://www.google.com/search?q=',
|
||||
);
|
||||
setDefaultProviderPrefix(defaultProviderData[0]?.prefix ?? '/g');
|
||||
setDefaultProviderLabel(defaultProviderData[0]?.name ?? 'Google Search');
|
||||
setDefaultProviderIcon(defaultProviderData[0]?.icon ?? 'FaGoogle');
|
||||
updateSetting('defaultProviderPrefix', defaultProviderData[0]?.prefix ?? '/g');
|
||||
updateSetting('defaultProviderIcon', defaultProviderData[0]?.icon ?? 'FaGoogle');
|
||||
updateSetting(
|
||||
'defaultProviderLabel',
|
||||
defaultProviderData[0]?.name ?? 'Google Search',
|
||||
);
|
||||
}
|
||||
}, [defaultProviderData, defaultProviderDataIsFetched]);
|
||||
|
||||
|
||||
@@ -24,11 +24,11 @@ export default function RenderItem({
|
||||
setHistoryMultiplier: React.Dispatch<React.SetStateAction<number>>;
|
||||
searchEndpoint: string;
|
||||
}) {
|
||||
const { defaultProviderURL } = useCommonHooks();
|
||||
const { settings } = useCommonHooks();
|
||||
const handleSearchHistoryItem = ({ option }: { option: SearchHistoryItem }) => {
|
||||
const url =
|
||||
providers.find((provider: Provider) => provider.prefix === option.prefix)?.url ??
|
||||
defaultProviderURL;
|
||||
settings.defaultProviderURL;
|
||||
open(`${url}${option.searchTerm}`, '_self');
|
||||
};
|
||||
|
||||
|
||||
@@ -130,6 +130,8 @@ function SearchSelectItem({
|
||||
method: 'DELETE',
|
||||
endpoint: searchEndpoint,
|
||||
body: { id: item.option.id },
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -33,26 +33,7 @@ export function SettingsButton({ openProviderModal }: { openProviderModal: () =>
|
||||
const [selectedClient, setSelectedClient] = useState<WolClient>(wolClientData?.[0]);
|
||||
const navigate = useNavigate();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const {
|
||||
theme,
|
||||
isMobile,
|
||||
appFilter,
|
||||
setAppFilter,
|
||||
appSort,
|
||||
setAppSort,
|
||||
appDensity,
|
||||
setAppDensity,
|
||||
appScale,
|
||||
setAppScale,
|
||||
bookmarkSort,
|
||||
setBookmarkSort,
|
||||
bookmarkFilter,
|
||||
setBookmarkFilter,
|
||||
bookmarkDensity,
|
||||
setBookmarkDensity,
|
||||
bookmarkScale,
|
||||
setBookmarkScale,
|
||||
} = useCommonHooks();
|
||||
const { theme, isMobile, settings } = useCommonHooks();
|
||||
|
||||
const handleWakeRequest = () => {
|
||||
mutateAsync({
|
||||
@@ -123,26 +104,18 @@ export function SettingsButton({ openProviderModal }: { openProviderModal: () =>
|
||||
<SettingsCard
|
||||
categories={categories}
|
||||
sectionTitle={'Applications'}
|
||||
filter={appFilter}
|
||||
setFilter={setAppFilter}
|
||||
sort={appSort}
|
||||
setSort={setAppSort}
|
||||
density={appDensity}
|
||||
setDensity={setAppDensity}
|
||||
scale={appScale}
|
||||
setScale={setAppScale}
|
||||
filter={settings.appFilter}
|
||||
sort={settings.appSort}
|
||||
density={settings.appDensity}
|
||||
scale={settings.appScale}
|
||||
/>
|
||||
<SettingsCard
|
||||
categories={categories}
|
||||
sectionTitle={'Bookmarks'}
|
||||
filter={bookmarkFilter}
|
||||
setFilter={setBookmarkFilter}
|
||||
sort={bookmarkSort}
|
||||
setSort={setBookmarkSort}
|
||||
density={bookmarkDensity}
|
||||
setDensity={setBookmarkDensity}
|
||||
scale={bookmarkScale}
|
||||
setScale={setBookmarkScale}
|
||||
filter={settings.bookmarkFilter}
|
||||
sort={settings.bookmarkSort}
|
||||
density={settings.bookmarkDensity}
|
||||
scale={settings.bookmarkScale}
|
||||
/>
|
||||
</Stack>
|
||||
</Group>
|
||||
|
||||
@@ -14,26 +14,18 @@ function SettingsCard({
|
||||
categories,
|
||||
sectionTitle,
|
||||
filter,
|
||||
setFilter,
|
||||
sort,
|
||||
setSort,
|
||||
density,
|
||||
setDensity,
|
||||
scale,
|
||||
setScale,
|
||||
}: {
|
||||
categories: ComboboxItem[];
|
||||
sectionTitle: string;
|
||||
filter: any;
|
||||
setFilter: any;
|
||||
sort: any;
|
||||
setSort: any;
|
||||
density: any;
|
||||
setDensity: any;
|
||||
scale: any;
|
||||
setScale: any;
|
||||
}) {
|
||||
const { theme } = useCommonHooks();
|
||||
const { theme, updateSetting } = useCommonHooks();
|
||||
return (
|
||||
<Stack
|
||||
gap={0}
|
||||
@@ -70,11 +62,23 @@ function SettingsCard({
|
||||
className={sliderClasses.default}
|
||||
classNames={sliderClasses}
|
||||
value={density}
|
||||
onChange={(value) => setDensity(value)}
|
||||
onChange={(value) => {
|
||||
if (sectionTitle === 'Applications') {
|
||||
updateSetting('appDensity', value);
|
||||
} else if (sectionTitle === 'Bookmarks') {
|
||||
updateSetting('bookmarkDensity', value);
|
||||
}
|
||||
}}
|
||||
min={1}
|
||||
max={16}
|
||||
marks={[{ value: 6, label: 'Default' }]}
|
||||
onDoubleClick={() => setDensity(6)}
|
||||
onDoubleClick={() => {
|
||||
if (sectionTitle === 'Applications') {
|
||||
updateSetting('appDensity', 6);
|
||||
} else if (sectionTitle === 'Bookmarks') {
|
||||
updateSetting('bookmarkDensity', 6);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
{scale !== undefined && (
|
||||
@@ -86,12 +90,24 @@ function SettingsCard({
|
||||
className={sliderClasses.default}
|
||||
classNames={sliderClasses}
|
||||
value={scale}
|
||||
onChange={(value) => setScale(value)}
|
||||
onChange={(value) => {
|
||||
if (sectionTitle === 'Applications') {
|
||||
updateSetting('appScale', value);
|
||||
} else if (sectionTitle === 'Bookmarks') {
|
||||
updateSetting('bookmarkScale', value);
|
||||
}
|
||||
}}
|
||||
step={0.1}
|
||||
min={0.2}
|
||||
max={3}
|
||||
marks={[{ value: 1, label: 'Default' }]}
|
||||
onDoubleClick={() => setScale(1)}
|
||||
onDoubleClick={() => {
|
||||
if (sectionTitle === 'Applications') {
|
||||
updateSetting('appScale', 1);
|
||||
} else if (sectionTitle === 'Bookmarks') {
|
||||
updateSetting('bookmarkScale', 1);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
@@ -113,7 +129,11 @@ function SettingsCard({
|
||||
}
|
||||
defaultValue={filter}
|
||||
onChange={(data: string[]) => {
|
||||
setFilter(data);
|
||||
if (sectionTitle === 'Applications') {
|
||||
updateSetting('appFilter', data);
|
||||
} else if (sectionTitle === 'Bookmarks') {
|
||||
updateSetting('bookmarkFilter', data);
|
||||
}
|
||||
}}
|
||||
classNames={multiSelectClasses}
|
||||
comboboxProps={{
|
||||
@@ -147,7 +167,11 @@ function SettingsCard({
|
||||
position: 'top',
|
||||
}}
|
||||
onChange={(_value, option: ComboboxItem) => {
|
||||
setSort(option.value);
|
||||
if (sectionTitle === 'Applications') {
|
||||
updateSetting('appSort', option.value);
|
||||
} else if (sectionTitle === 'Bookmarks') {
|
||||
updateSetting('bookmarkSort', option.value);
|
||||
}
|
||||
}}
|
||||
labelProps={{ top: 8 }}
|
||||
styles={
|
||||
|
||||
@@ -31,16 +31,8 @@ function ThemingCard({ wolEnabled }: { wolEnabled: number }) {
|
||||
});
|
||||
const allThemes: Theme[] = allThemeData || [];
|
||||
|
||||
const {
|
||||
isMobile,
|
||||
currentTheme,
|
||||
setCurrentTheme,
|
||||
setThemeName,
|
||||
handleThemeChange,
|
||||
theme,
|
||||
currentThemePlaylist,
|
||||
setCurrentThemePlaylist,
|
||||
} = useCommonHooks();
|
||||
const { isMobile, handleThemeChange, theme, settings, updateSetting } =
|
||||
useCommonHooks();
|
||||
|
||||
const [current, setCurrent] = useState<string>(theme.name);
|
||||
|
||||
@@ -128,11 +120,11 @@ function ThemingCard({ wolEnabled }: { wolEnabled: number }) {
|
||||
borderRadius: '50%',
|
||||
},
|
||||
}}
|
||||
disabled={currentThemePlaylist === 'Time of Day' || isAnimating}
|
||||
disabled={settings.currentThemePlaylist === 'Time of Day' || isAnimating}
|
||||
onClick={() => {
|
||||
const themeList = JSON.parse(
|
||||
themePlaylistData?.find(
|
||||
(t: ThemePlaylist) => t?.name === currentThemePlaylist,
|
||||
(t: ThemePlaylist) => t?.name === settings.currentThemePlaylist,
|
||||
)?.themeList ?? '[]',
|
||||
);
|
||||
if (themeList.length > 0) {
|
||||
@@ -140,16 +132,16 @@ function ThemingCard({ wolEnabled }: { wolEnabled: number }) {
|
||||
themeList.includes(t.id),
|
||||
);
|
||||
handleThemeChange(filteredThemes, false);
|
||||
} else if (currentThemePlaylist === 'All') {
|
||||
} else if (settings.currentThemePlaylist === 'All') {
|
||||
handleThemeChange(allThemes, true);
|
||||
} else if (currentThemePlaylist === 'Backgrounds') {
|
||||
} else if (settings.currentThemePlaylist === 'Backgrounds') {
|
||||
handleThemeChange(
|
||||
allThemes.filter(
|
||||
(a: any) => !a.backgroundImage.toLowerCase().includes('none'),
|
||||
),
|
||||
false,
|
||||
);
|
||||
} else if (currentThemePlaylist === 'No Backgrounds') {
|
||||
} else if (settings.currentThemePlaylist === 'No Backgrounds') {
|
||||
handleThemeChange(
|
||||
allThemes.filter((a: any) =>
|
||||
a.backgroundImage.toLowerCase().includes('none'),
|
||||
@@ -169,7 +161,7 @@ function ThemingCard({ wolEnabled }: { wolEnabled: number }) {
|
||||
placeholder='Pick a playlist...'
|
||||
mb={'.25rem'}
|
||||
ml={'.25rem'}
|
||||
value={currentThemePlaylist}
|
||||
value={settings.currentThemePlaylist}
|
||||
data={[
|
||||
{ value: 'All', label: 'All' },
|
||||
{
|
||||
@@ -226,20 +218,21 @@ function ThemingCard({ wolEnabled }: { wolEnabled: number }) {
|
||||
: undefined
|
||||
}
|
||||
onChange={(_value, option: ComboboxItem) => {
|
||||
setCurrentThemePlaylist(option.value);
|
||||
updateSetting('currentThemePlaylist', option.value);
|
||||
if (option.value === 'Time of Day') {
|
||||
const themeList = JSON.parse(
|
||||
themePlaylistData.find((t: ThemePlaylist) => t.name === 'Time of Day')
|
||||
.themeList,
|
||||
);
|
||||
setThemeName(
|
||||
updateSetting(
|
||||
'theme',
|
||||
allThemeData.find(
|
||||
(t: Theme) =>
|
||||
t.id === Number(themeList[Math.floor(new Date().getHours() / 6)]),
|
||||
).name,
|
||||
);
|
||||
} else {
|
||||
setThemeName('random');
|
||||
updateSetting('theme', 'random');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@@ -252,12 +245,12 @@ function ThemingCard({ wolEnabled }: { wolEnabled: number }) {
|
||||
<ActionIcon
|
||||
className={actionIconClasses.menuActions}
|
||||
classNames={actionIconClasses}
|
||||
disabled={currentThemePlaylist === 'Time of Day' || isAnimating}
|
||||
disabled={settings.currentThemePlaylist === 'Time of Day' || isAnimating}
|
||||
h={40}
|
||||
style={{
|
||||
backgroundColor: 'transparent',
|
||||
color:
|
||||
currentTheme === 'random'
|
||||
settings.theme === 'random'
|
||||
? 'var(--mantine-color-accent-0)'
|
||||
: 'var(--mantine-color-primary-0)',
|
||||
borderTopRightRadius: 0,
|
||||
@@ -271,7 +264,7 @@ function ThemingCard({ wolEnabled }: { wolEnabled: number }) {
|
||||
styles={{
|
||||
icon: {
|
||||
backgroundColor:
|
||||
currentTheme === 'random'
|
||||
settings.theme === 'random'
|
||||
? `${theme?.searchColor === theme?.primaryColor ? theme.searchColor?.substring(0, 7) : theme.primaryColor?.substring(0, 7)}88`
|
||||
: 'transparent',
|
||||
width: 'fit-content',
|
||||
@@ -284,8 +277,8 @@ function ThemingCard({ wolEnabled }: { wolEnabled: number }) {
|
||||
},
|
||||
}}
|
||||
onClick={() => {
|
||||
setCurrentTheme(currentTheme === 'random' ? theme.name : 'random');
|
||||
setCurrentThemePlaylist('All');
|
||||
updateSetting('theme', 'random');
|
||||
updateSetting('currentThemePlaylist', 'All');
|
||||
}}
|
||||
>
|
||||
<FiShuffle size={20} />
|
||||
|
||||
@@ -128,10 +128,12 @@ export function Applications() {
|
||||
|
||||
useEffect(() => {
|
||||
if (applicationList.length > 0 && applicationList !== applications) {
|
||||
void mutateAsync({
|
||||
mutateAsync({
|
||||
method: 'PATCH',
|
||||
endpoint: appsOrderEndpoint,
|
||||
body: applicationList,
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}, [applicationList]);
|
||||
|
||||
@@ -38,7 +38,13 @@ function DebouncedApplication({
|
||||
};
|
||||
useEffect(() => {
|
||||
if (debounced && Object.keys(debounced).length > 0) {
|
||||
mutateAsync({ method: 'PATCH', endpoint: 'api/applications', body: debounced });
|
||||
mutateAsync({
|
||||
method: 'PATCH',
|
||||
endpoint: 'api/applications',
|
||||
body: debounced,
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}, [debounced]);
|
||||
|
||||
|
||||
@@ -142,10 +142,12 @@ export function Bookmarks() {
|
||||
|
||||
useEffect(() => {
|
||||
if (groupList.length > 0 && groupList !== groups) {
|
||||
void mutateAsync({
|
||||
mutateAsync({
|
||||
method: 'PATCH',
|
||||
endpoint: 'api/bookmarkCategoryOrder',
|
||||
body: groupList,
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}, [groupList]);
|
||||
@@ -160,13 +162,15 @@ export function Bookmarks() {
|
||||
body={
|
||||
<form
|
||||
onSubmit={form.onSubmit((values) => {
|
||||
void mutateAsync({
|
||||
mutateAsync({
|
||||
method: 'POST',
|
||||
endpoint: categoriesEndpoint,
|
||||
body: {
|
||||
...values,
|
||||
categoryIndex: groups.length,
|
||||
},
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
handleClose();
|
||||
})}
|
||||
|
||||
@@ -33,7 +33,11 @@ function DebouncedBookmarkItem({
|
||||
};
|
||||
useEffect(() => {
|
||||
if (debounced && Object.keys(debounced).length) {
|
||||
mutateAsync({ method: 'PATCH', endpoint: 'api/bookmarks', body: debounced });
|
||||
mutateAsync({ method: 'PATCH', endpoint: 'api/bookmarks', body: debounced }).catch(
|
||||
(error) => {
|
||||
console.error(error);
|
||||
},
|
||||
);
|
||||
}
|
||||
}, [debounced]);
|
||||
|
||||
|
||||
@@ -94,6 +94,8 @@ function DebouncedGroupSettingsItem({
|
||||
method: 'PATCH',
|
||||
endpoint: 'api/bookmarkCategories',
|
||||
body: debounced,
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
setUpdate(undefined);
|
||||
}
|
||||
@@ -107,7 +109,13 @@ function DebouncedGroupSettingsItem({
|
||||
|
||||
useEffect(() => {
|
||||
if (bookmarkList && bookmarkList !== bookmarks) {
|
||||
mutateAsync({ method: 'PATCH', endpoint: 'api/bookmarkOrder', body: bookmarkList });
|
||||
mutateAsync({
|
||||
method: 'PATCH',
|
||||
endpoint: 'api/bookmarkOrder',
|
||||
body: bookmarkList,
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}, [bookmarkList]);
|
||||
|
||||
@@ -351,6 +359,8 @@ function DebouncedGroupSettingsItem({
|
||||
method: 'POST',
|
||||
endpoint: 'api/bookmarks',
|
||||
body: { ...values, index: bookmarks.length },
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
setPopoverOpened(false);
|
||||
form.reset();
|
||||
|
||||
@@ -47,7 +47,13 @@ function DebouncedGuacamoleClient({
|
||||
|
||||
useEffect(() => {
|
||||
if (debounced && Object.keys(debounced).length) {
|
||||
mutateAsync({ method: 'PATCH', endpoint: 'api/guacamoleClients', body: debounced });
|
||||
mutateAsync({
|
||||
method: 'PATCH',
|
||||
endpoint: 'api/guacamoleClients',
|
||||
body: debounced,
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}, [debounced]);
|
||||
|
||||
|
||||
@@ -108,10 +108,12 @@ export function GuacamoleClients() {
|
||||
body={
|
||||
<form
|
||||
onSubmit={form.onSubmit((values) => {
|
||||
void mutateAsync({
|
||||
mutateAsync({
|
||||
method: 'POST',
|
||||
endpoint: endpoint,
|
||||
body: { ...values, enabled: 1 },
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
handleClose();
|
||||
})}
|
||||
|
||||
@@ -71,7 +71,11 @@ export function Messages() {
|
||||
|
||||
useEffect(() => {
|
||||
if (Object.keys(debounced).length) {
|
||||
void mutateAsync({ method: 'PATCH', endpoint: endpoint, body: debounced });
|
||||
mutateAsync({ method: 'PATCH', endpoint: endpoint, body: debounced }).catch(
|
||||
(error) => {
|
||||
console.error(error);
|
||||
},
|
||||
);
|
||||
}
|
||||
}, [debounced]);
|
||||
|
||||
@@ -116,10 +120,12 @@ export function Messages() {
|
||||
body={
|
||||
<form
|
||||
onSubmit={form.onSubmit((values) => {
|
||||
void mutateAsync({
|
||||
mutateAsync({
|
||||
method: 'POST',
|
||||
endpoint: endpoint,
|
||||
body: { message: values.message },
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
handleClose();
|
||||
})}
|
||||
|
||||
@@ -37,7 +37,11 @@ export function Personalization() {
|
||||
const { isMobile, theme } = useCommonHooks();
|
||||
useEffect(() => {
|
||||
if (debounced && Object.keys(debounced).length) {
|
||||
void mutateAsync({ method: 'PATCH', endpoint: endpoint, body: debounced });
|
||||
mutateAsync({ method: 'PATCH', endpoint: endpoint, body: debounced }).catch(
|
||||
(error) => {
|
||||
console.error(error);
|
||||
},
|
||||
);
|
||||
setUpdate(undefined);
|
||||
}
|
||||
}, [debounced]);
|
||||
|
||||
@@ -40,7 +40,11 @@ function DebouncedSearchProvider({
|
||||
|
||||
useEffect(() => {
|
||||
if (debounced && Object.keys(debounced).length) {
|
||||
mutateAsync({ method: 'PATCH', endpoint: 'api/providers', body: debounced });
|
||||
mutateAsync({ method: 'PATCH', endpoint: 'api/providers', body: debounced }).catch(
|
||||
(error) => {
|
||||
console.error(error);
|
||||
},
|
||||
);
|
||||
}
|
||||
}, [debounced]);
|
||||
|
||||
|
||||
@@ -116,10 +116,12 @@ export function SearchProviders() {
|
||||
body={
|
||||
<form
|
||||
onSubmit={form.onSubmit((values) => {
|
||||
void mutateAsync({
|
||||
mutateAsync({
|
||||
method: 'POST',
|
||||
endpoint: endpoint,
|
||||
body: values,
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
handleClose();
|
||||
})}
|
||||
|
||||
@@ -59,6 +59,8 @@ export function DeleteBackground({
|
||||
method: 'DELETE',
|
||||
endpoint: 'api/backgrounds',
|
||||
body: { fileName },
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -72,6 +72,8 @@ export function EditBackground({
|
||||
fileName.length,
|
||||
)}`,
|
||||
},
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
} else {
|
||||
mutateAsync({
|
||||
@@ -81,6 +83,8 @@ export function EditBackground({
|
||||
fileName,
|
||||
newFileName: rename,
|
||||
},
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
handleEditBackgroundModalClose();
|
||||
|
||||
@@ -41,7 +41,7 @@ export function ThemePlaylists() {
|
||||
const [opened, { open, close }] = useDisclosure(false);
|
||||
const [selectedPlaylist, setSelectedPlaylist] =
|
||||
useState<ThemePlaylist>(defaultPlaylist);
|
||||
const { currentThemePlaylist, theme, isMobile } = useCommonHooks();
|
||||
const { settings, theme, isMobile } = useCommonHooks();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handleOpen = useCallback(
|
||||
@@ -58,10 +58,12 @@ export function ThemePlaylists() {
|
||||
}, [close]);
|
||||
|
||||
const handleSavePlaylist = useCallback(() => {
|
||||
void mutateAsync({
|
||||
mutateAsync({
|
||||
method: selectedPlaylist?.id === undefined ? 'POST' : 'PATCH',
|
||||
endpoint: themePlaylistsEndpoint,
|
||||
body: selectedPlaylist ?? {},
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
close();
|
||||
}, [selectedPlaylist, mutateAsync, themePlaylistsEndpoint, close]);
|
||||
@@ -189,7 +191,7 @@ export function ThemePlaylists() {
|
||||
</Text>
|
||||
<Group>
|
||||
<Group pos={'relative'}>
|
||||
{'All' === currentThemePlaylist && (
|
||||
{'All' === settings.currentThemePlaylist && (
|
||||
<FiChevronRight
|
||||
style={{
|
||||
position: 'absolute',
|
||||
@@ -210,7 +212,7 @@ export function ThemePlaylists() {
|
||||
/>
|
||||
</Group>
|
||||
<Group pos={'relative'}>
|
||||
{'Backgrounds' === currentThemePlaylist && (
|
||||
{'Backgrounds' === settings.currentThemePlaylist && (
|
||||
<FiChevronRight
|
||||
style={{
|
||||
position: 'absolute',
|
||||
@@ -234,7 +236,7 @@ export function ThemePlaylists() {
|
||||
/>
|
||||
</Group>
|
||||
<Group pos={'relative'}>
|
||||
{'No Backgrounds' === currentThemePlaylist && (
|
||||
{'No Backgrounds' === settings.currentThemePlaylist && (
|
||||
<FiChevronRight
|
||||
style={{
|
||||
position: 'absolute',
|
||||
@@ -258,7 +260,7 @@ export function ThemePlaylists() {
|
||||
/>
|
||||
</Group>
|
||||
<Group pos={'relative'}>
|
||||
{'Time of Day' === currentThemePlaylist && (
|
||||
{'Time of Day' === settings.currentThemePlaylist && (
|
||||
<FiChevronRight
|
||||
style={{
|
||||
position: 'absolute',
|
||||
@@ -311,7 +313,7 @@ export function ThemePlaylists() {
|
||||
.map((cardPlaylist: ThemePlaylist) => {
|
||||
return (
|
||||
<Group key={cardPlaylist.name} pos={'relative'}>
|
||||
{cardPlaylist.name === currentThemePlaylist && (
|
||||
{cardPlaylist.name === settings.currentThemePlaylist && (
|
||||
<FiChevronRight
|
||||
style={{
|
||||
position: 'absolute',
|
||||
|
||||
@@ -18,7 +18,7 @@ export function ThemePlaylistCard({
|
||||
allThemes: Theme[];
|
||||
}) {
|
||||
const [hover, setHover] = useState(false);
|
||||
const { setThemeName, setCurrentThemePlaylist, handleDelete } = useCommonHooks();
|
||||
const { updateSetting, handleDelete } = useCommonHooks();
|
||||
const defaultLists = ['All', 'Backgrounds', 'No Backgrounds'];
|
||||
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
||||
const themeList = JSON.parse(
|
||||
@@ -117,8 +117,8 @@ export function ThemePlaylistCard({
|
||||
border: 'solid var(--mantine-color-border-0)',
|
||||
}}
|
||||
onClick={() => {
|
||||
setCurrentThemePlaylist(cardPlaylist.name);
|
||||
setThemeName('random');
|
||||
updateSetting('currentThemePlaylist', cardPlaylist.name);
|
||||
updateSetting('theme', 'random');
|
||||
}}
|
||||
disabled={themeList.length === 0}
|
||||
>
|
||||
|
||||
@@ -25,8 +25,7 @@ export function ThemePlaylistStack({
|
||||
setPressed(true);
|
||||
});
|
||||
const [hover, setHover] = useState(false);
|
||||
const { setThemeName, setCurrentThemePlaylist, handleDelete, currentUser } =
|
||||
useCommonHooks();
|
||||
const { settings, updateSetting, handleDelete } = useCommonHooks();
|
||||
const [indexOffset, setIndexOffset] = useState(0);
|
||||
const [movingIndex, setMovingIndex] = useState(0);
|
||||
const missingTheme = {
|
||||
@@ -41,7 +40,7 @@ export function ThemePlaylistStack({
|
||||
randomEnabled: 1,
|
||||
textShadowsEnabled: 0,
|
||||
appShadowsEnabled: 0,
|
||||
themeFont: currentUser.defaultFont,
|
||||
themeFont: settings.defaultFont,
|
||||
};
|
||||
const [topTheme, setTopTheme] = useState<Theme>(missingTheme);
|
||||
const defaultLists = ['All', 'Backgrounds', 'No Backgrounds'];
|
||||
@@ -156,8 +155,8 @@ export function ThemePlaylistStack({
|
||||
borderRadius: '100%',
|
||||
}}
|
||||
onClick={() => {
|
||||
setCurrentThemePlaylist(cardPlaylist.name);
|
||||
setThemeName('random');
|
||||
updateSetting('currentThemePlaylist', cardPlaylist.name);
|
||||
updateSetting('theme', 'random');
|
||||
}}
|
||||
>
|
||||
{modalOpened && (
|
||||
|
||||
@@ -27,8 +27,7 @@ export function ThemeCard({
|
||||
}) {
|
||||
const { hovered, ref: hoverRef } = useHover();
|
||||
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
||||
const { currentTheme, setCurrentTheme, setCurrentThemePlaylist, handleDelete } =
|
||||
useCommonHooks();
|
||||
const { settings, updateSetting, handleDelete } = useCommonHooks();
|
||||
const [hoverPreview, setHoverPreview] = useDebouncedState(false, 1000);
|
||||
const [transition, setTransition] = useDebouncedState(false, 200);
|
||||
|
||||
@@ -113,7 +112,7 @@ export function ThemeCard({
|
||||
<Button
|
||||
w={'275px'}
|
||||
h={'115px'}
|
||||
disabled={currentTheme === 'random'}
|
||||
disabled={settings.theme === 'random'}
|
||||
style={{
|
||||
backgroundImage:
|
||||
cardTheme?.backgroundImage?.toLocaleLowerCase() === 'none'
|
||||
@@ -137,12 +136,12 @@ export function ThemeCard({
|
||||
}}
|
||||
onClick={() => {
|
||||
if (enabled !== false) {
|
||||
setCurrentTheme(cardTheme.name);
|
||||
setCurrentThemePlaylist('All');
|
||||
updateSetting('theme', cardTheme.name);
|
||||
updateSetting('currentThemePlaylist', 'All');
|
||||
}
|
||||
}}
|
||||
>
|
||||
{hovered && enabled && currentTheme !== 'random' && (
|
||||
{hovered && enabled && settings.theme !== 'random' && (
|
||||
<Overlay
|
||||
style={{
|
||||
opacity: !hoverPreview || transition ? 0.5 : 0,
|
||||
|
||||
@@ -29,14 +29,13 @@ export function ThemeManagementPill({
|
||||
transition: boolean;
|
||||
setTransition: (transition: boolean) => void;
|
||||
}) {
|
||||
const { currentTheme, currentThemePlaylist, handleThemeChange, theme } =
|
||||
useCommonHooks();
|
||||
const { settings, handleThemeChange, theme } = useCommonHooks();
|
||||
const [shiftEdit, setShiftEdit] = useState<number>(0);
|
||||
const [shiftShuffle, setShiftShuffle] = useState<number>(0);
|
||||
const { mutateAsync } = useMutationAPI();
|
||||
return (
|
||||
<>
|
||||
{currentTheme === 'random' && cardTheme.name === theme.name ? (
|
||||
{settings.theme === 'random' && cardTheme.name === theme.name ? (
|
||||
<Group>
|
||||
<Group
|
||||
onMouseEnter={() => {
|
||||
@@ -81,16 +80,16 @@ export function ThemeManagementPill({
|
||||
setShiftShuffle(0);
|
||||
}}
|
||||
onClick={() => {
|
||||
if (currentThemePlaylist === 'All') {
|
||||
if (settings.currentThemePlaylist === 'All') {
|
||||
handleThemeChange(allThemes, false);
|
||||
} else if (currentThemePlaylist === 'Backgrounds') {
|
||||
} else if (settings.currentThemePlaylist === 'Backgrounds') {
|
||||
handleThemeChange(
|
||||
allThemes.filter(
|
||||
(a: any) => !a.backgroundImage.toLowerCase().includes('none'),
|
||||
),
|
||||
false,
|
||||
);
|
||||
} else if (currentThemePlaylist === 'No Backgrounds') {
|
||||
} else if (settings.currentThemePlaylist === 'No Backgrounds') {
|
||||
handleThemeChange(
|
||||
allThemes.filter((a: any) =>
|
||||
a.backgroundImage.toLowerCase().includes('none'),
|
||||
@@ -100,7 +99,7 @@ export function ThemeManagementPill({
|
||||
} else {
|
||||
const themeList = JSON.parse(
|
||||
themePlaylists?.find(
|
||||
(t: ThemePlaylist) => t?.name === currentThemePlaylist,
|
||||
(t: ThemePlaylist) => t?.name === settings.currentThemePlaylist,
|
||||
)?.themeList ?? '[]',
|
||||
);
|
||||
const filteredThemes = allThemes.filter((t: Theme) =>
|
||||
@@ -203,6 +202,8 @@ export function ThemeManagementPill({
|
||||
method: 'PATCH',
|
||||
endpoint: 'api/themes',
|
||||
body: { ...cardTheme, randomEnabled: !cardTheme.randomEnabled },
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -30,7 +30,7 @@ export function ThemeModal({
|
||||
themePlaylists: ThemePlaylist[];
|
||||
}) {
|
||||
const themesEndpoint = 'api/themes';
|
||||
const { currentUser, isMobile } = useCommonHooks();
|
||||
const { settings, isMobile } = useCommonHooks();
|
||||
const [defaultTheme, setDefaultTheme] = useState<Theme>({ ...selectedTheme });
|
||||
const [designerTheme, setDesignerTheme] = useState<Theme>({
|
||||
...selectedTheme,
|
||||
@@ -68,7 +68,7 @@ export function ThemeModal({
|
||||
randomEnabled: 1,
|
||||
textShadowsEnabled: 0,
|
||||
appShadowsEnabled: 0,
|
||||
themeFont: currentUser.defaultFont,
|
||||
themeFont: settings.defaultFont,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -77,6 +77,8 @@ export function ThemeModal({
|
||||
method: designerTheme?.id === undefined ? 'POST' : 'PATCH',
|
||||
endpoint: themesEndpoint,
|
||||
body: designerTheme,
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
handleClose();
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ export default function ThemeChanges({
|
||||
designerTheme: Theme;
|
||||
setDesignerTheme: (theme: Theme) => void;
|
||||
}) {
|
||||
const { currentUser, getObjectDiff } = useCommonHooks();
|
||||
const { settings, getObjectDiff } = useCommonHooks();
|
||||
return (
|
||||
<Stack
|
||||
gap={0}
|
||||
@@ -148,7 +148,7 @@ export default function ThemeChanges({
|
||||
}}
|
||||
>
|
||||
{defaultTheme[key as keyof Theme] ||
|
||||
`Default (${currentUser.defaultFont})`}
|
||||
`Default (${settings.defaultFont})`}
|
||||
</Text>
|
||||
<Text>→</Text>
|
||||
<Text
|
||||
@@ -158,7 +158,7 @@ export default function ThemeChanges({
|
||||
}}
|
||||
>
|
||||
{designerTheme[key as keyof Theme] ||
|
||||
`Default (${currentUser.defaultFont})`}
|
||||
`Default (${settings.defaultFont})`}
|
||||
</Text>
|
||||
</Group>
|
||||
)}
|
||||
@@ -197,9 +197,11 @@ export default function ThemeChanges({
|
||||
<ColorSwatch
|
||||
h={50}
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(
|
||||
defaultTheme[key as keyof Theme] as string,
|
||||
);
|
||||
navigator.clipboard
|
||||
.writeText(defaultTheme[key as keyof Theme] as string)
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
notifications.show({
|
||||
title: (
|
||||
<Text fw={600} c={'var(--mantine-color-accent-0)'}>
|
||||
@@ -250,9 +252,11 @@ export default function ThemeChanges({
|
||||
<ColorSwatch
|
||||
h={50}
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(
|
||||
defaultTheme[key as keyof Theme] as string,
|
||||
);
|
||||
navigator.clipboard
|
||||
.writeText(defaultTheme[key as keyof Theme] as string)
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
notifications.show({
|
||||
title: (
|
||||
<Text fw={600} c={'var(--mantine-color-accent-0)'}>
|
||||
|
||||
@@ -37,7 +37,7 @@ export function ThemeEditor({
|
||||
duplicate: boolean;
|
||||
setDuplicate: (duplicate: boolean) => void;
|
||||
}) {
|
||||
const { currentUser, isMobile } = useCommonHooks();
|
||||
const { settings, isMobile } = useCommonHooks();
|
||||
const [colorId, setColorId] = useState<string>('');
|
||||
const [colorValue, setColorValue] = useState('#000000');
|
||||
const [rgba, setRgba] = useState<{ r: number; g: number; b: number; a?: number }>({
|
||||
@@ -80,7 +80,7 @@ export function ThemeEditor({
|
||||
} else if (font && !customFontEnabled) {
|
||||
setDesignerTheme({
|
||||
...designerTheme,
|
||||
themeFont: currentUser.defaultFont || 'Jost',
|
||||
themeFont: settings.defaultFont || 'Jost',
|
||||
});
|
||||
}
|
||||
}, [font]);
|
||||
|
||||
@@ -5,7 +5,7 @@ import { selectClasses, textClasses } from 'styles';
|
||||
import type { Theme } from 'types';
|
||||
|
||||
export function ThemeModalContent({ selectedTheme }: { selectedTheme: Theme }) {
|
||||
const { currentUser, textShadowStyle, largeTextShadowStyle } = useCommonHooks();
|
||||
const { settings, textShadowStyle, largeTextShadowStyle } = useCommonHooks();
|
||||
return (
|
||||
<Stack
|
||||
style={{
|
||||
@@ -31,7 +31,7 @@ export function ThemeModalContent({ selectedTheme }: { selectedTheme: Theme }) {
|
||||
'--mantine-color-border-0': selectedTheme?.borderColor,
|
||||
'--mantine-color-search-0': selectedTheme?.searchColor,
|
||||
transition: 'all 0.2s ease',
|
||||
fontFamily: selectedTheme.themeFont ?? currentUser.defaultFont,
|
||||
fontFamily: selectedTheme.themeFont ?? settings.defaultFont,
|
||||
}}
|
||||
align='center'
|
||||
gap={0}
|
||||
@@ -47,7 +47,7 @@ export function ThemeModalContent({ selectedTheme }: { selectedTheme: Theme }) {
|
||||
color: selectedTheme?.primaryColor,
|
||||
borderBottom: `thin solid ${selectedTheme?.borderColor}`,
|
||||
backgroundColor: `color-mix(in srgb, ${selectedTheme?.searchColor}, transparent 50%)`,
|
||||
fontFamily: selectedTheme.themeFont ?? currentUser.defaultFont,
|
||||
fontFamily: selectedTheme.themeFont ?? settings.defaultFont,
|
||||
},
|
||||
}}
|
||||
data={[
|
||||
|
||||
@@ -35,7 +35,7 @@ export default function ThemeOptions({
|
||||
duplicate: boolean;
|
||||
randomColor: (withAlpha?: boolean) => string;
|
||||
}) {
|
||||
const { currentUser } = useCommonHooks();
|
||||
const { settings } = useCommonHooks();
|
||||
return (
|
||||
<Stack h={'100%'} justify='space-between' align='center' gap={4}>
|
||||
<Stack w={'100%'} align='center' gap={0} h={'min-content'}>
|
||||
@@ -137,7 +137,7 @@ export default function ThemeOptions({
|
||||
onClick={() => {
|
||||
setCustomFontEnabled(!customFontEnabled);
|
||||
setFont(
|
||||
customFontEnabled ? currentUser.defaultFont : defaultTheme.themeFont,
|
||||
customFontEnabled ? settings.defaultFont : defaultTheme.themeFont,
|
||||
);
|
||||
}}
|
||||
style={{
|
||||
@@ -163,7 +163,7 @@ export default function ThemeOptions({
|
||||
autoLoad
|
||||
defaultValue={
|
||||
designerTheme.themeFont === null
|
||||
? currentUser.defaultFont
|
||||
? settings.defaultFont
|
||||
: designerTheme.themeFont
|
||||
}
|
||||
value={(pickerFont: string) => {
|
||||
@@ -183,7 +183,7 @@ export default function ThemeOptions({
|
||||
color={'red'}
|
||||
size={32}
|
||||
onClick={() => {
|
||||
setFont(currentUser.defaultFont);
|
||||
setFont(settings.defaultFont);
|
||||
setCustomFontEnabled(false);
|
||||
}}
|
||||
style={{
|
||||
|
||||
@@ -84,15 +84,7 @@ export function Themes() {
|
||||
),
|
||||
] as string[];
|
||||
|
||||
const {
|
||||
currentTheme,
|
||||
currentUser,
|
||||
setCurrentTheme,
|
||||
theme,
|
||||
isMobile,
|
||||
currentThemePlaylist,
|
||||
setCurrentThemePlaylist,
|
||||
} = useCommonHooks();
|
||||
const { settings, updateSetting, theme, isMobile } = useCommonHooks();
|
||||
|
||||
const [selectedTheme, setSelectedTheme] = useState<Theme>({
|
||||
id: undefined,
|
||||
@@ -106,7 +98,7 @@ export function Themes() {
|
||||
randomEnabled: 1,
|
||||
textShadowsEnabled: 0,
|
||||
appShadowsEnabled: 0,
|
||||
themeFont: currentUser.defaultFont,
|
||||
themeFont: settings.defaultFont,
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isNew, setIsNew] = useState(false);
|
||||
@@ -184,7 +176,7 @@ export function Themes() {
|
||||
>
|
||||
<Overlay
|
||||
display={
|
||||
currentTheme === 'random' && debouncedSearch === '' ? 'flex' : 'none'
|
||||
settings.theme === 'random' && debouncedSearch === '' ? 'flex' : 'none'
|
||||
}
|
||||
blur={5}
|
||||
style={{
|
||||
@@ -199,7 +191,7 @@ export function Themes() {
|
||||
{allThemes?.length > 0 ? (
|
||||
allThemes.map((cardTheme: Theme) => (
|
||||
<Group key={cardTheme.name} pos={'relative'}>
|
||||
{cardTheme.name === currentTheme && (
|
||||
{cardTheme.name === settings.theme && (
|
||||
<FiChevronRight
|
||||
style={{
|
||||
position: 'absolute',
|
||||
@@ -219,11 +211,12 @@ export function Themes() {
|
||||
<Box
|
||||
style={{
|
||||
zIndex:
|
||||
currentTheme === 'random' &&
|
||||
settings.theme === 'random' &&
|
||||
(themePlaylists
|
||||
.find(
|
||||
(t: ThemePlaylist) =>
|
||||
t.name.toLowerCase() === currentThemePlaylist.toLowerCase(),
|
||||
t.name.toLowerCase() ===
|
||||
settings.currentThemePlaylist.toLowerCase(),
|
||||
)
|
||||
?.themeList.includes(`${cardTheme.id}`) ||
|
||||
cardTheme.name === theme.name)
|
||||
@@ -310,7 +303,9 @@ export function Themes() {
|
||||
fontSize: isMobile ? '.8rem' : '1.2rem',
|
||||
}}
|
||||
>
|
||||
{currentThemePlaylist === 'All' ? 'All' : currentThemePlaylist}
|
||||
{settings.currentThemePlaylist === 'All'
|
||||
? 'All'
|
||||
: settings.currentThemePlaylist}
|
||||
</Text>
|
||||
</Group>
|
||||
<Divider
|
||||
@@ -320,13 +315,13 @@ export function Themes() {
|
||||
/>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (currentThemePlaylist === 'All') {
|
||||
if (settings.currentThemePlaylist === 'All') {
|
||||
void navigate(
|
||||
`/${import.meta.env.VITE_BASE_URL}/settings/themePlaylists`,
|
||||
);
|
||||
} else {
|
||||
setCurrentThemePlaylist('All');
|
||||
setCurrentTheme(theme.name);
|
||||
updateSetting('currentThemePlaylist', 'All');
|
||||
updateSetting('theme', theme.name);
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
@@ -334,7 +329,7 @@ export function Themes() {
|
||||
padding: isMobile ? 0 : undefined,
|
||||
}}
|
||||
>
|
||||
{currentThemePlaylist === 'All' ? (
|
||||
{settings.currentThemePlaylist === 'All' ? (
|
||||
isMobile ? (
|
||||
<FiMousePointer />
|
||||
) : (
|
||||
@@ -350,11 +345,11 @@ export function Themes() {
|
||||
className={toolTipClasses.default}
|
||||
classNames={toolTipClasses}
|
||||
label='Shuffle is always on when a playlist is active.'
|
||||
disabled={currentThemePlaylist === 'All'}
|
||||
disabled={settings.currentThemePlaylist === 'All'}
|
||||
>
|
||||
<Group>
|
||||
<Switch
|
||||
disabled={currentThemePlaylist !== 'All'}
|
||||
disabled={settings.currentThemePlaylist !== 'All'}
|
||||
label={
|
||||
isMobile ? (
|
||||
<FaDice size={20} color={'var(--mantine-color-primary-0)'} />
|
||||
@@ -363,10 +358,10 @@ export function Themes() {
|
||||
)
|
||||
}
|
||||
labelPosition='left'
|
||||
checked={currentTheme === 'random'}
|
||||
checked={settings.theme === 'random'}
|
||||
onChange={(e) => {
|
||||
setCurrentTheme(e.target.checked ? 'random' : theme.name);
|
||||
setCurrentThemePlaylist('All');
|
||||
updateSetting('theme', e.target.checked ? 'random' : theme.name);
|
||||
updateSetting('currentThemePlaylist', 'All');
|
||||
}}
|
||||
className={switchClasses.default}
|
||||
classNames={switchClasses}
|
||||
|
||||
@@ -28,7 +28,11 @@ function DebouncedWolClient({
|
||||
|
||||
useEffect(() => {
|
||||
if (debounced && Object.keys(debounced).length) {
|
||||
mutateAsync({ method: 'PATCH', endpoint: 'api/WolClients', body: debounced });
|
||||
mutateAsync({ method: 'PATCH', endpoint: 'api/WolClients', body: debounced }).catch(
|
||||
(error) => {
|
||||
console.error(error);
|
||||
},
|
||||
);
|
||||
}
|
||||
}, [debounced]);
|
||||
|
||||
|
||||
@@ -99,10 +99,12 @@ export function WolClients() {
|
||||
body={
|
||||
<form
|
||||
onSubmit={form.onSubmit((values) => {
|
||||
void mutateAsync({
|
||||
mutateAsync({
|
||||
method: 'POST',
|
||||
endpoint: endpoint,
|
||||
body: values,
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
handleClose();
|
||||
})}
|
||||
|
||||
@@ -7,6 +7,7 @@ export type { Message } from './message';
|
||||
export type { PersonalData } from './personalData';
|
||||
export type { Provider } from './provider';
|
||||
export type { SearchHistoryItem, SearchShortcut } from './search';
|
||||
export type { Settings } from './settings';
|
||||
export type { Theme, ThemePlaylist } from './theme';
|
||||
export { ThemeProperties } from './theme';
|
||||
export type { User } from './user';
|
||||
|
||||
28
src/types/settings.ts
Normal file
28
src/types/settings.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
export interface Settings {
|
||||
theme: string;
|
||||
appFilter: string[];
|
||||
appSort: string;
|
||||
appDensity: number;
|
||||
appScale: number;
|
||||
bookmarkSort: string;
|
||||
bookmarkFilter: string[];
|
||||
bookmarkDensity: number;
|
||||
bookmarkScale: number;
|
||||
currentThemePlaylist: string;
|
||||
defaultProviderLabel: string;
|
||||
defaultProviderURL: string;
|
||||
defaultProviderPrefix: string;
|
||||
defaultProviderIcon: string;
|
||||
timeFormat: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
physicalAddress: string;
|
||||
enableWol: number;
|
||||
enableGuacamole: number;
|
||||
guacamoleHost: string;
|
||||
nickname: string;
|
||||
defaultFont: string;
|
||||
lat: number;
|
||||
lon: number;
|
||||
}
|
||||
Reference in New Issue
Block a user