完善对话页面!
* 消息组件显示移植 * 最近对话直接打开的补充 * 提及的修补 * ......
This commit is contained in:
@@ -1,8 +1,15 @@
|
|||||||
import { $ } from 'mdui'
|
import { $ } from 'mdui'
|
||||||
import showSnackbar from "../../utils/showSnackbar.ts";
|
import showSnackbar from "../../utils/showSnackbar.ts"
|
||||||
customElements.define('chat-mention', class extends HTMLElement {
|
import { Chat, User } from 'lingchair-client-protocol'
|
||||||
|
|
||||||
|
export default class ChatMentionElement extends HTMLElement {
|
||||||
declare link: HTMLAnchorElement
|
declare link: HTMLAnchorElement
|
||||||
static observedAttributes = ['user-id']
|
static observedAttributes = ['user-id']
|
||||||
|
|
||||||
|
// 这两个方法应当在被渲染后由渲染组件主动提供
|
||||||
|
declare openChatInfo?: (chat: Chat | string) => void
|
||||||
|
declare openUserInfo?: (user: Chat | User | string) => void
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
@@ -57,4 +64,6 @@ customElements.define('chat-mention', class extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
customElements.define('chat-mention', ChatMentionElement)
|
||||||
|
|||||||
@@ -44,7 +44,10 @@ export default function ChatFragment({
|
|||||||
const inputRef = React.useRef<TextField>()
|
const inputRef = React.useRef<TextField>()
|
||||||
const chatPagePanelRef = React.useRef<ChatPanelRef>()
|
const chatPagePanelRef = React.useRef<ChatPanelRef>()
|
||||||
|
|
||||||
|
async function performSendMessage() {
|
||||||
|
await chatInfo.sendMessageOrThrow(inputRef.current!.value)
|
||||||
|
inputRef.current!.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{
|
<div style={{
|
||||||
@@ -164,6 +167,7 @@ export default function ChatFragment({
|
|||||||
}} onKeyDown={(event: KeyboardEvent) => {
|
}} onKeyDown={(event: KeyboardEvent) => {
|
||||||
if (event.ctrlKey && event.key == 'Enter') {
|
if (event.ctrlKey && event.key == 'Enter') {
|
||||||
// 发送消息
|
// 发送消息
|
||||||
|
performSendMessage()
|
||||||
}
|
}
|
||||||
}} onPaste={(event: ClipboardEvent) => {
|
}} onPaste={(event: ClipboardEvent) => {
|
||||||
for (const item of event.clipboardData?.items || []) {
|
for (const item of event.clipboardData?.items || []) {
|
||||||
@@ -185,11 +189,7 @@ export default function ChatFragment({
|
|||||||
}}></mdui-button-icon>
|
}}></mdui-button-icon>
|
||||||
<mdui-button-icon icon="send" style={{
|
<mdui-button-icon icon="send" style={{
|
||||||
marginRight: '7px',
|
marginRight: '7px',
|
||||||
}} onClick={async () => {
|
}} onClick={performSendMessage}></mdui-button-icon>
|
||||||
// 发送消息
|
|
||||||
await chatInfo.sendMessageOrThrow(inputRef.current!.value)
|
|
||||||
inputRef.current!.value = ''
|
|
||||||
}}></mdui-button-icon>
|
|
||||||
<div style={{
|
<div style={{
|
||||||
display: 'none'
|
display: 'none'
|
||||||
}}>
|
}}>
|
||||||
|
|||||||
@@ -1,5 +1,263 @@
|
|||||||
import { Message } from "lingchair-client-protocol"
|
import { Message } from "lingchair-client-protocol"
|
||||||
|
import isMobileUI from "../../utils/isMobileUI"
|
||||||
|
import useAsyncEffect from "../../utils/useAsyncEffect"
|
||||||
|
import ClientCache from "../../ClientCache"
|
||||||
|
import getClient from "../../getClient"
|
||||||
|
import Avatar from "../Avatar"
|
||||||
|
import AppStateContext from "../app-state/AppStateContext"
|
||||||
|
import { $, dialog, Dropdown } from "mdui"
|
||||||
|
import useEventListener from "../../utils/useEventListener"
|
||||||
|
import DOMPurify from 'dompurify'
|
||||||
|
import * as React from 'react'
|
||||||
|
import ChatMentionElement from "../chat-elements/chat-mention"
|
||||||
|
|
||||||
export default function ChatMessage({ message }: { message: Message }) {
|
function escapeHTML(str: string) {
|
||||||
return null
|
const div = document.createElement('div')
|
||||||
|
div.textContent = str
|
||||||
|
const re = div.innerHTML
|
||||||
|
div.remove()
|
||||||
|
return re
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将扁平化的渲染文本重新排版
|
||||||
|
*
|
||||||
|
* 旨在优化图片, 文件, 视频等消息元素的显示
|
||||||
|
*
|
||||||
|
* @param html
|
||||||
|
* @returns { string }
|
||||||
|
*/
|
||||||
|
function prettyFlatParsedMessage(html: string) {
|
||||||
|
const elements = new DOMParser().parseFromString(html, 'text/html').body.children
|
||||||
|
// 纯文本直接处理
|
||||||
|
if (elements.length == 0)
|
||||||
|
return `<chat-text-container><chat-text>${escapeHTML(html)}</chat-text></chat-text-container>`
|
||||||
|
let ls: Element[] = []
|
||||||
|
let ret = ''
|
||||||
|
// 第一个元素时, 不会被聚合在一起
|
||||||
|
let lastElementType = ''
|
||||||
|
const textElementTags = [
|
||||||
|
'chat-text',
|
||||||
|
'chat-mention',
|
||||||
|
'chat-quote',
|
||||||
|
]
|
||||||
|
function checkContinuousElement(tagName: string) {
|
||||||
|
// 如果上一个元素的类型和当前不一致, 或者上一个元素的类型和这个元素的类型都属于文本类型 (亦或者到最后一步时) 执行
|
||||||
|
if ((lastElementType != tagName || (textElementTags.indexOf(lastElementType) != -1 && textElementTags.indexOf(tagName) != -1)) || tagName == 'LAST_CHICKEN') {
|
||||||
|
// 如果上一个元素类型为文本类型, 且当前不是文本类型时, 用文本块包裹
|
||||||
|
if (textElementTags.indexOf(lastElementType) != -1) {
|
||||||
|
// 当前的文本类型不应该和上一个分离, 滚出去
|
||||||
|
if (textElementTags.indexOf(tagName) != -1) return
|
||||||
|
// 由于 chat-mention 不是用内部元素实现的, 因此在这个元素的生成中必须放置占位字符串
|
||||||
|
// 尽管显示上占位字符串不会显示, 但是在这里依然是会被处理的, 因为本身还是 innerHTML
|
||||||
|
|
||||||
|
// 当文本非空时, 将文字合并在一起
|
||||||
|
if (ls.map((v) => v.innerHTML).join('').trim() != '')
|
||||||
|
ret += `<chat-text-container>${ls.map((v) => v.outerHTML).join('')}</chat-text-container>`
|
||||||
|
} 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('LAST_CHICKEN')
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
const sanitizeConfig = {
|
||||||
|
ALLOWED_TAGS: [
|
||||||
|
"chat-image",
|
||||||
|
"chat-video",
|
||||||
|
"chat-file",
|
||||||
|
'chat-text',
|
||||||
|
"chat-link",
|
||||||
|
'chat-mention',
|
||||||
|
'chat-quote',
|
||||||
|
],
|
||||||
|
ALLOWED_ATTR: [
|
||||||
|
'underline',
|
||||||
|
'em',
|
||||||
|
'src',
|
||||||
|
'alt',
|
||||||
|
'href',
|
||||||
|
'name',
|
||||||
|
'user-id',
|
||||||
|
'chat-id',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ChatMessage({ message, noUserDisplay, avatarMenuItems, messageMenuItems }: { message: Message, noUserDisplay?: boolean, avatarMenuItems?: globalThis.React.JSX.IntrinsicElements['mdui-menu-item'][], messageMenuItems?: globalThis.React.JSX.IntrinsicElements['mdui-menu-item'][] }) {
|
||||||
|
const AppState = React.useContext(AppStateContext)
|
||||||
|
|
||||||
|
const [isAtRight, setAtRight] = React.useState(false)
|
||||||
|
|
||||||
|
const messageDropDownRef = React.useRef<Dropdown>()
|
||||||
|
const [isMessageDropDownOpen, setMessageDropDownOpen] = React.useState(false)
|
||||||
|
useEventListener(messageDropDownRef, 'closed', () => {
|
||||||
|
setMessageDropDownOpen(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
const avatarDropDownRef = React.useRef<Dropdown>()
|
||||||
|
const [isAvatarDropDownOpen, setAvatarDropDownOpen] = React.useState(false)
|
||||||
|
useEventListener(avatarDropDownRef, 'closed', () => {
|
||||||
|
setAvatarDropDownOpen(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
const [nickName, setNickName] = React.useState('')
|
||||||
|
const [avatarUrl, setAvatarUrl] = React.useState('')
|
||||||
|
useAsyncEffect(async () => {
|
||||||
|
const user = await ClientCache.getUser(message.getUserId()!)
|
||||||
|
setAtRight(await ClientCache.getMySelf().then((re) => re?.getId()) == user?.getId())
|
||||||
|
setNickName(user?.getNickName() || '')
|
||||||
|
setAvatarUrl(getClient().getUrlForFileByHash(user?.getAvatarFileHash() || '') || '')
|
||||||
|
}, [message])
|
||||||
|
|
||||||
|
const messageInnerRef = React.useRef<HTMLSpanElement>(null)
|
||||||
|
React.useEffect(() => {
|
||||||
|
messageInnerRef.current!.innerHTML = prettyFlatParsedMessage(DOMPurify.sanitize(message.parseWithTransformers({
|
||||||
|
attachment({ fileType, attachment }) {
|
||||||
|
const url = getClient().getUrlForFileByHash(attachment.getFileHash())
|
||||||
|
return ({
|
||||||
|
Image: `<chat-image src="${url}" alt="${attachment.getFileName()}"></chat-image>`,
|
||||||
|
Video: `<chat-video src="${url}"></chat-video>`,
|
||||||
|
File: `<chat-file href="${url}" name="${attachment.getFileName()}"></chat-file>`,
|
||||||
|
})?.[fileType]
|
||||||
|
},
|
||||||
|
mention({ mentionType, mention }) {
|
||||||
|
switch (mentionType) {
|
||||||
|
case "UserMention":
|
||||||
|
return `<chat-mention user-id="${mention.user_id}" text="${mention.text}">[对话提及]</chat-mention>`
|
||||||
|
case "ChatMention":
|
||||||
|
return `<chat-mention chat-id="${mention.chat_id}" text="${mention.text}">[对话提及]</chat-mention>`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}), sanitizeConfig))
|
||||||
|
|
||||||
|
// 没有办法的办法 (笑)
|
||||||
|
// 姐姐, 谁让您不是 React 组件呢
|
||||||
|
messageInnerRef.current!.querySelectorAll('chat-mention').forEach((v) => {
|
||||||
|
const e = v as ChatMentionElement
|
||||||
|
e.openChatInfo = AppState.openChatInfo
|
||||||
|
e.openUserInfo = AppState.openUserInfo
|
||||||
|
})
|
||||||
|
}, [message])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
slot="trigger"
|
||||||
|
onContextMenu={(e) => {
|
||||||
|
if (isMobileUI()) return
|
||||||
|
e.preventDefault()
|
||||||
|
setMessageDropDownOpen(!isMessageDropDownOpen)
|
||||||
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
if (!isMobileUI()) return
|
||||||
|
e.preventDefault()
|
||||||
|
setMessageDropDownOpen(!isMessageDropDownOpen)
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: isAtRight ? "flex-end" : "flex-start",
|
||||||
|
flexDirection: "column"
|
||||||
|
}}>
|
||||||
|
{
|
||||||
|
!noUserDisplay &&
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: isAtRight ? "flex-end" : "flex-start",
|
||||||
|
}}>
|
||||||
|
{
|
||||||
|
// 发送者昵称(左)
|
||||||
|
isAtRight && <span
|
||||||
|
style={{
|
||||||
|
alignSelf: "center",
|
||||||
|
fontSize: "90%"
|
||||||
|
}}>
|
||||||
|
{nickName}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// 发送者头像
|
||||||
|
}
|
||||||
|
<mdui-dropdown trigger="manual" ref={avatarDropDownRef} open={isAvatarDropDownOpen}>
|
||||||
|
<Avatar
|
||||||
|
slot="trigger"
|
||||||
|
src={avatarUrl}
|
||||||
|
text={nickName}
|
||||||
|
style={{
|
||||||
|
width: "43px",
|
||||||
|
height: "43px",
|
||||||
|
margin: "11px"
|
||||||
|
}}
|
||||||
|
onContextMenu={(e) => {
|
||||||
|
if (isMobileUI()) return
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
setAvatarDropDownOpen(!isAvatarDropDownOpen)
|
||||||
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
AppState.openUserInfo(message.getUserId()!)
|
||||||
|
}} />
|
||||||
|
<mdui-menu>
|
||||||
|
{avatarMenuItems}
|
||||||
|
</mdui-menu>
|
||||||
|
</mdui-dropdown>
|
||||||
|
{
|
||||||
|
// 发送者昵称(右)
|
||||||
|
!isAtRight && <span
|
||||||
|
style={{
|
||||||
|
alignSelf: "center",
|
||||||
|
fontSize: "90%"
|
||||||
|
}}>
|
||||||
|
{nickName}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<mdui-card
|
||||||
|
variant="elevated"
|
||||||
|
style={{
|
||||||
|
maxWidth: 'var(--whitesilk-widget-message-maxwidth)', // (window.matchMedia('(pointer: fine)') && "50%") || (window.matchMedia('(pointer: coarse)') && "77%"),
|
||||||
|
minWidth: "0%",
|
||||||
|
[isAtRight ? "marginRight" : "marginLeft"]: "55px",
|
||||||
|
marginTop: noUserDisplay ? '5px' : "-5px",
|
||||||
|
alignSelf: isAtRight ? "flex-end" : "flex-start",
|
||||||
|
// boxShadow: isUsingFullDisplay ? 'inherit' : 'var(--mdui-elevation-level1)',
|
||||||
|
// padding: isUsingFullDisplay ? undefined : "13px",
|
||||||
|
// paddingTop: isUsingFullDisplay ? undefined : "14px",
|
||||||
|
// backgroundColor: isUsingFullDisplay ? "inherit" : undefined
|
||||||
|
}}>
|
||||||
|
<mdui-dropdown trigger="manual" ref={messageDropDownRef} open={isMessageDropDownOpen}>
|
||||||
|
<span
|
||||||
|
slot="trigger"
|
||||||
|
id="msg"
|
||||||
|
style={{
|
||||||
|
fontSize: "94%",
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}}
|
||||||
|
ref={messageInnerRef} />
|
||||||
|
<mdui-menu onClick={(e: MouseEvent) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
setMessageDropDownOpen(false)
|
||||||
|
}}>
|
||||||
|
{messageMenuItems}
|
||||||
|
</mdui-menu>
|
||||||
|
</mdui-dropdown>
|
||||||
|
</mdui-card>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Chat, Message } from 'lingchair-client-protocol'
|
import { Chat, Message } from 'lingchair-client-protocol'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
import ChatMessage from './ChatMessage'
|
||||||
|
|
||||||
export default function ChatMessageContainer({ messages }: { messages: Message[] }) {
|
export default function ChatMessageContainer({ messages }: { messages: Message[] }) {
|
||||||
return (
|
return (
|
||||||
@@ -12,7 +13,51 @@ export default function ChatMessageContainer({ messages }: { messages: Message[]
|
|||||||
paddingTop: "15px",
|
paddingTop: "15px",
|
||||||
flexGrow: '1',
|
flexGrow: '1',
|
||||||
}}>
|
}}>
|
||||||
{messages?.map((v) => v.getText())}
|
{
|
||||||
|
(() => {
|
||||||
|
// 添加时间
|
||||||
|
let date = new Date(0)
|
||||||
|
function timeAddZeroPrefix(t: number) {
|
||||||
|
if (t >= 0 && t < 10)
|
||||||
|
return '0' + t
|
||||||
|
return t + ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并同用户消息
|
||||||
|
let user: string | undefined
|
||||||
|
return messages?.map((msg) => {
|
||||||
|
// 添加时间
|
||||||
|
const lastDate = date
|
||||||
|
date = new Date(msg.getTime())
|
||||||
|
const shouldShowTime = msg.getUserId() != null &&
|
||||||
|
(date.getMinutes() != lastDate.getMinutes() || date.getDate() != lastDate.getDate() || date.getMonth() != lastDate.getMonth() || date.getFullYear() != lastDate.getFullYear())
|
||||||
|
|
||||||
|
// 合并同用户消息
|
||||||
|
const lastUser = user
|
||||||
|
user = msg.getUserId()
|
||||||
|
|
||||||
|
return <>
|
||||||
|
{
|
||||||
|
shouldShowTime && <mdui-tooltip content={`${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日 ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`}>
|
||||||
|
<div style={{
|
||||||
|
fontSize: '87%',
|
||||||
|
marginTop: '13px',
|
||||||
|
marginBottom: '10px',
|
||||||
|
}}>
|
||||||
|
{
|
||||||
|
(date.getFullYear() != lastDate.getFullYear() ? `${date.getFullYear()}年` : '')
|
||||||
|
+ `${date.getMonth() + 1}月`
|
||||||
|
+ `${date.getDate()}日`
|
||||||
|
+ ` ${timeAddZeroPrefix(date.getHours())}:${timeAddZeroPrefix(date.getMinutes())}`
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</mdui-tooltip>
|
||||||
|
}
|
||||||
|
<ChatMessage message={msg} noUserDisplay={lastUser == user && !shouldShowTime} />
|
||||||
|
</>
|
||||||
|
})
|
||||||
|
})()
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import showSnackbar from "../../utils/showSnackbar.ts"
|
|||||||
import { useContextSelector } from "use-context-selector"
|
import { useContextSelector } from "use-context-selector"
|
||||||
import MainSharedContext, { Shared } from "../MainSharedContext.ts"
|
import MainSharedContext, { Shared } from "../MainSharedContext.ts"
|
||||||
import isMobileUI from "../../utils/isMobileUI.ts"
|
import isMobileUI from "../../utils/isMobileUI.ts"
|
||||||
import gotoChatInfo from "../routers/gotoChatInfo.ts"
|
|
||||||
import ClientCache from "../../ClientCache.ts"
|
import ClientCache from "../../ClientCache.ts"
|
||||||
import { useNavigate } from "react-router"
|
import { useNavigate } from "react-router"
|
||||||
import AppStateContext from "../app-state/AppStateContext.ts"
|
import AppStateContext from "../app-state/AppStateContext.ts"
|
||||||
@@ -25,7 +24,7 @@ export default function FavouriteChatsList({ ...props }: React.HTMLAttributes<HT
|
|||||||
const [searchText, setSearchText] = React.useState('')
|
const [searchText, setSearchText] = React.useState('')
|
||||||
const [checkedList, setCheckedList] = React.useState<{ [key: string]: boolean }>({})
|
const [checkedList, setCheckedList] = React.useState<{ [key: string]: boolean }>({})
|
||||||
|
|
||||||
const DialogState = React.useContext(AppStateContext)
|
const AppState = React.useContext(AppStateContext)
|
||||||
|
|
||||||
useEventListener(searchRef, 'input', (e) => {
|
useEventListener(searchRef, 'input', (e) => {
|
||||||
setSearchText((e.target as unknown as TextField).value)
|
setSearchText((e.target as unknown as TextField).value)
|
||||||
@@ -75,7 +74,7 @@ export default function FavouriteChatsList({ ...props }: React.HTMLAttributes<HT
|
|||||||
<mdui-list-item rounded style={{
|
<mdui-list-item rounded style={{
|
||||||
marginTop: '13px',
|
marginTop: '13px',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}} icon="person_add" onClick={() => DialogState.openAddFavouriteChat()}>添加收藏</mdui-list-item>
|
}} icon="person_add" onClick={() => AppState.openAddFavouriteChat()}>添加收藏</mdui-list-item>
|
||||||
<mdui-list-item rounded style={{
|
<mdui-list-item rounded style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}} icon="refresh" onClick={() => shared.functions_lazy.current.updateFavouriteChats()}>刷新列表</mdui-list-item>
|
}} icon="refresh" onClick={() => shared.functions_lazy.current.updateFavouriteChats()}>刷新列表</mdui-list-item>
|
||||||
@@ -162,7 +161,7 @@ export default function FavouriteChatsList({ ...props }: React.HTMLAttributes<HT
|
|||||||
[v.getId()]: !checkedList[v.getId()],
|
[v.getId()]: !checkedList[v.getId()],
|
||||||
})
|
})
|
||||||
else
|
else
|
||||||
DialogState.openChatInfo(v.getId())
|
AppState.openChatInfo(v.getId())
|
||||||
}}
|
}}
|
||||||
key={v.getId()}
|
key={v.getId()}
|
||||||
chat={v} />
|
chat={v} />
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ import { useContextSelector } from "use-context-selector"
|
|||||||
import showSnackbar from "../../utils/showSnackbar.ts"
|
import showSnackbar from "../../utils/showSnackbar.ts"
|
||||||
import MainSharedContext, { Shared } from "../MainSharedContext.ts"
|
import MainSharedContext, { Shared } from "../MainSharedContext.ts"
|
||||||
import ClientCache from "../../ClientCache.ts"
|
import ClientCache from "../../ClientCache.ts"
|
||||||
|
import AppStateContext from "../app-state/AppStateContext.ts"
|
||||||
|
|
||||||
export default function RecentChatsList({ ...props }: React.HTMLAttributes<HTMLElement>) {
|
export default function RecentChatsList({ ...props }: React.HTMLAttributes<HTMLElement>) {
|
||||||
|
const AppState = React.useContext(AppStateContext)
|
||||||
const shared = useContextSelector(MainSharedContext, (context: Shared) => ({
|
const shared = useContextSelector(MainSharedContext, (context: Shared) => ({
|
||||||
functions_lazy: context.functions_lazy,
|
functions_lazy: context.functions_lazy,
|
||||||
state: context.state,
|
state: context.state,
|
||||||
@@ -75,7 +77,7 @@ export default function RecentChatsList({ ...props }: React.HTMLAttributes<HTMLE
|
|||||||
).map((v) =>
|
).map((v) =>
|
||||||
<RecentsListItem
|
<RecentsListItem
|
||||||
active={isMobileUI() ? false : shared.state.currentSelectedChatId == v.getId()}
|
active={isMobileUI() ? false : shared.state.currentSelectedChatId == v.getId()}
|
||||||
onClick={() => {}}
|
onClick={() => AppState.openChat(v.getId())}
|
||||||
key={v.getId()}
|
key={v.getId()}
|
||||||
recentChat={v} />
|
recentChat={v} />
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user