From 32bc3847fa0e7d0908b1429a8501d97735e79498 Mon Sep 17 00:00:00 2001 From: MartinBraquet Date: Wed, 15 Oct 2025 15:45:47 +0200 Subject: [PATCH] Add option to save / bookmark profiles --- web/components/filters/search.tsx | 21 +++- web/components/profile-grid.tsx | 2 +- web/components/profile/profile-header.tsx | 19 ++-- web/components/profile/profile-info.tsx | 3 +- web/components/profiles/profiles-home.tsx | 13 ++- web/components/searches/button.tsx | 123 +++++++++++++++++++++- web/components/widgets/star-button.tsx | 4 +- web/lib/supabase/stars.ts | 11 +- 8 files changed, 170 insertions(+), 26 deletions(-) diff --git a/web/components/filters/search.tsx b/web/components/filters/search.tsx index 07a7ca9b..a3414b77 100644 --- a/web/components/filters/search.tsx +++ b/web/components/filters/search.tsx @@ -10,13 +10,14 @@ import {Select} from 'web/components/widgets/select' import {DesktopFilters} from './desktop-filters' import {LocationFilterProps} from './location-filter' import {MobileFilters} from './mobile-filters' -import {BookmarkSearchButton} from "web/components/searches/button"; +import {BookmarkSearchButton, BookmarkStarButton} from "web/components/searches/button"; import {BookmarkedSearchesType} from "web/hooks/use-bookmarked-searches"; import {submitBookmarkedSearch} from "web/lib/supabase/searches"; import {useUser} from "web/hooks/use-user"; import {isEqual} from "lodash"; import toast from "react-hot-toast"; import {FilterFields, initialFilters} from "common/filters"; +import {DisplayUser} from "common/api/user-types"; function isOrderBy(input: string): input is FilterFields['orderBy'] { return ['last_online_time', 'created_time', 'compatibility_score'].includes( @@ -96,7 +97,8 @@ function getRandomPair(count = 3): string { const MAX_BOOKMARKED_SEARCHES = 10; export const Search = (props: { youProfile: Profile | undefined | null - starredUserIds: string[] + starredUsers: DisplayUser[] + refreshStars: () => void // filter props filters: Partial updateFilter: (newState: Partial) => void @@ -117,6 +119,8 @@ export const Search = (props: { filters, bookmarkedSearches, refreshBookmarkedSearches, + starredUsers, + refreshStars, } = props const [openFiltersModal, setOpenFiltersModal] = useState(false) @@ -128,6 +132,7 @@ export const Search = (props: { const [bookmarked, setBookmarked] = useState(false); const [loadingBookmark, setLoadingBookmark] = useState(false); const [openBookmarks, setOpenBookmarks] = useState(false); + const [openStarBookmarks, setOpenStarBookmarks] = useState(false); const user = useUser() useEffect(() => { @@ -254,7 +259,7 @@ export const Search = (props: { color={'none'} className={'bg-canvas-100 hover:bg-canvas-200'} > - {bookmarked ? 'Bookmarked!' : loadingBookmark ? '' : 'Get Notified'} + {bookmarked ? 'Saved!' : loadingBookmark ? '' : 'Get Notified'} + + { + setOpenStarBookmarks(checked) + refreshStars() + }} + /> ) diff --git a/web/components/profile-grid.tsx b/web/components/profile-grid.tsx index 881fc460..ad6e64f6 100644 --- a/web/components/profile-grid.tsx +++ b/web/components/profile-grid.tsx @@ -66,7 +66,7 @@ export const ProfileGrid = (props: { {!isLoadingMore && !isReloading && other_profiles.length === 0 && (

No profiles found.

-

Feel free to click on Get Notified and we'll notify you when new users match it!

+

Feel free to click on Get Notified and we'll notify you when new users match your search!

)} diff --git a/web/components/profile/profile-header.tsx b/web/components/profile/profile-header.tsx index 1197592b..aac6ab56 100644 --- a/web/components/profile/profile-header.tsx +++ b/web/components/profile/profile-header.tsx @@ -22,6 +22,7 @@ import {useState} from 'react' import {VisibilityConfirmationModal} from './visibility-confirmation-modal' import {deleteAccount} from "web/lib/util/delete"; import toast from "react-hot-toast"; +import {StarButton} from "web/components/widgets/star-button"; export default function ProfileHeader(props: { user: User @@ -38,8 +39,8 @@ export default function ProfileHeader(props: { profile, userActivity, simpleView, - // starredUserIds, - // refreshStars, + starredUserIds, + refreshStars, showMessageButton, refreshProfile, } = props @@ -145,13 +146,13 @@ export default function ProfileHeader(props: { className="sm:flex" username={user.username} /> - {/*{currentUser && (*/} - {/* */} - {/*)}*/} + {currentUser && ( + + )} {currentUser && showMessageButton && ( )} diff --git a/web/components/profile/profile-info.tsx b/web/components/profile/profile-info.tsx index bd8518d4..7fd63403 100644 --- a/web/components/profile/profile-info.tsx +++ b/web/components/profile/profile-info.tsx @@ -31,11 +31,12 @@ export function ProfileInfo(props: { // const currentProfile = useProfile() // const isCurrentUser = currentUser?.id === user.id - const {data: starredUserIds, refresh: refreshStars} = useGetter( + const {data: starredUsers, refresh: refreshStars} = useGetter( 'stars', currentUser?.id, getStars ) + const starredUserIds = starredUsers?.map((u) => u.id) // const { data, refresh } = useAPIGetter('get-likes-and-ships', { // userId: user.id, diff --git a/web/components/profiles/profiles-home.tsx b/web/components/profiles/profiles-home.tsx index cb18355b..5cc67b19 100644 --- a/web/components/profiles/profiles-home.tsx +++ b/web/components/profiles/profiles-home.tsx @@ -13,7 +13,6 @@ import {usePersistentInMemoryState} from 'web/hooks/use-persistent-in-memory-sta import {useUser} from 'web/hooks/use-user' import {api} from 'web/lib/api' import {useBookmarkedSearches} from "web/hooks/use-bookmarked-searches"; -import {orderProfiles} from "common/filters"; import {useFilters} from "web/components/filters/use-filters"; export function ProfilesHome() { @@ -64,9 +63,12 @@ export function ProfilesHome() { }); }, [filters]); - const {data: starredUserIds, refresh: refreshStars} = useGetter('star', user?.id, getStars); - const compatibleProfiles = useCompatibleProfiles(user?.id); - const displayProfiles = profiles && orderProfiles(profiles, starredUserIds); + const {data: starredUsers, refresh: refreshStars} = useGetter('star', user?.id, getStars) + const starredUserIds = starredUsers?.map((u) => u.id) + + const compatibleProfiles = useCompatibleProfiles(user?.id) + // const displayProfiles = profiles && orderProfiles(profiles, starredUserIds); + const displayProfiles = profiles const loadMore = useCallback(async () => { if (!profiles || isLoadingMore) return false; @@ -96,7 +98,8 @@ export function ProfilesHome() { Profiles -

Bookmarked Searches

-

We'll notify you daily when new people match your searches below.

+

Saved Searches

+ {bookmarkedSearches?.length ? (<> +

We'll notify you daily when new people match your searches below.

    @@ -83,6 +88,116 @@ function ButtonModal(props: {
+ + ) :

You haven't saved any search. To save one, click on Get Notified and we'll notify you daily when new people match it.

} + {/* {*/} + {/* setOpen(false)*/} + {/* }}*/} + {/* isLastQuestion={questionIndex === bookmarkedSearches.length - 1}*/} + {/* onNext={() => {*/} + {/* if (questionIndex === bookmarkedSearches.length - 1) {*/} + {/* setOpen(false)*/} + {/* } else {*/} + {/* setQuestionIndex(questionIndex + 1)*/} + {/* }*/} + {/* }}*/} + {/*/>*/} + + + ) +} + +export function BookmarkStarButton(props: { + starredUsers: DisplayUser[] + refreshStars: () => void + open: boolean + setOpen: (checked: boolean) => void +}) { + const { + starredUsers, + refreshStars, + open, + setOpen, + } = props + const user = useUser() + + if (!user) return null + return ( + <> + + + + ) +} + + +function StarModal(props: { + open: boolean + setOpen: (open: boolean) => void + user: User + starredUsers: DisplayUser[] + refreshStars: () => void +}) { + const {open, setOpen, starredUsers, refreshStars} = props + + return ( + { + // refreshBookmarkedSearches() + // }} + > + +

Saved Profiles

+ {starredUsers?.length ? (<> +

Here are the profiles you saved:

+ +
    + {(starredUsers || []).map((user) => ( +
  1. + {user.name} ( + @{user.username} + ) {' '} + +
  2. + ))} +
+ + + + ) :

You haven't saved any profile. To save one, click on the star on their profile page.

} {/* + {button} ) diff --git a/web/lib/supabase/stars.ts b/web/lib/supabase/stars.ts index 37b56d4c..7b56f12b 100644 --- a/web/lib/supabase/stars.ts +++ b/web/lib/supabase/stars.ts @@ -1,5 +1,6 @@ import { run } from 'common/supabase/utils' import { db } from 'web/lib/supabase/db' +import {DisplayUser} from "common/api/user-types"; export const getStars = async (creatorId: string) => { const { data } = await run( @@ -12,5 +13,13 @@ export const getStars = async (creatorId: string) => { if (!data) return [] - return data.map((d) => d.target_id as string) + const ids = data.map((d) => d.target_id as string) + const {data: users} = await run( + db + .from('users') + .select(`id, name, username`) + .in('id', ids) + ) + + return users as unknown as DisplayUser[] }