1// Copyright (C) 2018 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import m from 'mithril'; 16import {classNames} from '../base/classnames'; 17import {taskTracker} from './task_tracker'; 18import {Popup, PopupPosition} from '../widgets/popup'; 19import {assertFalse} from '../base/logging'; 20import {OmniboxMode} from '../core/omnibox_manager'; 21import {AppImpl} from '../core/app_impl'; 22import {TraceImpl, TraceImplAttrs} from '../core/trace_impl'; 23 24class Progress implements m.ClassComponent<TraceImplAttrs> { 25 view({attrs}: m.CVnode<TraceImplAttrs>): m.Children { 26 const engine = attrs.trace.engine; 27 const isLoading = 28 AppImpl.instance.isLoadingTrace || 29 engine.numRequestsPending > 0 || 30 taskTracker.hasPendingTasks(); 31 const classes = classNames(isLoading && 'progress-anim'); 32 return m('.progress', {class: classes}); 33 } 34} 35 36class TraceErrorIcon implements m.ClassComponent<TraceImplAttrs> { 37 private tracePopupErrorDismissed = false; 38 39 view({attrs}: m.CVnode<TraceImplAttrs>) { 40 const trace = attrs.trace; 41 if (AppImpl.instance.embeddedMode) return; 42 43 const mode = AppImpl.instance.omnibox.mode; 44 const totErrors = trace.traceInfo.importErrors + trace.loadingErrors.length; 45 if (totErrors === 0 || mode === OmniboxMode.Command) { 46 return; 47 } 48 const message = Boolean(totErrors) 49 ? `${totErrors} import or data loss errors detected.` 50 : `Metric error detected.`; 51 return m( 52 '.error-box', 53 m( 54 Popup, 55 { 56 trigger: m('.popup-trigger'), 57 isOpen: !this.tracePopupErrorDismissed, 58 position: PopupPosition.Left, 59 onChange: (shouldOpen: boolean) => { 60 assertFalse(shouldOpen); 61 this.tracePopupErrorDismissed = true; 62 }, 63 }, 64 m('.error-popup', 'Data-loss/import error. Click for more info.'), 65 ), 66 m( 67 'a.error', 68 {href: '#!/info'}, 69 m( 70 'i.material-icons', 71 { 72 title: message + ` Click for more info.`, 73 }, 74 'announcement', 75 ), 76 ), 77 ); 78 } 79} 80 81export interface TopbarAttrs { 82 omnibox: m.Children; 83 trace?: TraceImpl; 84} 85 86export class Topbar implements m.ClassComponent<TopbarAttrs> { 87 view({attrs}: m.Vnode<TopbarAttrs>) { 88 const {omnibox} = attrs; 89 return m( 90 '.topbar', 91 { 92 class: AppImpl.instance.sidebar.visible ? '' : 'hide-sidebar', 93 }, 94 omnibox, 95 attrs.trace && m(Progress, {trace: attrs.trace}), 96 attrs.trace && m(TraceErrorIcon, {trace: attrs.trace}), 97 ); 98 } 99} 100