Enhance swipe UI and add chat date separators

This commit is contained in:
jjbrunton committed 2026-01-03 18:37:18 +00:00
1 parent 7e686c147a
commit a567fba349
12 files changed
+507 -286

No files matched your search

+30 -30
View File
@@ -11,8 +11,8 @@ import Animated, {
FadeInRight,
FadeInUp,
} from "react-native-reanimated";
import { GradientBackground, GlassCard, GlassButton, DecorativeSeparator } from "../../src/components/ui";
import { useAmbientOrbAnimation } from "../../src/hooks";
import { GradientBackground, GlassCard, GlassButton, DecorativeSeparator } from "../../src/components/ui";
import { useAmbientOrbAnimation } from "../../src/hooks";
import { colors, gradients, spacing, typography, radius, shadows } from "../../src/theme";
import { MatchesTutorial } from "../../src/components/tutorials";
import { hasSeenMatchesTutorial, markMatchesTutorialSeen } from "../../src/lib/matchesTutorialSeen";
@@ -29,11 +29,11 @@ export default function MatchesScreen() {
const { matches, fetchMatches, markAllAsSeen } = useMatchStore();
const { user } = useAuthStore();
const router = useRouter();
const { width } = useWindowDimensions();
const isWideScreen = width > MAX_CONTENT_WIDTH;
const [showTutorial, setShowTutorial] = useState(false);
// Ambient orb breathing animations
const { width } = useWindowDimensions();
const isWideScreen = width > MAX_CONTENT_WIDTH;
const [showTutorial, setShowTutorial] = useState(false);
// Ambient orb breathing animations
const { orbStyle1, orbStyle2 } = useAmbientOrbAnimation();
// Check if tutorial should be shown when screen is focused or matches change
@@ -101,15 +101,15 @@ export default function MatchesScreen() {
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
/>
{/* Top silk highlight */}
<LinearGradient
colors={[`${ACCENT_RGBA}0.06)`, 'transparent']}
style={styles.cardSilkHighlight}
start={{ x: 0.5, y: 0 }}
end={{ x: 0.5, y: 1 }}
/>
<View style={styles.matchRow}>
{/* Top silk highlight */}
<LinearGradient
colors={[`${ACCENT_RGBA}0.06)`, 'transparent']}
style={styles.cardSilkHighlight}
start={{ x: 0.5, y: 0 }}
end={{ x: 0.5, y: 1 }}
/>
<View style={styles.matchRow}>
{/* Premium icon container */}
<View style={[
styles.iconContainerPremium,
@@ -295,13 +295,13 @@ export default function MatchesScreen() {
/>
</View>
{/* Matches Tutorial Overlay */}
{showTutorial && (
<MatchesTutorial onComplete={handleTutorialComplete} />
)}
</GradientBackground>
);
}
{/* Matches Tutorial Overlay */}
{showTutorial && (
<MatchesTutorial onComplete={handleTutorialComplete} />
)}
</GradientBackground>
);
}
const styles = StyleSheet.create({
container: {
@@ -387,13 +387,13 @@ const styles = StyleSheet.create({
maxWidth: MAX_CONTENT_WIDTH,
},
// Premium Match Card
matchCardPremium: {
marginBottom: spacing.sm,
borderRadius: radius.lg,
overflow: 'hidden',
padding: spacing.md,
},
cardSilkHighlight: {
matchCardPremium: {
marginBottom: spacing.sm,
borderRadius: radius.lg,
overflow: 'hidden',
padding: spacing.md,
},
cardSilkHighlight: {
position: 'absolute',
top: 0,
left: 0,
+37 -23
View File
@@ -20,20 +20,22 @@ import { skipQuestion, getSkippedQuestionIds } from "../../src/lib/skippedQuesti
import { hasSeenSwipeTutorial, markSwipeTutorialSeen } from "../../src/lib/swipeTutorialSeen";
import { invokeWithAuthRetry } from "../../src/lib/authErrorHandler";
// import SwipeCard from "../../src/components/SwipeCard";
import { SwipeCardPremium as SwipeCard } from "../../src/components/swipe"; // PoC: Premium styling
import { SwipeTutorial } from "../../src/components/tutorials";
import { GradientBackground, GlassCard, GlassButton, DecorativeSeparator } from "../../src/components/ui";
import { SwipeCardPremium as SwipeCard } from "../../src/components/swipe"; // PoC: Premium styling
import { SwipeTutorial } from "../../src/components/tutorials";
import { QuestionFeedbackModal } from "../../src/components/feedback";
import { GradientBackground, GlassCard, GlassButton, DecorativeSeparator } from "../../src/components/ui";
import { Events } from "../../src/lib/analytics";
import { colors, gradients, spacing, typography, radius, shadows } from "../../src/theme";
export default function SwipeScreen() {
const { packId } = useLocalSearchParams();
const [questions, setQuestions] = useState<any[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [currentIndex, setCurrentIndex] = useState(0);
const [showTutorial, setShowTutorial] = useState(false);
const [isBlocked, setIsBlocked] = useState(false);
const [gapInfo, setGapInfo] = useState<{ unanswered: number; threshold: number } | null>(null);
const [questions, setQuestions] = useState<any[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [currentIndex, setCurrentIndex] = useState(0);
const [showTutorial, setShowTutorial] = useState(false);
const [feedbackQuestion, setFeedbackQuestion] = useState<{id: string, text: string} | null>(null);
const [isBlocked, setIsBlocked] = useState(false);
const [gapInfo, setGapInfo] = useState<{ unanswered: number; threshold: number } | null>(null);
const { enabledPackIds, fetchPacks } = usePacksStore();
const { partner, couple } = useAuthStore();
const hasTrackedExhausted = useRef(false);
@@ -572,13 +574,17 @@ export default function SwipeScreen() {
<View style={[styles.stackCard, styles.stackCardSecond]} />
)}
{/* Active card */}
<SwipeCard
key={questions[currentIndex].id}
question={questions[currentIndex]}
onSwipe={handleSwipe}
/>
</View>
{/* Active card */}
<SwipeCard
key={questions[currentIndex].id}
question={questions[currentIndex]}
onSwipe={handleSwipe}
onReport={() => setFeedbackQuestion({
id: questions[currentIndex].id,
text: questions[currentIndex].text
})}
/>
</View>
{/* Hint - Premium styling */}
<Animated.View
@@ -592,13 +598,21 @@ export default function SwipeScreen() {
<View style={styles.bottomSpacer} />
</View>
{/* Tutorial Overlay */}
{showTutorial && (
<SwipeTutorial onComplete={handleTutorialComplete} />
)}
</GradientBackground>
);
}
{/* Tutorial Overlay */}
{showTutorial && (
<SwipeTutorial onComplete={handleTutorialComplete} />
)}
{/* Question Feedback Modal */}
<QuestionFeedbackModal
visible={!!feedbackQuestion}
onClose={() => setFeedbackQuestion(null)}
questionId={feedbackQuestion?.id || ''}
questionText={feedbackQuestion?.text || ''}
/>
</GradientBackground>
);
}
const styles = StyleSheet.create({
container: {
+1 -1
View File
@@ -781,7 +781,7 @@ const styles = StyleSheet.create({
color: colors.primary,
},
forgotPassword: {
alignSelf: "flex-end",
alignSelf: "center",
marginTop: -spacing.xs,
marginBottom: spacing.xs,
},
@@ -505,7 +505,10 @@
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = "$(inherited) ";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
@@ -560,7 +563,10 @@
);
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_LDFLAGS = "$(inherited) ";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;
@@ -1,5 +1,5 @@
import { useState, useEffect } from "react";
import { View, Text, StyleSheet, Platform, useWindowDimensions } from "react-native";
import { View, Text, StyleSheet, Platform, useWindowDimensions, TouchableOpacity as RNTouchableOpacity } from "react-native";
import { Gesture, GestureDetector, TouchableOpacity } from "react-native-gesture-handler";
import { BlurView } from "expo-blur";
import { LinearGradient } from "expo-linear-gradient";
@@ -36,7 +36,7 @@ const MAX_CARD_WIDTH = 400;
const SWIPE_THRESHOLD = 100;
interface Props {
question: { id: string; text: string; intensity: number; partner_text?: string | null; is_two_part?: boolean };
question: { id: string; text: string; intensity: number; partner_text?: string | null; is_two_part?: boolean; partner_answered?: boolean };
onSwipe: (direction: "left" | "right" | "up" | "down") => void;
}
@@ -305,14 +305,18 @@ function CardContent({
/>
{/* Feedback Button */}
<View style={styles.feedbackButton}>
<RNTouchableOpacity
style={styles.feedbackButton}
onPress={onFeedbackPress}
hitSlop={{ top: 20, bottom: 20, left: 20, right: 20 }}
activeOpacity={0.7}
>
<Ionicons
name="flag-outline"
size={18}
size={22}
color={colors.textTertiary}
onPress={onFeedbackPress}
/>
</View>
</RNTouchableOpacity>
{/* Overlays with gradient badges */}
<Animated.View style={[styles.overlay, overlayStyle("right")]} pointerEvents="none">
@@ -368,6 +372,21 @@ function CardContent({
<Text style={[styles.text, question.is_two_part && styles.twoPartText]}>
{question.text}
</Text>
{/* Partner's version of the question - only show when user is answering first */}
{question.partner_text && !question.partner_answered && (
<View style={styles.partnerTextContainer}>
<View style={styles.partnerDivider}>
<View style={styles.partnerDividerLine} />
<View style={styles.partnerLabelContainer}>
<Ionicons name="swap-horizontal" size={12} color={colors.secondary} />
<Text style={styles.partnerLabel}>Partner sees</Text>
</View>
<View style={styles.partnerDividerLine} />
</View>
<Text style={styles.partnerText}>{question.partner_text}</Text>
</View>
)}
</View>
{/* Action Buttons */}
@@ -524,14 +543,14 @@ const styles = StyleSheet.create({
position: 'absolute',
top: spacing.md,
right: spacing.md,
width: 36,
height: 36,
borderRadius: 18,
width: 44,
height: 44,
borderRadius: 22,
backgroundColor: colors.glass.background,
justifyContent: 'center',
alignItems: 'center',
zIndex: 100,
elevation: 10,
zIndex: 999,
elevation: 999,
borderWidth: 1,
borderColor: colors.glass.border,
},
@@ -565,6 +584,42 @@ const styles = StyleSheet.create({
...typography.title3,
lineHeight: 28,
},
partnerTextContainer: {
marginTop: spacing.lg,
width: '100%',
paddingHorizontal: spacing.sm,
},
partnerDivider: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: spacing.sm,
},
partnerDividerLine: {
flex: 1,
height: 1,
backgroundColor: colors.glass.border,
},
partnerLabelContainer: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: spacing.md,
paddingVertical: spacing.xs,
backgroundColor: colors.secondaryLight,
borderRadius: radius.full,
gap: spacing.xs,
},
partnerLabel: {
...typography.caption1,
color: colors.secondary,
fontWeight: '500',
letterSpacing: 0.3,
},
partnerText: {
...typography.subhead,
color: colors.textSecondary,
textAlign: 'center',
fontStyle: 'italic',
},
footer: {
paddingBottom: spacing.lg,
paddingHorizontal: spacing.lg,
@@ -7,7 +7,7 @@ const MAX_CARD_WIDTH = 400;
const SWIPE_THRESHOLD = 100;
interface Props {
question: { id: string; text: string; intensity: number; partner_text?: string | null };
question: { id: string; text: string; intensity: number; partner_text?: string | null; partner_answered?: boolean };
onSwipe: (direction: "left" | "right" | "up" | "down") => void;
}
@@ -208,6 +208,21 @@ export default function SwipeCard({ question, onSwipe }: Props) {
))}
</View>
<Text style={[styles.text, question.partner_text ? styles.twoPartText : null]}>{question.text}</Text>
{/* Partner's version of the question - only show when user is answering first */}
{question.partner_text && !question.partner_answered && (
<View style={styles.partnerTextContainer}>
<View style={styles.partnerDivider}>
<View style={styles.partnerDividerLine} />
<View style={styles.partnerLabelContainer}>
<Ionicons name="swap-horizontal" size={12} color="#9b59b6" />
<Text style={styles.partnerLabel}>Partner sees</Text>
</View>
<View style={styles.partnerDividerLine} />
</View>
<Text style={styles.partnerText}>{question.partner_text}</Text>
</View>
)}
</View>
<View style={styles.footer}>
@@ -369,26 +384,41 @@ const styles = StyleSheet.create({
paddingVertical: 8,
borderRadius: 12,
},
partnerContainer: {
partnerTextContainer: {
marginTop: 24,
width: "100%",
alignItems: "center",
paddingHorizontal: 8,
},
divider: {
partnerDivider: {
flexDirection: "row",
alignItems: "center",
marginBottom: 8,
},
partnerDividerLine: {
flex: 1,
height: 1,
width: "80%",
backgroundColor: "rgba(255,255,255,0.1)",
marginVertical: 24,
position: "relative",
justifyContent: "center",
alignItems: "center",
backgroundColor: "rgba(255, 255, 255, 0.08)",
},
dividerText: {
color: "#666",
backgroundColor: "#16213e",
paddingHorizontal: 10,
partnerLabelContainer: {
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 16,
paddingVertical: 4,
backgroundColor: "rgba(155, 89, 182, 0.15)",
borderRadius: 9999,
gap: 4,
},
partnerLabel: {
fontSize: 12,
fontWeight: "bold",
position: "absolute",
color: "#9b59b6",
fontWeight: "500",
letterSpacing: 0.3,
},
partnerText: {
fontSize: 14,
color: "rgba(255, 255, 255, 0.7)",
textAlign: "center",
fontStyle: "italic",
},
buttonContainer: {
flexDirection: "row",
@@ -53,11 +53,12 @@ const MAX_CARD_WIDTH = 400;
const SWIPE_THRESHOLD = 100;
interface Props {
question: { id: string; text: string; intensity: number; partner_text?: string | null; is_two_part?: boolean };
question: { id: string; text: string; intensity: number; partner_text?: string | null; is_two_part?: boolean; partner_answered?: boolean };
onSwipe: (direction: "left" | "right" | "up" | "down") => void;
onReport?: () => void;
}
export default function SwipeCardPremium({ question, onSwipe }: Props) {
export default function SwipeCardPremium({ question, onSwipe, onReport }: Props) {
const { width: screenWidth } = useWindowDimensions();
const cardWidth = Math.min(screenWidth - 48, MAX_CARD_WIDTH);
@@ -173,6 +174,17 @@ export default function SwipeCardPremium({ question, onSwipe }: Props) {
return (
<View style={[styles.cardWrapper, { width: cardWidth }]}>
{/* Report Button - outside GestureDetector to receive touches */}
{onReport && (
<Pressable
style={styles.reportButton}
onPress={onReport}
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
>
<Ionicons name="flag-outline" size={16} color={colors.textTertiary} />
</Pressable>
)}
<GestureDetector gesture={gesture}>
<Animated.View style={[styles.cardOuter, animatedStyle, cardShadowStyle, { shadowColor: ACCENT }]}>
{/* Premium dark background */}
@@ -244,6 +256,21 @@ export default function SwipeCardPremium({ question, onSwipe }: Props) {
{question.text}
</Text>
{/* Partner's version of the question - only show when user is answering first */}
{question.partner_text && !question.partner_answered && (
<View style={styles.partnerTextContainer}>
<View style={styles.partnerDivider}>
<View style={styles.partnerDividerLine} />
<View style={styles.partnerLabelBadge}>
<Ionicons name="swap-horizontal" size={10} color={ROSE} />
<Text style={styles.partnerLabelText}>PARTNER SEES</Text>
</View>
<View style={styles.partnerDividerLine} />
</View>
<Text style={styles.partnerQuestionText}>{question.partner_text}</Text>
</View>
)}
{/* Bottom separator */}
<View style={styles.separator}>
<View style={styles.separatorLine} />
@@ -366,6 +393,14 @@ const styles = StyleSheet.create({
right: 0,
height: 120,
},
reportButton: {
position: 'absolute',
top: spacing.md,
right: spacing.md,
zIndex: 100,
padding: spacing.sm,
opacity: 0.6,
},
premiumBorder: {
position: 'absolute',
top: 0,
@@ -430,6 +465,45 @@ const styles = StyleSheet.create({
lineHeight: 32,
paddingHorizontal: spacing.sm,
},
partnerTextContainer: {
marginTop: spacing.lg,
width: '100%',
paddingHorizontal: spacing.sm,
},
partnerDivider: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: spacing.sm,
},
partnerDividerLine: {
flex: 1,
height: 1,
backgroundColor: rgba.rose(0.2),
},
partnerLabelBadge: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: spacing.md,
paddingVertical: spacing.xs,
backgroundColor: rgba.rose(0.1),
borderRadius: radius.full,
borderWidth: 1,
borderColor: rgba.rose(0.2),
gap: spacing.xs,
},
partnerLabelText: {
...typography.caption2,
fontWeight: '600',
letterSpacing: 1.5,
color: rgba.rose(0.8),
},
partnerQuestionText: {
...typography.subhead,
color: colors.textSecondary,
textAlign: 'center',
fontStyle: 'italic',
opacity: 0.85,
},
footer: {
paddingBottom: spacing.xl,
paddingHorizontal: spacing.lg,
@@ -78,24 +78,24 @@ const STEPS: TutorialStep[] = [
icon: "arrow-down",
color: colors.textTertiary,
gradient: ["#6c757d", "#495057"],
translateX: 0,
translateY: 80,
isSwipe: true,
},
{
key: "feedback",
label: "FEEDBACK",
description: "Tap the flag to report issues with a question",
icon: "flag-outline",
color: colors.primary,
gradient: gradients.primary as [string, string],
translateX: 0,
translateY: 0,
isSwipe: false,
},
];
export default function SwipeTutorial({ onComplete }: Props) {
translateX: 0,
translateY: 80,
isSwipe: true,
},
{
key: "feedback",
label: "FEEDBACK",
description: "Tap the flag to report issues with a question",
icon: "flag-outline",
color: colors.primary,
gradient: gradients.primary as [string, string],
translateX: 0,
translateY: 0,
isSwipe: false,
},
];
export default function SwipeTutorial({ onComplete }: Props) {
const { width: screenWidth, height: screenHeight } = useWindowDimensions();
const insets = useSafeAreaInsets();
const [currentStep, setCurrentStep] = useState(0);
@@ -330,27 +330,27 @@ export default function SwipeTutorial({ onComplete }: Props) {
</View>
)}
{/* Demo Card */}
<Animated.View style={[styles.demoCard, cardAnimatedStyle, shadows.xl]}>
<LinearGradient
colors={[colors.glass.backgroundLight, colors.glass.background]}
style={styles.demoCardInner}
>
{/* Feedback button indicator */}
<View style={[
styles.feedbackButtonDemo,
!isSwipeStep && styles.feedbackButtonHighlight
]}>
{!isSwipeStep && (
<Animated.View style={[styles.feedbackPulse, pulseAnimatedStyle]}>
<View style={[styles.pulseInner, { backgroundColor: currentStep_.color }]} />
</Animated.View>
)}
<Ionicons name="flag-outline" size={18} color={!isSwipeStep ? colors.text : colors.textTertiary} />
</View>
{/* Overlay - only for swipe steps */}
{isSwipeStep && (
{/* Demo Card */}
<Animated.View style={[styles.demoCard, cardAnimatedStyle, shadows.xl]}>
<LinearGradient
colors={[colors.glass.backgroundLight, colors.glass.background]}
style={styles.demoCardInner}
>
{/* Feedback button indicator */}
<View style={[
styles.feedbackButtonDemo,
!isSwipeStep && styles.feedbackButtonHighlight
]}>
{!isSwipeStep && (
<Animated.View style={[styles.feedbackPulse, pulseAnimatedStyle]}>
<View style={[styles.pulseInner, { backgroundColor: currentStep_.color }]} />
</Animated.View>
)}
<Ionicons name="flag-outline" size={18} color={!isSwipeStep ? colors.text : colors.textTertiary} />
</View>
{/* Overlay - only for swipe steps */}
{isSwipeStep && (
<Animated.View
style={[
styles.demoOverlay,
@@ -552,40 +552,40 @@ const styles = StyleSheet.create({
borderRadius: radius.xl,
overflow: "visible",
},
demoCardInner: {
flex: 1,
borderRadius: radius.xl,
borderWidth: 1,
borderColor: colors.glass.border,
overflow: "hidden",
},
feedbackButtonDemo: {
position: 'absolute',
top: spacing.md,
right: spacing.md,
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: colors.glass.background,
justifyContent: 'center',
alignItems: 'center',
zIndex: 100,
borderWidth: 1,
borderColor: colors.glass.border,
},
feedbackButtonHighlight: {
backgroundColor: colors.primary,
borderColor: colors.primary,
},
feedbackPulse: {
position: "absolute",
width: 56,
height: 56,
borderRadius: 28,
justifyContent: "center",
alignItems: "center",
},
demoOverlay: {
demoCardInner: {
flex: 1,
borderRadius: radius.xl,
borderWidth: 1,
borderColor: colors.glass.border,
overflow: "hidden",
},
feedbackButtonDemo: {
position: 'absolute',
top: spacing.md,
right: spacing.md,
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: colors.glass.background,
justifyContent: 'center',
alignItems: 'center',
zIndex: 100,
borderWidth: 1,
borderColor: colors.glass.border,
},
feedbackButtonHighlight: {
backgroundColor: colors.primary,
borderColor: colors.primary,
},
feedbackPulse: {
position: "absolute",
width: 56,
height: 56,
borderRadius: 28,
justifyContent: "center",
alignItems: "center",
},
demoOverlay: {
...StyleSheet.absoluteFillObject,
justifyContent: "center",
alignItems: "center",
+1 -1
View File
@@ -282,7 +282,7 @@ export const ChatScreen: React.FC = () => {
user={user}
match={match}
insets={insets}
onBack={() => router.back()}
onBack={() => router.push('/(app)/matches')}
/>
<ChatMessages
@@ -5,6 +5,7 @@ import Animated, { FadeIn } from 'react-native-reanimated';
import { MessageBubble } from './MessageBubble';
import { MessageContent } from './MessageContent';
import { DateSeparator } from './DateSeparator';
import { UploadProgress } from './UploadProgress';
import { TypingIndicator } from './TypingIndicator';
import { DecorativeSeparator } from '../../../components/ui';
@@ -29,6 +30,17 @@ interface ChatMessagesProps {
const ACCENT = colors.premium.gold;
const ACCENT_RGBA = 'rgba(212, 175, 55, ';
/**
* Check if two dates are on the same calendar day
*/
function isSameDay(date1: Date, date2: Date): boolean {
return (
date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate()
);
}
export const ChatMessages: React.FC<ChatMessagesProps> = ({
messages,
userId,
@@ -43,22 +55,35 @@ export const ChatMessages: React.FC<ChatMessagesProps> = ({
const renderMessage = ({ item, index }: { item: Message; index: number }) => {
const isMe = item.user_id === userId;
const messageDate = new Date(item.created_at!);
// Determine if we should show a date separator above this message
// In an inverted list, index 0 is newest (bottom), higher indices are older (top)
// Show separator when this message's date differs from the next (older) message
const nextMessage = messages[index + 1];
const showDateSeparator = !nextMessage || (
nextMessage.created_at &&
!isSameDay(messageDate, new Date(nextMessage.created_at))
);
return (
<MessageBubble
isMe={isMe}
index={index}
onLongPress={() => onMessageLongPress(item, isMe)}
>
<MessageContent
item={item}
<>
<MessageBubble
isMe={isMe}
currentUserId={userId || ''}
revealMessage={revealMessage}
onImagePress={onImagePress}
onVideoFullScreen={onVideoFullScreen}
index={index}
onLongPress={() => onMessageLongPress(item, isMe)}
/>
</MessageBubble>
>
<MessageContent
item={item}
isMe={isMe}
currentUserId={userId || ''}
revealMessage={revealMessage}
onImagePress={onImagePress}
onVideoFullScreen={onVideoFullScreen}
/>
</MessageBubble>
{showDateSeparator && <DateSeparator date={messageDate} />}
</>
);
};
@@ -0,0 +1,84 @@
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { colors, spacing, typography, radius } from '../../../theme';
interface DateSeparatorProps {
date: Date;
}
/**
* Formats a date for display in chat, similar to WhatsApp/Signal:
* - "Today" for today's date
* - "Yesterday" for yesterday
* - "Monday" through "Saturday" for the past week
* - "Jan 3, 2024" for older dates
*/
function formatDateLabel(date: Date): string {
const now = new Date();
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const messageDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
const diffTime = today.getTime() - messageDate.getTime();
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
if (diffDays === 0) {
return 'Today';
}
if (diffDays === 1) {
return 'Yesterday';
}
// Within the last week, show day name
if (diffDays < 7) {
return date.toLocaleDateString(undefined, { weekday: 'long' });
}
// Same year - show "Jan 3"
if (date.getFullYear() === now.getFullYear()) {
return date.toLocaleDateString(undefined, {
month: 'short',
day: 'numeric'
});
}
// Different year - show "Jan 3, 2024"
return date.toLocaleDateString(undefined, {
month: 'short',
day: 'numeric',
year: 'numeric'
});
}
export const DateSeparator: React.FC<DateSeparatorProps> = ({ date }) => {
const label = formatDateLabel(date);
return (
<View style={styles.container}>
<View style={styles.pill}>
<Text style={styles.text}>{label}</Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
alignItems: 'center',
justifyContent: 'center',
paddingVertical: spacing.md,
},
pill: {
backgroundColor: colors.glass.background,
borderRadius: radius.full,
paddingHorizontal: spacing.md,
paddingVertical: spacing.xs + 2,
borderWidth: 1,
borderColor: colors.glass.border,
},
text: {
...typography.caption1,
color: colors.textSecondary,
textAlign: 'center',
},
});
+47 -114
View File
@@ -182,6 +182,7 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
@@ -585,7 +586,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz",
"integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/traverse": "^7.28.5"
@@ -602,7 +602,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz",
"integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -618,7 +617,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz",
"integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -634,7 +632,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz",
"integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
@@ -652,7 +649,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz",
"integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/traverse": "^7.28.3"
@@ -753,7 +749,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
"integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6.9.0"
},
@@ -874,7 +869,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz",
"integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1061,7 +1055,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
"integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
"@babel/helper-plugin-utils": "^7.18.6"
@@ -1127,7 +1120,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz",
"integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1174,7 +1166,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz",
"integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.28.3",
"@babel/helper-plugin-utils": "^7.27.1"
@@ -1243,7 +1234,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz",
"integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.27.1",
"@babel/helper-plugin-utils": "^7.27.1"
@@ -1260,7 +1250,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz",
"integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1276,7 +1265,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz",
"integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.27.1",
"@babel/helper-plugin-utils": "^7.27.1"
@@ -1293,7 +1281,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz",
"integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1309,7 +1296,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz",
"integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/plugin-transform-destructuring": "^7.28.0"
@@ -1326,7 +1312,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.5.tgz",
"integrity": "sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1406,7 +1391,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz",
"integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1452,7 +1436,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz",
"integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1468,7 +1451,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz",
"integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-module-transforms": "^7.27.1",
"@babel/helper-plugin-utils": "^7.27.1"
@@ -1501,7 +1483,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.28.5.tgz",
"integrity": "sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-module-transforms": "^7.28.3",
"@babel/helper-plugin-utils": "^7.27.1",
@@ -1520,7 +1501,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz",
"integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-module-transforms": "^7.27.1",
"@babel/helper-plugin-utils": "^7.27.1"
@@ -1553,7 +1533,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz",
"integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1618,7 +1597,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz",
"integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/helper-replace-supers": "^7.27.1"
@@ -1714,7 +1692,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz",
"integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1840,7 +1817,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz",
"integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.27.1",
"@babel/helper-plugin-utils": "^7.27.1"
@@ -1857,7 +1833,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz",
"integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1954,7 +1929,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz",
"integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1989,7 +1963,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz",
"integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -2005,7 +1978,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz",
"integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.27.1",
"@babel/helper-plugin-utils": "^7.27.1"
@@ -2038,7 +2010,6 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz",
"integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.27.1",
"@babel/helper-plugin-utils": "^7.27.1"
@@ -2157,7 +2128,6 @@
"resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
"integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/types": "^7.4.4",
@@ -6267,6 +6237,7 @@
"resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.23.1.tgz",
"integrity": "sha512-Qd2kQ3yi6Y3+AcUlrHxSLlnBvpdCEMVGFlVBneVOjaFaPU61g1huc38g339ysXspwY1QZA2aNhrk/KlHGO+ewA==",
"license": "MIT",
"peer": true,
"dependencies": {
"merge-options": "^3.0.4"
},
@@ -6712,6 +6683,7 @@
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.26.tgz",
"integrity": "sha512-RhKmeD0E2ejzKS6z8elAfdfwShpcdkYY8zJzvHYLq+wv183BBcElTeyMLcIX6wIn7QutXeI92Yi21t7aUWfqNQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@react-navigation/core": "^7.13.7",
"escape-string-regexp": "^4.0.0",
@@ -7883,7 +7855,6 @@
"integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/estree": "*",
"@types/json-schema": "*"
@@ -7895,7 +7866,6 @@
"integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/eslint": "*",
"@types/estree": "*"
@@ -8109,6 +8079,7 @@
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.2.2"
@@ -8120,6 +8091,7 @@
"integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
"devOptional": true,
"license": "MIT",
"peer": true,
"peerDependencies": {
"@types/react": "^18.0.0"
}
@@ -8236,7 +8208,6 @@
"integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@webassemblyjs/helper-numbers": "1.13.2",
"@webassemblyjs/helper-wasm-bytecode": "1.13.2"
@@ -8247,24 +8218,21 @@
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
"integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/@webassemblyjs/helper-api-error": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
"integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/@webassemblyjs/helper-buffer": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
"integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/@webassemblyjs/helper-numbers": {
"version": "1.13.2",
@@ -8272,7 +8240,6 @@
"integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@webassemblyjs/floating-point-hex-parser": "1.13.2",
"@webassemblyjs/helper-api-error": "1.13.2",
@@ -8284,8 +8251,7 @@
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
"integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/@webassemblyjs/helper-wasm-section": {
"version": "1.14.1",
@@ -8293,7 +8259,6 @@
"integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-buffer": "1.14.1",
@@ -8307,7 +8272,6 @@
"integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@xtuc/ieee754": "^1.2.0"
}
@@ -8318,7 +8282,6 @@
"integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@xtuc/long": "4.2.2"
}
@@ -8328,8 +8291,7 @@
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
"integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/@webassemblyjs/wasm-edit": {
"version": "1.14.1",
@@ -8337,7 +8299,6 @@
"integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-buffer": "1.14.1",
@@ -8355,7 +8316,6 @@
"integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-wasm-bytecode": "1.13.2",
@@ -8370,7 +8330,6 @@
"integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-buffer": "1.14.1",
@@ -8384,7 +8343,6 @@
"integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@webassemblyjs/ast": "1.14.1",
"@webassemblyjs/helper-api-error": "1.13.2",
@@ -8400,7 +8358,6 @@
"integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@webassemblyjs/ast": "1.14.1",
"@xtuc/long": "4.2.2"
@@ -8421,16 +8378,14 @@
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
"integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
"dev": true,
"license": "BSD-3-Clause",
"peer": true
"license": "BSD-3-Clause"
},
"node_modules/@xtuc/long": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
"dev": true,
"license": "Apache-2.0",
"peer": true
"license": "Apache-2.0"
},
"node_modules/abab": {
"version": "2.0.6",
@@ -8470,6 +8425,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -8494,7 +8450,6 @@
"integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=10.13.0"
},
@@ -8570,6 +8525,7 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -9159,8 +9115,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
"license": "ISC",
"peer": true
"license": "ISC"
},
"node_modules/bplist-creator": {
"version": "0.0.7",
@@ -9224,6 +9179,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -9620,7 +9576,6 @@
"integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6.0"
}
@@ -10112,7 +10067,6 @@
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
"integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
"license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^6.1.0",
@@ -10129,7 +10083,6 @@
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
"integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"mdn-data": "2.0.14",
"source-map": "^0.6.1"
@@ -10143,7 +10096,6 @@
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"license": "BSD-3-Clause",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -10153,7 +10105,6 @@
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
"integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
"license": "BSD-2-Clause",
"peer": true,
"engines": {
"node": ">= 6"
},
@@ -10686,7 +10637,6 @@
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
"license": "MIT",
"peer": true,
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
@@ -10706,8 +10656,7 @@
"url": "https://github.com/sponsors/fb55"
}
],
"license": "BSD-2-Clause",
"peer": true
"license": "BSD-2-Clause"
},
"node_modules/domexception": {
"version": "4.0.0",
@@ -10738,7 +10687,6 @@
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
"license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"domelementtype": "^2.3.0"
},
@@ -10754,7 +10702,6 @@
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
"integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
"license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"dom-serializer": "^2.0.0",
"domelementtype": "^2.3.0",
@@ -10840,7 +10787,8 @@
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-5.6.0.tgz",
"integrity": "sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/emoji-regex": {
"version": "8.0.0",
@@ -10872,7 +10820,6 @@
"integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
@@ -10886,7 +10833,6 @@
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"license": "BSD-2-Clause",
"peer": true,
"engines": {
"node": ">=0.12"
},
@@ -10944,8 +10890,7 @@
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz",
"integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
@@ -11089,7 +11034,6 @@
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
"dev": true,
"license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^4.1.1"
@@ -11104,7 +11048,6 @@
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
"dev": true,
"license": "BSD-2-Clause",
"peer": true,
"engines": {
"node": ">=4.0"
}
@@ -11128,7 +11071,6 @@
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
"dev": true,
"license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"estraverse": "^5.2.0"
},
@@ -11512,6 +11454,7 @@
"resolved": "https://registry.npmjs.org/expo/-/expo-52.0.48.tgz",
"integrity": "sha512-/HR/vuo57KGEWlvF3GWaquwEAjXuA5hrOCsaLcZ3pMSA8mQ27qKd1jva4GWzpxXYedlzs/7LLP1XpZo6hXTsog==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.20.0",
"@expo/cli": "0.22.27",
@@ -11661,6 +11604,7 @@
"resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-17.0.8.tgz",
"integrity": "sha512-XfWRyQAf1yUNgWZ1TnE8pFBMqGmFP5Gb+SFSgszxDdOoheB/NI5D4p7q86kI2fvGyfTrxAe+D+74nZkfsGvUlg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@expo/config": "~10.0.11",
"@expo/env": "~0.4.2"
@@ -11743,6 +11687,7 @@
"resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-18.0.12.tgz",
"integrity": "sha512-HAkrd/mb8r+G3lJ9MzmGeuW2B+BxQR1joKfeCyY4deLl1zoZ48FrAWjgZjHK9aHUVhJ0ehzInu/NQtikKytaeg==",
"license": "MIT",
"peer": true,
"dependencies": {
"web-streams-polyfill": "^3.3.2"
},
@@ -11843,6 +11788,7 @@
"resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-7.0.5.tgz",
"integrity": "sha512-3KptlJtcYDPWohk0MfJU75MJFh2ybavbtcSd84zEPfw9s1q3hjimw3sXnH03ZxP54kiEWldvKmmnGcVffBDB1g==",
"license": "MIT",
"peer": true,
"dependencies": {
"expo-constants": "~17.0.5",
"invariant": "^2.2.4"
@@ -12610,8 +12556,7 @@
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
"dev": true,
"license": "BSD-2-Clause",
"peer": true
"license": "BSD-2-Clause"
},
"node_modules/globby": {
"version": "11.1.0",
@@ -13611,6 +13556,7 @@
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@jest/core": "^29.7.0",
"@jest/types": "^29.6.3",
@@ -14053,8 +13999,7 @@
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0-rc-6230622a1a-20240610.tgz",
"integrity": "sha512-GTIQdJXthps5mgkIFo7yAq03M0QQYTfN8z+GrnMC/SCKFSuyFP5tk2BMaaWUsVy4u4r+dTLdiXH8JEivVls0Bw==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/jest-get-type": {
"version": "29.6.3",
@@ -14629,6 +14574,7 @@
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"jiti": "bin/jiti.js"
}
@@ -14926,6 +14872,7 @@
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.27.0.tgz",
"integrity": "sha512-8f7aNmS1+etYSLHht0fQApPc2kNO8qGRutifN5rVIc6Xo6ABsEbqOr758UwI7ALVbTt4x1fllKt0PYgzD9S3yQ==",
"license": "MPL-2.0",
"peer": true,
"dependencies": {
"detect-libc": "^1.0.3"
},
@@ -15174,7 +15121,6 @@
"integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6.11.5"
},
@@ -15415,8 +15361,7 @@
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
"integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
"license": "CC0-1.0",
"peer": true
"license": "CC0-1.0"
},
"node_modules/memoize-one": {
"version": "5.2.1",
@@ -16262,7 +16207,6 @@
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
"license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"boolbase": "^1.0.0"
},
@@ -16931,6 +16875,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -17333,7 +17278,6 @@
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"safe-buffer": "^5.1.0"
}
@@ -17367,6 +17311,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0"
},
@@ -17410,6 +17355,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.2"
@@ -17457,13 +17403,15 @@
"version": "19.2.3",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.3.tgz",
"integrity": "sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/react-native": {
"version": "0.76.9",
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.76.9.tgz",
"integrity": "sha512-+LRwecWmTDco7OweGsrECIqJu0iyrREd6CTCgC/uLLYipiHvk+MH9nd6drFtCw/6Blz6eoKTcH9YTTJusNtrWg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jest/create-cache-key-function": "^29.6.3",
"@react-native/assets-registry": "0.76.9",
@@ -17632,6 +17580,7 @@
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.16.7.tgz",
"integrity": "sha512-qoUUQOwE1pHlmQ9cXTJ2MX9FQ9eHllopCLiWOkDkp6CER95ZWeXhJCP4cSm6AD4jigL5jHcZf/SkWrg8ttZUsw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/plugin-transform-arrow-functions": "^7.0.0-0",
"@babel/plugin-transform-class-properties": "^7.0.0-0",
@@ -17656,6 +17605,7 @@
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.12.0.tgz",
"integrity": "sha512-ukk5PxcF4p3yu6qMZcmeiZgowhb5AsKRnil54YFUUAXVIS7PJcMHGGC+q44fCiBg44/1AJk5njGMez1m9H0BVQ==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"react": "*",
"react-native": "*"
@@ -17666,6 +17616,7 @@
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.4.0.tgz",
"integrity": "sha512-c7zc7Zwjty6/pGyuuvh9gK3YBYqHPOxrhXfG1lF4gHlojQSmIx2piNbNaV+Uykj+RDTmFXK0e/hA+fucw/Qozg==",
"license": "MIT",
"peer": true,
"dependencies": {
"react-freeze": "^1.0.0",
"warn-once": "^0.1.0"
@@ -17708,6 +17659,7 @@
"resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.19.13.tgz",
"integrity": "sha512-etv3bN8rJglrRCp/uL4p7l8QvUNUC++QwDbdZ8CB7BvZiMvsxfFIRM1j04vxNldG3uo2puRd6OSWR3ibtmc29A==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.18.6",
"@react-native/normalize-colors": "^0.74.1",
@@ -17806,6 +17758,7 @@
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/use-sync-external-store": "^0.0.6",
"use-sync-external-store": "^1.4.0"
@@ -17962,6 +17915,7 @@
"integrity": "sha512-KkAgygexHUkQqtvvx/otwxtuFu5cVjfzTCtjXLH9boS19/Nbtg84zS7wIQn39G8IlrhThBpQsMKkq5ZHZIYFXA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"react-is": "^18.3.1",
"react-shallow-renderer": "^16.15.0",
@@ -18142,7 +18096,8 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/redux-thunk": {
"version": "3.1.0",
@@ -18647,7 +18602,6 @@
"integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"dev": true,
"license": "BSD-3-Clause",
"peer": true,
"dependencies": {
"randombytes": "^2.1.0"
}
@@ -19439,31 +19393,12 @@
}
}
},
"node_modules/tailwindcss/node_modules/yaml": {
"version": "2.8.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
"integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
"dev": true,
"license": "ISC",
"optional": true,
"peer": true,
"bin": {
"yaml": "bin.mjs"
},
"engines": {
"node": ">= 14.6"
},
"funding": {
"url": "https://github.com/sponsors/eemeli"
}
},
"node_modules/tapable": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
"integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6"
},
@@ -19638,6 +19573,7 @@
"resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz",
"integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==",
"license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.15.0",
@@ -19657,7 +19593,6 @@
"integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.25",
"jest-worker": "^27.4.5",
@@ -19693,7 +19628,6 @@
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/node": "*",
"merge-stream": "^2.0.0",
@@ -19709,7 +19643,6 @@
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -20324,6 +20257,7 @@
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
@@ -20418,7 +20352,6 @@
"integrity": "sha512-e6vZvY6xboSwLz2GD36c16+O/2Z6fKvIf4pOXptw2rY9MVwE/TXc6RGqxD3I3x0a28lwBY7DE+76uTPSsBrrCA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.1.2"
@@ -20507,7 +20440,6 @@
"integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=10.13.0"
}
@@ -20698,6 +20630,7 @@
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=10.0.0"
},