@@ -10,7 +10,8 @@ import {FilterFields} from "common/filters";
1010import { api } from "web/lib/api" ;
1111import { DisplayUser } from "common/api/user-types" ;
1212import { Link } from "@react-email/components" ;
13- import { DOMAIN } from "common/envs/constants" ;
13+ import { useState } from "react" ;
14+ import toast from "react-hot-toast" ;
1415
1516export function BookmarkSearchButton ( props : {
1617 bookmarkedSearches : BookmarkedSearchesType [ ]
@@ -63,33 +64,35 @@ function ButtonModal(props: {
6364 < Col className = { MODAL_CLASS } >
6465 < h3 > Saved Searches</ h3 >
6566 { bookmarkedSearches ?. length ? ( < >
66- < p > We'll notify you daily when new people match your searches below.</ p >
67- < Col
68- className = {
69- 'border-ink-300bg-canvas-0 inline-flex flex-col gap-2 rounded-md border p-1 shadow-sm'
70- }
71- >
72- < ol className = "list-decimal list-inside space-y-2" >
73- { ( bookmarkedSearches || [ ] ) . map ( ( search ) => (
74- < li key = { search . id }
75- className = "items-center justify-between gap-2 list-item marker:text-ink-500 marker:font-bold" >
76- { formatFilters ( search . search_filters as Partial < FilterFields > , search . location as locationType ) ?. join ( " • " ) }
77- < button
78- onClick = { async ( ) => {
79- await deleteBookmarkedSearch ( search . id )
80- refreshBookmarkedSearches ( )
81- } }
82- 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"
83- >
84- ×
85- </ button >
86- </ li >
87- ) ) }
88- </ ol >
67+ < p > We'll notify you daily when new people match your searches below.</ p >
68+ < Col
69+ className = {
70+ 'border-ink-300bg-canvas-0 inline-flex flex-col gap-2 rounded-md border p-1 shadow-sm'
71+ }
72+ >
73+ < ol className = "list-decimal list-inside space-y-2" >
74+ { ( bookmarkedSearches || [ ] ) . map ( ( search ) => (
75+ < li key = { search . id }
76+ className = "items-center justify-between gap-2 list-item marker:text-ink-500 marker:font-bold" >
77+ { formatFilters ( search . search_filters as Partial < FilterFields > , search . location as locationType ) ?. join ( " • " ) }
78+ < button
79+ onClick = { async ( ) => {
80+ await deleteBookmarkedSearch ( search . id )
81+ refreshBookmarkedSearches ( )
82+ } }
83+ 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"
84+ >
85+ ×
86+ </ button >
87+ </ li >
88+ ) ) }
89+ </ ol >
8990
90- </ Col >
91- </ >
92- ) : < 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 > }
91+ </ Col >
92+ </ >
93+ ) :
94+ < p > You haven't saved any search. To save one, click on Get Notified and we'll notify you daily when new people
95+ match it.</ p > }
9396 { /*<BookmarkSearchContent*/ }
9497 { /* total={bookmarkedSearches.length}*/ }
9598 { /* compatibilityQuestion={bookmarkedSearches[questionIndex]}*/ }
@@ -151,6 +154,10 @@ function StarModal(props: {
151154 refreshStars : ( ) => void
152155} ) {
153156 const { open, setOpen, starredUsers, refreshStars} = props
157+ // Track items being optimistically removed so we can hide them immediately
158+ const [ removingIds , setRemovingIds ] = useState < Set < string > > ( new Set ( ) )
159+
160+ const visibleUsers = ( starredUsers || [ ] ) . filter ( ( u ) => ! removingIds . has ( u . id ) )
154161
155162 return (
156163 < Modal
@@ -162,42 +169,57 @@ function StarModal(props: {
162169 >
163170 < Col className = { MODAL_CLASS } >
164171 < h3 > Saved Profiles</ h3 >
165- { starredUsers ?. length ? ( < >
166- < p > Here are the profiles you saved:</ p >
167- < Col
168- className = {
169- 'border-ink-300bg-canvas-0 inline-flex flex-col gap-2 rounded-md border p-1 shadow-sm'
170- }
171- >
172- < ol className = "list-decimal list-inside space-y-2" >
173- { ( starredUsers || [ ] ) . map ( ( user ) => (
174- < li key = { user . id }
175- className = "items-center justify-between gap-2 list-item marker:text-ink-500 marker:font-bold" >
176- { user . name } (< Link
177- href = { `https://${ DOMAIN } /${ user . username } ` }
178- // style={{color: "#2563eb", textDecoration: "none"}}
179- >
180- @{ user . username }
181- </ Link > ) { ' ' }
182- < button
183- onClick = { async ( ) => {
184- await api ( 'star-profile' , {
185- targetUserId : user . id ,
186- remove : true ,
187- } )
188- refreshStars ( )
189- } }
190- 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"
191- >
192- ×
193- </ button >
194- </ li >
195- ) ) }
196- </ ol >
172+ { visibleUsers ?. length ? ( < >
173+ < p > Here are the profiles you saved:</ p >
174+ < Col
175+ className = {
176+ 'border-ink-300bg-canvas-0 inline-flex flex-col gap-2 rounded-md border p-1 shadow-sm'
177+ }
178+ >
179+ < ol className = "list-decimal list-inside space-y-2" >
180+ { visibleUsers . map ( ( user ) => (
181+ < li key = { user . id }
182+ className = "items-center justify-between gap-2 list-item marker:text-ink-500 marker:font-bold" >
183+ { user . name } (< Link
184+ href = { `/${ user . username } ` }
185+ // style={{color: "#2563eb", textDecoration: "none"}}
186+ >
187+ @{ user . username }
188+ </ Link > ) { ' ' }
189+ < button
190+ onClick = { ( ) => {
191+ // Optimistically remove the user from the list
192+ setRemovingIds ( ( prev ) => new Set ( prev ) . add ( user . id ) )
193+ // Fire the API call without blocking UI
194+ api ( 'star-profile' , {
195+ targetUserId : user . id ,
196+ remove : true ,
197+ } )
198+ . then ( ( ) => {
199+ // Sync with server state
200+ refreshStars ( )
201+ } )
202+ . catch ( ( ) => {
203+ toast . error ( "Couldn't remove saved profile. Please try again." )
204+ // Revert optimistic removal on failure
205+ setRemovingIds ( ( prev ) => {
206+ const next = new Set ( prev )
207+ next . delete ( user . id )
208+ return next
209+ } )
210+ } )
211+ } }
212+ 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"
213+ >
214+ ×
215+ </ button >
216+ </ li >
217+ ) ) }
218+ </ ol >
197219
198- </ Col >
220+ </ Col >
199221 </ >
200- ) : < p > You haven't saved any profile. To save one, click on the star on their profile page.</ p > }
222+ ) : < p > You haven't saved any profile. To save one, click on the star on their profile page.</ p > }
201223 { /*<BookmarkSearchContent*/ }
202224 { /* total={bookmarkedSearches.length}*/ }
203225 { /* compatibilityQuestion={bookmarkedSearches[questionIndex]}*/ }
0 commit comments