Skip to content

Commit df3484b

Browse files
committed
Fetch language versions
1 parent 6eba964 commit df3484b

File tree

4 files changed

+86
-29
lines changed

4 files changed

+86
-29
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import axios from "axios";
2+
3+
const API_URL = "https://emkc.org/api/v2/piston";
4+
5+
const pistonAxios = axios.create({ baseURL: API_URL });
6+
7+
class PistonClient {
8+
static async executeCode(language: string, version: string, sourceCode: string) {
9+
const res = await pistonAxios.post("/execute", { language, version, files: [{ content: sourceCode }] });
10+
}
11+
12+
static async getLanguageVersions() {
13+
const res = await pistonAxios.get("/runtimes");
14+
return res.data;
15+
}
16+
}
17+
18+
export default PistonClient;

frontend/src/domain/context/CollaborationContext.tsx

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@ import { WebsocketProvider } from "y-websocket";
77
import { useAuth } from "./AuthContext";
88
import { toast } from "react-toastify";
99
import { COLLABORATION_AWARENESS_KEYS, COLLABORATION_YMAP_KEYS } from "presentation/utils/constants";
10+
import PistonClient from "data/piston/PistonClient";
11+
import { Language } from "domain/entities/Language";
1012

1113
interface CollaborationContextType {
1214
initialiseEditor: (roomId: string, editor: any, monaco: Monaco) => void;
13-
selectedLanguage: string;
14-
languages: any;
15-
handleChangeLanguage: (lang: string) => void;
15+
selectedLanguage: Language;
16+
languages: Language[];
17+
handleChangeLanguage: (lang: Language) => void;
18+
handleExecuteCode: () => void;
1619
}
1720
const CollaborationContext = createContext<CollaborationContextType | undefined>(undefined);
1821

@@ -23,50 +26,56 @@ export const CollaborationProvider: React.FC<{ children: ReactNode }> = ({ child
2326
const { USERNAME } = COLLABORATION_AWARENESS_KEYS;
2427
const { SELECTED_LANGUAGE } = COLLABORATION_YMAP_KEYS;
2528

26-
const [selectedLanguage, setSelectedLanguage] = useState<string>("javascript");
27-
const [languages, setLanguages] = useState<{ label: string; value: string }[]>([]);
29+
const [selectedLanguage, setSelectedLanguage] = useState<Language>({
30+
language: "javascript",
31+
version: "",
32+
alias: "Javascript"
33+
});
34+
const [languages, setLanguages] = useState<Language[]>([]);
2835

2936
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);
3037
const monacoRef = useRef<Monaco | null>(null);
3138
const bindingRef = useRef<MonacoBinding | null>(null);
3239
const providerRef = useRef<WebsocketProvider | null>(null);
3340
const ydocRef = useRef<Y.Doc | null>(null);
34-
const yMapRef = useRef<Y.Map<string> | null>(null);
41+
const yMapRef = useRef<Y.Map<any> | null>(null);
3542

36-
const initialiseEditor = (roomId: string, editor: monaco.editor.IStandaloneCodeEditor, monaco: Monaco) => {
43+
const initialiseEditor = async (roomId: string, editor: monaco.editor.IStandaloneCodeEditor, monaco: Monaco) => {
3744
editorRef.current = editor;
3845
monacoRef.current = monaco;
3946

40-
initialiseLanguages(monaco);
47+
await initialiseLanguages(monaco);
4148
const { yDoc, provider, yMap } = initialiseYdoc(roomId);
4249
bindEditorToDoc(editor, yDoc, provider);
4350
setUpObserver(yMap);
4451
setUpConnectionAwareness(provider);
4552
};
4653

47-
const initialiseLanguages = (monaco: Monaco) => {
54+
const initialiseLanguages = async (monaco: Monaco) => {
4855
const allLanguages = monaco.languages.getLanguages();
49-
50-
// TODO: Filter all the useless ones like HTML
56+
const pistonLanguageVersions = await PistonClient.getLanguageVersions();
5157
setLanguages(
52-
allLanguages.map((lang) => ({
53-
label: lang.aliases && lang.aliases.length > 0 ? lang.aliases[0] : lang.id,
54-
value: lang.id
55-
}))
58+
allLanguages
59+
.filter((lang) => pistonLanguageVersions.some((pistonLang: any) => pistonLang.language === lang.id))
60+
.map((lang) => ({
61+
alias: lang.aliases && lang.aliases.length > 0 ? lang.aliases[0] : lang.id,
62+
language: lang.id,
63+
version: pistonLanguageVersions.find((pistonLang: any) => pistonLang.language === lang.id)?.version
64+
}))
5665
);
5766
};
5867

59-
const initialiseYdoc = (roomId: string): { yDoc: Y.Doc; yMap: Y.Map<string>; provider: WebsocketProvider } => {
68+
const initialiseYdoc = (roomId: string): { yDoc: Y.Doc; yMap: Y.Map<any>; provider: WebsocketProvider } => {
6069
const yDoc = new Y.Doc();
61-
const yMap: Y.Map<string> = yDoc.getMap("sharedMap");
70+
const yMap: Y.Map<any> = yDoc.getMap("sharedMap");
6271
ydocRef.current = yDoc;
6372
yMapRef.current = yMap;
6473
// TODO: Replace serverUrl once BE ready
6574
// Test locally across browers with 'HOST=localhost PORT 1234 npx y-websocket'
6675
const provider = new WebsocketProvider("ws://localhost:1234", roomId, yDoc);
6776
provider.on("status", (event: any) => {
6877
if (event.status === "disconnected") {
69-
toast.error("You have disconnected");
78+
//toast.error("You have disconnected");
7079
}
7180
});
7281
providerRef.current = provider;
@@ -85,7 +94,7 @@ export const CollaborationProvider: React.FC<{ children: ReactNode }> = ({ child
8594
};
8695

8796
// Observer to listen to any changes to shared state (e.g. language changes)
88-
const setUpObserver = (yMap: Y.Map<string>) => {
97+
const setUpObserver = (yMap: Y.Map<any>) => {
8998
yMap.observe((event) => {
9099
event.changes.keys.forEach((change, key) => {
91100
if (key === SELECTED_LANGUAGE) {
@@ -116,12 +125,18 @@ export const CollaborationProvider: React.FC<{ children: ReactNode }> = ({ child
116125
};
117126
}, []);
118127

119-
const handleChangeLanguage = (lang: string) => {
128+
const handleChangeLanguage = (lang: Language) => {
120129
yMapRef.current?.set(SELECTED_LANGUAGE, lang);
121130
};
122131

132+
const handleExecuteCode = () => {
133+
const sourceCode = editorRef.current?.getValue();
134+
};
135+
123136
return (
124-
<CollaborationContext.Provider value={{ initialiseEditor, selectedLanguage, languages, handleChangeLanguage }}>
137+
<CollaborationContext.Provider
138+
value={{ initialiseEditor, selectedLanguage, languages, handleChangeLanguage, handleExecuteCode }}
139+
>
125140
{children}
126141
</CollaborationContext.Provider>
127142
);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface Language {
2+
alias: string;
3+
language: string;
4+
version: string;
5+
}

frontend/src/presentation/components/CodeEditor/CodeEditor.tsx

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,39 @@
1-
import React, { useState } from "react";
1+
import React, { useMemo, useState } from "react";
22
import styles from "./CodeEditor.module.css";
33
import Editor, { Monaco } from "@monaco-editor/react";
44
import { Button, Select } from "antd";
55
import { useCollaboration } from "domain/context/CollaborationContext";
66
import { PlayCircleOutlined, CloudUploadOutlined } from "@ant-design/icons";
77
import * as monaco from "monaco-editor";
88
import { SunOutlined, MoonFilled } from "@ant-design/icons";
9+
import { Language } from "domain/entities/Language";
910

1011
interface CodeEditorProps {
1112
roomId: string;
1213
}
14+
15+
interface LanguageOption {
16+
label: string;
17+
value: string;
18+
langData: Language;
19+
}
20+
1321
const CodeEditor: React.FC<CodeEditorProps> = ({ roomId }) => {
14-
const { initialiseEditor, languages, selectedLanguage, handleChangeLanguage } = useCollaboration();
22+
const { initialiseEditor, languages, selectedLanguage, handleChangeLanguage, handleExecuteCode } =
23+
useCollaboration();
1524
const [theme, setTheme] = useState("vs-light");
16-
1725
const handleEditorDidMount = (editor: monaco.editor.IStandaloneCodeEditor, monaco: Monaco) => {
1826
initialiseEditor(roomId, editor, monaco);
1927
};
2028

29+
const languageOptions: LanguageOption[] = useMemo(() => {
30+
return languages.map((lang: Language) => ({
31+
label: lang.alias,
32+
value: lang.language,
33+
langData: lang
34+
}));
35+
}, [languages]);
36+
2137
const handleToggleTheme = () => {
2238
if (theme === "vs-light") {
2339
setTheme("vs-dark");
@@ -33,9 +49,12 @@ const CodeEditor: React.FC<CodeEditorProps> = ({ roomId }) => {
3349
variant="borderless"
3450
style={{ width: "150px" }}
3551
placeholder="Select language"
36-
options={languages}
37-
value={selectedLanguage}
38-
onChange={handleChangeLanguage}
52+
options={languageOptions}
53+
value={selectedLanguage.language}
54+
onChange={(_, option) => {
55+
const langOption = option as LanguageOption;
56+
handleChangeLanguage(langOption.langData);
57+
}}
3958
/>
4059
<div className={styles.buttonGroup}>
4160
<Button
@@ -44,7 +63,7 @@ const CodeEditor: React.FC<CodeEditorProps> = ({ roomId }) => {
4463
icon={theme === "vs-light" ? <SunOutlined /> : <MoonFilled />}
4564
/>
4665

47-
<Button className={styles.runButton} icon={<PlayCircleOutlined />}>
66+
<Button onClick={handleExecuteCode} className={styles.runButton} icon={<PlayCircleOutlined />}>
4867
Run
4968
</Button>
5069
<Button className={styles.submitButton} icon={<CloudUploadOutlined />}>
@@ -55,7 +74,7 @@ const CodeEditor: React.FC<CodeEditorProps> = ({ roomId }) => {
5574
<div className={styles.editor}>
5675
<Editor
5776
theme={theme}
58-
language={selectedLanguage}
77+
language={selectedLanguage.language}
5978
onMount={handleEditorDidMount}
6079
options={{
6180
minimap: { enabled: false },

0 commit comments

Comments
 (0)