Skip to content

Conversation

@google-labs-jules
Copy link
Contributor

@google-labs-jules google-labs-jules bot commented Nov 19, 2025

User description

This change adds a custom user location marker to the map, as requested by the user. It includes a new UserMarker component and updates the Mapbox component to use it.


PR created automatically by Jules for task 4752322746026312812 started by @ngoiyaeric


PR Type

Enhancement


Description

  • Add custom user location marker component with styled container

  • Integrate marker into map with dynamic creation and position updates

  • Replace deprecated ReactDOM.render with createRoot for React 18 compatibility

  • Implement proper marker cleanup in component unmount lifecycle


Diagram Walkthrough

flowchart LR
  A["User Position Updates"] -->|"triggers updateMapPosition"| B["UserMarker Component"]
  B -->|"renders with Image"| C["Custom Marker on Map"]
  D["React 18 createRoot"] -->|"replaces deprecated render"| E["Marker Element Creation"]
  F["Component Cleanup"] -->|"removes marker reference"| G["Memory Management"]
Loading

File Walkthrough

Relevant files
Enhancement
mapbox-map.tsx
Integrate custom user marker with React 18 support             

components/map/mapbox-map.tsx

  • Import createRoot from react-dom/client for React 18 compatibility
  • Add userMarkerRef to track custom user location marker instance
  • Implement marker creation and position updates in updateMapPosition
    callback
  • Add marker cleanup logic in component unmount to prevent memory leaks
  • Refactor code formatting for consistency and readability
+77/-43 
user-marker.tsx
Create custom user location marker component                         

components/map/user-marker.tsx

  • Create new UserMarker component with styled circular container
  • Use Next.js Image component for optimized marker icon display
  • Apply blue semi-transparent background with centered user location
    icon
  • Export as default component for use in mapbox-map
+29/-0   

This commit introduces a custom marker for the user's live location on the map.

- A new `user-marker.tsx` component has been created to render the custom marker.
- The `mapbox-map.tsx` component has been updated to use this new marker, creating it when the user's position is first available and updating its position as it changes.
- The user-provided image for the marker has been added to the `public` directory.
@google-labs-jules
Copy link
Contributor Author

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!


For security, I will only act on instructions from the user who triggered this task.

New to Jules? Learn more at jules.google/docs.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 19, 2025

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Comment @coderabbitai help to get the list of available commands and usage tips.

@CLAassistant
Copy link

CLAassistant commented Nov 19, 2025

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ ngoiyaeric
❌ google-labs-jules[bot]
You have signed the CLA already but the status is still pending? Let us recheck it.

@vercel
Copy link

vercel bot commented Nov 19, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
qcx Ready Ready Preview Comment Nov 19, 2025 8:35pm

This commit introduces a custom marker for the user's live location on the map.

- A new `user-marker.tsx` component has been created to render the custom marker.
- The `mapbox-map.tsx` component has been updated to use this new marker, creating it when the user's position is first available and updating its position as it changes.
- The user-provided image for the marker has been added to the `public` directory.

This commit also addresses a build error by updating the deprecated `ReactDOM.render` to `createRoot` and a build warning by using `next/image` for image optimization.
@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Nov 19, 2025

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Client-exposed API token

Description: The Mapbox access token is read from NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN, which exposes the
token to the client bundle; if this token has elevated scopes or is not restricted in
Mapbox dashboard, it could be abused—ensure it is a public-scoped, domain-restricted token
with minimal permissions.
mapbox-map.tsx [18-18]

Referred Code
mapboxgl.accessToken = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN as string
Untrusted asset path

Description: The marker image is loaded from a relative path "/user-location-marker.png" without an
import/bundling reference or integrity check; if this path can be overridden via asset
pipeline or CDN mapping, it could allow UI spoofing—ensure the asset is bundled and
immutable (e.g., imported static file) and served from trusted origin.
user-marker.tsx [19-24]

Referred Code
<Image
  src="/user-location-marker.png"
  alt="User Location"
  width={20}
  height={20}
/>
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
No audit logs: The new user location marker creation and map position updates are not logged, but it is
unclear whether these actions are considered critical within this context.

Referred Code
const updateMapPosition = useCallback(
  async (latitude: number, longitude: number) => {
    if (map.current && !isUpdatingPositionRef.current) {
      isUpdatingPositionRef.current = true
      stopRotation()

      try {
        currentMapCenterRef.current.center = [longitude, latitude]

        if (userMarkerRef.current) {
          userMarkerRef.current.setLngLat([longitude, latitude])
        } else {
          const markerElement = document.createElement('div')
          const root = createRoot(markerElement)
          root.render(<UserMarker />)
          userMarkerRef.current = new mapboxgl.Marker({
            element: markerElement
          })
            .setLngLat([longitude, latitude])
            .addTo(map.current)
        }


 ... (clipped 28 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Minimal error info: Errors in updateMapPosition are only logged to console without structured context, and
external input validation on latitude/longitude is not visible in the diff.

Referred Code
  }, 500)
} catch (error) {
  console.error('Error updating map position:', error)
  isUpdatingPositionRef.current = false
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Input validation: The function updateMapPosition accepts latitude and longitude but does not validate ranges
or types in the shown code, which may rely on upstream validation not visible in the diff.

Referred Code
const updateMapPosition = useCallback(
  async (latitude: number, longitude: number) => {
    if (map.current && !isUpdatingPositionRef.current) {
      isUpdatingPositionRef.current = true
      stopRotation()

      try {
        currentMapCenterRef.current.center = [longitude, latitude]

        if (userMarkerRef.current) {
          userMarkerRef.current.setLngLat([longitude, latitude])
        } else {
          const markerElement = document.createElement('div')
          const root = createRoot(markerElement)
          root.render(<UserMarker />)
          userMarkerRef.current = new mapboxgl.Marker({
            element: markerElement
          })
            .setLngLat([longitude, latitude])
            .addTo(map.current)
        }


 ... (clipped 28 lines)

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Nov 19, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Prevent memory leak by unmounting React root

To prevent a memory leak, store the React root created for the custom marker in
a useRef. This will allow it to be unmounted later during component cleanup.

components/map/mapbox-map.tsx [216-227]

 if (userMarkerRef.current) {
   userMarkerRef.current.setLngLat([longitude, latitude])
 } else {
   const markerElement = document.createElement('div')
+  // Create and store the root to be unmounted later
   const root = createRoot(markerElement)
+  userMarkerRootRef.current = root
   root.render(<UserMarker />)
   userMarkerRef.current = new mapboxgl.Marker({
     element: markerElement
   })
     .setLngLat([longitude, latitude])
     .addTo(map.current)
 }
  • Apply / Chat
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a memory leak caused by not unmounting the React root created with createRoot. This is a critical issue that needs to be fixed.

High
Unmount React root on component cleanup

Update the component cleanup logic to unmount the React root associated with the
user marker. This will prevent a memory leak when the component is unmounted.

components/map/mapbox-map.tsx [498-501]

 if (userMarkerRef.current) {
   userMarkerRef.current.remove()
   userMarkerRef.current = null
 }
+if (userMarkerRootRef.current) {
+  userMarkerRootRef.current.unmount()
+  userMarkerRootRef.current = null
+}
  • Apply / Chat
Suggestion importance[1-10]: 9

__

Why: This suggestion correctly points out that the React root for the user marker must be unmounted in the cleanup function to prevent a memory leak, which is a critical fix.

High
  • Update

Copy link

@charliecreates charliecreates bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main concern is lifecycle management of the React root created via createRoot for the custom user marker: it is never stored or unmounted, which risks memory leaks over time. Both cleanup paths remove the Mapbox marker but do not yet unmount the associated React component tree, so React resources are not fully released. The UserMarker component is functionally correct, though its current inline, fixed-size styling could be made more reusable and maintainable with configurable sizing and extracted styles. Addressing the root lifecycle will significantly improve the robustness of this feature.

Additional notes (2)
  • Maintainability | components/map/mapbox-map.tsx:207-255
    The updateMapPosition logic now both changes the map view and manages the lifecycle of the userMarkerRef, which mixes two separate concerns into one callback and adds complexity. This makes the function harder to understand and more error-prone if marker behavior needs to evolve (e.g., different marker per map mode, marker visibility toggles, etc.). Consider extracting the marker creation/update into a small helper (e.g., updateUserMarker(latitude, longitude)) that only deals with userMarkerRef and map.current, and having updateMapPosition call that helper before/after flyTo. That would keep responsibilities clearer and simplify future changes to marker behavior without risking regressions in camera movement.

  • Maintainability | components/map/user-marker.tsx:8-25
    The inline style approach is simple, but the hard-coded 50px container and 20px inner image size means the marker scale is fixed and not easily reusable or themeable. It also makes it slightly harder to adjust the marker size in response to device pixel ratio or map zoom if you ever need that.

While not strictly incorrect, encapsulating styles in a CSS module or tailwind classes (if used elsewhere in the project) would improve maintainability and consistency with the rest of the codebase.

Summary of changes

Summary of Changes

  • Added a new UserMarker React component (components/map/user-marker.tsx) that renders a styled circular container with a user-location image using next/image.
  • Updated Mapbox map component (components/map/mapbox-map.tsx) to:
    • Import and use createRoot from react-dom/client and the new UserMarker component.
    • Maintain a userMarkerRef to track a Mapbox Marker representing the user's location.
    • Extend updateMapPosition to create the marker (via a React root) on first use and update its position on subsequent updates while still animating the map with flyTo.
    • Ensure the custom user marker is removed and reference cleared in cleanup paths.
  • Added the public/user-location-marker.png asset used by UserMarker.

Comment on lines +219 to +251
const markerElement = document.createElement('div')
const root = createRoot(markerElement)
root.render(<UserMarker />)
userMarkerRef.current = new mapboxgl.Marker({
element: markerElement
})
.setLngLat([longitude, latitude])
.addTo(map.current)
}

await new Promise<void>(resolve => {
map.current?.flyTo({
center: [longitude, latitude],
zoom: 12,
essential: true,
speed: 0.5,
curve: 1
})
map.current?.once('moveend', () => {
resolve()
})
})

setTimeout(() => {
if (mapType === MapToggleEnum.RealTimeMode) {
startRotation()
}
isUpdatingPositionRef.current = false
}, 500)
} catch (error) {
console.error('Error updating map position:', error)
isUpdatingPositionRef.current = false
}, 500)
} catch (error) {
console.error('Error updating map position:', error)
isUpdatingPositionRef.current = false
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createRoot is correctly used to render UserMarker into a detached DOM node, but the React root instance is not retained or unmounted. This leaves React tree lifecycle unmanaged and risks memory leaks when the map is destroyed or if the marker is ever replaced, because root.unmount() is never called.

Also, the userMarkerRef only tracks the Mapbox Marker, not the React root, so you cannot currently cleanly unmount the React component during cleanup. This is particularly important in long-lived sessions with route changes or map reinitializations.

Suggestion

Consider storing the React root in a ref alongside the marker and explicitly unmounting it during cleanup when the marker is removed. For example:

const userMarkerRef = useRef<{
  marker: mapboxgl.Marker | null
  root: Root | null
}>({ marker: null, root: null })

Then when creating the marker:

const markerElement = document.createElement('div')
const root = createRoot(markerElement)
root.render(<UserMarker />)
userMarkerRef.current = {
  marker: new mapboxgl.Marker({ element: markerElement })
    .setLngLat([longitude, latitude])
    .addTo(map.current),
  root,
}

And in your cleanup (both in the main useEffect return and the map-style change cleanup), ensure you unmount:

if (userMarkerRef.current?.marker) {
  userMarkerRef.current.marker.remove()
}
if (userMarkerRef.current?.root) {
  userMarkerRef.current.root.unmount()
}
userMarkerRef.current = { marker: null, root: null }

This keeps React and Mapbox lifecycles aligned and avoids orphaned React roots. Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.

@charliecreates charliecreates bot removed the request for review from CharlieHelps November 19, 2025 19:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants