From bc59ea805f32568f0835bd55d39575236c24a066 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Thu, 21 Mar 2019 22:22:25 -0400
Subject: added very basic cycle detection when adding to collections

---
 src/client/views/collections/CollectionVideoView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src/client/views/collections/CollectionVideoView.tsx')

diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx
index 05f759967..a3921696f 100644
--- a/src/client/views/collections/CollectionVideoView.tsx
+++ b/src/client/views/collections/CollectionVideoView.tsx
@@ -44,7 +44,7 @@ export class CollectionVideoView extends React.Component<CollectionViewProps> {
     public SelectedDocs: FieldId[] = []
     public active: () => boolean = () => CollectionView.Active(this);
 
-    addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); }
+    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 => {
-- 
cgit v1.2.3-70-g09d2


From 2264fb874a09ee01540141986325c76650f32c21 Mon Sep 17 00:00:00 2001
From: Bob Zeleznik <zzzman@gmail.com>
Date: Mon, 25 Mar 2019 23:20:22 -0400
Subject: added 1-level navigation to region annotations

---
 .../views/collections/CollectionDockingView.tsx    |   2 +-
 .../views/collections/CollectionVideoView.tsx      | 108 ++++++++++++---------
 src/client/views/collections/CollectionView.tsx    |   6 +-
 src/client/views/nodes/LinkBox.tsx                 |  21 +++-
 src/client/views/nodes/PDFBox.tsx                  |  21 ++--
 src/client/views/nodes/VideoBox.tsx                |  29 ++++--
 src/fields/KeyStore.ts                             |   2 +
 7 files changed, 120 insertions(+), 69 deletions(-)

(limited to 'src/client/views/collections/CollectionVideoView.tsx')

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");
-- 
cgit v1.2.3-70-g09d2

