Add network call as promise for ext views
[aai/sparky-fe.git] / src / app / MainScreenHeader.jsx
1 /*
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017-2018 Amdocs
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *       http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21 import React, {Component} from 'react';
22 import {connect} from 'react-redux';
23 import FontAwesome from 'react-fontawesome';
24 import {clearFilters} from 'filter-bar-utils';
25 import Button from 'react-bootstrap/lib/Button.js';
26 import Modal from 'react-bootstrap/lib/Modal.js';
27 import GlobalAutoCompleteSearchBar from 'app/globalAutoCompleteSearchBar/GlobalAutoCompleteSearchBar.jsx';
28 import {postAnalyticsData} from 'app/analytics/AnalyticsActions.js';
29 import GlobalInlineMessageBar from 'app/globalInlineMessageBar/GlobalInlineMessageBar.jsx';
30 import {getClearGlobalMessageEvent} from 'app/globalInlineMessageBar/GlobalInlineMessageBarActions.js';
31 import {externalUrlRequest, externalMessageRequest, getSubscriptionPayload} from 'app/contextHandler/ContextHandlerActions.js';
32 import {
33   filterBarActionTypes
34 } from 'utils/GlobalConstants.js';
35
36 import {
37   Route,
38   NavLink
39 } from 'react-router-dom';
40
41 import {
42   AAI_TITLE,
43   MENU_ITEM_TIER_SUPPORT,
44   MENU_ITEM_VNF_SEARCH
45 } from './MainScreenWrapperConstants.js';
46
47 import {
48   showMainMenu,
49   clearExtensibleViewData,
50   setSecondaryTitle
51 } from './MainScreenWrapperActionHelper.js';
52
53 import {clearSuggestionsTextField} from 'app/globalAutoCompleteSearchBar/GlobalAutoCompleteSearchBarActions.js';
54 import {changeUrlAddress} from 'utils/Routes.js';
55 import extensibleViews from 'resources/views/extensibleViews.json';
56
57
58 const mapStateToProps = ({mainWrapper}) => {
59   let {
60     showMenu = false,
61     toggleButtonActive = false,
62     externalRequestFound = {},
63     secondaryTitle = '',
64     subscriptionPayload = {},
65     subscriptionEnabled = false
66   } = mainWrapper;
67
68   return {
69     showMenu,
70     toggleButtonActive,
71     externalRequestFound,
72     secondaryTitle,
73     subscriptionPayload,
74     subscriptionEnabled
75   };
76 };
77
78
79 const mapActionsToProps = (dispatch) => {
80   return {
81     onShowMenu: () => dispatch(showMainMenu(true)),
82     onHideMenu: () => {
83       dispatch(showMainMenu(false));
84     },
85     dispatchAnalyticsData: () => dispatch(
86       postAnalyticsData(document.documentElement.outerHTML.replace('\s+', ''))),
87     onRouteChange: () => {
88       dispatch(getClearGlobalMessageEvent());
89       dispatch(clearSuggestionsTextField());
90       dispatch(clearExtensibleViewData());
91       dispatch(clearFilters(filterBarActionTypes.CLEAR_FILTERS));
92       dispatch(setSecondaryTitle(undefined));
93     },
94     onExternalUrlRequest: (urlParamString) => {
95       dispatch(externalUrlRequest(urlParamString));
96     },
97     onExternalMessageRecieved: (messageJson) => {
98       dispatch(externalMessageRequest(messageJson));
99     },
100     onGetSubscriptionPayload: () => {
101       dispatch(getSubscriptionPayload());
102     }
103   };
104 };
105
106 class MainScreenHeader extends Component {
107   static propTypes = {
108     showMenu: React.PropTypes.bool,
109     toggleButtonActive: React.PropTypes.bool,
110     externalRequestFound: React.PropTypes.object,
111     secondaryTitle: React.PropTypes.string,
112     subscriptionPayload: React.PropTypes.object
113   };
114
115   navigationLinkAndCurrentPathMatch(location, to) {
116     let linkPathElements = to.split('/');
117     let locationElements = location.pathname.split('/');
118
119     // the element arrays above will have the route at index 1 ... need to
120     // verify if the routes match
121     return locationElements[1] === linkPathElements[1];
122   }
123
124   hasRouteChanged(currentPath, nextPath) {
125     let currentPathParts = currentPath.split('/');
126     let nextPathParts = nextPath.split('/');
127
128     if (currentPathParts[1] !== nextPathParts[1]) {
129       return true;
130     } else {
131       return false;
132     }
133   }
134   
135   isValidExternalURL(url) {
136     if(decodeURIComponent(url).indexOf('&') > 0 ) {
137       return true;
138     } else {
139       return false;
140     }
141   }
142
143   componentWillMount() {
144     this.props.onGetSubscriptionPayload();
145     if(this.props.match.params.externalUrl !== undefined &&
146       this.isValidExternalURL(this.props.match.params.externalUrl)) {
147       this.props.onExternalUrlRequest(this.props.match.params.externalUrl);
148     }
149   }
150
151   componentWillReceiveProps(nextProps) {
152     if (this.props.location &&
153       this.props.location.pathname !==
154       nextProps.location.pathname) {
155       // update analytics
156       this.props.dispatchAnalyticsData();
157
158       if (this.hasRouteChanged(this.props.location.pathname,
159           nextProps.location.pathname)) {
160         this.props.onRouteChange();
161       }
162     }
163
164     if(nextProps.match.params.externalUrl !== undefined &&
165       nextProps.match.params.externalUrl !== this.props.match.params.externalUrl &&
166       this.isValidExternalURL(nextProps.match.params.externalUrl)) {
167       this.props.onExternalUrlRequest(nextProps.match.params.externalUrl);
168     }
169     /* if the externalURL is not valid, we do not add any message as other proper
170     views will get that messages since the route will be this parameter.*/
171
172     if(this.props.externalRequestFound !== nextProps.externalRequestFound &&
173       nextProps.externalRequestFound !== undefined && nextProps.externalRequestFound.suggestion !== undefined) {
174       changeUrlAddress(nextProps.externalRequestFound.suggestion, nextProps.history);
175     }
176
177     if (nextProps.subscriptionEnabled) {
178       if (nextProps.subscriptionPayload !== this.props.subscriptionPayload &&
179         Object.keys(nextProps.subscriptionPayload).length > 0) {
180         var getWindowUrl = function (url) {
181           var split = url.split('/');
182           return split[0] + '//' + split[2];
183         };
184         window.parent.postMessage(
185           JSON.stringify(nextProps.subscriptionPayload),
186           getWindowUrl(document.referrer));
187       }
188     }
189   }
190
191   receiveMessage(event, $this) {
192     function isJson(str) {
193       try {
194         JSON.parse(str);
195       } catch (e) {
196         return false;
197       }
198       return true;
199     }
200     if(isJson(event.data)) {
201       let messageData = JSON.parse(event.data);
202       if(isJson(messageData.message)) {
203         $this.props.onExternalMessageRecieved(messageData.message);
204       }
205     }
206
207   }
208   componentDidMount() {
209     //TODO Move this logic to the component will receive props.
210     //Check if the event lister is available and if the subscription is
211     // enabled before registering for it
212     if(document.referrer) {
213       var $this = this;
214       window.addEventListener('message', function (e) {
215         $this.receiveMessage(e, $this);
216       }, false);
217     }
218   }
219   componentWillUnmount() {
220     if(this.props.subscriptionEnabled) {
221       var $this = this;
222       window.removeEventListener('message', function (e) {
223         $this.receiveMessage(e, $this);
224       }
225       );
226     }
227   }
228
229   render() {
230     let {
231       showMenu,
232       onShowMenu,
233       onHideMenu,
234       toggleButtonActive,
235       secondaryTitle
236     } = this.props;
237
238     let menuOptions = [];
239
240     const MenuItem = ({label, iconClass, to}) => (
241       <Route path={to} children={({location}) => (
242         <NavLink to={to} onClick={onHideMenu}>
243           <div className={this.navigationLinkAndCurrentPathMatch(location, to) ? 'main-menu-button-active' : 'main-menu-button'}>
244             <div className={iconClass}/>
245             <div className='button-icon'>{label}</div>
246           </div>
247         </NavLink>
248       )}/>
249     );
250
251     // add Tier Support view
252     menuOptions.push(
253       <MenuItem key='schemaMenu' to='/schema' label={MENU_ITEM_TIER_SUPPORT}
254                 iconClass='button-icon view-inspect-button-icon'/>
255     );
256
257     // add VNF view
258     menuOptions.push(
259       <MenuItem key='vnfSearchMenu'
260         to='/vnfSearch'
261         label={MENU_ITEM_VNF_SEARCH}
262         iconClass='button-icon vnf-search-button-icon'/>
263     );
264
265     // add all custom view menu options
266     for (let view in extensibleViews) {
267       let shouldDisplayIcon = false;
268       if(extensibleViews[view]['onlyRoute'] === undefined){
269         shouldDisplayIcon = true;
270       } else if(extensibleViews[view]['onlyRoute'] === false){
271         shouldDisplayIcon = true;
272       }
273       if(shouldDisplayIcon === true){
274         menuOptions.push(
275           <MenuItem key={extensibleViews[view]['viewName'] + 'Menu'} to={'/' + extensibleViews[view]['viewName']}
276                     label={extensibleViews[view]['displayName']}
277                     iconClass={'button-icon ' + extensibleViews[view]['iconClass']}/>
278         );
279       } 
280     }
281
282     let secondaryTitleClass = 'secondary-header';
283     if (secondaryTitle === undefined || secondaryTitle === '') {
284       secondaryTitleClass = secondaryTitleClass + ' hidden';
285     }
286
287     return (
288       <div className='header'>
289         <div>
290           <Button
291             bsClass={(toggleButtonActive)
292             ? 'toggle-view-button-active'
293             : 'toggle-view-button'}
294             onClick={onShowMenu}>
295             <FontAwesome name='bars'/>
296           </Button>
297           <Modal show={showMenu} onHide={onHideMenu}
298                  dialogClassName='modal-main-menu'>
299             <Modal.Body>
300               {menuOptions}
301             </Modal.Body>
302           </Modal>
303           <span className='application-title'>{AAI_TITLE}</span>
304           <GlobalAutoCompleteSearchBar history={this.props.history}/>
305         </div>
306         <GlobalInlineMessageBar />
307         <div className={secondaryTitleClass}>
308           <span className='secondary-title'>{secondaryTitle}</span>
309         </div>
310       </div>
311     );
312   }
313 }
314
315 export default connect(mapStateToProps, mapActionsToProps)(MainScreenHeader);