Ubicación y Cercanía — Referencia técnica
← Volver a Ubicación y Cercanía
Dónde vive esto
Backend
apps/backend/graphql/types/user-location.type.js/graphql/resolvers/user-location.resolver.js—myLocation,nearbyUsers,updateMyLocation,deleteMyLocation. Construido en esta pasada — era la función "fantasma" más grande encontrada en toda la auditoría del código base: existía la lógica completa del manager sin ninguna exposición en GraphQL en ningún lado.apps/backend/managers/user-managers/user-location.manager.js—updateLocation,getLocation,deleteLocation,getNearbyUsers, cálculo de distancia por fórmula de Haversineapps/backend/data-access-services/user/user-location.access-service.js— acceso a la base de datos de ubicaciónapps/backend/database/models/UserLocation.js— modeloUserLocation(latitude,longitude,city,country,isPublic)
Frontend
apps/frontend-nextjs/src/page-components/settings/LocationSharingPage.tsx— compartir/actualizar la ubicación mediante la API de Geolocalización del navegador, alternar la visibilidad pública, dejar de compartir, además de una lista integrada de "People nearby" (nearbyUsers). En Configuración → Ubicación (/settings/location).apps/frontend-nextjs/src/page-components/DiscoverPeoplePage.tsx— pantalla completa de descubrimiento en/discover-people; su sección "Cerca de ti" llama anearbyUsers, con un enlace de estado vacío hacia/settings/locationcuando quien llama todavía no ha compartido su propia ubicaciónapps/frontend-nextjs/src/components/discovery/RecommendationsSection.tsx— tiene una pestaña "Cercanos" respaldada pornearbyUsers, accesible mediante una barra de pestañas de categorías real y clicable (Recommended/You may know/Similar/Trending/Nearby) — esta documentación antes decía que la barra de pestañas se había eliminado yactiveTabestaba fijado; corregido
Bugs reales corregidos al construir esto
user-location.manager.js llamaba a cuatro métodos de user-location.access-service.js — findByUserId, getAll, getByCity, getByCountry — que no existían en esa clase (solo tenía findById/findByUser/create/update/updateByUser/delete). Cualquier método del manager que tocara datos de ubicación (getLocation, updateLocation, deleteLocation, getNearbyUsers, searchByLocation, getPopularLocations) habría lanzado TypeError: ...is not a function en su primera llamada real. Se agregaron los cuatro métodos faltantes como envoltorios simples.
getNearbyUsers devolvía las coordenadas exactas de todos los usuarios sin ningún filtro de privacidad, aunque UserLocation.isPublic viene en false por defecto precisamente para que la ubicación de un usuario no se comparta hasta que decida activarlo — una fuga de privacidad real si esto se hubiera publicado tal cual estaba escrito. Se corrigió para filtrar por isPublic === true.
canSeeLocation() y setLocationPrivacy() están sin terminar — canSeeLocation son tres comentarios // TODO que siempre return true, y setLocationPrivacy escribe en una columna privacy_level que no existe en el modelo UserLocation (Sequelize descarta silenciosamente los atributos desconocidos en .update(), así que era un no-op silencioso). Ninguno de los dos se expone en esta capa de GraphQL — isPublic (un booleano simple) es el único control de privacidad de ubicación que el modelo realmente persiste y aplica. Un sistema de tres niveles público/amigos/privado, si se quiere más adelante, necesita primero una columna real y una aplicación real en canSeeLocation.
updateLocation antes nunca tocaba isPublic en absoluto (siempre lo dejaba en el valor por defecto false del modelo al crearse, nunca lo actualizaba) y escribía en una columna accuracy que no existe. Se corrigió para aceptar y persistir isPublic.
Checklist de implementación técnica
-
myLocation/updateMyLocation/deleteMyLocation— conectado;LocationSharingPage.tsx -
nearbyUsers— conectado, filtrado solo a quienes activaronisPublic: true; usado por la lista "People nearby" deLocationSharingPage.tsxy por la sección "Cerca de ti" deDiscoverPeoplePage.tsx(RecommendationsSection.tsxtambién tiene una pestaña de cercanos, pero su barra de pestañas está actualmente oculta, por lo que esa ruta no es accesible desde la interfaz) - Feed local por radio (
getLocalFeeden el manager) — no expuesto en GraphQL - Búsqueda por ciudad/país o coordenadas (
searchByLocationen el manager) — no expuesto en GraphQL - Ubicaciones populares y estadísticas (
getPopularLocations/getLocationStatsen el manager) — no expuesto en GraphQL - Privacidad de ubicación por niveles (
público/amigos/privado) —setLocationPrivacy/canSeeLocationson stubs sin terminar; hoy solo existe un interruptor binarioisPublicreal - El envío de ubicación estática dentro de los mensajes de chat (
shareLocation) es una función aparte que ya está conectada — la acción rápida "share location" del compositor enChatView.tsx(handleShareLocation) la invoca, yMessageBubble.tsxrenderiza el mensaje de ubicación resultante. Los resolvers de transmisión de ubicación en vivo (updateLiveLocation/stopLiveLocation) también existen enmessage.resolver.js, pero nada en el frontend los invoca todavía yLocationInputsigue sin tener un campolocationTypepara marcar un mensaje como en vivo; ver Mensajes y Conversaciones
Modelo UserLocation
| Campo | Descripción |
|---|---|
latitude / longitude | Coordenadas (WGS84), DECIMAL(10,8)/DECIMAL(11,8) |
city / country | Ubicación legible (opcional, no se geocodifica en reversa del lado del servidor — el frontend no los envía hoy) |
isPublic | Booleano, false por defecto. El único control de privacidad que realmente se aplica. |
Compartir y actualizar ubicación
mutation UpdateMyLocation($input: UpdateLocationInput!) {
updateMyLocation(input: $input) { id city country latitude longitude isPublic updatedAt }
}
query MyLocation { myLocation { id city country latitude longitude isPublic updatedAt } }
mutation DeleteMyLocation { deleteMyLocation }
UpdateLocationInput: latitude: Float!, longitude: Float!, city: String, country: String, isPublic: Boolean (por defecto false/privado en el primer envío si se omite). Llama a updateMyLocation de nuevo con las mismas coordenadas para simplemente alternar isPublic sin volver a pedir el permiso de geolocalización del navegador.
Usuarios cercanos
query NearbyUsers($radiusKm: Float, $limit: Int) {
nearbyUsers(radiusKm: $radiusKm, limit: $limit) {
user { id username profilePicture isVerified }
distanceKm
city
}
}
Requiere que quien llama ya haya compartido su propia ubicación (myLocation debe ser distinto de null) — si no, nearbyUsers devuelve []. Solo devuelve a otros usuarios con isPublic: true, ordenados por distancia, calculada con la fórmula de Haversine en user-location.manager.js#calculateDistance.