From 59191cc42ed5168f209484c2f544ac6baa4b34da Mon Sep 17 00:00:00 2001 From: CrescentLeaf Date: Sun, 23 Nov 2025 12:32:59 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9F=A5=E7=9C=8B=E8=87=AA=E5=B7=B1?= =?UTF-8?q?=E6=89=80=E6=9C=89=E7=9A=84=E5=AF=B9=E8=AF=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client-protocol/ApiDeclare.ts | 3 + client-protocol/UserMySelf.ts | 20 +++++++ client/api/ApiDeclare.ts | 3 + client/ui/App.tsx | 24 +++++++- client/ui/AppMobile.tsx | 27 ++++++++- client/ui/main/AllChatsList.tsx | 85 +++++++++++++++++++++++++++++ client/ui/main/AllChatsListItem.tsx | 29 ++++++++++ server/api/UserApi.ts | 32 +++++++++++ server/data/User.ts | 4 ++ 9 files changed, 224 insertions(+), 3 deletions(-) create mode 100644 client/ui/main/AllChatsList.tsx create mode 100644 client/ui/main/AllChatsListItem.tsx diff --git a/client-protocol/ApiDeclare.ts b/client-protocol/ApiDeclare.ts index f288735..6b5d0a0 100644 --- a/client-protocol/ApiDeclare.ts +++ b/client-protocol/ApiDeclare.ts @@ -22,6 +22,9 @@ export type CallMethod = // 最近对话列表 "User.getMyRecentChats" | + // 所有对话列表 + "User.getMyAllChats" | + // 对话信息 "Chat.getInfo" | "Chat.getAnotherUserIdFromPrivate" | diff --git a/client-protocol/UserMySelf.ts b/client-protocol/UserMySelf.ts index 082a2ca..770ea58 100644 --- a/client-protocol/UserMySelf.ts +++ b/client-protocol/UserMySelf.ts @@ -177,4 +177,24 @@ export default class UserMySelf extends User { return re.data!.recent_chats as RecentChatBean[] throw new CallbackError(re) } + /* + * ================================================ + * 所有对话 + * ================================================ + */ + async getMyAllChatBeans() { + try { + return await this.getMyAllChatBeansOrThrow() + } catch (_) { + return [] + } + } + async getMyAllChatBeansOrThrow() { + const re = await this.client.invoke("User.getMyAllChats", { + token: this.client.access_token + }) + if (re.code == 200) + return re.data!.all_chats as ChatBean[] + throw new CallbackError(re) + } } diff --git a/client/api/ApiDeclare.ts b/client/api/ApiDeclare.ts index f288735..6b5d0a0 100644 --- a/client/api/ApiDeclare.ts +++ b/client/api/ApiDeclare.ts @@ -22,6 +22,9 @@ export type CallMethod = // 最近对话列表 "User.getMyRecentChats" | + // 所有对话列表 + "User.getMyAllChats" | + // 对话信息 "Chat.getInfo" | "Chat.getAnotherUserIdFromPrivate" | diff --git a/client/ui/App.tsx b/client/ui/App.tsx index ce5c2fc..3f36443 100644 --- a/client/ui/App.tsx +++ b/client/ui/App.tsx @@ -25,6 +25,7 @@ import DataCaches from "../api/DataCaches.ts" import getUrlForFileByHash from "../getUrlForFileByHash.ts" import Message from "../api/client_data/Message.ts" import EventBus from "../EventBus.ts" +import AllChatsList from "./main/AllChatsList.tsx"; declare global { namespace React { @@ -205,8 +206,21 @@ export default function App() { - + + + { + EventBus.emit('RecentsList.updateRecents') + EventBus.emit('ContactsList.updateContacts') + EventBus.emit('AllChatsList.updateAllChats') + }}> + + + + addContactDialogRef.current!.open = true}>添加收藏对话 + createGroupDialogRef.current!.open = true}>创建群组 + + { @@ -220,9 +234,17 @@ export default function App() { display={navigationItemSelected == "Recents"} currentChatId={currentChatId} /> } + { + // 最近聊天 + + } { // 對話列表 { ({ Recents: "最近对话", - Contacts: "所有对话" + Contacts: "收藏对话" })[navigationItemSelected] }
+ { + EventBus.emit('RecentsList.updateRecents') + EventBus.emit('ContactsList.updateContacts') + EventBus.emit('AllChatsList.updateAllChats') + }}> + + + + addContactDialogRef.current!.open = true}>添加收藏对话 + createGroupDialogRef.current!.open = true}>创建群组 + + @@ -227,9 +241,17 @@ export default function AppMobile() { display={navigationItemSelected == "Recents"} currentChatId={currentChatId} /> } + { + // 最近聊天 + + } { // 對話列表 最近对话 - 所有对话 + 收藏对话 + 全部对话 ) diff --git a/client/ui/main/AllChatsList.tsx b/client/ui/main/AllChatsList.tsx new file mode 100644 index 0000000..b9fccd4 --- /dev/null +++ b/client/ui/main/AllChatsList.tsx @@ -0,0 +1,85 @@ +import { TextField } from "mdui" +import useEventListener from "../useEventListener.ts" +import RecentsListItem from "./RecentsListItem.tsx" +import React from "react" +import useAsyncEffect from "../useAsyncEffect.ts" +import Client from "../../api/Client.ts" +import { checkApiSuccessOrSncakbar } from "../snackbar.ts" +import data from "../../Data.ts" +import EventBus from "../../EventBus.ts" +import isMobileUI from "../isMobileUI.ts" +import Chat from "../../api/client_data/Chat.ts" +import AllChatsListItem from "./AllChatsListItem.tsx" + +interface Args extends React.HTMLAttributes { + display: boolean + currentChatId: string + openChatInfoDialog: (chat: Chat) => void +} + +export default function AllChatsList({ + currentChatId, + display, + openChatInfoDialog, + ...props +}: Args) { + const searchRef = React.useRef(null) + const [searchText, setSearchText] = React.useState('') + const [allChatsList, setAllChatsList] = React.useState([]) + + useEventListener(searchRef, 'input', (e) => { + setSearchText((e.target as unknown as TextField).value) + }) + + useAsyncEffect(async () => { + async function updateAllChats() { + const re = await Client.invoke("User.getMyAllChats", { + token: data.access_token, + }) + if (re.code != 200) { + if (re.code != 401 && re.code != 400) checkApiSuccessOrSncakbar(re, "获取所有对话列表失败") + return + } + + setAllChatsList(re.data!.all_chats as Chat[]) + } + updateAllChats() + EventBus.on('AllChatsList.updateAllChats', () => updateAllChats()) + return () => { + EventBus.off('AllChatsList.updateAllChats') + } + }) + + return + + { + allChatsList.filter((chat) => + searchText == '' || + chat.title.includes(searchText) || + chat.id.includes(searchText) + ).map((v) => + { + openChatInfoDialog(v) + }} + chat={v} /> + ) + } + +} \ No newline at end of file diff --git a/client/ui/main/AllChatsListItem.tsx b/client/ui/main/AllChatsListItem.tsx new file mode 100644 index 0000000..88ae47e --- /dev/null +++ b/client/ui/main/AllChatsListItem.tsx @@ -0,0 +1,29 @@ +import { $ } from "mdui/jq" +import Avatar from "../Avatar.tsx" +import React from 'react' +import getUrlForFileByHash from "../../getUrlForFileByHash.ts" +import Chat from "../../api/client_data/Chat.ts" + +interface Args extends React.HTMLAttributes { + chat: Chat + active?: boolean +} + +export default function AllChatsListItem({ chat, active, ...prop }: Args) { + const { title, avatar_file_hash } = chat + + const ref = React.useRef(null) + + return ( + + {title} + + + ) +} diff --git a/server/api/UserApi.ts b/server/api/UserApi.ts index 581b858..7e0a6d6 100644 --- a/server/api/UserApi.ts +++ b/server/api/UserApi.ts @@ -267,6 +267,38 @@ export default class UserApi extends BaseApi { } } }) + // 獲取聯絡人列表 + this.registerEvent("User.getMyAllChats", (args, { deviceId }) => { + if (this.checkArgsMissing(args, ['token'])) return { + msg: "参数缺失", + code: 400, + } + + const token = TokenManager.decode(args.token as string) + if (!this.checkToken(token, deviceId)) return { + code: 401, + msg: "令牌无效", + } + + const user = User.findById(token.author) as User + const list = user.getAllChatsList() + + return { + msg: "成功", + code: 200, + data: { + all_chats: list.map((id) => { + const chat = Chat.findById(id) + return { + id, + type: chat?.bean.type, + title: chat?.getTitle(user) || "未知", + avatar_file_hash: chat?.getAvatarFileHash(user) ? chat?.getAvatarFileHash(user) : undefined + } + }) + } + } + }) // 獲取最近对话列表 this.registerEvent("User.getMyRecentChats", (args, { deviceId }) => { if (this.checkArgsMissing(args, ['token'])) return { diff --git a/server/data/User.ts b/server/data/User.ts index ce48b44..827fded 100644 --- a/server/data/User.ts +++ b/server/data/User.ts @@ -15,6 +15,7 @@ import Chat from "./Chat.ts" import ChatBean from "./ChatBean.ts" import MapJson from "../MapJson.ts" import DataWrongError from '../api/DataWrongError.ts' +import UserChatLinker from "./UserChatLinker.ts"; type UserBeanKey = keyof UserBean @@ -155,6 +156,9 @@ export default class User { return [] } } + getAllChatsList() { + return UserChatLinker.getUserChats(this.bean.id) + } getNickName(): string { return this.bean.nickname }