diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..da2a5e3b3 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,14 @@ +# These are supported funding model platforms + +github: [The-Commit-Company] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +polar: # Replace with a single Polar username +buy_me_a_coffee: # Replace with a single Buy Me a Coffee username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/workflows/apply-issue-labels-to-pr.yml b/.github/workflows/apply-issue-labels-to-pr.yml deleted file mode 100644 index b1eb6ea37..000000000 --- a/.github/workflows/apply-issue-labels-to-pr.yml +++ /dev/null @@ -1,75 +0,0 @@ -name: "Apply Issue labels to PR" - -on: - pull_request_target: - types: - - opened - - synchronize - - reopened -jobs: - label_on_pr: - runs-on: ubuntu-latest - - permissions: - contents: none - issues: read - pull-requests: write - - steps: - - name: Apply labels from linked issue to PR - uses: actions/github-script@v5 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - async function getLinkedIssues(owner, repo, prNumber) { - const query = `query GetLinkedIssues($owner: String!, $repo: String!, $prNumber: Int!) { - repository(owner: $owner, name: $repo) { - pullRequest(number: $prNumber) { - closingIssuesReferences(first: 10) { - nodes { - number - labels(first: 10) { - nodes { - name - } - } - } - } - } - } - }`; - - const variables = { - owner: owner, - repo: repo, - prNumber: prNumber, - }; - - const result = await github.graphql(query, variables); - return result.repository.pullRequest.closingIssuesReferences.nodes; - } - - const pr = context.payload.pull_request; - const linkedIssues = await getLinkedIssues( - context.repo.owner, - context.repo.repo, - pr.number - ); - - const labelsToAdd = new Set(); - for (const issue of linkedIssues) { - if (issue.labels && issue.labels.nodes) { - for (const label of issue.labels.nodes) { - labelsToAdd.add(label.name); - } - } - } - - if (labelsToAdd.size) { - await github.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - labels: Array.from(labelsToAdd), - }); - } diff --git a/mobile/package.json b/mobile/package.json index 3c542a00a..8b94e27f0 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -1,7 +1,7 @@ { "name": "mobile", "private": true, - "version": "1.5.1", + "version": "1.5.2", "type": "module", "scripts": { "dev": "vite", diff --git a/mobile/src/components/common/EmojiPicker.tsx b/mobile/src/components/common/EmojiPicker.tsx index b1c71ff48..e3f8dc21d 100644 --- a/mobile/src/components/common/EmojiPicker.tsx +++ b/mobile/src/components/common/EmojiPicker.tsx @@ -2,12 +2,17 @@ import { createElement, useEffect, useRef } from "react" import 'emoji-picker-element' import './emojiPicker.styles.css' +import { Database } from "emoji-picker-element"; + +export const emojiDatabase = new Database(); + const EmojiPicker = ({ onSelect }: { onSelect: (emoji: string) => void }) => { const ref = useRef(null) useEffect(() => { const handler = (event: any) => { + emojiDatabase.incrementFavoriteEmojiCount(event.detail.unicode) onSelect(event.detail.unicode) } ref.current?.addEventListener('emoji-click', handler) diff --git a/mobile/src/components/features/chat-input/EmojiList.tsx b/mobile/src/components/features/chat-input/EmojiList.tsx new file mode 100644 index 000000000..0818edabd --- /dev/null +++ b/mobile/src/components/features/chat-input/EmojiList.tsx @@ -0,0 +1,94 @@ +import { Text } from '@radix-ui/themes' +import { ReactRendererOptions } from '@tiptap/react' +import { NativeEmoji } from 'emoji-picker-element/shared' +import { + forwardRef, useEffect, useImperativeHandle, + useState, +} from 'react' + +export default forwardRef((props: ReactRendererOptions['props'], ref) => { + + const [selectedIndex, setSelectedIndex] = useState(0) + + const selectItem = (index: number) => { + const item = props?.items[index] + if (item) { + props.command(item) + } + } + + const upHandler = () => { + setSelectedIndex((selectedIndex + props?.items.length - 1) % props?.items.length) + } + + const downHandler = () => { + setSelectedIndex((selectedIndex + 1) % props?.items.length) + } + + const enterHandler = () => { + selectItem(selectedIndex) + } + + useEffect(() => setSelectedIndex(0), [props?.items]) + + useImperativeHandle(ref, () => ({ + onKeyDown: ({ event }: { event: KeyboardEvent }) => { + if (event.key === 'ArrowUp') { + upHandler() + return true + } + + if (event.key === 'ArrowDown') { + downHandler() + return true + } + + if (event.key === 'Enter') { + enterHandler() + return true + } + + return false + }, + })) + + if (props?.items.length) { + return ( + + ) + } else { + return null + } +}) + +const EmojiItem = ({ item, index, selectItem, selectedIndex, itemsLength }: { itemsLength: number, selectedIndex: number, index: number, item: NativeEmoji, selectItem: (index: number) => void }) => { + + const roundedTop = index === 0 ? ' rounded-t-md' : '' + + const roundedBottom = index === itemsLength - 1 ? ' rounded-b-md' : '' + + return
  • selectItem(index)} + > + + {item.unicode} {item.shortcodes?.[0] ?? item.annotation} + +
  • +} \ No newline at end of file diff --git a/mobile/src/components/features/chat-input/EmojiSuggestion.tsx b/mobile/src/components/features/chat-input/EmojiSuggestion.tsx new file mode 100644 index 000000000..a243f0f7e --- /dev/null +++ b/mobile/src/components/features/chat-input/EmojiSuggestion.tsx @@ -0,0 +1,104 @@ +import { Node } from '@tiptap/core' +import Suggestion from '@tiptap/suggestion' +import { ReactRenderer } from '@tiptap/react' +import EmojiList from './EmojiList' +import tippy from 'tippy.js'; +import { NativeEmoji } from 'emoji-picker-element/shared'; +import { PluginKey } from '@tiptap/pm/state'; +import { emojiDatabase } from '@/components/common/EmojiPicker'; + +export const EmojiSuggestion = Node.create({ + name: 'emoji', + group: 'inline', + pluginKey: new PluginKey('emojiSuggestion'), + + inline: true, + + selectable: false, + + atom: true, + + addProseMirrorPlugins() { + return [ + Suggestion({ + editor: this.editor, + char: ':', + items: (query) => { + if (query.query.length !== 0) { + return emojiDatabase.getEmojiBySearchQuery(query.query.toLowerCase()).then((emojis) => { + return emojis.slice(0, 10) as NativeEmoji[] + }); + } else { + return emojiDatabase.getTopFavoriteEmoji(10) as Promise + } + + + return [] + }, + render: () => { + let component: any; + let popup: any; + + return { + onStart: props => { + component = new ReactRenderer(EmojiList, { + props, + editor: props.editor, + }) + + if (!props.clientRect) { + return + } + + popup = tippy('body' as any, { + getReferenceClientRect: props.clientRect as any, + appendTo: () => document.body, + content: component.element, + showOnCreate: true, + interactive: true, + trigger: 'manual', + placement: 'bottom-start', + }) + }, + + onUpdate(props) { + component.updateProps(props) + + if (!props.clientRect) { + return + } + + popup[0].setProps({ + getReferenceClientRect: props.clientRect, + }) + }, + + onKeyDown(props) { + if (props.event.key === 'Escape') { + popup[0].hide() + + return true + } + + return component.ref?.onKeyDown(props) + }, + + onExit() { + popup[0].destroy() + component.destroy() + }, + } + }, + command: ({ editor, range, props }) => { + // Replace the text from : to with the emoji node + editor.chain().focus().deleteRange(range).insertContent(props.unicode).run() + + emojiDatabase.incrementFavoriteEmojiCount(props.unicode) + + window.getSelection()?.collapseToEnd() + }, + }), + ] + }, + +}) \ No newline at end of file diff --git a/mobile/src/components/features/chat-input/Tiptap.tsx b/mobile/src/components/features/chat-input/Tiptap.tsx index fca43b63f..a9f2d751c 100644 --- a/mobile/src/components/features/chat-input/Tiptap.tsx +++ b/mobile/src/components/features/chat-input/Tiptap.tsx @@ -21,13 +21,13 @@ import ts from 'highlight.js/lib/languages/typescript' import html from 'highlight.js/lib/languages/xml' import json from 'highlight.js/lib/languages/json' import python from 'highlight.js/lib/languages/python' -import { BiSend, BiSolidSend } from 'react-icons/bi' -import { AiOutlinePaperClip } from 'react-icons/ai'; +import { BiSolidSend } from 'react-icons/bi' import { IconButton } from '@radix-ui/themes' import { useKeyboardState } from '@ionic/react-hooks/keyboard'; import MessageInputActions from './MessageInputActions' import { useClickAway } from '@uidotdev/usehooks' import { FiPlus } from 'react-icons/fi' +import { EmojiSuggestion } from './EmojiSuggestion' const lowlight = createLowlight(common) @@ -109,7 +109,7 @@ export const Tiptap = ({ onMessageSend, messageSending, defaultText = '', onPick }, code: { HTMLAttributes: { - class: 'font-mono bg-slate-950 text-sm radius-md p-1 text-white' + class: 'pt-0.5 px-1 pb-px bg-[var(--gray-a3)] dark:bg-[#0d0d0d] text-[var(--ruby-a11)] dark-[var(--accent-a3)] text text-xs font-mono rounded border border-gray-4 dark:border-gray-6' } }, @@ -273,6 +273,7 @@ export const Tiptap = ({ onMessageSend, messageSending, defaultText = '', onPick Placeholder.configure({ placeholder: 'Type a message...' }), + EmojiSuggestion ] const [focused, setFocused] = useState(false) 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 df57a323a..86754875d 100644 --- a/mobile/src/components/features/chat-space/chat-view/MessageBlock.tsx +++ b/mobile/src/components/features/chat-space/chat-view/MessageBlock.tsx @@ -1,4 +1,4 @@ -import { memo, useContext, useEffect, useMemo, useRef, useState } from 'react' +import { memo, useContext, useMemo, useRef, useState } from 'react' import { FileMessage, ImageMessage, Message, TextMessage, PollMessage } from '../../../../../../types/Messaging/Message' import { IonIcon, IonSkeletonText, IonText } from '@ionic/react' import { UserFields } from '@/utils/users/UserListProvider' @@ -12,16 +12,13 @@ import { useLongPress } from "@uidotdev/usehooks"; import MessageReactions from './components/MessageReactions' import parse from 'html-react-parser'; import clsx from 'clsx' -import { Avatar, Badge, Box, Button, Checkbox, Flex, RadioGroup, Separator, Text, Theme } from '@radix-ui/themes' +import { Avatar, Badge, Text, Theme } from '@radix-ui/themes' import { useGetUser } from '@/hooks/useGetUser' import { generateAvatarColor, getInitials } from '@/components/common/UserAvatar' import { RiRobot2Fill } from 'react-icons/ri' -import { useFrappeDocumentEventListener, useFrappeGetCall, useFrappePostCall, useSWRConfig } from 'frappe-react-sdk' -import { RavenPoll } from '@/types/RavenMessaging/RavenPoll' -import { RavenPollOption } from '@/types/RavenMessaging/RavenPollOption' import { MdOutlineBarChart } from 'react-icons/md' -import { ViewPollVotes } from '../../polls/ViewPollVotes' import { TiptapRenderer } from './components/TiptapRenderer/TiptapRenderer' +import PollMessageBlock from './components/PollMessage' type Props = { message: Message, @@ -348,222 +345,3 @@ const ReplyBlock = ({ message }: { message: Message }) => { } } - -export interface Poll { - 'poll': RavenPoll, - 'current_user_votes': { 'option': string }[] -} - -const PollMessageBlock = ({ message, onModalClose, onModalOpen }: { message: PollMessage, onModalOpen: VoidFunction, onModalClose: VoidFunction }) => { - - const { mutate: globalMutate } = useSWRConfig() - // fetch poll data using message_id - const { data, error, mutate } = useFrappeGetCall<{ message: Poll }>('raven.api.raven_poll.get_poll', { - 'message_id': message.name, - }, `poll_data_${message.poll_id}`, { - revalidateOnFocus: false, - revalidateIfStale: false, - revalidateOnReconnect: false, - revalidateOnMount: true - }) - - useFrappeDocumentEventListener('Raven Poll', message.poll_id, () => { - mutate() - globalMutate(`poll_votes_${message.poll_id}`) - }) - - return ( -
    - {data && } -
    - ) -} - -const PollMessageBox = ({ data, messageID, onModalClose, onModalOpen }: { data: Poll, messageID: string, onModalOpen: VoidFunction, onModalClose: VoidFunction }) => { - - const [isOpen, setOpen] = useState(false) - const onViewClick: React.MouseEventHandler = (e) => { - setOpen(true) - onModalOpen() - } - - const closeModal = () => { - setOpen(false) - onModalClose() - } - return ( - - - - - {data.poll.question} - - {data.poll.is_anonymous ? Anonymous : null} - - {data.current_user_votes.length > 0 ? - : - <> - {data.poll.is_multi_choice ? - : - - } - - } - {data.poll.is_disabled ? Poll is now closed : null} - - {data.poll.is_anonymous ? null : - <> - - - - - - - } - - ) -} - -const PollResults = ({ data }: { data: Poll }) => { - return ( - - {data.poll.options.map(option => { - return - })} - {data.poll.total_votes} vote{data.poll.total_votes > 1 ? 's' : ''} - - ) -} - -const PollOption = ({ data, option }: { data: Poll, option: RavenPollOption }) => { - - const getPercentage = (votes: number) => { - if (data.poll.is_multi_choice) { - const totalVotes = data.poll.options.reduce((acc, opt) => acc + (opt.votes ?? 0), 0) - return (votes / totalVotes) * 100 - } else return (votes / data.poll.total_votes) * 100 - } - - // State to track whether the animation should be triggered - const [triggerAnimation, setTriggerAnimation] = useState(false) - - // Use useEffect to trigger animation after the component is mounted - useEffect(() => { - setTriggerAnimation(true) - }, []) - - const isCurrentUserVote = useMemo(() => { - return data.current_user_votes.some(vote => vote.option === option.name) - }, [data.current_user_votes, option.name]) - - const percentage = useMemo(() => { - return getPercentage(option.votes ?? 0) - }, [option.votes]) - - const width = `${percentage}%` - - return ( - - - - {option.option} - {percentage.toFixed(1)}% - - ) -} - -const SingleChoicePoll = ({ data, messageID }: { data: Poll, messageID: string }) => { - - const { mutate } = useSWRConfig() - const { call } = useFrappePostCall('raven.api.raven_poll.add_vote') - const onVoteSubmit = async (option: RavenPollOption) => { - return call({ - 'message_id': messageID, - 'option_id': option.name - }).then(() => { - mutate(`poll_data_${data.poll.name}`) - }) - } - - return ( - - {data.poll.options.map(option => ( -
    - - - onVoteSubmit(option)} /> - {option.option} - - -
    - ))} -
    - ) -} - -const MultiChoicePoll = ({ data, messageID }: { data: Poll, messageID: string }) => { - - const [selectedOptions, setSelectedOptions] = useState([]) - const { mutate } = useSWRConfig() - - const handleCheckboxChange = (name: string, value: boolean | string) => { - if (value) { - setSelectedOptions((opts) => [...opts, name]) - } else { - setSelectedOptions((opts) => opts.filter(opt => opt !== name)) - } - } - - const { call } = useFrappePostCall('raven.api.raven_poll.add_vote') - const onVoteSubmit = async () => { - return call({ - 'message_id': messageID, - 'option_id': selectedOptions - }).then(() => { - mutate(`poll_data_${data.poll.name}`) - }) - } - - return ( -
    - {data.poll.options.map(option => ( -
    - - - handleCheckboxChange(option.name, v)} /> - {option.option} - - -
    - ))} - - To view the poll results, please submit your choice(s) - - -
    - ) -} \ No newline at end of file diff --git a/mobile/src/components/features/chat-space/chat-view/components/PollMessage.tsx b/mobile/src/components/features/chat-space/chat-view/components/PollMessage.tsx new file mode 100644 index 000000000..4af4090c1 --- /dev/null +++ b/mobile/src/components/features/chat-space/chat-view/components/PollMessage.tsx @@ -0,0 +1,231 @@ +import { useEffect, useMemo, useState } from 'react' +import { Badge, Box, Button, Checkbox, Flex, RadioGroup, Separator, Text } from '@radix-ui/themes' +import { useFrappeDocumentEventListener, useFrappeGetCall, useFrappePostCall, useSWRConfig } from 'frappe-react-sdk' +import { RavenPoll } from '@/types/RavenMessaging/RavenPoll' +import { RavenPollOption } from '@/types/RavenMessaging/RavenPollOption' +import { PollMessage } from '../../../../../../../types/Messaging/Message' +import { ViewPollVotes } from '@/components/features/polls/ViewPollVotes' + +export interface Poll { + 'poll': RavenPoll, + 'current_user_votes': { 'option': string }[] +} + +const PollMessageBlock = ({ message, onModalClose, onModalOpen }: { message: PollMessage, onModalOpen: VoidFunction, onModalClose: VoidFunction }) => { + + const { mutate: globalMutate } = useSWRConfig() + // fetch poll data using message_id + const { data, error, mutate } = useFrappeGetCall<{ message: Poll }>('raven.api.raven_poll.get_poll', { + 'message_id': message.name, + }, `poll_data_${message.poll_id}`, { + revalidateOnFocus: false, + revalidateIfStale: false, + revalidateOnReconnect: false, + revalidateOnMount: true + }) + + useFrappeDocumentEventListener('Raven Poll', message.poll_id, () => { + mutate() + globalMutate(`poll_votes_${message.poll_id}`) + }) + + return ( +
    + {data && } +
    + ) +} + +export default PollMessageBlock + +const PollMessageBox = ({ data, messageID, onModalClose, onModalOpen }: { data: Poll, messageID: string, onModalOpen: VoidFunction, onModalClose: VoidFunction }) => { + + const [isOpen, setOpen] = useState(false) + const onViewClick: React.MouseEventHandler = (e) => { + setOpen(true) + onModalOpen() + } + + const closeModal = () => { + setOpen(false) + onModalClose() + } + return ( + + + + + {data.poll.question} + + {data.poll.is_anonymous ? Anonymous : null} + + {data.current_user_votes.length > 0 ? + : + <> + {data.poll.is_multi_choice ? + : + + } + + } + {data.poll.is_disabled ? Poll is now closed : null} + + {data.poll.is_anonymous ? null : + <> + + + + + + + } + + ) +} + +const PollResults = ({ data }: { data: Poll }) => { + return ( + + {data.poll.options.map(option => { + return + })} + {data.poll.total_votes} vote{data.poll.total_votes > 1 ? 's' : ''} + + ) +} + +const PollOption = ({ data, option }: { data: Poll, option: RavenPollOption }) => { + + + // State to track whether the animation should be triggered + const [triggerAnimation, setTriggerAnimation] = useState(false) + + // Use useEffect to trigger animation after the component is mounted + useEffect(() => { + setTriggerAnimation(true) + }, [data]) + + const isCurrentUserVote = useMemo(() => { + return data.current_user_votes.some(vote => vote.option === option.name) + }, [data.current_user_votes, option.name]) + + const percentage = useMemo(() => { + + const getPercentage = (votes: number) => { + if (data.poll.is_multi_choice) { + const totalVotes = data.poll.options.reduce((acc, opt) => acc + (opt.votes ?? 0), 0) + return (votes / totalVotes) * 100 + } else return (votes / data.poll.total_votes) * 100 + } + + + return getPercentage(option.votes ?? 0) + }, [option.votes, data]) + + const width = `${percentage}%` + + return ( + + + + {option.option} + {percentage.toFixed(1)}% + + ) +} + +const SingleChoicePoll = ({ data, messageID }: { data: Poll, messageID: string }) => { + + const { mutate } = useSWRConfig() + const { call } = useFrappePostCall('raven.api.raven_poll.add_vote') + const onVoteSubmit = async (option: RavenPollOption) => { + return call({ + 'message_id': messageID, + 'option_id': option.name + }).then(() => { + mutate(`poll_data_${data.poll.name}`) + }) + } + + return ( + + {data.poll.options.map(option => ( +
    + + + onVoteSubmit(option)} /> + {option.option} + + +
    + ))} +
    + ) +} + +const MultiChoicePoll = ({ data, messageID }: { data: Poll, messageID: string }) => { + + const [selectedOptions, setSelectedOptions] = useState([]) + const { mutate } = useSWRConfig() + + const handleCheckboxChange = (name: string, value: boolean | string) => { + if (value) { + setSelectedOptions((opts) => [...opts, name]) + } else { + setSelectedOptions((opts) => opts.filter(opt => opt !== name)) + } + } + + const { call } = useFrappePostCall('raven.api.raven_poll.add_vote') + const onVoteSubmit = async () => { + return call({ + 'message_id': messageID, + 'option_id': selectedOptions + }).then(() => { + mutate(`poll_data_${data.poll.name}`) + }) + } + + return ( +
    + {data.poll.options.map(option => ( +
    + + + handleCheckboxChange(option.name, v)} /> + {option.option} + + +
    + ))} + + To view the poll results, please submit your choice(s) + + +
    + ) +} \ No newline at end of file diff --git a/mobile/src/components/features/chat-space/chat-view/components/TiptapRenderer/Blockquote.tsx b/mobile/src/components/features/chat-space/chat-view/components/TiptapRenderer/Blockquote.tsx deleted file mode 100644 index b579d247d..000000000 --- a/mobile/src/components/features/chat-space/chat-view/components/TiptapRenderer/Blockquote.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Blockquote } from '@radix-ui/themes'; -import TiptapBlockquote from '@tiptap/extension-blockquote' -import { NodeViewRendererProps, NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react"; - -export const CustomBlockquote = TiptapBlockquote.extend({ - addNodeView() { - return ReactNodeViewRenderer(BlockquoteRenderer) - } -}) - -const BlockquoteRenderer = ({ node }: NodeViewRendererProps) => { - return ( - -
    - {node.textContent} -
    -
    - ); -}; \ No newline at end of file diff --git a/mobile/src/components/features/chat-space/chat-view/components/TiptapRenderer/TiptapRenderer.tsx b/mobile/src/components/features/chat-space/chat-view/components/TiptapRenderer/TiptapRenderer.tsx index d8e85123d..2b7a47a14 100644 --- a/mobile/src/components/features/chat-space/chat-view/components/TiptapRenderer/TiptapRenderer.tsx +++ b/mobile/src/components/features/chat-space/chat-view/components/TiptapRenderer/TiptapRenderer.tsx @@ -12,7 +12,6 @@ import json from 'highlight.js/lib/languages/json' import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight' import { common, createLowlight } from 'lowlight' import python from 'highlight.js/lib/languages/python' -import { CustomBlockquote } from './Blockquote' import { CustomBold } from './Bold' import { CustomUserMention } from './Mention' import { CustomLink } from './Link' @@ -52,7 +51,6 @@ export const TiptapRenderer = ({ message, user, isScrolling = false, isTruncated heading: false, codeBlock: false, bold: false, - blockquote: false, italic: false, listItem: { HTMLAttributes: { @@ -63,6 +61,16 @@ export const TiptapRenderer = ({ message, user, isScrolling = false, isTruncated HTMLAttributes: { class: 'rt-Text text-base' } + }, + blockquote: { + HTMLAttributes: { + class: 'pl-4 border-l-4 border-gray-500' + } + }, + code: { + HTMLAttributes: { + class: 'pt-0.5 px-1 pb-px bg-[var(--gray-a3)] dark:bg-[#0d0d0d] text-[var(--ruby-a11)] dark-[var(--accent-a3)] text text-xs font-mono rounded border border-gray-4 dark:border-gray-6' + } } }), Highlight.configure({ @@ -75,7 +83,6 @@ export const TiptapRenderer = ({ message, user, isScrolling = false, isTruncated CodeBlockLowlight.configure({ lowlight }), - CustomBlockquote, CustomBold, CustomUserMention, CustomLink, diff --git a/mobile/src/pages/auth/LoginWithEmail.tsx b/mobile/src/pages/auth/LoginWithEmail.tsx index 1bcc5d1e3..cc9083081 100644 --- a/mobile/src/pages/auth/LoginWithEmail.tsx +++ b/mobile/src/pages/auth/LoginWithEmail.tsx @@ -1,14 +1,14 @@ import { useState } from "react"; import { useFrappePostCall } from "frappe-react-sdk"; -import { IonSpinner } from '@ionic/react' -import { SuccessCallout, CalloutObject, ErrorCallout } from '@/components/common/Callouts' +import { SuccessCallout, CalloutObject } from '@/components/common/Callouts' import { useForm } from 'react-hook-form' import { LoginInputs } from "@/types/Auth/Login"; import { ActiveScreenProps } from "@/components/layout/AuthContainer"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { isEmailValid } from "@/utils/validations/validations"; -import { Button, Link } from "@radix-ui/themes"; +import { Button } from "@radix-ui/themes"; +import { ErrorBanner } from "@/components/layout"; export const LoginWithEmail = (props: ActiveScreenProps) => { @@ -32,7 +32,7 @@ export const LoginWithEmail = (props: ActiveScreenProps) => { state: true, message: "Login Link sent on Email", }); - }).catch((err)=>{ + }).catch((err) => { setCallout(null) }) } @@ -42,7 +42,7 @@ export const LoginWithEmail = (props: ActiveScreenProps) => {
    - {error && } + {error && } {callout && }
    diff --git a/mobile/src/pages/profile/Profile.tsx b/mobile/src/pages/profile/Profile.tsx index 32bbf27ed..f7f1ddbdf 100644 --- a/mobile/src/pages/profile/Profile.tsx +++ b/mobile/src/pages/profile/Profile.tsx @@ -62,7 +62,7 @@ export const Profile = () => {
    - raven v1.5.1 + raven v1.5.2
    Made by The Commit Company
    diff --git a/mobile/src/types/RavenMessaging/RavenMessage.ts b/mobile/src/types/RavenMessaging/RavenMessage.ts index a0f2c7e3c..1fc4cbdde 100644 --- a/mobile/src/types/RavenMessaging/RavenMessage.ts +++ b/mobile/src/types/RavenMessaging/RavenMessage.ts @@ -26,7 +26,7 @@ export interface RavenMessage{ /** Replied Message Details : JSON */ replied_message_details?: any /** Message Type : Select */ - message_type?: "Text" | "Image" | "File" + message_type?: "Text" | "Image" | "File" | "Poll" /** Content : Long Text */ content?: string /** File : Attach */ @@ -49,8 +49,12 @@ export interface RavenMessage{ is_edited?: 0 | 1 /** Mentions : Table - Raven Mention */ mentions?: RavenMention[] + /** Poll ID : Link - Raven Poll */ + poll_id?: string /** Is Bot Message : Check */ is_bot_message?: 0 | 1 /** Bot : Link - Raven User */ bot?: string + /** Hide link preview : Check */ + hide_link_preview?: 0 | 1 } \ No newline at end of file diff --git a/package.json b/package.json index 47db4c594..af81f9675 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "raven", - "version": "1.5.1", + "version": "1.5.2", "description": "Messaging Application", "main": "index.js", "scripts": { diff --git a/pyproject.toml b/pyproject.toml index 7e3450da2..a55a07050 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ requires-python = ">=3.10" readme = "README.md" dynamic = ["version"] dependencies = [ - "linkpreview", + "linkpreview~=0.9.0" ] [build-system] diff --git a/raven-app/package.json b/raven-app/package.json index 73aa33f9b..a3040aba9 100644 --- a/raven-app/package.json +++ b/raven-app/package.json @@ -1,7 +1,7 @@ { "name": "raven-app", "private": true, - "version": "1.5.1", + "version": "1.5.2", "type": "module", "scripts": { "dev": "vite", @@ -10,20 +10,19 @@ "copy-html-entry": "cp ../raven/public/raven/index.html ../raven/www/raven.html" }, "dependencies": { - "@radix-ui/react-toast": "^1.1.5", "@radix-ui/themes": "^2.0.3", - "@tiptap/extension-code-block-lowlight": "^2.2.2", - "@tiptap/extension-highlight": "^2.2.2", - "@tiptap/extension-image": "^2.2.4", - "@tiptap/extension-link": "^2.2.2", - "@tiptap/extension-mention": "^2.2.2", - "@tiptap/extension-placeholder": "^2.2.2", - "@tiptap/extension-typography": "^2.2.2", - "@tiptap/extension-underline": "^2.2.2", - "@tiptap/pm": "^2.2.2", - "@tiptap/react": "^2.2.2", - "@tiptap/starter-kit": "^2.2.2", - "@tiptap/suggestion": "^2.2.2", + "@tiptap/extension-code-block-lowlight": "^2.3", + "@tiptap/extension-highlight": "^2.3", + "@tiptap/extension-image": "^2.3", + "@tiptap/extension-link": "^2.3", + "@tiptap/extension-mention": "^2.3", + "@tiptap/extension-placeholder": "^2.3", + "@tiptap/extension-typography": "^2.3", + "@tiptap/extension-underline": "^2.3", + "@tiptap/pm": "^2.3", + "@tiptap/react": "^2.3", + "@tiptap/starter-kit": "^2.3", + "@tiptap/suggestion": "^2.3", "@vitejs/plugin-react": "^4.2.1", "autoprefixer": "^10.4.17", "cal-sans": "^1.0.1", @@ -32,7 +31,7 @@ "cva": "npm:class-variance-authority", "downshift": "^8.3.1", "emoji-picker-element": "^1.21.0", - "frappe-react-sdk": "^1.5.1", + "frappe-react-sdk": "^1.6.0", "highlight.js": "^11.9.0", "html-react-parser": "^5.1.8", "js-cookie": "^3.0.5", @@ -46,6 +45,7 @@ "react-idle-timer": "^5.7.2", "react-intersection-observer": "^9.8.1", "react-router-dom": "^6.22.0", + "sonner": "^1.4.41", "tailwindcss": "^3.4.1", "tailwindcss-animate": "^1.0.7", "tippy.js": "^6.3.7", diff --git a/raven-app/src/App.tsx b/raven-app/src/App.tsx index cdab4d829..2efa8e255 100644 --- a/raven-app/src/App.tsx +++ b/raven-app/src/App.tsx @@ -6,7 +6,7 @@ import { UserProvider } from './utils/auth/UserProvider' import { ChannelRedirect } from './utils/channel/ChannelRedirect' import "cal-sans"; import { ThemeProvider } from './ThemeProvider' -import { Toaster } from './components/common/Toast/Toaster' +import { Toaster } from 'sonner' import { FullPageLoader } from './components/layout/Loaders' import { useStickyState } from './hooks/useStickyState' @@ -17,7 +17,7 @@ const router = createBrowserRouter( import('@/pages/auth/Login')} /> import('@/pages/auth/LoginWithEmail')} /> import('@/pages/auth/SignUp')} /> - import('@/pages/auth/ForgotPassword')} /> + import('@/pages/auth/ForgotPassword')} /> }> }> } > @@ -70,6 +70,7 @@ function App() { siteName={getSiteName()} > + } /> - diff --git a/raven-app/src/components/common/Callouts/ErrorCallouts.tsx b/raven-app/src/components/common/Callouts/ErrorCallouts.tsx index 29651888f..67f3a93f0 100644 --- a/raven-app/src/components/common/Callouts/ErrorCallouts.tsx +++ b/raven-app/src/components/common/Callouts/ErrorCallouts.tsx @@ -3,10 +3,10 @@ import { FiAlertTriangle } from "react-icons/fi" import { CustomCallout } from "./CustomCallout" -export const ErrorCallout = ({ children,...props }: PropsWithChildren<{message?: string}>) => { +export const ErrorCallout = ({ children, ...props }: PropsWithChildren<{ message?: string }>) => { return (} - textChildren = { props.message } - />) + rootProps={{ color: "red" }} + iconChildren={} + textChildren={children || props.message || "An error occurred"} + />) } \ No newline at end of file diff --git a/raven-app/src/components/common/EmojiPicker/EmojiPicker.tsx b/raven-app/src/components/common/EmojiPicker/EmojiPicker.tsx index 280963aff..c3ccc8983 100644 --- a/raven-app/src/components/common/EmojiPicker/EmojiPicker.tsx +++ b/raven-app/src/components/common/EmojiPicker/EmojiPicker.tsx @@ -2,6 +2,9 @@ import { createElement, useEffect, useRef } from "react" import 'emoji-picker-element' import './emojiPicker.styles.css' import { useTheme } from "@/ThemeProvider" +import { Database } from "emoji-picker-element"; + +export const emojiDatabase = new Database(); const EmojiPicker = ({ onSelect }: { onSelect: (emoji: string) => void }) => { @@ -10,13 +13,14 @@ const EmojiPicker = ({ onSelect }: { onSelect: (emoji: string) => void }) => { useEffect(() => { const handler = (event: any) => { + emojiDatabase.incrementFavoriteEmojiCount(event.detail.unicode) onSelect(event.detail.unicode) } ref.current?.addEventListener('emoji-click', handler) ref.current.skinToneEmoji = '👍' const style = document.createElement('style'); - style.textContent = `.picker { border-radius: var(--radius-4); box-shadow: var(--shadow-6); } input.search{ color: ${appearance === 'light' ? '#020617' : '#f1f5f9' } }` + style.textContent = `.picker { border-radius: var(--radius-4); box-shadow: var(--shadow-6); } input.search{ color: ${appearance === 'light' ? '#020617' : '#f1f5f9'} }` ref.current.shadowRoot.appendChild(style); return () => { diff --git a/raven-app/src/components/common/GIFPicker/GIFFeaturedResults.tsx b/raven-app/src/components/common/GIFPicker/GIFFeaturedResults.tsx index af9f424fa..6d9ff2fe6 100644 --- a/raven-app/src/components/common/GIFPicker/GIFFeaturedResults.tsx +++ b/raven-app/src/components/common/GIFPicker/GIFFeaturedResults.tsx @@ -1,28 +1,66 @@ -import { useSWR } from "frappe-react-sdk" import { TENOR_API_KEY, TENOR_CLIENT_KEY, TENOR_FEATURED_API_ENDPOINT_BASE } from "./GIFPicker" import { GIFGallerySkeleton } from "./GIFGallerySkeleton" +import { useMemo } from "react"; +import { Button } from "@radix-ui/themes"; +import { useSWRInfinite } from "frappe-react-sdk"; + export interface Props { onSelect: (gif: Result) => void } -const fetcher = (url: string) => fetch(url).then(res => res.json()) +const fetcher = async (url: string) => { + const response = await fetch(url); + const data = await response.json(); + return data; +}; export const GIFFeaturedResults = ({ onSelect }: Props) => { - const { data: GIFS, isLoading } = useSWR(`${TENOR_FEATURED_API_ENDPOINT_BASE}?&key=${TENOR_API_KEY}&client_key=${TENOR_CLIENT_KEY}`, fetcher) + const { data, size, setSize, isLoading } = useSWRInfinite( + (index: any, previousPageData: any) => { + // reached the end + if (previousPageData && previousPageData.next === null) return null; + + // first page, we don't have `previousPageData` + if (index === 0) return `${TENOR_FEATURED_API_ENDPOINT_BASE}?&key=${TENOR_API_KEY}&client_key=${TENOR_CLIENT_KEY}`; + + // add the cursor to the API endpoint + return `${TENOR_FEATURED_API_ENDPOINT_BASE}?&key=${TENOR_API_KEY}&client_key=${TENOR_CLIENT_KEY}&pos=${previousPageData.next}`; + }, + fetcher, + { + initialSize: 1, + parallel: false, + } + ); + + + const GIFS = useMemo(() => { + let gifs: TenorResultObject = { results: [], next: "" }; + // data is an array of objects. Each object is a array called 'results' & cursor called 'next' + // We need to merge all the 'results' array into one array, and get the last 'next' cursor + return data?.reduce((acc, val) => { + gifs.results = [...acc.results, ...val.results]; + gifs.next = val.next; + return gifs; + }, gifs); + }, [data]); return (
    {isLoading ? :
    - {GIFS && GIFS.results.map((gif, index) => ( + {GIFS && GIFS?.results?.map((gif: Result, index: number) => (
    onSelect(gif)}> {gif.title}
    ))}
    } + {GIFS?.next &&
    + +
    }
    ) } \ No newline at end of file diff --git a/raven-app/src/components/common/GIFPicker/GIFSearchResults.tsx b/raven-app/src/components/common/GIFPicker/GIFSearchResults.tsx index be6876ea3..755863ed8 100644 --- a/raven-app/src/components/common/GIFPicker/GIFSearchResults.tsx +++ b/raven-app/src/components/common/GIFPicker/GIFSearchResults.tsx @@ -1,29 +1,65 @@ -import { useSWR } from "frappe-react-sdk" import { TENOR_API_KEY, TENOR_CLIENT_KEY, TENOR_SEARCH_API_ENDPOINT_BASE } from "./GIFPicker" import { GIFGallerySkeleton } from "./GIFGallerySkeleton" +import { useMemo } from "react"; +import { Button } from "@radix-ui/themes"; +import { useSWRInfinite } from "frappe-react-sdk"; export interface Props { query: string onSelect: (gif: Result) => void } -const fetcher = (url: string) => fetch(url).then(res => res.json()) +const fetcher = async (url: string) => { + const response = await fetch(url); + const data = await response.json(); + return data; +}; export const GIFSearchResults = ({ query, onSelect }: Props) => { - const { data: GIFS, isLoading } = useSWR(`${TENOR_SEARCH_API_ENDPOINT_BASE}?q=${query}&key=${TENOR_API_KEY}&client_key=${TENOR_CLIENT_KEY}&limit=10`, fetcher) + const { data, size, setSize, isLoading } = useSWRInfinite( + (index: any, previousPageData: any) => { + // reached the end + if (previousPageData && previousPageData.next === null) return null; + + // first page, we don't have `previousPageData` + if (index === 0) return `${TENOR_SEARCH_API_ENDPOINT_BASE}?q=${query}&key=${TENOR_API_KEY}&client_key=${TENOR_CLIENT_KEY}`; + + // add the cursor to the API endpoint + return `${TENOR_SEARCH_API_ENDPOINT_BASE}?q=${query}&key=${TENOR_API_KEY}&client_key=${TENOR_CLIENT_KEY}&pos=${previousPageData.next}`; + }, + fetcher, + { + initialSize: 1, + parallel: false, + } + ); + + const GIFS = useMemo(() => { + let gifs: TenorResultObject = { results: [], next: "" }; + // data is an array of objects. Each object is a array called 'results' & cursor called 'next' + // We need to merge all the 'results' array into one array, and get the last 'next' cursor + return data?.reduce((acc, val) => { + gifs.results = [...acc.results, ...val.results]; + gifs.next = val.next; + return gifs; + }, gifs); + }, [data]); return (
    {isLoading ? :
    - {GIFS && GIFS.results.map((gif, index) => ( + {GIFS && GIFS?.results?.map((gif: Result, index: number) => (
    onSelect(gif)}> {gif.title}
    ))}
    } + {GIFS?.next &&
    + +
    }
    ) } \ No newline at end of file diff --git a/raven-app/src/components/common/Toast/Toast.tsx b/raven-app/src/components/common/Toast/Toast.tsx deleted file mode 100644 index 939f555c9..000000000 --- a/raven-app/src/components/common/Toast/Toast.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import * as React from "react" -import * as ToastPrimitives from "@radix-ui/react-toast" -import { cva, type VariantProps } from "cva" -import { BiX } from "react-icons/bi" -import { clsx } from "clsx" - -const ToastProvider = ToastPrimitives.Provider - -const ToastViewport = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -ToastViewport.displayName = ToastPrimitives.Viewport.displayName - -const toastVariants = cva( - "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", - { - variants: { - variant: { - default: "border bg-background text-gray-12", - destructive: - "group destructive border-red-9 bg-red-9 text-red-9-contrast", - success: "success group border-green-9 bg-green-9 text-green-9-contrast", - accent: "accent group border-accent-9 bg-accent-9 text-accent-9-contrast", - }, - }, - defaultVariants: { - variant: "default", - }, - } -) - -const Toast = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef & - VariantProps ->(({ className, variant, ...props }, ref) => { - return ( - - ) -}) -Toast.displayName = ToastPrimitives.Root.displayName -//TODO: Improve design of toast action button -const ToastAction = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -ToastAction.displayName = ToastPrimitives.Action.displayName - -const ToastClose = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - -)) -ToastClose.displayName = ToastPrimitives.Close.displayName - -const ToastTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -ToastTitle.displayName = ToastPrimitives.Title.displayName - -const ToastDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -ToastDescription.displayName = ToastPrimitives.Description.displayName - -type ToastProps = React.ComponentPropsWithoutRef - -type ToastActionElement = React.ReactElement - -export { - type ToastProps, - type ToastActionElement, - ToastProvider, - ToastViewport, - Toast, - ToastTitle, - ToastDescription, - ToastClose, - ToastAction, -} diff --git a/raven-app/src/components/common/Toast/Toaster.tsx b/raven-app/src/components/common/Toast/Toaster.tsx deleted file mode 100644 index a8a605fb9..000000000 --- a/raven-app/src/components/common/Toast/Toaster.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { - Toast, - ToastClose, - ToastDescription, - ToastProvider, - ToastTitle, - ToastViewport, -} from "./Toast" -import { useToast } from "@/hooks/useToast" - -export function Toaster() { - const { toasts } = useToast() - - return ( - - {toasts.map(function ({ id, title, description, action, ...props }) { - return ( - -
    - {title && {title}} - {description && ( - {description} - )} -
    - {action} - -
    - ) - })} - -
    - ) -} \ No newline at end of file diff --git a/raven-app/src/components/feature/channel-details/edit-channel-description/EditChannelDescriptionModal.tsx b/raven-app/src/components/feature/channel-details/edit-channel-description/EditChannelDescriptionModal.tsx index 81c100b17..d64ddd9b0 100644 --- a/raven-app/src/components/feature/channel-details/edit-channel-description/EditChannelDescriptionModal.tsx +++ b/raven-app/src/components/feature/channel-details/edit-channel-description/EditChannelDescriptionModal.tsx @@ -5,8 +5,7 @@ import { ChannelListItem } from "@/utils/channel/ChannelListProvider" import { Box, Dialog, Flex, Button, TextArea, Text } from "@radix-ui/themes" import { Loader } from "@/components/common/Loader" import { ErrorText, Label } from "@/components/common/Form" -import { useToast } from "@/hooks/useToast" -import { ToastAction } from "@/components/common/Toast/Toast" +import { toast } from 'sonner' interface RenameChannelForm { channel_description: string @@ -26,23 +25,13 @@ export const EditChannelDescriptionModalContent = ({ channelData, onClose }: Ren }) const { register, handleSubmit, formState: { errors } } = methods const { updateDoc, loading: updatingDoc, error } = useFrappeUpdateDoc() - const { toast } = useToast() const onSubmit = (data: RenameChannelForm) => { updateDoc("Raven Channel", channelData?.name ?? null, { channel_description: data.channel_description }).then(() => { - toast({ - title: "Channel description updated", - variant: 'success', - }) + toast.success("Channel description updated") onClose() - }).catch((err) => { - toast({ - title: "Error updating channel description", - description: err.message, - variant: "destructive", - }) }) } diff --git a/raven-app/src/components/feature/channel-details/leave-channel/LeaveChannelModal.tsx b/raven-app/src/components/feature/channel-details/leave-channel/LeaveChannelModal.tsx index 86f1c3bf6..7d167ab83 100644 --- a/raven-app/src/components/feature/channel-details/leave-channel/LeaveChannelModal.tsx +++ b/raven-app/src/components/feature/channel-details/leave-channel/LeaveChannelModal.tsx @@ -1,5 +1,5 @@ import { useFrappeDeleteDoc, useFrappeGetCall } from 'frappe-react-sdk' -import { useContext, useRef } from 'react' +import { useContext } from 'react' import { useNavigate } from 'react-router-dom' import { UserContext } from '../../../../utils/auth/UserProvider' import { ErrorBanner } from '../../../layout/AlertBanner' @@ -7,7 +7,8 @@ import { ChannelListContext, ChannelListContextType, ChannelListItem } from '@/u import { ChannelIcon } from '@/utils/layout/channelIcon' import { AlertDialog, Button, Flex, Text } from '@radix-ui/themes' import { Loader } from '@/components/common/Loader' -import { useToast } from '@/hooks/useToast' +import { toast } from 'sonner' +import { getErrorMessage } from '@/components/layout/AlertBanner/ErrorBanner' interface LeaveChannelModalProps { onClose: () => void, @@ -19,7 +20,6 @@ export const LeaveChannelModal = ({ onClose, channelData, closeDetailsModal }: L const { currentUser } = useContext(UserContext) const { deleteDoc, loading: deletingDoc, error } = useFrappeDeleteDoc() - const { toast } = useToast() const navigate = useNavigate() const { data: channelMember } = useFrappeGetCall<{ message: { name: string } }>('frappe.client.get_value', { @@ -33,19 +33,15 @@ export const LeaveChannelModal = ({ onClose, channelData, closeDetailsModal }: L const { mutate } = useContext(ChannelListContext) as ChannelListContextType const onSubmit = async () => { - return deleteDoc('Raven Channel Member', channelMember?.message.name).then(() => { - toast({ - title: 'You have left the channel', - }) + return deleteDoc('Raven Chnnel Member', channelMember?.message.name).then(() => { + toast('You have left the channel') onClose() mutate() navigate('../general') closeDetailsModal() }).catch((e) => { - toast({ - title: 'Error: Could leave channel.', - variant: 'destructive', - description: `${e.message}` + toast.error('Could not leave channel', { + description: getErrorMessage(e) }) }) } diff --git a/raven-app/src/components/feature/channel-details/rename-channel/ChannelRenameModal.tsx b/raven-app/src/components/feature/channel-details/rename-channel/ChannelRenameModal.tsx index 616b34fca..6f85da6da 100644 --- a/raven-app/src/components/feature/channel-details/rename-channel/ChannelRenameModal.tsx +++ b/raven-app/src/components/feature/channel-details/rename-channel/ChannelRenameModal.tsx @@ -7,7 +7,7 @@ import { ChannelListItem } from "@/utils/channel/ChannelListProvider" import { Box, Dialog, Flex, Text, TextField, Button } from "@radix-ui/themes" import { ErrorText, Label } from "@/components/common/Form" import { Loader } from "@/components/common/Loader" -import { useToast } from "@/hooks/useToast" +import { toast } from "sonner" interface RenameChannelForm { channel_name: string @@ -30,17 +30,12 @@ export const RenameChannelModalContent = ({ channelID, channelName, type, onClos }) const { control, handleSubmit, setValue, formState: { errors } } = methods const { updateDoc, loading: updatingDoc, error } = useFrappeUpdateDoc() - const { toast } = useToast() const onSubmit = async (data: RenameChannelForm) => { return updateDoc("Raven Channel", channelID ?? null, { channel_name: data.channel_name }).then(() => { - toast({ - title: "Channel name updated", - variant: 'success', - duration: 800 - }) + toast.success("Channel name updated") onClose() }) } diff --git a/raven-app/src/components/feature/channel-member-details/add-members/AddChannelMemberModal.tsx b/raven-app/src/components/feature/channel-member-details/add-members/AddChannelMemberModal.tsx index ea0ac95d6..ad787f383 100644 --- a/raven-app/src/components/feature/channel-member-details/add-members/AddChannelMemberModal.tsx +++ b/raven-app/src/components/feature/channel-member-details/add-members/AddChannelMemberModal.tsx @@ -9,7 +9,7 @@ import { ChannelMembers } from '@/utils/channel/ChannelMembersProvider' import { Suspense, lazy } from 'react' import { UserFields } from '@/utils/users/UserListProvider' import { ErrorText } from '@/components/common/Form' -import { useToast } from '@/hooks/useToast' +import { toast } from 'sonner' const AddMembersDropdown = lazy(() => import('../../select-member/AddMembersDropdown')) interface AddChannelMemberForm { add_members: UserFields[] | null @@ -34,12 +34,11 @@ export const AddChannelMembersModalContent = ({ channelID, channel_name, onClose }) const { handleSubmit, control } = methods - const { toast } = useToast() const onSubmit = (data: AddChannelMemberForm) => { if (data.add_members && data.add_members.length > 0) { const promises = data.add_members.map(async (member) => { - return createDoc('Raven Channel Member', { + return createDoc('Raven Channel Mmber', { channel_id: channelID, user_id: member.name }) @@ -47,11 +46,7 @@ export const AddChannelMembersModalContent = ({ channelID, channel_name, onClose Promise.all(promises) .then(() => { - toast({ - title: 'Members added successfully', - variant: 'success', - duration: 1000 - }) + toast.success("Members added") updateMembers() onClose() }) diff --git a/raven-app/src/components/feature/channel-member-details/remove-members/RemoveChannelMemberModal.tsx b/raven-app/src/components/feature/channel-member-details/remove-members/RemoveChannelMemberModal.tsx index 0e4b0c809..8fa02e526 100644 --- a/raven-app/src/components/feature/channel-member-details/remove-members/RemoveChannelMemberModal.tsx +++ b/raven-app/src/components/feature/channel-member-details/remove-members/RemoveChannelMemberModal.tsx @@ -5,7 +5,7 @@ import { ChannelMembers } from '@/utils/channel/ChannelMembersProvider' import { ChannelIcon } from '@/utils/layout/channelIcon' import { AlertDialog, Button, Flex, Text } from '@radix-ui/themes' import { Loader } from '@/components/common/Loader' -import { useToast } from '@/hooks/useToast' +import { toast } from 'sonner' interface RemoveChannelMemberModalProps { onClose: (refresh?: boolean) => void, @@ -18,7 +18,6 @@ interface RemoveChannelMemberModalProps { export const RemoveChannelMemberModal = ({ onClose, user_id, channelData, channelMembers, updateMembers }: RemoveChannelMemberModalProps) => { const { deleteDoc, error, loading: deletingDoc } = useFrappeDeleteDoc() - const { toast } = useToast() const { data: member, error: errorFetchingChannelMember } = useFrappeGetCall<{ message: { name: string } }>('frappe.client.get_value', { doctype: "Raven Channel Member", @@ -30,11 +29,7 @@ export const RemoveChannelMemberModal = ({ onClose, user_id, channelData, channe const onSubmit = async () => { return deleteDoc('Raven Channel Member', member?.message.name).then(() => { - toast({ - title: 'Member removed successfully', - variant: 'success', - duration: 1000 - }) + toast.success(`Removed`) updateMembers() onClose() }) diff --git a/raven-app/src/components/feature/channel-settings/archive-channel/ArchiveChannelModal.tsx b/raven-app/src/components/feature/channel-settings/archive-channel/ArchiveChannelModal.tsx index aa5b1baf5..193697c06 100644 --- a/raven-app/src/components/feature/channel-settings/archive-channel/ArchiveChannelModal.tsx +++ b/raven-app/src/components/feature/channel-settings/archive-channel/ArchiveChannelModal.tsx @@ -4,7 +4,7 @@ import { useNavigate } from 'react-router-dom' import { ChannelListItem } from '@/utils/channel/ChannelListProvider' import { AlertDialog, Flex, Text, Button } from '@radix-ui/themes' import { Loader } from '@/components/common/Loader' -import { useToast } from '@/hooks/useToast' +import { toast } from 'sonner' interface ArchiveChannelModalProps { onClose: () => void, @@ -14,7 +14,6 @@ interface ArchiveChannelModalProps { export const ArchiveChannelModal = ({ onClose, onCloseViewDetails, channelData }: ArchiveChannelModalProps) => { - const { toast } = useToast() const { updateDoc, loading: archivingDoc, error } = useFrappeUpdateDoc() const navigate = useNavigate() @@ -25,11 +24,7 @@ export const ArchiveChannelModal = ({ onClose, onCloseViewDetails, channelData } onClose() onCloseViewDetails() navigate('/channel/general') - toast({ - title: "Channel archived", - variant: "success", - duration: 1000, - }) + toast('Channel archived') }) } diff --git a/raven-app/src/components/feature/channel-settings/change-channel-type/ChangeChannelTypeModal.tsx b/raven-app/src/components/feature/channel-settings/change-channel-type/ChangeChannelTypeModal.tsx index 06d0e9895..1d01588c0 100644 --- a/raven-app/src/components/feature/channel-settings/change-channel-type/ChangeChannelTypeModal.tsx +++ b/raven-app/src/components/feature/channel-settings/change-channel-type/ChangeChannelTypeModal.tsx @@ -3,7 +3,7 @@ import { ErrorBanner } from '../../../layout/AlertBanner' import { ChannelListItem } from '@/utils/channel/ChannelListProvider' import { Button, Dialog, Flex, Text } from '@radix-ui/themes' import { Loader } from '@/components/common/Loader' -import { useToast } from '@/hooks/useToast' +import { toast } from 'sonner' interface ChangeChannelTypeModalProps { onClose: () => void @@ -13,7 +13,6 @@ interface ChangeChannelTypeModalProps { export const ChangeChannelTypeModal = ({ onClose, channelData, newChannelType }: ChangeChannelTypeModalProps) => { - const { toast } = useToast() const { mutate } = useSWRConfig() const { updateDoc, loading: updatingDoc, error } = useFrappeUpdateDoc() @@ -22,11 +21,7 @@ export const ChangeChannelTypeModal = ({ onClose, channelData, newChannelType }: type: newChannelType }).then(() => { mutate(`raven.api.chat.get_channel_members:${channelData.name}`) - toast({ - title: "Channel type updated", - variant: "success", - duration: 1000, - }) + toast.success("Channel changed to " + newChannelType.toLocaleLowerCase()) onClose() }) } diff --git a/raven-app/src/components/feature/channel-settings/delete-channel/DeleteChannelModal.tsx b/raven-app/src/components/feature/channel-settings/delete-channel/DeleteChannelModal.tsx index 7f57c7d2b..3ff9f69ce 100644 --- a/raven-app/src/components/feature/channel-settings/delete-channel/DeleteChannelModal.tsx +++ b/raven-app/src/components/feature/channel-settings/delete-channel/DeleteChannelModal.tsx @@ -5,8 +5,8 @@ import { useFrappeDeleteDoc } from 'frappe-react-sdk' import { ChannelListItem } from '@/utils/channel/ChannelListProvider' import { AlertDialog, Button, Callout, Checkbox, Flex, Text } from '@radix-ui/themes' import { Loader } from '@/components/common/Loader' -import { useToast } from '@/hooks/useToast' import { FiAlertTriangle } from 'react-icons/fi' +import { toast } from 'sonner' type DeleteChannelModalProps = { onClose: () => void, @@ -23,7 +23,6 @@ export const DeleteChannelModal = ({ onClose, onCloseParent, channelData }: Dele reset() } - const { toast } = useToast() const navigate = useNavigate() const onSubmit = () => { @@ -34,10 +33,7 @@ export const DeleteChannelModal = ({ onClose, onCloseParent, channelData }: Dele onCloseParent() localStorage.removeItem('ravenLastChannel') navigate('/channel') - toast({ - title: `Channel ${channelData.name} deleted`, - variant: 'success', - }) + toast(`Channel ${channelData.name} deleted.`) }) } } diff --git a/raven-app/src/components/feature/channels/ChannelList.tsx b/raven-app/src/components/feature/channels/ChannelList.tsx index 59dd2fba4..c16436fe7 100644 --- a/raven-app/src/components/feature/channels/ChannelList.tsx +++ b/raven-app/src/components/feature/channels/ChannelList.tsx @@ -6,12 +6,13 @@ import { ChannelListContext, ChannelListContextType, ChannelListItem, UnreadCoun import { ChannelIcon } from "@/utils/layout/channelIcon" import { Box, Flex, Text } from "@radix-ui/themes" import { useLocation, useParams } from "react-router-dom" +import { useStickyState } from "@/hooks/useStickyState" export const ChannelList = ({ unread_count }: { unread_count?: UnreadCountData }) => { const { channels, mutate } = useContext(ChannelListContext) as ChannelListContextType - const [showData, setShowData] = useState(true) + const [showData, setShowData] = useStickyState(true, 'expandChannelList') const toggle = () => setShowData(d => !d) @@ -20,7 +21,7 @@ export const ChannelList = ({ unread_count }: { unread_count?: UnreadCountData } return ( - + Channels diff --git a/raven-app/src/components/feature/channels/CreateChannelModal.tsx b/raven-app/src/components/feature/channels/CreateChannelModal.tsx index aba3ce2c6..feb59196f 100644 --- a/raven-app/src/components/feature/channels/CreateChannelModal.tsx +++ b/raven-app/src/components/feature/channels/CreateChannelModal.tsx @@ -9,7 +9,7 @@ import { BiPlus } from 'react-icons/bi' import { ErrorText, HelperText, Label } from '@/components/common/Form' import { Loader } from '@/components/common/Loader' import { DIALOG_CONTENT_CLASS } from '@/utils/layout/dialog' -import { useToast } from '@/hooks/useToast' +import { toast } from 'sonner' interface ChannelCreationForm { channel_name: string, @@ -53,19 +53,12 @@ export const CreateChannelButton = ({ updateChannelList }: { updateChannelList: reset() } - - const { toast } = useToast() - const channelType = watch('type') const onSubmit = (data: ChannelCreationForm) => { createDoc('Raven Channel', data).then(result => { if (result) { - toast({ - title: "Channel Created", - variant: "success", - duration: 1000, - }) + toast.success('Channel created') onClose(result.name) } }) diff --git a/raven-app/src/components/feature/chat/ChatInput/EmojiList.tsx b/raven-app/src/components/feature/chat/ChatInput/EmojiList.tsx new file mode 100644 index 000000000..797a42a30 --- /dev/null +++ b/raven-app/src/components/feature/chat/ChatInput/EmojiList.tsx @@ -0,0 +1,108 @@ +import { Flex, Text, Theme } from '@radix-ui/themes' +import { ReactRendererOptions } from '@tiptap/react' +import { clsx } from 'clsx' +import { NativeEmoji } from 'emoji-picker-element/shared' +import { + forwardRef, useEffect, useImperativeHandle, + useRef, + useState, +} from 'react' + +export default forwardRef((props: ReactRendererOptions['props'], ref) => { + + const [selectedIndex, setSelectedIndex] = useState(0) + + const selectItem = (index: number) => { + const item = props?.items[index] + + if (item) { + props.command(item) + } + } + + const upHandler = () => { + setSelectedIndex((selectedIndex + props?.items.length - 1) % props?.items.length) + } + + const downHandler = () => { + setSelectedIndex((selectedIndex + 1) % props?.items.length) + } + + const enterHandler = () => { + selectItem(selectedIndex) + } + + useEffect(() => setSelectedIndex(0), [props?.items]) + + useImperativeHandle(ref, () => ({ + onKeyDown: ({ event }: { event: KeyboardEvent }) => { + if (event.key === 'ArrowUp') { + upHandler() + return true + } + + if (event.key === 'ArrowDown') { + downHandler() + return true + } + + if (event.key === 'Enter') { + enterHandler() + return true + } + + return false + }, + })) + + return ( + + + {props?.items.length + ? props.items.map((item: NativeEmoji, index: number) => ( + + )) + : null + } + + + ) +}) + +const MentionItem = ({ item, index, selectItem, selectedIndex, itemsLength }: { itemsLength: number, selectedIndex: number, index: number, item: NativeEmoji, selectItem: (index: number) => void }) => { + + const ref = useRef(null) + + useEffect(() => { + if (index === selectedIndex) ref.current?.scrollIntoView({ block: 'nearest' }) + }, [selectedIndex, index]) + + + return selectItem(index)} + > + {item.unicode} {item.shortcodes?.[0] ?? item.annotation} + +} \ No newline at end of file diff --git a/raven-app/src/components/feature/chat/ChatInput/EmojiSuggestion.tsx b/raven-app/src/components/feature/chat/ChatInput/EmojiSuggestion.tsx new file mode 100644 index 000000000..20a5a1c39 --- /dev/null +++ b/raven-app/src/components/feature/chat/ChatInput/EmojiSuggestion.tsx @@ -0,0 +1,104 @@ +import { Node } from '@tiptap/core' +import Suggestion from '@tiptap/suggestion' +import { ReactRenderer } from '@tiptap/react' +import EmojiList from './EmojiList' +import tippy from 'tippy.js'; +import { NativeEmoji } from 'emoji-picker-element/shared'; +import { PluginKey } from '@tiptap/pm/state'; +import { emojiDatabase } from '@/components/common/EmojiPicker/EmojiPicker'; + +export const EmojiSuggestion = Node.create({ + name: 'emoji', + group: 'inline', + pluginKey: new PluginKey('emojiSuggestion'), + + inline: true, + + selectable: false, + + atom: true, + + addProseMirrorPlugins() { + return [ + Suggestion({ + editor: this.editor, + char: ':', + items: (query) => { + if (query.query.length !== 0) { + return emojiDatabase.getEmojiBySearchQuery(query.query.toLowerCase()).then((emojis) => { + return emojis.slice(0, 10) as NativeEmoji[] + }); + } else { + return emojiDatabase.getTopFavoriteEmoji(10) as Promise + } + + + return [] + }, + render: () => { + let component: any; + let popup: any; + + return { + onStart: props => { + component = new ReactRenderer(EmojiList, { + props, + editor: props.editor, + }) + + if (!props.clientRect) { + return + } + + popup = tippy('body' as any, { + getReferenceClientRect: props.clientRect as any, + appendTo: () => document.body, + content: component.element, + showOnCreate: true, + interactive: true, + trigger: 'manual', + placement: 'bottom-start', + }) + }, + + onUpdate(props) { + component.updateProps(props) + + if (!props.clientRect) { + return + } + + popup[0].setProps({ + getReferenceClientRect: props.clientRect, + }) + }, + + onKeyDown(props) { + if (props.event.key === 'Escape') { + popup[0].hide() + + return true + } + + return component.ref?.onKeyDown(props) + }, + + onExit() { + popup[0].destroy() + component.destroy() + }, + } + }, + command: ({ editor, range, props }) => { + // Replace the text from : to with the emoji node + editor.chain().focus().deleteRange(range).insertContent(props.unicode).run() + + emojiDatabase.incrementFavoriteEmojiCount(props.unicode) + + window.getSelection()?.collapseToEnd() + }, + }), + ] + }, + +}) \ No newline at end of file diff --git a/raven-app/src/components/feature/chat/ChatInput/TextFormattingMenu.tsx b/raven-app/src/components/feature/chat/ChatInput/TextFormattingMenu.tsx index 90eaac878..18ed29114 100644 --- a/raven-app/src/components/feature/chat/ChatInput/TextFormattingMenu.tsx +++ b/raven-app/src/components/feature/chat/ChatInput/TextFormattingMenu.tsx @@ -1,5 +1,5 @@ import { useCurrentEditor } from '@tiptap/react' -import { BiBold, BiCodeAlt, BiHighlight, BiItalic, BiLink, BiListOl, BiListUl, BiStrikethrough, BiUnderline, BiSolidQuoteAltRight } from 'react-icons/bi' +import { BiBold, BiCodeAlt, BiCodeBlock ,BiHighlight, BiItalic, BiListOl, BiListUl, BiStrikethrough, BiUnderline, BiSolidQuoteAltRight } from 'react-icons/bi' import { DEFAULT_BUTTON_STYLE, ICON_PROPS } from './ToolPanel' import { Box, Flex, IconButton, Separator, Tooltip } from '@radix-ui/themes' import { getKeyboardMetaKeyString } from '@/utils/layout/keyboardKey' @@ -76,11 +76,30 @@ export const TextFormattingMenu = () => { editor.chain().focus().toggleCodeBlock().run()} + onClick={() => editor.chain().focus().toggleCode().run()} aria-label='code' variant='ghost' size='1' title='Code' + className={editor.isActive('code') ? highlightBgColor : DEFAULT_BUTTON_STYLE} + disabled={ + !editor.can() + .chain() + .focus() + .toggleCode() + .run() + } + > + + + + + editor.chain().focus().toggleCodeBlock().run()} + aria-label='code block' + variant='ghost' + size='1' + title='Code Block' className={editor.isActive('codeBlock') ? highlightBgColor : DEFAULT_BUTTON_STYLE} disabled={ !editor.can() @@ -90,7 +109,7 @@ export const TextFormattingMenu = () => { .run() } > - + {/* */} diff --git a/raven-app/src/components/feature/chat/ChatInput/Tiptap.tsx b/raven-app/src/components/feature/chat/ChatInput/Tiptap.tsx index f6cd2c3d9..bd93b60b9 100644 --- a/raven-app/src/components/feature/chat/ChatInput/Tiptap.tsx +++ b/raven-app/src/components/feature/chat/ChatInput/Tiptap.tsx @@ -18,6 +18,7 @@ import { ToolPanel } from './ToolPanel' import { RightToolbarButtons } from './RightToolbarButtons' import { common, createLowlight } from 'lowlight' import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight' +import Code from '@tiptap/extension-code' import css from 'highlight.js/lib/languages/css' import js from 'highlight.js/lib/languages/javascript' import ts from 'highlight.js/lib/languages/typescript' @@ -29,6 +30,7 @@ import { Box } from '@radix-ui/themes' import { useSessionStickyState } from '@/hooks/useStickyState' import { Message } from '../../../../../../types/Messaging/Message' import Image from '@tiptap/extension-image' +import { EmojiSuggestion } from './EmojiSuggestion' const lowlight = createLowlight(common) lowlight.register('html', html) @@ -74,6 +76,7 @@ export const ChannelMention = Mention.extend({ pluginKey: new PluginKey('channelMention'), } }) + const Tiptap = ({ slotBefore, fileProps, onMessageSend, replyMessage, clearReplyMessage, placeholder = 'Type a message...', messageSending, sessionStorageKey = 'tiptap-editor', disableSessionStorage = false, defaultText = '' }: TiptapEditorProps) => { const { enabledUsers } = useContext(UserListContext) @@ -90,13 +93,9 @@ const Tiptap = ({ slotBefore, fileProps, onMessageSend, replyMessage, clearReply const isCodeBlockActive = this.editor.isActive('codeBlock'); const isListItemActive = this.editor.isActive('listItem'); - - // FIXME: This breaks sometimes when the key is not `userMention$` but a random number appended in front of it - //@ts-expect-error - const isSuggestionOpen = this.editor.state.userMention$?.active || this.editor.state.channelMention$?.active const hasContent = this.editor.getText().trim().length > 0 - if (isCodeBlockActive || isListItemActive || isSuggestionOpen) { + if (isCodeBlockActive || isListItemActive) { return false; } let html = '' @@ -167,20 +166,15 @@ const Tiptap = ({ slotBefore, fileProps, onMessageSend, replyMessage, clearReply } return false - } - - // 'Shift-Enter': () => { - // /** - // * currently we do not have an option to show a soft line break in the posts, so we overwrite - // * the behavior from tiptap with the default behavior on pressing enter - // */ - // return this.editor.commands.first(({ commands }) => [ - // () => commands.newlineInCode(), - // () => commands.createParagraphNear(), - // () => commands.liftEmptyBlock(), - // () => commands.splitBlock(), - // ]); - // }, + }, + 'Shift-Enter': () => { + return this.editor.commands.first(({ commands }) => [ + () => commands.newlineInCode(), + () => commands.createParagraphNear(), + () => commands.liftEmptyBlock(), + () => commands.splitBlock(), + ]); + }, }; }, addProseMirrorPlugins() { @@ -256,8 +250,43 @@ const Tiptap = ({ slotBefore, fileProps, onMessageSend, replyMessage, clearReply HTMLAttributes: { class: 'rt-Text text-sm' } + }, + code: { + HTMLAttributes: { + class: 'pt-0.5 px-1 pb-px bg-[var(--gray-a3)] dark:bg-[#0d0d0d] text-[var(--ruby-a11)] dark-[var(--accent-a3)] text text-xs font-mono rounded border border-gray-4 dark:border-gray-6' + } + }, + }), + Underline, + Highlight.configure({ + multicolor: true, + HTMLAttributes: { + class: 'bg-[var(--yellow-6)] dark:bg-[var(--yellow-11)] px-2 py-1' } }), + Link.extend({ inclusive: false }).configure({ + autolink: true, + protocols: ['mailto', 'https', 'http'], + }), + Placeholder.configure({ + // Pick a random placeholder from the list. + placeholder, + }), + CodeBlockLowlight.extend({ + addKeyboardShortcuts() { + return { + // this extends existing shortcuts instead of overwriting + ...this.parent?.(), + 'Mod-Shift-E': () => this.editor.commands.toggleCodeBlock(), + } + } + }).configure({ + lowlight + }), + Image.configure({ + inline: true, + }), + KeyboardHandler, UserMention.configure({ HTMLAttributes: { class: 'mention', @@ -398,27 +427,7 @@ const Tiptap = ({ slotBefore, fileProps, onMessageSend, replyMessage, clearReply } }), - Underline, - Highlight.configure({ - multicolor: true, - HTMLAttributes: { - class: 'bg-[var(--yellow-6)] dark:bg-[var(--yellow-11)] px-2 py-1' - } - }), - Link.configure({ - protocols: ['mailto', 'https', 'http'] - }), - Placeholder.configure({ - // Pick a random placeholder from the list. - placeholder, - }), - CodeBlockLowlight.configure({ - lowlight - }), - Image.configure({ - inline: true, - }), - KeyboardHandler + EmojiSuggestion ] const [content, setContent] = useSessionStickyState(defaultText, sessionStorageKey, disableSessionStorage) diff --git a/raven-app/src/components/feature/chat/ChatMessage/ActionModals/DeleteMessageModal.tsx b/raven-app/src/components/feature/chat/ChatMessage/ActionModals/DeleteMessageModal.tsx index 9783957e0..f18990555 100644 --- a/raven-app/src/components/feature/chat/ChatMessage/ActionModals/DeleteMessageModal.tsx +++ b/raven-app/src/components/feature/chat/ChatMessage/ActionModals/DeleteMessageModal.tsx @@ -2,9 +2,9 @@ import { useFrappeDeleteDoc } from "frappe-react-sdk" import { ErrorBanner } from "../../../../layout/AlertBanner" import { AlertDialog, Button, Callout, Flex, Text } from "@radix-ui/themes" import { Loader } from "@/components/common/Loader" -import { useToast } from "@/hooks/useToast" import { FiAlertTriangle } from "react-icons/fi" import { Message } from "../../../../../../../types/Messaging/Message" +import { toast } from "sonner" interface DeleteMessageModalProps { onClose: (refresh?: boolean) => void, @@ -14,16 +14,11 @@ interface DeleteMessageModalProps { export const DeleteMessageModal = ({ onClose, message }: DeleteMessageModalProps) => { const { deleteDoc, error, loading: deletingDoc } = useFrappeDeleteDoc() - const { toast } = useToast() const onSubmit = async () => { - return deleteDoc('Raven Message', message.name - ).then(() => { - toast({ - title: 'Message deleted', - duration: 1000, - variant: 'destructive' - }) + + return deleteDoc('Raven Message', message.name).then(() => { + toast('Message deleted') onClose() }) } diff --git a/raven-app/src/components/feature/chat/ChatMessage/ActionModals/EditMessageModal.tsx b/raven-app/src/components/feature/chat/ChatMessage/ActionModals/EditMessageModal.tsx index a40237ea1..889de04a6 100644 --- a/raven-app/src/components/feature/chat/ChatMessage/ActionModals/EditMessageModal.tsx +++ b/raven-app/src/components/feature/chat/ChatMessage/ActionModals/EditMessageModal.tsx @@ -3,9 +3,9 @@ import { Suspense, lazy, useEffect } from "react" import { ErrorBanner } from "../../../../layout/AlertBanner" import { IconButton, Dialog, Flex, Text } from "@radix-ui/themes" import { BiX } from "react-icons/bi" -import { useToast } from "@/hooks/useToast" import { Loader } from "@/components/common/Loader" import { TextMessage } from "../../../../../../../types/Messaging/Message" +import { toast } from "sonner" const Tiptap = lazy(() => import("../../ChatInput/Tiptap")) @@ -16,7 +16,6 @@ interface EditMessageModalProps { export const EditMessageModal = ({ onClose, message }: EditMessageModalProps) => { - const { toast } = useToast() const { updateDoc, error, loading: updatingDoc, reset } = useFrappeUpdateDoc() useEffect(() => { @@ -27,13 +26,7 @@ export const EditMessageModal = ({ onClose, message }: EditMessageModalProps) => return updateDoc('Raven Message', message.name, { text: html, json }).then((d) => { onClose(true) - toast({ - title: "Message updated", - description: "Your message has been updated", - variant: "success", - duration: 1000, - }) - + toast.info("Message updated") }) } 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 4646495f9..0a0222089 100644 --- a/raven-app/src/components/feature/chat/ChatMessage/MessageActions/MessageActions.tsx +++ b/raven-app/src/components/feature/chat/ChatMessage/MessageActions/MessageActions.tsx @@ -6,8 +6,9 @@ import { BiBookmarkMinus, BiBookmarkPlus, BiCopy, BiDownload, BiEditAlt, BiLink, import { HiReply } from 'react-icons/hi' import { FrappeConfig, FrappeContext } from 'frappe-react-sdk' import { useMessageCopy } from './useMessageCopy' -import { useToast } from '@/hooks/useToast' import { RetractVote } from './RetractVote' +import { toast } from 'sonner' +import { getErrorMessage } from '@/components/layout/AlertBanner/ErrorBanner' export interface MessageContextMenuProps { message?: Message | null, @@ -123,25 +124,21 @@ const SaveMessageAction = ({ message }: { message: Message }) => { const { call } = useContext(FrappeContext) as FrappeConfig - const { toast } = useToast() - const handleLike = () => { call.post('raven.api.raven_message.save_message', { // doctype: 'Raven Message', message_id: message.name, add: isSaved ? 'No' : 'Yes' }).then(() => { - toast({ - title: isSaved ? 'Message unsaved' : 'Message saved', - variant: 'accent', - duration: 800, - }) + if (isSaved) { + toast('Message unsaved') + } else { + toast.success('Message saved') + } }) - .catch(() => { - toast({ - title: 'Could not perform the action', - variant: 'destructive', - duration: 800, + .catch((e) => { + toast.error('Could not perform the action', { + description: getErrorMessage(e) }) }) } diff --git a/raven-app/src/components/feature/chat/ChatMessage/MessageActions/RetractVote.tsx b/raven-app/src/components/feature/chat/ChatMessage/MessageActions/RetractVote.tsx index b6f09629c..7f794c0b6 100644 --- a/raven-app/src/components/feature/chat/ChatMessage/MessageActions/RetractVote.tsx +++ b/raven-app/src/components/feature/chat/ChatMessage/MessageActions/RetractVote.tsx @@ -3,7 +3,8 @@ import { useFrappeGetCall, useFrappePostCall } from 'frappe-react-sdk' import { TiArrowBackOutline } from 'react-icons/ti' import { Poll } from '../Renderers/PollMessage' import { Message } from '../../../../../../../types/Messaging/Message' -import { toast } from '@/hooks/useToast' +import { toast } from 'sonner' +import { getErrorMessage } from '@/components/layout/AlertBanner/ErrorBanner' interface RetractVoteProps { message: Message @@ -25,16 +26,10 @@ export const RetractVote = ({ message }: RetractVoteProps) => { return call({ poll_id: message?.poll_id, }).then(() => { - toast({ - title: 'Vote retracted', - variant: 'accent', - duration: 800, - }) - }).catch(() => { - toast({ - title: 'Could not retract vote', - variant: 'destructive', - duration: 800, + toast('Vote retracted') + }).catch((e) => { + toast.error('Could not retract vote', { + description: getErrorMessage(e) }) }) } diff --git a/raven-app/src/components/feature/chat/ChatMessage/MessageActions/useMessageCopy.ts b/raven-app/src/components/feature/chat/ChatMessage/MessageActions/useMessageCopy.ts index 16fe9a878..5fb31d86c 100644 --- a/raven-app/src/components/feature/chat/ChatMessage/MessageActions/useMessageCopy.ts +++ b/raven-app/src/components/feature/chat/ChatMessage/MessageActions/useMessageCopy.ts @@ -1,10 +1,9 @@ +import { toast } from 'sonner' import { Message } from '../../../../../../../types/Messaging/Message' -import { useToast } from '@/hooks/useToast' import turndown from 'turndown' type Props = {} export const useMessageCopy = (message?: Message | null) => { - const { toast } = useToast() const copy = () => { if (!message) return @@ -29,31 +28,19 @@ export const useMessageCopy = (message?: Message | null) => { if (markdown) { navigator.clipboard.writeText(markdown) - toast({ - title: 'Text copied', - duration: 800, - variant: 'accent' - }) + toast.success('Text copied to clipboard') } else { - toast({ - title: 'Could not copy text', - duration: 800, - variant: 'destructive' - }) + toast.error('Could not copy text') } - } else { + } else if (message.message_type === "Image" || message.message_type === "File") { if (message.file.startsWith('http') || message.file.startsWith('https')) { navigator.clipboard.writeText(message.file) } else { navigator.clipboard.writeText(window.location.origin + message.file) } - toast({ - title: 'Link copied', - duration: 800, - variant: 'accent' - }) + toast.success('Link copied') } } diff --git a/raven-app/src/components/feature/chat/ChatMessage/MessageItem.tsx b/raven-app/src/components/feature/chat/ChatMessage/MessageItem.tsx index cabef7d28..890f3aace 100644 --- a/raven-app/src/components/feature/chat/ChatMessage/MessageItem.tsx +++ b/raven-app/src/components/feature/chat/ChatMessage/MessageItem.tsx @@ -242,7 +242,7 @@ export const MessageContent = ({ message, user, ...props }: MessageContentProps) {message.text ? : null} + }} user={user} showLinkPreview={message.hide_link_preview ? false : true} /> : null} {message.message_type === 'Image' && } {message.message_type === 'File' && } {message.message_type === 'Poll' && } diff --git a/raven-app/src/components/feature/chat/ChatMessage/Renderers/DoctypeLinkRenderer.tsx b/raven-app/src/components/feature/chat/ChatMessage/Renderers/DoctypeLinkRenderer.tsx index b842d842d..486c0f4cc 100644 --- a/raven-app/src/components/feature/chat/ChatMessage/Renderers/DoctypeLinkRenderer.tsx +++ b/raven-app/src/components/feature/chat/ChatMessage/Renderers/DoctypeLinkRenderer.tsx @@ -1,7 +1,7 @@ -import { useToast } from "@/hooks/useToast" import { Flex, IconButton, Link } from "@radix-ui/themes" import { BiLink, BiRightArrowAlt } from "react-icons/bi" import { FiExternalLink } from "react-icons/fi" +import { toast } from "sonner" const getRoute = (doctype: string, docname: string) => { const lowerCaseDoctype = doctype.toLowerCase().split(' ').join('-') @@ -10,17 +10,10 @@ const getRoute = (doctype: string, docname: string) => { } export const DoctypeLinkRenderer = ({ doctype, docname }: { doctype: string, docname: string }) => { - const { toast } = useToast() - const copyLink = () => { navigator.clipboard.writeText(window.location.origin + getRoute(doctype, docname)) - - toast({ - title: 'Link copied', - duration: 800, - variant: 'accent' - }) + toast.success('Link copied') } const route = getRoute(doctype, docname) diff --git a/raven-app/src/components/feature/chat/ChatMessage/Renderers/FileMessage.tsx b/raven-app/src/components/feature/chat/ChatMessage/Renderers/FileMessage.tsx index 66441da03..c00c100f4 100644 --- a/raven-app/src/components/feature/chat/ChatMessage/Renderers/FileMessage.tsx +++ b/raven-app/src/components/feature/chat/ChatMessage/Renderers/FileMessage.tsx @@ -4,12 +4,12 @@ import { UserFields } from "@/utils/users/UserListProvider" import { Box, Button, Dialog, Flex, IconButton, Link, Text } from "@radix-ui/themes" import { BoxProps } from "@radix-ui/themes/dist/cjs/components/box" import { BiDownload, BiLink, BiShow } from "react-icons/bi" -import { useToast } from "@/hooks/useToast" import { DIALOG_CONTENT_CLASS } from "@/utils/layout/dialog" import { DateMonthAtHourMinuteAmPm } from "@/utils/dateConversions" import { clsx } from "clsx" import { FileExtensionIcon } from "@/utils/layout/FileExtIcon" import { memo } from "react" +import { toast } from "sonner" interface FileMessageBlockProps extends BoxProps { message: FileMessage, @@ -25,7 +25,6 @@ export const FileMessageBlock = memo(({ message, user, ...props }: FileMessageBl const isVideo = isVideoFile(fileExtension) const isPDF = fileExtension === 'pdf' - const { toast } = useToast() const copyLink = () => { if (message.file.startsWith('http') || message.file.startsWith('https')) { @@ -35,11 +34,7 @@ export const FileMessageBlock = memo(({ message, user, ...props }: FileMessageBl navigator.clipboard.writeText(window.location.origin + message.file) } - toast({ - title: 'Link copied', - duration: 800, - variant: 'accent' - }) + toast.success('Link copied') } return @@ -52,7 +47,7 @@ export const FileMessageBlock = memo(({ message, user, ...props }: FileMessageBl title="Download" color='gray' target='_blank'>{fileName} - : diff --git a/raven-app/src/components/feature/chat/ChatMessage/Renderers/PollMessage.tsx b/raven-app/src/components/feature/chat/ChatMessage/Renderers/PollMessage.tsx index 751d3341c..36d8d101b 100644 --- a/raven-app/src/components/feature/chat/ChatMessage/Renderers/PollMessage.tsx +++ b/raven-app/src/components/feature/chat/ChatMessage/Renderers/PollMessage.tsx @@ -7,8 +7,9 @@ import { useFrappeDocumentEventListener, useFrappeGetCall, useFrappePostCall } f import { RavenPoll } from "@/types/RavenMessaging/RavenPoll" import { ErrorBanner } from "@/components/layout/AlertBanner" import { RavenPollOption } from "@/types/RavenMessaging/RavenPollOption" -import { useToast } from "@/hooks/useToast" + import { ViewPollVotes } from "@/components/feature/polls/ViewPollVotes" +import { toast } from "sonner" interface PollMessageBlockProps extends BoxProps { message: PollMessage, @@ -89,28 +90,29 @@ const PollResults = ({ data }: { data: Poll }) => { const PollOption = ({ data, option }: { data: Poll, option: RavenPollOption }) => { - const getPercentage = (votes: number) => { - if (data.poll.is_multi_choice) { - const totalVotes = data.poll.options.reduce((acc, opt) => acc + (opt.votes ?? 0), 0) - return (votes / totalVotes) * 100 - } else return (votes / data.poll.total_votes) * 100 - } - // State to track whether the animation should be triggered const [triggerAnimation, setTriggerAnimation] = useState(false) // Use useEffect to trigger animation after the component is mounted useEffect(() => { setTriggerAnimation(true) - }, []) + }, [data]) const isCurrentUserVote = useMemo(() => { return data.current_user_votes.some(vote => vote.option === option.name) }, [data.current_user_votes, option.name]) const percentage = useMemo(() => { + + const getPercentage = (votes: number) => { + if (data.poll.is_multi_choice) { + const totalVotes = data.poll.options.reduce((acc, opt) => acc + (opt.votes ?? 0), 0) + return (votes / totalVotes) * 100 + } else return (votes / data.poll.total_votes) * 100 + } + return getPercentage(option.votes ?? 0) - }, [option.votes]) + }, [option.votes, data]) const width = `${percentage}%` @@ -135,17 +137,12 @@ const PollOption = ({ data, option }: { data: Poll, option: RavenPollOption }) = const SingleChoicePoll = ({ data, messageID }: { data: Poll, messageID: string }) => { const { call } = useFrappePostCall('raven.api.raven_poll.add_vote') - const { toast } = useToast() const onVoteSubmit = async (option: RavenPollOption) => { return call({ 'message_id': messageID, 'option_id': option.name }).then(() => { - toast({ - title: "Your vote has been submitted!", - variant: 'success', - duration: 800 - }) + toast.success('Your vote has been submitted!') }) } @@ -168,7 +165,6 @@ const SingleChoicePoll = ({ data, messageID }: { data: Poll, messageID: string } const MultiChoicePoll = ({ data, messageID }: { data: Poll, messageID: string }) => { const [selectedOptions, setSelectedOptions] = useState([]) - const { toast } = useToast() const handleCheckboxChange = (name: string, value: boolean | string) => { if (value) { @@ -184,11 +180,7 @@ const MultiChoicePoll = ({ data, messageID }: { data: Poll, messageID: string }) 'message_id': messageID, 'option_id': selectedOptions }).then(() => { - toast({ - title: "Your vote has been submitted!", - variant: 'success', - duration: 800 - }) + toast.success('Your vote has been submitted!') }) } diff --git a/raven-app/src/components/feature/chat/ChatMessage/Renderers/TiptapRenderer/Blockquote.tsx b/raven-app/src/components/feature/chat/ChatMessage/Renderers/TiptapRenderer/Blockquote.tsx deleted file mode 100644 index b579d247d..000000000 --- a/raven-app/src/components/feature/chat/ChatMessage/Renderers/TiptapRenderer/Blockquote.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Blockquote } from '@radix-ui/themes'; -import TiptapBlockquote from '@tiptap/extension-blockquote' -import { NodeViewRendererProps, NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react"; - -export const CustomBlockquote = TiptapBlockquote.extend({ - addNodeView() { - return ReactNodeViewRenderer(BlockquoteRenderer) - } -}) - -const BlockquoteRenderer = ({ node }: NodeViewRendererProps) => { - return ( - -
    - {node.textContent} -
    -
    - ); -}; \ No newline at end of file diff --git a/raven-app/src/components/feature/chat/ChatMessage/Renderers/TiptapRenderer/Link.tsx b/raven-app/src/components/feature/chat/ChatMessage/Renderers/TiptapRenderer/Link.tsx index e6bcb483c..9f6bc15e2 100644 --- a/raven-app/src/components/feature/chat/ChatMessage/Renderers/TiptapRenderer/Link.tsx +++ b/raven-app/src/components/feature/chat/ChatMessage/Renderers/TiptapRenderer/Link.tsx @@ -1,9 +1,9 @@ -import { Skeleton } from '@/components/common/Skeleton'; -import { Box, Flex, Text } from '@radix-ui/themes'; +import { Box, Flex, IconButton, Text, Tooltip } from '@radix-ui/themes'; import TiptapLink from '@tiptap/extension-link' import { mergeAttributes, useCurrentEditor } from "@tiptap/react"; -import { useFrappeGetCall } from 'frappe-react-sdk'; +import { useFrappeGetCall, useFrappePostCall } from 'frappe-react-sdk'; import { memo, useMemo } from 'react'; +import { BiX } from 'react-icons/bi'; export const CustomLink = TiptapLink.extend({ renderHTML({ HTMLAttributes }) { @@ -19,7 +19,6 @@ export const CustomLink = TiptapLink.extend({ protocols: ['mailto', 'https', 'http'], openOnClick: false, }) - export type LinkPreviewDetails = { title: string, description: string, @@ -29,7 +28,7 @@ export type LinkPreviewDetails = { site_name: string } -export const LinkPreview = memo(({ isScrolling }: { isScrolling?: boolean }) => { +export const LinkPreview = memo(({ messageID }: { messageID: string }) => { const { editor } = useCurrentEditor() @@ -78,50 +77,75 @@ export const LinkPreview = memo(({ isScrolling }: { isScrolling?: boolean }) => // const href = editor?.getAttributes('link').href - const { data, isLoading } = useFrappeGetCall<{ message: LinkPreviewDetails[] }>('raven.api.preview_links.get_preview_link', { + const { data } = useFrappeGetCall<{ message: LinkPreviewDetails[] }>('raven.api.preview_links.get_preview_link', { urls: JSON.stringify([href]) - }, href ? undefined : null, { + }, href ? `link_preview_${href}` : null, { revalidateIfStale: false, revalidateOnFocus: false, revalidateOnReconnect: false, shouldRetryOnError: false, }) + const { call } = useFrappePostCall('raven.api.preview_links.hide_link_preview') + + const hidePreviewLink = () => { + call({ + message_id: messageID + }) + } + if (!href) return null const linkPreview = data?.message?.[0] - return - - {linkPreview ? linkPreview.site_name && linkPreview.description ? - {(linkPreview.absolute_image || linkPreview.image) && - + return + {linkPreview ? linkPreview.site_name && linkPreview.description ? + {(linkPreview.absolute_image || linkPreview.image) && + + {/* Absolute positioned skeleton loader */} - - + + - {linkPreview.title} - - } - - - {linkPreview.title} - {linkPreview.site_name} + + } + + + + + + {linkPreview.title} + {linkPreview.site_name} + + {linkPreview.description} - {linkPreview.description} - -
    : null : - - null} -
    - + +
    + + + + + +
    + + : null : + + null + } + }) \ No newline at end of file diff --git a/raven-app/src/components/feature/chat/ChatMessage/Renderers/TiptapRenderer/TiptapRenderer.tsx b/raven-app/src/components/feature/chat/ChatMessage/Renderers/TiptapRenderer/TiptapRenderer.tsx index 827983453..0008b1e31 100644 --- a/raven-app/src/components/feature/chat/ChatMessage/Renderers/TiptapRenderer/TiptapRenderer.tsx +++ b/raven-app/src/components/feature/chat/ChatMessage/Renderers/TiptapRenderer/TiptapRenderer.tsx @@ -13,7 +13,6 @@ import json from 'highlight.js/lib/languages/json' import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight' import { common, createLowlight } from 'lowlight' import python from 'highlight.js/lib/languages/python' -import { CustomBlockquote } from './Blockquote' import { CustomBold } from './Bold' import { CustomUserMention } from './Mention' import { CustomLink, LinkPreview } from './Link' @@ -21,6 +20,7 @@ import { CustomUnderline } from './Underline' import { Image } from '@tiptap/extension-image' import { clsx } from 'clsx' import Italic from '@tiptap/extension-italic'; +import './tiptap-renderer.styles.css' const lowlight = createLowlight(common) lowlight.register('html', html) @@ -44,7 +44,7 @@ export const TiptapRenderer = ({ message, user, isScrolling = false, isTruncated editable: false, editorProps: { attributes: { - class: isTruncated ? 'line-clamp-3' : '' + class: isTruncated ? 'tiptap-renderer line-clamp-3' : 'tiptap-renderer' } }, enableCoreExtensions: true, @@ -53,7 +53,6 @@ export const TiptapRenderer = ({ message, user, isScrolling = false, isTruncated heading: false, codeBlock: false, bold: false, - blockquote: false, italic: false, listItem: { HTMLAttributes: { @@ -64,6 +63,11 @@ export const TiptapRenderer = ({ message, user, isScrolling = false, isTruncated HTMLAttributes: { class: 'rt-Text text-sm' } + }, + code: { + HTMLAttributes: { + class: 'pt-0.5 px-1 pb-px bg-[var(--gray-a3)] dark:bg-[#0d0d0d] text-[var(--ruby-a11)] dark-[var(--accent-a3)] text text-xs font-mono rounded border border-gray-4 dark:border-gray-6' + } } }), Highlight.configure({ @@ -76,7 +80,6 @@ export const TiptapRenderer = ({ message, user, isScrolling = false, isTruncated CodeBlockLowlight.configure({ lowlight }), - CustomBlockquote, CustomBold, CustomUserMention, CustomLink, @@ -99,7 +102,7 @@ export const TiptapRenderer = ({ message, user, isScrolling = false, isTruncated contentEditable={false} editor={editor} readOnly /> - {showLinkPreview && } + {showLinkPreview && } ) diff --git a/raven-app/src/components/feature/chat/ChatMessage/Renderers/TiptapRenderer/tiptap-renderer.styles.css b/raven-app/src/components/feature/chat/ChatMessage/Renderers/TiptapRenderer/tiptap-renderer.styles.css new file mode 100644 index 000000000..8cbc97efc --- /dev/null +++ b/raven-app/src/components/feature/chat/ChatMessage/Renderers/TiptapRenderer/tiptap-renderer.styles.css @@ -0,0 +1,5 @@ +.tiptap-renderer blockquote { + border-left: 3px solid var(--gray-11); + padding-left: 0.8rem; + margin: 1rem; +} \ No newline at end of file diff --git a/raven-app/src/components/feature/chat/chat-footer/ArchivedChannelBox.tsx b/raven-app/src/components/feature/chat/chat-footer/ArchivedChannelBox.tsx index 58ccd3887..c91a5f308 100644 --- a/raven-app/src/components/feature/chat/chat-footer/ArchivedChannelBox.tsx +++ b/raven-app/src/components/feature/chat/chat-footer/ArchivedChannelBox.tsx @@ -5,8 +5,8 @@ import { ChannelMembers } from "@/utils/channel/ChannelMembersProvider" import { useFrappeUpdateDoc } from "frappe-react-sdk" import { useContext } from "react" import { Box, Button, Flex, Text } from "@radix-ui/themes" -import { useToast } from "@/hooks/useToast" import { Loader } from "@/components/common/Loader" +import { toast } from "sonner" interface ArchivedChannelBoxProps { channelData: ChannelListItem, @@ -16,17 +16,12 @@ interface ArchivedChannelBoxProps { export const ArchivedChannelBox = ({ channelData, channelMembers }: ArchivedChannelBoxProps) => { const { updateDoc, error, loading } = useFrappeUpdateDoc() - const { toast } = useToast() const unArchiveChannel = async () => { return updateDoc('Raven Channel', channelData.name, { is_archived: 0 }).then(() => { - toast({ - title: 'Channel restored.', - variant: 'success', - duration: 1000, - }) + toast.success('Channel restored.') }) } diff --git a/raven-app/src/components/feature/direct-messages/DirectMessageList.tsx b/raven-app/src/components/feature/direct-messages/DirectMessageList.tsx index ae5c510ed..9989724db 100644 --- a/raven-app/src/components/feature/direct-messages/DirectMessageList.tsx +++ b/raven-app/src/components/feature/direct-messages/DirectMessageList.tsx @@ -9,20 +9,22 @@ import { useIsUserActive } from "@/hooks/useIsUserActive" import { ChannelListContext, ChannelListContextType, DMChannelListItem, ExtraUsersData, UnreadCountData } from "../../../utils/channel/ChannelListProvider" import { Box, Flex, Text } from "@radix-ui/themes" import { UserAvatar } from "@/components/common/UserAvatar" -import { useToast } from "@/hooks/useToast" +import { toast } from "sonner" +import { getErrorMessage } from "@/components/layout/AlertBanner/ErrorBanner" +import { useStickyState } from "@/hooks/useStickyState" export const DirectMessageList = ({ unread_count }: { unread_count?: UnreadCountData }) => { const { extra_users } = useContext(ChannelListContext) as ChannelListContextType - const [showData, setShowData] = useState(true) + const [showData, setShowData] = useStickyState(true, 'expandDirectMessageList') const toggle = () => setShowData(d => !d) return ( - + Direct Messages {!showData && unread_count && unread_count?.total_unread_count_in_dms > 0 && @@ -93,7 +95,6 @@ const ExtraUsersItemList = () => { const { extra_users, mutate } = useContext(ChannelListContext) as ChannelListContextType const { call } = useFrappePostCall<{ message: string }>("raven.api.raven_channel.create_direct_message_channel") - const { toast } = useToast() const navigate = useNavigate() const createDMChannel = async (user_id: string) => { @@ -102,12 +103,9 @@ const ExtraUsersItemList = () => { navigate(`${r?.message}`) mutate() }) - .catch(() => { - toast({ - title: "Error", - description: "Could not create channel.", - variant: 'destructive', - duration: 2000, + .catch((e) => { + toast.error('Could not create channel', { + description: getErrorMessage(e) }) }) } diff --git a/raven-app/src/components/feature/file-upload/FileDrop.tsx b/raven-app/src/components/feature/file-upload/FileDrop.tsx index aa79d334c..034722f9a 100644 --- a/raven-app/src/components/feature/file-upload/FileDrop.tsx +++ b/raven-app/src/components/feature/file-upload/FileDrop.tsx @@ -1,8 +1,8 @@ -import { useToast } from "@/hooks/useToast" import { Flex, Text } from "@radix-ui/themes" import { FlexProps } from "@radix-ui/themes/dist/cjs/components/flex" import { forwardRef, useImperativeHandle, useState } from "react" import { Accept, useDropzone } from "react-dropzone" +import { toast } from "sonner" export interface CustomFile extends File { fileID: string, @@ -31,17 +31,12 @@ export interface FileDropProps extends FlexProps { export const FileDrop = forwardRef((props: FileDropProps, ref) => { const { files, onFileChange, maxFiles, accept, maxFileSize, children, ...compProps } = props - const { toast } = useToast() const [onDragEnter, setOnDragEnter] = useState(false) const fileSizeValidator = (file: any) => { if (maxFileSize && file.size > maxFileSize * 1000000) { - toast({ - title: `Uh Oh! ${file.name} exceeded the maximum file size required.`, - variant: 'destructive', - duration: 2500 - }) + toast.error(`Uh Oh! ${file.name} exceeded the maximum file size required.`) return { code: "size-too-large", message: `File size is larger than the required size.` @@ -57,11 +52,7 @@ export const FileDrop = forwardRef((props: FileDropProps, ref) => { }))]) if (maxFiles && maxFiles < fileRejections.length) { - toast({ - title: `Uh Oh! Maximum ${maxFiles} files can be uploaded. Please try again.`, - variant: 'destructive', - duration: 1800, - }) + toast.error(`Uh Oh! Maximum ${maxFiles} files can be uploaded. Please try again.`) } }, maxFiles: maxFiles ? maxFiles : 0, diff --git a/raven-app/src/components/feature/integrations/webhooks/ViewWebhookPage.tsx b/raven-app/src/components/feature/integrations/webhooks/ViewWebhookPage.tsx index d2597c17c..e3e7598bc 100644 --- a/raven-app/src/components/feature/integrations/webhooks/ViewWebhookPage.tsx +++ b/raven-app/src/components/feature/integrations/webhooks/ViewWebhookPage.tsx @@ -3,7 +3,6 @@ import { AlertDialog, Badge, Button, DropdownMenu, Flex, IconButton, Separator, import { FrappeDoc, useFrappeUpdateDoc } from "frappe-react-sdk" import { FieldValues, FormProvider, useForm } from "react-hook-form" import { KeyedMutator } from 'swr' -import { useToast } from "@/hooks/useToast" import { BiDotsVerticalRounded } from "react-icons/bi" import { useState } from "react" import { DIALOG_CONTENT_CLASS } from "@/utils/layout/dialog" @@ -11,6 +10,7 @@ import { Loader } from "@/components/common/Loader" import { RavenWebhook } from "@/types/RavenIntegrations/RavenWebhook" import { BackToList } from "./BackToList" import { WebhookForm } from "./WebhookForm" +import { toast } from "sonner" export const ViewWebhookPage = ({ data, mutate }: { data: FrappeDoc, mutate: KeyedMutator> }) => { @@ -23,17 +23,12 @@ export const ViewWebhookPage = ({ data, mutate }: { data: FrappeDoc { return updateDoc('Raven Webhook', data.name, data) .then((doc) => { - toast({ - title: "Webhook updated successfully.", - variant: 'success', - }) + toast.success("Webhook updated") reset() mutate() if (doc) { @@ -54,10 +49,7 @@ export const ViewWebhookPage = ({ data, mutate }: { data: FrappeDoc { - toast({ - title: "Webhook updated successfully.", - variant: 'success', - }) + toast.success(`Webhook ${data.enabled ? 'disabled' : 'enabled'}`) reset() mutate() }) diff --git a/raven-app/src/components/feature/integrations/webhooks/WebhookItem.tsx b/raven-app/src/components/feature/integrations/webhooks/WebhookItem.tsx index c35d1f9c1..107ff4281 100644 --- a/raven-app/src/components/feature/integrations/webhooks/WebhookItem.tsx +++ b/raven-app/src/components/feature/integrations/webhooks/WebhookItem.tsx @@ -1,6 +1,5 @@ import { Loader } from "@/components/common/Loader" import { ErrorBanner } from "@/components/layout/AlertBanner" -import { useToast } from "@/hooks/useToast" import { RavenWebhook } from "@/types/RavenIntegrations/RavenWebhook" import { DateMonthYear } from "@/utils/dateConversions" import { DIALOG_CONTENT_CLASS } from "@/utils/layout/dialog" @@ -9,6 +8,7 @@ import { useFrappeDeleteDoc } from "frappe-react-sdk" import { useState } from "react" import { BiEdit, BiTrash } from "react-icons/bi" import { useNavigate } from "react-router-dom" +import { toast } from "sonner" export const WebhookItem = ({ webhook, mutate }: { webhook: RavenWebhook, mutate: () => void }) => { @@ -79,16 +79,11 @@ const DeleteWebhookAlertContent = ({ webhhookID, onClose, mutate }: { webhhookID const { deleteDoc, error, loading } = useFrappeDeleteDoc() - const { toast } = useToast() - const onDelete = () => { deleteDoc('Raven Webhook', webhhookID).then(() => { mutate() onClose() - toast({ - title: "Webhook deleted successfully.", - variant: 'success', - }) + toast.error('Webhook deleted.') }) } diff --git a/raven-app/src/components/feature/polls/CreatePoll.tsx b/raven-app/src/components/feature/polls/CreatePoll.tsx index 7c6472c38..eafb033ad 100644 --- a/raven-app/src/components/feature/polls/CreatePoll.tsx +++ b/raven-app/src/components/feature/polls/CreatePoll.tsx @@ -1,6 +1,6 @@ import { ErrorText, Label } from "@/components/common/Form" import { ErrorBanner } from "@/components/layout/AlertBanner" -import { useToast } from "@/hooks/useToast" +import { getErrorMessage } from "@/components/layout/AlertBanner/ErrorBanner" import { RavenPoll } from "@/types/RavenMessaging/RavenPoll" import { DIALOG_CONTENT_CLASS } from "@/utils/layout/dialog" import { Button, Checkbox, Dialog, Flex, IconButton, TextArea, TextField, Text, Box } from "@radix-ui/themes" @@ -10,6 +10,7 @@ import { Controller, FormProvider, useFieldArray, useForm } from "react-hook-for import { BiPlus, BiTrash } from "react-icons/bi" import { MdOutlineBarChart } from "react-icons/md" import { useParams } from "react-router-dom" +import { toast } from "sonner" interface CreatePollProps { buttonStyle?: string, @@ -43,7 +44,6 @@ export const CreatePoll = ({ buttonStyle, isDisabled = false }: CreatePollProps) const { register, handleSubmit, formState: { errors }, control, reset: resetForm } = methods const [isOpen, setIsOpen] = useState(false) - const { toast } = useToast() const { fields, remove, append } = useFieldArray({ control: control, @@ -94,21 +94,16 @@ export const CreatePoll = ({ buttonStyle, isDisabled = false }: CreatePollProps) const { call: createPoll, error } = useFrappePostCall('raven.api.raven_poll.create_poll') const { channelID } = useParams<{ channelID: string }>() - const onSubmit = (data: RavenPoll) => { + const onSubmit = async (data: RavenPoll) => { return createPoll({ ...data, "channel_id": channelID }).then(() => { - toast({ - title: "Poll created successfully", - variant: 'success', - }) + toast.success("Poll created") onClose() }).catch((err) => { - toast({ - title: "Error creating poll", - description: err.message, - variant: "destructive", + toast.error("There was an error.", { + description: getErrorMessage(err) }) }) } diff --git a/raven-app/src/components/feature/polls/ViewPollVotes.tsx b/raven-app/src/components/feature/polls/ViewPollVotes.tsx index d36c85a70..82cdd2b7d 100644 --- a/raven-app/src/components/feature/polls/ViewPollVotes.tsx +++ b/raven-app/src/components/feature/polls/ViewPollVotes.tsx @@ -89,7 +89,7 @@ const VotesBlock = ({ votesData, poll }: { votesData: PollVotesResponse, poll: P
    - {optionName} + {optionName} - {option.percentage.toFixed(2)}% {option.count} vote{option.count > 1 ? 's' : ''} diff --git a/raven-app/src/components/feature/raven-users/AddRavenUsersContent.tsx b/raven-app/src/components/feature/raven-users/AddRavenUsersContent.tsx index f75c25bc9..75722592c 100644 --- a/raven-app/src/components/feature/raven-users/AddRavenUsersContent.tsx +++ b/raven-app/src/components/feature/raven-users/AddRavenUsersContent.tsx @@ -13,8 +13,8 @@ import { UserListContext } from "@/utils/users/UserListProvider" import { Button, Dialog, Em, Flex, Strong, Text, TextField } from "@radix-ui/themes" import { Loader } from "@/components/common/Loader" import { BiSearch } from "react-icons/bi" -import { useToast } from "@/hooks/useToast" import { ErrorCallout } from "@/components/common/Callouts/ErrorCallouts" +import { toast } from "sonner" interface AddUsersResponse { failed_users: User[], @@ -51,7 +51,6 @@ const AddRavenUsersContent = ({ onClose }: { onClose: VoidFunction }) => { const [selected, setSelected] = useState([]) const { loading, call, error: postError } = useFrappePostCall<{ message: AddUsersResponse }>('raven.api.raven_users.add_users_to_raven') - const { toast } = useToast() const [failedUsers, setFailedUsers] = useState([]) @@ -63,11 +62,7 @@ const AddRavenUsersContent = ({ onClose }: { onClose: VoidFunction }) => { users: JSON.stringify(selected) }).then((res) => { if (res.message.success_users.length !== 0) { - toast({ - title: `You have added ${res.message.success_users.length} users to Raven`, - variant: 'success', - duration: 1000 - }) + toast.success(`You have added ${res.message.success_users.length} users to Raven`) } mutate('raven.api.raven_users.get_list') diff --git a/raven-app/src/components/feature/saved-messages/SavedMessages.tsx b/raven-app/src/components/feature/saved-messages/SavedMessages.tsx index 36fbfb08a..07ec2531c 100644 --- a/raven-app/src/components/feature/saved-messages/SavedMessages.tsx +++ b/raven-app/src/components/feature/saved-messages/SavedMessages.tsx @@ -6,7 +6,6 @@ import { EmptyStateForSavedMessages } from "../../layout/EmptyState/EmptyState" import { PageHeader } from "../../layout/Heading/PageHeader" import { MessageBox } from "../GlobalSearch/MessageBox" import { Heading } from "@radix-ui/themes" -import { useToast } from "@/hooks/useToast" import { SearchButton } from "../chat-header/SearchButton" import { Box, Flex } from '@radix-ui/themes' diff --git a/raven-app/src/components/feature/select-member/AddMembersDropdown.tsx b/raven-app/src/components/feature/select-member/AddMembersDropdown.tsx index 1c965ccba..b0a00479e 100644 --- a/raven-app/src/components/feature/select-member/AddMembersDropdown.tsx +++ b/raven-app/src/components/feature/select-member/AddMembersDropdown.tsx @@ -28,8 +28,9 @@ const AddMembersDropdown = ({ channelMembers, label = 'Select users', selectedUs const lowerCasedInputValue = inputValue.toLowerCase() return nonChannelMembers.filter((user: UserFields) => { + const isUserSelected = selectedUsers.find(selectedUser => selectedUser.name === user.name) return ( - !selectedUsers.includes(user) && + !isUserSelected && (user.full_name.toLowerCase().includes(lowerCasedInputValue) || user.name.toLowerCase().includes(lowerCasedInputValue)) ) diff --git a/raven-app/src/components/feature/settings/common/DeleteAlert.tsx b/raven-app/src/components/feature/settings/common/DeleteAlert.tsx index 78e8bed62..557437e04 100644 --- a/raven-app/src/components/feature/settings/common/DeleteAlert.tsx +++ b/raven-app/src/components/feature/settings/common/DeleteAlert.tsx @@ -1,11 +1,11 @@ import { Loader } from "@/components/common/Loader" import { ErrorBanner } from "@/components/layout/AlertBanner" -import { useToast } from "@/hooks/useToast" import { AlertDialog, Button, Callout, Checkbox, Flex, Text } from "@radix-ui/themes" import { useFrappeDeleteDoc } from "frappe-react-sdk" import { useState } from "react" import { FiAlertTriangle } from "react-icons/fi" import { useNavigate } from "react-router-dom" +import { toast } from "sonner" export interface Props { isOpen: boolean, @@ -41,7 +41,6 @@ const AlertContent = ({ onClose, docname, path }: DeleteDocModalProps) => { reset() } - const { toast } = useToast() const navigate = useNavigate() const onSubmit = () => { @@ -50,10 +49,7 @@ const AlertContent = ({ onClose, docname, path }: DeleteDocModalProps) => { .then(() => { onClose() navigate(path) - toast({ - title: `${docname} deleted`, - variant: 'success', - }) + toast(`Event ${docname} deleted.`) }) } } diff --git a/raven-app/src/components/layout/AlertBanner/ErrorBanner.tsx b/raven-app/src/components/layout/AlertBanner/ErrorBanner.tsx index 7a738d115..c88f7841a 100644 --- a/raven-app/src/components/layout/AlertBanner/ErrorBanner.tsx +++ b/raven-app/src/components/layout/AlertBanner/ErrorBanner.tsx @@ -14,6 +14,47 @@ interface ParsedErrorMessage { title?: string, indicator?: string, } + +export const getErrorMessage = (error?: FrappeError | null): string => { + const messages = getErrorMessages(error) + return messages.map(m => m.message).join('\n') +} + +const getErrorMessages = (error?: FrappeError | null): ParsedErrorMessage[] => { + if (!error) return [] + let eMessages: ParsedErrorMessage[] = error?._server_messages ? JSON.parse(error?._server_messages) : [] + eMessages = eMessages.map((m: any) => { + try { + return JSON.parse(m) + } catch (e) { + return m + } + }) + + if (eMessages.length === 0) { + // Get the message from the exception by removing the exc_type + const indexOfFirstColon = error?.exception?.indexOf(':') + if (indexOfFirstColon) { + const exception = error?.exception?.slice(indexOfFirstColon + 1) + if (exception) { + eMessages = [{ + message: exception, + title: "Error" + }] + } + } + + if (eMessages.length === 0) { + eMessages = [{ + message: error?.message, + title: "Error", + indicator: "red" + }] + } + } + return eMessages + +} export const ErrorBanner = ({ error, overrideHeading, children }: ErrorBannerProps) => { diff --git a/raven-app/src/components/layout/Sidebar/SidebarComp.tsx b/raven-app/src/components/layout/Sidebar/SidebarComp.tsx index 69003a3fc..d7956b777 100644 --- a/raven-app/src/components/layout/Sidebar/SidebarComp.tsx +++ b/raven-app/src/components/layout/Sidebar/SidebarComp.tsx @@ -132,12 +132,11 @@ export const SidebarButtonItem = ({ children, subtle, onClick, isLoading, active } interface SidebarViewMoreButtonProps extends IconButtonProps { - onClick: () => void + onClick: () => void, + expanded: boolean } -export const SidebarViewMoreButton = ({ onClick, ...props }: SidebarViewMoreButtonProps) => { - - const [isViewMore, setIsViewMore] = useState(false) +export const SidebarViewMoreButton = ({ expanded, onClick, ...props }: SidebarViewMoreButtonProps) => { return ( { - setIsViewMore(!isViewMore) - onClick() - }} + onClick={onClick} {...props}> - {isViewMore ? : } + {expanded ? : } ) } diff --git a/raven-app/src/hooks/useActiveState.tsx b/raven-app/src/hooks/useActiveState.tsx index 2b55dabd2..ac8ba71d4 100644 --- a/raven-app/src/hooks/useActiveState.tsx +++ b/raven-app/src/hooks/useActiveState.tsx @@ -2,7 +2,7 @@ import { useContext, useEffect } from 'react' import { useIdleTimer, PresenceType } from 'react-idle-timer' import { FrappeContext, FrappeConfig } from 'frappe-react-sdk' import { useBoolean } from './useBoolean' -import { useToast } from './useToast' +import { toast } from 'sonner' /** * We need to track and sync user's active state with the server @@ -18,8 +18,6 @@ export const useActiveState = () => { off: deactivate, }] = useBoolean(true) - const { toast } = useToast() - /** * Make an API call to the server to refresh the user's active state * If an error occurs while updating the "active" state, show a toast to the user @@ -43,11 +41,7 @@ export const useActiveState = () => { const showToast = () => { // Check if the toast is already active // If it is, don't show it again - toast({ - description: "There was an error while refreshing your active state. You may appear offline to other users.", - duration: 4000, - variant: 'destructive', - }) + toast.error("There was an error while refreshing your active state. You may appear offline to other users.") } /** diff --git a/raven-app/src/hooks/useFileURLCopy.ts b/raven-app/src/hooks/useFileURLCopy.ts index 66c574264..333a704f0 100644 --- a/raven-app/src/hooks/useFileURLCopy.ts +++ b/raven-app/src/hooks/useFileURLCopy.ts @@ -1,4 +1,4 @@ -import { useToast } from "./useToast" +import { toast } from "sonner" /** * Simple hook to copy a file URL to the clipboard and present a toast message @@ -7,7 +7,6 @@ import { useToast } from "./useToast" */ const useFileURLCopy = (file: string) => { - const { toast } = useToast() const copy = () => { if (file.startsWith('http') || file.startsWith('https')) { navigator.clipboard.writeText(file) @@ -15,11 +14,7 @@ const useFileURLCopy = (file: string) => { else { navigator.clipboard.writeText(window.location.origin + file) } - toast({ - title: 'Link copied', - duration: 800, - variant: 'accent' - }) + toast.success('Link copied') } return copy diff --git a/raven-app/src/hooks/useToast.tsx b/raven-app/src/hooks/useToast.tsx deleted file mode 100644 index 7e8a65905..000000000 --- a/raven-app/src/hooks/useToast.tsx +++ /dev/null @@ -1,192 +0,0 @@ -import * as React from "react" - -import type { - ToastActionElement, - ToastProps, -} from "@/components/common/Toast/Toast" - -const TOAST_LIMIT = 1 -const TOAST_REMOVE_DELAY = 1000 - -type ToasterToast = ToastProps & { - id: string - title?: React.ReactNode - description?: React.ReactNode - action?: ToastActionElement -} - -const actionTypes = { - ADD_TOAST: "ADD_TOAST", - UPDATE_TOAST: "UPDATE_TOAST", - DISMISS_TOAST: "DISMISS_TOAST", - REMOVE_TOAST: "REMOVE_TOAST", -} as const - -let count = 0 - -function genId() { - count = (count + 1) % Number.MAX_SAFE_INTEGER - return count.toString() -} - -type ActionType = typeof actionTypes - -type Action = - | { - type: ActionType["ADD_TOAST"] - toast: ToasterToast - } - | { - type: ActionType["UPDATE_TOAST"] - toast: Partial - } - | { - type: ActionType["DISMISS_TOAST"] - toastId?: ToasterToast["id"] - } - | { - type: ActionType["REMOVE_TOAST"] - toastId?: ToasterToast["id"] - } - -interface State { - toasts: ToasterToast[] -} - -const toastTimeouts = new Map>() - -const addToRemoveQueue = (toastId: string) => { - if (toastTimeouts.has(toastId)) { - return - } - - const timeout = setTimeout(() => { - toastTimeouts.delete(toastId) - dispatch({ - type: "REMOVE_TOAST", - toastId: toastId, - }) - }, TOAST_REMOVE_DELAY) - - toastTimeouts.set(toastId, timeout) -} - -export const reducer = (state: State, action: Action): State => { - switch (action.type) { - case "ADD_TOAST": - return { - ...state, - toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT), - } - - case "UPDATE_TOAST": - return { - ...state, - toasts: state.toasts.map((t) => - t.id === action.toast.id ? { ...t, ...action.toast } : t - ), - } - - case "DISMISS_TOAST": { - const { toastId } = action - - // ! Side effects ! - This could be extracted into a dismissToast() action, - // but I'll keep it here for simplicity - if (toastId) { - addToRemoveQueue(toastId) - } else { - state.toasts.forEach((toast) => { - addToRemoveQueue(toast.id) - }) - } - - return { - ...state, - toasts: state.toasts.map((t) => - t.id === toastId || toastId === undefined - ? { - ...t, - open: false, - } - : t - ), - } - } - case "REMOVE_TOAST": - if (action.toastId === undefined) { - return { - ...state, - toasts: [], - } - } - return { - ...state, - toasts: state.toasts.filter((t) => t.id !== action.toastId), - } - } -} - -const listeners: Array<(state: State) => void> = [] - -let memoryState: State = { toasts: [] } - -function dispatch(action: Action) { - memoryState = reducer(memoryState, action) - listeners.forEach((listener) => { - listener(memoryState) - }) -} - -type Toast = Omit - -function toast({ ...props }: Toast) { - const id = genId() - - const update = (props: ToasterToast) => - dispatch({ - type: "UPDATE_TOAST", - toast: { ...props, id }, - }) - const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id }) - - dispatch({ - type: "ADD_TOAST", - toast: { - duration: TOAST_REMOVE_DELAY, - ...props, - id, - open: true, - onOpenChange: (open) => { - if (!open) dismiss() - }, - }, - }) - - return { - id: id, - dismiss, - update, - } -} - -function useToast() { - const [state, setState] = React.useState(memoryState) - - React.useEffect(() => { - listeners.push(setState) - return () => { - const index = listeners.indexOf(setState) - if (index > -1) { - listeners.splice(index, 1) - } - } - }, [state]) - - return { - ...state, - toast, - dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }), - } -} - -export { useToast, toast } \ No newline at end of file diff --git a/raven-app/src/pages/AddRavenUsersPage.tsx b/raven-app/src/pages/AddRavenUsersPage.tsx index 82ce0a921..ed4b66104 100644 --- a/raven-app/src/pages/AddRavenUsersPage.tsx +++ b/raven-app/src/pages/AddRavenUsersPage.tsx @@ -7,7 +7,6 @@ import { Filter, useFrappeGetDocList, useFrappePostCall } from 'frappe-react-sdk import { ChangeEvent, useContext, useState } from 'react' import { FaInfo } from 'react-icons/fa' import { User } from '../../../types/Core/User' -import { useToast } from '@/hooks/useToast' import { PageLengthSelector } from '@/components/feature/pagination/PageLengthSelector' import { PageSelector } from '@/components/feature/pagination/PageSelector' import { UsersTable } from '@/components/feature/raven-users/UsersTable' @@ -17,6 +16,7 @@ import { TableLoader } from '@/components/layout/Loaders/TableLoader' import { BiLeftArrowAlt, BiSearch } from 'react-icons/bi' import { UserContext } from '@/utils/auth/UserProvider' import { Loader } from '@/components/common/Loader' +import { toast } from 'sonner' const AddRavenUsersPage = () => { @@ -92,19 +92,13 @@ const AddRavenUsersCard = () => { const [selected, setSelected] = useState([]) - const { toast } = useToast() - const { loading, call, error: postError } = useFrappePostCall('raven.api.raven_users.add_users_to_raven') const handleAddUsers = () => { call({ users: JSON.stringify([...selected, currentUser]) }).then(() => { - toast({ - title: `You have added ${selected.length} users to Raven`, - variant: 'success', - duration: 1000 - }) + toast.success(`You have added ${selected.length} users to Raven`) window.location.reload() }) } diff --git a/raven-app/src/pages/auth/Login.tsx b/raven-app/src/pages/auth/Login.tsx index 8dd5721a1..ba33e4373 100644 --- a/raven-app/src/pages/auth/Login.tsx +++ b/raven-app/src/pages/auth/Login.tsx @@ -8,8 +8,8 @@ import { Loader } from "@/components/common/Loader"; import { ErrorText, Label } from "@/components/common/Form"; import { LoginInputs, LoginContext } from "@/types/Auth/Login"; import AuthContainer from "@/components/layout/AuthContainer"; -import { ErrorCallout } from "@/components/common/Callouts/ErrorCallouts"; import { TwoFactor } from "@/pages/auth/TwoFactor"; +import { ErrorBanner } from "@/components/layout/AlertBanner"; const SocialProviderIcons = { "github": , @@ -73,7 +73,7 @@ export const Component = () => { return ( - {error && } + {error && } { isTwoFactorEnabled ? : @@ -136,9 +136,9 @@ export const Component = () => { asChild size="2" > - - Forgot Password? - + + Forgot Password? + diff --git a/raven-app/src/pages/auth/LoginWithEmail.tsx b/raven-app/src/pages/auth/LoginWithEmail.tsx index 1a7de1a4b..f0d36ecd8 100644 --- a/raven-app/src/pages/auth/LoginWithEmail.tsx +++ b/raven-app/src/pages/auth/LoginWithEmail.tsx @@ -17,6 +17,7 @@ import { SuccessCallout } from "@/components/common/Callouts/SuccessCallout"; import { isEmailValid } from "@/utils/validations"; import { LoginInputs } from "@/types/Auth/Login"; import AuthContainer from "@/components/layout/AuthContainer"; +import { ErrorBanner } from "@/components/layout/AlertBanner"; export const Component = () => { @@ -32,14 +33,14 @@ export const Component = () => { async function sendEmailLink(values: LoginInputs) { return call({ - email: values.email, - }) + email: values.email, + }) .then((result) => { setCallout({ state: true, message: "Login Link sent on Email", }); - }).catch((err)=>{ + }).catch((err) => { setCallout(null) }) } @@ -47,7 +48,7 @@ export const Component = () => { return ( - {error && } + {error && } {callout && } diff --git a/raven-app/src/pages/auth/SignUp.tsx b/raven-app/src/pages/auth/SignUp.tsx index cc2adc97b..5fb5b669e 100644 --- a/raven-app/src/pages/auth/SignUp.tsx +++ b/raven-app/src/pages/auth/SignUp.tsx @@ -15,8 +15,8 @@ import { Loader } from "@/components/common/Loader"; import { isEmailValid } from "@/utils/validations"; import AuthContainer from '@/components/layout/AuthContainer'; import { CalloutObject } from "@/components/common/Callouts/CustomCallout"; -import { ErrorCallout } from "@/components/common/Callouts/ErrorCallouts"; import { SuccessCallout } from "@/components/common/Callouts/SuccessCallout"; +import { ErrorBanner } from "@/components/layout/AlertBanner"; export type SignUpInputs = { email: string, @@ -45,7 +45,7 @@ export const Component = () => { .then((result) => { setCallout({ state: true, - message: result?.message[0] ? result?.message[1]:result?.message[1]+". Please Try Login.", + message: result?.message[0] ? result?.message[1] : result?.message[1] + ". Please Try Login.", }); }) .catch((err) => { @@ -55,73 +55,73 @@ export const Component = () => { return ( - {error && } + {error && } {callout && } - - - - - - - - - {errors?.email && ( - {errors?.email?.message} - )} - + + + + + + + + + {errors?.email && ( + {errors?.email?.message} + )} + - - - - - isEmailValid(email) || - "Please enter a valid email address.", - required: "Email is required.", - })} - name="email" - type="email" - required - placeholder="jane@example.com" - tabIndex={0} - /> - - {errors?.email && ( - {errors?.email?.message} - )} - + + + + + isEmailValid(email) || + "Please enter a valid email address.", + required: "Email is required.", + })} + name="email" + type="email" + required + placeholder="jane@example.com" + tabIndex={0} + /> + + {errors?.email && ( + {errors?.email?.message} + )} + - - - - - Have an Account? - - - Login - - + + + + + Have an Account? + + + Login + + + - - - + + ) } diff --git a/raven-app/src/pages/settings/ServerScripts/SchedulerEvents/CreateSchedulerEvent.tsx b/raven-app/src/pages/settings/ServerScripts/SchedulerEvents/CreateSchedulerEvent.tsx index 47c6406fc..670877cd1 100644 --- a/raven-app/src/pages/settings/ServerScripts/SchedulerEvents/CreateSchedulerEvent.tsx +++ b/raven-app/src/pages/settings/ServerScripts/SchedulerEvents/CreateSchedulerEvent.tsx @@ -1,11 +1,11 @@ import { SchedulerEventForm, SchedulerEventsForm } from "@/components/feature/settings/scheduler-events/SchedulerEventsForm" import { ErrorBanner } from "@/components/layout/AlertBanner" -import { useToast } from "@/hooks/useToast" import { Box, Button, Flex, Heading, Section } from "@radix-ui/themes" import { useFrappeCreateDoc } from "frappe-react-sdk" import { FormProvider, useForm } from "react-hook-form" import { FiArrowLeft } from "react-icons/fi" import { useNavigate } from "react-router-dom" +import { toast } from "sonner" export const CreateSchedulerEvent = () => { @@ -15,8 +15,6 @@ export const CreateSchedulerEvent = () => { const { createDoc, error } = useFrappeCreateDoc() - const { toast } = useToast() - const onSubmit = (data: Partial) => { let cron_expression = '' if (data.event_frequency === 'Every Day') { @@ -47,10 +45,7 @@ export const CreateSchedulerEvent = () => { if (doc) { navigate(`../${doc?.name}`) } - toast({ - title: `Temporal Event created`, - variant: 'success', - }) + toast.success("Scheduler Event created") }) } //TODO: Figure out a way to show _server_messages in the UI (especially the script editor might have some errors that we need to show to the user) diff --git a/raven-app/src/pages/settings/ServerScripts/SchedulerEvents/ViewSchedulerEvent.tsx b/raven-app/src/pages/settings/ServerScripts/SchedulerEvents/ViewSchedulerEvent.tsx index 8ebd5acd3..ba22a7c34 100644 --- a/raven-app/src/pages/settings/ServerScripts/SchedulerEvents/ViewSchedulerEvent.tsx +++ b/raven-app/src/pages/settings/ServerScripts/SchedulerEvents/ViewSchedulerEvent.tsx @@ -1,13 +1,13 @@ import { DeleteAlert } from "@/components/feature/settings/common/DeleteAlert" import { SchedulerEventsForm } from "@/components/feature/settings/scheduler-events/SchedulerEventsForm" import { ErrorBanner } from "@/components/layout/AlertBanner" -import { useToast } from "@/hooks/useToast" import { Badge, Box, Button, DropdownMenu, Flex, Heading, Section } from "@radix-ui/themes" import { useFrappeGetDoc, useFrappeUpdateDoc } from "frappe-react-sdk" import { useState } from "react" import { FormProvider, useForm } from "react-hook-form" import { FiArrowLeft, FiChevronDown } from "react-icons/fi" import { useNavigate, useParams } from "react-router-dom" +import { toast } from "sonner" export interface Props { } @@ -51,8 +51,6 @@ const ViewSchedulerEventPage = ({ data, onUpdate }: { data: any, onUpdate: () => const { updateDoc, error } = useFrappeUpdateDoc() - const { toast } = useToast() - const onSubmit = (data: any) => { let cron_expression = '' if (data.event_frequency === 'Every Day') { @@ -76,10 +74,7 @@ const ViewSchedulerEventPage = ({ data, onUpdate }: { data: any, onUpdate: () => }) .then(() => { onUpdate() - toast({ - title: `Scheduler Event ${data.name} updated`, - variant: 'success', - }) + toast.success("Scheduler Event updated") }) } @@ -89,10 +84,7 @@ const ViewSchedulerEventPage = ({ data, onUpdate }: { data: any, onUpdate: () => }) .then(() => { onUpdate() - toast({ - title: `Scheduler Event ${data.name} ${data.disabled ? "enabled" : "disabled"}`, - variant: 'success', - }) + toast(`Scheduler Event ${data.name} ${data.disabled ? "enabled" : "disabled"}`) }) } diff --git a/raven-app/src/pages/settings/Webhooks/CreateWebhook.tsx b/raven-app/src/pages/settings/Webhooks/CreateWebhook.tsx index 27695ab99..1734412c0 100644 --- a/raven-app/src/pages/settings/Webhooks/CreateWebhook.tsx +++ b/raven-app/src/pages/settings/Webhooks/CreateWebhook.tsx @@ -5,9 +5,9 @@ import { useNavigate } from 'react-router-dom' import { Button, Flex, Separator, Text } from '@radix-ui/themes' import { ErrorBanner } from '@/components/layout/AlertBanner' import { WebhookForm } from '../../../components/feature/integrations/webhooks/WebhookForm' -import { useToast } from '@/hooks/useToast' import { RavenWebhook } from '@/types/RavenIntegrations/RavenWebhook' import { BackToList } from '@/components/feature/integrations/webhooks/BackToList' +import { toast } from 'sonner' const CreateWebhook = () => { @@ -21,17 +21,12 @@ const CreateWebhook = () => { }) const { createDoc, loading, reset, error } = useFrappeCreateDoc() - const { toast } = useToast() - const onSubmit = (data: RavenWebhook) => { createDoc('Raven Webhook', data) .then((doc) => { reset() methods.reset() - toast({ - title: "Webhook created successfully.", - variant: 'success', - }) + toast.success("Webhook created") return doc }).then((doc) => { navigate(`../webhooks/${doc.name}`) diff --git a/raven-app/src/types/RavenMessaging/RavenMessage.ts b/raven-app/src/types/RavenMessaging/RavenMessage.ts index a0f2c7e3c..1fc4cbdde 100644 --- a/raven-app/src/types/RavenMessaging/RavenMessage.ts +++ b/raven-app/src/types/RavenMessaging/RavenMessage.ts @@ -26,7 +26,7 @@ export interface RavenMessage{ /** Replied Message Details : JSON */ replied_message_details?: any /** Message Type : Select */ - message_type?: "Text" | "Image" | "File" + message_type?: "Text" | "Image" | "File" | "Poll" /** Content : Long Text */ content?: string /** File : Attach */ @@ -49,8 +49,12 @@ export interface RavenMessage{ is_edited?: 0 | 1 /** Mentions : Table - Raven Mention */ mentions?: RavenMention[] + /** Poll ID : Link - Raven Poll */ + poll_id?: string /** Is Bot Message : Check */ is_bot_message?: 0 | 1 /** Bot : Link - Raven User */ bot?: string + /** Hide link preview : Check */ + hide_link_preview?: 0 | 1 } \ No newline at end of file diff --git a/raven-app/src/utils/channel/ChannelListProvider.tsx b/raven-app/src/utils/channel/ChannelListProvider.tsx index e65f598d0..13fd4fdff 100644 --- a/raven-app/src/utils/channel/ChannelListProvider.tsx +++ b/raven-app/src/utils/channel/ChannelListProvider.tsx @@ -2,8 +2,9 @@ import { FrappeConfig, FrappeContext, FrappeError, useFrappeDocTypeEventListener import { PropsWithChildren, createContext, useContext, useMemo } from 'react' import { KeyedMutator } from 'swr' import { RavenChannel } from '../../../../types/RavenChannelManagement/RavenChannel' -import { useToast } from '@/hooks/useToast' import { useSWRConfig } from 'frappe-react-sdk' +import { toast } from 'sonner' +import { getErrorMessage } from '@/components/layout/AlertBanner/ErrorBanner' export type ExtraUsersData = { name: string, @@ -62,7 +63,6 @@ export const ChannelListProvider = ({ children }: PropsWithChildren) => { */ export const useFetchChannelList = (): ChannelListContextType => { - const { toast } = useToast() const { mutate: globalMutate } = useSWRConfig() const { data, mutate, ...rest } = useFrappeGetCall<{ message: ChannelList }>("raven.api.raven_channel.get_all_channels", { hide_archived: false @@ -70,9 +70,8 @@ export const useFetchChannelList = (): ChannelListContextType => { revalidateOnFocus: false, revalidateIfStale: false, onError: (error) => { - toast({ - title: error.message, - variant: 'destructive' + toast.error("There was an error while fetching the channel list.", { + description: getErrorMessage(error) }) } }) diff --git a/raven-app/yarn.lock b/raven-app/yarn.lock index 1fb9e09eb..21af2c75a 100644 --- a/raven-app/yarn.lock +++ b/raven-app/yarn.lock @@ -32,23 +32,23 @@ "@babel/highlight" "^7.24.2" picocolors "^1.0.0" -"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.23.5", "@babel/compat-data@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.1.tgz#31c1f66435f2a9c329bb5716a6d6186c516c3742" - integrity sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA== +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.23.5", "@babel/compat-data@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.4.tgz#6f102372e9094f25d908ca0d34fc74c74606059a" + integrity sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ== "@babel/core@^7.11.1", "@babel/core@^7.21.3", "@babel/core@^7.23.5": - version "7.24.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.3.tgz#568864247ea10fbd4eff04dda1e05f9e2ea985c3" - integrity sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ== + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.4.tgz#1f758428e88e0d8c563874741bc4ffc4f71a4717" + integrity sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.24.2" - "@babel/generator" "^7.24.1" + "@babel/generator" "^7.24.4" "@babel/helper-compilation-targets" "^7.23.6" "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.24.1" - "@babel/parser" "^7.24.1" + "@babel/helpers" "^7.24.4" + "@babel/parser" "^7.24.4" "@babel/template" "^7.24.0" "@babel/traverse" "^7.24.1" "@babel/types" "^7.24.0" @@ -58,10 +58,10 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.1.tgz#e67e06f68568a4ebf194d1c6014235344f0476d0" - integrity sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A== +"@babel/generator@^7.24.1", "@babel/generator@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.4.tgz#1fc55532b88adf952025d5d2d1e71f946cb1c498" + integrity sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw== dependencies: "@babel/types" "^7.24.0" "@jridgewell/gen-mapping" "^0.3.5" @@ -93,10 +93,10 @@ lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.1.tgz#db58bf57137b623b916e24874ab7188d93d7f68f" - integrity sha512-1yJa9dX9g//V6fDebXoEfEsxkZHk3Hcbm+zLhyu6qVgYFLvmTALTeV+jNU9e5RnYtioBrGEOdoI2joMSNQ/+aA== +"@babel/helper-create-class-features-plugin@^7.24.1", "@babel/helper-create-class-features-plugin@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.4.tgz#c806f73788a6800a5cfbbc04d2df7ee4d927cce3" + integrity sha512-lG75yeuUSVu0pIcbhiYMXBXANHrpUPaOfu7ryAzskCgKUHuAxRQI5ssrtmF0X9UXldPlvT0XM/A4F44OXRt6iQ== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" "@babel/helper-environment-visitor" "^7.22.20" @@ -248,10 +248,10 @@ "@babel/template" "^7.22.15" "@babel/types" "^7.22.19" -"@babel/helpers@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.1.tgz#183e44714b9eba36c3038e442516587b1e0a1a94" - integrity sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg== +"@babel/helpers@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.4.tgz#dc00907fd0d95da74563c142ef4cd21f2cb856b6" + integrity sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw== dependencies: "@babel/template" "^7.24.0" "@babel/traverse" "^7.24.1" @@ -267,10 +267,18 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.24.0", "@babel/parser@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.1.tgz#1e416d3627393fab1cb5b0f2f1796a100ae9133a" - integrity sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg== +"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.24.0", "@babel/parser@^7.24.1", "@babel/parser@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.4.tgz#234487a110d89ad5a3ed4a8a566c36b9453e8c88" + integrity sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg== + +"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.4.tgz#6125f0158543fb4edf1c22f322f3db67f21cb3e1" + integrity sha512-qpl6vOOEEzTLLcsuqYYo8yDtrTocmu2xkGvgNebvPjT9DTtfFYGmgDqY+rBYXNlqL4s9qLDn6xkrJv4RxAPiTA== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-plugin-utils" "^7.24.0" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.24.1": version "7.24.1" @@ -461,10 +469,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.0" -"@babel/plugin-transform-block-scoping@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.1.tgz#27af183d7f6dad890531256c7a45019df768ac1f" - integrity sha512-h71T2QQvDgM2SmT29UYU6ozjMlAt7s7CSs5Hvy8f8cf/GM/Z4a2zMfN+fjVGaieeCrXR3EdQl6C4gQG+OgmbKw== +"@babel/plugin-transform-block-scoping@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.4.tgz#28f5c010b66fbb8ccdeef853bef1935c434d7012" + integrity sha512-nIFUZIpGKDf9O9ttyRXpHFpKC+X3Y5mtshZONuEUYBomAKoM4y029Jr+uB1bHGPhNmK8YXHevDtKDOLmtRrp6g== dependencies: "@babel/helper-plugin-utils" "^7.24.0" @@ -476,12 +484,12 @@ "@babel/helper-create-class-features-plugin" "^7.24.1" "@babel/helper-plugin-utils" "^7.24.0" -"@babel/plugin-transform-class-static-block@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.1.tgz#4e37efcca1d9f2fcb908d1bae8b56b4b6e9e1cb6" - integrity sha512-FUHlKCn6J3ERiu8Dv+4eoz7w8+kFLSyeVG4vDAikwADGjUCoHw/JHokyGtr8OR4UjpwPVivyF+h8Q5iv/JmrtA== +"@babel/plugin-transform-class-static-block@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.4.tgz#1a4653c0cf8ac46441ec406dece6e9bc590356a4" + integrity sha512-B8q7Pz870Hz/q9UgP8InNpY01CSLDSCyqX7zcRuv3FcPl87A2G17lASroHWaCtbdIcbYzOZ7kWmXFKbijMSmFg== dependencies: - "@babel/helper-create-class-features-plugin" "^7.24.1" + "@babel/helper-create-class-features-plugin" "^7.24.4" "@babel/helper-plugin-utils" "^7.24.0" "@babel/plugin-syntax-class-static-block" "^7.14.5" @@ -830,14 +838,15 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/preset-env@^7.11.0": - version "7.24.3" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.24.3.tgz#f3f138c844ffeeac372597b29c51b5259e8323a3" - integrity sha512-fSk430k5c2ff8536JcPvPWK4tZDwehWLGlBp0wrsBUjZVdeQV6lePbwKWZaZfK2vnh/1kQX1PzAJWsnBmVgGJA== + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.24.4.tgz#46dbbcd608771373b88f956ffb67d471dce0d23b" + integrity sha512-7Kl6cSmYkak0FK/FXjSEnLJ1N9T/WA2RkMhu17gZ/dsxKJUuTYNIylahPTzqpLyJN4WhDif8X0XK1R8Wsguo/A== dependencies: - "@babel/compat-data" "^7.24.1" + "@babel/compat-data" "^7.24.4" "@babel/helper-compilation-targets" "^7.23.6" "@babel/helper-plugin-utils" "^7.24.0" "@babel/helper-validator-option" "^7.23.5" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.24.4" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.24.1" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.24.1" "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.24.1" @@ -864,9 +873,9 @@ "@babel/plugin-transform-async-generator-functions" "^7.24.3" "@babel/plugin-transform-async-to-generator" "^7.24.1" "@babel/plugin-transform-block-scoped-functions" "^7.24.1" - "@babel/plugin-transform-block-scoping" "^7.24.1" + "@babel/plugin-transform-block-scoping" "^7.24.4" "@babel/plugin-transform-class-properties" "^7.24.1" - "@babel/plugin-transform-class-static-block" "^7.24.1" + "@babel/plugin-transform-class-static-block" "^7.24.4" "@babel/plugin-transform-classes" "^7.24.1" "@babel/plugin-transform-computed-properties" "^7.24.1" "@babel/plugin-transform-destructuring" "^7.24.1" @@ -930,9 +939,9 @@ integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== "@babel/runtime@^7.11.2", "@babel/runtime@^7.13.10", "@babel/runtime@^7.22.15", "@babel/runtime@^7.8.4": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.1.tgz#431f9a794d173b53720e69a6464abc6f0e2a5c57" - integrity sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ== + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.4.tgz#de795accd698007a66ba44add6cc86542aff1edd" + integrity sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA== dependencies: regenerator-runtime "^0.14.0" @@ -1767,25 +1776,6 @@ "@radix-ui/react-roving-focus" "1.0.4" "@radix-ui/react-use-controllable-state" "1.0.1" -"@radix-ui/react-toast@^1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@radix-ui/react-toast/-/react-toast-1.1.5.tgz#f5788761c0142a5ae9eb97f0051fd3c48106d9e6" - integrity sha512-fRLn227WHIBRSzuRzGJ8W+5YALxofH23y0MlPLddaIpLpCDqdE0NZlS2NRQDRiptfxDeeCjgFIpexB1/zkxDlw== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/primitive" "1.0.1" - "@radix-ui/react-collection" "1.0.3" - "@radix-ui/react-compose-refs" "1.0.1" - "@radix-ui/react-context" "1.0.1" - "@radix-ui/react-dismissable-layer" "1.0.5" - "@radix-ui/react-portal" "1.0.4" - "@radix-ui/react-presence" "1.0.1" - "@radix-ui/react-primitive" "1.0.3" - "@radix-ui/react-use-callback-ref" "1.0.1" - "@radix-ui/react-use-controllable-state" "1.0.1" - "@radix-ui/react-use-layout-effect" "1.0.1" - "@radix-ui/react-visually-hidden" "1.0.3" - "@radix-ui/react-tooltip@^1.0.7": version "1.0.7" resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz#8f55070f852e7e7450cc1d9210b793d2e5a7686e" @@ -1993,9 +1983,9 @@ picomatch "^2.3.1" "@socket.io/component-emitter@~3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" - integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== + version "3.1.1" + resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.1.tgz#621270c02c0a5d4f4669eb1caa9723c91cf201de" + integrity sha512-dzJtaDAAoXx4GCOJpbB2eG/Qj8VDpdwkLsWGzGm+0L7E8/434RyMbAHmk9ubXWVAb9nXmc44jUf8GKqVDiKezg== "@surma/rollup-plugin-off-main-thread@^2.2.3": version "2.2.3" @@ -2090,161 +2080,161 @@ "@svgr/hast-util-to-babel-ast" "8.0.0" svg-parser "^2.0.4" -"@tiptap/core@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.2.4.tgz#6f957678eb733e70b9282fb5098d284a77bd09a3" - integrity sha512-cRrI8IlLIhCE1hacBQzXIC8dsRvGq6a4lYWQK/BaHuZg21CG7szp3Vd8Ix+ra1f5v0xPOT+Hy+QFNQooRMKMCw== +"@tiptap/core@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.3.0.tgz#5a0b41b63af62860dec6d6b689e2893118f76e83" + integrity sha512-Gk2JN3i5CMkYGmsbyFI7cBUftWa+F7QYmeCLTWfbuy+hCM2OBsnYVKxhggFPGXRL5KLBEgBWeCeWMHfIw3B2MA== -"@tiptap/extension-blockquote@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.2.4.tgz#d733bea016986c0017e308a2540378c9551c7f10" - integrity sha512-FrfPnn0VgVrUwWLwja1afX99JGLp6PE9ThVcmri+tLwUZQvTTVcCvHoCdOakav3/nge1+aV4iE3tQdyq1tWI9Q== +"@tiptap/extension-blockquote@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.3.0.tgz#23ce7c706df5a885999a25d9cbbbe12d78754e78" + integrity sha512-Cztt77t7f+f0fuPy+FWUL8rKTIpcdsVT0z0zYQFFafvGaom0ZALQSOdTR/q+Kle9I4DaCMO3/Q0mwax/D4k4+A== -"@tiptap/extension-bold@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.2.4.tgz#3690eea1ebef49f8d30043fdcfc7bf2866a9b887" - integrity sha512-v3tTLc8YESFZPOGj5ByFr8VbmQ/PTo49T1vsK50VubxIN/5r9cXlKH8kb3dZlZxCxJa3FrXNO/M8rdGBSWQvSg== +"@tiptap/extension-bold@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.3.0.tgz#a499d2934bea5578e3a76e065af617caf15f7043" + integrity sha512-SzkbJibHXFNU7TRaAebTtwbXUEhGZ8+MhlBn12aQ4QhdjNtFpQwKXQPyYeDyZGcyiOFgtFTb+WIfCGm8ZX0Fpw== -"@tiptap/extension-bubble-menu@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.2.4.tgz#2477901c78a18c629f7ae828394ff1f91a500c60" - integrity sha512-Nx1fS9jcFlhxaTDYlnayz2UulhK6CMaePc36+7PQIVI+u20RhgTCRNr25zKNemvsiM0RPZZVUjlHkxC0l5as1Q== +"@tiptap/extension-bubble-menu@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.3.0.tgz#3447dcc68aaaa76c8327cb636976164986b270c7" + integrity sha512-dqyfQ8idTlhapvt0fxCGvkyjw92pBEwPqmkJ01h3EE8wTh53j0ytOHyMSf1KBuzardxpd8Yya3zlrAcR0Z3DlQ== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-bullet-list@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.2.4.tgz#1763ae7686bc3f209c1662d571e738280d190ff8" - integrity sha512-z/MPmW8bhRougMuorl6MAQBXeK4rhlP+jBWlNwT+CT8h5IkXqPnDbM1sZeagp2nYfVV6Yc4RWpzimqHHtGnYTA== +"@tiptap/extension-bullet-list@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.3.0.tgz#14400e19b87e7e9b1cf9c6d3900e7243461abc8d" + integrity sha512-4nU4vJ5FjRDLqHm085vYAkuo68UK84Wl6CDSjm7sPVcu0FvQX02Okqt65azoSYQeS1SSSd5qq9YZuGWcYdp4Cw== -"@tiptap/extension-code-block-lowlight@^2.2.2": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.2.4.tgz#804f8b3d872140566d89fde30f44b0ea5873c72f" - integrity sha512-FxrpY2Lj6kV6pu5LcaeccE3lqOqvOyFSfMGRV6x1OGOMV9TIFJKIVEIcEhqoiqEnuJZzSmQSx7QZqzOvquZo+A== +"@tiptap/extension-code-block-lowlight@^2.3": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.3.0.tgz#0bd1c45c2c5115d9cd964288a58f23688217f4fe" + integrity sha512-xMxWr/Fvv0hnN+u+6SW0OI3RVan+C6nJDU8xKh2Tx2DlBXJ0yODmq5v8WJJpW38AbaLkFuJuY/OA3AZ6n9pNbg== -"@tiptap/extension-code-block@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.2.4.tgz#6d72f3e458cf417a4be6b9cc1f9409ad89f55b78" - integrity sha512-h6WV9TmaBEZmvqe1ezMR83DhCPUap6P2mSR5pwVk0WVq6rvZjfgU0iF3EetBJOeDgPlz7cNe2NMDfVb1nGTM/g== +"@tiptap/extension-code-block@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.3.0.tgz#9bfaaeaf6abf2452233d1da64b6c5712cab2503b" + integrity sha512-+Ne6PRBwQt70Pp8aW2PewaEy4bHrNYn4N+y8MObsFtqLutXBz4nXnsXWiNYFQZwzlUY+CHG4XS73mx8oMOFfDw== -"@tiptap/extension-code@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.2.4.tgz#ba77dc56daac75bdf83a2aaecff896e9301c2cdf" - integrity sha512-JB4SJ2mUU/9qXFUf+K5K9szvovnN9AIcCb0f0UlcVBuddKHSqCl3wO3QJgYt44BfQTLMNuyzr+zVqfFd6BNt/g== +"@tiptap/extension-code@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.3.0.tgz#19b5435c59021f11e24ac8427da4434b03596b35" + integrity sha512-O2FZmosiIRoVbW82fZy8xW4h4gb2xAzxWzHEcsHPlwCbE3vYvcBMmbkQ5p+33eRtuRQInzl3Q/cwupv9ctIepQ== -"@tiptap/extension-document@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.2.4.tgz#d46c1fdd1ea5b7191112fae4e9f8a63a46309afc" - integrity sha512-z+05xGK0OFoXV1GL+/8bzcZuWMdMA3+EKwk5c+iziG60VZcvGTF7jBRsZidlu9Oaj0cDwWHCeeo6L9SgSh6i2A== +"@tiptap/extension-document@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.3.0.tgz#a15e17a48f6d24f317f73a7fff79385a2524f081" + integrity sha512-WC55SMrtlsNOnHXpzbXDzJOp7eKmZV0rXooKmvCDqoiLO/DKpyQXyF+0UHfcRPmUAi2GWFPaer7+p1H9xzcjXg== -"@tiptap/extension-dropcursor@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.2.4.tgz#df39861159a022029f733924e9a3977a76dfed17" - integrity sha512-IHwkEKmqpqXyJi16h7871NrcIqeyN7I6XRE2qdqi+MhGigVWI8nWHoYbjRKa7K/1uhs5zeRYyDlq5EuZyL6mgA== +"@tiptap/extension-dropcursor@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.3.0.tgz#4220434bbf96202d7e4c366f28693fe64c3f7542" + integrity sha512-WWxxGQPWdbzxyYP6jtBYSq4wMRhINhI0wBC8pgkxTVwCIWftMuYj++FP4LLIpuWgj78PWApuoM0QQxk4Lj7FOw== -"@tiptap/extension-floating-menu@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.2.4.tgz#a97becec8cedfa1fb282a7a52caed35a80098581" - integrity sha512-U25l7PEzOmlAPugNRl8t8lqyhQZS6W/+3f92+FdwW9qXju3i62iX/3OGCC3Gv+vybmQ4fbZmMjvl+VDfenNi3A== +"@tiptap/extension-floating-menu@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.3.0.tgz#ac61ffeac479cdf019a270d23464fd08af6ab931" + integrity sha512-bNY43/yU/+wGfmk2eDV7EPDAN/akbC+YnSKTA5VPJADzscvlrL2HlQrxbd/STIdlwKqdPU5MokcvCChhfZ4f6w== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-gapcursor@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.2.4.tgz#607b2682376c5ced086258f8329eb080e8047627" - integrity sha512-Y6htT/RDSqkQ1UwG2Ia+rNVRvxrKPOs3RbqKHPaWr3vbFWwhHyKhMCvi/FqfI3d5pViVHOZQ7jhb5hT/a0BmNw== +"@tiptap/extension-gapcursor@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.3.0.tgz#f7fae7086b5e49c3754ee48833e27c66686fb0cd" + integrity sha512-OxcXcfD0uzNcXdXu2ZpXFAtXIsgK2MBHvFUs0t0gxtcL/t43pTOQBLy+29Ei30BxpwLghtX8jQ6IDzMiybq/sA== -"@tiptap/extension-hard-break@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.2.4.tgz#2be463b2f23fc8f57004f3481829aa0b236c17f6" - integrity sha512-FPvS57GcqHIeLbPKGJa3gnH30Xw+YB1PXXnAWG2MpnMtc2Vtj1l5xaYYBZB+ADdXLAlU0YMbKhFLQO4+pg1Isg== +"@tiptap/extension-hard-break@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.3.0.tgz#01bd200b3a5f23644e796f061ef7e331b579357f" + integrity sha512-9pXi69SzLabbjY5KZ54UKzu7HAHTla9aYZKH56VatOAiJOPKJppFbU2/NfJwGzDrEtfOiDqr3dYbUDF3RuCFoQ== -"@tiptap/extension-heading@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.2.4.tgz#a90469a879713dc074ce45e3d7d8cf7a02ae6a1f" - integrity sha512-gkq7Ns2FcrOCRq7Q+VRYt5saMt2R9g4REAtWy/jEevJ5UV5vA2AiGnYDmxwAkHutoYU0sAUkjqx37wE0wpamNw== +"@tiptap/extension-heading@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.3.0.tgz#4daf92c94797fdd504e798935efd6ca2fbbd73a8" + integrity sha512-YcZoUYfqb0nohoPgem4f8mjn5OqDomFrbJiC9VRHUOCIuEu+aJEYwp8mmdkLnS3f+LRCZ6G76cJJ50lkzSAZRw== -"@tiptap/extension-highlight@^2.2.2": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-highlight/-/extension-highlight-2.2.4.tgz#f94c3bf794ddd7f0ac8613243b0c95e4c6ff8b0f" - integrity sha512-GGl6ehKQ0Q0gGgUQhkWg2XYPfhVU5c0JD3NHzV4OrBP6JAtFeMYeSLdfYzFcmoYnGafvSZaJ3NukUvnDHZGzRg== +"@tiptap/extension-highlight@^2.3": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-highlight/-/extension-highlight-2.3.0.tgz#b743b878ffaad7731c6a3509dbcfb5073a96de83" + integrity sha512-An/tzoCMbugdaU02ORJeJ74DZI5pf9oqwX9RoYPQ5K81Ia3jG52BBVtFjGq/j10Tr4iOuCmOuE+PzNtnzz3UIw== -"@tiptap/extension-history@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.2.4.tgz#2d37ccfce9a8b7997bb4d46ebb19e986c5eb6f39" - integrity sha512-FDM32XYF5NU4mzh+fJ8w2CyUqv0l2Nl15sd6fOhQkVxSj8t57z+DUXc9ZR3zkH+1RAagYJo/2Gu3e99KpMr0tg== +"@tiptap/extension-history@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.3.0.tgz#4ecec273a3634704d3accbdcc571106ea575b3d0" + integrity sha512-EF5Oq9fe/VBzU1Lsow2ubOlx1e1r4OQT1WUPGsRnL7pr94GH1Skpk7/hs9COJ9K6kP3Ebt42XjP0JEQodR58YA== -"@tiptap/extension-horizontal-rule@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.2.4.tgz#e1fc33a05d337c2871180f569a7af1774c74658a" - integrity sha512-iCRHjFQQHApWg3R4fkKkJQhWEOdu1Fdc4YEAukdOXPSg3fg36IwjvsMXjt9SYBtVZ+iio3rORCZGXyMvgCH9uw== +"@tiptap/extension-horizontal-rule@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.3.0.tgz#e4934b32c005b1cf0f03fab5f4caff8cacbf4c7e" + integrity sha512-4DB8GU3uuDzzyqUmONIb3CHXcQ6Nuy4mHHkFSmUyEjg1i5eMQU5H7S6mNvZbltcJB2ImgCSwSMlj1kVN3MLIPg== -"@tiptap/extension-image@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-2.2.4.tgz#55c267e9ff77e2bf3c514896331ad91d18da08c3" - integrity sha512-xOnqZpnP/fAfmK5AKmXplVQdXBtY5AoZ9B+qllH129aLABaDRzl3e14ZRHC8ahQawOmCe6AOCCXYUBXDOlY5Jg== +"@tiptap/extension-image@^2.3": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-2.3.0.tgz#914c4c5030fa4278f53e808c03819f7422da4497" + integrity sha512-v1fLEEzrfXWavsLFUEkTiYYxwm1WDNrjuUriU5tG2Jv22NL1BL4BLVbZbGdkAk+qHWy8QWszrDJbcgGh2VNCoQ== -"@tiptap/extension-italic@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.2.4.tgz#67403ec3aed630062c7063b7e9e7699d07db683e" - integrity sha512-qIhGNvWnsQswSgEMRA8jQQjxfkOGNAuNWKEVQX9DPoqAUgknT41hQcAMP8L2+OdACpb2jbVMOO5Cy5Dof2L8/w== +"@tiptap/extension-italic@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.3.0.tgz#72279632a2652642abed2078a920e2e05aff030e" + integrity sha512-jdFjLjdt5JtPlGMpoS6TEq5rznjbAYVlPwcw5VkYENVIYIGIR1ylIw2JwK1nUEsQ+OgYwVxHLejcUXWG1dCi2g== -"@tiptap/extension-link@^2.2.2": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.2.4.tgz#1cb4c89e15f1fdfb1b7f3f6f26c62f7d51a6c3cb" - integrity sha512-Qsx0cFZm4dxbkToXs5TcXbSoUdicv8db1gV1DYIZdETqjBm4wFjlzCUP7hPHFlvNfeSy1BzAMRt+RpeuiwvxWQ== +"@tiptap/extension-link@^2.3": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.3.0.tgz#37d25144a59cc71dd7b42e7c52b69e6b4ed29522" + integrity sha512-CnJAlV0ZOdEhKmDfYKuHJVG8g79iCFQ85cX/CROTWyuMfXz9uhj2rLpZ6nfidVbonqxAhQp7NAIr2y+Fj5/53A== dependencies: linkifyjs "^4.1.0" -"@tiptap/extension-list-item@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.2.4.tgz#2412dd92e51c3cbc4f2abc3c38ec2ab8348c1af9" - integrity sha512-lPLKGKsHpM9ClUa8n7GEUn8pG6HCYU0vFruIy3l2t6jZdHkrgBnYtVGMZ13K8UDnj/hlAlccxku0D0P4mA1Vrg== +"@tiptap/extension-list-item@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.3.0.tgz#7f0af465c1af160648ace586100ff3f8e612e9bf" + integrity sha512-mHU+IuRa56OT6YCtxf5Z7OSUrbWdKhGCEX7RTrteDVs5oMB6W3oF9j88M5qQmZ1WDcxvQhAOoXctnMt6eX9zcA== -"@tiptap/extension-mention@^2.2.2": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-mention/-/extension-mention-2.2.4.tgz#f787223facf952691136806839e4e65cbf721aaa" - integrity sha512-myUlwpbrQgWfRJwG4UHM2PbiSp+squJv6LPKfKINs5yDxIproaZ0/4TAJt3heeSXZJnboPAQxSP7eLd5pY8lBw== +"@tiptap/extension-mention@^2.3": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-mention/-/extension-mention-2.3.0.tgz#d61c1c2ffa74346d20e1d1ed18783dd83c9e442a" + integrity sha512-Zu18LIKOMWm7XQztGuwLTxGGwGMc0QDbEAXB5TLrKFYWdHoaU0wu65+mBa61kuUoZ1Ur71J7vu2tuU70uPd16w== -"@tiptap/extension-ordered-list@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.2.4.tgz#dfa6c6869a3d16fe5b2a10749ffe5b999346efd2" - integrity sha512-TpFy140O9Af1JciXt+xwqYUXxcJ6YG8zi/B5UDJujp+FH5sCmlYYBBnWxiFMhVaj6yEmA2eafu1qUkic/1X5Aw== +"@tiptap/extension-ordered-list@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.3.0.tgz#75f7f668201a4cd3ec507c78d2229ec670e3e707" + integrity sha512-gkf0tltXjlUj0cqyfDV2r7xy9YPKtcVSWwlCPun6OOi0KzKFiAMqQpA9hy2W6gJ+KCp8+KNRMClZOfH4TnnBfg== -"@tiptap/extension-paragraph@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.2.4.tgz#9126fafbf984e324bfb3fab34deb689def7eb98d" - integrity sha512-m1KwyvTNJxsq7StbspbcOhxO4Wk4YpElDbqOouWi+H4c8azdpI5Pn96ZqhFeE9bSyjByg6OcB/wqoJsLbeFWdQ== +"@tiptap/extension-paragraph@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.3.0.tgz#959928b57785f4647d807fffe76e63719f2e53c4" + integrity sha512-peCpA7DFqkd0cHb+cHv4YHNoMsXG8tKFNJlCHpLmsZWl2hWmpKgKmUrXAUfzjcFSvkZxn0xYc5oWbqUgg+2LzA== -"@tiptap/extension-placeholder@^2.2.2": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.2.4.tgz#d75572f6fb0cb3bbbedfa2ced49c55285ae8fdd5" - integrity sha512-UL4Fn9T33SoS7vdI3NnSxBJVeGUIgCIutgXZZ5J8CkcRoDIeS78z492z+6J+qGctHwTd0xUL5NzNJI82HfiTdg== +"@tiptap/extension-placeholder@^2.3": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.3.0.tgz#db1f4375c8365c491211457c6d13f6efa486cd3a" + integrity sha512-1BOyxVLzyUYf6yOOeJ8CfpP6DSCS4L6HjBZqj6WP1z1NyBV8RAfhf3UuLNcimfSWAETXFR3g0ZbaxxWffI1cEg== -"@tiptap/extension-strike@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.2.4.tgz#f987a6fe7b85e3179b413792ae3f33dd9c086e01" - integrity sha512-/a2EwQgA+PpG17V2tVRspcrIY0SN3blwcgM7lxdW4aucGkqSKnf7+91dkhQEwCZ//o8kv9mBCyRoCUcGy6S5Xg== +"@tiptap/extension-strike@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.3.0.tgz#0d5afbda45c1c1ad15487fa48ef41a93b39388f2" + integrity sha512-gOW4ALeH8gkJiUGGXVy/AOd5lAPTX0bzoOW1+sCLcTA7t8dluBW7M2ngNYxTEtlKqyv7aLfrgsYSiqucmmfSLw== -"@tiptap/extension-text@^2.2.4": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.2.4.tgz#aa101c568aa78a4ddc06a944eefcd3ac944987d4" - integrity sha512-NlKHMPnRJXB+0AGtDlU0P2Pg+SdesA2lMMd7JzDUgJgL7pX2jOb8eUqSeOjFKuSzFSqYfH6C3o6mQiNhuQMv+g== +"@tiptap/extension-text@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.3.0.tgz#2ebd13dcb4f54b9f72af25fc1015eec474afe89d" + integrity sha512-zkudl0TyKRy/8vHtyo5dMzjBRD0HEUnsS8YOsjR4xwQq5EYUXleRgM1s6lb6Yms2sLUAZRWdDddoQ686iq4zQg== -"@tiptap/extension-typography@^2.2.2": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-typography/-/extension-typography-2.2.4.tgz#f929d58c40a993f95dfbef00bbce00ecde26fc99" - integrity sha512-1gmvr74uk44Wzxd6QI+dKz/M//xaD15yYwUtcRc9+ohbfvCqtRl3XDVoxl3MQmDMljcui5kMMaRcFNvW1Kujvg== +"@tiptap/extension-typography@^2.3": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-typography/-/extension-typography-2.3.0.tgz#d52cf73b7e0d23eb7166146668eae81440a65739" + integrity sha512-bI9t6dVp3wvzp3RVhJYRAV5Gi4pCSnumYith62TJmEk7fI24XuwMAXJu32+RTtBkaWHX/nwSGPh/ol0PRmtzKw== -"@tiptap/extension-underline@^2.2.2": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-2.2.4.tgz#7be1a616e16452dd17a9c6fd03e40636bc676972" - integrity sha512-jCHgIJMwtXlGHVy/j3L8/QvglHCikkHJw7YS5yf8E/8HlPh1tZfVy/IxdgacDOpUN30X+UPJZQDdVKymafgwdA== +"@tiptap/extension-underline@^2.3": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-2.3.0.tgz#3b79174ac09794523c017ddeb8da7d04ce6228fe" + integrity sha512-vmmcwCPmWqGKYHZevz50+bxrpHyiu5y6YZweAE476hn8Mud6vYg7RpkXgW8bjkCOky6UA51uelslSc0XrLE6uw== -"@tiptap/pm@^2.2.2": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.2.4.tgz#701975e3221ac40b1bfba52d89e1345024212411" - integrity sha512-Po0klR165zgtinhVp1nwMubjyKx6gAY9kH3IzcniYLCkqhPgiqnAcCr61TBpp4hfK8YURBS4ihvCB1dyfCyY8A== +"@tiptap/pm@^2.3": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.3.0.tgz#3f971d5d26401ba0db306f2ee1e317acb8ff6c91" + integrity sha512-4WYqShZBwDyReKvapC0nmeYdOtZbZ31y4MjolpKQaSD4I7kg/oZspC+byUGdvIRsNpRN7i2X0IyvdISKk8gw5Q== dependencies: prosemirror-changeset "^2.2.1" prosemirror-collab "^1.3.1" @@ -2265,43 +2255,43 @@ prosemirror-transform "^1.8.0" prosemirror-view "^1.32.7" -"@tiptap/react@^2.2.2": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.2.4.tgz#bfb6a484a26d85df7a6f9636b98c38f4e83de4c0" - integrity sha512-HkYmMZWcETPZn3KpzdDg/ns2TKeFh54TvtCEInA4ljYtWGLoZc/A+KaiEtMIgVs+Mo1XwrhuoNGjL9c0OK2HJw== +"@tiptap/react@^2.3": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.3.0.tgz#b9a7c29a8bae38a28a3d6ab910da11290addcc58" + integrity sha512-ThgFJQTWYKRClTV2Zg0wBRqfy0EGz3U4NOey7jwncUjSjx5+o9nXbfQAYWDKQFfWyE+wnrBTYfddEP9pHNX5cQ== dependencies: - "@tiptap/extension-bubble-menu" "^2.2.4" - "@tiptap/extension-floating-menu" "^2.2.4" + "@tiptap/extension-bubble-menu" "^2.3.0" + "@tiptap/extension-floating-menu" "^2.3.0" -"@tiptap/starter-kit@^2.2.2": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-2.2.4.tgz#7d9c35fc423bb0bb6a9b2e660c41a080d8caa7e7" - integrity sha512-Kbk7qUfIZg3+bNa3e/wBeDQt4jJB46uQgM+xy5NSY6H8NZP6gdmmap3aIrn9S/W/hGpxJl4RcXAeaT0CQji9XA== - dependencies: - "@tiptap/core" "^2.2.4" - "@tiptap/extension-blockquote" "^2.2.4" - "@tiptap/extension-bold" "^2.2.4" - "@tiptap/extension-bullet-list" "^2.2.4" - "@tiptap/extension-code" "^2.2.4" - "@tiptap/extension-code-block" "^2.2.4" - "@tiptap/extension-document" "^2.2.4" - "@tiptap/extension-dropcursor" "^2.2.4" - "@tiptap/extension-gapcursor" "^2.2.4" - "@tiptap/extension-hard-break" "^2.2.4" - "@tiptap/extension-heading" "^2.2.4" - "@tiptap/extension-history" "^2.2.4" - "@tiptap/extension-horizontal-rule" "^2.2.4" - "@tiptap/extension-italic" "^2.2.4" - "@tiptap/extension-list-item" "^2.2.4" - "@tiptap/extension-ordered-list" "^2.2.4" - "@tiptap/extension-paragraph" "^2.2.4" - "@tiptap/extension-strike" "^2.2.4" - "@tiptap/extension-text" "^2.2.4" - -"@tiptap/suggestion@^2.2.2": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@tiptap/suggestion/-/suggestion-2.2.4.tgz#746e4659fb4be2f49ad43ee5aa9801cc8154889c" - integrity sha512-g6HHsKM6K3asW+ZlwMYyLCRqCRaswoliZOQofY4iZt5ru5HNTSzm3YW4XSyW5RGXJIuc319yyrOFgtJ3Fyu5rQ== +"@tiptap/starter-kit@^2.3": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-2.3.0.tgz#6a0834a98c0f780e706159dd3f3d70c992a98dd5" + integrity sha512-TjvCd/hzEnuEYOdr5uQqcfHOMuj7JRoZBPdheupwl3SbuYiCxtcqYyAE5qoGXWwuVe9xVGerOLVPkDUgmyrH6A== + dependencies: + "@tiptap/core" "^2.3.0" + "@tiptap/extension-blockquote" "^2.3.0" + "@tiptap/extension-bold" "^2.3.0" + "@tiptap/extension-bullet-list" "^2.3.0" + "@tiptap/extension-code" "^2.3.0" + "@tiptap/extension-code-block" "^2.3.0" + "@tiptap/extension-document" "^2.3.0" + "@tiptap/extension-dropcursor" "^2.3.0" + "@tiptap/extension-gapcursor" "^2.3.0" + "@tiptap/extension-hard-break" "^2.3.0" + "@tiptap/extension-heading" "^2.3.0" + "@tiptap/extension-history" "^2.3.0" + "@tiptap/extension-horizontal-rule" "^2.3.0" + "@tiptap/extension-italic" "^2.3.0" + "@tiptap/extension-list-item" "^2.3.0" + "@tiptap/extension-ordered-list" "^2.3.0" + "@tiptap/extension-paragraph" "^2.3.0" + "@tiptap/extension-strike" "^2.3.0" + "@tiptap/extension-text" "^2.3.0" + +"@tiptap/suggestion@^2.3": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@tiptap/suggestion/-/suggestion-2.3.0.tgz#6bb07afdf928853c1a638cae81da72b0ad92a09b" + integrity sha512-QngwR9ahodVfwqp/kXxJvuL3zNb6XZu+vCuWy8RJrGP8DA7SCI9t8t7iB6NfG4kSsRGxM+3DuLi+2xOZQUaEVQ== "@types/babel__core@^7.20.5": version "7.20.5" @@ -2359,9 +2349,9 @@ integrity sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ== "@types/node@*": - version "20.11.30" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.30.tgz#9c33467fc23167a347e73834f788f4b9f399d66f" - integrity sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw== + version "20.12.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.7.tgz#04080362fa3dd6c5822061aa3124f5c152cff384" + integrity sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg== dependencies: undici-types "~5.26.4" @@ -2371,19 +2361,18 @@ integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q== "@types/react-dom@^18.2.19": - version "18.2.22" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.22.tgz#d332febf0815403de6da8a97e5fe282cbe609bae" - integrity sha512-fHkBXPeNtfvri6gdsMYyW+dW7RXFo6Ad09nLFK0VQWR7yGLai/Cyvyj696gbwYvBnhGtevUG9cET0pmUbMtoPQ== + version "18.2.25" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.25.tgz#2946a30081f53e7c8d585eb138277245caedc521" + integrity sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA== dependencies: "@types/react" "*" "@types/react@*", "@types/react@^18.2.55": - version "18.2.71" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.71.tgz#77c3b97b02014bf351b21b684f80273a3a343f96" - integrity sha512-PxEsB9OjmQeYGffoWnYAd/r5FiJuUw2niFQHPc2v2idwh8wGPkkYzOHuinNJJY6NZqfoTCiOIizDOz38gYNsyw== + version "18.2.77" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.77.tgz#af2f857b6a6dfb6ca89ec102ebc147b1f1616880" + integrity sha512-CUT9KUUF+HytDM7WiXKLF9qUSg4tGImwy4FXTlfEDPEkkNUzJ7rVFolYweJ9fS1ljoIaP7M7Rdjc5eUm/Yu5AA== dependencies: "@types/prop-types" "*" - "@types/scheduler" "*" csstype "^3.0.2" "@types/resolve@1.17.1": @@ -2393,11 +2382,6 @@ dependencies: "@types/node" "*" -"@types/scheduler@*": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.23.0.tgz#0a6655b3e2708eaabca00b7372fafd7a792a7b09" - integrity sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw== - "@types/trusted-types@^2.0.2": version "2.0.7" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" @@ -2676,9 +2660,9 @@ camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.30001599: - version "1.0.30001600" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz#93a3ee17a35aa6a9f0c6ef1b2ab49507d1ab9079" - integrity sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ== + version "1.0.30001609" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001609.tgz#fc34fad75c0c6d6d6303bdbceec2da8f203dabd6" + integrity sha512-JFPQs34lHKx1B5t1EpQpWH4c+29zIyn/haGsbpfq3suuV9v56enjFt23zqijxGTMwy1p/4H2tjnQMY+p1WoAyA== chalk@^2.4.2: version "2.4.2" @@ -3009,14 +2993,14 @@ ejs@^3.1.6: jake "^10.8.5" electron-to-chromium@^1.4.668: - version "1.4.717" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.717.tgz#99db370cae8cd090d5b01f8748e9ad369924d0f8" - integrity sha512-6Fmg8QkkumNOwuZ/5mIbMU9WI3H2fmn5ajcVya64I5Yr5CcNmO7vcLt0Y7c96DCiMO5/9G+4sI2r6eEvdg1F7A== + version "1.4.735" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.735.tgz#c32914ef2cd0a3a545a3def841d253a31a8a93be" + integrity sha512-pkYpvwg8VyOTQAeBqZ7jsmpCjko1Qc6We1ZtZCjRyYbT5v4AIUKDy5cQTRotQlSSZmMr8jqpEt6JtOj5k7lR7A== emoji-picker-element@^1.21.0: - version "1.21.2" - resolved "https://registry.yarnpkg.com/emoji-picker-element/-/emoji-picker-element-1.21.2.tgz#132d2aa449a1687ce614d98abc5e2204057df619" - integrity sha512-DgeQdxHN/5vbUrmiDmGD7PZ2I6f5NOd1h8F2BV3oBbKl21dLbVetl9r5Q7859pgh8eksBmZDThHtjON6nXJwdQ== + version "1.21.3" + resolved "https://registry.yarnpkg.com/emoji-picker-element/-/emoji-picker-element-1.21.3.tgz#dea784cfe6d99b086cee8d4b696c602982ba85d9" + integrity sha512-o1wQmPX0+8dmp/v4BfHVSMD2x32ZmCQd5LMHpHTw1M9ltDW99P+IIPbveI7nPxwGw74TW9E6jds6Kitf14UCOg== emoji-regex@^8.0.0: version "8.0.0" @@ -3057,9 +3041,9 @@ error-ex@^1.3.1: is-arrayish "^0.2.1" es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2: - version "1.23.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.2.tgz#693312f3940f967b8dd3eebacb590b01712622e0" - integrity sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w== + version "1.23.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" + integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== dependencies: array-buffer-byte-length "^1.0.1" arraybuffer.prototype.slice "^1.0.3" @@ -3100,11 +3084,11 @@ es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23 safe-regex-test "^1.0.3" string.prototype.trim "^1.2.9" string.prototype.trimend "^1.0.8" - string.prototype.trimstart "^1.0.7" + string.prototype.trimstart "^1.0.8" typed-array-buffer "^1.0.2" typed-array-byte-length "^1.0.1" typed-array-byte-offset "^1.0.2" - typed-array-length "^1.0.5" + typed-array-length "^1.0.6" unbox-primitive "^1.0.2" which-typed-array "^1.1.15" @@ -3293,10 +3277,10 @@ frappe-js-sdk@^1.5.0: dependencies: axios "^1.6.7" -frappe-react-sdk@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/frappe-react-sdk/-/frappe-react-sdk-1.5.1.tgz#e6d3224e34c210c66a1882da3d140a067d5ccbbc" - integrity sha512-DhkQNWnEwBclUSQF33jFQG1pEwqA7/UBHDnY0AybRaxLN0CeVfMu9Ju6JwMCkS8hAGXVAaKo9fpNJCle3OIi5Q== +frappe-react-sdk@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/frappe-react-sdk/-/frappe-react-sdk-1.6.0.tgz#2216422b776beaeefc7af55e436f719f1245b9aa" + integrity sha512-i43chxCzWmS6nks/FMli+dHFfPJU6jZK7Oivf4enXH6D6U7RiYI6Aoexwda8cQVTQkR7ShFIboRzZX1oT8GOlA== dependencies: frappe-js-sdk "^1.5.0" socket.io-client "4.7.1" @@ -3392,15 +3376,15 @@ glob-parent@^6.0.2: is-glob "^4.0.3" glob@^10.3.10: - version "10.3.10" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b" - integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g== + version "10.3.12" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.12.tgz#3a65c363c2e9998d220338e88a5f6ac97302960b" + integrity sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg== dependencies: foreground-child "^3.1.0" - jackspeak "^2.3.5" + jackspeak "^2.3.6" minimatch "^9.0.1" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - path-scurry "^1.10.1" + minipass "^7.0.4" + path-scurry "^1.10.2" glob@^7.1.6: version "7.2.3" @@ -3498,14 +3482,14 @@ html-dom-parser@5.0.8: htmlparser2 "9.1.0" html-react-parser@^5.1.8: - version "5.1.9" - resolved "https://registry.yarnpkg.com/html-react-parser/-/html-react-parser-5.1.9.tgz#7a8eb3a0b243bddf68f1a77bba5e423933b64161" - integrity sha512-MP0MQDEGlzkJT0OwY//tKYrgIzBM1frYLxx9RF7ALdIjI+MCMumydcNovXDX4X/iDi1zfgaU28VxoNXZn7EPjQ== + version "5.1.10" + resolved "https://registry.yarnpkg.com/html-react-parser/-/html-react-parser-5.1.10.tgz#e65bf68df9b505756680d2cae842f7add3da5305" + integrity sha512-gV22PvLij4wdEdtrZbGVC7Zy2OVWnQ0bYhX63S196ZRSx4+K0TuutCreHSXr+saUia8KeKB+2TYziVfijpH4Tw== dependencies: domhandler "5.0.3" html-dom-parser "5.0.8" react-property "2.0.2" - style-to-js "1.1.11" + style-to-js "1.1.12" htmlparser2@9.1.0: version "9.1.0" @@ -3543,10 +3527,10 @@ inherits@2: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inline-style-parser@0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.2.tgz#d498b4e6de0373458fc610ff793f6b14ebf45633" - integrity sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ== +inline-style-parser@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.3.tgz#e35c5fb45f3a83ed7849fe487336eb7efa25971c" + integrity sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g== internal-slot@^1.0.7: version "1.0.7" @@ -3732,7 +3716,7 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -jackspeak@^2.3.5: +jackspeak@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== @@ -3760,7 +3744,7 @@ jest-worker@^26.2.1: merge-stream "^2.0.0" supports-color "^7.0.0" -jiti@^1.19.1: +jiti@^1.21.0: version "1.21.0" resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== @@ -3896,6 +3880,11 @@ lowlight@^3.1.0: devlop "^1.0.0" highlight.js "~11.9.0" +lru-cache@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" + integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -3903,11 +3892,6 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -"lru-cache@^9.1.1 || ^10.0.0": - version "10.2.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" - integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== - magic-string@^0.25.0, magic-string@^0.25.7: version "0.25.9" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" @@ -3977,13 +3961,13 @@ minimatch@^5.0.1: brace-expansion "^2.0.1" minimatch@^9.0.1: - version "9.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + version "9.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" + integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== dependencies: brace-expansion "^2.0.1" -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4: version "7.0.4" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== @@ -4116,12 +4100,12 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-scurry@^1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" - integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== +path-scurry@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.2.tgz#8f6357eb1239d5fa1da8b9f70e9c080675458ba7" + integrity sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA== dependencies: - lru-cache "^9.1.1 || ^10.0.0" + lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" path-type@^4.0.0: @@ -4312,10 +4296,10 @@ prosemirror-menu@^1.2.4: prosemirror-history "^1.0.0" prosemirror-state "^1.0.0" -prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.19.0, prosemirror-model@^1.19.4, prosemirror-model@^1.8.1: - version "1.19.4" - resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.19.4.tgz#e45e84480c97dd3922095dbe579e1c98c86c0704" - integrity sha512-RPmVXxUfOhyFdayHawjuZCxiROsm9L4FCUA6pWI+l7n2yCBsWy9VpdE1hpDHUS8Vad661YLY9AzqfjLhAKQ4iQ== +prosemirror-model@^1.0.0, prosemirror-model@^1.19.0, prosemirror-model@^1.19.4, prosemirror-model@^1.20.0, prosemirror-model@^1.8.1: + version "1.20.0" + resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.20.0.tgz#4bdc5221f7a58de9fb35d0919687b7e0c765ad53" + integrity sha512-q7AY7vMjKYqDCeoedgUiAgrLabliXxndJuuFmcmc2+YU1SblvnOiG2WEACF2lwAZsMlfLpiAilA3L+TWlDqIsQ== dependencies: orderedmap "^2.0.0" @@ -4371,11 +4355,11 @@ prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transfor prosemirror-model "^1.0.0" prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.27.0, prosemirror-view@^1.31.0, prosemirror-view@^1.32.7: - version "1.33.3" - resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.33.3.tgz#e93564b3aa4beac6ad0244e9b368563d0cc25727" - integrity sha512-P4Ao/bc4OrU/2yLIf8dL4lJaEtjLR3QjIvQHgJYp2jUS7kYM4bSR6okbBjkqzOs/FwUon6UGjTLdKMnPL1MZqw== + version "1.33.4" + resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.33.4.tgz#ff75e7ea3948f1bc00877e4f19150410f9ab5d52" + integrity sha512-xQqAhH8/HGleVpKDhQsrd+oqdyeKMxFtdCWDxWMmP+n0k27fBpyUqa8pA+RB5cFY8rqDDc1hll69aRZQa7UaAw== dependencies: - prosemirror-model "^1.16.0" + prosemirror-model "^1.20.0" prosemirror-state "^1.0.0" prosemirror-transform "^1.1.0" @@ -4424,9 +4408,9 @@ react-dropzone@^14.2.3: prop-types "^15.8.1" react-hook-form@^7.50.1: - version "7.51.1" - resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.51.1.tgz#3ce5f8b5ef41903b4054a641cef8c0dc8bf8ae85" - integrity sha512-ifnBjl+kW0ksINHd+8C/Gp6a4eZOdWyvRv0UBaByShwU8JbVx5hTcTWEcd5VdybvmPTATkVVXk9npXArHmo56w== + version "7.51.3" + resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.51.3.tgz#7486dd2d52280b6b28048c099a98d2545931cab3" + integrity sha512-cvJ/wbHdhYx8aviSWh28w9ImjmVsb5Y05n1+FW786vEZQJV5STNM0pW6ujS+oiBecb0ARBxJFyAnXj9+GHXACQ== react-icons@^5.0.1: version "5.0.1" @@ -4439,9 +4423,9 @@ react-idle-timer@^5.7.2: integrity sha512-+BaPfc7XEUU5JFkwZCx6fO1bLVK+RBlFH+iY4X34urvIzZiZINP6v2orePx3E6pAztJGE7t4DzvL7if2SL/0GQ== react-intersection-observer@^9.8.1: - version "9.8.1" - resolved "https://registry.yarnpkg.com/react-intersection-observer/-/react-intersection-observer-9.8.1.tgz#9c3631c0c9acd624a2af1c192318752ea73b5d91" - integrity sha512-QzOFdROX8D8MH3wE3OVKH0f3mLjKTtEN1VX/rkNuECCff+aKky0pIjulDhr3Ewqj5el/L+MhBkM3ef0Tbt+qUQ== + version "9.8.2" + resolved "https://registry.yarnpkg.com/react-intersection-observer/-/react-intersection-observer-9.8.2.tgz#a45db2441909a4d2f310de381686e151f016691d" + integrity sha512-901naEiiZmse3p+AmtbQ3NL9xx+gQ8TXLiGDc+8GiE3JKJkNV3vP737aGuWTAXBA+1QqxPrDDE+fIEgYpGDlrQ== react-is@^16.13.1: version "16.13.1" @@ -4769,6 +4753,11 @@ socket.io-parser@~4.2.4: "@socket.io/component-emitter" "~3.1.0" debug "~4.3.1" +sonner@^1.4.41: + version "1.4.41" + resolved "https://registry.yarnpkg.com/sonner/-/sonner-1.4.41.tgz#ff085ae4f4244713daf294959beaa3e90f842d2c" + integrity sha512-uG511ggnnsw6gcn/X+YKkWPo5ep9il9wYi3QJxHsYe7yTZ4+cOd1wuodOUmOpFuXL+/RE3R04LczdNCDygTDgQ== + source-map-js@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" @@ -4855,7 +4844,7 @@ string.prototype.trimend@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -string.prototype.trimstart@^1.0.7: +string.prototype.trimstart@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== @@ -4893,19 +4882,19 @@ strip-comments@^2.0.1: resolved "https://registry.yarnpkg.com/strip-comments/-/strip-comments-2.0.1.tgz#4ad11c3fbcac177a67a40ac224ca339ca1c1ba9b" integrity sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw== -style-to-js@1.1.11: - version "1.1.11" - resolved "https://registry.yarnpkg.com/style-to-js/-/style-to-js-1.1.11.tgz#7ba66214cab556fdded4786e80de0baccfa0e942" - integrity sha512-yHpYzXzEkx7iDjGEmE8Eyl4K/hWIm36FXPdRsl2NHEpbigLeawLVsv6tcYp+2xNhfpCrut4w08dYqeCxWMdRxw== +style-to-js@1.1.12: + version "1.1.12" + resolved "https://registry.yarnpkg.com/style-to-js/-/style-to-js-1.1.12.tgz#112dd054231e71643514013a4475d4649bb2b581" + integrity sha512-tv+/FkgNYHI2fvCoBMsqPHh5xovwiw+C3X0Gfnss/Syau0Nr3IqGOJ9XiOYXoPnToHVbllKFf5qCNFJGwFg5mg== dependencies: - style-to-object "1.0.5" + style-to-object "1.0.6" -style-to-object@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-1.0.5.tgz#5e918349bc3a39eee3a804497d97fcbbf2f0d7c0" - integrity sha512-rDRwHtoDD3UMMrmZ6BzOW0naTjMsVZLIjsGleSKS/0Oz+cgCfAPRspaqJuE8rDzpKha/nEvnM0IF4seEAZUTKQ== +style-to-object@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-1.0.6.tgz#0c28aed8be1813d166c60d962719b2907c26547b" + integrity sha512-khxq+Qm3xEyZfKd/y9L3oIWQimxuc4STrQKtQn8aSDRHb8mFgpukgX1hdzfrMEW6JCjyJ8p89x+IUMVnCBI1PA== dependencies: - inline-style-parser "0.2.2" + inline-style-parser "0.2.3" sucrase@^3.32.0: version "3.35.0" @@ -4958,9 +4947,9 @@ tailwindcss-animate@^1.0.7: integrity sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA== tailwindcss@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.1.tgz#f512ca5d1dd4c9503c7d3d28a968f1ad8f5c839d" - integrity sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA== + version "3.4.3" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.3.tgz#be48f5283df77dfced705451319a5dffb8621519" + integrity sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A== dependencies: "@alloc/quick-lru" "^5.2.0" arg "^5.0.2" @@ -4970,7 +4959,7 @@ tailwindcss@^3.4.1: fast-glob "^3.3.0" glob-parent "^6.0.2" is-glob "^4.0.3" - jiti "^1.19.1" + jiti "^1.21.0" lilconfig "^2.1.0" micromatch "^4.0.5" normalize-path "^3.0.0" @@ -5001,9 +4990,9 @@ tempy@^0.6.0: unique-string "^2.0.0" terser@^5.0.0: - version "5.29.2" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.29.2.tgz#c17d573ce1da1b30f21a877bffd5655dd86fdb35" - integrity sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw== + version "5.30.3" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.30.3.tgz#f1bb68ded42408c316b548e3ec2526d7dd03f4d2" + integrity sha512-STdUgOUx8rLbMGO9IOwHLpCqolkDITFFQSMYYwKE1N2lY6MVSaeoi10z/EhWxRc6ybqoVmKSkhKYH/XUpl7vSA== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.8.2" @@ -5104,7 +5093,7 @@ typed-array-byte-offset@^1.0.2: has-proto "^1.0.3" is-typed-array "^1.1.13" -typed-array-length@^1.0.5: +typed-array-length@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== @@ -5117,9 +5106,9 @@ typed-array-length@^1.0.5: possible-typed-array-names "^1.0.0" typescript@^5.3.3: - version "5.4.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.3.tgz#5c6fedd4c87bee01cd7a528a30145521f8e0feff" - integrity sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg== + version "5.4.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" + integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== uc.micro@^2.0.0, uc.micro@^2.1.0: version "2.1.0" diff --git a/raven/__init__.py b/raven/__init__.py index 0f228f258..5197c5f5a 100644 --- a/raven/__init__.py +++ b/raven/__init__.py @@ -1 +1 @@ -__version__ = "1.5.1" +__version__ = "1.5.2" diff --git a/raven/api/chat_stream.py b/raven/api/chat_stream.py index ecf37e363..7399af92c 100644 --- a/raven/api/chat_stream.py +++ b/raven/api/chat_stream.py @@ -51,6 +51,7 @@ def get_messages(channel_id: str, limit: int = 20, base_message: str | None = No message.poll_id, message.is_bot_message, message.bot, + message.hide_link_preview, ) .where(message.channel_id == channel_id) .orderby(message.creation, order=Order.desc) @@ -155,6 +156,7 @@ def fetch_older_messages( message.poll_id, message.is_bot_message, message.bot, + message.hide_link_preview, ) .where(message.channel_id == channel_id) .where( @@ -263,6 +265,7 @@ def fetch_newer_messages( message.poll_id, message.is_bot_message, message.bot, + message.hide_link_preview, ) .where(message.channel_id == channel_id) .where(condition) diff --git a/raven/api/preview_links.py b/raven/api/preview_links.py index 984f7d91b..30997f040 100644 --- a/raven/api/preview_links.py +++ b/raven/api/preview_links.py @@ -46,6 +46,10 @@ def get_preview_link(urls): if preview == None: data = empty_data else: + + # Description might have emojis in them, which comes in with special characters like copyright etc + # TODO: We need to replace these special characters with the actual emojis + data = { "title": preview.title, "description": preview.description, @@ -58,3 +62,14 @@ def get_preview_link(urls): message_links.append(data) return message_links + + +@frappe.whitelist(methods=["POST"]) +def hide_link_preview(message_id: str): + """ + Remove the preview from the message + """ + message = frappe.get_doc("Raven Message", message_id) + message.flags.ignore_permissions = True + message.hide_link_preview = 1 + message.save() diff --git a/raven/api/raven_poll.py b/raven/api/raven_poll.py index 7863b7890..7367f52cd 100644 --- a/raven/api/raven_poll.py +++ b/raven/api/raven_poll.py @@ -136,10 +136,9 @@ def get_all_votes(poll_id): if not frappe.has_permission(doctype="Raven Poll", doc=poll_id, ptype="read"): frappe.throw(_("You do not have permission to access this poll"), frappe.PermissionError) - # Check if the poll is anonymous - is_anonymous = frappe.get_cached_value("Raven Poll", poll_id, "is_anonymous") + poll_doc = frappe.get_cached_doc("Raven Poll", poll_id) - if is_anonymous: + if poll_doc.is_anonymous: frappe.throw(_("This poll is anonymous. You do not have permission to access the votes."), frappe.PermissionError) else: # Get all votes for this poll @@ -150,19 +149,12 @@ def get_all_votes(poll_id): ) # Initialize results dictionary - results = {} + results = {option.name: { 'users': [], 'count': option.votes } for option in poll_doc.options if option.votes} # Process votes for vote in votes: option = vote['option'] - if option not in results: - results[option] = { - 'users': [vote['user_id']], - 'count': 1 - } - else: - results[option]['users'].append(vote['user_id']) - results[option]['count'] += 1 + results[option]['users'].append(vote['user_id']) # Calculate total votes total_votes = sum(result['count'] for result in results.values()) diff --git a/raven/install.py b/raven/install.py index 019058d9e..cbe4b8d3c 100644 --- a/raven/install.py +++ b/raven/install.py @@ -1,20 +1,33 @@ +import click import frappe +from frappe.desk.page.setup_wizard.setup_wizard import make_records def after_install(): - add_standard_navbar_items() - create_general_channel() + try: + print("Setting up Raven...") + add_standard_navbar_items() + create_general_channel() + + click.secho("Thank you for installing Raven!", fg="green") + + except Exception as e: + BUG_REPORT_URL = "https://github.com/The-Commit-Company/Raven/issues/new" + click.secho( + "Installation for Raven failed due to an error." + " Please try re-installing the app or" + f" report the issue on {BUG_REPORT_URL} if not resolved.", + fg="bright_red", + ) + raise e def create_general_channel(): - if not frappe.db.exists("Raven Channel", "general"): - channel = frappe.new_doc("Raven Channel") - channel.channel_name = "General" - channel.name = "general" - channel.type = "Open" - channel.save(ignore_permissions=True) - # Part of installation, hence needs to be committed manually - frappe.db.commit() # nosemgrep + channel = [ + {"doctype": "Raven Channel", "name": "general", "type": "Open", "channel_name": "General"} + ] + + make_records(channel) def add_standard_navbar_items(): diff --git a/raven/package.json b/raven/package.json index c6ae1aa72..af9f8ffa5 100644 --- a/raven/package.json +++ b/raven/package.json @@ -1,6 +1,6 @@ { "name": "raven", - "version": "1.5.1", + "version": "1.5.2", "description": "", "main": "index.js", "scripts": { diff --git a/raven/raven_messaging/doctype/raven_message/raven_message.json b/raven/raven_messaging/doctype/raven_message/raven_message.json index 7d3fb1b74..8f066b294 100644 --- a/raven/raven_messaging/doctype/raven_message/raven_message.json +++ b/raven/raven_messaging/doctype/raven_message/raven_message.json @@ -1,213 +1,220 @@ { - "actions": [], - "allow_rename": 1, - "autoname": "hash", - "creation": "2023-02-12 17:29:25.498988", - "default_view": "List", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "channel_id", - "text", - "json", - "message_reactions", - "is_reply", - "linked_message", - "replied_message_details", - "column_break_wvje", - "message_type", - "content", - "file", - "image_width", - "image_height", - "file_thumbnail", - "thumbnail_width", - "thumbnail_height", - "link_doctype", - "link_document", - "is_edited", - "mentions", - "poll_id", - "is_bot_message", - "bot" - ], - "fields": [ - { - "fieldname": "channel_id", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Channel ID", - "options": "Raven Channel", - "reqd": 1, - "search_index": 1 - }, - { - "fieldname": "text", - "fieldtype": "Long Text", - "label": "Text" - }, - { - "fieldname": "json", - "fieldtype": "JSON", - "label": "JSON" - }, - { - "fieldname": "file", - "fieldtype": "Attach", - "label": "File" - }, - { - "fieldname": "message_type", - "fieldtype": "Select", - "label": "Message Type", - "options": "Text\nImage\nFile\nPoll" - }, - { - "fieldname": "message_reactions", - "fieldtype": "JSON", - "label": "Message Reactions" - }, - { - "default": "0", - "fieldname": "is_reply", - "fieldtype": "Check", - "label": "Is Reply" - }, - { - "fieldname": "linked_message", - "fieldtype": "Link", - "label": "Replied Message ID", - "options": "Raven Message" - }, - { - "fieldname": "file_thumbnail", - "fieldtype": "Attach", - "label": "File Thumbnail" - }, - { - "fieldname": "image_width", - "fieldtype": "Data", - "label": "Image Width" - }, - { - "fieldname": "image_height", - "fieldtype": "Data", - "label": "Image Height" - }, - { - "fieldname": "thumbnail_width", - "fieldtype": "Data", - "label": "Thumbnail Width" - }, - { - "fieldname": "thumbnail_height", - "fieldtype": "Data", - "label": "Thumbnail Height" - }, - { - "fieldname": "link_doctype", - "fieldtype": "Link", - "label": "Link Doctype", - "options": "DocType" - }, - { - "fieldname": "link_document", - "fieldtype": "Dynamic Link", - "label": "Link Document", - "options": "link_doctype" - }, - { - "default": "0", - "fieldname": "is_bot_message", - "fieldtype": "Check", - "label": "Is Bot Message" - }, - { - "depends_on": "eval: doc.is_bot_message == 1", - "fieldname": "bot", - "fieldtype": "Link", - "label": "Bot", - "mandatory_depends_on": "eval: doc.is_bot_message == 1", - "options": "Raven User" - }, - { - "fieldname": "content", - "fieldtype": "Long Text", - "label": "Content", - "read_only": 1 - }, - { - "default": "0", - "fieldname": "is_edited", - "fieldtype": "Check", - "label": "Is Edited" - }, - { - "fieldname": "column_break_wvje", - "fieldtype": "Column Break" - }, - { - "fieldname": "replied_message_details", - "fieldtype": "JSON", - "label": "Replied Message Details" - }, - { - "fieldname": "mentions", - "fieldtype": "Table", - "label": "Mentions", - "options": "Raven Mention" - }, - { - "fieldname": "poll_id", - "fieldtype": "Link", - "label": "Poll ID", - "options": "Raven Poll", - "unique": 1 - } - ], - "index_web_pages_for_search": 1, - "links": [], - "modified": "2024-03-30 15:32:15.182210", - "modified_by": "Administrator", - "module": "Raven Messaging", - "name": "Raven Message", - "naming_rule": "Random", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "select": 1, - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 1, - "print": 1, - "report": 1, - "role": "Raven User", - "share": 1, - "write": 1 - }, - { - "read": 1, - "role": "Raven User" - } - ], - "search_fields": "text", - "sort_field": "modified", - "sort_order": "DESC", - "states": [] + "actions": [], + "allow_rename": 1, + "autoname": "hash", + "creation": "2023-02-12 17:29:25.498988", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "channel_id", + "text", + "json", + "message_reactions", + "is_reply", + "linked_message", + "replied_message_details", + "column_break_wvje", + "message_type", + "content", + "file", + "image_width", + "image_height", + "file_thumbnail", + "thumbnail_width", + "thumbnail_height", + "link_doctype", + "link_document", + "is_edited", + "mentions", + "poll_id", + "is_bot_message", + "bot", + "hide_link_preview" + ], + "fields": [ + { + "fieldname": "channel_id", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Channel ID", + "options": "Raven Channel", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "text", + "fieldtype": "Long Text", + "label": "Text" + }, + { + "fieldname": "json", + "fieldtype": "JSON", + "label": "JSON" + }, + { + "fieldname": "file", + "fieldtype": "Attach", + "label": "File" + }, + { + "fieldname": "message_type", + "fieldtype": "Select", + "label": "Message Type", + "options": "Text\nImage\nFile\nPoll" + }, + { + "fieldname": "message_reactions", + "fieldtype": "JSON", + "label": "Message Reactions" + }, + { + "default": "0", + "fieldname": "is_reply", + "fieldtype": "Check", + "label": "Is Reply" + }, + { + "fieldname": "linked_message", + "fieldtype": "Link", + "label": "Replied Message ID", + "options": "Raven Message" + }, + { + "fieldname": "file_thumbnail", + "fieldtype": "Attach", + "label": "File Thumbnail" + }, + { + "fieldname": "image_width", + "fieldtype": "Data", + "label": "Image Width" + }, + { + "fieldname": "image_height", + "fieldtype": "Data", + "label": "Image Height" + }, + { + "fieldname": "thumbnail_width", + "fieldtype": "Data", + "label": "Thumbnail Width" + }, + { + "fieldname": "thumbnail_height", + "fieldtype": "Data", + "label": "Thumbnail Height" + }, + { + "fieldname": "link_doctype", + "fieldtype": "Link", + "label": "Link Doctype", + "options": "DocType" + }, + { + "fieldname": "link_document", + "fieldtype": "Dynamic Link", + "label": "Link Document", + "options": "link_doctype" + }, + { + "default": "0", + "fieldname": "is_bot_message", + "fieldtype": "Check", + "label": "Is Bot Message" + }, + { + "depends_on": "eval: doc.is_bot_message == 1", + "fieldname": "bot", + "fieldtype": "Link", + "label": "Bot", + "mandatory_depends_on": "eval: doc.is_bot_message == 1", + "options": "Raven User" + }, + { + "fieldname": "content", + "fieldtype": "Long Text", + "label": "Content", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "is_edited", + "fieldtype": "Check", + "label": "Is Edited" + }, + { + "fieldname": "column_break_wvje", + "fieldtype": "Column Break" + }, + { + "fieldname": "replied_message_details", + "fieldtype": "JSON", + "label": "Replied Message Details" + }, + { + "fieldname": "mentions", + "fieldtype": "Table", + "label": "Mentions", + "options": "Raven Mention" + }, + { + "fieldname": "poll_id", + "fieldtype": "Link", + "label": "Poll ID", + "options": "Raven Poll", + "unique": 1 + }, + { + "default": "0", + "fieldname": "hide_link_preview", + "fieldtype": "Check", + "label": "Hide link preview" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-04-12 16:30:21.689774", + "modified_by": "Administrator", + "module": "Raven Messaging", + "name": "Raven Message", + "naming_rule": "Random", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "select": 1, + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 1, + "print": 1, + "report": 1, + "role": "Raven User", + "share": 1, + "write": 1 + }, + { + "read": 1, + "role": "Raven User" + } + ], + "search_fields": "text", + "sort_field": "modified", + "sort_order": "DESC", + "states": [] } \ No newline at end of file diff --git a/raven/raven_messaging/doctype/raven_message/raven_message.py b/raven/raven_messaging/doctype/raven_message/raven_message.py index ee52e5c09..aa175f1fd 100644 --- a/raven/raven_messaging/doctype/raven_message/raven_message.py +++ b/raven/raven_messaging/doctype/raven_message/raven_message.py @@ -29,6 +29,7 @@ class RavenMessage(Document): content: DF.LongText | None file: DF.Attach | None file_thumbnail: DF.Attach | None + hide_link_preview: DF.Check image_height: DF.Data | None image_width: DF.Data | None is_bot_message: DF.Check @@ -135,27 +136,30 @@ def publish_unread_count_event(self): } ), ) + + channel_doc = frappe.get_cached_doc("Raven Channel", self.channel_id) # If the message is a direct message, then we can only send it to one user - is_direct_message = frappe.get_cached_value( - "Raven Channel", self.channel_id, "is_direct_message" - ) - if is_direct_message: - peer_raven_user = frappe.db.get_value( - "Raven Channel Member", - {"channel_id": self.channel_id, "user_id": ("!=", frappe.session.user)}, - "user_id", - ) - peer_user_id = frappe.get_cached_value("Raven User", peer_raven_user, "user") - frappe.publish_realtime( - "raven:unread_channel_count_updated", - { - "channel_id": self.channel_id, - "play_sound": True, - "sent_by": self.owner, - }, - user=peer_user_id, - after_commit=True, - ) + if channel_doc.is_direct_message: + + if not channel_doc.is_self_message: + + peer_raven_user = frappe.db.get_value( + "Raven Channel Member", + {"channel_id": self.channel_id, "user_id": ("!=", frappe.session.user)}, + "user_id", + ) + peer_user_id = frappe.get_cached_value("Raven User", peer_raven_user, "user") + + frappe.publish_realtime( + "raven:unread_channel_count_updated", + { + "channel_id": self.channel_id, + "play_sound": True, + "sent_by": self.owner, + }, + user=peer_user_id, + after_commit=True, + ) # Need to send this to sender as well since they need to update the last message timestamp frappe.publish_realtime( @@ -204,15 +208,11 @@ def send_push_notification(self): # 2. If the message has mentions, send a push notification to the mentioned users if they belong to the channel # 3. If the message is a reply, send a push notification to the user who is being replied to # 4. If the message is in a channel, send a push notification to all the users in the channel (topic) - is_direct_message = frappe.get_cached_value( - "Raven Channel", self.channel_id, "is_direct_message" - ) - if is_direct_message: - is_self_message = frappe.get_cached_value( - "Raven Channel Member", self.channel_id, "is_self_message" - ) - if not is_self_message: + channel_doc = frappe.get_cached_doc("Raven Channel", self.channel_id) + + if channel_doc.is_direct_message: + if not channel_doc.is_self_message: # The message was sent on a direct message channel self.send_notification_for_direct_message() else: @@ -257,6 +257,9 @@ def send_notification_for_direct_message(self): "user_id", ) + if not peer_raven_user: + return + message = self.get_notification_message_content() owner_name = self.get_message_owner_name() @@ -368,6 +371,7 @@ def on_update(self): "message_reactions": self.message_reactions, "is_bot_message": self.is_bot_message, "bot": self.bot, + "hide_link_preview": self.hide_link_preview, }, }, doctype="Raven Channel", @@ -417,6 +421,7 @@ def on_update(self): "name": self.name, "is_bot_message": self.is_bot_message, "bot": self.bot, + "hide_link_preview": self.hide_link_preview, }, }, doctype="Raven Channel", diff --git a/types/Messaging/Message.ts b/types/Messaging/Message.ts index 9c844df1f..b76ff7960 100644 --- a/types/Messaging/Message.ts +++ b/types/Messaging/Message.ts @@ -19,7 +19,8 @@ export interface BaseMessage { replied_message_details?: string, poll_id?: string is_bot_message?: 1 | 0, - bot?: string + bot?: string, + hide_link_preview?: 1 | 0, } export interface FileMessage extends BaseMessage {