aboutsummaryrefslogtreecommitdiff
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
parentddf37d997ded0faa87c844b02f4df1b98989fd2e (diff)
cleaned up showTitle and showAudio stuff just a bit.
-rw-r--r--src/client/documents/Documents.ts3
-rw-r--r--src/client/views/DocumentButtonBar.tsx10
-rw-r--r--src/client/views/PropertiesButtons.scss4
-rw-r--r--src/client/views/PropertiesButtons.tsx13
-rw-r--r--src/client/views/StyleProvider.tsx6
-rw-r--r--src/client/views/TemplateMenu.tsx4
-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
-rw-r--r--src/fields/documentSchemas.ts3
12 files changed, 127 insertions, 132 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index fee2679b6..7502aa685 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -76,8 +76,7 @@ export interface DocumentOptions {
_fitWidth?: boolean;
_fitToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents
_freeformLOD?: boolean; // whether to use LOD to render a freeform document
- _showTitleHover?: string; //
- _showTitle?: string; // which field to display in the title area. leave empty to have no title
+ _showTitle?: string; // field name to display in header (:hover is an optional suffix)
_showCaption?: string; // which field to display in the caption area. leave empty to have no caption
_scrollTop?: number; // scroll location for pdfs
_noAutoscroll?: boolean;// whether collections autoscroll when this item is dragged
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index eeef94d74..209e750d6 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -312,22 +312,20 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
return false;
}
- _ref = React.createRef<HTMLDivElement>();
+ _ref = React.createRef<TemplateMenu>();
@observable _tooltipOpen: boolean = false;
@computed
get templateButton() {
const view0 = this.view0;
- const templates: Map<string, boolean> = new Map();
const views = this.props.views();
- Array.from(["Caption", "Title", "TitleHover"]).map(template =>
- templates.set(template, views.reduce((checked, doc) => checked || doc?.props.Document["_show" + template] ? true : false, false as boolean)));
return !view0 ? (null) :
<Tooltip title={<div className="dash-tooltip">Tap to Customize Layout. Drag an embeddable alias</div>} open={this._tooltipOpen} onClose={action(() => this._tooltipOpen = false)} placement="bottom">
<div className="documentButtonBar-linkFlyout" ref={this._dragRef}
- onPointerEnter={action(() => !this._ref.current?.getBoundingClientRect().width && (this._tooltipOpen = true))} >
+ onPointerEnter={action(() => !(this._ref.current as any as HTMLElement)?.getBoundingClientRect().width && (this._tooltipOpen = true))} >
<Flyout anchorPoint={anchorPoints.LEFT_TOP} onOpen={action(() => this._aliasDown = true)} onClose={action(() => this._aliasDown = false)}
- content={!this._aliasDown ? (null) : <div ref={this._ref}> <TemplateMenu docViews={views.filter(v => v).map(v => v as DocumentView)} templates={templates} /></div>}>
+ content={!this._aliasDown ? (null) :
+ <TemplateMenu ref={this._ref} docViews={views.filter(v => v).map(v => v as DocumentView)} />}>
<div className={"documentButtonBar-linkButton-empty"} ref={this._dragRef} onPointerDown={this.onAliasButtonDown} >
{<FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="sm" />}
</div>
diff --git a/src/client/views/PropertiesButtons.scss b/src/client/views/PropertiesButtons.scss
index d63eb874c..ba212da7f 100644
--- a/src/client/views/PropertiesButtons.scss
+++ b/src/client/views/PropertiesButtons.scss
@@ -47,6 +47,10 @@ $linkGap : 3px;
background-color: white;
color: black;
}
+.propertiesButtons-linkButton-empty.toggle-hover {
+ background-color: gray;
+ color: black;
+}
.propertiesButtons-linkButton-empty.toggle-off {
color: white;
}
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 9a836313c..53a017592 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -6,7 +6,7 @@ import { observer } from "mobx-react";
import { Doc } from "../../fields/Doc";
import { InkField } from '../../fields/InkField';
import { RichTextField } from '../../fields/RichTextField';
-import { Cast, NumCast } from "../../fields/Types";
+import { Cast, NumCast, StrCast } from "../../fields/Types";
import { ImageField } from '../../fields/URLField';
import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
@@ -222,10 +222,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
}
@undoBatch
- @action
- setDictation = () => {
- SelectionManager.Views().forEach(dv => dv.rootDoc._showAudio = dv.rootDoc._showAudio === !dv.rootDoc._showAudio);
- }
+ setDictation = () => SelectionManager.Views().forEach(dv => dv.rootDoc._showAudio = !dv.rootDoc._showAudio);
@computed
get dictationButton() {
@@ -244,7 +241,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
@undoBatch
@action
setTitle = () => {
- SelectionManager.Views().forEach(dv => dv.rootDoc._showTitle = dv.rootDoc._showTitle === undefined ? "title" : undefined);
+ SelectionManager.Views().forEach(dv => dv.rootDoc._showTitle = !dv.rootDoc._showTitle ? "title" : dv.rootDoc._showTitle === "title" ? "title:hover" : undefined);
}
@computed
@@ -252,7 +249,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
const targetDoc = this.selectedDoc;
return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"Show Title Header"}</div>} placement="top">
<div>
- <div className={`propertiesButtons-linkButton-empty toggle-${targetDoc._showTitle ? "on" : "off"}`} onPointerDown={this.setTitle}>
+ <div className={`propertiesButtons-linkButton-empty toggle-${targetDoc._showTitle === "title" ? "on" : StrCast(targetDoc._showTitle).includes(":hover") ? "hover" : "off"}`} onPointerDown={this.setTitle}>
<FontAwesomeIcon className="propertiesButtons-icon" icon="text-width" size="lg" />
</div>
<div className="propertiesButtons-title"> Title </div>
@@ -502,7 +499,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
<div className="propertiesButtons-button">
{this.lockButton}
</div>
- <div className="propertiesButtons-button" style={{ display: isText || isImage ? "" : "none" }}>
+ <div className="propertiesButtons-button">
{this.dictationButton}
</div>
<div className="propertiesButtons-button">
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 058d21c92..0ab8c5dfb 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -36,7 +36,8 @@ export enum StyleProp {
PointerEvents = "pointerEvents", // pointer events for DocumentView -- inherits pointer events if not specified
Decorations = "decorations", // additional decoration to display above a DocumentView -- currently only used to display a Lock for making things background
HeaderMargin = "headerMargin", // margin at top of documentview, typically for displaying a title -- doc contents will start below that
- ShowTitle = "showTitle", // whether to display a title on a Document
+ TitleHeight = "titleHeight", // Height of Title area
+ ShowTitle = "showTitle", // whether to display a title on a Document (optional :hover suffix)
}
function darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); }
@@ -86,7 +87,8 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps |
return "white";
case StyleProp.Hidden: return BoolCast(doc?._hidden, BoolCast(doc?.hidden));
case StyleProp.BorderRounding: return StrCast(doc?._borderRounding, StrCast(doc?.borderRounding));
- case StyleProp.HeaderMargin: return ([CollectionViewType.Stacking, CollectionViewType.Masonry].includes(doc?._viewType as any) || doc?.type === DocumentType.RTF) && doc?._showTitle && !doc?._showTitleHover ? 15 : 0;
+ case StyleProp.TitleHeight: return 15;
+ case StyleProp.HeaderMargin: return ([CollectionViewType.Stacking, CollectionViewType.Masonry].includes(doc?._viewType as any) || doc?.type === DocumentType.RTF) && doc?._showTitle && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0;
case StyleProp.BackgroundColor: {
if (isAnchor && docProps) return "transparent";
if (isCaption) return "rgba(0,0,0 ,0.4)";
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index cdaf1bd0c..d2e0cb265 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -45,7 +45,7 @@ class OtherToggle extends React.Component<{ checked: boolean, name: string, togg
export interface TemplateMenuProps {
docViews: DocumentView[];
- templates: Map<string, boolean>;
+ templates?: Map<string, boolean>;
}
@@ -115,7 +115,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
const addedTypes = Doc.UserDoc().noviceMode ? [] : DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data);
const layout = Doc.Layout(firstDoc);
const templateMenu: Array<JSX.Element> = [];
- this.props.templates.forEach((checked, template) =>
+ this.props.templates?.forEach((checked, template) =>
templateMenu.push(<TemplateToggle key={template} template={template} checked={checked} toggle={this.toggleTemplate} />));
templateMenu.push(<OtherToggle key={"audio"} name={"Audio"} checked={firstDoc._showAudio ? true : false} toggle={this.toggleAudio} />);
templateMenu.push(<OtherToggle key={"default"} name={"Default"} checked={templateName === "layout"} toggle={this.toggleDefault} />);
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;
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index bdc498c97..df3dd1eb4 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -42,8 +42,7 @@ export const documentSchema = createSchema({
_yMargin: "number", // margin added on top/bottom of most documents to add separation from their container
_overflow: "string", // sets overflow behvavior for CollectionFreeForm views
_showCaption: "string", // whether editable caption text is overlayed at the bottom of the document
- _showTitle: "string", // the fieldkey whose contents should be displayed at the top of the document
- _showTitleHover: "string", // the showTitle should be shown only on hover
+ _showTitle: "string", // the fieldkey(s) whose contents should be displayed at the top of the document. separate multiple keys with ";". Use :hover suffix to indicate title should be shown on hover
_showAudio: "boolean", // whether to show the audio record icon on documents
_freeformLOD: "boolean", // whether to enable LOD switching for CollectionFreeFormViews
_pivotField: "string", // specifies which field key should be used as the timeline/pivot axis