aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2021-02-15 15:01:06 -0500
committerbobzel <zzzman@gmail.com>2021-02-15 15:01:06 -0500
commitbd5ca49636c5ff0d480003f9479cf40a562b4589 (patch)
tree8b1295e153af765970f3adb4cc6bb4b8f30ae8b6 /src/client/views/nodes
parentddf37d997ded0faa87c844b02f4df1b98989fd2e (diff)
cleaned up showTitle and showAudio stuff just a bit.
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/DocumentView.scss19
-rw-r--r--src/client/views/nodes/DocumentView.tsx92
-rw-r--r--src/client/views/nodes/ImageBox.scss18
-rw-r--r--src/client/views/nodes/ImageBox.tsx83
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx4
5 files changed, 106 insertions, 110 deletions
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 6f041e5ef..749ffa7fd 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -45,6 +45,25 @@
width:10px !important;
}
}
+
+ .documentView-audioBackground {
+ display: inline-block;
+ width: 10%;
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ border-radius: 25px;
+ background: white;
+ opacity: 0.3;
+
+ svg {
+ width: 90% !important;
+ height: 70%;
+ position: absolute;
+ left: 5%;
+ top: 15%;
+ }
+ }
.documentView-treeView {
max-height: 1.5em;
text-overflow: ellipsis;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7dea3784e..9c844c51e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,18 +1,22 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, Opt, StrListCast } from "../../../fields/Doc";
import { Document } from '../../../fields/documentSchemas';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
+import { List } from "../../../fields/List";
import { listSpec } from "../../../fields/Schema";
import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
+import { AudioField } from "../../../fields/URLField";
import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
import { MobileInterface } from '../../../mobile/MobileInterface';
import { emptyFunction, hasDescendantTarget, OmitKeys, returnFalse, returnVal, Utils } from "../../../Utils";
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { Docs, DocUtils } from "../../documents/Documents";
import { DocumentType } from '../../documents/DocumentTypes';
+import { Networking } from "../../Network";
import { CurrentUserUtils } from '../../util/CurrentUserUtils';
import { DocumentManager } from "../../util/DocumentManager";
import { DragManager, dropActionType } from "../../util/DragManager";
@@ -41,6 +45,17 @@ import { LinkDocPreview } from "./LinkDocPreview";
import { PresBox } from './PresBox';
import { RadialMenu } from './RadialMenu';
import React = require("react");
+const { Howl } = require('howler');
+
+interface Window {
+ MediaRecorder: MediaRecorder;
+}
+
+declare class MediaRecorder {
+ // whatever MediaRecorder has
+ constructor(e: any);
+}
+
export enum ViewAdjustment {
resetView = 1,
@@ -135,6 +150,7 @@ export interface DocumentViewInternalProps extends DocumentViewProps {
@observer
export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps, Document>(Document) {
@observable _animateScalingTo = 0;
+ @observable _audioState = 0;
private _downX: number = 0;
private _downY: number = 0;
private _firstX: number = -1;
@@ -146,8 +162,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
private _timeout: NodeJS.Timeout | undefined;
private _dropDisposer?: DragManager.DragDropDisposer;
private _holdDisposer?: InteractionUtils.MultiTouchEventDisposer;
- _componentView: Opt<DocComponentView>;
protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
+ _componentView: Opt<DocComponentView>;
private get topMost() { return this.props.renderDepth === 0; }
private get active() { return this.props.isSelected(true) || this.props.parentActive(true); }
@@ -165,6 +181,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@computed get backgroundColor() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor); }
@computed get docContents() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.DocContents); }
@computed get headerMargin() { return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0; }
+ @computed get titleHeight() { return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.TitleHeight) || 0; }
@computed get pointerEvents() { return this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents + (this.props.isSelected() ? ":selected" : "")); }
@computed get finalLayoutKey() { return StrCast(this.Document.layoutKey, "layout"); }
@computed get nativeWidth() { return this.props.NativeWidth(); }
@@ -737,6 +754,16 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@action setContentsActive = (setActive: () => boolean) => this.contentsActive = setActive;
@computed get contents() {
TraceMobx();
+ const audioView = !this.layoutDoc._showAudio ? (null) :
+ <div className="documentView-audioBackground"
+ onPointerDown={this.recordAudioAnnotation}
+ onPointerEnter={this.onPointerEnter}
+ style={{ height: 25, position: "absolute", top: 10, left: 10 }}
+ >
+ <FontAwesomeIcon className="documentView-audioFont"
+ style={{ color: [DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]).length ? "blue" : "gray", "green", "red"][this._audioState] }}
+ icon={!DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]).length ? "microphone" : "file-audio"} size="sm" />
+ </div>;
return <div className="documentView-contentsView"
style={{
pointerEvents: this.props.contentPointerEvents as any,
@@ -757,6 +784,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
{this.layoutDoc.hideAllLinks ? (null) : this.allAnchors}
{this.hideLinkButton ? (null) :
<DocumentLinksButton View={this.props.DocumentView()} links={this.allLinks} Offset={[this.topMost ? 0 : -15, undefined, undefined, this.topMost ? 10 : -20]} />}
+
+ {audioView}
</div>;
}
@@ -790,11 +819,62 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
</div >);
}
+ @action
+ onPointerEnter = () => {
+ const self = this;
+ const audioAnnos = DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]);
+ if (audioAnnos && audioAnnos.length && this._audioState === 0) {
+ const anno = audioAnnos[Math.floor(Math.random() * audioAnnos.length)];
+ anno.data instanceof AudioField && new Howl({
+ src: [anno.data.url.href],
+ format: ["mp3"],
+ autoplay: true,
+ loop: false,
+ volume: 0.5,
+ onend: function () {
+ runInAction(() => self._audioState = 0);
+ }
+ });
+ this._audioState = 1;
+ }
+ }
+ recordAudioAnnotation = () => {
+ let gumStream: any;
+ let recorder: any;
+ const self = this;
+ navigator.mediaDevices.getUserMedia({
+ audio: true
+ }).then(function (stream) {
+ gumStream = stream;
+ recorder = new MediaRecorder(stream);
+ recorder.ondataavailable = async (e: any) => {
+ const [{ result }] = await Networking.UploadFilesToServer(e.data);
+ if (!(result instanceof Error)) {
+ const audioDoc = Docs.Create.AudioDocument(Utils.prepend(result.accessPaths.agnostic.client), { title: "audio test", _width: 200, _height: 32 });
+ audioDoc.treeViewExpandedView = "layout";
+ const audioAnnos = Cast(self.dataDoc[self.LayoutFieldKey + "-audioAnnotations"], listSpec(Doc));
+ if (audioAnnos === undefined) {
+ self.dataDoc[self.LayoutFieldKey + "-audioAnnotations"] = new List([audioDoc]);
+ } else {
+ audioAnnos.push(audioDoc);
+ }
+ }
+ };
+ runInAction(() => self._audioState = 2);
+ recorder.start();
+ setTimeout(() => {
+ recorder.stop();
+ runInAction(() => self._audioState = 0);
+ gumStream.getAudioTracks()[0].stop();
+ }, 5000);
+ });
+ }
+
captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewInternalProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ":caption");
@computed get innards() {
TraceMobx();
- const showTitle = this.ShowTitle;
- const showTitleHover = StrCast(this.layoutDoc._showTitleHover);
+ const showTitle = this.ShowTitle?.split(":")[0];
+ const showTitleHover = this.ShowTitle?.includes(":hover");
const showCaption = StrCast(this.layoutDoc._showCaption);
const captionView = !showCaption ? (null) :
<div className="documentView-captionWrapper"
@@ -815,7 +895,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const titleView = !showTitle ? (null) :
<div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} key="title" style={{
position: this.headerMargin ? "relative" : "absolute",
- height: this.headerMargin,
+ height: this.titleHeight,
background: SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.userColor || (this.rootDoc.type === DocumentType.RTF ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)"),
pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : undefined,
}}>
@@ -932,9 +1012,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.ContentScale, this.props.PanelWidth(), this.props.PanelHeight());
contentsActive = () => this.docView?.contentsActive();
- focus = (doc: Doc, options?: DocFocusOptions) => {
- return this.docView?.focus(doc, options);
- }
+ focus = (doc: Doc, options?: DocFocusOptions) => this.docView?.focus(doc, options);
getBounds = () => {
if (!this.docView || !this.docView.ContentDiv || this.docView.props.renderDepth === 0 || this.docView.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) {
return undefined;
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 41055e2db..6d60c70bf 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -94,24 +94,6 @@
height: 100%;
}
-.imageBox-audioBackground {
- display: inline-block;
- width: 10%;
- position: absolute;
- top: 0px;
- left: 0px;
- border-radius: 25px;
- background: white;
- opacity: 0.3;
-
- svg {
- width: 90% !important;
- height: 70%;
- position: absolute;
- left: 5%;
- top: 15%;
- }
-}
.imageBox-fader {
position: relative;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 74bff2bfe..728e5e02f 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,4 +1,3 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
import { observer } from "mobx-react";
import { Dictionary } from 'typescript-collections';
@@ -10,12 +9,11 @@ import { ObjectField } from '../../../fields/ObjectField';
import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
import { ComputedField } from '../../../fields/ScriptField';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
-import { AudioField, ImageField } from '../../../fields/URLField';
+import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
import { emptyFunction, OmitKeys, returnOne, Utils } from '../../../Utils';
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
-import { Docs } from '../../documents/Documents';
import { Networking } from '../../Network';
import { DragManager } from '../../util/DragManager';
import { undoBatch } from '../../util/UndoManager';
@@ -30,8 +28,6 @@ import { FieldView, FieldViewProps } from './FieldView';
import "./ImageBox.scss";
import React = require("react");
const path = require('path');
-const { Howl } = require('howler');
-
export const pageSchema = createSchema({
_curPage: "number",
@@ -39,16 +35,6 @@ export const pageSchema = createSchema({
googlePhotosUrl: "string",
googlePhotosTags: "string"
});
-
-interface Window {
- MediaRecorder: MediaRecorder;
-}
-
-declare class MediaRecorder {
- // whatever MediaRecorder has
- constructor(e: any);
-}
-
type ImageDocument = makeInterface<[typeof pageSchema, typeof documentSchema]>;
const ImageDocument = makeInterface(pageSchema, documentSchema);
@@ -64,10 +50,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
protected _multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined;
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); }
private _imgRef: React.RefObject<HTMLImageElement> = React.createRef();
+ private _curSuffix = "_m";
private _dropDisposer?: DragManager.DragDropDisposer;
private _disposers: { [name: string]: IReactionDisposer } = {};
- @observable private _audioState = 0;
- @observable static _showControls: boolean;
@observable uploadIcon = uploadIcons.idle;
protected createDropTarget = (ele: HTMLDivElement) => {
@@ -120,37 +105,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
}
}
- recordAudioAnnotation = () => {
- let gumStream: any;
- let recorder: any;
- const self = this;
- navigator.mediaDevices.getUserMedia({
- audio: true
- }).then(function (stream) {
- gumStream = stream;
- recorder = new MediaRecorder(stream);
- recorder.ondataavailable = async (e: any) => {
- const [{ result }] = await Networking.UploadFilesToServer(e.data);
- if (!(result instanceof Error)) {
- const audioDoc = Docs.Create.AudioDocument(Utils.prepend(result.accessPaths.agnostic.client), { title: "audio test", _width: 200, _height: 32 });
- audioDoc.treeViewExpandedView = "layout";
- const audioAnnos = Cast(self.dataDoc[self.fieldKey + "-audioAnnotations"], listSpec(Doc));
- if (audioAnnos === undefined) {
- self.dataDoc[self.fieldKey + "-audioAnnotations"] = new List([audioDoc]);
- } else {
- audioAnnos.push(audioDoc);
- }
- }
- };
- runInAction(() => self._audioState = 2);
- recorder.start();
- setTimeout(() => {
- recorder.stop();
- runInAction(() => self._audioState = 0);
- gumStream.getAudioTracks()[0].stop();
- }, 5000);
- });
- }
@undoBatch
resolution = () => this.layoutDoc._showFullRes = !this.layoutDoc._showFullRes
@@ -250,29 +204,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
}
}
}
- _curSuffix = "_m";
-
- @action
- onPointerEnter = () => {
- const self = this;
- const audioAnnos = DocListCast(this.dataDoc[this.fieldKey + "-audioAnnotations"]);
- if (audioAnnos && audioAnnos.length && this._audioState === 0) {
- const anno = audioAnnos[Math.floor(Math.random() * audioAnnos.length)];
- anno.data instanceof AudioField && new Howl({
- src: [anno.data.url.href],
- format: ["mp3"],
- autoplay: true,
- loop: false,
- volume: 0.5,
- onend: function () {
- runInAction(() => self._audioState = 0);
- }
- });
- this._audioState = 1;
- }
- }
-
- audioDown = () => this.recordAudioAnnotation();
considerGooglePhotosLink = () => {
const remoteUrl = this.dataDoc.googlePhotosUrl;
@@ -387,16 +318,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
ref={this._imgRef}
onError={this.onError} /></div>}
</div>
- {!this.layoutDoc._showAudio ? (null) :
- <div className="imageBox-audioBackground"
- onPointerDown={this.audioDown}
- onPointerEnter={this.onPointerEnter}
- style={{ height: `calc(${.1 * nativeHeight / nativeWidth * 100}%)` }}
- >
- <FontAwesomeIcon className="imageBox-audioFont"
- style={{ color: [DocListCast(this.dataDoc[this.fieldKey + "-audioAnnotations"]).length ? "blue" : "gray", "green", "red"][this._audioState] }}
- icon={!DocListCast(this.dataDoc[this.fieldKey + "-audioAnnotations"]).length ? "microphone" : "file-audio"} size="sm" />
- </div>}
{this.considerDownloadIcon}
{this.considerGooglePhotosLink()}
<FaceRectangles document={this.dataDoc} color={"#0000FF"} backgroundColor={"#0000FF"} />
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 9e9be74f3..779da91e6 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -258,13 +258,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
_lastText = "";
dispatchTransaction = (tx: Transaction) => {
- let timeStamp;
- clearTimeout(timeStamp);
if (this._editorView) {
-
const metadata = tx.selection.$from.marks().find((m: Mark) => m.type === schema.marks.metadata);
if (metadata) {
-
const range = tx.selection.$from.blockRange(tx.selection.$to);
let text = range ? tx.doc.textBetween(range.start, range.end) : "";
let textEndSelection = tx.selection.to;