mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-10-31 18:19:43 +00:00
Collapse match card header on keyboard open
This commit is contained in:
1 parent
58209b94ef
commit
474bbb6829
2 files changed
+141
-99
No files matched your search
@@ -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;
|
||||
|
||||
@@ -3,7 +3,14 @@ import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
|
||||
import { Image } from 'expo-image';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import Animated, { FadeIn, FadeInDown } from 'react-native-reanimated';
|
||||
import Animated, {
|
||||
FadeIn,
|
||||
FadeInDown,
|
||||
useAnimatedKeyboard,
|
||||
useAnimatedStyle,
|
||||
interpolate,
|
||||
Extrapolation
|
||||
} from 'react-native-reanimated';
|
||||
import { EdgeInsets } from 'react-native-safe-area-context';
|
||||
|
||||
import { DecorativeSeparator } from '../../../components/ui';
|
||||
@@ -31,6 +38,29 @@ export const ChatHeader: React.FC<ChatHeaderProps> = ({
|
||||
}) => {
|
||||
const isYesYes = match?.match_type === 'yes_yes';
|
||||
|
||||
// Modern reanimated keyboard hook
|
||||
const keyboard = useAnimatedKeyboard();
|
||||
|
||||
const containerAnimatedStyle = useAnimatedStyle(() => {
|
||||
// Collapse when keyboard height exceeds 50px
|
||||
const animationValue = interpolate(
|
||||
keyboard.height.value,
|
||||
[0, 60],
|
||||
[0, 1],
|
||||
Extrapolation.CLAMP
|
||||
);
|
||||
|
||||
return {
|
||||
opacity: interpolate(animationValue, [0, 1], [1, 0]),
|
||||
maxHeight: interpolate(animationValue, [0, 1], [500, 0]), // Assuming card max height
|
||||
marginTop: interpolate(animationValue, [0, 1], [spacing.sm, 0]),
|
||||
transform: [
|
||||
{ scale: interpolate(animationValue, [0, 1], [1, 0.95]) }
|
||||
],
|
||||
overflow: 'hidden',
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Animated.View
|
||||
@@ -102,106 +132,108 @@ export const ChatHeader: React.FC<ChatHeaderProps> = ({
|
||||
{match?.question && (
|
||||
<Animated.View
|
||||
entering={FadeInDown.delay(100).duration(400).springify()}
|
||||
style={styles.matchCard}
|
||||
style={[styles.matchCardContainer, containerAnimatedStyle]}
|
||||
>
|
||||
{/* Card gradient background */}
|
||||
<LinearGradient
|
||||
colors={isYesYes
|
||||
? ['rgba(212, 175, 55, 0.12)', 'rgba(184, 134, 11, 0.08)']
|
||||
: ['rgba(232, 164, 174, 0.1)', 'rgba(22, 33, 62, 0.6)']
|
||||
}
|
||||
style={StyleSheet.absoluteFill}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
/>
|
||||
|
||||
{/* Top glow for YES+YES */}
|
||||
{isYesYes && (
|
||||
<View style={styles.matchCard}>
|
||||
{/* Card gradient background */}
|
||||
<LinearGradient
|
||||
colors={[`${ACCENT_RGBA}0.15)`, 'transparent']}
|
||||
style={styles.matchCardGlow}
|
||||
start={{ x: 0.5, y: 0 }}
|
||||
end={{ x: 0.5, y: 1 }}
|
||||
colors={isYesYes
|
||||
? ['rgba(212, 175, 55, 0.12)', 'rgba(184, 134, 11, 0.08)']
|
||||
: ['rgba(232, 164, 174, 0.1)', 'rgba(22, 33, 62, 0.6)']
|
||||
}
|
||||
style={StyleSheet.absoluteFill}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Match Label */}
|
||||
<Text style={[styles.matchLabel, isYesYes && styles.matchLabelYesYes]}>
|
||||
{isYesYes ? "PERFECT MATCH" : "SOFT MATCH"}
|
||||
</Text>
|
||||
{/* Top glow for YES+YES */}
|
||||
{isYesYes && (
|
||||
<LinearGradient
|
||||
colors={[`${ACCENT_RGBA}0.15)`, 'transparent']}
|
||||
style={styles.matchCardGlow}
|
||||
start={{ x: 0.5, y: 0 }}
|
||||
end={{ x: 0.5, y: 1 }}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Decorative Separator */}
|
||||
<DecorativeSeparator
|
||||
variant={isYesYes ? "gold" : "rose"}
|
||||
width="60%"
|
||||
marginVertical={spacing.md}
|
||||
/>
|
||||
|
||||
{/* Question and Responses - Show who answered what */}
|
||||
{match.responses && match.responses.length > 0 ? (
|
||||
<View style={styles.responsesContainer}>
|
||||
{(() => {
|
||||
// Sort responses by created_at to determine who answered first
|
||||
const sortedResponses = [...match.responses].sort(
|
||||
(a: any, b: any) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
|
||||
);
|
||||
const isTwoPart = !!match.question.partner_text;
|
||||
|
||||
return sortedResponses.map((response: any, index: number) => {
|
||||
const isUser = response.user_id === user?.id;
|
||||
const isYes = response.answer === 'yes';
|
||||
const name = isUser ? 'You' : (response.profiles?.name?.split(' ')[0] || 'Partner');
|
||||
|
||||
// First responder sees main text, second sees partner_text
|
||||
const questionText = isTwoPart && index === 1
|
||||
? match.question.partner_text
|
||||
: match.question.text;
|
||||
|
||||
return (
|
||||
<View key={response.user_id} style={styles.responseCard}>
|
||||
{/* Name and Answer Badge */}
|
||||
<View style={styles.responseHeader}>
|
||||
<Text style={styles.responseName}>{name}</Text>
|
||||
{isYes ? (
|
||||
<LinearGradient
|
||||
colors={isYesYes
|
||||
? gradients.premiumGold as [string, string]
|
||||
: [colors.success, '#27ae60']
|
||||
}
|
||||
style={styles.answerBadge}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
>
|
||||
<Ionicons name="checkmark" size={10} color={colors.text} />
|
||||
<Text style={styles.answerBadgeText}>YES</Text>
|
||||
</LinearGradient>
|
||||
) : (
|
||||
<View style={styles.answerBadgeMaybe}>
|
||||
<Ionicons name="help" size={10} color={colors.warning} />
|
||||
<Text style={styles.answerBadgeTextMaybe}>MAYBE</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
{/* Question they saw */}
|
||||
<Text style={styles.responseQuestionText}>
|
||||
"{questionText}"
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
});
|
||||
})()}
|
||||
</View>
|
||||
) : (
|
||||
<Text style={styles.matchQuestionText}>
|
||||
"{match.question.text}"
|
||||
{/* Match Label */}
|
||||
<Text style={[styles.matchLabel, isYesYes && styles.matchLabelYesYes]}>
|
||||
{isYesYes ? "PERFECT MATCH" : "SOFT MATCH"}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{/* Premium border */}
|
||||
<View style={[
|
||||
styles.matchCardBorder,
|
||||
isYesYes && styles.matchCardBorderYesYes
|
||||
]} pointerEvents="none" />
|
||||
{/* Decorative Separator */}
|
||||
<DecorativeSeparator
|
||||
variant={isYesYes ? "gold" : "rose"}
|
||||
width="60%"
|
||||
marginVertical={spacing.md}
|
||||
/>
|
||||
|
||||
{/* Question and Responses - Show who answered what */}
|
||||
{match.responses && match.responses.length > 0 ? (
|
||||
<View style={styles.responsesContainer}>
|
||||
{(() => {
|
||||
// Sort responses by created_at to determine who answered first
|
||||
const sortedResponses = [...match.responses].sort(
|
||||
(a: any, b: any) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
|
||||
);
|
||||
const isTwoPart = !!match.question.partner_text;
|
||||
|
||||
return sortedResponses.map((response: any, index: number) => {
|
||||
const isUser = response.user_id === user?.id;
|
||||
const isYes = response.answer === 'yes';
|
||||
const name = isUser ? 'You' : (response.profiles?.name?.split(' ')[0] || 'Partner');
|
||||
|
||||
// First responder sees main text, second sees partner_text
|
||||
const questionText = isTwoPart && index === 1
|
||||
? match.question.partner_text
|
||||
: match.question.text;
|
||||
|
||||
return (
|
||||
<View key={response.user_id} style={styles.responseCard}>
|
||||
{/* Name and Answer Badge */}
|
||||
<View style={styles.responseHeader}>
|
||||
<Text style={styles.responseName}>{name}</Text>
|
||||
{isYes ? (
|
||||
<LinearGradient
|
||||
colors={isYesYes
|
||||
? gradients.premiumGold as [string, string]
|
||||
: [colors.success, '#27ae60']
|
||||
}
|
||||
style={styles.answerBadge}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
>
|
||||
<Ionicons name="checkmark" size={10} color={colors.text} />
|
||||
<Text style={styles.answerBadgeText}>YES</Text>
|
||||
</LinearGradient>
|
||||
) : (
|
||||
<View style={styles.answerBadgeMaybe}>
|
||||
<Ionicons name="help" size={10} color={colors.warning} />
|
||||
<Text style={styles.answerBadgeTextMaybe}>MAYBE</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
{/* Question they saw */}
|
||||
<Text style={styles.responseQuestionText}>
|
||||
"{questionText}"
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
});
|
||||
})()}
|
||||
</View>
|
||||
) : (
|
||||
<Text style={styles.matchQuestionText}>
|
||||
"{match.question.text}"
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{/* Premium border */}
|
||||
<View style={[
|
||||
styles.matchCardBorder,
|
||||
isYesYes && styles.matchCardBorderYesYes
|
||||
]} pointerEvents="none" />
|
||||
</View>
|
||||
</Animated.View>
|
||||
)}
|
||||
</>
|
||||
@@ -301,10 +333,14 @@ const styles = StyleSheet.create({
|
||||
height: 1,
|
||||
backgroundColor: `${ACCENT_RGBA}0.15)`,
|
||||
},
|
||||
matchCard: {
|
||||
matchCardContainer: {
|
||||
marginHorizontal: spacing.md,
|
||||
marginTop: spacing.sm,
|
||||
marginBottom: spacing.xs,
|
||||
// Vertical margins are handled by animation
|
||||
borderRadius: radius.lg,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
matchCard: {
|
||||
// Margins moved to container
|
||||
borderRadius: radius.lg,
|
||||
paddingVertical: spacing.md,
|
||||
paddingHorizontal: spacing.md,
|
||||
|
||||
Reference in new issue
Block a user