diff --git a/mobile/src/components/features/chat-space/chat-view/MessageBlock.tsx b/mobile/src/components/features/chat-space/chat-view/MessageBlock.tsx index b4a57d9f5..4d580e4e8 100644 --- a/mobile/src/components/features/chat-space/chat-view/MessageBlock.tsx +++ b/mobile/src/components/features/chat-space/chat-view/MessageBlock.tsx @@ -6,7 +6,7 @@ import { SquareAvatar, UserAvatar } from '@/components/common/UserAvatar' import { MarkdownRenderer } from '@/components/common/MarkdownRenderer' import { UserFields } from '@/utils/users/UserListProvider' import { DateObjectToFormattedDateString, DateObjectToFormattedDateStringWithoutYear, DateObjectToTimeString } from '@/utils/operations/operations' -import { useFrappeGetDoc } from 'frappe-react-sdk' +import { useFrappeGetCall, useFrappeGetDoc } from 'frappe-react-sdk' import { ChannelMembersContext } from './ChatView' import { openOutline } from 'ionicons/icons' import { Haptics, ImpactStyle } from '@capacitor/haptics' @@ -160,12 +160,18 @@ const FileMessageBlock = ({ message }: { message: FileMessage }) => { const ReplyBlock = ({ linked_message }: { linked_message: string }) => { const members = useContext(ChannelMembersContext) - //TODO: Cache the messages to prevent API calls - const { data } = useFrappeGetDoc('Raven Message', linked_message) + const { data, isLoading } = useFrappeGetCall('raven.api.chat.get_reply_message_content', { + message_id: linked_message + }, `reply_message_${linked_message}`, { + revalidateIfStale: false, + revalidateOnFocus: false, + shouldRetryOnError: false, + revalidateOnReconnect: false + }) const user = useMemo(() => { - if (data) { - return members[data.owner] + if (data && data.message) { + return members[data?.message.owner] } else { return undefined } @@ -178,20 +184,20 @@ const ReplyBlock = ({ linked_message }: { linked_message: string }) => { document.getElementById(`message-${linked_message}`)?.scrollIntoView({ behavior: 'smooth' }) } - const date = data ? new Date(data?.creation) : null + const date = data ? new Date(data?.message?.creation) : null return
- {data &&
+ {data && data.message &&
- {user?.full_name ?? data.owner} + {user?.full_name ?? data.message.owner} {date && on {DateObjectToFormattedDateStringWithoutYear(date)} at {DateObjectToTimeString(date)}}
- {data.message_type === 'Text' &&
} - {data.message_type === 'Image' &&
- {`Image`} + {data.message.message_type === 'Text' &&
} + {data.message.message_type === 'Image' &&
+ {`Image`}

📸  Image

} - {data.message_type === 'File' &&

📎  {data.file?.split('/')[3]}

} + {data.message.message_type === 'File' &&

📎  {data.message.file?.split('/')[3]}

}
}
diff --git a/raven-app/src/components/common/Toast/Toast.tsx b/raven-app/src/components/common/Toast/Toast.tsx index 72b343b18..939f555c9 100644 --- a/raven-app/src/components/common/Toast/Toast.tsx +++ b/raven-app/src/components/common/Toast/Toast.tsx @@ -13,7 +13,7 @@ const ToastViewport = React.forwardRef< - + {channelMembers?.[peer]?.full_name ?? peer} diff --git a/raven-app/src/components/feature/chat/ChatInput/RightToolbarButtons.tsx b/raven-app/src/components/feature/chat/ChatInput/RightToolbarButtons.tsx index ec7dec1d9..05eb90350 100644 --- a/raven-app/src/components/feature/chat/ChatInput/RightToolbarButtons.tsx +++ b/raven-app/src/components/feature/chat/ChatInput/RightToolbarButtons.tsx @@ -11,7 +11,8 @@ const EmojiPicker = lazy(() => import('@/components/common/EmojiPicker/EmojiPick type RightToolbarButtonsProps = { fileProps?: ToolbarFileProps, sendMessage: (html: string, json: any) => Promise, - messageSending: boolean + messageSending: boolean, + setContent: (content: string) => void } /** * Component to render the right toolbar buttons: @@ -131,9 +132,10 @@ const FilePickerButton = ({ fileProps }: { fileProps: ToolbarFileProps }) => { } -const SendButton = ({ sendMessage, messageSending }: { +const SendButton = ({ sendMessage, messageSending, setContent }: { sendMessage: RightToolbarButtonsProps['sendMessage'], - messageSending: boolean + messageSending: boolean, + setContent: RightToolbarButtonsProps['setContent'] }) => { const { editor } = useCurrentEditor() const onClick = () => { @@ -151,6 +153,7 @@ const SendButton = ({ sendMessage, messageSending }: { editor.setEditable(false) sendMessage(html, json) .then(() => { + setContent('') editor.chain().focus().clearContent(true).run() editor.setEditable(true) }) diff --git a/raven-app/src/components/feature/chat/ChatInput/Tiptap.tsx b/raven-app/src/components/feature/chat/ChatInput/Tiptap.tsx index 001584975..4b6a375de 100644 --- a/raven-app/src/components/feature/chat/ChatInput/Tiptap.tsx +++ b/raven-app/src/components/feature/chat/ChatInput/Tiptap.tsx @@ -42,6 +42,7 @@ export interface ToolbarFileProps { type TiptapEditorProps = { slotBefore?: React.ReactNode, slotAfter?: React.ReactNode, + placeholder?: string, sessionStorageKey?: string, disableSessionStorage?: boolean, fileProps?: ToolbarFileProps, @@ -50,18 +51,6 @@ type TiptapEditorProps = { defaultText?: string } -const COOL_PLACEHOLDERS = [ - "Sure - you can send your message via pigeons, only if you want them covered in poop 😷", - "Delivering messages atop dragons 🐉 is available on a chargeable basis.", - "Note 🚨: Service beyond the wall is currently disrupted due to bad weather.", - "Pigeons just have better brand recognition tbh 🤷🏻", - "Ravens double up as spies. Eyes everywhere 👀", - "Ravens do not 'slack' off. See what we did there? 😉", - "Were you expecting a funny placeholder? 😂", - "Want to know who writes these placeholders? 🤔. No one.", - "Type a message..." -] - export const UserMention = Mention.extend({ name: 'userMention', }) @@ -81,7 +70,7 @@ export const ChannelMention = Mention.extend({ pluginKey: new PluginKey('channelMention'), } }) -const Tiptap = ({ slotBefore, fileProps, onMessageSend, messageSending, sessionStorageKey = 'tiptap-editor', disableSessionStorage = false, defaultText = '' }: TiptapEditorProps) => { +const Tiptap = ({ slotBefore, fileProps, onMessageSend, placeholder = 'Type a message...', messageSending, sessionStorageKey = 'tiptap-editor', disableSessionStorage = false, defaultText = '' }: TiptapEditorProps) => { const { users } = useContext(UserListContext) @@ -406,7 +395,7 @@ const Tiptap = ({ slotBefore, fileProps, onMessageSend, messageSending, sessionS }), Placeholder.configure({ // Pick a random placeholder from the list. - placeholder: COOL_PLACEHOLDERS[Math.floor(Math.random() * (COOL_PLACEHOLDERS.length))], + placeholder, }), CodeBlockLowlight.configure({ lowlight @@ -420,7 +409,6 @@ const Tiptap = ({ slotBefore, fileProps, onMessageSend, messageSending, sessionS const editor = useEditor({ extensions, content, - autofocus: "end", editorProps: { attributes: { class: 'tiptap-editor' @@ -431,6 +419,8 @@ const Tiptap = ({ slotBefore, fileProps, onMessageSend, messageSending, sessionS } }, [onMessageSend]) + + useEffect(() => { editor?.commands.setContent(content) }, [onMessageSend]) @@ -443,7 +433,7 @@ const Tiptap = ({ slotBefore, fileProps, onMessageSend, messageSending, sessionS - + diff --git a/raven-app/src/components/feature/chat/ChatMessage/MessageActions/MessageActions.tsx b/raven-app/src/components/feature/chat/ChatMessage/MessageActions/MessageActions.tsx index abb93f1de..fa7b8710c 100644 --- a/raven-app/src/components/feature/chat/ChatMessage/MessageActions/MessageActions.tsx +++ b/raven-app/src/components/feature/chat/ChatMessage/MessageActions/MessageActions.tsx @@ -126,7 +126,7 @@ const SaveMessageAction = ({ message, updateMessages }: { message: Message, upda }).then(() => { toast({ title: isSaved ? 'Message unsaved' : 'Message saved', - variant: isSaved ? 'default' : 'accent', + variant: 'accent', duration: 800, }) updateMessages() diff --git a/raven-app/src/components/feature/chat/ChatMessage/MessageActions/QuickActions/QuickActions.tsx b/raven-app/src/components/feature/chat/ChatMessage/MessageActions/QuickActions/QuickActions.tsx index 6f90e27ac..f4425fbbb 100644 --- a/raven-app/src/components/feature/chat/ChatMessage/MessageActions/QuickActions/QuickActions.tsx +++ b/raven-app/src/components/feature/chat/ChatMessage/MessageActions/QuickActions/QuickActions.tsx @@ -47,7 +47,9 @@ export const QuickActions = ({ message, onReply, onEdit, updateMessages, isOwner -top-6 right-4 group-hover:visible - z-2 + group-hover:transition-all + group-hover:delay-100 + z-50 p-1 shadow-md rounded-md @@ -70,13 +72,13 @@ export const QuickActions = ({ message, onReply, onEdit, updateMessages, isOwner - {isOwner && message.message_type === 'Text' ? - : + : + diff --git a/raven-app/src/components/feature/chat/ChatMessage/ReplyMessageBox/ReplyMessageBox.tsx b/raven-app/src/components/feature/chat/ChatMessage/ReplyMessageBox/ReplyMessageBox.tsx index 9614cb457..b87b7b682 100644 --- a/raven-app/src/components/feature/chat/ChatMessage/ReplyMessageBox/ReplyMessageBox.tsx +++ b/raven-app/src/components/feature/chat/ChatMessage/ReplyMessageBox/ReplyMessageBox.tsx @@ -1,5 +1,5 @@ import { FileMessage, Message, TextMessage } from "../../../../../../../types/Messaging/Message" -import { Flex, Separator, Text } from "@radix-ui/themes" +import { Box, Flex, Separator, Text } from "@radix-ui/themes" import { useGetUser } from "@/hooks/useGetUser" import { DateMonthAtHourMinuteAmPm } from "@/utils/dateConversions" import { FileExtensionIcon } from "@/utils/layout/FileExtIcon" @@ -31,15 +31,16 @@ export const ReplyMessageBox = ({ message, children, className, ...props }: Repl - {['File', 'Image'].includes(message.message_type) ? - - {message.message_type === 'File' && } - {message.message_type === 'Image' && {`Image} - {getFileName((message as FileMessage).file)} - - : - } - + + {['File', 'Image'].includes(message.message_type) ? + + {message.message_type === 'File' && } + {message.message_type === 'Image' && {`Image} + {getFileName((message as FileMessage).file)} + + : + } + {children} @@ -54,12 +55,8 @@ interface ReplyMessageProps extends FlexProps { */ export const ReplyMessage = ({ messageID, ...props }: ReplyMessageProps) => { - const { data, isLoading } = useFrappeGetCall('frappe.client.get_value', { - doctype: 'Raven Message', - filters: { - name: messageID - }, - fieldname: JSON.stringify(['owner', 'creation', 'message_type', 'file', 'text', 'channel_id', 'name']) + const { data, isLoading } = useFrappeGetCall('raven.api.chat.get_reply_message_content', { + message_id: messageID }, `reply_message_${messageID}`, { revalidateIfStale: false, revalidateOnFocus: false, diff --git a/raven-app/src/components/feature/chat/ChatStream/ChatBoxBody.tsx b/raven-app/src/components/feature/chat/ChatStream/ChatBoxBody.tsx index da6dd7a7c..b076f96b5 100644 --- a/raven-app/src/components/feature/chat/ChatStream/ChatBoxBody.tsx +++ b/raven-app/src/components/feature/chat/ChatStream/ChatBoxBody.tsx @@ -21,6 +21,19 @@ import { BiX } from "react-icons/bi" const Tiptap = lazy(() => import("../ChatInput/Tiptap")) + +const COOL_PLACEHOLDERS = [ + "Sure - you can send your message via pigeons, only if you want them covered in poop 😷", + "Delivering messages atop dragons 🐉 is available on a chargeable basis.", + "Note 🚨: Service beyond the wall is currently disrupted due to bad weather.", + "Pigeons just have better brand recognition tbh 🤷🏻", + "Ravens double up as spies. Eyes everywhere 👀", + "Ravens do not 'slack' off. See what we did there? 😉", + "Were you expecting a funny placeholder? 😂", + "Want to know who writes these placeholders? 🤔. No one.", + "Type a message..." +] +const randomPlaceholder = COOL_PLACEHOLDERS[Math.floor(Math.random() * (COOL_PLACEHOLDERS.length))] interface ChatBoxBodyProps { channelData: ChannelListItem | DMChannelListItem } @@ -101,17 +114,19 @@ export const ChatBoxBody = ({ channelData }: ChatBoxBodyProps) => { if (data) { return ( + - - + {isLoading ? : + + } {channelData?.is_archived == 0 && (isUserInChannel || channelData?.type === 'Open') && }> @@ -121,6 +136,7 @@ export const ChatBoxBody = ({ channelData }: ChatBoxBodyProps) => { fileInputRef, addFile }} + placeholder={randomPlaceholder} sessionStorageKey={`tiptap-${channelData.name}`} onMessageSend={sendMessage} messageSending={loading} diff --git a/raven-app/src/components/feature/chat/ChatStream/ChatHistory.tsx b/raven-app/src/components/feature/chat/ChatStream/ChatHistory.tsx index f2af27244..10f9568a8 100644 --- a/raven-app/src/components/feature/chat/ChatStream/ChatHistory.tsx +++ b/raven-app/src/components/feature/chat/ChatStream/ChatHistory.tsx @@ -5,7 +5,7 @@ import { useCallback, useContext, useRef } from "react"; import { Virtuoso } from 'react-virtuoso'; import { VirtuosoRefContext } from "../../../../utils/message/VirtuosoRefProvider"; import { ChannelListItem, DMChannelListItem } from "@/utils/channel/ChannelListProvider"; -import { Box, Flex } from "@radix-ui/themes"; +import { Box } from "@radix-ui/themes"; import { DateMonthYear } from "@/utils/dateConversions"; import { MessageItem } from "../ChatMessage/MessageItem"; import { DeleteMessageDialog, useDeleteMessage } from "../ChatMessage/MessageActions/DeleteMessage"; @@ -134,7 +134,7 @@ const RenderItem = ({ index, replyToMessage, updateMessages, block, onReplyMessa } if (block.block_type === 'message') { return ( - + - - {previewURL ? File preview : } + + {previewURL ? File preview : } diff --git a/raven-app/src/components/layout/EmptyState/EmptyState.tsx b/raven-app/src/components/layout/EmptyState/EmptyState.tsx index 4aad38a03..bd9fdf9e6 100644 --- a/raven-app/src/components/layout/EmptyState/EmptyState.tsx +++ b/raven-app/src/components/layout/EmptyState/EmptyState.tsx @@ -70,7 +70,7 @@ const EmptyStateForDM = ({ channelData }: EmptyStateForDMProps) => { {channelData?.is_direct_message == 1 && - + {users?.[peer]?.full_name} {users?.[peer]?.name} diff --git a/raven-app/src/components/layout/Heading/PageHeader.tsx b/raven-app/src/components/layout/Heading/PageHeader.tsx index 0d1443769..b539a15a9 100644 --- a/raven-app/src/components/layout/Heading/PageHeader.tsx +++ b/raven-app/src/components/layout/Heading/PageHeader.tsx @@ -3,19 +3,22 @@ import { Box, Flex } from '@radix-ui/themes' export const PageHeader = ({ children }: PropsWithChildren) => { return ( -
- - {children} - - +
+ + + {children} + +
) } \ No newline at end of file diff --git a/raven-app/src/main.tsx b/raven-app/src/main.tsx index 42f3cd810..49db8d196 100644 --- a/raven-app/src/main.tsx +++ b/raven-app/src/main.tsx @@ -11,7 +11,6 @@ if (import.meta.env.DEV) { .then(response => response.json()) .then((values) => { const v = JSON.parse(values.message) - console.log(v) //@ts-expect-error if (!window.frappe) window.frappe = {}; //@ts-ignore diff --git a/raven/api/chat.py b/raven/api/chat.py index 488763fe4..4475e0918 100644 --- a/raven/api/chat.py +++ b/raven/api/chat.py @@ -35,4 +35,33 @@ def get_channel_members(channel_id): frappe.throw(_("Channel {} does not exist".format(channel_id)), frappe.DoesNotExistError) else: - frappe.throw(_("You do not have permission to view this channel"), frappe.PermissionError) \ No newline at end of file + frappe.throw(_("You do not have permission to view this channel"), frappe.PermissionError) + + +@frappe.whitelist(methods=['GET']) +def get_reply_message_content(message_id): + # Check if the user has permission to view the message + # fetch all channel members + # get member details from user table, such as name, full_name, user_image, first_name + + if frappe.db.exists("Raven Message", message_id): + + channel_id = frappe.db.get_value("Raven Message", message_id, "channel_id") + channel_type = frappe.db.get_value("Raven Channel", channel_id, "type") + has_permission = False + + if channel_type == 'Public' or channel_type == 'Open': + has_permission = True + else: + if frappe.db.exists("Raven Channel Member", {"user_id": frappe.session.user, "channel_id": channel_id}): + has_permission = True + + if has_permission: + return frappe.db.sql(""" + SELECT owner, creation, message_type, file, text, channel_id, name + FROM `tabRaven Message` + WHERE name = %s + """, message_id, as_dict=True)[0] + else: + frappe.throw(_("Message {} does not exist".format(message_id)), frappe.DoesNotExistError) + # return frappe.db.get_value("Raven Message", message_id, ['owner', 'creation', 'message_type', 'file', 'text', 'channel_id', 'name'], ignore_permissions=True)