import { Tab, TextField } from "mdui" import useEventListener from "../useEventListener.ts" import Element_Message from "./Message.tsx" import MessageContainer from "./MessageContainer.tsx" import * as React from 'react' import Client from "../../api/Client.ts" import Message from "../../api/client_data/Message.ts" import Chat from "../../api/client_data/Chat.ts" import data from "../../Data.ts" import { checkApiSuccessOrSncakbar } from "../snackbar.ts" import useAsyncEffect from "../useAsyncEffect.ts" import * as marked from 'marked' import DOMPurify from 'dompurify' interface Args extends React.HTMLAttributes { target: string showReturnButton?: boolean onReturnButtonClicked?: () => void } const markedInstance = new marked.Marked({ renderer: { heading({ tokens, depth: _depth }) { const text = this.parser.parseInline(tokens) return `${text}` }, paragraph({ tokens, depth: _depth }) { const text = this.parser.parseInline(tokens) return `${text}` }, image({ title, href }) { return `` } } }) export default function ChatFragment({ target, showReturnButton, onReturnButtonClicked, ...props }: Args) { const [messagesList, setMessagesList] = React.useState([] as Message[]) const [chatInfo, setChatInfo] = React.useState({ title: '加載中...' } as Chat) const [tabItemSelected, setTabItemSelected] = React.useState('Chat') const tabRef = React.useRef(null) const chatPanelRef = React.useRef(null) useEventListener(tabRef, 'change', () => { setTabItemSelected(tabRef.current?.value || "Chat") }) useAsyncEffect(async () => { const re = await Client.invoke('Chat.getInfo', { token: data.access_token, target: target, }) if (re.code != 200) return checkApiSuccessOrSncakbar(re, "對話錯誤") setChatInfo(re.data as Chat) loadMore() }, [target]) const page = React.useRef(0) async function loadMore() { const re = await Client.invoke("Chat.getMessageHistory", { token: data.access_token, target, page: page.current, }) if (checkApiSuccessOrSncakbar(re, "拉取歷史記錄失敗")) return const returnMsgs = (re.data!.messages as Message[]).reverse() if (returnMsgs.length == 0) { setShowNoMoreMessagesTip(true) setTimeout(() => setShowNoMoreMessagesTip(false), 1000) return } setMessagesList(returnMsgs.concat(messagesList)) if (page.current == 0) setTimeout(() => chatPanelRef.current!.scrollTo({ top: 10000000000, behavior: "smooth", }), 100) page.current++ } React.useEffect(() => { interface OnMessageData { chat: string msg: Message } Client.on('Client.onMessage', (data: unknown) => { const { chat, msg } = (data as OnMessageData) if (target == chat) { setMessagesList(messagesList.concat([msg])) if ((chatPanelRef.current!.scrollHeight - chatPanelRef.current!.scrollTop - chatPanelRef.current!.clientHeight) < 80) setTimeout(() => chatPanelRef.current!.scrollTo({ top: 10000000000, behavior: "smooth", }), 100) } }) return () => { Client.off('Client.onMessage') } }) const inputRef = React.useRef(null) const [showLoadingMoreMessagesTip, setShowLoadingMoreMessagesTip] = React.useState(false) const [showNoMoreMessagesTip, setShowNoMoreMessagesTip] = React.useState(false) async function sendMessage() { const text = inputRef.current!.value const re = await Client.invoke("Chat.sendMessage", { token: data.access_token, target, text, }, 5000) if (checkApiSuccessOrSncakbar(re, "發送失敗")) return inputRef.current!.value = '' } return (
{ showReturnButton && } { chatInfo.title } 設定 { const scrollTop = (e.target as HTMLDivElement).scrollTop if (scrollTop == 0 && !showLoadingMoreMessagesTip) { setShowLoadingMoreMessagesTip(true) await loadMore() setShowLoadingMoreMessagesTip(false) } }}>
加載中...
沒有更多消息啦~
{ messagesList.map((msg) =>
) }
{ // 输入框 }
{ if (e.dataTransfer.files) { const files = e.dataTransfer.files // 基于当前的实现, 浏览器不会读取文件的字节流来确定其媒体类型, 其根据文件扩展名进行假设 // https://developer.mozilla.org/zh-CN/docs/Web/API/Blob/type for (const file of files) { if (file.type.startsWith("image/")) { } } } }}> { if (event.ctrlKey && event.key == 'Enter') sendMessage() }} style={{ marginRight: '10px', marginTop: '3px', marginBottom: '3px', }}> sendMessage()}>
Work in progress...
) }