xref: /aosp_15_r20/external/perfetto/ui/src/frontend/topbar.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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