Compare commits

...

13 Commits

Author SHA1 Message Date
CrescentLeaf
ab1ef2c30b 修改配置失败时配置回退 2025-10-08 15:14:22 +08:00
CrescentLeaf
61bc1a265c fix: 由于系统消息没有发送者导致的 NOT NULL 错误 2025-10-08 15:13:56 +08:00
CrescentLeaf
9c45f3e13e 用户可以在不成为成员的情况下查看对话详情 2025-10-08 15:13:30 +08:00
CrescentLeaf
23ad29fb2d feat: 添加 caused_by 字段以便客户端知道是什么情况 2025-10-08 15:13:11 +08:00
CrescentLeaf
5b64c6adcf feat(wip): 入群需要审批 2025-10-08 15:12:46 +08:00
CrescentLeaf
dd42f5e54e fix: 不能正常显示系统消息 2025-10-08 15:00:31 +08:00
CrescentLeaf
2d7b7818d7 chore: 建群时添加一条系统消息 2025-10-08 14:56:51 +08:00
CrescentLeaf
c27eb37852 fix: wrong status code (400 -> 403) 2025-10-08 14:52:01 +08:00
CrescentLeaf
bc48cf801b fix: 创建群组时, 没有任何管理员 2025-10-08 14:49:42 +08:00
CrescentLeaf
e46661ba15 feat(wip): Chat admin 2025-10-08 14:47:27 +08:00
CrescentLeaf
9cb71af85b feat: get old value of preference when is was updated 2025-10-08 14:41:45 +08:00
CrescentLeaf
241ff714b8 fix: 对话页面 2025-10-08 12:25:33 +08:00
CrescentLeaf
db43de19c4 fix: 配置组件没有正确同步状态
* 问题出在我应该根据 State 决定组件状态而不是组件状态决定 State
* 踩坑了, 浪费我时间, 唉
2025-10-08 12:24:33 +08:00
12 changed files with 187 additions and 109 deletions

View File

@@ -1,3 +1,6 @@
type ErrorCausedBy =
'NOT_IN_THIS_CHAT_MEMBER_LIST'
type ApiCallbackMessage = { type ApiCallbackMessage = {
msg: string, msg: string,
/** /**
@@ -8,9 +11,9 @@ type ApiCallbackMessage = {
* 404: Not Found * 404: Not Found
* 500: 伺服器端錯誤 * 500: 伺服器端錯誤
* 501: 伺服器端不支持請求的功能 * 501: 伺服器端不支持請求的功能
* -1: 客戶端錯誤
*/ */
code: 200 | 400 | 401 | 403 | 404 | 500 | 501 | -1 code: 200 | 400 | 401 | 403 | 404 | 500 | 501 | -1,
data?: { [key: string]: unknown } data?: { [key: string]: unknown },
caused_by?: ErrorCausedBy,
} }
export default ApiCallbackMessage export default ApiCallbackMessage

View File

@@ -25,6 +25,8 @@ import SelectPreference from '../preference/SelectPreference.tsx'
import TextFieldPreference from '../preference/TextFieldPreference.tsx' import TextFieldPreference from '../preference/TextFieldPreference.tsx'
import Preference from '../preference/Preference.tsx' import Preference from '../preference/Preference.tsx'
import GroupSettings from "../../api/client_data/GroupSettings.ts" import GroupSettings from "../../api/client_data/GroupSettings.ts"
import PreferenceUpdater from "../preference/PreferenceUpdater.ts"
import SystemMessage from "./SystemMessage.tsx"
interface Args extends React.HTMLAttributes<HTMLElement> { interface Args extends React.HTMLAttributes<HTMLElement> {
target: string target: string
@@ -65,7 +67,6 @@ export default function ChatFragment({ target, showReturnButton, onReturnButtonC
} as Chat) } as Chat)
const [tabItemSelected, setTabItemSelected] = React.useState('None') const [tabItemSelected, setTabItemSelected] = React.useState('None')
const [groupPreferenceDefaultValue, setGroupPreferenceDefaultValue] = React.useState<GroupSettings>({})
const tabRef = React.useRef<Tab>(null) const tabRef = React.useRef<Tab>(null)
const chatPanelRef = React.useRef<HTMLElement>(null) const chatPanelRef = React.useRef<HTMLElement>(null)
useEventListener(tabRef, 'change', () => { useEventListener(tabRef, 'change', () => {
@@ -85,9 +86,7 @@ export default function ChatFragment({ target, showReturnButton, onReturnButtonC
setTabItemSelected("Chat") setTabItemSelected("Chat")
if (re.data!.type == 'group') { if (re.data!.type == 'group') {
setGroupPreferenceDefaultValue(re.data!.settings as GroupSettings) groupPreferenceStore.setState(re.data!.settings as GroupSettings)
groupPreferenceStore.setter(re.data!.settings as GroupSettings)
console.log(re.data!.settings as GroupSettings)
} }
setTimeout(() => { setTimeout(() => {
chatPanelRef.current!.scrollTo({ chatPanelRef.current!.scrollTo({
@@ -105,7 +104,12 @@ export default function ChatFragment({ target, showReturnButton, onReturnButtonC
page: page.current, page: page.current,
}) })
if (checkApiSuccessOrSncakbar(re, "拉取对话记录失败")) return if (re.caused_by == 'NOT_IN_THIS_CHAT_MEMBER_LIST')
return setMessagesList([{
text: '您未在群成员之中, 请等待管理员审批...',
}] as Message[])
else if(checkApiSuccessOrSncakbar(re, "拉取对话记录失败"))
return
const returnMsgs = (re.data!.messages as Message[]).reverse() const returnMsgs = (re.data!.messages as Message[]).reverse()
page.current++ page.current++
if (returnMsgs.length == 0) { if (returnMsgs.length == 0) {
@@ -219,18 +223,13 @@ export default function ChatFragment({ target, showReturnButton, onReturnButtonC
}) })
const groupPreferenceStore = new PreferenceStore<GroupSettings>() const groupPreferenceStore = new PreferenceStore<GroupSettings>()
groupPreferenceStore.setOnUpdate(async (value) => { groupPreferenceStore.setOnUpdate(async (value, oldvalue) => {
const re = await Client.invoke("Chat.updateSettings", { const re = await Client.invoke("Chat.updateSettings", {
token: data.access_token, token: data.access_token,
target, target,
settings: value, settings: value,
}) })
if (checkApiSuccessOrSncakbar(re, "更新设定失败")) return if (checkApiSuccessOrSncakbar(re, "更新设定失败")) return groupPreferenceStore.setState(oldvalue)
setChatInfo(JSON.parse(JSON.stringify({
...chatInfo,
settings: value as object,
})))
}) })
return ( return (
@@ -257,6 +256,7 @@ export default function ChatFragment({ target, showReturnButton, onReturnButtonC
<mdui-tab value="Chat">{ <mdui-tab value="Chat">{
chatInfo.title chatInfo.title
}</mdui-tab> }</mdui-tab>
{chatInfo.type == 'group' && <mdui-tab value="NewMemberRequests"></mdui-tab>}
<mdui-tab value="Settings"></mdui-tab> <mdui-tab value="Settings"></mdui-tab>
<mdui-tab value="None" style={{ display: 'none' }}></mdui-tab> <mdui-tab value="None" style={{ display: 'none' }}></mdui-tab>
<div style={{ <div style={{
@@ -330,7 +330,7 @@ export default function ChatFragment({ target, showReturnButton, onReturnButtonC
const lastDate = date const lastDate = date
date = new Date(msg.time) date = new Date(msg.time)
const msgElement = <Element_Message const msgElement = msg.user_id == null ? <SystemMessage>{msg.text}</SystemMessage> : <Element_Message
rawData={msg.text} rawData={msg.text}
renderHTML={rendeText} renderHTML={rendeText}
message={msg} message={msg}
@@ -343,6 +343,7 @@ export default function ChatFragment({ target, showReturnButton, onReturnButtonC
return ( return (
<> <>
{ {
msg.user_id != null &&
(date.getMinutes() != lastDate.getMinutes() || date.getDate() != lastDate.getDate() || date.getMonth() != lastDate.getMonth() || date.getFullYear() != lastDate.getFullYear()) (date.getMinutes() != lastDate.getMinutes() || date.getDate() != lastDate.getDate() || date.getMonth() != lastDate.getMonth() || date.getFullYear() != lastDate.getFullYear())
&& <mdui-tooltip content={`${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`}> && <mdui-tooltip content={`${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`}>
<div style={{ <div style={{
@@ -449,6 +450,15 @@ export default function ChatFragment({ target, showReturnButton, onReturnButtonC
</div> </div>
</div> </div>
</mdui-tab-panel> </mdui-tab-panel>
{
chatInfo.type == 'group' && <mdui-tab-panel slot="panel" value="NewMemberRequests" style={{
display: tabItemSelected == "NewMemberRequests" ? "flex" : "none",
flexDirection: "column",
height: "100%",
}}>
</mdui-tab-panel>
}
<mdui-tab-panel slot="panel" value="Settings" style={{ <mdui-tab-panel slot="panel" value="Settings" style={{
display: tabItemSelected == "Settings" ? "flex" : "none", display: tabItemSelected == "Settings" ? "flex" : "none",
flexDirection: "column", flexDirection: "column",
@@ -456,48 +466,50 @@ export default function ChatFragment({ target, showReturnButton, onReturnButtonC
}}> }}>
{ {
chatInfo.type == 'group' && <PreferenceLayout> chatInfo.type == 'group' && <PreferenceLayout>
<PreferenceHeader <PreferenceUpdater.Provider value={groupPreferenceStore.createUpdater()}>
title="群组管理" /> <PreferenceHeader
<Preference title="群组管理" />
title="群组成员列表" <Preference
icon="group" title="群组成员列表"
disabled={true} icon="group"
description="别看了, 还没做" />
<PreferenceHeader
title="入群设定" />
<SwitchPreference
title="允许入群"
icon="person_add"
defaultState={groupPreferenceDefaultValue.allow_new_member_join || false}
updater={groupPreferenceStore.updater('allow_new_member_join')} />
<SwitchPreference
title="允许成员邀请"
description="目前压根没有这项功能, 甚至还不能查看成员列表, 以后再说吧"
icon="_"
disabled={true}
defaultState={groupPreferenceDefaultValue.allow_new_member_from_invitation || false}
updater={groupPreferenceStore.updater('allow_new_member_from_invitation')} />
<SelectPreference
title="入群验证方式"
icon="_"
selections={{
disabled: "无需验证",
allowed_by_admin: "只需要管理员批准 (WIP)",
answered_and_allowed_by_admin: "需要回答问题并获得管理员批准 (WIP)",
}}
disabled={!groupPreferenceStore.value.allow_new_member_join}
updater={groupPreferenceStore.updater('new_member_join_method')}
defaultCheckedId={groupPreferenceDefaultValue.new_member_join_method || 'disabled'} />
{
groupPreferenceStore.value.new_member_join_method == 'answered_and_allowed_by_admin'
&& <TextFieldPreference
title="设置问题"
icon="_"
description="WIP"
defaultState={groupPreferenceDefaultValue.answered_and_allowed_by_admin_question || ''}
disabled={true} disabled={true}
updater={groupPreferenceStore.updater('answered_and_allowed_by_admin_question')} /> description="别看了, 还没做" />
} <PreferenceHeader
title="入群设定" />
<SwitchPreference
title="允许入群"
icon="person_add"
id="allow_new_member_join"
state={groupPreferenceStore.state.allow_new_member_join || false} />
<SwitchPreference
title="允许成员邀请"
description="目前压根没有这项功能, 甚至还不能查看成员列表, 以后再说吧"
id="allow_new_member_from_invitation"
icon="_"
disabled={true}
state={groupPreferenceStore.state.allow_new_member_from_invitation || false} />
<SelectPreference
title="入群验证方式"
icon="_"
id="new_member_join_method"
selections={{
disabled: "无需验证",
allowed_by_admin: "只需要管理员批准 (WIP)",
answered_and_allowed_by_admin: "需要回答问题并获得管理员批准 (WIP)",
}}
disabled={!groupPreferenceStore.state.allow_new_member_join}
state={groupPreferenceStore.state.new_member_join_method || 'disabled'} />
{
groupPreferenceStore.state.new_member_join_method == 'answered_and_allowed_by_admin'
&& <TextFieldPreference
title="设置问题"
icon="_"
id="answered_and_allowed_by_admin_question"
description="WIP"
state={groupPreferenceStore.state.answered_and_allowed_by_admin_question || ''}
disabled={true} />
}
</PreferenceUpdater.Provider>
</PreferenceLayout> </PreferenceLayout>
} }
{ {

View File

@@ -1,8 +1,5 @@
/**
* export default function SystemMessage({ children }: React.HTMLAttributes<HTMLElement>) {
* @returns { React.JSX.Element }
*/
export default function SystemMessage({ children } = {}) {
return ( return (
<div style={{ <div style={{
width: '100%', width: '100%',

View File

@@ -1,26 +1,27 @@
import React from 'react' import React from 'react'
export default class PreferenceStore<T extends object> { export default class PreferenceStore<T extends object> {
declare value: T declare onUpdate: (value: T, oldvalue: T) => void
declare setter: React.Dispatch<React.SetStateAction<T>> declare state: T
declare onUpdate: (value: unknown) => void declare setState: React.Dispatch<React.SetStateAction<T>>
constructor() { constructor() {
const _ = React.useState<T>({} as T) const _ = React.useState({} as T)
this.value = _[0] this.state = _[0]
this.setter = _[1] this.setState = _[1]
} }
// 创建一个用于子选项的更新函数
updater(key: string) { createUpdater() {
return (value: unknown) => { return (key: string, value: unknown) => {
const newValue = JSON.parse(JSON.stringify({ const oldvalue = this.state
...this.value, const newValue = {
...this.state,
[key]: value, [key]: value,
})) }
this.setter(newValue) this.setState(newValue)
this.onUpdate?.(newValue) this.onUpdate?.(newValue, oldvalue)
} }
} }
setOnUpdate(onUpdate: (value: unknown) => void) { setOnUpdate(onUpdate: (value: T, oldvalue: T) => void) {
this.onUpdate = onUpdate this.onUpdate = onUpdate
} }
} }

View File

@@ -0,0 +1,6 @@
import React from 'react'
// deno-lint-ignore no-explicit-any
const PreferenceUpdater = React.createContext<(key: string, value: unknown) => void>(null as any)
export default PreferenceUpdater

View File

@@ -1,18 +1,19 @@
import React from 'react' import React from 'react'
import { Dropdown } from 'mdui' import { Dropdown } from 'mdui'
import useEventListener from '../useEventListener.ts' import useEventListener from '../useEventListener.ts'
import PreferenceUpdater from "./PreferenceUpdater.ts"
interface Args extends React.HTMLAttributes<HTMLElement> { interface Args extends React.HTMLAttributes<HTMLElement> {
title: string title: string
icon: string icon: string
id: string
disabled?: boolean disabled?: boolean
updater: (value: unknown) => void
selections: { [id: string]: string } selections: { [id: string]: string }
defaultCheckedId: string state: string
} }
export default function SelectPreference({ title, icon, updater, selections, defaultCheckedId, disabled }: Args) { export default function SelectPreference({ title, icon, id: preferenceId, selections, state, disabled }: Args) {
const [checkedId, setCheckedId] = React.useState(defaultCheckedId) const updater = React.useContext(PreferenceUpdater)
const dropDownRef = React.useRef<Dropdown>(null) const dropDownRef = React.useRef<Dropdown>(null)
const [isDropDownOpen, setDropDownOpen] = React.useState(false) const [isDropDownOpen, setDropDownOpen] = React.useState(false)
@@ -30,14 +31,13 @@ export default function SelectPreference({ title, icon, updater, selections, def
{ {
Object.keys(selections).map((id) => Object.keys(selections).map((id) =>
// @ts-ignore: selected 确实存在, 但是并不对外公开使用 // @ts-ignore: selected 确实存在, 但是并不对外公开使用
<mdui-menu-item key={id} selected={checkedId == id ? true : undefined} onClick={() => { <mdui-menu-item key={id} selected={state == id ? true : undefined} onClick={() => {
setCheckedId(id) updater(preferenceId, id)
updater(id)
}}>{selections[id]}</mdui-menu-item> }}>{selections[id]}</mdui-menu-item>
) )
} }
</mdui-menu> </mdui-menu>
</mdui-dropdown> </mdui-dropdown>
<span slot="description">{selections[checkedId]}</span> <span slot="description">{selections[state]}</span>
</mdui-list-item> </mdui-list-item>
} }

View File

@@ -1,25 +1,27 @@
import { Switch } from 'mdui' import { Switch } from 'mdui'
import React from 'react' import React from 'react'
import PreferenceUpdater from "./PreferenceUpdater.ts"
interface Args extends React.HTMLAttributes<HTMLElement> { interface Args extends React.HTMLAttributes<HTMLElement> {
title: string title: string
id: string
description?: string description?: string
icon: string icon: string
updater: (value: unknown) => void state: boolean
defaultState: boolean
disabled?: boolean disabled?: boolean
} }
export default function SwitchPreference({ title, icon, updater, disabled, description, defaultState }: Args) { export default function SwitchPreference({ title, icon, id, disabled, description, state }: Args) {
const updater = React.useContext(PreferenceUpdater)
const switchRef = React.useRef<Switch>(null) const switchRef = React.useRef<Switch>(null)
React.useEffect(() => { React.useEffect(() => {
switchRef.current!.checked = defaultState switchRef.current!.checked = state
}, [defaultState]) }, [state])
return <mdui-list-item disabled={disabled ? true : undefined} rounded icon={icon} onClick={() => { return <mdui-list-item disabled={disabled ? true : undefined} rounded icon={icon} onClick={() => {
switchRef.current!.checked = !switchRef.current!.checked updater(id, !state)
updater(switchRef.current!.checked)
}}> }}>
{title} {title}
{description && <span slot="description">{description}</span>} {description && <span slot="description">{description}</span>}

View File

@@ -1,17 +1,18 @@
import React from 'react' import React from 'react'
import { prompt } from 'mdui' import { prompt } from 'mdui'
import PreferenceUpdater from "./PreferenceUpdater.ts"
interface Args extends React.HTMLAttributes<HTMLElement> { interface Args extends React.HTMLAttributes<HTMLElement> {
title: string title: string
description?: string description?: string
icon: string icon: string
updater: (value: unknown) => void id: string
defaultState: string state: string
disabled?: boolean disabled?: boolean
} }
export default function TextFieldPreference({ title, icon, description, updater, defaultState, disabled }: Args) { export default function TextFieldPreference({ title, icon, description, id, state, disabled }: Args) {
const [ text, setText ] = React.useState(defaultState) const updater = React.useContext(PreferenceUpdater)
return <mdui-list-item icon={icon} rounded disabled={disabled ? true : undefined} onClick={() => { return <mdui-list-item icon={icon} rounded disabled={disabled ? true : undefined} onClick={() => {
prompt({ prompt({
@@ -19,13 +20,12 @@ export default function TextFieldPreference({ title, icon, description, updater,
confirmText: "确定", confirmText: "确定",
cancelText: "取消", cancelText: "取消",
onConfirm: (value) => { onConfirm: (value) => {
setText(value) updater(id, value)
updater(value)
}, },
onCancel: () => {}, onCancel: () => {},
textFieldOptions: { textFieldOptions: {
label: description, label: description,
value: text, value: state,
}, },
}) })
}}> }}>

View File

@@ -1,3 +1,6 @@
type ErrorCausedBy =
'NOT_IN_THIS_CHAT_MEMBER_LIST'
type ApiCallbackMessage = { type ApiCallbackMessage = {
msg: string, msg: string,
/** /**
@@ -11,5 +14,6 @@ type ApiCallbackMessage = {
*/ */
code: 200 | 400 | 401 | 403 | 404 | 500 | 501, code: 200 | 400 | 401 | 403 | 404 | 500 | 501,
data?: { [key: string]: unknown }, data?: { [key: string]: unknown },
caused_by?: ErrorCausedBy,
} }
export default ApiCallbackMessage export default ApiCallbackMessage

View File

@@ -10,6 +10,8 @@ import TokenManager from "./TokenManager.ts"
import ChatPrivate from "../data/ChatPrivate.ts" import ChatPrivate from "../data/ChatPrivate.ts"
import ChatGroup from "../data/ChatGroup.ts" import ChatGroup from "../data/ChatGroup.ts"
import GroupSettingsBean from "../data/GroupSettingsBean.ts" import GroupSettingsBean from "../data/GroupSettingsBean.ts"
import ChatAdminLinker from "../data/ChatAdminLinker.ts"
import AdminPermissions from "../data/AdminPermissions.ts"
export default class ChatApi extends BaseApi { export default class ChatApi extends BaseApi {
override getName(): string { override getName(): string {
@@ -38,10 +40,6 @@ export default class ChatApi extends BaseApi {
code: 404, code: 404,
msg: "对话不存在", msg: "对话不存在",
} }
if (!UserChatLinker.checkUserIsLinkedToChat(token.author, chat!.bean.id)) return {
code: 400,
msg: "用户无权访问此对话",
}
// 私聊 // 私聊
if (chat!.bean.type == 'private') { if (chat!.bean.type == 'private') {
@@ -102,7 +100,7 @@ export default class ChatApi extends BaseApi {
msg: "对话不存在", msg: "对话不存在",
} }
if (!UserChatLinker.checkUserIsLinkedToChat(token.author, chat!.bean.id)) return { if (!UserChatLinker.checkUserIsLinkedToChat(token.author, chat!.bean.id)) return {
code: 400, code: 403,
msg: "用户无权访问此对话", msg: "用户无权访问此对话",
} }
@@ -161,8 +159,9 @@ export default class ChatApi extends BaseApi {
msg: "对话不存在", msg: "对话不存在",
} }
if (!UserChatLinker.checkUserIsLinkedToChat(token.author, chat!.bean.id)) return { if (!UserChatLinker.checkUserIsLinkedToChat(token.author, chat!.bean.id)) return {
code: 400, code: 403,
msg: "用户无权访问此对话", msg: "用户无权访问此对话",
caused_by: 'NOT_IN_THIS_CHAT_MEMBER_LIST',
} }
return { return {
@@ -198,7 +197,7 @@ export default class ChatApi extends BaseApi {
msg: "对话不存在", msg: "对话不存在",
} }
if (!UserChatLinker.checkUserIsLinkedToChat(token.author, chat!.bean.id)) return { if (!UserChatLinker.checkUserIsLinkedToChat(token.author, chat!.bean.id)) return {
code: 400, code: 403,
msg: "用户无权访问此对话", msg: "用户无权访问此对话",
} }
@@ -238,6 +237,44 @@ export default class ChatApi extends BaseApi {
} }
const chat = ChatPrivate.findOrCreateForPrivate(user, targetUser) const chat = ChatPrivate.findOrCreateForPrivate(user, targetUser)
return {
code: 200,
msg: '成功',
data: {
chat_id: chat.bean.id,
}
}
})
/**
* 加入群组
* @param token 令牌
* @param target ID
*/
this.registerEvent("Chat.requestJoinGroup", (args, { deviceId }) => {
if (this.checkArgsMissing(args, ['token', 'target'])) 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 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: "用户无权访问此对话",
}
return { return {
code: 200, code: 200,
msg: '成功', msg: '成功',
@@ -280,7 +317,11 @@ export default class ChatApi extends BaseApi {
chat.addMembers([ chat.addMembers([
user.bean.id, user.bean.id,
]) ])
chat.addAdmin(user.bean.id, [
AdminPermissions.OWNER,
])
user.addContact(chat.bean.id) user.addContact(chat.bean.id)
MessagesManager.getInstanceForChat(chat).addSystemMessage("群组已创建")
return { return {
code: 200, code: 200,
@@ -316,7 +357,13 @@ export default class ChatApi extends BaseApi {
} }
if (chat.bean.type == 'group') if (chat.bean.type == 'group')
ChatGroup.fromChat(chat).getSettings().update(args.settings as GroupSettingsBean) if (ChatAdminLinker.checkAdminIsLinkedToChat(user.bean.id, chat.bean.id))
ChatGroup.fromChat(chat).getSettings().update(args.settings as GroupSettingsBean)
else
return {
code: 403,
msg: "没有此权限",
}
return { return {
code: 200, code: 200,
@@ -348,7 +395,7 @@ export default class ChatApi extends BaseApi {
msg: "对话不存在", msg: "对话不存在",
} }
if (!UserChatLinker.checkUserIsLinkedToChat(token.author, chat!.bean.id)) return { if (!UserChatLinker.checkUserIsLinkedToChat(token.author, chat!.bean.id)) return {
code: 400, code: 403,
msg: "用户无权访问此对话", msg: "用户无权访问此对话",
} }

View File

@@ -83,8 +83,14 @@ export default class Chat {
addAdmin(userId: string, permission: string[] | string) { addAdmin(userId: string, permission: string[] | string) {
ChatAdminLinker.linkAdminAndChat(userId, this.bean.id) ChatAdminLinker.linkAdminAndChat(userId, this.bean.id)
this.setAdminPermissions(userId, permission)
}
setAdminPermissions(userId: string, permission: string[] | string) {
ChatAdminLinker.updatePermissions(userId, this.bean.id, permission instanceof Array ? JSON.stringify(permission) : permission) ChatAdminLinker.updatePermissions(userId, this.bean.id, permission instanceof Array ? JSON.stringify(permission) : permission)
} }
removeAdmins(userIds: string[]) {
userIds.forEach((v) => ChatAdminLinker.unlinkAdminAndChat(v, this.bean.id))
}
getMembersList() { getMembersList() {
return UserChatLinker.getChatMembers(this.bean.id) return UserChatLinker.getChatMembers(this.bean.id)
} }

View File

@@ -27,7 +27,7 @@ export default class MessagesManager {
CREATE TABLE IF NOT EXISTS ${this.getTableName()} ( CREATE TABLE IF NOT EXISTS ${this.getTableName()} (
/* 序号, MessageId */ id INTEGER PRIMARY KEY AUTOINCREMENT, /* 序号, MessageId */ id INTEGER PRIMARY KEY AUTOINCREMENT,
/* 消息文本 */ text TEXT NOT NULL, /* 消息文本 */ text TEXT NOT NULL,
/* 发送者 */ user_id TEXT NOT NULL, /* 发送者 */ user_id TEXT,
/* 发送时间 */ time INT8 NOT NULL /* 发送时间 */ time INT8 NOT NULL
); );
`) `)