• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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';
16
17import {classNames} from '../base/classnames';
18import {raf} from '../core/raf_scheduler';
19
20import {globals} from './globals';
21import {taskTracker} from './task_tracker';
22
23export const DISMISSED_PANNING_HINT_KEY = 'dismissedPanningHint';
24
25class Progress implements m.ClassComponent {
26  view(_vnode: m.Vnode): m.Children {
27    const classes = classNames(this.isLoading() && 'progress-anim');
28    return m('.progress', {class: classes});
29  }
30
31  private isLoading(): boolean {
32    const engine = globals.getCurrentEngine();
33    return (
34      (engine && !engine.ready) ||
35      globals.numQueuedQueries > 0 ||
36      taskTracker.hasPendingTasks()
37    );
38  }
39}
40
41class HelpPanningNotification implements m.ClassComponent {
42  view() {
43    const dismissed = localStorage.getItem(DISMISSED_PANNING_HINT_KEY);
44    // Do not show the help notification in embedded mode because local storage
45    // does not persist for iFrames. The host is responsible for communicating
46    // to users that they can press '?' for help.
47    if (
48      globals.embeddedMode ||
49      dismissed === 'true' ||
50      !globals.showPanningHint
51    ) {
52      return;
53    }
54    return m(
55      '.helpful-hint',
56      m(
57        '.hint-text',
58        'Are you trying to pan? Use the WASD keys or hold shift to click ' +
59          "and drag. Press '?' for more help.",
60      ),
61      m(
62        'button.hint-dismiss-button',
63        {
64          onclick: () => {
65            globals.showPanningHint = false;
66            localStorage.setItem(DISMISSED_PANNING_HINT_KEY, 'true');
67            raf.scheduleFullRedraw();
68          },
69        },
70        'Dismiss',
71      ),
72    );
73  }
74}
75
76class TraceErrorIcon implements m.ClassComponent {
77  view() {
78    if (globals.embeddedMode) return;
79
80    const mode = globals.state.omniboxState.mode;
81
82    const errors = globals.traceErrors;
83    if ((!Boolean(errors) && !globals.metricError) || mode === 'COMMAND') {
84      return;
85    }
86    const message = Boolean(errors)
87      ? `${errors} import or data loss errors detected.`
88      : `Metric error detected.`;
89    return m(
90      'a.error',
91      {href: '#!/info'},
92      m(
93        'i.material-icons',
94        {
95          title: message + ` Click for more info.`,
96        },
97        'announcement',
98      ),
99    );
100  }
101}
102
103export interface TopbarAttrs {
104  omnibox: m.Children;
105}
106
107export class Topbar implements m.ClassComponent<TopbarAttrs> {
108  view({attrs}: m.Vnode<TopbarAttrs>) {
109    const {omnibox} = attrs;
110    return m(
111      '.topbar',
112      {class: globals.state.sidebarVisible ? '' : 'hide-sidebar'},
113      omnibox,
114      m(Progress),
115      m(HelpPanningNotification),
116      m(TraceErrorIcon),
117    );
118  }
119}
120