mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-03-26 10:31:10 -04:00
* Test * Add pretty formatting * Fix Tests * Fix Tests * Fix Tests * Fix * Add pretty formatting fix * Fix * Test * Fix tests * Clean typeckech * Add prettier check * Fix api tsconfig * Fix api tsconfig * Fix tsconfig * Fix * Fix * Prettier
170 lines
5.6 KiB
TypeScript
170 lines
5.6 KiB
TypeScript
import clsx from 'clsx'
|
|
import {ShipData} from 'common/api/profile-types'
|
|
import {Profile} from 'common/profiles/profile'
|
|
import {User} from 'common/user'
|
|
import {groupBy, max, orderBy} from 'lodash'
|
|
import {useState} from 'react'
|
|
import {Col} from 'web/components/layout/col'
|
|
import {Modal, MODAL_CLASS} from 'web/components/layout/modal'
|
|
import {Row} from 'web/components/layout/row'
|
|
import {Avatar, EmptyAvatar} from 'web/components/widgets/avatar'
|
|
import {Carousel} from 'web/components/widgets/carousel'
|
|
import {UserLink} from 'web/components/widgets/user-link'
|
|
import {useProfileByUserId} from 'web/hooks/use-profile'
|
|
import {useUser} from 'web/hooks/use-user'
|
|
import {useUserById} from 'web/hooks/use-user-supabase'
|
|
import {hasShipped} from 'web/lib/util/ship-util'
|
|
|
|
import {MatchAvatars} from '../matches/match-avatars'
|
|
import {Subtitle} from './profile-subtitle'
|
|
import {ShipButton} from './ship-button'
|
|
|
|
export const ShipsList = (props: {
|
|
label: string
|
|
ships: ShipData[]
|
|
profileProfile: Profile
|
|
refreshShips: () => Promise<void>
|
|
}) => {
|
|
const {label, ships, profileProfile, refreshShips} = props
|
|
|
|
const shipsWithTargetId = ships.map(({target1_id, target2_id, ...other}) => ({
|
|
...other,
|
|
target1_id,
|
|
target2_id,
|
|
targetId: target1_id === profileProfile.user_id ? target2_id : target1_id,
|
|
}))
|
|
const shipsByTargetId = groupBy(shipsWithTargetId, (s) => s.targetId)
|
|
const sortedTargetIds = orderBy(
|
|
Object.keys(shipsByTargetId),
|
|
(targetId) => max(shipsByTargetId[targetId].map((s) => s.created_time)),
|
|
'desc',
|
|
)
|
|
|
|
return (
|
|
<Col className="gap-1">
|
|
<Subtitle>{label}</Subtitle>
|
|
{sortedTargetIds.length > 0 ? (
|
|
<Carousel className="w-full" labelsParentClassName="gap-0">
|
|
{sortedTargetIds.map((targetId) => {
|
|
return (
|
|
<ShipsTargetDisplay
|
|
key={targetId}
|
|
ships={shipsByTargetId[targetId]}
|
|
profileProfile={profileProfile}
|
|
refreshShips={refreshShips}
|
|
/>
|
|
)
|
|
})}
|
|
</Carousel>
|
|
) : (
|
|
<div className="text-ink-500">None</div>
|
|
)}
|
|
</Col>
|
|
)
|
|
}
|
|
|
|
const ShipsTargetDisplay = (props: {
|
|
ships: (ShipData & {targetId: string})[]
|
|
refreshShips: () => Promise<void>
|
|
profileProfile: Profile
|
|
className?: string
|
|
}) => {
|
|
const {ships, refreshShips, profileProfile, className} = props
|
|
const {targetId} = ships[0]
|
|
|
|
const targetProfile = useProfileByUserId(targetId)
|
|
const targetUser = useUserById(targetId) as User | null | undefined
|
|
const [open, setOpen] = useState(false)
|
|
|
|
const currentUser = useUser()
|
|
const shipped = hasShipped(currentUser, profileProfile.user_id, targetId, ships)
|
|
|
|
return (
|
|
<>
|
|
<button
|
|
className={clsx(className, 'group flex flex-col items-center gap-1')}
|
|
onClick={() => setOpen(!open)}
|
|
>
|
|
<UserAvatar className="-ml-1 first:ml-0" userId={targetId} />
|
|
<div className="text-ink-500 group-hover:underline group-active:underline">
|
|
x {ships.length}
|
|
</div>
|
|
</button>
|
|
|
|
{open && (
|
|
<Modal open={open} setOpen={setOpen}>
|
|
<Col className={clsx(MODAL_CLASS, 'relative')}>
|
|
{targetProfile && targetUser && (
|
|
<>
|
|
<MatchAvatars
|
|
profileProfile={profileProfile}
|
|
matchedProfile={{...targetProfile, user: targetUser}}
|
|
/>
|
|
<Row className="w-full items-baseline justify-stretch gap-2 text-lg font-semibold">
|
|
<Row className="flex-1 justify-end">
|
|
<UserLink hideBadge user={profileProfile.user} noLink />
|
|
</Row>
|
|
&
|
|
<Row className="flex-1 justify-start">
|
|
<UserLink hideBadge user={targetUser} />
|
|
</Row>
|
|
</Row>
|
|
</>
|
|
)}
|
|
<Col className="gap-2 self-start">
|
|
<div className="text-ink-600 text-lg font-semibold">
|
|
Shipping them ({ships.length})
|
|
</div>
|
|
<Col className="gap-2">
|
|
{ships.map((ship) => (
|
|
<UserInfoRow key={ship.creator_id} userId={ship.creator_id} />
|
|
))}
|
|
</Col>
|
|
</Col>
|
|
{currentUser &&
|
|
profileProfile.user_id !== currentUser?.id &&
|
|
targetId !== currentUser?.id && (
|
|
<Row className="sticky bottom-[70px] right-0 mr-1 self-end lg:bottom-6">
|
|
<ShipButton
|
|
shipped={shipped}
|
|
targetId1={profileProfile.user_id}
|
|
targetId2={targetId}
|
|
refresh={refreshShips}
|
|
/>
|
|
</Row>
|
|
)}
|
|
</Col>
|
|
</Modal>
|
|
)}
|
|
</>
|
|
)
|
|
}
|
|
|
|
const UserAvatar = (props: {userId: string; className?: string}) => {
|
|
const {userId, className} = props
|
|
const profile = useProfileByUserId(userId)
|
|
const user = useUserById(userId)
|
|
|
|
if (!profile || !profile.pinned_url) return <EmptyAvatar className={className} size={10} />
|
|
return (
|
|
<Avatar className={className} avatarUrl={profile.pinned_url} username={user?.username} noLink />
|
|
)
|
|
}
|
|
|
|
const UserInfoRow = (props: {userId: string; className?: string}) => {
|
|
const {userId, className} = props
|
|
const user = useUserById(userId)
|
|
const profile = useProfileByUserId(userId)
|
|
|
|
return (
|
|
<Row className={clsx(className, 'items-center gap-2')}>
|
|
{!profile || !profile.pinned_url ? (
|
|
<EmptyAvatar size={10} />
|
|
) : (
|
|
<Avatar avatarUrl={profile.pinned_url} username={user?.username} />
|
|
)}
|
|
{user && <UserLink user={user} hideBadge />}
|
|
</Row>
|
|
)
|
|
}
|