mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-03-26 10:31:10 -04:00
Add filter for last online
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@compass/api",
|
||||
"description": "Backend API endpoints",
|
||||
"version": "1.17.2",
|
||||
"version": "1.18.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"watch:serve": "tsx watch src/serve.ts",
|
||||
|
||||
@@ -59,6 +59,7 @@ export type profileQueryType = {
|
||||
skipId?: string | undefined
|
||||
orderBy?: string | undefined
|
||||
lastModificationWithin?: string | undefined
|
||||
last_active?: string | undefined
|
||||
locale?: string | undefined
|
||||
} & {
|
||||
[K in OptionTableKey]?: string[] | undefined
|
||||
@@ -118,6 +119,7 @@ export const loadProfiles = async (props: profileQueryType) => {
|
||||
lastModificationWithin,
|
||||
skipId,
|
||||
locale = 'en',
|
||||
last_active,
|
||||
} = props
|
||||
|
||||
const filterLocation = lat && lon && radius
|
||||
@@ -171,7 +173,7 @@ export const loadProfiles = async (props: profileQueryType) => {
|
||||
)
|
||||
|
||||
const joins = [
|
||||
orderByParam === 'last_online_time' && leftJoin(userActivityJoin),
|
||||
(orderByParam === 'last_online_time' || last_active) && leftJoin(userActivityJoin),
|
||||
orderByParam === 'compatibility_score' && compatibleWithUserId && join(compatibilityScoreJoin),
|
||||
joinInterests && leftJoin(interestsJoin),
|
||||
joinCauses && leftJoin(causesJoin),
|
||||
@@ -413,6 +415,20 @@ export const loadProfiles = async (props: profileQueryType) => {
|
||||
lastModificationWithin,
|
||||
}),
|
||||
|
||||
last_active &&
|
||||
where(`user_activity.last_online_time >= NOW() - INTERVAL $(last_active_interval)`, {
|
||||
last_active_interval:
|
||||
last_active === 'today'
|
||||
? '1 day'
|
||||
: last_active === '3days'
|
||||
? '3 days'
|
||||
: last_active === 'week'
|
||||
? '7 days'
|
||||
: last_active === 'month'
|
||||
? '30 days'
|
||||
: '90 days',
|
||||
}),
|
||||
|
||||
// Exclude profiles that the requester has chosen to hide
|
||||
userId &&
|
||||
where(
|
||||
@@ -428,7 +444,7 @@ export const loadProfiles = async (props: profileQueryType) => {
|
||||
let selectCols = 'profiles.*, users.name, users.username, users.data as user'
|
||||
if (orderByParam === 'compatibility_score') {
|
||||
selectCols += ', cs.score as compatibility_score'
|
||||
} else if (orderByParam === 'last_online_time') {
|
||||
} else if (orderByParam === 'last_online_time' || last_active) {
|
||||
selectCols += ', user_activity.last_online_time'
|
||||
}
|
||||
if (joinInterests) selectCols += `, COALESCE(profile_interests.interests, '{}') AS interests`
|
||||
|
||||
@@ -18,7 +18,7 @@ import {arrify} from 'common/util/array'
|
||||
import {z} from 'zod'
|
||||
|
||||
import {LikeData, ShipData} from './profile-types'
|
||||
import {FullUser} from './user-types'
|
||||
import {FullUser} from './user-types' // mqp: very unscientific, just balancing our willingness to accept load
|
||||
|
||||
// mqp: very unscientific, just balancing our willingness to accept load
|
||||
// with user willingness to put up with stale data
|
||||
@@ -583,6 +583,7 @@ export const API = (_apiTypeCheck = {
|
||||
work: arraybeSchema.optional(),
|
||||
relationship_status: arraybeSchema.optional(),
|
||||
languages: arraybeSchema.optional(),
|
||||
last_active: z.string().optional(),
|
||||
wants_kids_strength: z.coerce.number().optional(),
|
||||
has_kids: z.coerce.number().optional(),
|
||||
is_smoker: zBoolean.optional().optional(),
|
||||
|
||||
@@ -226,6 +226,15 @@ export const GENDERS_PLURAL = {
|
||||
Other: 'other',
|
||||
}
|
||||
|
||||
export const LAST_ONLINE_CHOICES = {
|
||||
Today: 'today',
|
||||
'Last 3 days': '3days',
|
||||
'Last week': 'week',
|
||||
'Last month': 'month',
|
||||
'Last 3 months': '3months',
|
||||
'Any time': 'any',
|
||||
}
|
||||
|
||||
export const INVERTED_RELATIONSHIP_CHOICES = invert(RELATIONSHIP_CHOICES)
|
||||
export const INVERTED_RELATIONSHIP_STATUS_CHOICES = invert(RELATIONSHIP_STATUS_CHOICES)
|
||||
export const INVERTED_ROMANTIC_CHOICES = invert(ROMANTIC_CHOICES)
|
||||
@@ -237,3 +246,4 @@ export const INVERTED_LANGUAGE_CHOICES = invert(LANGUAGE_CHOICES)
|
||||
export const INVERTED_RACE_CHOICES = invert(RACE_CHOICES)
|
||||
export const INVERTED_MBTI_CHOICES = invert(MBTI_CHOICES)
|
||||
export const INVERTED_GENDERS = invert(GENDERS)
|
||||
export const INVERTED_LAST_ONLINE_CHOICES = invert(LAST_ONLINE_CHOICES)
|
||||
|
||||
@@ -11,6 +11,7 @@ import {cloneDeep} from 'lodash'
|
||||
|
||||
export type FilterFields = {
|
||||
orderBy: 'last_online_time' | 'created_time' | 'compatibility_score'
|
||||
last_active: string | undefined
|
||||
geodbCityIds: string[] | null
|
||||
lat: number | null
|
||||
lon: number | null
|
||||
@@ -112,6 +113,7 @@ export const initialFilters: Partial<FilterFields> = {
|
||||
big5_agreeableness_max: undefined,
|
||||
big5_neuroticism_min: undefined,
|
||||
big5_neuroticism_max: undefined,
|
||||
last_active: undefined,
|
||||
orderBy: 'created_time',
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ import {DietType, RelationshipType, RomanticType} from 'web/lib/util/convert-typ
|
||||
|
||||
import {AgeFilter, AgeFilterText} from './age-filter'
|
||||
import {GenderFilter, GenderFilterText} from './gender-filter'
|
||||
import {LastActiveFilter, LastActiveFilterText} from './last-active-filter'
|
||||
import {LocationFilter, LocationFilterProps, LocationFilterText} from './location-filter'
|
||||
import {MyMatchesToggle} from './my-matches-toggle'
|
||||
import {RelationshipFilter, RelationshipFilterText} from './relationship-filter'
|
||||
@@ -665,6 +666,28 @@ export function DesktopFilters(props: {
|
||||
popoverClassName="bg-canvas-50"
|
||||
menuWidth="w-50"
|
||||
/>
|
||||
|
||||
{/* LAST ACTIVE */}
|
||||
<CustomizeableDropdown
|
||||
buttonContent={(open: boolean) => (
|
||||
<DropdownButton
|
||||
open={open}
|
||||
content={
|
||||
<LastActiveFilterText
|
||||
last_active={filters.last_active}
|
||||
highlightedClass={open ? 'text-primary-500' : ''}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
dropdownMenuContent={(close) => (
|
||||
<Col className="mx-2 mb-4">
|
||||
<LastActiveFilter filters={filters} updateFilter={updateFilter} close={close} />
|
||||
</Col>
|
||||
)}
|
||||
popoverClassName="bg-canvas-50"
|
||||
menuWidth="w-50"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
76
web/components/filters/last-active-filter.tsx
Normal file
76
web/components/filters/last-active-filter.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import clsx from 'clsx'
|
||||
import {INVERTED_LAST_ONLINE_CHOICES, LAST_ONLINE_CHOICES} from 'common/choices'
|
||||
import {FilterFields} from 'common/filters'
|
||||
import {Row} from 'web/components/layout/row'
|
||||
import {useT} from 'web/lib/locale'
|
||||
|
||||
import {Col} from '../layout/col'
|
||||
|
||||
export function LastActiveFilterText(props: {
|
||||
last_active: string | undefined
|
||||
highlightedClass?: string
|
||||
}) {
|
||||
const {last_active, highlightedClass} = props
|
||||
const t = useT()
|
||||
const option = Object.values(LAST_ONLINE_CHOICES).find((opt) => opt === last_active)
|
||||
const label =
|
||||
INVERTED_LAST_ONLINE_CHOICES[option ?? ''] ?? t('filter.last_active.any', 'Any time')
|
||||
|
||||
return (
|
||||
<Row className="items-center gap-0.5">
|
||||
<span className={highlightedClass}>
|
||||
{t('filter.last_active.label', 'Active')}: {label}
|
||||
</span>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
export function LastActiveFilter(props: {
|
||||
filters: Partial<FilterFields>
|
||||
updateFilter: (newState: Partial<FilterFields>) => void
|
||||
close?: () => void
|
||||
}) {
|
||||
const {filters, updateFilter, close} = props
|
||||
|
||||
return (
|
||||
<DropdownOptions
|
||||
items={INVERTED_LAST_ONLINE_CHOICES}
|
||||
activeKey={filters.last_active || 'any'}
|
||||
onClick={(option) => {
|
||||
updateFilter({last_active: option === 'any' ? undefined : option})
|
||||
close?.()
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export function DropdownOptions(props: {
|
||||
items: Record<string, any>
|
||||
onClick: (item: any) => void
|
||||
activeKey: string
|
||||
}) {
|
||||
const {items, onClick, activeKey} = props
|
||||
return (
|
||||
<Col className={'w-[150px]'}>
|
||||
{Object.entries(items).map(([key, item]) => (
|
||||
<div key={key}>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
onClick(key)
|
||||
}}
|
||||
className={clsx(
|
||||
key == activeKey ? 'bg-primary-100' : 'hover:bg-canvas-100 hover:text-ink-900',
|
||||
'text-ink-700',
|
||||
'flex w-full gap-2 px-4 py-2 text-left text-sm rounded-md',
|
||||
)}
|
||||
>
|
||||
{item.icon && <div className="w-5">{item.icon}</div>}
|
||||
{item.label ?? item}
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import {AgeFilter, AgeFilterText, getNoMinMaxAge} from './age-filter'
|
||||
import {DrinksFilter, DrinksFilterText, getNoMinMaxDrinks} from './drinks-filter'
|
||||
import {GenderFilter, GenderFilterText} from './gender-filter'
|
||||
import {HasKidsFilter, HasKidsLabel} from './has-kids-filter'
|
||||
import {LastActiveFilter, LastActiveFilterText} from './last-active-filter'
|
||||
import {LocationFilter, LocationFilterProps, LocationFilterText} from './location-filter'
|
||||
import {MyMatchesToggle} from './my-matches-toggle'
|
||||
import {RelationshipFilter, RelationshipFilterText} from './relationship-filter'
|
||||
@@ -551,6 +552,22 @@ function MobileFilters(props: {
|
||||
>
|
||||
<EducationFilter filters={filters} updateFilter={updateFilter} />
|
||||
</MobileFilterSection>
|
||||
|
||||
{/* LAST ACTIVE */}
|
||||
<MobileFilterSection
|
||||
title={t('filter.last_active.title', 'Last active')}
|
||||
openFilter={openFilter}
|
||||
setOpenFilter={setOpenFilter}
|
||||
isActive={!!filters.last_active}
|
||||
selection={
|
||||
<LastActiveFilterText
|
||||
last_active={filters.last_active}
|
||||
highlightedClass={filters.last_active ? 'text-primary-600' : 'text-ink-900'}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<LastActiveFilter filters={filters} updateFilter={updateFilter} />
|
||||
</MobileFilterSection>
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user