diff --git a/src/browser/components/DesktopIntegration/index.jsx b/src/browser/components/DesktopIntegration/index.jsx index dc9845366f6..86d6fd8a4f0 100644 --- a/src/browser/components/DesktopIntegration/index.jsx +++ b/src/browser/components/DesktopIntegration/index.jsx @@ -23,7 +23,7 @@ import { getActiveGraph, getCredentials, eventToHandler } from './helpers' export default class DesktopIntegration extends Component { setupListener () { - const { integrationPoint } = this.props + const { integrationPoint, onArgumentsChange = null } = this.props if (integrationPoint && integrationPoint.onContextUpdate) { integrationPoint.onContextUpdate((event, newContext, oldContext) => { const handlerPropName = eventToHandler(event.type) @@ -32,6 +32,13 @@ export default class DesktopIntegration extends Component { this.props[handlerPropName](event, newContext, oldContext) }) } + if ( + integrationPoint && + integrationPoint.onArgumentsChange && + onArgumentsChange + ) { + integrationPoint.onArgumentsChange(onArgumentsChange) + } } loadInitialContext () { const { integrationPoint, onMount = null } = this.props diff --git a/src/browser/modules/App/App.jsx b/src/browser/modules/App/App.jsx index e6ab8c2c8ae..f83cb3329a5 100644 --- a/src/browser/modules/App/App.jsx +++ b/src/browser/modules/App/App.jsx @@ -70,6 +70,7 @@ import { getMetadata, getUserAuthStatus } from 'shared/modules/sync/syncDuck' import ErrorBoundary from 'browser-components/ErrorBoundary' import { getExperimentalFeatures } from 'shared/modules/experimentalFeatures/experimentalFeaturesDuck' import FeatureToggleProvider from '../FeatureToggle/FeatureToggleProvider' +import { URL_ARGUMENTS_CHANGE } from 'shared/modules/app/appDuck' export class App extends Component { componentDidMount () { @@ -118,6 +119,7 @@ export class App extends Component { { if (activeGraph) return // We still got an active graph, do nothing ownProps.bus.send(SILENT_DISCONNECT, {}) } + const onArgumentsChange = argsString => { + ownProps.bus.send(URL_ARGUMENTS_CHANGE, { url: `?${argsString}` }) + } return { ...stateProps, ...ownProps, ...dispatchProps, switchConnection, setInitialConnectionData, - closeConnectionMaybe + closeConnectionMaybe, + onArgumentsChange } } diff --git a/src/shared/modules/app/appDuck.js b/src/shared/modules/app/appDuck.js index d3876e05062..239ffefd088 100644 --- a/src/shared/modules/app/appDuck.js +++ b/src/shared/modules/app/appDuck.js @@ -23,6 +23,8 @@ export const NAME = 'app' export const APP_START = `${NAME}/APP_START` export const USER_CLEAR = `${NAME}/USER_CLEAR` +export const URL_ARGUMENTS_CHANGE = `${NAME}/URL_ARGUMENTS_CHANGE` + // State constants export const DESKTOP = 'DESKTOP' export const WEB = 'WEB' diff --git a/src/shared/modules/editor/editorDuck.js b/src/shared/modules/editor/editorDuck.js index ee7b4dbdb25..d26c301a5d1 100644 --- a/src/shared/modules/editor/editorDuck.js +++ b/src/shared/modules/editor/editorDuck.js @@ -21,7 +21,7 @@ import Rx from 'rxjs/Rx' import { getUrlParamValue } from 'services/utils' import { getSettings } from 'shared/modules/settings/settingsDuck' -import { APP_START } from 'shared/modules/app/appDuck' +import { APP_START, URL_ARGUMENTS_CHANGE } from 'shared/modules/app/appDuck' const NAME = 'editor' export const SET_CONTENT = NAME + '/SET_CONTENT' @@ -42,6 +42,7 @@ export const editContent = (id, message) => ({ export const populateEditorFromUrlEpic = (some$, store) => { return some$ .ofType(APP_START) + .merge(some$.ofType(URL_ARGUMENTS_CHANGE)) .delay(1) // Timing issue. Needs to be detached like this .mergeMap(action => { if (!action.url) return Rx.Observable.never() diff --git a/src/shared/modules/editor/editorDuck.test.js b/src/shared/modules/editor/editorDuck.test.js new file mode 100644 index 00000000000..1d055749204 --- /dev/null +++ b/src/shared/modules/editor/editorDuck.test.js @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2002-2018 "Neo4j, Inc" + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* global beforeAll */ +import configureMockStore from 'redux-mock-store' +import { createEpicMiddleware } from 'redux-observable' +import { createBus, createReduxMiddleware } from 'suber' +import { populateEditorFromUrlEpic, SET_CONTENT } from './editorDuck' +import { APP_START, URL_ARGUMENTS_CHANGE } from '../app/appDuck' + +describe('editorDuck Epics', () => { + let store + const bus = createBus() + const epicMiddleware = createEpicMiddleware(populateEditorFromUrlEpic) + const mockStore = configureMockStore([ + epicMiddleware, + createReduxMiddleware(bus) + ]) + beforeAll(() => { + store = mockStore({ + settings: { + cmdchar: ':' + } + }) + }) + afterEach(() => { + bus.reset() + store.clearActions() + }) + test('Sends a SET_CONTENT event on initial url arguments', done => { + const cmd = 'play' + const arg = 'test-guide' + const action = { + type: APP_START, + url: `http://url.com?cmd=${cmd}&arg=${arg}` + } + + bus.take(SET_CONTENT, currentAction => { + // Then + expect(store.getActions()).toEqual([ + action, + { type: SET_CONTENT, message: `:${cmd} ${arg}` } + ]) + done() + }) + + // When + store.dispatch(action) + }) + test('Sends a SET_CONTENT event on url arguments change', done => { + const cmd = 'play' + const arg = 'test-guide' + const action = { + type: URL_ARGUMENTS_CHANGE, + url: `?cmd=${cmd}&arg=${arg}` + } + + bus.take(SET_CONTENT, currentAction => { + // Then + expect(store.getActions()).toEqual([ + action, + { type: SET_CONTENT, message: `:${cmd} ${arg}` } + ]) + done() + }) + + // When + store.dispatch(action) + }) +})