From f5dbb4e7326c484f248cd42e0e0b173fe2ede1e3 Mon Sep 17 00:00:00 2001 From: StarNumber12046 <64470722+StarNumber12046@users.noreply.github.com> Date: Sun, 22 Sep 2024 17:13:32 +0200 Subject: [PATCH] =?UTF-8?q?Added=20gridview=20=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/_layout.tsx | 1 + app/gridView.tsx | 60 +++++++++++---- app/index.tsx | 164 +++++++++++++++++++++++++---------------- app/onboarding.tsx | 1 + app/post/[id].tsx | 133 +++++++++++++++++++++++++++++++++ components/MenuBar.tsx | 40 +++++----- components/ReBeal.tsx | 45 ++++++++--- package.json | 1 + yarn.lock | 12 +++ 9 files changed, 349 insertions(+), 108 deletions(-) create mode 100644 app/post/[id].tsx diff --git a/app/_layout.tsx b/app/_layout.tsx index 7cb6588..1cada86 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -100,6 +100,7 @@ export default function RootLayout() { + diff --git a/app/gridView.tsx b/app/gridView.tsx index a4630ae..3f4930b 100644 --- a/app/gridView.tsx +++ b/app/gridView.tsx @@ -1,32 +1,64 @@ import { SmallPost } from "@/components/ReBeal"; import { FriendsPost, FriendsPostPost, getFriendsPosts } from "@/sdk"; -import { useEffect, useState } from "react"; +import { router } from "expo-router"; +import { useEffect, useRef, useState } from "react"; import { FlatList, View, Text, StyleSheet, Dimensions } from "react-native"; +import { useSharedValue } from "react-native-reanimated"; +import { PanGestureHandler } from "react-native-gesture-handler"; +import Animated from "react-native-reanimated"; export default function GridView() { const screenWidth = Dimensions.get("window").width; // Get the screen width const itemWidth = screenWidth / 3 - 5; // Calculate item width with some margin const [friendsPosts, setFriendsPosts] = useState([]); let last = 0; + + const translateY = useSharedValue(0); + + const debounceTimeoutRef = useRef(null); + const debounce = (func: Function, delay: number) => { + // Clear any existing timeout + if (debounceTimeoutRef.current) { + clearTimeout(debounceTimeoutRef.current); + } + + // Set a new timeout + debounceTimeoutRef.current = setTimeout(() => { + func(); + }, delay); + }; + const onGestureEvent = (event: any) => { + if (event.nativeEvent.translationY > 50 && translateY.value === 0) { + // Check if the user swipes down and is at the top + debounce(() => { + router.push("/"); // Navigate to a new screen + }, 200); + } + }; + useEffect(() => { getFriendsPosts().then((posts) => setFriendsPosts(posts.map((item) => ({ ...item, i: last++ }))) ); }, []); return ( - - item.i.toString()} - renderItem={({ item }) => ( - - )} - /> - + + + + item.i.toString()} + renderItem={({ item }) => ( + + )} + /> + + + ); } diff --git a/app/index.tsx b/app/index.tsx index e958f45..ebbbd76 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect, useState } from "react"; +import React, { useContext, useEffect, useRef, useState } from "react"; import { IOScrollView, InView } from "react-native-intersection-observer"; import { View, StyleSheet, Text } from "react-native"; import { TopBar } from "@/components/TopBar"; @@ -7,6 +7,13 @@ import { CameraButton } from "@/components/CameraButton"; import { router } from "expo-router"; import AsyncStorage from "@react-native-async-storage/async-storage"; import getHeaders from "happy-headers"; +import { PanGestureHandler } from "react-native-gesture-handler"; // Import PanGestureHandler +import Animated, { + useSharedValue, + useAnimatedStyle, + withSpring, +} from "react-native-reanimated"; + import { checkToken, FullUser, @@ -28,6 +35,11 @@ export default function App() { const [myPosts, setMyPosts] = useState([]); const [me, setMe] = useState(null); const userContext = useContext(ProfileContext); + const translateY = useSharedValue(0); + const animatedStyle = useAnimatedStyle(() => ({ + transform: [{ translateY: translateY.value }], + })); + async function checkAuth() { if ( !(await AsyncStorage.getItem("authToken")) || @@ -63,84 +75,110 @@ export default function App() { })); }; + const debounceTimeoutRef = useRef(null); + const debounce = (func: Function, delay: number) => { + // Clear any existing timeout + if (debounceTimeoutRef.current) { + clearTimeout(debounceTimeoutRef.current); + } + + // Set a new timeout + debounceTimeoutRef.current = setTimeout(() => { + func(); + }, delay); + }; + const onGestureEvent = (event: any) => { + if (event.nativeEvent.translationY > 50 && translateY.value === 0) { + // Check if the user swipes down and is at the top + debounce(() => { + router.push("/gridView"); // Navigate to a new screen + }, 200); + } + }; + return ( - - { - getFriendsPosts().then((p) => setFriendsPosts(p)); - getMyPosts().then((p) => setMyPosts(p)); - getMyProfile(userContext).then((p) => setMe(p)); - }} - /> - - - {myPosts.map((value) => ( - + + + { + getFriendsPosts().then((p) => setFriendsPosts(p)); + getMyPosts().then((p) => setMyPosts(p)); + getMyProfile(userContext).then((p) => setMe(p)); + }} + /> + + - - - ))} - - - {friendsPosts.map( - (value) => - value && ( + {myPosts.map((value) => ( - handleVisibilityChange(value.id, inView) - } + style={{ + flex: 1, + height: 240, + marginHorizontal: -10, + }} > - - ) - )} - + ))} + + + {friendsPosts.map( + (value) => + value && ( + + handleVisibilityChange(value.id, inView) + } + > + + + ) + )} + + + + - - - + + ); } diff --git a/app/onboarding.tsx b/app/onboarding.tsx index 155ace2..f857abe 100644 --- a/app/onboarding.tsx +++ b/app/onboarding.tsx @@ -25,6 +25,7 @@ function handleRegistrationError(errorMessage: string) { // Register background handler messaging().setBackgroundMessageHandler(async (remoteMessage) => { console.log(remoteMessage); + if (remoteMessage.notification) return; // Ignore notifications if (remoteMessage.data?.type == "moment") { console.log("moments"); PushNotification.localNotification({ diff --git a/app/post/[id].tsx b/app/post/[id].tsx new file mode 100644 index 0000000..ffd5a77 --- /dev/null +++ b/app/post/[id].tsx @@ -0,0 +1,133 @@ +import { MenuBar } from "@/components/MenuBar"; +import { FriendsPost, FriendsPostPost, User } from "@/sdk"; +import { useRoute } from "@react-navigation/native"; +import { View, Text, Image, Dimensions, TouchableOpacity } from "react-native"; +import { Buffer } from "buffer"; +import { useEffect, useState } from "react"; +import { FontAwesome, MaterialIcons } from "@expo/vector-icons"; +interface Params { + id: string; +} + +function formatLateSeconds(lateInSeconds: number) { + if (lateInSeconds === 0) return null; + if (lateInSeconds < 60) return `${lateInSeconds} seconds late`; + const minutes = Math.floor(lateInSeconds / 60); + if (minutes < 60) return `${minutes} minutes late`; + const hours = Math.floor(minutes / 60); + return `${hours} hours late`; +} + +export default function Post() { + const params = useRoute().params as Params | undefined; + if (!params) return null; + const postData: FriendsPostPost & { user: User } = JSON.parse( + Buffer.from(params.id, "base64").toString() + ); + + const [bigImage, setBigImage] = useState<"primary" | "secondary">("primary"); + + const title = ( + + + {postData.user.username} + + + { + new Date(postData.postedAt as unknown as number) + .toTimeString() + .split(" ")[0] + } + {postData.lateInSeconds && + " - " + formatLateSeconds(postData.lateInSeconds)} + + + ); + return ( + + + + + + + { + console.log("Switching images"); + setBigImage(bigImage === "primary" ? "secondary" : "primary"); + }} + > + + + + + {" "} + {postData.retakeCounter}{" "} + {postData.retakeCounter === 1 ? "retake" : "retakes"} + + + + ); +} diff --git a/components/MenuBar.tsx b/components/MenuBar.tsx index cf12e50..98f99fb 100644 --- a/components/MenuBar.tsx +++ b/components/MenuBar.tsx @@ -1,23 +1,20 @@ import { AntDesign } from "@expo/vector-icons"; -import { router } from "expo-router"; +import { router, useRouter } from "expo-router"; +import React from "react"; import { View, Text, Pressable, StyleSheet } from "react-native"; export function MenuBar({ arrowLocation, title, }: { arrowLocation: "left" | "right"; - title?: string; + title?: string | React.ReactNode; }) { + const router = useRouter(); + return ( { - router.back(); - } - : () => {} - } + onPress={arrowLocation === "left" ? () => router.back() : undefined} > - - {title ?? ReBeal.} - + + + {!title && ReBeal.} + {title && typeof title === "string" && ( + {title} + )} + {title && typeof title !== "string" && title} + + { - router.navigate("/"); - } - : () => {} + arrowLocation === "right" ? () => router.navigate("/") : undefined } > @@ -66,6 +63,11 @@ const styles = StyleSheet.create({ paddingHorizontal: 20, backgroundColor: "#00000000", }, + textContainer: { + flex: 1, // Make the title's container take up available space + alignItems: "center", // Center the title within the container + }, + cameraButton: { backgroundColor: "#00000000", width: 75, diff --git a/components/ReBeal.tsx b/components/ReBeal.tsx index 251c8ef..b2eff13 100644 --- a/components/ReBeal.tsx +++ b/components/ReBeal.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; import * as Haptics from "expo-haptics"; - +import * as Buffer from "buffer"; import { View, Image, @@ -167,7 +167,15 @@ export function SmallPost({ } return ( - + router.push( + ("/post/" + + Buffer.Buffer.from(JSON.stringify(post)).toString( + "base64" + )) as Href + ) + } style={[ styles.smallPostContainer, { height: width / primaryAspectRatio }, @@ -215,7 +223,7 @@ export function SmallPost({ > {user.username} - + ); } @@ -235,6 +243,7 @@ export function ReBeal({ visible, blurred, userId, + postData, }: { primaryUrl: string; secondaryUrl: string; @@ -251,6 +260,7 @@ export function ReBeal({ visible: boolean; blurred: boolean; userId: string; + postData: FriendsPostPost & { user: User }; }) { console.log(blurred); const [bigImage, setBigImage] = useState<"primary" | "secondary">("primary"); @@ -312,15 +322,26 @@ export function ReBeal({ }} /> - + + router.push( + ("/post/" + + Buffer.Buffer.from(JSON.stringify(postData)).toString( + "base64" + )) as Href + ) + } + > + + diff --git a/package.json b/package.json index 54c1669..2e41ce6 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "firebase-admin": "^12.5.0", "happy-headers": "^1.5.0", "libphonenumber-js": "^1.11.7", + "pusher-js": "^8.4.0-rc2", "react": "18.2.0", "react-dom": "18.2.0", "react-native": "0.74.5", diff --git a/yarn.lock b/yarn.lock index abc6e34..d00ab83 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8186,6 +8186,13 @@ pure-rand@^6.0.0: resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== +pusher-js@^8.4.0-rc2: + version "8.4.0-rc2" + resolved "https://registry.yarnpkg.com/pusher-js/-/pusher-js-8.4.0-rc2.tgz#44f33bfe5bc89f741d82601125d6095bde28261b" + integrity sha512-d87GjOEEl9QgO5BWmViSqW0LOzPvybvX6WA9zLUstNdB57jVJuR27zHkRnrav2a3+zAMlHbP2Og8wug+rG8T+g== + dependencies: + tweetnacl "^1.0.3" + qrcode-terminal@0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.11.0.tgz#ffc6c28a2fc0bfb47052b47e23f4f446a5fbdb9e" @@ -9527,6 +9534,11 @@ turbo-stream@2.4.0: resolved "https://registry.yarnpkg.com/turbo-stream/-/turbo-stream-2.4.0.tgz#1e4fca6725e90fa14ac4adb782f2d3759a5695f0" integrity sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g== +tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + type-detect@4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"