Skip to content

Commit

Permalink
Preview in Activity Bar
Browse files Browse the repository at this point in the history
  • Loading branch information
Filip Andrzej Kaminski authored and Filip Andrzej Kaminski committed Mar 21, 2024
1 parent 8249033 commit 98e1834
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 150 deletions.
3 changes: 3 additions & 0 deletions packages/vscode-extension/assets/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 26 additions & 1 deletion packages/vscode-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,40 @@
"scope": "window",
"default": null,
"description": "Location of the React Native application root folder relative to the workspace workspace. This is used for monorepo type setups when the workspace root is not the root of the React Native project. The IDE extension tries to locate the React Native application root automatically, but in case it failes to do so (i.e. there are multiple applications defined in the workspace), you can use this setting to override the location."
},
"ReactNativeIDE.showPanelInActivityBar":{
"type": "boolean",
"scope": "window",
"default": false,
"description": "This option alows you to move IDE Panel to Activity Bar. (warning: You will need to reset your workspace for this setting to take effect)"
}
}
},
"viewsContainers": {
"activitybar": [
{
"id": "ReactNativeIDE",
"title": "React Native IDE",
"icon": "assets/logo.svg"
}
]
},
"views": {
"ReactNativeIDE": [
{
"type": "webview",
"id": "IDE-view",
"name": "IDE Panel",
"when": "config.ReactNativeIDE.showPanelInActivityBar"
}
]
},
"menus": {
"editor/title": [
{
"command": "RNIDE.openPanel",
"group": "navigation",
"when": "RNIDE.extensionIsActive && !RNIDE.panelIsOpen"
"when": "RNIDE.extensionIsActive && !RNIDE.panelIsOpen && !config.ReactNativeIDE.showPanelInActivityBar"
}
]
},
Expand Down
38 changes: 35 additions & 3 deletions packages/vscode-extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,23 @@ import {
ExtensionContext,
ExtensionMode,
DebugConfigurationProviderTriggerKind,
ConfigurationChangeEvent,
} from "vscode";
import { PreviewsPanel } from "./panels/PreviewsPanel";
import { PreviewCodeLensProvider } from "./providers/PreviewCodeLensProvider";
import { DebugConfigProvider } from "./providers/DebugConfigProvider";
import { DebugAdapterDescriptorFactory } from "./debugging/DebugAdapterDescriptorFactory";
import { Logger, enableDevModeLogging } from "./Logger";
import { setAppRootFolder, setExtensionContext } from "./utilities/extensionContext";
import {
extensionContext,
setAppRootFolder,
setExtensionContext,
} from "./utilities/extensionContext";
import { command } from "./utilities/subprocess";
import path from "path";
import os from "os";
import fs from "fs";
import { PreviewsViewProvider } from "./panels/PreviewsViewProvider";

const BIN_MODIFICATION_DATE_KEY = "bin_modification_date";

Expand Down Expand Up @@ -47,21 +53,36 @@ export function deactivate(context: ExtensionContext): undefined {

export async function activate(context: ExtensionContext) {
handleUncaughtErrors();
IDEPanelLocationListener();
setExtensionContext(context);
if (context.extensionMode === ExtensionMode.Development) {
enableDevModeLogging();
}

await fixBinaries(context);

context.subscriptions.push(
window.registerWebviewViewProvider(
PreviewsViewProvider.viewType,
new PreviewsViewProvider(context)
)
);
context.subscriptions.push(
commands.registerCommand("RNIDE.openPanel", (fileName?: string, lineNumber?: number) => {
PreviewsPanel.render(context, fileName, lineNumber);
if (workspace.getConfiguration("ReactNativeIDE").get<boolean>("showPanelInActivityBar")) {
commands.executeCommand("IDE-view.focus");
} else {
PreviewsPanel.render(context, fileName, lineNumber);
}
})
);
context.subscriptions.push(
commands.registerCommand("RNIDE.showPanel", (fileName?: string, lineNumber?: number) => {
PreviewsPanel.render(context, fileName, lineNumber);
if (workspace.getConfiguration("ReactNativeIDE").get<boolean>("showPanelInActivityBar")) {
commands.executeCommand("IDE-view.focus");
} else {
PreviewsPanel.render(context, fileName, lineNumber);
}
})
);
context.subscriptions.push(
Expand Down Expand Up @@ -96,6 +117,17 @@ export async function activate(context: ExtensionContext) {
await configureAppRootFolder();
}

function IDEPanelLocationListener() {
workspace.onDidChangeConfiguration((event: ConfigurationChangeEvent) => {
if (!event.affectsConfiguration("ReactNativeIDE")) {
return;
}
if (workspace.getConfiguration("ReactNativeIDE").get("showPanelInActivityBar")) {
PreviewsPanel.currentPanel?.dispose();
}
});
}

async function findSingleFileInWorkspace(fileGlobPattern: string, excludePattern: string | null) {
const files = await workspace.findFiles(fileGlobPattern, excludePattern, 2);
if (files.length === 1) {
Expand Down
144 changes: 144 additions & 0 deletions packages/vscode-extension/src/panels/PreviewWebviewController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { Webview, Disposable, window } from "vscode";
import { DependencyChecker } from "../dependency/DependencyChecker";
import { DependencyInstaller } from "../dependency/DependencyInstaller";
import { DeviceManager } from "../devices/DeviceManager";
import { Project } from "../project/project";
import { openExternalUrl } from "../utilities/vsc";
import { Logger } from "../Logger";
import { extensionContext } from "../utilities/extensionContext";

export class PreviewWebviewController implements Disposable {
private readonly dependencyChecker: DependencyChecker;
private readonly dependencyInstaller: DependencyInstaller;
private readonly deviceManager: DeviceManager;
public readonly project: Project;
private disposables: Disposable[] = [];

private followEnabled = false;

private readonly callableObjects: Map<string, object>;

constructor(private webview: Webview) {
// Set an event listener to listen for messages passed from the webview context
this._setWebviewMessageListener(webview);

// Set the manager to listen and change the persisting storage for the extension.
this.dependencyChecker = new DependencyChecker(webview);
this.dependencyChecker.setWebviewMessageListener();

this.dependencyInstaller = new DependencyInstaller(webview);
this.dependencyInstaller.setWebviewMessageListener();

this._setupEditorListeners();

this.deviceManager = new DeviceManager();
this.project = new Project(this.deviceManager);

this.disposables.push(
this.dependencyChecker,
this.dependencyInstaller,
this.deviceManager,
this.project
);

this.callableObjects = new Map([
["DeviceManager", this.deviceManager as object],
["Project", this.project as object],
]);
}

public dispose() {
// Dispose of all disposables (i.e. commands) for the current webview
while (this.disposables.length) {
const disposable = this.disposables.pop();
if (disposable) {
disposable.dispose();
}
}
}

private _setWebviewMessageListener(webview: Webview) {
webview.onDidReceiveMessage(
(message: any) => {
const command = message.command;

if (message.method !== "dispatchTouch") {
Logger.log("Message from webview", message);
}

switch (command) {
case "call":
this.handleRemoteCall(message);
return;
case "openExternalUrl":
openExternalUrl(message.url);
return;
case "stopFollowing":
this.followEnabled = false;
return;
case "startFollowing":
this.followEnabled = true;
return;
}
},
undefined,
this.disposables
);
}

private handleRemoteCall(message: any) {
const { object, method, args, callId } = message;
const callableObject = this.callableObjects.get(object);
if (callableObject && method in callableObject) {
const argsWithCallbacks = args.map((arg: any) => {
if (typeof arg === "object" && "__callbackId" in arg) {
const callbackId = arg.__callbackId;
return (...args: any[]) => {
this.webview.postMessage({
command: "callback",
callbackId,
args,
});
};
} else {
return arg;
}
});
// @ts-ignore
const result = callableObject[method](...argsWithCallbacks);
if (result instanceof Promise) {
result
.then((result) => {
this.webview.postMessage({
command: "callResult",
callId,
result,
});
})
.catch((error) => {
this.webview.postMessage({
command: "callResult",
callId,
error,
});
});
} else {
this.webview.postMessage({
command: "callResult",
callId,
result,
});
}
}
}

private _setupEditorListeners() {
extensionContext.subscriptions.push(
window.onDidChangeActiveTextEditor((editor) => {
if (editor) {
this.project.onActiveFileChange(editor.document.fileName, this.followEnabled);
}
})
);
}
}
Loading

0 comments on commit 98e1834

Please sign in to comment.