From e2e642dfb3d71ea37c4d521d93ab16f166cc63cf Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sat, 14 Sep 2019 05:06:59 -0400 Subject: update routine --- src/client/northstar/utils/Extensions.ts | 8 +-- .../util/Import & Export/DirectoryImportBox.scss | 6 ++ .../util/Import & Export/DirectoryImportBox.tsx | 70 ++++++++++++++++------ src/client/util/UtilExtensions.ts | 20 +++++-- src/client/views/MainView.tsx | 6 -- src/server/credentials/google_docs_token.json | 2 +- 6 files changed, 78 insertions(+), 34 deletions(-) create mode 100644 src/client/util/Import & Export/DirectoryImportBox.scss (limited to 'src') 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 = (batch: I[]) => O[]; -type BatchHandlerSync = (batch: I[]) => void; -type BatchConverterAsync = (batch: I[]) => Promise; -type BatchHandlerAsync = (batch: I[]) => Promise; +type BatchConverterSync = (batch: I[], isFullBatch: boolean) => O[]; +type BatchHandlerSync = (batch: I[], isFullBatch: boolean) => void; +type BatchConverterAsync = (batch: I[], isFullBatch: boolean) => Promise; +type BatchHandlerAsync = (batch: I[], isFullBatch: boolean) => Promise; type BatchConverter = BatchConverterSync | BatchConverterAsync; type BatchHandler = BatchHandlerSync | BatchHandlerAsync; 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 @observable private top = 0; @observable private left = 0; private dimensions = 50; + @observable private phase = ""; + private disposer: Opt; @observable private entries: ImportMetadataEntry[] = []; @@ -73,7 +75,10 @@ export default class DirectoryImportBox extends React.Component } handleSelection = async (e: React.ChangeEvent) => { - 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 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(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 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 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 percent = percent.split(".")[0]; percent = percent.startsWith("100") ? "99" : percent; let marginOffset = (percent.length === 1 ? 5 : 0) - 1.6; + const message = {this.phase}; + const centerPiece = this.phase.includes("Google Photos") ? + + :
{percent}%
; return ( {({ measureRef }) =>
+ {message} opacity: showRemoveLabel ? 1 : 0, transition: "0.4s opacity ease" }}>Template will be {persistent ? "kept" : "removed"} after upload

-
{percent}%
+ {centerPiece}
(batchSize: number): T[][] { module.exports.ExecuteBatches = function (batchSize: number, handler: BatchHandlerSync): 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 (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 (batchSize: number, handler: Ba module.exports.ExecuteInBatchesAsync = async function (batchSize: number, handler: BatchHandler): Promise { 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 (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 (batchSize: number const next = iterator.next(); await new Promise(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 (batchSize: num const next = iterator.next(); await new Promise(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 {
)}
  • - {ClientUtils.RELEASE ? [] : [ -
  • , -
  • , -
  • , -
  • - ]}