@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 +9 -2
- package/dist/index.d.ts +9 -2
- package/dist/index.js +51 -21
- package/dist/index.mjs +51 -21
- package/package.json +1 -1
- package/src/components/header/Header.tsx +11 -2
- package/src/components/push/PushTokenRegistrar.tsx +8 -0
- package/src/push/registerExpoPushToken.ts +41 -18
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
|
-
|
|
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)
|
|
2193
|
+
if (!deviceId) {
|
|
2194
|
+
skip("missing_device_id");
|
|
2195
|
+
return null;
|
|
2196
|
+
}
|
|
2185
2197
|
const Notifications = loadNotifications();
|
|
2186
2198
|
if (!Notifications) {
|
|
2187
|
-
|
|
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
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
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")
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
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
|
|
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
|
-
|
|
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)
|
|
2183
|
+
if (!deviceId) {
|
|
2184
|
+
skip("missing_device_id");
|
|
2185
|
+
return null;
|
|
2186
|
+
}
|
|
2175
2187
|
const Notifications = loadNotifications();
|
|
2176
2188
|
if (!Notifications) {
|
|
2177
|
-
|
|
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
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
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")
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
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
|
|
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
|
@@ -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
|
-
|
|
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
|
-
|
|
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)
|
|
87
|
+
if (!deviceId) {
|
|
88
|
+
skip("missing_device_id");
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
76
91
|
|
|
77
92
|
const Notifications = loadNotifications();
|
|
78
93
|
if (!Notifications) {
|
|
79
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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")
|
|
134
|
+
if (finalStatus !== "granted") {
|
|
135
|
+
skip("permission_denied", { status: finalStatus });
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
126
138
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
132
|
-
|
|
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
|
}
|