diff --git a/src/App.tsx b/src/App.tsx index 2d7755c..1b84a56 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,15 +1,22 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { createTheme, NextUIProvider, getDocumentTheme } from '@nextui-org/react'; -import { RecoilRoot } from 'recoil'; +import { useRecoilState } from 'recoil'; import 'tippy.js/animations/shift-toward-subtle.css'; import Main from './Main' +import { themeState } from './Store' + import './Newtab.scss'; +import { useThemeDetector } from './hooks'; const Newtab = () => { - const [isDark, setIsDark] = useState(false); + const prefersDarkTheme = useThemeDetector() + + const [theme, setTheme] = useRecoilState(themeState) + + const isDark = useMemo(() => theme === 'dark-theme', [theme]) const lightTheme = createTheme({ type: 'light' }) @@ -23,29 +30,23 @@ const Newtab = () => { }) useEffect(() => { - // you can use any storage let theme = window.localStorage.getItem('data-theme'); - setIsDark(theme === 'dark-theme'); + setTheme(theme!); + }, []); - const observer = new MutationObserver(() => { - let newTheme = getDocumentTheme(document?.documentElement); - setIsDark(newTheme === 'dark-theme'); - }); + useEffect(() => { + window.localStorage.setItem('data-theme', theme); + }, [theme]) - // Observe the document theme changes - observer.observe(document?.documentElement, { - attributes: true, - attributeFilter: ['data-theme', 'style'] - }); + const calculatedTheme = useMemo(() => { + if (theme === 'system-theme') return prefersDarkTheme ? darkTheme : lightTheme - return () => observer.disconnect(); - }, []); + return isDark ? darkTheme : lightTheme + }, [isDark, prefersDarkTheme, darkTheme, lightTheme, theme]) return ( - - -
- + +
); }; diff --git a/src/Store.tsx b/src/Store.tsx index 81b72d4..426007a 100644 --- a/src/Store.tsx +++ b/src/Store.tsx @@ -2,6 +2,11 @@ import { atom } from 'recoil'; import { Note, QuickLink } from './types' +export const themeState = atom({ + key: 'themeState', + default: 'system-theme' +}) + export const notesState = atom({ key: 'notesState', default: [] diff --git a/src/components/MainTop.tsx b/src/components/MainTop.tsx index ce07210..0ae5130 100644 --- a/src/components/MainTop.tsx +++ b/src/components/MainTop.tsx @@ -1,14 +1,14 @@ -import React, { useCallback, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' -import { Button, changeTheme, Tooltip, useTheme } from '@nextui-org/react' +import { Button, Dropdown, Tooltip, useTheme } from '@nextui-org/react' import { saveAs } from 'file-saver' import { BiExport, BiImport } from 'react-icons/bi' import { FiHome } from 'react-icons/fi' -import { RiAddLine, RiMenuFoldFill, RiMenuUnfoldFill, RiMoonFill, RiPrinterLine, RiSunFill } from 'react-icons/ri' +import { RiAddLine, RiComputerLine, RiMenuFoldFill, RiMenuUnfoldFill, RiMoonFill, RiPrinterLine, RiSunFill } from 'react-icons/ri' import { useRecoilState, useRecoilValue } from 'recoil' import { v4 as uuidv4 } from 'uuid' -import { activeNoteState, binNotesState, notesState, sidebarActiveState } from '../Store' +import { activeNoteState, binNotesState, notesState, sidebarActiveState, themeState } from '../Store' import { Note, QuickLink } from '../types' import { ImportDataModal } from './data' @@ -64,14 +64,6 @@ const MainTop = () => { setActiveNote(undefined) } - const onThemeChange = () => { - const nextTheme = isDark ? 'light-theme' : 'dark-theme'; - - changeTheme(nextTheme); - - window.localStorage.setItem('data-theme', nextTheme); // I think local storage is good enough for this - } - const printEditorContent = () => { const printContents = document.querySelector(".editor-content")?.innerHTML; @@ -113,6 +105,25 @@ const MainTop = () => { }) }, []) + const [themeSelection, setThemeSelection] = useState('system-theme') + + const onThemeChange = (theme: string) => { + setTheme(theme) + } + + const onDropdownThemeChange = (keys: any) => { + let theme = [...keys][0] + + setThemeSelection(theme) + onThemeChange(theme) + } + + const [theme, setTheme] = useRecoilState(themeState) + + useEffect(() => { + setThemeSelection(theme) + }, [theme]) + return (
@@ -120,7 +131,7 @@ const MainTop = () => {
) diff --git a/src/hooks/index.ts b/src/hooks/index.ts new file mode 100644 index 0000000..5bde78d --- /dev/null +++ b/src/hooks/index.ts @@ -0,0 +1 @@ +export * from './useThemeDetector' diff --git a/src/hooks/useThemeDetector.ts b/src/hooks/useThemeDetector.ts new file mode 100644 index 0000000..b366d96 --- /dev/null +++ b/src/hooks/useThemeDetector.ts @@ -0,0 +1,18 @@ +import { useEffect, useState } from 'react' + +export const useThemeDetector = () => { + const getCurrentTheme = () => window.matchMedia("(prefers-color-scheme: dark)").matches + const [isDarkTheme, setIsDarkTheme] = useState(getCurrentTheme()) + + const mqListener = (e: MediaQueryListEvent) => setIsDarkTheme(e.matches) + + useEffect(() => { + const darkThemeMq = window.matchMedia("(prefers-color-scheme: dark)") + + darkThemeMq.addEventListener('change', mqListener) + + return () => darkThemeMq.removeEventListener('change', mqListener) + }, []) + + return isDarkTheme +} diff --git a/src/index.tsx b/src/index.tsx index 8032e47..223b538 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,9 +1,14 @@ -import { createRoot } from 'react-dom/client'; +import { createRoot } from 'react-dom/client' +import { RecoilRoot } from 'recoil' -import App from './App'; -import './index.scss'; +import App from './App' +import './index.scss' -const container = document.getElementById('root'); +const container = document.getElementById('root') -const root = createRoot(container!); // createRoot(container!) if you use TypeScript -root.render(); +const root = createRoot(container!) +root.render( + + + +);