aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2023-12-29 01:01:55 -0500
committerbobzel <zzzman@gmail.com>2023-12-29 01:01:55 -0500
commit865478933af8166cb699cbe4a2653aa52423dedb (patch)
tree57dedd5dfe4349432a7c445f408f9e675f1f53df /src/client
parentdf2fc3f11e3b474144db5062620c9f65ca857203 (diff)
fixed dropping images from web. fixed exif data on images and autorotating exif rotated images. fixed selecting on web pages, and resizing web pages upward so that pointer events aren't grabbed.
Diffstat (limited to 'src/client')
-rw-r--r--src/client/apis/youtube/YoutubeBox.tsx5
-rw-r--r--src/client/documents/Documents.ts1
-rw-r--r--src/client/util/Import & Export/ImageUtils.ts41
-rw-r--r--src/client/views/DocumentDecorations.tsx12
-rw-r--r--src/client/views/PreviewCursor.tsx2
-rw-r--r--src/client/views/collections/CollectionSubView.tsx31
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx4
-rw-r--r--src/client/views/nodes/DocumentView.tsx1
-rw-r--r--src/client/views/nodes/VideoBox.tsx2
-rw-r--r--src/client/views/nodes/WebBox.tsx12
-rw-r--r--src/client/views/webcam/DashWebRTCVideo.tsx5
11 files changed, 61 insertions, 55 deletions
diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx
index ceb80acbc..d3a15cd84 100644
--- a/src/client/apis/youtube/YoutubeBox.tsx
+++ b/src/client/apis/youtube/YoutubeBox.tsx
@@ -11,6 +11,7 @@ import { FieldView, FieldViewProps } from '../../views/nodes/FieldView';
import '../../views/nodes/WebBox.scss';
import './YoutubeBox.scss';
import * as React from 'react';
+import { SnappingManager } from '../../util/SnappingManager';
interface VideoTemplate {
thumbnailUrl: string;
@@ -355,9 +356,9 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
</div>
);
- const frozen = !this.props.isSelected() || DocumentView.Interacting;
+ const frozen = !this.props.isSelected() || SnappingManager.IsResizing;
- const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !DocumentView.Interacting ? '-interactive' : '');
+ const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !SnappingManager.IsResizing ? '-interactive' : '');
return (
<>
<div className={classname}>{content}</div>
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 0a4d3a294..79285deb5 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1873,6 +1873,7 @@ export namespace DocUtils {
proto['data_nativeHeight'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
proto['data_nativeWidth'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
}
+ proto.data_exif = JSON.stringify(result.exifData?.data);
proto.data_contentSize = result.contentSize;
// exif gps data coordinates are stored in DMS (Degrees Minutes Seconds), the following operation converts that to decimal coordinates
const latitude = result.exifData?.data?.GPSLatitude;
diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts
index 55d37f544..d99828956 100644
--- a/src/client/util/Import & Export/ImageUtils.ts
+++ b/src/client/util/Import & Export/ImageUtils.ts
@@ -4,28 +4,33 @@ import { Cast, StrCast, NumCast } from '../../../fields/Types';
import { Networking } from '../../Network';
import { Id } from '../../../fields/FieldSymbols';
import { Utils } from '../../../Utils';
+import { DocData } from '../../../fields/DocSymbols';
export namespace ImageUtils {
- export const ExtractExif = async (document: Doc): Promise<boolean> => {
+ export type imgInfo = {
+ contentSize: number;
+ nativeWidth: number;
+ nativeHeight: number;
+ source: string;
+ exifData: { error: string | undefined; data: string };
+ };
+ export const ExtractImgInfo = async (document: Doc): Promise<imgInfo | undefined> => {
const field = Cast(document.data, ImageField);
- if (!field) {
- return false;
+ return field ? await Networking.PostToServer('/inspectImage', { source: field.url.href }) : undefined;
+ };
+
+ export const AssignImgInfo = (document: Doc, data?: imgInfo) => {
+ if (data) {
+ data.nativeWidth && (document._height = (NumCast(document._width) * data.nativeHeight) / data.nativeWidth);
+ const proto = document[DocData];
+ const field = Doc.LayoutFieldKey(document);
+ proto[`${field}_nativeWidth`] = data.nativeWidth;
+ proto[`${field}_nativeHeight`] = data.nativeHeight;
+ proto[`${field}_path`] = data.source;
+ proto[`${field}_exif`] = JSON.stringify(data.exifData.data);
+ proto[`${field}_contentSize`] = data.contentSize ? data.contentSize : undefined;
}
- const source = field.url.href;
- const {
- contentSize,
- nativeWidth,
- nativeHeight,
- exifData: { error, data },
- } = await Networking.PostToServer('/inspectImage', { source });
- document.exif = error || Doc.Get.FromJson({ data });
- const proto = Doc.GetProto(document);
- nativeWidth && (document._height = (NumCast(document._width) * nativeHeight) / nativeWidth);
- proto['data_nativeWidth'] = nativeWidth;
- proto['data_nativeHeight'] = nativeHeight;
- proto['data-path'] = source;
- proto.data_contentSize = contentSize ? contentSize : undefined;
- return data !== undefined;
+ return document;
};
export const ExportHierarchyToFileSystem = async (collection: Doc): Promise<void> => {
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 7003485d2..f3da133c3 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -310,7 +310,8 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
*/
@action
onRadiusDown = (e: React.PointerEvent): void => {
- this._isRounding = DocumentView.Interacting = true;
+ SnappingManager.SetIsResizing(SelectionManager.Docs.lastElement());
+ this._isRounding = true;
this._resizeUndo = UndoManager.StartBatch('DocDecs set radius');
setupMoveUpEvents(
this,
@@ -327,7 +328,8 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
return false;
},
action(e => {
- DocumentView.Interacting = this._isRounding = false;
+ SnappingManager.SetIsResizing(undefined);
+ this._isRounding = false;
this._resizeUndo?.end();
}), // upEvent
emptyFunction,
@@ -439,10 +441,9 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
@action
onPointerDown = (e: React.PointerEvent): void => {
- SnappingManager.SetIsResizing(SelectionManager.Docs.lastElement());
+ SnappingManager.SetIsResizing(SelectionManager.Docs.lastElement()); // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them
setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
e.stopPropagation();
- DocumentView.Interacting = true; // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them
this._resizeHdlId = e.currentTarget.className;
const bounds = e.currentTarget.getBoundingClientRect();
this._offset = { x: this._resizeHdlId.toLowerCase().includes('left') ? bounds.right - e.clientX : bounds.left - e.clientX, y: this._resizeHdlId.toLowerCase().includes('top') ? bounds.bottom - e.clientY : bounds.top - e.clientY };
@@ -595,7 +596,6 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
onPointerUp = (e: PointerEvent): void => {
SnappingManager.SetIsResizing(undefined);
SnappingManager.clearSnapLines();
- DocumentView.Interacting = false;
this._resizeHdlId = '';
this._resizeUndo?.end();
@@ -766,7 +766,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
top: bounds.y - this._resizeBorderWidth / 2,
transformOrigin,
background: SnappingManager.ShiftKey ? undefined : 'yellow',
- pointerEvents: SnappingManager.ShiftKey || DocumentView.Interacting ? 'none' : 'all',
+ pointerEvents: SnappingManager.ShiftKey || SnappingManager.IsResizing ? 'none' : 'all',
display: SelectionManager.Views.length <= 1 || hideDecorations ? 'none' : undefined,
transform: `rotate(${rotation}deg)`,
}}
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 5ec9a7d46..166afc0c2 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -102,7 +102,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> {
x: newPoint[0],
y: newPoint[1],
});
- ImageUtils.ExtractExif(doc);
+ ImageUtils.ExtractImgInfo(doc);
this._addDocument?.(doc);
})();
}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index b56973dc6..2ff435ce2 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -4,11 +4,11 @@ import * as rp from 'request-promise';
import { Utils, returnFalse } from '../../../Utils';
import CursorField from '../../../fields/CursorField';
import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc';
-import { AclPrivate } from '../../../fields/DocSymbols';
+import { AclPrivate, DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
-import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { WebField } from '../../../fields/URLField';
import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
import { GestureUtils } from '../../../pen-gestures/GestureUtils';
@@ -309,20 +309,19 @@ export function CollectionSubView<X>(moreProps?: X) {
const cors = img.includes('corsProxy') ? img.match(/http.*corsProxy\//)![0] : '';
img = cors ? img.replace(cors, '') : img;
if (img) {
- const split = img.split('src="')[1].split('"')[0];
- let source = split;
- if (split.startsWith('data:image') && split.includes('base64')) {
- const [{ accessPaths }] = await Networking.PostToServer('/uploadRemoteImage', { sources: [split] });
- if (accessPaths.agnostic.client.indexOf('dashblobstore') === -1) {
- source = Utils.prepend(accessPaths.agnostic.client);
- } else {
- source = accessPaths.agnostic.client;
- }
- }
- if (source.startsWith('http')) {
- const doc = Docs.Create.ImageDocument(source, { ...options, _width: 300 });
- ImageUtils.ExtractExif(doc);
- addDocument(doc);
+ const imgSrc = img.split('src="')[1].split('"')[0];
+ const imgOpts = { ...options, _width: 300 };
+ if (imgSrc.startsWith('data:image') && imgSrc.includes('base64')) {
+ const result = (await Networking.PostToServer('/uploadRemoteImage', { sources: [imgSrc] })).lastElement();
+ const newImgSrc =
+ result.accessPaths.agnostic.client.indexOf('dashblobstore') === -1 //
+ ? Utils.prepend(result.accessPaths.agnostic.client)
+ : result.accessPaths.agnostic.client;
+
+ addDocument(ImageUtils.AssignImgInfo(Docs.Create.ImageDocument(newImgSrc, imgOpts), result));
+ } else if (imgSrc.startsWith('http')) {
+ const doc = Docs.Create.ImageDocument(imgSrc, imgOpts);
+ addDocument(ImageUtils.AssignImgInfo(doc, await ImageUtils.ExtractImgInfo(doc)));
}
return;
} else {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 1574deede..645e9cff7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1190,7 +1190,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@computed get childPointerEvents() {
const engine = this._props.layoutEngine?.() || StrCast(this._props.Document._layoutEngine);
- const pointerevents = DocumentView.Interacting
+ const pointerevents = SnappingManager.IsResizing
? 'none'
: this._props.childPointerEvents?.() ??
(this._props.viewDefDivClick || //
@@ -1479,7 +1479,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._disposers.pointerevents = reaction(
() => {
const engine = this._props.layoutEngine?.() || StrCast(this._props.Document._layoutEngine);
- return DocumentView.Interacting
+ return SnappingManager.IsResizing
? 'none'
: this._props.childPointerEvents?.() ??
(this._props.viewDefDivClick || //
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 2752fa7f5..823ec885b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1336,7 +1336,6 @@ export class DocumentView extends ObservableReactComponent<DocumentViewProps> {
public set SELECTED(val) {
runInAction(() => (this._selected = val));
}
- @observable public static Interacting = false;
@observable public static LongPress = false;
@observable public static ExploreMode = false;
@observable public static LastPressedSidebarBtn: Opt<Doc>; // bcz: this is a hack to handle highlighting buttons in the leftpanel menu .. need to find a cleaner approach
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index df73dffe4..f205dbd56 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -630,7 +630,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
() => !this._playing && this.Seek(NumCast(this.layoutDoc._layout_currentTimecode))
);
this._disposers.youtubeReactionDisposer = reaction(
- () => Doc.ActiveTool === InkTool.None && this._props.isSelected() && !SnappingManager.IsDragging && !DocumentView.Interacting,
+ () => Doc.ActiveTool === InkTool.None && this._props.isSelected() && !SnappingManager.IsDragging && !SnappingManager.IsResizing,
interactive => (iframe.style.pointerEvents = interactive ? 'all' : 'none'),
{ fireImmediately: true }
);
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 2522a674d..758c919d2 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -220,7 +220,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
} // else it's an HTMLfield
} else if (this.webField && !this.dataDoc.text) {
WebRequest.get(Utils.CorsProxy(this.webField.href)) //
- .then(result => result && (this.dataDoc.text = htmlToText.convert(result.content)));
+ .then(result => result && (this.dataDoc.text = htmlToText(result.content)));
}
this._disposers.scrollReaction = reaction(
@@ -491,12 +491,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
this.layoutDoc.height = Math.min(NumCast(this.layoutDoc._height), (NumCast(this.layoutDoc._width) * NumCast(this.Document.nativeHeight)) / NumCast(this.Document.nativeWidth));
}
};
- const swidth = Math.max(NumCast(this.layoutDoc.nativeWidth), iframeContent.body.scrollWidth || 0);
+ const swidth = Math.max(NumCast(this.Document.nativeWidth), iframeContent.body.scrollWidth || 0);
if (swidth) {
- const aspectResize = swidth / NumCast(this.Document.nativeWidth);
- this.Document.nativeWidth = swidth;
- this.Document.nativeHeight = NumCast(this.Document.nativeHeight) * aspectResize;
+ const aspectResize = swidth / NumCast(this.Document.nativeWidth, swidth);
this.layoutDoc.height = NumCast(this.layoutDoc._height) * aspectResize;
+ this.Document.nativeWidth = swidth;
+ this.Document.nativeHeight = (swidth * NumCast(this.layoutDoc._height)) / NumCast(this.layoutDoc._width);
}
initHeights();
this._iframetimeout && clearTimeout(this._iframetimeout);
@@ -813,7 +813,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
key={this._warning}
className="webBox-iframe"
ref={action((r: HTMLIFrameElement | null) => (this._iframe = r))}
- style={{ pointerEvents: DocumentView.Interacting ? 'none' : undefined }}
+ style={{ pointerEvents: SnappingManager.IsResizing ? 'none' : undefined }}
src={url}
onLoad={this.iframeLoaded}
scrolling="no" // ugh.. on windows, I get an inner scroll bar for the iframe's body even though the scrollHeight should be set to the full height of the document.
diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx
index 093806127..f1739a41a 100644
--- a/src/client/views/webcam/DashWebRTCVideo.tsx
+++ b/src/client/views/webcam/DashWebRTCVideo.tsx
@@ -12,6 +12,7 @@ import { FieldView, FieldViewProps } from '../nodes/FieldView';
import './DashWebRTCVideo.scss';
import { hangup, initialize, refreshVideos } from './WebCamLogic';
import * as React from 'react';
+import { SnappingManager } from '../../util/SnappingManager';
/**
* This models the component that will be rendered, that can be used as a doc that will reflect the video cams.
@@ -71,8 +72,8 @@ export class DashWebRTCVideo extends React.Component<CollectionFreeFormDocumentV
</div>
);
- const frozen = !this.props.isSelected() || DocumentView.Interacting;
- const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !DocumentView.Interacting ? '-interactive' : '');
+ const frozen = !this.props.isSelected() || SnappingManager.IsResizing;
+ const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !SnappingManager.IsResizing ? '-interactive' : '');
return (
<>