diff options
Diffstat (limited to 'src/client/views')
| -rw-r--r-- | src/client/views/PreviewCursor.tsx | 55 | ||||
| -rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 12 | ||||
| -rw-r--r-- | src/client/views/nodes/LoadingBox.tsx | 10 | ||||
| -rw-r--r-- | src/client/views/nodes/VideoBox.scss | 20 | ||||
| -rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 212 |
6 files changed, 169 insertions, 142 deletions
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index 68f5f072d..4c17d5a97 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -4,9 +4,9 @@ import 'normalize.css'; import * as React from 'react'; import { Doc } from '../../fields/Doc'; import { Cast, NumCast, StrCast } from '../../fields/Types'; -import { returnFalse } from '../../Utils'; +import { emptyFunction, returnFalse } from '../../Utils'; import { DocServer } from '../DocServer'; -import { Docs, DocUtils } from '../documents/Documents'; +import { Docs, DocumentOptions, DocUtils } from '../documents/Documents'; import { Transform } from '../util/Transform'; import { undoBatch, UndoManager } from '../util/UndoManager'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; @@ -16,15 +16,25 @@ import './PreviewCursor.scss'; export class PreviewCursor extends React.Component<{}> { static _onKeyPress?: (e: KeyboardEvent) => void; static _getTransform: () => Transform; - static _addDocument: (doc: Doc | Doc[]) => void; + static _addDocument: (doc: Doc | Doc[]) => boolean; static _addLiveTextDoc: (doc: Doc) => void; static _nudge?: undefined | ((x: number, y: number) => boolean); + static _slowLoadDocuments?: ( + files: File[] | string, + options: DocumentOptions, + generatedDocuments: Doc[], + text: string, + completed: ((doc: Doc[]) => void) | undefined, + clientX: number, + clientY: number, + addDocument: (doc: Doc | Doc[]) => boolean + ) => Promise<void>; @observable static _clickPoint = [0, 0]; @observable public static Visible = false; constructor(props: any) { super(props); document.addEventListener('keydown', this.onKeyPress); - document.addEventListener('paste', this.paste); + document.addEventListener('paste', this.paste, true); } paste = async (e: ClipboardEvent) => { @@ -38,20 +48,16 @@ export class PreviewCursor extends React.Component<{}> { if (plain) { // tests for youtube and makes video document if (plain.indexOf('www.youtube.com/watch') !== -1) { - const url = plain.replace('youtube.com/watch?v=', 'youtube.com/embed/'); - undoBatch(() => - PreviewCursor._addDocument( - Docs.Create.VideoDocument(url, { - title: url, - _width: 400, - _height: 315, - _nativeWidth: 600, - _nativeHeight: 472.5, - x: newPoint[0], - y: newPoint[1], - }) - ) - )(); + const batch = UndoManager.StartBatch('youtube upload'); + const generatedDocuments: Doc[] = []; + const options = { + title: plain, + _width: 400, + _height: 315, + x: newPoint[0], + y: newPoint[1], + }; + PreviewCursor._slowLoadDocuments?.(plain.split('v=')[1].split('&')[0], options, generatedDocuments, '', undefined, newPoint[0], newPoint[1], PreviewCursor._addDocument).then(batch.end); } else if (re.test(plain)) { const url = plain; undoBatch(() => @@ -184,7 +190,17 @@ export class PreviewCursor extends React.Component<{}> { addLiveText: (doc: Doc) => void, getTransform: () => Transform, addDocument: undefined | ((doc: Doc | Doc[]) => boolean), - nudge: undefined | ((nudgeX: number, nudgeY: number) => boolean) + nudge: undefined | ((nudgeX: number, nudgeY: number) => boolean), + slowLoadDocuments: ( + files: File[] | string, + options: DocumentOptions, + generatedDocuments: Doc[], + text: string, + completed: ((doc: Doc[]) => void) | undefined, + clientX: number, + clientY: number, + addDocument: (doc: Doc | Doc[]) => boolean + ) => Promise<void> ) { this._clickPoint = [x, y]; this._onKeyPress = onKeyPress; @@ -192,6 +208,7 @@ export class PreviewCursor extends React.Component<{}> { this._getTransform = getTransform; this._addDocument = addDocument || returnFalse; this._nudge = nudge; + this._slowLoadDocuments = slowLoadDocuments; this.Visible = true; } render() { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 0c4de681a..947bd8aaa 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -52,7 +52,6 @@ import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCurso import './CollectionFreeFormView.scss'; import { MarqueeView } from './MarqueeView'; import React = require('react'); -import e = require('connect-flash'); export type collectionFreeformViewProps = { annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox) @@ -1858,6 +1857,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection ungroup={this.props.Document._isGroup ? this.promoteCollection : undefined} nudge={this.isAnnotationOverlay || this.props.renderDepth > 0 ? undefined : this.nudge} addDocTab={this.addDocTab} + slowLoadDocuments={this.slowLoadDocuments} trySelectCluster={this.trySelectCluster} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 65a11cbcb..58a00bbac 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -40,6 +40,16 @@ interface MarqueeViewProps { nudge?: (x: number, y: number, nudgeTime?: number) => boolean; ungroup?: () => void; setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void; + slowLoadDocuments: ( + files: File[] | string, + options: DocumentOptions, + generatedDocuments: Doc[], + text: string, + completed: ((doc: Doc[]) => void) | undefined, + clientX: number, + clientY: number, + addDocument: (doc: Doc | Doc[]) => boolean + ) => Promise<void>; } export interface MarqueeViewBounds { @@ -330,7 +340,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque this._downY = y; const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]); if ([AclAdmin, AclEdit, AclAugment].includes(effectiveAcl)) { - PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument, this.props.nudge); + PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument, this.props.nudge, this.props.slowLoadDocuments); } this.clearSelection(); } diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx index 462ad425a..acf728ebb 100644 --- a/src/client/views/nodes/LoadingBox.tsx +++ b/src/client/views/nodes/LoadingBox.tsx @@ -1,12 +1,10 @@ import { observer } from 'mobx-react'; -import { ViewBoxAnnotatableComponent } from '../DocComponent'; -import { FieldView, FieldViewProps } from './FieldView'; import * as React from 'react'; -import './LoadingBox.scss'; import ReactLoading from 'react-loading'; import { StrCast } from '../../../fields/Types'; -import { computed } from 'mobx'; -import { Docs } from '../../documents/Documents'; +import { ViewBoxAnnotatableComponent } from '../DocComponent'; +import { FieldView, FieldViewProps } from './FieldView'; +import './LoadingBox.scss'; /** * LoadingBox Class represents a placeholder doc for documents that are currently @@ -39,7 +37,7 @@ export class LoadingBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { return ( <div className="loadingBoxContainer" style={{ background: this.rootDoc.isLoading ? '' : 'red' }}> <div className="textContainer"> - <p className="headerText">{this.rootDoc.isLoading ? 'Loading:' : StrCast(this.rootDoc.errorMessage, 'Error Loading File:')}</p> + <p className="headerText">{this.rootDoc.isLoading ? 'Loading (can take several minutes):' : StrCast(this.rootDoc.errorMessage, 'Error Loading File:')}</p> <span className="text">{StrCast(this.rootDoc.title)}</span> {!this.rootDoc.isLoading ? null : <ReactLoading type={'spinningBubbles'} color={'blue'} height={100} width={100} />} </div> diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss index aa51714da..c2aee7a1b 100644 --- a/src/client/views/nodes/VideoBox.scss +++ b/src/client/views/nodes/VideoBox.scss @@ -1,4 +1,4 @@ -@import "../global/globalCssVariables.scss"; +@import '../global/globalCssVariables.scss'; .mini-viewer { cursor: grab; @@ -97,7 +97,7 @@ height: 40px; padding: 0 10px 0 7px; transition: opacity 0.3s; - z-index: 100001; + z-index: 10001; .timecode-controls { display: flex; @@ -114,7 +114,8 @@ } } - .toolbar-slider.volume, .toolbar-slider.zoom { + .toolbar-slider.volume, + .toolbar-slider.zoom { width: 50px; } @@ -157,7 +158,8 @@ } } -.videoBox-content-fullScreen, .videoBox-content-fullScreen-interactive { +.videoBox-content-fullScreen, +.videoBox-content-fullScreen-interactive { display: flex; justify-content: center; align-items: flex-end; @@ -175,16 +177,16 @@ video::-webkit-media-controls { display: none !important; } -input[type="range"] { +input[type='range'] { -webkit-appearance: none; background: none; } -input[type="range"]:focus { +input[type='range']:focus { outline: none; } -input[type="range"]::-webkit-slider-runnable-track { +input[type='range']::-webkit-slider-runnable-track { width: 100%; height: 10px; cursor: pointer; @@ -193,7 +195,7 @@ input[type="range"]::-webkit-slider-runnable-track { border-radius: 10px; } -input[type="range"]::-webkit-slider-thumb { +input[type='range']::-webkit-slider-thumb { box-shadow: 0; border: 0; height: 12px; @@ -203,4 +205,4 @@ input[type="range"]::-webkit-slider-thumb { cursor: pointer; -webkit-appearance: none; margin-top: -1px; -}
\ No newline at end of file +} diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 6ff11258d..bfb8c1528 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -935,112 +935,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp ); }; - @computed get UIButtons() { - const bounds = this.props.docViewPath().lastElement().getBounds(); - const width = (bounds?.right || 0) - (bounds?.left || 0); - const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0); - return ( - <> - <div className="videobox-button" title={this._playing ? 'play' : 'pause'} onPointerDown={this.onPlayDown}> - <FontAwesomeIcon icon={this._playing ? 'pause' : 'play'} /> - </div> - - {this.timeline && width > 150 && ( - <div className="timecode-controls"> - <div className="timecode-current">{formatTime(curTime)}</div> - - {this._fullScreen || (this.heightPercent === 100 && width > 200) ? ( - <div className="timeline-slider"> - <input - type="range" - step="0.1" - min={this.timeline.clipStart} - max={this.timeline.clipEnd} - value={curTime} - className="toolbar-slider time-progress" - onPointerDown={action((e: React.PointerEvent) => { - e.stopPropagation(); - this._scrubbing = true; - })} - onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setPlayheadTime(Number(e.target.value))} - onPointerUp={action((e: React.PointerEvent) => { - e.stopPropagation(); - this._scrubbing = false; - })} - /> - </div> - ) : ( - <div>/</div> - )} - - <div className="timecode-end">{formatTime(this.timeline.clipDuration)}</div> - </div> - )} - - <div className="videobox-button" title={'full screen'} onPointerDown={this.onFullDown}> - <FontAwesomeIcon icon="expand" /> - </div> - - {!this._fullScreen && width > 300 && ( - <div className="videobox-button" title={'show timeline'} onPointerDown={this.onTimelineHdlDown}> - <FontAwesomeIcon icon="eye" /> - </div> - )} - - {!this._fullScreen && width > 300 && ( - <div className="videobox-button" title={this.timeline?.IsTrimming !== TrimScope.None ? 'finish trimming' : 'start trim'} onPointerDown={this.onClipPointerDown}> - <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? 'check' : 'cut'} /> - </div> - )} - - <div - className="videobox-button" - title={this._muted ? 'unmute' : 'mute'} - onPointerDown={e => { - e.stopPropagation(); - this.toggleMute(); - }}> - <FontAwesomeIcon icon={this._muted ? 'volume-mute' : 'volume-up'} /> - </div> - {width > 300 && ( - <input - type="range" - style={{ width: `min(25%, 50px)` }} - step="0.1" - min="0" - max="1" - value={this._muted ? 0 : this._volume} - className="toolbar-slider volume" - onPointerDown={(e: React.PointerEvent) => e.stopPropagation()} - onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))} - /> - )} - - {!this._fullScreen && this.heightPercent !== 100 && width > 300 && ( - <> - <div className="videobox-button" title="zoom"> - <FontAwesomeIcon icon="search-plus" /> - </div> - <input - type="range" - step="0.1" - min="1" - max="5" - value={this.timeline?._zoomFactor} - className="toolbar-slider zoom" - onPointerDown={(e: React.PointerEvent) => { - e.stopPropagation(); - }} - onChange={(e: React.ChangeEvent<HTMLInputElement>) => { - this.zoom(Number(e.target.value)); - }} - /> - </> - )} - </> - ); - } - // renders CollectionStackedTimeline @computed get renderTimeline() { return ( @@ -1149,6 +1043,112 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp </div> ); } + + @computed get UIButtons() { + const bounds = this.props.docViewPath().lastElement().getBounds(); + const width = (bounds?.right || 0) - (bounds?.left || 0); + const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0); + return ( + <> + <div className="videobox-button" title={this._playing ? 'play' : 'pause'} onPointerDown={this.onPlayDown}> + <FontAwesomeIcon icon={this._playing ? 'pause' : 'play'} /> + </div> + + {this.timeline && width > 150 && ( + <div className="timecode-controls"> + <div className="timecode-current">{formatTime(curTime)}</div> + + {this._fullScreen || (this.heightPercent === 100 && width > 200) ? ( + <div className="timeline-slider"> + <input + type="range" + step="0.1" + min={this.timeline.clipStart} + max={this.timeline.clipEnd} + value={curTime} + className="toolbar-slider time-progress" + onPointerDown={action((e: React.PointerEvent) => { + e.stopPropagation(); + this._scrubbing = true; + })} + onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setPlayheadTime(Number(e.target.value))} + onPointerUp={action((e: React.PointerEvent) => { + e.stopPropagation(); + this._scrubbing = false; + })} + /> + </div> + ) : ( + <div>/</div> + )} + + <div className="timecode-end">{formatTime(this.timeline.clipDuration)}</div> + </div> + )} + + <div className="videobox-button" title={'full screen'} onPointerDown={this.onFullDown}> + <FontAwesomeIcon icon="expand" /> + </div> + + {!this._fullScreen && width > 300 && ( + <div className="videobox-button" title={'show timeline'} onPointerDown={this.onTimelineHdlDown}> + <FontAwesomeIcon icon="eye" /> + </div> + )} + + {!this._fullScreen && width > 300 && ( + <div className="videobox-button" title={this.timeline?.IsTrimming !== TrimScope.None ? 'finish trimming' : 'start trim'} onPointerDown={this.onClipPointerDown}> + <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? 'check' : 'cut'} /> + </div> + )} + + <div + className="videobox-button" + title={this._muted ? 'unmute' : 'mute'} + onPointerDown={e => { + e.stopPropagation(); + this.toggleMute(); + }}> + <FontAwesomeIcon icon={this._muted ? 'volume-mute' : 'volume-up'} /> + </div> + {width > 300 && ( + <input + type="range" + style={{ width: `min(25%, 50px)` }} + step="0.1" + min="0" + max="1" + value={this._muted ? 0 : this._volume} + className="toolbar-slider volume" + onPointerDown={(e: React.PointerEvent) => e.stopPropagation()} + onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))} + /> + )} + + {!this._fullScreen && this.heightPercent !== 100 && width > 300 && ( + <> + <div className="videobox-button" title="zoom"> + <FontAwesomeIcon icon="search-plus" /> + </div> + <input + type="range" + step="0.1" + min="1" + max="5" + value={this.timeline?._zoomFactor} + className="toolbar-slider zoom" + onPointerDown={(e: React.PointerEvent) => { + e.stopPropagation(); + }} + onChange={(e: React.ChangeEvent<HTMLInputElement>) => { + this.zoom(Number(e.target.value)); + }} + /> + </> + )} + </> + ); + } } VideoBox._nativeControls = false; |
