mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-26 16:09:50 +03:00
More work
This commit is contained in:
parent
696a669bd0
commit
084f9b9429
|
@ -1,6 +1,11 @@
|
|||
import mapValues from 'lodash/mapValues';
|
||||
|
||||
export const FilterState = {
|
||||
export type FilterStateValue =
|
||||
| 'DO_NOT_FILTER'
|
||||
| 'BLACKLIST_SPECIFIC'
|
||||
| 'WHITELIST_SPECIFIC';
|
||||
|
||||
export const FilterState: { [K in FilterStateValue]: FilterStateValue } = {
|
||||
DO_NOT_FILTER: 'DO_NOT_FILTER',
|
||||
BLACKLIST_SPECIFIC: 'BLACKLIST_SPECIFIC',
|
||||
WHITELIST_SPECIFIC: 'WHITELIST_SPECIFIC',
|
|
@ -1,14 +1,22 @@
|
|||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { connect, ResolveThunks } from 'react-redux';
|
||||
import { Container, Notification } from 'devui';
|
||||
import { getActiveInstance } from '@redux-devtools/app/lib/reducers/instances';
|
||||
import Settings from '@redux-devtools/app/lib/components/Settings';
|
||||
import Actions from '@redux-devtools/app/lib/containers/Actions';
|
||||
import Header from '@redux-devtools/app/lib/components/Header';
|
||||
import { clearNotification } from '@redux-devtools/app/lib/actions';
|
||||
import { StoreState } from '@redux-devtools/app/lib/reducers';
|
||||
|
||||
class App extends Component {
|
||||
openWindow = (position) => {
|
||||
type StateProps = ReturnType<typeof mapStateToProps>;
|
||||
type DispatchProps = ResolveThunks<typeof actionCreators>;
|
||||
interface OwnProps {
|
||||
readonly position: string;
|
||||
}
|
||||
type Props = StateProps & DispatchProps & OwnProps;
|
||||
|
||||
class App extends Component<Props> {
|
||||
openWindow = (position: string) => {
|
||||
chrome.runtime.sendMessage({ type: 'OPEN', position });
|
||||
};
|
||||
openOptionsPage = () => {
|
||||
|
@ -62,7 +70,7 @@ class App extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
function mapStateToProps(state: StoreState) {
|
||||
const instances = state.instances;
|
||||
const id = getActiveInstance(instances);
|
||||
return {
|
|
@ -1,9 +1,16 @@
|
|||
import { Dispatch, Store } from 'redux';
|
||||
import {
|
||||
SELECT_INSTANCE,
|
||||
UPDATE_STATE,
|
||||
} from '@redux-devtools/app/lib/constants/actionTypes';
|
||||
import { StoreAction } from '@redux-devtools/app/lib/actions';
|
||||
import { StoreState } from '@redux-devtools/app/lib/reducers';
|
||||
|
||||
function selectInstance(tabId, store, next) {
|
||||
function selectInstance(
|
||||
tabId: number,
|
||||
store: Store<StoreState, StoreAction>,
|
||||
next: Dispatch<StoreAction>
|
||||
) {
|
||||
const instances = store.getState().instances;
|
||||
if (instances.current === 'default') return;
|
||||
const connections = instances.connections[tabId];
|
||||
|
@ -12,7 +19,7 @@ function selectInstance(tabId, store, next) {
|
|||
}
|
||||
}
|
||||
|
||||
function getCurrentTabId(next) {
|
||||
function getCurrentTabId(next: (tabId: number) => void) {
|
||||
chrome.tabs.query(
|
||||
{
|
||||
active: true,
|
||||
|
@ -21,13 +28,13 @@ function getCurrentTabId(next) {
|
|||
(tabs) => {
|
||||
const tab = tabs[0];
|
||||
if (!tab) return;
|
||||
next(tab.id);
|
||||
next(tab.id!);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default function popupSelector(store) {
|
||||
return (next) => (action) => {
|
||||
export default function popupSelector(store: Store<StoreState, StoreAction>) {
|
||||
return (next: Dispatch<StoreAction>) => (action: StoreAction) => {
|
||||
const result = next(action);
|
||||
if (action.type === UPDATE_STATE) {
|
||||
if (chrome.devtools && chrome.devtools.inspectedWindow) {
|
|
@ -1,31 +0,0 @@
|
|||
import {
|
||||
LIFTED_ACTION,
|
||||
UPDATE_STATE,
|
||||
SELECT_INSTANCE,
|
||||
} from '@redux-devtools/app/lib/constants/actionTypes';
|
||||
import { getActiveInstance } from '@redux-devtools/app/lib/reducers/instances';
|
||||
|
||||
function panelDispatcher(bgConnection) {
|
||||
let autoselected = false;
|
||||
const tabId = chrome.devtools.inspectedWindow.tabId;
|
||||
|
||||
return (store) => (next) => (action) => {
|
||||
const result = next(action);
|
||||
if (!autoselected && action.type === UPDATE_STATE && tabId) {
|
||||
autoselected = true;
|
||||
const connections = store.getState().instances.connections[tabId];
|
||||
if (connections && connections.length === 1) {
|
||||
next({ type: SELECT_INSTANCE, selected: connections[0] });
|
||||
}
|
||||
}
|
||||
if (action.type === LIFTED_ACTION || action.type === 'TOGGLE_PERSIST') {
|
||||
const instances = store.getState().instances;
|
||||
const instanceId = getActiveInstance(instances);
|
||||
const id = instances.options[instanceId].connectionId;
|
||||
bgConnection.postMessage({ ...action, instanceId, id });
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export default panelDispatcher;
|
36
extension/src/app/middlewares/panelSync.ts
Normal file
36
extension/src/app/middlewares/panelSync.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import {
|
||||
LIFTED_ACTION,
|
||||
UPDATE_STATE,
|
||||
SELECT_INSTANCE,
|
||||
} from '@redux-devtools/app/lib/constants/actionTypes';
|
||||
import { getActiveInstance } from '@redux-devtools/app/lib/reducers/instances';
|
||||
import { Dispatch, MiddlewareAPI, Store } from 'redux';
|
||||
import { StoreState } from '@redux-devtools/app/lib/reducers';
|
||||
import { StoreAction } from '@redux-devtools/app/lib/actions';
|
||||
|
||||
function panelDispatcher(bgConnection: chrome.runtime.Port) {
|
||||
let autoselected = false;
|
||||
const tabId = chrome.devtools.inspectedWindow.tabId;
|
||||
|
||||
return (store: MiddlewareAPI<Dispatch<StoreAction>, StoreState>) =>
|
||||
(next: Dispatch<StoreAction>) =>
|
||||
(action: StoreAction) => {
|
||||
const result = next(action);
|
||||
if (!autoselected && action.type === UPDATE_STATE && tabId) {
|
||||
autoselected = true;
|
||||
const connections = store.getState().instances.connections[tabId];
|
||||
if (connections && connections.length === 1) {
|
||||
next({ type: SELECT_INSTANCE, selected: connections[0] });
|
||||
}
|
||||
}
|
||||
if (action.type === LIFTED_ACTION || action.type === 'TOGGLE_PERSIST') {
|
||||
const instances = store.getState().instances;
|
||||
const instanceId = getActiveInstance(instances);
|
||||
const id = instances.options[instanceId].connectionId;
|
||||
bgConnection.postMessage({ ...action, instanceId, id });
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export default panelDispatcher;
|
|
@ -1,10 +0,0 @@
|
|||
import { combineReducers } from 'redux';
|
||||
import instances from '@redux-devtools/app/lib/reducers/instances';
|
||||
import persistStates from './persistStates';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
instances,
|
||||
persistStates,
|
||||
});
|
||||
|
||||
export default rootReducer;
|
17
extension/src/app/reducers/background/index.ts
Normal file
17
extension/src/app/reducers/background/index.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { combineReducers } from 'redux';
|
||||
import instances, {
|
||||
InstancesState,
|
||||
} from '@redux-devtools/app/lib/reducers/instances';
|
||||
import persistStates from './persistStates';
|
||||
|
||||
export interface BackgroundState {
|
||||
readonly instances: InstancesState;
|
||||
readonly persistStates: boolean;
|
||||
}
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
instances,
|
||||
persistStates,
|
||||
});
|
||||
|
||||
export default rootReducer;
|
|
@ -5,14 +5,20 @@ import notification from '@redux-devtools/app/lib/reducers/notification';
|
|||
import reports from '@redux-devtools/app/lib/reducers/reports';
|
||||
import section from '@redux-devtools/app/lib/reducers/section';
|
||||
import theme from '@redux-devtools/app/lib/reducers/theme';
|
||||
import connection from '@redux-devtools/app/lib/reducers/connection';
|
||||
import socket from '@redux-devtools/app/lib/reducers/socket';
|
||||
import { StoreState } from '@redux-devtools/app/lib/reducers';
|
||||
import { StoreAction } from '@redux-devtools/app/lib/actions';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
const rootReducer = combineReducers<StoreState, StoreAction>({
|
||||
instances,
|
||||
monitor,
|
||||
reports,
|
||||
notification,
|
||||
section,
|
||||
theme,
|
||||
connection,
|
||||
socket,
|
||||
});
|
||||
|
||||
export default rootReducer;
|
||||
|
|
|
@ -6,8 +6,11 @@ import socket from '@redux-devtools/app/lib/reducers/socket';
|
|||
import reports from '@redux-devtools/app/lib/reducers/reports';
|
||||
import section from '@redux-devtools/app/lib/reducers/section';
|
||||
import theme from '@redux-devtools/app/lib/reducers/theme';
|
||||
import connection from '@redux-devtools/app/lib/reducers/connection';
|
||||
import { StoreState } from '@redux-devtools/app/lib/reducers';
|
||||
import { StoreAction } from '@redux-devtools/app/lib/actions';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
const rootReducer = combineReducers<StoreState, StoreAction>({
|
||||
instances,
|
||||
monitor,
|
||||
socket,
|
||||
|
@ -15,6 +18,7 @@ const rootReducer = combineReducers({
|
|||
notification,
|
||||
section,
|
||||
theme,
|
||||
connection,
|
||||
});
|
||||
|
||||
export default rootReducer;
|
|
@ -7,8 +7,9 @@ import {
|
|||
SELECT_INSTANCE,
|
||||
LIFTED_ACTION,
|
||||
} from '@redux-devtools/app/lib/constants/actionTypes';
|
||||
import { StoreAction } from '@redux-devtools/app/lib/actions';
|
||||
|
||||
export default function instances(state = initialState, action) {
|
||||
export default function instances(state = initialState, action: StoreAction) {
|
||||
switch (action.type) {
|
||||
case UPDATE_STATE:
|
||||
return { ...action.instances, selected: state.selected };
|
|
@ -1,8 +1,10 @@
|
|||
import { createStore, applyMiddleware } from 'redux';
|
||||
import rootReducer from '../reducers/background';
|
||||
import { createStore, applyMiddleware, PreloadedState } from 'redux';
|
||||
import rootReducer, { BackgroundState } from '../reducers/background';
|
||||
import api from '../middlewares/api';
|
||||
|
||||
export default function configureStore(preloadedState) {
|
||||
export default function configureStore(
|
||||
preloadedState: PreloadedState<BackgroundState>
|
||||
) {
|
||||
return createStore(rootReducer, preloadedState, applyMiddleware(api));
|
||||
/*
|
||||
let enhancer;
|
|
@ -1,12 +1,13 @@
|
|||
import { createStore, applyMiddleware } from 'redux';
|
||||
import { createStore, applyMiddleware, PreloadedState } from 'redux';
|
||||
import exportState from '@redux-devtools/app/lib/middlewares/exportState';
|
||||
import panelDispatcher from '../middlewares/panelSync';
|
||||
import rootReducer from '../reducers/panel';
|
||||
import { StoreState } from '@redux-devtools/app/lib/reducers';
|
||||
|
||||
export default function configureStore(
|
||||
position: string,
|
||||
bgConnection: chrome.runtime.Port,
|
||||
preloadedState
|
||||
preloadedState: PreloadedState<StoreState>
|
||||
) {
|
||||
const enhancer = applyMiddleware(exportState, panelDispatcher(bgConnection));
|
||||
return createStore(rootReducer, preloadedState, enhancer);
|
||||
|
|
|
@ -1,12 +1,25 @@
|
|||
import { createStore, compose, applyMiddleware } from 'redux';
|
||||
import {
|
||||
createStore,
|
||||
compose,
|
||||
applyMiddleware,
|
||||
Store,
|
||||
PreloadedState,
|
||||
} from 'redux';
|
||||
import exportState from '@redux-devtools/app/lib/middlewares/exportState';
|
||||
import api from '@redux-devtools/app/lib/middlewares/api';
|
||||
import { CONNECT_REQUEST } from '@redux-devtools/app/lib/constants/socketActionTypes';
|
||||
import { StoreState } from '@redux-devtools/app/lib/reducers';
|
||||
import { StoreAction } from '@redux-devtools/app/lib/actions';
|
||||
import syncStores from '../middlewares/windowSync';
|
||||
import instanceSelector from '../middlewares/instanceSelector';
|
||||
import rootReducer from '../reducers/window';
|
||||
import { BackgroundState } from '../reducers/background';
|
||||
|
||||
export default function configureStore(baseStore, position, preloadedState) {
|
||||
export default function configureStore(
|
||||
baseStore: Store<BackgroundState, StoreAction>,
|
||||
position: string,
|
||||
preloadedState: PreloadedState<StoreState>
|
||||
) {
|
||||
let enhancer;
|
||||
const middlewares = [exportState, api, syncStores(baseStore)];
|
||||
if (!position || position === '#popup') {
|
|
@ -1,3 +1,6 @@
|
|||
import { PreloadedState } from 'redux';
|
||||
import { StoreState } from '@redux-devtools/app/lib/reducers';
|
||||
|
||||
const getIfExists = (sel, template) =>
|
||||
typeof sel === 'undefined' ||
|
||||
typeof template === 'undefined' ||
|
||||
|
@ -5,7 +8,10 @@ const getIfExists = (sel, template) =>
|
|||
? 0
|
||||
: sel;
|
||||
|
||||
export default function getPreloadedState(position, cb) {
|
||||
export default function getPreloadedState(
|
||||
position: string,
|
||||
cb: (state: PreloadedState<StoreState>) => void
|
||||
) {
|
||||
chrome.storage.local.get(
|
||||
[
|
||||
'monitor' + position,
|
|
@ -7,6 +7,9 @@ import configureStore from '../../../app/stores/panelStore';
|
|||
import getPreloadedState from '../background/getPreloadedState';
|
||||
|
||||
import '../../views/devpanel.pug';
|
||||
import { PreloadedState, Store } from 'redux';
|
||||
import { StoreState } from '@redux-devtools/app/lib/reducers';
|
||||
import { StoreAction } from '@redux-devtools/app/lib/actions';
|
||||
|
||||
const position = location.hash;
|
||||
const messageStyle: CSSProperties = {
|
||||
|
@ -16,10 +19,10 @@ const messageStyle: CSSProperties = {
|
|||
};
|
||||
|
||||
let rendered: boolean;
|
||||
let store;
|
||||
let store: Store<StoreState, StoreAction> | undefined;
|
||||
let bgConnection: chrome.runtime.Port;
|
||||
let naTimeout: NodeJS.Timeout;
|
||||
let preloadedState;
|
||||
let preloadedState: PreloadedState<StoreState>;
|
||||
|
||||
const isChrome = navigator.userAgent.indexOf('Firefox') === -1;
|
||||
|
||||
|
@ -96,10 +99,10 @@ function init(id: number) {
|
|||
bgConnection.onMessage.addListener((message) => {
|
||||
if (message.type === 'NA') {
|
||||
if (message.id === id) renderNA();
|
||||
else store.dispatch({ type: REMOVE_INSTANCE, id: message.id });
|
||||
else store!.dispatch({ type: REMOVE_INSTANCE, id: message.id });
|
||||
} else {
|
||||
if (!rendered) renderDevTools();
|
||||
store.dispatch(message);
|
||||
store!.dispatch(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import '../../views/devtools.pug';
|
||||
|
||||
function createPanel(url) {
|
||||
function createPanel(url: string) {
|
||||
chrome.devtools.panels.create(
|
||||
'Redux',
|
||||
'img/logo/scalable.png',
|
|
@ -5,7 +5,7 @@ if (process.env.NODE_ENV === 'production') {
|
|||
const { default: script } = require('raw-loader!tmp/page.bundle.js');
|
||||
s.appendChild(document.createTextNode(script));
|
||||
(document.head || document.documentElement).appendChild(s);
|
||||
s.parentNode.removeChild(s);
|
||||
s.parentNode!.removeChild(s);
|
||||
} else {
|
||||
s.src = chrome.extension.getURL('page.bundle.js');
|
||||
s.onload = function () {
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import { OptionsProps } from './Options';
|
||||
|
||||
export default ({ options, saveOption }) => {
|
||||
export default ({ options, saveOption }: OptionsProps) => {
|
||||
const AllowToRunState = {
|
||||
EVERYWHERE: true,
|
||||
ON_SPECIFIC_URLS: false,
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import { OptionsProps } from './Options';
|
||||
|
||||
export default ({ options, saveOption }) => {
|
||||
export default ({ options, saveOption }: OptionsProps) => {
|
||||
return (
|
||||
<fieldset className="option-group">
|
||||
<legend className="option-group__title">Context Menu</legend>
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import { OptionsProps } from './Options';
|
||||
|
||||
export default ({ options, saveOption }) => {
|
||||
export default ({ options, saveOption }: OptionsProps) => {
|
||||
const EditorState = {
|
||||
BROWSER: 0,
|
||||
EXTERNAL: 1,
|
||||
|
@ -45,7 +46,7 @@ export default ({ options, saveOption }) => {
|
|||
className="option__element"
|
||||
id="editor"
|
||||
type="text"
|
||||
size="33"
|
||||
size={33}
|
||||
maxLength={30}
|
||||
placeholder="vscode, atom, webstorm, sublime..."
|
||||
value={options.editor}
|
|
@ -1,7 +1,8 @@
|
|||
import React from 'react';
|
||||
import { FilterState } from '../../../app/api/filters';
|
||||
import { OptionsProps } from './Options';
|
||||
|
||||
export default ({ options, saveOption }) => {
|
||||
export default ({ options, saveOption }: OptionsProps) => {
|
||||
return (
|
||||
<fieldset className="option-group">
|
||||
<legend className="option-group__title">
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import { OptionsProps } from './Options';
|
||||
|
||||
export default ({ options, saveOption }) => {
|
||||
export default ({ options, saveOption }: OptionsProps) => {
|
||||
const browserName = navigator.userAgent.includes('Firefox')
|
||||
? 'Firefox'
|
||||
: 'Chrome';
|
|
@ -4,8 +4,17 @@ import FilterGroup from './FilterGroup';
|
|||
import AllowToRunGroup from './AllowToRunGroup';
|
||||
import MiscellaneousGroup from './MiscellaneousGroup';
|
||||
import ContextMenuGroup from './ContextMenuGroup';
|
||||
import { Options } from './syncOptions';
|
||||
|
||||
export default (props) => (
|
||||
export interface OptionsProps {
|
||||
readonly options: Options;
|
||||
readonly saveOption: <K extends keyof Options>(
|
||||
name: K,
|
||||
value: Options[K]
|
||||
) => void;
|
||||
}
|
||||
|
||||
export default (props: OptionsProps) => (
|
||||
<div>
|
||||
<EditorGroup {...props} />
|
||||
<FilterGroup {...props} />
|
|
@ -1,108 +0,0 @@
|
|||
import { FilterState } from '../../../app/api/filters';
|
||||
|
||||
let options;
|
||||
let subscribers = [];
|
||||
|
||||
const save = (toAllTabs) => (key, value) => {
|
||||
let obj = {};
|
||||
obj[key] = value;
|
||||
chrome.storage.sync.set(obj);
|
||||
options[key] = value;
|
||||
toAllTabs({ options: options });
|
||||
subscribers.forEach((s) => s(options));
|
||||
};
|
||||
|
||||
const migrateOldOptions = (oldOptions) => {
|
||||
let newOptions = Object.assign({}, oldOptions);
|
||||
|
||||
// Migrate the old `filter` option from 2.2.1
|
||||
if (typeof oldOptions.filter === 'boolean') {
|
||||
if (oldOptions.filter && oldOptions.whitelist.length > 0) {
|
||||
newOptions.filter = FilterState.WHITELIST_SPECIFIC;
|
||||
} else if (oldOptions.filter) {
|
||||
newOptions.filter = FilterState.BLACKLIST_SPECIFIC;
|
||||
} else {
|
||||
newOptions.filter = FilterState.DO_NOT_FILTER;
|
||||
}
|
||||
}
|
||||
|
||||
return newOptions;
|
||||
};
|
||||
|
||||
const get = (callback) => {
|
||||
if (options) callback(options);
|
||||
else {
|
||||
chrome.storage.sync.get(
|
||||
{
|
||||
useEditor: 0,
|
||||
editor: '',
|
||||
projectPath: '',
|
||||
maxAge: 50,
|
||||
filter: FilterState.DO_NOT_FILTER,
|
||||
whitelist: '',
|
||||
blacklist: '',
|
||||
shouldCatchErrors: false,
|
||||
inject: true,
|
||||
urls: '^https?://localhost|0\\.0\\.0\\.0:\\d+\n^https?://.+\\.github\\.io',
|
||||
showContextMenus: true,
|
||||
},
|
||||
function (items) {
|
||||
options = migrateOldOptions(items);
|
||||
callback(options);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const subscribe = (callback) => {
|
||||
subscribers = subscribers.concat(callback);
|
||||
};
|
||||
|
||||
const toReg = (str) =>
|
||||
str !== '' ? str.split('\n').filter(Boolean).join('|') : null;
|
||||
|
||||
export const injectOptions = (newOptions) => {
|
||||
if (!newOptions) return;
|
||||
if (newOptions.filter !== FilterState.DO_NOT_FILTER) {
|
||||
newOptions.whitelist = toReg(newOptions.whitelist);
|
||||
newOptions.blacklist = toReg(newOptions.blacklist);
|
||||
}
|
||||
|
||||
options = newOptions;
|
||||
let s = document.createElement('script');
|
||||
s.type = 'text/javascript';
|
||||
s.appendChild(
|
||||
document.createTextNode(
|
||||
'window.devToolsOptions = Object.assign(window.devToolsOptions||{},' +
|
||||
JSON.stringify(options) +
|
||||
');'
|
||||
)
|
||||
);
|
||||
(document.head || document.documentElement).appendChild(s);
|
||||
s.parentNode.removeChild(s);
|
||||
};
|
||||
|
||||
export const getOptionsFromBg = () => {
|
||||
/* chrome.runtime.sendMessage({ type: 'GET_OPTIONS' }, response => {
|
||||
if (response && response.options) injectOptions(response.options);
|
||||
});
|
||||
*/
|
||||
get((newOptions) => {
|
||||
injectOptions(newOptions);
|
||||
}); // Legacy
|
||||
};
|
||||
|
||||
export const isAllowed = (localOptions = options) =>
|
||||
!localOptions ||
|
||||
localOptions.inject ||
|
||||
!localOptions.urls ||
|
||||
location.href.match(toReg(localOptions.urls));
|
||||
|
||||
export default function syncOptions(toAllTabs) {
|
||||
if (toAllTabs && !options) get(() => {}); // Initialize
|
||||
return {
|
||||
save: save(toAllTabs),
|
||||
get: get,
|
||||
subscribe: subscribe,
|
||||
};
|
||||
}
|
142
extension/src/browser/extension/options/syncOptions.ts
Normal file
142
extension/src/browser/extension/options/syncOptions.ts
Normal file
|
@ -0,0 +1,142 @@
|
|||
import { FilterState, FilterStateValue } from '../../../app/api/filters';
|
||||
|
||||
export interface Options {
|
||||
readonly useEditor: number;
|
||||
readonly editor: string;
|
||||
readonly projectPath: string;
|
||||
readonly maxAge: number;
|
||||
readonly filter: FilterStateValue;
|
||||
readonly whitelist: string;
|
||||
readonly blacklist: string;
|
||||
readonly shouldCatchErrors: boolean;
|
||||
readonly inject: boolean;
|
||||
readonly urls: string;
|
||||
readonly showContextMenus: boolean;
|
||||
}
|
||||
|
||||
interface OldOrNewOptions {
|
||||
readonly useEditor: number;
|
||||
readonly editor: string;
|
||||
readonly projectPath: string;
|
||||
readonly maxAge: number;
|
||||
readonly filter: FilterStateValue | boolean;
|
||||
readonly whitelist: string;
|
||||
readonly blacklist: string;
|
||||
readonly shouldCatchErrors: boolean;
|
||||
readonly inject: boolean;
|
||||
readonly urls: string;
|
||||
readonly showContextMenus: boolean;
|
||||
}
|
||||
|
||||
let options: Options | undefined;
|
||||
let subscribers: ((options: Options) => void)[] = [];
|
||||
|
||||
type ToAllTabs = (msg: { readonly options: Options }) => void;
|
||||
|
||||
const save =
|
||||
(toAllTabs: ToAllTabs | undefined) =>
|
||||
<K extends keyof Options>(key: K, value: Options[K]) => {
|
||||
let obj: { [K1 in keyof Options]?: Options[K1] } = {};
|
||||
obj[key] = value;
|
||||
chrome.storage.sync.set(obj);
|
||||
options![key] = value;
|
||||
toAllTabs!({ options: options! });
|
||||
subscribers.forEach((s) => s(options!));
|
||||
};
|
||||
|
||||
const migrateOldOptions = (oldOptions: OldOrNewOptions): Options => ({
|
||||
...oldOptions,
|
||||
filter:
|
||||
// Migrate the old `filter` option from 2.2.1
|
||||
typeof oldOptions.filter === 'boolean'
|
||||
? oldOptions.filter && oldOptions.whitelist.length > 0
|
||||
? FilterState.WHITELIST_SPECIFIC
|
||||
: oldOptions.filter
|
||||
? FilterState.BLACKLIST_SPECIFIC
|
||||
: FilterState.DO_NOT_FILTER
|
||||
: oldOptions.filter,
|
||||
});
|
||||
|
||||
const get = (callback: (options: Options) => void) => {
|
||||
if (options) callback(options);
|
||||
else {
|
||||
chrome.storage.sync.get(
|
||||
{
|
||||
useEditor: 0,
|
||||
editor: '',
|
||||
projectPath: '',
|
||||
maxAge: 50,
|
||||
filter: FilterState.DO_NOT_FILTER,
|
||||
whitelist: '',
|
||||
blacklist: '',
|
||||
shouldCatchErrors: false,
|
||||
inject: true,
|
||||
urls: '^https?://localhost|0\\.0\\.0\\.0:\\d+\n^https?://.+\\.github\\.io',
|
||||
showContextMenus: true,
|
||||
},
|
||||
function (items) {
|
||||
options = migrateOldOptions(items as OldOrNewOptions);
|
||||
callback(options);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const subscribe = (callback: (options: Options) => void) => {
|
||||
subscribers = subscribers.concat(callback);
|
||||
};
|
||||
|
||||
const toReg = (str: string) =>
|
||||
str !== '' ? str.split('\n').filter(Boolean).join('|') : null;
|
||||
|
||||
export const injectOptions = (newOptions: Options) => {
|
||||
if (!newOptions) return;
|
||||
|
||||
options = {
|
||||
...newOptions,
|
||||
whitelist:
|
||||
newOptions.filter !== FilterState.DO_NOT_FILTER
|
||||
? toReg(newOptions.whitelist)!
|
||||
: newOptions.whitelist,
|
||||
blacklist:
|
||||
newOptions.filter !== FilterState.DO_NOT_FILTER
|
||||
? toReg(newOptions.blacklist)!
|
||||
: newOptions.blacklist,
|
||||
};
|
||||
let s = document.createElement('script');
|
||||
s.type = 'text/javascript';
|
||||
s.appendChild(
|
||||
document.createTextNode(
|
||||
'window.devToolsOptions = Object.assign(window.devToolsOptions||{},' +
|
||||
JSON.stringify(options) +
|
||||
');'
|
||||
)
|
||||
);
|
||||
(document.head || document.documentElement).appendChild(s);
|
||||
s.parentNode!.removeChild(s);
|
||||
};
|
||||
|
||||
export const getOptionsFromBg = () => {
|
||||
/* chrome.runtime.sendMessage({ type: 'GET_OPTIONS' }, response => {
|
||||
if (response && response.options) injectOptions(response.options);
|
||||
});
|
||||
*/
|
||||
get((newOptions) => {
|
||||
injectOptions(newOptions);
|
||||
}); // Legacy
|
||||
};
|
||||
|
||||
export const isAllowed = (localOptions = options) =>
|
||||
!localOptions ||
|
||||
localOptions.inject ||
|
||||
!localOptions.urls ||
|
||||
location.href.match(toReg(localOptions.urls)!);
|
||||
|
||||
export default function syncOptions(toAllTabs?: ToAllTabs) {
|
||||
if (toAllTabs && !options) get(() => {}); // Initialize
|
||||
return {
|
||||
save: save(toAllTabs),
|
||||
get: get,
|
||||
subscribe: subscribe,
|
||||
};
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { PreloadedState } from 'redux';
|
||||
import { Provider } from 'react-redux';
|
||||
import { UPDATE_STATE } from '@redux-devtools/app/lib/constants/actionTypes';
|
||||
import { StoreState } from '@redux-devtools/app/lib/reducers';
|
||||
import App from '../../../app/containers/App';
|
||||
import configureStore from '../../../app/stores/windowStore';
|
||||
import getPreloadedState from '../background/getPreloadedState';
|
||||
|
@ -9,7 +11,7 @@ import getPreloadedState from '../background/getPreloadedState';
|
|||
import '../../views/window.pug';
|
||||
|
||||
const position = location.hash;
|
||||
let preloadedState;
|
||||
let preloadedState: PreloadedState<StoreState>;
|
||||
getPreloadedState(position, (state) => {
|
||||
preloadedState = state;
|
||||
});
|
|
@ -19,7 +19,6 @@ chrome.storage.local.get(
|
|||
selectMonitor={options['select-monitor']}
|
||||
testTemplates={options['test-templates']}
|
||||
selectedTemplate={options['test-templates-sel']}
|
||||
testTemplates={options['test-templates']}
|
||||
useCodemirror
|
||||
socketOptions={
|
||||
options['s:hostname'] && options['s:port']
|
Loading…
Reference in New Issue
Block a user