Skip to content

Commit 808ee63

Browse files
Merge branch 'folders-public-wip' of github.com:source-academy/frontend into folders-public-wip
2 parents 3a7d83b + 0312299 commit 808ee63

File tree

4 files changed

+93
-109
lines changed

4 files changed

+93
-109
lines changed

src/commons/application/ApplicationTypes.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,9 +413,8 @@ export const createDefaultWorkspace = (workspaceLocation: WorkspaceLocation): Wo
413413
});
414414

415415
const defaultFileName = 'program.js';
416-
const defaultTopLevelFolderName = 'proj';
417416
export const getDefaultFilePath = (workspaceLocation: WorkspaceLocation) =>
418-
`${WORKSPACE_BASE_PATHS[workspaceLocation]}/${defaultTopLevelFolderName}/${defaultFileName}`;
417+
`${WORKSPACE_BASE_PATHS[workspaceLocation]}/${defaultFileName}`;
419418

420419
export const defaultWorkspaceManager: WorkspaceManagerState = {
421420
assessment: {

src/commons/sagas/PersistenceSaga.tsx

Lines changed: 85 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import {
2222
} from '../../features/persistence/PersistenceTypes';
2323
import { store } from '../../pages/createStore';
2424
import { OverallState } from '../application/ApplicationTypes';
25-
import { ExternalLibraryName } from '../application/types/ExternalTypes';
2625
import { LOGIN_GOOGLE, LOGOUT_GOOGLE } from '../application/types/SessionTypes';
2726
import {
2827
retrieveFilesInWorkspaceAsRecord,
@@ -107,6 +106,13 @@ export function* persistenceSaga(): SagaIterator {
107106
let toastKey: string | undefined;
108107
try {
109108
yield call(ensureInitialisedAndAuthorised);
109+
const fileSystem: FSModule | null = yield select(
110+
(state: OverallState) => state.fileSystem.inBrowserFileSystem
111+
);
112+
// If the file system is not initialised, do nothing.
113+
if (fileSystem === null) {
114+
throw new Error("No filesystem!");
115+
}
110116
const { id, name, mimeType, picked, parentId } = yield call(
111117
pickFile,
112118
'Pick a file/folder to open',
@@ -125,7 +131,7 @@ export function* persistenceSaga(): SagaIterator {
125131
contents: (
126132
<p>
127133
Opening <strong>{name}</strong> will overwrite the current contents of your workspace.
128-
Are you sure?
134+
All local files/folders will be deleted. Are you sure?
129135
</p>
130136
),
131137
positiveLabel: 'Open',
@@ -147,14 +153,6 @@ export function* persistenceSaga(): SagaIterator {
147153
const fileList = yield call(getFilesOfFolder, id, name); // this needed the extra scope mimetypes to have every file
148154
yield call(console.log, 'fileList', fileList);
149155

150-
const fileSystem: FSModule | null = yield select(
151-
(state: OverallState) => state.fileSystem.inBrowserFileSystem
152-
);
153-
// If the file system is not initialised, do nothing.
154-
if (fileSystem === null) {
155-
throw new Error("No filesystem!");
156-
}
157-
158156
yield call(rmFilesInDirRecursively, fileSystem, '/playground');
159157
yield call(store.dispatch, actions.deleteAllPersistenceFiles());
160158

@@ -221,14 +219,13 @@ export function* persistenceSaga(): SagaIterator {
221219
actions.chapterSelect(parseInt('4', 10) as Chapter, Variant.DEFAULT, 'playground')
222220
);
223221

224-
yield call(store.dispatch, actions.enableFileSystemContextMenus());
225222
yield call(
226223
store.dispatch,
227224
actions.removeEditorTabsForDirectory('playground', WORKSPACE_BASE_PATHS['playground'])
228225
);
229226

230227
yield put(
231-
actions.playgroundUpdatePersistenceFolder({ id, name, parentId, lastSaved: new Date() })
228+
actions.playgroundUpdatePersistenceFile({ id, name, parentId, lastSaved: new Date(), isFolder: true })
232229
);
233230

234231
// delay to increase likelihood addPersistenceFile for last loaded file has completed
@@ -245,36 +242,32 @@ export function* persistenceSaga(): SagaIterator {
245242
intent: Intent.PRIMARY
246243
});
247244

248-
const { result: meta } = yield call([gapi.client.drive.files, 'get'], {
249-
// get fileid here using gapi.client.drive.files
250-
fileId: id,
251-
fields: 'appProperties'
252-
});
253245
const contents = yield call([gapi.client.drive.files, 'get'], { fileId: id, alt: 'media' });
254-
const activeEditorTabIndex: number | null = yield select(
255-
(state: OverallState) => state.workspaces.playground.activeEditorTabIndex
246+
247+
yield call(rmFilesInDirRecursively, fileSystem, '/playground');
248+
yield call(store.dispatch, actions.deleteAllPersistenceFiles());
249+
250+
// add file to BrowserFS
251+
yield call(
252+
writeFileRecursively,
253+
fileSystem,
254+
'/playground/' + name,
255+
contents.body
256256
);
257-
if (activeEditorTabIndex === null) {
258-
throw new Error('No active editor tab found.');
259-
}
260-
yield put(actions.updateEditorValue('playground', activeEditorTabIndex, contents.body)); // CONTENTS OF SELECTED FILE LOADED HERE
261-
yield put(actions.playgroundUpdatePersistenceFile({ id, name, lastSaved: new Date() }));
262-
if (meta && meta.appProperties) {
263-
yield put(
264-
actions.chapterSelect(
265-
parseInt(meta.appProperties.chapter || '4', 10) as Chapter,
266-
meta.appProperties.variant || Variant.DEFAULT,
267-
'playground'
268-
)
269-
);
270-
yield put(
271-
actions.externalLibrarySelect(
272-
Object.values(ExternalLibraryName).find(v => v === meta.appProperties.external) ||
273-
ExternalLibraryName.NONE,
274-
'playground'
275-
)
276-
);
277-
}
257+
// update playground PersistenceFile
258+
const newPersistenceFile = { id, name, lastSaved: new Date(), path: '/playground/' + name};
259+
yield put(actions.playgroundUpdatePersistenceFile(newPersistenceFile));
260+
// add file to persistenceFileArray
261+
yield put(actions.addPersistenceFile(newPersistenceFile));
262+
263+
yield call(
264+
store.dispatch,
265+
actions.removeEditorTabsForDirectory('playground', WORKSPACE_BASE_PATHS['playground'])
266+
);
267+
268+
// delay to increase likelihood addPersistenceFile for last loaded file has completed
269+
// and for the toasts to not overlap
270+
yield call(() => new Promise( resolve => setTimeout(resolve, 1000)));
278271
yield call(showSuccessMessage, `Loaded ${name}.`, 1000);
279272
} catch (ex) {
280273
console.error(ex);
@@ -283,6 +276,7 @@ export function* persistenceSaga(): SagaIterator {
283276
if (toastKey) {
284277
dismiss(toastKey);
285278
}
279+
yield call(store.dispatch, actions.enableFileSystemContextMenus());
286280
yield call(store.dispatch, actions.updateRefreshFileViewKey());
287281
}
288282
});
@@ -310,7 +304,8 @@ export function* persistenceSaga(): SagaIterator {
310304
);
311305

312306
if (activeEditorTabIndex === null) {
313-
throw new Error('No active editor tab found.');
307+
yield call(showWarningMessage, `Please open an editor tab.`, 1000);
308+
return;
314309
}
315310
const code = editorTabs[activeEditorTabIndex].value;
316311

@@ -420,11 +415,12 @@ export function* persistenceSaga(): SagaIterator {
420415
);
421416
if (areAllFilesSavedGoogleDrive(updatedPersistenceFileArray)) {
422417
yield put(
423-
actions.playgroundUpdatePersistenceFolder({
418+
actions.playgroundUpdatePersistenceFile({
424419
id: currPersistenceFile.id,
425420
name: currPersistenceFile.name,
426421
parentId: currPersistenceFile.parentId,
427-
lastSaved: new Date()
422+
lastSaved: new Date(),
423+
isFolder: true
428424
})
429425
);
430426
}
@@ -438,8 +434,10 @@ export function* persistenceSaga(): SagaIterator {
438434
);
439435
return;
440436
}
441-
yield put(actions.playgroundUpdatePersistenceFile(pickedFile));
442-
yield put(actions.persistenceSaveFile(pickedFile));
437+
// Single file mode case
438+
const singleFileModePersFile: PersistenceFile = {...pickedFile, lastSaved: new Date(), path: '/playground/' + pickedFile.name};
439+
yield put(actions.playgroundUpdatePersistenceFile(singleFileModePersFile));
440+
yield put(actions.persistenceSaveFile(singleFileModePersFile));
443441
} else {
444442
const response: AsyncReturnType<typeof showSimplePromptDialog> = yield call(
445443
showSimplePromptDialog,
@@ -465,8 +463,6 @@ export function* persistenceSaga(): SagaIterator {
465463
return;
466464
}
467465

468-
// yield call(store.dispatch, actions.disableFileSystemContextMenus());
469-
470466
const config: IPlaygroundConfig = {
471467
chapter,
472468
variant,
@@ -542,7 +538,21 @@ export function* persistenceSaga(): SagaIterator {
542538
return;
543539
}
544540

545-
yield put(actions.playgroundUpdatePersistenceFile({ ...newFile, lastSaved: new Date() }));
541+
542+
// Single file case
543+
const newPersFile: PersistenceFile = {...newFile, lastSaved: new Date(), path: "/playground/" + newFile.name};
544+
if (!currPersistenceFile) { // no file loaded prior
545+
// update playground pers file
546+
yield put(actions.playgroundUpdatePersistenceFile(newPersFile));
547+
// add new pers file to persFileArray
548+
} else { // file loaded prior
549+
// update playground pers file
550+
yield put(actions.playgroundUpdatePersistenceFile(newPersFile));
551+
// remove old pers file from persFileArray
552+
// add new pers file to persFileArray
553+
}
554+
555+
//
546556
yield call(
547557
showSuccessMessage,
548558
`${response.value} successfully saved to Google Drive.`,
@@ -760,11 +770,12 @@ export function* persistenceSaga(): SagaIterator {
760770
}
761771

762772
yield put(
763-
actions.playgroundUpdatePersistenceFolder({
773+
actions.playgroundUpdatePersistenceFile({
764774
id: topLevelFolderId,
765775
name: topLevelFolderName,
766776
parentId: saveToDir.id,
767-
lastSaved: new Date()
777+
lastSaved: new Date(),
778+
isFolder: true
768779
})
769780
);
770781

@@ -860,11 +871,12 @@ export function* persistenceSaga(): SagaIterator {
860871
}
861872

862873
yield put(
863-
actions.playgroundUpdatePersistenceFolder({
874+
actions.playgroundUpdatePersistenceFile({
864875
id: currFolderObject.id,
865876
name: currFolderObject.name,
866877
parentId: currFolderObject.parentId,
867-
lastSaved: new Date()
878+
lastSaved: new Date(),
879+
isFolder: true
868880
})
869881
);
870882
yield call(store.dispatch, actions.updateRefreshFileViewKey());
@@ -891,7 +903,7 @@ export function* persistenceSaga(): SagaIterator {
891903
yield call(store.dispatch, actions.disableFileSystemContextMenus());
892904
let toastKey: string | undefined;
893905

894-
const [currFolderObject] = yield select(
906+
const [playgroundPersistenceFile] = yield select(
895907
(state: OverallState) => [state.playground.persistenceFile]
896908
);
897909

@@ -908,8 +920,9 @@ export function* persistenceSaga(): SagaIterator {
908920
);
909921

910922
try {
911-
if (activeEditorTabIndex === null) {
912-
throw new Error('No active editor tab found.');
923+
if (activeEditorTabIndex === null && !playgroundPersistenceFile.isFolder) {
924+
yield call(showWarningMessage, `Please have ${name} open as the active editor tab.`, 1000);
925+
return;
913926
}
914927
const code = editorTabs[activeEditorTabIndex].value;
915928

@@ -918,7 +931,7 @@ export function* persistenceSaga(): SagaIterator {
918931
variant,
919932
external
920933
};
921-
if ((currFolderObject as PersistenceFile).isFolder) {
934+
if ((playgroundPersistenceFile as PersistenceFile).isFolder) {
922935
yield call(console.log, 'folder opened! updating pers specially');
923936
const persistenceFileArray: PersistenceFile[] = yield select(
924937
(state: OverallState) => state.fileSystem.persistenceFileArray
@@ -957,27 +970,36 @@ export function* persistenceSaga(): SagaIterator {
957970
);
958971
if (areAllFilesSavedGoogleDrive(updatedPersistenceFileArray)) {
959972
yield put(
960-
actions.playgroundUpdatePersistenceFolder({
961-
id: currFolderObject.id,
962-
name: currFolderObject.name,
963-
parentId: currFolderObject.parentId,
964-
lastSaved: new Date()
973+
actions.playgroundUpdatePersistenceFile({
974+
id: playgroundPersistenceFile.id,
975+
name: playgroundPersistenceFile.name,
976+
parentId: playgroundPersistenceFile.parentId,
977+
lastSaved: new Date(),
978+
isFolder: true
965979
})
966980
);
967981
}
968982

969983
return;
970984
}
971985

986+
if ((editorTabs[activeEditorTabIndex] as EditorTabState).filePath !== playgroundPersistenceFile.path) {
987+
yield call(showWarningMessage, `Please have ${name} open as the active editor tab.`, 1000);
988+
return;
989+
}
990+
972991
toastKey = yield call(showMessage, {
973992
message: `Saving as ${name}...`,
974993
timeout: 0,
975994
intent: Intent.PRIMARY
976995
});
977996

978997
yield call(updateFile, id, name, MIME_SOURCE, code, config);
979-
yield put(actions.playgroundUpdatePersistenceFile({ id, name, lastSaved: new Date() }));
998+
const updatedPlaygroundPersFile = { ...playgroundPersistenceFile, lastSaved: new Date() };
999+
yield put(actions.addPersistenceFile(updatedPlaygroundPersFile));
1000+
yield put(actions.playgroundUpdatePersistenceFile(updatedPlaygroundPersFile));
9801001
yield call(showSuccessMessage, `${name} successfully saved to Google Drive.`, 1000);
1002+
yield call(store.dispatch, actions.updateRefreshFileViewKey());
9811003
} catch (ex) {
9821004
console.error(ex);
9831005
yield call(showWarningMessage, `Error while saving file.`, 1000);
@@ -1312,7 +1334,7 @@ export function* persistenceSaga(): SagaIterator {
13121334
if (currFolderObject.name === oldFolderName) {
13131335
// update playground PersistenceFile
13141336
yield put(
1315-
actions.playgroundUpdatePersistenceFolder({ ...currFolderObject, name: newFolderName })
1337+
actions.playgroundUpdatePersistenceFile({ ...currFolderObject, name: newFolderName, isFolder: true })
13161338
);
13171339
}
13181340
} catch (ex) {

src/commons/sagas/WorkspaceSaga/index.ts

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import { EVAL_STORY } from 'src/features/stories/StoriesTypes';
1010
import { EventType } from '../../../features/achievement/AchievementTypes';
1111
import DataVisualizer from '../../../features/dataVisualizer/dataVisualizer';
1212
import {
13-
defaultEditorValue,
14-
getDefaultFilePath,
1513
OverallState,
1614
styliseSublanguage
1715
} from '../../application/ApplicationTypes';
@@ -24,7 +22,6 @@ import {
2422
} from '../../application/types/InterpreterTypes';
2523
import { Library, Testcase } from '../../assessment/AssessmentTypes';
2624
import { Documentation } from '../../documentation/Documentation';
27-
import { writeFileRecursively } from '../../fileSystem/FileSystemUtils';
2825
import { resetSideContent } from '../../sideContent/SideContentActions';
2926
import { actions } from '../../utils/ActionsHelper';
3027
import {
@@ -42,7 +39,6 @@ import {
4239
ADD_HTML_CONSOLE_ERROR,
4340
BEGIN_CLEAR_CONTEXT,
4441
CHAPTER_SELECT,
45-
EditorTabState,
4642
EVAL_EDITOR,
4743
EVAL_EDITOR_AND_TESTCASES,
4844
EVAL_REPL,
@@ -101,45 +97,6 @@ export default function* WorkspaceSaga(): SagaIterator {
10197
if (isFolderModeEnabled) {
10298
return;
10399
}
104-
105-
const editorTabs: EditorTabState[] = yield select(
106-
(state: OverallState) => state.workspaces[workspaceLocation].editorTabs
107-
);
108-
// If Folder mode is disabled and there are no open editor tabs, add an editor tab.
109-
if (editorTabs.length === 0) {
110-
const defaultFilePath = getDefaultFilePath(workspaceLocation);
111-
const fileSystem: FSModule | null = yield select(
112-
(state: OverallState) => state.fileSystem.inBrowserFileSystem
113-
);
114-
// If the file system is not initialised, add an editor tab with the default editor value.
115-
if (fileSystem === null) {
116-
yield put(actions.addEditorTab(workspaceLocation, defaultFilePath, defaultEditorValue));
117-
return;
118-
}
119-
const editorValue: string = yield new Promise((resolve, reject) => {
120-
fileSystem.exists(defaultFilePath, fileExists => {
121-
if (!fileExists) {
122-
// If the file does not exist, we need to also create it in the file system.
123-
writeFileRecursively(fileSystem, defaultFilePath, defaultEditorValue)
124-
.then(() => resolve(defaultEditorValue))
125-
.catch(err => reject(err));
126-
return;
127-
}
128-
fileSystem.readFile(defaultFilePath, 'utf-8', (err, fileContents) => {
129-
if (err) {
130-
reject(err);
131-
return;
132-
}
133-
if (fileContents === undefined) {
134-
reject(new Error('File exists but has no contents.'));
135-
return;
136-
}
137-
resolve(fileContents);
138-
});
139-
});
140-
});
141-
yield put(actions.addEditorTab(workspaceLocation, defaultFilePath, editorValue));
142-
}
143100
});
144101

145102
// Mirror editor updates to the associated file in the filesystem.

0 commit comments

Comments
 (0)