Ver Fonte

Improve Wizard page reload state preservation

Reloading / Reopening of a Wizard page URL data link now fully supports
complex options (destination, source options API calls). Previously,
destination and source options API calls would not get made after page
reload.

Schedule and storage mapping is now also saved in URL data link.

Changing the Wizard type (i.e.: from Replica to Migration) now gets
properly reflected in the URL.
Sergiu Miclea há 6 anos atrás
pai
commit
3795f09a3d

+ 26 - 25
src/components/pages/WizardPage/WizardPage.jsx

@@ -96,7 +96,7 @@ class WizardPage extends React.Component<Props, State> {
   }
   }
 
 
   componentWillMount() {
   componentWillMount() {
-    this.initializeState()
+    this.initializeState(this.props.match)
     this.handleResize()
     this.handleResize()
   }
   }
 
 
@@ -108,11 +108,11 @@ class WizardPage extends React.Component<Props, State> {
   }
   }
 
 
   componentWillReceiveProps(newProps: Props) {
   componentWillReceiveProps(newProps: Props) {
-    if (newProps.location.search === this.props.location.search) {
+    if (newProps.location === this.props.location) {
       return
       return
     }
     }
     wizardStore.clearData()
     wizardStore.clearData()
-    this.initializeState()
+    this.initializeState(newProps.match)
   }
   }
 
 
   componentWillUnmount() {
   componentWillUnmount() {
@@ -182,8 +182,8 @@ class WizardPage extends React.Component<Props, State> {
       source: null,
       source: null,
     })
     })
     wizardStore.clearStorageMap()
     wizardStore.clearStorageMap()
-    wizardStore.setPermalink(wizardStore.data)
-    this.setState({ type: isReplica ? 'replica' : 'migration' })
+    let type = isReplica ? 'replica' : 'migration'
+    this.props.history.replace(`/wizard/${type}`)
   }
   }
 
 
   handleBackClick() {
   handleBackClick() {
@@ -215,7 +215,7 @@ class WizardPage extends React.Component<Props, State> {
   async handleSourceEndpointChange(source: ?EndpointType) {
   async handleSourceEndpointChange(source: ?EndpointType) {
     wizardStore.updateData({ source, selectedInstances: null, networks: null, sourceOptions: null })
     wizardStore.updateData({ source, selectedInstances: null, networks: null, sourceOptions: null })
     wizardStore.clearStorageMap()
     wizardStore.clearStorageMap()
-    wizardStore.setPermalink(wizardStore.data)
+    wizardStore.updateUrlState()
 
 
     if (!source) {
     if (!source) {
       return
       return
@@ -237,7 +237,7 @@ class WizardPage extends React.Component<Props, State> {
   async handleTargetEndpointChange(target: EndpointType) {
   async handleTargetEndpointChange(target: EndpointType) {
     wizardStore.updateData({ target, networks: null, destOptions: null })
     wizardStore.updateData({ target, networks: null, destOptions: null })
     wizardStore.clearStorageMap()
     wizardStore.clearStorageMap()
-    wizardStore.setPermalink(wizardStore.data)
+    wizardStore.updateUrlState()
     if (this.pages.find(p => p.id === 'storage')) {
     if (this.pages.find(p => p.id === 'storage')) {
       endpointStore.loadStorage(target.id, {})
       endpointStore.loadStorage(target.id, {})
     }
     }
@@ -273,7 +273,7 @@ class WizardPage extends React.Component<Props, State> {
         wizardStore.updateData({ target: endpointStore.endpoints[0] })
         wizardStore.updateData({ target: endpointStore.endpoints[0] })
       }
       }
     }
     }
-    wizardStore.setPermalink(wizardStore.data)
+    wizardStore.updateUrlState()
     this.setState({ showNewEndpointModal: false })
     this.setState({ showNewEndpointModal: false })
   }
   }
 
 
@@ -293,7 +293,7 @@ class WizardPage extends React.Component<Props, State> {
     wizardStore.updateData({ networks: null })
     wizardStore.updateData({ networks: null })
     wizardStore.clearStorageMap()
     wizardStore.clearStorageMap()
     wizardStore.toggleInstanceSelection(instance)
     wizardStore.toggleInstanceSelection(instance)
-    wizardStore.setPermalink(wizardStore.data)
+    wizardStore.updateUrlState()
   }
   }
 
 
   handleInstancePageClick(page: number) {
   handleInstancePageClick(page: number) {
@@ -312,7 +312,7 @@ class WizardPage extends React.Component<Props, State> {
     if (field.type !== 'string' || field.enum) {
     if (field.type !== 'string' || field.enum) {
       this.loadExtraOptions(field, 'destination')
       this.loadExtraOptions(field, 'destination')
     }
     }
-    wizardStore.setPermalink(wizardStore.data)
+    wizardStore.updateUrlState()
   }
   }
 
 
   handleSourceOptionsChange(field: Field, value: any) {
   handleSourceOptionsChange(field: Field, value: any) {
@@ -321,28 +321,32 @@ class WizardPage extends React.Component<Props, State> {
     if (field.type !== 'string' || field.enum) {
     if (field.type !== 'string' || field.enum) {
       this.loadExtraOptions(field, 'source')
       this.loadExtraOptions(field, 'source')
     }
     }
-    wizardStore.setPermalink(wizardStore.data)
+    wizardStore.updateUrlState()
   }
   }
 
 
   handleNetworkChange(sourceNic: Nic, targetNetwork: Network, targetSecurityGroups: ?SecurityGroup[]) {
   handleNetworkChange(sourceNic: Nic, targetNetwork: Network, targetSecurityGroups: ?SecurityGroup[]) {
     wizardStore.updateNetworks({ sourceNic, targetNetwork, targetSecurityGroups })
     wizardStore.updateNetworks({ sourceNic, targetNetwork, targetSecurityGroups })
-    wizardStore.setPermalink(wizardStore.data)
+    wizardStore.updateUrlState()
   }
   }
 
 
   handleStorageChange(source: Disk, target: StorageBackend, type: 'backend' | 'disk') {
   handleStorageChange(source: Disk, target: StorageBackend, type: 'backend' | 'disk') {
     wizardStore.updateStorage({ source, target, type })
     wizardStore.updateStorage({ source, target, type })
+    wizardStore.updateUrlState()
   }
   }
 
 
   handleAddScheduleClick(schedule: Schedule) {
   handleAddScheduleClick(schedule: Schedule) {
     wizardStore.addSchedule(schedule)
     wizardStore.addSchedule(schedule)
+    wizardStore.updateUrlState()
   }
   }
 
 
   handleScheduleChange(scheduleId: string, data: Schedule) {
   handleScheduleChange(scheduleId: string, data: Schedule) {
     wizardStore.updateSchedule(scheduleId, data)
     wizardStore.updateSchedule(scheduleId, data)
+    wizardStore.updateUrlState()
   }
   }
 
 
   handleScheduleRemove(scheduleId: string) {
   handleScheduleRemove(scheduleId: string) {
     wizardStore.removeSchedule(scheduleId)
     wizardStore.removeSchedule(scheduleId)
+    wizardStore.updateUrlState()
   }
   }
 
 
   async handleReloadOptionsClick() {
   async handleReloadOptionsClick() {
@@ -364,9 +368,9 @@ class WizardPage extends React.Component<Props, State> {
     await this.loadExtraOptions(undefined, optionsType, false)
     await this.loadExtraOptions(undefined, optionsType, false)
   }
   }
 
 
-  initializeState() {
-    wizardStore.getDataFromPermalink()
-    let type = this.props.match && this.props.match.params.type
+  initializeState(match: any) {
+    wizardStore.getUrlState()
+    let type = match && match.params && match.params.type
     if (type === 'migration' || type === 'replica') {
     if (type === 'migration' || type === 'replica') {
       this.setState({ type })
       this.setState({ type })
     }
     }
@@ -409,16 +413,13 @@ class WizardPage extends React.Component<Props, State> {
         useCache: true,
         useCache: true,
       })
       })
 
 
-      // Preload source options if data is set from 'Permalink'
-      if (providerStore.sourceOptions.length === 0) {
-        await providerStore.getOptionsValues({
-          optionsType,
-          endpointId: endpoint.id,
-          providerName: endpoint.type,
-          useCache: true,
-        })
-        await this.loadExtraOptions(undefined, optionsType)
-      }
+      await providerStore.getOptionsValues({
+        optionsType,
+        endpointId: endpoint.id,
+        providerName: endpoint.type,
+        useCache: true,
+      })
+      await this.loadExtraOptions(undefined, optionsType)
     }
     }
 
 
     switch (page.id) {
     switch (page.id) {

+ 12 - 7
src/sources/WizardSource.js

@@ -73,19 +73,24 @@ class WizardSource {
     return mainItems.filter(Boolean).map(i => i)
     return mainItems.filter(Boolean).map(i => i)
   }
   }
 
 
-  setPermalink(data: WizardData) {
-    // window.history.replaceState({}, null, `${window.location.href}?d=${btoa(JSON.stringify(data))}`)
-    let exp = /.*?(?:\?|$)/.exec(window.location.href)
-    if (!exp) {
+  setUrlState(data: any) {
+    let locationExp = /.*?(?:\?|$)/.exec(window.location.href)
+    if (!locationExp) {
       return
       return
     }
     }
-    let location = exp[0].replace('?', '')
+    let location = locationExp[0].replace('?', '')
     window.history.replaceState({}, null, `${location}?d=${btoa(JSON.stringify(data))}`)
     window.history.replaceState({}, null, `${location}?d=${btoa(JSON.stringify(data))}`)
   }
   }
 
 
-  getDataFromPermalink() {
+  getUrlState() {
     let dataExpExec = /\?d=(.*)/.exec(window.location.href)
     let dataExpExec = /\?d=(.*)/.exec(window.location.href)
-    return dataExpExec && JSON.parse(atob(dataExpExec[1]))
+    let result = null
+    try {
+      result = dataExpExec && JSON.parse(atob(dataExpExec[1]))
+    } catch (err) {
+      console.error(err)
+    }
+    return result
   }
   }
 }
 }
 
 

+ 11 - 13
src/stores/WizardStore.js

@@ -24,7 +24,7 @@ import type { NetworkMap } from '../types/Network'
 import type { StorageMap } from '../types/Endpoint'
 import type { StorageMap } from '../types/Endpoint'
 import type { Schedule } from '../types/Schedule'
 import type { Schedule } from '../types/Schedule'
 import { wizardPages } from '../constants'
 import { wizardPages } from '../constants'
-import Source from '../sources/WizardSource'
+import source from '../sources/WizardSource'
 
 
 const updateOptions = (oldOptions: ?{ [string]: mixed }, data: { field: Field, value: any }) => {
 const updateOptions = (oldOptions: ?{ [string]: mixed }, data: { field: Field, value: any }) => {
   let options = { ...oldOptions }
   let options = { ...oldOptions }
@@ -141,7 +141,7 @@ class WizardStore {
     this.creatingItem = true
     this.creatingItem = true
 
 
     try {
     try {
-      let item: MainItem = await Source.create(type, data, storageMap)
+      let item: MainItem = await source.create(type, data, storageMap)
       runInAction(() => { this.createdItem = item })
       runInAction(() => { this.createdItem = item })
     } catch (err) {
     } catch (err) {
       throw err
       throw err
@@ -154,27 +154,25 @@ class WizardStore {
     this.creatingItems = true
     this.creatingItems = true
 
 
     try {
     try {
-      let items: MainItem[] = await Source.createMultiple(type, data, storageMap)
+      let items: MainItem[] = await source.createMultiple(type, data, storageMap)
       runInAction(() => { this.createdItems = items })
       runInAction(() => { this.createdItems = items })
     } finally {
     } finally {
       runInAction(() => { this.creatingItems = false })
       runInAction(() => { this.creatingItems = false })
     }
     }
   }
   }
 
 
-  setPermalink(data: WizardData) {
-    Source.setPermalink(data)
+  updateUrlState() {
+    source.setUrlState({ data: this.data, schedules: this.schedules, storageMap: this.storageMap })
   }
   }
 
 
-  @action getDataFromPermalink() {
-    let data = Source.getDataFromPermalink()
-    if (!data) {
+  @action getUrlState() {
+    let state = source.getUrlState()
+    if (!state) {
       return
       return
     }
     }
-
-    this.data = {
-      ...this.data,
-      ...data,
-    }
+    this.data = state.data
+    this.schedules = state.schedules
+    this.storageMap = state.storageMap
   }
   }
 }
 }