Skip to content

Commit

Permalink
Connect welcome view (UI) and new connection status bar (#24)
Browse files Browse the repository at this point in the history
* New UI/functionality
 - Welcome view added.
 - Status bar added
 - NodeConfig view removed and the functionality was moved to the status bar.

* Update package.json - removing redundant condition.
Co-authored-by: Xuefei Han <[email protected]>

* The welcome view messages updated.
Co-authored-by: Manas Kumar Das <[email protected]>

---------

Co-authored-by: Xuefei Han <[email protected]>
Co-authored-by: Manas Kumar Das <[email protected]>
  • Loading branch information
3 people committed Jan 31, 2023
1 parent a3be7f5 commit 25290d5
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 333 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Pyrsia support for Microsoft VS Code (extension).**This is an early prototype an
npm install
npm run watch
```

- Open VS Code, in the Activity Bar select "Run and Debug" and make sure the "Lunch Extension" configuration is selected.
- Press F5 to run the Pyrsia extension (debug mode), a new VS Code instance will appear and should have the Pyrsia extension installed (should be shown in the Activity Bar).
Expand Down
17 changes: 15 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@
],
"main": "./out/extension.js",
"contributes": {
"viewsWelcome": [
{
"view": "pyrsia.node-status",
"contents": "Thank you for installing the Pyrsia extension! \nIf you already have a Pyrsia node [installed and running](https://pyrsia.io/docs/tutorials/quick-installation), please connect it to VS Code:\n[Connect to Pyrsia](command:pyrsia.node-config.update-url)\n👋 Don't have a Pyrsia installed? [Download it here!](https://pyrsia.io/docs/tutorials/quick-installation)",
"when": "!pyrsia.connection.configured"
},
{
"view": "pyrsia.node-status",
"contents": "👋 Is Pyrsia node offline? \nIf you already have a Pyrsia node [installed and running](https://pyrsia.io/docs/tutorials/quick-installation), please connect it to VS Code:\n[Connect to Pyrsia](command:pyrsia.node-config.update-url)\nDon't have a Pyrsia node installed? [Download it here!](https://pyrsia.io/docs/tutorials/quick-installation)",
"when": "pyrsia.connection.configured && pyrsia.connection.status == false"
}
],
"viewsContainers": {
"activitybar": [
{
Expand All @@ -30,8 +42,9 @@
"views": {
"pyrsia": [
{
"id": "pyrsia.node-config",
"name": "Node Status"
"id": "pyrsia.node-status",
"name": "Connect to Pyrsia",
"when": "!pyrsia.connection.status"
},
{
"id": "pyrsia.node-integrations",
Expand Down
22 changes: 16 additions & 6 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import * as vscode from "vscode";
import { NodeConfigView } from "./views/NodeConfigView";
// import { NodeStatusViewProvider } from "./webviews/NodeStatusView";
import { IntegrationsView as IntegrationsView } from "./views/IntegrationsView";
import { Util } from "./utilities/Util";
import { HelpView } from "./views/HelpView";
import { Integration } from "./api/Integration";
import { DockerIntegration } from "./integrations/DockerIntegration";
import { ConnectionStatusBar } from "./views/ConnectionStatusBar";

// This const is used for how often we should check if the Pyrsia node is online
const REFRESH_UI_INTERVAL = 60 * 1000;

export const activate = (context: vscode.ExtensionContext) => {
// Init the extension utils
Expand All @@ -14,16 +16,24 @@ export const activate = (context: vscode.ExtensionContext) => {
// Create docker integration
const dockerIntegration: Integration = new DockerIntegration(context);

// Create the node config view
const nodeConfigView = new NodeConfigView(context);
nodeConfigView.addIntegration(dockerIntegration);

// Create the integrations view
const integrationView = new IntegrationsView(context);
integrationView.addIntegration(dockerIntegration);

// Create Help view
new HelpView(context);

// Create Status Bar
const connectionStatusBar = new ConnectionStatusBar(context);

// trigger the UI updates every 10 seconds based on the
setInterval(() => {
// update the status bar
connectionStatusBar.requestUpdateStatusBar();
// update the integrations
IntegrationsView.requestIntegrationsUpdate();
IntegrationsView.requestIntegrationsViewUpdate();
}, REFRESH_UI_INTERVAL);
};

export const deactivate = () => {
Expand Down
59 changes: 49 additions & 10 deletions src/utilities/Util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,35 @@ import { readdir } from "fs/promises";
* Utility static method (don't create instances)
*/
export class Util {
public static readonly setConnectedContextId = "setContext";
private static readonly nodeConnectionStatusKey: string = "pyrsia.connection.status"; // NOI18N
private static resourcePath: string;
private static config: NodeConfig;
private static dockerClient: DockerClient;
private static globalState: vscode.Memento;

/**
* Node connections status, indicates if the provided node URL can be used to reach a Pyrsia node.
* @returns {boolean} if Pyrsia node connected returns 'true'
*/
public static get nodeConnected(): boolean | undefined {
if (!this.globalState) {
throw new Error("Global state not available");
}

return this.globalState.get(this.nodeConnectionStatusKey);
}

/**
* Node connections status, indicates if the provided node URL can be used to reach a Pyrsia node.
* @param {boolean} nodeConnected - Pyrsia node connection status
*/
public static set nodeConnected(nodeConnected: boolean | undefined) {
if (!this.globalState) {
throw new Error("Global state not available");
}
vscode.commands.executeCommand(this.setConnectedContextId, this.nodeConnectionStatusKey, nodeConnected);
}

/**
* It's called once to pass the init values.
Expand All @@ -24,7 +50,8 @@ export class Util {
// set the resource path
Util.resourcePath = context.asAbsolutePath(path.join('resources')); // NOI18N
// load the configuration from the context (context is used to store the node configuration - e.g URL)
this.config = new NodeConfigImpl(context.workspaceState);
this.config = new NodeConfigImpl(context.globalState);
this.globalState = context.globalState;

return this;
}
Expand Down Expand Up @@ -121,20 +148,27 @@ export class Util {
* Private NodeConfig implementation.
*/
class NodeConfigImpl implements NodeConfig {
public static readonly nodeConnectionConfiguredKey: string = "pyrsia.connection.configured"; // NOI18N
// the node supported protocol
private static readonly protocol = "http"; // NOI18N
// default node URL
private static readonly defaultNodeUrl = new URL("localhost:7888"); // NOI18N
// the configuration ket, it uses to store configuration in context.workspaceState
private static readonly nodeUrlKey: string = "PYRSIA_NODE_URL_KEY"; // NOI18N
// the configuration ket, it uses to store configuration in context.globalState
private static readonly nodeUrlKey: string = "pyrsia.node.url"; // NOI18N

private nodeUrl: URL;
private workspaceState: vscode.Memento;

constructor(workspaceState: vscode.Memento) {
this.workspaceState = workspaceState;
const nodeUrl: string | undefined = workspaceState.get(NodeConfigImpl.nodeUrlKey);
this.url = !nodeUrl ? this.defaultUrl : new URL(nodeUrl);
private globalState: vscode.Memento;

constructor(globalState: vscode.Memento) {
this.globalState = globalState;
const nodeUrl: string | undefined = globalState.get(NodeConfigImpl.nodeUrlKey);
try {
this.url = !nodeUrl ? this.defaultUrl : new URL(nodeUrl);
} catch (error) {
// something is wrong, reset the url to the default value
console.error(error);
this.url = this.defaultUrl;
}
}

get defaultUrl(): URL {
Expand Down Expand Up @@ -172,6 +206,11 @@ class NodeConfigImpl implements NodeConfig {
} else {
this.nodeUrl = nodeUrl || NodeConfigImpl.defaultNodeUrl;
}
this.workspaceState.update(NodeConfigImpl.nodeUrlKey, this.nodeUrl);
// set node url
vscode.commands.executeCommand(Util.setConnectedContextId, NodeConfigImpl.nodeUrlKey, this.nodeUrl.href);
this.globalState.update(NodeConfigImpl.nodeUrlKey, this.nodeUrl.href);
// set connection configured
vscode.commands.executeCommand(Util.setConnectedContextId, NodeConfigImpl.nodeConnectionConfiguredKey, true);
this.globalState.update(NodeConfigImpl.nodeConnectionConfiguredKey, true);
}
}
93 changes: 93 additions & 0 deletions src/views/ConnectionStatusBar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import * as vscode from "vscode";
import * as client from "../utilities/pyrsiaClient";
import { Util } from "../utilities/Util";

export class ConnectionStatusBar {
public static readonly updateStatusBarCommandId: string = "pyrsia.status-bar.update"; // NOI18N
public static readonly showMessageStatusBarCommandId: string = "pyrsia.status-bar.show-message"; // NOI18N
public static readonly configNodeCommandId = "pyrsia.node-config.update-url"; // NOI18N
private readonly statusBar;

constructor(context: vscode.ExtensionContext) {
// create a new status bar item that we can now manage
this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
this.statusBar.command = ConnectionStatusBar.showMessageStatusBarCommandId;
context.subscriptions.push(this.statusBar);

// register the command to update the Pyrsia connection status bar
const updateStatusBarCommand = vscode.commands.registerCommand(ConnectionStatusBar.updateStatusBarCommandId, async () => {
await this.requestUpdateStatusBar();
});
context.subscriptions.push(updateStatusBarCommand);

// register the show message command
const showMessageStatusBarCommand = vscode.commands.registerCommand(ConnectionStatusBar.showMessageStatusBarCommandId, async () => {
await this.clickOnStatusBarShowMessage();
});
context.subscriptions.push(showMessageStatusBarCommand);

// Add a command to update the Pyrsia node configuration (actually just URL)
const configureNodeCommand = vscode.commands.registerCommand(
ConnectionStatusBar.configNodeCommandId,
async () => {
// the update node url input box
const options: vscode.InputBoxOptions = {
prompt: "Update the Pyrsia node address (e.g. localhost:7888)",
validateInput(value) {
let errorMessage: string | undefined;
console.debug(`Node configuration input: ${value}`);
if (!value.toLocaleLowerCase().startsWith(Util.getNodeConfig.prototype)) {
value = `${Util.getNodeConfig().protocol}://${value}`;
}
try {
new URL(value);
} catch (error) {
errorMessage =
"Incorrect Pyrsia node address, please provide a correct address (e.g localhost:7888)";
}

return errorMessage;
},
value: Util.getNodeConfig().host
};
context.subscriptions.push(configureNodeCommand);

// show the url input box so the user can provide a new node address
Util.getNodeConfig().url = await vscode.window.showInputBox(options);
await this.requestUpdateStatusBar();
// check the new url connection
const healthy = await client.isNodeHealthy();
if (!healthy) {
vscode.window.showErrorMessage(`Cannot connect to Pyrsi node: '${Util.getNodeConfig().url}',
please make sure the Pyrsia node is available.'`);
}
}
);
context.subscriptions.push(configureNodeCommand);
// update the status bar
this.requestUpdateStatusBar();
}

public async requestUpdateStatusBar() {
const connected: boolean = await client.isNodeHealthy();
this.statusBar.text = connected ? "🟢 Pyrsia" : "🔴 Pyrsia";
this.statusBar.show();
Util.nodeConnected = connected;
}

private async clickOnStatusBarShowMessage() {
const connected: boolean = await client.isNodeHealthy();
const nodeConfig = Util.getNodeConfig();
if (connected) {
vscode.window.showInformationMessage(`Connected, Pyrsia node: '${nodeConfig.host}'`);
} else {
const connectOptions = "Connect";
const result = await vscode.window.showErrorMessage(`Not connected, Pyrsia node: '${nodeConfig.host}'`, connectOptions);
if (result === connectOptions) {
vscode.commands.executeCommand(ConnectionStatusBar.configNodeCommandId);
}
}
}
}


Loading

0 comments on commit 25290d5

Please sign in to comment.