@autobusal/providers 1.6.4 → 1.6.6
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/CHANGELOG.md +41 -0
- package/Providers.tsx +27 -5
- package/Queries/apiClient.ts +19 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,47 @@ All notable changes to `@autobusal/providers` are documented here. This project
|
|
|
4
4
|
adheres to [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
|
|
5
5
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [1.6.6] - 2026-07-31
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **`Providers.tsx`: `Setup` (and its `useGetMenu()` call) now has its own
|
|
12
|
+
inner `<Suspense>` boundary**, instead of sharing the outer one with
|
|
13
|
+
`Recaptcha`/`Styles`/`Notifications`. `Setup` calls a second,
|
|
14
|
+
independently-resolving `useSuspenseQuery` (`get-menu`) after
|
|
15
|
+
`useGetSettings()`; on a cold menu cache this suspended AFTER
|
|
16
|
+
Recaptcha's `GoogleReCaptchaProvider` had already committed and
|
|
17
|
+
injected its script/badge - and a Suspense boundary that re-suspends
|
|
18
|
+
discards its already-committed content and remounts it once ready.
|
|
19
|
+
`GoogleReCaptchaProvider` isn't remount-safe (its script/badge
|
|
20
|
+
injection is async, outside React's own DOM bookkeeping), so the
|
|
21
|
+
remount left a duplicate, half-torn-down `.grecaptcha-badge` behind.
|
|
22
|
+
That corrupted DOM state then threw an uncaught `TypeError: Cannot
|
|
23
|
+
read properties of null (reading 'removeChild')` the next time React
|
|
24
|
+
reconciled anywhere near the root - i.e. on every subsequent route
|
|
25
|
+
change, since Recaptcha wraps the whole router. The exception isn't
|
|
26
|
+
caught by the app's `ErrorBoundary` (render-phase only), so it
|
|
27
|
+
silently aborted the commit: the URL would update (the router's
|
|
28
|
+
`history.pushState` already ran) but the page itself never
|
|
29
|
+
re-rendered - freezing client-side navigation app-wide, most visibly
|
|
30
|
+
reproduced via the account dropdown menu (Password, Telegram, and the
|
|
31
|
+
impersonation-aware Logout/"Return to my account" link, which is what
|
|
32
|
+
surfaced this).
|
|
33
|
+
|
|
34
|
+
## [1.6.5] - 2026-07-31
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- **`Queries/apiClient.ts` sends `Content-Language`** on every request,
|
|
39
|
+
reflecting the live active i18next language. The API's
|
|
40
|
+
ChangeApiLanguage middleware/Languaged trait expect this to localize
|
|
41
|
+
city/country/article/... names, but the frontend never sent it -
|
|
42
|
+
those names always rendered in the API's own default locale
|
|
43
|
+
regardless of the UI's actual language. Requires obtapi's
|
|
44
|
+
Traits\Languaged fix (falls back safely for the 13 UI languages the
|
|
45
|
+
database has no columns for) and @autobusal/bff 1.2.0 (forwards the
|
|
46
|
+
header upstream in bff mode) to actually take effect end-to-end.
|
|
47
|
+
|
|
7
48
|
## [1.6.4] - 2026-07-30
|
|
8
49
|
|
|
9
50
|
### Added
|
package/Providers.tsx
CHANGED
|
@@ -24,16 +24,38 @@ const Providers = ({ type, loading, children }: Props): JSX.Element => (
|
|
|
24
24
|
// Recaptcha moved INSIDE Queries: it now reads the label's runtime captcha
|
|
25
25
|
// settings via useGetSettings(), which needs the QueryClientProvider (and
|
|
26
26
|
// suspends into the outer boundary on a cold cache, like Styles/Setup).
|
|
27
|
+
// Edited: Ferjolt Ozuni - Date: 2026-07-31
|
|
28
|
+
// Bug: Setup also calls useGetMenu() (a SEPARATE useSuspenseQuery, its own
|
|
29
|
+
// cache key/promise) in its own render body, still inside this same outer
|
|
30
|
+
// boundary. On a cold menu cache, that suspends AFTER Recaptcha/Styles/
|
|
31
|
+
// Notifications above it have already committed - and a Suspense boundary
|
|
32
|
+
// re-suspending after its content is already committed hides/discards that
|
|
33
|
+
// whole committed subtree and mounts it again once ready. Recaptcha's
|
|
34
|
+
// GoogleReCaptchaProvider isn't remount-safe: its script/badge injection is
|
|
35
|
+
// async and outside React's DOM bookkeeping, so remounting it left a
|
|
36
|
+
// duplicate, half-torn-down <div class="grecaptcha-badge"> behind. That
|
|
37
|
+
// corrupted DOM state then threw an uncaught "Cannot read properties of
|
|
38
|
+
// null (reading 'removeChild')" the next time React reconciled anywhere
|
|
39
|
+
// near the root - which is every route change, since Recaptcha wraps the
|
|
40
|
+
// whole router - silently aborting the commit. The URL would update (the
|
|
41
|
+
// router's history.pushState already ran) but the page itself would never
|
|
42
|
+
// re-render, freezing navigation app-wide with no console-visible error
|
|
43
|
+
// (uncaught DOM exceptions like this don't hit the render-phase-only
|
|
44
|
+
// ErrorBoundary). Giving Setup its own inner boundary means its menu
|
|
45
|
+
// suspension can only hide/retry ITSELF, not the already-committed
|
|
46
|
+
// Recaptcha/Styles/Notifications above it.
|
|
27
47
|
<Suspense fallback={ loading }>
|
|
28
48
|
<Queries>
|
|
29
49
|
<Recaptcha>
|
|
30
50
|
<Styles>
|
|
31
51
|
<Notifications>
|
|
32
|
-
<
|
|
33
|
-
<
|
|
34
|
-
{
|
|
35
|
-
|
|
36
|
-
|
|
52
|
+
<Suspense fallback={ loading }>
|
|
53
|
+
<Setup type={ type }>
|
|
54
|
+
<Suspense fallback={ loading }>
|
|
55
|
+
{ children }
|
|
56
|
+
</Suspense>
|
|
57
|
+
</Setup>
|
|
58
|
+
</Suspense>
|
|
37
59
|
</Notifications>
|
|
38
60
|
</Styles>
|
|
39
61
|
</Recaptcha>
|
package/Queries/apiClient.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
+
import i18next from 'i18next';
|
|
2
3
|
import languages from '@lang/errors';
|
|
3
4
|
import { getLanguage } from '../Setup/languages';
|
|
4
5
|
import { error as systemError } from '@autobusal/utilities';
|
|
@@ -37,6 +38,24 @@ if (!isBffMode) {
|
|
|
37
38
|
});
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
// The API localizes city/country/article/... names off this header
|
|
42
|
+
// (ChangeApiLanguage middleware) - was never sent at all, so every one of
|
|
43
|
+
// those names always rendered in the API's own default locale regardless
|
|
44
|
+
// of the UI's actual language. i18next.language reflects the live active
|
|
45
|
+
// language (kept in sync by Setup/languages.ts on every change), read
|
|
46
|
+
// fresh per request rather than closed over once, so a language switch
|
|
47
|
+
// takes effect on the very next call with no extra wiring anywhere else.
|
|
48
|
+
// In bff mode this also needs public/bff/index.php's api() to forward it
|
|
49
|
+
// upstream (see @autobusal/bff) - both sides had to change, only one of
|
|
50
|
+
// them silently doing nothing would look identical to neither working.
|
|
51
|
+
apiClient.interceptors.request.use(config => {
|
|
52
|
+
if (i18next.language) {
|
|
53
|
+
config.headers['Content-Language'] = i18next.language;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return config;
|
|
57
|
+
});
|
|
58
|
+
|
|
40
59
|
apiClient.interceptors.response.use(response => (
|
|
41
60
|
response
|
|
42
61
|
), async (error) => {
|