Skip to content

Commit aa1ef6b

Browse files
sumomomomomolinedoestrolling
authored andcommitted
Make folders from Google Drive readable
1 parent b4abbb8 commit aa1ef6b

File tree

10 files changed

+157
-24
lines changed

10 files changed

+157
-24
lines changed

src/commons/controlBar/ControlBarGoogleDriveButtons.tsx

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export const ControlBarGoogleDriveButtons: React.FC<Props> = props => {
3939
label={(props.currentFile && props.currentFile.name) || 'Google Drive'}
4040
icon={IconNames.CLOUD}
4141
options={{ intent: stateToIntent[state] }}
42-
isDisabled={props.isFolderModeEnabled}
42+
//isDisabled={props.isFolderModeEnabled}
4343
/>
4444
);
4545
const openButton = (
@@ -61,12 +61,20 @@ export const ControlBarGoogleDriveButtons: React.FC<Props> = props => {
6161
);
6262
const saveAsButton = (
6363
<ControlButton
64-
label="Save as"
64+
label="Save As"
6565
icon={IconNames.SEND_TO}
6666
onClick={props.onClickSaveAs}
6767
isDisabled={props.accessToken ? false : true}
6868
/>
6969
);
70+
const saveAllButton = (
71+
<ControlButton
72+
label="Save All"
73+
icon={IconNames.DOUBLE_CHEVRON_UP}
74+
onClick={props.onClickSaveAs}
75+
isDisabled={props.accessToken ? false : true}
76+
/>
77+
);
7078

7179
const loginButton = props.accessToken ? (
7280
<Tooltip2 content={`Logged in as ${props.loggedInAs}`} disabled={!props.loggedInAs}>
@@ -76,12 +84,13 @@ export const ControlBarGoogleDriveButtons: React.FC<Props> = props => {
7684
<ControlButton label="Log In" icon={IconNames.LOG_IN} onClick={props.onClickLogIn} />
7785
);
7886

79-
const tooltipContent = props.isFolderModeEnabled
80-
? 'Currently unsupported in Folder mode'
81-
: undefined;
87+
//const tooltipContent = props.isFolderModeEnabled
88+
// ? 'Currently unsupported in Folder mode'
89+
// : undefined;
90+
const tooltipContent = undefined;
8291

8392
return (
84-
<Tooltip2 content={tooltipContent} disabled={tooltipContent === undefined}>
93+
<Tooltip2 content={tooltipContent} disabled={false}>
8594
<Popover2
8695
autoFocus={false}
8796
content={
@@ -90,13 +99,14 @@ export const ControlBarGoogleDriveButtons: React.FC<Props> = props => {
9099
{openButton}
91100
{saveButton}
92101
{saveAsButton}
102+
{saveAllButton}
93103
{loginButton}
94104
</ButtonGroup>
95105
</div>
96106
}
97107
onOpening={props.onPopoverOpening}
98108
popoverClassName={Classes.POPOVER_DISMISS}
99-
disabled={props.isFolderModeEnabled}
109+
//disabled={props.isFolderModeEnabled}
100110
>
101111
{mainButton}
102112
</Popover2>

src/commons/controlBar/ControlBarToggleFolderModeButton.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const ControlBarToggleFolderModeButton: React.FC<Props> = ({
2020
}) => {
2121
const tooltipContent = isSessionActive
2222
? 'Currently unsupported while a collaborative session is active'
23-
: isPersistenceActive
23+
: false && isPersistenceActive
2424
? 'Currently unsupported while a persistence method is active'
2525
: `${isFolderModeEnabled ? 'Disable' : 'Enable'} Folder mode`;
2626
return (
@@ -32,7 +32,7 @@ export const ControlBarToggleFolderModeButton: React.FC<Props> = ({
3232
iconColor: isFolderModeEnabled ? Colors.BLUE4 : undefined
3333
}}
3434
onClick={toggleFolderMode}
35-
isDisabled={isSessionActive || isPersistenceActive}
35+
isDisabled={isSessionActive || false && isPersistenceActive}
3636
/>
3737
</Tooltip2>
3838
);

src/commons/controlBar/github/ControlBarGitHubButtons.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export const ControlBarGitHubButtons: React.FC<Props> = props => {
4848
label={mainButtonDisplayText}
4949
icon={IconNames.GIT_BRANCH}
5050
options={{ intent: mainButtonIntent }}
51-
isDisabled={props.isFolderModeEnabled}
51+
//isDisabled={props.isFolderModeEnabled}
5252
/>
5353
);
5454

@@ -79,18 +79,28 @@ export const ControlBarGitHubButtons: React.FC<Props> = props => {
7979
/>
8080
);
8181

82+
const saveAllButton = (
83+
<ControlButton
84+
label="Save All"
85+
icon={IconNames.DOUBLE_CHEVRON_UP}
86+
onClick={props.onClickSaveAs}
87+
isDisabled={shouldDisableButtons}
88+
/>
89+
);
90+
8291
const loginButton = isLoggedIn ? (
8392
<ControlButton label="Log Out" icon={IconNames.LOG_OUT} onClick={props.onClickLogOut} />
8493
) : (
8594
<ControlButton label="Log In" icon={IconNames.LOG_IN} onClick={props.onClickLogIn} />
8695
);
8796

88-
const tooltipContent = props.isFolderModeEnabled
89-
? 'Currently unsupported in Folder mode'
90-
: undefined;
97+
//const tooltipContent = props.isFolderModeEnabled
98+
// ? 'Currently unsupported in Folder mode'
99+
// : undefined;
100+
const tooltipContent = undefined;
91101

92102
return (
93-
<Tooltip2 content={tooltipContent} disabled={tooltipContent === undefined}>
103+
<Tooltip2 content={tooltipContent} disabled={false}>
94104
<Popover2
95105
autoFocus={false}
96106
content={
@@ -99,12 +109,13 @@ export const ControlBarGitHubButtons: React.FC<Props> = props => {
99109
{openButton}
100110
{saveButton}
101111
{saveAsButton}
112+
{saveAllButton}
102113
{loginButton}
103114
</ButtonGroup>
104115
</div>
105116
}
106117
popoverClassName={Classes.POPOVER_DISMISS}
107-
disabled={props.isFolderModeEnabled}
118+
//disabled={props.isFolderModeEnabled}
108119
>
109120
{mainButton}
110121
</Popover2>

src/commons/fileSystemView/FileSystemViewDirectoryNode.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import React from 'react';
66
import { useDispatch } from 'react-redux';
77
import classes from 'src/styles/FileSystemView.module.scss';
88

9-
import { rmdirRecursively } from '../fileSystem/utils';
9+
import { rmdirRecursively } from '../fileSystem/FileSystemUtils';
1010
import { showSimpleConfirmDialog, showSimpleErrorDialog } from '../utils/DialogHelper';
1111
import { removeEditorTabsForDirectory } from '../workspace/WorkspaceActions';
1212
import { WorkspaceLocation } from '../workspace/WorkspaceTypes';

src/commons/fileSystemView/FileSystemViewList.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ type Props = {
1616
indentationLevel: number;
1717
};
1818

19+
export let refreshFileView: () => any;
20+
1921
const FileSystemViewList: React.FC<Props> = ({
2022
workspaceLocation,
2123
fileSystem,
@@ -65,6 +67,8 @@ const FileSystemViewList: React.FC<Props> = ({
6567
});
6668
};
6769

70+
refreshFileView = readDirectory;
71+
6872
React.useEffect(readDirectory, [fileSystem, basePath]);
6973

7074
if (!fileNames || !dirNames) {

src/commons/sagas/PersistenceSaga.tsx

Lines changed: 113 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,13 @@ import {
2626
} from '../utils/notifications/NotificationsHelper';
2727
import { AsyncReturnType } from '../utils/TypeHelper';
2828
import { safeTakeEvery as takeEvery, safeTakeLatest as takeLatest } from './SafeEffects';
29+
import { FSModule } from 'browserfs/dist/node/core/FS';
30+
import { rmFilesInDirRecursively, writeFileRecursively } from '../fileSystem/FileSystemUtils';
31+
import { refreshFileView } from '../fileSystemView/FileSystemViewList';
2932

3033
const DISCOVERY_DOCS = ['https://www.googleapis.com/discovery/v1/apis/drive/v3/rest'];
3134
const SCOPES =
32-
'profile https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/userinfo.email';
35+
'profile https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/drive';
3336
const UPLOAD_PATH = 'https://www.googleapis.com/upload/drive/v3/files';
3437
const USER_INFO_PATH = 'https://www.googleapis.com/oauth2/v3/userinfo';
3538

@@ -87,10 +90,67 @@ export function* persistenceSaga(): SagaIterator {
8790
let toastKey: string | undefined;
8891
try {
8992
yield call(ensureInitialisedAndAuthorised);
90-
const { id, name, picked } = yield call(pickFile, 'Pick a file to open');
93+
const { id, name, mimeType, picked } = yield call(pickFile,
94+
'Pick a file/folder to open',
95+
{
96+
pickFolders: true
97+
}
98+
); // id, name, picked gotten here
9199
if (!picked) {
92100
return;
93101
}
102+
103+
// Note: for mimeType, text/plain -> file, application/vnd.google-apps.folder -> folder
104+
105+
if (mimeType === "application/vnd.google-apps.folder") { // handle folders
106+
yield call(console.log, "is folder");
107+
108+
const fileList = yield call(getFilesOfFolder, id, name); // this needed the extra scope mimetypes to have every file
109+
// TODO: add type for each resp?
110+
yield call(console.log, "fileList", fileList);
111+
112+
113+
114+
const fileSystem: FSModule | null = yield select(
115+
(state: OverallState) => state.fileSystem.inBrowserFileSystem
116+
);
117+
// If the file system is not initialised, do nothing.
118+
if (fileSystem === null) {
119+
yield call(console.log, "no filesystem!");
120+
return;
121+
}
122+
yield call(console.log, "there is a filesystem");
123+
124+
// rmdir everything TODO replace everything hardcoded with playground?
125+
yield call(rmFilesInDirRecursively, fileSystem, "/playground");
126+
127+
128+
for (const currFile of fileList) {
129+
// TODO add code to actually load contents here
130+
const contents = yield call([gapi.client.drive.files, 'get'], { fileId: currFile.id, alt: 'media' });
131+
yield call(writeFileRecursively, fileSystem, "/playground" + currFile.path, contents.body);
132+
}
133+
134+
135+
136+
137+
138+
139+
140+
141+
142+
143+
144+
145+
146+
// refresh needed
147+
yield put(store.dispatch(actions.removeEditorTabsForDirectory("playground", "/"))); // deletes all active tabs
148+
yield call(refreshFileView); // refreshes folder view TODO super jank
149+
150+
return;
151+
}
152+
153+
94154
const confirmOpen: boolean = yield call(showSimpleConfirmDialog, {
95155
title: 'Opening from Google Drive',
96156
contents: (
@@ -112,7 +172,7 @@ export function* persistenceSaga(): SagaIterator {
112172
intent: Intent.PRIMARY
113173
});
114174

115-
const { result: meta } = yield call([gapi.client.drive.files, 'get'], {
175+
const { result: meta } = yield call([gapi.client.drive.files, 'get'], { // get fileid here using gapi.client.drive.files
116176
fileId: id,
117177
fields: 'appProperties'
118178
});
@@ -123,12 +183,12 @@ export function* persistenceSaga(): SagaIterator {
123183
if (activeEditorTabIndex === null) {
124184
throw new Error('No active editor tab found.');
125185
}
126-
yield put(actions.updateEditorValue('playground', activeEditorTabIndex, contents.body));
186+
yield put(actions.updateEditorValue('playground', activeEditorTabIndex, contents.body)); // CONTENTS OF SELECTED FILE LOADED HERE
127187
yield put(actions.playgroundUpdatePersistenceFile({ id, name, lastSaved: new Date() }));
128188
if (meta && meta.appProperties) {
129189
yield put(
130190
actions.chapterSelect(
131-
parseInt(meta.appProperties.chapter || '4', 10) as Chapter,
191+
parseInt(meta.appProperties.chapter || '4', 10) as Chapter, // how does this work??
132192
meta.appProperties.variant || Variant.DEFAULT,
133193
'playground'
134194
)
@@ -452,6 +512,7 @@ function pickFile(
452512
.setCallback((data: any) => {
453513
switch (data[google.picker.Response.ACTION]) {
454514
case google.picker.Action.PICKED: {
515+
console.log("data", data);
455516
const { id, name, mimeType, parentId } = data.docs[0];
456517
res({ id, name, mimeType, parentId, picked: true });
457518
break;
@@ -468,6 +529,53 @@ function pickFile(
468529
});
469530
}
470531

532+
async function getFilesOfFolder( // recursively get files
533+
folderId: string,
534+
currFolderName: string,
535+
currPath: string = '' // pass in name of folder picked
536+
) {
537+
console.log(folderId, currPath, currFolderName);
538+
let fileList: gapi.client.drive.File[] | undefined;
539+
540+
await gapi.client.drive.files.list({
541+
q: '\'' + folderId + '\'' + ' in parents and trashed = false',
542+
}).then(res => {
543+
fileList = res.result.files
544+
});
545+
546+
console.log("fileList", fileList);
547+
548+
if (!fileList || fileList.length === 0) {
549+
return [{
550+
name: currFolderName,
551+
id: folderId,
552+
path: currPath + '/' + currFolderName,
553+
isFile: false
554+
}];
555+
}
556+
557+
558+
let ans: any[] = []; // TODO: add type for each resp?
559+
for (const currFile of fileList) {
560+
if (currFile.mimeType === "application/vnd.google-apps.folder") { // folder
561+
ans = ans.concat(await
562+
getFilesOfFolder(currFile.id!, currFile.name!, currPath + '/' + currFolderName)
563+
);
564+
}
565+
else { // file
566+
console.log("found file " + currFile.name);
567+
ans.push({
568+
name: currFile.name,
569+
id: currFile.id,
570+
path: currPath + '/' + currFolderName + '/' + currFile.name,
571+
isFile: true
572+
});
573+
}
574+
}
575+
576+
return ans;
577+
}
578+
471579
function createFile(
472580
filename: string,
473581
parent: string,

src/commons/sagas/PlaygroundSaga.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
import { GENERATE_LZ_STRING, SHORTEN_URL } from '../../features/playground/PlaygroundTypes';
1515
import { isSourceLanguage, OverallState } from '../application/ApplicationTypes';
1616
import { ExternalLibraryName } from '../application/types/ExternalTypes';
17-
import { retrieveFilesInWorkspaceAsRecord } from '../fileSystem/utils';
17+
import { retrieveFilesInWorkspaceAsRecord } from '../fileSystem/FileSystemUtils';
1818
import { visitSideContent } from '../sideContent/SideContentActions';
1919
import { SideContentType, VISIT_SIDE_CONTENT } from '../sideContent/SideContentTypes';
2020
import Constants from '../utils/Constants';

src/pages/fileSystem/createInBrowserFileSystem.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Store } from 'redux';
55

66
import { OverallState } from '../../commons/application/ApplicationTypes';
77
import { setInBrowserFileSystem } from '../../commons/fileSystem/FileSystemActions';
8-
import { writeFileRecursively } from '../../commons/fileSystem/utils';
8+
import { writeFileRecursively } from '../../commons/fileSystem/FileSystemUtils';
99
import { EditorTabState, WorkspaceManagerState } from '../../commons/workspace/WorkspaceTypes';
1010

1111
/**

src/pages/playground/Playground.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ import {
110110
NormalEditorContainerProps
111111
} from '../../commons/editor/EditorContainer';
112112
import { Position } from '../../commons/editor/EditorTypes';
113-
import { overwriteFilesInWorkspace } from '../../commons/fileSystem/utils';
113+
import { overwriteFilesInWorkspace } from '../../commons/fileSystem/FileSystemUtils';
114114
import FileSystemView from '../../commons/fileSystemView/FileSystemView';
115115
import MobileWorkspace, {
116116
MobileWorkspaceProps

0 commit comments

Comments
 (0)