Faro & OSM
Run Tests on Branches / Detect Changes (push) Successful in 11s
Run Tests on Branches / Backend Tests (push) Has been skipped
Run Tests on Branches / Frontend Tests (push) Has been skipped
Run Tests on Branches / Frontend Mobile Tests (push) Successful in 1m53s
Run Tests on Branches / Parapharmacy API Tests (push) Has been skipped
Run Tests on Branches / PIP Platform Tests (push) Has been skipped

This commit is contained in:
2026-07-17 15:58:46 +00:00
parent 159e14bed8
commit 2f24dd4ff8
8 changed files with 54 additions and 220 deletions
-5
View File
@@ -6,8 +6,3 @@ EXPO_PUBLIC_API_URL=http://localhost:3001/api
# For production builds, update this to:
# EXPO_PUBLIC_API_URL=https://api.yourdomain.com/api
# Grafana Alloy OTLP/HTTP endpoint for Faro RUM (must be reachable from the
# device — use a LAN IP / public hostname, NOT localhost).
# Open router :4318 → srv84-macos:4318, or proxy /faro via Nginx Proxy Manager.
EXPO_PUBLIC_FARO_URL=http://grafana.hacecalor.net:4318
+9 -1
View File
@@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react';
import { View, StyleSheet, Text } from 'react-native';
import MapView, { Marker } from 'react-native-maps';
import MapView, { Marker, UrlTile, PROVIDER_DEFAULT } from 'react-native-maps';
import { useRouter } from 'expo-router';
import { getPharmacies } from '../../services/pharmacies';
import { LoadingSpinner } from '../../components/LoadingSpinner';
@@ -54,11 +54,19 @@ export default function MapScreen() {
<View style={styles.container}>
<MapView
style={styles.map}
provider={PROVIDER_DEFAULT}
mapType="none"
region={region}
onRegionChangeComplete={setRegion}
showsUserLocation={true}
showsMyLocationButton={true}
>
<UrlTile
urlTemplate="https://tile.openstreetmap.org/{z}/{x}/{y}.png"
maximumZ={19}
flipY={false}
tileSize={256}
/>
{pharmacies.map((pharmacy) => (
<Marker
key={pharmacy.id}
-4
View File
@@ -9,10 +9,6 @@ import { useAuthStore } from '../store/authStore';
import { registerForPushNotifications, addNotificationListener, addNotificationResponseListener } from '../services/notifications';
import { ThemeProvider, useThemeContext } from '../components/ThemeProvider';
import { LanguageProvider, useTranslation } from '../src/i18n';
import { initFaro } from '../services/faro';
// Boot Faro RUM once, as early as possible.
initFaro();
const queryClient = new QueryClient();
+36 -11
View File
@@ -3,6 +3,7 @@ import { View, Text, ScrollView, StyleSheet, TouchableOpacity, Linking } from 'r
import { useLocalSearchParams, useRouter } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
import * as Location from 'expo-location';
import MapView, { Marker, UrlTile, PROVIDER_DEFAULT } from 'react-native-maps';
import { getMedicine, getMedicinePharmacies } from '../../services/medicines';
import { subscribeToMedicine, unsubscribeFromMedicine } from '../../services/notifications';
import { useAuth } from '../../hooks/useAuth';
@@ -205,11 +206,39 @@ export default function MedicineDetailScreen() {
</View>
{locatedPharmacies.length > 0 && (
<View style={[styles.mapContainer, { backgroundColor: colors.surfaceVariant }]}>
<Ionicons name="map-outline" size={48} color={colors.textSecondary} />
<Text style={[styles.mapPlaceholder, { color: colors.textSecondary }]}>
{t('medicine.mapSoon')}
</Text>
<View style={styles.mapContainer}>
<MapView
style={styles.map}
provider={PROVIDER_DEFAULT}
mapType="none"
initialRegion={{
latitude: mapCenter.latitude,
longitude: mapCenter.longitude,
latitudeDelta: 0.05,
longitudeDelta: 0.05,
}}
>
<UrlTile
urlTemplate="https://tile.openstreetmap.org/{z}/{x}/{y}.png"
maximumZ={19}
flipY={false}
tileSize={256}
/>
{locatedPharmacies.map((pharm) => {
const lat = getPharmacyLat(pharm);
const lon = getPharmacyLon(pharm);
if (lat == null || lon == null) return null;
return (
<Marker
key={pharm.id}
coordinate={{ latitude: lat, longitude: lon }}
title={pharm.pharmacy?.name || pharm.name}
description={pharm.pharmacy?.address || pharm.address}
onCalloutPress={() => router.push(`/pharmacy/${pharm.pharmacy_id || pharm.id}`)}
/>
);
})}
</MapView>
</View>
)}
@@ -362,13 +391,9 @@ const styles = StyleSheet.create({
height: 200,
borderRadius: borderRadius.lg,
overflow: 'hidden',
alignItems: 'center',
justifyContent: 'center',
gap: spacing.sm,
},
mapPlaceholder: {
fontSize: 14,
fontWeight: '500',
map: {
flex: 1,
},
pharmaciesSection: {
marginTop: spacing.sm,
+9 -1
View File
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
import { View, Text, ScrollView, StyleSheet, TouchableOpacity, Linking } from 'react-native';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
import MapView, { Marker } from 'react-native-maps';
import MapView, { Marker, UrlTile, PROVIDER_DEFAULT } from 'react-native-maps';
import { getPharmacy, getPharmacyMedicines } from '../../services/pharmacies';
import { subscribeToMedicine, unsubscribeFromMedicine } from '../../services/notifications';
import { useAuth } from '../../hooks/useAuth';
@@ -125,6 +125,8 @@ export default function PharmacyDetailScreen() {
<View style={styles.mapContainer}>
<MapView
style={styles.map}
provider={PROVIDER_DEFAULT}
mapType="none"
initialRegion={{
latitude: pharmacy.latitude,
longitude: pharmacy.longitude,
@@ -133,6 +135,12 @@ export default function PharmacyDetailScreen() {
}}
scrollEnabled={false}
>
<UrlTile
urlTemplate="https://tile.openstreetmap.org/{z}/{x}/{y}.png"
maximumZ={19}
flipY={false}
tileSize={256}
/>
<Marker
coordinate={{
latitude: pharmacy.latitude,
-1
View File
@@ -4,7 +4,6 @@
"main": "expo-router/entry",
"dependencies": {
"@expo/vector-icons": "^15.0.2",
"@grafana/faro-react-native": "^1.3.0",
"@react-native-async-storage/async-storage": "2.2.0",
"@tanstack/react-query": "^5.101.2",
"axios": "^1.18.1",
-36
View File
@@ -1,36 +0,0 @@
import { initializeFaro } from '@grafana/faro-react-native';
let initialized = false;
/**
* Initializes Grafana Faro RUM for the React Native app.
*
* Telemetry (crashes, JS errors, console, app-start, memory/ANR vitals,
* session + screen tracking) is sent OTLP/HTTP to the Alloy collector.
*
* NOTE: `@grafana/faro-react-native` ships a native module, so it requires a
* custom dev build / EAS build (not Expo Go). Initialization is wrapped in a
* try/catch so a collector outage never crashes the app.
*/
export function initFaro(): void {
if (initialized) return;
initialized = true;
try {
const url =
process.env.EXPO_PUBLIC_FARO_URL || 'http://grafana.hacecalor.net:4318';
initializeFaro({
app: {
name: 'farmafinder-mobile',
version: '1.0.0',
environment: __DEV__ ? 'development' : 'production',
},
url,
sessionTracking: { enabled: true },
// Crash/JS error/console capture are on by default.
});
} catch (err) {
console.warn('[faro] initialization failed:', err);
}
}