@proulr/sdui-components 0.1.79 → 0.1.81

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proulr/sdui-components",
3
- "version": "0.1.79",
3
+ "version": "0.1.81",
4
4
  "type": "module",
5
5
  "main": "./src/renderer/index.ts",
6
6
  "types": "./src/renderer/index.ts",
@@ -1,5 +1,5 @@
1
- import React, { useEffect, useState } from 'react';
2
- import { StyleSheet, Text, View } from 'react-native';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import { AppState, type AppStateStatus, StyleSheet, Text, View } from 'react-native';
3
3
  import { LinearGradient } from 'expo-linear-gradient';
4
4
  import {
5
5
  DEFAULT_NATIVE_AD_ASPECT_RATIO,
@@ -12,6 +12,13 @@ import type { SduiBlockRenderProps } from './SduiPrimitiveBlocks';
12
12
  type NativeAdModule = typeof import('react-native-google-mobile-ads');
13
13
  type LoadedNativeAd = Awaited<ReturnType<NativeAdModule['NativeAd']['createForAdRequest']>>;
14
14
 
15
+ /**
16
+ * Ao voltar do segundo plano o `NativeMediaView` do Android costuma renderizar
17
+ * em branco e o ad expira (~1h). Recarrega quando falhou antes ou está mais
18
+ * velho que isto; abaixo do limiar são só app-switches rápidos.
19
+ */
20
+ const NATIVE_AD_RESUME_RELOAD_MS = 30_000;
21
+
15
22
  function resolveBlockWidth(props: Record<string, unknown>): number | undefined {
16
23
  if (typeof props.width === 'number' && props.width > 0) return props.width;
17
24
  return undefined;
@@ -42,6 +49,10 @@ export function SduiNativeAdBlock({ block }: SduiBlockRenderProps) {
42
49
  const [adsModule, setAdsModule] = useState<NativeAdModule | null>(null);
43
50
  const [nativeAd, setNativeAd] = useState<LoadedNativeAd | null>(null);
44
51
  const [failed, setFailed] = useState(false);
52
+ const [reloadToken, setReloadToken] = useState(0);
53
+
54
+ const loadedAtRef = useRef(0);
55
+ const failedRef = useRef(false);
45
56
 
46
57
  useEffect(() => {
47
58
  if (!catalog?.isEnabled()) return;
@@ -66,16 +77,22 @@ export function SduiNativeAdBlock({ block }: SduiBlockRenderProps) {
66
77
  return;
67
78
  }
68
79
 
80
+ loadedAtRef.current = Date.now();
81
+ failedRef.current = false;
82
+ setFailed(false);
69
83
  setNativeAd(ad);
70
84
  } catch {
71
- if (!cancelled) setFailed(true);
85
+ if (!cancelled) {
86
+ failedRef.current = true;
87
+ setFailed(true);
88
+ }
72
89
  }
73
90
  })();
74
91
 
75
92
  return () => {
76
93
  cancelled = true;
77
94
  };
78
- }, [catalog, placement]);
95
+ }, [catalog, placement, reloadToken]);
79
96
 
80
97
  useEffect(() => {
81
98
  if (!nativeAd) return;
@@ -84,6 +101,35 @@ export function SduiNativeAdBlock({ block }: SduiBlockRenderProps) {
84
101
  };
85
102
  }, [nativeAd]);
86
103
 
104
+ useEffect(() => {
105
+ if (!catalog?.isEnabled()) return;
106
+
107
+ let backgroundSince: number | null = null;
108
+
109
+ const subscription = AppState.addEventListener('change', (state: AppStateStatus) => {
110
+ if (state === 'active') {
111
+ if (backgroundSince === null) return;
112
+ const backgroundMs = Date.now() - backgroundSince;
113
+ backgroundSince = null;
114
+ if (backgroundMs < 3_000) return;
115
+ const stale =
116
+ loadedAtRef.current === 0 ||
117
+ Date.now() - loadedAtRef.current > NATIVE_AD_RESUME_RELOAD_MS;
118
+ if (failedRef.current || stale) {
119
+ setReloadToken((token) => token + 1);
120
+ }
121
+ return;
122
+ }
123
+ if (backgroundSince === null) {
124
+ backgroundSince = Date.now();
125
+ }
126
+ });
127
+
128
+ return () => {
129
+ subscription.remove();
130
+ };
131
+ }, [catalog]);
132
+
87
133
  if (!catalog?.isEnabled()) {
88
134
  return null;
89
135
  }
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  /** Intensidade do frost — forte (estilo Telegram) p/ conteúdo não competir com o texto. */
7
- export const CHROME_GLASS_BLUR_INTENSITY = 60;
7
+ export const CHROME_GLASS_BLUR_INTENSITY = 50;
8
8
 
9
9
  /**
10
10
  * Tint limpo (sem material cinza do system material).
@@ -108,11 +108,12 @@ export const colors = {
108
108
  surfaceGlass: 'rgba(255,255,255,0.06)',
109
109
  /** Pílula / chrome sem BlurView — preto OLED. */
110
110
  chromePillBackground: 'rgba(0, 0, 0, 0.82)',
111
- /** Overlay sobre BlurView — véu escuro no tom da marca (como o azul-escuro do
112
- * Telegram): escurece o fundo p/ texto branco ler bem, mantendo o hue fuchsia. */
113
- chromeGlassOverlay: 'rgba(10, 3, 15, 0.52)',
114
- /** Véu escuro sobre glass de painéis grandes (drawer) — legibilidade do conteúdo. */
115
- chromeGlassPanelVeil: 'rgba(20, 6, 25, 0.52)',
111
+ /** Overlay sobre BlurView — véu quase preto puxando pro fundo OLED (estilo
112
+ * Telegram): vidro escuro, com só um sussurro de fuchsia no hue. Opacidade
113
+ * alta escurece o conteúdo borrado sem matar o frost. */
114
+ chromeGlassOverlay: 'rgba(7, 4, 10, 0.64)',
115
+ /** Véu quase preto sobre glass de painéis grandes (drawer) — legibilidade máxima. */
116
+ chromeGlassPanelVeil: 'rgba(6, 3, 9, 0.7)',
116
117
  chromePillBorder: 'rgba(255, 255, 255, 0.08)',
117
118
  videoControlBackground: 'rgba(0,0,0,0.45)',
118
119
  videoTrackBackground: 'rgba(255,255,255,0.25)',