Skip to content

Commit

Permalink
chat: reply menu item, show reply in ui, cleanups
Browse files Browse the repository at this point in the history
  • Loading branch information
natew committed Dec 30, 2024
1 parent 161dd06 commit 557c40d
Show file tree
Hide file tree
Showing 11 changed files with 231 additions and 73 deletions.
36 changes: 24 additions & 12 deletions apps/chat/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import './_layout.css'
import { ZeroProvider } from '@rocicorp/zero/react'
import { SchemeProvider, useColorScheme } from '@vxrn/color-scheme'
import { LoadProgressBar, Slot } from 'one'
import { useLayoutEffect, useState } from 'react'
import { useEffect, useLayoutEffect, useState } from 'react'
import { isWeb, TamaguiProvider } from 'tamagui'
import { AuthEffects } from '~/better-auth/AuthEffects'
import { Dialogs } from '~/interface/dialogs/Dialogs'
Expand All @@ -14,6 +14,7 @@ import config from '~/tamagui/tamagui.config'
import { isTauri } from '~/tauri/constants'
import { useZeroEmit, zero } from '~/zero'
import { Gallery } from '~/interface/gallery/Gallery'
import { showToast, ToastProvider } from '~/interface/toast/Toast'

export default function Layout() {
useLayoutEffect(() => {
Expand All @@ -22,6 +23,15 @@ export default function Layout() {
}
}, [isTauri])

// if web, send errors to showToast
if (isWeb) {
useEffect(() => {
window.addEventListener('error', (e) => {
showToast(e.message)
})
}, [])
}

return (
<>
{isWeb && (
Expand All @@ -42,17 +52,19 @@ export default function Layout() {

<AuthEffects />

<DragDropFile>
<DataProvider>
<SchemeProvider>
<ThemeProvider>
<Slot />
<Dialogs />
<Gallery />
</ThemeProvider>
</SchemeProvider>
</DataProvider>
</DragDropFile>
<ToastProvider>
<DragDropFile>
<DataProvider>
<SchemeProvider>
<ThemeProvider>
<Slot />
<Dialogs />
<Gallery />
</ThemeProvider>
</SchemeProvider>
</DataProvider>
</DragDropFile>
</ToastProvider>
</>
)
}
Expand Down
2 changes: 1 addition & 1 deletion apps/chat/src/interface/TopBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const TopBar = memo(() => {
pr={4}
mb={4}
>
<XStack gap="$2">
<XStack pe="box-none" gap="$2">
<TooltipSimple label="Menu">
<HotMenu />
</TooltipSimple>
Expand Down
14 changes: 11 additions & 3 deletions apps/chat/src/interface/gallery/Gallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AnimatePresence } from '@tamagui/animate-presence'
import { Image } from '@tamagui/image-next'
import { ChevronLeft, ChevronRight, X } from '@tamagui/lucide-icons'
import { createEmitter } from '@vxrn/emitter'
import { useState } from 'react'
import { useEffect, useState } from 'react'
import { useWindowDimensions } from 'react-native'
import { Button, type ButtonProps, styled, XStack, YStack } from 'tamagui'
import type { Attachment } from '~/zero'
Expand All @@ -26,12 +26,12 @@ const GalleryItem = styled(YStack, {
going: {
':number': (going) => ({
enterStyle: {
x: going > 0 ? 1000 : -1000,
x: going === 0 ? 0 : going > 0 ? 1000 : -1000,
opacity: 0,
},
exitStyle: {
zIndex: 0,
x: going < 0 ? 1000 : -1000,
x: going === 0 ? 0 : going < 0 ? 1000 : -1000,
opacity: 0,
},
}),
Expand All @@ -49,6 +49,14 @@ export const Gallery = () => {
setPage([page + going, going])
}

const firstItem = galleryItems?.firstItem

useEffect(() => {
if (firstItem) {
setPage([items.findIndex((x) => x.id === firstItem), 0])
}
}, [firstItem])

return (
<XStack
animation="quickest"
Expand Down
92 changes: 49 additions & 43 deletions apps/chat/src/interface/main/MainOpenThread.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,62 @@
import { YStack } from 'tamagui'
import { Configuration, YStack } from 'tamagui'
import { updateUserCurrentChannel, useCurrentThread } from '~/state/user'
import { animationsCSS } from '~/tamagui/animations.css'
import { ButtonClose } from '../ButtonClose'
import { MessagesList } from '../messages/MessagesList'
import { MessageInput } from '../messages/MessageInput'
import { MessagesList } from '../messages/MessagesList'

export const MainOpenThread = () => {
const thread = useCurrentThread()

return (
<YStack
bg="$color2"
shadowColor="$shadowColor"
shadowRadius={10}
animation={[
'quicker',
{
opacity: {
overshootClamping: true,
<Configuration animationDriver={animationsCSS}>
<YStack
bg="$color2"
shadowColor="$shadowColor"
shadowRadius={10}
animation={[
'quickest',
{
opacity: {
overshootClamping: true,
},
},
},
]}
pos="absolute"
t={0}
r={0}
b={0}
w="70%"
zi={1000}
{...(thread
? {}
: {
opacity: 0,
pe: 'none',
x: 7,
})}
>
<ButtonClose
]}
pos="absolute"
t={0}
r={0}
b={0}
w="70%"
zi={1000}
t={10}
l={-20}
onPress={() => {
updateUserCurrentChannel({
openedThreadId: undefined,
})
}}
/>
{thread && (
<>
<MessagesList messages={thread?.messages || []} />
<MessageInput inThread />
</>
)}
</YStack>
{...(thread
? {
opacity: 1,
x: 0,
}
: {
opacity: 0,
pe: 'none',
x: 7,
})}
>
<ButtonClose
pos="absolute"
zi={1000}
t={10}
l={-20}
onPress={() => {
updateUserCurrentChannel({
openedThreadId: undefined,
})
}}
/>
{thread && (
<>
<MessagesList messages={thread?.messages || []} />
<MessageInput inThread />
</>
)}
</YStack>
</Configuration>
)
}
20 changes: 17 additions & 3 deletions apps/chat/src/interface/messages/MessageInput.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { createEmitter } from '@vxrn/emitter'
import { useEffect, useRef, useState } from 'react'
import { Progress, XStack, YStack } from 'tamagui'
import { useAuth } from '~/better-auth/authClient'
Expand All @@ -15,12 +14,12 @@ import { type Attachment, zero } from '~/zero'
import { AttachmentItem } from '../attachments/AttachmentItem'
import { attachmentEmitter } from '../upload/DragDropFile'
import type { FileUpload } from '../upload/uploadImage'
import { messageInputEmitter, messageReplyEmitter } from './emitters'
import { MessageInputReply } from './MessageInputReply'
import { messagesListEmitter } from './MessagesList'

let mainInputRef: EditorRef | null = null

export const messageInputEmitter = createEmitter<{ type: 'submit' }>()

export const MessageInput = ({ inThread }: { inThread?: boolean }) => {
const inputRef = useRef<EditorRef>(null)
const channel = useCurrentChannel()
Expand Down Expand Up @@ -52,6 +51,14 @@ export const MessageInput = ({ inThread }: { inThread?: boolean }) => {
}
}, [channel, inThread])

messageInputEmitter.use((value) => {
if (value.type === 'focus') {
setTimeout(() => {
mainInputRef?.textarea?.focus()
})
}
})

return (
<YStack
btw={1}
Expand All @@ -63,6 +70,8 @@ export const MessageInput = ({ inThread }: { inThread?: boolean }) => {
pointerEvents: 'none',
})}
>
<MessageInputReply />

<Editor
ref={inputRef}
onKeyDown={(e) => {
Expand Down Expand Up @@ -92,6 +101,11 @@ export const MessageInput = ({ inThread }: { inThread?: boolean }) => {
}

case 'Escape': {
if (messageReplyEmitter.value?.type === 'reply') {
messageReplyEmitter.emit({ type: 'cancel' })
return
}

inputRef.current?.textarea?.blur()

if (getDerivedUserState().activeThread) {
Expand Down
55 changes: 55 additions & 0 deletions apps/chat/src/interface/messages/MessageInputReply.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { X } from '@tamagui/lucide-icons'
import { useState } from 'react'
import { Button, SizableText, XStack } from 'tamagui'
import { useQuery } from '~/zero'
import { Avatar } from '../Avatar'
import { showToast } from '../toast/Toast'
import { messageInputEmitter, messageReplyEmitter } from './emitters'

export const MessageInputReply = () => {
const [replyId, setReplyId] = useState('')
const message = useQuery((q) =>
q.message.where('id', replyId).related('sender', (q) => q.one())
)[0][0]
const sender = message?.sender

messageReplyEmitter.use((value) => {
switch (value.type) {
case 'reply': {
setReplyId(value.messageId)
messageInputEmitter.emit({ type: 'focus' })
break
}
case 'cancel': {
setReplyId('')
break
}
}
})

if (!message || !sender) {
return null
}

return (
<XStack bg="$color6" py="$1.5" px="$2" ai="center" gap="$3" br="$3">
<Button
onPress={() => {
messageReplyEmitter.emit({ type: 'cancel' })
}}
size="$1"
circular
icon={X}
/>

<SizableText size="$3">Replying to</SizableText>

<XStack gap="$2">
<Avatar size={24} image={sender.image} />
<SizableText size="$3" fow="500">
{sender.name || sender.username}
</SizableText>
</XStack>
</XStack>
)
}
19 changes: 18 additions & 1 deletion apps/chat/src/interface/messages/MessageItem.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IndentIncrease } from '@tamagui/lucide-icons'
import MDEditor from '@uiw/react-md-editor'
import { memo } from 'react'
import { memo, useState } from 'react'
import { SizableText, XStack, YStack } from 'tamagui'
import { Editor } from '~/editor/Editor'
import { Avatar } from '~/interface/Avatar'
Expand All @@ -17,6 +17,7 @@ import { MessageReactions } from './MessageReactions'
import { messageHover } from './constants'
import { AttachmentItem } from '../attachments/AttachmentItem'
import { galleryEmitter } from '../gallery/Gallery'
import { messageReplyEmitter } from './emitters'

export const MessageItem = memo(
({
Expand Down Expand Up @@ -45,6 +46,15 @@ export const MessageItem = memo(
const isMyMessage = sender?.id === user?.id
const isFocused = !disableEvents && channelState?.focusedMessageId === message.id
const isEditing = channelState?.editingMessageId === message.id
const [isReplying, setIsReplying] = useState(false)

messageReplyEmitter.use((value) => {
if (value.type === 'reply') {
setIsReplying(value.messageId === message.id)
} else {
setIsReplying(false)
}
})

return (
<XStack
Expand Down Expand Up @@ -74,6 +84,13 @@ export const MessageItem = memo(
openThread()
},
})}
{...(isReplying && {
backgroundColor: '$blue5',
borderColor: '$blue7',
hoverStyle: {
backgroundColor: '$blue5',
},
})}
>
<MessageActionBar channel={channel} message={message} />

Expand Down
Loading

0 comments on commit 557c40d

Please sign in to comment.