Skip to content

Commit 4f2e871

Browse files
committed
Complete breathing routine
1 parent 0d0e04a commit 4f2e871

16 files changed

+266
-213
lines changed

.eslintrc

+7
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@
2323
"skip": ["Strong", "Bold"]
2424
}
2525
],
26+
"@typescript-eslint/no-unused-vars": [
27+
"error",
28+
{
29+
"varsIgnorePattern": "^_",
30+
"args": "after-used"
31+
}
32+
],
2633
"@typescript-eslint/consistent-type-imports": [
2734
"error",
2835
{

package-lock.json

+21
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"@react-navigation/native-stack": "^6.9.17",
2222
"@react-navigation/stack": "^6.3.20",
2323
"@shopify/react-native-skia": "0.1.196",
24+
"date-fns": "^3.2.0",
2425
"expo": "^49.0.21",
2526
"expo-blur": "~12.4.1",
2627
"expo-constants": "~14.4.2",
@@ -40,6 +41,7 @@
4041
},
4142
"devDependencies": {
4243
"@babel/core": "^7.20.0",
44+
"@types/date-fns": "^2.6.0",
4345
"@types/react": "~18.2.14",
4446
"@typescript-eslint/eslint-plugin": "^6.18.1",
4547
"@typescript-eslint/parser": "^6.18.1",

src/components/BackButton.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,25 @@ export type BackButtonProps = {
1919
tint?: BlurTint;
2020
style?: StyleProp<ViewStyle>;
2121
iconOnly?: boolean;
22+
onPress?(): void;
2223
};
2324

2425
export function BackButton({
2526
style,
2627
intensity,
2728
tint = "dark",
2829
iconOnly,
30+
onPress,
2931
}: BackButtonProps) {
3032
const navigation = useNavigation();
3133

3234
return (
3335
<Pressable
3436
style={[styles.cross, iconOnly && styles.crossIconOnly, style]}
3537
onPress={() => {
36-
if (navigation.canGoBack()) {
38+
if (onPress) {
39+
onPress();
40+
} else if (navigation.canGoBack()) {
3741
navigation.goBack();
3842
}
3943
}}

src/components/Breather/BreatherSlice.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,15 @@ export function BreatherSlice({ layout, index, animate }: BreatherSliceProps) {
3535
duration: 3000,
3636
easing: Easing.in(Easing.elastic(0.2)),
3737
}),
38-
10,
38+
-1,
3939
true
4040
)
4141
);
42+
} else {
43+
scale.value = withTiming(1, {
44+
duration: 1000,
45+
easing: Easing.in(Easing.elastic(0.2)),
46+
});
4247
}
4348
}, [animate, index]);
4449

src/router/routes.ts

-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ export enum Routes {
22
HOME = "home",
33
COLORS = "colors",
44
BREATHE = "breathe",
5-
BREATHE_COUNTDOWN = "breathe-countdown",
6-
BREATHE_TIMER = "breathe-timer",
7-
BREATHE_SUCCESS = "breathe-success",
85
}
96

107
export type RootStackParamsList = {
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export enum BreatheState {
2+
COUNTDOWN,
3+
TIMER,
4+
SUCCESS,
5+
}

src/screens/Breathe/Breathe.tsx

+57-127
Original file line numberDiff line numberDiff line change
@@ -1,147 +1,77 @@
1-
import React, { useEffect, useState } from "react";
1+
import React, { useState } from "react";
22

33
import { StyleSheet, View } from "react-native";
44

5-
import Animated, {
6-
FadeIn,
7-
FadeInUp,
8-
FadeOut,
9-
useAnimatedStyle,
10-
useSharedValue,
11-
withRepeat,
12-
withTiming,
13-
} from "react-native-reanimated";
5+
import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
146

15-
import { Feather } from "@expo/vector-icons";
7+
import { BreatheState } from "./Breathe.interface";
8+
import { BreatheCountdown } from "./BreatheCountdown";
9+
import { BreatheSuccess } from "./BreatheSuccess";
10+
import BreatheTimer from "./BreatheTimer";
11+
import { commonStyles } from "./common.styles";
12+
import { BreatheProvider } from "./context/BreatheProvider";
1613

1714
import { useColor } from "~/color";
18-
import { BackButton } from "~/components/BackButton";
19-
2015
import { Box } from "~/components/Box";
2116
import { Breather } from "~/components/Breather";
22-
import { Bold } from "~/components/Text";
23-
import { Palette } from "~/styles";
17+
import { Spacer } from "~/components/Spacer";
2418

2519
export function Breathe() {
2620
const { color } = useColor();
27-
const [counter, setCounter] = useState(5);
28-
const [timer, setTimer] = useState(30);
29-
const [animate, setAnimate] = useState(false);
30-
31-
const countdown = useSharedValue<number>(0);
32-
33-
useEffect(() => {
34-
const interval = setInterval(() => {
35-
setCounter((prev) => {
36-
if (prev) {
37-
return prev - 1;
38-
} else {
39-
clearInterval(interval);
40-
setAnimate(true);
41-
}
42-
43-
return prev;
44-
});
45-
}, 1000);
46-
47-
return () => {
48-
clearInterval(interval);
49-
};
50-
}, []);
51-
52-
useEffect(() => {
53-
let interval: NodeJS.Timeout;
21+
const store = useState(BreatheState.COUNTDOWN);
22+
const [timer, setTimer] = useState(0);
5423

55-
if (!counter) {
56-
interval = setInterval(() => {
57-
setTimer((prev) => {
58-
if (prev) {
59-
return prev - 1;
60-
} else {
61-
clearInterval(interval);
62-
}
63-
64-
return prev;
65-
});
66-
}, 1000);
67-
}
68-
69-
return () => {
70-
clearInterval(interval);
71-
};
72-
}, [counter]);
73-
74-
useEffect(() => {
75-
countdown.value = withRepeat(
76-
withTiming(1, {
77-
duration: 500,
78-
}),
79-
10,
80-
true
81-
);
82-
}, []);
24+
const [state] = store;
8325

8426
return (
85-
<Box
86-
style={[
87-
styles.wrapper,
88-
{
89-
backgroundColor: color,
90-
},
91-
]}
92-
>
93-
<Breather animate={animate} />
94-
<View style={styles.content}>
95-
{/* {!!counter && (
96-
<Animated.Text
97-
entering={FadeIn}
98-
layout={FadeInUp}
99-
exiting={FadeOut}
100-
>
101-
<Bold style={styles.countdown}>{counter}</Bold>
102-
</Animated.Text>
103-
)}
104-
{!!timer && !counter && (
105-
<Animated.Text
106-
entering={FadeIn}
107-
layout={FadeInUp}
108-
exiting={FadeOut}
109-
>
110-
<Bold style={styles.countdown}>{timer}</Bold>
111-
</Animated.Text>
112-
)}
113-
{!timer && !countdown && ( */}
114-
<Animated.View entering={FadeInUp}></Animated.View>
115-
{/* )} */}
116-
</View>
117-
<View style={styles.infoBox}>
118-
<BackButton />
119-
</View>
120-
</Box>
27+
<BreatheProvider value={store}>
28+
<Box
29+
style={[
30+
commonStyles.wrapper,
31+
{
32+
backgroundColor: color,
33+
},
34+
]}
35+
>
36+
<Breather animate={state === BreatheState.TIMER} />
37+
<View style={[commonStyles.content, styles.container]}>
38+
<Spacer header />
39+
{state === BreatheState.COUNTDOWN && (
40+
<Animated.View
41+
exiting={FadeOut.duration(500)}
42+
style={commonStyles.content}
43+
>
44+
<BreatheCountdown />
45+
</Animated.View>
46+
)}
47+
48+
{state === BreatheState.TIMER && (
49+
<Animated.View
50+
entering={FadeIn.delay(500).duration(500)}
51+
exiting={FadeOut.duration(500)}
52+
style={commonStyles.content}
53+
>
54+
<BreatheTimer setTimer={setTimer} />
55+
</Animated.View>
56+
)}
57+
58+
{state === BreatheState.SUCCESS && (
59+
<Animated.View
60+
entering={FadeIn.delay(500).duration(500)}
61+
exiting={FadeOut.duration(1000)}
62+
style={commonStyles.content}
63+
>
64+
<BreatheSuccess time={timer} />
65+
</Animated.View>
66+
)}
67+
</View>
68+
</Box>
69+
</BreatheProvider>
12170
);
12271
}
12372

12473
const styles = StyleSheet.create({
125-
content: {
126-
...StyleSheet.absoluteFillObject,
127-
alignItems: "center",
128-
justifyContent: "center",
129-
},
130-
countdown: {
131-
color: Palette.FOREGROUND,
132-
fontSize: 72,
133-
},
134-
infoBox: {
135-
alignItems: "center",
136-
bottom: 50,
137-
gap: 10,
138-
justifyContent: "center",
139-
left: 0,
140-
position: "absolute",
141-
right: 0,
142-
zIndex: 999,
143-
},
144-
wrapper: {
145-
position: "relative",
74+
container: {
75+
marginHorizontal: 20,
14676
},
14777
});

src/screens/Breathe/Breathe.utils.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { intervalToDuration } from "date-fns";
2+
3+
export function formatSecondsToCounter(time: number) {
4+
const intervals = intervalToDuration({
5+
start: 0,
6+
end: time * 1000,
7+
});
8+
9+
const minutes = String(intervals.minutes || 0).padStart(2, "0");
10+
const seconds = String(intervals.seconds || 0).padStart(2, "0");
11+
12+
return `${minutes}:${seconds}`;
13+
}

0 commit comments

Comments
 (0)