From 865478933af8166cb699cbe4a2653aa52423dedb Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 29 Dec 2023 01:01:55 -0500 Subject: 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. --- src/client/apis/youtube/YoutubeBox.tsx | 5 +-- src/client/documents/Documents.ts | 1 + src/client/util/Import & Export/ImageUtils.ts | 41 ++++++++++++---------- src/client/views/DocumentDecorations.tsx | 12 +++---- src/client/views/PreviewCursor.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 31 ++++++++-------- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +-- src/client/views/nodes/DocumentView.tsx | 1 - src/client/views/nodes/VideoBox.tsx | 2 +- src/client/views/nodes/WebBox.tsx | 12 +++---- src/client/views/webcam/DashWebRTCVideo.tsx | 5 +-- 11 files changed, 61 insertions(+), 55 deletions(-) (limited to 'src/client') 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 { ); - 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 ( <>
{content}
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 => { + export type imgInfo = { + contentSize: number; + nativeWidth: number; + nativeHeight: number; + source: string; + exifData: { error: string | undefined; data: string }; + }; + export const ExtractImgInfo = async (document: Doc): Promise => { 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 => { 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 { - 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 { - 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 { - 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 { SnappingManager.SetIsResizing(undefined); SnappingManager.clearSnapLines(); - DocumentView.Interacting = false; this._resizeHdlId = ''; this._resizeUndo?.end(); @@ -766,7 +766,7 @@ export class DocumentDecorations 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(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 { 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 { 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; // 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 !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 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 (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 ); - 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 ( <> -- cgit v1.2.3-70-g09d2