feat: 对话中打开用户的资料

This commit is contained in:
CrescentLeaf
2025-10-04 15:32:54 +08:00
parent 81cdb4afd9
commit debdb93935
6 changed files with 122 additions and 17 deletions

View File

@@ -21,6 +21,8 @@ import useAsyncEffect from "./useAsyncEffect.ts"
import ChatInfoDialog from "./dialog/ChatInfoDialog.tsx" import ChatInfoDialog from "./dialog/ChatInfoDialog.tsx"
import Chat from "../api/client_data/Chat.ts" import Chat from "../api/client_data/Chat.ts"
import AddContactDialog from './dialog/AddContactDialog.tsx' import AddContactDialog from './dialog/AddContactDialog.tsx'
import UserProfileDialog from "./dialog/UserProfileDialog.tsx"
import DataCaches from "../api/DataCaches.ts"
declare global { declare global {
namespace React { namespace React {
@@ -56,6 +58,9 @@ export default function App() {
myProfileDialogRef.current!.open = true myProfileDialogRef.current!.open = true
}) })
const userProfileDialogRef = React.useRef<Dialog>(null)
const [userInfo, setUserInfo] = React.useState(null as unknown as User)
const addContactDialogRef = React.useRef<Dialog>(null) const addContactDialogRef = React.useRef<Dialog>(null)
const chatInfoDialogRef = React.useRef<Dialog>(null) const chatInfoDialogRef = React.useRef<Dialog>(null)
@@ -94,6 +99,21 @@ export default function App() {
chatInfoDialogRef.current!.open = true chatInfoDialogRef.current!.open = true
} }
function openChatFragment(chatId: string) {
setCurrentChatId(chatId)
setIsShowChatFragment(true)
}
async function openUserInfoDialog(user: User | string) {
if (user instanceof User) {
setUserInfo(user)
} else {
setUserInfo(await DataCaches.getUserProfile(user))
}
userProfileDialogRef.current!.open = true
}
return ( return (
<div style={{ <div style={{
display: "flex", display: "flex",
@@ -118,13 +138,14 @@ export default function App() {
<MyProfileDialog <MyProfileDialog
myProfileDialogRef={myProfileDialogRef as any} myProfileDialogRef={myProfileDialogRef as any}
user={myUserProfileCache} /> user={myUserProfileCache} />
<UserProfileDialog
userProfileDialogRef={userProfileDialogRef as any}
openChatFragment={openChatFragment}
user={userInfo} />
<ChatInfoDialog <ChatInfoDialog
chatInfoDialogRef={chatInfoDialogRef as any} chatInfoDialogRef={chatInfoDialogRef as any}
openChatFragment={(id) => { openChatFragment={openChatFragment}
setCurrentChatId(id)
setIsShowChatFragment(true)
}}
chat={chatInfo} /> chat={chatInfo} />
<AddContactDialog <AddContactDialog
@@ -147,10 +168,7 @@ export default function App() {
{ {
// 最近聊天 // 最近聊天
<RecentsList <RecentsList
openChatFragment={(id) => { openChatFragment={openChatFragment}
setCurrentChatId(id)
setIsShowChatFragment(true)
}}
display={navigationItemSelected == "Recents"} display={navigationItemSelected == "Recents"}
currentChatId={currentChatId} /> currentChatId={currentChatId} />
} }
@@ -181,6 +199,7 @@ export default function App() {
{ {
isShowChatFragment && <ChatFragment isShowChatFragment && <ChatFragment
target={currentChatId} target={currentChatId}
openUserInfoDialog={openUserInfoDialog}
openChatInfoDialog={openChatInfoDialog} openChatInfoDialog={openChatInfoDialog}
key={currentChatId} /> key={currentChatId} />
} }

View File

@@ -20,6 +20,8 @@ import useAsyncEffect from "./useAsyncEffect.ts"
import ChatInfoDialog from "./dialog/ChatInfoDialog.tsx" import ChatInfoDialog from "./dialog/ChatInfoDialog.tsx"
import Chat from "../api/client_data/Chat.ts" import Chat from "../api/client_data/Chat.ts"
import AddContactDialog from './dialog/AddContactDialog.tsx' import AddContactDialog from './dialog/AddContactDialog.tsx'
import UserProfileDialog from "./dialog/UserProfileDialog.tsx"
import DataCaches from "../api/DataCaches.ts"
declare global { declare global {
namespace React { namespace React {
@@ -60,6 +62,9 @@ export default function AppMobile() {
const chatInfoDialogRef = React.useRef<Dialog>(null) const chatInfoDialogRef = React.useRef<Dialog>(null)
const [chatInfo, setChatInfo] = React.useState(null as unknown as Chat) const [chatInfo, setChatInfo] = React.useState(null as unknown as Chat)
const userProfileDialogRef = React.useRef<Dialog>(null)
const [userInfo, setUserInfo] = React.useState(null as unknown as User)
const [myUserProfileCache, setMyUserProfileCache] = React.useState(null as unknown as User) const [myUserProfileCache, setMyUserProfileCache] = React.useState(null as unknown as User)
const [isShowChatFragment, setIsShowChatFragment] = React.useState(false) const [isShowChatFragment, setIsShowChatFragment] = React.useState(false)
@@ -96,6 +101,21 @@ export default function AppMobile() {
chatInfoDialogRef.current!.open = true chatInfoDialogRef.current!.open = true
} }
function openChatFragment(chatId: string) {
setCurrentChatId(chatId)
setIsShowChatFragment(true)
}
async function openUserInfoDialog(user: User | string) {
if (user instanceof User) {
setUserInfo(user)
} else {
setUserInfo(await DataCaches.getUserProfile(user))
}
userProfileDialogRef.current!.open = true
}
return ( return (
<div style={{ <div style={{
display: "flex", display: "flex",
@@ -114,6 +134,7 @@ export default function AppMobile() {
}}> }}>
<ChatFragment <ChatFragment
showReturnButton={true} showReturnButton={true}
openUserInfoDialog={openUserInfoDialog}
onReturnButtonClicked={() => setIsShowChatFragment(false)} onReturnButtonClicked={() => setIsShowChatFragment(false)}
key={currentChatId} key={currentChatId}
openChatInfoDialog={openChatInfoDialog} openChatInfoDialog={openChatInfoDialog}
@@ -138,6 +159,10 @@ export default function AppMobile() {
<MyProfileDialog <MyProfileDialog
myProfileDialogRef={myProfileDialogRef as any} myProfileDialogRef={myProfileDialogRef as any}
user={myUserProfileCache} /> user={myUserProfileCache} />
<UserProfileDialog
userProfileDialogRef={userProfileDialogRef as any}
openChatFragment={openChatFragment}
user={userInfo} />
<ChatInfoDialog <ChatInfoDialog
chatInfoDialogRef={chatInfoDialogRef as any} chatInfoDialogRef={chatInfoDialogRef as any}

View File

@@ -14,13 +14,15 @@ import useAsyncEffect from "../useAsyncEffect.ts"
import * as marked from 'marked' import * as marked from 'marked'
import DOMPurify from 'dompurify' import DOMPurify from 'dompurify'
import randomUUID from "../../randomUUID.ts" import randomUUID from "../../randomUUID.ts"
import EventBus from "../../EventBus.ts"; import EventBus from "../../EventBus.ts"
import User from "../../api/client_data/User.ts"
interface Args extends React.HTMLAttributes<HTMLElement> { interface Args extends React.HTMLAttributes<HTMLElement> {
target: string target: string
showReturnButton?: boolean showReturnButton?: boolean
openChatInfoDialog: (chat: Chat) => void openChatInfoDialog: (chat: Chat) => void
onReturnButtonClicked?: () => void onReturnButtonClicked?: () => void
openUserInfoDialog: (user: User | string) => void
} }
const markedInstance = new marked.Marked({ const markedInstance = new marked.Marked({
@@ -47,7 +49,7 @@ const markedInstance = new marked.Marked({
} }
}) })
export default function ChatFragment({ target, showReturnButton, onReturnButtonClicked, openChatInfoDialog, ...props }: Args) { export default function ChatFragment({ target, showReturnButton, onReturnButtonClicked, openChatInfoDialog, openUserInfoDialog, ...props }: Args) {
const [messagesList, setMessagesList] = React.useState([] as Message[]) const [messagesList, setMessagesList] = React.useState([] as Message[])
const [chatInfo, setChatInfo] = React.useState({ const [chatInfo, setChatInfo] = React.useState({
title: '加載中...' title: '加載中...'
@@ -305,7 +307,8 @@ export default function ChatFragment({ target, showReturnButton, onReturnButtonC
key={msg.id} key={msg.id}
slot="trigger" slot="trigger"
id={`chat_${target}_message_${msg.id}`} id={`chat_${target}_message_${msg.id}`}
userId={msg.user_id} /> userId={msg.user_id}
openUserInfoDialog={openUserInfoDialog} />
return ( return (
<> <>

View File

@@ -8,18 +8,19 @@ import copyToClipboard from "../copyToClipboard.ts"
import useAsyncEffect from "../useAsyncEffect.ts" import useAsyncEffect from "../useAsyncEffect.ts"
import useEventListener from "../useEventListener.ts" import useEventListener from "../useEventListener.ts"
import React from "react" import React from "react"
import useEventListener from "../useEventListener.ts"
import isMobileUI from "../isMobileUI.ts" import isMobileUI from "../isMobileUI.ts"
import ReactJson from 'react-json-view' import ReactJson from 'react-json-view'
import User from "../../api/client_data/User.ts"
interface Args extends React.HTMLAttributes<HTMLElement> { interface Args extends React.HTMLAttributes<HTMLElement> {
userId: string userId: string
rawData: string rawData: string
renderHTML: string renderHTML: string
message: Data_Message message: Data_Message
openUserInfoDialog: (user: User | string) => void
} }
export default function Message({ userId, rawData, renderHTML, message, ...props }: Args) { export default function Message({ userId, rawData, renderHTML, message, openUserInfoDialog, ...props }: Args) {
const isAtRight = Client.myUserProfile?.id == userId const isAtRight = Client.myUserProfile?.id == userId
const [nickName, setNickName] = React.useState("") const [nickName, setNickName] = React.useState("")
@@ -87,7 +88,8 @@ export default function Message({ userId, rawData, renderHTML, message, ...props
width: "43px", width: "43px",
height: "43px", height: "43px",
margin: "11px" margin: "11px"
}} /> }}
onClick={() => openUserInfoDialog(userId)} />
{ {
// 发送者昵称(右) // 发送者昵称(右)
!isAtRight && <span !isAtRight && <span
@@ -128,7 +130,7 @@ export default function Message({ userId, rawData, renderHTML, message, ...props
}}> }}>
<mdui-menu-item icon="content_copy" onClick={() => copyToClipboard($(dropDownRef.current as HTMLElement).find('#msg').text().trim())}></mdui-menu-item> <mdui-menu-item icon="content_copy" onClick={() => copyToClipboard($(dropDownRef.current as HTMLElement).find('#msg').text().trim())}></mdui-menu-item>
<mdui-menu-item icon="content_copy" onClick={() => copyToClipboard(rawData)}></mdui-menu-item> <mdui-menu-item icon="content_copy" onClick={() => copyToClipboard(rawData)}></mdui-menu-item>
<mdui-menu-item icon="info" onClick={() => messageJsonDialogRef.current.open = true}></mdui-menu-item> <mdui-menu-item icon="info" onClick={() => messageJsonDialogRef.current!.open = true}></mdui-menu-item>
</mdui-menu> </mdui-menu>
</mdui-dropdown> </mdui-dropdown>
</mdui-card> </mdui-card>

View File

@@ -18,8 +18,6 @@ export default function MyProfileDialog({
myProfileDialogRef, myProfileDialogRef,
user user
}: Refs) { }: Refs) {
const isMySelf = Client.myUserProfile?.id == user?.id
const editAvatarButtonRef = React.useRef<HTMLElement>(null) const editAvatarButtonRef = React.useRef<HTMLElement>(null)
const chooseAvatarFileRef = React.useRef<HTMLInputElement>(null) const chooseAvatarFileRef = React.useRef<HTMLInputElement>(null)
useEventListener(editAvatarButtonRef, 'click', () => chooseAvatarFileRef.current!.click()) useEventListener(editAvatarButtonRef, 'click', () => chooseAvatarFileRef.current!.click())

View File

@@ -0,0 +1,58 @@
import * as React from 'react'
import { Button, Dialog, TextField, dialog } from "mdui"
import useEventListener from "../useEventListener.ts"
import { checkApiSuccessOrSncakbar, snackbar } from "../snackbar.ts"
import Client from "../../api/Client.ts"
import * as CryptoJS from 'crypto-js'
import data from "../../Data.ts"
import Avatar from "../Avatar.tsx"
import User from "../../api/client_data/User.ts"
interface Refs {
userProfileDialogRef: React.MutableRefObject<Dialog>
openChatFragment: (id: string) => void
user: User
}
export default function UserProfileDialog({
userProfileDialogRef,
openChatFragment,
user
}: Refs) {
return (
<mdui-dialog close-on-overlay-click close-on-esc ref={userProfileDialogRef}>
<div style={{
display: 'flex',
alignItems: 'center',
}}>
<Avatar src={user?.avatar} text={user?.nickname} style={{
width: '50px',
height: '50px',
}} />
<span style={{
marginLeft: "15px",
fontSize: '16.5px',
}}>{user?.nickname}</span>
</div>
<mdui-divider style={{
marginTop: "10px",
}}></mdui-divider>
<mdui-list>
{/* <mdui-list-item icon="edit" rounded>设置备注</mdui-list-item> */}
<mdui-list-item icon="chat" rounded onClick={async () => {
const re = await Client.invoke("Chat.getIdForPrivate", {
token: data.access_token,
target: user.id,
})
if (re.code != 200)
return checkApiSuccessOrSncakbar(re, '获取对话失败')
openChatFragment(re.data!.chat_id as string)
userProfileDialogRef.current!.open = false
}}></mdui-list-item>
</mdui-list>
</mdui-dialog>
)
}