Skip to content

Commit

Permalink
Add Server Offline Landing to Placeholder Widget
Browse files Browse the repository at this point in the history
Adds a prompt to start the server when the front-end detects that there is no server
currently running.  This provides a clearer application flow for the user.

Signed-off-by: Will Yang <[email protected]>
  • Loading branch information
williamsyang-work committed Feb 20, 2024
1 parent 90f7489 commit 2d5c012
Show file tree
Hide file tree
Showing 10 changed files with 211 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface TraceContextProps {
removeResizeHandler: (handler: () => void) => void;
backgroundTheme: string;
persistedState?: PersistedState;
serverStatus?: boolean;
}

export interface TraceContextState {
Expand Down Expand Up @@ -491,20 +492,29 @@ export class TraceContextComponent extends React.Component<TraceContextProps, Tr
}

render(): JSX.Element {
const { serverStatus } = this.props;
const shouldRenderOutputs =
this.state.style.width > 0 && (this.props.outputs.length || this.props.overviewDescriptor);
return (
<div
className="trace-context-container"
onContextMenu={event => this.onContextMenu(event)}
onKeyDown={event => this.onKeyDown(event)}
onKeyUp={event => this.onKeyUp(event)}
ref={this.traceContextContainer}
>
<TooltipComponent ref={this.tooltipComponent} />
<TooltipXYComponent ref={this.tooltipXYComponent} />
{shouldRenderOutputs ? this.renderOutputs() : this.renderPlaceHolder()}
</div>
<>
{/* Render the grey-out overlay if the server is down */}
{serverStatus === false && (
<div className="overlay">
<div className="warning-text">Please reconnect to resume using the application.</div>
</div>
)}
<div
className="trace-context-container"
onContextMenu={event => this.onContextMenu(event)}
onKeyDown={event => this.onKeyDown(event)}
onKeyUp={event => this.onKeyUp(event)}
ref={this.traceContextContainer}
>
<TooltipComponent ref={this.tooltipComponent} />
<TooltipXYComponent ref={this.tooltipXYComponent} />
{shouldRenderOutputs ? this.renderOutputs() : this.renderPlaceHolder()}
</div>
</>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,38 @@ import * as React from 'react';

export interface ReactPlaceholderWidgetProps {
loading: boolean;
serverOn: boolean;
tracesOpen: boolean;
handleOpenTrace: () => void;
handleStartServer: () => void;
}

export class ReactExplorerPlaceholderWidget extends React.Component<ReactPlaceholderWidgetProps, unknown> {
export class ReactExplorerPlaceholderWidget extends React.Component<ReactPlaceholderWidgetProps, {}> {
constructor(props: ReactPlaceholderWidgetProps) {
super(props);
}

render(): React.ReactNode {
const { loading, serverOn, handleOpenTrace, handleStartServer } = this.props;
const onClick = serverOn ? handleOpenTrace : handleStartServer;
const infoText = serverOn
? 'You have not yet opened a trace.'
: 'No trace server instance is currently running.';
const buttonText = serverOn ? 'Open Trace' : 'Start Trace Server';

return (
<div className="placeholder-container" tabIndex={0}>
<div className="center">{'You have not yet opened a trace.'}</div>
<div className="center">{infoText}</div>
<div className="placeholder-open-workspace-button-container">
<button
className="plcaeholder-open-workspace-button"
title="Select a trace to open"
onClick={this.props.handleOpenTrace}
disabled={this.props.loading}
title={buttonText}
onClick={onClick}
disabled={loading}
>
{this.props.loading && <FontAwesomeIcon icon={faSpinner} spin style={{ marginRight: '5px' }} />}
{this.props.loading && <span>Connecting to trace server</span>}
{!this.props.loading && <span>Open Trace</span>}
{loading && <FontAwesomeIcon icon={faSpinner} spin style={{ marginRight: '5px' }} />}
{loading && <span>Connecting to trace server</span>}
{!loading && <span>{buttonText}</span>}
</button>
</div>
</div>
Expand Down
20 changes: 20 additions & 0 deletions packages/react-components/style/trace-viewer.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,24 @@ div {
.trace-viewer-container {
margin: 0px 5px 0px 5px;
height: 100%;
}

/* Grey out container */
.overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent black */
display: flex;
justify-content: center;
align-items: center;
z-index: 999; /* Ensure the overlay appears on top */
}

.warning-text {
color: white; /* Color of the warning text */
text-align: center;
font-size: 24px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,24 @@ import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'
import { ReactWidget } from '@theia/core/lib/browser';
import * as React from 'react';
import { CommandService } from '@theia/core';
import { OpenTraceCommand } from '../../trace-viewer/trace-viewer-commands';
import { OpenTraceCommand, StartServerCommand } from '../../trace-viewer/trace-viewer-commands';
import { ReactExplorerPlaceholderWidget } from 'traceviewer-react-components/lib/trace-explorer/trace-explorer-placeholder-widget';
import { TraceServerConnectionStatusClient } from '../../../common/trace-server-connection-status';

@injectable()
export class TraceExplorerPlaceholderWidget extends ReactWidget {
static ID = 'trace-explorer-placeholder-widget';
static LABEL = 'Trace Explorer Placeholder Widget';

state = {
loading: false
loading: false,
serverStatus: false,
tracesOpened: false
};

@inject(CommandService) protected readonly commandService!: CommandService;
@inject(TraceServerConnectionStatusClient)
protected traceServerConnectionStatusProxy: TraceServerConnectionStatusClient;

@postConstruct()
protected init(): void {
Expand All @@ -23,10 +28,17 @@ export class TraceExplorerPlaceholderWidget extends ReactWidget {
this.update();
}

dispose(): void {
super.dispose();
}

render(): React.ReactNode {
const { loading } = this.state;
const { loading, serverStatus, tracesOpened } = this.state;
return (
<ReactExplorerPlaceholderWidget
tracesOpen={tracesOpened}
serverOn={serverStatus}
handleStartServer={this.handleStartServer}
loading={loading}
handleOpenTrace={this.handleOpenTrace}
></ReactExplorerPlaceholderWidget>
Expand All @@ -42,4 +54,19 @@ export class TraceExplorerPlaceholderWidget extends ReactWidget {
this.state.loading = false;
this.update();
}

protected handleStartServer = async (): Promise<void> => this.doHandleStartServer();

private async doHandleStartServer() {
this.state.loading = true;
this.update();
await this.commandService.executeCommand(StartServerCommand.id);
this.state.loading = false;
this.update();
}

public setStateAndShow(newState: { serverStatus: boolean; tracesOpened: boolean }): void {
this.state = { ...newState, ...this.state };
this.show();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { CommandService } from '@theia/core';
export class TraceExplorerServerStatusWidget extends ReactWidget {
static ID = 'trace-explorer-server-status-widget';
static LABEL = 'Trace Explorer Server Status Widget';
private serverOn = false;

@inject(CommandService) protected readonly commandService!: CommandService;

Expand All @@ -17,16 +18,24 @@ export class TraceExplorerServerStatusWidget extends ReactWidget {
this.update();
}

public updateStatus = (status: boolean): void => {
this.serverOn = status;
this.update();
};

render(): React.ReactNode {
const className = this.serverOn ? 'fa fa-check-circle-o fa-lg' : 'fa fa-times-circle-o fa-lg';

const title = this.serverOn
? 'Server health and latency are good. No known issues'
: 'Trace Viewer Critical Error: Trace Server Offline';

const color = this.serverOn ? 'green' : 'red';

return (
<div className="server-status-header">
<span className="theia-header">Server Status </span>
<i
id="server-status-id"
className="fa fa-times-circle-o fa-lg"
title="Trace Viewer Critical Error: Trace Server Offline"
style={{ color: 'red', marginLeft: '5px' }}
/>
<i id="server-status-id" className={className} title={title} style={{ color, marginLeft: '5px' }} />
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,16 @@ export class TraceExplorerWidget extends BaseWidget {
layout.addWidget(this.traceViewsContainer);
this.node.tabIndex = 0;
signalManager().on(Signals.OPENED_TRACES_UPDATED, this.onUpdateSignal);
// TODO - Should we be using the backend, since we're on the backend?
this.connectionStatusClient.addServerStatusChangeListener(this.onServerStatusChange);
this.update();
}

dispose(): void {
super.dispose();
signalManager().off(Signals.OPENED_TRACES_UPDATED, this.onUpdateSignal);
// TODO - Should we be using the backend, since we're on the backend?
this.connectionStatusClient.removeServerStatusChangeListener(this.onServerStatusChange);
}

protected onUpdateSignal = (payload: OpenedTracesUpdatedSignalPayload): void =>
Expand All @@ -100,12 +104,17 @@ export class TraceExplorerWidget extends BaseWidget {

protected onUpdateRequest(msg: Message): void {
super.onUpdateRequest(msg);
if (this._numberOfOpenedTraces > 0) {
this.traceViewsContainer.show();
this.placeholderWidget.hide();
} else {

const serverStatus = this.connectionStatusClient.status;
const tracesOpened = this._numberOfOpenedTraces > 0;
const shouldShowPlaceholder = serverStatus === false || tracesOpened === false;

if (shouldShowPlaceholder) {
this.placeholderWidget.setStateAndShow({ serverStatus, tracesOpened });
this.traceViewsContainer.hide();
this.placeholderWidget.show();
} else {
this.placeholderWidget.hide();
this.traceViewsContainer.show();
}
}

Expand All @@ -115,12 +124,18 @@ export class TraceExplorerWidget extends BaseWidget {
}

protected async onAfterShow(): Promise<void> {
this.connectionStatusClient.addConnectionStatusListener();
this.connectionStatusClient.activate();
const status = await this.traceServerConnectionStatusProxy.getStatus();
this.connectionStatusClient.updateStatus(status);
}

protected onAfterHide(): void {
this.connectionStatusClient.removeConnectionStatusListener();
this.connectionStatusClient.deactivate();
}

protected onServerStatusChange = (status: boolean): void => this.doHandleOnServerStatusChange(status);

protected doHandleOnServerStatusChange(status: boolean): void {
this.serverStatusWidget.updateStatus(status);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,40 @@
import { injectable } from 'inversify';
import { TraceServerConnectionStatusClient } from '../common/trace-server-connection-status';

type Listener = (serverStatus: boolean) => void;
@injectable()
export class TraceServerConnectionStatusClientImpl implements TraceServerConnectionStatusClient {
protected active = false;
protected _status = false;
protected listeners: Listener[] = [];

updateStatus(status: boolean): void {
public updateStatus(status: boolean): void {
this._status = status;
if (this.active) {
TraceServerConnectionStatusClientImpl.renderStatus(status);
this.listeners.forEach(fn => fn(status));
}
}

addConnectionStatusListener(): void {
public activate(): void {
this.active = true;
}

removeConnectionStatusListener(): void {
public deactivate(): void {
this.active = false;
}

static renderStatus(status: boolean): void {
if (document.getElementById('server-status-id')) {
document.getElementById('server-status-id')!.className = status
? 'fa fa-check-circle-o fa-lg'
: 'fa fa-times-circle-o fa-lg';
document.getElementById('server-status-id')!.title = status
? 'Server health and latency are good. No known issues'
: 'Trace Viewer Critical Error: Trace Server Offline';
document.getElementById('server-status-id')!.style.color = status ? 'green' : 'red';
public addServerStatusChangeListener(fn: Listener): void {
this.listeners.push(fn);
}

public removeServerStatusChangeListener(fn: Listener): void {
const index = this.listeners.indexOf(fn);
if (index) {
this.listeners.splice(index, 1);
}
}

get status(): boolean {
return this._status;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import { TracePreferences, TRACE_PATH, TRACE_ARGS } from '../trace-server-prefer
import { TspClientProvider } from '../tsp-client-provider-impl';
import { ChartShortcutsDialog } from '../trace-explorer/trace-explorer-sub-widgets/charts-cheatsheet-component';
import { signalManager } from 'traceviewer-base/lib/signals/signal-manager';
import { TraceServerConnectionStatusClientImpl } from '../trace-server-connection-status-client-impl';
import { FileStat } from '@theia/filesystem/lib/common/files';
import { ITspClient } from 'tsp-typescript-client';
import { TraceServerConnectionStatusClient } from '../../common/trace-server-connection-status';

interface TraceViewerWidgetOpenerOptions extends WidgetOpenerOptions {
traceUUID: string;
Expand All @@ -50,6 +50,8 @@ export class TraceViewerContribution
@inject(TracePreferences) protected tracePreferences: TracePreferences;
@inject(TraceServerConfigService) protected readonly traceServerConfigService: TraceServerConfigService;
@inject(MessageService) protected readonly messageService: MessageService;
@inject(TraceServerConnectionStatusClient)
protected readonly serverStatusService: TraceServerConnectionStatusClient;

readonly id = TraceViewerWidget.ID;
readonly label = 'Trace Viewer';
Expand Down Expand Up @@ -94,7 +96,7 @@ export class TraceViewerContribution
progress.report({ message: 'Trace server started.', work: { done: 100, total: 100 } });
}
progress.cancel();
TraceServerConnectionStatusClientImpl.renderStatus(true);
this.serverStatusService.updateStatus(true);
signalManager().fireTraceServerStartedSignal();
this.openDialog(rootPath);
}
Expand Down Expand Up @@ -163,7 +165,7 @@ export class TraceViewerContribution
} else {
progress.report({ message: 'Trace server started.', work: { done: 100, total: 100 } });
}
TraceServerConnectionStatusClientImpl.renderStatus(true);
this.serverStatusService.updateStatus(true);
signalManager().fireTraceServerStartedSignal();
return super.open(traceURI, options);
}
Expand Down Expand Up @@ -230,7 +232,7 @@ export class TraceViewerContribution
} else {
progress.report({ message: 'Trace server started.', work: { done: 100, total: 100 } });
}
TraceServerConnectionStatusClientImpl.renderStatus(true);
this.serverStatusService.updateStatus(true);
signalManager().fireTraceServerStartedSignal();
return;
}
Expand Down Expand Up @@ -261,7 +263,7 @@ export class TraceViewerContribution
try {
await this.traceServerConfigService.stopTraceServer();
this.messageService.info('Trace server terminated successfully.');
TraceServerConnectionStatusClientImpl.renderStatus(false);
this.serverStatusService.updateStatus(false);
} catch (err) {
this.messageService.error('Failed to stop the trace server.');
}
Expand Down
Loading

0 comments on commit 2d5c012

Please sign in to comment.