aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/ImageBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/ImageBox.tsx')
-rw-r--r--src/client/views/nodes/ImageBox.tsx267
1 files changed, 77 insertions, 190 deletions
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 07fd832be..3341a1bf1 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -2,10 +2,8 @@ import { library } from '@fortawesome/fontawesome-svg-core';
import { faEye } from '@fortawesome/free-regular-svg-icons';
import { faAsterisk, faFileAudio, faImage, faPaintBrush } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, runInAction } from 'mobx';
+import { action, computed, observable, runInAction, trace } from 'mobx';
import { observer } from "mobx-react";
-import Lightbox from 'react-image-lightbox';
-import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
import { Doc, DocListCast, HeightSym, WidthSym } from '../../../new_fields/Doc';
import { List } from '../../../new_fields/List';
import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema';
@@ -21,12 +19,13 @@ import { ContextMenu } from "../../views/ContextMenu";
import { ContextMenuProps } from '../ContextMenuItem';
import { DocAnnotatableComponent } from '../DocComponent';
import { InkingControl } from '../InkingControl';
-import { documentSchema } from './DocumentView';
import FaceRectangles from './FaceRectangles';
import { FieldView, FieldViewProps } from './FieldView';
import "./ImageBox.scss";
import React = require("react");
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
+import { documentSchema } from '../../../new_fields/documentSchemas';
+import { Id } from '../../../new_fields/FieldSymbols';
var requestImageSize = require('../../util/request-image-size');
var path = require('path');
const { Howl } = require('howler');
@@ -38,7 +37,10 @@ library.add(faFileAudio, faAsterisk);
export const pageSchema = createSchema({
curPage: "number",
- fitWidth: "boolean"
+ fitWidth: "boolean",
+ rotation: "number",
+ googlePhotosUrl: "string",
+ googlePhotosTags: "string"
});
interface Window {
@@ -55,29 +57,15 @@ const ImageDocument = makeInterface(pageSchema, documentSchema);
@observer
export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocument>(ImageDocument) {
-
- public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(ImageBox, "data", fieldExt); }
- @observable static _showControls: boolean;
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); }
private _imgRef: React.RefObject<HTMLImageElement> = React.createRef();
- private _downX: number = 0;
- private _downY: number = 0;
- private _lastTap: number = 0;
- @observable private _isOpen: boolean = false;
- private dropDisposer?: DragManager.DragDropDisposer;
- @observable private hoverActive = false;
+ private _dropDisposer?: DragManager.DragDropDisposer;
+ @observable private _audioState = 0;
+ @observable static _showControls: boolean;
protected createDropTarget = (ele: HTMLDivElement) => {
- if (this.dropDisposer) {
- this.dropDisposer();
- }
- if (ele) {
- this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } });
- }
- }
- onDrop = (e: React.DragEvent) => {
- e.stopPropagation();
- e.preventDefault();
- console.log("IMPLEMENT ME PLEASE");
+ this._dropDisposer && this._dropDisposer();
+ ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }));
}
@undoBatch
@@ -89,61 +77,18 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
e.stopPropagation();
}
de.mods === "MetaKey" && de.data.droppedDocuments.forEach(action((drop: Doc) => {
- Doc.AddDocToList(Doc.GetProto(this.extensionDoc), "Alternates", drop);
+ this.extensionDoc && Doc.AddDocToList(Doc.GetProto(this.extensionDoc), "Alternates", drop);
e.stopPropagation();
}));
}
}
- onPointerDown = (e: React.PointerEvent): void => {
- if (e.shiftKey && e.ctrlKey) {
- e.stopPropagation(); // allows default system drag drop of images with shift+ctrl only
- }
- // if (Date.now() - this._lastTap < 300) {
- // if (e.buttons === 1) {
- // this._downX = e.clientX;
- // this._downY = e.clientY;
- // document.removeEventListener("pointerup", this.onPointerUp);
- // document.addEventListener("pointerup", this.onPointerUp);
- // }
- // } else {
- // this._lastTap = Date.now();
- // }
- }
- @action
- onPointerUp = (e: PointerEvent): void => {
- document.removeEventListener("pointerup", this.onPointerUp);
- if (Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2) {
- this._isOpen = true;
- }
- e.stopPropagation();
- }
-
- @action
- lightbox = (images: string[]) => {
- if (this._isOpen) {
- return (<Lightbox
- mainSrc={images[this.Document.curPage || 0]}
- nextSrc={images[((this.Document.curPage || 0) + 1) % images.length]}
- prevSrc={images[((this.Document.curPage || 0) + images.length - 1) % images.length]}
- onCloseRequest={action(() =>
- this._isOpen = false
- )}
- onMovePrevRequest={action(() =>
- this.Document.curPage = ((this.Document.curPage || 0) + images.length - 1) % images.length
- )}
- onMoveNextRequest={action(() =>
- this.Document.curPage = ((this.Document.curPage || 0) + 1) % images.length
- )}
- />);
- }
- }
-
recordAudioAnnotation = () => {
let gumStream: any;
let recorder: any;
let self = this;
- navigator.mediaDevices.getUserMedia({
+ const extensionDoc = this.extensionDoc;
+ extensionDoc && navigator.mediaDevices.getUserMedia({
audio: true
}).then(function (stream) {
gumStream = stream;
@@ -156,13 +101,13 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
body: formData
});
const files = await res.json();
- const url = Utils.prepend(files[0]);
+ const url = Utils.prepend(files[0].path);
// upload to server with known URL
- let audioDoc = Docs.Create.AudioDocument(url, { title: "audio test", x: NumCast(self.props.Document.x), y: NumCast(self.props.Document.y), width: 200, height: 32 });
+ let audioDoc = Docs.Create.AudioDocument(url, { title: "audio test", width: 200, height: 32 });
audioDoc.treeViewExpandedView = "layout";
- let audioAnnos = Cast(self.extensionDoc.audioAnnotations, listSpec(Doc));
+ let audioAnnos = Cast(extensionDoc.audioAnnotations, listSpec(Doc));
if (audioAnnos === undefined) {
- self.extensionDoc.audioAnnotations = new List([audioDoc]);
+ extensionDoc.audioAnnotations = new List([audioDoc]);
} else {
audioAnnos.push(audioDoc);
}
@@ -179,25 +124,22 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
@undoBatch
rotate = action(() => {
- let proto = Doc.GetProto(this.props.Document);
- let nw = this.props.Document.nativeWidth;
- let nh = this.props.Document.nativeHeight;
- let w = this.props.Document.width;
- let h = this.props.Document.height;
- proto.rotation = (NumCast(this.props.Document.rotation) + 90) % 360;
- proto.nativeWidth = nh;
- proto.nativeHeight = nw;
- this.props.Document.width = h;
- this.props.Document.height = w;
+ let nw = this.Document.nativeWidth;
+ let nh = this.Document.nativeHeight;
+ let w = this.Document.width;
+ let h = this.Document.height;
+ this.Document.rotation = ((this.Document.rotation || 0) + 90) % 360;
+ this.Document.nativeWidth = nh;
+ this.Document.nativeHeight = nw;
+ this.Document.width = h;
+ this.Document.height = w;
});
specificContextMenu = (e: React.MouseEvent): void => {
- let field = Cast(this.Document[this.props.fieldKey], ImageField);
+ const field = Cast(this.Document[this.props.fieldKey], ImageField);
if (field) {
- let url = field.url.href;
let funcs: ContextMenuProps[] = [];
- funcs.push({ description: "Copy path", event: () => Utils.CopyText(url), icon: "expand-arrows-alt" });
- funcs.push({ description: "Record 1sec audio", event: this.recordAudioAnnotation, icon: "expand-arrows-alt" });
+ funcs.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" });
funcs.push({ description: "Rotate", event: this.rotate, icon: "expand-arrows-alt" });
let existingAnalyze = ContextMenu.Instance.findByDescription("Analyzers...");
@@ -213,12 +155,10 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
extractFaces = () => {
let converter = (results: any) => {
let faceDocs = new List<Doc>();
- results.map((face: CognitiveServices.Image.Face) => faceDocs.push(Docs.Get.DocumentHierarchyFromJson(face, `Face: ${face.faceId}`)!));
+ results.reduce((face: CognitiveServices.Image.Face, faceDocs: List<Doc>) => faceDocs.push(Docs.Get.DocumentHierarchyFromJson(face, `Face: ${face.faceId}`)!), new List<Doc>());
return faceDocs;
};
- if (this.url) {
- CognitiveServices.Image.Appliers.ProcessImage(this.extensionDoc, ["faces"], this.url, Service.Face, converter);
- }
+ this.url && this.extensionDoc && CognitiveServices.Image.Appliers.ProcessImage(this.extensionDoc, ["faces"], this.url, Service.Face, converter);
}
generateMetadata = (threshold: Confidence = Confidence.Excellent) => {
@@ -230,36 +170,19 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
let sanitized = tag.name.replace(" ", "_");
tagDoc[sanitized] = ComputedField.MakeFunction(`(${tag.confidence} >= this.confidence) ? ${tag.confidence} : "${ComputedField.undefined}"`);
});
- this.extensionDoc.generatedTags = tagsList;
+ this.extensionDoc && (this.extensionDoc.generatedTags = tagsList);
tagDoc.title = "Generated Tags Doc";
tagDoc.confidence = threshold;
return tagDoc;
};
- if (this.url) {
- CognitiveServices.Image.Appliers.ProcessImage(this.extensionDoc, ["generatedTagsDoc"], this.url, Service.ComputerVision, converter);
- }
+ this.url && this.extensionDoc && CognitiveServices.Image.Appliers.ProcessImage(this.extensionDoc, ["generatedTagsDoc"], this.url, Service.ComputerVision, converter);
}
- @action
- onDotDown(index: number) {
- this.Document.curPage = index;
- }
@computed private get url() {
- let data = Cast(Doc.GetProto(this.props.Document)[this.props.fieldKey], ImageField);
+ let data = Cast(this.dataDoc[this.props.fieldKey], ImageField);
return data ? data.url.href : undefined;
}
- dots(paths: string[]) {
- let nativeWidth = FieldValue(this.Document.nativeWidth, 1);
- let dist = Math.min(nativeWidth / paths.length, 40);
- let left = (nativeWidth - paths.length * dist) / 2;
- return paths.map((p, i) =>
- <div className="imageBox-placer" key={i} >
- <div className="imageBox-dot" style={{ background: (i === this.Document.curPage ? "black" : "gray"), transform: `translate(${i * dist + left}px, 0px)` }} onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); this.onDotDown(i); }} />
- </div>
- );
- }
-
choosePath(url: URL) {
const lower = url.href.toLowerCase();
if (url.protocol === "data") {
@@ -290,32 +213,28 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
}
_curSuffix = "_m";
- resize(srcpath: string, layoutdoc: Doc) {
+ resize = (srcpath: string) => {
requestImageSize(srcpath)
.then((size: any) => {
let rotation = NumCast(this.dataDoc.rotation) % 180;
let realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size;
let aspect = realsize.height / realsize.width;
- if (layoutdoc.width && (Math.abs(1 - NumCast(layoutdoc.height) / NumCast(layoutdoc.width) / (realsize.height / realsize.width)) > 0.1)) {
+ if (this.Document.width && (Math.abs(1 - NumCast(this.Document.height) / NumCast(this.Document.width) / (realsize.height / realsize.width)) > 0.1)) {
setTimeout(action(() => {
- layoutdoc.height = layoutdoc[WidthSym]() * aspect;
- layoutdoc.nativeHeight = realsize.height;
- layoutdoc.nativeWidth = realsize.width;
+ this.Document.height = this.Document[WidthSym]() * aspect;
+ this.Document.nativeHeight = realsize.height;
+ this.Document.nativeWidth = realsize.width;
}), 0);
}
})
- .catch((err: any) => {
- console.log(err);
- });
+ .catch((err: any) => console.log(err));
}
- @observable _audioState = 0;
-
@action
onPointerEnter = () => {
let self = this;
- let audioAnnos = DocListCast(this.extensionDoc.audioAnnotations);
- if (audioAnnos.length && this._audioState === 0) {
+ let audioAnnos = this.extensionDoc && DocListCast(this.extensionDoc.audioAnnotations);
+ if (audioAnnos && audioAnnos.length && this._audioState === 0) {
let anno = audioAnnos[Math.floor(Math.random() * audioAnnos.length)];
anno.data instanceof AudioField && new Howl({
src: [anno.data.url.href],
@@ -329,70 +248,41 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
});
this._audioState = 1;
}
- // else {
- // if (this._audioState === 0) {
- // this._audioState = 1;
- // new Howl({
- // src: ["https://www.kozco.com/tech/piano2-CoolEdit.mp3"],
- // autoplay: true,
- // loop: false,
- // volume: 0.5,
- // onend: function () {
- // runInAction(() => self._audioState = 0);
- // }
- // });
- // }
- // }
}
- @action
- audioDown = () => {
- this.recordAudioAnnotation();
- }
+ audioDown = () => this.recordAudioAnnotation();
considerGooglePhotosLink = () => {
- const remoteUrl = StrCast(this.props.Document.googlePhotosUrl);
- if (remoteUrl) {
- return (
- <img
- id={"google-photos"}
- src={"/assets/google_photos.png"}
- style={{ opacity: this.hoverActive ? 1 : 0 }}
- onClick={() => window.open(remoteUrl)}
- />
- );
- }
- return (null);
+ const remoteUrl = this.Document.googlePhotosUrl;
+ return !remoteUrl ? (null) : (<img
+ id={"google-photos"}
+ src={"/assets/google_photos.png"}
+ onClick={() => window.open(remoteUrl)}
+ />);
}
considerGooglePhotosTags = () => {
- const tags = StrCast(this.props.Document.googlePhotosTags);
- if (tags) {
- return (
- <img
- id={"google-tags"}
- src={"/assets/google_tags.png"}
- />
- );
- }
- return (null);
+ const tags = this.Document.googlePhotosTags;
+ return !tags ? (null) : (<img id={"google-tags"} src={"/assets/google_tags.png"} />);
}
- @computed
- get content() {
+ @computed get content() {
+ trace();
+ const extensionDoc = this.extensionDoc;
+ if (!extensionDoc) return (null);
// 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 nativeWidth = FieldValue(this.Document.nativeWidth, pw);
- let nativeHeight = FieldValue(this.Document.nativeHeight, 0);
- let paths: string[] = [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
+ let nativeWidth = (this.Document.nativeWidth || pw);
+ let nativeHeight = (this.Document.nativeHeight || 0);
+ let paths = [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
// this._curSuffix = "";
// if (w > 20) {
- let alts = DocListCast(this.extensionDoc.Alternates);
- let altpaths: string[] = alts.filter(doc => doc.data instanceof ImageField).map(doc => this.choosePath((doc.data as ImageField).url));
+ let alts = DocListCast(extensionDoc.Alternates);
+ let altpaths = alts.filter(doc => doc.data instanceof ImageField).map(doc => this.choosePath((doc.data as ImageField).url));
let field = this.dataDoc[this.props.fieldKey];
// if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s";
// else if (w < 600 && this._mediumRetryCount < 10) this._curSuffix = "_m";
@@ -400,21 +290,17 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
if (field instanceof ImageField) paths = [this.choosePath(field.url)];
paths.push(...altpaths);
// }
- let interactive = InkingControl.Instance.selectedTool || this.props.Document.isBackground ? "" : "-interactive";
- let rotation = NumCast(this.dataDoc.rotation, 0);
- let aspect = (rotation % 180) ? this.dataDoc[HeightSym]() / this.dataDoc[WidthSym]() : 1;
+ let interactive = InkingControl.Instance.selectedTool || this.Document.isBackground ? "" : "-interactive";
+ let rotation = NumCast(this.Document.rotation, 0);
+ let aspect = (rotation % 180) ? this.Document[HeightSym]() / this.Document[WidthSym]() : 1;
let shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0;
- let srcpath = paths[Math.min(paths.length - 1, this.Document.curPage || 0)];
+ let srcpath = paths[Math.min(paths.length - 1, (this.Document.curPage || 0))];
let fadepath = paths[Math.min(paths.length - 1, 1)];
- if (!this.props.Document.ignoreAspect && !this.props.leaveNativeSize) this.resize(srcpath, this.props.Document);
+ !this.Document.ignoreAspect && this.resize(srcpath);
return (
- <div className={`imageBox-cont${interactive}`} style={{ background: "transparent" }}
- onPointerDown={this.onPointerDown}
- onPointerEnter={action(() => this.hoverActive = true)}
- onPointerLeave={action(() => this.hoverActive = false)}
- onDrop={this.onDrop} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
+ <div className={`imageBox-cont${interactive}`} key={this.props.Document[Id]} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}>
<div id="cf">
<img
key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys
@@ -431,31 +317,32 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
ref={this._imgRef}
onError={this.onError} /></div>}
</div>
- {paths.length > 1 ? this.dots(paths) : (null)}
<div className="imageBox-audioBackground"
onPointerDown={this.audioDown}
onPointerEnter={this.onPointerEnter}
style={{ height: `calc(${.1 * nativeHeight / nativeWidth * 100}%)` }}
>
<FontAwesomeIcon className="imageBox-audioFont"
- style={{ color: [DocListCast(this.extensionDoc.audioAnnotations).length ? "blue" : "gray", "green", "red"][this._audioState] }} icon={faFileAudio} size="sm" />
+ style={{ color: [DocListCast(extensionDoc.audioAnnotations).length ? "blue" : "gray", "green", "red"][this._audioState] }} icon={!DocListCast(extensionDoc.audioAnnotations).length ? "microphone" : faFileAudio} size="sm" />
</div>
{this.considerGooglePhotosLink()}
- {/* {this.lightbox(paths)} */}
- <FaceRectangles document={this.extensionDoc} color={"#0000FF"} backgroundColor={"#0000FF"} />
+ <FaceRectangles document={extensionDoc} color={"#0000FF"} backgroundColor={"#0000FF"} />
</div>);
}
+ contentFunc = () => [this.content];
render() {
- Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey);
- return (<div className={"imageBox-container"} onContextMenu={this.specificContextMenu}>
+ return (<div className={"imageBox-container"} onContextMenu={this.specificContextMenu}
+ style={{ transformOrigin: "top left", transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
<CollectionFreeFormView {...this.props}
PanelHeight={this.props.PanelHeight}
PanelWidth={this.props.PanelWidth}
+ annotationsKey={this.annotationsKey}
+ isAnnotationOverlay={true}
focus={this.props.focus}
isSelected={this.props.isSelected}
select={emptyFunction}
- active={this.active}
+ active={this.annotationsActive}
ContentScaling={returnOne}
whenActiveChanged={this.whenActiveChanged}
removeDocument={this.removeDocument}
@@ -467,7 +354,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum
renderDepth={this.props.renderDepth + 1}
ContainingCollectionDoc={this.props.ContainingCollectionDoc}
chromeCollapsed={true}>
- {() => [this.content]}
+ {this.contentFunc}
</CollectionFreeFormView>
</div >);
}