@wasto/cms-components 1.5.14 → 1.5.16

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.d.mts CHANGED
@@ -133,15 +133,20 @@ type HeaderProps = {
133
133
  canGoBack: () => boolean;
134
134
  goBack: () => void;
135
135
  };
136
- /** Flotte au-dessus du contenu (ex. carte) : fond transparent. */
136
+ /** Flotte au-dessus du contenu (ex. carte) : fond transparent + safe-area top. */
137
137
  readonly overlay?: boolean;
138
138
  /**
139
139
  * Config layout (`header.elements`) — logo / actions.
140
140
  * Si fournie (même vide), le fallback titre d’écran ne s’applique pas.
141
141
  */
142
142
  readonly headerConfig?: AppHeaderConfig | null;
143
+ /**
144
+ * Force le padding safe-area top. Par défaut : uniquement en `overlay`
145
+ * (le header React Navigation est déjà placé sous la status bar).
146
+ */
147
+ readonly safeTop?: boolean;
143
148
  };
144
- declare function Header({ options, route, navigation, overlay, headerConfig, }: HeaderProps): ReactNode;
149
+ declare function Header({ options, route, navigation, overlay, headerConfig, safeTop, }: HeaderProps): ReactNode;
145
150
 
146
151
  type LocationsMapProps = {
147
152
  fullScreen?: boolean;
@@ -484,6 +489,8 @@ declare function useOptionalAppData(): AppDataContextType | null;
484
489
  type RegisterExpoPushTokenOptions = {
485
490
  deviceId: string;
486
491
  registerPushToken: (deviceId: string, token: string) => Promise<unknown>;
492
+ /** Called when registration is skipped or fails (not only thrown errors). */
493
+ onSkip?: (reason: string, detail?: Record<string, unknown>) => void;
487
494
  };
488
495
  /**
489
496
  * Request OS permission, fetch Expo push token, register it on the API.
package/dist/index.d.ts CHANGED
@@ -133,15 +133,20 @@ type HeaderProps = {
133
133
  canGoBack: () => boolean;
134
134
  goBack: () => void;
135
135
  };
136
- /** Flotte au-dessus du contenu (ex. carte) : fond transparent. */
136
+ /** Flotte au-dessus du contenu (ex. carte) : fond transparent + safe-area top. */
137
137
  readonly overlay?: boolean;
138
138
  /**
139
139
  * Config layout (`header.elements`) — logo / actions.
140
140
  * Si fournie (même vide), le fallback titre d’écran ne s’applique pas.
141
141
  */
142
142
  readonly headerConfig?: AppHeaderConfig | null;
143
+ /**
144
+ * Force le padding safe-area top. Par défaut : uniquement en `overlay`
145
+ * (le header React Navigation est déjà placé sous la status bar).
146
+ */
147
+ readonly safeTop?: boolean;
143
148
  };
144
- declare function Header({ options, route, navigation, overlay, headerConfig, }: HeaderProps): ReactNode;
149
+ declare function Header({ options, route, navigation, overlay, headerConfig, safeTop, }: HeaderProps): ReactNode;
145
150
 
146
151
  type LocationsMapProps = {
147
152
  fullScreen?: boolean;
@@ -484,6 +489,8 @@ declare function useOptionalAppData(): AppDataContextType | null;
484
489
  type RegisterExpoPushTokenOptions = {
485
490
  deviceId: string;
486
491
  registerPushToken: (deviceId: string, token: string) => Promise<unknown>;
492
+ /** Called when registration is skipped or fails (not only thrown errors). */
493
+ onSkip?: (reason: string, detail?: Record<string, unknown>) => void;
487
494
  };
488
495
  /**
489
496
  * Request OS permission, fetch Expo push token, register it on the API.
package/dist/index.js CHANGED
@@ -2179,25 +2179,31 @@ function resolveEasProjectId() {
2179
2179
  return projectId || null;
2180
2180
  }
2181
2181
  async function registerExpoPushToken(options) {
2182
- if (import_react_native7.Platform.OS === "web") return null;
2182
+ const skip = (reason, detail) => {
2183
+ options.onSkip?.(reason, detail);
2184
+ if (__DEV__) {
2185
+ console.warn(`[wasto] push registration skipped: ${reason}`, detail ?? "");
2186
+ }
2187
+ };
2188
+ if (import_react_native7.Platform.OS === "web") {
2189
+ skip("web_platform");
2190
+ return null;
2191
+ }
2183
2192
  const deviceId = options.deviceId?.trim();
2184
- if (!deviceId) return null;
2193
+ if (!deviceId) {
2194
+ skip("missing_device_id");
2195
+ return null;
2196
+ }
2185
2197
  const Notifications = loadNotifications();
2186
2198
  if (!Notifications) {
2187
- if (__DEV__) {
2188
- console.warn(
2189
- "[wasto] expo-notifications is not installed; push registration skipped"
2190
- );
2191
- }
2199
+ skip("expo_notifications_missing");
2192
2200
  return null;
2193
2201
  }
2194
2202
  const projectId = resolveEasProjectId();
2195
2203
  if (!projectId) {
2196
- if (__DEV__) {
2197
- console.warn(
2198
- "[wasto] EAS projectId missing (extra.eas.projectId or EXPO_PUBLIC_EAS_PROJECT_ID); push registration skipped"
2199
- );
2200
- }
2204
+ skip("eas_project_id_missing", {
2205
+ hint: "Set EXPO_PUBLIC_EAS_PROJECT_ID or extra.eas.projectId before EAS build"
2206
+ });
2201
2207
  return null;
2202
2208
  }
2203
2209
  if (!handlerConfigured) {
@@ -2226,12 +2232,26 @@ async function registerExpoPushToken(options) {
2226
2232
  const { status } = await Notifications.requestPermissionsAsync();
2227
2233
  finalStatus = status;
2228
2234
  }
2229
- if (finalStatus !== "granted") return null;
2230
- const push = await Notifications.getExpoPushTokenAsync({ projectId });
2231
- const token = push?.data?.trim();
2232
- if (!token) return null;
2233
- await options.registerPushToken(deviceId, token);
2234
- return token;
2235
+ if (finalStatus !== "granted") {
2236
+ skip("permission_denied", { status: finalStatus });
2237
+ return null;
2238
+ }
2239
+ try {
2240
+ const push = await Notifications.getExpoPushTokenAsync({ projectId });
2241
+ const token = push?.data?.trim();
2242
+ if (!token) {
2243
+ skip("empty_expo_push_token", { projectId });
2244
+ return null;
2245
+ }
2246
+ await options.registerPushToken(deviceId, token);
2247
+ return token;
2248
+ } catch (error) {
2249
+ skip("get_expo_push_token_failed", {
2250
+ projectId,
2251
+ message: error instanceof Error ? error.message : String(error)
2252
+ });
2253
+ throw error;
2254
+ }
2235
2255
  }
2236
2256
 
2237
2257
  // src/components/push/PushTokenRegistrar.tsx
@@ -2256,7 +2276,15 @@ function PushTokenRegistrar() {
2256
2276
  token: pushToken,
2257
2277
  cityId: selectedCityId,
2258
2278
  wasteCollectionPlaceId: selectedPlaceId
2259
- })
2279
+ }),
2280
+ onSkip: (reason, detail) => {
2281
+ reportError2(new Error(`push_skip:${reason}`), {
2282
+ source: "push",
2283
+ deviceId,
2284
+ reason,
2285
+ ...detail
2286
+ });
2287
+ }
2260
2288
  });
2261
2289
  if (!cancelled && token) {
2262
2290
  lastRegistered.current = registrationKey;
@@ -8292,12 +8320,14 @@ function Header({
8292
8320
  route,
8293
8321
  navigation,
8294
8322
  overlay = false,
8295
- headerConfig
8323
+ headerConfig,
8324
+ safeTop
8296
8325
  }) {
8297
8326
  const { theme } = useTheme();
8298
8327
  const t = resolveTheme(theme);
8299
8328
  const themedStyles = createThemedStyles14(theme);
8300
- const topInset = useSafeAreaPadding(["top"]);
8329
+ const applyTopInset = safeTop ?? overlay;
8330
+ const topInset = useSafeAreaPadding(applyTopInset ? ["top"] : []);
8301
8331
  const { hasUnread } = useAnnouncements();
8302
8332
  const { push } = useAppNavigation();
8303
8333
  const screenTitle = (0, import_react35.useMemo)(
package/dist/index.mjs CHANGED
@@ -2169,25 +2169,31 @@ function resolveEasProjectId() {
2169
2169
  return projectId || null;
2170
2170
  }
2171
2171
  async function registerExpoPushToken(options) {
2172
- if (Platform4.OS === "web") return null;
2172
+ const skip = (reason, detail) => {
2173
+ options.onSkip?.(reason, detail);
2174
+ if (__DEV__) {
2175
+ console.warn(`[wasto] push registration skipped: ${reason}`, detail ?? "");
2176
+ }
2177
+ };
2178
+ if (Platform4.OS === "web") {
2179
+ skip("web_platform");
2180
+ return null;
2181
+ }
2173
2182
  const deviceId = options.deviceId?.trim();
2174
- if (!deviceId) return null;
2183
+ if (!deviceId) {
2184
+ skip("missing_device_id");
2185
+ return null;
2186
+ }
2175
2187
  const Notifications = loadNotifications();
2176
2188
  if (!Notifications) {
2177
- if (__DEV__) {
2178
- console.warn(
2179
- "[wasto] expo-notifications is not installed; push registration skipped"
2180
- );
2181
- }
2189
+ skip("expo_notifications_missing");
2182
2190
  return null;
2183
2191
  }
2184
2192
  const projectId = resolveEasProjectId();
2185
2193
  if (!projectId) {
2186
- if (__DEV__) {
2187
- console.warn(
2188
- "[wasto] EAS projectId missing (extra.eas.projectId or EXPO_PUBLIC_EAS_PROJECT_ID); push registration skipped"
2189
- );
2190
- }
2194
+ skip("eas_project_id_missing", {
2195
+ hint: "Set EXPO_PUBLIC_EAS_PROJECT_ID or extra.eas.projectId before EAS build"
2196
+ });
2191
2197
  return null;
2192
2198
  }
2193
2199
  if (!handlerConfigured) {
@@ -2216,12 +2222,26 @@ async function registerExpoPushToken(options) {
2216
2222
  const { status } = await Notifications.requestPermissionsAsync();
2217
2223
  finalStatus = status;
2218
2224
  }
2219
- if (finalStatus !== "granted") return null;
2220
- const push = await Notifications.getExpoPushTokenAsync({ projectId });
2221
- const token = push?.data?.trim();
2222
- if (!token) return null;
2223
- await options.registerPushToken(deviceId, token);
2224
- return token;
2225
+ if (finalStatus !== "granted") {
2226
+ skip("permission_denied", { status: finalStatus });
2227
+ return null;
2228
+ }
2229
+ try {
2230
+ const push = await Notifications.getExpoPushTokenAsync({ projectId });
2231
+ const token = push?.data?.trim();
2232
+ if (!token) {
2233
+ skip("empty_expo_push_token", { projectId });
2234
+ return null;
2235
+ }
2236
+ await options.registerPushToken(deviceId, token);
2237
+ return token;
2238
+ } catch (error) {
2239
+ skip("get_expo_push_token_failed", {
2240
+ projectId,
2241
+ message: error instanceof Error ? error.message : String(error)
2242
+ });
2243
+ throw error;
2244
+ }
2225
2245
  }
2226
2246
 
2227
2247
  // src/components/push/PushTokenRegistrar.tsx
@@ -2246,7 +2266,15 @@ function PushTokenRegistrar() {
2246
2266
  token: pushToken,
2247
2267
  cityId: selectedCityId,
2248
2268
  wasteCollectionPlaceId: selectedPlaceId
2249
- })
2269
+ }),
2270
+ onSkip: (reason, detail) => {
2271
+ reportError2(new Error(`push_skip:${reason}`), {
2272
+ source: "push",
2273
+ deviceId,
2274
+ reason,
2275
+ ...detail
2276
+ });
2277
+ }
2250
2278
  });
2251
2279
  if (!cancelled && token) {
2252
2280
  lastRegistered.current = registrationKey;
@@ -8370,12 +8398,14 @@ function Header({
8370
8398
  route,
8371
8399
  navigation,
8372
8400
  overlay = false,
8373
- headerConfig
8401
+ headerConfig,
8402
+ safeTop
8374
8403
  }) {
8375
8404
  const { theme } = useTheme();
8376
8405
  const t = resolveTheme(theme);
8377
8406
  const themedStyles = createThemedStyles14(theme);
8378
- const topInset = useSafeAreaPadding(["top"]);
8407
+ const applyTopInset = safeTop ?? overlay;
8408
+ const topInset = useSafeAreaPadding(applyTopInset ? ["top"] : []);
8379
8409
  const { hasUnread } = useAnnouncements();
8380
8410
  const { push } = useAppNavigation();
8381
8411
  const screenTitle = useMemo20(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wasto/cms-components",
3
- "version": "1.5.14",
3
+ "version": "1.5.16",
4
4
  "description": "Wast'O CMS React Native components",
5
5
  "main": "./src/index.ts",
6
6
  "module": "./src/index.ts",
@@ -86,13 +86,18 @@ export type HeaderProps = {
86
86
  canGoBack: () => boolean;
87
87
  goBack: () => void;
88
88
  };
89
- /** Flotte au-dessus du contenu (ex. carte) : fond transparent. */
89
+ /** Flotte au-dessus du contenu (ex. carte) : fond transparent + safe-area top. */
90
90
  readonly overlay?: boolean;
91
91
  /**
92
92
  * Config layout (`header.elements`) — logo / actions.
93
93
  * Si fournie (même vide), le fallback titre d’écran ne s’applique pas.
94
94
  */
95
95
  readonly headerConfig?: AppHeaderConfig | null;
96
+ /**
97
+ * Force le padding safe-area top. Par défaut : uniquement en `overlay`
98
+ * (le header React Navigation est déjà placé sous la status bar).
99
+ */
100
+ readonly safeTop?: boolean;
96
101
  };
97
102
 
98
103
  function resolveLogoUri(
@@ -172,11 +177,15 @@ export function Header({
172
177
  navigation,
173
178
  overlay = false,
174
179
  headerConfig,
180
+ safeTop,
175
181
  }: HeaderProps): ReactNode {
176
182
  const { theme } = useTheme();
177
183
  const t = resolveTheme(theme);
178
184
  const themedStyles = createThemedStyles(theme);
179
- const topInset = useSafeAreaPadding(["top"]);
185
+ // Nav header : RN place déjà sous la status bar → pas de double inset.
186
+ // Overlay (carte) : on flotte sous le notch → inset requis.
187
+ const applyTopInset = safeTop ?? overlay;
188
+ const topInset = useSafeAreaPadding(applyTopInset ? ["top"] : []);
180
189
  const { hasUnread } = useAnnouncements();
181
190
  const { push } = useAppNavigation();
182
191
 
@@ -38,6 +38,14 @@ export function PushTokenRegistrar(): ReactNode {
38
38
  cityId: selectedCityId,
39
39
  wasteCollectionPlaceId: selectedPlaceId,
40
40
  }),
41
+ onSkip: (reason, detail) => {
42
+ reportError(new Error(`push_skip:${reason}`), {
43
+ source: "push",
44
+ deviceId,
45
+ reason,
46
+ ...detail,
47
+ });
48
+ },
41
49
  });
42
50
  if (!cancelled && token) {
43
51
  lastRegistered.current = registrationKey;
@@ -60,6 +60,8 @@ function resolveEasProjectId(): string | null {
60
60
  export type RegisterExpoPushTokenOptions = {
61
61
  deviceId: string;
62
62
  registerPushToken: (deviceId: string, token: string) => Promise<unknown>;
63
+ /** Called when registration is skipped or fails (not only thrown errors). */
64
+ onSkip?: (reason: string, detail?: Record<string, unknown>) => void;
63
65
  };
64
66
 
65
67
  /**
@@ -69,28 +71,35 @@ export type RegisterExpoPushTokenOptions = {
69
71
  export async function registerExpoPushToken(
70
72
  options: RegisterExpoPushTokenOptions,
71
73
  ): Promise<string | null> {
72
- if (Platform.OS === "web") return null;
74
+ const skip = (reason: string, detail?: Record<string, unknown>) => {
75
+ options.onSkip?.(reason, detail);
76
+ if (__DEV__) {
77
+ console.warn(`[wasto] push registration skipped: ${reason}`, detail ?? "");
78
+ }
79
+ };
80
+
81
+ if (Platform.OS === "web") {
82
+ skip("web_platform");
83
+ return null;
84
+ }
73
85
 
74
86
  const deviceId = options.deviceId?.trim();
75
- if (!deviceId) return null;
87
+ if (!deviceId) {
88
+ skip("missing_device_id");
89
+ return null;
90
+ }
76
91
 
77
92
  const Notifications = loadNotifications();
78
93
  if (!Notifications) {
79
- if (__DEV__) {
80
- console.warn(
81
- "[wasto] expo-notifications is not installed; push registration skipped",
82
- );
83
- }
94
+ skip("expo_notifications_missing");
84
95
  return null;
85
96
  }
86
97
 
87
98
  const projectId = resolveEasProjectId();
88
99
  if (!projectId) {
89
- if (__DEV__) {
90
- console.warn(
91
- "[wasto] EAS projectId missing (extra.eas.projectId or EXPO_PUBLIC_EAS_PROJECT_ID); push registration skipped",
92
- );
93
- }
100
+ skip("eas_project_id_missing", {
101
+ hint: "Set EXPO_PUBLIC_EAS_PROJECT_ID or extra.eas.projectId before EAS build",
102
+ });
94
103
  return null;
95
104
  }
96
105
 
@@ -122,12 +131,26 @@ export async function registerExpoPushToken(
122
131
  const { status } = await Notifications.requestPermissionsAsync();
123
132
  finalStatus = status;
124
133
  }
125
- if (finalStatus !== "granted") return null;
134
+ if (finalStatus !== "granted") {
135
+ skip("permission_denied", { status: finalStatus });
136
+ return null;
137
+ }
126
138
 
127
- const push = await Notifications.getExpoPushTokenAsync({ projectId });
128
- const token = push?.data?.trim();
129
- if (!token) return null;
139
+ try {
140
+ const push = await Notifications.getExpoPushTokenAsync({ projectId });
141
+ const token = push?.data?.trim();
142
+ if (!token) {
143
+ skip("empty_expo_push_token", { projectId });
144
+ return null;
145
+ }
130
146
 
131
- await options.registerPushToken(deviceId, token);
132
- return token;
147
+ await options.registerPushToken(deviceId, token);
148
+ return token;
149
+ } catch (error) {
150
+ skip("get_expo_push_token_failed", {
151
+ projectId,
152
+ message: error instanceof Error ? error.message : String(error),
153
+ });
154
+ throw error;
155
+ }
133
156
  }