Skip to content

Conversation

@anurag2787
Copy link
Contributor

@anurag2787 anurag2787 commented Nov 15, 2025

Resolves #2583

Proposed change

This PR adds an opt-in precise location sharing option for the Chapter Map.

  • When a user visits /chapter, they can choose to share their current location.
  • If the user allows location sharing, the map updates to show the nearest chapter based on the user’s coordinates.
  • If the user denies or skips location sharing, the map behaves as before and uses the default chapter geoLocation data.

Checklist

  • I've read and followed the contributing guidelines.
  • I've run make check-test locally; all checks and tests passed.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 15, 2025

Summary by CodeRabbit

New Features

  • Added location sharing capability that allows users to view chapters sorted by proximity to their current location
  • Map now intelligently displays nearby chapters when location sharing is enabled, with automatic distance-based sorting and repositioning

Walkthrough

Adds opt-in browser geolocation: new geolocation utilities, updates ChapterMap and ChapterMapWrapper to accept/use userLocation and sort/center chapters by distance, and enables the location-sharing UI on the Chapters page via showLocationSharing={true}.

Changes

Cohort / File(s) Summary
Geolocation utilities
frontend/src/utils/geolocationUtils.ts
New module exporting UserLocation, calculateDistance (haversine), getUserLocationFromBrowser() (Promise), helpers to extract chapter coords, and sortChaptersByDistance() which annotates and sorts chapters by distance.
Map component
frontend/src/components/ChapterMap.tsx
Added optional `userLocation?: UserLocation
Map wrapper component
frontend/src/components/ChapterMapWrapper.tsx
Introduced ChapterMapWrapperProps with showLocationSharing?: boolean; added state for userLocation, loading, and sortedData; implemented handleShareLocation to request browser location, sort chapters via sortChaptersByDistance, toggle/reset sharing, and render location-sharing UI when enabled; falls back to previous behavior when disabled.
Page integration
frontend/src/app/chapters/page.tsx
Enabled location-sharing UI by rendering <ChapterMapWrapper showLocationSharing={true} ... />.
Manifest
package.json
Manifest modified (details not shown in diff).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–30 minutes

  • Pay extra attention to:
    • Haversine implementation and distance unit correctness in calculateDistance.
    • Robustness of coordinate extraction and filtering in extractChapterCoordinates / sortChaptersByDistance.
    • Geolocation permission/error handling and loading/toggle state in ChapterMapWrapper.
    • Type boundaries and prop compatibility between ChapterMap, ChapterMapWrapper, and callers.

Possibly related PRs

Suggested reviewers

  • arkid15r
  • kasya

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding a location sharing option for the chapter map.
Description check ✅ Passed The description is well-related to the changeset, explaining the opt-in location sharing feature, user behavior, and fallback to existing behavior.
Linked Issues check ✅ Passed The PR successfully implements the objective from issue #2583: opt-in geolocation sharing with nearest chapter display and fallback to default behavior.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the location sharing feature; no out-of-scope modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@anurag2787 anurag2787 marked this pull request as ready for review November 15, 2025 10:07
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
frontend/src/utils/geolocationUtils.ts (3)

31-59: Ensure getUserLocationFromBrowser stays client‑only and consider explicit “denied” handling

The geolocation flow and error handling are reasonable, but this helper assumes a browser environment (navigator.geolocation). Make sure it’s only imported and called from client components, and consider whether you want to distinguish “denied / failed” from “not requested yet” in the UI instead of always resolving null with only a console warning.


61-80: Coordinate extraction is robust but could be tightened and deduplicated

The multiple fallbacks for lat/lng across _geoloc, geoLocation, and location are helpful for handling legacy shapes, but:

  • The repeated casts to Record<string, unknown> could be wrapped in a small helper to avoid duplication.
  • If this is only intended for Chapter objects today, typing the parameter as Chapter (or a narrower structural type) would give you better safety than Record<string, unknown>.

85-103: Distance sorting works, but the typing and filter could be more precise

The map/filter/sort pipeline correctly excludes chapters without valid coordinates and orders the rest by distance. Two type‑level improvements would reduce assertions and make the return type easier to use:

  • Narrow the filter with a type predicate instead of item !== null plus as/!.
  • Align the generic shape with actual usage (e.g., Chapter[] or a generic <T> that returns Array<T & { distance: number }>), so you don’t lose the concrete type when calling this from ChapterMapWrapper.

Example pattern:

.filter((item): item is { /* your shape */ } => item !== null)

This will also help keep sortedData’s type in sync with what’s returned.

frontend/src/components/ChapterMapWrapper.tsx (2)

57-100: Avoid leaking showLocationSharing prop into ChapterMap and align sortedData typing

Two related refinements here:

  1. ChapterMap likely doesn’t declare a showLocationSharing prop, but {...props} forwards it anyway. This can cause type errors if ChapterMap has a strict props interface and also blurs the wrapper’s responsibility boundary.

  2. sortedData is typed as Chapter[] | null, while sortChaptersByDistance currently returns objects augmented with a distance field. Even though this extra field is harmless at runtime, the mismatch can force casts or assertions elsewhere.

You can address both by stripping the wrapper‑only prop and tightening the types:

-const ChapterMapWrapper: React.FC<ChapterMapWrapperProps> = (props) => {
-  const [userLocation, setUserLocation] = useState<UserLocation | null>(null)
-  const [isLoadingLocation, setIsLoadingLocation] = useState(false)
-  const [sortedData, setSortedData] = useState<Chapter[] | null>(null)
+const ChapterMapWrapper: React.FC<ChapterMapWrapperProps> = (props) => {
+  const { showLocationSharing, ...mapProps } = props
+  const [userLocation, setUserLocation] = useState<UserLocation | null>(null)
+  const [isLoadingLocation, setIsLoadingLocation] = useState(false)
+  const [sortedData, setSortedData] = useState<Chapter[] | null>(null) // or Chapter & { distance: number }[]
@@
-  const enableLocationSharing = props.showLocationSharing === true
+  const enableLocationSharing = showLocationSharing === true
@@
-        setSortedData(sortChaptersByDistance(props.geoLocData, location))
+        setSortedData(sortChaptersByDistance(mapProps.geoLocData, location))
@@
-  const mapData = sortedData ?? props.geoLocData
+  const mapData = sortedData ?? mapProps.geoLocData
@@
-    <div className="space-y-4">
+    <div className="space-y-4">
@@
-      <ChapterMap {...props} geoLocData={mapData} />
+      <ChapterMap {...mapProps} geoLocData={mapData} />

Adjusting sortChaptersByDistance’s return type as suggested in geolocationUtils.ts will let you update sortedData’s type accordingly.


60-97: Location‑sharing UI and accessibility are in good shape; consider a failure state message

The button’s aria-label, title, and isLoading/disabled flags make the interaction accessible, and the status text clearly indicates whether a location filter is active. One minor UX enhancement would be to surface a brief inline message when geolocation fails or is denied (instead of silently returning to the default “Find chapters near you” text), so users understand why nothing changed after clicking.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c6451c5 and 27b4a44.

📒 Files selected for processing (3)
  • frontend/src/app/chapters/page.tsx (1 hunks)
  • frontend/src/components/ChapterMapWrapper.tsx (1 hunks)
  • frontend/src/utils/geolocationUtils.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-13T11:29:25.245Z
Learnt from: Rajgupta36
Repo: OWASP/Nest PR: 1717
File: frontend/src/app/mentorship/programs/page.tsx:59-61
Timestamp: 2025-07-13T11:29:25.245Z
Learning: In Next.js 13+ app router, components with the 'use client' directive run entirely on the client side and don't require window object existence checks or SSR hydration considerations. Direct access to window.location and other browser APIs is safe in client components.

Applied to files:

  • frontend/src/components/ChapterMapWrapper.tsx
🧬 Code graph analysis (1)
frontend/src/components/ChapterMapWrapper.tsx (2)
frontend/src/types/chapter.ts (1)
  • Chapter (4-21)
frontend/src/utils/geolocationUtils.ts (3)
  • UserLocation (1-6)
  • getUserLocationFromBrowser (31-59)
  • sortChaptersByDistance (85-103)
🔇 Additional comments (3)
frontend/src/app/chapters/page.tsx (1)

90-102: Prop wiring for enabling location sharing looks correct

Passing showLocationSharing={true} here cleanly enables the new opt‑in location UI for the chapters page without affecting behavior when no chapters are loaded.

frontend/src/utils/geolocationUtils.ts (1)

13-29: Haversine distance implementation looks mathematically sound

The calculateDistance implementation (degree→radian conversion, a term, and R = 6371 km) is correct for a haversine distance in kilometers and handles edge cases (identical points) as expected.

frontend/src/components/ChapterMapWrapper.tsx (1)

22-57: Location‑sharing flow and toggle behavior are logically sound

The wrapper cleanly gates the feature behind showLocationSharing, toggles the filter on repeated clicks (set/unset userLocation and sortedData), and correctly guards loading state around getUserLocationFromBrowser with a finally block. This matches the opt‑in requirement without altering default behavior when the feature is disabled.

@anurag2787
Copy link
Contributor Author

Could you please review this? Let me know if its looks fine so that i can proceed with test part

Copy link
Collaborator

@kasya kasya left a comment

Choose a reason for hiding this comment

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

@anurag2787 this works great! Please proceed with working on tests 👍🏼 This looks good so far! Feel free to move this back into drafts if needed.

I do have a couple of questions!
When I use the button the map is pretty zoomed out 🤔 For example, it shows the full state I'm in. Is there a way to zoom in more or is it the best we can get? Also a few comments down below ⬇️

Comment on lines 40 to 43
// # NOSONAR Geolocation permission is necessary for "Find chapters near you" feature.
// User explicitly opts-in via button click. Location stays client-side only.
// eslint-disable-next-line no-unsanitized/property
navigator.geolocation.getCurrentPosition(
Copy link
Collaborator

Choose a reason for hiding this comment

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

I can see Sonar still gets triggered on this for some reason 🤔

@anurag2787 anurag2787 marked this pull request as draft November 16, 2025 14:45
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (4)
frontend/src/components/ChapterMap.tsx (1)

109-148: Consider extracting duplicate logic.

The userLocation block (lines 109-128) and showLocal block (lines 129-148) share nearly identical logic with only two differences: maxZoom value (12 vs 7) and the triggering condition. This duplication increases maintenance burden.

Consider extracting the shared logic:

const applyLocalZoom = (zoom: number) => {
  const maxNearestChapters = 5
  const localChapters = validGeoLocData.slice(0, maxNearestChapters)
  const localBounds = L.latLngBounds(
    localChapters.map((chapter) => [
      chapter._geoloc?.lat ?? chapter.geoLocation?.lat,
      chapter._geoloc?.lng ?? chapter.geoLocation?.lng,
    ])
  )
  const nearestChapter = validGeoLocData[0]
  map.setView(
    [
      nearestChapter._geoloc?.lat ?? nearestChapter.geoLocation?.lat,
      nearestChapter._geoloc?.lng ?? nearestChapter.geoLocation?.lng,
    ],
    zoom
  )
  map.fitBounds(localBounds, { maxZoom: zoom })
}

if (userLocation && validGeoLocData.length > 0) {
  applyLocalZoom(12)
} else if (showLocal && validGeoLocData.length > 0) {
  applyLocalZoom(7)
}
frontend/src/utils/geolocationUtils.ts (2)

31-60: Consider enabling high accuracy for "precise location" feature.

The function uses enableHighAccuracy: false which may provide less accurate results. Since the PR title and feature description specifically mention "precise location sharing," consider setting this to true for better accuracy, acknowledging it may take longer and use more battery.

If you want to prioritize accuracy:

 {
-  enableHighAccuracy: false,
+  enableHighAccuracy: true,
   timeout: 10000,
   maximumAge: 0,
 }

86-104: Consider stronger typing for sortChaptersByDistance.

The function uses Record<string, unknown>[] instead of the more specific Chapter[] type, which loses type safety and IDE support. Since the function is specifically designed to sort chapters and ChapterMapWrapper passes props.geoLocData (typed as Chapter[]), consider using the Chapter type directly.

import type { Chapter } from 'types/chapter'

export const sortChaptersByDistance = (
  chapters: Chapter[],
  userLocation: UserLocation
): Array<Chapter & { distance: number }> => {
  return chapters
    .map((chapter) => {
      const { lat, lng } = extractChapterCoordinates(chapter as unknown as Record<string, unknown>)
      
      if (typeof lat !== 'number' || typeof lng !== 'number') return null
      
      const distance = calculateDistance(userLocation.latitude, userLocation.longitude, lat, lng)
      
      return { ...chapter, distance }
    })
    .filter((item): item is Chapter & { distance: number } => item !== null)
    .sort((a, b) => a.distance - b.distance)
}

This eliminates the non-null assertions and provides better type safety.

frontend/src/components/ChapterMapWrapper.tsx (1)

50-52: Consider providing user feedback on location errors.

The error handler logs to console but doesn't provide user feedback when location detection fails. Users who grant permission but encounter errors (timeout, position unavailable, etc.) won't know what happened.

Consider adding error state and displaying a user-friendly message:

const [error, setError] = useState<string | null>(null)

// In handleShareLocation:
try {
  setError(null)
  const location = await getUserLocationFromBrowser()
  if (!location) {
    setError('Unable to determine your location. Please try again.')
    return
  }
  // ... rest of logic
} catch (error) {
  console.error('Error detecting location:', error)
  setError('Failed to access location. Please check your browser permissions.')
}

// In render:
{error && (
  <div className="text-xs text-red-600 dark:text-red-400">{error}</div>
)}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 47e56a2 and bd0111b.

📒 Files selected for processing (3)
  • frontend/src/components/ChapterMap.tsx (4 hunks)
  • frontend/src/components/ChapterMapWrapper.tsx (1 hunks)
  • frontend/src/utils/geolocationUtils.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/components/ChapterMap.tsx (2)
frontend/src/types/chapter.ts (1)
  • Chapter (4-21)
frontend/src/utils/geolocationUtils.ts (1)
  • UserLocation (1-6)
frontend/src/components/ChapterMapWrapper.tsx (2)
frontend/src/types/chapter.ts (1)
  • Chapter (4-21)
frontend/src/utils/geolocationUtils.ts (3)
  • UserLocation (1-6)
  • getUserLocationFromBrowser (31-60)
  • sortChaptersByDistance (86-104)
🔇 Additional comments (7)
frontend/src/components/ChapterMap.tsx (1)

5-5: LGTM!

The UserLocation type import and optional prop addition are properly implemented with correct TypeScript typing.

Also applies to: 16-16, 21-21

frontend/src/utils/geolocationUtils.ts (2)

1-6: LGTM!

Clean interface definition with appropriate required and optional fields.


13-29: LGTM!

The Haversine formula implementation is correct for calculating great-circle distance between two points on a sphere.

frontend/src/components/ChapterMapWrapper.tsx (4)

1-20: LGTM!

Clean imports and proper TypeScript typing for the component props and function component.

Also applies to: 22-22


23-31: LGTM!

Clean state initialization and early return pattern when location sharing is disabled.


77-97: LGTM! Clean UI feedback states.

The location status display provides clear feedback for both active and inactive states, with appropriate styling and accessibility attributes.

Note: I see from past review comments that displaying coordinates instead of city names was already discussed (would require reverse geocoding). The current coordinate display is a reasonable approach.

Based on learnings


58-58: LGTM! Proper data flow to ChapterMap.

The component correctly selects between sorted and original data, and properly passes both the data and userLocation to ChapterMap for rendering.

Also applies to: 100-100

@anurag2787
Copy link
Contributor Author

@kasya @arkid15r I tried adding // NOSONAR and also added a proper justification comment explaining the geolocation usage but the sonar warning still isn’t being resolved Could you please help me into this?

@anurag2787 anurag2787 marked this pull request as ready for review November 18, 2025 10:57
@anurag2787 anurag2787 requested a review from kasya November 18, 2025 10:57
@kasya
Copy link
Collaborator

kasya commented Nov 19, 2025

@kasya @arkid15r I tried adding // NOSONAR and also added a proper justification comment explaining the geolocation usage but the sonar warning still isn’t being resolved Could you please help me into this?

@anurag2787 It seems that Sonar is totally against it and only suggests to use user input and go from there :( I'm not sure if we can override this.

@arkid15r is there anything in the SQ admin that we could disable for this approach to work?

Copy link
Collaborator

@arkid15r arkid15r left a comment

Choose a reason for hiding this comment

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

Shouldn't the new button be part of the map and available after a click on "Click to interact with map" button only? I believe it'd be cleaner.

We could just add a small button consistent with the map style (with a good tooltip). Instead of showing coordinates values we could just add a pin (the red one) indicating user's location on the map.

@kasya wdyt?

Screenshot 2025-11-18 at 6 26 16 PM

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
frontend/src/utils/geolocationUtils.ts (2)

31-64: Harden browser-only usage and align // NOSONAR with the geolocation line

The overall flow (opt‑in, local‑only usage, timeout, graceful error handling) looks solid. Two small tweaks would tighten things up and should help with Sonar:

  • Guard against non‑browser environments (typeof navigator === 'undefined') so this safely returns null instead of throwing if ever called outside the browser.
  • Move // NOSONAR onto the line where navigator.geolocation is actually read, since Sonar only suppresses issues on the same line.

Suggested refactor:

-export const getUserLocationFromBrowser = (): Promise<UserLocation | null> => {
-  return new Promise((resolve) => {
-    if (!navigator.geolocation) {
-      // eslint-disable-next-line no-console
-      console.warn('Geolocation API not supported')
-      resolve(null)
-      return
-    }
+export const getUserLocationFromBrowser = (): Promise<UserLocation | null> => {
+  return new Promise((resolve) => {
+    if (typeof navigator === 'undefined' || !navigator.geolocation) {
+      // eslint-disable-next-line no-console
+      console.warn('Geolocation API not supported')
+      resolve(null)
+      return
+    }
@@
-    navigator.geolocation.getCurrentPosition(
+    const geolocation = navigator.geolocation // NOSONAR - geolocation used only after explicit user opt-in; data stays client-side (see comment above).
+    geolocation.getCurrentPosition(
       (position) => {
         resolve({
           latitude: position.coords.latitude,
           longitude: position.coords.longitude,
         })
@@
-      {
-        enableHighAccuracy: false,
-        timeout: 10000,
-        maximumAge: 0,
-      }
-    )
-  }) // NOSONAR
+      {
+        enableHighAccuracy: false,
+        timeout: 10000,
+        maximumAge: 0,
+      }
+    )
+  })
 }

Please re-run the Sonar check after this change to confirm the warning is actually suppressed on the targeted line.


80-101: Consider a small typing cleanup in sortChaptersByDistance

Logic is correct, but you can make the API more ergonomic and type‑safe by preserving the caller’s chapter type and avoiding the null + type assertion combo:

  • Make the function generic over the chapter type.
  • Use a type‑predicate in the filter so you don’t need a! and the final cast.

Example refactor:

-export const sortChaptersByDistance = (
-  chapters: Record<string, unknown>[],
-  userLocation: UserLocation
-): Array<Record<string, unknown> & { distance: number }> => {
-  return chapters
-    .map((chapter) => {
-      const { lat, lng } = extractChapterCoordinates(chapter)
-
-      if (typeof lat !== 'number' || typeof lng !== 'number') return null
-
-      const distance = calculateDistance(userLocation.latitude, userLocation.longitude, lat, lng)
-
-      return { ...chapter, distance }
-    })
-    .filter((item) => item !== null) // remove invalid ones
-    .sort((a, b) => a!.distance - b!.distance) as Array<
-    Record<string, unknown> & { distance: number }
-  >
-}
+export const sortChaptersByDistance = <T extends Record<string, unknown>>(
+  chapters: T[],
+  userLocation: UserLocation
+): Array<T & { distance: number }> => {
+  return chapters
+    .map((chapter) => {
+      const { lat, lng } = extractChapterCoordinates(chapter)
+
+      if (typeof lat !== 'number' || typeof lng !== 'number') return null
+
+      const distance = calculateDistance(userLocation.latitude, userLocation.longitude, lat, lng)
+
+      return { ...chapter, distance }
+    })
+    .filter((item): item is T & { distance: number } => item !== null) // remove invalid ones
+    .sort((a, b) => a.distance - b.distance)
+}

This keeps the original chapter shape at call sites while maintaining the same runtime behavior.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 81d3bd7 and b613afe.

📒 Files selected for processing (1)
  • frontend/src/utils/geolocationUtils.ts (1 hunks)
🔇 Additional comments (3)
frontend/src/utils/geolocationUtils.ts (3)

1-11: Location and chapter coordinate shapes look good

The UserLocation and ChapterCoordinates structures match how this module uses them (lat/lng plus optional metadata), so there are no functional or safety concerns here from my side.


13-29: Haversine distance calculation is implemented correctly

The formula, radian conversions, and choice of radius (Earth ≈ 6371 km) are all correct for computing great‑circle distance in kilometers. No changes needed.


66-78: Coordinate extraction matches current chapter data model

This helper now cleanly reflects the actual _geoloc/geoLocation shape (lat/lng only) and defers validity checks to the caller. That’s simple and effective; no further changes needed here.

@sonarqubecloud
Copy link

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
frontend/src/utils/geolocationUtils.ts (2)

13-29: LGTM! Consider adding coordinate validation.

The Haversine formula implementation is mathematically correct and appropriately returns distance in kilometers.

Optionally, consider adding input validation to ensure coordinates are within valid ranges (latitude: -90 to 90, longitude: -180 to 180) to prevent nonsensical results from invalid inputs.


83-99: Consider stronger TypeScript types to eliminate assertions.

The logic is correct, but using Record<string, unknown> throughout loses type safety. The non-null assertion on line 98 is safe due to the filter on line 97, but indicates that TypeScript can't verify the logic.

Consider typing chapters more specifically (e.g., with a Chapter interface or type that includes the coordinate properties) to improve type safety and eliminate the need for type assertions.

Example approach:

interface ChapterWithCoords {
  _geoloc?: { lat?: number; lng?: number }
  geoLocation?: { lat?: number; lng?: number }
  // other chapter properties...
}

export const sortChaptersByDistance = (
  chapters: ChapterWithCoords[],
  userLocation: UserLocation
): Array<ChapterWithCoords & { distance: number }> => {
  return chapters
    .map((chapter) => {
      const { lat, lng } = extractChapterCoordinates(chapter as Record<string, unknown>)
      if (typeof lat !== 'number' || typeof lng !== 'number') return null
      const distance = calculateDistance(userLocation.latitude, userLocation.longitude, lat, lng)
      return { ...chapter, distance }
    })
    .filter((item): item is ChapterWithCoords & { distance: number } => item !== null)
    .sort((a, b) => a.distance - b.distance)
}

Note the type predicate in the filter removes the need for non-null assertions.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a520752 and 1ea9a97.

📒 Files selected for processing (1)
  • frontend/src/utils/geolocationUtils.ts (1 hunks)
🔇 Additional comments (3)
frontend/src/utils/geolocationUtils.ts (3)

8-11: LGTM!

The internal ChapterCoordinates type appropriately models nullable coordinates for defensive extraction logic.


31-64: LGTM!

The geolocation implementation follows best practices:

  • Gracefully handles unsupported API
  • Never rejects (resolves to null on error for good UX)
  • Includes justification comment for security scanner
  • Uses reasonable options (low accuracy for battery savings, 10s timeout)
  • Implements opt-in pattern as required by PR objectives

The SonarCloud issue mentioned in PR comments is a configuration matter, not a code defect.


66-78: LGTM!

The simplified extraction logic correctly addresses past review feedback and appropriately handles the fallback chain from _geoloc to geoLocation with null safety.

Comment on lines +1 to +6
export interface UserLocation {
latitude: number
longitude: number
city?: string
country?: string
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove or document unused optional fields.

The city and country fields are defined but never populated by getUserLocationFromBrowser (which only sets latitude and longitude). Either remove these unused fields or add a comment indicating they're reserved for future enhancement.

Apply this diff to remove the unused fields:

 export interface UserLocation {
   latitude: number
   longitude: number
-  city?: string
-  country?: string
 }

Alternatively, if these are planned for future use, add a comment:

 export interface UserLocation {
   latitude: number
   longitude: number
+  // Reserved for future enhancement (reverse geocoding)
   city?: string
   country?: string
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface UserLocation {
latitude: number
longitude: number
city?: string
country?: string
}
export interface UserLocation {
latitude: number
longitude: number
}
Suggested change
export interface UserLocation {
latitude: number
longitude: number
city?: string
country?: string
}
export interface UserLocation {
latitude: number
longitude: number
// Reserved for future enhancement (reverse geocoding)
city?: string
country?: string
}
🤖 Prompt for AI Agents
In frontend/src/utils/geolocationUtils.ts around lines 1 to 6, the UserLocation
interface currently declares optional city and country fields that are never
populated by getUserLocationFromBrowser; either remove these unused fields from
the interface or retain them but add a clear comment on their intended future
use (e.g., "reserved for future reverse-geocoding/population") so reviewers know
they are intentional; update the interface accordingly and, if keeping them, add
a brief comment above each field explaining why it's present and when it will be
populated.

@kasya
Copy link
Collaborator

kasya commented Nov 19, 2025

Shouldn't the new button be part of the map and available after a click on "Click to interact with map" button only? I believe it'd be cleaner.

@kasya wdyt?

@arkid15r yes, love this idea 👍🏼

@kasya
Copy link
Collaborator

kasya commented Nov 21, 2025

Shouldn't the new button be part of the map and available after a click on "Click to interact with map" button only? I believe it'd be cleaner.
@kasya wdyt?

@arkid15r yes, love this idea 👍🏼

@anurag2787 Are you planning to update the button location as per this feedback? 👀
Just saw some new commits here, but realized they were not about the button location update, so thought I'd check with you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add precise location sharing option for chapter map

3 participants