From f62f4d92001e775b7248b7e786de733225e107b5 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 20 May 2019 11:15:43 -0400 Subject: added switching to low res version based on display size. fixed marqueeing on full size images, fixed bug with kvp pane and image sizing, --- src/client/views/MainView.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 38 ++++++++++++++++++---- 3 files changed, 33 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 9edbba997..6169466bc 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -131,7 +131,7 @@ export class MainView extends React.Component { createNewWorkspace = async (id?: string) => { const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc)); if (list) { - let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, title: `WS collection ${list.length + 1}` }); + let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, width: this.pwidth * .7, height: this.pheight, title: `WS collection ${list.length + 1}` }); var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(CurrentUserUtils.UserDocument, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] }; let mainDoc = Docs.DockDocument([CurrentUserUtils.UserDocument, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` }, id); list.push(mainDoc); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index e904358a9..9721bf804 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -434,7 +434,7 @@ export class DockedFrameRenderer extends React.Component { if (this._mainCont.current && this._mainCont.current.children) { let { scale, translateX, translateY } = Utils.GetScreenTransform(this._mainCont.current.children[0].firstChild as HTMLElement); scale = Utils.GetScreenTransform(this._mainCont.current).scale; - return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / scale); + return CollectionDockingView.Instance.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale); } return Transform.Identity(); } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 828ac9bc8..0a479cf41 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -17,6 +17,13 @@ import { ImageField } from '../../../new_fields/URLField'; import { List } from '../../../new_fields/List'; import { InkingControl } from '../InkingControl'; import { Doc } from '../../../new_fields/Doc'; +import { faImage } from '@fortawesome/free-solid-svg-icons'; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +var path = require('path'); + +library.add(faImage); + export const pageSchema = createSchema({ curPage: "number" @@ -41,7 +48,7 @@ export class ImageBox extends DocComponent(ImageD onLoad = (target: any) => { var h = this._imgRef.current!.naturalHeight; var w = this._imgRef.current!.naturalWidth; - if (this._photoIndex === 0 && (this.props as any).id !== "isExpander") { + if (this._photoIndex === 0 && (this.props as any).id !== "isExpander" && (!this.Document.nativeHeight || !this.Document.nativeWidth)) { Doc.SetOnPrototype(this.Document, "nativeHeight", FieldValue(this.Document.nativeWidth, 0) * h / w); this.Document.height = FieldValue(this.Document.width, 0) * h / w; } @@ -155,14 +162,30 @@ export class ImageBox extends DocComponent(ImageD ); } + @observable _smallImageMissing = false; render() { - let field = this.Document[this.props.fieldKey]; - let paths: string[] = ["http://www.cs.brown.edu/~bcz/face.gif"]; - if (field instanceof ImageField) paths = [field.url.href]; - else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => (p as ImageField).url.href); - let nativeWidth = FieldValue(this.Document.nativeWidth, (this.props.PanelWidth as any) as string ? Number((this.props.PanelWidth as any) as string) : 50); - let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive"; + let transform = this.props.ScreenToLocalTransform().inverse(); + let pw = Object.keys(this.props.PanelWidth).length === 0 ? this.props.PanelWidth() : (this.props.PanelWidth as any) as string ? Number((this.props.PanelWidth as any) as string) : 50; + var [sptX, sptY] = transform.transformPoint(0, 0); + let [bptX, bptY] = transform.transformPoint(pw, this.props.PanelHeight()); + let w = bptX - sptX; + let id = (this.props as any).id; // bcz: used to set id = "isExpander" in templates.tsx + let nativeWidth = FieldValue(this.Document.nativeWidth, pw); + let paths: string[] = ["http://www.cs.brown.edu/~bcz/noImage.png"]; + if (w > 20) { + if (w < 100 && !this._smallImageMissing) { + let field = this.Document[this.props.fieldKey]; + if (field instanceof ImageField) paths = [field.url.href.replace(path.extname(field.url.href), "_s" + path.extname(field.url.href))]; + else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => + (p as ImageField).url.href.replace(path.extname((p as ImageField).url.href), "_s" + path.extname((p as ImageField).url.href))); + } else { + let field = this.Document[this.props.fieldKey]; + if (field instanceof ImageField) paths = [field.url.href]; + else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => (p as ImageField).url.href); + } + } + let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive"; return (
(ImageD style={{ objectFit: (this._photoIndex === 0 ? undefined : "contain") }} width={nativeWidth} ref={this._imgRef} + onError={action(() => this._smallImageMissing = true)} onLoad={this.onLoad} /> {paths.length > 1 ? this.dots(paths) : (null)} {this.lightbox(paths)} -- cgit v1.2.3-70-g09d2 From baa57bae05df83c37e4573b7afb92948fc6e910b Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 20 May 2019 11:24:54 -0400 Subject: using all image sizes now. --- src/client/views/nodes/ImageBox.tsx | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 0a479cf41..b4aae3646 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -163,6 +163,9 @@ export class ImageBox extends DocComponent(ImageD } @observable _smallImageMissing = false; + @observable _mediumImageMissing = false; + @observable _largeImageMissing = false; + _curImageSize = ""; render() { let transform = this.props.ScreenToLocalTransform().inverse(); let pw = Object.keys(this.props.PanelWidth).length === 0 ? this.props.PanelWidth() : (this.props.PanelWidth as any) as string ? Number((this.props.PanelWidth as any) as string) : 50; @@ -175,14 +178,23 @@ export class ImageBox extends DocComponent(ImageD let paths: string[] = ["http://www.cs.brown.edu/~bcz/noImage.png"]; if (w > 20) { if (w < 100 && !this._smallImageMissing) { + this._curImageSize = "small"; let field = this.Document[this.props.fieldKey]; if (field instanceof ImageField) paths = [field.url.href.replace(path.extname(field.url.href), "_s" + path.extname(field.url.href))]; else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => (p as ImageField).url.href.replace(path.extname((p as ImageField).url.href), "_s" + path.extname((p as ImageField).url.href))); - } else { + } else if (w < 600 && !this._mediumImageMissing) { + this._curImageSize = "medium"; let field = this.Document[this.props.fieldKey]; - if (field instanceof ImageField) paths = [field.url.href]; - else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => (p as ImageField).url.href); + if (field instanceof ImageField) paths = [field.url.href.replace(path.extname(field.url.href), "_m" + path.extname(field.url.href))]; + else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => + (p as ImageField).url.href.replace(path.extname((p as ImageField).url.href), "_m" + path.extname((p as ImageField).url.href))); + } else if (!this._largeImageMissing) { + this._curImageSize = "large"; + let field = this.Document[this.props.fieldKey]; + if (field instanceof ImageField) paths = [field.url.href.replace(path.extname(field.url.href), "_l" + path.extname(field.url.href))]; + else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => + (p as ImageField).url.href.replace(path.extname((p as ImageField).url.href), "_l" + path.extname((p as ImageField).url.href))); } } let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive"; @@ -194,7 +206,11 @@ export class ImageBox extends DocComponent(ImageD style={{ objectFit: (this._photoIndex === 0 ? undefined : "contain") }} width={nativeWidth} ref={this._imgRef} - onError={action(() => this._smallImageMissing = true)} + onError={action(() => { + if (this._curImageSize === "small") this._smallImageMissing = true; + if (this._curImageSize === "medium") this._mediumImageMissing = true; + if (this._curImageSize === "large") this._largeImageMissing = true; + })} onLoad={this.onLoad} /> {paths.length > 1 ? this.dots(paths) : (null)} {this.lightbox(paths)} -- cgit v1.2.3-70-g09d2 From e5d71fc1362c77f076c10187625c74fe294669aa Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 20 May 2019 12:20:57 -0400 Subject: fixes to caching. lower quality Summary images. --- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 57 +++++++++++----------- 2 files changed, 30 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 2029b91e5..6ea230aab 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -257,7 +257,7 @@ export class MarqueeView extends React.Component if (e.key === "s" || e.key === "p") { - htmlToImage.toPng(this._mainCont.current!, { width: bounds.width * zoomBasis, height: bounds.height * zoomBasis, quality: 1 }).then((dataUrl) => { + htmlToImage.toPng(this._mainCont.current!, { width: bounds.width * zoomBasis, height: bounds.height * zoomBasis, quality: 0.2 }).then((dataUrl) => { selected.map(d => { this.props.removeDocument(d); d.x = NumCast(d.x) - bounds.left - bounds.width / 2; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index b4aae3646..29a99e53c 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -162,13 +162,25 @@ export class ImageBox extends DocComponent(ImageD ); } - @observable _smallImageMissing = false; - @observable _mediumImageMissing = false; - @observable _largeImageMissing = false; - _curImageSize = ""; + choosePath(url: URL) { + if (url.protocol === "data") + return url.href; + let ext = path.extname(url.href); + return url.href.replace(ext, this._curSuffix + ext); + } + + @observable _smallRetryCount = 1; + @observable _mediumRetryCount = 1; + @observable _largeRetryCount = 1; + @action retryPath = () => { + if (this._curSuffix === "_s") this._smallRetryCount++; + if (this._curSuffix === "_m") this._mediumRetryCount++; + if (this._curSuffix === "_l") this._largeRetryCount++; + } + _curSuffix = ""; render() { let transform = this.props.ScreenToLocalTransform().inverse(); - let pw = Object.keys(this.props.PanelWidth).length === 0 ? this.props.PanelWidth() : (this.props.PanelWidth as any) as string ? Number((this.props.PanelWidth as any) as string) : 50; + let pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50; var [sptX, sptY] = transform.transformPoint(0, 0); let [bptX, bptY] = transform.transformPoint(pw, this.props.PanelHeight()); let w = bptX - sptX; @@ -176,40 +188,29 @@ export class ImageBox extends DocComponent(ImageD let id = (this.props as any).id; // bcz: used to set id = "isExpander" in templates.tsx let nativeWidth = FieldValue(this.Document.nativeWidth, pw); let paths: string[] = ["http://www.cs.brown.edu/~bcz/noImage.png"]; + this._curSuffix = ""; if (w > 20) { - if (w < 100 && !this._smallImageMissing) { - this._curImageSize = "small"; - let field = this.Document[this.props.fieldKey]; - if (field instanceof ImageField) paths = [field.url.href.replace(path.extname(field.url.href), "_s" + path.extname(field.url.href))]; - else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => - (p as ImageField).url.href.replace(path.extname((p as ImageField).url.href), "_s" + path.extname((p as ImageField).url.href))); - } else if (w < 600 && !this._mediumImageMissing) { - this._curImageSize = "medium"; - let field = this.Document[this.props.fieldKey]; - if (field instanceof ImageField) paths = [field.url.href.replace(path.extname(field.url.href), "_m" + path.extname(field.url.href))]; - else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => - (p as ImageField).url.href.replace(path.extname((p as ImageField).url.href), "_m" + path.extname((p as ImageField).url.href))); - } else if (!this._largeImageMissing) { - this._curImageSize = "large"; - let field = this.Document[this.props.fieldKey]; - if (field instanceof ImageField) paths = [field.url.href.replace(path.extname(field.url.href), "_l" + path.extname(field.url.href))]; - else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => - (p as ImageField).url.href.replace(path.extname((p as ImageField).url.href), "_l" + path.extname((p as ImageField).url.href))); - } + let field = this.Document[this.props.fieldKey]; + if (w < 100) this._curSuffix = "_s"; + else if (w < 600) this._curSuffix = "_m"; + else this._curSuffix = "_l"; + if (field instanceof ImageField) paths = [this.choosePath(field.url)]; + else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => this.choosePath((p as ImageField).url)); } let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive"; return (
- { - if (this._curImageSize === "small") this._smallImageMissing = true; - if (this._curImageSize === "medium") this._mediumImageMissing = true; - if (this._curImageSize === "large") this._largeImageMissing = true; + let timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount; + setTimeout(this.retryPath, Math.min(10000, timeout * 5)); })} onLoad={this.onLoad} /> {paths.length > 1 ? this.dots(paths) : (null)} -- cgit v1.2.3-70-g09d2 From 850ac067fb4e62f91a6eee8df40fe95eca3a3326 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 20 May 2019 13:15:16 -0400 Subject: changed so that cat image works. --- src/client/views/nodes/ImageBox.tsx | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 29a99e53c..1bd1b80a7 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -177,6 +177,11 @@ export class ImageBox extends DocComponent(ImageD if (this._curSuffix === "_m") this._mediumRetryCount++; if (this._curSuffix === "_l") this._largeRetryCount++; } + @action onError = () => { + let timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount; + if (timeout < 10) + setTimeout(this.retryPath, Math.min(10000, timeout * 5)); + } _curSuffix = ""; render() { let transform = this.props.ScreenToLocalTransform().inverse(); @@ -191,9 +196,9 @@ export class ImageBox extends DocComponent(ImageD this._curSuffix = ""; if (w > 20) { let field = this.Document[this.props.fieldKey]; - if (w < 100) this._curSuffix = "_s"; - else if (w < 600) this._curSuffix = "_m"; - else this._curSuffix = "_l"; + if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s"; + else if (w < 600 && this._mediumRetryCount < 10) this._curSuffix = "_m"; + else if (this._largeRetryCount < 10) this._curSuffix = "_l"; if (field instanceof ImageField) paths = [this.choosePath(field.url)]; else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => this.choosePath((p as ImageField).url)); } @@ -205,16 +210,13 @@ export class ImageBox extends DocComponent(ImageD { - let timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount; - setTimeout(this.retryPath, Math.min(10000, timeout * 5)); - })} + onError={this.onError} onLoad={this.onLoad} /> {paths.length > 1 ? this.dots(paths) : (null)} - {this.lightbox(paths)} + {/* {this.lightbox(paths)} */}
); } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 9c8cd369019f1da21c75c07abecdbe3cf5da4cfe Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 20 May 2019 13:25:06 -0400 Subject: only apply suffixes to urls that are on our server... --- src/client/views/nodes/ImageBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 1bd1b80a7..a2291c7cf 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -163,7 +163,7 @@ export class ImageBox extends DocComponent(ImageD } choosePath(url: URL) { - if (url.protocol === "data") + if (url.protocol === "data" || url.href.indexOf(window.location.origin) === -1) return url.href; let ext = path.extname(url.href); return url.href.replace(ext, this._curSuffix + ext); -- cgit v1.2.3-70-g09d2 From e32ccd138ff70f2225e5c2c13e0003e02593ef09 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 20 May 2019 14:14:19 -0400 Subject: minimal change to brushing presentation view. --- src/client/views/PresentationView.tsx | 14 ++++++++++++-- .../views/collections/collectionFreeForm/MarqueeView.tsx | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/PresentationView.tsx b/src/client/views/PresentationView.tsx index ce679aa0a..9d5798ff1 100644 --- a/src/client/views/PresentationView.tsx +++ b/src/client/views/PresentationView.tsx @@ -6,7 +6,7 @@ import { DocumentManager } from "../util/DocumentManager"; import { Utils } from "../../Utils"; import { Doc, DocListCast, DocListCastAsync } from "../../new_fields/Doc"; import { listSpec } from "../../new_fields/Schema"; -import { Cast, NumCast, FieldValue, PromiseValue, StrCast } from "../../new_fields/Types"; +import { Cast, NumCast, FieldValue, PromiseValue, StrCast, BoolCast } from "../../new_fields/Types"; import { Id } from "../../new_fields/FieldSymbols"; import { List } from "../../new_fields/List"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; @@ -27,6 +27,7 @@ interface PresListProps extends PresViewProps { */ class PresentationViewList extends React.Component { + /** * Renders a single child document. It will just append a list element. * @param document The document to render. @@ -42,8 +43,17 @@ class PresentationViewList extends React.Component { //this doc is selected className += " presentationView-selected"; } + let onEnter = (e: React.PointerEvent) => { document.libraryBrush = true; } + let onLeave = (e: React.PointerEvent) => { document.libraryBrush = false; } return ( -
{ this.props.gotoDocument(index); e.stopPropagation(); }}> +
{ this.props.gotoDocument(index); e.stopPropagation(); }}> {`${index + 1}. ${title}`} diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 6ea230aab..97e610e13 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -135,7 +135,7 @@ export class MarqueeView extends React.Component document.addEventListener("pointerup", this.onPointerUp, true); document.addEventListener("keydown", this.marqueeCommand, true); if (e.altKey) { - e.stopPropagation(); + //e.stopPropagation(); // bcz: removed so that you can alt-click on button in a collection to switch link following behaviors. e.preventDefault(); } // bcz: do we need this? it kills the context menu on the main collection if !altKey -- cgit v1.2.3-70-g09d2 From 99bbb42cfa5718f543213f7a64463bd31c73d781 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 20 May 2019 14:36:43 -0400 Subject: Added route to save data uri as image --- package.json | 1 + src/server/RouteStore.ts | 1 + src/server/index.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) (limited to 'src') diff --git a/package.json b/package.json index 0d0e6ff3e..40402b44c 100644 --- a/package.json +++ b/package.json @@ -118,6 +118,7 @@ "golden-layout": "^1.5.9", "html-to-image": "^0.1.0", "i": "^0.3.6", + "image-data-uri": "^2.0.0", "jsonwebtoken": "^8.5.0", "jsx-to-string": "^1.4.0", "lodash": "^4.17.11", diff --git a/src/server/RouteStore.ts b/src/server/RouteStore.ts index fdf5b6a5c..c4af5cdaa 100644 --- a/src/server/RouteStore.ts +++ b/src/server/RouteStore.ts @@ -11,6 +11,7 @@ export enum RouteStore { // UPLOAD AND STATIC FILE SERVING public = "/public", upload = "/upload", + dataUriToImage = "/uploadURI", images = "/images", // USER AND WORKSPACES diff --git a/src/server/index.ts b/src/server/index.ts index af99b116a..a12751fe3 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -7,6 +7,7 @@ import * as expressValidator from 'express-validator'; import * as formidable from 'formidable'; import * as fs from 'fs'; import * as sharp from 'sharp'; +const imageDataUri = require('image-data-uri'); import * as mobileDetect from 'mobile-detect'; import { ObservableMap } from 'mobx'; import * as passport from 'passport'; @@ -216,6 +217,45 @@ addSecureRoute( RouteStore.upload ); +addSecureRoute( + Method.POST, + (user, res, req) => { + const uri = req.query.uri; + const filename = req.query.name; + if (!uri || !filename) { + res.status(400).send("incorrect parameters specified"); + return; + } + imageDataUri.outputFile(uri, uploadDir + filename).then((savedName: string) => { + const ext = path.extname(savedName); + let resizers = [ + { resizer: sharp().resize(100, undefined, { withoutEnlargement: true }), suffix: "_s" }, + { resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: "_m" }, + { resizer: sharp().resize(900, undefined, { withoutEnlargement: true }), suffix: "_l" }, + ]; + let isImage = false; + if (pngTypes.includes(ext)) { + resizers.forEach(element => { + element.resizer = element.resizer.png(); + }); + isImage = true; + } else if (jpgTypes.includes(ext)) { + resizers.forEach(element => { + element.resizer = element.resizer.jpeg(); + }); + isImage = true; + } + if (isImage) { + resizers.forEach(resizer => { + fs.createReadStream(savedName).pipe(resizer.resizer).pipe(fs.createWriteStream(uploadDir + filename + resizer.suffix + ext)); + }); + } + res.send("/files/" + filename + ext); + }); + }, + undefined, + RouteStore.dataUriToImage +); // AUTHENTICATION // Sign Up -- cgit v1.2.3-70-g09d2 From e02e8bee7ee36490b83edb0a416978216b0f4a17 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 20 May 2019 15:46:21 -0400 Subject: hooking up image data url stuff. --- src/client/views/SearchBox.tsx | 17 +++++++++++++++++ src/client/views/nodes/PDFBox.tsx | 10 +++++++--- src/server/index.ts | 8 +++++--- 3 files changed, 29 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/views/SearchBox.tsx b/src/client/views/SearchBox.tsx index 8efd8d266..0ec1a6758 100644 --- a/src/client/views/SearchBox.tsx +++ b/src/client/views/SearchBox.tsx @@ -20,6 +20,7 @@ import { Id } from '../../new_fields/FieldSymbols'; import { DocumentManager } from '../util/DocumentManager'; import { SetupDrag } from '../util/DragManager'; import { Docs } from '../documents/Documents'; +import { RouteStore } from '../../server/RouteStore'; library.add(faSearch); library.add(faObjectGroup); @@ -70,6 +71,22 @@ export class SearchBox extends React.Component { } return docs; } + public static async convertDataUri(imageUri: string, returnedFilename: string) { + try { + let posting = DocServer.prepend(RouteStore.dataUriToImage); + const returnedUri = await rp.post(posting, { + body: { + uri: imageUri, + name: returnedFilename + }, + json: true, + }); + return returnedUri; + + } catch (e) { + console.log(e); + } + } @action handleClickFilter = (e: Event): void => { diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index e71ac4924..1017f5235 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -21,6 +21,8 @@ import { positionSchema } from "./DocumentView"; import { pageSchema } from "./ImageBox"; import { ImageField, PdfField } from "../../../new_fields/URLField"; import { InkingControl } from "../InkingControl"; +import { SearchBox } from "../SearchBox"; +import { Id } from "../../../new_fields/FieldSymbols"; /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx * This method renders PDF and puts all kinds of functionalities such as annotation, highlighting, @@ -250,9 +252,11 @@ export class PDFBox extends DocComponent(PdfDocumen let nheight = FieldValue(this.Document.nativeHeight, 0); htmlToImage.toPng(this._mainDiv.current!, { width: nwidth, height: nheight, quality: 1 }) .then(action((dataUrl: string) => { - this.props.Document.thumbnail = new ImageField(new URL(dataUrl)); - this.props.Document.thumbnailPage = FieldValue(this.Document.curPage, -1); - this._renderAsSvg = true; + SearchBox.convertDataUri(dataUrl, this.Document[Id] + "_" + this.curPage).then((returnedFilename) => { + this.props.Document.thumbnail = new ImageField(new URL(dataUrl)); + this.props.Document.thumbnailPage = FieldValue(this.Document.curPage, -1); + this._renderAsSvg = true; + }) })) .catch(function (error: any) { console.error('oops, something went wrong!', error); diff --git a/src/server/index.ts b/src/server/index.ts index a12751fe3..6843afbe4 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -114,6 +114,8 @@ function addSecureRoute(method: Method, app.use(express.static(__dirname + RouteStore.public)); app.use(RouteStore.images, express.static(__dirname + RouteStore.public)); +app.use(express.json()); + app.get("/pull", (req, res) => exec('"C:\\Program Files\\Git\\git-bash.exe" -c "git pull"', (err, stdout, stderr) => { if (err) { @@ -220,10 +222,10 @@ addSecureRoute( addSecureRoute( Method.POST, (user, res, req) => { - const uri = req.query.uri; - const filename = req.query.name; + const uri = req.body.uri; + const filename = req.body.name; if (!uri || !filename) { - res.status(400).send("incorrect parameters specified"); + res.status(401).send("incorrect parameters specified"); return; } imageDataUri.outputFile(uri, uploadDir + filename).then((savedName: string) => { -- cgit v1.2.3-70-g09d2 From ba57c927bbb7b7ba80e65e8ada9520533f1270b6 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 20 May 2019 15:49:57 -0400 Subject: uncommented search thing --- src/server/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/server/index.ts b/src/server/index.ts index a12751fe3..0952e20aa 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -429,8 +429,8 @@ function UpdateField(socket: Socket, diff: Diff) { Object.values(suffixMap).forEach(suf => update[key + getSuffix(suf)] = { set: null }); let term = ToSearchTerm(val); if (term !== undefined) { - // let { suffix, value } = term; - // update[key + suffix] = { set: value }; + let { suffix, value } = term; + update[key + suffix] = { set: value }; } } if (dynfield) { -- cgit v1.2.3-70-g09d2 From fe3a7e6fbdcb10d764813d551ae052fa837f76ff Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 20 May 2019 15:56:20 -0400 Subject: Fixed pdf uploader --- src/server/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/server/index.ts b/src/server/index.ts index 5cdd62726..574241f62 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -60,7 +60,7 @@ app.use(session({ app.use(flash()); app.use(expressFlash()); -app.use(bodyParser.json()); +app.use(bodyParser.json({ limit: "10mb" })); app.use(bodyParser.urlencoded({ extended: true })); app.use(expressValidator()); app.use(passport.initialize()); @@ -114,8 +114,6 @@ function addSecureRoute(method: Method, app.use(express.static(__dirname + RouteStore.public)); app.use(RouteStore.images, express.static(__dirname + RouteStore.public)); -app.use(express.json()); - app.get("/pull", (req, res) => exec('"C:\\Program Files\\Git\\git-bash.exe" -c "git pull"', (err, stdout, stderr) => { if (err) { -- cgit v1.2.3-70-g09d2 From 7cde8383e45ccf8293ec9cba2beddc64bceda2aa Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 20 May 2019 17:08:40 -0400 Subject: Added update search script Added debug current file launch config Made database not ignore requests before it is initialized. --- .vscode/launch.json | 24 +++++---- package.json | 1 + src/server/database.ts | 124 +++++++++++++++++++++++++++++---------------- src/server/updateSearch.ts | 101 ++++++++++++++++++++++++++++++++++++ 4 files changed, 196 insertions(+), 54 deletions(-) create mode 100644 src/server/updateSearch.ts (limited to 'src') diff --git a/.vscode/launch.json b/.vscode/launch.json index 452acc823..d245be2ad 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -31,6 +31,20 @@ "localRoot": "${workspaceFolder}", "remoteRoot": "." }, + { + "type": "node", + "request": "launch", + "name": "Current TS File", + "runtimeExecutable": "npx", + "runtimeArgs": [ + "ts-node-dev", + "--nolazy", + "--inspect", + "--", + "${relativeFile}" + ], + "port": 9229 + }, { "type": "node", "request": "launch", @@ -65,15 +79,5 @@ "internalConsoleOptions": "openOnSessionStart", "protocol": "inspector" }, - { - "type": "node", - "request": "launch", - "name": "Launch via NPM", - "runtimeExecutable": "npm", - "runtimeArgs": [ - "start" - ], - "port": 9229 - } ] } \ No newline at end of file diff --git a/package.json b/package.json index 40402b44c..790535728 100644 --- a/package.json +++ b/package.json @@ -133,6 +133,7 @@ "nodemon": "^1.18.10", "normalize.css": "^8.0.1", "npm": "^6.9.0", + "p-limit": "^2.2.0", "passport": "^0.4.0", "passport-local": "^1.0.0", "prosemirror-commands": "^1.0.7", diff --git a/src/server/database.ts b/src/server/database.ts index 69005d2d3..70b3efced 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -8,9 +8,13 @@ export class Database { private url = 'mongodb://localhost:27017/Dash'; private currentWrites: { [id: string]: Promise } = {}; private db?: mongodb.Db; + private onConnect: (() => void)[] = []; constructor() { - this.MongoClient.connect(this.url, (err, client) => this.db = client.db()); + this.MongoClient.connect(this.url, (err, client) => { + this.db = client.db(); + this.onConnect.forEach(fn => fn()); + }); } public update(id: string, value: any, callback: () => void, upsert = true, collectionName = Database.DocumentsCollection) { @@ -32,66 +36,98 @@ export class Database { }; newProm = prom ? prom.then(run) : run(); this.currentWrites[id] = newProm; + } else { + this.onConnect.push(() => this.update(id, value, callback, upsert, collectionName)); } } public delete(id: string, collectionName = Database.DocumentsCollection) { - this.db && this.db.collection(collectionName).remove({ id: id }); + if (this.db) { + this.db.collection(collectionName).remove({ id: id }); + } else { + this.onConnect.push(() => this.delete(id, collectionName)); + } } public deleteAll(collectionName = Database.DocumentsCollection): Promise { - return new Promise(res => - this.db && this.db.collection(collectionName).deleteMany({}, res)); + return new Promise(res => { + if (this.db) { + this.db.collection(collectionName).deleteMany({}, res); + } else { + this.onConnect.push(() => this.db && this.db.collection(collectionName).deleteMany({}, res)); + } + }); } public insert(value: any, collectionName = Database.DocumentsCollection) { - if (!this.db) { return; } - if ("id" in value) { - value._id = value.id; - delete value.id; - } - const id = value._id; - const collection = this.db.collection(collectionName); - const prom = this.currentWrites[id]; - let newProm: Promise; - const run = (): Promise => { - return new Promise(resolve => { - collection.insertOne(value, (err, res) => { - if (this.currentWrites[id] === newProm) { - delete this.currentWrites[id]; - } - resolve(); + if (this.db) { + if ("id" in value) { + value._id = value.id; + delete value.id; + } + const id = value._id; + const collection = this.db.collection(collectionName); + const prom = this.currentWrites[id]; + let newProm: Promise; + const run = (): Promise => { + return new Promise(resolve => { + collection.insertOne(value, (err, res) => { + if (this.currentWrites[id] === newProm) { + delete this.currentWrites[id]; + } + resolve(); + }); }); - }); - }; - newProm = prom ? prom.then(run) : run(); - this.currentWrites[id] = newProm; + }; + newProm = prom ? prom.then(run) : run(); + this.currentWrites[id] = newProm; + } else { + this.onConnect.push(() => this.insert(value, collectionName)); + } } public getDocument(id: string, fn: (result?: Transferable) => void, collectionName = Database.DocumentsCollection) { - this.db && this.db.collection(collectionName).findOne({ _id: id }, (err, result) => { - if (result) { - result.id = result._id; - delete result._id; - fn(result); - } else { - fn(undefined); - } - }); + if (this.db) { + this.db.collection(collectionName).findOne({ _id: id }, (err, result) => { + if (result) { + result.id = result._id; + delete result._id; + fn(result); + } else { + fn(undefined); + } + }); + } else { + this.onConnect.push(() => this.getDocument(id, fn, collectionName)); + } } public getDocuments(ids: string[], fn: (result: Transferable[]) => void, collectionName = Database.DocumentsCollection) { - this.db && this.db.collection(collectionName).find({ _id: { "$in": ids } }).toArray((err, docs) => { - if (err) { - console.log(err.message); - console.log(err.errmsg); - } - fn(docs.map(doc => { - doc.id = doc._id; - delete doc._id; - return doc; - })); - }); + if (this.db) { + this.db.collection(collectionName).find({ _id: { "$in": ids } }).toArray((err, docs) => { + if (err) { + console.log(err.message); + console.log(err.errmsg); + } + fn(docs.map(doc => { + doc.id = doc._id; + delete doc._id; + return doc; + })); + }); + } else { + this.onConnect.push(() => this.getDocuments(ids, fn, collectionName)); + } + } + + public query(query: any): Promise { + if (this.db) { + return Promise.resolve(this.db.collection('newDocuments').find(query)); + } else { + return new Promise(res => { + this.onConnect.push(() => res(this.query(query))); + }); + } } public print() { diff --git a/src/server/updateSearch.ts b/src/server/updateSearch.ts new file mode 100644 index 000000000..da33e3747 --- /dev/null +++ b/src/server/updateSearch.ts @@ -0,0 +1,101 @@ +import { Database } from "./database"; +import { Cursor } from "mongodb"; +import { Search } from "./Search"; +import pLimit from 'p-limit'; + +const suffixMap: { [type: string]: (string | [string, string | ((json: any) => any)]) } = { + "number": "_n", + "string": "_t", + // "boolean": "_b", + "image": ["_t", "url"], + "video": ["_t", "url"], + "pdf": ["_t", "url"], + "audio": ["_t", "url"], + "web": ["_t", "url"], + "date": ["_d", value => new Date(value.date).toISOString()], + "proxy": ["_i", "fieldId"], + "list": ["_l", list => { + const results = []; + for (const value of list.fields) { + const term = ToSearchTerm(value); + if (term) { + results.push(term.value); + } + } + return results.length ? results : null; + }] +}; + +function ToSearchTerm(val: any): { suffix: string, value: any } | undefined { + if (val === null || val === undefined) { + return; + } + const type = val.__type || typeof val; + let suffix = suffixMap[type]; + if (!suffix) { + return; + } + + if (Array.isArray(suffix)) { + const accessor = suffix[1]; + if (typeof accessor === "function") { + val = accessor(val); + } else { + val = val[accessor]; + } + suffix = suffix[0]; + } + + return { suffix, value: val }; +} + +function getSuffix(value: string | [string, any]): string { + return typeof value === "string" ? value : value[0]; +} + +const limit = pLimit(5); +async function update() { + // await new Promise(res => setTimeout(res, 5)); + console.log("update"); + await Search.Instance.clear(); + const cursor = await Database.Instance.query({}); + console.log("Cleared"); + const updates: any[] = []; + let numDocs = 0; + function updateDoc(doc: any) { + numDocs++; + if ((numDocs % 50) === 0) { + console.log("updateDoc " + numDocs); + } + console.log("doc " + numDocs); + if (doc.__type !== "Doc") { + return; + } + const fields = doc.fields; + if (!fields) { + return; + } + const update: any = { id: doc._id }; + let dynfield = false; + for (const key in fields) { + const value = fields[key]; + const term = ToSearchTerm(value); + if (term !== undefined) { + let { suffix, value } = term; + update[key + suffix] = value; + dynfield = true; + } + } + if (dynfield) { + updates.push(update); + console.log(updates.length); + } + } + await cursor.forEach(updateDoc); + await Promise.all(updates.map(update => { + return limit(() => Search.Instance.updateDocument(update)); + })); + cursor.close(); +} + +update(); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 059faf25a0f8ba80e1d2853dcefa1a70df4cbb6a Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 20 May 2019 17:10:21 -0400 Subject: fixed icons for pdfs. fixed activeItem issue for golden layout. --- src/client/goldenLayout.js | 2 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 1 + src/client/views/nodes/PDFBox.tsx | 58 +++++++++++----------- 3 files changed, 31 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js index ab2bcefed..54c9c6068 100644 --- a/src/client/goldenLayout.js +++ b/src/client/goldenLayout.js @@ -4466,7 +4466,7 @@ } if (this.contentItems.length > 0) { - initialItem = this.contentItems[this.config.activeItemIndex || 0]; + initialItem = this.contentItems[Math.min(this.contentItems.length - 1, this.config.activeItemIndex || 0)]; if (!initialItem) { throw new Error('Configured activeItemIndex out of bounds'); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 5bff08280..fa2b09ba7 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -189,6 +189,7 @@ export class CollectionFreeFormDocumentView extends DocComponent(PdfDocumen @computed private get thumbnailPage() { return NumCast(this.props.Document.thumbnailPage, -1); } componentDidMount() { - let wasSelected = false; + let wasSelected = this.props.isSelected(); this._reactionDisposer = reaction( - () => this.props.isSelected(), + () => [this.props.isSelected(), this.curPage], () => { - if (this.curPage > 0 && this.curPage !== this.thumbnailPage && wasSelected && !this.props.isSelected()) { + if (this.curPage > 0 && !this.props.isTopMost && this.curPage !== this.thumbnailPage && wasSelected && !this.props.isSelected()) { this.saveThumbnail(); } wasSelected = this._interactive = this.props.isSelected(); @@ -246,16 +245,19 @@ export class PDFBox extends DocComponent(PdfDocumen @action saveThumbnail = () => { + this.props.Document.thumbnailPage = FieldValue(this.Document.curPage, -1); this._renderAsSvg = false; setTimeout(() => { let nwidth = FieldValue(this.Document.nativeWidth, 0); let nheight = FieldValue(this.Document.nativeHeight, 0); - htmlToImage.toPng(this._mainDiv.current!, { width: nwidth, height: nheight, quality: 1 }) + htmlToImage.toPng(this._mainDiv.current!, { width: nwidth, height: nheight, quality: 0.8 }) .then(action((dataUrl: string) => { - SearchBox.convertDataUri(dataUrl, this.Document[Id] + "_" + this.curPage).then((returnedFilename) => { - this.props.Document.thumbnail = new ImageField(new URL(dataUrl)); - this.props.Document.thumbnailPage = FieldValue(this.Document.curPage, -1); - this._renderAsSvg = true; + SearchBox.convertDataUri(dataUrl, "icon" + this.Document[Id] + "_" + this.curPage).then((returnedFilename) => { + if (returnedFilename) { + let url = DocServer.prepend(returnedFilename); + this.props.Document.thumbnail = new ImageField(new URL(url)); + } + runInAction(() => this._renderAsSvg = true); }) })) .catch(function (error: any) { @@ -292,7 +294,6 @@ export class PDFBox extends DocComponent(PdfDocumen } @computed get pdfContent() { - trace(); let pdfUrl = Cast(this.props.Document[this.props.fieldKey], PdfField); if (!pdfUrl) { return

No pdf url to render

; @@ -309,7 +310,7 @@ export class PDFBox extends DocComponent(PdfDocumen ; let xf = (this.Document.nativeHeight || 0) / this.renderHeight; return
- + {body}
; @@ -317,26 +318,25 @@ export class PDFBox extends DocComponent(PdfDocumen @computed get pdfRenderer() { - let proxy = this._loaded ? (null) : this.imageProxyRenderer; let pdfUrl = Cast(this.props.Document[this.props.fieldKey], PdfField); - if ((!this._interactive && proxy) || !pdfUrl) { + let proxy = this.imageProxyRenderer; + if ((!this._interactive && proxy && (!this.props.ContainingCollectionView || !this.props.ContainingCollectionView.props.isTopMost)) || !pdfUrl) { return proxy; } return [ this._pageInfo.area.filter(() => this._pageInfo.area).map((element: any) => element), this._currAnno.map((element: any) => element), - this.pdfContent, - proxy + this.pdfContent ]; } @computed get imageProxyRenderer() { let thumbField = this.props.Document.thumbnail; - if (thumbField) { - let path = this.thumbnailPage !== this.curPage ? "https://image.flaticon.com/icons/svg/66/66163.svg" : + if (thumbField && this._renderAsSvg) { + let path = // this.thumbnailPage !== this.curPage ? "https://image.flaticon.com/icons/svg/66/66163.svg" : thumbField instanceof ImageField ? thumbField.url.href : "http://cs.brown.edu/people/bcz/prairie.jpg"; - return ; + return ; } return (null); } -- cgit v1.2.3-70-g09d2 From e5c9ba12e9a50a688e8c1554b525c87b1efbafb8 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 20 May 2019 17:23:09 -0400 Subject: added s/m/l icons for pdf rendering. --- src/client/views/nodes/ImageBox.tsx | 1 - src/client/views/nodes/PDFBox.tsx | 43 +++++++++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index a2291c7cf..8156ec872 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -19,7 +19,6 @@ import { InkingControl } from '../InkingControl'; import { Doc } from '../../../new_fields/Doc'; import { faImage } from '@fortawesome/free-solid-svg-icons'; import { library } from '@fortawesome/fontawesome-svg-core'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; var path = require('path'); library.add(faImage); diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index d1b9098b0..2a59eb7e5 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -21,6 +21,7 @@ import { positionSchema } from "./DocumentView"; import { FieldView, FieldViewProps } from './FieldView'; import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; +var path = require('path'); import React = require("react"); /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx @@ -330,20 +331,54 @@ export class PDFBox extends DocComponent(PdfDocumen ]; } + choosePath(url: URL) { + if (url.protocol === "data" || url.href.indexOf(window.location.origin) === -1) + return url.href; + let ext = path.extname(url.href); + return url.href.replace(ext, this._curSuffix + ext); + } + @observable _smallRetryCount = 1; + @observable _mediumRetryCount = 1; + @observable _largeRetryCount = 1; + @action retryPath = () => { + if (this._curSuffix === "_s") this._smallRetryCount++; + if (this._curSuffix === "_m") this._mediumRetryCount++; + if (this._curSuffix === "_l") this._largeRetryCount++; + } + @action onError = () => { + let timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount; + if (timeout < 10) + setTimeout(this.retryPath, Math.min(10000, timeout * 5)); + } + _curSuffix = ""; + @computed get imageProxyRenderer() { let thumbField = this.props.Document.thumbnail; if (thumbField && this._renderAsSvg) { - let path = // this.thumbnailPage !== this.curPage ? "https://image.flaticon.com/icons/svg/66/66163.svg" : - thumbField instanceof ImageField ? thumbField.url.href : "http://cs.brown.edu/people/bcz/prairie.jpg"; - return ; + + let transform = this.props.ScreenToLocalTransform().inverse(); + let pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50; + var [sptX, sptY] = transform.transformPoint(0, 0); + let [bptX, bptY] = transform.transformPoint(pw, this.props.PanelHeight()); + let w = bptX - sptX; + + let path = thumbField instanceof ImageField ? thumbField.url.href : "http://cs.brown.edu/people/bcz/prairie.jpg"; + this._curSuffix = ""; + if (w > 20) { + let field = thumbField; + if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s"; + else if (w < 400 && this._mediumRetryCount < 10) this._curSuffix = "_m"; + else if (this._largeRetryCount < 10) this._curSuffix = "_l"; + if (field instanceof ImageField) path = this.choosePath(field.url); + } + return ; } return (null); } @action onKeyDown = (e: React.KeyboardEvent) => e.key === "Alt" && (this._alt = true); @action onKeyUp = (e: React.KeyboardEvent) => e.key === "Alt" && (this._alt = false); render() { - trace(); let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (
-- cgit v1.2.3-70-g09d2 From 2e2dafc6d805e54e4119e28943a3800fe08c0ca9 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 20 May 2019 17:33:59 -0400 Subject: Disabled images in search --- src/server/index.ts | 2 +- src/server/updateSearch.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/server/index.ts b/src/server/index.ts index 574241f62..5a534afae 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -366,7 +366,7 @@ const suffixMap: { [type: string]: (string | [string, string | ((json: any) => a "number": "_n", "string": "_t", // "boolean": "_b", - "image": ["_t", "url"], + // "image": ["_t", "url"], "video": ["_t", "url"], "pdf": ["_t", "url"], "audio": ["_t", "url"], diff --git a/src/server/updateSearch.ts b/src/server/updateSearch.ts index da33e3747..de1fd25e1 100644 --- a/src/server/updateSearch.ts +++ b/src/server/updateSearch.ts @@ -7,7 +7,7 @@ const suffixMap: { [type: string]: (string | [string, string | ((json: any) => a "number": "_n", "string": "_t", // "boolean": "_b", - "image": ["_t", "url"], + // "image": ["_t", "url"], "video": ["_t", "url"], "pdf": ["_t", "url"], "audio": ["_t", "url"], -- cgit v1.2.3-70-g09d2 From 70930405f8bdf43c05a77cb3dcbdadcbd3c9ab70 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 20 May 2019 19:10:10 -0400 Subject: PDF and Image test Context work --- .../views/collections/CollectionDockingView.tsx | 3 ++- .../views/collections/ParentDocumentSelector.scss | 10 ++++++++ .../views/collections/ParentDocumentSelector.tsx | 24 ++++++++++++++----- src/client/views/nodes/ImageBox.tsx | 28 +++++++++++----------- src/client/views/nodes/PDFBox.tsx | 26 ++++++++++---------- src/new_fields/Doc.ts | 13 ++++++---- 6 files changed, 65 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 9721bf804..180a8be46 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -332,7 +332,8 @@ export class CollectionDockingView extends React.Component0
`); tab.element.append(counter); let upDiv = document.createElement("span"); - ReactDOM.render(, upDiv); + const stack = tab.contentItem.parent; + ReactDOM.render( CollectionDockingView.Instance.AddTab(stack, doc)} />, upDiv); tab.reactComponents = [upDiv]; tab.element.append(upDiv); counter.DashDocId = tab.contentItem.config.props.documentId; diff --git a/src/client/views/collections/ParentDocumentSelector.scss b/src/client/views/collections/ParentDocumentSelector.scss index f3c605f3e..1ab12bb72 100644 --- a/src/client/views/collections/ParentDocumentSelector.scss +++ b/src/client/views/collections/ParentDocumentSelector.scss @@ -5,4 +5,14 @@ box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); min-width: 150px; color: black; + + hr { + height: 1px; + margin: 0px; + background-color: gray; + border-top: 0px; + border-bottom: 0px; + border-right: 0px; + border-left: 0px; + } } \ No newline at end of file diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index 4d07c31a7..65ae7f9ec 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -7,32 +7,44 @@ import { Id } from "../../../new_fields/FieldSymbols"; import { SearchUtil } from "../../util/SearchUtil"; import { CollectionDockingView } from "./CollectionDockingView"; +type SelectorProps = { Document: Doc, addDocTab(doc: Doc, location: string): void }; @observer -export class SelectorContextMenu extends React.Component<{ Document: Doc }> { +export class SelectorContextMenu extends React.Component { @observable private _docs: Doc[] = []; + @observable private _otherDocs: Doc[] = []; - constructor(props: { Document: Doc }) { + constructor(props: SelectorProps) { super(props); this.fetchDocuments(); } async fetchDocuments() { + let aliases = (await SearchUtil.GetAliasesOfDocument(this.props.Document)).filter(doc => doc !== this.props.Document); const docs = await SearchUtil.Search(`data_l:"${this.props.Document[Id]}"`, true); - runInAction(() => this._docs = docs); + const otherDocs: Set = new Set; + const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search(`data_l:"${doc[Id]}"`, true))); + allDocs.forEach(docs => docs.forEach(doc => otherDocs.add(doc))); + docs.forEach(doc => otherDocs.delete(doc)); + runInAction(() => { + this._docs = docs.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)); + this._otherDocs = Array.from(otherDocs).filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)); + }); } render() { return ( <> - {this._docs.map(doc =>

CollectionDockingView.Instance.AddRightSplit(doc)}>{doc.title}

)} + {this._docs.map(doc =>

this.props.addDocTab(Doc.IsPrototype(doc) ? Doc.MakeDelegate(doc) : doc, "inTab")}>{doc.title}

)} + {this._otherDocs.length ?
: null} + {this._otherDocs.map(doc =>

this.props.addDocTab(Doc.IsPrototype(doc) ? Doc.MakeDelegate(doc) : doc, "inTab")}>{doc.title}

)} ); } } @observer -export class ParentDocSelector extends React.Component<{ Document: Doc }> { +export class ParentDocSelector extends React.Component { @observable hover = false; @action @@ -50,7 +62,7 @@ export class ParentDocSelector extends React.Component<{ Document: Doc }> { if (this.hover) { flyout = (
- +
); } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 8156ec872..e022793eb 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -181,26 +181,26 @@ export class ImageBox extends DocComponent(ImageD if (timeout < 10) setTimeout(this.retryPath, Math.min(10000, timeout * 5)); } - _curSuffix = ""; + _curSuffix = "_m"; render() { - let transform = this.props.ScreenToLocalTransform().inverse(); + // let transform = this.props.ScreenToLocalTransform().inverse(); let pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50; - var [sptX, sptY] = transform.transformPoint(0, 0); - let [bptX, bptY] = transform.transformPoint(pw, this.props.PanelHeight()); - let w = bptX - sptX; + // var [sptX, sptY] = transform.transformPoint(0, 0); + // let [bptX, bptY] = transform.transformPoint(pw, this.props.PanelHeight()); + // let w = bptX - sptX; let id = (this.props as any).id; // bcz: used to set id = "isExpander" in templates.tsx let nativeWidth = FieldValue(this.Document.nativeWidth, pw); let paths: string[] = ["http://www.cs.brown.edu/~bcz/noImage.png"]; - this._curSuffix = ""; - if (w > 20) { - let field = this.Document[this.props.fieldKey]; - if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s"; - else if (w < 600 && this._mediumRetryCount < 10) this._curSuffix = "_m"; - else if (this._largeRetryCount < 10) this._curSuffix = "_l"; - if (field instanceof ImageField) paths = [this.choosePath(field.url)]; - else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => this.choosePath((p as ImageField).url)); - } + // this._curSuffix = ""; + // if (w > 20) { + let field = this.Document[this.props.fieldKey]; + // if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s"; + // else if (w < 600 && this._mediumRetryCount < 10) this._curSuffix = "_m"; + // else if (this._largeRetryCount < 10) this._curSuffix = "_l"; + if (field instanceof ImageField) paths = [this.choosePath(field.url)]; + else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => this.choosePath((p as ImageField).url)); + // } let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive"; return (
(PdfDocumen if (timeout < 10) setTimeout(this.retryPath, Math.min(10000, timeout * 5)); } - _curSuffix = ""; + _curSuffix = "_m"; @computed get imageProxyRenderer() { let thumbField = this.props.Document.thumbnail; if (thumbField && this._renderAsSvg) { - let transform = this.props.ScreenToLocalTransform().inverse(); + // let transform = this.props.ScreenToLocalTransform().inverse(); let pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50; - var [sptX, sptY] = transform.transformPoint(0, 0); - let [bptX, bptY] = transform.transformPoint(pw, this.props.PanelHeight()); - let w = bptX - sptX; + // var [sptX, sptY] = transform.transformPoint(0, 0); + // let [bptX, bptY] = transform.transformPoint(pw, this.props.PanelHeight()); + // let w = bptX - sptX; let path = thumbField instanceof ImageField ? thumbField.url.href : "http://cs.brown.edu/people/bcz/prairie.jpg"; - this._curSuffix = ""; - if (w > 20) { - let field = thumbField; - if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s"; - else if (w < 400 && this._mediumRetryCount < 10) this._curSuffix = "_m"; - else if (this._largeRetryCount < 10) this._curSuffix = "_l"; - if (field instanceof ImageField) path = this.choosePath(field.url); - } + // this._curSuffix = ""; + // if (w > 20) { + let field = thumbField; + // if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s"; + // else if (w < 400 && this._mediumRetryCount < 10) this._curSuffix = "_m"; + // else if (this._largeRetryCount < 10) this._curSuffix = "_l"; + if (field instanceof ImageField) path = this.choosePath(field.url); + // } return ; } return (null); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index f4514c33e..92d3c140a 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -149,6 +149,9 @@ export namespace Doc { export function GetT(doc: Doc, key: string, ctor: ToConstructor, ignoreProto: boolean = false): FieldResult { return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult; } + export function IsPrototype(doc: Doc) { + return GetT(doc, "isPrototype", "boolean", true); + } export async function SetOnPrototype(doc: Doc, key: string, value: Field) { const proto = Object.getOwnPropertyNames(doc).indexOf("isPrototype") === -1 ? doc.proto : doc; @@ -180,11 +183,11 @@ export namespace Doc { // compare whether documents or their protos match export function AreProtosEqual(doc: Doc, other: Doc) { - let r = (doc[Id] === other[Id]); - let r2 = (doc.proto && doc.proto.Id === other[Id]); - let r3 = (other.proto && other.proto.Id === doc[Id]); - let r4 = (doc.proto && other.proto && doc.proto[Id] === other.proto[Id]); - return r || r2 || r3 || r4 ? true : false; + let r = (doc === other); + let r2 = (doc.proto === other); + let r3 = (other.proto === doc); + let r4 = (doc.proto === other.proto); + return r || r2 || r3 || r4; } // gets the document's prototype or returns the document if it is a prototype -- cgit v1.2.3-70-g09d2 From 060455feec89c94073636f612ccca54a0aaabb90 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 20 May 2019 21:29:17 -0400 Subject: fixed a few things for Firefox - dragging images, typing spaces, scrool speed. and focusing proeprly with zoomBasis. --- .vscode/launch.json | 9 +++++++++ .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 9 +++------ src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 3 +++ src/client/views/nodes/FormattedTextBox.tsx | 4 ++-- 4 files changed, 17 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/.vscode/launch.json b/.vscode/launch.json index d245be2ad..3b6549767 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -13,6 +13,15 @@ "url": "http://localhost:1050/login", "webRoot": "${workspaceFolder}", }, + { + "type": "firefox", + "request": "launch", + "name": "Launch Firefox against localhost", + "sourceMaps": "client", + "reAttach": true, + "url": "http://localhost:1050/login", + "webRoot": "${workspaceFolder}", + }, { "type": "chrome", "request": "launch", diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ee6f4821f..3ec7184d2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -183,7 +183,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return; } e.stopPropagation(); - const coefficient = 1000; + const coefficient = 100; if (e.ctrlKey) { let deltaScale = (1 - (e.deltaY / coefficient)); @@ -215,7 +215,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action setPan(panX: number, panY: number) { - this.panDisposer && clearTimeout(this.panDisposer); this.props.Document.panTransformType = "None"; var scale = this.getLocalTransform().inverse().Scale; const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX)); @@ -243,7 +242,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { doc.zIndex = docs.length + 1; } - panDisposer?: NodeJS.Timeout; focusDocument = (doc: Doc) => { const panX = this.Document.panX; const panY = this.Document.panY; @@ -265,15 +263,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } SelectionManager.DeselectAll(); - const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2; - const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2; + const newPanX = NumCast(doc.x) + NumCast(doc.width) / NumCast(doc.zoomBasis, 1) / 2; + const newPanY = NumCast(doc.y) + NumCast(doc.height) / NumCast(doc.zoomBasis, 1) / 2; const newState = HistoryUtil.getState(); newState.initializers[id] = { panX: newPanX, panY: newPanY }; HistoryUtil.pushState(newState); this.setPan(newPanX, newPanY); this.props.Document.panTransformType = "Ease"; this.props.focus(this.props.Document); - this.panDisposer = setTimeout(() => this.props.Document.panTransformType = "None", 2000); // wait 3 seconds, then reset to false } diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index fa2b09ba7..7c3180496 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -177,6 +177,9 @@ export class CollectionFreeFormDocumentView extends DocComponent { document.removeEventListener("pointerup", this.onPointerUp); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index d15813f9a..5afef221c 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -366,7 +366,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe style={{ pointerEvents: interactive ? "all" : "none", }} - onKeyDown={this.onKeyPress} + // onKeyDown={this.onKeyPress} onKeyPress={this.onKeyPress} onFocus={this.onFocused} onClick={this.onClick} @@ -378,7 +378,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe // tfs: do we need this event handler onWheel={this.onPointerWheel} > -
+
); } -- cgit v1.2.3-70-g09d2 From 505676028698f22e46854fdd8ea25ea3118b5d6f Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 20 May 2019 22:31:37 -0400 Subject: fixed annotation pages for videos --- src/Utils.ts | 13 +++++++++++++ src/client/views/InkingCanvas.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 6 ++++-- 4 files changed, 19 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/Utils.ts b/src/Utils.ts index 24878a368..611c61135 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -39,6 +39,19 @@ export class Utils { document.body.removeChild(textArea); } + public static GetClipboardText(): string { + var textArea = document.createElement("textarea"); + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + + try { document.execCommand('paste'); } catch (err) { } + + const val = textArea.value; + document.body.removeChild(textArea); + return val; + } + public static loggingEnabled: Boolean = false; public static logFilter: number | undefined = undefined; private static log(prefix: string, messageName: string, message: any, receiving: boolean) { diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx index afe3e3ecb..42ab08001 100644 --- a/src/client/views/InkingCanvas.tsx +++ b/src/client/views/InkingCanvas.tsx @@ -146,7 +146,7 @@ export class InkingCanvas extends React.Component { get drawnPaths() { let curPage = NumCast(this.props.Document.curPage, -1); let paths = Array.from(this.inkData).reduce((paths, [id, strokeData]) => { - if (strokeData.page === -1 || strokeData.page === curPage) { + if (strokeData.page === -1 || Math.round(strokeData.page) === Math.round(curPage)) { paths.push( { if (!(doc instanceof Doc)) return prev; var page = NumCast(doc.page, -1); - if (page === curPage || page === -1) { + if (Math.round(page) === Math.round(curPage) || page === -1) { let minim = BoolCast(doc.isMinimized, false); if (minim === undefined || !minim) { prev.push(); diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 81c429a02..6ae55d151 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -52,7 +52,7 @@ export class VideoBox extends DocComponent(VideoD @action public Play() { this.Playing = true; if (this.player) this.player.play(); - if (!this._playTimer) this._playTimer = setInterval(this.updateTimecode, 1000); + if (!this._playTimer) this._playTimer = setInterval(this.updateTimecode, 500); } @action public Pause() { @@ -70,7 +70,9 @@ export class VideoBox extends DocComponent(VideoD } @action - updateTimecode = () => this.player && (this.props.Document.curPage = this.player.currentTime) + updateTimecode = () => { + this.player && (this.props.Document.curPage = this.player.currentTime); + } componentDidMount() { if (this.props.setVideoBox) this.props.setVideoBox(this); -- cgit v1.2.3-70-g09d2 From c91a87d8e324089166373ec828f101836555585a Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 21 May 2019 05:50:22 -0400 Subject: Going up a level now centers on the document --- .../views/collections/ParentDocumentSelector.tsx | 33 ++++++++++++++++------ 1 file changed, 24 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index 65ae7f9ec..e6eb51a0c 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -6,12 +6,14 @@ import { observable, action, runInAction } from "mobx"; import { Id } from "../../../new_fields/FieldSymbols"; import { SearchUtil } from "../../util/SearchUtil"; import { CollectionDockingView } from "./CollectionDockingView"; +import { NumCast } from "../../../new_fields/Types"; +import { CollectionViewType } from "./CollectionBaseView"; type SelectorProps = { Document: Doc, addDocTab(doc: Doc, location: string): void }; @observer export class SelectorContextMenu extends React.Component { - @observable private _docs: Doc[] = []; - @observable private _otherDocs: Doc[] = []; + @observable private _docs: { col: Doc, target: Doc }[] = []; + @observable private _otherDocs: { col: Doc, target: Doc }[] = []; constructor(props: SelectorProps) { super(props); @@ -22,22 +24,35 @@ export class SelectorContextMenu extends React.Component { async fetchDocuments() { let aliases = (await SearchUtil.GetAliasesOfDocument(this.props.Document)).filter(doc => doc !== this.props.Document); const docs = await SearchUtil.Search(`data_l:"${this.props.Document[Id]}"`, true); - const otherDocs: Set = new Set; + const map: Map = new Map; const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search(`data_l:"${doc[Id]}"`, true))); - allDocs.forEach(docs => docs.forEach(doc => otherDocs.add(doc))); - docs.forEach(doc => otherDocs.delete(doc)); + allDocs.forEach((docs, index) => docs.forEach(doc => map.set(doc, aliases[index]))); + docs.forEach(doc => map.delete(doc)); runInAction(() => { - this._docs = docs.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)); - this._otherDocs = Array.from(otherDocs).filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)); + this._docs = docs.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)).map(doc => ({ col: doc, target: this.props.Document })); + this._otherDocs = Array.from(map.entries()).filter(entry => !Doc.AreProtosEqual(entry[0], CollectionDockingView.Instance.props.Document)).map(([col, target]) => ({ col, target })); }); } + getOnClick({ col, target }: { col: Doc, target: Doc }) { + return () => { + col = Doc.IsPrototype(col) ? Doc.MakeDelegate(col) : col; + if (NumCast(col.viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) { + const newPanX = NumCast(target.x) + NumCast(target.width) / NumCast(target.zoomBasis, 1) / 2; + const newPanY = NumCast(target.y) + NumCast(target.height) / NumCast(target.zoomBasis, 1) / 2; + col.panX = newPanX; + col.panY = newPanY; + } + this.props.addDocTab(col, "inTab"); + }; + } + render() { return ( <> - {this._docs.map(doc =>

this.props.addDocTab(Doc.IsPrototype(doc) ? Doc.MakeDelegate(doc) : doc, "inTab")}>{doc.title}

)} + {this._docs.map(doc =>

{doc.col.title}

)} {this._otherDocs.length ?
: null} - {this._otherDocs.map(doc =>

this.props.addDocTab(Doc.IsPrototype(doc) ? Doc.MakeDelegate(doc) : doc, "inTab")}>{doc.title}

)} + {this._otherDocs.map(doc =>

{doc.col.title}

)} ); } -- cgit v1.2.3-70-g09d2 From 3abf1b817a5ed0e2bcacaa08f4a15a9193794a3a Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 21 May 2019 08:22:24 -0400 Subject: Fixed performance problems --- src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 6 +++--- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 97e610e13..31864f357 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -1,5 +1,5 @@ import * as htmlToImage from "html-to-image"; -import { action, computed, observable } from "mobx"; +import { action, computed, observable, trace } from "mobx"; import { observer } from "mobx-react"; import { Docs } from "../../../documents/Documents"; import { SelectionManager } from "../../../util/SelectionManager"; @@ -344,10 +344,10 @@ export class MarqueeView extends React.Component } render() { - let p = this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY); + let p: [number, number] = this._visible ? this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY) : [0, 0]; return
- {!this._visible ? null : this.marqueeDiv} + {this._visible ? this.marqueeDiv : null}
{this.props.children}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 7c3180496..bf0c272e3 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -1,4 +1,4 @@ -import { action, computed, IReactionDisposer, reaction } from "mobx"; +import { action, computed, IReactionDisposer, reaction, trace } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc"; import { List } from "../../../new_fields/List"; @@ -211,7 +211,7 @@ export class CollectionFreeFormDocumentView extends DocComponent Doc.GetProto(doc)) + let expandedProtoDocs = expandedDocs.map(doc => Doc.GetProto(doc)); let maxLocation = StrCast(this.props.Document.maximizeLocation, "inPlace"); let getDispDoc = (target: Doc) => Object.getOwnPropertyNames(target).indexOf("isPrototype") === -1 ? target : Doc.MakeDelegate(target); if (altKey) { @@ -286,7 +286,9 @@ export class CollectionFreeFormDocumentView extends DocComponent Date: Tue, 21 May 2019 09:34:10 -0400 Subject: Fixed speed again? --- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 10 +++++----- src/client/views/nodes/DocumentContentsView.tsx | 5 +++-- src/client/views/nodes/ImageBox.tsx | 2 +- src/new_fields/Doc.ts | 1 - 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index bf0c272e3..aaaa6a9c5 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -266,15 +266,15 @@ export class CollectionFreeFormDocumentView extends DocComponent 800 ? Math.max(0, Math.min(1, 2 - 5 * (zoom < this.scale ? this.scale / zoom : zoom / this.scale))) : 1; const screenWidth = Math.min(50 * NumCast(this.props.Document.nativeWidth, 0), 1800); let fadeUp = .75 * screenWidth; let fadeDown = (maximizedDoc ? .0075 : .075) * screenWidth; - zoomFade = w < fadeDown /* || w > fadeUp */ ? Math.max(0.1, Math.min(1, 2 - (w < fadeDown ? Math.sqrt(Math.sqrt(fadeDown / w)) : w / fadeUp))) : 1; + // zoomFade = w < fadeDown /* || w > fadeUp */ ? Math.max(0.1, Math.min(1, 2 - (w < fadeDown ? Math.sqrt(Math.sqrt(fadeDown / w)) : w / fadeUp))) : 1; return (
Date: Tue, 21 May 2019 11:44:51 -0400 Subject: Fixed various scripting things --- src/client/util/type_decls.d | 33 ++++++++++++++++------ .../views/collections/CollectionSchemaView.tsx | 10 +++---- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/KeyValuePair.tsx | 8 ++---- src/debug/Viewer.tsx | 2 +- src/new_fields/Doc.ts | 7 +++-- src/new_fields/URLField.ts | 9 ++++-- 7 files changed, 46 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/client/util/type_decls.d b/src/client/util/type_decls.d index 51114d0e2..557f6f574 100644 --- a/src/client/util/type_decls.d +++ b/src/client/util/type_decls.d @@ -140,33 +140,50 @@ declare const ToScriptString: unique symbol; declare abstract class RefField { readonly [Id]: FieldId; - constructor(id?: FieldId); - protected [HandleUpdate]?(diff: any): void; + constructor(); + // protected [HandleUpdate]?(diff: any): void; - abstract [ToScriptString](): string; + // abstract [ToScriptString](): string; } declare abstract class ObjectField { protected [OnUpdate](diff?: any): void; private [Parent]?: RefField | ObjectField; - abstract [Copy](): ObjectField; + // abstract [Copy](): ObjectField; - abstract [ToScriptString](): string; + // abstract [ToScriptString](): string; } + +declare abstract class URLField extends ObjectField { + readonly url: URL; + + constructor(url: string); + constructor(url: URL); +} + +declare class AudioField extends URLField { } +declare class VideoField extends URLField { } +declare class ImageField extends URLField { } +declare class WebField extends URLField { } +declare class PdfField extends URLField { } + declare type FieldId = string; declare type Field = number | string | boolean | ObjectField | RefField; declare type Opt = T | undefined; declare class Doc extends RefField { + constructor(); + [key: string]: Field | undefined; - [ToScriptString](): string; + // [ToScriptString](): string; } declare class ListImpl extends ObjectField { + constructor(fields?: T[]); [index: number]: T | (T extends RefField ? Promise : never); - [ToScriptString](): string; - [Copy](): ObjectField; + // [ToScriptString](): string; + // [Copy](): ObjectField; } // @ts-ignore diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index b25b48339..488f7d6cb 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -115,22 +115,20 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { height={Number(MAX_ROW_HEIGHT)} GetValue={() => { let field = props.Document[props.fieldKey]; - if (field) { - //TODO Types - // return field.ToScriptString(); - return String(field); + if (Field.IsField(field)) { + return Field.toScriptString(field); } return ""; }} SetValue={(value: string) => { - let script = CompileScript(value, { addReturn: true, params: { this: Document.name } }); + let script = CompileScript(value, { addReturn: true, params: { this: Doc.name } }); if (!script.compiled) { return false; } return applyToDoc(props.Document, script.run); }} OnFillDown={async (value: string) => { - let script = CompileScript(value, { addReturn: true, params: { this: Document.name } }); + let script = CompileScript(value, { addReturn: true, params: { this: Doc.name } }); if (!script.compiled) { return; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ba6a4bbab..7a0a02318 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -183,7 +183,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return; } e.stopPropagation(); - const coefficient = 100; + const coefficient = 1000; if (e.ctrlKey) { let deltaScale = (1 - (e.deltaY / coefficient)); diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 7a88985c0..2363553df 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -60,10 +60,8 @@ export class KeyValuePair extends React.Component { { let field = FieldValue(props.Document[props.fieldKey]); - if (field) { - //TODO Types - return String(field); - // return field.ToScriptString(); + if (Field.IsField(field)) { + return Field.toScriptString(field); } return ""; }} @@ -75,7 +73,7 @@ export class KeyValuePair extends React.Component { let res = script.run(); if (!res.success) return false; const field = res.result; - if (Field.IsField(field)) { + if (Field.IsField(field, true)) { props.Document[props.fieldKey] = field; return true; } diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx index 4314e2132..b22300d0b 100644 --- a/src/debug/Viewer.tsx +++ b/src/debug/Viewer.tsx @@ -20,7 +20,7 @@ function applyToDoc(doc: any, key: string | number, scriptString: string): boole } const res = script.run({ this: doc }); if (!res.success) return false; - if (!Field.IsField(res.result)) return false; + if (!Field.IsField(res.result, true)) return false; doc[key] = res.result; return true; } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 08bc2ec4d..b0237d04d 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -19,12 +19,15 @@ export namespace Field { return field[ToScriptString](); } } - export function IsField(field: any): field is Field { + export function IsField(field: any): field is Field; + export function IsField(field: any, includeUndefined: true): field is Field | undefined; + export function IsField(field: any, includeUndefined: boolean = false): field is Field | undefined { return (typeof field === "string") || (typeof field === "number") || (typeof field === "boolean") || (field instanceof ObjectField) - || (field instanceof RefField); + || (field instanceof RefField) + || (includeUndefined && field === undefined); } } export type Field = number | string | boolean | ObjectField | RefField; diff --git a/src/new_fields/URLField.ts b/src/new_fields/URLField.ts index a6f8f1cc5..4a2841fb6 100644 --- a/src/new_fields/URLField.ts +++ b/src/new_fields/URLField.ts @@ -18,13 +18,18 @@ export abstract class URLField extends ObjectField { @serializable(url()) readonly url: URL; - constructor(url: URL) { + constructor(url: string); + constructor(url: URL); + constructor(url: URL | string) { super(); + if (typeof url === "string") { + url = new URL(url); + } this.url = url; } [ToScriptString]() { - return `new ${this.constructor.name}(new URL(${this.url.href}))`; + return `new ${this.constructor.name}("${this.url.href}")`; } [Copy](): this { -- cgit v1.2.3-70-g09d2 From cfb7fdb1a7b2db263502677e57ee882a6fe23f13 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 21 May 2019 12:29:28 -0400 Subject: Made cursors disappear after 1 second --- package.json | 1 + src/client/views/collections/CollectionSubView.tsx | 6 ++---- .../collectionFreeForm/CollectionFreeFormRemoteCursors.tsx | 5 ++++- src/new_fields/CursorField.ts | 9 ++++++--- 4 files changed, 13 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/package.json b/package.json index 790535728..aa4abb0a5 100644 --- a/package.json +++ b/package.json @@ -126,6 +126,7 @@ "mobx": "^5.9.0", "mobx-react": "^5.3.5", "mobx-react-devtools": "^6.1.1", + "mobx-utils": "^5.4.0", "mongodb": "^3.1.13", "mongoose": "^5.4.18", "node-sass": "^4.12.0", diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 864fdfa4b..7800b35df 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -16,9 +16,7 @@ import { listSpec } from "../../../new_fields/Schema"; import { Cast, PromiseValue, FieldValue, ListSpec } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { DocServer } from "../../DocServer"; -import { ObjectField } from "../../../new_fields/ObjectField"; -import CursorField, { CursorPosition, CursorMetadata } from "../../../new_fields/CursorField"; -import { url } from "inspector"; +import CursorField from "../../../new_fields/CursorField"; export interface CollectionViewProps extends FieldViewProps { addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; @@ -72,7 +70,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.data.metadata.id === id)) > -1) { cursors[ind].setPosition(pos); } else { - let entry = new CursorField({ metadata: { id: id, identifier: email }, position: pos }); + let entry = new CursorField({ metadata: { id: id, identifier: email, timestamp: Date.now() }, position: pos }); cursors.push(entry); } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx index 642118d75..2838b7905 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx @@ -9,6 +9,7 @@ import CursorField from "../../../../new_fields/CursorField"; import { List } from "../../../../new_fields/List"; import { Cast } from "../../../../new_fields/Types"; import { listSpec } from "../../../../new_fields/Schema"; +import * as mobxUtils from 'mobx-utils'; @observer export class CollectionFreeFormRemoteCursors extends React.Component { @@ -23,7 +24,9 @@ export class CollectionFreeFormRemoteCursors extends React.Component cursor.data.metadata.id !== id); + const now = mobxUtils.now(); + // const now = Date.now(); + return (cursors || []).filter(cursor => cursor.data.metadata.id !== id && (now - cursor.data.metadata.timestamp) < 1000); } private crosshairs?: HTMLCanvasElement; diff --git a/src/new_fields/CursorField.ts b/src/new_fields/CursorField.ts index 1be1ec3e0..fd86031a8 100644 --- a/src/new_fields/CursorField.ts +++ b/src/new_fields/CursorField.ts @@ -1,7 +1,7 @@ import { ObjectField } from "./ObjectField"; import { observable } from "mobx"; import { Deserializable } from "../client/util/SerializationHelper"; -import { serializable, createSimpleSchema, object } from "serializr"; +import { serializable, createSimpleSchema, object, date } from "serializr"; import { OnUpdate, ToScriptString, Copy } from "./FieldSymbols"; export type CursorPosition = { @@ -11,7 +11,8 @@ export type CursorPosition = { export type CursorMetadata = { id: string, - identifier: string + identifier: string, + timestamp: number }; export type CursorData = { @@ -26,7 +27,8 @@ const PositionSchema = createSimpleSchema({ const MetadataSchema = createSimpleSchema({ id: true, - identifier: true + identifier: true, + timestamp: true }); const CursorSchema = createSimpleSchema({ @@ -47,6 +49,7 @@ export default class CursorField extends ObjectField { setPosition(position: CursorPosition) { this.data.position = position; + this.data.metadata.timestamp = Date.now(); this[OnUpdate](); } -- cgit v1.2.3-70-g09d2 From 7bafa21e8c37826686a012151674b71631fa9c8b Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 21 May 2019 12:35:36 -0400 Subject: fixed dragging headers for histograms --- src/client/documents/Documents.ts | 1 - src/client/views/collections/CollectionSchemaView.tsx | 16 +++++++++++++--- .../views/collections/collectionFreeForm/MarqueeView.tsx | 5 ++++- 3 files changed, 17 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 9d2f4d3cd..3d65826a9 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -33,7 +33,6 @@ import { DocServer } from "../DocServer"; import { StrokeData, InkField } from "../../new_fields/InkField"; import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; -import { schema } from "prosemirror-schema-basic"; import { UndoManager } from "../util/UndoManager"; export interface DocumentOptions { diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index b25b48339..c42a0332d 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -68,7 +68,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { return this.columns.map(col => { const ref = React.createRef(); return { - Header:

this.onHeaderDrag(col))}>{col}

, + Header:

this.onHeaderDrag(col), undefined, "copy")}>{col}

, accessor: (doc: Doc) => doc ? doc[col] : 0, id: col }; @@ -76,6 +76,15 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { } onHeaderDrag = (columnName: string) => { + let dbDoc = Cast(this.props.Document.DBDoc, Doc); + if (dbDoc instanceof Doc) { + let columnDocs = DocListCast(dbDoc.data); + if (columnDocs) { + let ddoc = columnDocs.find(doc => doc.title === columnName); + if (ddoc) + return ddoc; + } + } return this.props.Document; } @@ -237,7 +246,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { csv = csv.substr(0, csv.length - 1) + "\n"; let self = this; DocListCast(this.props.Document.data).map(doc => { - csv += self.columns.reduce((val, col) => val + (doc[col] ? doc[col]!.toString() : "") + ",", ""); + csv += self.columns.reduce((val, col) => val + (doc[col] ? doc[col]!.toString() : "0") + ",", ""); csv = csv.substr(0, csv.length - 1) + "\n"; }); csv.substring(0, csv.length - 1); @@ -246,7 +255,8 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { if (self.props.CollectionView.props.addDocument) { let schemaDoc = await Docs.DBDocument("https://www.cs.brown.edu/" + dbName, { title: dbName }); if (schemaDoc) { - self.props.CollectionView.props.addDocument(schemaDoc, false); + //self.props.CollectionView.props.addDocument(schemaDoc, false); + self.props.Document.DBDoc = schemaDoc; } } } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 31864f357..7ccf480f3 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -98,18 +98,21 @@ export class MarqueeView extends React.Component let columns = ns[0].split("\t"); let docList: Doc[] = []; let groupAttr: string | number = ""; + let rowProto = new Doc(); + rowProto.width = 200; for (let i = 1; i < ns.length - 1; i++) { let values = ns[i].split("\t"); if (values.length === 1 && columns.length > 1) { groupAttr = values[0]; continue; } - let doc = new Doc(); + let doc = Doc.MakeDelegate(rowProto); columns.forEach((col, i) => doc[columns[i]] = (values.length > i ? ((values[i].indexOf(Number(values[i]).toString()) !== -1) ? Number(values[i]) : values[i]) : undefined)); if (groupAttr) { doc._group = groupAttr; } doc.title = i.toString(); + doc.width = 200; docList.push(doc); } let newCol = Docs.SchemaDocument([...(groupAttr ? ["_group"] : []), ...columns.filter(c => c)], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 }); -- cgit v1.2.3-70-g09d2 From 7e5ba95b02e4ead3ee2b41eca1af0acb72d6f7cd Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 21 May 2019 14:15:06 -0400 Subject: fixes for histogram brushing. --- src/client/northstar/dash-fields/HistogramField.ts | 2 +- src/client/northstar/dash-nodes/HistogramBox.tsx | 4 ++- .../CollectionFreeFormLinksView.tsx | 12 +++---- .../collections/collectionFreeForm/MarqueeView.tsx | 38 ++++++++++++---------- src/client/views/nodes/FieldView.tsx | 2 +- 5 files changed, 31 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/client/northstar/dash-fields/HistogramField.ts b/src/client/northstar/dash-fields/HistogramField.ts index 31040a474..e6f32272e 100644 --- a/src/client/northstar/dash-fields/HistogramField.ts +++ b/src/client/northstar/dash-fields/HistogramField.ts @@ -58,6 +58,6 @@ export class HistogramField extends ObjectField { } [ToScriptString]() { - return "invalid"; + return this.toString(); } } \ No newline at end of file diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx index a9646ed31..d7732ee86 100644 --- a/src/client/northstar/dash-nodes/HistogramBox.tsx +++ b/src/client/northstar/dash-nodes/HistogramBox.tsx @@ -125,9 +125,11 @@ export class HistogramBox extends React.Component { let mapped = brushingDocs.map((brush, i) => { brush.backgroundColor = StyleConstants.BRUSH_COLORS[i % StyleConstants.BRUSH_COLORS.length]; let brushed = DocListCast(brush.brushingDocs); + if (!brushed.length) + return null; return { l: brush, b: brushed[0][Id] === proto[Id] ? brushed[1] : brushed[0] }; }); - this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...mapped); + runInAction(() => this.HistoOp.BrushLinks.splice(0, this.HistoOp.BrushLinks.length, ...mapped.filter(m => m) as { l: Doc, b: Doc }[])); } }, { fireImmediately: true }); reaction(() => this.createOperationParamsCache, () => this.HistoOp.Update(), { fireImmediately: true }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index e1ff715d1..c5f7ad0d1 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -32,8 +32,8 @@ export class CollectionFreeFormLinksView extends React.Component)[]) => field.findIndex(brush => { @@ -60,12 +60,12 @@ export class CollectionFreeFormLinksView extends React.Component(); + if (srcTarg.brushingDocs === undefined) srcTarg.brushingDocs = new List(); let dstBrushDocs = Cast(dstTarg.brushingDocs, listSpec(Doc), []); let srcBrushDocs = Cast(srcTarg.brushingDocs, listSpec(Doc), []); - if (dstBrushDocs === undefined) dstTarg.brushingDocs = dstBrushDocs = new List(); - else brushAction(dstBrushDocs); - if (srcBrushDocs === undefined) srcTarg.brushingDocs = srcBrushDocs = new List(); - else brushAction(srcBrushDocs); + brushAction(dstBrushDocs); + brushAction(srcBrushDocs); } }); }); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 7ccf480f3..f0ccda140 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -228,6 +228,7 @@ export class MarqueeView extends React.Component if (e.key === "c" || e.key === "s" || e.key === "e" || e.key === "p") { this._commandExecuted = true; e.stopPropagation(); + e.preventDefault(); (e as any).propagationIsStopped = true; let bounds = this.Bounds; let selected = this.marqueeSelect(); @@ -260,25 +261,26 @@ export class MarqueeView extends React.Component if (e.key === "s" || e.key === "p") { - htmlToImage.toPng(this._mainCont.current!, { width: bounds.width * zoomBasis, height: bounds.height * zoomBasis, quality: 0.2 }).then((dataUrl) => { - selected.map(d => { - this.props.removeDocument(d); - d.x = NumCast(d.x) - bounds.left - bounds.width / 2; - d.y = NumCast(d.y) - bounds.top - bounds.height / 2; - d.page = -1; - return d; - }); - let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" }); - summary.proto!.thumbnail = new ImageField(new URL(dataUrl)); - summary.proto!.templates = new List([Templates.ImageOverlay(Math.min(50, bounds.width), bounds.height * Math.min(50, bounds.width) / bounds.width, "thumbnail")]); - newCollection.proto!.summaryDoc = summary; - selected = [newCollection]; - newCollection.x = bounds.left + bounds.width; - //this.props.addDocument(newCollection, false); - summary.proto!.summarizedDocs = new List(selected); - summary.proto!.maximizeLocation = "inTab"; // or "inPlace", or "onRight" - this.props.addLiveTextDocument(summary); + // htmlToImage.toPng(this._mainCont.current!, { width: bounds.width * zoomBasis, height: bounds.height * zoomBasis, quality: 0.2 }).then((dataUrl) => { + selected.map(d => { + this.props.removeDocument(d); + d.x = NumCast(d.x) - bounds.left - bounds.width / 2; + d.y = NumCast(d.y) - bounds.top - bounds.height / 2; + d.page = -1; + return d; }); + let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" }); + // summary.proto!.thumbnail = new ImageField(new URL(dataUrl)); + // summary.proto!.templates = new List([Templates.ImageOverlay(Math.min(50, bounds.width), bounds.height * Math.min(50, bounds.width) / bounds.width, "thumbnail")]); + newCollection.proto!.summaryDoc = summary; + selected = [newCollection]; + newCollection.x = bounds.left + bounds.width; + //this.props.addDocument(newCollection, false); + summary.proto!.summarizedDocs = new List(selected); + summary.proto!.maximizeLocation = "inTab"; // or "inPlace", or "onRight" + + this.props.addLiveTextDocument(summary); + // }); } else { this.props.addDocument(newCollection, false); diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 5c149af99..092ccb9b0 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -117,7 +117,7 @@ export class FieldView extends React.Component { // return // } else if (!(field instanceof Promise)) { - return

{JSON.stringify(field)}

; + return

{field.toString()}

; } else { return

{"Waiting for server..."}

; -- cgit v1.2.3-70-g09d2