diff --git a/extension/src/app/api/filters.js b/extension/src/app/api/filters.ts similarity index 96% rename from extension/src/app/api/filters.js rename to extension/src/app/api/filters.ts index 609dcac4..e072437d 100644 --- a/extension/src/app/api/filters.js +++ b/extension/src/app/api/filters.ts @@ -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', diff --git a/extension/src/app/containers/App.js b/extension/src/app/containers/App.tsx similarity index 81% rename from extension/src/app/containers/App.js rename to extension/src/app/containers/App.tsx index df23bcee..c030f400 100644 --- a/extension/src/app/containers/App.js +++ b/extension/src/app/containers/App.tsx @@ -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; +type DispatchProps = ResolveThunks; +interface OwnProps { + readonly position: string; +} +type Props = StateProps & DispatchProps & OwnProps; + +class App extends Component { + 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 { diff --git a/extension/src/app/middlewares/instanceSelector.js b/extension/src/app/middlewares/instanceSelector.ts similarity index 63% rename from extension/src/app/middlewares/instanceSelector.js rename to extension/src/app/middlewares/instanceSelector.ts index 72c6d03d..cc89f97c 100644 --- a/extension/src/app/middlewares/instanceSelector.js +++ b/extension/src/app/middlewares/instanceSelector.ts @@ -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, + next: Dispatch +) { 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) { + return (next: Dispatch) => (action: StoreAction) => { const result = next(action); if (action.type === UPDATE_STATE) { if (chrome.devtools && chrome.devtools.inspectedWindow) { diff --git a/extension/src/app/middlewares/panelSync.js b/extension/src/app/middlewares/panelSync.js deleted file mode 100644 index f88f421d..00000000 --- a/extension/src/app/middlewares/panelSync.js +++ /dev/null @@ -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; diff --git a/extension/src/app/middlewares/panelSync.ts b/extension/src/app/middlewares/panelSync.ts new file mode 100644 index 00000000..aae303ad --- /dev/null +++ b/extension/src/app/middlewares/panelSync.ts @@ -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, StoreState>) => + (next: Dispatch) => + (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; diff --git a/extension/src/app/middlewares/windowSync.js b/extension/src/app/middlewares/windowSync.ts similarity index 100% rename from extension/src/app/middlewares/windowSync.js rename to extension/src/app/middlewares/windowSync.ts diff --git a/extension/src/app/reducers/background/index.js b/extension/src/app/reducers/background/index.js deleted file mode 100644 index 81bd41c6..00000000 --- a/extension/src/app/reducers/background/index.js +++ /dev/null @@ -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; diff --git a/extension/src/app/reducers/background/index.ts b/extension/src/app/reducers/background/index.ts new file mode 100644 index 00000000..12124418 --- /dev/null +++ b/extension/src/app/reducers/background/index.ts @@ -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; diff --git a/extension/src/app/reducers/background/persistStates.js b/extension/src/app/reducers/background/persistStates.ts similarity index 100% rename from extension/src/app/reducers/background/persistStates.js rename to extension/src/app/reducers/background/persistStates.ts diff --git a/extension/src/app/reducers/panel/index.ts b/extension/src/app/reducers/panel/index.ts index 42cc531f..b628688d 100644 --- a/extension/src/app/reducers/panel/index.ts +++ b/extension/src/app/reducers/panel/index.ts @@ -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({ instances, monitor, reports, notification, section, theme, + connection, + socket, }); export default rootReducer; diff --git a/extension/src/app/reducers/window/index.js b/extension/src/app/reducers/window/index.ts similarity index 68% rename from extension/src/app/reducers/window/index.js rename to extension/src/app/reducers/window/index.ts index 5420e736..c715b55a 100644 --- a/extension/src/app/reducers/window/index.js +++ b/extension/src/app/reducers/window/index.ts @@ -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({ instances, monitor, socket, @@ -15,6 +18,7 @@ const rootReducer = combineReducers({ notification, section, theme, + connection, }); export default rootReducer; diff --git a/extension/src/app/reducers/window/instances.js b/extension/src/app/reducers/window/instances.ts similarity index 80% rename from extension/src/app/reducers/window/instances.js rename to extension/src/app/reducers/window/instances.ts index 7454eb90..6a50d08b 100644 --- a/extension/src/app/reducers/window/instances.js +++ b/extension/src/app/reducers/window/instances.ts @@ -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 }; diff --git a/extension/src/app/stores/backgroundStore.js b/extension/src/app/stores/backgroundStore.ts similarity index 62% rename from extension/src/app/stores/backgroundStore.js rename to extension/src/app/stores/backgroundStore.ts index 2a9568e2..2348804b 100644 --- a/extension/src/app/stores/backgroundStore.js +++ b/extension/src/app/stores/backgroundStore.ts @@ -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 +) { return createStore(rootReducer, preloadedState, applyMiddleware(api)); /* let enhancer; diff --git a/extension/src/app/stores/panelStore.ts b/extension/src/app/stores/panelStore.ts index e15266e9..977ed96e 100644 --- a/extension/src/app/stores/panelStore.ts +++ b/extension/src/app/stores/panelStore.ts @@ -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 ) { const enhancer = applyMiddleware(exportState, panelDispatcher(bgConnection)); return createStore(rootReducer, preloadedState, enhancer); diff --git a/extension/src/app/stores/windowStore.js b/extension/src/app/stores/windowStore.ts similarity index 75% rename from extension/src/app/stores/windowStore.js rename to extension/src/app/stores/windowStore.ts index 2e67ef96..99aed5f3 100644 --- a/extension/src/app/stores/windowStore.js +++ b/extension/src/app/stores/windowStore.ts @@ -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, + position: string, + preloadedState: PreloadedState +) { let enhancer; const middlewares = [exportState, api, syncStores(baseStore)]; if (!position || position === '#popup') { diff --git a/extension/src/browser/extension/background/getPreloadedState.js b/extension/src/browser/extension/background/getPreloadedState.ts similarity index 78% rename from extension/src/browser/extension/background/getPreloadedState.js rename to extension/src/browser/extension/background/getPreloadedState.ts index 89d49d50..00ea732e 100644 --- a/extension/src/browser/extension/background/getPreloadedState.js +++ b/extension/src/browser/extension/background/getPreloadedState.ts @@ -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) => void +) { chrome.storage.local.get( [ 'monitor' + position, diff --git a/extension/src/browser/extension/background/index.js b/extension/src/browser/extension/background/index.ts similarity index 100% rename from extension/src/browser/extension/background/index.js rename to extension/src/browser/extension/background/index.ts diff --git a/extension/src/browser/extension/devpanel/index.tsx b/extension/src/browser/extension/devpanel/index.tsx index f5422c4a..5b58b884 100644 --- a/extension/src/browser/extension/devpanel/index.tsx +++ b/extension/src/browser/extension/devpanel/index.tsx @@ -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 | undefined; let bgConnection: chrome.runtime.Port; let naTimeout: NodeJS.Timeout; -let preloadedState; +let preloadedState: PreloadedState; 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); } }); } diff --git a/extension/src/browser/extension/devtools/index.js b/extension/src/browser/extension/devtools/index.ts similarity index 92% rename from extension/src/browser/extension/devtools/index.js rename to extension/src/browser/extension/devtools/index.ts index 9365ec61..de6abaaa 100644 --- a/extension/src/browser/extension/devtools/index.js +++ b/extension/src/browser/extension/devtools/index.ts @@ -1,6 +1,6 @@ import '../../views/devtools.pug'; -function createPanel(url) { +function createPanel(url: string) { chrome.devtools.panels.create( 'Redux', 'img/logo/scalable.png', diff --git a/extension/src/browser/extension/inject/deprecatedWarn.js b/extension/src/browser/extension/inject/deprecatedWarn.ts similarity index 100% rename from extension/src/browser/extension/inject/deprecatedWarn.js rename to extension/src/browser/extension/inject/deprecatedWarn.ts diff --git a/extension/src/browser/extension/inject/index.js b/extension/src/browser/extension/inject/index.ts similarity index 100% rename from extension/src/browser/extension/inject/index.js rename to extension/src/browser/extension/inject/index.ts diff --git a/extension/src/browser/extension/inject/pageScriptWrap.js b/extension/src/browser/extension/inject/pageScriptWrap.ts similarity index 93% rename from extension/src/browser/extension/inject/pageScriptWrap.js rename to extension/src/browser/extension/inject/pageScriptWrap.ts index 0a8cef09..7f28cfea 100644 --- a/extension/src/browser/extension/inject/pageScriptWrap.js +++ b/extension/src/browser/extension/inject/pageScriptWrap.ts @@ -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 () { diff --git a/extension/src/browser/extension/options/AllowToRunGroup.js b/extension/src/browser/extension/options/AllowToRunGroup.tsx similarity index 93% rename from extension/src/browser/extension/options/AllowToRunGroup.js rename to extension/src/browser/extension/options/AllowToRunGroup.tsx index d7c0cd82..f08990ae 100644 --- a/extension/src/browser/extension/options/AllowToRunGroup.js +++ b/extension/src/browser/extension/options/AllowToRunGroup.tsx @@ -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, diff --git a/extension/src/browser/extension/options/ContextMenuGroup.js b/extension/src/browser/extension/options/ContextMenuGroup.tsx similarity index 87% rename from extension/src/browser/extension/options/ContextMenuGroup.js rename to extension/src/browser/extension/options/ContextMenuGroup.tsx index 8ceb80f8..727dda98 100644 --- a/extension/src/browser/extension/options/ContextMenuGroup.js +++ b/extension/src/browser/extension/options/ContextMenuGroup.tsx @@ -1,6 +1,7 @@ import React from 'react'; +import { OptionsProps } from './Options'; -export default ({ options, saveOption }) => { +export default ({ options, saveOption }: OptionsProps) => { return (
Context Menu diff --git a/extension/src/browser/extension/options/EditorGroup.js b/extension/src/browser/extension/options/EditorGroup.tsx similarity index 95% rename from extension/src/browser/extension/options/EditorGroup.js rename to extension/src/browser/extension/options/EditorGroup.tsx index 321c30b2..130ef824 100644 --- a/extension/src/browser/extension/options/EditorGroup.js +++ b/extension/src/browser/extension/options/EditorGroup.tsx @@ -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} diff --git a/extension/src/browser/extension/options/FilterGroup.js b/extension/src/browser/extension/options/FilterGroup.tsx similarity index 95% rename from extension/src/browser/extension/options/FilterGroup.js rename to extension/src/browser/extension/options/FilterGroup.tsx index 3567249f..4bc24536 100644 --- a/extension/src/browser/extension/options/FilterGroup.js +++ b/extension/src/browser/extension/options/FilterGroup.tsx @@ -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 (
diff --git a/extension/src/browser/extension/options/MiscellaneousGroup.js b/extension/src/browser/extension/options/MiscellaneousGroup.tsx similarity index 94% rename from extension/src/browser/extension/options/MiscellaneousGroup.js rename to extension/src/browser/extension/options/MiscellaneousGroup.tsx index c335c372..1c3c9a05 100644 --- a/extension/src/browser/extension/options/MiscellaneousGroup.js +++ b/extension/src/browser/extension/options/MiscellaneousGroup.tsx @@ -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'; diff --git a/extension/src/browser/extension/options/Options.js b/extension/src/browser/extension/options/Options.tsx similarity index 81% rename from extension/src/browser/extension/options/Options.js rename to extension/src/browser/extension/options/Options.tsx index 693a6f4a..579dd3ac 100644 --- a/extension/src/browser/extension/options/Options.js +++ b/extension/src/browser/extension/options/Options.tsx @@ -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: ( + name: K, + value: Options[K] + ) => void; +} + +export default (props: OptionsProps) => (
diff --git a/extension/src/browser/extension/options/index.js b/extension/src/browser/extension/options/index.tsx similarity index 100% rename from extension/src/browser/extension/options/index.js rename to extension/src/browser/extension/options/index.tsx diff --git a/extension/src/browser/extension/options/syncOptions.js b/extension/src/browser/extension/options/syncOptions.js deleted file mode 100644 index 04827b5c..00000000 --- a/extension/src/browser/extension/options/syncOptions.js +++ /dev/null @@ -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, - }; -} diff --git a/extension/src/browser/extension/options/syncOptions.ts b/extension/src/browser/extension/options/syncOptions.ts new file mode 100644 index 00000000..051095a5 --- /dev/null +++ b/extension/src/browser/extension/options/syncOptions.ts @@ -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) => + (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, + }; +} diff --git a/extension/src/browser/extension/window/index.js b/extension/src/browser/extension/window/index.tsx similarity index 88% rename from extension/src/browser/extension/window/index.js rename to extension/src/browser/extension/window/index.tsx index e1f1260f..14aaedea 100644 --- a/extension/src/browser/extension/window/index.js +++ b/extension/src/browser/extension/window/index.tsx @@ -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; getPreloadedState(position, (state) => { preloadedState = state; }); diff --git a/extension/src/browser/extension/window/remote.js b/extension/src/browser/extension/window/remote.tsx similarity index 94% rename from extension/src/browser/extension/window/remote.js rename to extension/src/browser/extension/window/remote.tsx index e4e271e6..3c31bb66 100644 --- a/extension/src/browser/extension/window/remote.js +++ b/extension/src/browser/extension/window/remote.tsx @@ -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']