-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
UI for manage Theia development mode (#13)
* UI for development mode * Code improvements Signed-off-by: Vitaliy Gulyy <vguliy@codenvy.com>
- Loading branch information
Showing
6 changed files
with
444 additions
and
41 deletions.
There are no files selected for viewing
90 changes: 90 additions & 0 deletions
90
packages/plugin-ext/src/hosted/browser/hosted-plugin-informer.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/* | ||
* Copyright (C) 2018 Red Hat, Inc. and others. | ||
* | ||
* 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 | ||
*/ | ||
|
||
import { injectable, inject } from 'inversify'; | ||
import { StatusBar } from '@theia/core/lib/browser/status-bar/status-bar'; | ||
import { StatusBarAlignment, StatusBarEntry, FrontendApplicationContribution } from '@theia/core/lib/browser'; | ||
import { WorkspaceService } from '@theia/workspace/lib/browser'; | ||
import { HostedPluginServer } from '../../common/plugin-protocol'; | ||
import { ConnectionStatusService, ConnectionState } from '@theia/core/lib/browser/connection-status-service'; | ||
import URI from '@theia/core/lib/common/uri'; | ||
import { FileStat } from '@theia/filesystem/lib/common'; | ||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; | ||
|
||
/** | ||
* Informs the user whether Theia is running with hosted plugin. | ||
* Adds 'Development Host' status bar element and appends the same prefix to window title. | ||
*/ | ||
@injectable() | ||
export class HostedPluginInformer implements FrontendApplicationContribution { | ||
|
||
public static readonly DEVELOPMENT_HOST_TITLE = "Development Host"; | ||
|
||
public static readonly DEVELOPMENT_HOST = "development-host"; | ||
|
||
public static readonly DEVELOPMENT_HOST_OFFLINE = "development-host-offline"; | ||
|
||
private entry: StatusBarEntry; | ||
|
||
@inject(StatusBar) | ||
protected readonly statusBar: StatusBar; | ||
|
||
@inject(WorkspaceService) | ||
protected readonly workspaceService: WorkspaceService; | ||
|
||
@inject(HostedPluginServer) | ||
protected readonly hostedPluginServer: HostedPluginServer; | ||
|
||
@inject(ConnectionStatusService) | ||
protected readonly connectionStatusService: ConnectionStatusService; | ||
|
||
@inject(FrontendApplicationStateService) | ||
protected readonly frontendApplicationStateService: FrontendApplicationStateService; | ||
|
||
public initialize(): void { | ||
this.workspaceService.root.then(root => { | ||
this.hostedPluginServer.getHostedPlugin().then(pluginMetadata => { | ||
if (pluginMetadata) { | ||
this.updateTitle(root); | ||
|
||
this.entry = { | ||
text: `$(cube) ${HostedPluginInformer.DEVELOPMENT_HOST_TITLE}`, | ||
tooltip: `Hosted Plugin '${pluginMetadata.model.name}'`, | ||
alignment: StatusBarAlignment.LEFT, | ||
priority: 100 | ||
}; | ||
|
||
this.frontendApplicationStateService.reachedState('ready').then(() => { | ||
this.updateStatusBarElement(); | ||
}); | ||
|
||
this.connectionStatusService.onStatusChange(() => this.updateStatusBarElement()); | ||
} | ||
}); | ||
}); | ||
} | ||
|
||
private updateStatusBarElement(): void { | ||
if (this.connectionStatusService.currentState.state === ConnectionState.OFFLINE) { | ||
this.entry.className = HostedPluginInformer.DEVELOPMENT_HOST_OFFLINE; | ||
} else { | ||
this.entry.className = HostedPluginInformer.DEVELOPMENT_HOST; | ||
} | ||
|
||
this.statusBar.setElement(HostedPluginInformer.DEVELOPMENT_HOST, this.entry); | ||
} | ||
|
||
private updateTitle(root: FileStat | undefined): void { | ||
if (root) { | ||
const uri = new URI(root.uri); | ||
document.title = HostedPluginInformer.DEVELOPMENT_HOST_TITLE + " - " + uri.displayName; | ||
} else { | ||
document.title = HostedPluginInformer.DEVELOPMENT_HOST_TITLE; | ||
} | ||
} | ||
|
||
} |
241 changes: 241 additions & 0 deletions
241
packages/plugin-ext/src/main/browser/hosted-plugin-controller.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
/* | ||
* Copyright (C) 2018 Red Hat, Inc. and others. | ||
* | ||
* 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 | ||
*/ | ||
|
||
import { injectable, inject } from 'inversify'; | ||
import { StatusBar } from '@theia/core/lib/browser/status-bar/status-bar'; | ||
import { StatusBarAlignment, StatusBarEntry, FrontendApplicationContribution } from '@theia/core/lib/browser'; | ||
import { HostedPluginServer } from '../../common/plugin-protocol'; | ||
import { HostedPluginManagerClient, HostedPluginState, HostedPluginCommands } from './plugin-manager-client'; | ||
import { CommandRegistry } from '@phosphor/commands'; | ||
import { Menu } from '@phosphor/widgets'; | ||
import { setTimeout } from 'timers'; | ||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; | ||
import { ConnectionStatusService, ConnectionState } from '@theia/core/lib/browser/connection-status-service'; | ||
|
||
/** | ||
* Adds a status bar element displaying the state of secondary Theia instance with hosted plugin and | ||
* allows controlling the instance by simple clicking on the status bar element. | ||
*/ | ||
@injectable() | ||
export class HostedPluginController implements FrontendApplicationContribution { | ||
|
||
public static readonly HOSTED_PLUGIN = "hosted-plugin"; | ||
public static readonly HOSTED_PLUGIN_OFFLINE = "hosted-plugin-offline"; | ||
public static readonly HOSTED_PLUGIN_FAILED = "hosted-plugin-failed"; | ||
|
||
@inject(StatusBar) | ||
protected readonly statusBar: StatusBar; | ||
|
||
@inject(FrontendApplicationStateService) | ||
protected readonly frontendApplicationStateService: FrontendApplicationStateService; | ||
|
||
@inject(HostedPluginServer) | ||
protected readonly hostedPluginServer: HostedPluginServer; | ||
|
||
@inject(HostedPluginManagerClient) | ||
protected readonly hostedPluginManagerClient: HostedPluginManagerClient; | ||
|
||
@inject(ConnectionStatusService) | ||
protected readonly connectionStatusService: ConnectionStatusService; | ||
|
||
private pluginState: HostedPluginState = HostedPluginState.Stopped; | ||
|
||
private entry: StatusBarEntry | undefined; | ||
|
||
public initialize(): void { | ||
this.hostedPluginServer.getHostedPlugin().then(pluginMetadata => { | ||
if (!pluginMetadata) { | ||
this.frontendApplicationStateService.reachedState('ready').then(() => { | ||
this.hostedPluginManagerClient.onStateChanged(e => { | ||
if (e === 'starting') { | ||
this.onHostedPluginStarting(); | ||
} else if (e === 'running') { | ||
this.onHostedPluginRunning(); | ||
} else if (e === 'stopped') { | ||
this.onHostedPluginStopped(); | ||
} else if (e === 'failed') { | ||
this.onHostedPluginFailed(); | ||
} | ||
}); | ||
|
||
this.hostedPluginServer.isHostedTheiaRunning().then((running) => { | ||
if (running) { | ||
this.onHostedPluginRunning(); | ||
} | ||
}); | ||
}); | ||
|
||
this.connectionStatusService.onStatusChange(() => this.onConnectionStatusChanged()); | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* Display status bar element for stopped plugin. | ||
*/ | ||
protected async onHostedPluginStopped(): Promise<void> { | ||
this.pluginState = HostedPluginState.Stopped; | ||
|
||
this.entry = { | ||
text: `Hosted Plugin: Stopped $(angle-up)`, | ||
alignment: StatusBarAlignment.LEFT, | ||
priority: 100, | ||
onclick: e => { | ||
this.showMenu(e.clientX, e.clientY); | ||
} | ||
}; | ||
|
||
this.entry.className = HostedPluginController.HOSTED_PLUGIN; | ||
await this.statusBar.setElement(HostedPluginController.HOSTED_PLUGIN, this.entry); | ||
} | ||
|
||
/** | ||
* Display status bar element for starting plugin. | ||
*/ | ||
protected async onHostedPluginStarting(): Promise<void> { | ||
this.pluginState = HostedPluginState.Starting; | ||
|
||
this.entry = { | ||
text: `$(cog~spin) Hosted Plugin: Starting`, | ||
alignment: StatusBarAlignment.LEFT, | ||
priority: 100 | ||
}; | ||
|
||
this.entry.className = HostedPluginController.HOSTED_PLUGIN; | ||
await this.statusBar.setElement(HostedPluginController.HOSTED_PLUGIN, this.entry); | ||
} | ||
|
||
/** | ||
* Display status bar element for running plugin. | ||
*/ | ||
protected async onHostedPluginRunning(): Promise<void> { | ||
this.pluginState = HostedPluginState.Running; | ||
|
||
this.entry = { | ||
text: `$(cog~spin) Hosted Plugin: Running $(angle-up)`, | ||
alignment: StatusBarAlignment.LEFT, | ||
priority: 100, | ||
onclick: e => { | ||
this.showMenu(e.clientX, e.clientY); | ||
} | ||
}; | ||
|
||
this.entry.className = HostedPluginController.HOSTED_PLUGIN; | ||
await this.statusBar.setElement(HostedPluginController.HOSTED_PLUGIN, this.entry); | ||
} | ||
|
||
/** | ||
* Display status bar element for failed plugin. | ||
*/ | ||
protected async onHostedPluginFailed(): Promise<void> { | ||
this.pluginState = HostedPluginState.Failed; | ||
|
||
this.entry = { | ||
text: `Hosted Plugin: Stopped $(angle-up)`, | ||
alignment: StatusBarAlignment.LEFT, | ||
priority: 100, | ||
onclick: e => { | ||
this.showMenu(e.clientX, e.clientY); | ||
} | ||
}; | ||
|
||
this.entry.className = HostedPluginController.HOSTED_PLUGIN_FAILED; | ||
await this.statusBar.setElement(HostedPluginController.HOSTED_PLUGIN, this.entry); | ||
} | ||
|
||
/** | ||
* Updaing status bar element when changing connection status. | ||
*/ | ||
private onConnectionStatusChanged(): void { | ||
if (this.connectionStatusService.currentState.state === ConnectionState.OFFLINE) { | ||
// Re-set the element only if it's visible on status bar | ||
if (this.entry) { | ||
const offlineElement = { | ||
text: `Hosted Plugin: Stopped`, | ||
alignment: StatusBarAlignment.LEFT, | ||
priority: 100 | ||
}; | ||
|
||
this.entry.className = HostedPluginController.HOSTED_PLUGIN_OFFLINE; | ||
this.statusBar.setElement(HostedPluginController.HOSTED_PLUGIN, offlineElement); | ||
} | ||
} else { | ||
// ask state of hosted plugin when switching to Online | ||
if (this.entry) { | ||
this.hostedPluginServer.isHostedTheiaRunning().then((running) => { | ||
if (running) { | ||
this.onHostedPluginRunning(); | ||
} else { | ||
this.onHostedPluginStopped(); | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Show menu containing actions to start/stop/restart hosted plugin. | ||
*/ | ||
protected showMenu(x: number, y: number): void { | ||
const commands = new CommandRegistry(); | ||
const menu = new Menu({ | ||
commands | ||
}); | ||
|
||
if (this.pluginState === 'running') { | ||
this.addCommandsForRunningPlugin(commands, menu); | ||
} else if (this.pluginState === 'stopped' || this.pluginState === 'failed') { | ||
this.addCommandsForStoppedPlugin(commands, menu); | ||
} | ||
|
||
menu.open(x, y); | ||
} | ||
|
||
/** | ||
* Adds commands to the menu for running plugin. | ||
*/ | ||
protected addCommandsForRunningPlugin(commands: CommandRegistry, menu: Menu): void { | ||
commands.addCommand(HostedPluginCommands.STOP.id, { | ||
label: 'Stop Instance', | ||
icon: 'fa fa-stop', | ||
execute: () => setTimeout(() => this.hostedPluginManagerClient.stop(), 100) | ||
}); | ||
|
||
menu.addItem({ | ||
type: 'command', | ||
command: HostedPluginCommands.STOP.id | ||
}); | ||
|
||
commands.addCommand(HostedPluginCommands.RESTART.id, { | ||
label: 'Restart Instance', | ||
icon: 'fa fa-repeat', | ||
execute: () => setTimeout(() => this.hostedPluginManagerClient.restart(), 100) | ||
}); | ||
|
||
menu.addItem({ | ||
type: 'command', | ||
command: HostedPluginCommands.RESTART.id | ||
}); | ||
} | ||
|
||
/** | ||
* Adds command to the menu for stopped plugin. | ||
*/ | ||
protected addCommandsForStoppedPlugin(commands: CommandRegistry, menu: Menu): void { | ||
commands.addCommand(HostedPluginCommands.START.id, { | ||
label: "Start Instance", | ||
icon: 'fa fa-play', | ||
execute: () => setTimeout(() => this.hostedPluginManagerClient.start(), 100) | ||
}); | ||
|
||
menu.addItem({ | ||
type: 'command', | ||
command: HostedPluginCommands.START.id | ||
}); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.