aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Utils.ts16
-rw-r--r--src/client/documents/Documents.ts30
-rw-r--r--src/client/util/DocumentManager.ts8
-rw-r--r--src/client/views/ContextMenu.tsx2
-rw-r--r--src/client/views/DocumentDecorations.tsx92
-rw-r--r--src/client/views/GlobalKeyHandler.ts6
-rw-r--r--src/client/views/PreviewCursor.tsx21
-rw-r--r--src/client/views/collections/CollectionSubView.tsx61
-rw-r--r--src/client/views/collections/CollectionView.tsx12
-rw-r--r--src/client/views/nodes/DocumentView.tsx3
-rw-r--r--src/client/views/nodes/FontIconBox.tsx9
11 files changed, 149 insertions, 111 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index dba802f98..a01a94134 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -7,6 +7,22 @@ import { ColorState } from 'react-color';
export namespace Utils {
export let DRAG_THRESHOLD = 4;
+ export function readUploadedFileAsText(inputFile: File) {
+ const temporaryFileReader = new FileReader();
+
+ return new Promise((resolve, reject) => {
+ temporaryFileReader.onerror = () => {
+ temporaryFileReader.abort();
+ reject(new DOMException("Problem parsing input file."));
+ };
+
+ temporaryFileReader.onload = () => {
+ resolve(temporaryFileReader.result);
+ };
+ temporaryFileReader.readAsText(inputFile);
+ });
+ }
+
export function GenerateGuid(): string {
return v4();
}
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 4c27cf47d..85da621e0 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,5 +1,5 @@
import { runInAction } from "mobx";
-import { extname } from "path";
+import { extname, basename } from "path";
import { DateField } from "../../fields/DateField";
import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym } from "../../fields/Doc";
import { HtmlField } from "../../fields/HtmlField";
@@ -48,6 +48,8 @@ import { PresElementBox } from "../views/presentationview/PresElementBox";
import { RecommendationsBox } from "../views/RecommendationsBox";
import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo";
import { DocumentType } from "./DocumentTypes";
+import { Networking } from "../Network";
+import { Upload } from "../../server/SharedMediaTypes";
const path = require('path');
export interface DocumentOptions {
@@ -1104,6 +1106,32 @@ export namespace DocUtils {
});
return optionsCollection;
}
+
+ export async function uploadFilesToDocs(files: File[], options: DocumentOptions) {
+ const generatedDocuments: Doc[] = [];
+ for (const { source: { name, type }, result } of await Networking.UploadFilesToServer(files)) {
+ if (result instanceof Error) {
+ alert(`Upload failed: ${result.message}`);
+ return [];
+ }
+ const full = { ...options, _width: 400, title: name };
+ const pathname = Utils.prepend(result.accessPaths.agnostic.client);
+ const doc = await DocUtils.DocumentFromType(type, pathname, full);
+ if (!doc) {
+ continue;
+ }
+ const proto = Doc.GetProto(doc);
+ proto.text = result.rawText;
+ proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
+ if (Upload.isImageInformation(result)) {
+ proto["data-nativeWidth"] = (result.nativeWidth > result.nativeHeight) ? 400 * result.nativeWidth / result.nativeHeight : 400;
+ proto["data-nativeHeight"] = (result.nativeWidth > result.nativeHeight) ? 400 : 400 / (result.nativeWidth / result.nativeHeight);
+ proto.contentSize = result.contentSize;
+ }
+ generatedDocuments.push(doc);
+ }
+ return generatedDocuments;
+ }
}
Scripting.addGlobal("Docs", Docs);
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index fb5d1717e..1fa5faeb3 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -146,7 +146,7 @@ export class DocumentManager {
};
const docView = getFirstDocView(targetDoc, originatingDoc);
let annotatedDoc = await Cast(targetDoc.annotationOn, Doc);
- if (annotatedDoc) {
+ if (annotatedDoc && !linkDoc?.isPushpin) {
const first = getFirstDocView(annotatedDoc);
if (first) {
annotatedDoc = first.props.Document;
@@ -156,7 +156,11 @@ export class DocumentManager {
}
}
if (docView) { // we have a docView already and aren't forced to create a new one ... just focus on the document. TODO move into view if necessary otherwise just highlight?
- docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish);
+ if (linkDoc?.isPushpin) docView.props.Document.hidden = !docView.props.Document.hidden;
+ else {
+ docView.props.Document.hidden && (docView.props.Document.hidden = undefined);
+ docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish);
+ }
highlight();
} else {
const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined;
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 941d7b44a..07f7b8e6d 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -155,9 +155,11 @@ export class ContextMenu extends React.Component {
@action
closeMenu = () => {
+ const wasOpen = this._display;
this.clearItems();
this._display = false;
this._shouldDisplay = false;
+ return wasOpen;
}
@computed get filteredItems(): (OriginalMenuProps | string[])[] {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 4fda10926..a45ef8862 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -600,52 +600,54 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
zIndex: SelectionManager.SelectedDocuments().length > 1 ? 900 : 0,
}} onPointerDown={this.onBackgroundDown} onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} >
</div>
- <div className="documentDecorations-container" ref={this.setTextBar} style={{
- width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
- height: (bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight) + "px",
- left: bounds.x - this._resizeBorderWidth / 2,
- top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
- }}>
- {maximizeIcon}
- {titleArea}
- {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) :
- <div className="documentDecorations-iconifyButton" title={`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`} onPointerDown={this.onIconifyDown}>
- {"_"}
- </div>}
- <div className="documentDecorations-closeButton" title="Open Document in Tab" onPointerDown={this.onMaximizeDown}>
- {SelectionManager.SelectedDocuments().length === 1 ? DocumentDecorations.DocumentIcon(StrCast(seldoc.props.Document.layout, "...")) : "..."}
+ {bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? (null) : <>
+ <div className="documentDecorations-container" key="container" ref={this.setTextBar} style={{
+ width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
+ height: (bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight) + "px",
+ left: bounds.x - this._resizeBorderWidth / 2,
+ top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight,
+ }}>
+ {maximizeIcon}
+ {titleArea}
+ {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) :
+ <div className="documentDecorations-iconifyButton" title={`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`} onPointerDown={this.onIconifyDown}>
+ {"_"}
+ </div>}
+ <div className="documentDecorations-closeButton" title="Open Document in Tab" onPointerDown={this.onMaximizeDown}>
+ {SelectionManager.SelectedDocuments().length === 1 ? DocumentDecorations.DocumentIcon(StrCast(seldoc.props.Document.layout, "...")) : "..."}
+ </div>
+ <div id="documentDecorations-rotation" className="documentDecorations-rotation"
+ onPointerDown={this.onRotateDown}> ⟲ </div>
+ <div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer"
+ onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+ <div id="documentDecorations-topResizer" className="documentDecorations-resizer"
+ onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+ <div id="documentDecorations-topRightResizer" className="documentDecorations-resizer"
+ onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+ <div id="documentDecorations-leftResizer" className="documentDecorations-resizer"
+ onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+ <div id="documentDecorations-centerCont"></div>
+ <div id="documentDecorations-rightResizer" className="documentDecorations-resizer"
+ onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+ <div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer"
+ onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+ <div id="documentDecorations-bottomResizer" className="documentDecorations-resizer"
+ onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+ <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer"
+ onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
+ {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) :
+ <div id="documentDecorations-levelSelector" className="documentDecorations-selector"
+ title="tap to select containing document" onPointerDown={this.onSelectorUp} onContextMenu={e => e.preventDefault()}>
+ <FontAwesomeIcon className="documentdecorations-times" icon={faArrowAltCircleUp} size="lg" />
+ </div>}
+ <div id="documentDecorations-borderRadius" className="documentDecorations-radius"
+ onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}></div>
+
+ </div >
+ <div className="link-button-container" key="links" style={{ left: bounds.x - this._resizeBorderWidth / 2 + 10, top: bounds.b + this._resizeBorderWidth / 2 }}>
+ <DocumentButtonBar views={SelectionManager.SelectedDocuments} />
</div>
- <div id="documentDecorations-rotation" className="documentDecorations-rotation"
- onPointerDown={this.onRotateDown}> ⟲ </div>
- <div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer"
- onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-topResizer" className="documentDecorations-resizer"
- onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-topRightResizer" className="documentDecorations-resizer"
- onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-leftResizer" className="documentDecorations-resizer"
- onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-centerCont"></div>
- <div id="documentDecorations-rightResizer" className="documentDecorations-resizer"
- onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer"
- onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-bottomResizer" className="documentDecorations-resizer"
- onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer"
- onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div>
- {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) :
- <div id="documentDecorations-levelSelector" className="documentDecorations-selector"
- title="tap to select containing document" onPointerDown={this.onSelectorUp} onContextMenu={e => e.preventDefault()}>
- <FontAwesomeIcon className="documentdecorations-times" icon={faArrowAltCircleUp} size="lg" />
- </div>}
- <div id="documentDecorations-borderRadius" className="documentDecorations-radius"
- onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}></div>
-
- </div >
- <div className="link-button-container" style={{ left: bounds.x - this._resizeBorderWidth / 2 + 10, top: bounds.b + this._resizeBorderWidth / 2 }}>
- <DocumentButtonBar views={SelectionManager.SelectedDocuments} />
- </div>
+ </>}
</div >
);
}
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index e3546dece..c85849adb 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -20,6 +20,7 @@ import { MainView } from "./MainView";
import { DocumentView } from "./nodes/DocumentView";
import { DocumentLinksButton } from "./nodes/DocumentLinksButton";
import PDFMenu from "./pdf/PDFMenu";
+import { ContextMenu } from "./ContextMenu";
const modifiers = ["control", "meta", "shift", "alt"];
type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>;
@@ -81,16 +82,17 @@ export default class KeyManager {
DocumentLinksButton.StartLink = undefined;
const main = MainView.Instance;
Doc.SetSelectedTool(InkTool.None);
+ var doDeselect = true;
if (main.isPointerDown) {
DragManager.AbortDrag();
} else {
if (CollectionDockingView.Instance.HasFullScreen()) {
CollectionDockingView.Instance.CloseFullScreen();
} else {
- SelectionManager.DeselectAll();
+ doDeselect = !ContextMenu.Instance.closeMenu();
}
}
- SelectionManager.DeselectAll();
+ doDeselect && SelectionManager.DeselectAll();
DictationManager.Controls.stop();
// RecommendationsBox.Instance.closeMenu();
GoogleAuthenticationManager.Instance.cancel();
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 1ab99881d..6583589f3 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -3,13 +3,18 @@ import { observer } from 'mobx-react';
import "normalize.css";
import * as React from 'react';
import "./PreviewCursor.scss";
-import { Docs } from '../documents/Documents';
+import { Docs, DocUtils } from '../documents/Documents';
import { Doc } from '../../fields/Doc';
import { Transform } from "../util/Transform";
import { DocServer } from '../DocServer';
-import { undoBatch } from '../util/UndoManager';
+import { undoBatch, UndoManager } from '../util/UndoManager';
import { NumCast } from '../../fields/Types';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import * as rp from 'request-promise';
+import { Utils } from '../../Utils';
+import { Networking } from '../Network';
+import { Upload } from '../../server/SharedMediaTypes';
+import { basename } from 'path';
@observer
export class PreviewCursor extends React.Component<{}> {
@@ -26,7 +31,7 @@ export class PreviewCursor extends React.Component<{}> {
document.addEventListener("paste", this.paste);
}
- paste = (e: ClipboardEvent) => {
+ paste = async (e: ClipboardEvent) => {
if (PreviewCursor.Visible && e.clipboardData) {
const newPoint = PreviewCursor._getTransform().transformPoint(PreviewCursor._clickPoint[0], PreviewCursor._clickPoint[1]);
runInAction(() => PreviewCursor.Visible = false);
@@ -104,6 +109,16 @@ export class PreviewCursor extends React.Component<{}> {
x: newPoint[0],
y: newPoint[1],
})))();
+ } else if (e.clipboardData.items.length) {
+ const batch = UndoManager.StartBatch("collection view drop");
+ const files: File[] = [];
+ for (let i = 0; i < e.clipboardData.items.length; i++) {
+ const file = e.clipboardData.items[i].getAsFile();
+ file && files.push(file);
+ }
+ const generatedDocuments = await DocUtils.uploadFilesToDocs(files, { x: newPoint[0], y: newPoint[1] });
+ generatedDocuments.forEach(PreviewCursor._addDocument);
+ batch.end();
}
}
}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index d107db86b..ecd16d22f 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -225,7 +225,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
const res = addedDocs.length ? this.addDocument(addedDocs) : true;
- added = movedDocs.length ? docDragData.moveDocument(movedDocs, this.props.Document, de.embedKey || !this.props.isAnnotationOverlay ? this.addDocument : returnFalse) : res;
+ added = movedDocs.length ? docDragData.moveDocument(movedDocs, this.props.Document, Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document) || de.embedKey || !this.props.isAnnotationOverlay ? this.addDocument : returnFalse) : res;
} else {
added = this.addDocument(docDragData.droppedDocuments);
}
@@ -238,21 +238,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
}
return false;
}
- readUploadedFileAsText = (inputFile: File) => {
- const temporaryFileReader = new FileReader();
- return new Promise((resolve, reject) => {
- temporaryFileReader.onerror = () => {
- temporaryFileReader.abort();
- reject(new DOMException("Problem parsing input file."));
- };
-
- temporaryFileReader.onload = () => {
- resolve(temporaryFileReader.result);
- };
- temporaryFileReader.readAsText(inputFile);
- });
- }
@undoBatch
@action
protected async onExternalDrop(e: React.DragEvent, options: DocumentOptions, completed?: () => void) {
@@ -271,8 +257,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
e.stopPropagation();
e.preventDefault();
- const { addDocument } = this;
- if (!addDocument) {
+ if (!this.addDocument) {
alert("this.props.addDocument does not exist. Aborting drop operation.");
return;
}
@@ -286,14 +271,14 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
DocServer.GetRefField(docid).then(f => {
if (f instanceof Doc) {
if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView
- (f instanceof Doc) && addDocument(f);
+ (f instanceof Doc) && this.addDocument(f);
}
});
} else {
- addDocument(Docs.Create.WebDocument(href, { ...options, title: href }));
+ this.addDocument(Docs.Create.WebDocument(href, { ...options, title: href }));
}
} else if (text) {
- addDocument(Docs.Create.TextDocument(text, { ...options, _width: 100, _height: 25 }));
+ this.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 100, _height: 25 }));
}
return;
}
@@ -313,7 +298,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
if (source.startsWith("http")) {
const doc = Docs.Create.ImageDocument(source, { ...options, _width: 300 });
ImageUtils.ExtractExif(doc);
- addDocument(doc);
+ this.addDocument(doc);
}
return;
} else {
@@ -360,7 +345,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
if (text) {
if (text.includes("www.youtube.com/watch") || text.includes("www.youtube.com/embed")) {
const url = text.replace("youtube.com/watch?v=", "youtube.com/embed/").split("&")[0];
- addDocument(Docs.Create.VideoDocument(url, {
+ this.addDocument(Docs.Create.VideoDocument(url, {
...options,
title: url,
_width: 400,
@@ -413,10 +398,10 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
const file = item.getAsFile();
file?.type && files.push(file);
- file?.type === "application/json" && this.readUploadedFileAsText(file).then(result => {
+ file?.type === "application/json" && Utils.readUploadedFileAsText(file).then(result => {
console.log(result);
const json = JSON.parse(result as string);
- addDocument(Docs.Create.TreeDocument(
+ this.addDocument(Docs.Create.TreeDocument(
json["rectangular-puzzle"].crossword.clues[0].clue.map((c: any) => {
const label = Docs.Create.LabelDocument({ title: c["#text"], _width: 120, _height: 20 });
const proto = Doc.GetProto(label);
@@ -428,38 +413,18 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
});
}
}
- for (const { source: { name, type }, result } of await Networking.UploadFilesToServer(files)) {
- if (result instanceof Error) {
- alert(`Upload failed: ${result.message}`);
- return;
- }
- const full = { ...options, _width: 400, title: name };
- const pathname = Utils.prepend(result.accessPaths.agnostic.client);
- const doc = await DocUtils.DocumentFromType(type, pathname, full);
- if (!doc) {
- continue;
- }
- const proto = Doc.GetProto(doc);
- proto.text = result.rawText;
- proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
- if (Upload.isImageInformation(result)) {
- proto["data-nativeWidth"] = (result.nativeWidth > result.nativeHeight) ? 400 * result.nativeWidth / result.nativeHeight : 400;
- proto["data-nativeHeight"] = (result.nativeWidth > result.nativeHeight) ? 400 : 400 / (result.nativeWidth / result.nativeHeight);
- proto.contentSize = result.contentSize;
- }
- generatedDocuments.push(doc);
- }
+ generatedDocuments.push(...await DocUtils.uploadFilesToDocs(files, options));
if (generatedDocuments.length) {
const set = generatedDocuments.length > 1 && generatedDocuments.map(d => DocUtils.iconify(d));
if (set) {
- addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!);
+ this.addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!);
} else {
- generatedDocuments.forEach(addDocument);
+ generatedDocuments.forEach(this.addDocument);
}
completed?.();
} else {
if (text && !text.includes("https://")) {
- addDocument(Docs.Create.TextDocument(text, { ...options, _width: 400, _height: 315 }));
+ this.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 400, _height: 315 }));
}
}
batch.end();
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index dab82185a..705fbdd34 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -141,15 +141,17 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
} else {
added.map(doc => {
const context = Cast(doc.context, Doc, null);
- if (context && (context?.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) {
- const pushpin = Docs.Create.FreeformDocument([], {
- title: "pushpin", x: Cast(doc.x, "number", null), y: Cast(doc.y, "number", null),
- _width: 10, _height: 10, _backgroundColor: "red", isLinkButton: true, displayTimecode: Cast(doc.displayTimecode, "number", null)
+ if (context && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG) &&
+ !DocListCast(doc.links).some(d => d.isPushpin)) {
+ const pushpin = Docs.Create.FontIconDocument({
+ icon: "map-pin", x: Cast(doc.x, "number", null), y: Cast(doc.y, "number", null), _backgroundColor: "#0000003d", color: "#ACCEF7",
+ _width: 15, _height: 15, _xPadding: 0, isLinkButton: true, displayTimecode: Cast(doc.displayTimecode, "number", null)
});
Doc.AddDocToList(context, Doc.LayoutFieldKey(context) + "-annotations", pushpin);
- DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin");
+ const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin");
const first = DocListCast(pushpin.links).find(d => d instanceof Doc);
first && (first.hidden = true);
+ pushpinLink && (Doc.GetProto(pushpinLink).isPushpin = true);
doc.displayTimecode = undefined;
}
doc.context = this.props.Document;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 21b6d8310..09eeaee36 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1169,8 +1169,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
render() {
- if (this.props.Document[AclSym] && this.props.Document[AclSym] === AclPrivate) return (null);
if (!(this.props.Document instanceof Doc)) return (null);
+ if (this.props.Document[AclSym] && this.props.Document[AclSym] === AclPrivate) return (null);
+ if (this.props.Document.hidden) return (null);
const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : this.props.forcedBackgroundColor?.(this.Document) || StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document);
const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null)));
const finalOpacity = this.props.opacity ? this.props.opacity() : opacity;
diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx
index cf0b16c7c..5e8dd2497 100644
--- a/src/client/views/nodes/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox.tsx
@@ -5,7 +5,7 @@ import { createSchema, makeInterface } from '../../../fields/Schema';
import { DocComponent } from '../DocComponent';
import './FontIconBox.scss';
import { FieldView, FieldViewProps } from './FieldView';
-import { StrCast, Cast } from '../../../fields/Types';
+import { StrCast, Cast, NumCast } from '../../../fields/Types';
import { Utils } from "../../../Utils";
import { runInAction, observable, reaction, IReactionDisposer } from 'mobx';
import { Doc } from '../../../fields/Doc';
@@ -59,13 +59,14 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>(
render() {
const referenceDoc = (this.layoutDoc.dragFactory instanceof Doc ? this.layoutDoc.dragFactory : this.layoutDoc);
- const referenceLayout = Doc.Layout(referenceDoc);
+ const refLayout = Doc.Layout(referenceDoc);
return <button className="fontIconBox-outerDiv" title={StrCast(this.layoutDoc.title)} ref={this._ref} onContextMenu={this.specificContextMenu}
style={{
- background: StrCast(referenceLayout.backgroundColor),
+ padding: Cast(this.layoutDoc._xPadding, "number", null),
+ background: StrCast(refLayout._backgroundColor, StrCast(refLayout.backgroundColor)),
boxShadow: this.layoutDoc.ischecked ? `4px 4px 12px black` : undefined
}}>
- <FontAwesomeIcon className="fontIconBox-icon" icon={this.dataDoc.icon as any} color={this._foregroundColor} size="sm" />
+ <FontAwesomeIcon className="fontIconBox-icon" icon={this.dataDoc.icon as any} color={StrCast(this.layoutDoc.color, this._foregroundColor)} size="sm" />
{!this.rootDoc.label ? (null) : <div className="fontIconBox-label"> {StrCast(this.rootDoc.label).substring(0, 5)} </div>}
</button>;
}