blob: 27d59f7100744691bd013ccd6efa329afe808e90 [file]
// Copyright (C) 2026 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 {Button, ButtonVariant} from '../../widgets/button';
import {Callout} from '../../widgets/callout';
import {Intent} from '../../widgets/common';
import {Popup, PopupPosition} from '../../widgets/popup';
import {TextInput} from '../../widgets/text_input';
import {bigTraceSettingsStorage} from '../settings/bigtrace_settings_storage';
import {
endpointStorage,
getBigtraceEndpoint,
} from '../settings/endpoint_storage';
// Host of the configured endpoint, for the button label. Falls back to the raw
// string when it isn't a URL (a half-typed endpoint still reads sensibly).
function endpointLabel(endpoint: string): string {
if (endpoint === '') return 'Connect backend';
try {
return new URL(endpoint).host;
} catch {
return endpoint;
}
}
// The endpoint is the one piece of state that isn't per-query — it's a
// connection, not a configuration — so it lives up here rather than in the
// per-tab settings. Auto-opens once when there's no working backend, which is
// the app's onboarding step now that the settings page is gone.
export class ConnectionButton implements m.ClassComponent {
private open = false;
private autoOpened = false;
view(): m.Children {
const endpoint = getBigtraceEndpoint();
// The endpoint ships with a default, so "set" doesn't mean "reachable" —
// a backend that failed to answer counts as not connected, otherwise this
// button claims a connection the app doesn't have.
const connected =
endpoint !== '' &&
bigTraceSettingsStorage.execConfigLoadError === undefined;
if (
!this.autoOpened &&
!connected &&
!bigTraceSettingsStorage.isExecConfigLoading
) {
this.autoOpened = true;
this.open = true;
}
return m(
Popup,
{
trigger: m(Button, {
icon: connected ? 'cloud_done' : 'cloud_off',
label: endpointLabel(endpoint),
title: connected
? endpoint
: `Not connected to ${endpoint === '' ? 'a backend' : endpoint}`,
onclick: () => {
this.open = !this.open;
},
}),
isOpen: this.open,
onChange: (isOpen: boolean) => {
this.open = isOpen;
},
position: PopupPosition.BottomEnd,
className: 'pf-bt-connection-popup',
},
this.renderPanel(),
);
}
private renderPanel(): m.Children {
const setting = endpointStorage.get('bigtraceEndpoint');
if (setting === undefined) return null;
const loadError = bigTraceSettingsStorage.execConfigLoadError;
return m('.pf-bt-connection-panel', [
m('.pf-bt-connection-panel__title', 'BigTrace backend'),
m(
'.pf-bt-connection-panel__desc',
'The service that holds the traces and runs your queries.',
),
m(TextInput, {
value: setting.get() as string,
placeholder: 'https://your-bigtrace-backend/v1',
className: 'pf-bt-connection-panel__input',
oninput: (e: Event) => {
setting.set((e.target as HTMLInputElement).value);
},
}),
m('.pf-bt-connection-panel__actions', [
// The endpoint is read once at module init, so a change needs a reload.
endpointStorage.isReloadRequired() &&
m(Button, {
label: 'Reload to apply',
icon: 'refresh',
intent: Intent.Primary,
variant: ButtonVariant.Filled,
onclick: () => window.location.reload(),
}),
m(Button, {
label: 'Reset',
icon: 'settings_backup_restore',
title: 'Restore the default endpoint.',
onclick: () => setting.reset(),
}),
]),
loadError !== undefined &&
m(
Callout,
{intent: Intent.Danger, icon: 'error'},
`Couldn't load settings from this backend: ${loadError}`,
),
]);
}
}