aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Wilkins <samwilkins333@gmail.com>2019-09-14 05:06:59 -0400
committerSam Wilkins <samwilkins333@gmail.com>2019-09-14 05:06:59 -0400
commite2e642dfb3d71ea37c4d521d93ab16f166cc63cf (patch)
treef8f9e8de7f3d9e3129184c61d2305710d59933b6
parent4f382d9629fbfdf6502782bbb8f39dba06ae51fa (diff)
update routine
-rw-r--r--src/client/northstar/utils/Extensions.ts8
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.scss6
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx70
-rw-r--r--src/client/util/UtilExtensions.ts20
-rw-r--r--src/client/views/MainView.tsx6
-rw-r--r--src/server/credentials/google_docs_token.json2
6 files changed, 78 insertions, 34 deletions
diff --git a/src/client/northstar/utils/Extensions.ts b/src/client/northstar/utils/Extensions.ts
index 00c1e113c..f1fddf6c8 100644
--- a/src/client/northstar/utils/Extensions.ts
+++ b/src/client/northstar/utils/Extensions.ts
@@ -20,10 +20,10 @@ String.prototype.Truncate = function (length: number, replacement: string): Stri
return target;
};
-type BatchConverterSync<I, O> = (batch: I[]) => O[];
-type BatchHandlerSync<I> = (batch: I[]) => void;
-type BatchConverterAsync<I, O> = (batch: I[]) => Promise<O[]>;
-type BatchHandlerAsync<I> = (batch: I[]) => Promise<void>;
+type BatchConverterSync<I, O> = (batch: I[], isFullBatch: boolean) => O[];
+type BatchHandlerSync<I> = (batch: I[], isFullBatch: boolean) => void;
+type BatchConverterAsync<I, O> = (batch: I[], isFullBatch: boolean) => Promise<O[]>;
+type BatchHandlerAsync<I> = (batch: I[], isFullBatch: boolean) => Promise<void>;
type BatchConverter<I, O> = BatchConverterSync<I, O> | BatchConverterAsync<I, O>;
type BatchHandler<I> = BatchHandlerSync<I> | BatchHandlerAsync<I>;
diff --git a/src/client/util/Import & Export/DirectoryImportBox.scss b/src/client/util/Import & Export/DirectoryImportBox.scss
new file mode 100644
index 000000000..d33cb524b
--- /dev/null
+++ b/src/client/util/Import & Export/DirectoryImportBox.scss
@@ -0,0 +1,6 @@
+.phase {
+ position: absolute;
+ top: 15px;
+ left: 15px;
+ font-style: italic;
+} \ No newline at end of file
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 93ab5cb3b..7634d8234 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -1,9 +1,8 @@
import "fs";
import React = require("react");
-import { Doc, Opt, DocListCast, DocListCastAsync } from "../../../new_fields/Doc";
-import { DocServer } from "../../DocServer";
+import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc";
import { RouteStore } from "../../../server/RouteStore";
-import { action, observable, autorun, runInAction, computed } from "mobx";
+import { action, observable, autorun, runInAction, computed, reaction, IReactionDisposer } from "mobx";
import { FieldViewProps, FieldView } from "../../views/nodes/FieldView";
import Measure, { ContentRect } from "react-measure";
import { library } from '@fortawesome/fontawesome-svg-core';
@@ -20,6 +19,7 @@ import { Cast, BoolCast, NumCast } from "../../../new_fields/Types";
import { listSpec } from "../../../new_fields/Schema";
import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils";
import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
+import "./DirectoryImportBox.scss";
const unsupported = ["text/html", "text/plain"];
interface FileResponse {
@@ -34,6 +34,8 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
@observable private top = 0;
@observable private left = 0;
private dimensions = 50;
+ @observable private phase = "";
+ private disposer: Opt<IReactionDisposer>;
@observable private entries: ImportMetadataEntry[] = [];
@@ -73,7 +75,10 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
}
handleSelection = async (e: React.ChangeEvent<HTMLInputElement>) => {
- runInAction(() => this.uploading = true);
+ runInAction(() => {
+ this.uploading = true;
+ this.phase = "Initializing download...";
+ });
let docs: Doc[] = [];
@@ -108,8 +113,12 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
return (await fetch(Utils.prepend(RouteStore.upload), parameters)).json();
};
+ runInAction(() => this.phase = `Internal: uploading ${this.quota - this.completed} files to Dash...`);
+
const uploads = await validated.convertInBatchesAsync<FileResponse>(15, uploadLocally);
+ runInAction(() => this.phase = `Creating documents from uploads...`);
+
await Promise.all(uploads.map(async upload => {
const type = upload.type;
const path = Utils.prepend(upload.path);
@@ -151,8 +160,10 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
const headers = ["title", "size"].map(key => new SchemaHeaderField(key));
importContainer = Docs.Create.SchemaDocument(headers, docs, options);
}
- await GooglePhotos.Export.CollectionToAlbum({ collection: importContainer });
+ runInAction(() => this.phase = 'External: uploading files to Google Photos...');
importContainer.singleColumn = false;
+ await GooglePhotos.Export.CollectionToAlbum({ collection: importContainer });
+ runInAction(() => this.phase = 'All files uploaded to Google Photos...');
Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
!this.persistent && this.props.removeDocument && this.props.removeDocument(doc);
DocumentManager.Instance.jumpToDocument(importContainer, true);
@@ -168,6 +179,14 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
componentDidMount() {
this.selector.current!.setAttribute("directory", "");
this.selector.current!.setAttribute("webkitdirectory", "");
+ this.disposer = reaction(
+ () => this.completed,
+ completed => runInAction(() => this.phase = `Internal: uploading ${this.quota - completed} files to Dash...`)
+ );
+ }
+
+ componentWillUnmount() {
+ this.disposer && this.disposer();
}
@action
@@ -218,10 +237,38 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
percent = percent.split(".")[0];
percent = percent.startsWith("100") ? "99" : percent;
let marginOffset = (percent.length === 1 ? 5 : 0) - 1.6;
+ const message = <span className={"phase"}>{this.phase}</span>;
+ const centerPiece = this.phase.includes("Google Photos") ?
+ <img src={"/assets/google_photos.png"} style={{
+ transition: "0.4s opacity ease",
+ width: 30,
+ height: 30,
+ opacity: uploading ? 1 : 0,
+ pointerEvents: "none",
+ position: "absolute",
+ left: 12,
+ top: this.top + 10,
+ fontSize: 18,
+ color: "white",
+ marginLeft: this.left + marginOffset
+ }} />
+ : <div
+ style={{
+ transition: "0.4s opacity ease",
+ opacity: uploading ? 1 : 0,
+ pointerEvents: "none",
+ position: "absolute",
+ left: 10,
+ top: this.top + 12.3,
+ fontSize: 18,
+ color: "white",
+ marginLeft: this.left + marginOffset
+ }}>{percent}%</div>;
return (
<Measure offset onResize={this.preserveCentering}>
{({ measureRef }) =>
<div ref={measureRef} style={{ width: "100%", height: "100%", pointerEvents: "all" }} >
+ {message}
<input
id={"selector"}
ref={this.selector}
@@ -294,18 +341,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
opacity: showRemoveLabel ? 1 : 0,
transition: "0.4s opacity ease"
}}>Template will be <span style={{ textDecoration: "underline", textDecorationColor: persistent ? "green" : "red", color: persistent ? "green" : "red" }}>{persistent ? "kept" : "removed"}</span> after upload</p>
- <div
- style={{
- transition: "0.4s opacity ease",
- opacity: uploading ? 1 : 0,
- pointerEvents: "none",
- position: "absolute",
- left: 10,
- top: this.top + 12.3,
- fontSize: 18,
- color: "white",
- marginLeft: this.left + marginOffset
- }}>{percent}%</div>
+ {centerPiece}
<div
style={{
position: "absolute",
diff --git a/src/client/util/UtilExtensions.ts b/src/client/util/UtilExtensions.ts
index eca10c3b1..0b325bc51 100644
--- a/src/client/util/UtilExtensions.ts
+++ b/src/client/util/UtilExtensions.ts
@@ -12,7 +12,8 @@ module.exports.Batch = function <T>(batchSize: number): T[][] {
module.exports.ExecuteBatches = function <I, O>(batchSize: number, handler: BatchHandlerSync<I>): void {
if (this.length) {
for (let batch of this.batch(batchSize)) {
- handler(batch);
+ const isFullBatch = batch.length === batchSize;
+ handler(batch, isFullBatch);
}
}
};
@@ -23,7 +24,8 @@ module.exports.ConvertInBatches = function <I, O>(batchSize: number, handler: Ba
}
let collector: O[] = [];
for (let batch of this.batch(batchSize)) {
- collector.push(...handler(batch));
+ const isFullBatch = batch.length === batchSize;
+ collector.push(...handler(batch, isFullBatch));
}
return collector;
};
@@ -31,7 +33,8 @@ module.exports.ConvertInBatches = function <I, O>(batchSize: number, handler: Ba
module.exports.ExecuteInBatchesAsync = async function <I>(batchSize: number, handler: BatchHandler<I>): Promise<void> {
if (this.length) {
for (let batch of this.batch(batchSize)) {
- await handler(batch);
+ const isFullBatch = batch.length === batchSize;
+ await handler(batch, isFullBatch);
}
}
};
@@ -42,7 +45,8 @@ module.exports.ConvertInBatchesAsync = async function <I, O>(batchSize: number,
}
let collector: O[] = [];
for (let batch of this.batch(batchSize)) {
- collector.push(...(await handler(batch)));
+ const isFullBatch = batch.length === batchSize;
+ collector.push(...(await handler(batch, isFullBatch)));
}
return collector;
};
@@ -59,7 +63,9 @@ module.exports.ExecuteInBatchesAtInterval = async function <I>(batchSize: number
const next = iterator.next();
await new Promise<void>(resolve => {
setTimeout(async () => {
- await handler(next.value);
+ const batch = next.value;
+ const isFullBatch = batch.length === batchSize;
+ await handler(batch, isFullBatch);
resolve();
}, interval * 1000);
});
@@ -84,7 +90,9 @@ module.exports.ConvertInBatchesAtInterval = async function <I, O>(batchSize: num
const next = iterator.next();
await new Promise<void>(resolve => {
setTimeout(async () => {
- collector.push(...(await handler(next.value)));
+ const batch = next.value;
+ const isFullBatch = batch.length === batchSize;
+ collector.push(...(await handler(batch, isFullBatch)));
resolve();
}, interval * 1000);
});
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index f7b66cae3..d1e0733a7 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -576,12 +576,6 @@ export class MainView extends React.Component {
</button>
</div></li>)}
<li key="undoTest"><button className="add-button round-button" title="Click if undo isn't working" onClick={() => UndoManager.TraceOpenBatches()}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li>
- {ClientUtils.RELEASE ? [] : [
- <li key="test"><button className="add-button round-button" title="Default" onClick={() => this.setWriteMode(DocServer.WriteMode.Default)}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li>,
- <li key="test1"><button className="add-button round-button" title="Playground" onClick={() => this.setWriteMode(DocServer.WriteMode.Playground)}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li>,
- <li key="test2"><button className="add-button round-button" title="Live Playground" onClick={() => this.setWriteMode(DocServer.WriteMode.LivePlayground)}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li>,
- <li key="test3"><button className="add-button round-button" title="Live Readonly" onClick={() => this.setWriteMode(DocServer.WriteMode.LiveReadonly)}><FontAwesomeIcon icon="exclamation" size="sm" /></button></li>
- ]}
<li key="color"><button className="add-button round-button" title="Select Color" style={{ zIndex: 1000 }} onClick={() => this.toggleColorPicker()}><div className="toolbar-color-button" style={{ backgroundColor: InkingControl.Instance.selectedColor }} >
<div className="toolbar-color-picker" onClick={this.onColorClick} style={this._colorPickerDisplay ? { color: "black", display: "block" } : { color: "black", display: "none" }}>
<SketchPicker color={InkingControl.Instance.selectedColor} onChange={InkingControl.Instance.switchColor} />
diff --git a/src/server/credentials/google_docs_token.json b/src/server/credentials/google_docs_token.json
index a5197b5f3..5d7fe5455 100644
--- a/src/server/credentials/google_docs_token.json
+++ b/src/server/credentials/google_docs_token.json
@@ -1 +1 @@
-{"access_token":"ya29.ImCDByMCQwL1GEi2BT-yGcoD7Y1Bn-UY-N-Nguu5mijnhOkahApylC6HAJJ8mbkoGdFxywbn-yIkJkr6xQZkZMa2TW29ekjl_791aEvnr_ZUn2O-xJ7r909Rm1FuOhcGdwQ","refresh_token":"1/HTv_xFHszu2Nf3iiFrUTaeKzC_Vp2-6bpIB06xW_WHI","scope":"https://www.googleapis.com/auth/presentations.readonly https://www.googleapis.com/auth/documents.readonly https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/documents https://www.googleapis.com/auth/photoslibrary https://www.googleapis.com/auth/photoslibrary.appendonly https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/presentations https://www.googleapis.com/auth/photoslibrary.sharing","token_type":"Bearer","expiry_date":1568448563751} \ No newline at end of file
+{"access_token":"ya29.ImCDB3rM9R_0Sg7kblgjz0CJvz43FxDGnbC97xBSDhV20QyzQLrom5uJB-eEOOwrvRbP3e4LiumtI_ghx-6a0q9ydDsctF0-0Npohmo5cWFtwpcph7bmbBoPmJIJvQrjQ1Q","refresh_token":"1/HTv_xFHszu2Nf3iiFrUTaeKzC_Vp2-6bpIB06xW_WHI","scope":"https://www.googleapis.com/auth/presentations.readonly https://www.googleapis.com/auth/documents.readonly https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/documents https://www.googleapis.com/auth/photoslibrary https://www.googleapis.com/auth/photoslibrary.appendonly https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/presentations https://www.googleapis.com/auth/photoslibrary.sharing","token_type":"Bearer","expiry_date":1568452654650} \ No newline at end of file