diff --git a/packages/pl-fe/src/actions/settings.ts b/packages/pl-fe/src/actions/settings.ts index 04e6c526b..65ded3f3f 100644 --- a/packages/pl-fe/src/actions/settings.ts +++ b/packages/pl-fe/src/actions/settings.ts @@ -17,13 +17,16 @@ const FE_NAME = 'pl_fe'; type SettingOpts = { /** Whether to display an alert when settings are saved. */ showAlert?: boolean; + save?: boolean; } const saveSuccessMessage = defineMessage({ id: 'settings.save.success', defaultMessage: 'Your preferences have been saved!' }); const changeSetting = (path: string[], value: any, opts?: SettingOpts) => { useSettingsStore.getState().changeSetting(path, value); - return saveSettings(opts); + + if (opts?.save !== false) return saveSettings(opts); + return () => {}; }; const saveSettings = (opts?: SettingOpts) => diff --git a/packages/pl-fe/src/features/preferences/index.tsx b/packages/pl-fe/src/features/preferences/index.tsx index 306cbcbb1..ee31cd601 100644 --- a/packages/pl-fe/src/features/preferences/index.tsx +++ b/packages/pl-fe/src/features/preferences/index.tsx @@ -1,7 +1,8 @@ +import debounce from 'lodash/debounce'; import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { changeSetting } from 'pl-fe/actions/settings'; +import { changeSetting, saveSettings } from 'pl-fe/actions/settings'; import List, { ListItem } from 'pl-fe/components/list'; import Form from 'pl-fe/components/ui/form'; import { Mutliselect, SelectDropdown } from 'pl-fe/features/forms'; @@ -9,10 +10,15 @@ import SettingToggle from 'pl-fe/features/notifications/components/setting-toggl import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch'; import { useFeatures } from 'pl-fe/hooks/use-features'; import { useInstance } from 'pl-fe/hooks/use-instance'; +import { usePlFeConfig } from 'pl-fe/hooks/use-pl-fe-config'; import { useSettings } from 'pl-fe/hooks/use-settings'; +import colors from 'pl-fe/utils/colors'; +import { PaletteListItem } from '../theme-editor'; import ThemeToggle from '../ui/components/theme-toggle'; +import type { AppDispatch } from 'pl-fe/store'; + const languages = { en: 'English', ar: 'العربية', @@ -91,13 +97,19 @@ const messages = defineMessages({ content_type_plaintext: { id: 'preferences.options.content_type_plaintext', defaultMessage: 'Plain text' }, content_type_markdown: { id: 'preferences.options.content_type_markdown', defaultMessage: 'Markdown' }, content_type_html: { id: 'preferences.options.content_type_html', defaultMessage: 'HTML' }, + brandColor: { id: 'preferences.options.brand_color', defaultMessage: 'Base color' }, }); +const debouncedSave = debounce((dispatch: AppDispatch) => { + dispatch(saveSettings({ showAlert: true })); +}, 1000); + const Preferences = () => { const intl = useIntl(); const dispatch = useAppDispatch(); const features = useFeatures(); const settings = useSettings(); + const plFeConfig = usePlFeConfig(); const instance = useInstance(); const onSelectChange = (event: React.ChangeEvent, path: string[]) => { @@ -109,7 +121,14 @@ const Preferences = () => { }; const onToggleChange = (key: string[], checked: boolean) => { - dispatch(changeSetting(key, checked, { showAlert: true })); + dispatch(changeSetting(key, checked)); + }; + + const onBrandColorChange = (brandColor: string) => { + if (!settings.theme?.brandColor && brandColor === (plFeConfig.brandColor || '#d80482')) return; + + dispatch(changeSetting(['theme', 'brandColor'], brandColor, { showAlert: true, save: false })); + debouncedSave(dispatch); }; const displayMediaOptions = React.useMemo(() => ({ @@ -152,7 +171,14 @@ const Preferences = () => { }> + onBrandColorChange(palette['500'])} + /> + + }> = ({ label, value, onChange }) => ); }; -export { ThemeEditor as default }; +export { ThemeEditor as default, PaletteListItem }; diff --git a/packages/pl-fe/src/locales/en.json b/packages/pl-fe/src/locales/en.json index 5937b8b77..4e20aeab1 100644 --- a/packages/pl-fe/src/locales/en.json +++ b/packages/pl-fe/src/locales/en.json @@ -1266,6 +1266,7 @@ "preferences.fields.wrench_label": "Display wrench reaction button", "preferences.hints.demetricator": "Decrease social media anxiety by hiding all numbers from the site.", "preferences.notifications.advanced": "Show all notification categories", + "preferences.options.brand_color": "Base color", "preferences.options.content_type_html": "HTML", "preferences.options.content_type_markdown": "Markdown", "preferences.options.content_type_mfm": "MFM", diff --git a/packages/pl-fe/src/schemas/pl-fe/settings.ts b/packages/pl-fe/src/schemas/pl-fe/settings.ts index 424ef3dc3..446731377 100644 --- a/packages/pl-fe/src/schemas/pl-fe/settings.ts +++ b/packages/pl-fe/src/schemas/pl-fe/settings.ts @@ -38,6 +38,12 @@ const settingsSchema = v.object({ knownLanguages: v.fallback(v.array(v.string()), []), showWrenchButton: v.fallback(v.boolean(), false), + theme: v.fallback(v.optional(v.object({ + brandColor: v.fallback(v.string(), ''), + accentColor: v.fallback(v.string(), ''), + colors: v.any(), + })), undefined), + systemFont: v.fallback(v.boolean(), false), demetricator: v.fallback(v.boolean(), false), diff --git a/packages/pl-fe/src/stores/settings.ts b/packages/pl-fe/src/stores/settings.ts index 885769cee..aac53d9d9 100644 --- a/packages/pl-fe/src/stores/settings.ts +++ b/packages/pl-fe/src/stores/settings.ts @@ -62,6 +62,7 @@ const useSettingsStore = create()(mutative((set) => ({ }), changeSetting: (path: string[], value: any) => set((state: State) => { + state.userSettings.saved = false; changeSetting(state.userSettings, path, value); mergeSettings(state);