Skip to content

Commit b3f8ed2

Browse files
committed
Implement retrieval of playground configuration from backend UUID and reinstate configuration by hash parameters
1 parent d63f217 commit b3f8ed2

File tree

13 files changed

+163
-255
lines changed

13 files changed

+163
-255
lines changed

src/commons/controlBar/ControlBarShareButton.tsx

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,11 @@ import { externalUrlShortenerRequest } from '../sagas/PlaygroundSaga';
1212
import { postSharedProgram } from '../sagas/RequestsSaga';
1313
import Constants, { Links } from '../utils/Constants';
1414
import { showSuccessMessage, showWarningMessage } from '../utils/notifications/NotificationsHelper';
15-
import { request } from '../utils/RequestHelper';
16-
import { RemoveLast } from '../utils/TypeHelper';
1715

1816
type ControlBarShareButtonProps = {
1917
isSicp?: boolean;
2018
};
2119

22-
type ShareLinkRequestHelperParams = RemoveLast<Parameters<typeof request>>;
23-
24-
export const requestToShareProgram = async (
25-
...[path, method, opts]: ShareLinkRequestHelperParams
26-
) => {
27-
const resp = await request(path, method, opts);
28-
return resp;
29-
};
30-
3120
/**
3221
* Generates the share link for programs in the Playground.
3322
*

src/commons/sagas/RequestsSaga.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { call } from 'redux-saga/effects';
22
import { backendParamsToProgressStatus } from 'src/features/grading/GradingUtils';
3+
import { ShareLinkState } from 'src/features/playground/shareLinks/ShareLinkState';
34
import { OptionType } from 'src/pages/academy/teamFormation/subcomponents/TeamFormationForm';
45

56
import {
@@ -1661,6 +1662,26 @@ export async function deleteDevice(device: Pick<Device, 'id'>, tokens?: Tokens):
16611662
return true;
16621663
}
16631664

1665+
/**
1666+
* GET /shared_programs/:uuid
1667+
*/
1668+
export async function getSharedProgram(uuid: string, tokens?: Tokens): Promise<ShareLinkState> {
1669+
tokens = fillTokens(tokens);
1670+
const resp = await request(`shared_programs/${uuid}`, 'GET', {
1671+
...tokens
1672+
});
1673+
1674+
if (!resp) {
1675+
throw new Error('Failed to fetch program from shared link!');
1676+
}
1677+
1678+
if (!resp.ok) {
1679+
throw new Error('Invalid shared link!');
1680+
}
1681+
1682+
return resp.json();
1683+
}
1684+
16641685
/**
16651686
* POST /shared_programs
16661687
*/
Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
1-
type ShareLinkState = Partial<{
2-
isFolder: string;
3-
tabs: string;
4-
tabIdx: string;
1+
import { Chapter, Variant } from 'js-slang/dist/types';
2+
3+
export type ShareLinkState = {
4+
isFolder: boolean;
5+
files: Record<string, string>;
6+
tabs: string[];
7+
tabIdx: number | null;
8+
chap: Chapter;
9+
variant: Variant;
10+
exec: number;
11+
};
12+
13+
export type ParsedIntermediateShareLinkState = {
14+
isFolder?: string;
15+
files?: string;
16+
tabs?: string[];
17+
tabIdx?: string;
518
chap: string;
619
variant: string;
7-
ext: string;
820
exec: string;
9-
files: string;
10-
prgrm: string;
11-
}>;
12-
13-
export default ShareLinkState;
21+
prgrm?: string; // for backwards compatibility of old hash parameter shared links
22+
};

src/features/playground/shareLinks/decoder/Decoder.ts

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import ShareLinkState from '../ShareLinkState';
1+
import { Chapter, Variant } from 'js-slang/dist/types';
2+
import { decompressFromEncodedURIComponent } from 'lz-string';
3+
import { getDefaultFilePath } from 'src/commons/application/ApplicationTypes';
4+
import { convertParamToBoolean, convertParamToInt } from 'src/commons/utils/ParamParseHelper';
5+
import { parseQuery } from 'src/commons/utils/QueryHelper';
6+
import { WorkspaceLocation } from 'src/commons/workspace/WorkspaceTypes';
7+
8+
import { ShareLinkState } from '../ShareLinkState';
29
import DecoderDelegate from './delegates/DecoderDelegate';
310

411
/**
@@ -11,8 +18,34 @@ class ShareLinkStateDecoder {
1118
this.encodedString = encodedString;
1219
}
1320

14-
decodeWith(decoderDelegate: DecoderDelegate): ShareLinkState {
15-
return decoderDelegate.decode(this.encodedString);
21+
decodeWith(
22+
decoderDelegate: DecoderDelegate,
23+
workspaceLocation: WorkspaceLocation
24+
): ShareLinkState {
25+
const parsedObject = decoderDelegate.decode(this.encodedString);
26+
27+
// For backward compatibility with old share links - 'prgrm' is no longer used.
28+
const program =
29+
parsedObject.prgrm === undefined ? '' : decompressFromEncodedURIComponent(parsedObject.prgrm);
30+
31+
// By default, create just the default file.
32+
const defaultFilePath = getDefaultFilePath(workspaceLocation);
33+
const filesObject: Record<string, string> =
34+
parsedObject.files === undefined
35+
? {
36+
[defaultFilePath]: program
37+
}
38+
: parseQuery(decompressFromEncodedURIComponent(parsedObject.files));
39+
40+
return {
41+
chap: convertParamToInt(parsedObject.chap) ?? Chapter.SOURCE_1,
42+
exec: Math.max(convertParamToInt(parsedObject.exec) || 1000, 1000),
43+
files: filesObject,
44+
isFolder: convertParamToBoolean(parsedObject.isFolder) ?? false,
45+
tabIdx: convertParamToInt(parsedObject.tabIdx) ?? 0, // By default, use the first editor tab.
46+
tabs: parsedObject.tabs?.map(decompressFromEncodedURIComponent) ?? [defaultFilePath], // By default, open a single editor tab containing the default playground file.
47+
variant: parsedObject.variant as Variant
48+
};
1649
}
1750
}
1851

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import ShareLinkState from '../../ShareLinkState';
1+
import { ParsedIntermediateShareLinkState } from '../../ShareLinkState';
22

33
interface DecoderDelegate {
4-
decode(str: string): ShareLinkState;
4+
decode(str: string): ParsedIntermediateShareLinkState;
55
}
66

77
export default DecoderDelegate;

src/features/playground/shareLinks/decoder/delegates/JsonDecoderDelegate.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import ShareLinkState from '../../ShareLinkState';
1+
import { ParsedIntermediateShareLinkState } from '../../ShareLinkState';
22
import DecoderDelegate from './DecoderDelegate';
33

44
class JsonDecoderDelegate implements DecoderDelegate {
5-
decode(str: string): ShareLinkState {
6-
const jsonObject = JSON.parse(str);
7-
return jsonObject.data;
5+
decode(str: string): ParsedIntermediateShareLinkState {
6+
return JSON.parse(str);
87
}
98
}
109

src/features/playground/shareLinks/decoder/delegates/UrlParamsDecoderDelegate.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
import { IParsedQuery, parseQuery } from 'src/commons/utils/QueryHelper';
1+
import { parseQuery } from 'src/commons/utils/QueryHelper';
22

3-
import ShareLinkState from '../../ShareLinkState';
3+
import { ParsedIntermediateShareLinkState } from '../../ShareLinkState';
44
import DecoderDelegate from './DecoderDelegate';
55

66
class UrlParamsDecoderDelegate implements DecoderDelegate {
7-
decode(str: string): ShareLinkState {
8-
const qs: Partial<IParsedQuery> = parseQuery(str);
7+
decode(str: string): ParsedIntermediateShareLinkState {
8+
const qs = parseQuery(str);
9+
910
return {
1011
chap: qs.chap,
1112
exec: qs.exec,
1213
files: qs.files,
1314
isFolder: qs.isFolder,
1415
tabIdx: qs.tabIdx,
15-
tabs: qs.tabs,
16+
tabs: qs.tabs?.split(','),
1617
variant: qs.variant,
17-
prgrm: qs.prgrm,
18-
ext: qs.ext
18+
prgrm: qs.prgrm
1919
};
2020
}
2121
}

src/features/playground/shareLinks/encoder/Encoder.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import ShareLinkState from '../ShareLinkState';
1+
import { compressToEncodedURIComponent } from 'lz-string';
2+
import qs from 'query-string';
3+
4+
import { ParsedIntermediateShareLinkState, ShareLinkState } from '../ShareLinkState';
25
import EncoderDelegate from './delegates/EncoderDelegate';
36

47
class ShareLinkStateEncoder {
@@ -9,7 +12,16 @@ class ShareLinkStateEncoder {
912
}
1013

1114
encodeWith(encoderDelegate: EncoderDelegate): string {
12-
return encoderDelegate.encode(this.state);
15+
const processedState: ParsedIntermediateShareLinkState = {
16+
isFolder: this.state.isFolder.toString(),
17+
tabIdx: this.state.tabIdx?.toString() ?? '',
18+
chap: this.state.chap.toString(),
19+
variant: this.state.variant,
20+
exec: this.state.exec.toString(),
21+
tabs: this.state.tabs.map(compressToEncodedURIComponent),
22+
files: compressToEncodedURIComponent(qs.stringify(this.state.files))
23+
};
24+
return encoderDelegate.encode(processedState);
1325
}
1426
}
1527

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
import { FSModule } from 'browserfs/dist/node/core/FS';
2-
import { compressToEncodedURIComponent } from 'lz-string';
3-
import qs from 'query-string';
42
import { useState } from 'react';
53
import { retrieveFilesInWorkspaceAsRecord } from 'src/commons/fileSystem/utils';
64
import { useTypedSelector } from 'src/commons/utils/Hooks';
75
import { EditorTabState } from 'src/commons/workspace/WorkspaceTypes';
86

9-
import ShareLinkState from '../ShareLinkState';
7+
import { ShareLinkState } from '../ShareLinkState';
108
import ShareLinkStateEncoder from './Encoder';
119

1210
export const usePlaygroundConfigurationEncoder = (): ShareLinkStateEncoder => {
1311
const isFolderModeEnabled = useTypedSelector(
1412
state => state.workspaces.playground.isFolderModeEnabled
1513
);
16-
1714
const editorTabs = useTypedSelector(state => state.workspaces.playground.editorTabs);
1815
const editorTabFilePaths = editorTabs
1916
.map((editorTab: EditorTabState) => editorTab.filePath)
@@ -27,14 +24,13 @@ export const usePlaygroundConfigurationEncoder = (): ShareLinkStateEncoder => {
2724
const files = useGetFile();
2825

2926
const result: ShareLinkState = {
30-
isFolder: isFolderModeEnabled.toString(),
31-
files: files.toString(),
32-
tabs: editorTabFilePaths.map(compressToEncodedURIComponent)[0],
33-
tabIdx: activeEditorTabIndex?.toString(),
34-
chap: chapter.toString(),
27+
isFolder: isFolderModeEnabled,
28+
files: files,
29+
tabs: editorTabFilePaths,
30+
tabIdx: activeEditorTabIndex,
31+
chap: chapter,
3532
variant,
36-
ext: 'NONE',
37-
exec: execTime.toString()
33+
exec: execTime
3834
};
3935

4036
return new ShareLinkStateEncoder(result);
@@ -46,5 +42,5 @@ const useGetFile = () => {
4642
retrieveFilesInWorkspaceAsRecord('playground', fileSystem as FSModule).then(result => {
4743
setFiles(result);
4844
});
49-
return compressToEncodedURIComponent(qs.stringify(files));
45+
return files;
5046
};
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import ShareLinkState from '../../ShareLinkState';
1+
import { ParsedIntermediateShareLinkState } from '../../ShareLinkState';
22

33
interface EncoderDelegate {
4-
encode(state: ShareLinkState): string;
4+
encode(state: ParsedIntermediateShareLinkState): string;
55
}
66

77
export default EncoderDelegate;

0 commit comments

Comments
 (0)