Skip to content

Commit 10a2453

Browse files
committed
Merge main into mcq-ui
1 parent 837906a commit 10a2453

File tree

9 files changed

+234
-15
lines changed

9 files changed

+234
-15
lines changed

assets/icon.png

399 Bytes
Loading

assets/icon.svg

Lines changed: 50 additions & 0 deletions
Loading

package.json

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@
4747
".sjs"
4848
],
4949
"icon": {
50-
"light": "assets/icon.png",
51-
"dark": "assets/icon.png"
50+
"light": "assets/icon.svg",
51+
"dark": "assets/icon.svg"
5252
},
5353
"configuration": "./language-configuration.json"
5454
}
@@ -65,6 +65,31 @@
6565
"command": "source-academy.eval-editor",
6666
"key": "shift+enter"
6767
}
68+
],
69+
"views": {
70+
"source-academy": [
71+
{
72+
"id": "assessments",
73+
"name": "Assessments",
74+
"icon": "assets/icon.svg",
75+
"contextualTitle": "Source Academy"
76+
}
77+
]
78+
},
79+
"viewsContainers": {
80+
"activitybar": [
81+
{
82+
"id": "source-academy",
83+
"title": "Source Academy",
84+
"icon": "assets/icon.svg"
85+
}
86+
]
87+
},
88+
"viewsWelcome": [
89+
{
90+
"view": "assessments",
91+
"contents": "Launch the Source Academy panel to begin coding.\n[Launch!](command:source-academy.show-panel)\nNavigate to Missions page to populate this panel."
92+
}
6893
]
6994
},
7095
"scripts-info": {

src/commands/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as vscode from "vscode";
22
import { runLanguagePicker } from "./language";
33
import { evalEditor } from "./evalEditor";
44
import { showPanel } from "./showPanel";
5+
import { navigate } from "./navigate";
56

67
const EXTENSION_ID = "source-academy";
78

@@ -11,8 +12,9 @@ const EXTENSION_ID = "source-academy";
1112
*/
1213
const commands = (context: vscode.ExtensionContext) => ({
1314
pick: () => runLanguagePicker(context),
14-
"show-panel": () => showPanel(context),
15+
"show-panel": (route?: string) => showPanel(context, route),
1516
"eval-editor": () => evalEditor(context),
17+
navigate: (route: string) => navigate(context, route),
1618
});
1719

1820
/**

src/commands/navigate.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as vscode from "vscode";
2+
import { sendToFrontendWrapped } from "./showPanel";
3+
import Messages from "../utils/messages";
4+
5+
export async function navigate(
6+
context: vscode.ExtensionContext,
7+
route: string,
8+
) {
9+
const didNavigate = await sendToFrontendWrapped(Messages.Navigate(route));
10+
if (!didNavigate) {
11+
vscode.commands.executeCommand("source-academy.show-panel", route);
12+
}
13+
}

src/commands/showPanel.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,21 @@
11
import * as vscode from "vscode";
22
// Allow using JSX within this file by overriding our own createElement function
33
import React from "../utils/FakeReact";
4-
import ReactDOMServer from "react-dom/server";
54

6-
import Messages, {
7-
MessageType,
8-
MessageTypeNames,
9-
sendToFrontend,
10-
} from "../utils/messages";
5+
import { MessageType, sendToFrontend } from "../utils/messages";
116
import { LANGUAGES } from "../utils/languages";
127
import { setWebviewContent } from "../utils/webview";
138
import config from "../utils/config";
14-
import { Editor } from "../utils/editor";
159
import { FRONTEND_ELEMENT_ID } from "../constants";
16-
import _ from "lodash";
1710
import { MessageHandler } from "../utils/messageHandler";
1811

1912
let messageHandler = MessageHandler.getInstance();
13+
import { SOURCE_ACADEMY_ICON_URI } from "../extension";
2014

21-
export async function showPanel(context: vscode.ExtensionContext) {
15+
export async function showPanel(
16+
context: vscode.ExtensionContext,
17+
route?: string,
18+
) {
2219
let language: string | undefined = context.workspaceState.get("language");
2320
if (!language) {
2421
language = LANGUAGES.SOURCE_1;
@@ -43,7 +40,8 @@ export async function showPanel(context: vscode.ExtensionContext) {
4340
context.subscriptions,
4441
);
4542

46-
const frontendUrl = new URL("/playground", config.frontendBaseUrl).href;
43+
const iframeUrl = new URL(route ?? "/playground", config.frontendBaseUrl)
44+
.href;
4745

4846
setWebviewContent(
4947
messageHandler.panel,
@@ -56,7 +54,7 @@ export async function showPanel(context: vscode.ExtensionContext) {
5654
>
5755
<iframe
5856
id={FRONTEND_ELEMENT_ID}
59-
src={frontendUrl}
57+
src={iframeUrl}
6058
width="100%"
6159
height="100%"
6260
// @ts-ignore
@@ -74,6 +72,8 @@ export async function showPanel(context: vscode.ExtensionContext) {
7472
}
7573

7674
export async function sendToFrontendWrapped(message: MessageType) {
75+
sendToFrontend(messageHandler.panel, message);
76+
// TODO: This returning of status code shouldn't be necessary after refactor
7777
if (!messageHandler.panel) {
7878
console.error("ERROR: panel is not set");
7979
return;

src/extension.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
// The module 'vscode' contains the VS Code extensibility API
22
// Import the module and reference it with the alias vscode in your code below
33
import * as vscode from "vscode";
4+
import { setupTreeView } from "./treeview";
45
import { setupStatusBar } from "./statusbar/status";
5-
import { evalEditor } from "./commands/evalEditor";
66
import { registerAllCommands } from "./commands";
77
import { activateLspClient, deactivateLspClient } from "./lsp/client";
88
import { LanguageClient } from "vscode-languageclient/node";
99

1010
// TODO: Don't expose this object directly, create an interface via a wrapper class
1111
export let client: LanguageClient;
1212

13+
export let SOURCE_ACADEMY_ICON_URI: vscode.Uri;
14+
1315
// This method is called when your extension is activated
1416
// Your extension is activated the very first time the command is executed
1517
export function activate(context: vscode.ExtensionContext) {
18+
setupTreeView(context);
1619
registerAllCommands(context);
1720

1821
context.subscriptions.push(setupStatusBar(context));
@@ -25,6 +28,12 @@ export function activate(context: vscode.ExtensionContext) {
2528
const info = context.globalState.get("info") ?? {};
2629

2730
client.sendRequest("source/publishInfo", info);
31+
32+
SOURCE_ACADEMY_ICON_URI = vscode.Uri.joinPath(
33+
context.extensionUri,
34+
"assets",
35+
"icon.svg",
36+
);
2837
}
2938

3039
// This method is called when your extension is deactivated

src/treeview/index.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import * as vscode from "vscode";
2+
import { VscAssessmentOverview } from "../utils/messages";
3+
import { SOURCE_ACADEMY_ICON_URI } from "../extension";
4+
5+
export let treeDataProvider: AssessmentsSidebarProvider;
6+
7+
// This will be a source of bug on first extension loads!!
8+
let courseId: number;
9+
10+
export function setupTreeView(context: vscode.ExtensionContext) {
11+
courseId = context.globalState.get("courseId") as number;
12+
13+
treeDataProvider = new AssessmentsSidebarProvider(context);
14+
vscode.window.createTreeView("assessments", {
15+
treeDataProvider: treeDataProvider,
16+
});
17+
}
18+
19+
export class AssessmentsSidebarProvider
20+
implements vscode.TreeDataProvider<BaseTreeItem>
21+
{
22+
constructor(private context: vscode.ExtensionContext) {}
23+
24+
private _onDidChangeTreeData: vscode.EventEmitter<
25+
BaseTreeItem | undefined | null | void
26+
> = new vscode.EventEmitter<BaseTreeItem | undefined | null | void>();
27+
readonly onDidChangeTreeData: vscode.Event<
28+
BaseTreeItem | undefined | null | void
29+
> = this._onDidChangeTreeData.event;
30+
31+
getTreeItem(element: BaseTreeItem): vscode.TreeItem {
32+
return element;
33+
}
34+
35+
getChildren(element?: BaseTreeItem): Thenable<BaseTreeItem[]> {
36+
// @ts-ignore
37+
const assessmentOverviews: VscAssessmentOverview[] =
38+
this.context.globalState.get("assessmentOverviews");
39+
if (!assessmentOverviews) {
40+
return Promise.resolve([]);
41+
}
42+
43+
if (!element) {
44+
const assessmentTypes = [
45+
...new Set(assessmentOverviews.map((ao) => ao.type)),
46+
];
47+
return Promise.resolve(
48+
assessmentTypes.map((at) => new AssessmentFolder(at)),
49+
);
50+
}
51+
52+
if (element && element.type === "AssessmentFolder") {
53+
const elem = element as AssessmentFolder;
54+
55+
return Promise.resolve(
56+
assessmentOverviews
57+
.filter((ao) => ao.type == elem.assessmentType)
58+
.map((ao) => {
59+
return new AssessmentOverview(ao);
60+
}),
61+
);
62+
}
63+
return Promise.resolve([]);
64+
}
65+
66+
refresh() {
67+
this._onDidChangeTreeData.fire();
68+
}
69+
}
70+
71+
class BaseTreeItem extends vscode.TreeItem {
72+
type?: string;
73+
74+
constructor(
75+
public readonly label: string,
76+
public readonly collapsibleState: vscode.TreeItemCollapsibleState,
77+
) {
78+
super(label, collapsibleState);
79+
}
80+
81+
iconPath = {
82+
light: SOURCE_ACADEMY_ICON_URI,
83+
dark: SOURCE_ACADEMY_ICON_URI,
84+
};
85+
}
86+
87+
class AssessmentFolder extends BaseTreeItem {
88+
constructor(public readonly assessmentType: string) {
89+
super(assessmentType, vscode.TreeItemCollapsibleState.Collapsed);
90+
this.type = "AssessmentFolder";
91+
}
92+
}
93+
94+
class AssessmentOverview extends BaseTreeItem {
95+
constructor(assessmentOverview: VscAssessmentOverview) {
96+
super(assessmentOverview.title, vscode.TreeItemCollapsibleState.None);
97+
98+
this.type = "AssessmentOverview";
99+
this.description = assessmentOverview.closeAt;
100+
// this.tooltip = "if needed in the future"
101+
102+
this.command = {
103+
title: "Navigate",
104+
command: "source-academy.navigate",
105+
arguments: [
106+
`/courses/${courseId}/${assessmentOverview.type.toLowerCase()}/${assessmentOverview.id}/0`,
107+
],
108+
};
109+
}
110+
}

src/utils/messageHandler.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { setWebviewContent } from "./webview";
1212
import { Editor } from "./editor";
1313
import { client } from "../extension";
1414
import _ from "lodash";
15+
import { treeDataProvider } from "../treeview";
1516
import McqPanel from "../webview/components/McqPanel";
1617

1718
/*
@@ -165,6 +166,15 @@ export class MessageHandler {
165166
sendToFrontend(this.panel, message);
166167
});
167168
break;
169+
case MessageTypeNames.NotifyAssessmentsOverview:
170+
const { assessmentOverviews, courseId } = message;
171+
context.globalState.update(
172+
"assessmentOverviews",
173+
assessmentOverviews,
174+
);
175+
context.globalState.update("courseId", courseId);
176+
treeDataProvider.refresh();
177+
break;
168178
}
169179
console.log(`${Date.now()} Finish handleMessage: ${message.type}`);
170180
}

0 commit comments

Comments
 (0)