Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
},
"dependencies": {
"@emoji-mart/react": "^1.1.1",
"@livekit/components-react": "^2.8.1",
"@livekit/components-styles": "^1.1.4",
"@radix-ui/themes": "^3.2.0",
"@tiptap/extension-code-block-lowlight": "2.5.9",
"@tiptap/extension-highlight": "2.5.9",
Expand Down Expand Up @@ -45,6 +47,7 @@
"html-react-parser": "^5.1.8",
"jotai": "^2.10.3",
"js-cookie": "^3.0.5",
"livekit-client": "^2.9.6",
"lowlight": "^3.1.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down Expand Up @@ -78,4 +81,4 @@
"resolutions": {
"@radix-ui/react-dialog": "1.1.5"
}
}
}
3 changes: 3 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ const router = createBrowserRouter(
<Route path="thread/:threadID" lazy={() => import('./components/feature/threads/ThreadDrawer/ThreadDrawer')} />
</Route>
</Route>
<Route path="meeting-room" lazy={() => import('./pages/meeting/MeetingPage')}>
<Route path=":roomID" lazy={() => import('./pages/meeting/MeetingRoom')} />
</Route>
</Route>
</Route>
<Route path='*' lazy={() => import('./pages/NotFound')} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { ViewChannelMemberAvatars } from "./ViewChannelMemberAvatars"
import { BiChevronLeft } from "react-icons/bi"
import { Link } from "react-router-dom"
import { ViewPinnedMessagesButton } from "../pinned-messages/ViewPinnedMessagesButton"
import CreateVideoCallButton from "../video-calling/CreateVideoCallButton"
import { HStack } from "@/components/layout/Stack"

interface ChannelHeaderProps {
channelData: ChannelListItem
Expand Down Expand Up @@ -42,7 +44,10 @@ export const ChannelHeader = ({ channelData }: ChannelHeaderProps) => {

<Flex gap='2' align='center' className="animate-fadein">
<ViewChannelMemberAvatars channelData={channelData} />
<ChannelHeaderMenu channelData={channelData} />
<HStack gap='0'>
{channelData.member_id && <CreateVideoCallButton channelData={channelData} />}
<ChannelHeaderMenu channelData={channelData} />
</HStack>
</Flex>
</PageHeader>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const ChannelHeaderMenu = ({ channelData }: Props) => {
<><DropdownMenu.Root>
<DropdownMenu.Trigger>
<IconButton color='gray' className='bg-transparent text-gray-12 hover:bg-gray-3'>
<BiDotsVerticalRounded />
<BiDotsVerticalRounded size='18' />
</IconButton>
</DropdownMenu.Trigger>
<DropdownMenu.Content className='min-w-48'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import useIsUserOnLeave from "@/hooks/fetchers/useIsUserOnLeave"
import { UserContext } from "@/utils/auth/UserProvider"
import { replaceCurrentUserFromDMChannelName } from "@/utils/operations"
import { useIsDesktop } from "@/hooks/useMediaQuery"
import CreateVideoCallButton from "../video-calling/CreateVideoCallButton"

interface DMChannelHeaderProps {
channelData: DMChannelListItem,
Expand Down Expand Up @@ -83,7 +84,8 @@ export const DMChannelHeader = ({ channelData }: DMChannelHeaderProps) => {
</div>
</Heading>
</Flex>
<Flex gap='4' align='center'>
<Flex gap='1' align='center'>
{channelData.is_self_message === 0 && <CreateVideoCallButton channelData={channelData} />}
<ChannelHeaderMenu channelData={channelData} />
</Flex>
</PageHeader>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { Message, MessageBlock } from "../../../../../../../types/Messaging/Mess
import { MessageContent, MessageSenderAvatar, UserHoverCard } from "../MessageItem"
import { Box, BoxProps, ContextMenu, Flex, Text } from "@radix-ui/themes"
import { MessageReactions } from "../MessageReactions"
import { DateTooltip, DateTooltipShort } from "../Renderers/DateTooltip"
import { DateTooltip } from "../Renderers/DateTooltip"
import { RiPushpinFill, RiShareForwardFill } from "react-icons/ri"
import { ReplyMessageBox } from "../ReplyMessageBox/ReplyMessageBox"
import { useContext, useMemo, useState } from "react"
import clsx from "clsx"
import { DoctypeLinkRenderer } from "../Renderers/DoctypeLinkRenderer"
import { DoctypeLinkMessageRenderer } from "../Renderers/DoctypeLinkRenderer"
import { ThreadMessage } from "../Renderers/ThreadMessage"
import { UserContext } from "@/utils/auth/UserProvider"
import { Stack } from "@/components/layout/Stack"
Expand Down Expand Up @@ -120,7 +120,7 @@ export const LeftRightLayout = ({ message, user, isActive, isHighlighted, onRepl
/>

{message.link_doctype && message.link_document && <Box className={clsx(message.is_continuation ? 'ml-0.5' : '-ml-0.5')}>
<DoctypeLinkRenderer doctype={message.link_doctype} docname={message.link_document} />
<DoctypeLinkMessageRenderer doctype={message.link_doctype} docname={message.link_document} />
</Box>}

{message.is_edited === 1 && <Text size='1' className='text-gray-10'>(edited)</Text>}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { QuickActions } from './MessageActions/QuickActions/QuickActions'
import { memo, useContext, useMemo, useState } from 'react'
import { ReplyMessageBox } from './ReplyMessageBox/ReplyMessageBox'
import { generateAvatarColor } from '../../selectDropdowns/GenerateAvatarColor'
import { DoctypeLinkRenderer } from './Renderers/DoctypeLinkRenderer'
import { DoctypeLinkMessageRenderer } from './Renderers/DoctypeLinkRenderer'
import { useDebounce } from '@/hooks/useDebounce'
import { RiPushpinFill, RiRobot2Fill, RiShareForwardFill } from 'react-icons/ri'
import { useIsDesktop } from '@/hooks/useMediaQuery'
Expand Down Expand Up @@ -204,7 +204,7 @@ export const MessageItem = ({ message, setDeleteMessage, isHighlighted, onReplyM
/>

{message.link_doctype && message.link_document && <Box className={clsx(message.is_continuation ? 'ml-0.5' : '-ml-0.5')}>
<DoctypeLinkRenderer doctype={message.link_doctype} docname={message.link_document} />
<DoctypeLinkMessageRenderer doctype={message.link_doctype} docname={message.link_document} />
</Box>}
{message.is_edited === 1 && <Text size='1' className='text-gray-10'>(edited)</Text>}
{message_reactions?.length &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ import useDoctypeMeta from "@/hooks/useDoctypeMeta"
import { HStack } from "@/components/layout/Stack"
import { ErrorBanner, getErrorMessage } from "@/components/layout/AlertBanner/ErrorBanner"
import parse from 'html-react-parser';
import VideoCallMessageRenderer from "@/components/feature/video-calling/VideoCallMessageRenderer"

export const DoctypeLinkMessageRenderer = ({ doctype, docname }: { doctype: string, docname: string }) => {

if (doctype !== 'Raven LiveKit Room') {
return <DoctypeLinkRenderer doctype={doctype} docname={docname} />
}

return <VideoCallMessageRenderer roomID={docname} />
}

export const DoctypeLinkRenderer = ({ doctype, docname }: { doctype: string, docname: string }) => {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { ErrorText, Label } from '@/components/common/Form'
import { Loader } from '@/components/common/Loader'
import { ErrorBanner } from '@/components/layout/AlertBanner/ErrorBanner'
import { HStack, Stack } from '@/components/layout/Stack'
import { useGetUser } from '@/hooks/useGetUser'
import { useUserData } from '@/hooks/useUserData'
import { RavenLiveKitRoom } from '@/types/RavenIntegrations/RavenLiveKitRoom'
import { UserContext } from '@/utils/auth/UserProvider'
import { ChannelListItem, DMChannelListItem } from '@/utils/channel/ChannelListProvider'
import { Box, Button, Checkbox, Dialog, Flex, IconButton, Text, TextArea, TextField, VisuallyHidden } from '@radix-ui/themes'
import { useFrappeCreateDoc } from 'frappe-react-sdk'
import { useContext, useState } from 'react'
import { Controller, FormProvider, useForm } from 'react-hook-form'
import { BiPhone } from 'react-icons/bi'
import { toast } from 'sonner'

type Props = {
channelData: ChannelListItem
}

const CreateVideoCallButton = ({ channelData }: Props) => {

const [open, setOpen] = useState(false)

return (
<Dialog.Root open={open} onOpenChange={setOpen}>
<Dialog.Trigger>
<IconButton color='gray' className='bg-transparent text-gray-12 hover:bg-gray-3'>
<BiPhone size={16} />
</IconButton>
</Dialog.Trigger>
<Dialog.Content>
<Dialog.Title>Start a Call</Dialog.Title>
<VisuallyHidden>
<Dialog.Description size='2'>
Start a call
</Dialog.Description>
</VisuallyHidden>
<CreateVideoCallDialog channelData={channelData} onClose={() => setOpen(false)} />

</Dialog.Content>
</Dialog.Root>
)
}

const CreateVideoCallDialog = ({ channelData, onClose }: { channelData: ChannelListItem, onClose: () => void }) => {

let defaultSubject = `Call with #${channelData.channel_name}`

const { currentUser } = useContext(UserContext)

const currentUserData = useUserData()

const peerUser = useGetUser((channelData as DMChannelListItem).peer_user_id ?? undefined)

if (channelData.is_direct_message) {
defaultSubject = `Call between ${peerUser?.full_name ?? (channelData as DMChannelListItem).peer_user_id} and ${currentUserData?.full_name ?? currentUser}`
}


const methods = useForm<RavenLiveKitRoom>({
defaultValues: {
channel_id: channelData.name,
workspace: channelData.workspace,
invite_entire_channel: 1,
room_name: defaultSubject,
}
})

const { register, control, formState: { errors } } = methods

const { createDoc, loading, error } = useFrappeCreateDoc<RavenLiveKitRoom>()

const onSubmit = async (data: RavenLiveKitRoom) => {

return createDoc("Raven LiveKit Room", data).then((res) => {
toast.success("Call created. You can join it now.")
onClose()
})
}

return <FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)}>
<Stack>
<ErrorBanner error={error} />

<Stack>
<Box>
<Label htmlFor='room_name' isRequired>Name</Label>
<TextField.Root
{...register('room_name', {
required: "Name is required"
})}
/>
</Box>
{errors?.room_name && <ErrorText>{errors.room_name?.message}</ErrorText>}
</Stack>
<Stack>
<Box>
<Label htmlFor='description'>Description</Label>
<TextArea
{...register('description')}
/>
</Box>

{errors?.description && <ErrorText>{errors.description?.message}</ErrorText>}
</Stack>

<Stack>
<Text as="label" size="2">
<HStack>
<Controller
control={control}
name='invite_entire_channel'
render={({ field }) => (
<Checkbox
checked={field.value ? true : false}
onCheckedChange={(v) => field.onChange(v ? 1 : 0)}
/>
)} />

Invite entire channel
</HStack>
</Text>
</Stack>
<Flex gap="3" mt="6" justify="end" align='center'>
<Dialog.Close disabled={loading}>
<Button variant="soft" color="gray">Cancel</Button>
</Dialog.Close>
<Button type='submit' disabled={loading}>
{loading && <Loader className="text-white" />}
{loading ? "Setting up..." : "Start Call"}
</Button>
</Flex>
</Stack>
</form>
</FormProvider>
}

export default CreateVideoCallButton
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { ErrorBanner } from "@/components/layout/AlertBanner/ErrorBanner"
import { HStack, Stack } from "@/components/layout/Stack"
import { RavenLiveKitRoom } from "@/types/RavenIntegrations/RavenLiveKitRoom"
import { Box, Button, Card, Link, Skeleton, Text } from "@radix-ui/themes"
import { useFrappeGetCall } from "frappe-react-sdk"
import { BiPhone } from "react-icons/bi"

const VideoCallMessageRenderer = ({ roomID }: { roomID: string }) => {
const { data, error, isLoading } = useFrappeGetCall<{ message: RavenLiveKitRoom }>('raven.api.livekit.get_room_details', {
room_id: roomID
})

return (
<Box className='max-w-[550px] min-w-[75px] py-2'>
{
isLoading ?
<Skeleton className='w-96 h-32 rounded-md' /> :
error ?
<Card>
<ErrorBanner error={error} />
</Card> :
data && <VideoCallCard data={data.message} />
}
</Box>
)
}

const VideoCallCard = ({ data }: { data: RavenLiveKitRoom }) => {
return <Card>
<HStack justify='between' align='center'>
<Stack gap='1'>
<Text weight='bold' size='3'>{data.room_name}</Text>
{data.description && <Text size='2' className="text-gray-11">{data.description}</Text>}

</Stack>
<HStack>
<Button className="not-cal" asChild>
<Link href={`/meeting-room/${data.name}`} target="_blank" weight='medium'>
<BiPhone size={16} />
Join Call
</Link>
</Button>
</HStack>
</HStack>

</Card>
}

export default VideoCallMessageRenderer
Loading