diff options
-rw-r--r-- | deploy/mobile/image.html | 6 | ||||
-rw-r--r-- | src/.DS_Store | bin | 0 -> 8196 bytes | |||
-rw-r--r-- | src/Utils.ts | 4 | ||||
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 21 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 1 | ||||
-rw-r--r-- | src/client/views/nodes/AudioBox.scss | 11 | ||||
-rw-r--r-- | src/client/views/nodes/AudioBox.tsx | 380 | ||||
-rw-r--r-- | src/client/views/nodes/FieldView.tsx | 7 | ||||
-rw-r--r-- | src/mobile/ImageUpload.tsx | 2 | ||||
-rw-r--r-- | src/mobile/MobileInterface.tsx | 2 | ||||
-rw-r--r-- | src/mobile/MobileMenu.scss | 271 |
11 files changed, 414 insertions, 291 deletions
diff --git a/deploy/mobile/image.html b/deploy/mobile/image.html index d30ad6ac2..cca6f763b 100644 --- a/deploy/mobile/image.html +++ b/deploy/mobile/image.html @@ -3,11 +3,13 @@ <head> <title>Dash Mobile</title> <link href="https://fonts.googleapis.com/css?family=Fjalla+One|Hind+Siliguri:300" rel="stylesheet"> + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" + integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> + <script src="https://cdnjs.cloudflare.com/ajax/libs/typescript/3.3.1/typescript.min.js"></script> </head> <body> - <div id="root"> - </div> + <div id="root"></div> <script src="../mobileInterface.js"></script> </body> diff --git a/src/.DS_Store b/src/.DS_Store Binary files differnew file mode 100644 index 000000000..299b902c6 --- /dev/null +++ b/src/.DS_Store diff --git a/src/Utils.ts b/src/Utils.ts index 0be27b25d..d9a5353e8 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -547,7 +547,7 @@ export function setupMoveUpEvents( target: object, e: React.PointerEvent, moveEvent: (e: PointerEvent, down: number[], delta: number[]) => boolean, - upEvent: (e: PointerEvent) => void, + upEvent: (e: PointerEvent, movement: number[]) => void, clickEvent: (e: PointerEvent, doubleTap?: boolean) => void, stopPropagation: boolean = true, stopMovePropagation: boolean = true @@ -571,7 +571,7 @@ export function setupMoveUpEvents( const _upEvent = (e: PointerEvent): void => { (target as any)._doubleTap = (Date.now() - (target as any)._lastTap < 300); (target as any)._lastTap = Date.now(); - upEvent(e); + upEvent(e, [e.clientX - (target as any)._downX, e.clientY - (target as any)._downY]); if (Math.abs(e.clientX - (target as any)._downX) < 4 && Math.abs(e.clientY - (target as any)._downY) < 4) { clickEvent(e, (target as any)._doubleTap); } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 49377a728..817125752 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -658,8 +658,25 @@ export class CurrentUserUtils { return Cast(userDoc.thumbDoc, Doc); } - // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker. - // when clicked, this panel will be displayed in the target container (ie, sidebarContainer) + static setupMobileInkingDoc(userDoc: Doc) { + return Docs.Create.FreeformDocument([], { title: "Mobile Inking", backgroundColor: "white" }); + } + + static setupMobileUploadDoc(userDoc: Doc) { + // const addButton = Docs.Create.FontIconDocument({ onDragStart: ScriptField.MakeScript('addWebToMobileUpload()'), title: "Add Web Doc to Upload Collection", icon: "plus", backgroundColor: "black" }) + const webDoc = Docs.Create.WebDocument("https://www.britannica.com/biography/Miles-Davis", { + title: "Upload Images From the Web", _chromeStatus: "enabled", lockedPosition: true + }); + const uploadDoc = Docs.Create.StackingDocument([], { + title: "Mobile Upload Collection", backgroundColor: "white", lockedPosition: true + }); + return Docs.Create.StackingDocument([webDoc, uploadDoc], { + _width: screen.width, lockedPosition: true, _chromeStatus: "disabled", title: "Upload", _autoHeight: true, _yMargin: 80, backgroundColor: "lightgray" + }); + } + + // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker. + // when clicked, this panel will be displayed in the target container (ie, sidebarContainer) static async setupToolsBtnPanel(doc: Doc) { // setup a masonry view of all he creators const creatorBtns = await CurrentUserUtils.setupCreatorButtons(doc); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ef4b7b9d2..509d7cda8 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -605,6 +605,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P pan = (e: PointerEvent | React.Touch | { clientX: number, clientY: number }): void => { // bcz: theres should be a better way of doing these than referencing these static instances directly MarqueeOptionsMenu.Instance?.fadeOut(true);// I think it makes sense for the marquee menu to go away when panned. -syip2 + // PDFMenu.Instance.fadeOut(true); (commented out for mobile) const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY); this.setPan((this.Document._panX || 0) - dx, (this.Document._panY || 0) - dy, undefined, true); diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss index c0743933e..0d787d9af 100644 --- a/src/client/views/nodes/AudioBox.scss +++ b/src/client/views/nodes/AudioBox.scss @@ -179,7 +179,16 @@ height: 100%; overflow: hidden; z-index: -1000; - bottom: -30%; + bottom: 0; + pointer-events: none; + div { + height: 100% !important; + width: 100% !important; + } + canvas { + height: 100% !important; + width: 100% !important; + } } .audiobox-linker, diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 4805b8643..bc89cb6f9 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -31,9 +31,7 @@ declare class MediaRecorder { // whatever MediaRecorder has constructor(e: any); } -export const audioSchema = createSchema({ - playOnSelect: "boolean" -}); +export const audioSchema = createSchema({ playOnSelect: "boolean" }); type AudioDocument = makeInterface<[typeof documentSchema, typeof audioSchema]>; const AudioDocument = makeInterface(documentSchema, audioSchema); @@ -64,24 +62,24 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD _dragging = false; _count: Array<any> = []; + _audioRef = React.createRef<HTMLDivElement>(); _timeline: Opt<HTMLDivElement>; _duration = 0; - _containerX: number = 0; - _invertedX: boolean = false; - private _isPointerDown = false; + _markerStart: number = 0; private _currMarker: any; @observable _visible: boolean = false; - @observable _currX: number = 0; + @observable _markerEnd: number = 0; @observable _position: number = 0; @observable _buckets: Array<number> = new Array<number>(); - @observable _waveHeight: number | undefined = this.layoutDoc._height; + @observable _waveHeight: Opt<number> = this.layoutDoc._height; @observable private _paused: boolean = false; @observable private static _scrubTime = 0; @computed get audioState(): undefined | "recording" | "paused" | "playing" { return this.dataDoc.audioState as (undefined | "recording" | "paused" | "playing"); } set audioState(value) { this.dataDoc.audioState = value; } - public static SetScrubTime = (timeInMillisFrom1970: number) => { runInAction(() => AudioBox._scrubTime = 0); runInAction(() => AudioBox._scrubTime = timeInMillisFrom1970); }; + public static SetScrubTime = action((timeInMillisFrom1970: number) => { AudioBox._scrubTime = 0; AudioBox._scrubTime = timeInMillisFrom1970; }); @computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); } + @computed get audioDuration() { return NumCast(this.dataDoc.duration); } async slideTemplate() { return (await Cast((await Cast(Doc.UserDoc().slidesBtn, Doc) as Doc).dragFactory, Doc) as Doc); } constructor(props: Readonly<FieldViewProps>) { @@ -137,21 +135,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD la2 = l.anchor1 as Doc; linkTime = NumCast(l.anchor1_timecode); } - if (la2.audioStart) { - linkTime = NumCast(la2.audioStart); - } - - if (la1.audioStart) { - linkTime = NumCast(la1.audioStart); - } + if (la2.audioStart) linkTime = NumCast(la2.audioStart); + if (la1.audioStart) linkTime = NumCast(la1.audioStart); - if (la1.audioEnd) { - endTime = NumCast(la1.audioEnd); - } - - if (la2.audioEnd) { - endTime = NumCast(la2.audioEnd); - } + if (la1.audioEnd) endTime = NumCast(la1.audioEnd); + if (la2.audioEnd) endTime = NumCast(la2.audioEnd); if (linkTime) { link = true; @@ -203,7 +191,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD // play back the audio from time @action - playFrom = (seekTimeInSeconds: number, endTime: number = this.dataDoc.duration) => { + playFrom = (seekTimeInSeconds: number, endTime: number = this.audioDuration) => { let play; clearTimeout(play); this._duration = endTime - seekTimeInSeconds; @@ -218,7 +206,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD this._ele.currentTime = seekTimeInSeconds; this._ele.play(); runInAction(() => this.audioState = "playing"); - if (endTime !== this.dataDoc.duration) { + if (endTime !== this.audioDuration) { play = setTimeout(() => this.pause(), (this._duration) * 1000); // use setTimeout to play a specific duration } } else { @@ -230,11 +218,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD // update the recording time updateRecordTime = () => { if (this.audioState === "recording") { + setTimeout(this.updateRecordTime, 30); if (this._paused) { - setTimeout(this.updateRecordTime, 30); this._pausedTime += (new Date().getTime() - this._recordStart) / 1000; } else { - setTimeout(this.updateRecordTime, 30); this.layoutDoc.currentTimecode = (new Date().getTime() - this._recordStart - this.pauseTime) / 1000; } } @@ -353,74 +340,55 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD // return the total time paused to update the correct time @computed get pauseTime() { - return (this._pauseEnd - this._pauseStart); + return this._pauseEnd - this._pauseStart; } // starting the drag event for marker resizing @action onPointerDownTimeline = (e: React.PointerEvent): void => { - e.stopPropagation(); - e.preventDefault(); - this._isPointerDown = true; - this._timeline?.setPointerCapture(e.pointerId); - - this.start(this._ele!.currentTime); - const rect = (e.target as any).getBoundingClientRect(); - this._containerX = this._currX = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration); - - document.removeEventListener("pointermove", this.onPointerMoveTimeline); - document.addEventListener("pointermove", this.onPointerMoveTimeline); - document.removeEventListener("pointerup", this.onPointerUpTimeline); - document.addEventListener("pointerup", this.onPointerUpTimeline); + const toTimeline = (screen_delta: number) => screen_delta / rect.width * this.audioDuration; + this._markerStart = this._markerEnd = toTimeline(e.clientX - rect.x); + setupMoveUpEvents(this, e, action((e: PointerEvent) => { + this._visible = true; + this._markerEnd = toTimeline(e.clientX - rect.x); + if (this._markerEnd < this._markerStart) { + const tmp = this._markerStart; + this._markerStart = this._markerEnd; + this._markerEnd = tmp; + } + return false; + }), + action((e: PointerEvent, movement: number[]) => { + if (Math.abs(movement[0]) > 15) { + this.createNewMarker(this._markerStart, toTimeline(e.clientX - rect.x)); + } + this._visible = false; + }), + emptyFunction); } - - // ending the drag event for marker resizing - @action - onPointerUpTimeline = (e: PointerEvent): void => { - e.stopPropagation(); - e.preventDefault(); - this._isPointerDown = false; - - const rect = (e.target as any).getBoundingClientRect(); - const time = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration); - - // if drag is greater than 15px (didn't use setupMoveEvent) - (this._visible && Math.abs(this._currX - this._containerX) * rect.width / NumCast(this.dataDoc.duration) > 15) ? this.end(time) : this._start = 0; - this._visible = false; - - this._containerX = 0; - this._timeline?.releasePointerCapture(e.pointerId); - - document.removeEventListener("pointermove", this.onPointerMoveTimeline); - document.removeEventListener("pointerup", this.onPointerUpTimeline); + // returns the selection container + @computed get container() { + return <div className="audiobox-container" style={{ + left: `${NumCast(this._markerStart) / this.audioDuration * 100}%`, + width: `${Math.abs(this._markerStart - this._markerEnd) / this.audioDuration * 100}%`, height: "100%", top: "0%" + }}></div>; } - // resizes the marker while dragging + // creates a new marker @action - onPointerMoveTimeline = (e: PointerEvent) => { - e.stopPropagation(); - e.preventDefault(); - - if (!this._isPointerDown) { - return; - } - this._visible = true; - const rect = (e.target as any).getBoundingClientRect(); - - this._currX = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration); - - (this._currX - this._containerX < 0) ? this._invertedX = true : this._invertedX = false; - } - - // returns the selection container - @computed get container() { - return <div className="audiobox-container" style={{ left: !this._invertedX ? `${NumCast(this._containerX) / NumCast(this.dataDoc.duration, 1) * 100}%` : `${this._currX / NumCast(this.dataDoc.duration, 1) * 100}%`, width: `${Math.abs(this._containerX - this._currX) / NumCast(this.dataDoc.duration, 1) * 100}%`, height: "100%", top: "0%" }}></div> + createNewMarker(audioStart: number, audioEnd: number) { + const newMarker = Docs.Create.LabelDocument({ + title: ComputedField.MakeFunction(`formatToTime(self.audioStart) + "-" + formatToTime(self.audioEnd)`) as any, isLabel: false, + useLinkSmallAnchor: true, hideLinkButton: true, audioStart, audioEnd, _showSidebar: false, + _autoHeight: true, annotationOn: this.props.Document + }); + this.addMark(newMarker); } - // creates a new label + // adds an annotation marker or label @action - newMarker(marker: Doc) { + addMark(marker: Doc) { marker.data = ""; if (this.dataDoc[this.annotationKey]) { this.dataDoc[this.annotationKey].push(marker); @@ -429,104 +397,39 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD } } - // the starting time of the marker - start(startingPoint: number) { - this._hold = true; - this._start = startingPoint; - } - - // creates a new marker - @action - end(marker: number) { - this._hold = false; - const newMarker = this._invertedX ? - Docs.Create.LabelDocument({ title: ComputedField.MakeFunction(`formatToTime(self.audioStart) + "-" + formatToTime(self.audioEnd)`) as any, isLabel: false, useLinkSmallAnchor: true, hideLinkButton: true, audioStart: marker, audioEnd: this._start, _showSidebar: false, _autoHeight: true, annotationOn: this.props.Document }) - : - Docs.Create.LabelDocument({ title: ComputedField.MakeFunction(`formatToTime(self.audioStart) + "-" + formatToTime(self.audioEnd)`) as any, isLabel: false, useLinkSmallAnchor: true, hideLinkButton: true, audioStart: this._start, audioEnd: marker, _showSidebar: false, _autoHeight: true, annotationOn: this.props.Document }); - newMarker.data = ""; - if (this.dataDoc[this.annotationKey]) { - this.dataDoc[this.annotationKey].push(newMarker); - } else { - this.dataDoc[this.annotationKey] = new List<Doc>([newMarker]); - } - - this._start = 0; - } - // starting the drag event for marker resizing onPointerDown = (e: React.PointerEvent, m: any, left: boolean): void => { - e.stopPropagation(); - e.preventDefault(); - this._isPointerDown = true; this._currMarker = m; - this._timeline?.setPointerCapture(e.pointerId); this._left = left; - - document.removeEventListener("pointermove", this.onPointerMove); - document.addEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointerup", this.onPointerUp); - } - - // ending the drag event for marker resizing - @action - onPointerUp = (e: PointerEvent): void => { - e.stopPropagation(); - e.preventDefault(); - this._isPointerDown = false; - this._dragging = false; - const rect = (e.target as any).getBoundingClientRect(); - this._ele!.currentTime = this.layoutDoc.currentTimecode = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration); - - this._timeline?.releasePointerCapture(e.pointerId); - - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - } - - // resizes the marker while dragging - onPointerMove = async (e: PointerEvent) => { - e.stopPropagation(); - e.preventDefault(); - - if (!this._isPointerDown) { - return; - } - - const rect = await (e.target as any).getBoundingClientRect(); - - const newTime = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration); - - this.changeMarker(this._currMarker, newTime); + const toTimeline = (screen_delta: number) => screen_delta / rect.width * this.audioDuration; + setupMoveUpEvents(this, e, () => { + this.changeMarker(this._currMarker, toTimeline(e.clientX - rect.x)); + return false; + }, + () => this._ele!.currentTime = this.layoutDoc.currentTimecode = toTimeline(e.clientX - rect.x), + emptyFunction); } // updates the marker with the new time @action changeMarker = (m: any, time: any) => { - DocListCast(this.dataDoc[this.annotationKey]).forEach((marker: Doc) => { - if (this.isSame(marker, m)) { - this._left ? marker.audioStart = time : marker.audioEnd = time; - } - }); + DocListCast(this.dataDoc[this.annotationKey]).filter(marker => this.isSame(marker, m)).forEach(marker => + this._left ? marker.audioStart = time : marker.audioEnd = time); } // checks if the two markers are the same with start and end time isSame = (m1: any, m2: any) => { - if (m1.audioStart === m2.audioStart && m1.audioEnd === m2.audioEnd) { - return true; - } - return false; + return m1.audioStart === m2.audioStart && m1.audioEnd === m2.audioEnd; } // instantiates a new array of size 500 for marker layout markers = () => { - const increment = NumCast(this.layoutDoc.duration) / 500; + const increment = this.audioDuration / 500; this._count = []; for (let i = 0; i < 500; i++) { this._count.push([increment * i, 0]); } - } // makes sure no markers overlaps each other by setting the correct position and width @@ -551,7 +454,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD if (this._count[i][0] >= m.audioStart && this._count[i][0] <= m.audioEnd) { this._count[i][1] = max; } - } if (this.dataDoc.markerAmount < max) { @@ -567,8 +469,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD height={this._waveHeight} barWidth={0.1} // pos={this.layoutDoc.currentTimecode} need to correctly resize parent to make this work (not very necessary for function) - pos={this.dataDoc.duration} - duration={this.dataDoc.duration} + pos={this.audioDuration} + duration={this.audioDuration} peaks={this._buckets.length === 100 ? this._buckets : undefined} progressColor={"blue"} />; } @@ -609,74 +511,31 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD return this.buckets(); } - // for updating the width and height of the waveform with timeline ref - timelineRef = (timeline: HTMLDivElement) => { - const observer = new _global.ResizeObserver(action((entries: any) => { - for (const entry of entries) { - this.update(entry.contentRect.width, entry.contentRect.height); - this._position = entry.contentRect.width; - } - })); - timeline && observer.observe(timeline); - - this._timeline = timeline; - } - - // update the width and height of the audio waveform - @action - update = (width: number, height: number) => { - if (height) { - const height = 0.8 * NumCast(this.layoutDoc._height); - let canvas2 = document.getElementsByTagName("canvas")[0]; - if (canvas2) { - let oldWidth = canvas2.width; - let oldHeight = canvas2.height; - canvas2.style.height = `${height}`; - canvas2.style.width = `${width}`; - - const ratio1 = oldWidth / window.innerWidth; - const ratio2 = oldHeight / window.innerHeight; - const context = canvas2.getContext('2d'); - if (context) { - context.scale(ratio1, ratio2); - } - } - - const canvas1 = document.getElementsByTagName("canvas")[1]; - if (canvas1) { - const oldWidth = canvas1.width; - const oldHeight = canvas1.height; - canvas1.style.height = `${height}`; - canvas1.style.width = `${width}`; - - const ratio1 = oldWidth / window.innerWidth; - const ratio2 = oldHeight / window.innerHeight; - const context = canvas1.getContext('2d'); - if (context) { - context.scale(ratio1, ratio2); - } - - const parent = canvas1.parentElement; - if (parent) { - parent.style.width = `${width}`; - parent.style.height = `${height}`; - } - } - } - } - rangeScript = () => AudioBox.RangeScript; - labelScript = () => AudioBox.LabelScript; - // for indicating the first marker that is rendered - reset = () => this._first = true; - render() { const interactive = this.active() ? "-interactive" : ""; - this.reset(); + this._first = true; // for indicating the first marker that is rendered this.path && this._buckets.length !== 100 ? this.peaks : null; // render waveform if audio is done recording - return <div className={`audiobox-container`} onContextMenu={this.specificContextMenu} onClick={!this.path ? this.recordClick : undefined}> + const markerDoc = (mark: Doc, script: undefined | (() => ScriptField)) => { + return <DocumentView {...this.props} + Document={mark} + pointerEvents={true} + NativeHeight={returnZero} + NativeWidth={returnZero} + rootSelected={returnFalse} + LayoutTemplate={undefined} + ContainingCollectionDoc={this.props.Document} + removeDocument={this.removeDocument} + parentActive={returnTrue} + onClick={this.layoutDoc.playOnClick ? script : undefined} + ignoreAutoHeight={false} + bringToFront={emptyFunction} + scriptContext={this} />; + }; + return <div className={`audiobox-container`} onContextMenu={this.specificContextMenu} onClick={!this.path ? this.recordClick : undefined} + style={{ pointerEvents: !interactive ? "none" : undefined }}> {!this.path ? <div className="audiobox-buttons"> <div className="audiobox-dictation" onClick={this.onFile}> @@ -701,88 +560,51 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD <div className="audiobox-dictation"></div> <div className="audiobox-player" > <div className="audiobox-playhead" title={this.audioState === "paused" ? "play" : "pause"} onClick={this.onPlay}> <FontAwesomeIcon style={{ width: "100%", position: "absolute", left: "0px", top: "5px", borderWidth: "thin", borderColor: "white" }} icon={this.audioState === "paused" ? "play" : "pause"} size={"1x"} /></div> - <div className="audiobox-timeline" ref={this.timelineRef} onClick={e => { e.stopPropagation(); e.preventDefault(); }} + <div className="audiobox-timeline" onClick={e => { e.stopPropagation(); e.preventDefault(); }} onPointerDown={e => { e.stopPropagation(); e.preventDefault(); if (e.button === 0 && !e.ctrlKey) { const rect = (e.target as any).getBoundingClientRect(); - - if (e.target as HTMLElement !== document.getElementById("current")) { + if (e.target !== this._audioRef.current) { const wasPaused = this.audioState === "paused"; - this._ele!.currentTime = this.layoutDoc.currentTimecode = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration); + this._ele!.currentTime = this.layoutDoc.currentTimecode = (e.clientX - rect.x) / rect.width * this.audioDuration; wasPaused && this.pause(); } this.onPointerDownTimeline(e); } if (e.button === 0 && e.shiftKey) { - this.newMarker(Docs.Create.LabelDocument({ title: ComputedField.MakeFunction(`formatToTime(self.audioStart)`) as any, useLinkSmallAnchor: true, hideLinkButton: true, isLabel: true, audioStart: this._ele!.currentTime, _showSidebar: false, _autoHeight: true, annotationOn: this.props.Document })); + this.addMark(Docs.Create.LabelDocument({ title: ComputedField.MakeFunction(`formatToTime(self.audioStart)`) as any, useLinkSmallAnchor: true, hideLinkButton: true, isLabel: true, audioStart: this._ele!.currentTime, _showSidebar: false, _autoHeight: true, annotationOn: this.props.Document })); } - - // if (e.button === 0 && e.shiftKey) { - // const rect = (e.target as any).getBoundingClientRect(); - // this._ele!.currentTime = this.layoutDoc.currentTimecode = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration); - // this._hold ? this.end(this._ele!.currentTime) : this.start(this._ele!.currentTime); - // } }}> - <div className="waveform" id="waveform" style={{ height: `${100}%`, width: "100%", bottom: "0px", pointerEvents: "none" }}> + <div className="waveform"> {this.waveform} </div> - {DocListCast(this.dataDoc[this.annotationKey]).map((m, i) => { - let rect; + {DocListCast(this.dataDoc[this.annotationKey]).map((m, i) => (!m.isLabel) ? (this.layoutDoc.hideMarkers) ? (null) : - rect = - <div key={i} id={"audiobox-marker-container1"} className={this.props.PanelHeight() < 32 ? "audiobox-marker-minicontainer" : "audiobox-marker-container1"} + <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}container1`} key={i} title={`${formatTime(Math.round(NumCast(m.audioStart)))}` + " - " + `${formatTime(Math.round(NumCast(m.audioEnd)))}`} style={{ - left: `${NumCast(m.audioStart) / NumCast(this.dataDoc.duration, 1) * 100}%`, + left: `${NumCast(m.audioStart) / this.audioDuration * 100}%`, top: `${this.isOverlap(m) * 1 / (this.dataDoc.markerAmount + 1) * 100}%`, - width: `${(NumCast(m.audioEnd) - NumCast(m.audioStart)) / NumCast(this.dataDoc.duration, 1) * 100}%`, height: `${1 / (this.dataDoc.markerAmount + 1) * 100}%` + width: `${(NumCast(m.audioEnd) - NumCast(m.audioStart)) / this.audioDuration * 100}%`, height: `${1 / (this.dataDoc.markerAmount + 1) * 100}%` }} onClick={e => { this.playFrom(NumCast(m.audioStart), NumCast(m.audioEnd)); e.stopPropagation(); }} > <div className="left-resizer" onPointerDown={e => this.onPointerDown(e, m, true)}></div> - <DocumentView {...this.props} - Document={m} - pointerEvents={true} - NativeHeight={returnZero} - NativeWidth={returnZero} - rootSelected={returnFalse} - LayoutTemplate={undefined} - ContainingCollectionDoc={this.props.Document} - removeDocument={this.removeDocument} - parentActive={returnTrue} - onClick={this.layoutDoc.playOnClick ? this.rangeScript : undefined} - ignoreAutoHeight={false} - bringToFront={emptyFunction} - scriptContext={this} /> + {markerDoc(m, this.rangeScript)} <div className="resizer" onPointerDown={e => this.onPointerDown(e, m, false)}></div> </div> : (this.layoutDoc.hideLabels) ? (null) : - rect = - <div className={this.props.PanelHeight() < 32 ? "audiobox-marker-minicontainer" : "audiobox-marker-container"} key={i} style={{ left: `${NumCast(m.audioStart) / NumCast(this.dataDoc.duration, 1) * 100}%` }}> - <DocumentView {...this.props} - Document={m} - pointerEvents={true} - NativeHeight={returnZero} - NativeWidth={returnZero} - rootSelected={returnFalse} - LayoutTemplate={undefined} - ContainingCollectionDoc={this.props.Document} - removeDocument={this.removeDocument} - parentActive={returnTrue} - onClick={this.layoutDoc.playOnClick ? this.labelScript : undefined} - ignoreAutoHeight={false} - bringToFront={emptyFunction} - scriptContext={this} /> - </div>; - return rect; - })} + <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}container`} key={i} + style={{ left: `${NumCast(m.audioStart) / this.audioDuration * 100}%` }}> + {markerDoc(m, this.labelScript)} + </div> + )} {DocListCast(this.dataDoc.links).map((l, i) => { - let la1 = l.anchor1 as Doc; let la2 = l.anchor2 as Doc; let linkTime = NumCast(l.anchor2_timecode); @@ -797,7 +619,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD } return !linkTime ? (null) : - <div className={this.props.PanelHeight() < 32 ? "audiobox-marker-minicontainer" : "audiobox-marker-container"} key={l[Id]} style={{ left: `${linkTime / NumCast(this.dataDoc.duration, 1) * 100}%` }} onClick={e => e.stopPropagation()}> + <div className={`audiobox-marker-${this.props.PanelHeight() < 32 ? "mini" : ""}container`} key={l[Id]} style={{ left: `${linkTime / this.audioDuration * 100}%` }} onClick={e => e.stopPropagation()}> <DocumentView {...this.props} Document={l} NativeHeight={returnZero} @@ -814,19 +636,19 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(l, la2)}`)} /> <div key={i} className={`audiobox-marker`} onPointerEnter={() => Doc.linkFollowHighlight(la1)} - onPointerDown={e => { if (e.button === 0 && !e.ctrlKey) { const wasPaused = this.audioState === "paused"; this.playFrom(linkTime); e.stopPropagation(); e.preventDefault(); } }} /> + onPointerDown={e => { if (e.button === 0 && !e.ctrlKey) { this.playFrom(linkTime); e.stopPropagation(); e.preventDefault(); } }} /> </div>; })} {this._visible ? this.container : null} - <div className="audiobox-current" id="current" onClick={e => { e.stopPropagation(); e.preventDefault(); }} style={{ left: `${NumCast(this.layoutDoc.currentTimecode) / NumCast(this.dataDoc.duration, 1) * 100}%`, pointerEvents: "none" }} /> + <div className="audiobox-current" ref={this._audioRef} onClick={e => { e.stopPropagation(); e.preventDefault(); }} style={{ left: `${NumCast(this.layoutDoc.currentTimecode) / this.audioDuration * 100}%`, pointerEvents: "none" }} /> {this.audio} </div> <div className="current-time"> {formatTime(Math.round(NumCast(this.layoutDoc.currentTimecode)))} </div> <div className="total-time"> - {formatTime(Math.round(NumCast(this.dataDoc.duration)))} + {formatTime(Math.round(this.audioDuration))} </div> </div> </div> diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index e631ad5fe..dd70fb1db 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -102,9 +102,10 @@ export class FieldView extends React.Component<FieldViewProps> { else if (field instanceof VideoField) { return <VideoBox {...this.props} />; } - else if (field instanceof AudioField) { - return <AudioBox {...this.props} />; - } else if (field instanceof DateField) { + // else if (field instanceof AudioField) { + // return <AudioBox {...this.props} />; + //} + else if (field instanceof DateField) { return <p>{field.date.toLocaleString()}</p>; } else if (field instanceof Doc) { diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index c6420bab5..d21d326f6 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -177,4 +177,4 @@ export class Uploader extends React.Component<ImageUploadProps> { ); } -} +}
\ No newline at end of file diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index 40aa65372..02993fdcb 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -681,4 +681,4 @@ Scripting.addGlobal(function openMobileSettings() { return SettingsManager.Insta // Other global functions for mobile Scripting.addGlobal(function switchMobileView(doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) { return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch); }, - "changes the active document displayed on the Dash Mobile", "(doc: any)"); + "changes the active document displayed on the Dash Mobile", "(doc: any)");
\ No newline at end of file diff --git a/src/mobile/MobileMenu.scss b/src/mobile/MobileMenu.scss index e69de29bb..7f286efc4 100644 --- a/src/mobile/MobileMenu.scss +++ b/src/mobile/MobileMenu.scss @@ -0,0 +1,271 @@ +$navbar-height: 120px; +$pathbar-height: 50px; + +* { + margin: 0px; + padding: 0px; + box-sizing: border-box; + font-family: "Open Sans"; +} + +body { + overflow: hidden; +} + +.navbar { + position: fixed; + top: 0px; + left: 0px; + width: 100vw; + height: $navbar-height; + background-color: whitesmoke; + border-bottom: 5px solid black; +} + +.navbar .toggle-btn { + position: absolute; + right: 20px; + top: 30px; + height: 70px; + width: 70px; + transition: all 300ms ease-in-out 200ms; +} + +.navbar .header { + position: absolute; + top: 50%; + top: calc(9px + 50%); + right: 50%; + transform: translate(50%, -50%); + font-size: 40; + user-select: none; + text-transform: uppercase; + font-family: Arial, Helvetica, sans-serif; +} + +.navbar .toggle-btn span { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 70%; + height: 4px; + background: black; + transition: all 200ms ease; +} + +.navbar .toggle-btn span:nth-child(1) { + transition: top 200ms ease-in-out; + top: 30%; +} + +.navbar .toggle-btn span:nth-child(3) { + transition: top 200ms ease-in-out; + top: 70%; +} + +.navbar .toggle-btn.active { + transition: transform 200ms ease-in-out 200ms; + transform: rotate(135deg); +} + +.navbar .toggle-btn.active span:nth-child(1) { + top: 50%; +} + +.navbar .toggle-btn.active span:nth-child(2) { + transform: translate(-50%, -50%) rotate(90deg); +} + +.navbar .toggle-btn.active span:nth-child(3) { + top: 50%; +} +// .navbar .home { +// position: relative; +// right: 5px; +// transform: translate(50%, -50%); +// font-size: 40; +// user-select: none; +// text-transform: uppercase; +// font-family: Arial, Helvetica, sans-serif; +// z-index: 200; +// } + +.sidebar { + position: absolute; + top: 200px; + opacity: 0; + right: -100%; + width: 100%; + height: calc(100% - (200px)); + z-index: 5; + background-color: whitesmoke; + transition: all 400ms ease 50ms; + padding: 20px; + // overflow-y: auto; + // -webkit-overflow-scrolling: touch; + + // border-right: 5px solid black; +} + +.sidebar .item { + width: 100%; + padding: 13px 12px; + border-bottom: 1px solid rgba(200, 200, 200, 0.7); + font-family: Arial, Helvetica, sans-serif; + font-style: normal; + font-weight: normal; + user-select: none; + font-size: 35px; + text-transform: uppercase; + color: black; + +} + +.sidebar .ink { + width: 100%; + padding: 13px 12px; + border-bottom: 1px solid rgba(200, 200, 200, 0.7); + font-family: Arial, Helvetica, sans-serif; + font-style: normal; + font-weight: normal; + user-select: none; + font-size: 35px; + text-transform: uppercase; + color: black; +} + +.sidebar .ink:focus { + outline: 1px solid blue; +} + +.sidebar .home { + position: absolute; + top: -135px; + right: calc(50% + 80px); + transform: translate(0%, -50%); + font-size: 40; + user-select: none; + text-transform: uppercase; + font-family: Arial, Helvetica, sans-serif; + z-index: 200; +} + +.type { + display: inline; + text-transform: lowercase; + margin-left: 20px; + font-size: 35px; + font-style: italic; + color: rgb(28, 28, 28); +} + +.right { + margin-left: 20px; + z-index: 200; +} + +.left { + width: 100%; + height: 100%; +} + +.sidebar .logout { + width: 100%; + padding: 13px 12px; + border-bottom: 1px solid rgba(200, 200, 200, 0.7); + font-family: Arial, Helvetica, sans-serif; + font-style: normal; + font-weight: normal; + user-select: none; + font-size: 30px; + text-transform: uppercase; + color: black; +} + +.sidebar .settings { + width: 100%; + padding: 13px 12px; + border-bottom: 1px solid rgba(200, 200, 200, 0.7); + font-family: Arial, Helvetica, sans-serif; + font-style: normal; + font-weight: normal; + user-select: none; + font-size: 30px; + text-transform: uppercase; + color: black; +} + + +.sidebar.active { + right: 0%; + opacity: 1; +} + +.back { + position: absolute; + top: -140px; + left: 50px; + transform: translate(0%, -50%); + color: black; + font-size: 60; + user-select: none; + text-transform: uppercase; + z-index: 100; + font-family: Arial, Helvetica, sans-serif; +} + + +.pathbar { + position: absolute; + top: 118px; + background: #1a1a1a; + z-index: 20; + border-radius: 0px; + width: 100%; + height: 80px; + transition: all 400ms ease 50ms; +} + +.pathname { + position: relative; + font-size: 25; + top: 50%; + width: 90%; + left: 3%; + color: whitesmoke; + transform: translate(0%, -50%); + z-index: 20; + font-family: sans-serif; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + direction: rtl; + text-align: left; + text-transform: uppercase; +} + +.homeContainer { + position: relative; + top: 200px; + height: calc(100% - 250px); + width: 90%; + overflow: scroll; + left: 5%; + background-color: lightpink; +} + +.pinButton { + position: relative; + width: 100px; + height: 100px; + font-size: 90px; + text-align: center; + left: 50%; + transform: translate(-50%, 0); + border-style: solid; + border-radius: 50px; + border-width: medium; + background-color: pink; + z-index: 100; +}
\ No newline at end of file |