Compare commits
13 Commits
38c28c3fb6
...
ab1ef2c30b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab1ef2c30b | ||
|
|
61bc1a265c | ||
|
|
9c45f3e13e | ||
|
|
23ad29fb2d | ||
|
|
5b64c6adcf | ||
|
|
dd42f5e54e | ||
|
|
2d7b7818d7 | ||
|
|
c27eb37852 | ||
|
|
bc48cf801b | ||
|
|
e46661ba15 | ||
|
|
9cb71af85b | ||
|
|
241ff714b8 | ||
|
|
db43de19c4 |
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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%',
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
client/ui/preference/PreferenceUpdater.ts
Normal file
6
client/ui/preference/PreferenceUpdater.ts
Normal 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
|
||||||
@@ -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>
|
||||||
}
|
}
|
||||||
@@ -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>}
|
||||||
|
|||||||
@@ -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,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}}>
|
}}>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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: "用户无权访问此对话",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
);
|
);
|
||||||
`)
|
`)
|
||||||
|
|||||||
Reference in New Issue
Block a user