mirror of
https://github.com/LifeArchiveProject/WeChatDataAnalysis.git
synced 2026-06-18 15:54:08 +08:00
16a7e410a1
- 移除聊天页 setup 阶段的服务端会话预取\n- 改为客户端挂载后再加载账号和会话数据\n- 修复 nuxt generate 在发布机构建时访问本地聊天接口导致的失败
290 lines
8.7 KiB
JavaScript
290 lines
8.7 KiB
JavaScript
import { computed, onMounted, ref } from 'vue'
|
|
import { normalizeSessionPreview } from '~/lib/chat/formatters'
|
|
|
|
const SESSION_LIST_WIDTH_KEY = 'ui.chat.session_list_width_physical'
|
|
const SESSION_LIST_WIDTH_KEY_LEGACY = 'ui.chat.session_list_width'
|
|
const SESSION_LIST_WIDTH_DEFAULT = 295
|
|
const SESSION_LIST_WIDTH_MIN = 220
|
|
const SESSION_LIST_WIDTH_MAX = 520
|
|
|
|
export const useChatSessions = ({ chatAccounts, selectedAccount, realtimeEnabled, api }) => {
|
|
const showSearchAccountSwitcher = false
|
|
|
|
const contacts = ref([])
|
|
const selectedContact = ref(null)
|
|
const searchQuery = ref('')
|
|
const isLoadingContacts = ref(false)
|
|
const contactsError = ref('')
|
|
|
|
const sessionListWidth = ref(SESSION_LIST_WIDTH_DEFAULT)
|
|
const sessionListResizing = ref(false)
|
|
|
|
let sessionListResizeStartX = 0
|
|
let sessionListResizeStartWidth = SESSION_LIST_WIDTH_DEFAULT
|
|
let sessionListResizeStartDpr = 1
|
|
let sessionListResizePrevCursor = ''
|
|
let sessionListResizePrevUserSelect = ''
|
|
|
|
const availableAccounts = computed(() => {
|
|
return Array.isArray(chatAccounts?.accounts) ? chatAccounts.accounts : []
|
|
})
|
|
|
|
const clampSessionListWidth = (value) => {
|
|
const next = Number.isFinite(value) ? value : SESSION_LIST_WIDTH_DEFAULT
|
|
return Math.min(SESSION_LIST_WIDTH_MAX, Math.max(SESSION_LIST_WIDTH_MIN, Math.round(next)))
|
|
}
|
|
|
|
const loadSessionListWidth = () => {
|
|
if (!process.client) return
|
|
try {
|
|
const raw = localStorage.getItem(SESSION_LIST_WIDTH_KEY)
|
|
const value = parseInt(String(raw || ''), 10)
|
|
if (!Number.isNaN(value)) {
|
|
sessionListWidth.value = clampSessionListWidth(value)
|
|
return
|
|
}
|
|
|
|
const legacy = localStorage.getItem(SESSION_LIST_WIDTH_KEY_LEGACY)
|
|
const legacyValue = parseInt(String(legacy || ''), 10)
|
|
if (!Number.isNaN(legacyValue)) {
|
|
const dpr = window.devicePixelRatio || 1
|
|
const converted = clampSessionListWidth(legacyValue * dpr)
|
|
sessionListWidth.value = converted
|
|
try {
|
|
localStorage.setItem(SESSION_LIST_WIDTH_KEY, String(converted))
|
|
localStorage.removeItem(SESSION_LIST_WIDTH_KEY_LEGACY)
|
|
} catch {}
|
|
}
|
|
} catch {}
|
|
}
|
|
|
|
const saveSessionListWidth = () => {
|
|
if (!process.client) return
|
|
try {
|
|
localStorage.setItem(SESSION_LIST_WIDTH_KEY, String(clampSessionListWidth(sessionListWidth.value)))
|
|
} catch {}
|
|
}
|
|
|
|
const setSessionListResizingActive = (active) => {
|
|
if (!process.client) return
|
|
try {
|
|
const body = document.body
|
|
if (!body) return
|
|
if (active) {
|
|
sessionListResizePrevCursor = body.style.cursor || ''
|
|
sessionListResizePrevUserSelect = body.style.userSelect || ''
|
|
body.style.cursor = 'col-resize'
|
|
body.style.userSelect = 'none'
|
|
} else {
|
|
body.style.cursor = sessionListResizePrevCursor
|
|
body.style.userSelect = sessionListResizePrevUserSelect
|
|
sessionListResizePrevCursor = ''
|
|
sessionListResizePrevUserSelect = ''
|
|
}
|
|
} catch {}
|
|
}
|
|
|
|
const onSessionListResizerPointerMove = (event) => {
|
|
if (!sessionListResizing.value) return
|
|
const clientX = Number(event?.clientX || 0)
|
|
sessionListWidth.value = clampSessionListWidth(
|
|
sessionListResizeStartWidth + (clientX - sessionListResizeStartX) * (sessionListResizeStartDpr || 1)
|
|
)
|
|
}
|
|
|
|
const stopSessionListResize = () => {
|
|
if (!process.client) return
|
|
if (!sessionListResizing.value) return
|
|
sessionListResizing.value = false
|
|
setSessionListResizingActive(false)
|
|
try {
|
|
window.removeEventListener('pointermove', onSessionListResizerPointerMove)
|
|
} catch {}
|
|
saveSessionListWidth()
|
|
}
|
|
|
|
const onSessionListResizerPointerUp = () => {
|
|
stopSessionListResize()
|
|
}
|
|
|
|
const onSessionListResizerPointerDown = (event) => {
|
|
if (!process.client) return
|
|
try {
|
|
event?.preventDefault?.()
|
|
} catch {}
|
|
|
|
sessionListResizing.value = true
|
|
sessionListResizeStartX = Number(event?.clientX || 0)
|
|
sessionListResizeStartWidth = Number(sessionListWidth.value || SESSION_LIST_WIDTH_DEFAULT)
|
|
sessionListResizeStartDpr = window.devicePixelRatio || 1
|
|
setSessionListResizingActive(true)
|
|
|
|
try {
|
|
window.addEventListener('pointermove', onSessionListResizerPointerMove)
|
|
window.addEventListener('pointerup', onSessionListResizerPointerUp, { once: true })
|
|
} catch {}
|
|
}
|
|
|
|
const resetSessionListWidth = () => {
|
|
sessionListWidth.value = SESSION_LIST_WIDTH_DEFAULT
|
|
saveSessionListWidth()
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadSessionListWidth()
|
|
})
|
|
|
|
const filteredContacts = computed(() => {
|
|
const query = String(searchQuery.value || '').trim().toLowerCase()
|
|
if (!query) return contacts.value
|
|
return contacts.value.filter((contact) => {
|
|
const name = String(contact?.name || '').toLowerCase()
|
|
const username = String(contact?.username || '').toLowerCase()
|
|
return name.includes(query) || username.includes(query)
|
|
})
|
|
})
|
|
|
|
const mapSessions = (sessions) => {
|
|
return sessions.map((session) => ({
|
|
id: session.id,
|
|
name: session.name || session.username || session.id,
|
|
avatar: session.avatar || null,
|
|
lastMessage: normalizeSessionPreview(session.lastMessage || ''),
|
|
lastMessageTime: session.lastMessageTime || '',
|
|
unreadCount: session.unreadCount || 0,
|
|
isGroup: !!session.isGroup,
|
|
isTop: !!session.isTop,
|
|
username: session.username
|
|
}))
|
|
}
|
|
|
|
const clearContactsState = (errorMessage = '') => {
|
|
contacts.value = []
|
|
selectedContact.value = null
|
|
contactsError.value = errorMessage
|
|
}
|
|
|
|
const loadSessionsForSelectedAccount = async () => {
|
|
if (!selectedAccount.value) {
|
|
clearContactsState('')
|
|
return []
|
|
}
|
|
|
|
const fetchSessions = async (source) => {
|
|
const params = {
|
|
account: selectedAccount.value,
|
|
limit: 400,
|
|
include_hidden: false,
|
|
include_official: false
|
|
}
|
|
if (source) params.source = source
|
|
return api.listChatSessions(params)
|
|
}
|
|
|
|
let sessionsResp = null
|
|
if (realtimeEnabled?.value) {
|
|
try {
|
|
sessionsResp = await fetchSessions('realtime')
|
|
} catch {
|
|
sessionsResp = null
|
|
}
|
|
}
|
|
if (!sessionsResp) {
|
|
sessionsResp = await fetchSessions('')
|
|
}
|
|
|
|
const sessions = Array.isArray(sessionsResp?.sessions) ? sessionsResp.sessions : []
|
|
contacts.value = mapSessions(sessions)
|
|
contactsError.value = ''
|
|
return contacts.value
|
|
}
|
|
|
|
const refreshSessionsForSelectedAccount = async ({ sourceOverride } = {}) => {
|
|
if (!process.client || typeof window === 'undefined') return
|
|
if (!selectedAccount.value) return
|
|
if (isLoadingContacts.value) return
|
|
|
|
const previousUsername = selectedContact.value?.username || ''
|
|
const desiredSource = (sourceOverride != null)
|
|
? String(sourceOverride || '').trim()
|
|
: (realtimeEnabled?.value ? 'realtime' : '')
|
|
|
|
const params = {
|
|
account: selectedAccount.value,
|
|
limit: 400,
|
|
include_hidden: false,
|
|
include_official: false
|
|
}
|
|
|
|
let sessionsResp = null
|
|
if (desiredSource) {
|
|
try {
|
|
sessionsResp = await api.listChatSessions({ ...params, source: desiredSource })
|
|
} catch {
|
|
sessionsResp = null
|
|
}
|
|
}
|
|
if (!sessionsResp) {
|
|
try {
|
|
sessionsResp = await api.listChatSessions(params)
|
|
} catch {
|
|
return
|
|
}
|
|
}
|
|
|
|
const sessions = Array.isArray(sessionsResp?.sessions) ? sessionsResp.sessions : []
|
|
const nextContacts = mapSessions(sessions)
|
|
contacts.value = nextContacts
|
|
|
|
if (previousUsername) {
|
|
const matched = nextContacts.find((contact) => contact.username === previousUsername)
|
|
if (matched) selectedContact.value = matched
|
|
}
|
|
}
|
|
|
|
const loadContacts = async () => {
|
|
if (contacts.value.length && !isLoadingContacts.value) {
|
|
return { usedPrefetched: true }
|
|
}
|
|
|
|
isLoadingContacts.value = true
|
|
contactsError.value = ''
|
|
try {
|
|
await chatAccounts.ensureLoaded()
|
|
|
|
if (!selectedAccount.value) {
|
|
clearContactsState(chatAccounts.error || '未检测到已解密账号,请先解密数据库。')
|
|
return { usedPrefetched: false }
|
|
}
|
|
|
|
await loadSessionsForSelectedAccount()
|
|
return { usedPrefetched: false }
|
|
} catch (error) {
|
|
clearContactsState(error?.message || '加载联系人失败')
|
|
return { usedPrefetched: false }
|
|
} finally {
|
|
isLoadingContacts.value = false
|
|
}
|
|
}
|
|
|
|
return {
|
|
showSearchAccountSwitcher,
|
|
availableAccounts,
|
|
contacts,
|
|
selectedContact,
|
|
searchQuery,
|
|
filteredContacts,
|
|
isLoadingContacts,
|
|
contactsError,
|
|
sessionListWidth,
|
|
sessionListResizing,
|
|
clearContactsState,
|
|
loadContacts,
|
|
loadSessionsForSelectedAccount,
|
|
refreshSessionsForSelectedAccount,
|
|
onSessionListResizerPointerDown,
|
|
stopSessionListResize,
|
|
resetSessionListWidth
|
|
}
|
|
}
|