koishi-plugin-chat-patch 5.3.0 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/web/dist/assets/Chat-Clqxn7CB.js +28 -0
- package/client/web/dist/assets/{Chat-CTdyeaX2.css → Chat-DTEsebnY.css} +1 -1
- package/client/web/dist/assets/{MsgBody-DDacjWJ9.js → MsgBody-C0TKsXhC.js} +1 -1
- package/client/web/dist/assets/{MsgBody.vue_vue_type_script_setup_true_lang-B1dWyCB0.js → MsgBody.vue_vue_type_script_setup_true_lang-CCdBwlN_.js} +2 -2
- package/client/web/dist/assets/{append_darwin-CqBNU62J.css → append_darwin-Cql-dV6d.css} +1 -1
- package/client/web/dist/assets/{append_full_vibrancy-j0D2LM9m.css → append_full_vibrancy-ih3loy1O.css} +1 -1
- package/client/web/dist/assets/{append_linux-uhxeBqeC.css → append_linux-B3LvR8KB.css} +1 -1
- package/client/web/dist/assets/{append_new-407Lo5_V.css → append_new-DfhN_NX9.css} +1 -1
- package/client/web/dist/assets/{append_vibrancy-6FHctkOY.css → append_vibrancy-D3pVsmAa.css} +1 -1
- package/client/web/dist/assets/{append_win32-srmD-78y.css → append_win32-Dh9JlmlE.css} +1 -1
- package/client/web/dist/assets/index-B71pPwae.css +1 -0
- package/client/web/dist/assets/{index-BlLut5xn.js → index-BA37EQGa.js} +97 -76
- package/client/web/dist/index.html +2 -2
- package/client/web/src/App.vue +2 -2
- package/client/web/src/assets/css/append/append_darwin.css +2 -2
- package/client/web/src/assets/css/append/append_full_vibrancy.css +2 -2
- package/client/web/src/assets/css/append/append_linux.css +2 -2
- package/client/web/src/assets/css/append/append_new.css +2 -2
- package/client/web/src/assets/css/append/append_vibrancy.css +1 -1
- package/client/web/src/assets/css/append/append_win32.css +2 -2
- package/client/web/src/assets/css/append/mobile/append_mobile_horizontal.css +1 -1
- package/client/web/src/assets/css/chat.css +4 -3
- package/client/web/src/assets/css/msg.css +7 -3
- package/client/web/src/assets/css/options.css +2 -2
- package/client/web/src/assets/css/view.css +14 -3
- package/client/web/src/assets/l10n/zh-CN.po +21 -0
- package/client/web/src/components/AboutPan.vue +7 -4
- package/client/web/src/components/BulletinBody.vue +1 -1
- package/client/web/src/components/DepPan.vue +1 -1
- package/client/web/src/components/FriendBody.vue +1 -0
- package/client/web/src/components/MsgBody.vue +1 -3
- package/client/web/src/components/ThemeColorPickerPan.vue +1 -1
- package/client/web/src/components/TinySessionBody.vue +1 -0
- package/client/web/src/components/UmamiInfoPan.vue +1 -1
- package/client/web/src/components/UpdatePan.vue +2 -2
- package/client/web/src/components/WelPan.vue +1 -1
- package/client/web/src/components/tooltip/UserInfoTooltip.vue +1 -1
- package/client/web/src/function/connect.ts +279 -86
- package/client/web/src/function/elements/information.ts +1 -0
- package/client/web/src/function/msg.ts +42 -23
- package/client/web/src/function/satori-model.ts +35 -13
- package/client/web/src/function/satori.ts +33 -2
- package/client/web/src/function/utils/appUtil.ts +3 -11
- package/client/web/src/function/utils/avatarUtil.ts +18 -3
- package/client/web/src/function/utils/localHistoryUtil.ts +36 -12
- package/client/web/src/function/utils/msgUtil.ts +32 -21
- package/client/web/src/function/utils/sessionUtil.ts +20 -18
- package/client/web/src/main.ts +5 -0
- package/client/web/src/pages/Chat.vue +50 -47
- package/client/web/src/pages/Friends.vue +28 -24
- package/client/web/src/pages/Info.vue +5 -5
- package/client/web/src/pages/Messages.vue +57 -36
- package/client/web/src/pages/Qzone.vue +2 -2
- package/client/web/src/pages/chat-view/ChatTerminal.vue +4 -1
- package/client/web/src/pages/chat-view/SystemNotice.vue +3 -3
- package/client/web/src/pages/options/OptAccount.vue +6 -4
- package/client/web/src/pages/options/OptDev.vue +45 -2
- package/lib/database.d.ts +58 -0
- package/lib/index.js +234 -82
- package/lib/media.d.ts +24 -0
- package/lib/recorder.d.ts +21 -0
- package/lib/self-message.d.ts +27 -0
- package/lib/types.d.ts +121 -0
- package/lib/web.d.ts +7 -0
- package/package.json +1 -1
- package/src/bootstrap.ts +1 -0
- package/src/cache.ts +1 -5
- package/src/config.ts +1 -1
- package/src/database.ts +52 -8
- package/src/index.ts +4 -4
- package/src/media.ts +97 -0
- package/src/recorder.ts +35 -26
- package/src/self-message.ts +68 -19
- package/src/types.ts +10 -0
- package/src/web.ts +57 -48
- package/client/web/dist/assets/Chat-B448YOeN.js +0 -28
- package/client/web/dist/assets/index-fW_zcub3.css +0 -1
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type { Session } from '../elements/information'
|
|
2
2
|
|
|
3
3
|
export function normalizeGroupId(value: string): string {
|
|
4
|
-
|
|
5
|
-
const wrapped = raw.match(/^\[_?([\s\S]+?)_?\]$/)
|
|
6
|
-
return wrapped ? wrapped[1] : raw || value
|
|
4
|
+
return String(value)
|
|
7
5
|
}
|
|
8
6
|
|
|
9
7
|
export function normalizeSessionId(value: number | string): string {
|
|
@@ -27,12 +25,6 @@ export function getSessionAliases(item: Session): string[] {
|
|
|
27
25
|
getSessionId(item),
|
|
28
26
|
getLegacySessionId(item),
|
|
29
27
|
]
|
|
30
|
-
if (item.group_id !== undefined && item.group_id !== null) {
|
|
31
|
-
rawAliases.push(normalizeGroupId(String(item.group_id)))
|
|
32
|
-
}
|
|
33
|
-
if (item.user_id !== undefined && item.user_id !== null) {
|
|
34
|
-
rawAliases.push(String(item.user_id).replace(/^(?:private|direct):/i, ''))
|
|
35
|
-
}
|
|
36
28
|
const aliases = rawAliases.filter((value): value is number | string => {
|
|
37
29
|
const text = String(value ?? '')
|
|
38
30
|
return text !== '' && text !== '0'
|
|
@@ -44,11 +36,10 @@ export function getSessionDedupKey(item: Session): string {
|
|
|
44
36
|
const channelId = String(item.channel_id ?? item.channelId ?? '')
|
|
45
37
|
const groupId = String(item.group_id ?? '')
|
|
46
38
|
const userId = String(item.user_id ?? '')
|
|
47
|
-
if (
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
: `group:${normalizeGroupId(channelId)}`
|
|
39
|
+
if (channelId) return normalizeSessionId(channelId)
|
|
40
|
+
if (groupId) return normalizeSessionId(groupId)
|
|
41
|
+
if (userId) return normalizeSessionId(userId)
|
|
42
|
+
return ''
|
|
52
43
|
}
|
|
53
44
|
|
|
54
45
|
export function setSessionContact(
|
|
@@ -60,6 +51,21 @@ export function setSessionContact(
|
|
|
60
51
|
}
|
|
61
52
|
}
|
|
62
53
|
|
|
54
|
+
export function upsertSessionContact(
|
|
55
|
+
contacts: Session[],
|
|
56
|
+
item: Session,
|
|
57
|
+
): Session[] {
|
|
58
|
+
const key = getSessionDedupKey(item)
|
|
59
|
+
if (!key) return contacts
|
|
60
|
+
const index = contacts.findIndex((current) => getSessionDedupKey(current) === key)
|
|
61
|
+
if (index >= 0) {
|
|
62
|
+
const next = [...contacts]
|
|
63
|
+
Object.assign(next[index], item)
|
|
64
|
+
return next
|
|
65
|
+
}
|
|
66
|
+
return [...contacts, item]
|
|
67
|
+
}
|
|
68
|
+
|
|
63
69
|
export function findSessionContact(
|
|
64
70
|
contacts: Session[],
|
|
65
71
|
sessionId: number | string,
|
|
@@ -67,10 +73,6 @@ export function findSessionContact(
|
|
|
67
73
|
const target = normalizeSessionId(sessionId)
|
|
68
74
|
return contacts.find((item) => {
|
|
69
75
|
return getSessionAliases(item).some((alias) => alias === target)
|
|
70
|
-
|| (
|
|
71
|
-
Boolean(item.group_id) &&
|
|
72
|
-
normalizeGroupId(String(item.group_id)) === normalizeGroupId(target)
|
|
73
|
-
)
|
|
74
76
|
})
|
|
75
77
|
}
|
|
76
78
|
|
package/client/web/src/main.ts
CHANGED
|
@@ -21,10 +21,15 @@ import './assets/css/sys_notice.css'
|
|
|
21
21
|
|
|
22
22
|
import { getPortableFileLang } from './function/utils/systemUtil'
|
|
23
23
|
import { preloadPinyin } from './function/utils/pinyin'
|
|
24
|
+
import { avatarError, isAvatarImage } from './function/utils/avatarUtil'
|
|
24
25
|
|
|
25
26
|
// 媒体资源由 <img>/<audio> 等元素加载时,失败事件只在前端控制台输出
|
|
26
27
|
window.addEventListener('error', (event) => {
|
|
27
28
|
const target = event.target
|
|
29
|
+
if (target instanceof HTMLImageElement && isAvatarImage(target)) {
|
|
30
|
+
avatarError(event)
|
|
31
|
+
return
|
|
32
|
+
}
|
|
28
33
|
if (!(target instanceof HTMLImageElement) && !(target instanceof HTMLMediaElement)) return
|
|
29
34
|
const source = target.currentSrc || target.src
|
|
30
35
|
if (source.includes('/chat-patch/web/media') || source.includes('/chat-patch/api/media')) {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
<!-- 聊天基本信息 -->
|
|
26
26
|
<div class="info">
|
|
27
27
|
<font-awesome-icon class="back" :icon="['fas', 'angle-left']" @click="exitWin" />
|
|
28
|
-
<img :src="chat.show.avatar || '/img/icons/icon.svg'" @error="avatarError">
|
|
28
|
+
<img data-avatar :src="chat.show.avatar || '/img/icons/icon.svg'" @error="avatarError">
|
|
29
29
|
<div class="info">
|
|
30
30
|
<p>
|
|
31
31
|
{{ chat.show.name }}
|
|
@@ -181,7 +181,7 @@
|
|
|
181
181
|
<div v-for="(item, index) in chat.info.jin_info.list"
|
|
182
182
|
:key="'jin-' + index">
|
|
183
183
|
<div>
|
|
184
|
-
<img :src="item.sender_avatar || '/img/icons/icon.svg'">
|
|
184
|
+
<img data-avatar :src="item.sender_avatar || '/img/icons/icon.svg'">
|
|
185
185
|
<div>
|
|
186
186
|
<a>{{ item.sender_nick }}</a>
|
|
187
187
|
<span>{{ item.sender_time ? Intl.DateTimeFormat(
|
|
@@ -517,7 +517,7 @@
|
|
|
517
517
|
<div><font-awesome-icon :icon="['fas', 'arrow-up-right-from-square']" /></div>
|
|
518
518
|
<a>{{ $t('跳转到消息') }}</a>
|
|
519
519
|
</div>
|
|
520
|
-
<div
|
|
520
|
+
<div @click="consoleLogMsg">
|
|
521
521
|
<div><font-awesome-icon :icon="['fas', 'screwdriver-wrench']" /></div>
|
|
522
522
|
<a>{{ $t('调试信息') }}</a>
|
|
523
523
|
</div>
|
|
@@ -738,13 +738,6 @@ const searchRequestId = ref(0)
|
|
|
738
738
|
const forwardList = ref(contactStore.userList)
|
|
739
739
|
const chatImg = ref<any>(undefined)
|
|
740
740
|
const trueLang = getTrueLang()
|
|
741
|
-
const isDev = import.meta.env.DEV
|
|
742
|
-
let selfRefreshTimer: number | null = null
|
|
743
|
-
|
|
744
|
-
function ensureChannelPrefix(channelId: string, prefix: string): string {
|
|
745
|
-
if (/^(?:group|room|chat|channel|guild|private):/i.test(channelId)) return channelId
|
|
746
|
-
return `${prefix}:${channelId}`
|
|
747
|
-
}
|
|
748
741
|
|
|
749
742
|
function messageLocalTimeMs(item: any): number {
|
|
750
743
|
const local = Number(item?.local_time ?? item?.timestamp_ms ?? item?.time_ms ?? 0)
|
|
@@ -757,8 +750,8 @@ async function refreshSelfHistory() {
|
|
|
757
750
|
const id = chat.show.id
|
|
758
751
|
if (!id || id === 0 || details.value[3].open || tags.value.showForwardPan) return
|
|
759
752
|
const channelId = chat.show.type === 'group'
|
|
760
|
-
?
|
|
761
|
-
:
|
|
753
|
+
? String(chat.show.channel_id ?? id)
|
|
754
|
+
: String(chat.show.channel_id ?? id)
|
|
762
755
|
let cached: Record<string, unknown>[] = []
|
|
763
756
|
try {
|
|
764
757
|
cached = await loadChatHistoryFromCache({
|
|
@@ -799,22 +792,8 @@ async function refreshSelfHistory() {
|
|
|
799
792
|
}
|
|
800
793
|
|
|
801
794
|
function startSelfRefresh() {
|
|
802
|
-
if (selfRefreshTimer !== null) {
|
|
803
|
-
window.clearInterval(selfRefreshTimer)
|
|
804
|
-
selfRefreshTimer = null
|
|
805
|
-
}
|
|
806
795
|
if (!chat.show.id) return
|
|
807
796
|
void refreshSelfHistory()
|
|
808
|
-
selfRefreshTimer = window.setInterval(() => {
|
|
809
|
-
void refreshSelfHistory()
|
|
810
|
-
}, 2500)
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
function stopSelfRefresh() {
|
|
814
|
-
if (selfRefreshTimer !== null) {
|
|
815
|
-
window.clearInterval(selfRefreshTimer)
|
|
816
|
-
selfRefreshTimer = null
|
|
817
|
-
}
|
|
818
797
|
}
|
|
819
798
|
|
|
820
799
|
//#region == 窗口移动相关 ==================================================
|
|
@@ -974,7 +953,6 @@ onMounted(() => {
|
|
|
974
953
|
})
|
|
975
954
|
|
|
976
955
|
onBeforeUnmount(() => {
|
|
977
|
-
stopSelfRefresh()
|
|
978
956
|
if (resizeMainInputFrame !== null) {
|
|
979
957
|
cancelAnimationFrame(resizeMainInputFrame)
|
|
980
958
|
resizeMainInputFrame = null
|
|
@@ -1218,19 +1196,24 @@ async function loadMoreHistory() {
|
|
|
1218
1196
|
}
|
|
1219
1197
|
uiStore.loadHistoryFail = false
|
|
1220
1198
|
|
|
1199
|
+
const channelId = String(
|
|
1200
|
+
chatStore.chatInfo.show.channel_id
|
|
1201
|
+
?? chatStore.chatInfo.show.id
|
|
1202
|
+
?? '',
|
|
1203
|
+
)
|
|
1221
1204
|
if (useMixedHistory) {
|
|
1222
1205
|
let localMsgs = [] as any[]
|
|
1223
1206
|
if (Number.isFinite(firstMsgTime)) {
|
|
1224
1207
|
localMsgs = await dbGetBeforeByTime(
|
|
1225
1208
|
authStore.loginInfo.uin,
|
|
1226
|
-
|
|
1209
|
+
channelId,
|
|
1227
1210
|
firstMsgTime,
|
|
1228
1211
|
20,
|
|
1229
1212
|
)
|
|
1230
1213
|
} else {
|
|
1231
1214
|
localMsgs = await dbGetBefore(
|
|
1232
1215
|
authStore.loginInfo.uin,
|
|
1233
|
-
|
|
1216
|
+
channelId,
|
|
1234
1217
|
firstMsgId,
|
|
1235
1218
|
20,
|
|
1236
1219
|
)
|
|
@@ -1252,12 +1235,6 @@ async function loadMoreHistory() {
|
|
|
1252
1235
|
}
|
|
1253
1236
|
}
|
|
1254
1237
|
|
|
1255
|
-
const type = chatStore.chatInfo.show.type
|
|
1256
|
-
const id = chatStore.chatInfo.show.id
|
|
1257
|
-
const rawChannelId = String(chatStore.chatInfo.show.channel_id ?? '')
|
|
1258
|
-
const channelId = type === 'group'
|
|
1259
|
-
? ensureChannelPrefix(rawChannelId || String(id), 'group')
|
|
1260
|
-
: ensureChannelPrefix(rawChannelId || String(id), 'private')
|
|
1261
1238
|
const cached = await loadChatHistoryFromCache({
|
|
1262
1239
|
platform: String(authStore.loginInfo.platform ?? ''),
|
|
1263
1240
|
selfId: String(authStore.loginInfo.uin ?? ''),
|
|
@@ -2031,6 +2008,7 @@ function forwardMsg(data: UserFriendElem & UserGroupElem) {
|
|
|
2031
2008
|
targetType as 'group' | 'user',
|
|
2032
2009
|
targetId,
|
|
2033
2010
|
msgBody,
|
|
2011
|
+
targetChannelId || undefined,
|
|
2034
2012
|
)
|
|
2035
2013
|
if (!result.ok) {
|
|
2036
2014
|
new PopInfo().add(PopType.ERR, $t('合并转发发送失败'))
|
|
@@ -2073,11 +2051,7 @@ function forwardMsg(data: UserFriendElem & UserGroupElem) {
|
|
|
2073
2051
|
scrollToMsg('chat-' + sentMsg.message_id, true)
|
|
2074
2052
|
})
|
|
2075
2053
|
}
|
|
2076
|
-
const sentChannelId =
|
|
2077
|
-
? (targetChannelId || targetId)
|
|
2078
|
-
: /^(?:private|direct):/i.test(targetChannelId || '')
|
|
2079
|
-
? targetChannelId
|
|
2080
|
-
: `private:${targetId}`
|
|
2054
|
+
const sentChannelId = result.channelId || targetChannelId || targetId
|
|
2081
2055
|
if (result.native && !result.messageId) {
|
|
2082
2056
|
// 原生路径已经由后端 before-send/发送拦截写入,缺少消息 id 时不再重复写入
|
|
2083
2057
|
} else {
|
|
@@ -2097,7 +2071,8 @@ function forwardMsg(data: UserFriendElem & UserGroupElem) {
|
|
|
2097
2071
|
kind: 'forward',
|
|
2098
2072
|
})
|
|
2099
2073
|
}
|
|
2100
|
-
const
|
|
2074
|
+
const targetSessionKey = targetChannelId || targetId
|
|
2075
|
+
const targetSession = contactStore.baseOnMsgList.get(normalizeSessionId(targetSessionKey))
|
|
2101
2076
|
if (targetSession) {
|
|
2102
2077
|
const preview = `[${$t('聊天记录')}]`
|
|
2103
2078
|
targetSession.raw_msg_base = preview
|
|
@@ -2105,7 +2080,7 @@ function forwardMsg(data: UserFriendElem & UserGroupElem) {
|
|
|
2105
2080
|
? `${String(authStore.loginInfo.nickname ?? '')}: ${preview}`
|
|
2106
2081
|
: preview
|
|
2107
2082
|
targetSession.time = Math.floor(sentTime / 1000)
|
|
2108
|
-
contactStore.baseOnMsgList.set(normalizeSessionId(
|
|
2083
|
+
contactStore.baseOnMsgList.set(normalizeSessionId(targetSessionKey), targetSession)
|
|
2109
2084
|
updateBaseOnMsgList()
|
|
2110
2085
|
}
|
|
2111
2086
|
}
|
|
@@ -2154,8 +2129,9 @@ function forwardMsg(data: UserFriendElem & UserGroupElem) {
|
|
|
2154
2129
|
|
|
2155
2130
|
function finishForward(data: UserFriendElem & UserGroupElem, id: string | number) {
|
|
2156
2131
|
cancelForward()
|
|
2157
|
-
|
|
2158
|
-
|
|
2132
|
+
const sessionKey = String(data.channel_id ?? data.channelId ?? id ?? '')
|
|
2133
|
+
if(contactStore.baseOnMsgList.get(normalizeSessionId(sessionKey)) == undefined) {
|
|
2134
|
+
contactStore.baseOnMsgList.set(normalizeSessionId(sessionKey), data)
|
|
2159
2135
|
}
|
|
2160
2136
|
nextTick(() => {
|
|
2161
2137
|
const user = document.getElementById('user-' + id)
|
|
@@ -2268,7 +2244,15 @@ async function revokeMsg() {
|
|
|
2268
2244
|
?? chatStore.chatInfo.show.channel_id
|
|
2269
2245
|
?? chat.show.id,
|
|
2270
2246
|
)
|
|
2271
|
-
|
|
2247
|
+
try {
|
|
2248
|
+
await Connector.callApi('delete_msg', { channel_id: channelId, message_id: msgId })
|
|
2249
|
+
} catch {
|
|
2250
|
+
new PopInfo().add(PopType.ERR, $t('撤回失败'))
|
|
2251
|
+
return
|
|
2252
|
+
}
|
|
2253
|
+
msgData.revoke = true
|
|
2254
|
+
msgData.revoked = true
|
|
2255
|
+
msgData.revokeTime = Date.now() / 1000
|
|
2272
2256
|
}
|
|
2273
2257
|
|
|
2274
2258
|
async function reeditMsg() {
|
|
@@ -2630,13 +2614,31 @@ function sendVideo(file: File, fileName: string | null) {
|
|
|
2630
2614
|
sendMedia(file, fileName, 'video')
|
|
2631
2615
|
}
|
|
2632
2616
|
|
|
2617
|
+
async function isGifImage(file: File): Promise<boolean> {
|
|
2618
|
+
if (file.type === 'image/gif' || /\.gif$/i.test(file.name || '')) return true
|
|
2619
|
+
try {
|
|
2620
|
+
const buffer = await file.slice(0, 6).arrayBuffer()
|
|
2621
|
+
const bytes = new Uint8Array(buffer)
|
|
2622
|
+
return bytes.length >= 6
|
|
2623
|
+
&& bytes[0] === 0x47
|
|
2624
|
+
&& bytes[1] === 0x49
|
|
2625
|
+
&& bytes[2] === 0x46
|
|
2626
|
+
&& bytes[3] === 0x38
|
|
2627
|
+
&& (bytes[4] === 0x37 || bytes[4] === 0x39)
|
|
2628
|
+
&& bytes[5] === 0x61
|
|
2629
|
+
} catch {
|
|
2630
|
+
return false
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2633
2634
|
async function setImg(file: File | null) {
|
|
2634
2635
|
const popInfo = new PopInfo()
|
|
2635
2636
|
if (!file) return
|
|
2636
2637
|
if (!file.type.includes('image/')) return
|
|
2637
2638
|
if (file.size === 0) return
|
|
2639
|
+
const isGif = await isGifImage(file)
|
|
2638
2640
|
|
|
2639
|
-
if (file.size > 3145728) {
|
|
2641
|
+
if (file.size > 3145728 && !isGif) {
|
|
2640
2642
|
const options = { maxSizeMB: 3, useWebWorker: true }
|
|
2641
2643
|
try {
|
|
2642
2644
|
popInfo.add(PopType.INFO, $t('正在压缩图片 ……'))
|
|
@@ -3171,7 +3173,8 @@ async function handleInput(event: Event) {
|
|
|
3171
3173
|
searchDebounceTimer.value = setTimeout(async () => {
|
|
3172
3174
|
const results = await dbSearchMessages(
|
|
3173
3175
|
authStore.loginInfo.uin,
|
|
3174
|
-
chatStore.chatInfo.show.
|
|
3176
|
+
chatStore.chatInfo.show.channel_id
|
|
3177
|
+
?? chatStore.chatInfo.show.id,
|
|
3175
3178
|
value,
|
|
3176
3179
|
)
|
|
3177
3180
|
if (requestId !== searchRequestId.value || !details.value[3].open) return
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<div id="friend-list" :class="'friend-list' + (uiStore.openSideBar ? ' open' : '')">
|
|
15
15
|
<div>
|
|
16
16
|
<div class="base">
|
|
17
|
-
<span>{{ $t('
|
|
17
|
+
<span>{{ $t('联系人列表') }}</span>
|
|
18
18
|
<div style="flex: 1" />
|
|
19
19
|
<font-awesome-icon :icon="['fas', 'rotate-right']" @click="reloadUser" />
|
|
20
20
|
</div>
|
|
@@ -56,17 +56,17 @@
|
|
|
56
56
|
@click="userClick(item, $event)" />
|
|
57
57
|
</div>
|
|
58
58
|
<template v-else>
|
|
59
|
-
<div v-for="bot in satoriLogins" :key="'bot-' + bot.selfId"
|
|
60
|
-
:class="['robot-accordion', { expanded: bot.selfId === activeBotId }]">
|
|
59
|
+
<div v-for="bot in satoriLogins" :key="'bot-' + botKey(bot.platform, bot.selfId)"
|
|
60
|
+
:class="['robot-accordion', { expanded: botKey(bot.platform, bot.selfId) === activeBotId }]">
|
|
61
61
|
<div class="robot-accordion-header" @click="toggleRobot(bot)">
|
|
62
|
-
<img :src="bot.avatar || '/img/icons/icon.svg'" :alt="bot.name" @error="avatarError">
|
|
62
|
+
<img data-avatar :src="bot.avatar || '/img/icons/icon.svg'" :alt="bot.name" @error="avatarError">
|
|
63
63
|
<div>
|
|
64
64
|
<span :title="bot.name">{{ bot.name }}</span>
|
|
65
65
|
<small>{{ bot.platform }}</small>
|
|
66
66
|
</div>
|
|
67
|
-
<font-awesome-icon :icon="['fas', bot.selfId === activeBotId ? 'angle-up' : 'angle-down']" />
|
|
67
|
+
<font-awesome-icon :icon="['fas', botKey(bot.platform, bot.selfId) === activeBotId ? 'angle-up' : 'angle-down']" />
|
|
68
68
|
</div>
|
|
69
|
-
<div v-if="bot.selfId === activeBotId" class="robot-accordion-content">
|
|
69
|
+
<div v-if="botKey(bot.platform, bot.selfId) === activeBotId" class="robot-accordion-content">
|
|
70
70
|
<div v-if="!contactStore.friendLoading && contactStore.userList.length === 0"
|
|
71
71
|
class="empty-state">
|
|
72
72
|
{{ $t('空') }}
|
|
@@ -183,8 +183,8 @@
|
|
|
183
183
|
|
|
184
184
|
import { reloadUsers } from '../function/utils/appUtil'
|
|
185
185
|
import { Connector, flushPendingBotEvents, loadContactsFromCache, login as loginInfo } from '../function/connect'
|
|
186
|
-
import { getActiveBot, getLogins, setActiveBot } from '../function/satori'
|
|
187
|
-
import {
|
|
186
|
+
import { botKey, getActiveBot, getLogins, setActiveBot } from '../function/satori'
|
|
187
|
+
import { normalizeSessionId } from '../function/utils/sessionUtil'
|
|
188
188
|
import { avatarError } from '../function/utils/avatarUtil'
|
|
189
189
|
import { backend } from '../runtime/backend'
|
|
190
190
|
import { matchPinyin } from '../function/utils/pinyin'
|
|
@@ -220,7 +220,12 @@
|
|
|
220
220
|
const searchInfo = ref('')
|
|
221
221
|
const classStatus = ref<{ [key: string]: boolean }>({})
|
|
222
222
|
const satoriLogins = ref<Array<{ platform: string; selfId: string; name: string; avatar?: string; status?: number; features?: string[] }>>(getLogins())
|
|
223
|
-
const
|
|
223
|
+
const active = getActiveBot()
|
|
224
|
+
const activeBotId = ref(
|
|
225
|
+
active?.platform && active.selfId
|
|
226
|
+
? botKey(active.platform, active.selfId)
|
|
227
|
+
: loginInfo.selectedSatoriBot ?? '',
|
|
228
|
+
)
|
|
224
229
|
|
|
225
230
|
watch(() => loginInfo.satoriLogins, (list) => {
|
|
226
231
|
satoriLogins.value = list ?? []
|
|
@@ -230,7 +235,7 @@
|
|
|
230
235
|
}, { immediate: true })
|
|
231
236
|
|
|
232
237
|
const currentBotName = computed(() => {
|
|
233
|
-
return satoriLogins.value.find((item) => item.selfId === activeBotId.value)?.name ?? ''
|
|
238
|
+
return satoriLogins.value.find((item) => botKey(item.platform, item.selfId) === activeBotId.value)?.name ?? ''
|
|
234
239
|
})
|
|
235
240
|
|
|
236
241
|
const allContacts = computed<ContactWithBot[]>(() => {
|
|
@@ -248,19 +253,17 @@
|
|
|
248
253
|
|| '',
|
|
249
254
|
)
|
|
250
255
|
const groupId = String(item.group_id || '')
|
|
251
|
-
const id = groupId
|
|
252
|
-
? `group:${normalizeGroupId(rawId || groupId)}`
|
|
253
|
-
: `user:${rawId.replace(/^(?:private|direct):/i, '')}`
|
|
256
|
+
const id = `${groupId ? 'group' : 'user'}|${String(rawId || groupId || item.user_id || '')}`
|
|
254
257
|
const key = [bot?.platform ?? '', bot?.selfId ?? '', id]
|
|
255
258
|
.map((value) => encodeURIComponent(String(value)))
|
|
256
259
|
.join(':')
|
|
257
260
|
map.set(key, { ...item, _bot: bot })
|
|
258
261
|
}
|
|
259
262
|
}
|
|
260
|
-
const currentBot = satoriLogins.value.find((item) => item.selfId === activeBotId.value)
|
|
263
|
+
const currentBot = satoriLogins.value.find((item) => botKey(item.platform, item.selfId) === activeBotId.value)
|
|
261
264
|
addContacts(contactStore.userList, currentBot)
|
|
262
|
-
for (const [
|
|
263
|
-
const bot = satoriLogins.value.find((item) => item.selfId ===
|
|
265
|
+
for (const [stateKey, state] of contactStore.botStates) {
|
|
266
|
+
const bot = satoriLogins.value.find((item) => botKey(item.platform, item.selfId) === stateKey)
|
|
264
267
|
addContacts(state.userList, bot)
|
|
265
268
|
}
|
|
266
269
|
return [...map.values()]
|
|
@@ -284,9 +287,10 @@
|
|
|
284
287
|
}
|
|
285
288
|
|
|
286
289
|
function selectRobot(bot: { platform: string; selfId: string; name: string; avatar?: string; features?: string[] }) {
|
|
287
|
-
|
|
290
|
+
const key = botKey(bot.platform, bot.selfId)
|
|
291
|
+
if (key === activeBotId.value) return
|
|
288
292
|
snapshotCurrentBot()
|
|
289
|
-
activeBotId.value =
|
|
293
|
+
activeBotId.value = key
|
|
290
294
|
setActiveBot(bot.platform, bot.selfId)
|
|
291
295
|
loginInfo.uin = bot.selfId
|
|
292
296
|
loginInfo.nickname = bot.name
|
|
@@ -299,9 +303,9 @@
|
|
|
299
303
|
platform: bot.platform,
|
|
300
304
|
avatar: bot.avatar,
|
|
301
305
|
satoriLogins: satoriLogins.value,
|
|
302
|
-
selectedSatoriBot:
|
|
306
|
+
selectedSatoriBot: key,
|
|
303
307
|
}
|
|
304
|
-
const saved = contactStore.botStates.get(
|
|
308
|
+
const saved = contactStore.botStates.get(key)
|
|
305
309
|
if (saved) {
|
|
306
310
|
contactStore.userList = saved.userList
|
|
307
311
|
contactStore.baseOnMsgList.clear()
|
|
@@ -339,7 +343,7 @@
|
|
|
339
343
|
}
|
|
340
344
|
|
|
341
345
|
function toggleRobot(bot: { platform: string; selfId: string; name: string; avatar?: string; features?: string[] }) {
|
|
342
|
-
if (activeBotId.value === bot.selfId) {
|
|
346
|
+
if (activeBotId.value === botKey(bot.platform, bot.selfId)) {
|
|
343
347
|
backToRobots()
|
|
344
348
|
} else {
|
|
345
349
|
selectRobot(bot)
|
|
@@ -395,7 +399,7 @@
|
|
|
395
399
|
}
|
|
396
400
|
|
|
397
401
|
function userClick(data: ContactWithBot, event: Event) {
|
|
398
|
-
if (data._bot && data._bot.selfId !== activeBotId.value) {
|
|
402
|
+
if (data._bot && botKey(data._bot.platform, data._bot.selfId) !== activeBotId.value) {
|
|
399
403
|
selectRobot(data._bot)
|
|
400
404
|
}
|
|
401
405
|
const sender = event.currentTarget as HTMLDivElement
|
|
@@ -659,12 +663,12 @@
|
|
|
659
663
|
font-size: 0.9rem;
|
|
660
664
|
}
|
|
661
665
|
|
|
662
|
-
@media (max-width:
|
|
666
|
+
@media (max-width: 800px) {
|
|
663
667
|
.exp-header:not(.open) {
|
|
664
668
|
display: none;
|
|
665
669
|
}
|
|
666
670
|
}
|
|
667
|
-
@media (max-width:
|
|
671
|
+
@media (max-width: 800px) {
|
|
668
672
|
.exp-header > span {
|
|
669
673
|
display: block !important;
|
|
670
674
|
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
</header>
|
|
17
17
|
<div class="chat-info-base user">
|
|
18
18
|
<div>
|
|
19
|
-
<img :src="chat.show.avatar || '/img/icons/icon.svg'" @error="avatarError">
|
|
19
|
+
<img data-avatar :src="chat.show.avatar || '/img/icons/icon.svg'" @error="avatarError">
|
|
20
20
|
<div>
|
|
21
21
|
<a>{{ chat.show.name }}</a>
|
|
22
22
|
<span>{{ chat.show.id }}</span>
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
:item-size="60"
|
|
110
110
|
key-field="user_id">
|
|
111
111
|
<div class="member-item edit">
|
|
112
|
-
<img alt="nk" loading="lazy"
|
|
112
|
+
<img alt="nk" loading="lazy" data-avatar
|
|
113
113
|
:src="memberAvatar(item) || '/img/icons/icon.svg'">
|
|
114
114
|
<div>
|
|
115
115
|
<a @click="startChat(item)">{{
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
</BcTab>
|
|
129
129
|
<div :class="'ss-card user-config' + (Object.keys(showUserConfig).length > 0 ? ' show' : '')">
|
|
130
130
|
<div>
|
|
131
|
-
<img alt="nk" :src="showUserConfig.avatar || '/img/icons/icon.svg'">
|
|
131
|
+
<img alt="nk" data-avatar :src="showUserConfig.avatar || '/img/icons/icon.svg'">
|
|
132
132
|
<div>
|
|
133
133
|
<a>{{ showUserConfig.card != '' ? showUserConfig.card : showUserConfig.nickname }}</a>
|
|
134
134
|
<span>{{ showUserConfig.user_id }}</span>
|
|
@@ -246,7 +246,7 @@ function userChannelId(): string {
|
|
|
246
246
|
|| props.chat.show.channelId
|
|
247
247
|
|| props.chat.info.user_info.channel_id
|
|
248
248
|
|| props.chat.info.user_info.channelId
|
|
249
|
-
||
|
|
249
|
+
|| String(props.chat.show.id),
|
|
250
250
|
)
|
|
251
251
|
}
|
|
252
252
|
|
|
@@ -256,7 +256,7 @@ function groupChannelId(): string {
|
|
|
256
256
|
|| props.chat.show.channelId
|
|
257
257
|
|| props.chat.info.group_info.channel_id
|
|
258
258
|
|| props.chat.info.group_info.channelId
|
|
259
|
-
||
|
|
259
|
+
|| String(props.chat.show.id),
|
|
260
260
|
)
|
|
261
261
|
}
|
|
262
262
|
|