mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-23 14:39:58 +03:00
feat(redux-devtools-app): use prefers-color-scheme
to set theme.light
if user has not set a preferred theme
This commit is contained in:
parent
a9ef668d9b
commit
b3250c068e
|
@ -8,7 +8,10 @@ import configureStore from '../../../app/stores/panelStore';
|
||||||
|
|
||||||
import '../../views/devpanel.pug';
|
import '../../views/devpanel.pug';
|
||||||
import { Action, Store } from 'redux';
|
import { Action, Store } from 'redux';
|
||||||
import { StoreAction } from '@redux-devtools/app/lib/actions';
|
import {
|
||||||
|
applyMediaFeaturesPreferences,
|
||||||
|
StoreAction,
|
||||||
|
} from '@redux-devtools/app/lib/actions';
|
||||||
import { PanelMessage } from '../../../app/middlewares/api';
|
import { PanelMessage } from '../../../app/middlewares/api';
|
||||||
import { StoreStateWithoutSocket } from '../../../app/reducers/panel';
|
import { StoreStateWithoutSocket } from '../../../app/reducers/panel';
|
||||||
import { PersistGate } from 'redux-persist/integration/react';
|
import { PersistGate } from 'redux-persist/integration/react';
|
||||||
|
@ -33,9 +36,20 @@ function renderDevTools() {
|
||||||
unmountComponentAtNode(node!);
|
unmountComponentAtNode(node!);
|
||||||
clearTimeout(naTimeout);
|
clearTimeout(naTimeout);
|
||||||
({ store, persistor } = configureStore(position, bgConnection));
|
({ store, persistor } = configureStore(position, bgConnection));
|
||||||
|
|
||||||
|
const onBeforeLift = () => {
|
||||||
|
if (store) {
|
||||||
|
store.dispatch(applyMediaFeaturesPreferences());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<PersistGate loading={null} persistor={persistor}>
|
<PersistGate
|
||||||
|
loading={null}
|
||||||
|
persistor={persistor}
|
||||||
|
onBeforeLift={onBeforeLift}
|
||||||
|
>
|
||||||
<App position={position} />
|
<App position={position} />
|
||||||
</PersistGate>
|
</PersistGate>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
|
|
|
@ -8,6 +8,7 @@ import configureStore from '../../../app/stores/windowStore';
|
||||||
import { MonitorMessage } from '../../../app/middlewares/api';
|
import { MonitorMessage } from '../../../app/middlewares/api';
|
||||||
|
|
||||||
import '../../views/window.pug';
|
import '../../views/window.pug';
|
||||||
|
import { applyMediaFeaturesPreferences } from '@redux-devtools/app/lib/actions';
|
||||||
|
|
||||||
const position = location.hash;
|
const position = location.hash;
|
||||||
|
|
||||||
|
@ -25,9 +26,17 @@ chrome.runtime.getBackgroundPage((window) => {
|
||||||
bg.onMessage.addListener(update);
|
bg.onMessage.addListener(update);
|
||||||
update();
|
update();
|
||||||
|
|
||||||
|
const onBeforeLift = () => {
|
||||||
|
localStore.dispatch(applyMediaFeaturesPreferences());
|
||||||
|
};
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<Provider store={localStore}>
|
<Provider store={localStore}>
|
||||||
<PersistGate loading={null} persistor={persistor}>
|
<PersistGate
|
||||||
|
loading={null}
|
||||||
|
persistor={persistor}
|
||||||
|
onBeforeLift={onBeforeLift}
|
||||||
|
>
|
||||||
<App position={position} />
|
<App position={position} />
|
||||||
</PersistGate>
|
</PersistGate>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { AuthStates, States } from 'socketcluster-client/lib/scclientsocket';
|
||||||
import {
|
import {
|
||||||
CHANGE_SECTION,
|
CHANGE_SECTION,
|
||||||
CHANGE_THEME,
|
CHANGE_THEME,
|
||||||
|
APPLY_MEDIA_FEATURES_PREFERENCES,
|
||||||
SELECT_INSTANCE,
|
SELECT_INSTANCE,
|
||||||
SELECT_MONITOR,
|
SELECT_MONITOR,
|
||||||
UPDATE_MONITOR_STATE,
|
UPDATE_MONITOR_STATE,
|
||||||
|
@ -44,9 +45,9 @@ import {
|
||||||
import { Action } from 'redux';
|
import { Action } from 'redux';
|
||||||
import { Features, State } from '../reducers/instances';
|
import { Features, State } from '../reducers/instances';
|
||||||
import { MonitorStateMonitorState } from '../reducers/monitor';
|
import { MonitorStateMonitorState } from '../reducers/monitor';
|
||||||
import { LiftedAction } from '@redux-devtools/core';
|
import { LiftedAction, LiftedState } from '@redux-devtools/core';
|
||||||
import { Data } from '../reducers/reports';
|
import { Data } from '../reducers/reports';
|
||||||
import { LiftedState } from '@redux-devtools/core';
|
import { prefersDarkColorScheme } from '../utils/media-queries';
|
||||||
|
|
||||||
let monitorReducer: (
|
let monitorReducer: (
|
||||||
monitorProps: unknown,
|
monitorProps: unknown,
|
||||||
|
@ -77,10 +78,29 @@ export interface ChangeThemeAction {
|
||||||
readonly scheme: Scheme;
|
readonly scheme: Scheme;
|
||||||
readonly dark: boolean;
|
readonly dark: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ApplyMediaFeaturesPreferencesAction {
|
||||||
|
readonly type: typeof APPLY_MEDIA_FEATURES_PREFERENCES;
|
||||||
|
readonly prefersDarkColorScheme: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export function changeTheme(data: ChangeThemeData): ChangeThemeAction {
|
export function changeTheme(data: ChangeThemeData): ChangeThemeAction {
|
||||||
return { type: CHANGE_THEME, ...data.formData };
|
return { type: CHANGE_THEME, ...data.formData };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries#media_features
|
||||||
|
*/
|
||||||
|
export function applyMediaFeaturesPreferences(
|
||||||
|
payload?: Partial<Omit<ApplyMediaFeaturesPreferencesAction, 'type'>>
|
||||||
|
): ApplyMediaFeaturesPreferencesAction {
|
||||||
|
return {
|
||||||
|
prefersDarkColorScheme: prefersDarkColorScheme(),
|
||||||
|
...payload,
|
||||||
|
type: APPLY_MEDIA_FEATURES_PREFERENCES,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface InitMonitorAction {
|
export interface InitMonitorAction {
|
||||||
type: '@@INIT_MONITOR';
|
type: '@@INIT_MONITOR';
|
||||||
newMonitorState: unknown;
|
newMonitorState: unknown;
|
||||||
|
@ -564,6 +584,7 @@ export interface ErrorAction {
|
||||||
export type StoreActionWithoutUpdateStateOrLiftedAction =
|
export type StoreActionWithoutUpdateStateOrLiftedAction =
|
||||||
| ChangeSectionAction
|
| ChangeSectionAction
|
||||||
| ChangeThemeAction
|
| ChangeThemeAction
|
||||||
|
| ApplyMediaFeaturesPreferencesAction
|
||||||
| MonitorActionAction
|
| MonitorActionAction
|
||||||
| SelectInstanceAction
|
| SelectInstanceAction
|
||||||
| SelectMonitorAction
|
| SelectMonitorAction
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
export const CHANGE_SECTION = 'main/CHANGE_SECTION';
|
export const CHANGE_SECTION = 'main/CHANGE_SECTION';
|
||||||
export const CHANGE_THEME = 'main/CHANGE_THEME';
|
export const CHANGE_THEME = 'main/CHANGE_THEME';
|
||||||
|
export const APPLY_MEDIA_FEATURES_PREFERENCES =
|
||||||
|
'main/APPLY_MEDIA_FEATURES_PREFERENCES';
|
||||||
|
|
||||||
export const UPDATE_STATE = 'devTools/UPDATE_STATE';
|
export const UPDATE_STATE = 'devTools/UPDATE_STATE';
|
||||||
export const SET_STATE = 'devTools/SET_STATE';
|
export const SET_STATE = 'devTools/SET_STATE';
|
||||||
|
|
|
@ -7,7 +7,7 @@ import configureStore from './store/configureStore';
|
||||||
import { CONNECT_REQUEST } from './constants/socketActionTypes';
|
import { CONNECT_REQUEST } from './constants/socketActionTypes';
|
||||||
import App from './containers/App';
|
import App from './containers/App';
|
||||||
import { StoreState } from './reducers';
|
import { StoreState } from './reducers';
|
||||||
import { StoreAction } from './actions';
|
import { StoreAction, applyMediaFeaturesPreferences } from './actions';
|
||||||
|
|
||||||
class Root extends Component {
|
class Root extends Component {
|
||||||
store?: Store<StoreState, StoreAction>;
|
store?: Store<StoreState, StoreAction>;
|
||||||
|
@ -27,11 +27,26 @@ class Root extends Component {
|
||||||
this.persistor = persistor;
|
this.persistor = persistor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hidden
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _checkMediaFeaturesPreferences = () => {
|
||||||
|
if (this.store) {
|
||||||
|
this.store.dispatch(applyMediaFeaturesPreferences());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.store) return null;
|
if (!this.store) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Provider store={this.store}>
|
<Provider store={this.store}>
|
||||||
<PersistGate loading={null} persistor={this.persistor!}>
|
<PersistGate
|
||||||
|
loading={null}
|
||||||
|
persistor={this.persistor!}
|
||||||
|
onBeforeLift={this._checkMediaFeaturesPreferences}
|
||||||
|
>
|
||||||
<App />
|
<App />
|
||||||
</PersistGate>
|
</PersistGate>
|
||||||
</Provider>
|
</Provider>
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { Scheme, Theme } from '@redux-devtools/ui';
|
import { Scheme, Theme } from '@redux-devtools/ui';
|
||||||
import { CHANGE_THEME } from '../constants/actionTypes';
|
import { CHANGE_THEME, APPLY_MEDIA_FEATURES_PREFERENCES, } from '../constants/actionTypes';
|
||||||
import { StoreAction } from '../actions';
|
import { StoreAction } from '../actions';
|
||||||
|
|
||||||
export interface ThemeState {
|
export interface ThemeState {
|
||||||
readonly theme: Theme;
|
readonly theme: Theme;
|
||||||
readonly scheme: Scheme;
|
readonly scheme: Scheme;
|
||||||
readonly light: boolean;
|
readonly light: boolean;
|
||||||
|
readonly latestChangeBy?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function theme(
|
export default function theme(
|
||||||
|
@ -21,7 +22,20 @@ export default function theme(
|
||||||
theme: action.theme,
|
theme: action.theme,
|
||||||
scheme: action.scheme,
|
scheme: action.scheme,
|
||||||
light: !action.dark,
|
light: !action.dark,
|
||||||
|
latestChangeBy: CHANGE_THEME,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
action.type === APPLY_MEDIA_FEATURES_PREFERENCES &&
|
||||||
|
state.latestChangeBy !== CHANGE_THEME
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
light: !action.prefersDarkColorScheme,
|
||||||
|
latestChangeBy: APPLY_MEDIA_FEATURES_PREFERENCES,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
13
packages/redux-devtools-app/src/utils/media-queries.ts
Normal file
13
packages/redux-devtools-app/src/utils/media-queries.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
/**
|
||||||
|
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme
|
||||||
|
*/
|
||||||
|
export function prefersDarkColorScheme(): boolean {
|
||||||
|
if (
|
||||||
|
typeof window !== 'undefined' &&
|
||||||
|
typeof window.matchMedia === 'function'
|
||||||
|
) {
|
||||||
|
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user