Просмотр исходного кода

Encode Wizard's state in URL ('Permalink' support)

Save Wizard's state as base64 to URL, so that reloading the page no
longer clears everything already set in the Wizard.
This also allows sharing URL between people with the exact same
environment.
To clear the saved state, simply remove everything from the URL's `d`
parameter and reload the page.
This is especially useful for debugging, could be disabled for
production if not needed.
This feature must be viewed as a BETA version, since there may be
issues if the encoded data is no longer available in Wizard's actual
data.
Sergiu Miclea 8 лет назад
Родитель
Сommit
cc745bff87

+ 10 - 0
src/actions/WizardActions.js

@@ -86,6 +86,16 @@ class WizardActions {
   createMultipleFailed(response) {
     return response || true
   }
+
+  setPermalink(data) {
+    WizardSource.setPermalink(data)
+    return data || true
+  }
+
+  getDataFromPermalink() {
+    let data = WizardSource.getDataFromPermalink()
+    return data || true
+  }
 }
 
 export default alt.createActions(WizardActions)

+ 17 - 1
src/components/pages/WizardPage/WizardPage.jsx

@@ -83,6 +83,7 @@ class WizardPage extends React.Component {
   }
 
   componentWillMount() {
+    WizardActions.getDataFromPermalink()
     let type = this.props.match && this.props.match.params.type
     if (type === 'migration' || type === 'replica') {
       this.setState({ type })
@@ -99,10 +100,16 @@ class WizardPage extends React.Component {
 
   loadDataForPage(page) {
     switch (page.id) {
-      case 'source':
+      case 'source': {
         ProviderActions.loadProviders()
         EndpointActions.getEndpoints()
+        // Preload instances if data is set from 'Permalink'
+        let source = WizardStore.getState().data.source
+        if (InstanceStore.getState().instances.length === 0 && source) {
+          InstanceActions.loadInstances(source.id)
+        }
         break
+      }
       case 'options':
         ProviderActions.loadOptionsSchema(this.props.wizardStore.data.target.type, this.state.type)
         break
@@ -271,12 +278,14 @@ class WizardPage extends React.Component {
 
   handleSourceEndpointChange(source) {
     WizardActions.updateData({ source, selectedInstances: null, networks: null })
+    WizardActions.setPermalink(WizardStore.getState().data)
     // Preload instances for 'vms' page
     InstanceActions.loadInstances(source.id)
   }
 
   handleTargetEndpointChange(target) {
     WizardActions.updateData({ target, networks: null, options: null })
+    WizardActions.setPermalink(WizardStore.getState().data)
   }
 
   handleAddEndpoint(newEndpointType, newEndpointFromSource) {
@@ -295,6 +304,7 @@ class WizardPage extends React.Component {
         WizardActions.updateData({ target: this.props.endpointStore.endpoints[0] })
       }
     }
+    WizardActions.setPermalink(WizardStore.getState().data)
     this.setState({ showNewEndpointModal: false })
   }
 
@@ -317,27 +327,33 @@ class WizardPage extends React.Component {
   handleInstanceClick(instance) {
     WizardActions.updateData({ networks: null })
     WizardActions.toggleInstanceSelection(instance)
+    WizardActions.setPermalink(WizardStore.getState().data)
   }
 
   handleOptionsChange(field, value) {
     WizardActions.updateData({ networks: null })
     WizardActions.updateOptions({ field, value })
+    WizardActions.setPermalink(WizardStore.getState().data)
   }
 
   handleNetworkChange(sourceNic, targetNetwork) {
     WizardActions.updateNetworks({ sourceNic, targetNetwork })
+    WizardActions.setPermalink(WizardStore.getState().data)
   }
 
   handleAddScheduleClick(schedule) {
     WizardActions.addSchedule(schedule)
+    WizardActions.setPermalink(WizardStore.getState().data)
   }
 
   handleScheduleChange(scheduleId, data) {
     WizardActions.updateSchedule(scheduleId, data)
+    WizardActions.setPermalink(WizardStore.getState().data)
   }
 
   handleScheduleRemove(scheduleId) {
     WizardActions.removeSchedule(scheduleId)
+    WizardActions.setPermalink(WizardStore.getState().data)
   }
 
   render() {

+ 21 - 0
src/sources/WizardSource.js

@@ -101,6 +101,27 @@ class WizardSource {
       })
     })
   }
+
+  static setPermalink(data) {
+    let hashExp = /(#\/wizard\/.*?)(?:\?|$)/
+
+    if (!hashExp.test(window.location.hash)) {
+      return
+    }
+
+    let hash = hashExp.exec(window.location.hash)[1]
+    window.history.replaceState({}, null, `${hash}?d=${btoa(JSON.stringify(data))}`)
+  }
+
+  static getDataFromPermalink() {
+    let dataExp = /\?d=(.*)/
+
+    if (!dataExp.test(window.location.hash)) {
+      return null
+    }
+
+    return JSON.parse(atob(dataExp.exec(window.location.hash)[1]))
+  }
 }
 
 export default WizardSource

+ 12 - 0
src/stores/WizardStore.js

@@ -42,6 +42,7 @@ class WizardStore {
       handleCreateMultiple: WizardActions.CREATE_MULTIPLE,
       handleCreateMultipleSuccess: WizardActions.CREATE_MULTIPLE_SUCCESS,
       handleCreateMultipleFailed: WizardActions.CREATE_MULTIPLE_FAILED,
+      handleGetDataFromPermalink: WizardActions.GET_DATA_FROM_PERMALINK,
     })
   }
 
@@ -145,6 +146,17 @@ class WizardStore {
   handleCreateMultipleFailed() {
     this.creatingItems = false
   }
+
+  handleGetDataFromPermalink(data) {
+    if (data === true) {
+      return
+    }
+
+    this.data = {
+      ...this.data,
+      ...data,
+    }
+  }
 }
 
 export default alt.createStore(WizardStore)