import { Dropdown, Dialog, dialog } from "mdui" import { $ } from "mdui/jq" import Client from "../../api/Client.ts" import Data_Message from "../../api/client_data/Message.ts" import DataCaches from "../../api/DataCaches.ts" import Avatar from "../Avatar.tsx" import copyToClipboard from "../copyToClipboard.ts" import useAsyncEffect from "../useAsyncEffect.ts" import useEventListener from "../useEventListener.ts" import React from "react" import isMobileUI from "../isMobileUI.ts" import ReactJson from 'react-json-view' import User from "../../api/client_data/User.ts" import getUrlForFileByHash from "../../getUrlForFileByHash.ts" interface Args extends React.HTMLAttributes { userId: string rawData: string renderHTML: string message: Data_Message openUserInfoDialog: (user: User | string) => void } function prettyFlatParsedMessage(html: string) { const elements = new DOMParser().parseFromString(html, 'text/html').body.children let ls: Element[] = [] let ret = '' // 第一个元素时, 不会被聚合在一起 let lastElementType = '' const textElementTags = [ 'chat-text', 'chat-mention', ] function checkContinuousElement(tagName: string) { if (lastElementType != tagName) { if (textElementTags.indexOf(lastElementType) != -1) { // 由于 chat-mention 不是用内部元素实现的, 因此在这个元素的生成中必须放置占位字符串 // 尽管显示上占位字符串不会显示, 但是在这里依然是会被处理的, 因为本身还是 innerHTML if (ls.map((v) => v.innerHTML).join('').trim() != '') ret += `${ls.map((v) => v.outerHTML).join('')}` } else ret += ls.map((v) => v.outerHTML).join('') ls = [] } } for (const e of elements) { // 当出现非文本元素时, 将文本聚合在一起 // 如果是其他类型, 虽然也执行聚合, 但是不会有外层包裹 checkContinuousElement(e.nodeName.toLowerCase()) ls.push(e) lastElementType = e.nodeName.toLowerCase() } // 最后将剩余的转换 checkContinuousElement('') return ret } export default function Message({ userId, rawData, renderHTML, message, openUserInfoDialog, ...props }: Args) { const isAtRight = Client.myUserProfile?.id == userId const [nickName, setNickName] = React.useState("") const [avatarUrl, setAvatarUrl] = React.useState("") useAsyncEffect(async () => { const user = await DataCaches.getUserProfile(userId) setNickName(user.nickname) setAvatarUrl(getUrlForFileByHash(user?.avatar_file_hash)) }, [userId]) const dropDownRef = React.useRef(null) const messageJsonDialogRef = React.useRef(null) useEventListener(messageJsonDialogRef, 'click', (e) => { e.stopPropagation() }) useEventListener(dropDownRef, 'closed', (e) => { setDropDownOpen(false) }) const [isDropDownOpen, setDropDownOpen] = React.useState(false) /* const [isUsingFullDisplay, setIsUsingFullDisplay] = React.useState(false) */ /* React.useEffect(() => { const text = $(dropDownRef.current as HTMLElement).find('#msg').text().trim() setIsUsingFullDisplay(text == '' || ( rawData.split("tws:\/\/file\?hash=").length == 2 && /\<\/chat\-(file|image|video)\>(\<\/span\>)?$/.test(renderHTML.trim()) )) }, [renderHTML]) */ return (
{ if (isMobileUI()) return e.preventDefault() setDropDownOpen(!isDropDownOpen) }} onClick={(e) => { if (!isMobileUI()) return e.preventDefault() setDropDownOpen(!isDropDownOpen) }} style={{ width: "100%", display: "flex", justifyContent: isAtRight ? "flex-end" : "flex-start", flexDirection: "column" }} {...props}>
{ // 发送者昵称(左) isAtRight && {nickName} } { // 发送者头像 } { e.stopPropagation() openUserInfoDialog(userId) }} /> { // 发送者昵称(右) !isAtRight && {nickName} }
{ // @ts-ignore 这是可以正常工作的 } { e.stopPropagation() setDropDownOpen(false) }}> copyToClipboard($(dropDownRef.current as HTMLElement).find('#msg').text().trim())}>复制文字 copyToClipboard(rawData)}>复制原文 messageJsonDialogRef.current!.open = true}>JSON
) }