mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-27 00:19:55 +03:00
More work
This commit is contained in:
parent
696a669bd0
commit
084f9b9429
|
@ -1,6 +1,11 @@
|
||||||
import mapValues from 'lodash/mapValues';
|
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',
|
DO_NOT_FILTER: 'DO_NOT_FILTER',
|
||||||
BLACKLIST_SPECIFIC: 'BLACKLIST_SPECIFIC',
|
BLACKLIST_SPECIFIC: 'BLACKLIST_SPECIFIC',
|
||||||
WHITELIST_SPECIFIC: 'WHITELIST_SPECIFIC',
|
WHITELIST_SPECIFIC: 'WHITELIST_SPECIFIC',
|
|
@ -1,14 +1,22 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect, ResolveThunks } from 'react-redux';
|
||||||
import { Container, Notification } from 'devui';
|
import { Container, Notification } from 'devui';
|
||||||
import { getActiveInstance } from '@redux-devtools/app/lib/reducers/instances';
|
import { getActiveInstance } from '@redux-devtools/app/lib/reducers/instances';
|
||||||
import Settings from '@redux-devtools/app/lib/components/Settings';
|
import Settings from '@redux-devtools/app/lib/components/Settings';
|
||||||
import Actions from '@redux-devtools/app/lib/containers/Actions';
|
import Actions from '@redux-devtools/app/lib/containers/Actions';
|
||||||
import Header from '@redux-devtools/app/lib/components/Header';
|
import Header from '@redux-devtools/app/lib/components/Header';
|
||||||
import { clearNotification } from '@redux-devtools/app/lib/actions';
|
import { clearNotification } from '@redux-devtools/app/lib/actions';
|
||||||
|
import { StoreState } from '@redux-devtools/app/lib/reducers';
|
||||||
|
|
||||||
class App extends Component {
|
type StateProps = ReturnType<typeof mapStateToProps>;
|
||||||
openWindow = (position) => {
|
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 });
|
chrome.runtime.sendMessage({ type: 'OPEN', position });
|
||||||
};
|
};
|
||||||
openOptionsPage = () => {
|
openOptionsPage = () => {
|
||||||
|
@ -62,7 +70,7 @@ class App extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
function mapStateToProps(state: StoreState) {
|
||||||
const instances = state.instances;
|
const instances = state.instances;
|
||||||
const id = getActiveInstance(instances);
|
const id = getActiveInstance(instances);
|
||||||
return {
|
return {
|
|
@ -1,9 +1,16 @@
|
||||||
|
import { Dispatch, Store } from 'redux';
|
||||||
import {
|
import {
|
||||||
SELECT_INSTANCE,
|
SELECT_INSTANCE,
|
||||||
UPDATE_STATE,
|
UPDATE_STATE,
|
||||||
} from '@redux-devtools/app/lib/constants/actionTypes';
|
} 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;
|
const instances = store.getState().instances;
|
||||||
if (instances.current === 'default') return;
|
if (instances.current === 'default') return;
|
||||||
const connections = instances.connections[tabId];
|
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(
|
chrome.tabs.query(
|
||||||
{
|
{
|
||||||
active: true,
|
active: true,
|
||||||
|
@ -21,13 +28,13 @@ function getCurrentTabId(next) {
|
||||||
(tabs) => {
|
(tabs) => {
|
||||||
const tab = tabs[0];
|
const tab = tabs[0];
|
||||||
if (!tab) return;
|
if (!tab) return;
|
||||||
next(tab.id);
|
next(tab.id!);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function popupSelector(store) {
|
export default function popupSelector(store: Store<StoreState, StoreAction>) {
|
||||||
return (next) => (action) => {
|
return (next: Dispatch<StoreAction>) => (action: StoreAction) => {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
if (action.type === UPDATE_STATE) {
|
if (action.type === UPDATE_STATE) {
|
||||||
if (chrome.devtools && chrome.devtools.inspectedWindow) {
|
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 reports from '@redux-devtools/app/lib/reducers/reports';
|
||||||
import section from '@redux-devtools/app/lib/reducers/section';
|
import section from '@redux-devtools/app/lib/reducers/section';
|
||||||
import theme from '@redux-devtools/app/lib/reducers/theme';
|
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,
|
instances,
|
||||||
monitor,
|
monitor,
|
||||||
reports,
|
reports,
|
||||||
notification,
|
notification,
|
||||||
section,
|
section,
|
||||||
theme,
|
theme,
|
||||||
|
connection,
|
||||||
|
socket,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default rootReducer;
|
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 reports from '@redux-devtools/app/lib/reducers/reports';
|
||||||
import section from '@redux-devtools/app/lib/reducers/section';
|
import section from '@redux-devtools/app/lib/reducers/section';
|
||||||
import theme from '@redux-devtools/app/lib/reducers/theme';
|
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,
|
instances,
|
||||||
monitor,
|
monitor,
|
||||||
socket,
|
socket,
|
||||||
|
@ -15,6 +18,7 @@ const rootReducer = combineReducers({
|
||||||
notification,
|
notification,
|
||||||
section,
|
section,
|
||||||
theme,
|
theme,
|
||||||
|
connection,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default rootReducer;
|
export default rootReducer;
|
|
@ -7,8 +7,9 @@ import {
|
||||||
SELECT_INSTANCE,
|
SELECT_INSTANCE,
|
||||||
LIFTED_ACTION,
|
LIFTED_ACTION,
|
||||||
} from '@redux-devtools/app/lib/constants/actionTypes';
|
} 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) {
|
switch (action.type) {
|
||||||
case UPDATE_STATE:
|
case UPDATE_STATE:
|
||||||
return { ...action.instances, selected: state.selected };
|
return { ...action.instances, selected: state.selected };
|
|
@ -1,8 +1,10 @@
|
||||||
import { createStore, applyMiddleware } from 'redux';
|
import { createStore, applyMiddleware, PreloadedState } from 'redux';
|
||||||
import rootReducer from '../reducers/background';
|
import rootReducer, { BackgroundState } from '../reducers/background';
|
||||||
import api from '../middlewares/api';
|
import api from '../middlewares/api';
|
||||||
|
|
||||||
export default function configureStore(preloadedState) {
|
export default function configureStore(
|
||||||
|
preloadedState: PreloadedState<BackgroundState>
|
||||||
|
) {
|
||||||
return createStore(rootReducer, preloadedState, applyMiddleware(api));
|
return createStore(rootReducer, preloadedState, applyMiddleware(api));
|
||||||
/*
|
/*
|
||||||
let enhancer;
|
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 exportState from '@redux-devtools/app/lib/middlewares/exportState';
|
||||||
import panelDispatcher from '../middlewares/panelSync';
|
import panelDispatcher from '../middlewares/panelSync';
|
||||||
import rootReducer from '../reducers/panel';
|
import rootReducer from '../reducers/panel';
|
||||||
|
import { StoreState } from '@redux-devtools/app/lib/reducers';
|
||||||
|
|
||||||
export default function configureStore(
|
export default function configureStore(
|
||||||
position: string,
|
position: string,
|
||||||
bgConnection: chrome.runtime.Port,
|
bgConnection: chrome.runtime.Port,
|
||||||
preloadedState
|
preloadedState: PreloadedState<StoreState>
|
||||||
) {
|
) {
|
||||||
const enhancer = applyMiddleware(exportState, panelDispatcher(bgConnection));
|
const enhancer = applyMiddleware(exportState, panelDispatcher(bgConnection));
|
||||||
return createStore(rootReducer, preloadedState, enhancer);
|
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 exportState from '@redux-devtools/app/lib/middlewares/exportState';
|
||||||
import api from '@redux-devtools/app/lib/middlewares/api';
|
import api from '@redux-devtools/app/lib/middlewares/api';
|
||||||
import { CONNECT_REQUEST } from '@redux-devtools/app/lib/constants/socketActionTypes';
|
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 syncStores from '../middlewares/windowSync';
|
||||||
import instanceSelector from '../middlewares/instanceSelector';
|
import instanceSelector from '../middlewares/instanceSelector';
|
||||||
import rootReducer from '../reducers/window';
|
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;
|
let enhancer;
|
||||||
const middlewares = [exportState, api, syncStores(baseStore)];
|
const middlewares = [exportState, api, syncStores(baseStore)];
|
||||||
if (!position || position === '#popup') {
|
if (!position || position === '#popup') {
|
|
@ -1,3 +1,6 @@
|
||||||
|
import { PreloadedState } from 'redux';
|
||||||
|
import { StoreState } from '@redux-devtools/app/lib/reducers';
|
||||||
|
|
||||||
const getIfExists = (sel, template) =>
|
const getIfExists = (sel, template) =>
|
||||||
typeof sel === 'undefined' ||
|
typeof sel === 'undefined' ||
|
||||||
typeof template === 'undefined' ||
|
typeof template === 'undefined' ||
|
||||||
|
@ -5,7 +8,10 @@ const getIfExists = (sel, template) =>
|
||||||
? 0
|
? 0
|
||||||
: sel;
|
: sel;
|
||||||
|
|
||||||
export default function getPreloadedState(position, cb) {
|
export default function getPreloadedState(
|
||||||
|
position: string,
|
||||||
|
cb: (state: PreloadedState<StoreState>) => void
|
||||||
|
) {
|
||||||
chrome.storage.local.get(
|
chrome.storage.local.get(
|
||||||
[
|
[
|
||||||
'monitor' + position,
|
'monitor' + position,
|
|
@ -7,6 +7,9 @@ import configureStore from '../../../app/stores/panelStore';
|
||||||
import getPreloadedState from '../background/getPreloadedState';
|
import getPreloadedState from '../background/getPreloadedState';
|
||||||
|
|
||||||
import '../../views/devpanel.pug';
|
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 position = location.hash;
|
||||||
const messageStyle: CSSProperties = {
|
const messageStyle: CSSProperties = {
|
||||||
|
@ -16,10 +19,10 @@ const messageStyle: CSSProperties = {
|
||||||
};
|
};
|
||||||
|
|
||||||
let rendered: boolean;
|
let rendered: boolean;
|
||||||
let store;
|
let store: Store<StoreState, StoreAction> | undefined;
|
||||||
let bgConnection: chrome.runtime.Port;
|
let bgConnection: chrome.runtime.Port;
|
||||||
let naTimeout: NodeJS.Timeout;
|
let naTimeout: NodeJS.Timeout;
|
||||||
let preloadedState;
|
let preloadedState: PreloadedState<StoreState>;
|
||||||
|
|
||||||
const isChrome = navigator.userAgent.indexOf('Firefox') === -1;
|
const isChrome = navigator.userAgent.indexOf('Firefox') === -1;
|
||||||
|
|
||||||
|
@ -96,10 +99,10 @@ function init(id: number) {
|
||||||
bgConnection.onMessage.addListener((message) => {
|
bgConnection.onMessage.addListener((message) => {
|
||||||
if (message.type === 'NA') {
|
if (message.type === 'NA') {
|
||||||
if (message.id === id) renderNA();
|
if (message.id === id) renderNA();
|
||||||
else store.dispatch({ type: REMOVE_INSTANCE, id: message.id });
|
else store!.dispatch({ type: REMOVE_INSTANCE, id: message.id });
|
||||||
} else {
|
} else {
|
||||||
if (!rendered) renderDevTools();
|
if (!rendered) renderDevTools();
|
||||||
store.dispatch(message);
|
store!.dispatch(message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import '../../views/devtools.pug';
|
import '../../views/devtools.pug';
|
||||||
|
|
||||||
function createPanel(url) {
|
function createPanel(url: string) {
|
||||||
chrome.devtools.panels.create(
|
chrome.devtools.panels.create(
|
||||||
'Redux',
|
'Redux',
|
||||||
'img/logo/scalable.png',
|
'img/logo/scalable.png',
|
|
@ -5,7 +5,7 @@ if (process.env.NODE_ENV === 'production') {
|
||||||
const { default: script } = require('raw-loader!tmp/page.bundle.js');
|
const { default: script } = require('raw-loader!tmp/page.bundle.js');
|
||||||
s.appendChild(document.createTextNode(script));
|
s.appendChild(document.createTextNode(script));
|
||||||
(document.head || document.documentElement).appendChild(s);
|
(document.head || document.documentElement).appendChild(s);
|
||||||
s.parentNode.removeChild(s);
|
s.parentNode!.removeChild(s);
|
||||||
} else {
|
} else {
|
||||||
s.src = chrome.extension.getURL('page.bundle.js');
|
s.src = chrome.extension.getURL('page.bundle.js');
|
||||||
s.onload = function () {
|
s.onload = function () {
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { OptionsProps } from './Options';
|
||||||
|
|
||||||
export default ({ options, saveOption }) => {
|
export default ({ options, saveOption }: OptionsProps) => {
|
||||||
const AllowToRunState = {
|
const AllowToRunState = {
|
||||||
EVERYWHERE: true,
|
EVERYWHERE: true,
|
||||||
ON_SPECIFIC_URLS: false,
|
ON_SPECIFIC_URLS: false,
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { OptionsProps } from './Options';
|
||||||
|
|
||||||
export default ({ options, saveOption }) => {
|
export default ({ options, saveOption }: OptionsProps) => {
|
||||||
return (
|
return (
|
||||||
<fieldset className="option-group">
|
<fieldset className="option-group">
|
||||||
<legend className="option-group__title">Context Menu</legend>
|
<legend className="option-group__title">Context Menu</legend>
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { OptionsProps } from './Options';
|
||||||
|
|
||||||
export default ({ options, saveOption }) => {
|
export default ({ options, saveOption }: OptionsProps) => {
|
||||||
const EditorState = {
|
const EditorState = {
|
||||||
BROWSER: 0,
|
BROWSER: 0,
|
||||||
EXTERNAL: 1,
|
EXTERNAL: 1,
|
||||||
|
@ -45,7 +46,7 @@ export default ({ options, saveOption }) => {
|
||||||
className="option__element"
|
className="option__element"
|
||||||
id="editor"
|
id="editor"
|
||||||
type="text"
|
type="text"
|
||||||
size="33"
|
size={33}
|
||||||
maxLength={30}
|
maxLength={30}
|
||||||
placeholder="vscode, atom, webstorm, sublime..."
|
placeholder="vscode, atom, webstorm, sublime..."
|
||||||
value={options.editor}
|
value={options.editor}
|
|
@ -1,7 +1,8 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FilterState } from '../../../app/api/filters';
|
import { FilterState } from '../../../app/api/filters';
|
||||||
|
import { OptionsProps } from './Options';
|
||||||
|
|
||||||
export default ({ options, saveOption }) => {
|
export default ({ options, saveOption }: OptionsProps) => {
|
||||||
return (
|
return (
|
||||||
<fieldset className="option-group">
|
<fieldset className="option-group">
|
||||||
<legend className="option-group__title">
|
<legend className="option-group__title">
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { OptionsProps } from './Options';
|
||||||
|
|
||||||
export default ({ options, saveOption }) => {
|
export default ({ options, saveOption }: OptionsProps) => {
|
||||||
const browserName = navigator.userAgent.includes('Firefox')
|
const browserName = navigator.userAgent.includes('Firefox')
|
||||||
? 'Firefox'
|
? 'Firefox'
|
||||||
: 'Chrome';
|
: 'Chrome';
|
|
@ -4,8 +4,17 @@ import FilterGroup from './FilterGroup';
|
||||||
import AllowToRunGroup from './AllowToRunGroup';
|
import AllowToRunGroup from './AllowToRunGroup';
|
||||||
import MiscellaneousGroup from './MiscellaneousGroup';
|
import MiscellaneousGroup from './MiscellaneousGroup';
|
||||||
import ContextMenuGroup from './ContextMenuGroup';
|
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>
|
<div>
|
||||||
<EditorGroup {...props} />
|
<EditorGroup {...props} />
|
||||||
<FilterGroup {...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 React from 'react';
|
||||||
import { render } from 'react-dom';
|
import { render } from 'react-dom';
|
||||||
|
import { PreloadedState } from 'redux';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { UPDATE_STATE } from '@redux-devtools/app/lib/constants/actionTypes';
|
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 App from '../../../app/containers/App';
|
||||||
import configureStore from '../../../app/stores/windowStore';
|
import configureStore from '../../../app/stores/windowStore';
|
||||||
import getPreloadedState from '../background/getPreloadedState';
|
import getPreloadedState from '../background/getPreloadedState';
|
||||||
|
@ -9,7 +11,7 @@ import getPreloadedState from '../background/getPreloadedState';
|
||||||
import '../../views/window.pug';
|
import '../../views/window.pug';
|
||||||
|
|
||||||
const position = location.hash;
|
const position = location.hash;
|
||||||
let preloadedState;
|
let preloadedState: PreloadedState<StoreState>;
|
||||||
getPreloadedState(position, (state) => {
|
getPreloadedState(position, (state) => {
|
||||||
preloadedState = state;
|
preloadedState = state;
|
||||||
});
|
});
|
|
@ -19,7 +19,6 @@ chrome.storage.local.get(
|
||||||
selectMonitor={options['select-monitor']}
|
selectMonitor={options['select-monitor']}
|
||||||
testTemplates={options['test-templates']}
|
testTemplates={options['test-templates']}
|
||||||
selectedTemplate={options['test-templates-sel']}
|
selectedTemplate={options['test-templates-sel']}
|
||||||
testTemplates={options['test-templates']}
|
|
||||||
useCodemirror
|
useCodemirror
|
||||||
socketOptions={
|
socketOptions={
|
||||||
options['s:hostname'] && options['s:port']
|
options['s:hostname'] && options['s:port']
|
Loading…
Reference in New Issue
Block a user