diff --git a/packages/components/src/MapWithPin/MapPin.stories.tsx b/packages/components/src/MapWithPin/MapPin.stories.tsx index f9bcd27225..f3b5751195 100644 --- a/packages/components/src/MapWithPin/MapPin.stories.tsx +++ b/packages/components/src/MapWithPin/MapPin.stories.tsx @@ -12,7 +12,6 @@ export const Default: StoryFn = () => { return ( { position.lng = lng }} diff --git a/packages/components/src/MapWithPin/MapPin.tsx b/packages/components/src/MapWithPin/MapPin.tsx index 6e75ab7306..5300e9028a 100644 --- a/packages/components/src/MapWithPin/MapPin.tsx +++ b/packages/components/src/MapWithPin/MapPin.tsx @@ -4,6 +4,8 @@ import L from 'leaflet' import customMarkerIcon from '../../assets/icons/map-marker.png' +import type { DivIcon } from 'leaflet' + const customMarker = L.icon({ iconUrl: customMarkerIcon, iconSize: [20, 28], @@ -15,8 +17,9 @@ export interface IProps { lat: number lng: number } - draggable: boolean ondragend(lng: number): void + markerIcon?: DivIcon + onClick?: () => void } export const MapPin = (props: IProps) => { @@ -24,8 +27,8 @@ export const MapPin = (props: IProps) => { return ( { + draggable + ondrag={() => { const marker: any = markerRef.current if (!marker) { @@ -39,7 +42,8 @@ export const MapPin = (props: IProps) => { }} position={[props.position.lat, props.position.lng]} ref={markerRef} - icon={customMarker} + icon={props.markerIcon || customMarker} + onclick={props.onClick} /> ) } diff --git a/packages/components/src/MapWithPin/MapWithPin.stories.tsx b/packages/components/src/MapWithPin/MapWithPin.stories.tsx index 7b9273c667..119164dc47 100644 --- a/packages/components/src/MapWithPin/MapWithPin.stories.tsx +++ b/packages/components/src/MapWithPin/MapWithPin.stories.tsx @@ -1,6 +1,9 @@ +import React from 'react' + import { MapWithPin } from './MapWithPin' import type { Meta, StoryFn } from '@storybook/react' +import type { Map } from 'react-leaflet' export default { title: 'Map/MapWithPin', @@ -9,10 +12,12 @@ export default { export const Default: StoryFn = () => { const position = { lat: 0, lng: 0 } + const newMapRef = React.useRef(null) + return ( { position.lat = _position.lat position.lng = _position.lng diff --git a/packages/components/src/MapWithPin/MapWithPin.tsx b/packages/components/src/MapWithPin/MapWithPin.tsx index 3d8b9cbe67..e174297ab5 100644 --- a/packages/components/src/MapWithPin/MapWithPin.tsx +++ b/packages/components/src/MapWithPin/MapWithPin.tsx @@ -7,25 +7,29 @@ import { Map } from '../Map/Map' import { OsmGeocoding } from '../OsmGeocoding/OsmGeocoding' import { MapPin } from './MapPin' -import type { LeafletMouseEvent } from 'leaflet' +import type { DivIcon, LeafletMouseEvent } from 'leaflet' +import type { Map as MapType } from 'react-leaflet' import type { Result } from '../OsmGeocoding/types' import 'leaflet/dist/leaflet.css' const useUserLocation = 'Use my current location' const mapInstructions = - "You can click on the map, or drag the marker to adjust it's position." + "You can double click on the map to adjust it's position. Click on your pin to see the preview of your card." export interface Props { + mapRef: React.RefObject position: { lat: number lng: number } - draggable: boolean + markerIcon?: DivIcon updatePosition?: any center?: any zoom?: number hasUserLocation?: boolean + onClickMapPin?: () => void + popup?: React.ReactNode } export const MapWithPin = (props: Props) => { @@ -33,7 +37,7 @@ export const MapWithPin = (props: Props) => { const [center, setCenter] = React.useState( props.center || [props.position.lat, props.position.lng], ) - const { draggable, position } = props + const { mapRef, position, markerIcon, onClickMapPin, popup } = props const hasUserLocation = props.hasUserLocation || false const onPositionChanged = @@ -58,22 +62,20 @@ export const MapWithPin = (props: Props) => { ) } - const onClick = (evt: LeafletMouseEvent) => { + const onDblClick = (evt: LeafletMouseEvent) => { onPositionChanged({ ...evt.latlng }) } return ( - {draggable && ( - - {mapInstructions} - - )} + + {mapInstructions} +
{ zIndex: 2, }} > - {draggable && ( - - { - if (data.lat && data.lon) { - onPositionChanged({ - lat: data.lat, - lng: data.lon, - }) - setCenter([data.lat, data.lon]) - setZoom(15) - } + + { + if (data.lat && data.lon) { + onPositionChanged({ + lat: data.lat, + lng: data.lon, + }) + setCenter([data.lat, data.lon]) + setZoom(15) + } + }} + countrycodes="" + acceptLanguage="en" + /> + {hasUserLocation && ( + - )} - - )} + > + {useUserLocation} + + )} + + {popup} { if (evt.lat && evt.lng) onPositionChanged({ diff --git a/src/models/common.models.tsx b/src/models/common.models.tsx index 1bac0512b8..c4976751fc 100644 --- a/src/models/common.models.tsx +++ b/src/models/common.models.tsx @@ -23,7 +23,8 @@ export interface ILocation { postcode: string value: string } -interface ILatLng { + +export interface ILatLng { lat: number lng: number } diff --git a/src/models/index.ts b/src/models/index.ts index 1e01c604b1..7440a80db7 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -1,5 +1,3 @@ -import type { IComment } from './discussion.models' - export * from './common.models' export * from './discussion.models' export * from './howto.models' @@ -14,7 +12,3 @@ export * from './tags.model' export * from './user.models' export * from './moderation.model' export * from './userPreciousPlastic.models' - -export interface UserComment extends IComment { - isEditable: boolean -} diff --git a/src/pages/Maps/Content/MapView/Popup.tsx b/src/pages/Maps/Content/MapView/Popup.tsx index 1fe8717387..5c1c6b8e6e 100644 --- a/src/pages/Maps/Content/MapView/Popup.tsx +++ b/src/pages/Maps/Content/MapView/Popup.tsx @@ -5,6 +5,7 @@ import { MapMemberCard, PinProfile } from 'oa-components' import { IModerationStatus } from 'oa-shared' import { MAP_GROUPINGS } from 'src/stores/Maps/maps.groupings' +import type { ILatLng } from 'oa-shared' import type { Map } from 'react-leaflet' import type { IMapPin, IMapPinWithDetail } from 'src/models/maps.models' @@ -15,12 +16,13 @@ interface IProps { mapRef: React.RefObject newMap?: boolean onClose?: () => void + customPosition?: ILatLng } export const Popup = (props: IProps) => { const leafletRef = useRef(null) const activePin = props.activePin as IMapPinWithDetail - const { mapRef, newMap, onClose } = props + const { mapRef, newMap, onClose, customPosition } = props useEffect(() => { openPopup() @@ -47,8 +49,14 @@ export const Popup = (props: IProps) => { activePin.location && ( ({ }, mapsStore: { getPin: vi.fn().mockResolvedValue(mockPin), + getPinDetail: vi.fn().mockResolvedValue(mockPin), + }, + themeStore: { + currentTheme: { + id: 'string', + siteName: 'string', + logo: 'string', + badge: 'string', + avatar: 'string', + howtoHeading: 'string', + academyResource: 'string', + styles: { + communityProgramURL: '', + }, + }, }, }, }), diff --git a/src/pages/UserSettings/SettingsPageMapPin.tsx b/src/pages/UserSettings/SettingsPageMapPin.tsx index 8d6712cf32..c28d38b918 100644 --- a/src/pages/UserSettings/SettingsPageMapPin.tsx +++ b/src/pages/UserSettings/SettingsPageMapPin.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import React, { useEffect, useState } from 'react' import { Field, Form } from 'react-final-form' import { toJS } from 'mobx' import { @@ -22,14 +22,23 @@ import { randomIntFromInterval } from 'src/utils/helpers' import { required } from 'src/utils/validators' import { Alert, Box, Flex, Heading, Text } from 'theme-ui' +import { Popup } from '../Maps/Content/MapView/Popup' +import { createMarkerIcon } from '../Maps/Content/MapView/Sprites' import { SettingsFormNotifications } from './content/SettingsFormNotifications' import { MAX_PIN_LENGTH } from './constants' -import type { ILocation, IMapPin, IUserPPDB } from 'src/models' +import type { DivIcon } from 'leaflet' +import type { Map } from 'react-leaflet' +import type { + ILatLng, + ILocation, + IMapPinWithDetail, + IUserPPDB, +} from 'src/models' import type { IFormNotification } from './content/SettingsFormNotifications' interface IPinProps { - mapPin: IMapPin | undefined + mapPin: IMapPinWithDetail | undefined } interface ILocationProps { @@ -145,7 +154,9 @@ const DeleteMapPin = (props: IPropsDeletePin) => { } export const SettingsPageMapPin = () => { - const [mapPin, setMapPin] = useState() + const [mapPin, setMapPin] = useState() + const [markerIcon, setMarkerIcon] = useState() + const [showPopup, setShowPopup] = useState(false) const [isLoading, setIsLoading] = useState(true) const [notification, setNotification] = useState< IFormNotification | undefined @@ -153,17 +164,28 @@ export const SettingsPageMapPin = () => { const { mapsStore, themeStore, userStore } = useCommonStores().stores const user = userStore.activeUser + const isMember = user?.profileType === ProfileTypeList.MEMBER + const { addPinTitle, yourPinTitle } = headings.map const formId = 'MapSection' - const isMember = user?.profileType === ProfileTypeList.MEMBER + + const currentTheme = themeStore.currentTheme + + const newMapRef = React.useRef(null) useEffect(() => { const init = async () => { if (!user) return - const pin = (await mapsStore.getPin(user.userName)) || null - setMapPin(pin) + const pin = await mapsStore.getPin(user.userName) + if (!pin) return + + const pinDetail = await mapsStore.getPinDetail(pin) + if (!pinDetail) return + + setMapPin(pinDetail) + setMarkerIcon(createMarkerIcon(pin, currentTheme)) setIsLoading(false) } @@ -223,7 +245,7 @@ export const SettingsPageMapPin = () => { > - {mapPin ? addPinTitle : yourPinTitle} + {mapPin ? yourPinTitle : addPinTitle} {isMember && ( { return ( { + updatePosition={(newPosition: ILatLng) => { onChange({ latlng: newPosition }) }} + markerIcon={markerIcon} + zoom={mapPin ? 15 : 1} + onClickMapPin={() => setShowPopup(!showPopup)} + popup={ + mapPin && showPopup ? ( + setShowPopup(!showPopup)} + customPosition={location.latlng || undefined} + /> + ) : undefined + } /> ) }} diff --git a/src/stores/Maps/maps.store.ts b/src/stores/Maps/maps.store.ts index 2958bb26fa..1e9abdc258 100644 --- a/src/stores/Maps/maps.store.ts +++ b/src/stores/Maps/maps.store.ts @@ -140,7 +140,7 @@ export class MapsStore extends ModuleStore { } } // call additional action when pin detail received to inform mobx correctly of update - private async getPinDetail(pin: IMapPin) { + public async getPinDetail(pin: IMapPin) { const detail: IMapPinDetail = await this.getUserProfilePin(pin._id) const pinWithDetail: IMapPinWithDetail = { ...pin, detail } return pinWithDetail