Merge branch 'pkg_mgmt' into v1.1
[osm/UI.git] / skyquake / plugins / composer / src / src / stores / ComposerAppStore.js
1
2 /*
3 *
4 * Copyright 2016 RIFT.IO Inc
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19 'use strict';
20
21 import _ from 'lodash'
22 import d3 from 'd3'
23 import alt from '../alt'
24 import UID from '../libraries/UniqueId'
25 import DescriptorModelFactory from '../libraries/model/DescriptorModelFactory'
26 import PanelResizeAction from '../actions/PanelResizeAction'
27 import CatalogItemsActions from '../actions/CatalogItemsActions'
28 import CanvasEditorActions from '../actions/CanvasEditorActions'
29 import ComposerAppActions from '../actions/ComposerAppActions'
30 import CatalogFilterActions from '../actions/CatalogFilterActions'
31 import CanvasPanelTrayActions from '../actions/CanvasPanelTrayActions'
32 import SelectionManager from '../libraries/SelectionManager'
33 import CatalogDataStore from '../stores/CatalogDataStore'
34 import isFullScreen from '../libraries/isFullScreen'
35
36 import FileManagerSource from '../components/filemanager/FileManagerSource';
37 import FileManagerActions from '../components/filemanager/FileManagerActions';
38
39 import React from 'react';
40
41 //Hack for crouton fix. Should eventually put composer in skyquake alt context
42 import SkyquakeComponent from 'widgets/skyquake_container/skyquakeComponent.jsx';
43 let NotificationError = null;
44
45 import utils from '../libraries/utils';
46
47 class ComponentBridge extends React.Component {
48 constructor(props) {
49 super(props);
50 NotificationError = this.props.flux.actions.global.showNotification;
51 }
52 render(){
53 return <i></i>
54 }
55 }
56 const getDefault = (name, defaultValue) => {
57 const val = window.localStorage.getItem('defaults-' + name);
58 if (val) {
59 if (_.isNumber(val)) {
60 if (val < 0) {
61 return setDefault(name, 0);
62 }
63 }
64 return Number(val);
65 }
66 setDefault(name, defaultValue);
67 return defaultValue;
68 };
69
70 const setDefault = (name, defaultValue) => {
71 window.localStorage.setItem('defaults-' + name, defaultValue);
72 return defaultValue;
73 };
74
75 /* the top and bottom positions are managed by css; requires div to be display: absolute*/
76 const defaults = {
77 left: getDefault('catalog-panel-start-width', 300),
78 right: getDefault('details-panel-start-width', 365),
79 bottom: 25 + getDefault('defaults-forwarding-graphs-panel-start-height', 0),
80 showMore: false,
81 zoom: getDefault('zoom', 100),
82 filterCatalogBy: 'nsd',
83 defaultPanelTrayOpenZoom: (() => {
84 let zoom = parseFloat(getDefault('panel-tray-zoom', 75));
85 if (isNaN(zoom)) {
86 zoom = 75;
87 }
88 zoom = Math.min(100, zoom);
89 zoom = Math.max(25, zoom);
90 setDefault('panel-tray-zoom', zoom);
91 return zoom;
92 })()
93 };
94
95 const autoZoomCanvasScale = d3.scale.linear().domain([0, 300]).range([100, 50]).clamp(true);
96
97 const uiTransientState = {};
98
99 class ComposerAppStore {
100
101 constructor() {
102 //Bridge for crouton fix
103 this.ComponentBridgeElement = SkyquakeComponent(ComponentBridge);
104
105 this.exportAsync(FileManagerSource)
106 // the catalog item currently being edited in the composer
107 this.item = null;
108 // the left and right sides of the canvas area
109 this.layout = {
110 left: defaults.left,
111 right: defaults.right,
112 bottom: defaults.bottom
113 };
114 uiTransientState.restoreLayout = this.layout;
115 this.zoom = defaults.zoom;
116 this.showMore = defaults.showMore;
117 this.filterCatalogByTypeValue = defaults.filterCatalogBy;
118 // transient ui state
119 this.drag = null;
120 this.message = '';
121 this.messageType = '';
122 this.showJSONViewer = false;
123 this.showClassifiers = {};
124 this.editPathsMode = false;
125 this.fullScreenMode = false;
126 this.panelTabShown = 'descriptor';
127 //File manager values
128 this.files = false;
129 this.filesState = {};
130 this.downloadJobs = {};
131 this.containers = [];
132 this.newPathName = '';
133 //End File manager values
134 this.bindListeners({
135 onResize: PanelResizeAction.RESIZE,
136 editCatalogItem: CatalogItemsActions.EDIT_CATALOG_ITEM,
137 catalogItemMetaDataChanged: CatalogItemsActions.CATALOG_ITEM_META_DATA_CHANGED,
138 catalogItemDescriptorChanged: CatalogItemsActions.CATALOG_ITEM_DESCRIPTOR_CHANGED,
139 toggleShowMoreInfo: CanvasEditorActions.TOGGLE_SHOW_MORE_INFO,
140 showMoreInfo: CanvasEditorActions.SHOW_MORE_INFO,
141 showLessInfo: CanvasEditorActions.SHOW_LESS_INFO,
142 applyDefaultLayout: CanvasEditorActions.APPLY_DEFAULT_LAYOUT,
143 addVirtualLinkDescriptor: CanvasEditorActions.ADD_VIRTUAL_LINK_DESCRIPTOR,
144 addForwardingGraphDescriptor: CanvasEditorActions.ADD_FORWARDING_GRAPH_DESCRIPTOR,
145 addVirtualDeploymentDescriptor: CanvasEditorActions.ADD_VIRTUAL_DEPLOYMENT_DESCRIPTOR,
146 selectModel: ComposerAppActions.SELECT_MODEL,
147 outlineModel: ComposerAppActions.OUTLINE_MODEL,
148 showError: ComposerAppActions.SHOW_ERROR,
149 clearError: ComposerAppActions.CLEAR_ERROR,
150 setDragState: ComposerAppActions.SET_DRAG_STATE,
151 filterCatalogByType: CatalogFilterActions.FILTER_BY_TYPE,
152 setCanvasZoom: CanvasEditorActions.SET_CANVAS_ZOOM,
153 showJsonViewer: ComposerAppActions.SHOW_JSON_VIEWER,
154 closeJsonViewer: ComposerAppActions.CLOSE_JSON_VIEWER,
155 toggleCanvasPanelTray: CanvasPanelTrayActions.TOGGLE_OPEN_CLOSE,
156 openCanvasPanelTray: CanvasPanelTrayActions.OPEN,
157 closeCanvasPanelTray: CanvasPanelTrayActions.CLOSE,
158 enterFullScreenMode: ComposerAppActions.ENTER_FULL_SCREEN_MODE,
159 exitFullScreenMode: ComposerAppActions.EXIT_FULL_SCREEN_MODE,
160 showAssets: ComposerAppActions.showAssets,
161 showDescriptor: ComposerAppActions.showDescriptor,
162 getFilelistSuccess: FileManagerActions.getFilelistSuccess,
163 updateFileLocationInput: FileManagerActions.updateFileLocationInput,
164 sendDownloadFileRequst: FileManagerActions.sendDownloadFileRequst,
165 addFileSuccess: FileManagerActions.addFileSuccess,
166 deletePackageFile: FileManagerActions.deletePackageFile,
167 deleteFileSuccess: FileManagerActions.deleteFileSuccess,
168 closeFileManagerSockets: FileManagerActions.closeFileManagerSockets,
169 openFileManagerSockets: FileManagerActions.openFileManagerSockets,
170 openDownloadMonitoringSocketSuccess: FileManagerActions.openDownloadMonitoringSocketSuccess,
171 getFilelistSocketSuccess: FileManagerActions.getFilelistSocketSuccess,
172 newPathNameUpdated: FileManagerActions.newPathNameUpdated,
173 createDirectory: FileManagerActions.createDirectory
174 });
175 this.exportPublicMethods({
176 closeFileManagerSockets: this.closeFileManagerSockets.bind(this)
177 })
178 }
179
180 onResize(e) {
181 if (e.type === 'resize-manager.resize.catalog-panel') {
182 const layout = Object.assign({}, this.layout);
183 layout.left = Math.max(0, layout.left - e.moved.x);
184 if (layout.left !== this.layout.left) {
185 this.setState({layout: layout});
186 }
187 } else if (e.type === 'resize-manager.resize.details-panel') {
188 const layout = Object.assign({}, this.layout);
189 layout.right = Math.max(0, layout.right + e.moved.x);
190 if (layout.right !== this.layout.right) {
191 this.setState({layout: layout});
192 }
193 } else if (/^resize-manager\.resize\.canvas-panel-tray/.test(e.type)) {
194 const layout = Object.assign({}, this.layout);
195 layout.bottom = Math.max(25, layout.bottom + e.moved.y);
196 if (layout.bottom !== this.layout.bottom) {
197 const zoom = autoZoomCanvasScale(layout.bottom) ;
198 if (this.zoom !== zoom) {
199 this.setState({layout: layout, zoom: zoom});
200 } else {
201 this.setState({layout: layout});
202 }
203 }
204 } else if (e.type !== 'resize') {
205 console.log('no resize handler for ', e.type, '. Do you need to add a handler in ComposerAppStore::onResize()?')
206 }
207 SelectionManager.refreshOutline();
208 }
209
210 updateItem(item) {
211 const self = this;
212 let containers = [];
213 let cpNumber = 0;
214 if(!document.body.classList.contains('resizing')) {
215 containers = [item].reduce(DescriptorModelFactory.buildCatalogItemFactory(CatalogDataStore.getState().catalogs), []);
216
217 containers.filter(d => DescriptorModelFactory.isConnectionPoint(d)).forEach(d => {
218 d.cpNumber = ++cpNumber;
219 containers.filter(d => DescriptorModelFactory.isVnfdConnectionPointRef(d)).filter(ref => ref.key === d.key).forEach(ref => ref.cpNumber = d.cpNumber);
220 });
221 this.setState({containers: containers, item: _.cloneDeep(item)});
222 }
223 SelectionManager.refreshOutline();
224 }
225
226 editCatalogItem(item) {
227 let self = this;
228 self.closeFileManagerSockets();
229 if (item && item.uiState) {
230 item.uiState.isOpenForEdit = true;
231 if (item.uiState.type !== 'nsd') {
232 this.closeCanvasPanelTray();
233 }
234 }
235 SelectionManager.select(item);
236 this.updateItem(item);
237 this.openFileManagerSockets(item)
238 }
239 catalogItemMetaDataChanged(item) {
240 this.updateItem(item);
241 }
242
243 catalogItemDescriptorChanged(itemDescriptor) {
244 this.catalogItemMetaDataChanged(itemDescriptor.model);
245 }
246
247 showMoreInfo() {
248 this.setState({showMore: true});
249 }
250
251 showLessInfo() {
252 this.setState({showMore: false});
253 }
254
255 showError(data) {
256 NotificationError.defer({msg: data.errorMessage, type: 'error'})
257 // this.setState({message: data.errorMessage, messageType: 'error'});
258 }
259
260 clearError() {
261 this.setState({message: '', messageType: ''});
262 }
263
264 toggleShowMoreInfo() {
265 this.setState({showMore: !this.showMore});
266 }
267
268 applyDefaultLayout() {
269 if (this.item && this.item.uiState && this.item.uiState.containerPositionMap) {
270 if (!_.isEmpty(this.item.uiState.containerPositionMap)) {
271 this.item.uiState.containerPositionMap = {};
272 CatalogItemsActions.catalogItemMetaDataChanged.defer(this.item);
273 }
274 }
275 }
276
277 addVirtualLinkDescriptor(dropCoordinates = null) {
278 let vld;
279 if (this.item) {
280 if (this.item.uiState.type === 'nsd') {
281 const nsdc = DescriptorModelFactory.newNetworkService(this.item);
282 vld = nsdc.createVld();
283 } else if (this.item.uiState.type === 'vnfd') {
284 const vnfd = DescriptorModelFactory.newVirtualNetworkFunction(this.item);
285 vld = vnfd.createVld();
286 }
287 if (vld) {
288 vld.uiState.dropCoordinates = dropCoordinates;
289 SelectionManager.clearSelectionAndRemoveOutline();
290 SelectionManager.addSelection(vld);
291 this.updateItem(vld.getRoot().model);
292 CatalogItemsActions.catalogItemDescriptorChanged.defer(vld.getRoot());
293 }
294 }
295 }
296
297 addForwardingGraphDescriptor(dropCoordinates = null) {
298 if (this.item && this.item.uiState.type === 'nsd') {
299 const nsdc = DescriptorModelFactory.newNetworkService(this.item);
300 const fg = nsdc.createVnffgd();
301 fg.uiState.dropCoordinates = dropCoordinates;
302 SelectionManager.clearSelectionAndRemoveOutline();
303 SelectionManager.addSelection(fg);
304 this.updateItem(nsdc.model);
305 CatalogItemsActions.catalogItemDescriptorChanged.defer(nsdc);
306 }
307 }
308
309 addVirtualDeploymentDescriptor(dropCoordinates = null) {
310 if (this.item.uiState.type === 'vnfd') {
311 const vnfd = DescriptorModelFactory.newVirtualNetworkFunction(this.item);
312 const vdu = vnfd.createVdu();
313 vdu.uiState.dropCoordinates = dropCoordinates;
314 SelectionManager.clearSelectionAndRemoveOutline();
315 SelectionManager.addSelection(vdu);
316 this.updateItem(vdu.getRoot().model);
317 CatalogItemsActions.catalogItemDescriptorChanged.defer(vdu.getRoot());
318 }
319 }
320
321 selectModel(container) {
322 if (SelectionManager.select(container)) {
323 const model = DescriptorModelFactory.isContainer(container) ? container.getRoot().model : container;
324 this.catalogItemMetaDataChanged(model);
325 }
326 }
327
328 outlineModel(obj) {
329 const uid = UID.from(obj);
330 requestAnimationFrame(() => {
331 SelectionManager.outline(Array.from(document.querySelectorAll(`[data-uid="${uid}"]`)));
332 });
333 }
334
335 clearSelection() {
336 SelectionManager.clearSelectionAndRemoveOutline();
337 this.catalogItemMetaDataChanged(this.item);
338 }
339
340 setDragState(dragState) {
341 this.setState({drag: dragState});
342 }
343
344 filterCatalogByType(typeValue) {
345 this.setState({filterCatalogByTypeValue: typeValue})
346 }
347
348 setCanvasZoom(zoom) {
349 this.setState({zoom: zoom});
350 }
351
352 showJsonViewer() {
353 this.setState({showJSONViewer: true});
354 }
355
356 closeJsonViewer() {
357 this.setState({showJSONViewer: false});
358 }
359
360 toggleCanvasPanelTray() {
361 const layout = this.layout;
362 if (layout.bottom > 25) {
363 this.closeCanvasPanelTray();
364 } else {
365 this.openCanvasPanelTray();
366 }
367 }
368
369 openCanvasPanelTray() {
370 const layout = {
371 left: this.layout.left,
372 right: this.layout.right,
373 bottom: 300
374 };
375 const zoom = defaults.defaultPanelTrayOpenZoom;
376 if (this.zoom !== zoom) {
377 this.setState({layout: layout, zoom: zoom, restoreZoom: this.zoom});
378 } else {
379 this.setState({layout: layout});
380 }
381 }
382
383 closeCanvasPanelTray() {
384 const layout = {
385 left: this.layout.left,
386 right: this.layout.right,
387 bottom: 25
388 };
389 const zoom = this.restoreZoom || autoZoomCanvasScale(layout.bottom);
390 if (this.zoom !== zoom) {
391 this.setState({layout: layout, zoom: zoom, restoreZoom: null});
392 } else {
393 this.setState({layout: layout, restoreZoom: null});
394 }
395 }
396
397 enterFullScreenMode() {
398
399 /**
400 * https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API
401 * This is an experimental api but works our target browsers and ignored by others
402 */
403 const eventNames = ['fullscreenchange', 'mozfullscreenchange', 'webkitfullscreenchange', 'msfullscreenchange'];
404
405 const appRoot = document.body;//.getElementById('RIFT_wareLaunchpadComposerAppRoot');
406
407 const comp = this;
408
409 function onFullScreenChange() {
410
411 if (isFullScreen()) {
412 const layout = comp.layout;
413 const restoreLayout = _.cloneDeep(layout);
414 uiTransientState.restoreLayout = restoreLayout;
415 layout.left = 0;
416 layout.right = 0;
417 comp.setState({fullScreenMode: true, layout: layout, restoreLayout: restoreLayout});
418 } else {
419 comp.setState({fullScreenMode: false, layout: uiTransientState.restoreLayout});
420 }
421
422 }
423
424 if (this.fullScreenMode === false) {
425
426 if (appRoot.requestFullscreen) {
427 appRoot.requestFullscreen();
428 } else if (appRoot.msRequestFullscreen) {
429 appRoot.msRequestFullscreen();
430 } else if (appRoot.mozRequestFullScreen) {
431 appRoot.mozRequestFullScreen();
432 } else if (appRoot.webkitRequestFullscreen) {
433 appRoot.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
434 }
435
436 eventNames.map(name => {
437 document.removeEventListener(name, onFullScreenChange);
438 document.addEventListener(name, onFullScreenChange);
439 });
440
441 }
442
443 }
444
445 exitFullScreenMode() {
446
447 if (document.exitFullscreen) {
448 document.exitFullscreen();
449 } else if (document.msExitFullscreen) {
450 document.msExitFullscreen();
451 } else if (document.mozCancelFullScreen) {
452 document.mozCancelFullScreen();
453 } else if (document.webkitExitFullscreen) {
454 document.webkitExitFullscreen();
455 }
456
457 this.setState({fullScreenMode: false});
458
459 }
460 showAssets() {
461 this.setState({
462 panelTabShown: 'assets'
463 });
464 }
465 showDescriptor() {
466 this.setState({
467 panelTabShown: 'descriptor'
468 });
469 }
470
471 //File Manager methods
472 getFilelistSuccess(data) {
473 let self = this;
474 let filesState = null;
475 if (self.fileMonitoringSocketID) {
476 let newState = {};
477 if(data.hasOwnProperty('contents')) {
478 filesState = addInputState( _.cloneDeep(this.filesState),data);
479 // filesState = _.merge(self.filesState, addInputState({},data));
480 let normalizedData = normalizeTree(data);
481 newState = {
482 files: {
483 data: _.mergeWith(normalizedData.data, self.files.data, function(obj, src) {
484 return _.uniqBy(obj? obj.concat(src) : src, 'name');
485 }),
486 id: normalizedData.id
487 },
488 filesState: filesState
489 }
490 } else {
491 newState = {
492 files: false
493 }
494 }
495
496 this.setState(newState);
497 }
498 function normalizeTree(data) {
499 let f = {
500 id:[],
501 data:{}
502 };
503 data.contents.map(getContents);
504 function getContents(d) {
505 if(d.hasOwnProperty('contents')) {
506 let contents = [];
507 d.contents.map(function(c,i) {
508 if (!c.hasOwnProperty('contents')) {
509 contents.push(c);
510 } else {
511 getContents(c);
512 }
513 })
514 f.id.push(d.name);
515 f.data[d.name] = contents;
516 }
517 }
518 return f;
519 }
520 function addInputState(obj, d) {
521 d.newFile = '';
522 if(d.hasOwnProperty('contents')) {
523 d.contents.map(addInputState.bind(null, obj))
524 }
525 if(!obj[d.name]) {
526 obj[d.name] = '';
527 }
528 return obj;
529 }
530 }
531 sendDownloadFileRequst(data) {
532 let id = data.id || this.item.id;
533 let type = data.type || this.item.uiState.type;
534 let path = data.path;
535 let url = data.url;
536 this.getInstance().addFile(id, type, path, url, data.refresh);
537 }
538 updateFileLocationInput = (data) => {
539 let name = data.name;
540 let value = data.value;
541 var filesState = _.cloneDeep(this.filesState);
542 filesState[name] = value;
543 this.setState({
544 filesState: filesState
545 });
546 }
547 addFileSuccess = (data) => {
548 if(!data.refresh) {
549 let path = data.path;
550 let fileName = data.fileName;
551 let files = _.cloneDeep(this.files);
552 let loadingIndex = files.data[path].push({
553 status: 'DOWNLOADING',
554 name: path + '/' + fileName
555 }) - 1;
556 this.setState({files: files});
557 }
558
559 }
560 startWatchingJob = () => {
561 let ws = window.multiplexer.channel(this.jobSocketId);
562 this.setState({
563 jobSocket:null
564 })
565 }
566 openDownloadMonitoringSocketSuccess = (id) => {
567 let self = this;
568 let ws = window.multiplexer.channel(id);
569 let downloadJobs = _.cloneDeep(self.downloadJobs);
570 let newFiles = false;
571 ws.onmessage = (socket) => {
572 if (self.files && self.files.length > 0) {
573 let jobs = [];
574 try {
575 jobs = JSON.parse(socket.data);
576 } catch(e) {}
577 newFiles = _.cloneDeep(self.files);
578 jobs.map(function(j) {
579 //check if not in completed state
580 let fullPath = j['package-path'];
581 let path = fullPath.split('/');
582 let fileName = path.pop();
583 path = path.join('/');
584 let index = _.findIndex(self.files.data[path], function(o){
585 return fullPath == o.name
586 });
587 if((index > -1) && newFiles.data[path][index]) {
588 newFiles.data[path][index].status = j.status
589 } else {
590 if(j.status.toUpperCase() == 'LOADING...' || j.status.toUpperCase() == 'IN_PROGRESS') {
591 newFiles.data[path].push({
592 status: j.status,
593 name: fullPath
594 })
595 } else {
596 // if ()
597 }
598 }
599 })
600 self.setState({
601 files: newFiles
602 })
603 // console.log(JSON.parse(socket.data));
604 }
605 }
606 this.setState({
607 jobSocketId: id,
608 jobSocket: ws
609 })
610
611 }
612 getFilelistSocketSuccess = (id) => {
613 let self = this;
614 let ws = window.multiplexer.channel(id);
615 ws.onmessage = (socket) => {
616 if (self.fileMonitoringSocketID) {
617 let data = [];
618 try {
619 data = JSON.parse(socket.data);
620 } catch(e) {}
621 self.getFilelistSuccess(data)
622 }
623 }
624
625 this.setState({
626 fileMonitoringSocketID: id,
627 fileMonitoringSocket: ws
628 })
629
630 }
631 closeFileManagerSockets() {
632 this.fileMonitoringSocketID = null;
633 this.setState({
634 jobSocketId : null,
635 fileMonitoringSocketID : null
636 // jobSocket : null,
637 // fileMonitoringSocket : null,
638 });
639 this.jobSocket && this.jobSocket.close();
640 this.fileMonitoringSocket && this.fileMonitoringSocket.close();
641 console.log('closing');
642 }
643 openFileManagerSockets(i) {
644 let self = this;
645 let item = i || self.item;
646 // this.closeFileManagerSockets();
647 this.getInstance().openFileMonitoringSocket(item.id, item.uiState.type).then(function() {
648 // // self.getInstance().openDownloadMonitoringSocket(item.id);
649 });
650 this.getInstance().openDownloadMonitoringSocket(item.id);
651 }
652 endWatchingJob(id) {
653
654 }
655 deletePackageFile(name) {
656 let id = this.item.id;
657 let type = this.item.uiState.type;
658 this.getInstance().deleteFile(id, type, name);
659 }
660 deleteFileSuccess = (data) => {
661 let path = data.path.split('/')
662 let files = _.cloneDeep(this.files);
663 path.pop();
664 path = path.join('/');
665 let pathFiles = files.data[path]
666 _.remove(pathFiles, function(c) {
667 return c.name == data.path;
668 });
669
670 this.setState({
671 files: files
672 })
673 }
674 newPathNameUpdated = (event) => {
675 const value = event.target.value;
676 this.setState({
677 newPathName: value
678 })
679 }
680 createDirectory = () => {
681 console.log(this.newPathName);
682 this.sendDownloadFileRequst({
683 id: this.item.id,
684 type: this.item.uiState.type,
685 path: this.item.name + '/' + this.newPathName,
686 url: utils.getSearchParams(window.location).dev_download_server || window.location.protocol + '//' + window.location.host,
687 refresh: true
688 });
689 this.setState({
690 newPathName: ''
691 })
692 }
693 }
694
695 export default alt.createStore(ComposerAppStore, 'ComposerAppStore');