|  | // Copyright (C) 2018 The Android Open Source Project | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | import m from 'mithril'; | 
|  |  | 
|  | import {classNames} from '../base/classnames'; | 
|  | import {raf} from '../core/raf_scheduler'; | 
|  | import {VERSION} from '../gen/perfetto_version'; | 
|  |  | 
|  | import {globals} from './globals'; | 
|  | import {taskTracker} from './task_tracker'; | 
|  |  | 
|  | export const DISMISSED_PANNING_HINT_KEY = 'dismissedPanningHint'; | 
|  |  | 
|  | class Progress implements m.ClassComponent { | 
|  | view(_vnode: m.Vnode): m.Children { | 
|  | const classes = classNames(this.isLoading() && 'progress-anim'); | 
|  | return m('.progress', {class: classes}); | 
|  | } | 
|  |  | 
|  | private isLoading(): boolean { | 
|  | const engine = globals.getCurrentEngine(); | 
|  | return ( | 
|  | (engine && !engine.ready) || | 
|  | globals.numQueuedQueries > 0 || | 
|  | taskTracker.hasPendingTasks() | 
|  | ); | 
|  | } | 
|  | } | 
|  |  | 
|  | class NewVersionNotification implements m.ClassComponent { | 
|  | view() { | 
|  | return m( | 
|  | '.new-version-toast', | 
|  | `Updated to ${VERSION} and ready for offline use!`, | 
|  | m( | 
|  | 'button.notification-btn.preferred', | 
|  | { | 
|  | onclick: () => { | 
|  | globals.newVersionAvailable = false; | 
|  | raf.scheduleFullRedraw(); | 
|  | }, | 
|  | }, | 
|  | 'Dismiss', | 
|  | ), | 
|  | ); | 
|  | } | 
|  | } | 
|  |  | 
|  | class HelpPanningNotification implements m.ClassComponent { | 
|  | view() { | 
|  | const dismissed = localStorage.getItem(DISMISSED_PANNING_HINT_KEY); | 
|  | // Do not show the help notification in embedded mode because local storage | 
|  | // does not persist for iFrames. The host is responsible for communicating | 
|  | // to users that they can press '?' for help. | 
|  | if ( | 
|  | globals.embeddedMode || | 
|  | dismissed === 'true' || | 
|  | !globals.showPanningHint | 
|  | ) { | 
|  | return; | 
|  | } | 
|  | return m( | 
|  | '.helpful-hint', | 
|  | m( | 
|  | '.hint-text', | 
|  | 'Are you trying to pan? Use the WASD keys or hold shift to click ' + | 
|  | "and drag. Press '?' for more help.", | 
|  | ), | 
|  | m( | 
|  | 'button.hint-dismiss-button', | 
|  | { | 
|  | onclick: () => { | 
|  | globals.showPanningHint = false; | 
|  | localStorage.setItem(DISMISSED_PANNING_HINT_KEY, 'true'); | 
|  | raf.scheduleFullRedraw(); | 
|  | }, | 
|  | }, | 
|  | 'Dismiss', | 
|  | ), | 
|  | ); | 
|  | } | 
|  | } | 
|  |  | 
|  | class TraceErrorIcon implements m.ClassComponent { | 
|  | view() { | 
|  | if (globals.embeddedMode) return; | 
|  |  | 
|  | const mode = globals.state.omniboxState.mode; | 
|  |  | 
|  | const errors = globals.traceErrors; | 
|  | if ((!Boolean(errors) && !globals.metricError) || mode === 'COMMAND') { | 
|  | return; | 
|  | } | 
|  | const message = Boolean(errors) | 
|  | ? `${errors} import or data loss errors detected.` | 
|  | : `Metric error detected.`; | 
|  | return m( | 
|  | 'a.error', | 
|  | {href: '#!/info'}, | 
|  | m( | 
|  | 'i.material-icons', | 
|  | { | 
|  | title: message + ` Click for more info.`, | 
|  | }, | 
|  | 'announcement', | 
|  | ), | 
|  | ); | 
|  | } | 
|  | } | 
|  |  | 
|  | export interface TopbarAttrs { | 
|  | omnibox: m.Children; | 
|  | } | 
|  |  | 
|  | export class Topbar implements m.ClassComponent<TopbarAttrs> { | 
|  | view({attrs}: m.Vnode<TopbarAttrs>) { | 
|  | const {omnibox} = attrs; | 
|  | return m( | 
|  | '.topbar', | 
|  | {class: globals.state.sidebarVisible ? '' : 'hide-sidebar'}, | 
|  | globals.newVersionAvailable ? m(NewVersionNotification) : omnibox, | 
|  | m(Progress), | 
|  | m(HelpPanningNotification), | 
|  | m(TraceErrorIcon), | 
|  | ); | 
|  | } | 
|  | } |