parent
fb3fe8be42
commit
2c1ab6b19d
2 changed files with 366 additions and 4 deletions
@ -0,0 +1,259 @@ |
||||
import { useState } from 'preact/hooks'; |
||||
import type { ListFriendsResponse } from './FriendsPage'; |
||||
import { DeleteUserIcon } from '../ReactIcons/DeleteUserIcon'; |
||||
import { pageProgressMessage } from '../../stores/page'; |
||||
import { httpDelete, httpPost } from '../../lib/http'; |
||||
import { useToast } from '../../hooks/use-toast'; |
||||
import { TrashIcon } from '../ReactIcons/TrashIcon'; |
||||
import { AddedUserIcon } from '../ReactIcons/AddedUserIcon'; |
||||
import { AddUserIcon } from '../ReactIcons/AddUserIcon'; |
||||
|
||||
type FriendProgressItemProps = { |
||||
friend: ListFriendsResponse[0]; |
||||
onShowResourceProgress: (resourceId: string) => void; |
||||
onReload: () => void; |
||||
}; |
||||
|
||||
export function FriendProgressItem(props: FriendProgressItemProps) { |
||||
const { friend, onShowResourceProgress, onReload } = props; |
||||
const toast = useToast(); |
||||
|
||||
async function deleteFriend(userId: string, successMessage: string) { |
||||
pageProgressMessage.set('Please wait...'); |
||||
const { response, error } = await httpDelete( |
||||
`${import.meta.env.PUBLIC_API_URL}/v1-delete-friend/${userId}`, |
||||
{} |
||||
); |
||||
|
||||
if (error || !response) { |
||||
toast.error(error?.message || 'Something went wrong'); |
||||
return; |
||||
} |
||||
|
||||
toast.success(successMessage); |
||||
onReload(); |
||||
} |
||||
|
||||
async function addFriend(userId: string, successMessage: string) { |
||||
pageProgressMessage.set('Please wait...'); |
||||
const { response, error } = await httpPost( |
||||
`${import.meta.env.PUBLIC_API_URL}/v1-add-friend/${userId}`, |
||||
{} |
||||
); |
||||
|
||||
if (error || !response) { |
||||
toast.error(error?.message || 'Something went wrong'); |
||||
return; |
||||
} |
||||
|
||||
toast.success(successMessage); |
||||
onReload(); |
||||
} |
||||
|
||||
const roadmaps = (friend.roadmaps || []).sort((a, b) => { |
||||
return b.done - a.done; |
||||
}); |
||||
|
||||
const [showAll, setShowAll] = useState(false); |
||||
const status = friend.status; |
||||
|
||||
return ( |
||||
<> |
||||
<div |
||||
className={`flex h-full min-h-[270px] flex-col overflow-hidden rounded-md border`} |
||||
key={friend.userId} |
||||
> |
||||
<div className={`relative flex items-center gap-3 border-b p-3`}> |
||||
<img |
||||
src={ |
||||
friend.avatar |
||||
? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${friend.avatar}` |
||||
: '/images/default-avatar.png' |
||||
} |
||||
alt={friend.name || ''} |
||||
className="h-8 w-8 rounded-full" |
||||
/> |
||||
<div className="inline-grid w-full"> |
||||
<h3 className="truncate font-medium">{friend.name}</h3> |
||||
<p className="truncate text-sm text-gray-500">{friend.email}</p> |
||||
</div> |
||||
</div> |
||||
{friend.status === 'accepted' && ( |
||||
<div className="relative flex grow flex-col space-y-2 p-3"> |
||||
{(showAll ? roadmaps : roadmaps.slice(0, 4)).map((progress) => { |
||||
return ( |
||||
<button |
||||
onClick={() => onShowResourceProgress(progress.resourceId)} |
||||
className="group relative overflow-hidden rounded-md border p-2 hover:border-gray-300 hover:text-black focus:outline-none" |
||||
key={progress.resourceId} |
||||
> |
||||
<span className="relative z-10 flex items-center justify-between text-sm"> |
||||
<span className="inline-grid"> |
||||
<span className={'truncate'}>{progress.title}</span> |
||||
</span> |
||||
<span className="ml-1.5 shrink-0 text-xs text-gray-400"> |
||||
{progress.done} / {progress.total} |
||||
</span> |
||||
</span> |
||||
<span |
||||
className="absolute inset-0 bg-gray-100 group-hover:bg-gray-200" |
||||
style={{ |
||||
width: `${(progress.done / progress.total) * 100}%`, |
||||
}} |
||||
/> |
||||
</button> |
||||
); |
||||
})} |
||||
|
||||
{roadmaps.length > 4 && !showAll && ( |
||||
<button |
||||
onClick={() => setShowAll(true)} |
||||
className={'text-sm text-gray-400 underline'} |
||||
> |
||||
+ {roadmaps.length - 4} more |
||||
</button> |
||||
)} |
||||
|
||||
{showAll && ( |
||||
<button |
||||
onClick={() => setShowAll(false)} |
||||
className={'text-sm text-gray-400 underline'} |
||||
> |
||||
- Show less |
||||
</button> |
||||
)} |
||||
|
||||
{roadmaps.length === 0 && ( |
||||
<div className="text-sm text-gray-500">No progress</div> |
||||
)} |
||||
</div> |
||||
)} |
||||
|
||||
{friend.status === 'rejected' && ( |
||||
<> |
||||
<div |
||||
className={'flex w-full flex-grow items-center justify-center'} |
||||
> |
||||
<span class=" flex flex-col items-center text-red-500"> |
||||
<DeleteUserIcon additionalClasses="mr-2 h-8 w-8 mb-1" /> |
||||
Request Rejected |
||||
</span> |
||||
</div> |
||||
<span class="flex cursor-default items-center justify-center border-t py-2 text-center text-sm"> |
||||
Changed your mind?{' '} |
||||
<button |
||||
className="ml-2 font-medium text-red-700 underline underline-offset-2 hover:text-red-500" |
||||
onClick={() => { |
||||
addFriend(friend.userId, 'Friend request accepted').finally( |
||||
() => { |
||||
pageProgressMessage.set(''); |
||||
} |
||||
); |
||||
}} |
||||
> |
||||
Accept |
||||
</button> |
||||
</span> |
||||
</> |
||||
)} |
||||
|
||||
{friend.status === 'got_rejected' && ( |
||||
<> |
||||
<div |
||||
className={'flex w-full flex-grow items-center justify-center'} |
||||
> |
||||
<span class=" flex flex-col items-center text-sm text-red-500"> |
||||
<DeleteUserIcon additionalClasses="mr-2 h-8 w-8 mb-1" /> |
||||
Request Rejected |
||||
</span> |
||||
</div> |
||||
<span class="flex cursor-default items-center justify-center border-t py-2.5 text-center text-sm"> |
||||
<button |
||||
className="ml-2 flex items-center font-medium text-red-700 underline underline-offset-2 hover:text-red-500" |
||||
onClick={() => { |
||||
deleteFriend(friend.userId, 'Friend request removed').finally( |
||||
() => { |
||||
pageProgressMessage.set(''); |
||||
} |
||||
); |
||||
}} |
||||
> |
||||
<TrashIcon className="mr-1 h-4 w-4" /> |
||||
Delete Request |
||||
</button> |
||||
</span> |
||||
</> |
||||
)} |
||||
|
||||
{friend.status === 'sent' && ( |
||||
<> |
||||
<div |
||||
className={'flex w-full flex-grow items-center justify-center'} |
||||
> |
||||
<span class=" flex flex-col items-center text-green-500"> |
||||
<AddedUserIcon additionalClasses="mr-2 h-8 w-8 mb-1" /> |
||||
Request Sent |
||||
</span> |
||||
</div> |
||||
<span class="flex cursor-default items-center justify-center border-t py-2 text-center text-sm"> |
||||
<button |
||||
className="ml-2 flex items-center font-medium text-red-700 underline underline-offset-2 hover:text-red-500" |
||||
onClick={() => { |
||||
deleteFriend( |
||||
friend.userId, |
||||
'Friend request withdrawn' |
||||
).finally(() => { |
||||
pageProgressMessage.set(''); |
||||
}); |
||||
}} |
||||
> |
||||
<TrashIcon className="mr-1 h-4 w-4" /> |
||||
Withdraw Request |
||||
</button> |
||||
</span> |
||||
</> |
||||
)} |
||||
|
||||
{friend.status === 'received' && ( |
||||
<> |
||||
<div |
||||
className={ |
||||
'flex w-full flex-grow flex-col items-center justify-center px-4' |
||||
} |
||||
> |
||||
<AddUserIcon additionalClasses="mr-2 h-10 w-10 mb-1 text-green-500" /> |
||||
<span className="mb-3 text-green-600">Request Received</span> |
||||
|
||||
<button |
||||
onClick={() => { |
||||
addFriend(friend.userId, 'Friend request accepted').finally( |
||||
() => { |
||||
pageProgressMessage.set(''); |
||||
} |
||||
); |
||||
}} |
||||
className="mb-1 block w-full max-w-[150px] rounded-md bg-black py-1.5 text-sm text-white" |
||||
> |
||||
Accept |
||||
</button> |
||||
|
||||
<button |
||||
onClick={() => { |
||||
deleteFriend( |
||||
friend.userId, |
||||
'Friend request rejected' |
||||
).finally(() => { |
||||
pageProgressMessage.set(''); |
||||
}); |
||||
}} |
||||
className="block w-full max-w-[150px] rounded-md border border-red-500 py-1 text-sm text-red-500" |
||||
> |
||||
Reject |
||||
</button> |
||||
</div> |
||||
</> |
||||
)} |
||||
</div> |
||||
</> |
||||
); |
||||
} |
Loading…
Reference in new issue