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
c978a3bd28
commit
0f229211ea
19 files changed
+1625
-624
No files matched your search
@@ -14,6 +14,7 @@ import { decode } from 'base64-arraybuffer';
|
||||
import { Database } from "../../../src/types/supabase";
|
||||
import { GradientBackground } from "../../../src/components/ui";
|
||||
import { colors, gradients, spacing, radius, typography } from "../../../src/theme";
|
||||
import { Events } from "../../../src/lib/analytics";
|
||||
|
||||
type Message = Database["public"]["Tables"]["messages"]["Row"];
|
||||
|
||||
@@ -205,6 +206,8 @@ export default function ChatScreen() {
|
||||
if (error) {
|
||||
Alert.alert("Error", "Failed to send message");
|
||||
setInputText(content);
|
||||
} else {
|
||||
Events.messageSent();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -283,6 +286,7 @@ export default function ChatScreen() {
|
||||
user_id: user.id,
|
||||
media_path: fileName,
|
||||
});
|
||||
Events.mediaUploaded();
|
||||
|
||||
} catch (error) {
|
||||
Alert.alert("Error", "Failed to upload image");
|
||||
|
||||
@@ -28,7 +28,9 @@ import { GlassButton } from '../../src/components/ui/GlassButton';
|
||||
import { colors, gradients, spacing, typography, radius, shadows } from '../../src/theme';
|
||||
import { useAuthStore } from '../../src/store';
|
||||
import { supabase } from '../../src/lib/supabase';
|
||||
import { getProfileError } from '../../src/lib/errors';
|
||||
import { registerForPushNotificationsAsync, savePushToken } from '../../src/lib/notifications';
|
||||
import { Events } from '../../src/lib/analytics';
|
||||
import type { Gender } from '../../src/types';
|
||||
|
||||
const GENDER_OPTIONS: { value: Gender; label: string; icon: string }[] = [
|
||||
@@ -59,6 +61,7 @@ export default function OnboardingScreen() {
|
||||
Keyboard.dismiss();
|
||||
setError(null);
|
||||
setStage('gender');
|
||||
Events.onboardingStageComplete('name');
|
||||
};
|
||||
|
||||
const handleGenderSelect = (selectedGender: Gender) => {
|
||||
@@ -72,10 +75,12 @@ export default function OnboardingScreen() {
|
||||
return;
|
||||
}
|
||||
setStage('explicit');
|
||||
Events.onboardingStageComplete('gender');
|
||||
};
|
||||
|
||||
const handleExplicitContinue = () => {
|
||||
setStage('notifications');
|
||||
Events.onboardingStageComplete('explicit');
|
||||
};
|
||||
|
||||
const handleEnableNotifications = async () => {
|
||||
@@ -124,10 +129,11 @@ export default function OnboardingScreen() {
|
||||
console.log('[Onboarding] Profile updated successfully, fetching user...');
|
||||
await fetchUser();
|
||||
console.log('[Onboarding] User fetched, navigating to home...');
|
||||
Events.onboardingComplete();
|
||||
router.replace('/');
|
||||
} catch (err: any) {
|
||||
console.error('Onboarding error:', err);
|
||||
setError(err.message || 'Something went wrong');
|
||||
setError(getProfileError(err));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ import Animated, { FadeInDown } from "react-native-reanimated";
|
||||
import * as Clipboard from "expo-clipboard";
|
||||
import { useAuthStore } from "../../src/store";
|
||||
import { supabase } from "../../src/lib/supabase";
|
||||
import { getPairingError } from "../../src/lib/errors";
|
||||
import { Events } from "../../src/lib/analytics";
|
||||
import { router } from "expo-router";
|
||||
import { GradientBackground, GlassCard, GlassButton } from "../../src/components/ui";
|
||||
import { colors, gradients, spacing, radius, typography, shadows } from "../../src/theme";
|
||||
@@ -81,8 +83,9 @@ export default function PairingScreen() {
|
||||
|
||||
await fetchUser(); // Refresh user to get couple_id
|
||||
await fetchCouple(); // Fetch couple data
|
||||
Events.coupleCreated();
|
||||
} catch (error: any) {
|
||||
Alert.alert("Error", error.message);
|
||||
Alert.alert("Error", getPairingError(error));
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
@@ -92,7 +95,7 @@ export default function PairingScreen() {
|
||||
// Validate invite code format (8 alphanumeric characters)
|
||||
const sanitizedCode = inviteCode.trim().toUpperCase();
|
||||
if (!/^[A-Z0-9]{8}$/.test(sanitizedCode)) {
|
||||
Alert.alert("Error", "Please enter a valid 8-character invite code");
|
||||
Alert.alert("Invalid Code", "Please enter a valid 8-character invite code.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -118,12 +121,13 @@ export default function PairingScreen() {
|
||||
|
||||
await fetchUser(); // Refresh user to get couple_id
|
||||
await fetchCouple(); // Fetch couple data
|
||||
Events.coupleJoined();
|
||||
|
||||
Alert.alert("Success", "You are now paired!", [
|
||||
{ text: "Let's Go", onPress: () => router.replace("/(app)/") }
|
||||
]);
|
||||
} catch (error: any) {
|
||||
Alert.alert("Error", error.message);
|
||||
Alert.alert("Error", getPairingError(error));
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
@@ -142,6 +146,7 @@ export default function PairingScreen() {
|
||||
await Share.share({
|
||||
message: `Join me on Sauci! Use my invite code to pair up: ${couple.invite_code}`,
|
||||
});
|
||||
Events.codeShared();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
@@ -169,8 +174,9 @@ export default function PairingScreen() {
|
||||
|
||||
await fetchUser();
|
||||
await fetchCouple();
|
||||
Events.pairingCancelled();
|
||||
} catch (error: any) {
|
||||
Alert.alert("Error", error.message);
|
||||
Alert.alert("Error", getPairingError(error));
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export default function ProfileScreen() {
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const [showFeedbackModal, setShowFeedbackModal] = useState(false);
|
||||
const [showPaywall, setShowPaywall] = useState(false);
|
||||
const [showExplicit, setShowExplicit] = useState(user?.show_explicit_content ?? false);
|
||||
const [showExplicit, setShowExplicit] = useState(user?.show_explicit_content ?? true);
|
||||
const [isUpdatingExplicit, setIsUpdatingExplicit] = useState(false);
|
||||
const [biometricAvailable, setBiometricAvailable] = useState(false);
|
||||
const [biometricEnabled, setBiometricEnabledState] = useState(false);
|
||||
|
||||
@@ -11,6 +11,7 @@ import { hasSeenSwipeTutorial, markSwipeTutorialSeen } from "../../src/lib/swipe
|
||||
import SwipeCard from "../../src/components/SwipeCard";
|
||||
import SwipeTutorial from "../../src/components/SwipeTutorial";
|
||||
import { GradientBackground, GlassCard, GlassButton } from "../../src/components/ui";
|
||||
import { Events } from "../../src/lib/analytics";
|
||||
import { colors, gradients, spacing, typography, radius, shadows } from "../../src/theme";
|
||||
|
||||
export default function SwipeScreen() {
|
||||
@@ -102,6 +103,7 @@ export default function SwipeScreen() {
|
||||
// Handle skip - don't submit a response, just store it for later
|
||||
if (direction === "down") {
|
||||
await skipQuestion(question.id);
|
||||
Events.questionSkipped();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -127,6 +129,8 @@ export default function SwipeScreen() {
|
||||
|
||||
if (error) {
|
||||
console.error("Submit response error:", error);
|
||||
} else {
|
||||
Events.questionAnswered(answer, question.pack_id);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to submit response", error);
|
||||
|
||||
@@ -17,6 +17,7 @@ import Animated, {
|
||||
} from "react-native-reanimated";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { supabase } from "../../src/lib/supabase";
|
||||
import { getAuthError } from "../../src/lib/errors";
|
||||
import { GradientBackground, GlassCard, GlassButton, GlassInput } from "../../src/components/ui";
|
||||
import { colors, spacing, radius, typography } from "../../src/theme";
|
||||
|
||||
@@ -71,7 +72,7 @@ export default function ForgotPasswordScreen() {
|
||||
setIsLoading(false);
|
||||
|
||||
if (resetError) {
|
||||
showError(resetError.message);
|
||||
showError(getAuthError(resetError));
|
||||
} else {
|
||||
setIsEmailSent(true);
|
||||
setResendCooldown(60);
|
||||
@@ -92,7 +93,7 @@ export default function ForgotPasswordScreen() {
|
||||
|
||||
if (resendError) {
|
||||
if (Platform.OS !== 'web') {
|
||||
Alert.alert("Error", resendError.message);
|
||||
Alert.alert("Error", getAuthError(resendError));
|
||||
}
|
||||
} else {
|
||||
setResendCooldown(60);
|
||||
|
||||
@@ -29,6 +29,8 @@ if (Platform.OS === "ios") {
|
||||
}
|
||||
}
|
||||
import { supabase } from "../../src/lib/supabase";
|
||||
import { getAuthError } from "../../src/lib/errors";
|
||||
import { Events } from "../../src/lib/analytics";
|
||||
import { useAuthStore } from "../../src/store";
|
||||
import { GradientBackground, GlassCard, GlassButton, GlassInput } from "../../src/components/ui";
|
||||
import { colors, spacing, radius, typography } from "../../src/theme";
|
||||
@@ -96,7 +98,7 @@ export default function LoginScreen() {
|
||||
|
||||
if (resendError) {
|
||||
if (Platform.OS !== 'web') {
|
||||
Alert.alert("Error", resendError.message);
|
||||
Alert.alert("Error", getAuthError(resendError));
|
||||
}
|
||||
} else {
|
||||
// Start 60 second cooldown
|
||||
@@ -125,7 +127,7 @@ export default function LoginScreen() {
|
||||
Alert.alert("Not yet verified", "Please check your email and click the verification link.");
|
||||
}
|
||||
} else {
|
||||
Alert.alert("Error", signInError.message);
|
||||
Alert.alert("Error", getAuthError(signInError));
|
||||
}
|
||||
}
|
||||
// If successful, the auth state change will handle the redirect
|
||||
@@ -175,9 +177,10 @@ export default function LoginScreen() {
|
||||
setIsLoading(false);
|
||||
|
||||
if (authError) {
|
||||
showError(authError.message);
|
||||
showError(getAuthError(authError));
|
||||
} else {
|
||||
setIsMagicLinkSent(true);
|
||||
Events.signIn("magic_link");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -207,7 +210,7 @@ export default function LoginScreen() {
|
||||
setIsLoading(false);
|
||||
|
||||
if (signUpError) {
|
||||
showError(signUpError.message);
|
||||
showError(getAuthError(signUpError));
|
||||
} else if (data.user && data.user.identities && data.user.identities.length === 0) {
|
||||
// User already exists - Supabase returns success but empty identities
|
||||
// for security (prevents email enumeration)
|
||||
@@ -218,6 +221,7 @@ export default function LoginScreen() {
|
||||
email: email.trim(),
|
||||
password,
|
||||
});
|
||||
Events.signUp("password");
|
||||
}
|
||||
} else {
|
||||
const { error: signInError } = await supabase.auth.signInWithPassword({
|
||||
@@ -227,7 +231,9 @@ export default function LoginScreen() {
|
||||
|
||||
setIsLoading(false);
|
||||
|
||||
if (signInError) {
|
||||
if (!signInError) {
|
||||
Events.signIn("password");
|
||||
} else if (signInError) {
|
||||
// Check if the error is due to unverified email
|
||||
if (signInError.message.includes("Email not confirmed")) {
|
||||
// Show the verification pending screen
|
||||
@@ -236,7 +242,7 @@ export default function LoginScreen() {
|
||||
password,
|
||||
});
|
||||
} else {
|
||||
showError(signInError.message);
|
||||
showError(getAuthError(signInError));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -264,7 +270,9 @@ export default function LoginScreen() {
|
||||
|
||||
if (error) {
|
||||
console.log("[Apple Sign In] Supabase error:", error.message);
|
||||
Alert.alert("Error", error.message);
|
||||
Alert.alert("Error", getAuthError(error));
|
||||
} else {
|
||||
Events.signIn("apple");
|
||||
}
|
||||
} else {
|
||||
Alert.alert("Error", "No identity token received from Apple");
|
||||
@@ -274,7 +282,7 @@ export default function LoginScreen() {
|
||||
if (e.code === "ERR_REQUEST_CANCELED") {
|
||||
// User canceled - do nothing
|
||||
} else {
|
||||
Alert.alert("Apple Sign In Error", `${e.code || 'Unknown'}: ${e.message || "Apple sign in failed"}`);
|
||||
Alert.alert("Error", "Apple sign in failed. Please try again.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -288,7 +296,7 @@ export default function LoginScreen() {
|
||||
});
|
||||
|
||||
if (error) {
|
||||
Alert.alert("Error", error.message);
|
||||
Alert.alert("Error", getAuthError(error));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ import Animated, {
|
||||
} from "react-native-reanimated";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { supabase } from "../../src/lib/supabase";
|
||||
import { getAuthError } from "../../src/lib/errors";
|
||||
import { GradientBackground, GlassCard, GlassButton, GlassInput } from "../../src/components/ui";
|
||||
import { colors, spacing, radius, typography } from "../../src/theme";
|
||||
|
||||
@@ -59,14 +60,7 @@ export default function ResetPasswordScreen() {
|
||||
setIsLoading(false);
|
||||
|
||||
if (updateError) {
|
||||
// Handle common errors
|
||||
if (updateError.message.includes("same as")) {
|
||||
showError("New password must be different from your current password");
|
||||
} else if (updateError.message.includes("session")) {
|
||||
showError("Your reset link has expired. Please request a new one.");
|
||||
} else {
|
||||
showError(updateError.message);
|
||||
}
|
||||
showError(getAuthError(updateError));
|
||||
} else {
|
||||
setIsSuccess(true);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@ import "../src/polyfills/web";
|
||||
import { initSentry, setUserContext, clearUserContext } from "../src/lib/sentry";
|
||||
initSentry();
|
||||
|
||||
// Initialize Firebase Analytics
|
||||
import { initAnalytics, setUserId, clearUserId } from "../src/lib/analytics";
|
||||
initAnalytics();
|
||||
|
||||
// Import push notification utilities
|
||||
import {
|
||||
clearPushToken,
|
||||
@@ -153,6 +157,8 @@ export default function RootLayout() {
|
||||
// Set Sentry user context for error tracking
|
||||
if (session?.user) {
|
||||
setUserContext(session.user.id, session.user.email);
|
||||
// Set Analytics user ID
|
||||
setUserId(session.user.id);
|
||||
}
|
||||
} else if (event === "SIGNED_OUT") {
|
||||
// Clear push token before clearing user state
|
||||
@@ -162,6 +168,7 @@ export default function RootLayout() {
|
||||
}
|
||||
setUser(null);
|
||||
clearUserContext();
|
||||
clearUserId();
|
||||
// Clear React Query cache
|
||||
queryClient.clear();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user