From ee4490654f21c3bf598985b6e89bb509340c51df Mon Sep 17 00:00:00 2001
From: Douglas Fabris <devfabris@gmail.com>
Date: Thu, 16 Feb 2023 00:18:25 -0300
Subject: [PATCH] Chore: Refactor useThemeMode in favor of userPreferences
 (#28063)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Júlia Jaeger Foresti <60678893+juliajforesti@users.noreply.github.com>
---
 .../meteor/app/lib/server/startup/settings.ts | 19 +++++++++++++++++
 .../client/sidebar/header/UserDropdown.tsx    |  6 +++---
 .../preferences/PreferencesGlobalSection.tsx  | 21 ++++++++++++++++---
 .../rocketchat-i18n/i18n/en.i18n.json         |  3 ++-
 .../ui-theming/src/hooks/useThemeMode.ts      | 14 +++++++++----
 .../v1/users/UsersSetPreferenceParamsPOST.ts  |  5 +++++
 .../methods/saveUserPreferences.ts            |  1 +
 7 files changed, 58 insertions(+), 11 deletions(-)

diff --git a/apps/meteor/app/lib/server/startup/settings.ts b/apps/meteor/app/lib/server/startup/settings.ts
index e8419471040..364c31741a8 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 6bb57cdccfc..ce74cf4945e 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 => {
 				<OptionIcon name='sun' />
 				<OptionContent>{t('Theme_light')}</OptionContent>
 				<OptionColumn>
-					<RadioButton checked={selectedTheme === 'light'} onChange={(): void => setTheme('light')} m='x4' />
+					<RadioButton checked={selectedTheme === 'light'} onChange={setTheme('light')} m='x4' />
 				</OptionColumn>
 			</Option>
 			<Option>
 				<OptionIcon name='moon' />
 				<OptionContent>{t('Theme_dark')}</OptionContent>
 				<OptionColumn>
-					<RadioButton checked={selectedTheme === 'dark'} onChange={(): void => setTheme('dark')} m='x4' />
+					<RadioButton checked={selectedTheme === 'dark'} onChange={setTheme('dark')} m='x4' />
 				</OptionColumn>
 			</Option>
 			<Option>
 				<OptionIcon name='desktop' />
 				<OptionContent>{t('Theme_match_system')}</OptionContent>
 				<OptionColumn>
-					<RadioButton checked={selectedTheme === 'auto'} onChange={(): void => setTheme('auto')} m='x4' />
+					<RadioButton checked={selectedTheme === 'auto'} onChange={setTheme('auto')} m='x4' />
 				</OptionColumn>
 			</Option>
 			<OptionDivider />
diff --git a/apps/meteor/client/views/account/preferences/PreferencesGlobalSection.tsx b/apps/meteor/client/views/account/preferences/PreferencesGlobalSection.tsx
index 7c2410deb9f..edc7e64b9dc 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 (
 		<Accordion.Item title={t('Global')} {...props}>
 			<FieldGroup>
@@ -48,6 +57,12 @@ const PreferencesGlobalSection = ({ onChange, commitRef, ...props }: FormSection
 						/>
 					</Field.Row>
 				</Field>
+				<Field>
+					<Field.Label>{t('Theme_Appearence')}</Field.Label>
+					<Field.Row>
+						<Select value={themeAppearence} onChange={handleThemeAppearence} options={themeOptions} />
+					</Field.Row>
+				</Field>
 			</FieldGroup>
 		</Accordion.Item>
 	);
diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
index c076a28648f..4dbffde6b8b 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 > Workspaces</1> and click <3>'Register self-managed'</3>.",
   "cloud.RegisterWorkspace_Setup_Terms_Privacy": "I agree with <1>Terms and Conditions</1> and <3>Privacy Policy</3>",
   "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 ff1c47a5f52..84deebb2837 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<SetStateAction<ThemeMode>>, 'light' | 'dark'] => {
-	const [theme, setTheme] = useSessionStorage<ThemeMode>(`rcx-theme`, value);
+export const useThemeMode = (): [ThemeMode, (value: ThemeMode) => () => void, 'light' | 'dark'] => {
+	const theme = useUserPreference<ThemeMode>('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 d3082d5c678..5a07797dc80 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 6b455f74f83..ae1df52b409 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;
 };
 
-- 
GitLab