|
|
|
@ -1,8 +1,11 @@ |
|
|
|
|
import { useEffect, useState } from 'react'; |
|
|
|
|
import { useToast } from '../../hooks/use-toast'; |
|
|
|
|
import { UserItem } from './UserItem'; |
|
|
|
|
import { Users2 } from 'lucide-react'; |
|
|
|
|
import {httpGet} from "../../lib/http"; |
|
|
|
|
import { Check, Copy, Group, UserPlus2, Users2 } from 'lucide-react'; |
|
|
|
|
import { httpGet } from '../../lib/http'; |
|
|
|
|
import { getUser } from '../../lib/jwt.ts'; |
|
|
|
|
import { useCopyText } from '../../hooks/use-copy-text.ts'; |
|
|
|
|
import { cn } from '../../lib/classname.ts'; |
|
|
|
|
|
|
|
|
|
export type FriendshipStatus = |
|
|
|
|
| 'none' |
|
|
|
@ -41,10 +44,13 @@ type ShareFriendListProps = { |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
export function ShareFriendList(props: ShareFriendListProps) { |
|
|
|
|
const userId = getUser()?.id!; |
|
|
|
|
const { setFriends, friends, sharedFriendIds, setSharedFriendIds } = props; |
|
|
|
|
const toast = useToast(); |
|
|
|
|
|
|
|
|
|
const { isCopied, copyText } = useCopyText(); |
|
|
|
|
const [isLoading, setIsLoading] = useState(true); |
|
|
|
|
const [isAddingFriend, setIsAddingFriend] = useState(false); |
|
|
|
|
|
|
|
|
|
async function loadFriends() { |
|
|
|
|
if (friends.length > 0) { |
|
|
|
@ -53,7 +59,7 @@ export function ShareFriendList(props: ShareFriendListProps) { |
|
|
|
|
|
|
|
|
|
setIsLoading(true); |
|
|
|
|
const { response, error } = await httpGet<ListFriendsResponse>( |
|
|
|
|
`${import.meta.env.PUBLIC_API_URL}/v1-list-friends` |
|
|
|
|
`${import.meta.env.PUBLIC_API_URL}/v1-list-friends`, |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if (error || !response) { |
|
|
|
@ -87,6 +93,10 @@ export function ShareFriendList(props: ShareFriendListProps) { |
|
|
|
|
</ul> |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
const isDev = import.meta.env.DEV; |
|
|
|
|
const baseWebUrl = isDev ? 'http://localhost:3000' : 'https://roadmap.sh'; |
|
|
|
|
const befriendUrl = `${baseWebUrl}/befriend?u=${userId}`; |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<> |
|
|
|
|
{(friends.length > 0 || isLoading) && ( |
|
|
|
@ -112,32 +122,85 @@ export function ShareFriendList(props: ShareFriendListProps) { |
|
|
|
|
|
|
|
|
|
{loadingFriends} |
|
|
|
|
{friends.length > 0 && !isLoading && ( |
|
|
|
|
<ul className="mt-2 grid grid-cols-3 gap-1.5"> |
|
|
|
|
{friends.map((friend) => { |
|
|
|
|
const isSelected = sharedFriendIds?.includes(friend.userId); |
|
|
|
|
return ( |
|
|
|
|
<li key={friend.userId}> |
|
|
|
|
<UserItem |
|
|
|
|
user={{ |
|
|
|
|
name: friend.name, |
|
|
|
|
avatar: friend.avatar, |
|
|
|
|
email: friend.email, |
|
|
|
|
}} |
|
|
|
|
isSelected={isSelected} |
|
|
|
|
onClick={() => { |
|
|
|
|
if (isSelected) { |
|
|
|
|
setSharedFriendIds( |
|
|
|
|
sharedFriendIds.filter((id) => id !== friend.userId) |
|
|
|
|
); |
|
|
|
|
} else { |
|
|
|
|
setSharedFriendIds([...sharedFriendIds, friend.userId]); |
|
|
|
|
} |
|
|
|
|
<> |
|
|
|
|
<ul className="mt-2 grid grid-cols-3 gap-1.5"> |
|
|
|
|
{friends.map((friend) => { |
|
|
|
|
const isSelected = sharedFriendIds?.includes(friend.userId); |
|
|
|
|
return ( |
|
|
|
|
<li key={friend.userId}> |
|
|
|
|
<UserItem |
|
|
|
|
user={{ |
|
|
|
|
name: friend.name, |
|
|
|
|
avatar: friend.avatar, |
|
|
|
|
email: friend.email, |
|
|
|
|
}} |
|
|
|
|
isSelected={isSelected} |
|
|
|
|
onClick={() => { |
|
|
|
|
if (isSelected) { |
|
|
|
|
setSharedFriendIds( |
|
|
|
|
sharedFriendIds.filter((id) => id !== friend.userId), |
|
|
|
|
); |
|
|
|
|
} else { |
|
|
|
|
setSharedFriendIds([...sharedFriendIds, friend.userId]); |
|
|
|
|
} |
|
|
|
|
}} |
|
|
|
|
/> |
|
|
|
|
</li> |
|
|
|
|
); |
|
|
|
|
})} |
|
|
|
|
</ul> |
|
|
|
|
{!isAddingFriend && ( |
|
|
|
|
<p className="mt-6 text-sm text-gray-600"> |
|
|
|
|
Don't see a Friend?{' '} |
|
|
|
|
<button |
|
|
|
|
onClick={() => { |
|
|
|
|
setIsAddingFriend(true); |
|
|
|
|
}} |
|
|
|
|
className="font-semibold text-gray-900 underline" |
|
|
|
|
> |
|
|
|
|
Add them |
|
|
|
|
</button> |
|
|
|
|
</p> |
|
|
|
|
)} |
|
|
|
|
{isAddingFriend && ( |
|
|
|
|
<div className="-mx-4 -mb-4 mt-6 border-t bg-gray-50 px-4 py-4"> |
|
|
|
|
<p className="mb-1.5 flex items-center gap-1 text-sm text-gray-800"> |
|
|
|
|
<UserPlus2 className="text-gray-500" size="20px" /> |
|
|
|
|
Share the link below with your friends to invite them |
|
|
|
|
</p> |
|
|
|
|
<div className="relative"> |
|
|
|
|
<input |
|
|
|
|
readOnly |
|
|
|
|
type="text" |
|
|
|
|
value={befriendUrl} |
|
|
|
|
onClick={(e) => { |
|
|
|
|
e.preventDefault(); |
|
|
|
|
(e.target as HTMLInputElement).select(); |
|
|
|
|
copyText(befriendUrl); |
|
|
|
|
}} |
|
|
|
|
className={cn( |
|
|
|
|
'w-full rounded-md border px-2 py-2 text-sm focus:shadow-none focus:outline-0', |
|
|
|
|
{ |
|
|
|
|
'border-green-400 bg-green-50': isCopied, |
|
|
|
|
}, |
|
|
|
|
)} |
|
|
|
|
/> |
|
|
|
|
</li> |
|
|
|
|
); |
|
|
|
|
})} |
|
|
|
|
</ul> |
|
|
|
|
<button |
|
|
|
|
onClick={() => copyText(befriendUrl)} |
|
|
|
|
className="absolute bottom-0 right-0 top-0 flex items-center px-2.5" |
|
|
|
|
> |
|
|
|
|
{isCopied ? ( |
|
|
|
|
<span className="flex items-center gap-1 text-sm font-medium text-green-600"> |
|
|
|
|
<Check className="text-green-600" size="18px" /> Copied |
|
|
|
|
</span> |
|
|
|
|
) : ( |
|
|
|
|
<Copy className="text-gray-400" size="18px" /> |
|
|
|
|
)} |
|
|
|
|
</button> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
)} |
|
|
|
|
</> |
|
|
|
|
)} |
|
|
|
|
|
|
|
|
|
{friends.length === 0 && !isLoading && ( |
|
|
|
|