Skip to content

Commit

Permalink
Merge branch '131-user-should-be-able-to-set-their-availability-statu…
Browse files Browse the repository at this point in the history
…s' into make-webapp-responsive
  • Loading branch information
janhvipatil authored May 31, 2024
2 parents 02563eb + 6e06840 commit e87f858
Show file tree
Hide file tree
Showing 25 changed files with 752 additions and 34 deletions.
21 changes: 19 additions & 2 deletions raven-app/src/components/common/UserAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import { clsx } from 'clsx'
import { generateAvatarColor } from '../feature/select-member/GenerateAvatarColor'
import { RiRobot2Fill } from 'react-icons/ri'
import { useMemo } from 'react'
import { AvailabilityStatus } from '../feature/userSettings/SetUserAvailabilityMenu'

interface UserAvatarProps extends Partial<AvatarProps> {
alt?: string,
isActive?: boolean,
availabilityStatus?: AvailabilityStatus,
isBot?: boolean,
skeletonSize?: BoxProps['width'] | BoxProps['height'],
}
Expand Down Expand Up @@ -45,17 +47,32 @@ const getIconSize = (size: AvatarProps['size']) => {
}
}

export const UserAvatar = ({ src, alt, size = '1', radius = 'medium', isActive, skeletonSize = '5', fallback, isBot, className, ...props }: UserAvatarProps) => {
export const UserAvatar = ({ src, alt, size = '1', radius = 'medium', isActive, availabilityStatus, skeletonSize = '5', fallback, isBot, className, ...props }: UserAvatarProps) => {
const color = useMemo(() => generateAvatarColor(alt), [alt])

return <Theme accentColor={color}><span className="relative inline-block">
<Avatar src={src} alt={alt}
loading='lazy'
fallback={fallback ?? getInitials(alt)} size={size} radius={radius} className={className} {...props} />
{isActive &&

{availabilityStatus && availabilityStatus === 'Away' &&
<span className={clsx("absolute block translate-x-1/2 translate-y-1/2 transform rounded-full", radius === 'full' ? 'bottom-1 right-1' : 'bottom-0.5 right-0.5')}>
<span className="block h-2 w-2 rounded-full border border-slate-2 bg-[#FFAA33] shadow-md" />
</span>
}

{availabilityStatus && availabilityStatus === 'Do not disturb' &&
<span className={clsx("absolute block translate-x-1/2 translate-y-1/2 transform rounded-full", radius === 'full' ? 'bottom-1 right-1' : 'bottom-0.5 right-0.5')}>
<span className="block h-2 w-2 rounded-full border border-slate-2 bg-[#D22B2B] shadow-md" />
</span>
}

{availabilityStatus !== 'Away' && availabilityStatus !== 'Do not disturb' && isActive &&
<span className={clsx("absolute block translate-x-1/2 translate-y-1/2 transform rounded-full", radius === 'full' ? 'bottom-1 right-1' : 'bottom-0.5 right-0.5')}>
<span className="block h-2 w-2 rounded-full border border-slate-2 bg-green-600 shadow-md" />
</span>
}

{isBot && <span className={clsx("absolute block translate-x-1/2 translate-y-1/2 transform rounded-full", radius === 'full' ? 'bottom-1 right-1' : 'bottom-0.5 right-0.5')}>
<RiRobot2Fill className="text-accent-11 dark:text-accent-11" size={getIconSize(size)} />
</span>}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useContext, useDeferredValue, useMemo, useState } from "react"
import { useContext, useMemo, useState } from "react"
import { useDebounce } from "../../../hooks/useDebounce"
import { UserContext } from "../../../utils/auth/UserProvider"
import { ChannelListItem } from "@/utils/channel/ChannelListProvider"
Expand Down Expand Up @@ -96,7 +96,7 @@ const MemberList = ({ channelData, channelMembers, activeUsers, updateMembers, i
<Box key={member.name} className={'hover:bg-slate-3 rounded-md'}>
<Flex justify='between' className={'pr-3'}>
<Flex className={'p-2'} gap='3'>
<UserAvatar src={member.user_image ?? ''} alt={member.full_name} size='2' isActive={activeUsers.includes(member.name)} />
<UserAvatar src={member.user_image ?? ''} alt={member.full_name} size='2' isActive={activeUsers.includes(member.name)} availabilityStatus={member.availability_status} />
<Flex gap='2' align={'center'}>
<Text weight='medium'>{member.first_name}</Text>
{activeUsers.includes(member.name) ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import useFetchChannelMembers from "@/hooks/fetchers/useFetchChannelMembers"
import ChannelHeaderMenu from "./ChannelHeaderMenu"
import { BiChevronLeft } from "react-icons/bi"
import { Link } from "react-router-dom"
import { useGetUser } from "@/hooks/useGetUser"

interface DMChannelHeaderProps {
channelData: DMChannelListItem,
Expand Down Expand Up @@ -37,6 +38,8 @@ export const DMChannelHeader = ({ channelData }: DMChannelHeaderProps) => {

}, [channelMembers, peer])

const user = useGetUser(peer)

return (
<PageHeader>
<Flex gap='3' align='center'>
Expand All @@ -48,12 +51,15 @@ export const DMChannelHeader = ({ channelData }: DMChannelHeaderProps) => {
alt={fullName}
src={userImage}
isActive={isActive}
availabilityStatus={user?.availability_status}
skeletonSize='6'
isBot={isBot}
size='2' />
<Heading size='5'>
<div className="flex items-center gap-2">
{fullName} {isBot && <Badge color='gray' className='font-semibold px-1.5 py-0.5'>Bot</Badge>}
{fullName}
{user?.custom_status && <Badge color='gray' className='font-semibold px-1.5 py-0.5'>{user.custom_status}</Badge>}
{isBot && <Badge color='gray' className='font-semibold px-1.5 py-0.5'>Bot</Badge>}
</div>
</Heading>
</Flex>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ const MentionItem = ({ item, index, selectItem, selectedIndex, itemsLength }: {
variant='solid'
radius='full'
isActive={isActive}
availabilityStatus={item.availability_status}
/>
<Text as='span' weight='medium' size='2'> {item.full_name}</Text>
</Flex >
</Flex>
}
35 changes: 29 additions & 6 deletions raven-app/src/components/feature/chat/ChatMessage/MessageItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { RiRobot2Fill } from 'react-icons/ri'
import { useIsDesktop } from '@/hooks/useMediaQuery'
import { useDoubleTap } from 'use-double-tap'
import useOutsideClick from '@/hooks/useOutsideClick'
import { getStatusText } from '../../userSettings/SetUserAvailabilityMenu'

interface MessageBlockProps {
message: Message,
Expand Down Expand Up @@ -211,16 +212,31 @@ interface UserProps {
export const MessageSenderAvatar = memo(({ user, userID, isActive = false }: UserProps) => {

const alt = user?.full_name ?? userID

const isBot = user?.type === 'Bot'
const color = useMemo(() => generateAvatarColor(user?.full_name ?? userID), [user?.full_name, userID])
const availabilityStatus = user?.availability_status

return <Theme accentColor={color}><span className="relative inline-block">
<Avatar src={user?.user_image} alt={user?.full_name ?? userID} loading='lazy' fallback={getInitials(alt)} size={'2'} radius={'medium'} />
{isActive &&

{availabilityStatus && availabilityStatus === 'Away' &&
<span className={clsx("absolute block translate-x-1/2 translate-y-1/2 transform rounded-full", 'bottom-0.5 right-0.5')}>
<span className="block h-2 w-2 rounded-full border border-slate-2 bg-[#FFAA33] shadow-md" />
</span>
}

{availabilityStatus && availabilityStatus === 'Do not disturb' &&
<span className={clsx("absolute block translate-x-1/2 translate-y-1/2 transform rounded-full", 'bottom-0.5 right-0.5')}>
<span className="block h-2 w-2 rounded-full border border-slate-2 bg-[#D22B2B] shadow-md" />
</span>
}

{availabilityStatus !== 'Away' && availabilityStatus !== 'Do not disturb' && isActive &&
<span className={clsx("absolute block translate-x-1/2 translate-y-1/2 transform rounded-full", 'bottom-0.5 right-0.5')}>
<span className="block h-2 w-2 rounded-full border border-slate-2 bg-green-600 shadow-md" />
</span>
}

{isBot && <span className="absolute block translate-x-1/2 translate-y-1/2 transform rounded-full bottom-0.5 right-0.5">
<RiRobot2Fill className="text-accent-11 dark:text-accent-11" size="1rem" />
</span>}
Expand All @@ -230,9 +246,11 @@ export const MessageSenderAvatar = memo(({ user, userID, isActive = false }: Use

export const UserHoverCard = memo(({ user, userID, isActive }: UserProps) => {

const { isBot, fullName, userImage } = useMemo(() => {
const { isBot, fullName, userImage, availabilityStatus, customStatus } = useMemo(() => {
return {
fullName: user?.full_name ?? userID,
availabilityStatus: user?.availability_status,
customStatus: user?.custom_status,
userImage: user?.user_image ?? '',
isBot: user?.type === 'Bot'
}
Expand All @@ -249,12 +267,17 @@ export const UserHoverCard = memo(({ user, userID, isActive }: UserProps) => {
<Flex direction='column'>
<Flex gap='3' align='center'>
<Text className='text-gray-12' weight='bold' size='3'>{fullName}</Text>
{isActive && <Flex gap='1' align='center'>
<BsFillCircleFill className='text-green-500' size='8' />
{/* if availabilityStatus is set to 'Invisible', don't show the status */}
{availabilityStatus && availabilityStatus !== 'Invisible' && <Flex className='text-gray-10 text-xs flex gap-1 items-center'>
{getStatusText(availabilityStatus)}
</Flex>}
{/* only show user active status if the user has not explicitly set their availability status */}
{!availabilityStatus && isActive && <Flex gap='1' align='center'>
<BsFillCircleFill color={'green'} size='8' />
<Text className='text-gray-10' size='1'>Online</Text>
</Flex>}
</Flex>
{user && !isBot && <Text className='text-gray-11' size='1'>{user?.name}</Text>}
{customStatus ? <Text className='text-gray-11' size='1'>{customStatus}</Text> : user && !isBot && <Text className='text-gray-11' size='1'>{user?.name}</Text>}
</Flex>
</Flex>
</HoverCard.Content>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { UserAvatar } from '@/components/common/UserAvatar';
import { getStatusText } from '@/components/feature/userSettings/SetUserAvailabilityMenu';
import { useGetUser } from '@/hooks/useGetUser';
import { useIsUserActive } from '@/hooks/useIsUserActive';
import { Flex, HoverCard, Link, Text } from '@radix-ui/themes';
Expand Down Expand Up @@ -26,6 +27,9 @@ const UserMentionRenderer = ({ node }: NodeViewRendererProps) => {
const user = useGetUser(node.attrs.id)
const isActive = useIsUserActive(node.attrs.id)

const availabilityStatus = user?.availability_status
const customStatus = user?.custom_status

return (
<NodeViewWrapper as={'span'}>
<HoverCard.Root>
Expand All @@ -34,21 +38,25 @@ const UserMentionRenderer = ({ node }: NodeViewRendererProps) => {
@{user?.full_name ?? node.attrs.label}
</Link>
</HoverCard.Trigger>
<HoverCard.Content>
<HoverCard.Content size='1'>
<Flex gap='2' align='center'>
<UserAvatar src={user?.user_image} alt={user?.full_name ?? node.attrs.label} size='4' />
<Flex direction='column'>
<Flex gap='3' align='center'>
<Text className='text-gray-12' weight='bold' size='3'>{user?.full_name ?? node.attrs.label}</Text>
{isActive && <Flex gap='1' align='center'>
<BsFillCircleFill className='text-green-400' size='8' />
{/* if availabilityStatus is set to 'Invisible', don't show the status */}
{availabilityStatus && availabilityStatus !== 'Invisible' && <Flex className='text-gray-10 text-xs flex gap-1 items-center'>
{getStatusText(availabilityStatus)}
</Flex>}
{/* only show user active status if the user has not explicitly set their availability status */}
{!availabilityStatus && isActive && <Flex gap='1' align='center'>
<BsFillCircleFill color={'green'} size='8' />
<Text className='text-gray-10' size='1'>Online</Text>
</Flex>}
</Flex>
{user && <Text className='text-gray-11' size='1'>{user?.name}</Text>}
{customStatus ? <Text className='text-gray-11' size='1'>{customStatus}</Text> : user && <Text className='text-gray-11' size='1'>{user?.name}</Text>}
</Flex>
</Flex>

</HoverCard.Content>
</HoverCard.Root>
{/* <Link>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useFrappePostCall, useSWRConfig } from "frappe-react-sdk"
import { useFrappePostCall } from "frappe-react-sdk"
import { useContext, useLayoutEffect, useMemo, useRef, useState } from "react"
import { useNavigate, useParams } from "react-router-dom"
import { SidebarGroup, SidebarGroupItem, SidebarGroupLabel, SidebarGroupList, SidebarIcon, SidebarButtonItem } from "../../layout/Sidebar"
Expand Down Expand Up @@ -99,7 +99,9 @@ export const DirectMessageItemElement = ({ channel, unreadCount }: { channel: DM
size={{
initial: '2',
md: '1'
}} />
}}
availabilityStatus={userData?.availability_status}
/>
</SidebarIcon>
<Flex justify='between' width='100%'>
<Text size={{
Expand Down Expand Up @@ -167,7 +169,8 @@ const ExtraUsersItem = ({ user, createDMChannel }: { user: UserFields, createDMC
size={{
initial: '2',
md: '1'
}} />
}}
availabilityStatus={user.availability_status}/>
</SidebarIcon>
<Flex justify='between' width='100%'>
<Text size={{
Expand Down
52 changes: 52 additions & 0 deletions raven-app/src/components/feature/userSettings/DeleteImageModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useFrappeUpdateDoc } from 'frappe-react-sdk'
import { AlertDialog, Button, Flex, Text } from '@radix-ui/themes'
import { Loader } from '@/components/common/Loader'
import { toast } from 'sonner'
import { ErrorBanner } from '@/components/layout/AlertBanner'
import { useUserData } from '@/hooks/useUserData'

interface DeleteImageModalProps {
onClose: () => void
}

export const DeleteImageModal = ({ onClose }: DeleteImageModalProps) => {

const { updateDoc, error, loading } = useFrappeUpdateDoc()
const userData = useUserData()

const deleteImage = () => {
updateDoc('Raven User', userData.name, {
'user_image': null
}).then(() => {
onClose()
toast.success("Image deleted successfully")
}).catch(() => {
toast("Could not delete image")
})
}

return (
<>
<AlertDialog.Title>Delete Image</AlertDialog.Title>

<Flex direction={'column'} gap='2'>
<ErrorBanner error={error} />
<Text>Are you sure you want to delete this image?</Text>
</Flex>

<Flex gap="3" mt="4" justify="end">
<AlertDialog.Cancel>
<Button variant="soft" color="gray">
Cancel
</Button>
</AlertDialog.Cancel>
<AlertDialog.Action>
<Button variant="solid" color="red" onClick={deleteImage} disabled={loading}>
{loading && <Loader />}
{loading ? "Deleting" : "Delete"}
</Button>
</AlertDialog.Action>
</Flex>
</>
)
}
Loading

0 comments on commit e87f858

Please sign in to comment.