Skip to content

Commit e8960ee

Browse files
committed
feat: update alert
1 parent f31a7e4 commit e8960ee

File tree

16 files changed

+248
-23
lines changed

16 files changed

+248
-23
lines changed

app/_layout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { StyleSheet } from 'react-native';
1414
import { GestureHandlerRootView } from 'react-native-gesture-handler';
1515

1616
import SandboxLabel from '@/components/shared/sandbox-label';
17+
import UpdateAlert from '@/components/shared/update-alert';
1718
import SentryAppWrap from '@/config/error/sentry.config';
1819
import { queryClient } from '@/config/query.config';
1920
import { useSplashScreen } from '@/hooks/useSplashScreen';
@@ -45,6 +46,7 @@ function RootLayout() {
4546
<GestureHandlerRootView style={styles.gestureHandler}>
4647
<BottomSheetModalProvider>
4748
<SandboxLabel />
49+
<UpdateAlert />
4850
<App />
4951
<FlashMessage position="bottom" />
5052
</BottomSheetModalProvider>

bun.lockb

-1.99 KB
Binary file not shown.

components/shared/challenge-list/challenge-attempts.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ const ChallengeAttempts = ({ goBack, challengeId }: Props) => {
4242

4343
const footer = useCallback(() => {
4444
return (
45-
<Button variant="text" onPress={goBack}>
46-
Volver
45+
<Button variant="text" onPress={goBack} translate>
46+
back
4747
</Button>
4848
);
4949
}, [goBack]);

components/shared/challenge-list/challenge-info.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,11 @@ const ChallengeInfo = ({ challengeId, viewAttempts }: Props) => {
6161
<Text>{data.description}</Text>
6262
</View>
6363
<View style={styles.footer}>
64-
<Button mode="gradient" onPress={tryAgain}>
65-
Realizar nuevamente
64+
<Button mode="gradient" onPress={tryAgain} translate>
65+
tryAgain
6666
</Button>
67-
<Button variant="text" onPress={viewAttempts}>
68-
Ver intentos
67+
<Button variant="text" onPress={viewAttempts} translate>
68+
seeAttempts
6969
</Button>
7070
</View>
7171
</BottomSheetView>

components/shared/header-with-back.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Feather } from '@expo/vector-icons';
22
import { router } from 'expo-router';
3-
import { View } from 'react-native';
3+
import { Pressable, View } from 'react-native';
44
import { createStyleSheet, useStyles } from 'react-native-unistyles';
55

66
import Text from '../ui/text';
@@ -18,9 +18,9 @@ const HeaderWithBack = ({ onBack, title }: HeaderWithBackProps) => {
1818
};
1919
return (
2020
<View style={styles.container}>
21-
<View style={styles.iconContainer}>
22-
<Feather name="arrow-left" size={24} color="white" onPress={goBack} />
23-
</View>
21+
<Pressable style={styles.iconContainer} onPress={goBack}>
22+
<Feather name="arrow-left" size={24} color="white" />
23+
</Pressable>
2424
<View style={styles.iconContainer}>
2525
<Text weight="700" size={20} align="center" translate>
2626
{title}
@@ -44,6 +44,7 @@ const stylesheet = createStyleSheet((theme, runtime) => ({
4444
},
4545
iconContainer: {
4646
flex: 1,
47+
zIndex: 9999,
4748
},
4849
titleText: {
4950
fontWeight: 'bold',

components/shared/update-alert.tsx

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { MotiView, View } from 'moti';
2+
import React from 'react';
3+
import Animated, { FadeIn } from 'react-native-reanimated';
4+
import { createStyleSheet, useStyles } from 'react-native-unistyles';
5+
6+
import AstronautWaving from '@/animations/components/astronaut-waving';
7+
import { useExpoUpdates } from '@/config/expo-updates.config';
8+
import useFakeProgress from '@/hooks/useFakeProgress';
9+
import { hex2rgba } from '@/utils/ui.utils';
10+
11+
import Button from '../ui/button';
12+
import ProgressBar from '../ui/progress-bar';
13+
import Text from '../ui/text';
14+
15+
const UpdateAlert = () => {
16+
const { styles } = useStyles(stylesheet);
17+
const { updatesAvailables, applyUpdate } = useExpoUpdates();
18+
const { progress, isFinished } = useFakeProgress(
19+
2000,
20+
2000,
21+
!!updatesAvailables,
22+
);
23+
if (!updatesAvailables) return;
24+
return (
25+
<Animated.View style={styles.container} entering={FadeIn.duration(2000)}>
26+
<MotiView
27+
style={styles.animationContainer}
28+
from={{
29+
translateX: -200,
30+
opacity: 0,
31+
}}
32+
animate={{
33+
translateX: 0,
34+
opacity: 1,
35+
}}
36+
transition={{
37+
type: 'timing',
38+
duration: 1000,
39+
delay: 1000,
40+
}}
41+
>
42+
<AstronautWaving />
43+
</MotiView>
44+
<MotiView
45+
from={{ opacity: 0, scale: 0.5 }}
46+
animate={{ opacity: 1, scale: 1 }}
47+
transition={{ type: 'timing', duration: 1000, delay: 1000 }}
48+
>
49+
<View>
50+
<Text weight="800" align="center" size={35} translate>
51+
update.title
52+
</Text>
53+
<Text align="center" translate>
54+
update.description
55+
</Text>
56+
</View>
57+
<View style={styles.progress}>
58+
<ProgressBar progress={progress} />
59+
{isFinished && (
60+
<Animated.View entering={FadeIn}>
61+
<Button onPress={applyUpdate} translate>
62+
update.cta
63+
</Button>
64+
</Animated.View>
65+
)}
66+
</View>
67+
</MotiView>
68+
</Animated.View>
69+
);
70+
};
71+
72+
export default UpdateAlert;
73+
74+
const stylesheet = createStyleSheet(() => ({
75+
container: {
76+
backgroundColor: hex2rgba('#000000', 0.9),
77+
position: 'absolute',
78+
zIndex: 9999,
79+
height: '100%',
80+
width: '100%',
81+
},
82+
animationContainer: {
83+
height: 140,
84+
width: 140,
85+
alignSelf: 'center',
86+
marginTop: '50%',
87+
},
88+
progress: {
89+
paddingHorizontal: 24,
90+
},
91+
}));

components/ui/progress-bar.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { View } from 'react-native';
2+
import { createStyleSheet, useStyles } from 'react-native-unistyles';
3+
4+
import Text from './text';
5+
6+
interface Props {
7+
progress: number;
8+
}
9+
const ProgressBar = ({ progress }: Props) => {
10+
const { styles } = useStyles(stylesheet);
11+
12+
return (
13+
<View style={styles.container}>
14+
<View style={styles.progressBarBackground}>
15+
<View
16+
style={[
17+
styles.progressBar,
18+
{
19+
width: `${progress * 100}%`,
20+
},
21+
]}
22+
/>
23+
</View>
24+
<Text weight="800" align="right" style={styles.label}>
25+
{Math.round(progress * 100)}%
26+
</Text>
27+
</View>
28+
);
29+
};
30+
31+
export default ProgressBar;
32+
33+
const stylesheet = createStyleSheet((theme) => ({
34+
container: {
35+
marginVertical: 16,
36+
gap: 5,
37+
},
38+
label: {
39+
marginBottom: 8,
40+
fontSize: 16,
41+
fontWeight: 'bold',
42+
color: theme.colors.button.primaryBg,
43+
},
44+
progressBarBackground: {
45+
width: '100%',
46+
height: 10,
47+
backgroundColor: theme.colors.button.secondaryBg,
48+
borderRadius: 5,
49+
overflow: 'hidden',
50+
},
51+
progressBar: {
52+
height: '100%',
53+
backgroundColor: theme.colors.button.primaryBg,
54+
},
55+
}));

config/expo-updates.config.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,26 @@ export const useExpoUpdates = () => {
1414
try {
1515
const update = await Updates.checkForUpdateAsync();
1616
setUpdates(update.isAvailable, update.manifest?.id);
17-
if (update.isAvailable) {
18-
await Updates.fetchUpdateAsync();
19-
await Updates.reloadAsync();
20-
}
2117
} catch (error) {
2218
console.error(`Error fetching latest Expo update: ${error}`);
2319
}
2420
}
2521

22+
const applyUpdate = async () => {
23+
try {
24+
await Updates.fetchUpdateAsync();
25+
await Updates.reloadAsync();
26+
} catch (error) {
27+
console.error(`Error fetching latest Expo update: ${error}`);
28+
}
29+
};
30+
2631
useEffect(() => {
2732
onFetchUpdateAsync();
2833
}, [appState]);
2934

3035
return {
31-
isLoading: updatesAvailables,
36+
updatesAvailables,
37+
applyUpdate,
3238
};
3339
};

hooks/useFakeProgress.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { useEffect, useState } from 'react';
2+
3+
const useFakeProgress = (time = 1500, delay = 0, start = true) => {
4+
const [progress, setProgress] = useState(0);
5+
const [isFinished, setIsFinished] = useState(false);
6+
7+
useEffect(() => {
8+
if (!start) return;
9+
setIsFinished(false);
10+
const startProgress = () => {
11+
let startTime = Date.now();
12+
const interval = setInterval(() => {
13+
const elapsed = Date.now() - startTime;
14+
const newProgress = Math.min(elapsed / time, 1);
15+
setProgress(newProgress);
16+
17+
if (newProgress === 1) {
18+
clearInterval(interval);
19+
setIsFinished(true);
20+
}
21+
}, 16);
22+
return () => clearInterval(interval);
23+
};
24+
25+
const timeout = setTimeout(startProgress, delay);
26+
27+
return () => {
28+
clearTimeout(timeout);
29+
setProgress(0);
30+
setIsFinished(false);
31+
};
32+
}, [time, delay, start]);
33+
34+
return { progress, isFinished };
35+
};
36+
37+
export default useFakeProgress;

hooks/useInit.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { useSentryRouter } from '@/config/error/useSentryRouter';
2-
import { useExpoUpdates } from '@/config/expo-updates.config';
32
import { useUserProfile } from '@/data/fetchers/auth.fetcher';
43
import { useSetUserLang } from '@/language/useSetUserLang';
54

@@ -9,7 +8,6 @@ import { useTrackScreens } from './useScreenChange';
98
export const useInit = () => {
109
const { isLoading: isFontsLoading } = useLoadFonts();
1110
useSentryRouter();
12-
useExpoUpdates();
1311
useTrackScreens();
1412
useSetUserLang();
1513
const { isLoading: isUserProfileLoading } = useUserProfile();

0 commit comments

Comments
 (0)