Remove uploading css functionality. Themes are hard-coded. Change the code
This commit is contained in:
@@ -2,75 +2,49 @@ import { useCallback, useEffect, useState } from 'react'
|
||||
import {
|
||||
activateProfile,
|
||||
createProfile,
|
||||
cssContentUrl,
|
||||
deleteCssEntry,
|
||||
deleteProfile,
|
||||
fetchCssEntries,
|
||||
fetchProfiles,
|
||||
fetchSettings,
|
||||
importCss,
|
||||
linkCss,
|
||||
fetchThemes,
|
||||
themeCssUrl,
|
||||
updateSettings,
|
||||
type ColorScheme,
|
||||
type CssEntry,
|
||||
type ProfileSummary,
|
||||
type Settings,
|
||||
type Theme,
|
||||
} from './api'
|
||||
|
||||
// `data-*` marker for the single injected `<link>` for the active custom theme, if any.
|
||||
const INJECTED_THEME_ATTR = 'data-ottyl-theme-css-id'
|
||||
// `data-*` marker for the single injected `<link>` for the active theme's stylesheet.
|
||||
const INJECTED_THEME_ATTR = 'data-ottyl-theme-css'
|
||||
|
||||
// Themes selectable independently of `system`, each backed by a `html.<name>` block in `index.css`.
|
||||
const THEME_CLASSES = [
|
||||
'light',
|
||||
'dark',
|
||||
'red',
|
||||
'green',
|
||||
'blue',
|
||||
'yellow',
|
||||
'pink',
|
||||
'orange',
|
||||
'light_green',
|
||||
'light_red',
|
||||
'light_blue',
|
||||
'light_yellow',
|
||||
'light_pink',
|
||||
'light_orange',
|
||||
] as const
|
||||
// `"system"` isn't an embedded theme file — it resolves to one of these two at apply time, based
|
||||
// on the OS light/dark preference.
|
||||
const SYSTEM_LIGHT_THEME = 'light'
|
||||
const SYSTEM_DARK_THEME = 'dark'
|
||||
|
||||
// Reflects `colorScheme` onto `<html>` as a class + `data-color-scheme`; when a custom theme is active, no built-in class is added at all, since a class selector would out-specificity its injected `:root` override (see `applyCustomThemeCss`) and mask it.
|
||||
function applyColorScheme(colorScheme: ColorScheme, customThemeActive: boolean) {
|
||||
const root = document.documentElement
|
||||
root.classList.remove(...THEME_CLASSES)
|
||||
if (customThemeActive) {
|
||||
root.dataset.colorScheme = 'custom'
|
||||
return
|
||||
}
|
||||
function resolveTheme(theme: Theme): string {
|
||||
if (theme !== 'system') return theme
|
||||
const prefersDark = window.matchMedia?.('(prefers-color-scheme: dark)').matches ?? true
|
||||
const effective = colorScheme === 'system' ? (prefersDark ? 'dark' : 'light') : colorScheme
|
||||
root.classList.add(effective)
|
||||
root.dataset.colorScheme = colorScheme
|
||||
return prefersDark ? SYSTEM_DARK_THEME : SYSTEM_LIGHT_THEME
|
||||
}
|
||||
|
||||
// Injects a `<link>` for the active custom theme entry, if any (ticket 020: every CSS entry is
|
||||
// always a selectable theme now, so this is the only injection path — there's no more
|
||||
// always-on-overlay mode). `entry` null just removes any previously-injected tag.
|
||||
function applyCustomThemeCss(entry: CssEntry | null) {
|
||||
// Injects a `<link>` for the resolved theme's stylesheet, replacing any previously-injected one.
|
||||
// `webapp/src/index.css`'s `:root` block is the only styling until this loads, so there's no
|
||||
// flash of unstyled content on first paint.
|
||||
function applyTheme(theme: Theme) {
|
||||
const resolved = resolveTheme(theme)
|
||||
document.querySelectorAll(`link[${INJECTED_THEME_ATTR}]`).forEach((el) => el.remove())
|
||||
if (!entry) return
|
||||
const href = entry.kind === 'imported' ? cssContentUrl(entry.id) : entry.path_or_url
|
||||
if (!href) return
|
||||
const link = document.createElement('link')
|
||||
link.rel = 'stylesheet'
|
||||
link.href = href
|
||||
link.setAttribute(INJECTED_THEME_ATTR, entry.id)
|
||||
link.href = themeCssUrl(resolved)
|
||||
link.setAttribute(INJECTED_THEME_ATTR, resolved)
|
||||
document.head.appendChild(link)
|
||||
}
|
||||
|
||||
// Owns fetching + mutating settings/CSS/profile state for the NavMenu panels, and applies colour scheme / imported CSS to the document as a side effect so changes are visible immediately, with no reload.
|
||||
// Owns fetching + mutating settings/theme/profile state for the NavMenu panels, and applies the
|
||||
// selected theme to the document as a side effect so changes are visible immediately, with no reload.
|
||||
export function useSettingsPanel() {
|
||||
const [settings, setSettings] = useState<Settings | null>(null)
|
||||
const [cssEntries, setCssEntries] = useState<CssEntry[]>([])
|
||||
const [themes, setThemes] = useState<string[]>([])
|
||||
const [profiles, setProfiles] = useState<ProfileSummary[]>([])
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
@@ -80,9 +54,9 @@ export function useSettingsPanel() {
|
||||
return next
|
||||
}, [])
|
||||
|
||||
const refreshCss = useCallback(async () => {
|
||||
const next = await fetchCssEntries()
|
||||
setCssEntries(next)
|
||||
const refreshThemes = useCallback(async () => {
|
||||
const next = await fetchThemes()
|
||||
setThemes(next)
|
||||
return next
|
||||
}, [])
|
||||
|
||||
@@ -94,21 +68,20 @@ export function useSettingsPanel() {
|
||||
|
||||
useEffect(() => {
|
||||
refreshSettings().catch(() => {})
|
||||
refreshCss().catch(() => {})
|
||||
refreshThemes().catch(() => {})
|
||||
refreshProfiles().catch(() => {})
|
||||
}, [refreshSettings, refreshCss, refreshProfiles])
|
||||
|
||||
const activeCustomTheme = settings?.custom_theme_id
|
||||
? cssEntries.find((entry) => entry.id === settings.custom_theme_id) ?? null
|
||||
: null
|
||||
}, [refreshSettings, refreshThemes, refreshProfiles])
|
||||
|
||||
useEffect(() => {
|
||||
if (settings) applyColorScheme(settings.color_scheme, activeCustomTheme !== null)
|
||||
}, [settings, activeCustomTheme])
|
||||
|
||||
useEffect(() => {
|
||||
applyCustomThemeCss(activeCustomTheme)
|
||||
}, [activeCustomTheme])
|
||||
if (!settings) return
|
||||
applyTheme(settings.theme)
|
||||
if (settings.theme !== 'system') return
|
||||
// Re-resolve if the OS preference changes while "system" is selected.
|
||||
const media = window.matchMedia?.('(prefers-color-scheme: dark)')
|
||||
const onChange = () => applyTheme(settings.theme)
|
||||
media?.addEventListener('change', onChange)
|
||||
return () => media?.removeEventListener('change', onChange)
|
||||
}, [settings])
|
||||
|
||||
const guarded = useCallback((action: () => Promise<void>) => {
|
||||
return async () => {
|
||||
@@ -121,20 +94,10 @@ export function useSettingsPanel() {
|
||||
}
|
||||
}, [])
|
||||
|
||||
const setColorScheme = useCallback(
|
||||
(colorScheme: ColorScheme) =>
|
||||
const setTheme = useCallback(
|
||||
(theme: Theme) =>
|
||||
guarded(async () => {
|
||||
// Selecting a built-in scheme always deselects any custom theme — the two are mutually exclusive (see `applyColorScheme`).
|
||||
const next = await updateSettings({ colorScheme, customThemeId: null })
|
||||
setSettings(next)
|
||||
})(),
|
||||
[guarded],
|
||||
)
|
||||
|
||||
const setCustomTheme = useCallback(
|
||||
(id: string) =>
|
||||
guarded(async () => {
|
||||
const next = await updateSettings({ customThemeId: id })
|
||||
const next = await updateSettings({ theme })
|
||||
setSettings(next)
|
||||
})(),
|
||||
[guarded],
|
||||
@@ -158,40 +121,6 @@ export function useSettingsPanel() {
|
||||
[guarded],
|
||||
)
|
||||
|
||||
// Every entry is always a selectable style, on equal footing with the built-in colour schemes
|
||||
// (ticket 020 unified the data model; ticket 047 unified the UI), so there's a single import path.
|
||||
const importCssEntry = useCallback(
|
||||
(filename: string, content: string) =>
|
||||
guarded(async () => {
|
||||
await importCss(filename, content)
|
||||
await refreshCss()
|
||||
})(),
|
||||
[guarded, refreshCss],
|
||||
)
|
||||
|
||||
const linkCssEntry = useCallback(
|
||||
(pathOrUrl: string) =>
|
||||
guarded(async () => {
|
||||
await linkCss(pathOrUrl)
|
||||
await refreshCss()
|
||||
})(),
|
||||
[guarded, refreshCss],
|
||||
)
|
||||
|
||||
const removeCssEntry = useCallback(
|
||||
(id: string) =>
|
||||
guarded(async () => {
|
||||
await deleteCssEntry(id)
|
||||
await refreshCss()
|
||||
// The removed entry may have been the active custom theme — fall back to `color_scheme` rather than leaving a dangling selection.
|
||||
if (settings?.custom_theme_id === id) {
|
||||
const next = await updateSettings({ customThemeId: null })
|
||||
setSettings(next)
|
||||
}
|
||||
})(),
|
||||
[guarded, refreshCss, settings],
|
||||
)
|
||||
|
||||
const addProfile = useCallback(
|
||||
(name: string) =>
|
||||
guarded(async () => {
|
||||
@@ -221,17 +150,12 @@ export function useSettingsPanel() {
|
||||
|
||||
return {
|
||||
settings,
|
||||
cssEntries,
|
||||
activeCustomTheme,
|
||||
themes,
|
||||
profiles,
|
||||
error,
|
||||
setColorScheme,
|
||||
setCustomTheme,
|
||||
setTheme,
|
||||
setMaxLogs,
|
||||
setNtfySink,
|
||||
importCssEntry,
|
||||
linkCssEntry,
|
||||
removeCssEntry,
|
||||
addProfile,
|
||||
removeProfile,
|
||||
switchProfile,
|
||||
|
||||
Reference in New Issue
Block a user