Skip to content

Instantly share code, notes, and snippets.

@tanner-west
Created October 9, 2023 12:04
Show Gist options
  • Save tanner-west/d71cb11e95651bcc5540eda85b1f73a2 to your computer and use it in GitHub Desktop.
Save tanner-west/d71cb11e95651bcc5540eda85b1f73a2 to your computer and use it in GitHub Desktop.
import React, { useRef, useMemo, useState } from "react";
import {
Image,
useWindowDimensions,
View,
Text,
TouchableOpacity,
FlatList,
} from "react-native";
import MaterialIcons from "@expo/vector-icons/MaterialCommunityIcons";
import BottomSheet, { BottomSheetTextInput } from "@gorhom/bottom-sheet";
import { Video, ResizeMode } from "expo-av";
import Animated, {
useSharedValue,
withTiming,
useAnimatedStyle,
withSequence,
FadeIn,
} from "react-native-reanimated";
import { LinearGradient } from "expo-linear-gradient";
const ActionIcon = ({
name,
action,
liked,
disliked,
}: {
name:
| "thumb-up"
| "thumb-down"
| "thumb-up-outline"
| "thumb-down-outline"
| "comment"
| "share";
action?: () => void;
liked?: boolean;
disliked?: boolean;
}) => {
return (
<TouchableOpacity onPress={action} style={{ marginTop: 20 }}>
<MaterialIcons
name={name}
color={disliked ? "#6173ff" : liked ? "#6173ff" : "white"}
style={{ shadowColor: "black", shadowRadius: 15, shadowOpacity: 0.75 }}
size={40}
/>
</TouchableOpacity>
);
};
const reels = [
{
source: require(`../assets/videos/waves.mp4`),
title: "On a boat",
tags: ["summervibes", "ocean", "cool"],
profilePic: require(`../assets/images/faces/humans/face_0.png`),
profileName: "chattanooga_chuck",
},
{
source: require(`../assets/videos/friends.mp4`),
title: "Besties 4 Eva",
tags: ["friends", "love", "feels"],
profilePic: require(`../assets/images/faces/humans/face_2.png`),
profileName: "sabrina28288",
},
{
source: require(`../assets/videos/read.mp4`),
title: "I read 400 books and this is what happened",
tags: ["text", "readingiscool", "smart"],
profilePic: require(`../assets/images/faces/humans/face_1.png`),
profileName: "reader_rita",
},
{
source: require(`../assets/videos/doughnuts.mp4`),
title: "I ate the whole box 😏",
tags: ["dough", "yeast", "sprinkleeees"],
profilePic: require(`../assets/images/faces/humans/face_3.png`),
profileName: "bakedgoodsaddict",
comments: [
{
text: "Looks great 😍😍😍",
pic: require(`../assets/images/faces/humans/face_12.png`),
profileName: "lovedough4673 | 2d",
},
{
text: "Yummmmmmmmm",
pic: require(`../assets/images/faces/humans/face_9.png`),
profileName: "jimbo | 1h",
},
{
text: "🍩🍩🍩🍩🍩 i would eat them all 🍩🍩🍩🍩",
pic: require(`../assets/images/faces/humans/face_10.png`),
profileName: "sarah_k | 12h",
},
{
text: "WHERE CAN I GET SOME???",
pic: require(`../assets/images/faces/humans/face_15.png`),
profileName: "amara_z | 1d",
},
{
text: "💯💯💯💯💯💯💯💯",
pic: require(`../assets/images/faces/humans/face_7.png`),
profileName: "jboss | 30m",
},
],
},
{
source: require(`../assets/videos/door.mp4`),
title: "Let me sell your house!!!",
tags: ["moving", "real_estate", "houses"],
profilePic: require(`../assets/images/faces/humans/face_8.png`),
profileName: "dream_team_trina",
},
{
source: require(`../assets/videos/beans.mp4`),
title: "Beans",
tags: ["fresh", "barista", "gourmet"],
profilePic: require(`../assets/images/faces/humans/face_7.png`),
profileName: "thejoe",
},
{
source: require(`../assets/videos/bake.mp4`),
title: "Ready, set, bake!",
tags: ["breadweek", "proof", "rise"],
profilePic: require(`../assets/images/faces/humans/face_16.png`),
profileName: "iwatchsports",
},
];
const Reel = ({ item }) => {
const { source, title, tags, comments, profilePic, profileName } = item;
const [liked, setLiked] = useState(false);
const [disliked, setDisliked] = useState(false);
const [myComment, setMyComment] = useState("");
const [myCommentEditing, setMyCommentEditing] = useState("");
const window = useWindowDimensions();
const bottomSheetRef = useRef<BottomSheet>(null);
const videoRef = useRef<Video>(null);
const snapPoints = useMemo(() => ["25%", "50%"], []);
const rotation = useSharedValue(0);
const translateY = useSharedValue(0);
const opacity = useSharedValue(1);
const rotationYThumbsDown = useSharedValue(0);
const translateYThumbsDown = useSharedValue(0);
const opacityThumbsDown = useSharedValue(1);
const onPressThumbsUp = () => {
if (!liked) {
rotation.value = withSequence(
withTiming(-45, { duration: 150 }),
withTiming(0, { duration: 150 }),
withTiming(0, { duration: 0 })
);
translateY.value = withSequence(
withTiming(-50, { duration: 150 }),
withTiming(0, { duration: 150 }),
withTiming(-500, { duration: 500 }),
withTiming(0, { duration: 0 })
);
opacity.value = withSequence(
withTiming(1, { duration: 200 }),
withTiming(0, { duration: 500 }),
withTiming(1, { duration: 250 })
);
setTimeout(() => setLiked(true), 500);
setDisliked(false);
} else {
setLiked(false);
}
};
const onPressThumbsDown = () => {
if (!disliked) {
rotationYThumbsDown.value = withSequence(
withTiming(-90, { duration: 500 }),
withTiming(0, { duration: 0 })
);
translateYThumbsDown.value = withSequence(
withTiming(50, { duration: 500 }),
withTiming(0, { duration: 0 })
);
opacityThumbsDown.value = withSequence(
withTiming(0, { duration: 500 }),
withTiming(1, { duration: 250 })
);
setTimeout(() => setDisliked(true), 500);
setLiked(false);
} else {
setDisliked(false);
}
};
const animatedStyleThumbsUp = useAnimatedStyle(() => {
return {
transform: [
{ rotate: `${rotation.value}deg` },
{ translateY: translateY.value },
],
opacity: opacity.value,
};
});
const animatedStyleThumbsDown = useAnimatedStyle(() => {
return {
transform: [
{ rotateY: `${rotationYThumbsDown.value}deg` },
{ translateY: translateYThumbsDown.value },
],
opacity: opacityThumbsDown.value,
};
});
const openBottomSheet = () => {
bottomSheetRef.current?.snapToIndex(1);
};
return (
<View style={{ width: window.width, height: window.height }}>
<Video
ref={videoRef}
style={{ width: window.width, height: window.height }}
source={source}
resizeMode={ResizeMode.COVER}
isLooping
shouldPlay
rate={2}
/>
<LinearGradient
style={{
height: window.height / 2,
width: window.width,
position: "absolute",
bottom: 0,
}}
colors={["transparent", "rgba(0,0,0,0.8)"]}
/>
<View
style={{
width: window.width,
position: "absolute",
left: 0,
bottom: 50,
paddingHorizontal: 10,
}}
>
<View
style={{
flexDirection: "row",
justifyContent: "flex-start",
alignItems: "center",
}}
>
<Image
source={profilePic}
style={{
maxHeight: 30,
maxWidth: 30,
marginRight: 10,
marginBottom: 10,
borderColor: "white",
borderWidth: 2,
borderRadius: 15,
}}
resizeMode={"contain"}
/>
<Text style={{ fontSize: 18, color: "white", marginBottom: 5 }}>
{profileName}
</Text>
</View>
<Text style={{ fontSize: 18, color: "white", marginBottom: 5 }}>
{title}
</Text>
<Text style={{ fontSize: 18, color: "white", fontWeight: "bold" }}>
{"#" + tags.join(" #")}
</Text>
</View>
<View
style={{
height: window.height,
position: "absolute",
right: 10,
justifyContent: "center",
alignItems: "center",
padding: 5,
}}
>
<Animated.View style={[animatedStyleThumbsUp]}>
{liked ? (
<ActionIcon
name="thumb-up"
action={onPressThumbsUp}
disliked={liked}
/>
) : (
<ActionIcon name="thumb-up-outline" action={onPressThumbsUp} />
)}
</Animated.View>
<Animated.View style={[animatedStyleThumbsDown]}>
{disliked ? (
<ActionIcon
name="thumb-down"
action={onPressThumbsDown}
disliked={disliked}
/>
) : (
<ActionIcon name="thumb-down-outline" action={onPressThumbsDown} />
)}
</Animated.View>
<ActionIcon name="comment" action={openBottomSheet} />
<ActionIcon name="share" />
</View>
<BottomSheet
snapPoints={snapPoints}
index={-1}
enablePanDownToClose
ref={bottomSheetRef}
keyboardBehavior="interactive"
keyboardBlurBehavior="restore"
>
<View style={{ padding: 10 }}>
{myComment && (
<Animated.View
entering={FadeIn.duration(1000)}
style={{
flexDirection: "row",
marginBottom: 20,
alignItems: "center",
}}
>
<Image
source={require(`../assets/images/faces/humans/face_9.png`)}
style={{ maxHeight: 20, maxWidth: 20 }}
resizeMode="contain"
/>
<View style={{ marginLeft: 5 }}>
<Text
style={{ fontSize: 12, fontWeight: "bold", marginBottom: 2 }}
>
{"reelz_lover"}
</Text>
<Text>{myComment}</Text>
</View>
</Animated.View>
)}
{comments?.map(({ text, pic, profileName }) => (
<View
key={text}
style={{
flexDirection: "row",
marginBottom: 20,
alignItems: "center",
}}
>
<Image
source={pic}
style={{ maxHeight: 20, maxWidth: 20 }}
resizeMode="contain"
/>
<View style={{ marginLeft: 5 }}>
<Text
style={{ fontSize: 12, fontWeight: "bold", marginBottom: 2 }}
>
{profileName}
</Text>
<Text>{text}</Text>
</View>
</View>
))}
<View style={{ flexDirection: "row" }}>
<Image
source={require(`../assets/images/faces/humans/face_9.png`)}
style={{ maxWidth: 50, maxHeight: 50 }}
/>
<BottomSheetTextInput
style={{
flex: 1,
borderColor: "lightgray",
borderWidth: 1,
borderRadius: 10,
padding: 5,
margin: 5,
}}
placeholder="How do you feel about it?"
returnKeyLabel="Send It"
returnKeyType="send"
value={myCommentEditing}
onChangeText={(text) => setMyCommentEditing(text)}
onSubmitEditing={({ nativeEvent }) => {
setMyComment(nativeEvent.text);
setMyCommentEditing("");
}}
/>
</View>
</View>
</BottomSheet>
</View>
);
};
const Reelz = () => {
return (
<View style={{ flex: 1, backgroundColor: "black" }}>
<FlatList
pagingEnabled
data={reels}
renderItem={({ item, index }) => <Reel key={item.title} item={item} />}
scrollEventThrottle={1}
decelerationRate={"fast"}
/>
</View>
);
};
export default Reelz;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment