Skip to content

Commit e6d1c41

Browse files
sumomomomomolinedoestrolling
authored andcommitted
WIP Recursive Google Drive saving
1 parent 432d867 commit e6d1c41

File tree

5 files changed

+159
-8
lines changed

5 files changed

+159
-8
lines changed

src/commons/controlBar/ControlBarGoogleDriveButtons.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ export const ControlBarGoogleDriveButtons: React.FC<Props> = props => {
7373
label="Save All"
7474
icon={IconNames.DOUBLE_CHEVRON_UP}
7575
onClick={props.onClickSaveAll}
76-
isDisabled={props.accessToken ? false : true}
76+
// disable if persistenceObject is not a folder
77+
isDisabled={props.currentObject ? props.currentObject.isFolder ? false : true : true}
7778
/>
7879
);
7980

src/commons/fileSystemView/FileSystemViewList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type Props = {
1616
indentationLevel: number;
1717
};
1818

19-
export let refreshFileView: () => any;
19+
export let refreshFileView: () => any; // TODO jank
2020

2121
const FileSystemViewList: React.FC<Props> = ({
2222
workspaceLocation,

src/commons/sagas/PersistenceSaga.tsx

Lines changed: 150 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import {
2828
import { AsyncReturnType } from '../utils/TypeHelper';
2929
import { safeTakeEvery as takeEvery, safeTakeLatest as takeLatest } from './SafeEffects';
3030
import { FSModule } from 'browserfs/dist/node/core/FS';
31-
import { rmFilesInDirRecursively, writeFileRecursively } from '../fileSystem/FileSystemUtils';
31+
import { retrieveFilesInWorkspaceAsRecord, rmFilesInDirRecursively, writeFileRecursively } from '../fileSystem/FileSystemUtils';
3232
import { refreshFileView } from '../fileSystemView/FileSystemViewList';
3333

3434
const DISCOVERY_DOCS = ['https://www.googleapis.com/discovery/v1/apis/drive/v3/rest'];
@@ -173,8 +173,6 @@ export function* persistenceSaga(): SagaIterator {
173173

174174
yield call(showSuccessMessage, `Loaded folder ${name}.`, 1000);
175175

176-
// TODO the Google Drive button isn't blue even after loading stuff
177-
178176
return;
179177
}
180178

@@ -347,10 +345,80 @@ export function* persistenceSaga(): SagaIterator {
347345
}
348346
});
349347

350-
yield takeEvery(
348+
yield takeEvery( // TODO work on this
351349
PERSISTENCE_SAVE_ALL,
352350
function* () {
353-
yield console.log("pers save all!");
351+
// Case init: Don't care, delete everything in remote and save again
352+
// Callable only if persistenceObject isFolder
353+
354+
const [currFolderObject] = yield select(
355+
(state: OverallState) => [
356+
state.playground.persistenceObject
357+
]
358+
);
359+
if (!currFolderObject) {
360+
yield call(console.log, "no obj!");
361+
return;
362+
}
363+
364+
console.log("currFolderObj", currFolderObject);
365+
366+
const fileSystem: FSModule | null = yield select(
367+
(state: OverallState) => state.fileSystem.inBrowserFileSystem
368+
);
369+
// If the file system is not initialised, do nothing.
370+
if (fileSystem === null) {
371+
yield call(console.log, "no filesystem!");
372+
return;
373+
}
374+
yield call(console.log, "there is a filesystem");
375+
376+
const currFiles: Record<string, string> = yield call(retrieveFilesInWorkspaceAsRecord, "playground", fileSystem);
377+
// TODO this does not get bare folders, is it a necessity?
378+
// behaviour of open folder for GDrive loads even empty folders
379+
yield call(console.log, "currfiles", currFiles);
380+
381+
//const fileNameRegex = new RegExp('@"[^\\]+$"');
382+
for (const currFullFilePath of Object.keys(currFiles)) {
383+
const currFileContent = currFiles[currFullFilePath];
384+
const regexResult = /^(.*[\\\/])?(\.*.*?)(\.[^.]+?|)$/.exec(currFullFilePath);
385+
if (regexResult === null) {
386+
yield call(console.log, "Regex null!");
387+
continue;
388+
}
389+
const currFileName = regexResult[2] + regexResult[3];
390+
const currFileParentFolders: string[] = regexResult[1].slice(
391+
("/playground/" + currFolderObject.name + "/").length, -1)
392+
.split("/");
393+
394+
// /fold1/ becomes ["fold1"]
395+
// /fold1/fold2/ becomes ["fold1", "fold2"]
396+
// If in top level folder, becomes [""]
397+
398+
yield call(getContainingFolderIdRecursively, currFileParentFolders,
399+
currFolderObject.id);
400+
401+
// yield call(
402+
// createFile,
403+
// fileName,
404+
// currFolderObject.id,
405+
406+
// );
407+
408+
yield call (console.log, "name", currFileName, "content", currFileContent
409+
, "path", currFileParentFolders);
410+
}
411+
412+
413+
// Case 1: Open picker to select location for saving, similar to save all
414+
// 1a No folder exists on remote, simple save
415+
// 1b Folder exists, popup confirmation to destroy remote folder and replace
416+
417+
// Case 2: Location already decided (PersistenceObject exists with isFolder === true)
418+
// TODO: Modify functions here to update string[] in persistenceObject for Folder
419+
// 2a No changes to folder/file structure, only content needs updating
420+
// TODO Maybe update the colors of the side view as well to reflect which have been modified?
421+
// 2b Changes to folder/file structure -> Delete and replace changed files
354422
}
355423
);
356424

@@ -595,6 +663,52 @@ async function getFilesOfFolder( // recursively get files
595663
return ans;
596664
}
597665

666+
async function getContainingFolderIdRecursively( // TODO memoize?
667+
parentFolders: string[],
668+
topFolderId: string,
669+
currDepth: integer = 0
670+
): Promise<string> {
671+
if (parentFolders[0] === '' || currDepth === parentFolders.length) {
672+
return topFolderId;
673+
}
674+
const currFolderName = parentFolders[parentFolders.length - 1 - currDepth];
675+
676+
const immediateParentFolderId = await getContainingFolderIdRecursively(
677+
parentFolders,
678+
topFolderId,
679+
currDepth + 1
680+
);
681+
682+
let folderList: gapi.client.drive.File[] | undefined;
683+
684+
await gapi.client.drive.files.list({
685+
q: '\'' + immediateParentFolderId + '\'' + ' in parents and trashed = false and mimeType = \''
686+
+ "application/vnd.google-apps.folder" + '\'',
687+
}).then(res => {
688+
folderList = res.result.files
689+
});
690+
691+
if (!folderList) {
692+
console.log("create!", currFolderName);
693+
const newId = await createFolderAndReturnId(immediateParentFolderId, currFolderName);
694+
return newId;
695+
}
696+
697+
console.log("folderList gcfir", folderList);
698+
699+
for (const currFolder of folderList) {
700+
if (currFolder.name === currFolderName) {
701+
console.log("found ", currFolder.name, " and id is ", currFolder.id);
702+
return currFolder.id!;
703+
}
704+
}
705+
706+
console.log("create!", currFolderName);
707+
const newId = await createFolderAndReturnId(immediateParentFolderId, currFolderName);
708+
return newId;
709+
710+
}
711+
598712
function createFile(
599713
filename: string,
600714
parent: string,
@@ -606,13 +720,15 @@ function createFile(
606720
const meta = {
607721
name,
608722
mimeType,
609-
parents: [parent],
723+
parents: [parent], //[id of the parent folder as a string]
610724
appProperties: {
611725
source: true,
612726
...config
613727
}
614728
};
615729

730+
console.log("METATAAAAAA", meta);
731+
616732
const { body, headers } = createMultipartBody(meta, contents, mimeType);
617733

618734
return gapi.client
@@ -644,6 +760,8 @@ function updateFile(
644760
}
645761
};
646762

763+
console.log("META", meta);
764+
647765
const { body, headers } = createMultipartBody(meta, contents, mimeType);
648766

649767
return gapi.client.request({
@@ -657,6 +775,32 @@ function updateFile(
657775
});
658776
}
659777

778+
function createFolderAndReturnId(
779+
parentFolderId: string,
780+
folderName: string
781+
): Promise<string> {
782+
const name = folderName;
783+
const mimeType = 'application/vnd.google-apps.folder';
784+
const meta = {
785+
name,
786+
mimeType,
787+
parents: [parentFolderId], //[id of the parent folder as a string]
788+
}
789+
790+
const { body, headers } = createMultipartBody(meta, '', mimeType);
791+
792+
return gapi.client
793+
.request({
794+
path: UPLOAD_PATH,
795+
method: 'POST',
796+
params: {
797+
uploadType: 'multipart'
798+
},
799+
headers,
800+
body
801+
}).then(res => res.result.id)
802+
};
803+
660804
function createMultipartBody(
661805
meta: any,
662806
contents: string,

src/features/persistence/PersistenceTypes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,9 @@ export type PersistenceObject = {
1111
name: string;
1212
lastSaved?: Date;
1313
isFolder?: boolean;
14+
modifiedFiles?: string[];
15+
addedFiles?: string[];
16+
removedFiles?: string[];
17+
addedFolders?: string[];
18+
removedFolders?: string[];
1419
};

src/pages/playground/Playground.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ const Playground: React.FC<PlaygroundProps> = props => {
408408
const onEditorValueChange = React.useCallback(
409409
(editorTabIndex: number, newEditorValue: string) => {
410410
setLastEdit(new Date());
411+
// TODO change editor tab label to reflect path of opened file?
411412
handleEditorValueChange(editorTabIndex, newEditorValue);
412413
},
413414
[handleEditorValueChange]

0 commit comments

Comments
 (0)