Files
paseo/patches/react-native-unistyles+3.2.4.patch
T
Mohamed Boudra 9477fe0c00 fix(app): refresh Android layout after background fold changes (#8285)
Backport the Unistyles 3.5.3 Activity configuration fix while keeping 3.2.4 and its existing fixes. Application resources could leave the responsive shell on the previous fold size after resume. Cover the real device-state sequence with a native UI regression.
2026-10-30 17:22:53 +07:00

708 lines
32 KiB
Diff

diff --git a/node_modules/react-native-unistyles/lib/commonjs/web/registry.js b/node_modules/react-native-unistyles/lib/commonjs/web/registry.js
index 120ff60..d1ff102 100644
--- a/node_modules/react-native-unistyles/lib/commonjs/web/registry.js
+++ b/node_modules/react-native-unistyles/lib/commonjs/web/registry.js
@@ -10,6 +10,17 @@ class UnistylesRegistry {
stylesheets = new Map();
stylesCache = new Set();
stylesCounter = new Map();
+ cleanupGeneration = 0;
+ finalizerTokens = new WeakMap();
+ finalizationRegistry = typeof FinalizationRegistry === 'undefined' ? undefined : new FinalizationRegistry(({
+ hash,
+ generation
+ }) => {
+ if (generation !== this.cleanupGeneration) {
+ return;
+ }
+ this.decrementAndMaybeCleanup(hash);
+ });
disposeListenersMap = new Map();
dependenciesMap = new Map();
constructor(services) {
@@ -50,26 +61,48 @@ class UnistylesRegistry {
this.disposeListenersMap.set(stylesheet, dispose);
};
connect = (ref, hash) => {
- const stylesCounter = this.stylesCounter.get(hash) ?? new Set();
- stylesCounter.add(ref);
- this.stylesCounter.set(hash, stylesCounter);
+ this.stylesCounter.set(hash, (this.stylesCounter.get(hash) ?? 0) + 1);
+ if (!this.finalizationRegistry) {
+ return;
+ }
+ const token = {};
+ const tokensByHash = this.finalizerTokens.get(ref) ?? new Map();
+ const tokens = tokensByHash.get(hash) ?? [];
+ tokens.push(token);
+ tokensByHash.set(hash, tokens);
+ this.finalizerTokens.set(ref, tokensByHash);
+ this.finalizationRegistry.register(ref, {
+ hash,
+ generation: this.cleanupGeneration
+ }, token);
};
remove = (ref, hash) => {
- const stylesCounter = this.stylesCounter.get(hash) ?? new Set();
- stylesCounter.delete(ref);
- if (stylesCounter.size === 0) {
- // Move this to the end of the event loop so the element is removed from the DOM
- return Promise.resolve().then(() => {
- // Check if element is still in the DOM
- if (document.querySelector(`.${hash}`)) {
- return false;
- }
- this.css.remove(hash);
- this.stylesCache.delete(hash);
- return true;
- });
+ const token = this.finalizerTokens.get(ref)?.get(hash)?.pop();
+ if (token) {
+ this.finalizationRegistry?.unregister(token);
+ }
+ return this.decrementAndMaybeCleanup(hash);
+ };
+ decrementAndMaybeCleanup = hash => {
+ const next = (this.stylesCounter.get(hash) ?? 0) - 1;
+ if (next > 0) {
+ this.stylesCounter.set(hash, next);
+ return Promise.resolve(false);
}
- return Promise.resolve(false);
+ this.stylesCounter.delete(hash);
+ // Move this to the end of the event loop so the element is removed from the DOM
+ return Promise.resolve().then(() => {
+ // Check if element is still in the DOM
+ if ((this.stylesCounter.get(hash) ?? 0) > 0) {
+ return false;
+ }
+ if (document.querySelector(`.${hash}`)) {
+ return false;
+ }
+ this.css.remove(hash);
+ this.stylesCache.delete(hash);
+ return true;
+ });
};
add = (value, forChild) => {
const generatedHash = (0, _utils.generateHash)(value);
@@ -91,7 +124,10 @@ class UnistylesRegistry {
this.css.add(hash, value);
};
reset = () => {
+ this.cleanupGeneration += 1;
+ this.disposeListenersMap.forEach(dispose => dispose());
this.css.reset();
+ this.stylesheets.clear();
this.stylesCache.clear();
this.dependenciesMap.clear();
this.disposeListenersMap.clear();
diff --git a/node_modules/react-native-unistyles/lib/module/web/registry.js b/node_modules/react-native-unistyles/lib/module/web/registry.js
index 6362f43..8e769f8 100644
--- a/node_modules/react-native-unistyles/lib/module/web/registry.js
+++ b/node_modules/react-native-unistyles/lib/module/web/registry.js
@@ -6,6 +6,17 @@ export class UnistylesRegistry {
stylesheets = new Map();
stylesCache = new Set();
stylesCounter = new Map();
+ cleanupGeneration = 0;
+ finalizerTokens = new WeakMap();
+ finalizationRegistry = typeof FinalizationRegistry === 'undefined' ? undefined : new FinalizationRegistry(({
+ hash,
+ generation
+ }) => {
+ if (generation !== this.cleanupGeneration) {
+ return;
+ }
+ this.decrementAndMaybeCleanup(hash);
+ });
disposeListenersMap = new Map();
dependenciesMap = new Map();
constructor(services) {
@@ -46,26 +57,48 @@ export class UnistylesRegistry {
this.disposeListenersMap.set(stylesheet, dispose);
};
connect = (ref, hash) => {
- const stylesCounter = this.stylesCounter.get(hash) ?? new Set();
- stylesCounter.add(ref);
- this.stylesCounter.set(hash, stylesCounter);
+ this.stylesCounter.set(hash, (this.stylesCounter.get(hash) ?? 0) + 1);
+ if (!this.finalizationRegistry) {
+ return;
+ }
+ const token = {};
+ const tokensByHash = this.finalizerTokens.get(ref) ?? new Map();
+ const tokens = tokensByHash.get(hash) ?? [];
+ tokens.push(token);
+ tokensByHash.set(hash, tokens);
+ this.finalizerTokens.set(ref, tokensByHash);
+ this.finalizationRegistry.register(ref, {
+ hash,
+ generation: this.cleanupGeneration
+ }, token);
};
remove = (ref, hash) => {
- const stylesCounter = this.stylesCounter.get(hash) ?? new Set();
- stylesCounter.delete(ref);
- if (stylesCounter.size === 0) {
- // Move this to the end of the event loop so the element is removed from the DOM
- return Promise.resolve().then(() => {
- // Check if element is still in the DOM
- if (document.querySelector(`.${hash}`)) {
- return false;
- }
- this.css.remove(hash);
- this.stylesCache.delete(hash);
- return true;
- });
+ const token = this.finalizerTokens.get(ref)?.get(hash)?.pop();
+ if (token) {
+ this.finalizationRegistry?.unregister(token);
+ }
+ return this.decrementAndMaybeCleanup(hash);
+ };
+ decrementAndMaybeCleanup = hash => {
+ const next = (this.stylesCounter.get(hash) ?? 0) - 1;
+ if (next > 0) {
+ this.stylesCounter.set(hash, next);
+ return Promise.resolve(false);
}
- return Promise.resolve(false);
+ this.stylesCounter.delete(hash);
+ // Move this to the end of the event loop so the element is removed from the DOM
+ return Promise.resolve().then(() => {
+ // Check if element is still in the DOM
+ if ((this.stylesCounter.get(hash) ?? 0) > 0) {
+ return false;
+ }
+ if (document.querySelector(`.${hash}`)) {
+ return false;
+ }
+ this.css.remove(hash);
+ this.stylesCache.delete(hash);
+ return true;
+ });
};
add = (value, forChild) => {
const generatedHash = generateHash(value);
@@ -87,7 +120,10 @@ export class UnistylesRegistry {
this.css.add(hash, value);
};
reset = () => {
+ this.cleanupGeneration += 1;
+ this.disposeListenersMap.forEach(dispose => dispose());
this.css.reset();
+ this.stylesheets.clear();
this.stylesCache.clear();
this.dependenciesMap.clear();
this.disposeListenersMap.clear();
diff --git a/node_modules/react-native-unistyles/src/web/registry.ts b/node_modules/react-native-unistyles/src/web/registry.ts
index 39e0d9d..a21ba95 100644
--- a/node_modules/react-native-unistyles/src/web/registry.ts
+++ b/node_modules/react-native-unistyles/src/web/registry.ts
@@ -9,7 +9,24 @@ import { error, extractUnistyleDependencies, generateHash } from './utils'
export class UnistylesRegistry {
private readonly stylesheets = new Map<StyleSheetWithSuperPowers<StyleSheet>, StyleSheet>()
private readonly stylesCache = new Set<string>()
- private readonly stylesCounter = new Map<string, Set<HTMLElement>>()
+ // Use counter instead of Set<HTMLElement> to avoid retaining detached DOM nodes
+ // FR is a fallback when remove() is missed
+ private readonly stylesCounter = new Map<string, number>()
+ // Bumped on reset() so stale FR callbacks are ignored
+ private cleanupGeneration = 0
+ // Per-registration unregister token, so remove() cancels only its own registration
+ private readonly finalizerTokens = new WeakMap<HTMLElement, Map<string, Array<object>>>()
+ // FR isn't available on every runtime - fall back to counter-only cleanup
+ private readonly finalizationRegistry =
+ typeof FinalizationRegistry === 'undefined'
+ ? undefined
+ : new FinalizationRegistry<{ hash: string; generation: number }>(({ hash, generation }) => {
+ if (generation !== this.cleanupGeneration) {
+ return
+ }
+
+ this.decrementAndMaybeCleanup(hash)
+ })
private readonly disposeListenersMap = new Map<object, VoidFunction>()
private readonly dependenciesMap = new Map<StyleSheetWithSuperPowers<StyleSheet>, Set<UnistyleDependency>>()
readonly css: CSSState
@@ -78,33 +95,61 @@ export class UnistylesRegistry {
}
connect = (ref: HTMLElement, hash: string) => {
- const stylesCounter = this.stylesCounter.get(hash) ?? new Set()
+ this.stylesCounter.set(hash, (this.stylesCounter.get(hash) ?? 0) + 1)
- stylesCounter.add(ref)
- this.stylesCounter.set(hash, stylesCounter)
+ if (!this.finalizationRegistry) {
+ return
+ }
+
+ const token = {}
+ const tokensByHash = this.finalizerTokens.get(ref) ?? new Map<string, Array<object>>()
+ const tokens = tokensByHash.get(hash) ?? []
+
+ tokens.push(token)
+ tokensByHash.set(hash, tokens)
+ this.finalizerTokens.set(ref, tokensByHash)
+ this.finalizationRegistry.register(ref, { hash, generation: this.cleanupGeneration }, token)
}
remove = (ref: HTMLElement, hash: string) => {
- const stylesCounter = this.stylesCounter.get(hash) ?? new Set()
+ const token = this.finalizerTokens.get(ref)?.get(hash)?.pop()
- stylesCounter.delete(ref)
+ if (token) {
+ this.finalizationRegistry?.unregister(token)
+ }
- if (stylesCounter.size === 0) {
- // Move this to the end of the event loop so the element is removed from the DOM
- return Promise.resolve().then(() => {
- // Check if element is still in the DOM
- if (document.querySelector(`.${hash}`)) {
- return false
- }
+ return this.decrementAndMaybeCleanup(hash)
+ }
+
+ private decrementAndMaybeCleanup = (hash: string): Promise<boolean> => {
+ const next = (this.stylesCounter.get(hash) ?? 0) - 1
- this.css.remove(hash)
- this.stylesCache.delete(hash)
+ if (next > 0) {
+ this.stylesCounter.set(hash, next)
- return true
- })
+ return Promise.resolve(false)
}
- return Promise.resolve(false)
+ // Drop the empty bucket from the Map
+ this.stylesCounter.delete(hash)
+
+ // Move this to the end of the event loop so the element is removed from the DOM
+ return Promise.resolve().then(() => {
+ // New connect raced in during the microtask
+ if ((this.stylesCounter.get(hash) ?? 0) > 0) {
+ return false
+ }
+
+ // Check if element is still in the DOM
+ if (document.querySelector(`.${hash}`)) {
+ return false
+ }
+
+ this.css.remove(hash)
+ this.stylesCache.delete(hash)
+
+ return true
+ })
}
add = (value: UnistylesValues, forChild?: boolean) => {
@@ -126,7 +171,11 @@ export class UnistylesRegistry {
}
reset = () => {
+ // Invalidate pending FR callbacks from this generation
+ this.cleanupGeneration += 1
+ this.disposeListenersMap.forEach((dispose) => dispose())
this.css.reset()
+ this.stylesheets.clear()
this.stylesCache.clear()
this.dependenciesMap.clear()
this.disposeListenersMap.clear()
diff --git a/node_modules/react-native-unistyles/android/build.gradle b/node_modules/react-native-unistyles/android/build.gradle
index 195954b..c4ac28c 100644
--- a/node_modules/react-native-unistyles/android/build.gradle
+++ b/node_modules/react-native-unistyles/android/build.gradle
@@ -38,6 +38,7 @@ if (isNewArchitectureEnabled()) {
android {
compileSdkVersion safeExtGet('compileSdkVersion', 35)
+ ndkVersion safeExtGet('ndkVersion', '27.1.12297006')
namespace "com.unistyles"
defaultConfig {
diff --git a/node_modules/react-native-unistyles/src/core/withUnistyles/withUnistyles.tsx b/node_modules/react-native-unistyles/src/core/withUnistyles/withUnistyles.tsx
--- a/node_modules/react-native-unistyles/src/core/withUnistyles/withUnistyles.tsx
+++ b/node_modules/react-native-unistyles/src/core/withUnistyles/withUnistyles.tsx
@@ -28,13 +28,14 @@
return forwardRef<GenericComponentRef<TComponent>, PropsWithUnistyles>((props, ref) => {
const narrowedProps = props as PropsWithUnistyles & UnistyleStyles
+ const { uniProps, ...componentProps } = narrowedProps
const styleClassNames = getClassName(narrowedProps.style, true)
const contentContainerStyleClassNames = getClassName(narrowedProps.contentContainerStyle)
const { proxifiedRuntime, proxifiedTheme } = useProxifiedUnistyles()
const { key: mappingsKey, ...mappingsProps } = mappings ? mappings(proxifiedTheme, proxifiedRuntime) : {}
- const { key: uniPropsKey, ...unistyleProps } = narrowedProps.uniProps
- ? narrowedProps.uniProps(proxifiedTheme, proxifiedRuntime)
+ const { key: uniPropsKey, ...unistyleProps } = uniProps
+ ? uniProps(proxifiedTheme, proxifiedRuntime)
: {}
const emptyStyles = narrowedProps.style
@@ -46,7 +47,7 @@
: undefined
const combinedProps = {
- ...deepMergeObjects(mappingsProps, unistyleProps, props),
+ ...deepMergeObjects(mappingsProps, unistyleProps, componentProps),
...(narrowedProps.style
? {
// Override default component styles with undefined values to reset them
diff --git a/node_modules/react-native-unistyles/src/core/withUnistyles/withUnistyles.native.tsx b/node_modules/react-native-unistyles/src/core/withUnistyles/withUnistyles.native.tsx
--- a/node_modules/react-native-unistyles/src/core/withUnistyles/withUnistyles.native.tsx
+++ b/node_modules/react-native-unistyles/src/core/withUnistyles/withUnistyles.native.tsx
@@ -63,6 +63,7 @@
return forwardRef<GenericComponentRef<TComponent>, PropsWithUnistyles>((props, ref) => {
const narrowedProps = props as PropsWithUnistyles & UnistyleStyles
+ const { uniProps, ...componentProps } = narrowedProps
const NativeComponent = Component as ComponentType
// @ts-ignore we don't know the type of the component
@@ -89,15 +90,15 @@
}, [narrowedProps.style, narrowedProps.contentContainerStyle])
const { key: mappingsKey, ...mappingsProps } = mappings ? mappings(proxifiedTheme, proxifiedRuntime) : {}
- const { key: uniPropsKey, ...unistyleProps } = narrowedProps.uniProps
- ? narrowedProps.uniProps(proxifiedTheme, proxifiedRuntime)
+ const { key: uniPropsKey, ...unistyleProps } = uniProps
+ ? uniProps(proxifiedTheme, proxifiedRuntime)
: {}
const styleSecrets = getSecrets(narrowedProps.style)
const contentContainerStyleSecrets = getSecrets(narrowedProps.contentContainerStyle)
const finalProps = {
- ...deepMergeObjects(mappingsProps, unistyleProps, props),
+ ...deepMergeObjects(mappingsProps, unistyleProps, componentProps),
...(narrowedProps.style
? {
style: styleSecrets.styles,
diff --git a/node_modules/react-native-unistyles/lib/module/core/withUnistyles/withUnistyles.js b/node_modules/react-native-unistyles/lib/module/core/withUnistyles/withUnistyles.js
--- a/node_modules/react-native-unistyles/lib/module/core/withUnistyles/withUnistyles.js
+++ b/node_modules/react-native-unistyles/lib/module/core/withUnistyles/withUnistyles.js
@@ -13,6 +13,7 @@
export const withUnistyles = (Component, mappings) => {
return /*#__PURE__*/forwardRef((props, ref) => {
const narrowedProps = props;
+ const { uniProps, ...componentProps } = narrowedProps;
const styleClassNames = getClassName(narrowedProps.style, true);
const contentContainerStyleClassNames = getClassName(narrowedProps.contentContainerStyle);
const {
@@ -26,10 +27,10 @@
const {
key: uniPropsKey,
...unistyleProps
- } = narrowedProps.uniProps ? narrowedProps.uniProps(proxifiedTheme, proxifiedRuntime) : {};
+ } = uniProps ? uniProps(proxifiedTheme, proxifiedRuntime) : {};
const emptyStyles = narrowedProps.style ? Object.fromEntries(Object.entries(Object.getOwnPropertyDescriptors(narrowedProps.style)).filter(([key]) => !key.startsWith('unistyles') && !key.startsWith('_')).map(([key]) => [key, undefined])) : undefined;
const combinedProps = {
- ...deepMergeObjects(mappingsProps, unistyleProps, props),
+ ...deepMergeObjects(mappingsProps, unistyleProps, componentProps),
...(narrowedProps.style ? {
// Override default component styles with undefined values to reset them
style: emptyStyles
diff --git a/node_modules/react-native-unistyles/lib/module/core/withUnistyles/withUnistyles.native.js b/node_modules/react-native-unistyles/lib/module/core/withUnistyles/withUnistyles.native.js
--- a/node_modules/react-native-unistyles/lib/module/core/withUnistyles/withUnistyles.native.js
+++ b/node_modules/react-native-unistyles/lib/module/core/withUnistyles/withUnistyles.native.js
@@ -27,6 +27,7 @@
};
return /*#__PURE__*/forwardRef((props, ref) => {
const narrowedProps = props;
+ const { uniProps, ...componentProps } = narrowedProps;
const NativeComponent = Component;
// @ts-ignore we don't know the type of the component
@@ -57,11 +58,11 @@
const {
key: uniPropsKey,
...unistyleProps
- } = narrowedProps.uniProps ? narrowedProps.uniProps(proxifiedTheme, proxifiedRuntime) : {};
+ } = uniProps ? uniProps(proxifiedTheme, proxifiedRuntime) : {};
const styleSecrets = getSecrets(narrowedProps.style);
const contentContainerStyleSecrets = getSecrets(narrowedProps.contentContainerStyle);
const finalProps = {
- ...deepMergeObjects(mappingsProps, unistyleProps, props),
+ ...deepMergeObjects(mappingsProps, unistyleProps, componentProps),
...(narrowedProps.style ? {
style: styleSecrets.styles
} : {}),
diff --git a/node_modules/react-native-unistyles/lib/commonjs/core/withUnistyles/withUnistyles.js b/node_modules/react-native-unistyles/lib/commonjs/core/withUnistyles/withUnistyles.js
--- a/node_modules/react-native-unistyles/lib/commonjs/core/withUnistyles/withUnistyles.js
+++ b/node_modules/react-native-unistyles/lib/commonjs/core/withUnistyles/withUnistyles.js
@@ -18,6 +18,7 @@
const withUnistyles = (Component, mappings) => {
return /*#__PURE__*/(0, _react.forwardRef)((props, ref) => {
const narrowedProps = props;
+ const { uniProps, ...componentProps } = narrowedProps;
const styleClassNames = (0, _getClassname.getClassName)(narrowedProps.style, true);
const contentContainerStyleClassNames = (0, _getClassname.getClassName)(narrowedProps.contentContainerStyle);
const {
@@ -31,10 +32,10 @@
const {
key: uniPropsKey,
...unistyleProps
- } = narrowedProps.uniProps ? narrowedProps.uniProps(proxifiedTheme, proxifiedRuntime) : {};
+ } = uniProps ? uniProps(proxifiedTheme, proxifiedRuntime) : {};
const emptyStyles = narrowedProps.style ? Object.fromEntries(Object.entries(Object.getOwnPropertyDescriptors(narrowedProps.style)).filter(([key]) => !key.startsWith('unistyles') && !key.startsWith('_')).map(([key]) => [key, undefined])) : undefined;
const combinedProps = {
- ...(0, _utils.deepMergeObjects)(mappingsProps, unistyleProps, props),
+ ...(0, _utils.deepMergeObjects)(mappingsProps, unistyleProps, componentProps),
...(narrowedProps.style ? {
// Override default component styles with undefined values to reset them
style: emptyStyles
diff --git a/node_modules/react-native-unistyles/lib/commonjs/core/withUnistyles/withUnistyles.native.js b/node_modules/react-native-unistyles/lib/commonjs/core/withUnistyles/withUnistyles.native.js
--- a/node_modules/react-native-unistyles/lib/commonjs/core/withUnistyles/withUnistyles.native.js
+++ b/node_modules/react-native-unistyles/lib/commonjs/core/withUnistyles/withUnistyles.native.js
@@ -32,6 +32,7 @@
};
return /*#__PURE__*/(0, _react.forwardRef)((props, ref) => {
const narrowedProps = props;
+ const { uniProps, ...componentProps } = narrowedProps;
const NativeComponent = Component;
// @ts-ignore we don't know the type of the component
@@ -62,11 +63,11 @@
const {
key: uniPropsKey,
...unistyleProps
- } = narrowedProps.uniProps ? narrowedProps.uniProps(proxifiedTheme, proxifiedRuntime) : {};
+ } = uniProps ? uniProps(proxifiedTheme, proxifiedRuntime) : {};
const styleSecrets = getSecrets(narrowedProps.style);
const contentContainerStyleSecrets = getSecrets(narrowedProps.contentContainerStyle);
const finalProps = {
- ...(0, _utils.deepMergeObjects)(mappingsProps, unistyleProps, props),
+ ...(0, _utils.deepMergeObjects)(mappingsProps, unistyleProps, componentProps),
...(narrowedProps.style ? {
style: styleSecrets.styles
} : {}),
diff --git a/node_modules/react-native-unistyles/lib/commonjs/web/runtime.js b/node_modules/react-native-unistyles/lib/commonjs/web/runtime.js
index 305fa49..5957aa5 100644
--- a/node_modules/react-native-unistyles/lib/commonjs/web/runtime.js
+++ b/node_modules/react-native-unistyles/lib/commonjs/web/runtime.js
@@ -35,10 +35,17 @@ class UnistylesRuntime {
return this.darkMedia;
}
get colorScheme() {
+ // Read the scheme through fresh lists, never through lightMedia/darkMedia. Chromium defers
+ // MediaQueryList change events while the document is hidden and drops the pending event
+ // of any list whose `matches` was read before the document became visible again, which
+ // would silence the adaptive-theme listeners registered on those two lists.
+ if ((0, _utils.isServer)()) {
+ return _types.ColorScheme.Unspecified;
+ }
switch (true) {
- case this.getLightMedia()?.matches:
+ case window.matchMedia('(prefers-color-scheme: light)').matches:
return _types.ColorScheme.Light;
- case this.getDarkMedia()?.matches:
+ case window.matchMedia('(prefers-color-scheme: dark)').matches:
return _types.ColorScheme.Dark;
default:
return _types.ColorScheme.Unspecified;
diff --git a/node_modules/react-native-unistyles/lib/module/web/runtime.js b/node_modules/react-native-unistyles/lib/module/web/runtime.js
index 7f13a06..c487bee 100644
--- a/node_modules/react-native-unistyles/lib/module/web/runtime.js
+++ b/node_modules/react-native-unistyles/lib/module/web/runtime.js
@@ -31,10 +31,17 @@ export class UnistylesRuntime {
return this.darkMedia;
}
get colorScheme() {
+ // Read the scheme through fresh lists, never through lightMedia/darkMedia. Chromium defers
+ // MediaQueryList change events while the document is hidden and drops the pending event
+ // of any list whose `matches` was read before the document became visible again, which
+ // would silence the adaptive-theme listeners registered on those two lists.
+ if (isServer()) {
+ return ColorScheme.Unspecified;
+ }
switch (true) {
- case this.getLightMedia()?.matches:
+ case window.matchMedia('(prefers-color-scheme: light)').matches:
return ColorScheme.Light;
- case this.getDarkMedia()?.matches:
+ case window.matchMedia('(prefers-color-scheme: dark)').matches:
return ColorScheme.Dark;
default:
return ColorScheme.Unspecified;
diff --git a/node_modules/react-native-unistyles/src/web/runtime.ts b/node_modules/react-native-unistyles/src/web/runtime.ts
index a16dac0..6519858 100644
--- a/node_modules/react-native-unistyles/src/web/runtime.ts
+++ b/node_modules/react-native-unistyles/src/web/runtime.ts
@@ -41,8 +41,16 @@ export class UnistylesRuntime {
get colorScheme() {
+ // Read the scheme through fresh lists, never through lightMedia/darkMedia. Chromium defers
+ // MediaQueryList change events while the document is hidden and drops the pending event
+ // of any list whose `matches` was read before the document became visible again, which
+ // would silence the adaptive-theme listeners registered on those two lists.
+ if (isServer()) {
+ return ColorScheme.Unspecified
+ }
+
switch (true) {
- case this.getLightMedia()?.matches:
+ case window.matchMedia('(prefers-color-scheme: light)').matches:
return ColorScheme.Light
- case this.getDarkMedia()?.matches:
+ case window.matchMedia('(prefers-color-scheme: dark)').matches:
return ColorScheme.Dark
default:
return ColorScheme.Unspecified
diff --git a/node_modules/react-native-unistyles/android/src/main/java/com/unistyles/NativePlatform+android.kt b/node_modules/react-native-unistyles/android/src/main/java/com/unistyles/NativePlatform+android.kt
index 0f8d1f0..154a12a 100644
--- a/node_modules/react-native-unistyles/android/src/main/java/com/unistyles/NativePlatform+android.kt
+++ b/node_modules/react-native-unistyles/android/src/main/java/com/unistyles/NativePlatform+android.kt
@@ -3,11 +3,16 @@ package com.unistyles
import android.os.Build
import android.content.Context
import android.content.res.Configuration
+import android.content.res.Resources
+import android.os.Handler
+import android.os.Looper
import android.util.DisplayMetrics
import android.view.View
import android.view.WindowManager
import androidx.annotation.Keep
+import androidx.core.content.OnConfigurationChangedProvider
import androidx.core.text.TextUtilsCompat
+import androidx.core.util.Consumer
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
@@ -22,6 +27,7 @@ import com.margelo.nitro.unistyles.Insets
import com.margelo.nitro.unistyles.Orientation
import com.margelo.nitro.unistyles.UnistyleDependency
import com.margelo.nitro.unistyles.UnistylesNativeMiniRuntime
+import java.lang.ref.WeakReference
import java.util.Locale
@Keep
@@ -30,6 +36,14 @@ class NativePlatformAndroid(private val reactContext: ReactApplicationContext):
private val _insets = NativePlatformInsets(reactContext, this::getMiniRuntime) { this.onConfigChange() }
private var _miniRuntime: UnistylesNativeMiniRuntime = buildMiniRuntime()
private val _listener = NativePlatformListener(reactContext, this::getMiniRuntime) { this.diffMiniRuntime() }
+ private var _configurationProvider: WeakReference<OnConfigurationChangedProvider>? = null
+ private val _activityConfigurationListener = Consumer<Configuration> {
+ Handler(Looper.getMainLooper()).post { this.onConfigChange() }
+ }
+
+ // Application resources can lag behind configuration changes
+ private val configurationResources: Resources
+ get() = reactContext.currentActivity?.resources ?: reactContext.resources
init {
checkEdgeToEdge()
@@ -37,12 +51,16 @@ class NativePlatformAndroid(private val reactContext: ReactApplicationContext):
}
fun onDestroy() {
+ stopObservingActivityConfiguration()
_listener.onDestroy()
_insets.onDestroy()
reactContext.removeLifecycleEventListener(this)
}
override fun onHostResume() {
+ observeActivityConfiguration()
+ // catch configuration changes missed while no Activity was attached
+ onConfigChange()
_insets.getInitialInsets(false)
_insets.startInsetsListener()
}
@@ -51,7 +69,26 @@ class NativePlatformAndroid(private val reactContext: ReactApplicationContext):
_insets.stopInsetsListener()
}
- override fun onHostDestroy() {}
+ override fun onHostDestroy() {
+ stopObservingActivityConfiguration()
+ }
+
+ private fun observeActivityConfiguration() {
+ val provider = reactContext.currentActivity as? OnConfigurationChangedProvider
+
+ if (_configurationProvider?.get() === provider) {
+ return
+ }
+
+ stopObservingActivityConfiguration()
+ provider?.addOnConfigurationChangedListener(_activityConfigurationListener)
+ _configurationProvider = provider?.let { WeakReference(it) }
+ }
+
+ private fun stopObservingActivityConfiguration() {
+ _configurationProvider?.get()?.removeOnConfigurationChangedListener(_activityConfigurationListener)
+ _configurationProvider = null
+ }
override val memorySize: Long
get() = 0
@@ -61,7 +98,7 @@ class NativePlatformAndroid(private val reactContext: ReactApplicationContext):
}
override fun getColorScheme(): ColorScheme {
- val uiMode = reactContext.resources.configuration.uiMode
+ val uiMode = configurationResources.configuration.uiMode
val colorScheme = when (uiMode.and(Configuration.UI_MODE_NIGHT_MASK)) {
Configuration.UI_MODE_NIGHT_YES -> ColorScheme.DARK
@@ -73,15 +110,15 @@ class NativePlatformAndroid(private val reactContext: ReactApplicationContext):
}
override fun getFontScale(): Double {
- return reactContext.resources.configuration.fontScale.toDouble()
+ return configurationResources.configuration.fontScale.toDouble()
}
override fun getPixelRatio(): Double {
- return reactContext.resources.displayMetrics.density.toDouble()
+ return configurationResources.displayMetrics.density.toDouble()
}
override fun getOrientation(): Orientation {
- val orientation = when (reactContext.resources.configuration.orientation) {
+ val orientation = when (configurationResources.configuration.orientation) {
Configuration.ORIENTATION_PORTRAIT -> Orientation.PORTRAIT
Configuration.ORIENTATION_LANDSCAPE -> Orientation.LANDSCAPE
else -> Orientation.PORTRAIT
@@ -122,7 +159,7 @@ class NativePlatformAndroid(private val reactContext: ReactApplicationContext):
return Dimensions(screenWidth, screenHeight)
}
else -> {
- val displayMetrics = reactContext.resources.displayMetrics
+ val displayMetrics = configurationResources.displayMetrics
reactContext.currentActivity?.windowManager?.currentWindowMetrics?.bounds?.let {
val boundsWidth = (it.width() / displayMetrics.density).toDouble()
@@ -157,7 +194,7 @@ class NativePlatformAndroid(private val reactContext: ReactApplicationContext):
"com.facebook.react.modules.i18nmanager.I18nUtil",
Context.MODE_PRIVATE
)
- val resourcesLocale = reactContext.resources.configuration.locales[0]
+ val resourcesLocale = configurationResources.configuration.locales[0]
val hasForcedRtl = sharedPrefs.getBoolean("RCTI18nUtil_forceRTL", false)
// user preferences
val isRtl = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL
diff --git a/node_modules/react-native-unistyles/android/src/main/java/com/unistyles/NativePlatform+insets.kt b/node_modules/react-native-unistyles/android/src/main/java/com/unistyles/NativePlatform+insets.kt
index a9c1804..f5a3c20 100644
--- a/node_modules/react-native-unistyles/android/src/main/java/com/unistyles/NativePlatform+insets.kt
+++ b/node_modules/react-native-unistyles/android/src/main/java/com/unistyles/NativePlatform+insets.kt
@@ -38,7 +38,7 @@ class NativePlatformInsets(
}
fun getInsets(): Insets {
- val density = reactContext.resources.displayMetrics.density
+ val density = (reactContext.currentActivity?.resources ?: reactContext.resources).displayMetrics.density
return Insets(
this._insets.top / density,