Compare commits

..

7 Commits

Author SHA1 Message Date
CrescentLeaf
940845db84 !break: 获取消息方法更新: 支持 offset, 客户端协议破坏性变更参数为对象 2025-12-28 00:30:51 +08:00
CrescentLeaf
01c1ccfd00 修正接口名称 2025-12-28 00:24:14 +08:00
CrescentLeaf
d944401691 一些参数命名的修改 2025-12-28 00:22:27 +08:00
CrescentLeaf
d4d28c2760 删除重复代码 2025-12-28 00:20:20 +08:00
CrescentLeaf
aa8a205e5f 前后端一些接口的重命名 2025-12-28 00:18:25 +08:00
CrescentLeaf
ef84cc30c0 修改参数符号名称 2025-12-28 00:09:07 +08:00
CrescentLeaf
f36c747a72 修正(取消)收藏对话框的关闭逻辑 2025-12-28 00:04:55 +08:00
9 changed files with 53 additions and 103 deletions

View File

@@ -53,7 +53,7 @@ export default class Chat extends BaseClientObject {
} }
} }
static async getOrCreatePrivateChatOrThrow(client: LingChairClient, user_id: string) { static async getOrCreatePrivateChatOrThrow(client: LingChairClient, user_id: string) {
const re = await client.invoke("Chat.getIdForPrivate", { const re = await client.invoke("Chat.getOrCreatePrivateChat", {
token: client.access_token, token: client.access_token,
target: user_id, target: user_id,
}) })
@@ -81,23 +81,24 @@ export default class Chat extends BaseClientObject {
* 对话消息 * 对话消息
* ================================================ * ================================================
*/ */
async getMessages(page: number = 0) { async getMessages(args: { page?: number, offset?: number }) {
return (await this.getMessageBeans(page)).map((v) => new Message(this.client, v)) return (await this.getMessageBeans(args)).map((v) => new Message(this.client, v))
} }
async getMessagesOrThrow(page: number = 0) { async getMessagesOrThrow(args: { page?: number, offset?: number }) {
return (await this.getMessageBeansOrThrow(page)).map((v) => new Message(this.client, v)) return (await this.getMessageBeansOrThrow(args)).map((v) => new Message(this.client, v))
} }
async getMessageBeans(page: number = 0) { async getMessageBeans(args: { page?: number, offset?: number }) {
try { try {
return await this.getMessageBeansOrThrow(page) return await this.getMessageBeansOrThrow(args)
} catch (_) { } catch (_) {
return [] return []
} }
} }
async getMessageBeansOrThrow(page: number = 0) { async getMessageBeansOrThrow({ page, offset }: { page?: number, offset?: number }) {
const re = await this.client.invoke("Chat.getMessageHistory", { const re = await this.client.invoke("Chat.getMessageHistory", {
token: this.client.access_token, token: this.client.access_token,
page, page,
offset,
target: this.bean.id, target: this.bean.id,
}) })
if (re.code == 200) return re.data!.messages as MessageBean[] if (re.code == 200) return re.data!.messages as MessageBean[]

View File

@@ -123,9 +123,9 @@ export default class UserMySelf extends User {
} }
} }
async addFavouriteChatsOrThrow(chat_ids: string[]) { async addFavouriteChatsOrThrow(chat_ids: string[]) {
const re = await this.client.invoke("User.addContacts", { const re = await this.client.invoke("User.addFavouriteChats", {
token: this.client.access_token, token: this.client.access_token,
targets: chat_ids, chat_ids,
}) })
if (re.code != 200) throw new CallbackError(re) if (re.code != 200) throw new CallbackError(re)
} }
@@ -138,9 +138,9 @@ export default class UserMySelf extends User {
} }
} }
async removeFavouriteChatsOrThrow(chat_ids: string[]) { async removeFavouriteChatsOrThrow(chat_ids: string[]) {
const re = await this.client.invoke("User.removeContacts", { const re = await this.client.invoke("User.removeFavouriteChats", {
token: this.client.access_token, token: this.client.access_token,
targets: chat_ids, chat_ids,
}) })
if (re.code != 200) throw new CallbackError(re) if (re.code != 200) throw new CallbackError(re)
} }
@@ -152,11 +152,11 @@ export default class UserMySelf extends User {
} }
} }
async getMyFavouriteChatBeansOrThrow() { async getMyFavouriteChatBeansOrThrow() {
const re = await this.client.invoke("User.getMyContacts", { const re = await this.client.invoke("User.getMyFavouriteChats", {
token: this.client.access_token token: this.client.access_token
}) })
if (re.code == 200) if (re.code == 200)
return (re.data!.favourite_chats || re.data!.contacts_list) as ChatBean[] return (re.data!.favourite_chats) as ChatBean[]
throw new CallbackError(re) throw new CallbackError(re)
} }
async getMyFavouriteChats() { async getMyFavouriteChats() {

View File

@@ -78,6 +78,8 @@ export default function UserOrChatInfoDialog({ chat, useRef }: { chat?: Chat, us
!isMySelf && <mdui-list-item icon={favourited ? "favorite_border" : "favorite"} rounded onClick={() => dialog({ !isMySelf && <mdui-list-item icon={favourited ? "favorite_border" : "favorite"} rounded onClick={() => dialog({
headline: favourited ? "取消收藏对话" : "收藏对话", headline: favourited ? "取消收藏对话" : "收藏对话",
description: favourited ? "确定从收藏对话列表中移除吗? (虽然这不会导致聊天记录丢失)" : "确定要添加到收藏对话列表吗?", description: favourited ? "确定从收藏对话列表中移除吗? (虽然这不会导致聊天记录丢失)" : "确定要添加到收藏对话列表吗?",
closeOnEsc: true,
closeOnOverlayClick: true,
actions: [ actions: [
{ {
text: "取消", text: "取消",

View File

@@ -2,9 +2,9 @@ import { Chat, Message } from 'lingchair-client-protocol'
import * as React from 'react' import * as React from 'react'
export default function ChatMessageContainer({ export default function ChatMessageContainer({
chatInfo, chat,
}: { }: {
chatInfo: Chat chat: Chat
}) { }) {
const [messages, setMessages] = React.useState<Message[]>() const [messages, setMessages] = React.useState<Message[]>()

View File

@@ -15,9 +15,9 @@ export type CallMethod =
"User.getInfo" | "User.getInfo" |
// 收藏对话列表 // 收藏对话列表
"User.getMyContacts" | "User.getMyFavouriteChats" |
"User.addContacts" | "User.addFavouriteChats" |
"User.removeContacts" | "User.removeFavouriteChats" |
// 最近对话列表 // 最近对话列表
"User.getMyRecentChats" | "User.getMyRecentChats" |
@@ -36,7 +36,7 @@ export type CallMethod =
// 对话创建 // 对话创建
"Chat.createGroup" | "Chat.createGroup" |
"Chat.getIdForPrivate" | "Chat.getOrCreatePrivateChat" |
// 入群请求 // 入群请求
"Chat.processJoinRequest" | "Chat.processJoinRequest" |
@@ -51,9 +51,6 @@ export type CallMethod =
"Chat.sendMessage" | "Chat.sendMessage" |
"Chat.getMessageHistory" "Chat.getMessageHistory"
// (废弃) 文件上传
// "Chat.uploadFile"
export type ClientEvent = export type ClientEvent =
// 对话收消息 // 对话收消息
"Client.onMessage" "Client.onMessage"

View File

@@ -87,7 +87,11 @@ export default class ChatApi extends BaseApi {
* @param page 頁面 * @param page 頁面
*/ */
this.registerEvent("Chat.getMessageHistory", (args, { deviceId }) => { this.registerEvent("Chat.getMessageHistory", (args, { deviceId }) => {
if (this.checkArgsMissing(args, ['token', 'target', 'page'])) return { if (this.checkArgsMissing(args, ['token', 'target'])) return {
msg: "参数缺失",
code: 400,
}
if (!args.page && !args.offset) return {
msg: "参数缺失", msg: "参数缺失",
code: 400, code: 400,
} }
@@ -112,49 +116,10 @@ export default class ChatApi extends BaseApi {
code: 200, code: 200,
msg: "成功", msg: "成功",
data: { data: {
messages: MessagesManager.getInstanceForChat(chat).getMessagesWithPage(15, args.page as number), messages: MessagesManager.getInstanceForChat(chat)[args.page ? 'getMessagesWithPage' : 'getMessagesWithOffset'](null, (args.page ? args.page : args.offset) as number),
}, },
} }
}) })
/**
* 上傳文件
* @param token 令牌
* @param target 目標對話
* @param file_name 文件名稱
* @param data 文件二進制數據
*/
/* this.registerEvent("Chat.uploadFile", async (args, { deviceId }) => {
if (this.checkArgsMissing(args, ['token', 'target', 'data', 'file_name'])) return {
msg: "参数缺失",
code: 400,
}
const token = TokenManager.decode(args.token as string)
if (!this.checkToken(token, deviceId)) return {
code: 401,
msg: "令牌无效",
}
const chat = Chat.findById(args.target as string)
if (chat == null) return {
code: 404,
msg: "对话不存在",
}
if (!UserChatLinker.checkUserIsLinkedToChat(token.author, chat!.bean.id)) return {
code: 403,
msg: "用户无权访问此对话",
}
const file = await FileManager.uploadFile(args.file_name as string, args.data as Buffer<ArrayBufferLike>, args.target as string)
return {
code: 200,
msg: "成功",
data: {
file_hash: file.getHash()
},
}
}) */
/** /**
* ====================================================== * ======================================================
* 对话成员 * 对话成员
@@ -388,7 +353,7 @@ export default class ChatApi extends BaseApi {
* @param token 令牌 * @param token 令牌
* @param target 目標用户 * @param target 目標用户
*/ */
this.registerEvent("Chat.getIdForPrivate", (args, { deviceId }) => { this.registerEvent("Chat.getOrCreatePrivateChat", (args, { deviceId }) => {
if (this.checkArgsMissing(args, ['token', 'target'])) return { if (this.checkArgsMissing(args, ['token', 'target'])) return {
msg: "参数缺失", msg: "参数缺失",
code: 400, code: 400,
@@ -413,10 +378,6 @@ export default class ChatApi extends BaseApi {
code: 200, code: 200,
msg: '成功', msg: '成功',
data: { data: {
// TODO: 移除这个, 将本方法重命名为 getOrCreatePrivateChat
// 并重构原 Web 客户端所引用的内容
chat_id: chat.bean.id,
id: chat.bean.id, id: chat.bean.id,
name: chat.bean.name, name: chat.bean.name,
type: chat.bean.type, type: chat.bean.type,
@@ -459,7 +420,7 @@ export default class ChatApi extends BaseApi {
chat.addAdmin(user.bean.id, [ chat.addAdmin(user.bean.id, [
AdminPermissions.OWNER, AdminPermissions.OWNER,
]) ])
user.addContact(chat.bean.id) user.addFavouriteChat(chat.bean.id)
MessagesManager.getInstanceForChat(chat).addSystemMessage("群组已创建") MessagesManager.getInstanceForChat(chat).addSystemMessage("群组已创建")
return { return {
@@ -570,10 +531,6 @@ export default class ChatApi extends BaseApi {
msg: "参数缺失", msg: "参数缺失",
code: 400, code: 400,
} }
/* if (!(args.avatar instanceof Buffer)) return {
msg: "参数不合法",
code: 400,
} */
const token = TokenManager.decode(args.token as string) const token = TokenManager.decode(args.token as string)
const user = User.findById(token.author) as User const user = User.findById(token.author) as User
@@ -587,9 +544,6 @@ export default class ChatApi extends BaseApi {
if (chat.bean.type == 'group') if (chat.bean.type == 'group')
if (chat.checkUserIsAdmin(user.bean.id)) { if (chat.checkUserIsAdmin(user.bean.id)) {
chat.setAvatarFileHash(args.file_hash as string) chat.setAvatarFileHash(args.file_hash as string)
/* const avatar: Buffer = args.avatar as Buffer
if (avatar)
chat.setAvatar(avatar) */
} else } else
return { return {
code: 403, code: 403,

View File

@@ -144,10 +144,6 @@ export default class UserApi extends BaseApi {
msg: "参数缺失", msg: "参数缺失",
code: 400, code: 400,
} }
if (this.checkArgsEmpty(args, ['nickname', 'password'])) return {
msg: "参数不得为空",
code: 400,
}
const username: string | null = args.username as string const username: string | null = args.username as string
const nickname: string = args.nickname as string const nickname: string = args.nickname as string
@@ -321,7 +317,7 @@ export default class UserApi extends BaseApi {
const user = User.findById(token.author) as User const user = User.findById(token.author) as User
const recentChats = user.getRecentChats() const recentChats = user.getRecentChats()
const recentChatsList = [] const recentChatsList: any[] = []
for (const [chatId, content] of recentChats) { for (const [chatId, content] of recentChats) {
const chat = Chat.findById(chatId) const chat = Chat.findById(chatId)
recentChatsList.push({ recentChatsList.push({
@@ -341,7 +337,7 @@ export default class UserApi extends BaseApi {
} }
}) })
// 獲取聯絡人列表 // 獲取聯絡人列表
this.registerEvent("User.getMyContacts", (args, { deviceId }) => { this.registerEvent("User.getMyFavouriteChats", (args, { deviceId }) => {
if (this.checkArgsMissing(args, ['token'])) return { if (this.checkArgsMissing(args, ['token'])) return {
msg: "参数缺失", msg: "参数缺失",
code: 400, code: 400,
@@ -354,7 +350,7 @@ export default class UserApi extends BaseApi {
} }
const user = User.findById(token.author) as User const user = User.findById(token.author) as User
const contacts = user.getContactsList() const contacts = user.getFavouriteChats()
contacts.push(ChatPrivate.getChatIdByUsersId(token.author, token.author)) contacts.push(ChatPrivate.getChatIdByUsersId(token.author, token.author))
return { return {
@@ -374,8 +370,8 @@ export default class UserApi extends BaseApi {
} }
}) })
// 添加聯絡人 // 添加聯絡人
this.registerEvent("User.addContacts", (args, { deviceId }) => { this.registerEvent("User.addFavouriteChats", (args, { deviceId }) => {
if (this.checkArgsMissing(args, ['token', 'targets'])) return { if (this.checkArgsMissing(args, ['token', 'chat_ids'])) return {
msg: "参数缺失", msg: "参数缺失",
code: 400, code: 400,
} }
@@ -388,14 +384,14 @@ export default class UserApi extends BaseApi {
let fail = 0 let fail = 0
const user = User.findById(token.author) as User const user = User.findById(token.author) as User
for (const target of (args.targets as string[])) { for (const target of (args.chat_ids as string[])) {
const chat = Chat.findById(target) || Chat.findByName(target) const chat = Chat.findById(target) || Chat.findByName(target)
const targetUser = User.findByAccount(target) as User const targetUser = User.findByAccount(target) as User
if (chat) if (chat)
user!.addContact(chat.bean.id) user!.addFavouriteChat(chat.bean.id)
else if (targetUser) { else if (targetUser) {
const privChat = ChatPrivate.findOrCreateForPrivate(user, targetUser) const privChat = ChatPrivate.findOrCreateForPrivate(user, targetUser)
user!.addContact(privChat.bean.id) user!.addFavouriteChat(privChat.bean.id)
} else { } else {
fail++ fail++
} }
@@ -407,8 +403,8 @@ export default class UserApi extends BaseApi {
} }
}) })
// 添加聯絡人 // 添加聯絡人
this.registerEvent("User.removeContacts", (args, { deviceId }) => { this.registerEvent("User.removeFavouriteChats", (args, { deviceId }) => {
if (this.checkArgsMissing(args, ['token', 'targets'])) return { if (this.checkArgsMissing(args, ['token', 'chat_ids'])) return {
msg: "参数缺失", msg: "参数缺失",
code: 400, code: 400,
} }
@@ -420,7 +416,7 @@ export default class UserApi extends BaseApi {
} }
const user = User.findById(token.author) as User const user = User.findById(token.author) as User
user.removeContacts(args.targets as string[]) user.removeFavouriteChats(args.chat_ids as string[])
return { return {
msg: "成功", msg: "成功",

View File

@@ -59,14 +59,14 @@ export default class MessagesManager {
text text
}) })
} }
getMessages(limit: number = 15, offset: number = 0) { getMessagesWithOffset(limit: number | undefined | null, offset: number = 0) {
const ls = MessagesManager.database.prepare(`SELECT * FROM ${this.getTableName()} ORDER BY id DESC LIMIT ? OFFSET ?;`).all(limit, offset) as unknown as MessageBean[] const ls = MessagesManager.database.prepare(`SELECT * FROM ${this.getTableName()} ORDER BY id DESC LIMIT ? OFFSET ?;`).all(limit || 15, offset) as unknown as MessageBean[]
return ls.map((v) => ({ return ls.map((v) => ({
...v, ...v,
chat_id: this.chat.bean.id, chat_id: this.chat.bean.id,
})) }))
} }
getMessagesWithPage(limit: number = 15, page: number = 0) { getMessagesWithPage(limit: number | undefined | null, page: number = 0) {
return this.getMessages(limit, limit * page) return this.getMessagesWithOffset(limit, (limit || 15) * page)
} }
} }

View File

@@ -138,17 +138,17 @@ export default class User {
return new Map() return new Map()
} }
} }
addContact(chatId: string) { addFavouriteChat(chatId: string) {
const ls = this.getContactsList() const ls = this.getFavouriteChats()
if (ls.indexOf(chatId) != -1 || ChatPrivate.getChatIdByUsersId(this.bean.id, this.bean.id) == chatId) return if (ls.indexOf(chatId) != -1 || ChatPrivate.getChatIdByUsersId(this.bean.id, this.bean.id) == chatId) return
ls.push(chatId) ls.push(chatId)
this.setAttr("contacts_list", JSON.stringify(ls)) this.setAttr("contacts_list", JSON.stringify(ls))
} }
removeContacts(contacts: string[]) { removeFavouriteChats(contacts: string[]) {
const ls = this.getContactsList().filter((v) => !contacts.includes(v)) const ls = this.getFavouriteChats().filter((v) => !contacts.includes(v))
this.setAttr("contacts_list", JSON.stringify(ls)) this.setAttr("contacts_list", JSON.stringify(ls))
} }
getContactsList() { getFavouriteChats() {
try { try {
return JSON.parse(this.bean.contacts_list) as string[] return JSON.parse(this.bean.contacts_list) as string[]
} catch (e) { } catch (e) {
@@ -159,13 +159,13 @@ export default class User {
getAllChatsList() { getAllChatsList() {
return UserChatLinker.getUserChats(this.bean.id) return UserChatLinker.getUserChats(this.bean.id)
} }
getNickName(): string { getNickName() {
return this.bean.nickname return this.bean.nickname
} }
setNickName(nickName: string) { setNickName(nickName: string) {
this.setAttr("nickname", nickName) this.setAttr("nickname", nickName)
} }
getPassword(): string { getPassword() {
return this.bean.password return this.bean.password
} }
setPassword(password: string) { setPassword(password: string) {