mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-04-27 10:01:00 -04:00
Clean home and fix profiles not loading
This commit is contained in:
@@ -1,14 +0,0 @@
|
||||
export function AboutBox(props: {
|
||||
title: string
|
||||
text: string
|
||||
}) {
|
||||
const {title, text} = props
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-lg font-bold">{title}</h3>
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
{text}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
79
web/components/home/home.tsx
Normal file
79
web/components/home/home.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import {useEffect} from "react";
|
||||
import {Col} from "web/components/layout/col";
|
||||
import {Button} from "web/components/buttons/button";
|
||||
import {signupRedirect} from "web/lib/util/signup";
|
||||
|
||||
export function AboutBox(props: {
|
||||
title: string
|
||||
text: string
|
||||
}) {
|
||||
const {title, text} = props
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-lg font-bold">{title}</h3>
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
{text}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function LoggedOutHome() {
|
||||
useEffect(() => {
|
||||
const text = "Search.";
|
||||
const el = document.getElementById("typewriter");
|
||||
if (!el) return;
|
||||
|
||||
let i = 0;
|
||||
let timeoutId: any;
|
||||
el.textContent = "";
|
||||
|
||||
function typeWriter() {
|
||||
if (i < text.length && el) {
|
||||
el.textContent = text.substring(0, i + 1);
|
||||
i++;
|
||||
timeoutId = setTimeout(typeWriter, 150);
|
||||
}
|
||||
}
|
||||
|
||||
const startId = setTimeout(typeWriter, 500);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeoutId);
|
||||
clearTimeout(startId);
|
||||
if (el) el.textContent = text;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Col className="mb-4 gap-2 lg:hidden">
|
||||
<Button
|
||||
className="flex-1"
|
||||
color="gradient"
|
||||
size="xl"
|
||||
onClick={signupRedirect}
|
||||
>
|
||||
Sign up
|
||||
</Button>
|
||||
{/*<SignUpAsMatchmaker className="flex-1"/>*/}
|
||||
</Col>
|
||||
<h1
|
||||
className="pt-12 pb-2 text-7xl md:text-8xl xs:text-6xl font-extrabold max-w-4xl leading-tight xl:whitespace-nowrap md:whitespace-nowrap">
|
||||
Don't Swipe.<br/>
|
||||
<span id="typewriter"></span>
|
||||
<span id="cursor" className="animate-pulse">|</span>
|
||||
</h1>
|
||||
<div className="w-full bg-gray-50 dark:bg-gray-900 py-8 mt-20">
|
||||
<div className="max-w-6xl mx-auto px-4">
|
||||
<div className="grid md:grid-cols-3 gap-8 text-center">
|
||||
<AboutBox title="Radically Transparent" text="No algorithms. Every profile searchable."/>
|
||||
<AboutBox title="Built for Depth" text="Filter by any keyword and what matters most."/>
|
||||
<AboutBox title="Community Owned" text="Free forever. Built by users, for users."/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
119
web/components/profiles/profiles-home.tsx
Normal file
119
web/components/profiles/profiles-home.tsx
Normal file
@@ -0,0 +1,119 @@
|
||||
import {Lover} from 'common/love/lover'
|
||||
import {removeNullOrUndefinedProps} from 'common/util/object'
|
||||
import {Search} from 'web/components/filters/search'
|
||||
import {useLover} from 'web/hooks/use-lover'
|
||||
import {useCompatibleLovers} from 'web/hooks/use-lovers'
|
||||
import {getStars} from 'web/lib/supabase/stars'
|
||||
import Router from 'next/router'
|
||||
import {useCallback, useEffect, useRef, useState} from 'react'
|
||||
import {Button} from 'web/components/buttons/button'
|
||||
import {orderLovers, useFilters} from 'web/components/filters/use-filters'
|
||||
import {ProfileGrid} from 'web/components/profile-grid'
|
||||
import {LoadingIndicator} from 'web/components/widgets/loading-indicator'
|
||||
import {Title} from 'web/components/widgets/title'
|
||||
import {useGetter} from 'web/hooks/use-getter'
|
||||
import {usePersistentInMemoryState} from 'web/hooks/use-persistent-in-memory-state'
|
||||
import {useUser} from 'web/hooks/use-user'
|
||||
import {api} from 'web/lib/api'
|
||||
import {debounce, omit} from 'lodash'
|
||||
import {PREF_AGE_MAX, PREF_AGE_MIN,} from 'web/components/filters/location-filter'
|
||||
|
||||
export function ProfilesHome() {
|
||||
const user = useUser();
|
||||
const lover = useLover();
|
||||
const you = lover;
|
||||
|
||||
const {
|
||||
filters,
|
||||
updateFilter,
|
||||
clearFilters,
|
||||
setYourFilters,
|
||||
isYourFilters,
|
||||
locationFilterProps,
|
||||
} = useFilters(you ?? undefined);
|
||||
|
||||
const [lovers, setLovers] = usePersistentInMemoryState<Lover[] | undefined>(undefined, 'profile-lovers');
|
||||
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
||||
const [isReloading, setIsReloading] = useState(false);
|
||||
|
||||
const [debouncedAgeRange, setRawAgeRange] = useState({
|
||||
min: filters.pref_age_min ?? PREF_AGE_MIN,
|
||||
max: filters.pref_age_max ?? PREF_AGE_MAX,
|
||||
});
|
||||
|
||||
const debouncedSetAge = useCallback(debounce((state) => setRawAgeRange(state), 50), []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) return;
|
||||
debouncedSetAge({min: filters.pref_age_min ?? PREF_AGE_MIN, max: filters.pref_age_max ?? PREF_AGE_MAX});
|
||||
}, [filters.pref_age_min, filters.pref_age_max]);
|
||||
|
||||
const id = useRef(0);
|
||||
useEffect(() => {
|
||||
if (!user) return;
|
||||
setIsReloading(true);
|
||||
const current = ++id.current;
|
||||
api('get-lovers', removeNullOrUndefinedProps({limit: 20, compatibleWithUserId: user?.id, ...filters}) as any)
|
||||
.then(({lovers}) => {
|
||||
if (current === id.current) setLovers(lovers);
|
||||
})
|
||||
.finally(() => {
|
||||
if (current === id.current) setIsReloading(false);
|
||||
});
|
||||
}, [JSON.stringify(omit(filters, ['pref_age_min', 'pref_age_max'])), debouncedAgeRange.min, debouncedAgeRange.max]);
|
||||
|
||||
const {data: starredUserIds, refresh: refreshStars} = useGetter('star', user?.id, getStars);
|
||||
const compatibleLovers = useCompatibleLovers(user?.id);
|
||||
const displayLovers = lovers && orderLovers(lovers, starredUserIds);
|
||||
|
||||
const loadMore = useCallback(async () => {
|
||||
if (!lovers || isLoadingMore) return false;
|
||||
try {
|
||||
setIsLoadingMore(true);
|
||||
const lastLover = lovers[lovers.length - 1];
|
||||
const result = await api('get-lovers', removeNullOrUndefinedProps({
|
||||
limit: 20,
|
||||
compatibleWithUserId: user?.id,
|
||||
after: lastLover?.id.toString(), ...filters
|
||||
}) as any);
|
||||
if (result.lovers.length === 0) return false;
|
||||
setLovers((prev) => (prev ? [...prev, ...result.lovers] : result.lovers));
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error('Failed to load more lovers', err);
|
||||
return false;
|
||||
} finally {
|
||||
setIsLoadingMore(false);
|
||||
}
|
||||
}, [lovers, filters, isLoadingMore, setLovers]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{!lover && <Button className="mb-4 lg:hidden" onClick={() => Router.push('signup')}>Create a profile</Button>}
|
||||
<Title className="!mb-2 text-3xl">Profiles</Title>
|
||||
<Search
|
||||
youLover={you}
|
||||
starredUserIds={starredUserIds ?? []}
|
||||
filters={filters}
|
||||
updateFilter={updateFilter}
|
||||
clearFilters={clearFilters}
|
||||
setYourFilters={setYourFilters}
|
||||
isYourFilters={isYourFilters}
|
||||
locationFilterProps={locationFilterProps}
|
||||
/>
|
||||
{displayLovers === undefined || compatibleLovers === undefined ? (
|
||||
<LoadingIndicator/>
|
||||
) : (
|
||||
<ProfileGrid
|
||||
lovers={displayLovers}
|
||||
loadMore={loadMore}
|
||||
isLoadingMore={isLoadingMore}
|
||||
isReloading={isReloading}
|
||||
compatibilityScores={compatibleLovers?.loverCompatibilityScores}
|
||||
starredUserIds={starredUserIds}
|
||||
refreshStars={refreshStars}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -30,6 +30,7 @@ export const useCompatibleLovers = (
|
||||
} else if (userId === null) setData(null)
|
||||
}, [userId])
|
||||
|
||||
console.log('debug', data)
|
||||
if (data && lover && options?.sortWithModifiers) {
|
||||
data.compatibleLovers = sortBy(data.compatibleLovers, (l) => {
|
||||
const modifier = !lover ? 1 : getLoversCompatibilityFactor(lover, l)
|
||||
|
||||
@@ -1,265 +1,18 @@
|
||||
import {Lover} from 'common/love/lover'
|
||||
import {removeNullOrUndefinedProps} from 'common/util/object'
|
||||
import {Search} from 'web/components/filters/search'
|
||||
import {LovePage} from 'web/components/love-page'
|
||||
import {useLover} from 'web/hooks/use-lover'
|
||||
import {useCompatibleLovers} from 'web/hooks/use-lovers'
|
||||
import {getStars} from 'web/lib/supabase/stars'
|
||||
import Router from 'next/router'
|
||||
import {useCallback, useEffect, useRef, useState} from 'react'
|
||||
import {Button} from 'web/components/buttons/button'
|
||||
import {orderLovers, useFilters} from 'web/components/filters/use-filters'
|
||||
import {Col} from 'web/components/layout/col'
|
||||
import {ProfileGrid} from 'web/components/profile-grid'
|
||||
import {LoadingIndicator} from 'web/components/widgets/loading-indicator'
|
||||
import {Title} from 'web/components/widgets/title'
|
||||
import {useGetter} from 'web/hooks/use-getter'
|
||||
import {usePersistentInMemoryState} from 'web/hooks/use-persistent-in-memory-state'
|
||||
import {useSaveReferral} from 'web/hooks/use-save-referral'
|
||||
import {useTracking} from 'web/hooks/use-tracking'
|
||||
import {useUser} from 'web/hooks/use-user'
|
||||
import {api} from 'web/lib/api'
|
||||
import {debounce, omit} from 'lodash'
|
||||
import {PREF_AGE_MAX, PREF_AGE_MIN,} from 'web/components/filters/location-filter'
|
||||
import {signupRedirect} from "web/lib/util/signup";
|
||||
import {AboutBox} from "web/components/about/box";
|
||||
import {LoggedOutHome} from "web/components/home/home";
|
||||
import {ProfilesHome} from "web/components/profiles/profiles-home";
|
||||
|
||||
|
||||
export default function ProfilesPage() {
|
||||
const you = useLover()
|
||||
|
||||
const {
|
||||
filters,
|
||||
updateFilter,
|
||||
clearFilters,
|
||||
setYourFilters,
|
||||
isYourFilters,
|
||||
locationFilterProps,
|
||||
} = useFilters(you ?? undefined)
|
||||
|
||||
// Store all loaded lovers
|
||||
const [lovers, setLovers] = usePersistentInMemoryState<Lover[] | undefined>(
|
||||
undefined,
|
||||
'profile-lovers'
|
||||
)
|
||||
|
||||
const [isLoadingMore, setIsLoadingMore] = useState(false)
|
||||
const [isReloading, setIsReloading] = useState(false)
|
||||
|
||||
// Refresh lovers when filters change
|
||||
|
||||
// debounce age filter
|
||||
const [debouncedAgeRange, setRawAgeRange] = useState({
|
||||
min: filters.pref_age_min ?? PREF_AGE_MIN,
|
||||
max: filters.pref_age_max ?? PREF_AGE_MAX,
|
||||
})
|
||||
|
||||
const debouncedSetAge = useCallback(
|
||||
debounce(
|
||||
(state: { min: number; max: number }) => setRawAgeRange(state),
|
||||
50
|
||||
),
|
||||
[]
|
||||
)
|
||||
|
||||
const user = useUser()
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) return
|
||||
debouncedSetAge({
|
||||
min: filters.pref_age_min ?? PREF_AGE_MIN,
|
||||
max: filters.pref_age_max ?? PREF_AGE_MAX,
|
||||
})
|
||||
}, [filters.pref_age_min, filters.pref_age_max])
|
||||
|
||||
const id = useRef(0)
|
||||
useEffect(() => {
|
||||
if (!user) return
|
||||
setIsReloading(true)
|
||||
const current = ++id.current
|
||||
api(
|
||||
'get-lovers',
|
||||
removeNullOrUndefinedProps({
|
||||
limit: 20,
|
||||
compatibleWithUserId: user?.id,
|
||||
...filters,
|
||||
}) as any
|
||||
)
|
||||
.then(({lovers}) => {
|
||||
if (current === id.current) {
|
||||
setLovers(lovers)
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (current === id.current) {
|
||||
setIsReloading(false)
|
||||
}
|
||||
})
|
||||
}, [
|
||||
JSON.stringify(omit(filters, ['pref_age_min', 'pref_age_max'])),
|
||||
debouncedAgeRange.min,
|
||||
debouncedAgeRange.max,
|
||||
])
|
||||
|
||||
useTracking('view love profiles')
|
||||
useSaveReferral(user)
|
||||
const lover = useLover()
|
||||
const {data: starredUserIds, refresh: refreshStars} = useGetter(
|
||||
'star',
|
||||
user?.id,
|
||||
getStars
|
||||
)
|
||||
|
||||
const compatibleLovers = useCompatibleLovers(user ? user.id : user)
|
||||
const loadMore = useCallback(async () => {
|
||||
if (!lovers || isLoadingMore) return false
|
||||
|
||||
try {
|
||||
setIsLoadingMore(true)
|
||||
|
||||
// Get the last lover's ID as the after parameter
|
||||
const lastLover = lovers[lovers.length - 1]
|
||||
|
||||
console.log('fetching lovers after', lastLover?.id)
|
||||
|
||||
const result = await api(
|
||||
'get-lovers',
|
||||
removeNullOrUndefinedProps({
|
||||
limit: 20,
|
||||
compatibleWithUserId: user?.id,
|
||||
after: lastLover?.id.toString(),
|
||||
...filters,
|
||||
} as any)
|
||||
)
|
||||
|
||||
if (result.lovers.length === 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Append new lovers to existing array
|
||||
setLovers((prevLovers) => {
|
||||
if (!prevLovers) return result.lovers
|
||||
|
||||
// Create a new array with all existing lovers plus new ones
|
||||
return [...prevLovers, ...result.lovers]
|
||||
})
|
||||
|
||||
return true
|
||||
} catch (err) {
|
||||
console.error('Failed to load more lovers', err)
|
||||
return false
|
||||
} finally {
|
||||
setIsLoadingMore(false)
|
||||
}
|
||||
}, [lovers, filters, isLoadingMore, setLovers])
|
||||
|
||||
const displayLovers = lovers && orderLovers(lovers, starredUserIds)
|
||||
|
||||
useEffect(() => {
|
||||
const text = "Search.";
|
||||
const typewriter = document.getElementById("typewriter");
|
||||
let i = 0;
|
||||
let timeoutId: any;
|
||||
if (typewriter) typewriter.textContent = ""
|
||||
|
||||
function typeWriter() {
|
||||
if (i < text.length && typewriter) {
|
||||
typewriter.textContent = text.substring(0, i + 1);
|
||||
i++;
|
||||
timeoutId = setTimeout(typeWriter, 150);
|
||||
}
|
||||
}
|
||||
|
||||
const intervalId = setTimeout(() => typeWriter(), 500);
|
||||
return () => {
|
||||
clearTimeout(timeoutId);
|
||||
clearTimeout(intervalId);
|
||||
if (typewriter) typewriter.textContent = "Search."
|
||||
};
|
||||
}, []);
|
||||
|
||||
// This makes document.getElementById("typewriter") null and prevents typeWriter from running.
|
||||
// Should not be needed anyway.
|
||||
// if (user === undefined) return <div/>
|
||||
const user = useUser();
|
||||
|
||||
return (
|
||||
<LovePage trackPageView={'user profiles'}>
|
||||
<Col className="items-center">
|
||||
<Col className={'bg-canvas-0 w-full rounded px-3 py-4 sm:px-6'}>
|
||||
{user && lovers && !lover && (
|
||||
<Button
|
||||
className="mb-4 lg:hidden"
|
||||
onClick={() => Router.push('signup')}
|
||||
>
|
||||
Create a profile
|
||||
</Button>
|
||||
)}
|
||||
{user === null && (
|
||||
<Col className="mb-4 gap-2 lg:hidden">
|
||||
<Button
|
||||
className="flex-1"
|
||||
color="gradient"
|
||||
size="xl"
|
||||
onClick={signupRedirect}
|
||||
>
|
||||
Sign up
|
||||
</Button>
|
||||
{/*<SignUpAsMatchmaker className="flex-1"/>*/}
|
||||
</Col>
|
||||
)}
|
||||
{!user && <>
|
||||
<h1
|
||||
className="pt-12 pb-2 text-7xl md:text-8xl xs:text-6xl font-extrabold max-w-4xl leading-tight xl:whitespace-nowrap md:whitespace-nowrap ">
|
||||
Don't Swipe.<br/><span id="typewriter"></span><span id="cursor" className="animate-pulse">|</span>
|
||||
</h1>
|
||||
<div className="w-full bg-gray-50 dark:bg-gray-900 py-8 mt-20">
|
||||
<div className="max-w-6xl mx-auto px-4">
|
||||
<div className="grid md:grid-cols-3 gap-8 text-center">
|
||||
<AboutBox
|
||||
title='Radically Transparent'
|
||||
text='No algorithms. Every profile searchable.'
|
||||
/>
|
||||
<AboutBox
|
||||
title='Built for Depth'
|
||||
text='Filter by any keyword and what matters most.'
|
||||
/>
|
||||
<AboutBox
|
||||
title='Community Owned'
|
||||
text='Free forever. Built by users, for users.'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>}
|
||||
{user &&
|
||||
<>
|
||||
<Title className="!mb-2 text-3xl">Profiles</Title>
|
||||
<Search
|
||||
youLover={you}
|
||||
starredUserIds={starredUserIds ?? []}
|
||||
filters={filters}
|
||||
updateFilter={updateFilter}
|
||||
clearFilters={clearFilters}
|
||||
setYourFilters={setYourFilters}
|
||||
isYourFilters={isYourFilters}
|
||||
locationFilterProps={locationFilterProps}
|
||||
/>
|
||||
|
||||
{displayLovers === undefined || compatibleLovers === undefined ? (
|
||||
<LoadingIndicator/>
|
||||
) : (
|
||||
<ProfileGrid
|
||||
lovers={displayLovers}
|
||||
loadMore={loadMore}
|
||||
isLoadingMore={isLoadingMore}
|
||||
isReloading={isReloading}
|
||||
compatibilityScores={compatibleLovers?.loverCompatibilityScores}
|
||||
starredUserIds={starredUserIds}
|
||||
refreshStars={refreshStars}
|
||||
/>)
|
||||
}
|
||||
</>
|
||||
}
|
||||
{user ? <ProfilesHome/> : <LoggedOutHome/>}
|
||||
</Col>
|
||||
</Col>
|
||||
</LovePage>
|
||||
|
||||
Reference in New Issue
Block a user