blob: 6546c64b5a1744d47bd1b6a91e1e6f305f796e97 [file] [log] [blame]
// 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),
);
}
}