Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(das-dui-tool): Refactor chat components #35

Merged
merged 1 commit into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 9 additions & 215 deletions apps/das-dui-tool/app/(auth)/(app)/chats/[chatId].tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
/* eslint-disable react/display-name */
import { Fragment, LegacyRef, ReactNode, forwardRef } from "react"
import { StyleSheet } from "react-native"

import * as Linking from "expo-linking"
import { Link, Stack, useLocalSearchParams } from "expo-router"

import {
Expand All @@ -12,28 +7,13 @@ import {
useQueryClient,
} from "@tanstack/react-query"

import {
H2,
Image,
ListItem,
ListItemSubtitle,
SizableText,
Spinner,
TamaguiElement,
Text,
View,
} from "tamagui"
import { H2, Spinner } from "tamagui"

import { FlashList } from "@shopify/flash-list"

import { NewsResponse } from "@das-dui/api-client"

import FileLink from "@/components/cloud/FileLink"
import FileListItemLink from "@/components/cloud/FileListItemLink"
import NewsListItem from "@/components/news/NewsListItem"
import ChatItem from "@/components/chat/ChatItem"
import GenericIcon from "@/components/ui/GenericIcon"
import MessageInput from "@/components/ui/MessageInput"
import RenderHTMLGeneric from "@/components/ui/RenderHTMLGeneric"
import { useGetUserId } from "@/context/userId"
import useApiClient from "@/hooks/useApiClient"

Expand Down Expand Up @@ -86,7 +66,7 @@ export default function ChatPage() {
<Stack.Screen
options={{
title: displayname,
headerTitle(props: { children: ReactNode }) {
headerTitle({ children }) {
return (
<Link
push
Expand All @@ -97,7 +77,7 @@ export default function ChatPage() {
},
}}
>
<H2>{props.children}</H2>
<H2>{children}</H2>
</Link>
)
},
Expand All @@ -124,9 +104,10 @@ export default function ChatPage() {
/>
<FlashList
contentContainerStyle={{ padding: 20 }}
data={messages ?? []}
onEndReached={() => fetchNextPage()}
ListEmptyComponent={<Spinner />}
data={messages}
extraData={{ messages: messages, userId: userId }}
onEndReached={fetchNextPage}
ListEmptyComponent={Spinner}
ListFooterComponent={
<>{isFetchingNextPage && <Spinner mb="$4" />}</>
}
Expand All @@ -142,196 +123,9 @@ export default function ChatPage() {
isLoading={chatMutation.isPending}
/>
}
disableAutoLayout
CellRendererComponent={forwardRef(
(props: { index: number; children: ReactNode }, ref) => {
const messageDate = new Date(
(messages ?? [])[props.index]?.created_at
)
const prevMessageDate = new Date(
(messages ?? [])[props.index + 1]?.created_at
)
return (
<View
ref={ref as LegacyRef<TamaguiElement>}
{...props}
py="$2"
>
{messageDate.getDate() !==
prevMessageDate.getDate() && (
<View alignItems="center" pb="$4">
<View
p="$2"
theme={"alt1"}
bw="$0.5"
bc={"$placeholderColor"}
br={"$4"}
>
<Text>
{messageDate.toLocaleDateString()}
</Text>
</View>
</View>
)}
{props.children}
</View>
)
}
)}
renderItem={({ item }) => (
<Fragment>
{item.type === "MESSAGE" && (
<View
alignItems={
item.user.id === userId
? "flex-end"
: "flex-start"
}
flex={1}
>
<View
py="$2"
px="$4"
bw="$0.5"
bc={"$placeholderColor"}
br={"$4"}
maxWidth={"80%"}
>
<ListItemSubtitle>
{item.user.meta.displayname}
</ListItemSubtitle>
{item.preview && (
<ListItem
onPress={() =>
Linking.openURL(
item.preview?.link ?? ""
)
}
pressTheme
hoverTheme
br="$4"
title={item.preview.title}
subTitle={item.preview.description}
/>
)}
{item.file ? (
<>
{item.file.type?.startsWith(
"image"
) ? (
<FileLink
cloud_id={
item.file.cloud_id
}
file_type={
item.file.file_type
}
name={item.file.name}
path={item.file.path}
uuid={item.file.uuid}
>
<Image
source={{
uri:
item.file.meta
.thumbnail_uri ??
item.file.meta
.uri,
}}
style={styles.image}
/>
</FileLink>
) : (
<FileListItemLink
cloud_id={
item.file.cloud_id
}
extension={
item.file.extension
}
file_type={
item.file.file_type
}
meta={item.file.meta}
name={item.file.name}
path={item.file.path}
uuid={item.file.uuid}
/>
)}
</>
) : (
<RenderHTMLGeneric
content={
item.content_rendered ||
"<i>Deleted</i>"
}
/>
)}

<View alignSelf="flex-end">
<SizableText fontSize={"$3"}>
{new Date(
item.created_at
).toLocaleTimeString()}
</SizableText>
</View>
</View>
</View>
)}
{item.type === "HINT" && (
<View alignItems={"center"} flex={1}>
{item.target_type === "news" ? (
<View
width={"100%"}
py="$2"
px="$4"
bw="$0.5"
bc={"$placeholderColor"}
br={"$4"}
>
<ListItemSubtitle>
{item.user.meta.displayname}
</ListItemSubtitle>
<NewsListItem
item={
item.target as NewsResponse.News
}
/>
<View alignSelf="flex-end">
<SizableText fontSize={"$3"}>
{new Date(
item.created_at
).toLocaleTimeString()}
</SizableText>
</View>
</View>
) : (
<View
py="$2"
px="$4"
theme={"alt1"}
backgroundColor={"$blue4"}
br={"$4"}
>
<RenderHTMLGeneric
content={item.content_rendered}
/>
</View>
)}
</View>
)}
</Fragment>
)}
renderItem={ChatItem}
inverted
/>
</>
)
}

const styles = StyleSheet.create({
image: {
width: 200,
height: 150,
resizeMode: "contain",
},
})
54 changes: 54 additions & 0 deletions apps/das-dui-tool/components/chat/ChatHint.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { FC } from "react"

import { ListItemSubtitle, SizableText, View } from "tamagui"

import { ChatMessagesResponse, NewsResponse } from "@das-dui/api-client"

import NewsListItem from "@/components/news/NewsListItem"
import RenderHTMLGeneric from "@/components/ui/RenderHTMLGeneric"

interface ChatHintProps {
message: ChatMessagesResponse.ChatMessage
}

const ChatHint: FC<ChatHintProps> = ({ message }) => {
if (message.target_type === "news")
return (
<View alignItems={"center"} flex={1}>
<View
width={"100%"}
py="$2"
px="$4"
bw="$0.5"
bc={"$placeholderColor"}
br={"$4"}
>
<ListItemSubtitle>
{message.user.meta.displayname}
</ListItemSubtitle>
<NewsListItem item={message.target as NewsResponse.News} />
<View alignSelf="flex-end">
<SizableText fontSize={"$3"}>
{new Date(message.created_at).toLocaleTimeString()}
</SizableText>
</View>
</View>
</View>
)

return (
<View alignItems={"center"} flex={1}>
<View
py="$2"
px="$4"
theme={"alt1"}
backgroundColor={"$blue4"}
br={"$4"}
>
<RenderHTMLGeneric content={message.content_rendered} />
</View>
</View>
)
}

export default ChatHint
26 changes: 26 additions & 0 deletions apps/das-dui-tool/components/chat/ChatItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ListRenderItem } from "@shopify/flash-list"

import { ChatMessagesResponse } from "@das-dui/api-client"

import ChatHint from "@/components/chat/ChatHint"
import ChatItemWrapper from "@/components/chat/ChatItemWrapper"
import ChatMessage from "@/components/chat/ChatMessage"

const ChatItem: ListRenderItem<ChatMessagesResponse.ChatMessage> = (info) => {
const { item, index } = info
const extraData = info.extraData as {
messages: ChatMessagesResponse.ChatMessage[]
userId: number
}

return (
<ChatItemWrapper messages={extraData.messages} index={index}>
{item.type === "MESSAGE" && (
<ChatMessage message={item} userId={extraData.userId} />
)}
{item.type === "HINT" && <ChatHint message={item} />}
</ChatItemWrapper>
)
}

export default ChatItem
39 changes: 39 additions & 0 deletions apps/das-dui-tool/components/chat/ChatItemWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { FC, PropsWithChildren } from "react"

import { Text, View } from "tamagui"

import { ChatMessagesResponse } from "@das-dui/api-client"

interface ChatItemWrapperProps {
messages: ChatMessagesResponse.ChatMessage[]
index: number
}

const ChatItemWrapper: FC<PropsWithChildren<ChatItemWrapperProps>> = ({
messages,
index,
children,
}) => {
const messageDate = new Date((messages ?? [])[index]?.created_at)
const prevMessageDate = new Date((messages ?? [])[index + 1]?.created_at)
return (
<View py="$2">
{messageDate.getDate() !== prevMessageDate.getDate() && (
<View alignItems="center" pb="$4">
<View
p="$2"
theme={"alt1"}
bw="$0.5"
bc={"$placeholderColor"}
br={"$4"}
>
<Text>{messageDate.toLocaleDateString()}</Text>
</View>
</View>
)}
{children}
</View>
)
}

export default ChatItemWrapper
Loading