@autobusal/providers 1.43.2 → 1.43.4
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/Setup/useUserBootstrap.ts +21 -1
- package/index.ts +2 -0
- package/package.json +1 -1
- package/services.ts +42 -4
|
@@ -19,9 +19,29 @@ import { UserData } from '../types/users';
|
|
|
19
19
|
// runs when there's an actual signal of a prior login: the bearer token
|
|
20
20
|
// (bearer mode) or the redacted user stub this same store persists (bff
|
|
21
21
|
// mode, where the token itself is an httpOnly cookie invisible to JS).
|
|
22
|
-
|
|
22
|
+
/*
|
|
23
|
+
* Claude - 2026-09-07: the BFF's readable marker, set by /bff/handoff and
|
|
24
|
+
* cleared by /bff/logout (@autobusal/bff 1.7.2).
|
|
25
|
+
*
|
|
26
|
+
* A handoff is the one way into a BFF session that writes NO stub: the
|
|
27
|
+
* browser lands on an origin it has never signed in on - that is what a
|
|
28
|
+
* handoff is for - so localStorage is empty by construction, and this hook
|
|
29
|
+
* stayed disabled. Measured on a clean browser: /bff/session said
|
|
30
|
+
* authenticated and the SPA still sent the visitor to the login form. The
|
|
31
|
+
* marker carries no secret; it is the one bit that makes this hook ask.
|
|
32
|
+
*/
|
|
33
|
+
const hasBffSignal = (): boolean => {
|
|
34
|
+
try {
|
|
35
|
+
return typeof document !== 'undefined' && /(?:^|;\s*)bff_signal=1(?:;|$)/.test(document.cookie);
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const hasLoginSignal = (): boolean => (
|
|
23
42
|
(!isBffMode && localStorage.getItem('token') !== null)
|
|
24
43
|
|| localStorage.getItem('user') !== null
|
|
44
|
+
|| (isBffMode && hasBffSignal())
|
|
25
45
|
);
|
|
26
46
|
|
|
27
47
|
const useUserBootstrap = (): void => {
|
package/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Providers from './Providers';
|
|
2
2
|
import apiClient, { isBffMode } from './Queries/apiClient';
|
|
3
3
|
import { storedReferral } from './Setup/useReferralTouch';
|
|
4
|
+
import { hasLoginSignal } from './Setup/useUserBootstrap';
|
|
4
5
|
import ErrorBoundary from './Errors/ErrorBoundary';
|
|
5
6
|
import Message from './Errors/Message';
|
|
6
7
|
import useMenuStore from './stores/menu';
|
|
@@ -15,6 +16,7 @@ export {
|
|
|
15
16
|
Message,
|
|
16
17
|
useMenuStore,
|
|
17
18
|
useUserStore,
|
|
19
|
+
hasLoginSignal,
|
|
18
20
|
registerStaleChunkRecovery
|
|
19
21
|
};
|
|
20
22
|
|
package/package.json
CHANGED
package/services.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
1
2
|
import { useSuspenseQuery, UseSuspenseQueryResult } from '@tanstack/react-query';
|
|
2
3
|
import i18next from 'i18next';
|
|
3
4
|
import { useTranslation } from 'react-i18next';
|
|
@@ -55,16 +56,53 @@ export const useGetMenu = (): UseSuspenseQueryResult<MenuData> => {
|
|
|
55
56
|
*/
|
|
56
57
|
const { i18n } = useTranslation();
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
const language = i18n.resolvedLanguage ?? i18next.language;
|
|
60
|
+
|
|
61
|
+
/*
|
|
62
|
+
* Claude - 2026-09-07 (Ferjolt: "when we switch to a language and
|
|
63
|
+
* immediately switch to another one, this does not work").
|
|
64
|
+
*
|
|
65
|
+
* A STABLE KEY, REFETCHED when the language changes - not a key that
|
|
66
|
+
* contains the language.
|
|
67
|
+
*
|
|
68
|
+
* With the language in the key, every switch handed this suspense query a
|
|
69
|
+
* key it had never loaded, and a suspense query with no data SUSPENDS.
|
|
70
|
+
* This one is mounted at the app's root (Setup), so the whole tree fell
|
|
71
|
+
* back to the loading screen - the "refresh" a visitor sees on every
|
|
72
|
+
* switch - and remounted when the labels arrived. Anything open at that
|
|
73
|
+
* moment was gone: measured on production, a language chosen while the
|
|
74
|
+
* previous switch's labels were still loading closed the picker and ran
|
|
75
|
+
* nothing, because the button it was clicked on no longer belonged to a
|
|
76
|
+
* mounted tree.
|
|
77
|
+
*
|
|
78
|
+
* Refetching an already-loaded key never suspends. The store keeps the
|
|
79
|
+
* previous language's labels for the few hundred milliseconds the new ones
|
|
80
|
+
* take, the page stays put, and a second choice lands on a live picker.
|
|
81
|
+
* `resolvedLanguage` for the reason the 2026-08-29 note gives.
|
|
82
|
+
*/
|
|
83
|
+
const query = useSuspenseQuery<MenuData>({
|
|
84
|
+
queryKey: ['get-menu'],
|
|
60
85
|
queryFn: async () => (
|
|
61
86
|
await apiClient
|
|
62
87
|
.get('/api/menus/get')
|
|
63
88
|
.then(response => {
|
|
64
89
|
menuStore.save(response.data);
|
|
65
90
|
|
|
66
|
-
|
|
91
|
+
// the store is the reader; the query's own data is a token (as before)
|
|
92
|
+
return {} as MenuData;
|
|
67
93
|
})
|
|
68
94
|
)
|
|
69
|
-
})
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const loaded = useRef<string>(language);
|
|
98
|
+
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (loaded.current !== language) {
|
|
101
|
+
loaded.current = language;
|
|
102
|
+
|
|
103
|
+
query.refetch();
|
|
104
|
+
}
|
|
105
|
+
}, [ language, query ]);
|
|
106
|
+
|
|
107
|
+
return query;
|
|
70
108
|
};
|