From 3c1b393732ef9dc704a2f40b103c37b3f8370ba7 Mon Sep 17 00:00:00 2001
From: Aubrey Li <Aubrey-Li>
Date: Tue, 26 Oct 2021 17:16:10 -0400
Subject: update Mapbox image rendering

---
 src/client/views/nodes/MapBox/MapBox.tsx | 207 +++++++++++++++++++++++--------
 1 file changed, 157 insertions(+), 50 deletions(-)

(limited to 'src/client/views/nodes/MapBox/MapBox.tsx')

diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 0cf3ae326..4c0c4c0c7 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -2,11 +2,11 @@ import { Autocomplete, GoogleMap, GoogleMapProps, InfoWindow, Marker } from '@re
 import { action, computed, IReactionDisposer, observable, ObservableMap } from 'mobx';
 import { observer } from "mobx-react";
 import * as React from "react";
-import { DataSym, Doc, DocListCast, FieldsSym, WidthSym } from '../../../../fields/Doc';
+import { DataSym, Doc, DocListCast, FieldsSym, Opt, WidthSym } from '../../../../fields/Doc';
 import { documentSchema } from '../../../../fields/documentSchemas';
 import { makeInterface } from '../../../../fields/Schema';
 import { NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, setupMoveUpEvents } from '../../../../Utils';
+import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
@@ -21,6 +21,13 @@ import { identity } from 'lodash';
 import { Id } from '../../../../fields/FieldSymbols';
 import { Colors } from '../../global/globalEnums';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { TraceMobx } from '../../../../fields/util';
+import { SnappingManager } from '../../../util/SnappingManager';
+import { InkTool } from '../../../../fields/InkField';
+import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
+import { CollectionFreeFormView } from '../../collections/collectionFreeForm';
+import { MarqueeAnnotator } from '../../MarqueeAnnotator';
+import { Annotation } from '../../pdf/Annotation';
 
 type MapDocument = makeInterface<[typeof documentSchema]>;
 const MapDocument = makeInterface(documentSchema);
@@ -72,25 +79,34 @@ const options = {
 export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>, MapDocument>(MapDocument) {
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _disposers: { [name: string]: IReactionDisposer } = {};
+    private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
+    @observable private _overlayAnnoInfo: Opt<Doc>;
+    showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno);
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(MapBox, fieldKey); }
+    public get SidebarKey() { return this.fieldKey + "-sidebar"; }
+    private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
+    @computed get inlineTextAnnotations() { return this.allMapMarkers.filter(a => a.textInlineAnnotations); }
 
     @observable private _map: google.maps.Map = null as unknown as google.maps.Map;
-    @observable private selectedPlace: MapMarker | undefined;
+    @observable private selectedPlace: MapMarker | Doc | undefined;
     @observable private markerMap: { [id: string]: google.maps.Marker } = {};
     @observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter;
     @observable private zoom = 2.5;
     @observable private infoWindowOpen = false;
+    @observable private _marqueeing: number[] | undefined;
+    @observable private _isAnnotating = false;
     @observable private bounds = new window.google.maps.LatLngBounds();
     @observable private inputRef = React.createRef<HTMLInputElement>();
     @observable private searchMarkers: google.maps.Marker[] = [];
     @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.SidebarKey]); };
-    @computed get allMapMarkers() { return DocListCast(this.dataDoc[this.annotationKey]); };
-    @observable private allMarkers: Doc[] = [];
+    @computed get allMapMarkers() { return DocListCast(this.dataDoc[this.annotationKey]); }; // method to add MapMarker to allMapMarkers
     //TODO: change all markers to a filter function to change 
     @observable private toggleAddMarker = false;
 
+    private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
+
 
     @observable _showSidebar = false;
     @computed get SidebarShown() { return this._showSidebar || this.layoutDoc._showSidebar ? true : false; }
@@ -112,7 +128,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     // iterate allMarkers to size, center, and zoom map to contain all markers
     private fitBounds = (map: google.maps.Map) => {
         console.log('map bound is:' + this.bounds);
-        this.allMarkers.map(place => {
+        this.allMapMarkers.map(place => {
             this.bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) });
             return place._markerId;
         });
@@ -185,29 +201,28 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         });
         map.panTo(position);
         const mapMarker = Docs.Create.MapMarkerDocument(NumCast(position.lat()), NumCast(position.lng()), [], {});
-        this.allMarkers.push(mapMarker);
+        this.addDocument(mapMarker, this.annotationKey);
+        // Doc.AddDocToList(this.dataDoc, this.annotationKey, mapMarker);
     }
 
     /**
      * A function that examines allMapMarkers docs in the map node and form MapMarkers
      */
-    private fillMarkers = () => {
-        this.allMapMarkers?.forEach(doc => {
-            console.log(doc);
-            // search for if the map marker exists, else create marker
-            if (doc.lat !== undefined && doc.lng !== undefined) {
-                console.log("image found! loading into marker document...")
-                const marker = Docs.Create.MapMarkerDocument(NumCast(doc.lat), NumCast(doc.lng), [doc], {})
-                this.allMarkers.push(marker)
-            }
-        })
-    }
-
-    // TODO: things to ask & think about when designing
-    // 1. All markers are stored in allMarkers[], when adding a new marker (from a button, ideally not using drawManager),
-    // the new marker will be stored in allMarkers[]
-    // currently markerloadhandler only gets called when the map is reloaded, but we want it to be update on the GUI in real time
-    // TODO ** core issue --> real time updates ** 
+    // private fillMarkers = () => {
+    //     // console.log("allSidebarDocs:");
+    //     // console.log(this.allSidebarDocs);
+    //     this.allSidebarDocs?.forEach(doc => {
+    //         console.log(doc);
+    //         // search for if the map marker exists, else create marker
+    //         if (doc.lat !== undefined && doc.lng !== undefined) {
+    //             console.log("image found! loading into marker document...")
+    //             const marker = Docs.Create.MapMarkerDocument(NumCast(doc.lat), NumCast(doc.lng), [doc], {})
+    //             Doc.AddDocToList(this.dataDoc, this.annotationKey, marker) // add marker to annotation key
+    //         }
+    //     });
+    //     // console.log("allMarkers:")
+    //     // console.log(this.allMarkers);
+    // }
 
 
     /**
@@ -235,20 +250,19 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             );
         } else {
             alert("Your geolocation is not supported by browser.")
-        }
-        this.fitBounds(map);
-        console.log("content in the allMapsMarker array:");
-        console.log(this.allMapMarkers);
-        console.log("content in the sidebarDocs array:");
+        };
+        console.log("all sidebar docs during map loading is:")
         console.log(this.allSidebarDocs);
-        this.fillMarkers();
+        this.fitBounds(map);
+        // this.fillMarkers();
+
 
         // listener to addmarker event
         this._map.addListener('click', (e) => {
             console.log("add marker map status:" + this.toggleAddMarker);
             if (this.toggleAddMarker == true) {
                 this.placeMarker(e.latLng, map)
-                console.log(this.allMarkers)
+                console.log(this.allMapMarkers)
             }
         })
         // this._map.addListener(drawingManager, 'markercomplete', this.addMarker)
@@ -281,10 +295,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     @action
-    private markerClickHandler = (e: MouseEvent, place: any) => {
+    private markerClickHandler = (e: MouseEvent, place: Doc) => {
         // set which place was clicked
         this.selectedPlace = place;
 
+        console.log("you have selected this location:");
         console.log(this.selectedPlace);
 
         // used so clicking a second marker works
@@ -303,6 +318,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
      * @returns 
      */
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
+        console.log("print all sidebar Docs");
+        console.log(this.allSidebarDocs);
         if (!this.layoutDoc._showSidebar) this.toggleSidebar();
         const docs = doc instanceof Doc ? [doc] : doc
         docs.forEach(doc => {
@@ -311,11 +328,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this.addDocument(marker, this.annotationKey)
             }
         }) //add to annotation list
+        console.log("sidebaraddDocument");
+        console.log(doc);
+
         return this.addDocument(doc, sidebarKey); // add to sidebar list
     }
 
     /**
-     * What does this do exactly? How to operate on sidebar?
+     * Toggle sidebar onclick the tiny comment button on the top right corner
      * @param e 
      */
     sidebarBtnDown = (e: React.PointerEvent) => {
@@ -397,16 +417,26 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.selectedPlace = undefined;
     }
 
-    public get SidebarKey() { return this.fieldKey + "-sidebar"; }
-
     @computed get sidebarHandle() {
+        TraceMobx();
         const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length;
-        //&& !this.isContentActive()
-        return (!annotated) ? (null) : <div className="mapBox-sidebar-handle" onPointerDown={this.sidebarDown}
-            style={{
-                left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${this.sidebarWidth() ? "- 5px" : "- 10px"}))`,
-                background: this.props.styleProvider?.(this.rootDoc, this.props as any, StyleProp.WidgetColor + (annotated ? ":annotated" : ""))
-            }} />;
+        const color = !annotated ? Colors.WHITE : Colors.BLACK;
+        const backgroundColor = !annotated ? this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK : this.props.styleProvider?.(this.rootDoc, this.props as any, StyleProp.WidgetColor + (annotated ? ":annotated" : ""));
+        return (!annotated) ? (null) :
+            <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown}
+                style={{
+                    left: `max(0px, calc(100% - ${this.sidebarWidthPercent} - 17px))`,
+                    backgroundColor: backgroundColor,
+                    color: color,
+                    opacity: annotated ? 1 : undefined
+                }} >
+                <FontAwesomeIcon icon={"comment-alt"} />
+            </div>;
+        // return (!annotated) ? (null) : <div className="mapBox-sidebar-handle" onPointerDown={this.sidebarDown}
+        //     style={{
+        //         left: `max(0px, calc(100% - ${this.sidebarWidthPercent} ${this.sidebarWidth() ? "- 5px" : "- 10px"}))`,
+        //         background: this.props.styleProvider?.(this.rootDoc, this.props as any, StyleProp.WidgetColor + (annotated ? ":annotated" : ""))
+        //     }} />;
     }
 
     @action
@@ -415,6 +445,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         this.layoutDoc._showSidebar = ((this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, "0%") === "0%" ? "50%" : "0%")) !== "0%";
         this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
     }
+
     sidebarDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), false);
     }
@@ -426,7 +457,29 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return false;
     }
 
-    // TODO what's the difference between savedAnnotations & allMapMarkers?
+    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func;
+
+    @action
+    onMarqueeDown = (e: React.PointerEvent) => {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+            setupMoveUpEvents(this, e, action(e => {
+                MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+                this._marqueeing = [e.clientX, e.clientY];
+                return true;
+            }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false);
+        }
+    }
+    @action finishMarquee = (x?: number, y?: number) => {
+        this._marqueeing = undefined;
+        this._isAnnotating = false;
+        x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false);
+    }
+
+    addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
+        return this.addDocument(doc, annotationKey);
+    }
+
+
     getAnchor = () => {
         const anchor =
             AnchorMenu.Instance?.GetAnchor(this._savedAnnotations) ??
@@ -440,12 +493,9 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return anchor;
     }
 
-    private saveMarkerInfo = () => {
-
-    }
     // create marker prop --> func that 
     private renderMarkers = () => {
-        return this.allMarkers.map(place => (
+        return this.allMapMarkers.map(place => (
             <Marker
                 key={place[Id]}
                 position={{ lat: NumCast(place.lat), lng: NumCast(place.lng) }}
@@ -458,7 +508,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private renderInfoWindow = () => {
         return this.infoWindowOpen && this.selectedPlace && (
             <InfoWindow
-                anchor={this.markerMap[this.selectedPlace._markerId!]}
+                // anchor={this.markerMap[this.selectedPlace.Id!]}
                 onCloseClick={this.handleInfoWindowClose}
             >
                 <div style={{ backgroundColor: 'white', opacity: 0.75, padding: 12 }}>
@@ -485,7 +535,48 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         )
     }
 
+    panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
+    panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
+    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
+    transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
+    opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
+
+    anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
+
+    @computed get annotationLayer() {
+        TraceMobx();
+        return <div className="webBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
+            {this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
+                <Annotation {...this.props} fieldKey={this.annotationKey} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />)
+            }
+        </div>;
+
+    }
+
     render() {
+        const renderAnnotations = (docFilters?: () => string[]) =>
+            <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
+                renderDepth={this.props.renderDepth + 1}
+                isAnnotationOverlay={true}
+                fieldKey={this.annotationKey}
+                CollectionView={undefined}
+                setPreviewCursor={this.setPreviewCursor}
+                PanelWidth={this.panelWidth}
+                PanelHeight={this.panelHeight}
+                ScreenToLocalTransform={this.scrollXf}
+                scaling={returnOne}
+                dropAction={"alias"}
+                docFilters={docFilters || this.props.docFilters}
+                dontRenderDocuments={docFilters ? false : true}
+                select={emptyFunction}
+                ContentScaling={returnOne}
+                bringToFront={emptyFunction}
+                whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                removeDocument={this.removeDocument}
+                moveDocument={this.moveDocument}
+                addDocument={this.sidebarAddDocument}
+                childPointerEvents={true}
+                pointerEvents={CurrentUserUtils.SelectedTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
         return <div className="mapBox" ref={this._ref}
         // style={{ pointerEvents: this.isContentActive() ? undefined : "none" }} 
         >
@@ -497,10 +588,16 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 onWheel={e => e.stopPropagation()}
                 onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()}
                 style={{ width: `calc(100% - ${this.sidebarWidthPercent})` }}>
+
+                <div style={{ mixBlendMode: "multiply" }}>
+                    {renderAnnotations(this.transparentFilter)}
+                </div>
+                {renderAnnotations(this.opaqueFilter)}
+                {SnappingManager.GetIsDragging() ? (null) : renderAnnotations()}
+                {this.annotationLayer}
                 <GoogleMap
                     mapContainerStyle={mapContainerStyle}
                     zoom={this.zoom}
-                    // center={this.center}
                     onLoad={map => this.loadHandler(map)}
                     options={mapOptions}
                 >
@@ -513,13 +610,24 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     {this.renderMarkers()}
                     {this.renderInfoWindow()}
                 </GoogleMap>
+                {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
+                    <MarqueeAnnotator rootDoc={this.rootDoc}
+                        anchorMenuClick={this.anchorMenuClick}
+                        scrollTop={0}
+                        down={this._marqueeing} scaling={returnOne}
+                        addDocument={this.addDocumentWrapper}
+                        docView={this.props.docViewPath().lastElement()}
+                        finishMarquee={this.finishMarquee}
+                        savedAnnotations={this._savedAnnotations}
+                        annotationLayer={this._annotationLayer.current}
+                        mainCont={this._mainCont.current} />}
             </div>
             {/* {/* </LoadScript > */}
             <div className="mapBox-sidebar"
                 style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
                 <SidebarAnnos ref={this._sidebarRef}
                     {...this.props}
-                    fieldKey={this.annotationKey}
+                    fieldKey={this.fieldKey}
                     rootDoc={this.rootDoc}
                     layoutDoc={this.layoutDoc}
                     dataDoc={this.dataDoc}
@@ -530,7 +638,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     sidebarAddDocument={this.sidebarAddDocument}
                     moveDocument={this.moveDocument}
                     removeDocument={this.removeDocument}
-                //isContentActive={this.isContentActive}
                 />
             </div>
             <div className="mapBox-overlayButton-sidebar" key="sidebar" title="Toggle Sidebar"
-- 
cgit v1.2.3-70-g09d2

