Skip to content

Commit d07f716

Browse files
committed
feat: 🎸 add reanimated option
Closes: #265
1 parent 4b51382 commit d07f716

File tree

7 files changed

+114
-16543
lines changed

7 files changed

+114
-16543
lines changed

Diff for: challenges/data/02.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,11 @@ If you want to display some images on your `Card`, you can use this placeholder
142142

143143
### Advanced
144144

145-
- [ ] Display a nice animated placeholder during loading with [rn-placeholder](https://github.com/mfrachet/rn-placeholder)
145+
- [ ] Display a nice animated placeholder during loading by choosing one of these approaches:
146+
- Option 1: Use [rn-placeholder](https://github.com/mfrachet/rn-placeholder) for quick implementation
147+
- ⚠️ If using npm, install with: `npm install rn-placeholder --legacy-peer-deps` (required due to outdated peer dependencies)
148+
- Option 2: Use [react-native-reanimated](https://docs.swmansion.com/react-native-reanimated/) to create your own animated placeholder, similar to our [`SkeletonListReanimated`](https://raw.githubusercontent.com/flexbox/react-native-bootcamp/main/hackathon/spacecraft/src/components/SkeletonListReanimated.tsx) component
149+
146150
- [ ] Add a "Pull to Refresh" functionality to your FlatList with `onRefresh`
147151

148152
### Debugging

Diff for: hackathon/spacecraft/src/components/SkeletonList.stories.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export default {
55
};
66

77
export const _SkeletonList = () => {
8-
return <SkeletonList />;
8+
return <SkeletonList numberItems={10} />;
99
};
1010

1111
export const _SkeletonListSingle = () => {

Diff for: hackathon/spacecraft/src/components/SkeletonList.tsx

+15-63
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { StyleSheet, View } from "react-native";
2-
import Animated, {
3-
useAnimatedStyle,
4-
withRepeat,
5-
withSequence,
6-
withTiming,
7-
} from "react-native-reanimated";
1+
import { View } from "react-native";
2+
import {
3+
Fade,
4+
Placeholder,
5+
PlaceholderLine,
6+
PlaceholderMedia,
7+
} from "rn-placeholder";
88

99
interface SkeletonListProps {
1010
numberItems?: number;
@@ -13,67 +13,19 @@ interface SkeletonListProps {
1313
export const SkeletonList = ({ numberItems = 10 }: SkeletonListProps) => {
1414
const items = [];
1515

16-
const animatedStyle = useAnimatedStyle(() => {
17-
return {
18-
opacity: withRepeat(
19-
withSequence(
20-
withTiming(0.3, { duration: 800 }),
21-
withTiming(0.7, { duration: 800 }),
22-
),
23-
-1,
24-
true,
25-
),
26-
};
27-
});
28-
2916
for (let i = 0; i < numberItems; i++) {
3017
items.push(
31-
<View
18+
<Placeholder
19+
Animation={Fade}
3220
key={`skeleton-${i}`}
33-
style={styles.itemContainer}
21+
Left={PlaceholderMedia}
22+
style={{ marginBottom: 12 }}
3423
>
35-
<Animated.View style={[styles.media, animatedStyle]} />
36-
<View style={styles.content}>
37-
<Animated.View
38-
style={[styles.line, styles.longLine, animatedStyle]}
39-
/>
40-
<Animated.View
41-
style={[styles.line, styles.shortLine, animatedStyle]}
42-
/>
43-
</View>
44-
</View>,
24+
<PlaceholderLine width={90} />
25+
<PlaceholderLine width={80} />
26+
</Placeholder>,
4527
);
4628
}
4729

4830
return <View>{items}</View>;
49-
};
50-
51-
const styles = StyleSheet.create({
52-
content: {
53-
flex: 1,
54-
},
55-
itemContainer: {
56-
alignItems: "center",
57-
flexDirection: "row",
58-
marginBottom: 12,
59-
},
60-
line: {
61-
backgroundColor: "#e1e1e1",
62-
borderRadius: 4,
63-
height: 12,
64-
marginVertical: 4,
65-
},
66-
longLine: {
67-
width: "90%",
68-
},
69-
media: {
70-
backgroundColor: "#e1e1e1",
71-
borderRadius: 20,
72-
height: 40,
73-
marginRight: 12,
74-
width: 40,
75-
},
76-
shortLine: {
77-
width: "80%",
78-
},
79-
});
31+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { SkeletonListReanimated } from "@/components/SkeletonListReanimated";
2+
3+
export default {
4+
title: "SkeletonListReanimated",
5+
};
6+
7+
export const _SkeletonListReanimated = () => {
8+
return <SkeletonListReanimated numberItems={10} />;
9+
};
10+
11+
export const _SkeletonListReanimatedSingle = () => {
12+
return <SkeletonListReanimated numberItems={1} />;
13+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { StyleSheet, View } from "react-native";
2+
import Animated, {
3+
useAnimatedStyle,
4+
withRepeat,
5+
withSequence,
6+
withTiming,
7+
} from "react-native-reanimated";
8+
9+
interface SkeletonListProps {
10+
numberItems?: number;
11+
}
12+
13+
export const SkeletonListReanimated = ({ numberItems = 10 }: SkeletonListProps) => {
14+
const items = [];
15+
16+
const animatedStyle = useAnimatedStyle(() => {
17+
return {
18+
opacity: withRepeat(
19+
withSequence(
20+
withTiming(0.3, { duration: 800 }),
21+
withTiming(0.7, { duration: 800 }),
22+
),
23+
-1,
24+
true,
25+
),
26+
};
27+
});
28+
29+
for (let i = 0; i < numberItems; i++) {
30+
items.push(
31+
<View
32+
key={`skeleton-${i}`}
33+
style={styles.itemContainer}
34+
>
35+
<Animated.View style={[styles.media, animatedStyle]} />
36+
<View style={styles.content}>
37+
<Animated.View
38+
style={[styles.line, styles.longLine, animatedStyle]}
39+
/>
40+
<Animated.View
41+
style={[styles.line, styles.shortLine, animatedStyle]}
42+
/>
43+
</View>
44+
</View>,
45+
);
46+
}
47+
48+
return <View>{items}</View>;
49+
};
50+
51+
const styles = StyleSheet.create({
52+
content: {
53+
flex: 1,
54+
},
55+
itemContainer: {
56+
alignItems: "center",
57+
flexDirection: "row",
58+
marginBottom: 12,
59+
},
60+
line: {
61+
backgroundColor: "#e1e1e1",
62+
borderRadius: 4,
63+
height: 12,
64+
marginVertical: 4,
65+
},
66+
longLine: {
67+
width: "90%",
68+
},
69+
media: {
70+
backgroundColor: "#e1e1e1",
71+
borderRadius: 20,
72+
height: 40,
73+
marginRight: 12,
74+
width: 40,
75+
},
76+
shortLine: {
77+
width: "80%",
78+
},
79+
});

Diff for: hackathon/spacecraft/src/screens/PilotScreen.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { PeopleProps } from "api/types";
22

33
import { PeopleItem } from "@/components/PeopleItem";
44
import { ScreenContainer } from "@/components/ScreenContainer";
5-
import { SkeletonList } from "@/components/SkeletonList";
5+
import { SkeletonList } from "@/components/SkeletonListReanimated";
66
import { usePilot } from "@/hooks/usePilot";
77
import { FlatList } from "react-native";
88
import { Button } from "react-native-paper";

0 commit comments

Comments
 (0)