Skip to content

Commit 036776b

Browse files
committed
Add search results count in profiles grid
1 parent 63e48d9 commit 036776b

File tree

5 files changed

+102
-65
lines changed

5 files changed

+102
-65
lines changed

backend/api/src/get-profiles.ts

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,15 @@ export const loadProfiles = async (props: profileQueryType) => {
8484
const keywords = name ? name.split(",").map(q => q.trim()).filter(Boolean) : []
8585
// console.debug('keywords:', keywords)
8686

87-
// compatibility. TODO: do this in sql
87+
// TODO: do this in SQL for better performance
8888
if (orderByParam === 'compatibility_score') {
8989
if (!compatibleWithUserId) {
9090
console.error('Incompatible with user ID')
9191
throw Error('Incompatible with user ID')
9292
}
9393

9494
const {compatibleProfiles} = await getCompatibleProfiles(compatibleWithUserId)
95-
const profiles = compatibleProfiles.filter(
95+
let profiles = compatibleProfiles.filter(
9696
(l) =>
9797
(!name || l.user.name.toLowerCase().includes(name.toLowerCase())) &&
9898
(!genders || genders.includes(l.gender ?? '')) &&
@@ -133,33 +133,37 @@ export const loadProfiles = async (props: profileQueryType) => {
133133
(l.id.toString() != skipId) &&
134134
(!geodbCityIds ||
135135
(l.geodb_city_id && geodbCityIds.includes(l.geodb_city_id))) &&
136-
(!filterLocation ||(
136+
(!filterLocation || (
137137
l.city_latitude && l.city_longitude &&
138138
Math.abs(l.city_latitude - lat) < radius / 69.0 &&
139139
Math.abs(l.city_longitude - lon) < radius / (69.0 * Math.cos(lat * Math.PI / 180)) &&
140140
Math.pow(l.city_latitude - lat, 2) + Math.pow((l.city_longitude - lon) * Math.cos(lat * Math.PI / 180), 2) < Math.pow(radius / 69.0, 2)
141-
)) &&
141+
)) &&
142142
(shortBio || (l.bio_length ?? 0) >= MIN_BIO_LENGTH)
143143
)
144144

145+
const count = profiles.length
146+
145147
const cursor = after
146148
? profiles.findIndex((l) => l.id.toString() === after) + 1
147149
: 0
148150
console.debug(cursor)
149151

150-
if (limitParam) return profiles.slice(cursor, cursor + limitParam)
152+
if (limitParam) profiles = profiles.slice(cursor, cursor + limitParam)
151153

152-
return profiles
154+
return {profiles, count}
153155
}
154156

155157
const tablePrefix = userActivityColumns.includes(orderByParam) ? 'user_activity' : 'profiles'
156158
const userActivityJoin = 'user_activity on user_activity.user_id = profiles.user_id'
157159

158-
const query = renderSql(
159-
select('profiles.*, name, username, users.data as user, user_activity.last_online_time'),
160+
const tableSelection = [
160161
from('profiles'),
161162
join('users on users.id = profiles.user_id'),
162163
leftJoin(userActivityJoin),
164+
]
165+
166+
const filters = [
163167
where('looking_for_matches = true'),
164168
where(`profiles.disabled != true`),
165169
// where(`pinned_url is not null and pinned_url != ''`),
@@ -270,35 +274,52 @@ export const loadProfiles = async (props: profileQueryType) => {
270274

271275
skipId && where(`profiles.user_id != $(skipId)`, {skipId}),
272276

277+
!shortBio && where(`bio_length >= ${MIN_BIO_LENGTH}`, {MIN_BIO_LENGTH}),
278+
279+
lastModificationWithin && where(`last_modification_time >= NOW() - INTERVAL $(lastModificationWithin)`, {lastModificationWithin}),
280+
]
281+
282+
const query = renderSql(
283+
select('profiles.*, name, username, users.data as user, user_activity.last_online_time'),
284+
...tableSelection,
285+
...filters,
286+
273287
orderBy(`${tablePrefix}.${orderByParam} DESC`),
274-
after &&
275-
where(
288+
289+
after && where(
276290
`${tablePrefix}.${orderByParam} < (
277291
SELECT ${tablePrefix}.${orderByParam}
278292
FROM profiles
279293
LEFT JOIN ${userActivityJoin}
280294
WHERE profiles.id = $(after)
281-
)`,
282-
{after}
283-
),
295+
)`, {after}),
284296

285-
!shortBio && where(`bio_length >= ${MIN_BIO_LENGTH}`, {MIN_BIO_LENGTH}),
297+
limitParam && limit(limitParam),
298+
)
286299

287-
lastModificationWithin && where(`last_modification_time >= NOW() - INTERVAL $(lastModificationWithin)`, {lastModificationWithin}),
300+
// console.debug('query:', query)
301+
302+
const profiles = await pg.map(query, [], convertRow)
288303

289-
limitParam && limit(limitParam)
304+
console.debug('profiles:', profiles)
305+
306+
const countQuery = renderSql(
307+
select(`count(*) as count`),
308+
...tableSelection,
309+
...filters,
290310
)
291311

292-
// console.debug('query:', query)
312+
const count = await pg.one<number>(countQuery, [], (r) => Number(r.count))
293313

294-
return await pg.map(query, [], convertRow)
314+
return {profiles, count}
295315
}
296316

297-
export const getProfiles: APIHandler<'get-profiles'> = async (props, _auth) => {
317+
export const getProfiles: APIHandler<'get-profiles'> = async (props, auth) => {
298318
try {
299-
const profiles = await loadProfiles(props)
300-
return {status: 'success', profiles: profiles}
319+
if (!props.skipId) props.skipId = auth.uid
320+
const {profiles, count} = await loadProfiles(props)
321+
return {status: 'success', profiles: profiles, count: count}
301322
} catch {
302-
return {status: 'fail', profiles: []}
323+
return {status: 'fail', profiles: [], count: 0}
303324
}
304325
}

backend/api/src/send-search-notifications.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export const sendSearchNotifications = async () => {
6060
lastModificationWithin: '24 hours',
6161
shortBio: true,
6262
}
63-
const profiles = await loadProfiles(props as profileQueryType)
63+
const {profiles} = await loadProfiles(props as profileQueryType)
6464
console.debug(profiles.map((item: any) => item.name))
6565
if (!profiles.length) continue
6666
if (!(row.creator_id in matches)) {

common/src/api/schema.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ export const API = (_apiTypeCheck = {
468468
lon: z.coerce.number().optional(),
469469
radius: z.coerce.number().optional(),
470470
compatibleWithUserId: z.string().optional(),
471+
skipId: z.string().optional(),
471472
orderBy: z
472473
.enum(['last_online_time', 'created_time', 'compatibility_score'])
473474
.optional()
@@ -476,7 +477,8 @@ export const API = (_apiTypeCheck = {
476477
.strict(),
477478
returns: {} as {
478479
status: 'success' | 'fail'
479-
profiles: Profile[]
480+
profiles: Profile[],
481+
count: number,
480482
},
481483
summary: 'List profiles with filters, pagination and ordering',
482484
tag: 'Profiles',

web/components/filters/search.tsx

Lines changed: 48 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ export const Search = (props: {
108108
locationFilterProps: LocationFilterProps
109109
bookmarkedSearches: BookmarkedSearchesType[]
110110
refreshBookmarkedSearches: () => void
111+
profileCount: number | undefined
111112
}) => {
112113
const {
113114
youProfile,
@@ -121,6 +122,7 @@ export const Search = (props: {
121122
refreshBookmarkedSearches,
122123
starredUsers,
123124
refreshStars,
125+
profileCount,
124126
} = props
125127

126128
const [openFiltersModal, setOpenFiltersModal] = useState(false)
@@ -237,48 +239,55 @@ export const Search = (props: {
237239
includeRelationshipFilters={youSeekingRelationship}
238240
/>
239241
</RightModal>
240-
<Row className={'mb-2 gap-2'}>
241-
<Button
242-
disabled={loadingBookmark}
243-
loading={loadingBookmark}
244-
onClick={() => {
245-
if (bookmarkedSearches.length >= MAX_BOOKMARKED_SEARCHES) {
246-
toast.error(`You can bookmark maximum ${MAX_BOOKMARKED_SEARCHES} searches; please delete one first.`)
247-
setOpenBookmarks(true)
248-
return
249-
}
250-
setLoadingBookmark(true)
251-
submitBookmarkedSearch(filters, locationFilterProps, user?.id)
252-
.finally(() => {
253-
setLoadingBookmark(false)
254-
setBookmarked(true)
255-
refreshBookmarkedSearches()
242+
<Row className="items-center justify-between w-full flex-wrap gap-2">
243+
<Row className={'mb-2 gap-2'}>
244+
<Button
245+
disabled={loadingBookmark}
246+
loading={loadingBookmark}
247+
onClick={() => {
248+
if (bookmarkedSearches.length >= MAX_BOOKMARKED_SEARCHES) {
249+
toast.error(`You can bookmark maximum ${MAX_BOOKMARKED_SEARCHES} searches; please delete one first.`)
256250
setOpenBookmarks(true)
257-
})
258-
}}
259-
size={'xs'}
260-
color={'none'}
261-
className={'bg-canvas-100 hover:bg-canvas-200'}
262-
>
263-
{bookmarked ? 'Saved!' : loadingBookmark ? '' : 'Get Notified'}
264-
</Button>
251+
return
252+
}
253+
setLoadingBookmark(true)
254+
submitBookmarkedSearch(filters, locationFilterProps, user?.id)
255+
.finally(() => {
256+
setLoadingBookmark(false)
257+
setBookmarked(true)
258+
refreshBookmarkedSearches()
259+
setOpenBookmarks(true)
260+
})
261+
}}
262+
size={'xs'}
263+
color={'none'}
264+
className={'bg-canvas-100 hover:bg-canvas-200'}
265+
>
266+
{bookmarked ? 'Saved!' : loadingBookmark ? '' : 'Get Notified'}
267+
</Button>
265268

266-
<BookmarkSearchButton
267-
refreshBookmarkedSearches={refreshBookmarkedSearches}
268-
bookmarkedSearches={bookmarkedSearches}
269-
open={openBookmarks}
270-
setOpen={setOpenBookmarks}
271-
/>
269+
<BookmarkSearchButton
270+
refreshBookmarkedSearches={refreshBookmarkedSearches}
271+
bookmarkedSearches={bookmarkedSearches}
272+
open={openBookmarks}
273+
setOpen={setOpenBookmarks}
274+
/>
272275

273-
<BookmarkStarButton
274-
refreshStars={refreshStars}
275-
starredUsers={starredUsers}
276-
open={openStarBookmarks}
277-
setOpen={(checked) => {
278-
setOpenStarBookmarks(checked)
279-
refreshStars()
280-
}}
281-
/>
276+
<BookmarkStarButton
277+
refreshStars={refreshStars}
278+
starredUsers={starredUsers}
279+
open={openStarBookmarks}
280+
setOpen={(checked) => {
281+
setOpenStarBookmarks(checked)
282+
refreshStars()
283+
}}
284+
/>
285+
</Row>
286+
{(profileCount ?? 0) > 0 && (
287+
<Col className="text-sm text-ink-500">
288+
<p>{profileCount} {(profileCount ?? 0) > 1 ? 'people' : 'person'}</p>
289+
</Col>
290+
)}
282291
</Row>
283292
</Col>
284293
)

web/components/profiles/profiles-home.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export function ProfilesHome() {
2929
} = useFilters(you ?? undefined);
3030

3131
const [profiles, setProfiles] = usePersistentInMemoryState<Profile[] | undefined>(undefined, 'profiles');
32+
const [profileCount, setProfileCount] = usePersistentInMemoryState<number | undefined>(undefined, 'profile-count');
3233
const {bookmarkedSearches, refreshBookmarkedSearches} = useBookmarkedSearches(user?.id)
3334
const [isLoadingMore, setIsLoadingMore] = useState(false);
3435
const [isReloading, setIsReloading] = useState(false);
@@ -57,8 +58,11 @@ export function ProfilesHome() {
5758
});
5859
console.debug('Refreshing profiles, filters:', args);
5960
api('get-profiles', args as any)
60-
.then(({profiles}) => {
61-
if (current === id.current) setProfiles(profiles);
61+
.then(({profiles, count}) => {
62+
if (current === id.current) {
63+
setProfiles(profiles)
64+
setProfileCount(count)
65+
}
6266
})
6367
.finally(() => {
6468
if (current === id.current) setIsReloading(false);
@@ -110,6 +114,7 @@ export function ProfilesHome() {
110114
locationFilterProps={locationFilterProps}
111115
bookmarkedSearches={bookmarkedSearches}
112116
refreshBookmarkedSearches={refreshBookmarkedSearches}
117+
profileCount={profileCount}
113118
/>
114119
{displayProfiles === undefined || compatibleProfiles === undefined ? (
115120
<CompassLoadingIndicator/>

0 commit comments

Comments
 (0)