Skip to content

Feat/add onchanged to theme storage #957

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ tsconfig.tsbuildinfo

# compiled
**/tailwind-output.css

.pnpm-store
5 changes: 5 additions & 0 deletions packages/storage/lib/base/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,15 @@ export const createStorage = <D = string>(key: string, fallback: D, config?: Sto
chrome?.storage[storageEnum].onChanged.addListener(_updateFromStorageOnChanged);
}

const onChanged = (callback: (changes: { oldValue?: D; newValue?: D }) => void) => {
chrome.storage[storageEnum].onChanged.addListener(storageChange => callback(storageChange[key]));
};

return {
get,
set,
getSnapshot,
subscribe,
onChanged,
};
};
1 change: 1 addition & 0 deletions packages/storage/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type BaseStorage<D> = {
set: (value: ValueOrUpdate<D>) => Promise<void>;
getSnapshot: () => D | null;
subscribe: (listener: () => void) => () => void;
onChanged: (callback: (changes: { oldValue?: D; newValue?: D }) => void) => void;
};

export type StorageConfig<D = string> = {
Expand Down
1 change: 1 addition & 0 deletions packages/tailwind-config/tailwind.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export default {
extend: {},
},
plugins: [],
darkMode: 'selector',
} as Omit<Config, 'content'>;
9 changes: 3 additions & 6 deletions packages/ui/lib/components/ToggleButton.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import { cn } from '@/lib/utils';
import { useStorage } from '@extension/shared';
import { exampleThemeStorage } from '@extension/storage';
import { exampleThemeStorage } from '@extension/storage/lib';
import type { ComponentPropsWithoutRef } from 'react';

type ToggleButtonProps = ComponentPropsWithoutRef<'button'>;

export const ToggleButton = ({ className, children, ...props }: ToggleButtonProps) => {
const { isLight } = useStorage(exampleThemeStorage);

return (
<button
className={cn(
'py-1 px-4 rounded shadow hover:scale-105 mt-4 border-2 font-bold',
isLight ? 'bg-white border-black text-black' : 'bg-black text-white border-white',
'py-1 px-4 rounded shadow hover:scale-105 bg-white text-black border-black mt-4 border-2 font-bold',
'dark:bg-black dark:text-white dark:border-white',
className,
)}
onClick={exampleThemeStorage.toggle}
Expand Down
28 changes: 16 additions & 12 deletions pages/devtools-panel/src/Panel.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,45 @@
import '@src/Panel.css';
import { t } from '@extension/i18n';
import { useStorage, withErrorBoundary, withSuspense } from '@extension/shared';
import { exampleThemeStorage } from '@extension/storage';
import { withErrorBoundary, withSuspense } from '@extension/shared';
import { exampleThemeStorage } from '@extension/storage/lib';
import { cn, ErrorDisplay, LoadingSpinner } from '@extension/ui';
import type { ComponentPropsWithoutRef } from 'react';

const Panel = () => {
const { isLight } = useStorage(exampleThemeStorage);
const logo = isLight ? 'devtools-panel/logo_horizontal.svg' : 'devtools-panel/logo_horizontal_dark.svg';

const goGithubSite = () =>
chrome.tabs.create({ url: 'https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite' });

return (
<div className={cn('App', isLight ? 'bg-slate-50' : 'bg-gray-800')}>
<header className={cn('App-header', isLight ? 'text-gray-900' : 'text-gray-100')}>
<div className="App bg-slate-50 dark:bg-gray-800">
<header className="App-header text-gray-900 dark:text-gray-100">
<button onClick={goGithubSite}>
<img src={chrome.runtime.getURL(logo)} className="App-logo" alt="logo" />
<img
src={chrome.runtime.getURL('devtools-panel/logo_horizontal.svg')}
className="App-logo dark:hidden"
alt="logo"
/>
<img
src={chrome.runtime.getURL('devtools-panel/logo_horizontal_dark.svg')}
className="App-logo hidden dark:block"
alt="logo"
/>
</button>
<p>
Edit <code>pages/devtools-panel/src/Panel.tsx</code>
</p>
<ToggleButton onClick={exampleThemeStorage.toggle}>{t('toggleTheme')}</ToggleButton>
<ToggleButton>{t('toggleTheme')}</ToggleButton>
</header>
</div>
);
};

const ToggleButton = (props: ComponentPropsWithoutRef<'button'>) => {
const { isLight } = useStorage(exampleThemeStorage);

return (
<button
className={cn(
props.className,
'font-bold mt-4 py-1 px-4 rounded shadow hover:scale-105 ',
isLight ? 'bg-white text-black' : 'bg-black text-white',
'bg-white text-black dark:bg-black dark:text-white',
)}
onClick={exampleThemeStorage.toggle}>
{props.children}
Expand Down
16 changes: 16 additions & 0 deletions pages/devtools-panel/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
import '@src/index.css';
import { exampleThemeStorage } from '@extension/storage/lib';
import Panel from '@src/Panel';
import { createRoot } from 'react-dom/client';
import type { ThemeState } from '@extension/storage/lib/types';

const init = () => {
const toggleTheme = (theme?: ThemeState['theme']) => {
if (!theme) {
return;
}

document.querySelector('html')?.classList.toggle('dark', theme === 'dark');
};

exampleThemeStorage.get().then(themeState => toggleTheme(themeState.theme));

exampleThemeStorage.onChanged(changes => {
toggleTheme(changes.newValue?.theme);
});

const appContainer = document.querySelector('#app-container');
if (!appContainer) {
throw new Error('Can not find #app-container');
Expand Down
21 changes: 11 additions & 10 deletions pages/new-tab/src/NewTab.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
import '@src/NewTab.css';
import '@src/NewTab.scss';
import { ErrorDisplay, LoadingSpinner, ToggleButton } from '@extension/ui';
import { withErrorBoundary, withSuspense } from '@extension/shared';
import { t } from '@extension/i18n';
import { useStorage, withErrorBoundary, withSuspense } from '@extension/shared';
import { exampleThemeStorage } from '@extension/storage';
import { cn, ErrorDisplay, LoadingSpinner, ToggleButton } from '@extension/ui';

const NewTab = () => {
const { isLight } = useStorage(exampleThemeStorage);
const logo = isLight ? 'new-tab/logo_horizontal.svg' : 'new-tab/logo_horizontal_dark.svg';

const goGithubSite = () =>
chrome.tabs.create({ url: 'https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite' });

console.log(t('hello', 'World'));
return (
<div className={cn('App', isLight ? 'bg-slate-50' : 'bg-gray-800')}>
<header className={cn('App-header', isLight ? 'text-gray-900' : 'text-gray-100')}>
<div className="App bg-slate-50 dark:bg-gray-800">
<header className="App-header text-gray-900 dark:text-gray-100">
<button onClick={goGithubSite}>
<img src={chrome.runtime.getURL(logo)} className="App-logo" alt="logo" />
<img src={chrome.runtime.getURL('new-tab/logo_horizontal.svg')} className="App-logo dark:hidden" alt="logo" />
<img
src={chrome.runtime.getURL('new-tab/logo_horizontal_dark.svg')}
className="App-logo hidden dark:block"
alt="logo"
/>
</button>
<p>
Edit <code>pages/new-tab/src/NewTab.tsx</code>
</p>
<h6>The color of this paragraph is defined using SASS.</h6>
<ToggleButton onClick={exampleThemeStorage.toggle}>{t('toggleTheme')}</ToggleButton>
<ToggleButton>{t('toggleTheme')}</ToggleButton>
</header>
</div>
);
Expand Down
16 changes: 16 additions & 0 deletions pages/new-tab/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
import '@src/index.css';
import { exampleThemeStorage } from '@extension/storage/lib';
import NewTab from '@src/NewTab';
import { createRoot } from 'react-dom/client';
import type { ThemeState } from '@extension/storage/lib/types';

const init = () => {
const toggleTheme = (theme?: ThemeState['theme']) => {
if (!theme) {
return;
}

document.querySelector('html')?.classList.toggle('dark', theme === 'dark');
};

exampleThemeStorage.get().then(themeState => toggleTheme(themeState.theme));

exampleThemeStorage.onChanged(changes => {
toggleTheme(changes.newValue?.theme);
});

const appContainer = document.querySelector('#app-container');
if (!appContainer) {
throw new Error('Can not find #app-container');
Expand Down
19 changes: 10 additions & 9 deletions pages/options/src/Options.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import '@src/Options.css';
import { t } from '@extension/i18n';
import { useStorage, withErrorBoundary, withSuspense } from '@extension/shared';
import { exampleThemeStorage } from '@extension/storage';
import { cn, ErrorDisplay, LoadingSpinner, ToggleButton } from '@extension/ui';
import { withErrorBoundary, withSuspense } from '@extension/shared';
import { ErrorDisplay, LoadingSpinner, ToggleButton } from '@extension/ui';

const Options = () => {
const { isLight } = useStorage(exampleThemeStorage);
const logo = isLight ? 'options/logo_horizontal.svg' : 'options/logo_horizontal_dark.svg';

const goGithubSite = () =>
chrome.tabs.create({ url: 'https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite' });

return (
<div className={cn('App', isLight ? 'bg-slate-50 text-gray-900' : 'bg-gray-800 text-gray-100')}>
<div className="App bg-slate-50 text-gray-900 dark:bg-gray-800 dark:text-gray-100">
<button onClick={goGithubSite}>
<img src={chrome.runtime.getURL(logo)} className="App-logo" alt="logo" />
<img src={chrome.runtime.getURL('options/logo_horizontal.svg')} className="App-logo dark:hidden" alt="logo" />
<img
src={chrome.runtime.getURL('options/logo_horizontal_dark.svg')}
className="App-logo hidden dark:block"
alt="logo"
/>
</button>
<p>
Edit <code>pages/options/src/Options.tsx</code>
</p>
<ToggleButton onClick={exampleThemeStorage.toggle}>{t('toggleTheme')}</ToggleButton>
<ToggleButton>{t('toggleTheme')}</ToggleButton>
</div>
);
};
Expand Down
17 changes: 17 additions & 0 deletions pages/options/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
import '@src/index.css';
import { exampleThemeStorage } from '@extension/storage/lib';
import Options from '@src/Options';
import { createRoot } from 'react-dom/client';
import type { ThemeState } from '@extension/storage/lib/types';

const init = () => {
const toggleTheme = (theme?: ThemeState['theme']) => {
if (!theme) {
return;
}

document.querySelector('html')?.classList.toggle('dark', theme === 'dark');
};

exampleThemeStorage.get().then(themeState => toggleTheme(themeState.theme));

exampleThemeStorage.onChanged(changes => {
toggleTheme(changes.newValue?.theme);
});

const appContainer = document.querySelector('#app-container');
if (!appContainer) {
throw new Error('Can not find #app-container');
}
const root = createRoot(appContainer);

root.render(<Options />);
};

Expand Down
26 changes: 13 additions & 13 deletions pages/popup/src/Popup.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import '@src/Popup.css';
import { t } from '@extension/i18n';
import { useStorage, withErrorBoundary, withSuspense } from '@extension/shared';
import { exampleThemeStorage } from '@extension/storage';
import { cn, ErrorDisplay, LoadingSpinner, ToggleButton } from '@extension/ui';
import { withErrorBoundary, withSuspense } from '@extension/shared';
import { ErrorDisplay, LoadingSpinner, ToggleButton } from '@extension/ui';

const notificationOptions = {
type: 'basic',
Expand All @@ -12,9 +11,6 @@ const notificationOptions = {
} as const;

const Popup = () => {
const { isLight } = useStorage(exampleThemeStorage);
const logo = isLight ? 'popup/logo_vertical.svg' : 'popup/logo_vertical_dark.svg';

const goGithubSite = () =>
chrome.tabs.create({ url: 'https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite' });

Expand All @@ -39,19 +35,23 @@ const Popup = () => {
};

return (
<div className={cn('App', isLight ? 'bg-slate-50' : 'bg-gray-800')}>
<header className={cn('App-header', isLight ? 'text-gray-900' : 'text-gray-100')}>
<div className="App bg-slate-50 dark:bg-gray-800">
<header className="App-header text-gray-900 dark:text-gray-100">
<button onClick={goGithubSite}>
<img src={chrome.runtime.getURL(logo)} className="App-logo" alt="logo" />
<img src={chrome.runtime.getURL('popup/logo_vertical.svg')} className="App-logo dark:hidden" alt="logo" />
<img
src={chrome.runtime.getURL('popup/logo_vertical_dark.svg')}
className="App-logo hidden dark:block"
alt="logo"
/>
</button>
<p>
Edit <code>pages/popup/src/Popup.tsx</code>
</p>
<button
className={cn(
'font-bold mt-4 py-1 px-4 rounded shadow hover:scale-105',
isLight ? 'bg-blue-200 text-black' : 'bg-gray-700 text-white',
)}
className="
font-bold mt-4 py-1 px-4 rounded shadow hover:scale-105 bg-blue-200 text-black
dark:bg-gray-700 dark:text-white"
onClick={injectContentScript}>
Click to inject Content Script
</button>
Expand Down
16 changes: 16 additions & 0 deletions pages/popup/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
import '@src/index.css';
import { exampleThemeStorage } from '@extension/storage/lib';
import Popup from '@src/Popup';
import { createRoot } from 'react-dom/client';
import type { ThemeState } from '@extension/storage/lib/types';

const init = () => {
const toggleTheme = (theme?: ThemeState['theme']) => {
if (!theme) {
return;
}

document.querySelector('html')?.classList.toggle('dark', theme === 'dark');
};

exampleThemeStorage.get().then(themeState => toggleTheme(themeState.theme));

exampleThemeStorage.onChanged(changes => {
toggleTheme(changes.newValue?.theme);
});

const appContainer = document.querySelector('#app-container');
if (!appContainer) {
throw new Error('Can not find #app-container');
Expand Down
17 changes: 17 additions & 0 deletions pages/side-panel/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
import '@src/index.css';
import { exampleThemeStorage } from '@extension/storage/lib';
import SidePanel from '@src/SidePanel';
import { createRoot } from 'react-dom/client';
import type { ThemeState } from '@extension/storage/lib/types';

const init = () => {
const toggleTheme = (theme?: ThemeState['theme']) => {
if (!theme) {
return;
}

document.querySelector('html')?.classList.toggle('dark', theme === 'dark');
};

exampleThemeStorage.get().then(themeState => toggleTheme(themeState.theme));

exampleThemeStorage.onChanged(changes => {
toggleTheme(changes.newValue?.theme);
});

const appContainer = document.querySelector('#app-container');
if (!appContainer) {
throw new Error('Can not find #app-container');
}
const root = createRoot(appContainer);

root.render(<SidePanel />);
};

Expand Down