mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-03-06 07:52:27 -05:00
244 lines
8.2 KiB
TypeScript
244 lines
8.2 KiB
TypeScript
import {User} from "common/user";
|
||
import {Button} from "web/components/buttons/button";
|
||
import {Modal, MODAL_CLASS} from "web/components/layout/modal";
|
||
import {Col} from "web/components/layout/col";
|
||
import {BookmarkedSearchesType} from "web/hooks/use-bookmarked-searches";
|
||
import {useUser} from "web/hooks/use-user";
|
||
import {deleteBookmarkedSearch} from "web/lib/supabase/searches";
|
||
import {formatFilters, locationType} from "common/searches";
|
||
import {FilterFields} from "common/filters";
|
||
import {api} from "web/lib/api";
|
||
import {DisplayUser} from "common/api/user-types";
|
||
import {useState} from "react";
|
||
import toast from "react-hot-toast";
|
||
import Link from "next/link";
|
||
|
||
export function BookmarkSearchButton(props: {
|
||
bookmarkedSearches: BookmarkedSearchesType[]
|
||
refreshBookmarkedSearches: () => void
|
||
open: boolean
|
||
setOpen: (checked: boolean) => void
|
||
}) {
|
||
const {
|
||
bookmarkedSearches,
|
||
refreshBookmarkedSearches,
|
||
open,
|
||
setOpen,
|
||
} = props
|
||
const user = useUser()
|
||
|
||
if (!user) return null
|
||
return (
|
||
<>
|
||
<Button onClick={() => setOpen(true)} color="gray-outline" size={'xs'}>
|
||
Saved Searches
|
||
</Button>
|
||
<ButtonModal
|
||
open={open}
|
||
setOpen={setOpen}
|
||
user={user}
|
||
bookmarkedSearches={bookmarkedSearches}
|
||
refreshBookmarkedSearches={refreshBookmarkedSearches}
|
||
/>
|
||
</>
|
||
)
|
||
}
|
||
|
||
|
||
function ButtonModal(props: {
|
||
open: boolean
|
||
setOpen: (open: boolean) => void
|
||
user: User
|
||
bookmarkedSearches: BookmarkedSearchesType[]
|
||
refreshBookmarkedSearches: () => void
|
||
}) {
|
||
const {open, setOpen, bookmarkedSearches, refreshBookmarkedSearches} = props
|
||
return (
|
||
<Modal
|
||
open={open}
|
||
setOpen={setOpen}
|
||
onClose={() => {
|
||
refreshBookmarkedSearches()
|
||
}}
|
||
>
|
||
<Col className={MODAL_CLASS}>
|
||
<h3>Saved Searches</h3>
|
||
{bookmarkedSearches?.length ? (<>
|
||
<p>We'll notify you daily when new people match your searches below.</p>
|
||
<Col
|
||
className={
|
||
'border-ink-300bg-canvas-0 inline-flex flex-col gap-2 rounded-md border p-1 shadow-sm'
|
||
}
|
||
>
|
||
<ol className="list-decimal list-inside space-y-2">
|
||
{(bookmarkedSearches || []).map((search) => (
|
||
<li key={search.id}
|
||
className="items-center justify-between gap-2 list-item marker:text-ink-500 marker:font-bold">
|
||
{formatFilters(search.search_filters as Partial<FilterFields>, search.location as locationType)?.join(" • ")}
|
||
<button
|
||
onClick={async () => {
|
||
await deleteBookmarkedSearch(search.id)
|
||
refreshBookmarkedSearches()
|
||
}}
|
||
className="inline-flex text-xl h-5 w-5 items-center justify-center rounded-full text-red-600 hover:bg-red-200 focus:outline-none focus:ring-2 focus:ring-red-400"
|
||
>
|
||
×
|
||
</button>
|
||
</li>
|
||
))}
|
||
</ol>
|
||
|
||
</Col>
|
||
</>
|
||
) :
|
||
<p>You haven't saved any search. To save one, click on Get Notified and we'll notify you daily when new people
|
||
match it.</p>}
|
||
{/*<BookmarkSearchContent*/}
|
||
{/* total={bookmarkedSearches.length}*/}
|
||
{/* compatibilityQuestion={bookmarkedSearches[questionIndex]}*/}
|
||
{/* user={user}*/}
|
||
{/* onSubmit={() => {*/}
|
||
{/* setOpen(false)*/}
|
||
{/* }}*/}
|
||
{/* isLastQuestion={questionIndex === bookmarkedSearches.length - 1}*/}
|
||
{/* onNext={() => {*/}
|
||
{/* if (questionIndex === bookmarkedSearches.length - 1) {*/}
|
||
{/* setOpen(false)*/}
|
||
{/* } else {*/}
|
||
{/* setQuestionIndex(questionIndex + 1)*/}
|
||
{/* }*/}
|
||
{/* }}*/}
|
||
{/*/>*/}
|
||
</Col>
|
||
</Modal>
|
||
)
|
||
}
|
||
|
||
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 (
|
||
<>
|
||
<Button onClick={() => setOpen(true)} color="gray-outline" size={'xs'}>
|
||
Saved People
|
||
</Button>
|
||
<StarModal
|
||
open={open}
|
||
setOpen={setOpen}
|
||
user={user}
|
||
starredUsers={starredUsers}
|
||
refreshStars={refreshStars}
|
||
/>
|
||
</>
|
||
)
|
||
}
|
||
|
||
|
||
function StarModal(props: {
|
||
open: boolean
|
||
setOpen: (open: boolean) => void
|
||
user: User
|
||
starredUsers: DisplayUser[]
|
||
refreshStars: () => void
|
||
}) {
|
||
const {open, setOpen, starredUsers, refreshStars} = props
|
||
// Track items being optimistically removed so we can hide them immediately
|
||
const [removingIds, setRemovingIds] = useState<Set<string>>(new Set())
|
||
|
||
const visibleUsers = (starredUsers || []).filter((u) => !removingIds.has(u.id))
|
||
|
||
return (
|
||
<Modal
|
||
open={open}
|
||
setOpen={setOpen}
|
||
// onClose={() => {
|
||
// refreshBookmarkedSearches()
|
||
// }}
|
||
>
|
||
<Col className={MODAL_CLASS}>
|
||
<h3>Saved People</h3>
|
||
{visibleUsers?.length ? (<>
|
||
<p>Here are the people you saved:</p>
|
||
<Col
|
||
className={
|
||
'border-ink-300bg-canvas-0 inline-flex flex-col gap-2 rounded-md border p-1 shadow-sm'
|
||
}
|
||
>
|
||
<ol className="list-decimal list-inside space-y-2">
|
||
{visibleUsers.map((user) => (
|
||
<li key={user.id}
|
||
className="items-center justify-between gap-2 list-item marker:text-ink-500 marker:font-bold">
|
||
<a className={'custom-link'}>
|
||
{user.name} (<Link
|
||
href={`/${user.username}`}
|
||
// style={{color: "#2563eb", textDecoration: "none"}}
|
||
>
|
||
@{user.username}
|
||
</Link>) {' '}
|
||
</a>
|
||
<button
|
||
onClick={() => {
|
||
// Optimistically remove the user from the list
|
||
setRemovingIds((prev) => new Set(prev).add(user.id))
|
||
// Fire the API call without blocking UI
|
||
api('star-profile', {
|
||
targetUserId: user.id,
|
||
remove: true,
|
||
})
|
||
.then(() => {
|
||
// Sync with server state
|
||
refreshStars()
|
||
})
|
||
.catch(() => {
|
||
toast.error("Couldn't remove saved profile. Please try again.")
|
||
// Revert optimistic removal on failure
|
||
setRemovingIds((prev) => {
|
||
const next = new Set(prev)
|
||
next.delete(user.id)
|
||
return next
|
||
})
|
||
})
|
||
}}
|
||
className="inline-flex text-xl h-5 w-5 items-center justify-center rounded-full text-red-600 hover:bg-red-200 focus:outline-none focus:ring-2 focus:ring-red-400"
|
||
>
|
||
×
|
||
</button>
|
||
</li>
|
||
))}
|
||
</ol>
|
||
|
||
</Col>
|
||
</>
|
||
) : <p>You haven't saved any profile. To save one, click on the star on their profile page.</p>}
|
||
{/*<BookmarkSearchContent*/}
|
||
{/* total={bookmarkedSearches.length}*/}
|
||
{/* compatibilityQuestion={bookmarkedSearches[questionIndex]}*/}
|
||
{/* user={user}*/}
|
||
{/* onSubmit={() => {*/}
|
||
{/* setOpen(false)*/}
|
||
{/* }}*/}
|
||
{/* isLastQuestion={questionIndex === bookmarkedSearches.length - 1}*/}
|
||
{/* onNext={() => {*/}
|
||
{/* if (questionIndex === bookmarkedSearches.length - 1) {*/}
|
||
{/* setOpen(false)*/}
|
||
{/* } else {*/}
|
||
{/* setQuestionIndex(questionIndex + 1)*/}
|
||
{/* }*/}
|
||
{/* }}*/}
|
||
{/*/>*/}
|
||
</Col>
|
||
</Modal>
|
||
)
|
||
} |