@wasto/cms-components 1.5.11 → 1.5.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -133,13 +133,13 @@ function useTheme() {
133
133
  }
134
134
 
135
135
  // src/hooks/useBlogPosts.ts
136
- import { useCallback as useCallback7, useEffect as useEffect8, useState as useState7 } from "react";
136
+ import { useCallback as useCallback7, useEffect as useEffect9, useState as useState7 } from "react";
137
137
 
138
138
  // src/contexts/WastoContext.tsx
139
139
  import {
140
140
  createContext as createContext6,
141
141
  useContext as useContext7,
142
- useEffect as useEffect6,
142
+ useEffect as useEffect7,
143
143
  useMemo as useMemo7
144
144
  } from "react";
145
145
 
@@ -2106,6 +2106,130 @@ var styles4 = StyleSheet4.create({
2106
2106
  }
2107
2107
  });
2108
2108
 
2109
+ // src/components/push/PushTokenRegistrar.tsx
2110
+ import { useEffect as useEffect6, useRef as useRef3 } from "react";
2111
+ import { Platform as Platform5 } from "react-native";
2112
+
2113
+ // src/push/registerExpoPushToken.ts
2114
+ import { Platform as Platform4 } from "react-native";
2115
+ var notificationsModule;
2116
+ var constantsModule;
2117
+ var handlerConfigured = false;
2118
+ function loadNotifications() {
2119
+ if (notificationsModule !== void 0) return notificationsModule;
2120
+ try {
2121
+ notificationsModule = __require("expo-notifications");
2122
+ } catch {
2123
+ notificationsModule = null;
2124
+ }
2125
+ return notificationsModule;
2126
+ }
2127
+ function loadConstants() {
2128
+ if (constantsModule !== void 0) return constantsModule;
2129
+ try {
2130
+ const mod = __require("expo-constants");
2131
+ constantsModule = "default" in mod && mod.default ? mod.default : mod;
2132
+ } catch {
2133
+ constantsModule = null;
2134
+ }
2135
+ return constantsModule;
2136
+ }
2137
+ function resolveEasProjectId() {
2138
+ const Constants = loadConstants();
2139
+ const fromExtra = Constants?.expoConfig?.extra?.eas?.projectId;
2140
+ const fromEasConfig = Constants?.easConfig?.projectId;
2141
+ const fromEnv = typeof process !== "undefined" ? String(process.env.EXPO_PUBLIC_EAS_PROJECT_ID ?? "").trim() : "";
2142
+ const projectId = String(fromExtra || fromEasConfig || fromEnv || "").trim();
2143
+ return projectId || null;
2144
+ }
2145
+ async function registerExpoPushToken(options) {
2146
+ if (Platform4.OS === "web") return null;
2147
+ const deviceId = options.deviceId?.trim();
2148
+ if (!deviceId) return null;
2149
+ const Notifications = loadNotifications();
2150
+ if (!Notifications) {
2151
+ if (__DEV__) {
2152
+ console.warn(
2153
+ "[wasto] expo-notifications is not installed; push registration skipped"
2154
+ );
2155
+ }
2156
+ return null;
2157
+ }
2158
+ const projectId = resolveEasProjectId();
2159
+ if (!projectId) {
2160
+ if (__DEV__) {
2161
+ console.warn(
2162
+ "[wasto] EAS projectId missing (extra.eas.projectId or EXPO_PUBLIC_EAS_PROJECT_ID); push registration skipped"
2163
+ );
2164
+ }
2165
+ return null;
2166
+ }
2167
+ if (!handlerConfigured) {
2168
+ Notifications.setNotificationHandler({
2169
+ handleNotification: async () => ({
2170
+ shouldShowAlert: true,
2171
+ shouldPlaySound: true,
2172
+ shouldSetBadge: false,
2173
+ shouldShowBanner: true,
2174
+ shouldShowList: true
2175
+ })
2176
+ });
2177
+ handlerConfigured = true;
2178
+ }
2179
+ if (Platform4.OS === "android") {
2180
+ await Notifications.setNotificationChannelAsync("default", {
2181
+ name: "default",
2182
+ importance: Notifications.AndroidImportance.MAX,
2183
+ vibrationPattern: [0, 250, 250, 250],
2184
+ lightColor: "#FF231F7C"
2185
+ });
2186
+ }
2187
+ const { status: existingStatus } = await Notifications.getPermissionsAsync();
2188
+ let finalStatus = existingStatus;
2189
+ if (existingStatus !== "granted") {
2190
+ const { status } = await Notifications.requestPermissionsAsync();
2191
+ finalStatus = status;
2192
+ }
2193
+ if (finalStatus !== "granted") return null;
2194
+ const push = await Notifications.getExpoPushTokenAsync({ projectId });
2195
+ const token = push?.data?.trim();
2196
+ if (!token) return null;
2197
+ await options.registerPushToken(deviceId, token);
2198
+ return token;
2199
+ }
2200
+
2201
+ // src/components/push/PushTokenRegistrar.tsx
2202
+ function PushTokenRegistrar() {
2203
+ const { deviceId } = useLocal();
2204
+ const { previewMode, reportError: reportError2 } = useWasto();
2205
+ const client = useWastoClient();
2206
+ const lastRegistered = useRef3(null);
2207
+ useEffect6(() => {
2208
+ if (previewMode || Platform5.OS === "web" || !deviceId) return;
2209
+ if (lastRegistered.current === deviceId) return;
2210
+ let cancelled = false;
2211
+ void (async () => {
2212
+ try {
2213
+ const token = await registerExpoPushToken({
2214
+ deviceId,
2215
+ registerPushToken: (id, pushToken) => client.public.devices.registerPushToken(id, { token: pushToken })
2216
+ });
2217
+ if (!cancelled && token) {
2218
+ lastRegistered.current = deviceId;
2219
+ }
2220
+ } catch (error) {
2221
+ if (!cancelled) {
2222
+ reportError2(error, { source: "push", deviceId });
2223
+ }
2224
+ }
2225
+ })();
2226
+ return () => {
2227
+ cancelled = true;
2228
+ };
2229
+ }, [client.public.devices, deviceId, previewMode, reportError2]);
2230
+ return null;
2231
+ }
2232
+
2109
2233
  // src/components/safe-area/OptionalSafeAreaProvider.tsx
2110
2234
  import { useContext as useContext6 } from "react";
2111
2235
  import {
@@ -2217,7 +2341,7 @@ function WastoProvider(props) {
2217
2341
  );
2218
2342
  const previewMode = Boolean(props.previewMode);
2219
2343
  const sentryConfig = props.sentry;
2220
- useEffect6(() => {
2344
+ useEffect7(() => {
2221
2345
  if (!sentryConfig) return;
2222
2346
  initWastoSentry({
2223
2347
  ...sentryConfig,
@@ -2252,11 +2376,14 @@ function WastoProvider(props) {
2252
2376
  }),
2253
2377
  [apiUrl, appId, cityGroupId, previewMode, reportError2, onRequestError]
2254
2378
  );
2255
- return /* @__PURE__ */ jsx10(WastoContext, { value, children: /* @__PURE__ */ jsx10(OptionalSafeAreaProvider, { children: /* @__PURE__ */ jsx10(LocalProvider, { previewMode, children: /* @__PURE__ */ jsx10(ChallengesProvider, { children: /* @__PURE__ */ jsx10(AnnouncementsProvider, { apiUrl, children: /* @__PURE__ */ jsx10(ThemeProvider, { theme: props.theme, children: /* @__PURE__ */ jsxs5(AppDataProvider, { apiUrl, children: [
2256
- props.children,
2257
- /* @__PURE__ */ jsx10(SelectStreetModal, {}),
2258
- /* @__PURE__ */ jsx10(AnnouncementModal, {})
2259
- ] }) }) }) }) }) }) });
2379
+ return /* @__PURE__ */ jsx10(WastoContext, { value, children: /* @__PURE__ */ jsx10(OptionalSafeAreaProvider, { children: /* @__PURE__ */ jsxs5(LocalProvider, { previewMode, children: [
2380
+ /* @__PURE__ */ jsx10(PushTokenRegistrar, {}),
2381
+ /* @__PURE__ */ jsx10(ChallengesProvider, { children: /* @__PURE__ */ jsx10(AnnouncementsProvider, { apiUrl, children: /* @__PURE__ */ jsx10(ThemeProvider, { theme: props.theme, children: /* @__PURE__ */ jsxs5(AppDataProvider, { apiUrl, children: [
2382
+ props.children,
2383
+ /* @__PURE__ */ jsx10(SelectStreetModal, {}),
2384
+ /* @__PURE__ */ jsx10(AnnouncementModal, {})
2385
+ ] }) }) }) })
2386
+ ] }) }) });
2260
2387
  }
2261
2388
  function useWasto() {
2262
2389
  const context = useContext7(WastoContext);
@@ -2416,7 +2543,7 @@ function formatArticleDate(value) {
2416
2543
  }
2417
2544
 
2418
2545
  // src/hooks/useNextCollection.ts
2419
- import { useCallback as useCallback6, useEffect as useEffect7, useMemo as useMemo8, useState as useState6 } from "react";
2546
+ import { useCallback as useCallback6, useEffect as useEffect8, useMemo as useMemo8, useState as useState6 } from "react";
2420
2547
  function useNextCollection(options = {}) {
2421
2548
  const { selectedCityId, selectedPlaceId: localPlaceId } = useLocal();
2422
2549
  const appData = useOptionalAppData();
@@ -2466,7 +2593,7 @@ function useNextCollection(options = {}) {
2466
2593
  setIsLoading(false);
2467
2594
  }
2468
2595
  }, [appData, client.public.cities, pageSize, resolvedCityId, useShared]);
2469
- useEffect7(() => {
2596
+ useEffect8(() => {
2470
2597
  void fetchData();
2471
2598
  }, [fetchData]);
2472
2599
  const sharedWasteTypes = useShared && appData ? appData.wasteTypes : wasteTypes;
@@ -2524,7 +2651,7 @@ function useBlogPosts(options = {}) {
2524
2651
  setIsLoading(false);
2525
2652
  }
2526
2653
  }, [cityGroupId, client.public.blogPosts, pageSize]);
2527
- useEffect8(() => {
2654
+ useEffect9(() => {
2528
2655
  void fetchPosts();
2529
2656
  }, [fetchPosts]);
2530
2657
  return { posts, isLoading, error, refetch: fetchPosts };
@@ -2534,7 +2661,7 @@ function useBlogPost(articleId) {
2534
2661
  const [post, setPost] = useState7(null);
2535
2662
  const [isLoading, setIsLoading] = useState7(false);
2536
2663
  const [error, setError] = useState7(null);
2537
- useEffect8(() => {
2664
+ useEffect9(() => {
2538
2665
  const id = articleId.trim();
2539
2666
  if (!id) {
2540
2667
  setPost(null);
@@ -3209,7 +3336,7 @@ function useChallenges(_options = {}) {
3209
3336
  }
3210
3337
 
3211
3338
  // src/hooks/useBadges.ts
3212
- import { useCallback as useCallback8, useEffect as useEffect9, useState as useState11 } from "react";
3339
+ import { useCallback as useCallback8, useEffect as useEffect10, useState as useState11 } from "react";
3213
3340
  function useBadges(options = {}) {
3214
3341
  const { selectedCityId } = useLocal();
3215
3342
  const resolvedCityId = options.cityId ?? selectedCityId ?? null;
@@ -3245,7 +3372,7 @@ function useBadges(options = {}) {
3245
3372
  setIsLoading(false);
3246
3373
  }
3247
3374
  }, [client.public.cities, page, pageSize, resolvedCityId]);
3248
- useEffect9(() => {
3375
+ useEffect10(() => {
3249
3376
  void fetchBadges();
3250
3377
  }, [fetchBadges]);
3251
3378
  const getBadgeById = useCallback8((id) => badges.find((b) => b.id === id), [badges]);
@@ -3485,7 +3612,7 @@ var styles13 = StyleSheet14.create({
3485
3612
  // src/components/challenge/ChallengesModal.tsx
3486
3613
  import { Dimensions as Dimensions4, Modal as Modal6, Pressable as Pressable7, ScrollView as ScrollView7, StyleSheet as StyleSheet18, Text as Text15, View as View17 } from "react-native";
3487
3614
  import { Play as Play2, Trophy as Trophy2, XIcon as XIcon4 } from "lucide-react-native";
3488
- import { useEffect as useEffect11, useState as useState15 } from "react";
3615
+ import { useEffect as useEffect12, useState as useState15 } from "react";
3489
3616
 
3490
3617
  // src/components/challenge/ChallengeModal.tsx
3491
3618
  import { Dimensions as Dimensions3, Pressable as Pressable6, ScrollView as ScrollView6, StyleSheet as StyleSheet17, Text as Text14, View as View16 } from "react-native";
@@ -3493,7 +3620,7 @@ import { Play, Trophy, XIcon as XIcon3 } from "lucide-react-native";
3493
3620
  import { useState as useState14 } from "react";
3494
3621
 
3495
3622
  // src/components/challenge/QuizModal.tsx
3496
- import { useEffect as useEffect10, useMemo as useMemo13, useRef as useRef3, useState as useState13 } from "react";
3623
+ import { useEffect as useEffect11, useMemo as useMemo13, useRef as useRef4, useState as useState13 } from "react";
3497
3624
  import {
3498
3625
  ActivityIndicator as ActivityIndicator7,
3499
3626
  Dimensions as Dimensions2,
@@ -3793,9 +3920,9 @@ function QuizModal({
3793
3920
  const [correct, setCorrect] = useState13(0);
3794
3921
  const [incorrect, setIncorrect] = useState13(0);
3795
3922
  const [showResults, setShowResults] = useState13(false);
3796
- const completedRef = useRef3(false);
3923
+ const completedRef = useRef4(false);
3797
3924
  const question = questions[index];
3798
- useEffect10(() => {
3925
+ useEffect11(() => {
3799
3926
  setIndex(0);
3800
3927
  setSelected(null);
3801
3928
  setCorrect(0);
@@ -4285,7 +4412,7 @@ function ChallengesModal({
4285
4412
  const bottomInset = useSafeAreaPadding(["bottom"]);
4286
4413
  const [selectedChallengeId, setSelectedChallengeId] = useState15(null);
4287
4414
  const selectedChallenge = selectedChallengeId != null ? challenges.find((c) => c.id === selectedChallengeId) ?? null : null;
4288
- useEffect11(() => {
4415
+ useEffect12(() => {
4289
4416
  if (!visible) setSelectedChallengeId(null);
4290
4417
  }, [visible]);
4291
4418
  const handleSelectChallenge = (challenge) => () => {
@@ -5016,7 +5143,7 @@ var styles19 = StyleSheet20.create({
5016
5143
  });
5017
5144
 
5018
5145
  // src/components/chat/Chat.tsx
5019
- import { useCallback as useCallback9, useEffect as useEffect12, useRef as useRef4, useState as useState18 } from "react";
5146
+ import { useCallback as useCallback9, useEffect as useEffect13, useRef as useRef5, useState as useState18 } from "react";
5020
5147
  import {
5021
5148
  ActivityIndicator as ActivityIndicator10,
5022
5149
  FlatList as FlatList2,
@@ -5391,11 +5518,11 @@ function Chat({ title, cityId, cityGroupId: cityGroupIdProp }) {
5391
5518
  null
5392
5519
  );
5393
5520
  const [pendingSectors, setPendingSectors] = useState18([]);
5394
- const listRef = useRef4(null);
5395
- useEffect12(() => {
5521
+ const listRef = useRef5(null);
5522
+ useEffect13(() => {
5396
5523
  setNeedsAddress(!hasAddress);
5397
5524
  }, [hasAddress]);
5398
- useEffect12(() => {
5525
+ useEffect13(() => {
5399
5526
  let cancelled = false;
5400
5527
  (async () => {
5401
5528
  if (!cityGroupId && !resolvedCityId) {
@@ -5423,7 +5550,7 @@ function Chat({ title, cityId, cityGroupId: cityGroupIdProp }) {
5423
5550
  cancelled = true;
5424
5551
  };
5425
5552
  }, [client, cityGroupId, resolvedCityId]);
5426
- useEffect12(() => {
5553
+ useEffect13(() => {
5427
5554
  if (!needsAddress || !open) return;
5428
5555
  const trimmed = addressQuery.trim();
5429
5556
  if (trimmed.length < 2 || !cityGroupId) {
@@ -6409,7 +6536,7 @@ var styles22 = StyleSheet23.create({
6409
6536
  // src/components/next-collection/NextCollection.tsx
6410
6537
  import {
6411
6538
  Dimensions as Dimensions6,
6412
- Platform as Platform4,
6539
+ Platform as Platform6,
6413
6540
  Pressable as Pressable11,
6414
6541
  StyleSheet as StyleSheet24,
6415
6542
  Text as Text20,
@@ -6429,7 +6556,7 @@ import Animated2, {
6429
6556
  useSharedValue
6430
6557
  } from "react-native-reanimated";
6431
6558
  import { ChevronLeftIcon as ChevronLeftIcon2, ChevronRightIcon } from "lucide-react-native";
6432
- import { useCallback as useCallback11, useEffect as useEffect13, useMemo as useMemo17, useRef as useRef5, useState as useState20 } from "react";
6559
+ import { useCallback as useCallback11, useEffect as useEffect14, useMemo as useMemo17, useRef as useRef6, useState as useState20 } from "react";
6433
6560
 
6434
6561
  // src/navigation/NavigationContext.tsx
6435
6562
  import {
@@ -6514,7 +6641,7 @@ function CollectionSlide({
6514
6641
  width: cardWidth,
6515
6642
  height: cardHeight,
6516
6643
  marginRight: isLast ? 0 : CARD_GAP,
6517
- ...Platform4.OS === "web" ? { scrollSnapAlign: "center", scrollSnapStop: "always" } : null
6644
+ ...Platform6.OS === "web" ? { scrollSnapAlign: "center", scrollSnapStop: "always" } : null
6518
6645
  },
6519
6646
  animatedStyle
6520
6647
  ],
@@ -6681,13 +6808,13 @@ function NextCollection({
6681
6808
  const { collectionsForDay, dateLabel } = useNextCollection({
6682
6809
  dateOffset: offset
6683
6810
  });
6684
- useEffect13(() => {
6811
+ useEffect14(() => {
6685
6812
  setOffset(Math.trunc(dayOffset));
6686
6813
  }, [dayOffset]);
6687
6814
  const [selectedWasteTypeId, setSelectedWasteTypeId] = useState20(null);
6688
- const listRef = useRef5(null);
6815
+ const listRef = useRef6(null);
6689
6816
  const scrollX = useSharedValue(0);
6690
- const lastSnappedIndex = useRef5(0);
6817
+ const lastSnappedIndex = useRef6(0);
6691
6818
  const screenWidth = Dimensions6.get("window").width;
6692
6819
  const cardWidth = Math.round(screenWidth - SIDE_INSET * 2);
6693
6820
  const cardHeight = Math.round(cardWidth * CARD_ASPECT);
@@ -6828,8 +6955,8 @@ function NextCollection({
6828
6955
  horizontal: true,
6829
6956
  showsHorizontalScrollIndicator: false,
6830
6957
  decelerationRate: "fast",
6831
- snapToInterval: Platform4.OS === "web" ? void 0 : snapInterval,
6832
- snapToOffsets: Platform4.OS === "web" ? void 0 : snapOffsets,
6958
+ snapToInterval: Platform6.OS === "web" ? void 0 : snapInterval,
6959
+ snapToOffsets: Platform6.OS === "web" ? void 0 : snapOffsets,
6833
6960
  snapToAlignment: "center",
6834
6961
  disableIntervalMomentum: true,
6835
6962
  bounces: false,
@@ -6843,7 +6970,7 @@ function NextCollection({
6843
6970
  },
6844
6971
  style: [
6845
6972
  { height: cardHeight },
6846
- Platform4.OS === "web" ? {
6973
+ Platform6.OS === "web" ? {
6847
6974
  scrollSnapType: "x mandatory",
6848
6975
  WebkitOverflowScrolling: "touch"
6849
6976
  } : null
@@ -7386,15 +7513,15 @@ function Image10(props) {
7386
7513
  // src/components/locations-map/LocationsMapView.web.tsx
7387
7514
  import {
7388
7515
  createElement,
7389
- useEffect as useEffect15,
7516
+ useEffect as useEffect16,
7390
7517
  useMemo as useMemo19,
7391
- useRef as useRef6,
7518
+ useRef as useRef7,
7392
7519
  useState as useState23
7393
7520
  } from "react";
7394
7521
  import { StyleSheet as StyleSheet28, Text as Text23, View as View27 } from "react-native";
7395
7522
 
7396
7523
  // src/hooks/useLocations.ts
7397
- import { useCallback as useCallback12, useEffect as useEffect14, useMemo as useMemo18, useState as useState22 } from "react";
7524
+ import { useCallback as useCallback12, useEffect as useEffect15, useMemo as useMemo18, useState as useState22 } from "react";
7398
7525
  import { MAX_PAGE_SIZE } from "@wasto/sdk";
7399
7526
  var PAGE_SIZE = MAX_PAGE_SIZE;
7400
7527
  function asArray2(res) {
@@ -7455,7 +7582,7 @@ function useLocations() {
7455
7582
  setIsLoading(false);
7456
7583
  }
7457
7584
  }, [appData, client.public.cities, selectedCityId, useShared]);
7458
- useEffect14(() => {
7585
+ useEffect15(() => {
7459
7586
  if (useShared) return;
7460
7587
  void fetchLocations();
7461
7588
  }, [fetchLocations, useShared]);
@@ -7535,11 +7662,11 @@ function LocationsMapView({
7535
7662
  selectedId,
7536
7663
  onSelect
7537
7664
  }) {
7538
- const containerRef = useRef6(null);
7539
- const mapRef = useRef6(null);
7540
- const maplibreRef = useRef6(null);
7541
- const markersRef = useRef6([]);
7542
- const onSelectRef = useRef6(onSelect);
7665
+ const containerRef = useRef7(null);
7666
+ const mapRef = useRef7(null);
7667
+ const maplibreRef = useRef7(null);
7668
+ const markersRef = useRef7([]);
7669
+ const onSelectRef = useRef7(onSelect);
7543
7670
  const [mapReady, setMapReady] = useState23(false);
7544
7671
  const [mapError, setMapError] = useState23(null);
7545
7672
  const safeCenter = useMemo19(() => {
@@ -7550,10 +7677,10 @@ function LocationsMapView({
7550
7677
  () => locations.filter((loc) => isValidLatLon(loc.lat, loc.lon)),
7551
7678
  [locations]
7552
7679
  );
7553
- useEffect15(() => {
7680
+ useEffect16(() => {
7554
7681
  onSelectRef.current = onSelect;
7555
7682
  }, [onSelect]);
7556
- useEffect15(() => {
7683
+ useEffect16(() => {
7557
7684
  let cancelled = false;
7558
7685
  let resizeObserver = null;
7559
7686
  async function init() {
@@ -7625,7 +7752,7 @@ function LocationsMapView({
7625
7752
  setMapReady(false);
7626
7753
  };
7627
7754
  }, []);
7628
- useEffect15(() => {
7755
+ useEffect16(() => {
7629
7756
  if (!mapReady) return;
7630
7757
  const map = mapRef.current;
7631
7758
  const maplibregl = maplibreRef.current;
@@ -7650,7 +7777,7 @@ function LocationsMapView({
7650
7777
  () => validLocations.map((l) => l.id).join("|"),
7651
7778
  [validLocations]
7652
7779
  );
7653
- useEffect15(() => {
7780
+ useEffect16(() => {
7654
7781
  if (!mapReady) return;
7655
7782
  const map = mapRef.current;
7656
7783
  const maplibregl = maplibreRef.current;
@@ -7672,7 +7799,7 @@ function LocationsMapView({
7672
7799
  for (const loc of visible) bounds.extend([loc.lon, loc.lat]);
7673
7800
  map.fitBounds(bounds, { padding: 56, maxZoom: 13, duration: 350 });
7674
7801
  }, [locationsKey, mapReady]);
7675
- useEffect15(() => {
7802
+ useEffect16(() => {
7676
7803
  if (!mapReady) return;
7677
7804
  const map = mapRef.current;
7678
7805
  if (!map || !selectedId) return;
@@ -7724,7 +7851,7 @@ var styles27 = StyleSheet28.create({
7724
7851
  });
7725
7852
 
7726
7853
  // src/components/locations-map/LocationsMapShell.tsx
7727
- import { useEffect as useEffect17, useMemo as useMemo21, useRef as useRef7, useState as useState25 } from "react";
7854
+ import { useEffect as useEffect18, useMemo as useMemo21, useRef as useRef8, useState as useState25 } from "react";
7728
7855
  import {
7729
7856
  ActivityIndicator as ActivityIndicator11,
7730
7857
  Pressable as Pressable14,
@@ -7743,7 +7870,7 @@ import Animated5, {
7743
7870
  } from "react-native-reanimated";
7744
7871
 
7745
7872
  // src/components/locations-map/LocationSheet.tsx
7746
- import { Linking as Linking3, Platform as Platform5, Pressable as Pressable12, ScrollView as ScrollView9, Share, StyleSheet as StyleSheet29, Text as Text24, View as View28 } from "react-native";
7873
+ import { Linking as Linking3, Platform as Platform7, Pressable as Pressable12, ScrollView as ScrollView9, Share, StyleSheet as StyleSheet29, Text as Text24, View as View28 } from "react-native";
7747
7874
  import {
7748
7875
  ChevronUp,
7749
7876
  Clock,
@@ -7752,7 +7879,7 @@ import {
7752
7879
  Phone,
7753
7880
  X as X4
7754
7881
  } from "lucide-react-native";
7755
- import { useEffect as useEffect16, useState as useState24 } from "react";
7882
+ import { useEffect as useEffect17, useState as useState24 } from "react";
7756
7883
  import { Gesture, GestureDetector } from "react-native-gesture-handler";
7757
7884
  import Animated4, {
7758
7885
  Easing,
@@ -7770,7 +7897,7 @@ var DISMISS_V = 900;
7770
7897
  async function copyText(value) {
7771
7898
  try {
7772
7899
  const webNav = globalThis.navigator;
7773
- if (Platform5.OS === "web" && webNav?.clipboard?.writeText) {
7900
+ if (Platform7.OS === "web" && webNav?.clipboard?.writeText) {
7774
7901
  await webNav.clipboard.writeText(value);
7775
7902
  return;
7776
7903
  }
@@ -7791,7 +7918,7 @@ function LocationSheet({
7791
7918
  const opacity = useSharedValue2(0);
7792
7919
  const hoursProgress = useSharedValue2(0);
7793
7920
  const hoursHeight = useSharedValue2(0);
7794
- useEffect16(() => {
7921
+ useEffect17(() => {
7795
7922
  setHoursOpen(false);
7796
7923
  hoursProgress.value = 0;
7797
7924
  translateY.value = 48;
@@ -8118,6 +8245,24 @@ var SYSTEM_ROUTES = {
8118
8245
 
8119
8246
  // src/components/header/Header.tsx
8120
8247
  import { Fragment as Fragment11, jsx as jsx41, jsxs as jsxs26 } from "react/jsx-runtime";
8248
+ var DEFAULT_HEADER_ACTIONS = [
8249
+ {
8250
+ id: "notifications",
8251
+ type: "action",
8252
+ slot: "right",
8253
+ icon: "bell",
8254
+ action: "navigate",
8255
+ href: SYSTEM_ROUTES.notifications
8256
+ },
8257
+ {
8258
+ id: "profile",
8259
+ type: "action",
8260
+ slot: "right",
8261
+ icon: "person",
8262
+ action: "navigate",
8263
+ href: SYSTEM_ROUTES.profile
8264
+ }
8265
+ ];
8121
8266
  function resolveLogoUri(apiUrl, el) {
8122
8267
  if (el.sourceType === "media") {
8123
8268
  return publicMediaUrl(apiUrl, el.mediaId);
@@ -8175,8 +8320,11 @@ function Header({
8175
8320
  () => options.title ?? route.name,
8176
8321
  [options.title, route.name]
8177
8322
  );
8178
- const elements = headerConfig?.elements ?? [];
8179
- const hasCustom = elements.length > 0;
8323
+ const hasExplicitConfig = headerConfig != null;
8324
+ const elements = useMemo20(() => {
8325
+ const raw = headerConfig?.elements ?? [];
8326
+ return raw.filter((el) => el.type !== "title");
8327
+ }, [headerConfig]);
8180
8328
  const hasCustomActions = elements.some((el) => el.type === "action");
8181
8329
  const bySlot = useMemo20(() => {
8182
8330
  const left = [];
@@ -8202,8 +8350,7 @@ function Header({
8202
8350
  return /* @__PURE__ */ jsx41(HeaderLogo, { element: el }, el.id);
8203
8351
  }
8204
8352
  if (el.type === "title") {
8205
- const text = el.useScreenTitle !== false ? screenTitle : (el.text || "").trim() || screenTitle;
8206
- return /* @__PURE__ */ jsx41(Title, { text, level: "1" }, el.id);
8353
+ return null;
8207
8354
  }
8208
8355
  const onPress = async () => {
8209
8356
  await hapticSelection();
@@ -8282,7 +8429,7 @@ function Header({
8282
8429
  topInset
8283
8430
  ],
8284
8431
  pointerEvents: "box-none",
8285
- children: /* @__PURE__ */ jsx41(View29, { style: styles29.header, pointerEvents: "box-none", children: hasCustom ? /* @__PURE__ */ jsxs26(Fragment11, { children: [
8432
+ children: /* @__PURE__ */ jsx41(View29, { style: styles29.header, pointerEvents: "box-none", children: hasExplicitConfig ? /* @__PURE__ */ jsxs26(Fragment11, { children: [
8286
8433
  /* @__PURE__ */ jsxs26(View29, { style: styles29.slotLeft, pointerEvents: "box-none", children: [
8287
8434
  showBack ? /* @__PURE__ */ jsx41(
8288
8435
  HeaderActionButton,
@@ -8413,8 +8560,25 @@ var styles29 = StyleSheet30.create({
8413
8560
  }
8414
8561
  });
8415
8562
 
8563
+ // src/contexts/HeaderConfigContext.tsx
8564
+ import {
8565
+ createContext as createContext8,
8566
+ useContext as useContext9
8567
+ } from "react";
8568
+ import { jsx as jsx42 } from "react/jsx-runtime";
8569
+ var HeaderConfigContext = createContext8(null);
8570
+ function HeaderConfigProvider({
8571
+ value,
8572
+ children
8573
+ }) {
8574
+ return /* @__PURE__ */ jsx42(HeaderConfigContext.Provider, { value, children });
8575
+ }
8576
+ function useHeaderConfig() {
8577
+ return useContext9(HeaderConfigContext);
8578
+ }
8579
+
8416
8580
  // src/components/locations-map/LocationsMapShell.tsx
8417
- import { Fragment as Fragment12, jsx as jsx42, jsxs as jsxs27 } from "react/jsx-runtime";
8581
+ import { Fragment as Fragment12, jsx as jsx43, jsxs as jsxs27 } from "react/jsx-runtime";
8418
8582
  var CATEGORIES = Object.keys(LOCATION_CATEGORY_META);
8419
8583
  var NOOP_NAV = { canGoBack: () => false, goBack: () => void 0 };
8420
8584
  var PANEL_OPEN = { duration: 300, easing: Easing2.out(Easing2.cubic) };
@@ -8427,7 +8591,7 @@ function RoundCtrl({
8427
8591
  backgroundColor,
8428
8592
  borderColor
8429
8593
  }) {
8430
- return /* @__PURE__ */ jsx42(
8594
+ return /* @__PURE__ */ jsx43(
8431
8595
  Pressable14,
8432
8596
  {
8433
8597
  onPress,
@@ -8453,9 +8617,9 @@ function FilterPanel({
8453
8617
  const t = resolveTheme(theme);
8454
8618
  const progress = useSharedValue3(0);
8455
8619
  const widthSv = useSharedValue3(0);
8456
- const closing = useRef7(false);
8457
- const closeTimer = useRef7(null);
8458
- useEffect17(() => {
8620
+ const closing = useRef8(false);
8621
+ const closeTimer = useRef8(null);
8622
+ useEffect18(() => {
8459
8623
  return () => {
8460
8624
  if (closeTimer.current) clearTimeout(closeTimer.current);
8461
8625
  };
@@ -8476,7 +8640,7 @@ function FilterPanel({
8476
8640
  ]
8477
8641
  };
8478
8642
  }, []);
8479
- return /* @__PURE__ */ jsx42(
8643
+ return /* @__PURE__ */ jsx43(
8480
8644
  Animated5.View,
8481
8645
  {
8482
8646
  onLayout: (event) => {
@@ -8497,8 +8661,8 @@ function FilterPanel({
8497
8661
  ],
8498
8662
  children: [
8499
8663
  /* @__PURE__ */ jsxs27(View30, { style: styles30.filterPanelHead, children: [
8500
- /* @__PURE__ */ jsx42(Text25, { style: [styles30.filterPanelTitle, { color: theme.colors.onSurface }], children: title }),
8501
- /* @__PURE__ */ jsx42(
8664
+ /* @__PURE__ */ jsx43(Text25, { style: [styles30.filterPanelTitle, { color: theme.colors.onSurface }], children: title }),
8665
+ /* @__PURE__ */ jsx43(
8502
8666
  Pressable14,
8503
8667
  {
8504
8668
  onPress: close,
@@ -8512,7 +8676,7 @@ function FilterPanel({
8512
8676
  borderRadius: t.radii.pill
8513
8677
  }
8514
8678
  ],
8515
- children: /* @__PURE__ */ jsx42(X5, { size: 16, color: theme.colors.onSurface })
8679
+ children: /* @__PURE__ */ jsx43(X5, { size: 16, color: theme.colors.onSurface })
8516
8680
  }
8517
8681
  )
8518
8682
  ] }),
@@ -8527,16 +8691,26 @@ function LocationsMapShell({
8527
8691
  fullScreen = false,
8528
8692
  showFilters = true,
8529
8693
  overlayHeader = false,
8530
- overlayTitle = "Carte",
8694
+ headerConfig: headerConfigProp,
8531
8695
  MapCanvas
8532
8696
  }) {
8533
8697
  const { theme } = useTheme();
8534
8698
  const t = resolveTheme(theme);
8535
8699
  const themedStyles = createThemedStyles15(theme);
8700
+ const layoutHeaderConfig = useHeaderConfig();
8536
8701
  const { locations, isLoading, error, center } = useLocations();
8537
8702
  const [filters, setFilters] = useState25(/* @__PURE__ */ new Set());
8538
8703
  const [selected, setSelected] = useState25(null);
8539
8704
  const [filtersOpen, setFiltersOpen] = useState25(false);
8705
+ const overlayHeaderConfig = useMemo21(() => {
8706
+ const raw = headerConfigProp ?? layoutHeaderConfig;
8707
+ if (raw?.elements?.length) {
8708
+ return {
8709
+ elements: raw.elements.filter((el) => el.type !== "title")
8710
+ };
8711
+ }
8712
+ return { elements: DEFAULT_HEADER_ACTIONS.map((a) => ({ ...a })) };
8713
+ }, [headerConfigProp, layoutHeaderConfig]);
8540
8714
  const toggle = (category) => {
8541
8715
  setFilters((prev) => {
8542
8716
  const next = new Set(prev);
@@ -8558,7 +8732,7 @@ function LocationsMapShell({
8558
8732
  };
8559
8733
  const roundRadius = t.radii.pill;
8560
8734
  return /* @__PURE__ */ jsxs27(View30, { style: [styles30.wrap, fullScreen && styles30.fullScreen], children: [
8561
- /* @__PURE__ */ jsx42(
8735
+ /* @__PURE__ */ jsx43(
8562
8736
  View30,
8563
8737
  {
8564
8738
  style: [
@@ -8567,10 +8741,10 @@ function LocationsMapShell({
8567
8741
  fullScreen ? styles30.mapShellFull : styles30.mapShellFixed
8568
8742
  ],
8569
8743
  children: isLoading ? /* @__PURE__ */ jsxs27(View30, { style: styles30.center, children: [
8570
- /* @__PURE__ */ jsx42(ActivityIndicator11, { color: theme.colors.primary }),
8571
- /* @__PURE__ */ jsx42(Text25, { style: themedStyles.muted, children: "Chargement de la carte\u2026" })
8744
+ /* @__PURE__ */ jsx43(ActivityIndicator11, { color: theme.colors.primary }),
8745
+ /* @__PURE__ */ jsx43(Text25, { style: themedStyles.muted, children: "Chargement de la carte\u2026" })
8572
8746
  ] }) : /* @__PURE__ */ jsxs27(Fragment12, { children: [
8573
- /* @__PURE__ */ jsx42(
8747
+ /* @__PURE__ */ jsx43(
8574
8748
  MapCanvas,
8575
8749
  {
8576
8750
  locations: visible,
@@ -8579,19 +8753,20 @@ function LocationsMapShell({
8579
8753
  onSelect: selectLocation
8580
8754
  }
8581
8755
  ),
8582
- error ? /* @__PURE__ */ jsx42(View30, { style: [styles30.center, styles30.mapOverlayMessage], pointerEvents: "none", children: /* @__PURE__ */ jsx42(Text25, { style: { color: theme.colors.error, textAlign: "center" }, children: error }) }) : null,
8583
- !error && visible.length === 0 ? /* @__PURE__ */ jsx42(View30, { style: [styles30.center, styles30.mapOverlayMessage], pointerEvents: "none", children: /* @__PURE__ */ jsx42(Text25, { style: themedStyles.muted, children: "Aucun point \xE0 afficher." }) }) : null
8756
+ error ? /* @__PURE__ */ jsx43(View30, { style: [styles30.center, styles30.mapOverlayMessage], pointerEvents: "none", children: /* @__PURE__ */ jsx43(Text25, { style: { color: theme.colors.error, textAlign: "center" }, children: error }) }) : null,
8757
+ !error && visible.length === 0 ? /* @__PURE__ */ jsx43(View30, { style: [styles30.center, styles30.mapOverlayMessage], pointerEvents: "none", children: /* @__PURE__ */ jsx43(Text25, { style: themedStyles.muted, children: "Aucun point \xE0 afficher." }) }) : null
8584
8758
  ] })
8585
8759
  }
8586
8760
  ),
8587
8761
  /* @__PURE__ */ jsxs27(View30, { style: styles30.topOverlay, pointerEvents: "box-none", children: [
8588
- overlayHeader ? /* @__PURE__ */ jsx42(
8762
+ overlayHeader ? /* @__PURE__ */ jsx43(
8589
8763
  Header,
8590
8764
  {
8591
8765
  overlay: true,
8592
- options: { title: overlayTitle, headerBackVisible: false },
8593
- route: { name: overlayTitle },
8594
- navigation: NOOP_NAV
8766
+ options: { headerBackVisible: false },
8767
+ route: { name: "map" },
8768
+ navigation: NOOP_NAV,
8769
+ headerConfig: overlayHeaderConfig
8595
8770
  }
8596
8771
  ) : null,
8597
8772
  showFilters ? /* @__PURE__ */ jsxs27(View30, { style: styles30.filtersOverlay, pointerEvents: "box-none", children: [
@@ -8603,7 +8778,7 @@ function LocationsMapShell({
8603
8778
  style: styles30.chipScrollView,
8604
8779
  contentContainerStyle: styles30.chipScroll,
8605
8780
  children: [
8606
- /* @__PURE__ */ jsx42(
8781
+ /* @__PURE__ */ jsx43(
8607
8782
  RoundCtrl,
8608
8783
  {
8609
8784
  label: "Ouvrir les filtres",
@@ -8611,13 +8786,13 @@ function LocationsMapShell({
8611
8786
  radius: roundRadius,
8612
8787
  backgroundColor: theme.colors.surface,
8613
8788
  borderColor: theme.colors.border,
8614
- children: /* @__PURE__ */ jsx42(ChevronRight, { size: 16, color: theme.colors.onSurface })
8789
+ children: /* @__PURE__ */ jsx43(ChevronRight, { size: 16, color: theme.colors.onSurface })
8615
8790
  }
8616
8791
  ),
8617
8792
  selectedCategories.map((category) => {
8618
8793
  const meta = LOCATION_CATEGORY_META[category];
8619
8794
  const fg = getContrastColor(meta.color);
8620
- return /* @__PURE__ */ jsx42(
8795
+ return /* @__PURE__ */ jsx43(
8621
8796
  Pressable14,
8622
8797
  {
8623
8798
  onPress: () => toggle(category),
@@ -8628,12 +8803,12 @@ function LocationsMapShell({
8628
8803
  borderRadius: t.radii.pill
8629
8804
  }
8630
8805
  ],
8631
- children: /* @__PURE__ */ jsx42(Text25, { style: [styles30.chipText, { color: fg }], children: meta.label })
8806
+ children: /* @__PURE__ */ jsx43(Text25, { style: [styles30.chipText, { color: fg }], children: meta.label })
8632
8807
  },
8633
8808
  category
8634
8809
  );
8635
8810
  }),
8636
- /* @__PURE__ */ jsx42(
8811
+ /* @__PURE__ */ jsx43(
8637
8812
  RoundCtrl,
8638
8813
  {
8639
8814
  label: "Effacer les filtres",
@@ -8641,7 +8816,7 @@ function LocationsMapShell({
8641
8816
  radius: roundRadius,
8642
8817
  backgroundColor: theme.colors.surface,
8643
8818
  borderColor: theme.colors.border,
8644
- children: /* @__PURE__ */ jsx42(X5, { size: 16, color: theme.colors.onSurface })
8819
+ children: /* @__PURE__ */ jsx43(X5, { size: 16, color: theme.colors.onSurface })
8645
8820
  }
8646
8821
  )
8647
8822
  ]
@@ -8656,12 +8831,12 @@ function LocationsMapShell({
8656
8831
  { borderRadius: t.radii.pill }
8657
8832
  ],
8658
8833
  children: [
8659
- /* @__PURE__ */ jsx42(Text25, { style: [styles30.filterTriggerText, { color: theme.colors.onSurface }], children: filterLabel }),
8660
- /* @__PURE__ */ jsx42(ChevronRight, { size: 16, color: theme.colors.onSurface })
8834
+ /* @__PURE__ */ jsx43(Text25, { style: [styles30.filterTriggerText, { color: theme.colors.onSurface }], children: filterLabel }),
8835
+ /* @__PURE__ */ jsx43(ChevronRight, { size: 16, color: theme.colors.onSurface })
8661
8836
  ]
8662
8837
  }
8663
8838
  ),
8664
- filtersOpen ? /* @__PURE__ */ jsx42(FilterPanel, { title: filterLabel, onClose: () => setFiltersOpen(false), children: /* @__PURE__ */ jsx42(View30, { style: styles30.filterList, children: CATEGORIES.map((category) => {
8839
+ filtersOpen ? /* @__PURE__ */ jsx43(FilterPanel, { title: filterLabel, onClose: () => setFiltersOpen(false), children: /* @__PURE__ */ jsx43(View30, { style: styles30.filterList, children: CATEGORIES.map((category) => {
8665
8840
  const meta = LOCATION_CATEGORY_META[category];
8666
8841
  const active2 = filters.has(category);
8667
8842
  const bg = active2 ? meta.color : theme.colors.background;
@@ -8678,8 +8853,8 @@ function LocationsMapShell({
8678
8853
  }
8679
8854
  ],
8680
8855
  children: [
8681
- /* @__PURE__ */ jsx42(MapPin4, { size: 16, color: fg }),
8682
- /* @__PURE__ */ jsx42(Text25, { style: [styles30.filterRowText, { color: fg }], children: meta.label })
8856
+ /* @__PURE__ */ jsx43(MapPin4, { size: 16, color: fg }),
8857
+ /* @__PURE__ */ jsx43(Text25, { style: [styles30.filterRowText, { color: fg }], children: meta.label })
8683
8858
  ]
8684
8859
  },
8685
8860
  category
@@ -8687,7 +8862,7 @@ function LocationsMapShell({
8687
8862
  }) }) }) : null
8688
8863
  ] }) : null
8689
8864
  ] }),
8690
- selected ? /* @__PURE__ */ jsx42(View30, { style: styles30.sheetOverlay, pointerEvents: "box-none", children: /* @__PURE__ */ jsx42(LocationSheet, { location: selected, onClose: () => setSelected(null) }) }) : null
8865
+ selected ? /* @__PURE__ */ jsx43(View30, { style: styles30.sheetOverlay, pointerEvents: "box-none", children: /* @__PURE__ */ jsx43(LocationSheet, { location: selected, onClose: () => setSelected(null) }) }) : null
8691
8866
  ] });
8692
8867
  }
8693
8868
  var createThemedStyles15 = (theme) => StyleSheet31.create({
@@ -8850,15 +9025,15 @@ var styles30 = StyleSheet31.create({
8850
9025
  });
8851
9026
 
8852
9027
  // src/components/locations-map/LocationsMap.tsx
8853
- import { jsx as jsx43 } from "react/jsx-runtime";
9028
+ import { jsx as jsx44 } from "react/jsx-runtime";
8854
9029
  function LocationsMap(props) {
8855
- return /* @__PURE__ */ jsx43(LocationsMapShell, { ...props, MapCanvas: LocationsMapView });
9030
+ return /* @__PURE__ */ jsx44(LocationsMapShell, { ...props, MapCanvas: LocationsMapView });
8856
9031
  }
8857
9032
 
8858
9033
  // src/components/page/Page.tsx
8859
9034
  import { useCallback as useCallback13, useState as useState26 } from "react";
8860
9035
  import { RefreshControl, ScrollView as ScrollView11, StyleSheet as StyleSheet32, View as View31 } from "react-native";
8861
- import { jsx as jsx44 } from "react/jsx-runtime";
9036
+ import { jsx as jsx45 } from "react/jsx-runtime";
8862
9037
  function Page({
8863
9038
  children,
8864
9039
  scroll = true,
@@ -8880,14 +9055,14 @@ function Page({
8880
9055
  setRefreshing(false);
8881
9056
  }
8882
9057
  }, [appData]);
8883
- return /* @__PURE__ */ jsx44(View31, { style: [styles31.root, themedStyles.root, topInset], children: scroll ? /* @__PURE__ */ jsx44(
9058
+ return /* @__PURE__ */ jsx45(View31, { style: [styles31.root, themedStyles.root, topInset], children: scroll ? /* @__PURE__ */ jsx45(
8884
9059
  ScrollView11,
8885
9060
  {
8886
9061
  style: styles31.scroll,
8887
9062
  contentContainerStyle: styles31.scrollContent,
8888
9063
  keyboardShouldPersistTaps: "handled",
8889
9064
  showsVerticalScrollIndicator: false,
8890
- refreshControl: enableRefresh && appData ? /* @__PURE__ */ jsx44(
9065
+ refreshControl: enableRefresh && appData ? /* @__PURE__ */ jsx45(
8891
9066
  RefreshControl,
8892
9067
  {
8893
9068
  refreshing,
@@ -8900,7 +9075,7 @@ function Page({
8900
9075
  ) : void 0,
8901
9076
  children
8902
9077
  }
8903
- ) : /* @__PURE__ */ jsx44(View31, { style: styles31.fill, children }) });
9078
+ ) : /* @__PURE__ */ jsx45(View31, { style: styles31.fill, children }) });
8904
9079
  }
8905
9080
  var createThemedStyles16 = (theme) => StyleSheet32.create({
8906
9081
  root: {
@@ -8927,7 +9102,7 @@ var styles31 = StyleSheet32.create({
8927
9102
 
8928
9103
  // src/components/parapraph/Paragraph.tsx
8929
9104
  import { StyleSheet as StyleSheet33, Text as Text26, View as View32 } from "react-native";
8930
- import { jsx as jsx45 } from "react/jsx-runtime";
9105
+ import { jsx as jsx46 } from "react/jsx-runtime";
8931
9106
  function justifyForVertical2(align) {
8932
9107
  if (align === "middle") return "center";
8933
9108
  if (align === "bottom") return "flex-end";
@@ -8946,11 +9121,11 @@ function Paragraph({
8946
9121
  const { theme } = useTheme();
8947
9122
  const themedStyles = createThemedStyles17(theme);
8948
9123
  const textAlign = textAlignFor2(alignHorizontal);
8949
- const content = /* @__PURE__ */ jsx45(Text26, { style: [styles32.text, themedStyles.text, { textAlign }, style], children: text });
9124
+ const content = /* @__PURE__ */ jsx46(Text26, { style: [styles32.text, themedStyles.text, { textAlign }, style], children: text });
8950
9125
  if (!alignVertical || alignVertical === "top") {
8951
9126
  return content;
8952
9127
  }
8953
- return /* @__PURE__ */ jsx45(View32, { style: [styles32.alignWrap, { justifyContent: justifyForVertical2(alignVertical) }], children: content });
9128
+ return /* @__PURE__ */ jsx46(View32, { style: [styles32.alignWrap, { justifyContent: justifyForVertical2(alignVertical) }], children: content });
8954
9129
  }
8955
9130
  var createThemedStyles17 = (theme) => StyleSheet33.create({
8956
9131
  text: {
@@ -9018,7 +9193,7 @@ function useRecyclingInfo(options = {}) {
9018
9193
 
9019
9194
  // src/components/recycling-info/RecyclingInfo.tsx
9020
9195
  import { useState as useState27 } from "react";
9021
- import { Fragment as Fragment13, jsx as jsx46, jsxs as jsxs28 } from "react/jsx-runtime";
9196
+ import { Fragment as Fragment13, jsx as jsx47, jsxs as jsxs28 } from "react/jsx-runtime";
9022
9197
  function RecyclingInfo({}) {
9023
9198
  const { recyclingInfos } = useRecyclingInfo();
9024
9199
  const { theme } = useTheme();
@@ -9032,7 +9207,7 @@ function RecyclingInfo({}) {
9032
9207
  };
9033
9208
  };
9034
9209
  return /* @__PURE__ */ jsxs28(Fragment13, { children: [
9035
- /* @__PURE__ */ jsx46(View33, { style: styles33.container, children: recyclingInfos.map((recyclingInfo) => {
9210
+ /* @__PURE__ */ jsx47(View33, { style: styles33.container, children: recyclingInfos.map((recyclingInfo) => {
9036
9211
  const color = recyclingInfo.color ?? theme.colors.primary;
9037
9212
  const illustration = resolveApiAssetUrl(apiUrl, recyclingInfo.imageUrl) || (recyclingInfo.icon?.trim().startsWith("http") ? recyclingInfo.icon.trim() : null);
9038
9213
  return /* @__PURE__ */ jsxs28(
@@ -9051,7 +9226,7 @@ function RecyclingInfo({}) {
9051
9226
  { backgroundColor: color }
9052
9227
  ],
9053
9228
  children: [
9054
- illustration ? /* @__PURE__ */ jsx46(
9229
+ illustration ? /* @__PURE__ */ jsx47(
9055
9230
  Image12,
9056
9231
  {
9057
9232
  source: illustration,
@@ -9059,7 +9234,7 @@ function RecyclingInfo({}) {
9059
9234
  contentFit: "contain"
9060
9235
  }
9061
9236
  ) : null,
9062
- /* @__PURE__ */ jsx46(
9237
+ /* @__PURE__ */ jsx47(
9063
9238
  Title,
9064
9239
  {
9065
9240
  text: recyclingInfo.name,
@@ -9071,8 +9246,8 @@ function RecyclingInfo({}) {
9071
9246
  }
9072
9247
  ),
9073
9248
  /* @__PURE__ */ jsxs28(View33, { style: styles33.body, children: [
9074
- recyclingInfo.description && /* @__PURE__ */ jsx46(Paragraph, { text: recyclingInfo.description, style: themedStyles.cardText }),
9075
- /* @__PURE__ */ jsx46(
9249
+ recyclingInfo.description && /* @__PURE__ */ jsx47(Paragraph, { text: recyclingInfo.description, style: themedStyles.cardText }),
9250
+ /* @__PURE__ */ jsx47(
9076
9251
  View33,
9077
9252
  {
9078
9253
  style: [
@@ -9098,7 +9273,7 @@ function RecyclingInfo({}) {
9098
9273
  }
9099
9274
  ],
9100
9275
  children: [
9101
- detailImageUrl ? /* @__PURE__ */ jsx46(
9276
+ detailImageUrl ? /* @__PURE__ */ jsx47(
9102
9277
  Image12,
9103
9278
  {
9104
9279
  source: detailImageUrl,
@@ -9107,7 +9282,7 @@ function RecyclingInfo({}) {
9107
9282
  }
9108
9283
  ) : null,
9109
9284
  /* @__PURE__ */ jsxs28(View33, { style: styles33.detailText, children: [
9110
- /* @__PURE__ */ jsx46(
9285
+ /* @__PURE__ */ jsx47(
9111
9286
  Text27,
9112
9287
  {
9113
9288
  style: [
@@ -9117,7 +9292,7 @@ function RecyclingInfo({}) {
9117
9292
  children: detail.material
9118
9293
  }
9119
9294
  ),
9120
- /* @__PURE__ */ jsx46(
9295
+ /* @__PURE__ */ jsx47(
9121
9296
  Text27,
9122
9297
  {
9123
9298
  style: { color: getContrastColor(recyclingInfo.colorLight ?? "#FFFFFF") },
@@ -9132,7 +9307,7 @@ function RecyclingInfo({}) {
9132
9307
  })
9133
9308
  }
9134
9309
  ),
9135
- /* @__PURE__ */ jsx46(
9310
+ /* @__PURE__ */ jsx47(
9136
9311
  Button,
9137
9312
  {
9138
9313
  style: { backgroundColor: color },
@@ -9147,7 +9322,7 @@ function RecyclingInfo({}) {
9147
9322
  recyclingInfo.id
9148
9323
  );
9149
9324
  }) }),
9150
- /* @__PURE__ */ jsx46(
9325
+ /* @__PURE__ */ jsx47(
9151
9326
  ConsignesModal,
9152
9327
  {
9153
9328
  visible: !!selectedWasteTypeId,
@@ -9207,10 +9382,10 @@ var styles33 = StyleSheet34.create({
9207
9382
  });
9208
9383
 
9209
9384
  // src/components/search/Search.tsx
9210
- import { useEffect as useEffect18, useMemo as useMemo23, useState as useState28 } from "react";
9385
+ import { useEffect as useEffect19, useMemo as useMemo23, useState as useState28 } from "react";
9211
9386
  import { Pressable as Pressable15, StyleSheet as StyleSheet35, Text as Text28, TextInput as TextInput3, View as View34 } from "react-native";
9212
9387
  import { ChevronRight as ChevronRight2, Search as SearchIcon } from "lucide-react-native";
9213
- import { Fragment as Fragment14, jsx as jsx47, jsxs as jsxs29 } from "react/jsx-runtime";
9388
+ import { Fragment as Fragment14, jsx as jsx48, jsxs as jsxs29 } from "react/jsx-runtime";
9214
9389
  function matchesQuery(haystack, query) {
9215
9390
  if (!haystack) return false;
9216
9391
  return haystack.toLowerCase().includes(query);
@@ -9227,14 +9402,14 @@ function ResultRow({
9227
9402
  const t = resolveTheme(theme);
9228
9403
  const content = /* @__PURE__ */ jsxs29(Fragment14, { children: [
9229
9404
  /* @__PURE__ */ jsxs29(View34, { style: styles34.rowInner, children: [
9230
- /* @__PURE__ */ jsx47(View34, { style: [styles34.dot, { backgroundColor: accent }] }),
9405
+ /* @__PURE__ */ jsx48(View34, { style: [styles34.dot, { backgroundColor: accent }] }),
9231
9406
  /* @__PURE__ */ jsxs29(View34, { style: styles34.rowText, children: [
9232
- /* @__PURE__ */ jsx47(Text28, { style: [styles34.rowTitle, { color: theme.colors.onSurface }], children: title }),
9233
- meta ? /* @__PURE__ */ jsx47(Text28, { style: [styles34.rowMeta, { color: t.colors.muted }], numberOfLines: 2, children: meta }) : null
9407
+ /* @__PURE__ */ jsx48(Text28, { style: [styles34.rowTitle, { color: theme.colors.onSurface }], children: title }),
9408
+ meta ? /* @__PURE__ */ jsx48(Text28, { style: [styles34.rowMeta, { color: t.colors.muted }], numberOfLines: 2, children: meta }) : null
9234
9409
  ] }),
9235
- onPress ? /* @__PURE__ */ jsx47(ChevronRight2, { size: 18, color: t.colors.muted }) : null
9410
+ onPress ? /* @__PURE__ */ jsx48(ChevronRight2, { size: 18, color: t.colors.muted }) : null
9236
9411
  ] }),
9237
- showDivider ? /* @__PURE__ */ jsx47(
9412
+ showDivider ? /* @__PURE__ */ jsx48(
9238
9413
  View34,
9239
9414
  {
9240
9415
  style: [
@@ -9248,9 +9423,9 @@ function ResultRow({
9248
9423
  ) : null
9249
9424
  ] });
9250
9425
  if (onPress) {
9251
- return /* @__PURE__ */ jsx47(Pressable15, { onPress, style: ({ pressed }) => pressed && styles34.pressed, children: content });
9426
+ return /* @__PURE__ */ jsx48(Pressable15, { onPress, style: ({ pressed }) => pressed && styles34.pressed, children: content });
9252
9427
  }
9253
- return /* @__PURE__ */ jsx47(View34, { children: content });
9428
+ return /* @__PURE__ */ jsx48(View34, { children: content });
9254
9429
  }
9255
9430
  function ResultSection({
9256
9431
  label,
@@ -9259,8 +9434,8 @@ function ResultSection({
9259
9434
  const { theme } = useTheme();
9260
9435
  const t = resolveTheme(theme);
9261
9436
  return /* @__PURE__ */ jsxs29(View34, { style: styles34.section, children: [
9262
- label ? /* @__PURE__ */ jsx47(Text28, { style: [styles34.sectionLabel, { color: t.colors.muted }], children: label }) : null,
9263
- /* @__PURE__ */ jsx47(View34, { style: groupedSectionStyle(theme), children })
9437
+ label ? /* @__PURE__ */ jsx48(Text28, { style: [styles34.sectionLabel, { color: t.colors.muted }], children: label }) : null,
9438
+ /* @__PURE__ */ jsx48(View34, { style: groupedSectionStyle(theme), children })
9264
9439
  ] });
9265
9440
  }
9266
9441
  function Search2({
@@ -9277,7 +9452,7 @@ function Search2({
9277
9452
  const [query, setQuery] = useState28("");
9278
9453
  const [wasteTypeId, setWasteTypeId] = useState28(null);
9279
9454
  const [article, setArticle] = useState28(null);
9280
- useEffect18(() => {
9455
+ useEffect19(() => {
9281
9456
  const handle = setTimeout(() => setQuery(draft.trim().toLowerCase()), 250);
9282
9457
  return () => clearTimeout(handle);
9283
9458
  }, [draft]);
@@ -9316,8 +9491,8 @@ function Search2({
9316
9491
  }
9317
9492
  ],
9318
9493
  children: [
9319
- /* @__PURE__ */ jsx47(SearchIcon, { size: 18, color: t.colors.muted }),
9320
- /* @__PURE__ */ jsx47(
9494
+ /* @__PURE__ */ jsx48(SearchIcon, { size: 18, color: t.colors.muted }),
9495
+ /* @__PURE__ */ jsx48(
9321
9496
  TextInput3,
9322
9497
  {
9323
9498
  value: draft,
@@ -9332,8 +9507,8 @@ function Search2({
9332
9507
  ]
9333
9508
  }
9334
9509
  ),
9335
- empty ? /* @__PURE__ */ jsx47(Text28, { style: [styles34.empty, { color: t.colors.muted }], children: "Aucun r\xE9sultat" }) : null,
9336
- wasteResults.length > 0 ? /* @__PURE__ */ jsx47(ResultSection, { label: searchType === "all" ? "Consignes" : void 0, children: wasteResults.map((info, index) => /* @__PURE__ */ jsx47(
9510
+ empty ? /* @__PURE__ */ jsx48(Text28, { style: [styles34.empty, { color: t.colors.muted }], children: "Aucun r\xE9sultat" }) : null,
9511
+ wasteResults.length > 0 ? /* @__PURE__ */ jsx48(ResultSection, { label: searchType === "all" ? "Consignes" : void 0, children: wasteResults.map((info, index) => /* @__PURE__ */ jsx48(
9337
9512
  ResultRow,
9338
9513
  {
9339
9514
  title: info.name,
@@ -9345,7 +9520,7 @@ function Search2({
9345
9520
  },
9346
9521
  info.id
9347
9522
  )) }) : null,
9348
- locationResults.length > 0 ? /* @__PURE__ */ jsx47(ResultSection, { label: searchType === "all" ? "Points de collecte" : void 0, children: locationResults.map((loc, index) => /* @__PURE__ */ jsx47(
9523
+ locationResults.length > 0 ? /* @__PURE__ */ jsx48(ResultSection, { label: searchType === "all" ? "Points de collecte" : void 0, children: locationResults.map((loc, index) => /* @__PURE__ */ jsx48(
9349
9524
  ResultRow,
9350
9525
  {
9351
9526
  title: loc.name,
@@ -9359,7 +9534,7 @@ function Search2({
9359
9534
  },
9360
9535
  loc.id
9361
9536
  )) }) : null,
9362
- articleResults.length > 0 ? /* @__PURE__ */ jsx47(ResultSection, { label: searchType === "all" ? "Articles" : void 0, children: articleResults.map((post, index) => /* @__PURE__ */ jsx47(
9537
+ articleResults.length > 0 ? /* @__PURE__ */ jsx48(ResultSection, { label: searchType === "all" ? "Articles" : void 0, children: articleResults.map((post, index) => /* @__PURE__ */ jsx48(
9363
9538
  ResultRow,
9364
9539
  {
9365
9540
  title: post.title,
@@ -9370,7 +9545,7 @@ function Search2({
9370
9545
  },
9371
9546
  post.id
9372
9547
  )) }) : null,
9373
- /* @__PURE__ */ jsx47(
9548
+ /* @__PURE__ */ jsx48(
9374
9549
  ConsignesModal,
9375
9550
  {
9376
9551
  visible: !!wasteTypeId,
@@ -9378,7 +9553,7 @@ function Search2({
9378
9553
  onClose: () => setWasteTypeId(null)
9379
9554
  }
9380
9555
  ),
9381
- /* @__PURE__ */ jsx47(
9556
+ /* @__PURE__ */ jsx48(
9382
9557
  ArticleReaderModal,
9383
9558
  {
9384
9559
  visible: !!article,
@@ -9461,7 +9636,7 @@ var styles34 = StyleSheet35.create({
9461
9636
  import {
9462
9637
  useCallback as useCallback14,
9463
9638
  useLayoutEffect,
9464
- useRef as useRef8,
9639
+ useRef as useRef9,
9465
9640
  useState as useState29
9466
9641
  } from "react";
9467
9642
  import { Pressable as Pressable16, StyleSheet as StyleSheet36, View as View35 } from "react-native";
@@ -9476,7 +9651,7 @@ import Animated6, {
9476
9651
  withSpring
9477
9652
  } from "react-native-reanimated";
9478
9653
  import { useSafeAreaInsets as useSafeAreaInsets2 } from "react-native-safe-area-context";
9479
- import { jsx as jsx48, jsxs as jsxs30 } from "react/jsx-runtime";
9654
+ import { jsx as jsx49, jsxs as jsxs30 } from "react/jsx-runtime";
9480
9655
  var PILL_W = 64;
9481
9656
  var PILL_H = 44;
9482
9657
  var ICON_SIZE = 24;
@@ -9549,11 +9724,11 @@ function TabItem({
9549
9724
  accessibilityLabel: label,
9550
9725
  children: [
9551
9726
  /* @__PURE__ */ jsxs30(View35, { style: styles35.iconSlot, children: [
9552
- /* @__PURE__ */ jsx48(Animated6.View, { style: [styles35.iconLayer, idleIconStyle], children: icon(muted) }),
9553
- /* @__PURE__ */ jsx48(Animated6.View, { style: [styles35.iconLayer, activeIconStyle], children: icon(onPrimary) })
9727
+ /* @__PURE__ */ jsx49(Animated6.View, { style: [styles35.iconLayer, idleIconStyle], children: icon(muted) }),
9728
+ /* @__PURE__ */ jsx49(Animated6.View, { style: [styles35.iconLayer, activeIconStyle], children: icon(onPrimary) })
9554
9729
  ] }),
9555
9730
  /* @__PURE__ */ jsxs30(View35, { style: styles35.labelSlot, children: [
9556
- /* @__PURE__ */ jsx48(
9731
+ /* @__PURE__ */ jsx49(
9557
9732
  Animated6.Text,
9558
9733
  {
9559
9734
  style: [styles35.label, styles35.labelIdle, { color: muted }, idleLabelStyle],
@@ -9561,7 +9736,7 @@ function TabItem({
9561
9736
  children: label
9562
9737
  }
9563
9738
  ),
9564
- /* @__PURE__ */ jsx48(
9739
+ /* @__PURE__ */ jsx49(
9565
9740
  Animated6.Text,
9566
9741
  {
9567
9742
  style: [styles35.label, styles35.labelActive, { color: primary }, activeLabelStyle],
@@ -9594,9 +9769,9 @@ function BottomTab({
9594
9769
  const dragStartIndex = useSharedValue4(state.index);
9595
9770
  const pressed = useSharedValue4(0);
9596
9771
  const lastTick = useSharedValue4(state.index);
9597
- const dragging = useRef8(false);
9598
- const routesRef = useRef8(state.routes);
9599
- const indexRef = useRef8(state.index);
9772
+ const dragging = useRef9(false);
9773
+ const routesRef = useRef9(state.routes);
9774
+ const indexRef = useRef9(state.index);
9600
9775
  routesRef.current = state.routes;
9601
9776
  indexRef.current = state.index;
9602
9777
  countSV.value = count;
@@ -9671,7 +9846,7 @@ function BottomTab({
9671
9846
  left: pillX.value,
9672
9847
  transform: [{ scale: interpolate4(pressed.value, [0, 1], [1, 0.96]) }]
9673
9848
  }), []);
9674
- return /* @__PURE__ */ jsx48(GestureHandlerRootView, { style: styles35.gestureRoot, children: /* @__PURE__ */ jsx48(
9849
+ return /* @__PURE__ */ jsx49(GestureHandlerRootView, { style: styles35.gestureRoot, children: /* @__PURE__ */ jsx49(
9675
9850
  View35,
9676
9851
  {
9677
9852
  style: [
@@ -9679,14 +9854,14 @@ function BottomTab({
9679
9854
  themedStyles.bottomTab,
9680
9855
  { paddingBottom: Math.max(insets.bottom, 12) }
9681
9856
  ],
9682
- children: /* @__PURE__ */ jsx48(GestureDetector2, { gesture: pan, children: /* @__PURE__ */ jsxs30(
9857
+ children: /* @__PURE__ */ jsx49(GestureDetector2, { gesture: pan, children: /* @__PURE__ */ jsxs30(
9683
9858
  View35,
9684
9859
  {
9685
9860
  collapsable: false,
9686
9861
  style: styles35.row,
9687
9862
  onLayout: (e) => setRowWidth(e.nativeEvent.layout.width),
9688
9863
  children: [
9689
- /* @__PURE__ */ jsx48(
9864
+ /* @__PURE__ */ jsx49(
9690
9865
  Animated6.View,
9691
9866
  {
9692
9867
  pointerEvents: "none",
@@ -9703,7 +9878,7 @@ function BottomTab({
9703
9878
  state.routes.map((route, index) => {
9704
9879
  const { options } = descriptors[route.key];
9705
9880
  const label = typeof options.title === "string" ? options.title : route.name;
9706
- return /* @__PURE__ */ jsx48(
9881
+ return /* @__PURE__ */ jsx49(
9707
9882
  TabItem,
9708
9883
  {
9709
9884
  index,
@@ -9807,7 +9982,7 @@ import {
9807
9982
  } from "react-native";
9808
9983
 
9809
9984
  // src/hooks/useCityAnnouncements.ts
9810
- import { useCallback as useCallback15, useEffect as useEffect19, useState as useState30 } from "react";
9985
+ import { useCallback as useCallback15, useEffect as useEffect20, useState as useState30 } from "react";
9811
9986
  import AsyncStorage4 from "@react-native-async-storage/async-storage";
9812
9987
  var DISMISSED_KEY2 = "dismissed_announcements";
9813
9988
  var PAGE_SIZE2 = 20;
@@ -9869,7 +10044,7 @@ function useCityAnnouncements() {
9869
10044
  setLoading(false);
9870
10045
  }
9871
10046
  }, [apiUrl, client.public.announcements, hasPlaceSelected, selectedCityId]);
9872
- useEffect19(() => {
10047
+ useEffect20(() => {
9873
10048
  void reload();
9874
10049
  }, [reload]);
9875
10050
  const items = all.slice(0, visibleCount);
@@ -9904,7 +10079,7 @@ function useCityAnnouncements() {
9904
10079
  }
9905
10080
 
9906
10081
  // src/components/notifications/NotificationsScreen.tsx
9907
- import { jsx as jsx49, jsxs as jsxs31 } from "react/jsx-runtime";
10082
+ import { jsx as jsx50, jsxs as jsxs31 } from "react/jsx-runtime";
9908
10083
  function formatSentAt(value) {
9909
10084
  if (!value) return null;
9910
10085
  const d = new Date(value);
@@ -9950,7 +10125,7 @@ function NotificationsScreen() {
9950
10125
  accessibilityLabel: item.title,
9951
10126
  children: [
9952
10127
  /* @__PURE__ */ jsxs31(View36, { style: styles36.rowHeader, children: [
9953
- /* @__PURE__ */ jsx49(
10128
+ /* @__PURE__ */ jsx50(
9954
10129
  Text29,
9955
10130
  {
9956
10131
  style: [styles36.rowTitle, { color: theme.colors.onSurface }],
@@ -9958,7 +10133,7 @@ function NotificationsScreen() {
9958
10133
  children: item.title
9959
10134
  }
9960
10135
  ),
9961
- item.unread ? /* @__PURE__ */ jsx49(
10136
+ item.unread ? /* @__PURE__ */ jsx50(
9962
10137
  View36,
9963
10138
  {
9964
10139
  style: [
@@ -9968,8 +10143,8 @@ function NotificationsScreen() {
9968
10143
  }
9969
10144
  ) : null
9970
10145
  ] }),
9971
- dateLabel ? /* @__PURE__ */ jsx49(Text29, { style: [styles36.rowMeta, { color: t.colors.muted }], children: dateLabel }) : null,
9972
- /* @__PURE__ */ jsx49(
10146
+ dateLabel ? /* @__PURE__ */ jsx50(Text29, { style: [styles36.rowMeta, { color: t.colors.muted }], children: dateLabel }) : null,
10147
+ /* @__PURE__ */ jsx50(
9973
10148
  Text29,
9974
10149
  {
9975
10150
  style: [styles36.rowBody, { color: t.colors.muted }],
@@ -9985,20 +10160,20 @@ function NotificationsScreen() {
9985
10160
  );
9986
10161
  if (!hasPlaceSelected) {
9987
10162
  return /* @__PURE__ */ jsxs31(View36, { style: styles36.center, children: [
9988
- /* @__PURE__ */ jsx49(Text29, { style: [styles36.emptyTitle, { color: theme.colors.onSurface }], children: "Aucun secteur s\xE9lectionn\xE9" }),
9989
- /* @__PURE__ */ jsx49(Text29, { style: [styles36.emptyText, { color: t.colors.muted }], children: "Choisissez votre rue pour voir les notifications de votre ville." })
10163
+ /* @__PURE__ */ jsx50(Text29, { style: [styles36.emptyTitle, { color: theme.colors.onSurface }], children: "Aucun secteur s\xE9lectionn\xE9" }),
10164
+ /* @__PURE__ */ jsx50(Text29, { style: [styles36.emptyText, { color: t.colors.muted }], children: "Choisissez votre rue pour voir les notifications de votre ville." })
9990
10165
  ] });
9991
10166
  }
9992
10167
  if (loading && items.length === 0) {
9993
- return /* @__PURE__ */ jsx49(View36, { style: styles36.center, children: /* @__PURE__ */ jsx49(ActivityIndicator12, { color: theme.colors.primary }) });
10168
+ return /* @__PURE__ */ jsx50(View36, { style: styles36.center, children: /* @__PURE__ */ jsx50(ActivityIndicator12, { color: theme.colors.primary }) });
9994
10169
  }
9995
10170
  if (error && items.length === 0) {
9996
10171
  return /* @__PURE__ */ jsxs31(View36, { style: styles36.center, children: [
9997
- /* @__PURE__ */ jsx49(Text29, { style: [styles36.emptyTitle, { color: theme.colors.error }], children: error }),
9998
- /* @__PURE__ */ jsx49(Button, { variant: "secondary", onPress: () => void reload(), style: styles36.retry, children: "R\xE9essayer" })
10172
+ /* @__PURE__ */ jsx50(Text29, { style: [styles36.emptyTitle, { color: theme.colors.error }], children: error }),
10173
+ /* @__PURE__ */ jsx50(Button, { variant: "secondary", onPress: () => void reload(), style: styles36.retry, children: "R\xE9essayer" })
9999
10174
  ] });
10000
10175
  }
10001
- return /* @__PURE__ */ jsx49(View36, { style: styles36.screen, children: /* @__PURE__ */ jsx49(
10176
+ return /* @__PURE__ */ jsx50(View36, { style: styles36.screen, children: /* @__PURE__ */ jsx50(
10002
10177
  FlatList3,
10003
10178
  {
10004
10179
  data: items,
@@ -10010,10 +10185,10 @@ function NotificationsScreen() {
10010
10185
  },
10011
10186
  onEndReachedThreshold: 0.4,
10012
10187
  ListEmptyComponent: /* @__PURE__ */ jsxs31(View36, { style: styles36.centerInline, children: [
10013
- /* @__PURE__ */ jsx49(Text29, { style: [styles36.emptyTitle, { color: theme.colors.onSurface }], children: "Aucune notification" }),
10014
- /* @__PURE__ */ jsx49(Text29, { style: [styles36.emptyText, { color: t.colors.muted }], children: "Les annonces de votre ville appara\xEEtront ici." })
10188
+ /* @__PURE__ */ jsx50(Text29, { style: [styles36.emptyTitle, { color: theme.colors.onSurface }], children: "Aucune notification" }),
10189
+ /* @__PURE__ */ jsx50(Text29, { style: [styles36.emptyText, { color: t.colors.muted }], children: "Les annonces de votre ville appara\xEEtront ici." })
10015
10190
  ] }),
10016
- ListFooterComponent: hasMore ? /* @__PURE__ */ jsx49(
10191
+ ListFooterComponent: hasMore ? /* @__PURE__ */ jsx50(
10017
10192
  Button,
10018
10193
  {
10019
10194
  variant: "ghost",
@@ -10107,7 +10282,7 @@ var styles36 = StyleSheet37.create({
10107
10282
  // src/components/profile/ProfileScreen.tsx
10108
10283
  import { Alert, StyleSheet as StyleSheet38, Text as Text30, View as View37 } from "react-native";
10109
10284
  import { MapPin as MapPin5, Trash2 } from "lucide-react-native";
10110
- import { jsx as jsx50, jsxs as jsxs32 } from "react/jsx-runtime";
10285
+ import { jsx as jsx51, jsxs as jsxs32 } from "react/jsx-runtime";
10111
10286
  function RecapRow({
10112
10287
  label,
10113
10288
  value,
@@ -10115,8 +10290,8 @@ function RecapRow({
10115
10290
  onSurface
10116
10291
  }) {
10117
10292
  return /* @__PURE__ */ jsxs32(View37, { style: styles37.recapRow, children: [
10118
- /* @__PURE__ */ jsx50(Text30, { style: [styles37.recapLabel, { color: muted }], children: label }),
10119
- /* @__PURE__ */ jsx50(Text30, { style: [styles37.recapValue, { color: onSurface }], numberOfLines: 2, children: value })
10293
+ /* @__PURE__ */ jsx51(Text30, { style: [styles37.recapLabel, { color: muted }], children: label }),
10294
+ /* @__PURE__ */ jsx51(Text30, { style: [styles37.recapValue, { color: onSurface }], numberOfLines: 2, children: value })
10120
10295
  ] });
10121
10296
  }
10122
10297
  function ProfileScreen() {
@@ -10157,7 +10332,7 @@ function ProfileScreen() {
10157
10332
  };
10158
10333
  return /* @__PURE__ */ jsxs32(View37, { style: styles37.screen, children: [
10159
10334
  /* @__PURE__ */ jsxs32(View37, { style: styles37.section, children: [
10160
- /* @__PURE__ */ jsx50(Text30, { style: [styles37.sectionLabel, { color: t.colors.muted }], children: "MON SECTEUR" }),
10335
+ /* @__PURE__ */ jsx51(Text30, { style: [styles37.sectionLabel, { color: t.colors.muted }], children: "MON SECTEUR" }),
10161
10336
  /* @__PURE__ */ jsxs32(
10162
10337
  View37,
10163
10338
  {
@@ -10168,7 +10343,7 @@ function ProfileScreen() {
10168
10343
  ],
10169
10344
  children: [
10170
10345
  /* @__PURE__ */ jsxs32(View37, { style: styles37.recapHead, children: [
10171
- /* @__PURE__ */ jsx50(
10346
+ /* @__PURE__ */ jsx51(
10172
10347
  View37,
10173
10348
  {
10174
10349
  style: [
@@ -10178,10 +10353,10 @@ function ProfileScreen() {
10178
10353
  borderRadius: t.radii.pill
10179
10354
  }
10180
10355
  ],
10181
- children: /* @__PURE__ */ jsx50(MapPin5, { size: 18, color: theme.colors.onPrimary })
10356
+ children: /* @__PURE__ */ jsx51(MapPin5, { size: 18, color: theme.colors.onPrimary })
10182
10357
  }
10183
10358
  ),
10184
- /* @__PURE__ */ jsx50(
10359
+ /* @__PURE__ */ jsx51(
10185
10360
  Text30,
10186
10361
  {
10187
10362
  style: [styles37.recapTitle, { color: theme.colors.onSurface }],
@@ -10190,7 +10365,7 @@ function ProfileScreen() {
10190
10365
  )
10191
10366
  ] }),
10192
10367
  hasPlaceSelected ? /* @__PURE__ */ jsxs32(View37, { style: styles37.recapList, children: [
10193
- /* @__PURE__ */ jsx50(
10368
+ /* @__PURE__ */ jsx51(
10194
10369
  RecapRow,
10195
10370
  {
10196
10371
  label: "Ville",
@@ -10199,7 +10374,7 @@ function ProfileScreen() {
10199
10374
  onSurface: theme.colors.onSurface
10200
10375
  }
10201
10376
  ),
10202
- /* @__PURE__ */ jsx50(
10377
+ /* @__PURE__ */ jsx51(
10203
10378
  View37,
10204
10379
  {
10205
10380
  style: [
@@ -10208,7 +10383,7 @@ function ProfileScreen() {
10208
10383
  ]
10209
10384
  }
10210
10385
  ),
10211
- /* @__PURE__ */ jsx50(
10386
+ /* @__PURE__ */ jsx51(
10212
10387
  RecapRow,
10213
10388
  {
10214
10389
  label: "Adresse",
@@ -10217,7 +10392,7 @@ function ProfileScreen() {
10217
10392
  onSurface: theme.colors.onSurface
10218
10393
  }
10219
10394
  ),
10220
- /* @__PURE__ */ jsx50(
10395
+ /* @__PURE__ */ jsx51(
10221
10396
  View37,
10222
10397
  {
10223
10398
  style: [
@@ -10226,7 +10401,7 @@ function ProfileScreen() {
10226
10401
  ]
10227
10402
  }
10228
10403
  ),
10229
- /* @__PURE__ */ jsx50(
10404
+ /* @__PURE__ */ jsx51(
10230
10405
  RecapRow,
10231
10406
  {
10232
10407
  label: "Secteur",
@@ -10235,15 +10410,15 @@ function ProfileScreen() {
10235
10410
  onSurface: theme.colors.onSurface
10236
10411
  }
10237
10412
  )
10238
- ] }) : /* @__PURE__ */ jsx50(Text30, { style: [styles37.emptyHint, { color: t.colors.muted }], children: "Choisissez votre rue pour personnaliser les collectes et notifications." })
10413
+ ] }) : /* @__PURE__ */ jsx51(Text30, { style: [styles37.emptyHint, { color: t.colors.muted }], children: "Choisissez votre rue pour personnaliser les collectes et notifications." })
10239
10414
  ]
10240
10415
  }
10241
10416
  )
10242
10417
  ] }),
10243
10418
  /* @__PURE__ */ jsxs32(View37, { style: styles37.section, children: [
10244
- /* @__PURE__ */ jsx50(Text30, { style: [styles37.sectionLabel, { color: t.colors.muted }], children: "ACTIONS" }),
10419
+ /* @__PURE__ */ jsx51(Text30, { style: [styles37.sectionLabel, { color: t.colors.muted }], children: "ACTIONS" }),
10245
10420
  /* @__PURE__ */ jsxs32(View37, { style: styles37.actions, children: [
10246
- /* @__PURE__ */ jsx50(
10421
+ /* @__PURE__ */ jsx51(
10247
10422
  Button,
10248
10423
  {
10249
10424
  variant: "secondary",
@@ -10253,7 +10428,7 @@ function ProfileScreen() {
10253
10428
  children: "Changer de secteur"
10254
10429
  }
10255
10430
  ),
10256
- /* @__PURE__ */ jsx50(
10431
+ /* @__PURE__ */ jsx51(
10257
10432
  Button,
10258
10433
  {
10259
10434
  variant: "ghost",
@@ -10336,7 +10511,7 @@ import { useCallback as useCallback18, useState as useState32 } from "react";
10336
10511
  import {
10337
10512
  ActivityIndicator as ActivityIndicator13,
10338
10513
  Modal as Modal11,
10339
- Platform as Platform6,
10514
+ Platform as Platform8,
10340
10515
  StyleSheet as StyleSheet42,
10341
10516
  Text as Text34,
10342
10517
  View as View41
@@ -10344,13 +10519,13 @@ import {
10344
10519
 
10345
10520
  // src/components/vision/VisionCapture.tsx
10346
10521
  import { StyleSheet as StyleSheet39, Text as Text31, View as View38 } from "react-native";
10347
- import { jsx as jsx51, jsxs as jsxs33 } from "react/jsx-runtime";
10522
+ import { jsx as jsx52, jsxs as jsxs33 } from "react/jsx-runtime";
10348
10523
  function VisionCapture({ tips, onCapture, busy }) {
10349
10524
  const { theme } = useTheme();
10350
10525
  const t = resolveTheme(theme);
10351
10526
  return /* @__PURE__ */ jsxs33(View38, { style: styles38.root, children: [
10352
- /* @__PURE__ */ jsx51(Text31, { style: [styles38.tips, { color: t.colors.muted }], children: tips }),
10353
- /* @__PURE__ */ jsx51(Button, { onPress: onCapture, disabled: busy, loading: busy, children: busy ? "Ouverture\u2026" : "Prendre une photo" })
10527
+ /* @__PURE__ */ jsx52(Text31, { style: [styles38.tips, { color: t.colors.muted }], children: tips }),
10528
+ /* @__PURE__ */ jsx52(Button, { onPress: onCapture, disabled: busy, loading: busy, children: busy ? "Ouverture\u2026" : "Prendre une photo" })
10354
10529
  ] });
10355
10530
  }
10356
10531
  var styles38 = StyleSheet39.create({
@@ -10367,7 +10542,7 @@ import {
10367
10542
  View as View39
10368
10543
  } from "react-native";
10369
10544
  import { Image as Image13 } from "expo-image";
10370
- import { jsx as jsx52, jsxs as jsxs34 } from "react/jsx-runtime";
10545
+ import { jsx as jsx53, jsxs as jsxs34 } from "react/jsx-runtime";
10371
10546
  function VisionTapOverlay({
10372
10547
  imageUri,
10373
10548
  tap,
@@ -10385,7 +10560,7 @@ function VisionTapOverlay({
10385
10560
  setSize({ w: Math.max(width, 1), h: Math.max(height, 1) });
10386
10561
  }, []);
10387
10562
  return /* @__PURE__ */ jsxs34(View39, { style: styles39.root, children: [
10388
- /* @__PURE__ */ jsx52(Text32, { style: [styles39.hint, { color: t.colors.muted }], children: "Touchez le d\xE9chet sur la photo, ou analysez toute l\u2019image." }),
10563
+ /* @__PURE__ */ jsx53(Text32, { style: [styles39.hint, { color: t.colors.muted }], children: "Touchez le d\xE9chet sur la photo, ou analysez toute l\u2019image." }),
10389
10564
  /* @__PURE__ */ jsxs34(
10390
10565
  Pressable18,
10391
10566
  {
@@ -10402,8 +10577,8 @@ function VisionTapOverlay({
10402
10577
  { borderRadius: t.radii.md, backgroundColor: t.colors.onSurface }
10403
10578
  ],
10404
10579
  children: [
10405
- /* @__PURE__ */ jsx52(Image13, { source: { uri: imageUri }, style: styles39.image, contentFit: "contain" }),
10406
- tap ? /* @__PURE__ */ jsx52(
10580
+ /* @__PURE__ */ jsx53(Image13, { source: { uri: imageUri }, style: styles39.image, contentFit: "contain" }),
10581
+ tap ? /* @__PURE__ */ jsx53(
10407
10582
  View39,
10408
10583
  {
10409
10584
  pointerEvents: "none",
@@ -10421,8 +10596,8 @@ function VisionTapOverlay({
10421
10596
  }
10422
10597
  ),
10423
10598
  /* @__PURE__ */ jsxs34(View39, { style: styles39.row, children: [
10424
- /* @__PURE__ */ jsx52(Button, { variant: "outline", onPress: onRetake, disabled: busy, style: styles39.flexBtn, children: "Reprendre" }),
10425
- /* @__PURE__ */ jsx52(
10599
+ /* @__PURE__ */ jsx53(Button, { variant: "outline", onPress: onRetake, disabled: busy, style: styles39.flexBtn, children: "Reprendre" }),
10600
+ /* @__PURE__ */ jsx53(
10426
10601
  Button,
10427
10602
  {
10428
10603
  variant: "secondary",
@@ -10432,7 +10607,7 @@ function VisionTapOverlay({
10432
10607
  children: "Toute l\u2019image"
10433
10608
  }
10434
10609
  ),
10435
- /* @__PURE__ */ jsx52(
10610
+ /* @__PURE__ */ jsx53(
10436
10611
  Button,
10437
10612
  {
10438
10613
  onPress: onConfirmTap,
@@ -10468,7 +10643,7 @@ var styles39 = StyleSheet40.create({
10468
10643
 
10469
10644
  // src/components/vision/VisionActionCard.tsx
10470
10645
  import { StyleSheet as StyleSheet41, Text as Text33, View as View40 } from "react-native";
10471
- import { jsx as jsx53, jsxs as jsxs35 } from "react/jsx-runtime";
10646
+ import { jsx as jsx54, jsxs as jsxs35 } from "react/jsx-runtime";
10472
10647
  function VisionActionCard({
10473
10648
  suggestions,
10474
10649
  primaryLabel,
@@ -10502,18 +10677,18 @@ function VisionActionCard({
10502
10677
  }
10503
10678
  ],
10504
10679
  children: [
10505
- /* @__PURE__ */ jsx53(Text33, { style: [styles40.title, { color: t.colors.onSurface }], children: primary.title }),
10506
- /* @__PURE__ */ jsx53(Text33, { style: [styles40.body, { color: t.colors.muted }], children: primary.body })
10680
+ /* @__PURE__ */ jsx54(Text33, { style: [styles40.title, { color: t.colors.onSurface }], children: primary.title }),
10681
+ /* @__PURE__ */ jsx54(Text33, { style: [styles40.body, { color: t.colors.muted }], children: primary.body })
10507
10682
  ]
10508
10683
  }
10509
10684
  ) : null,
10510
10685
  pedagogy ? /* @__PURE__ */ jsxs35(View40, { style: styles40.pedagogy, children: [
10511
- /* @__PURE__ */ jsx53(Text33, { style: [styles40.title, { color: t.colors.onSurface }], children: pedagogy.title }),
10512
- /* @__PURE__ */ jsx53(Text33, { style: [styles40.body, { color: t.colors.muted }], children: pedagogy.body })
10686
+ /* @__PURE__ */ jsx54(Text33, { style: [styles40.title, { color: t.colors.onSurface }], children: pedagogy.title }),
10687
+ /* @__PURE__ */ jsx54(Text33, { style: [styles40.body, { color: t.colors.muted }], children: pedagogy.body })
10513
10688
  ] }) : null,
10514
10689
  /* @__PURE__ */ jsxs35(View40, { style: styles40.row, children: [
10515
- /* @__PURE__ */ jsx53(Button, { variant: "outline", onPress: onRetake, disabled: busy, style: styles40.flexBtn, children: "Reprendre" }),
10516
- /* @__PURE__ */ jsx53(
10690
+ /* @__PURE__ */ jsx54(Button, { variant: "outline", onPress: onRetake, disabled: busy, style: styles40.flexBtn, children: "Reprendre" }),
10691
+ /* @__PURE__ */ jsx54(
10517
10692
  Button,
10518
10693
  {
10519
10694
  variant: "outline",
@@ -10524,7 +10699,7 @@ function VisionActionCard({
10524
10699
  children: "Ce n\u2019est pas \xE7a"
10525
10700
  }
10526
10701
  ),
10527
- !isUnsure && requireConfirm ? /* @__PURE__ */ jsx53(
10702
+ !isUnsure && requireConfirm ? /* @__PURE__ */ jsx54(
10528
10703
  Button,
10529
10704
  {
10530
10705
  onPress: onConfirm,
@@ -10549,7 +10724,7 @@ var styles40 = StyleSheet41.create({
10549
10724
  });
10550
10725
 
10551
10726
  // src/components/vision/Vision.tsx
10552
- import { jsx as jsx54, jsxs as jsxs36 } from "react/jsx-runtime";
10727
+ import { jsx as jsx55, jsxs as jsxs36 } from "react/jsx-runtime";
10553
10728
  var DEFAULT_TITLE = "Wast\u2019O Scan";
10554
10729
  var DEFAULT_DESCRIPTION = "Photographiez un d\xE9chet, touchez-le sur la photo, puis suivez la consigne locale.";
10555
10730
  var TIPS = "Astuce : bonne lumi\xE8re, objet centr\xE9, un d\xE9chet \xE0 la fois si possible.";
@@ -10774,25 +10949,25 @@ function Vision({
10774
10949
  { minHeight: cardMinHeight }
10775
10950
  ],
10776
10951
  children: [
10777
- /* @__PURE__ */ jsx54(Text34, { style: [styles41.cardTitle, { color: t.colors.onSurface }], children: displayTitle }),
10778
- /* @__PURE__ */ jsx54(Text34, { style: [styles41.cardDescription, { color: t.colors.muted }], children: displayDescription }),
10779
- errorMessage && !modalVisible ? /* @__PURE__ */ jsx54(Text34, { style: [styles41.errorInline, { color: t.colors.error }], children: errorMessage }) : null,
10780
- /* @__PURE__ */ jsx54(Button, { onPress: openScan, children: "Scanner un d\xE9chet" }),
10781
- /* @__PURE__ */ jsx54(
10952
+ /* @__PURE__ */ jsx55(Text34, { style: [styles41.cardTitle, { color: t.colors.onSurface }], children: displayTitle }),
10953
+ /* @__PURE__ */ jsx55(Text34, { style: [styles41.cardDescription, { color: t.colors.muted }], children: displayDescription }),
10954
+ errorMessage && !modalVisible ? /* @__PURE__ */ jsx55(Text34, { style: [styles41.errorInline, { color: t.colors.error }], children: errorMessage }) : null,
10955
+ /* @__PURE__ */ jsx55(Button, { onPress: openScan, children: "Scanner un d\xE9chet" }),
10956
+ /* @__PURE__ */ jsx55(
10782
10957
  Modal11,
10783
10958
  {
10784
10959
  visible: modalVisible,
10785
10960
  animationType: "fade",
10786
- presentationStyle: Platform6.OS === "ios" ? "fullScreen" : void 0,
10961
+ presentationStyle: Platform8.OS === "ios" ? "fullScreen" : void 0,
10787
10962
  statusBarTranslucent: true,
10788
10963
  onRequestClose: closeModal,
10789
10964
  children: /* @__PURE__ */ jsxs36(View41, { style: [styles41.modalRoot, { backgroundColor: t.colors.background }], children: [
10790
10965
  /* @__PURE__ */ jsxs36(View41, { style: styles41.modalHeader, children: [
10791
- /* @__PURE__ */ jsx54(Text34, { style: [styles41.modalTitle, { color: t.colors.onSurface }], children: displayTitle }),
10792
- /* @__PURE__ */ jsx54(Button, { variant: "ghost", onPress: closeModal, children: "Fermer" })
10966
+ /* @__PURE__ */ jsx55(Text34, { style: [styles41.modalTitle, { color: t.colors.onSurface }], children: displayTitle }),
10967
+ /* @__PURE__ */ jsx55(Button, { variant: "ghost", onPress: closeModal, children: "Fermer" })
10793
10968
  ] }),
10794
10969
  /* @__PURE__ */ jsxs36(View41, { style: styles41.modalBody, children: [
10795
- phase === "capture" ? /* @__PURE__ */ jsx54(
10970
+ phase === "capture" ? /* @__PURE__ */ jsx55(
10796
10971
  VisionCapture,
10797
10972
  {
10798
10973
  tips: TIPS,
@@ -10800,7 +10975,7 @@ function Vision({
10800
10975
  busy
10801
10976
  }
10802
10977
  ) : null,
10803
- phase === "tap" && imageUri ? /* @__PURE__ */ jsx54(
10978
+ phase === "tap" && imageUri ? /* @__PURE__ */ jsx55(
10804
10979
  VisionTapOverlay,
10805
10980
  {
10806
10981
  imageUri,
@@ -10813,10 +10988,10 @@ function Vision({
10813
10988
  }
10814
10989
  ) : null,
10815
10990
  phase === "loading" ? /* @__PURE__ */ jsxs36(View41, { style: styles41.center, children: [
10816
- /* @__PURE__ */ jsx54(ActivityIndicator13, { size: "large", color: t.colors.primary }),
10817
- /* @__PURE__ */ jsx54(Text34, { style: { color: t.colors.muted, marginTop: 12 }, children: "Analyse en cours\u2026" })
10991
+ /* @__PURE__ */ jsx55(ActivityIndicator13, { size: "large", color: t.colors.primary }),
10992
+ /* @__PURE__ */ jsx55(Text34, { style: { color: t.colors.muted, marginTop: 12 }, children: "Analyse en cours\u2026" })
10818
10993
  ] }) : null,
10819
- phase === "card" ? /* @__PURE__ */ jsx54(
10994
+ phase === "card" ? /* @__PURE__ */ jsx55(
10820
10995
  VisionActionCard,
10821
10996
  {
10822
10997
  suggestions,
@@ -10830,9 +11005,9 @@ function Vision({
10830
11005
  }
10831
11006
  ) : null,
10832
11007
  phase === "error" ? /* @__PURE__ */ jsxs36(View41, { style: styles41.center, children: [
10833
- /* @__PURE__ */ jsx54(Text34, { style: { color: t.colors.error, fontWeight: "700" }, children: "Impossible d\u2019analyser" }),
10834
- /* @__PURE__ */ jsx54(Text34, { style: { color: t.colors.muted, marginTop: 8 }, children: errorMessage }),
10835
- /* @__PURE__ */ jsx54(Button, { onPress: resetFlow, style: styles41.retry, children: "R\xE9essayer" })
11008
+ /* @__PURE__ */ jsx55(Text34, { style: { color: t.colors.error, fontWeight: "700" }, children: "Impossible d\u2019analyser" }),
11009
+ /* @__PURE__ */ jsx55(Text34, { style: { color: t.colors.muted, marginTop: 8 }, children: errorMessage }),
11010
+ /* @__PURE__ */ jsx55(Button, { onPress: resetFlow, style: styles41.retry, children: "R\xE9essayer" })
10836
11011
  ] }) : null
10837
11012
  ] })
10838
11013
  ] })
@@ -10850,7 +11025,7 @@ var styles41 = StyleSheet42.create({
10850
11025
  cardTitle: { fontSize: 18, fontWeight: "700" },
10851
11026
  cardDescription: { fontSize: 14, lineHeight: 20 },
10852
11027
  errorInline: { fontSize: 13 },
10853
- modalRoot: { flex: 1, paddingTop: Platform6.OS === "ios" ? 56 : 24 },
11028
+ modalRoot: { flex: 1, paddingTop: Platform8.OS === "ios" ? 56 : 24 },
10854
11029
  modalHeader: {
10855
11030
  flexDirection: "row",
10856
11031
  justifyContent: "space-between",
@@ -10939,7 +11114,9 @@ export {
10939
11114
  CollectionCalendar,
10940
11115
  Columns,
10941
11116
  ContentInset,
11117
+ DEFAULT_HEADER_ACTIONS,
10942
11118
  Header,
11119
+ HeaderConfigProvider,
10943
11120
  Image10 as Image,
10944
11121
  LocationsMap,
10945
11122
  MAQUETTE_COLORS,
@@ -10964,11 +11141,13 @@ export {
10964
11141
  groupedSectionStyle,
10965
11142
  initWastoSentry,
10966
11143
  isWastoSentryActive,
11144
+ registerExpoPushToken,
10967
11145
  reportError,
10968
11146
  resolveTheme,
10969
11147
  softCardStyle,
10970
11148
  useAppData,
10971
11149
  useAppNavigation,
11150
+ useHeaderConfig,
10972
11151
  useOptionalAppData,
10973
11152
  useOptionalAppNavigation,
10974
11153
  useWasto,