Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
Loading items

Target

Select target project
  • deadlock-public/deadlock-desktop
1 result
Select Git revision
Loading items
Show changes
Commits on Source (2)
Showing
with 138 additions and 168 deletions
......@@ -14,7 +14,7 @@ export default class ApiService {
private axiosInstance: AxiosInstance;
private static _instance: ApiService;
static getInstance(): ApiService {
static get instance(): ApiService {
if (!ApiService._instance) {
ApiService._instance = new ApiService();
}
......@@ -33,18 +33,6 @@ export default class ApiService {
this.initApiInterceptor();
}
private get controller(): Controller {
return Controller.getInstance();
}
private get extensionStore(): ExtensionStore {
return ExtensionStore.getInstance();
}
private get keycloackConnection(): KeycloakOAuth2DeviceFlowConnection {
return KeycloakOAuth2DeviceFlowConnection.getInstance();
}
initApiInterceptor() {
this.initRequestInterceptor();
this.initResponseInterceptor();
......@@ -53,7 +41,7 @@ export default class ApiService {
private initRequestInterceptor() {
this.axiosInstance.interceptors.request.use(
async (config) => {
const accessToken = await this.extensionStore.getAccessToken();
const accessToken = await ExtensionStore.instance.getAccessToken();
if (accessToken && config.headers) {
config.headers['authorization'] = `BEARER ${accessToken}`;
}
......@@ -98,14 +86,14 @@ export default class ApiService {
private async onRetry(originalConfig) {
try {
const storedRefreshToken = await this.extensionStore.getRefreshToken();
const { accessToken, refreshToken } = await this.keycloackConnection.getToken({
const storedRefreshToken = await ExtensionStore.instance.getRefreshToken();
const { accessToken, refreshToken } = await KeycloakOAuth2DeviceFlowConnection.instance.getToken({
refreshToken: storedRefreshToken,
openLink: Controller.openBrowserWithUrl,
});
await this.extensionStore.setAccessToken(accessToken);
await this.extensionStore.setRefreshToken(refreshToken);
await ExtensionStore.instance.setAccessToken(accessToken);
await ExtensionStore.instance.setRefreshToken(refreshToken);
this.axiosInstance.defaults.headers.common['authorization'] = `BEARER ${accessToken}`;
return this.axiosInstance(originalConfig);
......@@ -125,7 +113,7 @@ export default class ApiService {
private async onInvalidRefreshToken(originalConfig) {
try {
await this.controller.authenticate();
await Controller.instance.authenticate();
return this.axiosInstance(originalConfig);
} catch (_error) {
return Promise.reject(_error);
......@@ -161,7 +149,7 @@ export default class ApiService {
async pingUpdateWorkTime(): Promise<MissionUser> {
return this.axiosInstance
.post<MissionUser>(
`users/${(await this.getCurrentUser()).id}/missions/${UserMission.getInstance().missionId}/worktime`,
`users/${(await this.getCurrentUser()).id}/missions/${UserMission.instance.missionId}/worktime`,
{
action: 'PING',
},
......@@ -171,7 +159,7 @@ export default class ApiService {
async startUpdateWorkTime() {
return this.axiosInstance
.post(`users/${(await this.getCurrentUser()).id}/missions/${UserMission.getInstance().missionId}/worktime`, {
.post(`users/${(await this.getCurrentUser()).id}/missions/${UserMission.instance.missionId}/worktime`, {
action: 'START',
})
.then((res) => res.data);
......@@ -180,7 +168,7 @@ export default class ApiService {
async submitAttempt(attempt: Attempt): Promise<MissionUser> {
return this.axiosInstance
.post<MissionUser>(
`/users/${(await this.getCurrentUser()).id}/missions/${UserMission.getInstance().missionId}/solve/desktop`,
`/users/${(await this.getCurrentUser()).id}/missions/${UserMission.instance.missionId}/solve/desktop`,
attempt,
)
.then((res) => res.data);
......
import { Command, commands } from 'vscode';
import Controller from './controller';
export class CommandHandler {
constructor(private controller: Controller) {
this.initCommandHandler();
}
private static _instance?: CommandHandler;
public readonly disconnectCommand: Command;
public readonly authenticateCommand: Command = { title: 'Authenticate', command: 'deadlock.authenticate' };
public readonly openUrlInBrowserCommand: Command = { title: 'Open url in browser', command: 'vscode.open' };
initCommandHandler() {
commands.registerCommand(disconnectCommand.command, this.controller.disconnect.bind(this.controller));
commands.registerCommand(authenticateCommand.command, this.controller.authenticate.bind(this.controller));
public static get instance(): CommandHandler {
if (!this._instance) {
this._instance = new CommandHandler();
}
return this._instance;
}
export const disconnectCommand: Command = { title: 'Disconnect', command: 'deadlock.disconnect' };
export const authenticateCommand: Command = { title: 'Authenticate', command: 'deadlock.authenticate' };
export const openUrlInBrowserCommand: Command = { title: 'Open url in browser', command: 'vscode.open' };
private constructor() {
this.disconnectCommand = { title: 'Disconnect', command: 'deadlock.disconnect' };
this.authenticateCommand = { title: 'Authenticate', command: 'deadlock.authenticate' };
this.openUrlInBrowserCommand = { title: 'Open url in browser', command: 'vscode.open' };
commands.registerCommand(this.disconnectCommand.command, Controller.instance.disconnect.bind(Controller.instance));
commands.registerCommand(
this.authenticateCommand.command,
Controller.instance.authenticate.bind(Controller.instance),
);
}
}
import BriefingView from '../view/briefingView';
import QuickSetupView from '../view/quickSetupView';
import { CommandHandler, openUrlInBrowserCommand } from './commandHandler';
import { CommandHandler } from './commandHandler';
import ExtensionStore from './extensionStore';
import KeycloakOAuth2DeviceFlowConnection from './keycloakOAuth2DeviceFlowConnection';
import { MissionDevContainer } from './mission/missionDevContainer';
......@@ -20,43 +20,25 @@ import Mission from '../model/mission';
import ApiService from './api.service';
export default class Controller {
private static instance: Controller;
private commandHandler: CommandHandler;
private static _instance: Controller;
private briefingView?: BriefingView;
private quickSetupView: QuickSetupView;
private _extensionStore: ExtensionStore;
public get extensionStore() {
return this._extensionStore;
public constructor(public readonly context: ExtensionContext) {
if (Controller._instance) {
throw new Error('Controller is a singleton');
}
private get connection(): KeycloakOAuth2DeviceFlowConnection {
return KeycloakOAuth2DeviceFlowConnection.getInstance();
}
private get apiService(): ApiService {
return ApiService.getInstance();
}
private constructor(public readonly context: ExtensionContext) {
this._extensionStore = ExtensionStore.getInstance(context);
Controller._instance = this;
if (isDocker()) {
this.briefingView = new BriefingView();
}
this.quickSetupView = new QuickSetupView(context.extensionUri);
this.commandHandler = new CommandHandler(this);
this.quickSetupView = new QuickSetupView();
this.init();
}
public static getInstance(context?: ExtensionContext): Controller {
if (!Controller.instance) {
if (context === undefined) {
throw new Error('Controller needs a context when instantiated');
}
Controller.instance = new Controller(context);
}
return Controller.instance;
public static get instance(): Controller {
return Controller._instance;
}
getChallenge(missionId: string): Mission | undefined {
......@@ -67,7 +49,7 @@ export default class Controller {
public async getSolution(missionId: string, reviewedId: string) {
try {
const solution = await this.apiService.getSolution(missionId);
const solution = await ApiService.instance.getSolution(missionId);
const solutionFolder = join(getReviewedStudentWorkdirPath(reviewedId), missionId, '.solution');
const tarName = 'solution.tar.gz';
const tarPath = join(solutionFolder, tarName);
......@@ -122,7 +104,7 @@ export default class Controller {
},
});
this.quickSetupView.isAlreadyConnected = (await this._extensionStore.getAccessToken()) !== undefined;
this.quickSetupView.isAlreadyConnected = (await ExtensionStore.instance.getAccessToken()) !== undefined;
}
public async disconnect() {
......@@ -139,7 +121,7 @@ export default class Controller {
extensionWarn(e);
}
try {
await this.extensionStore.clear();
await ExtensionStore.instance.clear();
} catch (e) {
extensionWarn('Could not clear extension store');
extensionWarn(e);
......@@ -148,21 +130,23 @@ export default class Controller {
}
public async createSshKeyPair() {
const { publicKey, privateKey } = await this.apiService.getUserSshKey();
const { publicKey, privateKey } = await ApiService.instance.getUserSshKey();
await createSshKeyFiles(publicKey, privateKey);
}
public async authenticate() {
await this.connection.registerDevice();
const tokens = await this.connection.getToken({ openLink: Controller.openBrowserWithUrl });
await this._extensionStore.setAccessToken(tokens.accessToken);
await this._extensionStore.setRefreshToken(tokens.refreshToken);
await KeycloakOAuth2DeviceFlowConnection.instance.registerDevice();
const tokens = await KeycloakOAuth2DeviceFlowConnection.instance.getToken({
openLink: Controller.openBrowserWithUrl,
});
await ExtensionStore.instance.setAccessToken(tokens.accessToken);
await ExtensionStore.instance.setRefreshToken(tokens.refreshToken);
await this.createSshKeyPair();
this.quickSetupView.isAlreadyConnected = true;
}
public static openBrowserWithUrl(url: string) {
commands.executeCommand(openUrlInBrowserCommand.command, Uri.parse(url));
commands.executeCommand(CommandHandler.instance.openUrlInBrowserCommand.command, Uri.parse(url));
}
public async launchMission({
......@@ -178,30 +162,33 @@ export default class Controller {
}) {
window.showInformationMessage(`vous lancez la mission ${missionId}`);
const hadBeenConnected = (await this._extensionStore.getAccessToken()) !== undefined;
const hadBeenConnected = (await ExtensionStore.instance.getAccessToken()) !== undefined;
if (!hadBeenConnected) {
await this.authenticate();
window.showInformationMessage('Connexion validée');
}
const reviewer = await this.apiService.getCurrentUser();
const reviewer = await ApiService.instance.getCurrentUser();
if (!!revieweeId && revieweeId !== reviewer.id) {
try {
await this.apiService.grantAccessToRepository(revieweeId, missionId);
await ApiService.instance.grantAccessToRepository(revieweeId, missionId);
} catch (e) {
if (hasStatusNumber(e)) {
if (e.status === 403) {
window.showErrorMessage(`Vous n'avez pas accès à la mission: ${missionId}`);
return;
} else {
window.showErrorMessage(`L'élève n'a pas commencé l'exercice: ${missionId}`);
throw e;
}
} else {
throw e;
}
}
try {
await Controller.getInstance().getSolution(missionId, revieweeId);
await this.getSolution(missionId, revieweeId);
} catch (e) {
window.showErrorMessage(`Une erreur est survenue lors de la récupération de la solution`);
extensionError(e);
......
import { ExtensionContext, SecretStorage } from 'vscode';
import { SecretStorage } from 'vscode';
import { extensionLog as log } from '../recorder/utils/log';
import Controller from './controller';
export default class ExtensionStore {
private static instance: ExtensionStore;
private static _instance: ExtensionStore;
private secretStorage: SecretStorage;
private constructor(context: ExtensionContext) {
this.secretStorage = context.secrets;
private constructor() {
this.secretStorage = Controller.instance.context.secrets;
}
public async clear() {
......@@ -16,13 +17,12 @@ export default class ExtensionStore {
this.secretStorage.delete(SecretStoreKey.RefreshTokenKey);
}
public static getInstance(context?: ExtensionContext): ExtensionStore {
if (!ExtensionStore.instance) {
if (!context) throw new Error('ExtensionStore should be initiate with a storage first time');
ExtensionStore.instance = new ExtensionStore(context);
public static get instance(): ExtensionStore {
if (!ExtensionStore._instance) {
ExtensionStore._instance = new ExtensionStore();
}
return ExtensionStore.instance;
return ExtensionStore._instance;
}
public getAccessToken(): Thenable<string | undefined> {
......
......@@ -13,10 +13,18 @@ const defaultRemote = 'origin';
type Branch = 'master' | 'live';
export default class GitMission {
private static _instance: GitMission;
public static get instance(): GitMission {
if (!GitMission._instance) {
GitMission._instance = new GitMission();
}
return GitMission._instance;
}
private prefix = 'DEADLOCK-RECORDER';
private git: SimpleGit;
constructor() {
private constructor() {
const options: Partial<SimpleGitOptions> = {
baseDir: GITEA_PATH_IC,
binary: 'git',
......@@ -36,7 +44,7 @@ export default class GitMission {
async setupSshAgent() {
try {
const gitea = await ApiService.getInstance().getGiteaPublicProperties();
const gitea = await ApiService.instance.getGiteaPublicProperties();
await exec(`ssh-add /tmp/.ssh/id_rsa`);
await exec(`eval "$(ssh-agent -s)" && ssh-keyscan -p ${gitea.sshPort} -H ${gitea.sshHost} >> ~/.ssh/known_hosts`);
} catch (err) {
......@@ -58,7 +66,7 @@ export default class GitMission {
}
async getAuthor(): Promise<string> {
return (await UserMission.getInstance().getGiteaUser()).username;
return (await UserMission.instance.getGiteaUser()).username;
}
async init() {
......@@ -73,10 +81,10 @@ export default class GitMission {
return Promise.resolve(this);
}
const giteaUser = await UserMission.getInstance().getGiteaUser();
const giteaUser = await UserMission.instance.getGiteaUser();
await this.git.addRemote(defaultRemote, await this.getRemotePath());
await this.git.addConfig('user.email', await UserMission.getInstance().getEmail(), false, 'local');
await this.git.addConfig('user.email', await UserMission.instance.getEmail(), false, 'local');
await this.git.addConfig(
'user.name',
`${giteaUser.details.lastName} ${giteaUser.details.firstName}`,
......@@ -93,10 +101,10 @@ export default class GitMission {
}
private async getRemotePath() {
const giteaConfig = await ApiService.getInstance().getGiteaPublicProperties();
const giteaConfig = await ApiService.instance.getGiteaPublicProperties();
return `ssh://git@${giteaConfig.sshHost}:${giteaConfig.sshPort}/${
(await UserMission.getInstance().getGiteaUser()).username
}/${UserMission.getInstance().missionId}`;
(await UserMission.instance.getGiteaUser()).username
}/${UserMission.instance.missionId}`;
}
async readRemote() {
......
......@@ -35,7 +35,7 @@ export default class KeycloakOAuth2DeviceFlowConnection {
this.deviceAuthorizationRequestResponse = {};
}
public static getInstance(): KeycloakOAuth2DeviceFlowConnection {
public static get instance(): KeycloakOAuth2DeviceFlowConnection {
if (!KeycloakOAuth2DeviceFlowConnection._instance) {
KeycloakOAuth2DeviceFlowConnection._instance = new KeycloakOAuth2DeviceFlowConnection();
}
......
......@@ -24,19 +24,15 @@ export default class UserMission implements UserMissionModel {
UserMission._instance = this;
}
public static getInstance(): UserMission {
public static get instance(): UserMission {
if (!UserMission._instance) {
return (this._instance = new UserMission(JSON.parse(readFileSync(USER_MISSION_PATH, 'utf8'))));
}
return UserMission._instance;
}
private get apiService(): ApiService {
return ApiService.getInstance();
}
public async getGiteaUser(): Promise<User & { username: string }> {
const giteaUser: User & { username?: string } = this.reviewee ?? (await this.apiService.getCurrentUser());
const giteaUser: User & { username?: string } = this.reviewee ?? (await ApiService.instance.getCurrentUser());
return { ...giteaUser, username: giteaUser.id.split('-').join('') };
}
......@@ -69,7 +65,7 @@ export default class UserMission implements UserMissionModel {
missionId,
storyId,
missionVersion,
reviewee: revieweeId ? await ApiService.getInstance().getUser(revieweeId) : undefined,
reviewee: revieweeId ? await ApiService.instance.getUser(revieweeId) : undefined,
} as UserMissionModel,
null,
2,
......
import { window, ExtensionContext, workspace, commands } from 'vscode';
import Controller from './core/controller';
import isDocker from './core/utils/isdocker';
import Recorder from './recorder/recorder';
import { extensionError as error } from './recorder/utils/log';
import { DepNodeProvider } from './view/deadlockPanel';
import { CommandTreeProvider } from './view/CommandTree';
import StartedMissionsView from './view/startedMissionsView';
import Controller from './core/controller';
export async function activate(context: ExtensionContext) {
new Controller(context);
window.showInformationMessage('Bienvenue sur Deadlock!');
Controller.getInstance(context);
const workspaceFolders = workspace.workspaceFolders?.toString() ?? '';
if (!workspaceFolders) window.showInformationMessage('Pas de répertoires ouverts');
const deadlockPanelProvider = new DepNodeProvider();
window.registerTreeDataProvider('deadlockPanel', deadlockPanelProvider);
window.registerWebviewViewProvider('startedMissions', new StartedMissionsView(context));
window.registerWebviewViewProvider('startedMissions', new StartedMissionsView());
if (isDocker()) {
commands.executeCommand('setContext', 'deadlock.inContainer', true);
......
......@@ -8,13 +8,13 @@ import aquirePermissions from './utils/permission';
import UserMission from '../core/mission/model/userMission';
import ApiService from '../core/api.service';
import { window } from 'vscode';
import { pushOnCommitQueueIfNotReviewing } from './utils/gitea';
export default class Recorder {
private _gitMission?: GitMission;
private static _instance?: Recorder;
private get userMission(): UserMission {
return UserMission.getInstance();
return UserMission.instance;
}
private constructor() {
......@@ -28,10 +28,6 @@ export default class Recorder {
return this._instance;
}
public get gitMission(): GitMission {
return this._gitMission!;
}
async setupProject(gitMission: GitMission) {
const argsArray: string[] = [`user-git-${(await this.userMission.getGiteaUser()).username}`];
......@@ -39,11 +35,7 @@ export default class Recorder {
argsArray.push('.git');
}
await copyProjectSources(
GITEA_PATH_IC,
MISSION_PATH_IC,
...argsArray
);
await copyProjectSources(GITEA_PATH_IC, MISSION_PATH_IC, ...argsArray);
if (!this.userMission.isReviewing()) {
renameTempToUserGitFiles(MISSION_PATH_IC, await gitMission.getAuthor());
......@@ -67,13 +59,16 @@ export default class Recorder {
async run() {
try {
await aquirePermissions();
// TODO refactor make it as a sigleton
this._gitMission = await new GitMission().init();
await this.setupFromRemoteRepo(this._gitMission);
await this.setupProject(this._gitMission);
await GitMission.instance.init();
const isStarted = await GitMission.instance.isRemoteRepoExist();
await this.setupFromRemoteRepo(GitMission.instance);
await this.setupProject(GitMission.instance);
if (!isStarted) {
pushOnCommitQueueIfNotReviewing(GitMission.instance, 'Auto');
}
if (!this.userMission.isReviewing()) {
if (ENABLE_AUTOMATIC_SAVE) {
new AutomaticSave(MISSION_PATH_IC, this._gitMission);
new AutomaticSave(MISSION_PATH_IC, GitMission.instance);
}
try {
runTimer();
......@@ -82,7 +77,7 @@ export default class Recorder {
error(e);
}
} else {
await this.gitMission.forgetSshKeys();
await GitMission.instance.forgetSshKeys();
}
} catch (e) {
error('Cannot setup user repo.');
......@@ -92,7 +87,7 @@ export default class Recorder {
}
export async function runTimer() {
const apiService = ApiService.getInstance();
const apiService = ApiService.instance;
try {
await apiService.startUpdateWorkTime();
setInterval(async () => {
......
......@@ -40,7 +40,7 @@ export async function pushOnCommitQueueIfNotReviewing(
from: 'Run' | 'Auto' | 'HttpServer',
callback?: (commitResult: CommitResult) => void,
) {
if (!UserMission.getInstance().isReviewing()) {
if (!UserMission.instance.isReviewing()) {
await pushOnCommitQueue(gitMission, from, callback);
}
}
......
......@@ -4,6 +4,7 @@ import { join } from 'path';
import { log } from './log';
import { promisify } from 'util';
import { mkdir, rm } from 'fs/promises';
import Controller from '../../core/controller';
const exec = promisify(execCallback);
const PREFIX = '[DEADLOCK-RECORDER]';
......@@ -88,3 +89,7 @@ export async function emptyDirectories(...directoryPaths: string[]) {
export async function createDirectories(...directoryPaths: string[]) {
return Promise.all(directoryPaths.map((directoryPath) => mkdir(directoryPath, { recursive: true })));
}
export function getExtensionUri() {
return Controller.instance.context.extensionUri;
}
import { MissionCommand, MissionHandler } from '../model/mission';
import { commands, Event, EventEmitter, ThemeColor, ThemeIcon, TreeDataProvider, TreeItem, window } from 'vscode';
import Recorder from '../recorder/recorder';
import { MISSION_PATH_IC } from '../core/config';
import { ChildProcessWithoutNullStreams, spawn } from 'child_process';
import AttemptBuilder from '../model/attempt';
import ApiService from '../core/api.service';
import UserMission from '../core/mission/model/userMission';
import { pushOnCommitQueueIfNotReviewing } from '../recorder/utils/gitea';
import GitMission from '../core/gitMission';
export class CommandTreeProvider implements TreeDataProvider<CommandItem> {
getTreeItem(element: CommandItem): TreeItem {
......@@ -103,10 +103,10 @@ class CommandItem extends TreeItem {
const output = window.createOutputChannel(`${outputPrefix} - ${this.missionCommand.name}`);
output.show();
const attempt = new AttemptBuilder(
(await ApiService.getInstance().getCurrentUser()).id,
UserMission.getInstance().missionId,
(await ApiService.instance.getCurrentUser()).id,
UserMission.instance.missionId,
this.missionCommand.type === 'submit',
UserMission.getInstance().storyId,
UserMission.instance.storyId,
);
this.spawnHandler = spawn(this.parced.cmd, this.parced.args, {
cwd: MISSION_PATH_IC,
......@@ -123,8 +123,8 @@ class CommandItem extends TreeItem {
output.appendLine(`${outputPrefix} - ${this.missionCommand.name} - exited with code ${code}`);
attempt.setExitCode(code ?? undefined);
if (this.missionCommand.type === 'submit' || this.missionCommand.type === 'run') {
pushOnCommitQueueIfNotReviewing(Recorder.instance.gitMission, 'Run', async (commit) => {
await ApiService.getInstance().submitAttempt(attempt.build(commit?.commit));
pushOnCommitQueueIfNotReviewing(GitMission.instance, 'Run', async (commit) => {
await ApiService.instance.submitAttempt(attempt.build(commit?.commit));
});
}
this.isRunning = false;
......
......@@ -69,10 +69,10 @@ export default class BriefingView extends WebviewBase {
async render() {
let output = '';
const missionUser = UserMission.getInstance();
const missionUser = UserMission.instance;
if (missionUser.isReviewing()) {
const user = await ApiService.getInstance().getCurrentUser();
const user = await ApiService.instance.getCurrentUser();
const giteaUser = await missionUser.getGiteaUser();
output += `
<h2>Professeur</h2>
......
import { commands, Uri } from 'vscode';
import { authenticateCommand, disconnectCommand } from '../core/commandHandler';
import { commands } from 'vscode';
import { CommandHandler } from '../core/commandHandler';
import { openQuickSetupCommand } from '../core/controller';
import ExtensionStore from '../core/extensionStore';
import { extensionLog as log } from '../recorder/utils/log';
import { WebviewBase } from './webviewBase';
export const quickSetupId = 'quickSetup';
export default class QuickSetupView extends WebviewBase {
private extensionUri: Uri;
private extensionStore: ExtensionStore;
private _isAlreadyConnected: boolean;
constructor(extensionUri: Uri) {
constructor() {
super(quickSetupId, 'QuickSetup', openQuickSetupCommand);
this.extensionUri = extensionUri;
this.extensionStore = ExtensionStore.getInstance();
this._isAlreadyConnected = false;
}
......@@ -87,7 +82,7 @@ export default class QuickSetupView extends WebviewBase {
}
renderHeaderHtml() {
const toolkitUri = this.getExternalRessourcePath(this.extensionUri, [
const toolkitUri = this.getExternalRessourcePath([
'node_modules',
'@vscode',
'webview-ui-toolkit',
......@@ -95,13 +90,9 @@ export default class QuickSetupView extends WebviewBase {
'toolkit.js',
]);
const customStyle = this.getExternalRessourcePath(this.extensionUri, [
'resources',
'styles',
'gettingStartedView.css',
]);
const customStyle = this.getExternalRessourcePath(['resources', 'styles', 'gettingStartedView.css']);
const customScript = this.getExternalRessourcePath(this.extensionUri, ['resources', 'js', 'gettingStartedView.js']);
const customScript = this.getExternalRessourcePath(['resources', 'js', 'gettingStartedView.js']);
return `
<meta charset="UTF-8">
......@@ -114,10 +105,10 @@ export default class QuickSetupView extends WebviewBase {
onMessageReceive(message: any): void {
switch (message.command) {
case 'openAuthenticationPageAction':
commands.executeCommand(authenticateCommand.command);
commands.executeCommand(CommandHandler.instance.authenticateCommand.command);
return;
case 'disconnectUserAction':
commands.executeCommand(disconnectCommand.command);
commands.executeCommand(CommandHandler.instance.disconnectCommand.command);
return;
}
}
......
import { randomBytes } from 'crypto';
import { existsSync, readdirSync, readFileSync } from 'fs';
import { join } from 'path';
import { ExtensionContext, Webview, WebviewView, WebviewViewProvider } from 'vscode';
import { Webview, WebviewView, WebviewViewProvider } from 'vscode';
import Controller from '../core/controller';
import { extensionWarn } from '../recorder/utils/log';
import { getUri } from './webviewBase';
import { missionWorkdir } from '../core/config';
import UserMission from '../core/mission/model/userMission';
import { getExtensionUri } from '../recorder/utils/workdir';
export default class StartedMissionsView implements WebviewViewProvider {
private readonly controller: Controller;
constructor(private context: ExtensionContext) {
this.controller = Controller.getInstance(context);
}
resolveWebviewView(webviewView: WebviewView): void | Thenable<void> {
webviewView.webview.options = {
enableScripts: true,
localResourceRoots: [this.context.extensionUri],
localResourceRoots: [getExtensionUri()],
};
webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
webviewView.webview.onDidReceiveMessage(this.onMessageReceive.bind(this));
......@@ -28,7 +23,7 @@ export default class StartedMissionsView implements WebviewViewProvider {
case 'openMission': {
const path = join(missionWorkdir, message.mission, '.config', 'user-challenge.json');
const userMission: UserMission = JSON.parse(readFileSync(path, 'utf8'));
Controller.getInstance().launchMission({
Controller.instance.launchMission({
missionId: userMission.missionId,
missionVersion: userMission.missionVersion,
storyId: userMission.storyId,
......@@ -39,17 +34,11 @@ export default class StartedMissionsView implements WebviewViewProvider {
}
private _getHtmlForWebview(webview: Webview) {
const toolkitUri = getUri(webview, this.context.extensionUri, [
'node_modules',
'@vscode',
'webview-ui-toolkit',
'dist',
'toolkit.js',
]);
const toolkitUri = getUri(webview, ['node_modules', '@vscode', 'webview-ui-toolkit', 'dist', 'toolkit.js']);
const css = getUri(webview, this.context.extensionUri, ['resources', 'styles', 'startedMissionsView.css']);
const css = getUri(webview, ['resources', 'styles', 'startedMissionsView.css']);
const js = getUri(webview, this.context.extensionUri, ['resources', 'js', 'startedMissionsView.js']);
const js = getUri(webview, ['resources', 'js', 'startedMissionsView.js']);
// find all folders in mission directory
let missionsHtml = '';
......
'use strict';
import {
Command,
commands,
......@@ -10,6 +9,7 @@ import {
WebviewPanelOnDidChangeViewStateEvent,
window,
} from 'vscode';
import { getExtensionUri } from '../recorder/utils/workdir';
const emptyCommands: Disposable[] = [
{
......@@ -19,8 +19,8 @@ const emptyCommands: Disposable[] = [
},
];
export function getUri(webview: Webview, extensionUri: Uri, pathList: string[]) {
return webview.asWebviewUri(Uri.joinPath(extensionUri, ...pathList));
export function getUri(webview: Webview, pathList: string[]) {
return webview.asWebviewUri(Uri.joinPath(getExtensionUri(), ...pathList));
}
export abstract class WebviewBase implements Disposable {
......@@ -33,8 +33,8 @@ export abstract class WebviewBase implements Disposable {
this.load();
}
getExternalRessourcePath(extensionUri: Uri, pathList: string[]) {
return getUri(this.panel!.webview, extensionUri, pathList);
getExternalRessourcePath(pathList: string[]) {
return getUri(this.panel!.webview, pathList);
}
registerCommands(): Disposable[] {
......