Skip to content

feat: add new globe#619

Open
houssembaltii wants to merge 23 commits into
mainfrom
feat/add-new-globe-mapcn
Open

feat: add new globe#619
houssembaltii wants to merge 23 commits into
mainfrom
feat/add-new-globe-mapcn

Conversation

@houssembaltii

@houssembaltii houssembaltii commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

Map Tiles OpenFreeMap

The globe uses OpenFreeMap as the tile provider a free, open-source map tile service with no API key required.

Summary by CodeRabbit

  • New Features

    • Interactive global map showing events grouped by city with clustered, clickable markers, popups listing city events/status, and map UI primitives (markers, popups, routes).
    • Map navigation controls: zoom, compass, locate, and fullscreen.
    • "Global Community" section added to the homepage embedding the world map.
  • Style

    • Map popups use a transparent background for a cleaner look.
  • Chores

    • Added map rendering library dependency.

@vercel

vercel Bot commented Feb 6, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
forkit-community Ready Ready Preview, Comment Apr 10, 2026 9:26am

Request Review

@coderabbitai

coderabbitai Bot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds MapLibre GL as a dependency and implements a Map UI wrapper, marker/popup/cluster primitives, a GlobalMap React component rendering clustered city event markers with popups, a server utility grouping events by city, an index page integration (client:load), and a CSS rule for MapLibre popups.

Changes

Cohort / File(s) Summary
Dependency
package.json
Added maplibre-gl@5.17.0 to dependencies.
Map UI library
src/components/ui/map.tsx
New MapLibre wrapper exposing Map, useMap, MapRef type, marker/popup primitives (MapMarker, MarkerContent, MarkerPopup, MarkerTooltip, MarkerLabel), UI controls (MapControls, MapPopup), visualization helpers (MapRoute, MapClusterLayer), theming, lifecycle and portal-based DOM integration.
Global map feature
src/components/GlobalMap/GlobalMap.tsx, src/pages/index.astro
Added GlobalMap client component that transforms EventsByCities into clustered GeoJSON, handles cluster/point interactions and event-list popups; integrated into the index page with client:load.
Events grouping utility
src/lib/events.ts
Added eventsGroupedByCities() which fetches non-cancelled events and groups them by city.id; exported EventsByCities type.
Styles
src/styles/globals.css
Added .maplibregl-popup-content { background: transparent !important; } to override popup background.

Sequence Diagram

sequenceDiagram
    participant User
    participant IndexPage as "Index Page (server)"
    participant EventsLib as "eventsGroupedByCities"
    participant GlobalMap as "GlobalMap (client)"
    participant MapUI as "Map UI wrapper"
    participant MapLib as "MapLibre"

    User->>IndexPage: Request page
    IndexPage->>EventsLib: eventsGroupedByCities()
    EventsLib-->>IndexPage: EventsByCities
    IndexPage->>User: serve page (includes GlobalMap client:load)
    User->>GlobalMap: browser loads GlobalMap (client)
    GlobalMap->>MapUI: mount map, add sources/layers (clusters, points)
    MapUI->>MapLib: instantiate MapLibre map and set style
    MapLib-->>MapUI: map loaded / styledata ready
    User->>MapUI: click cluster or point
    MapUI->>GlobalMap: deliver feature + coordinates via handlers
    GlobalMap-->>User: open MapPopup with event list / navigation links
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hopped and poked a globe so neat,
Clusters sparkle under my tiny feet,
Popups spill names, dates that chime,
I nudge the map and count the time,
A rabbit maps the world — how sweet.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add new globe' directly and clearly describes the main change—adding a globe/map visualization component to the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/add-new-globe-mapcn

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🤖 Fix all issues with AI agents
In `@src/components/GlobalMap/GlobalMap.tsx`:
- Around line 37-72: The geoJsonData object is rebuilt on every render; wrap its
construction in React's useMemo inside the GlobalMap component: compute
geoJsonData using useMemo(() => { ...same code... }, [events]) so it only
recalculates when the events input changes, preserving the existing types
(FeatureCollection, Point, and the filter type guard) and keeping the
properties/events JSON.stringify logic intact; reference the geoJsonData
variable and the events dependency when applying the change.

In `@src/components/ui/map.tsx`:
- Around line 1128-1147: The initial map.addLayer call for clusterLayerId sets
paint values that don't use the passed-in clusterColors and don't match the
dynamic radii used later; update the paint in the initial map.addLayer (the
object created in the map.addLayer call that references clusterLayerId and
sourceId) to use the clusterColors prop for "circle-color" and to use the same
radius steps as the dynamic update (use clusterThresholds and the same radii
values used later, e.g., 20/30/40 or the variables used there) so initial render
matches subsequent style updates.
- Around line 956-968: Remove the leftover clusterProperties block from the
initial GeoJSON data passed to map.addSource for the route source (look for
map.addSource and the variable sourceId in src/components/ui/map.tsx); the
properties.clusterProperties object is a copy-paste artifact from
MapClusterLayer and is not applicable to a LineString route, so delete the
entire properties.clusterProperties entry (and if desired the enclosing
properties object if unused) so the initial Feature only defines type and
geometry for the LineString.
- Around line 678-701: The handler handleLocate sets setWaitingForLocation(true)
but never clears it when navigator.geolocation is absent; update handleLocate to
immediately reset waiting state in the "geolocation not in navigator" case
(e.g., add an else branch that calls setWaitingForLocation(false) and optionally
logs or calls an error callback), and keep the existing error callback that
clears state on failures; this change should be applied within the handleLocate
function (referenced symbols: handleLocate, setWaitingForLocation,
navigator.geolocation, onLocate, map).
- Around line 1194-1205: The cleanup block is missing removal of the unclustered
point label layer created as `${unclusteredLayerId}-label`; update the teardown
inside the try/catch to check for and remove that layer (e.g., if
(map.getLayer(`${unclusteredLayerId}-label`))
map.removeLayer(`${unclusteredLayerId}-label`)) alongside the existing removals
for clusterCountLayerId, unclusteredLayerId, clusterLayerId and sourceId so no
stale layers remain on remount.
- Around line 845-885: The popup close handler currently invokes onClose twice
because handleClose calls popup.remove() (which emits the "close" event) and
then calls onClose?.() directly; remove the direct onClose?.() call from
handleClose so the single source of truth is the popup's "close" event handler.
Also stop capturing the prop in the effect closure by introducing a mutable ref
(e.g., onCloseRef) that is kept up-to-date with the latest onClose prop and
change onCloseProp to call onCloseRef.current?.() so prop updates are respected;
keep popup.on("close", onCloseProp) and popup.off unchanged except for using the
ref-backed callback.
- Around line 291-330: The marker's event handlers (handleClick,
handleMouseEnter, handleMouseLeave, handleDragStart, handleDrag, handleDragEnd)
are captured once inside the useMemo with empty deps causing stale callbacks;
update the implementation to keep handlers up-to-date by storing the latest prop
callbacks (onClick, onMouseEnter, onMouseLeave, onDragStart, onDrag, onDragEnd)
in refs and referencing those refs inside the registered listener functions on
markerInstance, or alternatively move listener registration into a useEffect
that depends on those props and cleans up prior listeners; ensure you update
references to marker/markerInstance and remove or re-add listeners on change to
avoid leaks.
- Line 150: The current useImperativeHandle call exposes mapInstance as a
non-null MapLibreGL.Map which is unsafe when mapInstance can be null; update the
imperative handle to return a nullable type (e.g. MapLibreGL.Map | null) or
change the MapRef/forwardRef type to accept null and return mapInstance as that
nullable type, and ensure the identifier mapInstance and the useImperativeHandle
invocation are updated accordingly so consumers see a nullable ref instead of a
forced non-null MapLibreGL.Map.
🧹 Nitpick comments (6)
src/lib/events.ts (1)

637-644: Consider a lighter data-fetching path for the map.

getEventsCollection calls eventWithComputed for every event, which resolves talks, speakers, organizers, volunteers, and cover images — none of which are used by GlobalMap. This adds unnecessary build-time overhead (many getEntry calls).

Consider either creating a lightweight fetcher that only resolves city/country data, or extracting a simpler variant of eventWithComputed for this use case.

src/components/GlobalMap/GlobalMap.tsx (2)

33-35: Unused mapRef — dead code.

mapRef is created and passed to <Map ref={mapRef}> (line 81) but never read anywhere in this component. Remove it to reduce noise.

Suggested fix
 export function GlobalMap({ events, className }: GlobalMapProps) {
-  const mapRef = useRef<maplibregl.Map | null>(null);
   const [selectedCity, setSelectedCity] = useState<PopupInfo | null>(null);

And in the JSX:

-      <Map
-        ref={mapRef}
+      <Map
         center={[10, 30]}

93-106: JSON.parse without error handling on user-constructed GeoJSON properties.

While the data is self-generated via JSON.stringify (line 58), a try/catch around JSON.parse (line 97) would prevent a crash if properties are ever malformed or if MapLibre modifies them during clustering.

Suggested fix
           const properties = feature.properties;
-          const cityEvents =
-            typeof properties?.events === "string"
-              ? JSON.parse(properties.events)
-              : properties?.events;
+          let cityEvents = [];
+          try {
+            cityEvents =
+              typeof properties?.events === "string"
+                ? JSON.parse(properties.events)
+                : (properties?.events ?? []);
+          } catch {
+            console.error("Failed to parse city events");
+          }
src/pages/index.astro (2)

41-42: Consolidate MdPublic import with existing react-icons/md import block.

MdPublic is imported separately on line 42 while other react-icons/md icons are imported on lines 15–21.

Suggested fix
 import {
   MdArrowForward,
   MdEvent,
   MdFeed,
   MdOndemandVideo,
   MdPodcasts,
+  MdPublic,
 } from "react-icons/md";
 ...
-import { MdPublic } from "react-icons/md";

370-370: Consider client:visible instead of client:load for the GlobalMap.

The map section is well below the fold (after News, Podcasts, and VODs). Using client:visible would defer loading the MapLibre GL bundle (~200KB+) until the user scrolls near the map, improving initial page load performance.

-        <GlobalMap events={eventsByCities} client:load />
+        <GlobalMap events={eventsByCities} client:visible />
package.json (1)

55-56: Remove duplicate icon library: lucide-react is redundant with existing react-icons.

The project already depends on react-icons@5.5.0 which includes Lucide icons via the lu pack. All icons used in map.tsx (X, Minus, Plus, Locate, Maximize, Loader2) are available as LuX, LuMinus, LuPlus, LuLocate, LuMaximize, LuLoader2 from react-icons/lu—and the codebase already imports from react-icons/lu elsewhere. Migrate the map component to use react-icons/lu instead to avoid the extra dependency and reduce bundle size.

Comment thread src/components/GlobalMap/GlobalMap.tsx Outdated
Comment thread src/components/ui/map.tsx
Comment thread src/components/ui/map.tsx
Comment thread src/components/ui/map.tsx
Comment thread src/components/ui/map.tsx
Comment thread src/components/ui/map.tsx
Comment thread src/components/ui/map.tsx
Comment thread src/components/ui/map.tsx

@JeanneGrenet JeanneGrenet left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Maybe it could be great to have a difference between events and meetup on the event list

Comment thread src/components/ui/map.tsx Outdated
Comment on lines +94 to +95
dark: "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json",
light: "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We should check to use another map provider because we need a license for carto

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

i have found new Map Tiles OpenFreeMap

Comment thread src/components/GlobalMap/GlobalMap.tsx Outdated
Comment on lines +53 to +56
coordinates: [city.location.lng, city.location.lat] as [
number,
number,
],

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we use the real address of the event instead of the city ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

  • for this to happen we will be adding more data to the project for each event/meetup while we have global one for the city .
  • the usage of lang and lat that exists already in the project makes more sense now since it's only used by the map

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/components/ui/map.tsx (1)

371-398: Consider moving marker property updates into a useEffect.

Updating marker properties (position, draggable, offset, rotation, alignment) during render (lines 371-398) are side effects. While this works because the marker instance is stable, it's more idiomatic to wrap these in a useEffect with appropriate dependencies. This is a minor refactor suggestion.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/map.tsx` around lines 371 - 398, The code is mutating
Marker properties during render; move all marker updates (position, draggable,
offset, rotation, rotationAlignment, pitchAlignment) into a useEffect that runs
when the Marker instance or any relevant prop changes: include marker,
longitude, latitude, draggable, and
markerOptions.{offset,rotation,rotationAlignment,pitchAlignment} in the
dependency array; inside the effect guard that marker exists (e.g., if (!marker)
return), compute newOffsetX/Y like current code and call marker.setLngLat,
setDraggable, setOffset, setRotation, setRotationAlignment, setPitchAlignment
only when values differ to preserve current checks. Ensure no cleanup is
required besides leaving the marker intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/components/ui/map.tsx`:
- Around line 371-398: The code is mutating Marker properties during render;
move all marker updates (position, draggable, offset, rotation,
rotationAlignment, pitchAlignment) into a useEffect that runs when the Marker
instance or any relevant prop changes: include marker, longitude, latitude,
draggable, and markerOptions.{offset,rotation,rotationAlignment,pitchAlignment}
in the dependency array; inside the effect guard that marker exists (e.g., if
(!marker) return), compute newOffsetX/Y like current code and call
marker.setLngLat, setDraggable, setOffset, setRotation, setRotationAlignment,
setPitchAlignment only when values differ to preserve current checks. Ensure no
cleanup is required besides leaving the marker intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2b76c77b-7458-4e83-9b8c-cc2604551a45

📥 Commits

Reviewing files that changed from the base of the PR and between dff3f9f and 6302c27.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • package.json
  • src/components/GlobalMap/GlobalMap.tsx
  • src/components/ui/map.tsx
  • src/lib/events.ts
  • src/pages/index.astro
  • src/styles/globals.css
✅ Files skipped from review due to trivial changes (2)
  • package.json
  • src/styles/globals.css

Comment thread src/styles/globals.css Outdated
Comment on lines +98 to +100
.maplibregl-popup-content {
background: transparent !important;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is it possible to avoid using !important ?

Comment thread src/components/GlobalMap/GlobalMap.tsx Outdated
</div>
</div>
</div>
<div className="absolute left-1/2 top-4 -translate-x-1/2 rounded-lg border-2 bg-black px-4 py-2">

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I feel like this is too visible and taking too much visual space:

Image

Comment thread src/components/GlobalMap/GlobalMap.tsx Outdated
const [selectedCity, setSelectedCity] = useState<PopupInfo | null>(null);

const { geoJsonData, totalEvents } = useMemo(() => {
const features = Object.entries(events)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I feel like this should be done server side. At the time of retrieving the collection data

This changes the events prop for the component

@ntatoud

ntatoud commented Mar 26, 2026

Copy link
Copy Markdown
Collaborator
image These feels odd and does not really feed the global app styling

@JeanneGrenet JeanneGrenet left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can you please rebase this PR

houssembaltii and others added 21 commits April 10, 2026 10:03
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ted cities

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… specific selector

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Disable built-in attribution to prevent layout shifts on mobile
- Add pointer-events-none custom attribution that never blocks touch
- Move "Drag to explore" to top-left, desktop-only (hidden on mobile)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Moves the city GeoJSON feature building from a client-side useMemo in
GlobalMap to a server-side getCitiesGeoJson function in events.ts,
reducing client bundle size and aligning with the Astro data-fetching pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces hardcoded hex values with design tokens, fixes blurry text by
removing backdrop-blur, adds visual hierarchy to the city/country header,
and aligns typography and spacing with the site's EventCard patterns.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n map

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants