Skip to content

Commit

Permalink
refactor: 사용자 선호 테마 및 시스템 설정 고려하며 테마 제어하는 함수 리팩토링
Browse files Browse the repository at this point in the history
관리해야 할 상태 간소화하기 위함
  • Loading branch information
jaem1n207 committed Oct 3, 2024
1 parent 1dae7cf commit 50917f7
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 40 deletions.
1 change: 0 additions & 1 deletion src/entities/theme/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export { useDark } from './model/use-dark';
export { useThemeStore } from './model/use-theme-store';
36 changes: 16 additions & 20 deletions src/entities/theme/model/use-dark.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
import { useThemeStore } from '~/entities/theme';
import { useSystemDark } from '~/entities/theme/model/use-system-dark';
import type { Theme } from '~/entities/theme/types';
import { useThemePreferenceStore } from '~/entities/theme/model/use-theme-store';
import { getEffectiveTheme, getNextTheme } from '../utils/theme-utils';

const isDarkMode = (theme?: Theme | null, isSystemDark?: boolean | null) => {
return theme === 'dark' || (!!isSystemDark && theme !== 'light');
};

export const useDark = () => {
const theme = useThemeStore((state) => state.theme);
const toggleTheme = useThemeStore((state) => state.toggleTheme);
const isSystemDark = useSystemDark();
export const useDark = (): { isDark: boolean; toggleTheme: () => void } => {
const { preference, setPreference } = useThemePreferenceStore();
const isSystemDark = useSystemDark() || false;

useEffect(() => {
useThemeStore.setState({ isSystemDark });
}, [isSystemDark]);
const effectiveTheme = useMemo(
() => getEffectiveTheme(preference, isSystemDark),
[preference, isSystemDark],
);

const isDark = useMemo(() => isDarkMode(theme, isSystemDark), [theme, isSystemDark]);
const toggleTheme = useCallback(() => {
const nextPreference = getNextTheme(preference, isSystemDark);
setPreference(nextPreference);
}, [preference, isSystemDark, setPreference]);

useEffect(() => {
document.documentElement.classList.toggle('dark', isDark);
if ((theme === 'dark' && isSystemDark) || (theme === 'light' && !isSystemDark)) {
toggleTheme();
}
}, [isDark, theme, isSystemDark, toggleTheme]);
document.documentElement.classList.toggle('dark', effectiveTheme === 'dark');
}, [effectiveTheme]);

return { isDark, toggleTheme };
return { isDark: effectiveTheme === 'dark', toggleTheme };
};
28 changes: 10 additions & 18 deletions src/entities/theme/model/use-theme-store.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,22 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

import type { Theme } from '~/entities/theme/types';
import type { ThemePreference } from '../types';

interface ThemeState {
theme: Theme;
isSystemDark: boolean;
toggleTheme: () => void;
interface ThemePreferenceState {
preference: ThemePreference;
setPreference: (preference: ThemePreference) => void;
}

export const useThemeStore = create<ThemeState>()(
/** 사용자의 선호 테마 저장 및 관리 */
export const useThemePreferenceStore = create<ThemePreferenceState>()(
persist(
(set, get) => ({
theme: 'system',
isSystemDark: false,
toggleTheme: () => {
const { theme, isSystemDark } = get();
if (theme === 'system') {
set({ theme: isSystemDark ? 'light' : 'dark' });
} else {
set({ theme: 'system' });
}
},
(set) => ({
preference: 'system',
setPreference: (preference: ThemePreference) => set({ preference }),
}),
{
name: 'theme',
name: 'theme-preference',
},
),
);
5 changes: 4 additions & 1 deletion src/entities/theme/types/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
export type Theme = 'system' | 'light' | 'dark';
/** 사용자 설정 테마 */
export type ThemePreference = 'system' | 'light' | 'dark';
/** 실제 적용되는 테마 */
export type EffectiveTheme = 'light' | 'dark';
58 changes: 58 additions & 0 deletions src/entities/theme/utils/theme-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { EffectiveTheme, ThemePreference } from '../types';

/**
* @name getEffectiveTheme
* @description
* 현재 설정과 시스템 테마를 고려해 실제 적용되는 테마를 반환합니다.
* ```typescript
* getEffectiveTheme(
* // 사용자 설정 테마
* preference: 'system' | 'light' | 'dark',
* // 시스템 테마가 다크 모드인지 여부
* isSystemDark: boolean,
* ): 'light' | 'dark';
* ```
* @example
* getEffectiveTheme('system', true); // 'dark'
* getEffectiveTheme('system', false); // 'light'
* getEffectiveTheme('light', true); // 'light'
* getEffectiveTheme('dark', false); // 'dark'
*/
export const getEffectiveTheme = (
preference: ThemePreference,
isSystemDark: boolean,
): EffectiveTheme => {
if (preference === 'system') {
return isSystemDark ? 'dark' : 'light';
}

return preference;
};

/**
* @name getNextTheme
* @description
* 현재 설정과 시스템 테마를 고려해 다음 테마를 결절해 반환합니다.
* ```typescript
* getNextTheme(
* // 현재 설정
* currentPreference: 'system' | 'light' | 'dark',
* // 시스템 테마가 다크 모드인지 여부
* isSystemDark: boolean,
* ): 'system' | 'light' | 'dark';
* ```
* @example
* getNextTheme('system', true); // 'light'
* getNextTheme('system', false); // 'dark'
* getNextTheme('light', true); // 'dark'
*/
export const getNextTheme = (
currentPreference: ThemePreference,
isSystemDark: boolean,
): ThemePreference => {
if (currentPreference === 'system') {
return isSystemDark ? 'light' : 'dark';
}

return 'system';
};

0 comments on commit 50917f7

Please sign in to comment.