From b2aa0b80843e6e58f737e2937c45351d653255fa Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 27 Jan 2020 04:18:01 -0500 Subject: pivot field box and proper AND/OR behavior of facets --- .../views/collections/CollectionPivotView.scss | 36 +++++++++++++++++----- .../views/collections/CollectionPivotView.tsx | 22 +++++++++++++ src/client/views/collections/CollectionSubView.tsx | 29 ++++++++++++----- .../CollectionFreeFormLayoutEngines.tsx | 2 +- src/new_fields/Doc.ts | 4 +-- 5 files changed, 75 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionPivotView.scss b/src/client/views/collections/CollectionPivotView.scss index 2ddcc97ea..505091e98 100644 --- a/src/client/views/collections/CollectionPivotView.scss +++ b/src/client/views/collections/CollectionPivotView.scss @@ -2,12 +2,14 @@ display: flex; flex-direction: row; position: absolute; - height:100%; - width:100%; + height: 100%; + width: 100%; + .collectionPivotView-flyout { width: 400px; height: 300px; display: inline-block; + .collectionPivotView-flyout-item { background-color: lightgray; text-align: left; @@ -17,43 +19,61 @@ } } + .pivotKeyEntry { + position: absolute; + top: 5px; + right: 5px; + z-index: 10; + pointer-events: all; + padding: 5px; + border: 1px solid black; + } + .collectionPivotView-treeView { - display:flex; + display: flex; flex-direction: column; width: 200px; height: 100%; + .collectionPivotView-addfacet { - display:inline-block; + display: inline-block; width: 200px; height: 30px; background: darkGray; text-align: center; + .collectionPivotView-button { align-items: center; display: flex; width: 100%; height: 100%; + .collectionPivotView-span { margin: auto; } } - > div, > div > div { + + >div, + >div>div { width: 100%; height: 100%; text-align: center; } } + .collectionPivotView-tree { - display:inline-block; + display: inline-block; width: 100%; height: calc(100% - 30px); } } + .collectionPivotView-pivot { - display:inline-block; + display: inline-block; width: calc(100% - 200px); height: 100%; } + .collectionPivotView-dragger { background-color: lightgray; height: 40px; @@ -63,6 +83,6 @@ top: 55%; border: 1px black solid; z-index: 2; - left:-10px; + left: -10px; } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx index a44f990e1..98fc54b3b 100644 --- a/src/client/views/collections/CollectionPivotView.tsx +++ b/src/client/views/collections/CollectionPivotView.tsx @@ -16,6 +16,8 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { List } from "../../../new_fields/List"; import { Set } from "typescript-collections"; import { PrefetchProxy } from "../../../new_fields/Proxy"; +import { EditableView } from "../EditableView"; +import { listSpec } from "../../../new_fields/Schema"; @observer export class CollectionPivotView extends CollectionSubView(doc => doc) { @@ -76,6 +78,13 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { const found = DocListCast(facetCollection.data).findIndex(doc => doc.title === facetHeader); if (found !== -1) { (facetCollection.data as List).splice(found, 1); + const docFilter = Cast(this.props.Document._docFilter, listSpec("string")); + if (docFilter) { + let index: number; + while ((index = docFilter.findIndex(item => item === facetHeader)) !== -1) { + docFilter.splice(index, 3); + } + } } else { const newFacet = Docs.Create.TreeDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true }); const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc }; @@ -129,6 +138,19 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { ); return !facetCollection ? (null) :
+
+ StrCast(this.props.Document.pivotField)} + SetValue={value => { + if (value && value.length) { + this.props.Document.pivotField = value; + return true; + } + return false; + }} + /> +
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index db3fde797..b96b86929 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -109,15 +109,30 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField); const viewedDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs; const docFilters = Cast(this.props.Document._docFilter, listSpec("string"), []); + const clusters: { [key: string]: { [value: string]: string } } = {}; + for (let i = 0; i < docFilters.length; i += 3) { + const [key, value, modifiers] = docFilters.slice(i, i + 3); + const cluster = clusters[key]; + if (!cluster) { + const child: { [value: string]: string } = {}; + child[value] = modifiers; + clusters[key] = child; + } else { + cluster[value] = modifiers; + } + } const filteredDocs = docFilters.length ? viewedDocs.filter(d => { - let result = false; - for (let i = 0; i < docFilters.length; i += 3) { - const key = docFilters[i]; - const value = docFilters[i + 1]; - const modifiers = docFilters[i + 2]; - result = result || ((modifiers === "x") !== Doc.matchFieldValue(d, key, value)); + for (const key of Object.keys(clusters)) { + const cluster = clusters[key]; + const satisfiesFacet = Object.keys(cluster).some(inner => { + const modifier = cluster[inner]; + return (modifier === "x") !== Doc.matchFieldValue(d, key, inner); + }); + if (!satisfiesFacet) { + return false; + } } - return result; + return true; }) : viewedDocs; return filteredDocs; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 09ee3255d..be1317b25 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -59,7 +59,7 @@ export function computePivotLayout(poolData: ObservableMap, pivotDo let numCols = NumCast(pivotDoc.pivotNumColumns, Math.ceil(Math.sqrt(minSize))); const docMap = new Map(); const groupNames: PivotData[] = []; - numCols = Math.min(panelDim[0] / pivotAxisWidth, numCols) + numCols = Math.min(panelDim[0] / pivotAxisWidth, numCols); const expander = 1.05; const gap = .15; diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index cc146a121..41a9dddf2 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -761,7 +761,7 @@ export namespace Doc { return vals.some(v => v === value); } const fieldStr = Field.toString(fieldVal as Field); - return fieldStr == value; + return fieldStr === value; } } @@ -791,7 +791,7 @@ Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: an } if (modifiers !== undefined) { docFilters.push(key); - docFilters.push(value) + docFilters.push(value); docFilters.push(modifiers); container._docFilter = new List(docFilters); } -- cgit v1.2.3-70-g09d2 From c5d618538d4fd9669476dc7eb66ddd45783e5fa6 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 27 Jan 2020 05:50:15 -0500 Subject: nativewidth and nativeheight extension key fix and implemented UI to manually download a remotely hosted image --- .../util/Import & Export/DirectoryImportBox.tsx | 4 ++-- src/client/util/Import & Export/ImageUtils.ts | 4 ++-- src/client/views/DocComponent.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 2 +- src/client/views/nodes/AudioBox.tsx | 2 +- src/client/views/nodes/ImageBox.scss | 24 ++++++++++++++----- src/client/views/nodes/ImageBox.tsx | 28 +++++++++++++++++++++- src/mobile/ImageUpload.tsx | 2 +- src/server/ApiManagers/UploadManager.ts | 14 ++++++++++- src/server/DashUploadUtils.ts | 12 +++------- 10 files changed, 69 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index c7c94abed..071015193 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -116,13 +116,13 @@ export default class DirectoryImportBox extends React.Component formData.append(Utils.GenerateGuid(), file); }); - collector.push(...(await Networking.PostFormDataToServer("/upload", formData))); + collector.push(...(await Networking.PostFormDataToServer("/uploadFormData", formData))); runInAction(() => this.completed += batch.length); }); await Promise.all(uploads.map(async ({ name, type, clientAccessPath, exifData }) => { const path = Utils.prepend(clientAccessPath); - const document = await Docs.Get.DocumentFromType(type, path, { width: 300, title: name }); + const document = await Docs.Get.DocumentFromType(type, path, { _width: 300, title: name }); const { data, error } = exifData; if (document) { Doc.GetProto(document).exif = error || Docs.Get.DocumentHierarchyFromJson(data); diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts index 2f4db0e17..ff909cc6b 100644 --- a/src/client/util/Import & Export/ImageUtils.ts +++ b/src/client/util/Import & Export/ImageUtils.ts @@ -22,8 +22,8 @@ export namespace ImageUtils { } = await Networking.PostToServer("/inspectImage", { source }); document.exif = error || Docs.Get.DocumentHierarchyFromJson(data); const proto = Doc.GetProto(document); - proto.nativeWidth = nativeWidth; - proto.nativeHeight = nativeHeight; + proto["data-nativeWidth"] = nativeWidth; + proto["data-nativeHeight"] = nativeHeight; proto.contentSize = contentSize; return data !== undefined; }; diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index d98301cf6..6f3eb6808 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -61,7 +61,7 @@ export function DocAnnotatableComponent

(schema _annotationKey: string = "annotations"; public set annotationKey(val: string) { this._annotationKey = val; } - public get annotationKey() { return this._annotationKey } + public get annotationKey() { return this._annotationKey; } @action.bound removeDocument(doc: Doc): boolean { diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index b96b86929..80fcc33aa 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -324,7 +324,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { formData.append('file', file); const dropFileName = file ? file.name : "-empty-"; - promises.push(Networking.PostFormDataToServer("/upload", formData).then(results => { + promises.push(Networking.PostFormDataToServer("/uploadFormData", formData).then(results => { results.map(action((result: any) => { const { clientAccessPath, nativeWidth, nativeHeight, contentSize } = result; const full = { ...options, _width: 300, title: dropFileName }; diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 8ede79edc..62a479b2a 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -138,7 +138,7 @@ export class AudioBox extends DocExtendableComponent); } + @computed + private get considerDownloadIcon() { + const data = this.dataDoc[this.props.fieldKey]; + if (!(data instanceof ImageField)) { + return (null); + } + const primary = data.url.href; + if (primary.includes(window.location.origin)) { + return (null); + } + return ( + { + const { dataDoc } = this; + const [{ clientAccessPath }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [primary] }); + dataDoc.originalUrl = primary; + dataDoc[this.props.fieldKey] = new ImageField(Utils.prepend(clientAccessPath)); + }} + /> + ); + } + @computed get nativeSize() { const pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50; const nativeWidth = NumCast(this.dataDoc[this.props.fieldKey + "-nativeWidth"], pw); @@ -347,6 +372,7 @@ export class ImageBox extends DocAnnotatableComponent

+ {this.considerDownloadIcon} {this.considerGooglePhotosLink()} ; diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index dab4de110..1583e3d5d 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -45,7 +45,7 @@ class Uploader extends React.Component { const formData = new FormData(); formData.append("file", files[0]); - const upload = window.location.origin + "/upload"; + const upload = window.location.origin + "/uploadFormData"; this.status = "uploading image"; const res = await fetch(upload, { method: 'POST', diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts index 583eaa59b..a92b613b7 100644 --- a/src/server/ApiManagers/UploadManager.ts +++ b/src/server/ApiManagers/UploadManager.ts @@ -40,7 +40,7 @@ export default class UploadManager extends ApiManager { register({ method: Method.POST, - subscription: "/upload", + subscription: "/uploadFormData", secureHandler: async ({ req, res }) => { const form = new formidable.IncomingForm(); form.uploadDir = pathToDirectory(Directory.parsed_files); @@ -59,6 +59,18 @@ export default class UploadManager extends ApiManager { } }); + register({ + method: Method.POST, + subscription: "/uploadRemoteImage", + secureHandler: async ({ req, res }) => { + const { sources } = req.body; + if (Array.isArray(sources)) { + return res.send(await Promise.all(sources.map(url => DashUploadUtils.UploadImage(url)))); + } + res.send(); + } + }); + register({ method: Method.POST, subscription: "/uploadDoc", diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts index b826b4882..cb7104757 100644 --- a/src/server/DashUploadUtils.ts +++ b/src/server/DashUploadUtils.ts @@ -108,13 +108,7 @@ export namespace DashUploadUtils { return MoveParsedFile(absolutePath, Directory.pdfs); } - const generate = (prefix: string, url: string) => `${prefix}upload_${Utils.GenerateGuid()}${sanitizeExtension(url)}`; - const sanitizeExtension = (source: string) => { - let extension = path.extname(source); - extension = extension.toLowerCase(); - extension = extension.split("?")[0]; - return extension; - }; + const generate = (prefix: string, extension: string) => `${prefix}upload_${Utils.GenerateGuid()}.${extension}`; /** * Uploads an image specified by the @param source to Dash's /public/files/ @@ -209,8 +203,8 @@ export namespace DashUploadUtils { export const UploadInspectedImage = async (metadata: InspectionResults, filename?: string, format?: string, prefix = ""): Promise => { const { requestable, source, ...remaining } = metadata; - const resolved = filename || generate(prefix, requestable); - const extension = format || sanitizeExtension(requestable || resolved); + const extension = remaining.contentType.toLowerCase().split("/")[1]; //format || sanitizeExtension(requestable || resolved); + const resolved = filename || generate(prefix, extension); const information: ImageUploadInformation = { clientAccessPath: clientPathToFile(Directory.images, resolved), serverAccessPaths: {}, -- cgit v1.2.3-70-g09d2 From bb768ca2a9274d07ef4618e527452dcdd6cd430d Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 27 Jan 2020 13:19:50 -0500 Subject: fallback behavior for non-downloaded images and restored ink recognition --- src/Utils.ts | 2 +- src/client/cognitive_services/CognitiveServices.ts | 3 +- src/client/views/InkingStroke.tsx | 34 ++++++++++++++++++---- .../collectionFreeForm/CollectionFreeFormView.tsx | 19 +----------- src/client/views/nodes/ImageBox.tsx | 8 +++-- 5 files changed, 38 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/Utils.ts b/src/Utils.ts index 7bf05a6fc..4deac9035 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -48,7 +48,7 @@ export namespace Utils { } export async function getApiKey(target: string): Promise { - const response = await fetch(prepend(`environment/${target.toUpperCase()}`)); + const response = await fetch(prepend(`/environment/${target.toUpperCase()}`)); return response.text(); } diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index 57296c961..9e2ceac62 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -185,8 +185,9 @@ export namespace CognitiveServices { results.recognitionUnits && (results = results.recognitionUnits); target[keys[0]] = Docs.Get.DocumentHierarchyFromJson(results, "Ink Analysis"); const recognizedText = results.map((item: any) => item.recognizedText); + const recognizedObjects = results.map((item: any) => item.recognizedObject); const individualWords = recognizedText.filter((text: string) => text && text.split(" ").length === 1); - target[keys[1]] = individualWords.join(" "); + target[keys[1]] = individualWords.length ? individualWords.join(" ") : recognizedObjects.join(", "); } batch.end(); diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index aca507147..f315ce12a 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -11,6 +11,12 @@ import { FieldView, FieldViewProps } from "./nodes/FieldView"; import React = require("react"); import { TraceMobx } from "../../new_fields/util"; import { InteractionUtils } from "../util/InteractionUtils"; +import { ContextMenu } from "./ContextMenu"; +import { CognitiveServices } from "../cognitive_services/CognitiveServices"; +import { faPaintBrush } from "@fortawesome/free-solid-svg-icons"; +import { library } from "@fortawesome/fontawesome-svg-core"; + +library.add(faPaintBrush); type InkDocument = makeInterface<[typeof documentSchema]>; const InkDocument = makeInterface(documentSchema); @@ -22,6 +28,11 @@ export class InkingStroke extends DocExtendableComponent { + const data: InkData = Cast(this.Document.data, InkField)?.inkData ?? []; + CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.Document, ["inkAnalysis", "handwriting"], [data]); + } + render() { TraceMobx(); const data: InkData = Cast(this.Document.data, InkField)?.inkData ?? []; @@ -37,12 +48,23 @@ export class InkingStroke extends DocExtendableComponent + { + ContextMenu.Instance.addItem({ + description: "Analyze Stroke", + event: this.analyzeStrokes, + icon: "paint-brush" + }); + }} + > {points} ); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 70f8a0e73..4ba58ac84 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -819,23 +819,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { }, "arrange contents"); } - - analyzeStrokes = async () => { - const children = await DocListCastAsync(this.dataDoc.data); - if (!children) { - return; - } - const inkData: InkData[] = []; - for (const doc of children) { - const data = Cast(doc.data, InkField)?.inkData; - data && inkData.push(data); - } - if (!inkData.length) { - return; - } - CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ["inkAnalysis", "handwriting"], inkData); - } - private thumbIdentifier?: number; // @action @@ -887,7 +870,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { layoutItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); layoutItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" }); layoutItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }); - layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" }); + // layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" }); layoutItems.push({ description: "Jitter Rotation", event: action(() => this.props.Document.jitterRotation = 10), icon: "paint-brush" }); layoutItems.push({ description: "Import document", icon: "upload", event: ({ x, y }) => { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 3d15bcb15..adec13e32 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -8,7 +8,7 @@ import { Doc, DocListCast, HeightSym, WidthSym, DataSym } from '../../../new_fie import { List } from '../../../new_fields/List'; import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema'; import { ComputedField } from '../../../new_fields/ScriptField'; -import { Cast, NumCast } from '../../../new_fields/Types'; +import { Cast, NumCast, StrCast } from '../../../new_fields/Types'; import { AudioField, ImageField } from '../../../new_fields/URLField'; import { Utils, returnOne, emptyFunction } from '../../../Utils'; import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices'; @@ -214,11 +214,15 @@ export class ImageBox extends DocAnnotatableComponent { + @action onError = (error: any) => { const timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount; if (timeout < 10) { // setTimeout(this.retryPath, 500); } + const original = StrCast(this.dataDoc.originalUrl); + if (error.type === "error" && original) { + this.dataDoc[this.props.fieldKey] = new ImageField(original); + } } _curSuffix = "_m"; -- cgit v1.2.3-70-g09d2 From b049149fcf852d8a83338a24341083653fecff89 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 27 Jan 2020 16:59:36 -0500 Subject: added UI responsiveness for remote image upload routine --- deploy/assets/greencheck.png | Bin 0 -> 49332 bytes deploy/assets/redx.png | Bin 0 -> 7353 bytes src/client/documents/Documents.ts | 2 +- src/client/views/collections/CollectionSubView.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 21 +++++++++++++++++++-- 5 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 deploy/assets/greencheck.png create mode 100644 deploy/assets/redx.png (limited to 'src') diff --git a/deploy/assets/greencheck.png b/deploy/assets/greencheck.png new file mode 100644 index 000000000..064e9def1 Binary files /dev/null and b/deploy/assets/greencheck.png differ diff --git a/deploy/assets/redx.png b/deploy/assets/redx.png new file mode 100644 index 000000000..0c2c9ccc5 Binary files /dev/null and b/deploy/assets/redx.png differ diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ac643f91f..a7f939d7d 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -324,7 +324,7 @@ export namespace Docs { // whatever options pertain to this specific prototype const options = { title, type, baseProto: true, ...defaultOptions, ...(template.options || {}) }; options.layout = layout.view.LayoutString(layout.dataField); - let doc = Doc.assign(new Doc(prototypeId, true), { layoutKey: "layout", ...options }); + const doc = Doc.assign(new Doc(prototypeId, true), { layoutKey: "layout", ...options }); return doc; } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 80fcc33aa..321a6d34c 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -70,7 +70,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { (args) => { const childLayout = Cast(this.props.Document.childLayout, Doc); if (childLayout instanceof Doc) { - this.childDocs.map(doc => Doc.ApplyTemplateTo(childLayout as Doc, doc, "layoutFromParent")); + this.childDocs.map(doc => Doc.ApplyTemplateTo(childLayout, doc, "layoutFromParent")); } else if (!(childLayout instanceof Promise)) { this.childDocs.filter(d => !d.isTemplateForField).map(doc => doc.layoutKey === "layoutFromParent" && (doc.layoutKey = "layout")); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index adec13e32..3f25faae6 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -58,6 +58,8 @@ declare class MediaRecorder { type ImageDocument = makeInterface<[typeof pageSchema, typeof documentSchema]>; const ImageDocument = makeInterface(pageSchema, documentSchema); +const defaultUploadIcon = "downarrow.png"; + @observer export class ImageBox extends DocAnnotatableComponent(ImageDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); } @@ -65,6 +67,7 @@ export class ImageBox extends DocAnnotatableComponent { this._dropDisposer && this._dropDisposer(); @@ -305,12 +308,26 @@ export class ImageBox extends DocAnnotatableComponent { const { dataDoc } = this; + runInAction(() => this.uploadIcon = "loading.gif"); const [{ clientAccessPath }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [primary] }); dataDoc.originalUrl = primary; - dataDoc[this.props.fieldKey] = new ImageField(Utils.prepend(clientAccessPath)); + let success = true; + let data: ImageField | undefined; + try { + data = new ImageField(clientAccessPath); + } catch { + success = false; + } + runInAction(() => this.uploadIcon = success ? "greencheck.png" : "redx.png"); + setTimeout(action(() => { + this.uploadIcon = defaultUploadIcon; + if (data) { + dataDoc[this.props.fieldKey] = data; + } + }), 2000); }} /> ); -- cgit v1.2.3-70-g09d2 From 9a9524b4c6e2980eb37ece288890d6cb746eb81c Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 27 Jan 2020 17:10:34 -0500 Subject: factored out icon names --- src/client/views/nodes/ImageBox.tsx | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 3f25faae6..ffcbe459e 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -58,7 +58,12 @@ declare class MediaRecorder { type ImageDocument = makeInterface<[typeof pageSchema, typeof documentSchema]>; const ImageDocument = makeInterface(pageSchema, documentSchema); -const defaultUploadIcon = "downarrow.png"; +const uploadIcons = { + idle: "downarrow.png", + loading: "loading.gif", + success: "greencheck.png", + failure: "redx.png" +}; @observer export class ImageBox extends DocAnnotatableComponent(ImageDocument) { @@ -67,7 +72,7 @@ export class ImageBox extends DocAnnotatableComponent { this._dropDisposer && this._dropDisposer(); @@ -311,19 +316,20 @@ export class ImageBox extends DocAnnotatableComponent { const { dataDoc } = this; - runInAction(() => this.uploadIcon = "loading.gif"); + const { success, failure, idle, loading } = uploadIcons; + runInAction(() => this.uploadIcon = loading); const [{ clientAccessPath }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [primary] }); dataDoc.originalUrl = primary; - let success = true; + let succeeded = true; let data: ImageField | undefined; try { - data = new ImageField(clientAccessPath); + data = new ImageField(Utils.prepend(clientAccessPath)); } catch { - success = false; + succeeded = false; } - runInAction(() => this.uploadIcon = success ? "greencheck.png" : "redx.png"); + runInAction(() => this.uploadIcon = succeeded ? success : failure); setTimeout(action(() => { - this.uploadIcon = defaultUploadIcon; + this.uploadIcon = idle; if (data) { dataDoc[this.props.fieldKey] = data; } -- cgit v1.2.3-70-g09d2