Event API — Real Implementations (feat/event-api-stubs)

Branch: feat/event-api-stubs File: lib/core/network/event_api_service.dart


Background

Before this feature, three core EventApiService methods were stubs that returned hardcoded mock data:

MethodOld behaviour
getNearbyEvents()returned hardcoded mock list
getTrendingEvents()returned hardcoded mock list
searchEvents()returned hardcoded mock list

This meant the event discovery screen, the radar map, and search results never showed real events from Supabase.


What Changed

getNearbyEvents(lat, lon, radiusKm)

Three-tier fallback chain:

  1. RPC first — calls get_nearby_events(p_lat, p_lon, p_radius_km, p_limit, p_offset) (Haversine in SQL, no PostGIS required).
  2. Bounding-box fallback — if RPC throws (e.g. function not deployed), fetches up to 200 active future events inside an approximate bounding box and filters them client-side using an inline Taylor-series cosine approximation.
  3. Empty list — on all other failures, returns [] silently (error reported to IssueReportService).
// Haversine distance (km) — computed in SQL or client-side
6371 × acos(cos(lat1) × cos(lat2) × cos(lon2 - lon1) + sin(lat1) × sin(lat2))

SQL migration required: supabase/migrations/20260318000001_get_nearby_events_rpc.sql

getTrendingEvents(limit, timeframeDays)

Queries the events table directly, ordering by view_count DESC, share_count DESC, filtered to:

  • status = 'active'
  • start_date >= NOW()
  • start_date <= NOW() + timeframeDays

Falls back to [] on error.

searchEvents(query, category, type, limit, offset)

Uses PostgREST’s .or() filter to search across title and description with ilike:

title.ilike.%query%,description.ilike.%query%

Optional category and type filters add .eq() clauses. Falls back to [] on error.


Supabase Migration

20260318000001_get_nearby_events_rpc.sql — creates the get_nearby_events PostgreSQL function:

CREATE OR REPLACE FUNCTION public.get_nearby_events(
  p_lat FLOAT, p_lon FLOAT,
  p_radius_km FLOAT DEFAULT 10.0,
  p_limit INT DEFAULT 20, p_offset INT DEFAULT 0
)
RETURNS SETOF public.events
LANGUAGE sql STABLE SECURITY DEFINER
AS $$
  SELECT e.* FROM public.events e
  WHERE e.status = 'active'
    AND e.start_date >= NOW()
    AND (
      6371.0 * ACOS(LEAST(1.0,
        COS(RADIANS(p_lat)) * COS(RADIANS((e.location->>'latitude')::FLOAT))
        * COS(RADIANS((e.location->>'longitude')::FLOAT) - RADIANS(p_lon))
        + SIN(RADIANS(p_lat)) * SIN(RADIANS((e.location->>'latitude')::FLOAT))
      ))
    ) <= p_radius_km
  ORDER BY /* same expression */ ASC
  LIMIT p_limit OFFSET p_offset;
$$;
GRANT EXECUTE ON FUNCTION public.get_nearby_events TO authenticated, anon;

Run with: supabase db push or paste into the Supabase SQL editor.


Design Decisions

  • No PostGIS required — the Haversine formula is implemented in pure SQL using standard trig functions, making it portable to any Postgres instance.
  • Client-side fallback — the bounding-box + inline cosine approach avoids an import of dart:math for a one-off calculation; it uses a Taylor series (cos(x) ≈ 1 - x²/2 + x⁴/24) accurate enough for small distances.
  • Silent failures — all three methods return empty lists on error rather than propagating exceptions, since the event discovery UI handles empty state gracefully.