11/* eslint-disable react/destructuring-assignment */
22// @ts -check
33import * as React from 'react'
4- import { Marker , Popup , Circle } from 'react-leaflet'
4+ import { Marker , Popup , Circle , Polygon } from 'react-leaflet'
55import { t } from 'i18next'
6+ import { S2CellId , S2LatLng } from 'nodes2ts'
67
78import { useMarkerTimer } from '@hooks/useMarkerTimer'
8- import { getOffset } from '@utils/offset'
9+ import { NEARBY_CELL_LEVEL , getOffset } from '@utils/offset'
910import { getBadge } from '@utils/getBadge'
1011import { basicEqualFn , useMemory } from '@store/useMemory'
1112import { useStorage } from '@store/useStorage'
@@ -16,10 +17,14 @@ import { sendNotification } from '@services/desktopNotification'
1617import { useMapStore } from '@store/useMapStore'
1718import { TooltipWrapper } from '@components/ToolTipWrapper'
1819import { getTimeUntil } from '@utils/getTimeUntil'
20+ import { normalizeCategory } from '@utils/normalizeCategory'
21+ import { getS2Polygon } from '@utils/getS2Polygon'
1922
2023import { PokemonPopup } from './PokemonPopup'
2124import { basicPokemonMarker , fancyPokemonMarker } from './pokemonMarker'
2225
26+ const INTERACTION_RANGE_COLOR = '#BA42F6'
27+
2328/**
2429 *
2530 * @param {import('@rm/types').Pokemon } pkmn
@@ -110,18 +115,31 @@ const BasePokemonTile = (pkmn) => {
110115 ]
111116 } , basicEqualFn )
112117
118+ const manualParams = useMemory ( ( s ) => s . manualParams )
119+
120+ const isNearbyStop = pkmn . seen_type === 'nearby_stop'
121+ const isLure = pkmn . seen_type ?. includes ( 'lure' )
122+ const isNearbyCell = pkmn . seen_type === 'nearby_cell'
123+
124+ const isPopupOpen = React . useMemo ( ( ) => {
125+ if ( ! manualParams ) return false
126+ if ( normalizeCategory ( manualParams . category ) !== 'pokemon' ) {
127+ return false
128+ }
129+ return `${ manualParams . id } ` === `${ pkmn . id } `
130+ } , [ manualParams , pkmn . id ] )
131+
113132 /** @type {[number, number] } */
114- const finalLocation = React . useMemo (
115- ( ) =>
116- pkmn . seen_type ?. startsWith ( 'nearby' ) || pkmn . seen_type ?. includes ( 'lure' )
117- ? getOffset (
118- [ pkmn . lat , pkmn . lon ] ,
119- pkmn . seen_type === 'nearby_cell' ? 0.0002 : 0.00015 ,
120- pkmn . id ,
121- )
122- : [ pkmn . lat , pkmn . lon ] ,
123- [ pkmn . seen_type , pkmn . lat , pkmn . lon ] ,
124- )
133+ const finalLocation = React . useMemo ( ( ) => {
134+ const seenType = pkmn . seen_type
135+ return seenType ?. startsWith ( 'nearby' )
136+ ? getOffset ( {
137+ coords : /** @type {[number, number] } */ ( [ pkmn . lat , pkmn . lon ] ) ,
138+ id : pkmn . id ,
139+ seenType,
140+ } )
141+ : [ pkmn . lat , pkmn . lon ]
142+ } , [ pkmn . id , pkmn . seen_type , pkmn . lat , pkmn . lon ] )
125143
126144 /** @type {(string | import('react').ReactElement)[] } */
127145 const extras = React . useMemo ( ( ) => {
@@ -140,6 +158,18 @@ const BasePokemonTile = (pkmn) => {
140158 useForcePopup ( pkmn . id , markerRef )
141159 useMarkerTimer ( pkmn . expire_timestamp , markerRef )
142160 const handlePopupOpen = useManualPopupTracker ( 'pokemon' , pkmn . id )
161+
162+ const nearbyCellPolygon = React . useMemo ( ( ) => {
163+ if ( isNearbyCell ) {
164+ return getS2Polygon (
165+ S2CellId . fromPoint (
166+ S2LatLng . fromDegrees ( pkmn . lat , pkmn . lon ) . toPoint ( ) ,
167+ ) . parentL ( NEARBY_CELL_LEVEL ) ,
168+ )
169+ }
170+ return null
171+ } , [ isNearbyCell , pkmn . lat , pkmn . lon ] )
172+
143173 sendNotification (
144174 pkmn . id ,
145175 `${ t ( `poke_${ pkmn . pokemon_id } ` ) } ${
@@ -164,70 +194,110 @@ const BasePokemonTile = (pkmn) => {
164194 }
165195
166196 return (
167- < Marker
168- ref = { setMarkerRef }
169- zIndexOffset = {
170- ( typeof pkmn . iv === 'number' ? pkmn . iv || 99 : 0 ) * 100 +
171- 40.96 -
172- pkmn . bestPvp
173- }
174- position = { finalLocation }
175- icon = {
176- ( pkmn . bestPvp !== null && pkmn . bestPvp < 4 && extras . length === 0 ) ||
177- showGlow ||
178- showWeather ||
179- opacity < 1 ||
180- pkmn . seen_type === 'nearby_cell'
181- ? fancyPokemonMarker ( {
182- pkmn,
183- iconUrl,
184- iconSize,
185- showGlow,
186- showWeather,
187- badge : extras . length ? null : badge ,
188- opacity,
189- timeOfDay,
190- } )
191- : basicPokemonMarker ( { iconUrl, iconSize } )
192- }
193- eventHandlers = { { popupopen : handlePopupOpen } }
194- >
195- < Popup position = { finalLocation } >
196- < PokemonPopup pokemon = { pkmn } iconUrl = { iconUrl } />
197- </ Popup >
198- { ( showTimer || timerOverride || extras . length > 0 ) && (
199- < TooltipWrapper
200- timers = { showTimer || timerOverride ? [ pkmn . expire_timestamp ] : [ ] }
201- offset = { [ 0 , 14 ] }
202- >
203- { extras . length > 0 && (
204- < div className = "iv-badge flex-center" >
205- { extras . map ( ( val , i ) => (
206- < span
207- key = { typeof val === 'string' ? val : val . key }
208- className = "flex-center"
209- >
210- { i ? < > | </ > : null }
211- { val }
212- </ span >
213- ) ) }
214- </ div >
215- ) }
216- </ TooltipWrapper >
217- ) }
218- { showInteractionRange && configZoom && (
219- < Circle center = { finalLocation } radius = { 40 } color = "#BA42F6" weight = { 1 } />
220- ) }
221- { showSpacialRendRange && configZoom && (
197+ < >
198+ { isPopupOpen && ! ! nearbyCellPolygon ? (
199+ < Polygon
200+ positions = { nearbyCellPolygon }
201+ pathOptions = { {
202+ color : INTERACTION_RANGE_COLOR ,
203+ weight : 1 ,
204+ opacity : 1 ,
205+ fillColor : INTERACTION_RANGE_COLOR ,
206+ fillOpacity : 0.2 ,
207+ } }
208+ interactive = { false }
209+ />
210+ ) : null }
211+ { isPopupOpen && isNearbyStop ? (
222212 < Circle
223- center = { finalLocation }
224- radius = { 80 }
225- color = "#4E893E"
226- dashArray = "5, 5"
227- weight = { 1 }
213+ center = { [ pkmn . lat , pkmn . lon ] }
214+ radius = { 40 }
215+ pathOptions = { {
216+ color : INTERACTION_RANGE_COLOR ,
217+ weight : 1 ,
218+ opacity : 1 ,
219+ fillColor : INTERACTION_RANGE_COLOR ,
220+ fillOpacity : 0.2 ,
221+ } }
222+ interactive = { false }
228223 />
229- ) }
230- </ Marker >
224+ ) : null }
225+ < Marker
226+ ref = { setMarkerRef }
227+ zIndexOffset = {
228+ ( typeof pkmn . iv === 'number' ? pkmn . iv || 99 : 0 ) * 100 +
229+ 40.96 -
230+ pkmn . bestPvp
231+ }
232+ position = { finalLocation }
233+ icon = {
234+ ( pkmn . bestPvp !== null && pkmn . bestPvp < 4 && extras . length === 0 ) ||
235+ showGlow ||
236+ showWeather ||
237+ opacity < 1 ||
238+ pkmn . seen_type === 'nearby_cell'
239+ ? fancyPokemonMarker ( {
240+ pkmn,
241+ iconUrl,
242+ iconSize,
243+ showGlow,
244+ showWeather,
245+ badge : extras . length ? null : badge ,
246+ opacity,
247+ timeOfDay,
248+ } )
249+ : basicPokemonMarker ( { iconUrl, iconSize } )
250+ }
251+ eventHandlers = { { popupopen : handlePopupOpen } }
252+ >
253+ < Popup position = { finalLocation } >
254+ < PokemonPopup pokemon = { pkmn } iconUrl = { iconUrl } />
255+ </ Popup >
256+ { ( showTimer || timerOverride || extras . length > 0 ) && (
257+ < TooltipWrapper
258+ timers = { showTimer || timerOverride ? [ pkmn . expire_timestamp ] : [ ] }
259+ offset = { [ 0 , 14 ] }
260+ >
261+ { extras . length > 0 && (
262+ < div className = "iv-badge flex-center" >
263+ { extras . map ( ( val , i ) => (
264+ < span
265+ key = { typeof val === 'string' ? val : val . key }
266+ className = "flex-center"
267+ >
268+ { i ? < > | </ > : null }
269+ { val }
270+ </ span >
271+ ) ) }
272+ </ div >
273+ ) }
274+ </ TooltipWrapper >
275+ ) }
276+ { ! isNearbyStop &&
277+ ! isNearbyCell &&
278+ ( showInteractionRange || ( isLure && isPopupOpen ) ) &&
279+ configZoom && (
280+ < Circle
281+ center = { [ pkmn . lat , pkmn . lon ] }
282+ radius = { 40 }
283+ color = { INTERACTION_RANGE_COLOR }
284+ weight = { 1 }
285+ />
286+ ) }
287+ { ! isNearbyStop &&
288+ ! isNearbyCell &&
289+ showSpacialRendRange &&
290+ configZoom && (
291+ < Circle
292+ center = { [ pkmn . lat , pkmn . lon ] }
293+ radius = { 80 }
294+ color = "#4E893E"
295+ dashArray = "5, 5"
296+ weight = { 1 }
297+ />
298+ ) }
299+ </ Marker >
300+ </ >
231301 )
232302}
233303
0 commit comments