diff --git a/apps/meteor/app/lib/server/startup/settings.ts b/apps/meteor/app/lib/server/startup/settings.ts
index e8419471040a..364c31741a8c 100644
--- a/apps/meteor/app/lib/server/startup/settings.ts
+++ b/apps/meteor/app/lib/server/startup/settings.ts
@@ -375,6 +375,25 @@ settingsRegistry.addGroup('Accounts', function () {
public: true,
i18nLabel: 'Group_by_Type',
});
+ this.add('Accounts_Default_User_Preferences_themeAppearence', 'auto', {
+ type: 'select',
+ values: [
+ {
+ key: 'auto',
+ i18nLabel: 'Theme_match_system',
+ },
+ {
+ key: 'light',
+ i18nLabel: 'Theme_light',
+ },
+ {
+ key: 'dark',
+ i18nLabel: 'Theme_dark',
+ },
+ ],
+ public: true,
+ i18nLabel: 'Theme_Appearence',
+ });
this.add('Accounts_Default_User_Preferences_sidebarViewMode', 'medium', {
type: 'select',
values: [
diff --git a/apps/meteor/client/sidebar/header/UserDropdown.tsx b/apps/meteor/client/sidebar/header/UserDropdown.tsx
index 6bb57cdccfc2..ce74cf4945ee 100644
--- a/apps/meteor/client/sidebar/header/UserDropdown.tsx
+++ b/apps/meteor/client/sidebar/header/UserDropdown.tsx
@@ -192,21 +192,21 @@ const UserDropdown = ({ user, onClose }: UserDropdownProps): ReactElement => {
{t('Theme_light')}
- setTheme('light')} m='x4' />
+
diff --git a/apps/meteor/client/views/account/preferences/PreferencesGlobalSection.tsx b/apps/meteor/client/views/account/preferences/PreferencesGlobalSection.tsx
index 7c2410deb9fb..edc7e64b9dc8 100644
--- a/apps/meteor/client/views/account/preferences/PreferencesGlobalSection.tsx
+++ b/apps/meteor/client/views/account/preferences/PreferencesGlobalSection.tsx
@@ -1,5 +1,5 @@
import type { SelectOption } from '@rocket.chat/fuselage';
-import { Accordion, Field, FieldGroup, MultiSelect } from '@rocket.chat/fuselage';
+import { Select, Accordion, Field, FieldGroup, MultiSelect } from '@rocket.chat/fuselage';
import { useUserPreference, useTranslation } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import React, { useMemo } from 'react';
@@ -11,6 +11,7 @@ const PreferencesGlobalSection = ({ onChange, commitRef, ...props }: FormSection
const t = useTranslation();
const userDontAskAgainList = useUserPreference<{ action: string; label: string }[]>('dontAskAgainList');
+ const themePreference = useUserPreference<'light' | 'dark' | 'auto'>('themeAppearence');
const options = useMemo(
() => (userDontAskAgainList || []).map(({ action, label }) => [action, label]) as SelectOption[],
@@ -22,18 +23,26 @@ const PreferencesGlobalSection = ({ onChange, commitRef, ...props }: FormSection
const { values, handlers, commit } = useForm(
{
dontAskAgainList: selectedOptions,
+ themeAppearence: themePreference,
},
onChange,
);
- const { dontAskAgainList } = values as {
+ const { dontAskAgainList, themeAppearence } = values as {
dontAskAgainList: string[];
+ themeAppearence: string;
};
- const { handleDontAskAgainList } = handlers;
+ const { handleDontAskAgainList, handleThemeAppearence } = handlers;
commitRef.current.global = commit;
+ const themeOptions: SelectOption[] = [
+ ['auto', t('Theme_match_system')],
+ ['light', t('Theme_light')],
+ ['dark', t('Theme_dark')],
+ ];
+
return (
@@ -48,6 +57,12 @@ const PreferencesGlobalSection = ({ onChange, commitRef, ...props }: FormSection
/>
+
+ {t('Theme_Appearence')}
+
+
+
+
);
diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
index c076a28648f9..4dbffde6b8b9 100644
--- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
+++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
@@ -5643,5 +5643,6 @@
"cloud.RegisterWorkspace_Token_Step_One": "1. Go to: <1>cloud.rocket.chat > Workspaces1> and click <3>'Register self-managed'3>.",
"cloud.RegisterWorkspace_Setup_Terms_Privacy": "I agree with <1>Terms and Conditions1> and <3>Privacy Policy3>",
"Larger_amounts_of_active_connections": "For larger amounts of active connections you can consider our",
- "multiple_instance_solutions": "multiple instance solutions"
+ "multiple_instance_solutions": "multiple instance solutions",
+ "Theme_Appearence": "Theme Appearence"
}
diff --git a/ee/packages/ui-theming/src/hooks/useThemeMode.ts b/ee/packages/ui-theming/src/hooks/useThemeMode.ts
index ff1c47a5f52e..84deebb28374 100644
--- a/ee/packages/ui-theming/src/hooks/useThemeMode.ts
+++ b/ee/packages/ui-theming/src/hooks/useThemeMode.ts
@@ -1,5 +1,6 @@
-import { useDarkMode, useSessionStorage } from '@rocket.chat/fuselage-hooks';
-import type { Dispatch, SetStateAction } from 'react';
+import { useDarkMode } from '@rocket.chat/fuselage-hooks';
+import { useEndpoint, useUserPreference } from '@rocket.chat/ui-contexts';
+import { useCallback } from 'react';
type ThemeMode = 'light' | 'dark' | 'auto';
@@ -9,8 +10,13 @@ type ThemeMode = 'light' | 'dark' | 'auto';
* @returns [currentThemeMode, setThemeMode, resolvedThemeMode]
*/
-export const useThemeMode = (value: ThemeMode = 'auto'): [ThemeMode, Dispatch>, 'light' | 'dark'] => {
- const [theme, setTheme] = useSessionStorage(`rcx-theme`, value);
+export const useThemeMode = (): [ThemeMode, (value: ThemeMode) => () => void, 'light' | 'dark'] => {
+ const theme = useUserPreference('themeAppearence') || 'auto';
+
+ const saveUserPreferences = useEndpoint('POST', '/v1/users.setPreferences');
+
+ const setTheme = (value: ThemeMode): (() => void) =>
+ useCallback(() => saveUserPreferences({ data: { themeAppearence: value } }), [value]);
return [theme, setTheme, useDarkMode(theme === 'auto' ? undefined : theme === 'dark') ? 'dark' : 'light'];
};
diff --git a/packages/rest-typings/src/v1/users/UsersSetPreferenceParamsPOST.ts b/packages/rest-typings/src/v1/users/UsersSetPreferenceParamsPOST.ts
index d3082d5c6782..5a07797dc80a 100644
--- a/packages/rest-typings/src/v1/users/UsersSetPreferenceParamsPOST.ts
+++ b/packages/rest-typings/src/v1/users/UsersSetPreferenceParamsPOST.ts
@@ -38,6 +38,7 @@ export type UsersSetPreferencesParamsPOST = {
sidebarGroupByType?: boolean;
muteFocusedConversations?: boolean;
dontAskAgainList?: Array<{ action: string; label: string }>;
+ themeAppearence?: 'auto' | 'light' | 'dark';
receiveLoginDetectionEmail?: boolean;
idleTimeLimit?: number;
omnichannelTranscriptEmail?: boolean;
@@ -190,6 +191,10 @@ const UsersSetPreferencesParamsPostSchema = {
},
nullable: true,
},
+ themeAppearence: {
+ type: 'string',
+ nullable: true,
+ },
receiveLoginDetectionEmail: {
type: 'boolean',
nullable: true,
diff --git a/packages/ui-contexts/src/ServerContext/methods/saveUserPreferences.ts b/packages/ui-contexts/src/ServerContext/methods/saveUserPreferences.ts
index 6b455f74f835..ae1df52b409f 100644
--- a/packages/ui-contexts/src/ServerContext/methods/saveUserPreferences.ts
+++ b/packages/ui-contexts/src/ServerContext/methods/saveUserPreferences.ts
@@ -29,6 +29,7 @@ type UserPreferences = {
sidebarGroupByType: boolean;
muteFocusedConversations: boolean;
dontAskAgainList: { action: string; label: string }[];
+ themeAppearence: 'auto' | 'light' | 'dark';
receiveLoginDetectionEmail: boolean;
};