diff options
author | Bob Zeleznik <zzzman@gmail.com> | 2019-03-25 23:20:22 -0400 |
---|---|---|
committer | Bob Zeleznik <zzzman@gmail.com> | 2019-03-25 23:20:22 -0400 |
commit | 2264fb874a09ee01540141986325c76650f32c21 (patch) | |
tree | 8e474e1bc794087734afcdb871e1e79d7284060b /src | |
parent | 31049374ef26d33e9d5304e1975e550d9046a50a (diff) |
added 1-level navigation to region annotations
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/collections/CollectionDockingView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/CollectionVideoView.tsx | 108 | ||||
-rw-r--r-- | src/client/views/collections/CollectionView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/nodes/LinkBox.tsx | 21 | ||||
-rw-r--r-- | src/client/views/nodes/PDFBox.tsx | 21 | ||||
-rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 29 | ||||
-rw-r--r-- | src/fields/KeyStore.ts | 2 |
7 files changed, 120 insertions, 69 deletions
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 39b284d8e..d4f510f5d 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -191,7 +191,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp @action onPointerDown = (e: React.PointerEvent): void => { var className = (e.target as any).className; - if ((className == "lm_title" || className == "lm_tab lm_active") && e.ctrlKey) { + if ((className == "lm_title" || className == "lm_tab lm_active") && (e.ctrlKey || e.altKey)) { e.stopPropagation(); e.preventDefault(); let docid = (e.target as any).DashDocId; diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index a3921696f..470a853e3 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -1,4 +1,4 @@ -import { action, computed, observable } from "mobx"; +import { action, computed, observable, trace } from "mobx"; import { observer } from "mobx-react"; import { Document } from "../../../fields/Document"; import { KeyStore } from "../../../fields/KeyStore"; @@ -12,24 +12,26 @@ import "./CollectionVideoView.scss" @observer export class CollectionVideoView extends React.Component<CollectionViewProps> { + private _intervalTimer: any = undefined; + private _player: HTMLVideoElement | undefined = undefined; + + @observable _currentTimecode: number = 0; + @observable _isPlaying: boolean = false; public static LayoutString(fieldKey: string = "DataKey") { return `<${CollectionVideoView.name} Document={Document} ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings} isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`; } - - private _mainCont = React.createRef<HTMLDivElement>(); - private get uIButtons() { let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]); return ([ <div className="collectionVideoView-time" key="time" onPointerDown={this.onResetDown} style={{ transform: `scale(${scaling}, ${scaling})` }}> - <span>{"" + Math.round(this.ctime)}</span> - <span style={{ fontSize: 8 }}>{" " + Math.round((this.ctime - Math.trunc(this.ctime)) * 100)}</span> + <span>{"" + Math.round(this._currentTimecode)}</span> + <span style={{ fontSize: 8 }}>{" " + Math.round((this._currentTimecode - Math.trunc(this._currentTimecode)) * 100)}</span> </div>, <div className="collectionVideoView-play" key="play" onPointerDown={this.onPlayDown} style={{ transform: `scale(${scaling}, ${scaling})` }}> - {this.playing ? "\"" : ">"} + {this._isPlaying ? "\"" : ">"} </div>, <div className="collectionVideoView-full" key="full" onPointerDown={this.onFullDown} style={{ transform: `scale(${scaling}, ${scaling})` }}> F @@ -37,64 +39,54 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> { ]); } - - // "inherited" CollectionView API starts here... - - @observable - public SelectedDocs: FieldId[] = [] - public active: () => boolean = () => CollectionView.Active(this); - - addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); } - removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); } - - specificContextMenu = (e: React.MouseEvent): void => { - if (!e.isPropagationStopped() && this.props.Document.Id != "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 - ContextMenu.Instance.addItem({ description: "VideoOptions", event: () => { } }); + @action + mainCont = (ele: HTMLDivElement | null) => { + if (ele) { + this._player = ele!.getElementsByTagName("video")[0]; + if (this.props.Document.GetNumber(KeyStore.CurPage, -1) >= 0) { + this._currentTimecode = this.props.Document.GetNumber(KeyStore.CurPage, -1); + } } } - get collectionViewType(): CollectionViewType { return CollectionViewType.Freeform; } - get subView(): any { return CollectionView.SubView(this); } - componentDidMount() { - this.updateTimecode(); + this._intervalTimer = setInterval(this.updateTimecode, 1000); } - get player(): HTMLVideoElement | undefined { - return this._mainCont.current ? this._mainCont.current.getElementsByTagName("video")[0] : undefined; + componentWillUnmount() { + clearInterval(this._intervalTimer); } @action updateTimecode = () => { - if (this.player) { - this.ctime = this.player.currentTime; - this.props.Document.SetNumber(KeyStore.CurPage, Math.round(this.ctime)); + if (this._player) { + if ((this._player as any).AHackBecauseSomethingResetsTheVideoToZero != -1) { + this._player.currentTime = (this._player as any).AHackBecauseSomethingResetsTheVideoToZero; + (this._player as any).AHackBecauseSomethingResetsTheVideoToZero = -1; + } else { + this._currentTimecode = this._player.currentTime; + this.props.Document.SetNumber(KeyStore.CurPage, Math.round(this._currentTimecode)); + } } - setTimeout(() => this.updateTimecode(), 100) } - - @observable - ctime: number = 0 - @observable - playing: boolean = false; - @action onPlayDown = () => { - if (this.player) { - if (this.player.paused) { - this.player.play(); - this.playing = true; + if (this._player) { + if (this._player.paused) { + this._player.play(); + this._isPlaying = true; } else { - this.player.pause(); - this.playing = false; + this._player.pause(); + this._isPlaying = false; } } } + @action onFullDown = (e: React.PointerEvent) => { - if (this.player) { - this.player.requestFullscreen(); + if (this._player) { + this._player.requestFullscreen(); e.stopPropagation(); e.preventDefault(); } @@ -102,15 +94,35 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> { @action onResetDown = () => { - if (this.player) { - this.player.pause(); - this.player.currentTime = 0; + if (this._player) { + this._player.pause(); + this._player.currentTime = 0; } } + // "inherited" CollectionView API starts here... + + @observable + public SelectedDocs: FieldId[] = [] + public active: () => boolean = () => CollectionView.Active(this); + + addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); } + removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); } + + specificContextMenu = (e: React.MouseEvent): void => { + if (!e.isPropagationStopped() && this.props.Document.Id != "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 + ContextMenu.Instance.addItem({ description: "VideoOptions", event: () => { } }); + } + } + + get collectionViewType(): CollectionViewType { return CollectionViewType.Freeform; } + get subView(): any { return CollectionView.SubView(this); } + + render() { - return (<div className="collectionVideoView-cont" ref={this._mainCont} onContextMenu={this.specificContextMenu}> + trace(); + return (<div className="collectionVideoView-cont" ref={this.mainCont} onContextMenu={this.specificContextMenu}> {this.subView} {this.props.isSelected() ? this.uIButtons : (null)} </div>) diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index a740865ad..2fa2c9086 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -68,7 +68,11 @@ export class CollectionView extends React.Component<CollectionViewProps> { @action public static AddDocument(props: CollectionViewProps, doc: Document, allowDuplicates: boolean): boolean { - doc.SetNumber(KeyStore.Page, props.Document.GetNumber(KeyStore.CurPage, -1)); + var curPage = props.Document.GetNumber(KeyStore.CurPage, -1); + doc.SetNumber(KeyStore.Page, curPage); + if (curPage > 0) { + doc.Set(KeyStore.AnnotationOn, props.Document); + } if (props.Document.Get(props.fieldKey) instanceof Field) { //TODO This won't create the field if it doesn't already exist const value = props.Document.GetData(props.fieldKey, ListField, new Array<Document>()) diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index dd2f71b59..638d3b5a7 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -17,6 +17,8 @@ import { faEye } from '@fortawesome/free-solid-svg-icons'; import { faEdit } from '@fortawesome/free-solid-svg-icons'; import { faTimes } from '@fortawesome/free-solid-svg-icons'; import { undoBatch } from "../../util/UndoManager"; +import { FieldWaiting } from "../../../fields/Field"; +import { NumberField } from "../../../fields/NumberField"; library.add(faEye); @@ -41,7 +43,24 @@ export class LinkBox extends React.Component<Props> { if (docView) { docView.props.focus(this.props.pairedDoc); } else { - CollectionDockingView.Instance.AddRightSplit(this.props.pairedDoc) + this.props.pairedDoc.GetAsync(KeyStore.AnnotationOn, (contextDoc: any) => { + if (!contextDoc) { + CollectionDockingView.Instance.AddRightSplit(this.props.pairedDoc); + } else if (contextDoc instanceof Document) { + this.props.pairedDoc.GetTAsync(KeyStore.Page, NumberField).then((pfield: any) => { + contextDoc.GetTAsync(KeyStore.CurPage, NumberField).then((cfield: any) => { + if (pfield != cfield) + contextDoc.SetNumber(KeyStore.CurPage, pfield.Data); + let contextView = DocumentManager.Instance.getDocumentView(contextDoc); + if (contextView) { + contextView.props.focus(contextDoc); + } else { + CollectionDockingView.Instance.AddRightSplit(contextDoc); + } + }) + }); + } + }); } } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 3a0ef2d32..e273b0b4f 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -1,5 +1,5 @@ import * as htmlToImage from "html-to-image"; -import { action, computed, observable, reaction, IReactionDisposer, trace } from 'mobx'; +import { action, computed, observable, reaction, IReactionDisposer, trace, keys } from 'mobx'; import { observer } from "mobx-react"; import 'react-image-lightbox/style.css'; import Measure from "react-measure"; @@ -18,6 +18,7 @@ import "./PDFBox.scss"; import { Sticky } from './Sticky'; //you should look at sticky and annotation, because they are used here import React = require("react") import { RouteStore } from "../../../server/RouteStore"; +import { NumberField } from "../../../fields/NumberField"; /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx * This method renders PDF and puts all kinds of functionalities such as annotation, highlighting, @@ -60,7 +61,6 @@ export class PDFBox extends React.Component<FieldViewProps> { //very useful for keeping track of X and y position throughout the PDF Canvas private initX: number = 0; private initY: number = 0; - private initPage: boolean = false; //checks if tool is on private _toolOn: boolean = false; //checks if tool is on @@ -88,17 +88,15 @@ export class PDFBox extends React.Component<FieldViewProps> { @observable private _loaded: boolean = false; @computed private get curPage() { return this.props.doc.GetNumber(KeyStore.CurPage, -1); } + @computed private get thumbnailPage() { return this.props.doc.GetNumber(KeyStore.ThumbnailPage, -1); } componentDidMount() { this._reactionDisposer = reaction( - () => this.curPage, + () => [this.curPage, this.thumbnailPage], () => { - if (this.curPage && this.initPage) { + if (this.curPage > 0 && this.thumbnailPage > 0 && this.curPage != this.thumbnailPage) { this.saveThumbnail(); this._interactive = true; - } else { - if (this.curPage > 0) - this.initPage = true; } }, { fireImmediately: true }); @@ -384,6 +382,7 @@ export class PDFBox extends React.Component<FieldViewProps> { { width: me.props.doc.GetNumber(KeyStore.NativeWidth, 0), height: me.props.doc.GetNumber(KeyStore.NativeHeight, 0), quality: 0.5 }) .then(function (dataUrl: string) { me.props.doc.SetData(KeyStore.Thumbnail, new URL(dataUrl), ImageField); + me.props.doc.SetNumber(KeyStore.ThumbnailPage, me.props.doc.GetNumber(KeyStore.CurPage, -1)); }) .catch(function (error: any) { console.error('oops, something went wrong!', error); @@ -473,10 +472,10 @@ export class PDFBox extends React.Component<FieldViewProps> { @computed get imageProxyRenderer() { - let field = this.props.doc.Get(KeyStore.Thumbnail); - if (field) { - let path = field == FieldWaiting ? "https://image.flaticon.com/icons/svg/66/66163.svg" : - field instanceof ImageField ? field.Data.href : "http://cs.brown.edu/people/bcz/prairie.jpg"; + let thumbField = this.props.doc.Get(KeyStore.Thumbnail); + if (thumbField) { + let path = thumbField == FieldWaiting || this.thumbnailPage != this.curPage ? "https://image.flaticon.com/icons/svg/66/66163.svg" : + thumbField instanceof ImageField ? thumbField.Data.href : "http://cs.brown.edu/people/bcz/prairie.jpg"; return <img src={path} width="100%" />; } return (null); diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 09ae95183..7c0db83a8 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -1,23 +1,27 @@ import React = require("react") import { observer } from "mobx-react"; -import { FieldWaiting } from '../../../fields/Field'; +import { FieldWaiting, Opt } from '../../../fields/Field'; import { VideoField } from '../../../fields/VideoField'; import { FieldView, FieldViewProps } from './FieldView'; import "./VideoBox.scss"; import Measure from "react-measure"; -import { action, trace, observable } from "mobx"; +import { action, trace, observable, IReactionDisposer, computed, reaction } from "mobx"; import { KeyStore } from "../../../fields/KeyStore"; import { number } from "prop-types"; @observer export class VideoBox extends React.Component<FieldViewProps> { + private _reactionDisposer: Opt<IReactionDisposer>; + private _videoRef = React.createRef<HTMLVideoElement>() public static LayoutString() { return FieldView.LayoutString(VideoBox) } constructor(props: FieldViewProps) { super(props); } + @computed private get curPage() { return this.props.doc.GetNumber(KeyStore.CurPage, -1); } + _loaded: boolean = false; @@ -39,7 +43,17 @@ export class VideoBox extends React.Component<FieldViewProps> { } } + get player(): HTMLVideoElement | undefined { + return this._videoRef.current ? this._videoRef.current.getElementsByTagName("video")[0] : undefined; + } + @action + setVideoRef = (vref: HTMLVideoElement | null) => { + if (this.curPage >= 0 && vref) { + vref!.currentTime = this.curPage; + (vref! as any).AHackBecauseSomethingResetsTheVideoToZero = this.curPage; + } + } render() { let field = this.props.doc.GetT(this.props.fieldKey, VideoField); @@ -47,15 +61,16 @@ export class VideoBox extends React.Component<FieldViewProps> { return <div>Loading</div> } let path = field.Data.href; - - //setTimeout(action(() => this._loaded = true), 500); + trace(); return ( <Measure onResize={this.setScaling}> {({ measureRef }) => - <video className="videobox-cont" onClick={() => { }} ref={measureRef}> - <source src={path} type="video/mp4" /> - Not supported. + <div style={{ width: "100%", height: "auto" }} ref={measureRef}> + <video className="videobox-cont" onClick={() => { }} ref={this.setVideoRef}> + <source src={path} type="video/mp4" /> + Not supported. </video> + </div> } </Measure> ) diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts index 611e2951b..f9684b212 100644 --- a/src/fields/KeyStore.ts +++ b/src/fields/KeyStore.ts @@ -36,7 +36,9 @@ export namespace KeyStore { export const LinkDescription = new Key("LinkDescription"); export const LinkTags = new Key("LinkTag"); export const Thumbnail = new Key("Thumbnail"); + export const ThumbnailPage = new Key("ThumbnailPage"); export const CurPage = new Key("CurPage"); + export const AnnotationOn = new Key("AnnotationOn"); export const NumPages = new Key("NumPages"); export const Ink = new Key("Ink"); export const Cursors = new Key("Cursors"); |