Skip to content

Commit

Permalink
Merge pull request #1101 from The-Commit-Company/develop
Browse files Browse the repository at this point in the history
fix: time embedding as an action
  • Loading branch information
nikkothari22 authored Oct 14, 2024
2 parents 313780b + d7e5ed2 commit 69167ab
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 50 deletions.
103 changes: 101 additions & 2 deletions frontend/src/components/feature/chat/ChatInput/TextFormattingMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCurrentEditor } from '@tiptap/react'
import { BiBold, BiCodeAlt, BiCodeBlock, BiHighlight, BiItalic, BiListOl, BiListUl, BiStrikethrough, BiUnderline, BiSolidQuoteAltRight } from 'react-icons/bi'
import { BiBold, BiCodeAlt, BiCodeBlock, BiHighlight, BiItalic, BiListOl, BiListUl, BiStrikethrough, BiUnderline, BiSolidQuoteAltRight, BiTime } 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'
Expand Down Expand Up @@ -214,6 +214,105 @@ export const TextFormattingMenu = () => {
</IconButton>
</Tooltip>
</Flex>
<TimestampButton />
</Flex>
)
}
}

const TimestampButton = () => {
const { editor } = useCurrentEditor()

const parseDates = async (content: string): Promise<string> => {
let parsedContent = content

// Lazy import chrono-node
const chrono = await import('chrono-node')

const parsedDates = chrono.parse(parsedContent, undefined, {
forwardDate: true
})

// Sort parsedDates in reverse order based on their index. This is to ensure that we replace from the end to preserve the indices of the replaced strings.
parsedDates.sort((a, b) => b.index - a.index)

parsedDates.forEach(date => {

// Ignore if neither hour, minute, or date is certain
if (!date.start.isCertain('hour') && !date.start.isCertain('minute') && !date.start.isCertain('day')) {
return
}

const hasStartTime = date.start.isCertain('hour') && date.start.isCertain('minute')

const startTime: number = date.start.date().getTime()

const endTime: number | null = date.end?.date().getTime() ?? null

const hasEndTime = endTime ? date.end?.isCertain('hour') && date.end?.isCertain('minute') : false

// Replace the text with a span containing the timestamp after the given "index")
const index = date.index
const text = date.text

let attributes = ''
if (startTime) attributes += `data-timestamp-start="${startTime}"`

if (endTime) attributes += ` data-timestamp-end="${endTime}"`

if (!hasStartTime) {
attributes += ' data-timestamp-start-all-day="true"'
}

if (!hasEndTime) {
attributes += ' data-timestamp-end-all-day="true"'
}

parsedContent = parsedContent.slice(0, index) + `<span class="timestamp" ${attributes}">${text}</span>` + parsedContent.slice(index + text.length)
})
return parsedContent
}

const onClick = async () => {
if (editor) {
// Check if editor has selected text
const { from, to, replaceWith, empty } = editor.state.selection

if (empty) {
const content = editor.getHTML()
const parsedContent = await parseDates(content)
editor.chain().focus().setContent(parsedContent).run()
} else {
const selectedText = editor.view.state.doc.textBetween(from, to, ' ')
const parsedContent = await parseDates(selectedText)

// Replace only the selected text with the parsed content
editor.chain().focus().deleteSelection().insertContent(parsedContent).run()
}

}

}

if (!editor) {
return <Box></Box>
}


return <Flex gap='3' align='center'>
<Tooltip content={getKeyboardMetaKeyString() + ' + Alt + H'} aria-label={getKeyboardMetaKeyString() + ' + Alt + H'}>
<IconButton
aria-label='Parse timestamps from message'
onClick={onClick}
title='Parse timestamps from message'
variant='ghost'
size='1'
className={DEFAULT_BUTTON_STYLE}
disabled={!editor.can().chain().focus().run()}
>
<BiTime {...ICON_PROPS} />
</IconButton>
</Tooltip>
</Flex>


}
4 changes: 3 additions & 1 deletion frontend/src/components/feature/chat/ChatInput/Tiptap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { useIsDesktop } from '@/hooks/useMediaQuery'
import { BiPlus } from 'react-icons/bi'
import clsx from 'clsx'
import { ChannelMembers } from '@/hooks/fetchers/useFetchChannelMembers'
import TimestampRenderer from '../ChatMessage/Renderers/TiptapRenderer/TimestampRenderer'
const MobileInputActions = lazy(() => import('./MobileActions/MobileInputActions'))

const lowlight = createLowlight(common)
Expand Down Expand Up @@ -446,7 +447,8 @@ const Tiptap = ({ isEdit, slotBefore, fileProps, onMessageSend, channelMembers,

}
}),
EmojiSuggestion
EmojiSuggestion,
TimestampRenderer
]

const [content, setContent] = useSessionStickyState(defaultText, sessionStorageKey, disableSessionStorage)
Expand Down
46 changes: 0 additions & 46 deletions frontend/src/components/feature/chat/ChatInput/useSendMessage.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,13 @@
import { useFrappePostCall } from 'frappe-react-sdk'
import * as chrono from 'chrono-node';
import { Message } from '../../../../../../types/Messaging/Message'

export const useSendMessage = (channelID: string, noOfFiles: number, uploadFiles: () => Promise<void>, handleCancelReply: VoidFunction, selectedMessage?: Message | null) => {

const { call, loading } = useFrappePostCall('raven.api.raven_message.send_message')

const parseDates = (content: string): string => {

let parsedContent = content

const parsedDates = chrono.parse(parsedContent, undefined, {
forwardDate: true
})

// Sort parsedDates in reverse order based on their index. This is to ensure that we replace from the end to preserve the indices of the replaced strings.
parsedDates.sort((a, b) => b.index - a.index)

parsedDates.forEach(date => {

const hasStartTime = date.start.isCertain('hour') && date.start.isCertain('minute')

const startTime: number = date.start.date().getTime()

const endTime: number | null = date.end?.date().getTime() ?? null

const hasEndTime = endTime ? date.end?.isCertain('hour') && date.end?.isCertain('minute') : false

// Replace the text with a span containing the timestamp after the given "index")
const index = date.index
const text = date.text

let attributes = ''
if (startTime) attributes += `data-timestamp-start="${startTime}"`

if (endTime) attributes += ` data-timestamp-end="${endTime}"`

if (!hasStartTime) {
attributes += ' data-timestamp-start-all-day="true"'
}

if (!hasEndTime) {
attributes += ' data-timestamp-end-all-day="true"'
}

parsedContent = parsedContent.slice(0, index) + `<span class="timestamp" ${attributes}">${text}</span>` + parsedContent.slice(index + text.length)
})
return parsedContent
}

const sendMessage = async (content: string, json?: any): Promise<void> => {

if (content) {
// Parse the content to replace any "human" dates with a timestamp element
content = parseDates(content)

return call({
channel_id: channelID,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export default Node.create({

addAttributes() {
return {
class: {
default: 'timestamp',
},
'data-timestamp-start': {
default: '',
},
Expand All @@ -45,7 +48,7 @@ export default Node.create({
},

renderHTML({ HTMLAttributes, node }) {
return ['span', mergeAttributes(HTMLAttributes)]
return ['span', mergeAttributes(HTMLAttributes), 0]
},

addNodeView() {
Expand Down

0 comments on commit 69167ab

Please sign in to comment.