mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-10-31 18:19:43 +00:00
Update
This commit is contained in:
1 parent
ef7bc8c90d
commit
3fcbea8d60
68 files changed
+3579
-834
No files matched your search
@@ -84,9 +84,10 @@ export default function AppLayout() {
|
||||
const wentToBackgroundAt = useRef<number | null>(null);
|
||||
|
||||
// Check if we're on screens that should hide the tab bar
|
||||
const isOnOnboarding = segments.includes("onboarding");
|
||||
const isOnPairing = segments.includes("pairing");
|
||||
const isOnChat = segments.includes("chat");
|
||||
const segmentStrings = segments as string[];
|
||||
const isOnOnboarding = segmentStrings.includes("onboarding");
|
||||
const isOnPairing = segmentStrings.includes("pairing");
|
||||
const isOnChat = segmentStrings.includes("chat");
|
||||
const shouldHideTabBar = isOnOnboarding || isOnPairing || isOnChat;
|
||||
|
||||
// Redirect to login when not authenticated, or to onboarding if not completed
|
||||
@@ -309,10 +310,16 @@ export default function AppLayout() {
|
||||
.single();
|
||||
|
||||
if (match) {
|
||||
// Handle the joined question data from the query
|
||||
const questionData = match.question;
|
||||
const questionText = Array.isArray(questionData)
|
||||
? questionData[0]?.text
|
||||
: (questionData as { text: string } | null)?.text;
|
||||
|
||||
addMessage({
|
||||
...newMessage,
|
||||
delivered_at: newMessage.delivered_at || new Date().toISOString(),
|
||||
match: match as { id: string; question: { text: string } },
|
||||
match: { id: match.id, question: { text: questionText || "" } },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,7 +410,7 @@ export default function OnboardingScreen() {
|
||||
Show me everything
|
||||
</Text>
|
||||
<Text style={styles.explicitDescription}>
|
||||
Include 18+ question packs
|
||||
Include spicy question packs
|
||||
</Text>
|
||||
</View>
|
||||
<View style={[
|
||||
|
||||
@@ -31,7 +31,7 @@ export default function PairingScreen() {
|
||||
// Redirect if already paired
|
||||
useEffect(() => {
|
||||
if (couple && partner) {
|
||||
router.replace("/(app)/");
|
||||
router.replace("/(app)");
|
||||
}
|
||||
}, [couple, partner]);
|
||||
|
||||
@@ -124,7 +124,7 @@ export default function PairingScreen() {
|
||||
Events.coupleJoined();
|
||||
|
||||
Alert.alert("Success", "You are now paired!", [
|
||||
{ text: "Let's Go", onPress: () => router.replace("/(app)/") }
|
||||
{ text: "Let's Go", onPress: () => router.replace("/(app)") }
|
||||
]);
|
||||
} catch (error: any) {
|
||||
Alert.alert("Error", getPairingError(error));
|
||||
|
||||
@@ -726,8 +726,8 @@ export default function ProfileScreen() {
|
||||
<Ionicons name="flame" size={20} color={colors.text} />
|
||||
</LinearGradient>
|
||||
<View style={styles.rowTextContainer}>
|
||||
<Text style={styles.rowValue}>Explicit Content</Text>
|
||||
<Text style={styles.rowLabel}>Show 18+ question packs</Text>
|
||||
<Text style={styles.rowValue}>Spicy Content</Text>
|
||||
<Text style={styles.rowLabel}>Show spicy question packs</Text>
|
||||
</View>
|
||||
</View>
|
||||
<Switch
|
||||
|
||||
@@ -2,7 +2,17 @@ import { useState, useEffect, useRef } from "react";
|
||||
import { View, StyleSheet, Text, ActivityIndicator, Platform } from "react-native";
|
||||
import { useLocalSearchParams, router } from "expo-router";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import Animated, { FadeIn, FadeInUp } from "react-native-reanimated";
|
||||
import Animated, {
|
||||
FadeIn,
|
||||
FadeInUp,
|
||||
useSharedValue,
|
||||
useAnimatedStyle,
|
||||
withRepeat,
|
||||
withSequence,
|
||||
withTiming,
|
||||
interpolate,
|
||||
Easing,
|
||||
} from "react-native-reanimated";
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
import { supabase } from "../../src/lib/supabase";
|
||||
import { usePacksStore, useAuthStore } from "../../src/store";
|
||||
@@ -27,6 +37,74 @@ export default function SwipeScreen() {
|
||||
const { partner, couple } = useAuthStore();
|
||||
const hasTrackedExhausted = useRef(false);
|
||||
|
||||
// Ambient orb breathing animations
|
||||
const orbBreathing1 = useSharedValue(0);
|
||||
const orbBreathing2 = useSharedValue(0);
|
||||
const orbDrift = useSharedValue(0);
|
||||
const shimmerPosition = useSharedValue(-1);
|
||||
|
||||
useEffect(() => {
|
||||
// Primary orb breathing - 6 second cycle
|
||||
orbBreathing1.value = withRepeat(
|
||||
withSequence(
|
||||
withTiming(1, { duration: 3000, easing: Easing.inOut(Easing.sin) }),
|
||||
withTiming(0, { duration: 3000, easing: Easing.inOut(Easing.sin) })
|
||||
),
|
||||
-1,
|
||||
true
|
||||
);
|
||||
|
||||
// Secondary orb breathing - offset timing for variation
|
||||
orbBreathing2.value = withRepeat(
|
||||
withSequence(
|
||||
withTiming(0, { duration: 2000, easing: Easing.inOut(Easing.sin) }),
|
||||
withTiming(1, { duration: 4000, easing: Easing.inOut(Easing.sin) }),
|
||||
withTiming(0, { duration: 2000, easing: Easing.inOut(Easing.sin) })
|
||||
),
|
||||
-1,
|
||||
true
|
||||
);
|
||||
|
||||
// Subtle vertical drift - 8 second cycle
|
||||
orbDrift.value = withRepeat(
|
||||
withSequence(
|
||||
withTiming(1, { duration: 4000, easing: Easing.inOut(Easing.sin) }),
|
||||
withTiming(0, { duration: 4000, easing: Easing.inOut(Easing.sin) })
|
||||
),
|
||||
-1,
|
||||
true
|
||||
);
|
||||
|
||||
// Progress bar shimmer sweep - 2.5 second cycle
|
||||
shimmerPosition.value = withRepeat(
|
||||
withTiming(2, { duration: 2500, easing: Easing.linear }),
|
||||
-1,
|
||||
false
|
||||
);
|
||||
}, []);
|
||||
|
||||
const orbStyle1 = useAnimatedStyle(() => ({
|
||||
opacity: interpolate(orbBreathing1.value, [0, 1], [0.25, 0.5]),
|
||||
transform: [
|
||||
{ translateY: interpolate(orbDrift.value, [0, 1], [0, -20]) },
|
||||
{ scale: interpolate(orbBreathing1.value, [0, 1], [1, 1.1]) },
|
||||
],
|
||||
}));
|
||||
|
||||
const orbStyle2 = useAnimatedStyle(() => ({
|
||||
opacity: interpolate(orbBreathing2.value, [0, 1], [0.2, 0.4]),
|
||||
transform: [
|
||||
{ translateY: interpolate(orbDrift.value, [0, 1], [20, 0]) },
|
||||
{ scale: interpolate(orbBreathing2.value, [0, 1], [1, 1.1]) },
|
||||
],
|
||||
}));
|
||||
|
||||
const shimmerStyle = useAnimatedStyle(() => ({
|
||||
transform: [
|
||||
{ translateX: interpolate(shimmerPosition.value, [-1, 2], [-60, 220]) },
|
||||
],
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
fetchPacks().then(() => fetchQuestions());
|
||||
checkTutorial();
|
||||
@@ -323,6 +401,24 @@ export default function SwipeScreen() {
|
||||
|
||||
return (
|
||||
<GradientBackground>
|
||||
{/* Ambient Orbs */}
|
||||
<Animated.View style={[styles.ambientOrb, styles.orbTopRight, orbStyle1]} pointerEvents="none">
|
||||
<LinearGradient
|
||||
colors={[colors.primaryGlow, 'transparent']}
|
||||
style={styles.orbGradient}
|
||||
start={{ x: 0.5, y: 0.5 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
/>
|
||||
</Animated.View>
|
||||
<Animated.View style={[styles.ambientOrb, styles.orbBottomLeft, orbStyle2]} pointerEvents="none">
|
||||
<LinearGradient
|
||||
colors={[colors.secondaryGlow, 'transparent']}
|
||||
style={styles.orbGradient}
|
||||
start={{ x: 0.5, y: 0.5 }}
|
||||
end={{ x: 0, y: 0 }}
|
||||
/>
|
||||
</Animated.View>
|
||||
|
||||
<View style={styles.container}>
|
||||
{/* Header */}
|
||||
<Animated.View
|
||||
@@ -333,7 +429,7 @@ export default function SwipeScreen() {
|
||||
<Text style={styles.progressText}>
|
||||
{currentIndex + 1} of {questions.length}
|
||||
</Text>
|
||||
<View style={styles.progressBar}>
|
||||
<View style={[styles.progressBar, styles.progressBarEnhanced]}>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.progressFill,
|
||||
@@ -346,6 +442,15 @@ export default function SwipeScreen() {
|
||||
end={{ x: 1, y: 0 }}
|
||||
style={styles.progressGradient}
|
||||
/>
|
||||
{/* Shimmer sweep */}
|
||||
<Animated.View style={[styles.progressShimmer, shimmerStyle]}>
|
||||
<LinearGradient
|
||||
colors={['transparent', 'rgba(255,255,255,0.3)', 'transparent']}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 0 }}
|
||||
style={StyleSheet.absoluteFill}
|
||||
/>
|
||||
</Animated.View>
|
||||
</Animated.View>
|
||||
</View>
|
||||
</View>
|
||||
@@ -353,9 +458,14 @@ export default function SwipeScreen() {
|
||||
|
||||
{/* Card Stack */}
|
||||
<View style={styles.cardContainer}>
|
||||
{/* Background card (next) */}
|
||||
{/* Third card (deepest) */}
|
||||
{questions[currentIndex + 2] && (
|
||||
<View style={[styles.stackCard, styles.stackCardThird]} />
|
||||
)}
|
||||
|
||||
{/* Second card */}
|
||||
{questions[currentIndex + 1] && (
|
||||
<View style={styles.backgroundCard} />
|
||||
<View style={[styles.stackCard, styles.stackCardSecond]} />
|
||||
)}
|
||||
|
||||
{/* Active card */}
|
||||
@@ -395,6 +505,26 @@ const styles = StyleSheet.create({
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
// Ambient orbs - more visible
|
||||
ambientOrb: {
|
||||
position: 'absolute',
|
||||
width: 300,
|
||||
height: 300,
|
||||
borderRadius: 150,
|
||||
},
|
||||
orbTopRight: {
|
||||
top: 60,
|
||||
right: -40,
|
||||
},
|
||||
orbBottomLeft: {
|
||||
bottom: 180,
|
||||
left: -40,
|
||||
},
|
||||
orbGradient: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
borderRadius: 150,
|
||||
},
|
||||
header: {
|
||||
paddingTop: 60,
|
||||
paddingHorizontal: spacing.lg,
|
||||
@@ -415,21 +545,35 @@ const styles = StyleSheet.create({
|
||||
borderRadius: 3,
|
||||
overflow: "hidden",
|
||||
},
|
||||
progressBarEnhanced: {
|
||||
height: 8,
|
||||
borderRadius: 4,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.glass.borderLight,
|
||||
...shadows.glow(colors.primaryGlow),
|
||||
},
|
||||
progressFill: {
|
||||
height: "100%",
|
||||
borderRadius: 3,
|
||||
borderRadius: 4,
|
||||
overflow: "hidden",
|
||||
},
|
||||
progressGradient: {
|
||||
flex: 1,
|
||||
borderRadius: 3,
|
||||
borderRadius: 4,
|
||||
},
|
||||
progressShimmer: {
|
||||
position: 'absolute',
|
||||
width: 60,
|
||||
height: '100%',
|
||||
top: 0,
|
||||
},
|
||||
cardContainer: {
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
backgroundCard: {
|
||||
// Card stack
|
||||
stackCard: {
|
||||
position: "absolute",
|
||||
width: "85%",
|
||||
height: 480,
|
||||
@@ -437,10 +581,17 @@ const styles = StyleSheet.create({
|
||||
borderRadius: radius.xxl,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.glass.border,
|
||||
},
|
||||
stackCardSecond: {
|
||||
transform: [{ scale: 0.95 }, { translateY: 12 }],
|
||||
opacity: 0.5,
|
||||
...shadows.md,
|
||||
},
|
||||
stackCardThird: {
|
||||
transform: [{ scale: 0.90 }, { translateY: 24 }],
|
||||
opacity: 0.25,
|
||||
...shadows.sm,
|
||||
},
|
||||
hintContainer: {
|
||||
alignItems: "center",
|
||||
paddingBottom: spacing.md,
|
||||
|
||||
+821
-733
File diff suppressed because it is too large.
Load diff
@@ -24,7 +24,7 @@ import React, { useEffect, useRef } from "react";
|
||||
import { Platform, View, AppState, AppStateStatus } from "react-native";
|
||||
|
||||
// Suppress useLayoutEffect warning on server-side rendering
|
||||
if (Platform.OS === "web" && typeof window === "undefined") {
|
||||
if (Platform.OS === "web" && typeof globalThis.window === "undefined") {
|
||||
React.useLayoutEffect = useEffect;
|
||||
// Also silence the warning in case some libraries ignore the monkey-patch
|
||||
const originalError = console.error;
|
||||
|
||||
@@ -30,7 +30,7 @@ export default function Index() {
|
||||
}
|
||||
|
||||
// Otherwise go to home
|
||||
return <Redirect href="/(app)/" />;
|
||||
return <Redirect href="/(app)" />;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
||||
Reference in new issue
Block a user