Skip to content

Commit fb4f89c

Browse files
committed
Mostly working google drive reimplementation
1 parent a6b8942 commit fb4f89c

File tree

3 files changed

+149
-185
lines changed

3 files changed

+149
-185
lines changed

.idea/runConfigurations/Debug.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/google-drive.js

Lines changed: 116 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,187 +1,162 @@
11
"use strict";
2+
23
import _ from "underscore";
34
import * as utils from "./utils.js";
45
import { discFor } from "./fdc.js";
56

6-
export function GoogleDriveLoader() {
7-
const self = this;
8-
const MIME_TYPE = "application/vnd.jsbeeb.disc-image";
9-
const CLIENT_ID = "356883185894-bhim19837nroivv18p0j25gecora60r5.apps.googleusercontent.com";
10-
const SCOPES = "https://www.googleapis.com/auth/drive.file";
11-
let gapi = null;
7+
const MIME_TYPE = "application/vnd.jsbeeb.disc-image";
8+
const API_KEY = "AIzaSyAJOcuUV8x6qFL_ID3DmnH4dZ8VuAExTaU";
9+
const CLIENT_ID = "356883185894-bhim19837nroivv18p0j25gecora60r5.apps.googleusercontent.com";
10+
const SCOPES = "https://www.googleapis.com/auth/drive.file";
11+
const DISCOVERY_DOC = "https://www.googleapis.com/discovery/v1/apis/drive/v3/rest";
12+
const FILE_FIELDS = "id,name,capabilities";
13+
14+
const boundary = "-------314159265358979323846";
15+
const delimiter = `\r
16+
--${boundary}\r
17+
`;
18+
const close_delim = `\r
19+
--${boundary}--`;
20+
21+
export class GoogleDriveLoader {
22+
constructor() {
23+
this.gapi = null;
24+
this.authorized = false;
25+
}
1226

13-
self.initialise = function () {
14-
return new Promise(function (resolve) {
27+
async initialise() {
28+
console.log("Creating GAPI");
29+
this.gapi = await new Promise((resolve) => {
1530
// https://github.com/google/google-api-javascript-client/issues/319
1631
const gapiScript = document.createElement("script");
17-
gapiScript.src = "https://apis.google.com/js/client.js?onload=__onGapiLoad__";
18-
window.__onGapiLoad__ = function onGapiLoad() {
19-
gapi = window.gapi;
20-
gapi.client.load("drive", "v2", function () {
21-
console.log("Google Drive: available");
22-
resolve(true);
23-
});
32+
gapiScript.src = "https://apis.google.com/js/api.js";
33+
gapiScript.onload = function onGapiLoad() {
34+
resolve(window.gapi);
2435
};
2536
document.body.appendChild(gapiScript);
2637
});
27-
};
28-
29-
self.authorize = function (immediate) {
30-
return new Promise(function (resolve, reject) {
31-
console.log("Authorizing", immediate);
32-
gapi.auth.authorize(
33-
{
34-
client_id: CLIENT_ID,
35-
scope: SCOPES,
36-
immediate: immediate,
37-
},
38-
function (authResult) {
39-
if (authResult && !authResult.error) {
40-
console.log("Google Drive: authorized");
41-
resolve(true);
42-
} else if (authResult && authResult.error && !immediate) {
43-
reject(new Error(authResult.error));
44-
} else {
45-
console.log("Google Drive: Need to auth");
46-
resolve(false);
47-
}
48-
},
49-
);
38+
console.log("Got GAPI, creating token client");
39+
this.tokenClient = await new Promise((resolve) => {
40+
// https://github.com/google/google-api-javascript-client/issues/319
41+
const gsiScript = document.createElement("script");
42+
gsiScript.src = "https://accounts.google.com/gsi/client";
43+
gsiScript.onload = function onGsiLoad() {
44+
resolve(
45+
window.google.accounts.oauth2.initTokenClient({
46+
client_id: CLIENT_ID,
47+
scope: SCOPES,
48+
error_callback: "", // defined later
49+
callback: "", // defined later
50+
}),
51+
);
52+
};
53+
document.body.appendChild(gsiScript);
5054
});
51-
};
55+
console.log("Token client created, loading client");
5256

53-
const boundary = "-------314159265358979323846";
54-
const delimiter = "\r\n--" + boundary + "\r\n";
55-
const close_delim = "\r\n--" + boundary + "--";
57+
await this.gapi.load("client", async () => {
58+
console.log("Client loaded; initialising GAPI");
59+
await this.gapi.client.init({
60+
apiKey: API_KEY,
61+
discoveryDocs: [DISCOVERY_DOC],
62+
});
63+
console.log("GAPI initialised");
64+
});
65+
console.log("Google Drive: available");
66+
return true;
67+
}
5668

57-
function listFiles() {
58-
return new Promise(function (resolve) {
59-
const retrievePageOfFiles = function (request, result) {
60-
request.execute(function (resp) {
61-
result = result.concat(resp.items);
62-
const nextPageToken = resp.nextPageToken;
63-
if (nextPageToken) {
64-
request = gapi.client.drive.files.list({
65-
pageToken: nextPageToken,
66-
});
67-
retrievePageOfFiles(request, result);
68-
} else {
69-
resolve(result);
70-
}
71-
});
69+
authorize(imm) {
70+
if (this.authorized) return true;
71+
if (imm) return false;
72+
return new Promise((resolve, reject) => {
73+
console.log("Authorizing...");
74+
this.tokenClient.callback = (resp) => {
75+
if (resp.error !== undefined) reject(resp);
76+
console.log("Authorized OK");
77+
this.authorized = true;
78+
resolve(true);
79+
};
80+
window.moo = this.tokenClient; // DO NOT CHECK IN
81+
this.tokenClient.error_callback = (resp) => {
82+
console.log(`Token client failure: ${resp.type}; failed to authorize`);
83+
reject(new Error(`Token client failure: ${resp.type}; failed to authorize`));
7284
};
73-
retrievePageOfFiles(
74-
gapi.client.drive.files.list({
75-
q: "mimeType = '" + MIME_TYPE + "'",
76-
}),
77-
[],
78-
);
85+
this.tokenClient.requestAccessToken({ select_account: false });
7986
});
8087
}
8188

82-
function saveFile(name, data, idOrNone) {
89+
async listFiles() {
90+
let response = await this.gapi.client.drive.files.list({
91+
q: `mimeType = '${MIME_TYPE}'`,
92+
});
93+
let result = response.result.files;
94+
while (response.result.nextPageToken) {
95+
response = await this.gapi.client.drive.files.list({
96+
pageToken: response.result.nextPageToken,
97+
});
98+
result = result.concat(response.result.files);
99+
}
100+
return result;
101+
}
102+
103+
saveFile(name, data, idOrNone) {
83104
const metadata = {
84-
title: name,
85-
parents: ["jsbeeb disc images"], // TODO: parents doesn't work; also should probably prevent overwriting this on every save
105+
name,
106+
// parents: ["jsbeeb disc images"], // TODO: parents doesn't work, need folder ID maybe?
86107
mimeType: MIME_TYPE,
87108
};
88109

89110
const str = utils.uint8ArrayToString(data);
90111
const base64Data = btoa(str);
91-
const multipartRequestBody =
92-
delimiter +
93-
"Content-Type: application/json\r\n\r\n" +
94-
JSON.stringify(metadata) +
95-
delimiter +
96-
"Content-Type: " +
97-
MIME_TYPE +
98-
"\r\n" +
99-
"Content-Transfer-Encoding: base64\r\n" +
100-
"\r\n" +
101-
base64Data +
102-
close_delim;
112+
const multipartRequestBody = `${delimiter}Content-Type: application/json\r
113+
\r
114+
${JSON.stringify(metadata)}${delimiter}Content-Type: ${MIME_TYPE}\r
115+
Content-Transfer-Encoding: base64\r
116+
\r
117+
${base64Data}${close_delim}`;
103118

104-
return gapi.client.request({
105-
path: "/upload/drive/v2/files" + (idOrNone ? "/" + idOrNone : ""),
106-
method: idOrNone ? "PUT" : "POST",
107-
params: { uploadType: "multipart", newRevision: false },
119+
return this.gapi.client.request({
120+
path: `/upload/drive/v3/files${idOrNone ? `/${idOrNone}` : ""}`,
121+
method: idOrNone ? "PATCH" : "POST",
122+
params: { uploadType: "multipart", newRevision: false, fields: FILE_FIELDS },
108123
headers: {
109-
"Content-Type": 'multipart/mixed; boundary="' + boundary + '"',
124+
"Content-Type": `multipart/mixed; boundary="${boundary}"`,
110125
},
111126
body: multipartRequestBody,
112127
});
113128
}
114129

115-
function loadMetadata(fileId) {
116-
return gapi.client.drive.files.get({ fileId: fileId });
117-
}
118-
119-
self.create = function (fdc, name) {
120-
console.log("Google Drive: creating disc image: '" + name + "'");
130+
async create(fdc, name) {
131+
console.log(`Google Drive: creating disc image: '${name}'`);
121132
const byteSize = utils.discImageSize(name).byteSize;
122133
const data = new Uint8Array(byteSize);
123134
utils.setDiscName(data, name);
124-
return saveFile(name, data).then(function (response) {
125-
const meta = response.result;
126-
return { fileId: meta.id, disc: makeDisc(fdc, data, meta) };
127-
});
128-
};
129-
130-
function downloadFile(file) {
131-
if (file.downloadUrl) {
132-
return new Promise(function (resolve, reject) {
133-
const accessToken = gapi.auth.getToken().access_token;
134-
const xhr = new XMLHttpRequest();
135-
xhr.open("GET", file.downloadUrl, true);
136-
xhr.setRequestHeader("Authorization", "Bearer " + accessToken);
137-
xhr.overrideMimeType("text/plain; charset=x-user-defined");
138-
139-
xhr.onload = function () {
140-
if (xhr.status !== 200) {
141-
reject(new Error("Unable to load '" + file.title + "', http code " + xhr.status));
142-
} else if (typeof xhr.response !== "string") {
143-
resolve(xhr.response);
144-
} else {
145-
resolve(utils.stringToUint8Array(xhr.response));
146-
}
147-
};
148-
xhr.onerror = function () {
149-
reject(new Error("Error sending request for " + file));
150-
};
151-
xhr.send();
152-
});
153-
} else {
154-
return Promise.resolve(null);
155-
}
135+
const response = await this.saveFile(name, data);
136+
const meta = response.result;
137+
return { fileId: meta.id, disc: this.makeDisc(fdc, data, meta) };
156138
}
157139

158-
function makeDisc(fdc, data, meta) {
140+
makeDisc(fdc, data, meta) {
159141
let flusher = null;
160-
const name = meta.title;
161-
if (meta.editable) {
142+
const name = meta.name;
143+
const id = meta.id;
144+
if (meta.capabilities.canEdit) {
162145
console.log("Making editable disc");
163-
flusher = _.debounce(function () {
164-
saveFile(this.name, this.data, meta.id).then(function () {
165-
console.log("Saved ok");
166-
});
146+
flusher = _.debounce(async (changedData) => {
147+
console.log("Data changed...");
148+
await this.saveFile(name, changedData, id);
149+
console.log("Saved ok");
167150
}, 200);
168151
} else {
169152
console.log("Making read-only disc");
170153
}
171154
return discFor(fdc, name, data, flusher);
172155
}
173156

174-
self.load = function (fdc, fileId) {
175-
let meta = false;
176-
return loadMetadata(fileId)
177-
.then(function (response) {
178-
meta = response.result;
179-
return downloadFile(response.result);
180-
})
181-
.then(function (data) {
182-
return makeDisc(fdc, data, meta);
183-
});
184-
};
185-
186-
self.cat = listFiles;
157+
async load(fdc, fileId) {
158+
const meta = (await this.gapi.client.drive.files.get({ fileId: fileId, fields: FILE_FIELDS })).result;
159+
const data = (await this.gapi.client.drive.files.get({ fileId: fileId, alt: "media" })).body;
160+
return this.makeDisc(fdc, data, meta);
161+
}
187162
}

0 commit comments

Comments
 (0)