diff --git a/src/app.d.ts b/src/app.d.ts index 225dc5f8c..557f19e52 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -18,6 +18,7 @@ declare global { device: DeviceInfo isCookiesVisible: boolean isLiteVersion: boolean + isNightMode: boolean } } diff --git a/src/lib/ctx/ui/cookies.ts b/src/lib/ctx/ui/cookies.ts new file mode 100644 index 000000000..bca246710 --- /dev/null +++ b/src/lib/ctx/ui/cookies.ts @@ -0,0 +1,23 @@ +import type { RequestEvent } from '@sveltejs/kit' + +import { BROWSER } from 'esm-env' + +import { getCookie, setCookie } from '$lib/utils/cookies.js' + +const NIGHT_MODE_COOKIE_KEY = '_SB_NM' + +export const getNightModeFromCookies = (event?: RequestEvent) => { + const nmCookie = event + ? event.cookies.get(NIGHT_MODE_COOKIE_KEY) + : BROWSER + ? getCookie(NIGHT_MODE_COOKIE_KEY) + : null + + if (!nmCookie) return null + + return nmCookie === '+' +} + +export const saveNightModeCookie = (isNightMode: boolean) => { + setCookie(NIGHT_MODE_COOKIE_KEY, isNightMode ? '+' : '-') +} diff --git a/src/lib/ctx/ui/index.svelte.ts b/src/lib/ctx/ui/index.svelte.ts index e28ae6e47..9a3cbf9ee 100644 --- a/src/lib/ctx/ui/index.svelte.ts +++ b/src/lib/ctx/ui/index.svelte.ts @@ -1,3 +1,6 @@ +import type { RequestEvent } from '@sveltejs/kit' +import type { TCurrentUser } from '../customer/api.js' + import { BROWSER } from 'esm-env' import { ApiMutation } from '$lib/api/index.js' @@ -5,7 +8,7 @@ import { Query } from '$lib/api/executor.js' import { createCtx } from '$lib/utils/index.js' import { useCustomerCtx } from '$lib/ctx/customer/index.js' -import { getSavedNightMode, saveNightMode } from './storage.js' +import { getNightModeFromCookies, saveNightModeCookie } from './cookies.js' const mutateUpdateUserSettings = ApiMutation( (isNightMode: boolean) => `mutation { @@ -15,39 +18,59 @@ const mutateUpdateUserSettings = ApiMutation( }`, ) -export const useUiCtx = createCtx('useUiCtx', ({ isLiteVersion = false } = {}) => { - const { currentUser } = useCustomerCtx.get() +export function getNightModePreference({ + currentUser, + event, +}: { + currentUser?: TCurrentUser | null + event?: RequestEvent +}) { + return currentUser?.settings.theme === 'nightmode' || getNightModeFromCookies(event) +} - const isNightMode = - currentUser.$$?.settings.theme === 'nightmode' || - (BROWSER && (getSavedNightMode() ?? document.body.classList.contains('night-mode'))) +export const useUiCtx = createCtx( + 'useUiCtx', + (props: { isLiteVersion?: boolean; isNightMode?: boolean } = {}) => { + const { currentUser } = useCustomerCtx.get() - const ui = $state({ isNightMode, isLiteVersion, timeZone: 'UTC' }) + const ui = $state({ + isNightMode: props.isNightMode, + isLiteVersion: props.isLiteVersion, + timeZone: 'UTC', + }) - if (BROWSER) document.body.classList.toggle('night-mode', isNightMode || false) + if (BROWSER) { + toggleThemeClass(ui.isNightMode) + } - return { - ui: { - get $$() { - return ui - }, + function toggleThemeClass(value?: boolean) { + document.body.classList.toggle('night-mode', value) + } - toggleNightMode() { - document.body.classList.toggle('theme-transition', true) + return { + ui: { + get $$() { + return ui + }, - const isNightMode = document.body.classList.toggle('night-mode') + toggleNightMode() { + document.body.classList.toggle('theme-transition', true) - // NOTE: Awaiting sync DOM styles update - void document.body.offsetWidth - document.body.classList.toggle('theme-transition', false) + ui.isNightMode = !ui.isNightMode - if (currentUser.$$) { - mutateUpdateUserSettings(Query)(isNightMode) - } + toggleThemeClass(ui.isNightMode) - saveNightMode(isNightMode) - ui.isNightMode = isNightMode + // NOTE: Awaiting sync DOM styles update + void document.body.offsetWidth + document.body.classList.toggle('theme-transition', false) + + if (currentUser.$$) { + mutateUpdateUserSettings(Query)(ui.isNightMode) + } + + saveNightModeCookie(ui.isNightMode) + }, }, - }, - } -}) + } + }, +) diff --git a/src/lib/ctx/ui/index.ts b/src/lib/ctx/ui/index.ts index 688dccbd9..2ee380b69 100644 --- a/src/lib/ctx/ui/index.ts +++ b/src/lib/ctx/ui/index.ts @@ -1 +1 @@ -export { useUiCtx } from './index.svelte.js' +export { useUiCtx, getNightModePreference } from './index.svelte.js' diff --git a/src/lib/ctx/ui/storage.ts b/src/lib/ctx/ui/storage.ts deleted file mode 100644 index 833958f21..000000000 --- a/src/lib/ctx/ui/storage.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { getSavedBoolean, saveBoolean } from '$lib/utils/localStorage/index.js' - -const KEY = 'NIGHT_MODE_ENABLED' - -export function getSavedNightMode() { - return getSavedBoolean(KEY) -} - -export function saveNightMode(value: boolean) { - saveBoolean(KEY, value) -} diff --git a/src/lib/sveltekit/hooks/index.ts b/src/lib/sveltekit/hooks/index.ts index 00b7097b6..653a54c3a 100644 --- a/src/lib/sveltekit/hooks/index.ts +++ b/src/lib/sveltekit/hooks/index.ts @@ -4,6 +4,7 @@ import UAParser from 'ua-parser-js' import { loadCustomerData, type TCustomer } from '$lib/ctx/customer/api.js' import { normalizeDeviceType, getDeviceInfo } from '$lib/ctx/device/index.js' import { logger } from '$lib/logger/index.js' +import { getNightModePreference } from '$lib/ctx/ui/index.js' export const checkIsSanbaseCookiePresent = (event: RequestEvent) => event.cookies.get('_sanbase_sid') || event.cookies.get('_sanbase_stage_sid') @@ -20,13 +21,16 @@ export const appSessionHandle: Handle = async ({ event, resolve }) => { }) } - const theme = customer?.currentUser?.settings.theme === 'nightmode' ? 'night-mode' : '' + const isNightMode = getNightModePreference({ currentUser: customer?.currentUser, event }) + + const theme = isNightMode ? 'night-mode' : '' const userAgent = UAParser(event.request.headers.get('user-agent') as any) const device = normalizeDeviceType(userAgent.device.type) event.locals.device = getDeviceInfo(device) event.locals.customer = customer event.locals.theme = theme + event.locals.isNightMode = isNightMode ?? false let response: Response try { diff --git a/src/lib/sveltekit/session/index.svelte.ts b/src/lib/sveltekit/session/index.svelte.ts index 09b11be14..99fd5ae0f 100644 --- a/src/lib/sveltekit/session/index.svelte.ts +++ b/src/lib/sveltekit/session/index.svelte.ts @@ -11,10 +11,13 @@ if (BROWSER) { let __SESSION__ = (BROWSER ? window.__SESSION__ : {}) as object export function useAppSessionFlow(data: { - session: Pick + session: Pick }) { const { customer } = useCustomerCtx(data.session.customer) - const { ui } = useUiCtx({ isLiteVersion: data.session.isLiteVersion }) + const { ui } = useUiCtx({ + isLiteVersion: data.session.isLiteVersion, + isNightMode: data.session.isNightMode, + }) const { device } = useDeviceCtx(data.session.device.type) if (!BROWSER) return diff --git a/src/lib/ui/core/Share/ShareDialog.svelte b/src/lib/ui/core/Share/ShareDialog.svelte index 69b06c68c..87d6b8f2c 100644 --- a/src/lib/ui/core/Share/ShareDialog.svelte +++ b/src/lib/ui/core/Share/ShareDialog.svelte @@ -128,7 +128,9 @@ bind:this={inputNode} />