初始化

This commit is contained in:
2025-12-09 09:06:06 +08:00
commit 2c3e9b248d
98 changed files with 26883 additions and 0 deletions

17
src/api/index.ts Normal file
View File

@@ -0,0 +1,17 @@
/**
* API 统一导出
*/
export * as authApi from './modules/auth'
export * as userApi from './modules/user'
export * as contactApi from './modules/contact'
export * as conversationApi from './modules/conversation'
export * as messageApi from './modules/message'
export * as roomApi from './modules/room'
export * as momentApi from './modules/moment'
export * as attachmentApi from './modules/attachment'
export * as systemApi from './modules/system'
export { wsManager } from './websocket'
export type { WebSocketMessage, MessageHandler, SignalHandler, MomentNotifHandler } from './websocket'
export { default as request } from './request'

View File

@@ -0,0 +1,25 @@
/**
* 附件管理相关 API
*/
import request from '../request'
import type { Attachment, PaginatedResponse } from '@/types/api'
// 上传附件
export function uploadAttachment(filePath: string, type?: 'image' | 'video' | 'file') {
return request.upload<Attachment>('/attachments/upload', filePath, 'file', type ? { type } : undefined)
}
// 获取附件信息
export function getAttachment(id: number) {
return request.get<Attachment>(`/attachments/${id}`)
}
// 获取附件列表
export function getAttachments(type?: string, page = 1, pageSize = 20) {
return request.get<PaginatedResponse<Attachment>>('/attachments', { type, page, page_size: pageSize })
}
// 删除附件
export function deleteAttachment(id: number) {
return request.post<void>(`/attachments/delete/${id}`)
}

30
src/api/modules/auth.ts Normal file
View File

@@ -0,0 +1,30 @@
/**
* 认证相关 API
*/
import request from '../request'
import type { LoginRequest, LoginResponse, RegisterRequest, SendCodeRequest } from '@/types/api'
// 用户登录
export function login(data: LoginRequest) {
return request.post<LoginResponse>('/login', data)
}
// 用户注册
export function register(data: RegisterRequest) {
return request.post<void>('/register', data)
}
// 发送邮箱验证码
export function sendEmailCode(data: SendCodeRequest) {
return request.post<void>('/send-email-code', data)
}
// 发送短信验证码
export function sendSmsCode(data: SendCodeRequest) {
return request.post<void>('/send-sms-code', data)
}
// 检查Token有效性
export function checkToken() {
return request.get<void>('/check-token')
}

View File

@@ -0,0 +1,85 @@
/**
* 联系人管理相关 API
*/
import request from '../request'
import type { Contact, User, FriendRequest, ContactGroup } from '@/types/api'
// 获取联系人列表
export function getContacts() {
return request.get<Contact[]>('/contacts')
}
// 搜索用户
export function searchUsers(keyword: string, limit = 20) {
return request.get<User[]>('/contacts/search', { keyword, limit })
}
// 添加好友(发送申请)
export function addFriend(data: { to_user_id: string; message?: string }) {
return request.post<void>('/contacts/add-friend', data)
}
// 获取好友申请列表
export function getFriendRequests() {
return request.get<FriendRequest[]>('/contacts/friend-requests')
}
// 接受好友申请
export function acceptFriendRequest(requestId: number) {
return request.post<void>('/contacts/accept-request', { request_id: requestId })
}
// 拒绝好友申请
export function rejectFriendRequest(requestId: number) {
return request.post<void>('/contacts/reject-request', { request_id: requestId })
}
// 获取分组列表
export function getGroups() {
return request.get<ContactGroup[]>('/contacts/groups')
}
// 创建分组
export function createGroup(data: { group_name: string }) {
return request.post<ContactGroup>('/contacts/groups', data)
}
// 更新分组
export function updateGroup(id: number, data: { group_name?: string; sort_order?: number }) {
return request.post<ContactGroup>(`/contacts/groups/update/${id}`, data)
}
// 删除分组
export function deleteGroup(id: number) {
return request.post<void>(`/contacts/groups/delete/${id}`)
}
// 获取好友详情
export async function getContactDetail(id: string): Promise<Contact> {
const response = await request.get<{ contact: any; user: User }>(`/contacts/${id}`)
return {
...response.contact,
id: response.contact.id?.toString() || response.contact.contact_id,
user_id: response.contact.contact_id || response.contact.user_id,
contact_user_id: response.contact.contact_id,
room_id: response.contact.room_id,
user: response.user
} as Contact
}
// 更新好友信息
export function updateContact(id: string, data: {
remark_name?: string
group_id?: number
is_top?: boolean
is_muted?: boolean
is_special_care?: boolean
is_blocked?: boolean
}) {
return request.post<Contact>(`/contacts/update/${id}`, data)
}
// 删除好友
export function deleteContact(id: string) {
return request.post<void>(`/contacts/delete/${id}`)
}

View File

@@ -0,0 +1,39 @@
/**
* 会话列表相关 API
*/
import request from '../request'
import type { Conversation } from '@/types/conversation'
// 获取会话列表
export function getConversationList() {
return request.get<Conversation[]>('/conversations')
}
// 重置未读
export function resetUnread(targetId: string) {
return request.post<void>('/conversations/reset-unread', {
target_id: targetId
})
}
// 更新会话标记
export function updateConversation(data: {
target_id: string
is_top?: boolean
is_muted?: boolean
is_special_care?: boolean
}) {
return request.post<void>('/conversations/update', data)
}
// 删除会话
export function deleteConversation(targetId: string) {
return request.post<void>('/conversations/delete', {
target_id: targetId
})
}
// 根据 room_id 获取或创建会话
export function getConversationByRoom(roomId: string) {
return request.get<Conversation>(`/conversations/by-room/${roomId}`)
}

View File

@@ -0,0 +1,28 @@
/**
* 消息管理相关 API
*/
import request from '../request'
import type { ChatMessage, SendMessageRequest, PaginatedResponse } from '@/types/api'
// 发送消息
export function sendMessage(data: SendMessageRequest) {
return request.post<void>('/send', data)
}
// 获取历史消息
export function getMessages(roomId: string, page = 1, pageSize = 50) {
return request.get<PaginatedResponse<ChatMessage>>('/messages', {
room_id: roomId,
page,
page_size: pageSize
})
}
// 同步消息(兼容接口)
export function syncMessages(roomId: string, page = 1, pageSize = 50) {
return request.get<PaginatedResponse<ChatMessage>>('/messages/sync', {
room_id: roomId,
page,
page_size: pageSize
})
}

99
src/api/modules/moment.ts Normal file
View File

@@ -0,0 +1,99 @@
/**
* 朋友圈相关 API
*/
import request from '../request'
import type {
Moment,
MomentComment,
MomentNotification,
CreateMomentRequest,
CreateCommentRequest,
MarkReadRequest,
PaginatedResponse
} from '@/types/moment'
// ==========================================
// 动态相关
// ==========================================
// 发布动态
export function createMoment(data: CreateMomentRequest) {
return request.post<Moment>('/moments', data)
}
// 获取好友动态列表
export function getMoments(page = 1, pageSize = 20) {
return request.get<PaginatedResponse<Moment>>('/moments', { page, page_size: pageSize })
}
// 获取动态详情
export function getMomentDetail(id: number) {
return request.get<Moment>(`/moments/${id}`)
}
// 删除动态
export function deleteMoment(id: number) {
return request.delete(`/moments/${id}`)
}
// 获取指定用户的动态列表
export function getUserMoments(userId: string, page = 1, pageSize = 20) {
return request.get<PaginatedResponse<Moment>>(`/moments/user/${userId}`, { page, page_size: pageSize })
}
// ==========================================
// 点赞相关
// ==========================================
// 点赞动态
export function likeMoment(id: number) {
return request.post(`/moments/${id}/like`)
}
// 取消点赞
export function unlikeMoment(id: number) {
return request.delete(`/moments/${id}/like`)
}
// 获取动态的点赞列表
export function getMomentLikes(momentId: number) {
return request.get<Array<{ id: number; user_id: string; created_at: string }>>(`/moments/${momentId}/likes`)
}
// ==========================================
// 评论相关
// ==========================================
// 获取动态的评论列表
export function getMomentComments(momentId: number) {
return request.get<MomentComment[]>(`/moments/${momentId}/comments`)
}
// 发表评论
export function createComment(momentId: number, data: CreateCommentRequest) {
return request.post<MomentComment>(`/moments/${momentId}/comments`, data)
}
// 删除评论
export function deleteComment(commentId: number) {
return request.delete(`/moments/comments/${commentId}`)
}
// ==========================================
// 通知相关
// ==========================================
// 获取朋友圈通知列表
export function getNotifications(page = 1, pageSize = 20) {
return request.get<PaginatedResponse<MomentNotification>>('/moments/notifications', { page, page_size: pageSize })
}
// 标记通知为已读
export function markNotificationsRead(data: MarkReadRequest) {
return request.post('/moments/notifications/read', data)
}
// 获取未读通知数量
export function getUnreadCount() {
return request.get<{ count: number }>('/moments/notifications/unread-count')
}

151
src/api/modules/room.ts Normal file
View File

@@ -0,0 +1,151 @@
/**
* 房间/群聊管理相关 API
*/
import request from '../request'
import type { Room, GroupInfo, GroupMember } from '@/types/api'
// 创建房间(单聊)
export function createRoom(data: {
room_type: 'p2p' | 'group'
members: string[]
name?: string
avatar?: string
}) {
return request.post<Room>('/rooms', data)
}
// 获取房间信息
export function getRoom(id: string) {
return request.get<Room>(`/rooms/${id}`)
}
/**
* 群聊相关 API
*/
// 创建群聊
export function createGroup(data: {
name: string
avatar?: string
member_ids: string[]
admin_ids?: string[]
}) {
return request.post<GroupInfo>('/groups', data)
}
// 获取用户群聊列表(带分组信息)
export function getUserGroups() {
return request.get<Array<{
room_id: string
room_type: string
room_name: string
room_avatar: string
owner_id: string
creator_id: string
category: 'joined' | 'created' | 'managed'
role: number
last_message_time?: string
last_message?: string
created_at: string
updated_at: string
}>>('/groups')
}
// 获取群信息
export function getGroup(roomId: string) {
return request.get<GroupInfo>(`/groups/${roomId}`)
}
// 获取群成员列表
export function getGroupMembers(roomId: string, keyword?: string) {
return request.get<GroupMember[]>(`/groups/${roomId}/members`, keyword ? { keyword } : undefined)
}
// 邀请成员入群
export function inviteGroupMembers(roomId: string, data: { member_ids: string[] }) {
return request.post<void>(`/groups/${roomId}/members`, data)
}
// 移除群成员
export function removeGroupMember(roomId: string, userId: string) {
return request.post<void>(`/groups/${roomId}/members/${userId}/remove`, {})
}
// 修改群信息
export function updateGroup(roomId: string, data: { name?: string; avatar?: string }) {
return request.post<void>(`/groups/${roomId}/update`, data)
}
// 调整成员角色
export function updateMemberRole(roomId: string, userId: string, data: { role: number }) {
return request.post<void>(`/groups/${roomId}/members/${userId}/role`, data)
}
// 退出群聊
export function quitGroup(roomId: string) {
return request.post<void>(`/groups/${roomId}/quit`, {})
}
// 解散群聊
export function dissolveGroup(roomId: string) {
return request.post<void>(`/groups/${roomId}/dissolve`, {})
}
// 获取群公告
export function getGroupAnnouncement(roomId: string) {
return request.get<{ announcement: string }>(`/groups/${roomId}/announcement`)
}
// 更新群公告
export function updateGroupAnnouncement(roomId: string, announcement: string) {
return request.post<void>(`/groups/${roomId}/announcement`, { announcement })
}
// 获取群通知列表
export function getGroupNotifications(params?: {
page?: number
page_size?: number
is_read?: boolean
}) {
return request.get<{
data: Array<{
id: number
room_id: string
message_type: number
content: string
extra: string | object
is_read?: boolean
created_at: string
}>
total: number
page: number
page_size: number
}>('/group-notifications', params)
}
// 禁言成员
export function muteGroupMember(roomId: string, userId: string, duration: number) {
return request.post<void>(`/groups/${roomId}/members/${userId}/mute`, { duration })
}
// 更新群设置
export function updateGroupSettings(roomId: string, data: {
name?: string
avatar?: string
description?: string
invite_permission?: number
}) {
return request.post<void>(`/groups/${roomId}/settings`, data)
}
// 获取群设置
export function getGroupSettings(roomId: string) {
return request.get<{
name: string
avatar: string
description: string
invite_permission: number
created_at: string
updated_at: string
}>(`/groups/${roomId}/settings`)
}

25
src/api/modules/system.ts Normal file
View File

@@ -0,0 +1,25 @@
/**
* 系统接口相关 API
*/
import request from '../request'
import type { ICEServerConfig } from '@/types/api'
// 健康检查
export function healthCheck() {
return request.get<{ status: string; node: string; time: string }>('/health')
}
// 获取ICE服务器配置
export function getIceServers(userId: string) {
return request.get<ICEServerConfig[]>('/ice-servers', { user_id: userId })
}
// 检查用户在线状态
export function checkUserOnline(userId: string) {
return request.get<{ is_online: boolean }>('/check-user-online', { user_id: userId })
}
// 绑定ClientID和UserID
export function bindClient(data: { user_id: string; client_id: string }) {
return request.post<void>('/bind', data)
}

33
src/api/modules/user.ts Normal file
View File

@@ -0,0 +1,33 @@
/**
* 用户管理相关 API
*/
import request from '../request'
import type { User, PaginatedResponse } from '@/types/api'
// 获取当前用户信息
export function getMyInfo() {
return request.get<User>('/user/my-info')
}
// 获取用户列表
export function getUserList(page = 1, pageSize = 20) {
return request.get<PaginatedResponse<User>>('/user/list', {
page,
page_size: pageSize
})
}
// 创建用户(管理员功能)
export function createUser(data: Partial<User>) {
return request.post<User>('/user/create', data)
}
// 更新用户信息
export function updateUser(data: { id: string; updates: Partial<User> }) {
return request.post<User>('/user/update', data)
}
// 删除用户(管理员功能)
export function deleteUser(id: string) {
return request.post<void>('/user/delete', { id })
}

264
src/api/request/index.ts Normal file
View File

@@ -0,0 +1,264 @@
/**
* UniApp 请求封装
*/
import type { ApiResponse, RequestConfig } from './types'
import { storage } from '@/utils/storage'
// API 基础路径
const BASE_URL = 'http://127.0.0.1:12080/api'
// 默认超时时间
const TIMEOUT = 30000
/**
* 获取存储的 Token
*/
function getToken(): string | null {
return storage.getToken()
}
/**
* 获取存储的 User ID
*/
function getUserId(): string | null {
return storage.getUserId()
}
/**
* 显示加载提示
*/
function showLoading() {
uni.showLoading({
title: '加载中...',
mask: true
})
}
/**
* 隐藏加载提示
*/
function hideLoading() {
uni.hideLoading()
}
/**
* 显示错误提示
*/
function showError(message: string) {
uni.showToast({
title: message,
icon: 'none',
duration: 3000
})
}
/**
* 请求函数
*/
function request<T = any>(config: RequestConfig): Promise<T> {
return new Promise((resolve, reject) => {
const {
url,
method = 'GET',
data,
params,
header = {},
timeout = TIMEOUT,
showLoading: needLoading = false,
showError: needShowError = true
} = config
// 构建完整 URL
let fullUrl = BASE_URL + url
// 处理 GET 请求参数
if (params && Object.keys(params).length > 0) {
const queryString = Object.entries(params)
.filter(([_, v]) => v !== undefined && v !== null)
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)
.join('&')
if (queryString) {
fullUrl += (fullUrl.includes('?') ? '&' : '?') + queryString
}
}
// 设置请求头
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...header
}
// 添加 Token
const token = getToken()
if (token) {
headers['Authorization'] = `Bearer ${token}`
}
// 添加 User ID
const userId = getUserId()
if (userId) {
headers['X-User-ID'] = userId
}
// 显示加载
if (needLoading) {
showLoading()
}
// 发起请求
uni.request({
url: fullUrl,
method,
data,
header: headers,
timeout,
success: (res) => {
if (needLoading) {
hideLoading()
}
const response = res.data as ApiResponse<T>
// 检查 HTTP 状态码
if (res.statusCode !== 200) {
const errorMsg = `请求失败: ${res.statusCode}`
if (needShowError) {
showError(errorMsg)
}
reject(new Error(errorMsg))
return
}
// 检查业务状态码
if (response.code !== 0) {
const errorMsg = response.message || '请求失败'
if (needShowError) {
showError(errorMsg)
}
reject(new Error(errorMsg))
return
}
// 返回结果数据
resolve(response.result)
},
fail: (err) => {
if (needLoading) {
hideLoading()
}
const errorMsg = err.errMsg || '网络请求失败'
if (needShowError) {
showError(errorMsg)
}
reject(new Error(errorMsg))
}
})
})
}
/**
* GET 请求
*/
function get<T = any>(url: string, params?: Record<string, any>, config?: Partial<RequestConfig>): Promise<T> {
return request<T>({
url,
method: 'GET',
params,
...config
})
}
/**
* POST 请求
*/
function post<T = any>(url: string, data?: any, config?: Partial<RequestConfig>): Promise<T> {
return request<T>({
url,
method: 'POST',
data,
...config
})
}
/**
* PUT 请求
*/
function put<T = any>(url: string, data?: any, config?: Partial<RequestConfig>): Promise<T> {
return request<T>({
url,
method: 'PUT',
data,
...config
})
}
/**
* DELETE 请求
*/
function del<T = any>(url: string, data?: any, config?: Partial<RequestConfig>): Promise<T> {
return request<T>({
url,
method: 'DELETE',
data,
...config
})
}
/**
* 上传文件
*/
function upload<T = any>(url: string, filePath: string, name = 'file', formData?: Record<string, any>): Promise<T> {
return new Promise((resolve, reject) => {
const headers: Record<string, string> = {}
const token = getToken()
if (token) {
headers['Authorization'] = `Bearer ${token}`
}
const userId = getUserId()
if (userId) {
headers['X-User-ID'] = userId
}
uni.uploadFile({
url: BASE_URL + url,
filePath,
name,
formData,
header: headers,
success: (res) => {
if (res.statusCode !== 200) {
reject(new Error(`上传失败: ${res.statusCode}`))
return
}
try {
const response = JSON.parse(res.data) as ApiResponse<T>
if (response.code !== 0) {
reject(new Error(response.message || '上传失败'))
return
}
resolve(response.result)
} catch {
reject(new Error('解析响应失败'))
}
},
fail: (err) => {
reject(new Error(err.errMsg || '上传失败'))
}
})
})
}
export default {
request,
get,
post,
put,
delete: del,
upload
}
export { get, post, put, del as delete, upload, request }

30
src/api/request/types.ts Normal file
View File

@@ -0,0 +1,30 @@
/**
* API 请求/响应类型定义
*/
// 统一响应格式
export interface ApiResponse<T = any> {
code: number
message: string
result: T
}
// 分页响应
export interface PaginatedResponse<T> {
data: T[]
total: number
page: number
size: number
}
// 请求配置
export interface RequestConfig {
url: string
method?: 'GET' | 'POST' | 'PUT' | 'DELETE'
data?: any
params?: Record<string, any>
header?: Record<string, string>
timeout?: number
showLoading?: boolean
showError?: boolean
}

243
src/api/websocket/index.ts Normal file
View File

@@ -0,0 +1,243 @@
/**
* WebSocket 管理器 - UniApp 适配版
*/
import type { ChatMessage } from '@/types/api'
import type { MomentNotifPayload } from '@/types/moment'
export interface WebSocketMessage {
request_type?: string
clientId?: string
data?: ChatMessage | MomentNotifPayload
}
export type MessageHandler = (message: ChatMessage) => void
export type SignalHandler = (message: ChatMessage) => void
export type MomentNotifHandler = (payload: MomentNotifPayload) => void
class WebSocketManager {
private socketTask: UniApp.SocketTask | null = null
private clientId: string | null = null
private userId: string | null = null
private messageHandlers: MessageHandler[] = []
private signalHandlers: SignalHandler[] = []
private momentNotifHandlers: MomentNotifHandler[] = []
private reconnectAttempts = 0
private maxReconnectAttempts = 5
private reconnectDelay = 3000
private recentMomentNotifs: Set<string> = new Set()
private notifCacheTimeout = 5000
/**
* 连接 WebSocket
*/
connect(userId: string): Promise<void> {
return new Promise((resolve, reject) => {
if (this.socketTask) {
resolve()
return
}
this.userId = userId
// TODO: 替换为实际的 WebSocket 地址
const wsUrl = `ws://localhost:12080/ws?user_id=${userId}`
this.socketTask = uni.connectSocket({
url: wsUrl,
success: () => {
console.log('WebSocket connecting...')
},
fail: (err) => {
console.error('WebSocket connect failed:', err)
reject(err)
}
})
this.socketTask.onOpen(() => {
console.log('✅ WebSocket connected')
this.reconnectAttempts = 0
resolve()
})
this.socketTask.onMessage((res) => {
try {
const data = (res.data as string).trim()
const lines = data.split('\n').filter((line: string) => line.trim())
for (const line of lines) {
try {
const payload: WebSocketMessage = JSON.parse(line)
// 接收 clientId
if (payload.clientId) {
this.clientId = payload.clientId
}
// 处理接收消息
if (payload.request_type === 'receive_message' && payload.data) {
this.handleMessage(payload.data as ChatMessage)
}
// 处理朋友圈通知
if (payload.request_type === 'moment_notification' && payload.data) {
this.handleMomentNotification(payload.data as MomentNotifPayload)
}
} catch {
// 忽略单行解析错误
}
}
} catch (error) {
console.error('WebSocket message parse error:', error)
}
})
this.socketTask.onError((error) => {
console.error('WebSocket error:', error)
reject(error)
})
this.socketTask.onClose(() => {
console.log('WebSocket closed')
this.socketTask = null
this.attemptReconnect()
})
})
}
/**
* 断开连接
*/
disconnect() {
if (this.socketTask) {
this.socketTask.close({})
this.socketTask = null
}
this.clientId = null
this.userId = null
this.messageHandlers = []
this.signalHandlers = []
this.momentNotifHandlers = []
}
/**
* 获取 ClientID
*/
getClientId(): string | null {
return this.clientId
}
/**
* 添加普通消息处理器
*/
onMessage(handler: MessageHandler) {
if (!this.messageHandlers.includes(handler)) {
this.messageHandlers.push(handler)
}
}
/**
* 移除普通消息处理器
*/
offMessage(handler: MessageHandler) {
const index = this.messageHandlers.indexOf(handler)
if (index > -1) {
this.messageHandlers.splice(index, 1)
}
}
/**
* 添加信令处理器 (WebRTC用)
*/
onSignal(handler: SignalHandler) {
if (!this.signalHandlers.includes(handler)) {
this.signalHandlers.push(handler)
}
}
/**
* 移除信令处理器
*/
offSignal(handler: SignalHandler) {
const index = this.signalHandlers.indexOf(handler)
if (index > -1) {
this.signalHandlers.splice(index, 1)
}
}
/**
* 添加朋友圈通知处理器
*/
onMomentNotification(handler: MomentNotifHandler) {
if (!this.momentNotifHandlers.includes(handler)) {
this.momentNotifHandlers.push(handler)
}
}
/**
* 移除朋友圈通知处理器
*/
offMomentNotification(handler: MomentNotifHandler) {
const index = this.momentNotifHandlers.indexOf(handler)
if (index > -1) {
this.momentNotifHandlers.splice(index, 1)
}
}
/**
* 内部处理朋友圈通知(带去重)
*/
private handleMomentNotification(payload: MomentNotifPayload) {
const notifKey = `${payload.moment_id}_${payload.type}_${payload.from_user?.id || ''}_${payload.comment_id || ''}`
if (this.recentMomentNotifs.has(notifKey)) {
console.log('🔄 跳过重复朋友圈通知:', notifKey)
return
}
this.recentMomentNotifs.add(notifKey)
setTimeout(() => {
this.recentMomentNotifs.delete(notifKey)
}, this.notifCacheTimeout)
this.momentNotifHandlers.forEach((handler) => handler(payload))
}
/**
* 内部处理接收到的消息
*/
private handleMessage(message: ChatMessage) {
// 信令消息(message_type = 6)- 路由到 signalHandlers
if (message.message_type === 6) {
this.signalHandlers.forEach((handler) => handler(message))
return
}
// 普通消息 - 路由到 messageHandlers
this.messageHandlers.forEach((handler) => handler(message))
}
/**
* 尝试重连
*/
private attemptReconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.log('WebSocket max reconnect attempts reached')
return
}
if (!this.userId) {
return
}
this.reconnectAttempts++
console.log(`WebSocket reconnecting... (${this.reconnectAttempts}/${this.maxReconnectAttempts})`)
setTimeout(() => {
if (this.userId) {
this.connect(this.userId).catch(console.error)
}
}, this.reconnectDelay)
}
}
export const wsManager = new WebSocketManager()