-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy path_layout.tsx
More file actions
131 lines (121 loc) · 3.74 KB
/
_layout.tsx
File metadata and controls
131 lines (121 loc) · 3.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import "../global.css";
import { View, StyleSheet, Platform, KeyboardAvoidingView, AppState, Alert } from "react-native";
import { Stack } from "expo-router";
import * as Updates from "expo-updates";
import { AuthProvider } from "../auth/AuthContext";
import { TelegramProvider, useTelegram } from "../ui/components/Telegram";
import { GlobalLogoBarWithFallback } from "../ui/components/GlobalLogoBarWithFallback";
import { GlobalBottomBar } from "../ui/components/GlobalBottomBar";
import { useColors } from "../ui/theme";
import { useEffect, useRef } from "react";
/**
* Three-block column layout (same as Flutter):
* 1. Logo bar (optional in TMA when not fullscreen)
* 2. Main area (flex, scrollable per screen) – Stack updates on route change
* 3. AI & Search bar (fixed at bottom, platform-specific internals)
*/
export default function RootLayout() {
useOtaUpdateChecks();
return (
<TelegramProvider>
<AuthProvider>
{Platform.OS === "ios" ? (
<KeyboardAvoidingView
style={styles.keyboardAvoid}
behavior="padding"
keyboardVerticalOffset={0}
>
<RootContent />
</KeyboardAvoidingView>
) : (
<RootContent />
)}
</AuthProvider>
</TelegramProvider>
);
}
function useOtaUpdateChecks() {
const lastCheckAtRef = useRef(0);
useEffect(() => {
if (Platform.OS === "web") return;
const checkForOtaUpdate = async () => {
const now = Date.now();
// Throttle checks to avoid noisy network calls while app toggles foreground quickly.
if (now - lastCheckAtRef.current < 10 * 60 * 1000) return;
lastCheckAtRef.current = now;
try {
const result = await Updates.checkForUpdateAsync();
if (!result.isAvailable) return;
await Updates.fetchUpdateAsync();
Alert.alert(
"Update ready",
"A new version has been downloaded. Restart now to apply it?",
[
{ text: "Later", style: "cancel" },
{
text: "Restart",
onPress: () => {
void Updates.reloadAsync();
},
},
],
);
} catch (error) {
console.warn("[updates] OTA check failed", error);
}
};
void checkForOtaUpdate();
const sub = AppState.addEventListener("change", (nextState) => {
if (nextState === "active") {
void checkForOtaUpdate();
}
});
return () => sub.remove();
}, []);
}
function RootContent() {
const colors = useColors();
const { themeBgReady, useTelegramTheme } = useTelegram();
const backgroundColor = themeBgReady ? colors.background : "transparent";
// Stronger than opacity:0 — avoids one frame of dark RN-web compositing before themeBgReady.
const hideWebUntilTheme =
Platform.OS === "web" && useTelegramTheme && !themeBgReady;
return (
<View
style={[
styles.root,
{
backgroundColor,
opacity: themeBgReady ? 1 : 0,
pointerEvents: themeBgReady ? "auto" : "none",
...(Platform.OS === "web"
? { display: hideWebUntilTheme ? "none" : "flex" }
: {}),
},
]}
>
<GlobalLogoBarWithFallback />
<View style={styles.main}>
<Stack screenOptions={{ headerShown: false }} />
</View>
{
// Avoid mounting web internals before theme — kills dark flash from RN-web inputs.
Platform.OS !== "web" || !useTelegramTheme || themeBgReady ? <GlobalBottomBar /> : null
}
</View>
);
}
const styles = StyleSheet.create({
keyboardAvoid: {
flex: 1,
},
root: {
flex: 1,
flexDirection: "column",
overflow: "hidden",
},
main: {
flex: 1,
minHeight: 0,
},
});