diff --git a/web/components/filters/filters.tsx b/web/components/filters/filters.tsx index 625cc036..6c729fc3 100644 --- a/web/components/filters/filters.tsx +++ b/web/components/filters/filters.tsx @@ -842,7 +842,13 @@ export function FilterSection(props: { onClick={() => (isOpen ? setOpenFilter(undefined) : setOpenFilter(title))} > {showNewBadge && } - + {icon} {selection} @@ -870,7 +876,12 @@ function FilterGroup(props: { className="flex w-full flex-row items-center justify-between px-4 py-3 text-ink-600" onClick={() => (isOpen ? setOpenGroup(undefined) : setOpenGroup(title))} > - + {icon} {title} diff --git a/web/components/profile-grid.tsx b/web/components/profile-grid.tsx index a9c0953d..4c0d9fd3 100644 --- a/web/components/profile-grid.tsx +++ b/web/components/profile-grid.tsx @@ -161,6 +161,27 @@ function ProfilePreview(props: { const t = useT() // const currentUser = useUser() + const [isLoading, setIsLoading] = useState(false) + const [showRing, setShowRing] = useState(false) + const ringTimeoutRef = useRef(null) + + const handlePointerDown = () => { + setIsLoading(true) + setShowRing(true) + ringTimeoutRef.current = setTimeout(() => { + setShowRing(true) + }, 200) + } + + // Cleanup on unmount + useEffect(() => { + return () => { + if (ringTimeoutRef.current) { + clearTimeout(ringTimeoutRef.current) + } + } + }, []) + // Show the bottom transparent gradient only if the text can't fit the card const textRef = useRef(null) const [isOverflowing, setIsOverflowing] = useState(false) @@ -259,172 +280,214 @@ function ProfilePreview(props: { const hover = 'hover:bg-gray-50 dark:hover:bg-gray-800/50' return ( - track('click profile preview')} - href={`/${user.username}`} +
- - - {compatibilityScore && ( - - )} - {onHide && ( - - )} - - -
+ {/* Phase 2: Animated ring - appears after 200ms */} + {isLoading && showRing && ( + <>
+ {/* Mask to show only the ring strip */} +
+ + )} + { + // Cancel ring if navigation completes quickly + if (ringTimeoutRef.current) { + clearTimeout(ringTimeoutRef.current) + } + }} + className={clsx( + 'relative z-10 cursor-pointer group block rounded-lg overflow-hidden bg-transparent h-full border border-canvas-300', + hover, + )} + > + {/* Phase 1: Dim overlay */} + {isLoading && ( +
+ )} + + + {compatibilityScore && ( + )} - > -

- {user.name} -

- - {showCity !== false && } - {showAge !== false && profile.age && ( - } - /> - )} - {showGender !== false && profile.gender && ( - } - /> - )} - - {showHeadline !== false && profile.headline && ( -

"{profile.headline}"

- )} - {showKeywords !== false && !!profile.keywords?.length && ( - - {profile.keywords - ?.slice(0, 10) - ?.map(capitalize) - ?.map((tag, i) => ( - - {tag.trim()} - - ))} - - )} - {showSeeking !== false && seekingText && ( - } + {onHide && ( + )} - {showOccupation !== false && profile.occupation_title && ( - } - /> - )} - {showInterests !== false && !!profile.interests?.length && ( - choicesIdsToLabels['interests'][id]) - .join(' • ')} - icon={} - /> - )} - {showCauses !== false && !!profile.causes?.length && ( - choicesIdsToLabels['causes'][id]) - .join(' • ')} - icon={} - /> - )} - - {showDiet !== false && !!profile.diet?.length && ( - t(`profile.diet.${e}`, INVERTED_DIET_CHOICES[e])) - .join(' • ')} - icon={} - /> - )} - {showSmoking !== false && profile.is_smoker && ( - } - /> - )} - {showDrinks !== false && - profile.drinks_per_month !== null && - profile.drinks_per_month !== undefined && ( - } - /> - )} - {showMBTI !== false && profile.mbti && ( - } - /> - )} - {showLanguages !== false && !!profile.languages?.length && ( - t(`profile.language.${v}`, INVERTED_LANGUAGE_CHOICES[v])) - .join(' • ')} - icon={} - /> - )} - - {showBio !== false && bio && ( -
- -
- )} - {isOverflowing && ( -
- )} -
- {isPhotoRendered && ( +
+ +
- +

+ {user.name} +

+ + {showCity !== false && } + {showAge !== false && profile.age && ( + } + /> + )} + {showGender !== false && profile.gender && ( + } + /> + )} + + {showHeadline !== false && profile.headline && ( +

"{profile.headline}"

+ )} + {showKeywords !== false && !!profile.keywords?.length && ( + + {profile.keywords + ?.slice(0, 10) + ?.map(capitalize) + ?.map((tag, i) => ( + + {tag.trim()} + + ))} + + )} + {showSeeking !== false && seekingText && ( + } + /> + )} + {showOccupation !== false && profile.occupation_title && ( + } + /> + )} + {showInterests !== false && !!profile.interests?.length && ( + choicesIdsToLabels['interests'][id]) + .join(' • ')} + icon={} + /> + )} + {showCauses !== false && !!profile.causes?.length && ( + choicesIdsToLabels['causes'][id]) + .join(' • ')} + icon={} + /> + )} + + {showDiet !== false && !!profile.diet?.length && ( + t(`profile.diet.${e}`, INVERTED_DIET_CHOICES[e])) + .join(' • ')} + icon={} + /> + )} + {showSmoking !== false && profile.is_smoker && ( + } + /> + )} + {showDrinks !== false && + profile.drinks_per_month !== null && + profile.drinks_per_month !== undefined && ( + } + /> + )} + {showMBTI !== false && profile.mbti && ( + } + /> + )} + {showLanguages !== false && !!profile.languages?.length && ( + t(`profile.language.${v}`, INVERTED_LANGUAGE_CHOICES[v])) + .join(' • ')} + icon={} + /> + )} + + {showBio !== false && bio && ( +
+ +
+ )} + {isOverflowing && ( +
+ )}
- )} -
- - + {isPhotoRendered && ( +
+ +
+ )} +
+ + +
) } diff --git a/web/styles/globals.css b/web/styles/globals.css index 108d8c46..ba9854af 100644 --- a/web/styles/globals.css +++ b/web/styles/globals.css @@ -555,7 +555,8 @@ input { cursor: pointer; } -.hover-bold:hover { +.hover-bold:hover, +.force-bold { text-shadow: 0.01em 0 currentColor, -0.01em 0 currentColor,