Last active
January 25, 2024 00:56
-
-
Save DefectingCat/700def05fc9c0bc00cd1f282635c8239 to your computer and use it in GitHub Desktop.
Zego message slice
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { AppThunk, RootState } from '@/store'; | |
import { createSlice, PayloadAction } from '@reduxjs/toolkit'; | |
import { notification } from 'antd'; | |
import { ZegoUser } from 'zego-express-engine-webrtc/sdk/src/common/zego.entity'; | |
import type { ZegoRoomStateChangedReason } from 'zego-express-engine-webrtm/sdk/code/zh/ZegoExpressEntity'; | |
import { ZegoBroadcastMessageInfo } from 'zego-express-engine-webrtm/sdk/code/zh/ZegoExpressEntity'; | |
/** | |
* 是否是房间类消息 | |
*/ | |
export function isRoomMsg( | |
msg: RoomMessage | CharacterMessage | |
): msg is RoomMessage { | |
return msg.type === 'room'; | |
} | |
/** | |
* 是否是角色消息 | |
*/ | |
export function isCharMsg( | |
msg: RoomMessage | CharacterMessage | |
): msg is CharacterMessage { | |
return msg.type === 'character'; | |
} | |
// 房间 | |
export type RoomType = 'room'; | |
// 角色 | |
export type CharType = 'character'; | |
// 登录 登出 聊天消息 | |
export type RoomAction = 'login' | 'logout' | 'chat'; | |
// 移动 坐下 | |
export type CharAction = 'move' | 'sit-down'; | |
// 消息的类型 房间、角色 消息 | |
export type MessageType = RoomType | CharType; | |
// 从即构收到的消息 | |
export type ReceivedMessage<M extends RoomMessage | CharacterMessage> = { | |
message: M; | |
} & Omit<ZegoBroadcastMessageInfo, 'message'>; | |
// 验证接受到的消息 | |
export type ValidMsgType = { | |
room: ReceivedMessage<RoomMessage>[]; | |
character: ReceivedMessage<CharacterMessage>[]; | |
}; | |
// 验证消息指定属性 | |
export type ValidMessage<T extends 'data' | 'action'> = { | |
room: RoomMessage[T]; | |
character: CharacterMessage[T]; | |
}; | |
// 验证消息本体 | |
export type ValidMsg = { | |
room: RoomMessage; | |
character: CharacterMessage; | |
}; | |
/** 消息结构 */ | |
export type BaseMessage<T, A> = { | |
// nanoid | |
id: string; | |
type: T; | |
action: A; | |
/* data: D; */ | |
}; | |
/** 房间消息 */ | |
export type RoomDataMap = { | |
login: { | |
username: string; | |
}; | |
logout: { | |
age: string; | |
}; | |
chat: { | |
msg: string; | |
}; | |
}; | |
export type RoomData<K extends RoomAction> = RoomDataMap[K]; | |
export type RoomMessage< | |
T = RoomType, | |
// 登录 登出 | |
A extends RoomAction = RoomAction, | |
> = { | |
data: RoomData<A>; | |
} & BaseMessage<T, A>; | |
/** 角色消息 */ | |
export type CharacterDataMap = { | |
move: { | |
position: { | |
x: number; | |
y: number; | |
z: number; | |
}; | |
}; | |
'sit-down': boolean; | |
}; | |
export type CharacterData<T extends CharAction> = CharacterDataMap[T]; | |
export type CharacterMessage< | |
T = CharType, | |
// 移动 坐下 | |
A extends CharAction = CharAction, | |
> = { | |
data: CharacterData<A>; | |
} & BaseMessage<T, A>; | |
/** | |
* 房间以及自身在房间中的状态 | |
*/ | |
export type RoomState = { | |
// 即构 APP ID | |
appID: number; | |
// 当前房间 ID | |
roomID: string; | |
// 登录即构用到 tokne | |
token: string; | |
// 当前房间状态 | |
roomState: ZegoRoomStateChangedReason | ''; | |
/* export type ZegoUser = { | |
// 即构当前用户 ID 与参展商手机号相同 | |
userId: string; | |
// 用户名 | |
userName: string; | |
}; */ | |
} & ZegoUser; | |
export const initRoomState: RoomState = { | |
appID: 0, | |
roomID: '', | |
// 用户 id 为当前登陆展台的手机号,必须先登陆 | |
userID: '', | |
userName: '', | |
token: '', | |
roomState: '', | |
}; | |
// 房间内用户 | |
export type RoomUser = ZegoUser; | |
/** | |
* 即构状态 | |
*/ | |
export interface ZegoState { | |
// 房间状态 | |
roomState: RoomState; | |
// 房间内用户 | |
users: RoomUser[]; | |
// 收到的房间消息 | |
roomMessage: ReceivedMessage<RoomMessage>[]; | |
// 收到的角色消息 | |
charMessage: ReceivedMessage<CharacterMessage>[]; | |
// 已发送的所有消息 | |
sentMessage: (CharacterMessage | RoomMessage)[]; | |
} | |
const initialState: ZegoState = { | |
roomState: initRoomState, | |
users: [], | |
roomMessage: [], | |
charMessage: [], | |
sentMessage: [], | |
}; | |
/** | |
* 3.0 即构状态 | |
*/ | |
export const zego = createSlice({ | |
name: 'zego-v3', | |
initialState, | |
reducers: { | |
reset() { | |
return initialState; | |
}, | |
// 房间状态 | |
updateRoom(s, action: PayloadAction<Partial<RoomState>>) { | |
s.roomState = { | |
...s.roomState, | |
...action.payload, | |
}; | |
}, | |
/** | |
* 操作房间内用户列表 | |
* | |
* @param type: | |
* - add 添加参数数组到当前用户列表 | |
* - remove 从当前用户列表中移除参数数组中的用户 | |
* - reset 将当前用户列表替换为参数 users | |
*/ | |
updateUsers( | |
s, | |
action: PayloadAction<{ | |
users: RoomUser[]; | |
type: 'add' | 'remove' | 'reset'; | |
}> | |
) { | |
switch (action.payload.type) { | |
case 'add': | |
s.users = s.users.concat(action.payload.users); | |
action.payload.users.forEach((user) => { | |
notification.open({ | |
message: 'user joined', | |
description: `user ${user.userName} joined`, | |
}); | |
}); | |
break; | |
case 'remove': | |
const ids = action.payload.users.reduce<string[]>((prev, cur) => { | |
const user: ZegoUser | undefined = s.users.find( | |
(u) => u.userID === cur.userID | |
); | |
if (!user) return prev; | |
return prev.concat(user.userID); | |
}, []); | |
ids.forEach( | |
(id) => (s.users = s.users.filter((u) => u.userID !== id)) | |
); | |
action.payload.users.forEach((user) => { | |
notification.open({ | |
message: 'user left', | |
description: `user ${user.userName} left`, | |
}); | |
}); | |
break; | |
case 'reset': | |
s.users = action.payload.users; | |
break; | |
default: | |
throw new Error( | |
`updateUsers: wrong param type ${action.payload.type}` | |
); | |
} | |
}, | |
/** | |
* 根据消息类型来将消息添加到特定的消息队列中 | |
* | |
* @param type 消息类型 房间消息 角色消息 | |
* @param msg 消息实际内容 | |
* | |
* 断言,reducer 无法正确推断范型,使用 AppThunk 代替 | |
*/ | |
addRoomMsg<K extends keyof ValidMsgType>( | |
s: ZegoState, | |
action: PayloadAction<{ | |
type: K; | |
msg: ValidMsgType[K]; | |
}> | |
) { | |
const map: { | |
[key in keyof ValidMsgType]: 'roomMessage' | 'charMessage'; | |
} = { | |
room: 'roomMessage', | |
character: 'charMessage', | |
}; | |
const key = map?.[action.payload.type]; | |
if (!key) { | |
throw new Error( | |
`zego slice addRoomMsg wrong type ${action.payload.type}` | |
); | |
} | |
/** @ts-ignore 由 AppThunk addRoomMsg 约束 */ | |
s[key] = s[key].concat(action.payload.msg); | |
}, | |
sentMsg<K extends keyof ValidMsgType>( | |
s: ZegoState, | |
action: PayloadAction<{ | |
type: K; | |
msg: ValidMsg[K]; | |
}> | |
) { | |
s.sentMessage.push(action.payload.msg); | |
}, | |
}, | |
}); | |
export const { reset, updateRoom, updateUsers } = zego.actions; | |
/** | |
* 根据消息类型来将消息添加到特定的消息队列中 | |
* | |
* 从即构接受的消息为数组, | |
* 记录自身发送的消息时为单个消息 | |
* | |
* @param type 消息类型 房间消息 角色消息 | |
* @param msg 消息实际内容 | |
*/ | |
export const addRoomMsg = | |
<K extends MessageType>({ | |
type, | |
msg, | |
}: { | |
type: K; | |
msg: ValidMsgType[K] | ValidMsg[K]; | |
}): AppThunk => | |
async (dispatch, getState) => { | |
if (Array.isArray(msg)) { | |
dispatch(zego.actions.addRoomMsg({ type, msg })); | |
} else { | |
dispatch(zego.actions.sentMsg({ type, msg })); | |
} | |
}; | |
export default zego.reducer; | |
// selectors | |
export const selectRoomState = (s: RootState) => s.zegov3.roomState; | |
export const selectUser = (s: RootState) => s.zegov3.users; | |
export const selectRoomMessage = (s: RootState) => s.zegov3.roomMessage; | |
export const selectCharacterMessage = (s: RootState) => s.zegov3.charMessage; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment