@asteriskzuo/react-native-easemob
Version:
easemob chat sdk of react-native.
693 lines (642 loc) • 21.9 kB
text/typescript
import {
EmitterSubscription,
EventEmitter,
NativeEventEmitter,
NativeModules,
Platform,
} from 'react-native';
import {
ChatConnectionListener,
ChatContactGroupEventFromNumber,
ChatCustomListener,
ChatMultiDeviceListener,
} from './ChatEvents';
import { ChatManager } from './ChatManager';
import { ChatDeviceInfo } from './common/ChatDeviceInfo';
import type { ChatOptions } from './common/ChatOptions';
import {
MethodTypechangeAppKey,
MethodTypecompressLogs,
MethodTypecreateAccount,
MethodTypegetCurrentUser,
MethodTypegetLoggedInDevicesFromServer,
MethodTypegetToken,
MethodTypeinit,
MethodTypeisConnected,
MethodTypeisLoggedInBefore,
MethodTypekickAllDevices,
MethodTypekickDevice,
MethodTypelogin,
MethodTypeloginWithAgoraToken,
MethodTypelogout,
MethodTypeonConnected,
MethodTypeonDisconnected,
MethodTypeonMultiDeviceEvent,
MethodTypeonSendDataToFlutter,
MethodTypeonTokenDidExpire,
MethodTypeonTokenWillExpire,
MethodTyperefreshAgoraToken,
} from './_internal/Consts';
import { Native } from './_internal/Native';
const LINKING_ERROR =
`The package 'react-native-easemob' doesn't seem to be linked. Make sure: \n\n` +
Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
'- You rebuilt the app after installing the package\n' +
'- You are not using Expo managed workflow\n';
const ExtSdkApiRN = NativeModules.ExtSdkApiRN
? NativeModules.ExtSdkApiRN
: new Proxy(
{},
{
get() {
throw new Error(LINKING_ERROR);
},
}
);
const eventEmitter = new NativeEventEmitter(ExtSdkApiRN);
console.log('eventEmitter: ', eventEmitter);
export class ChatClient extends Native {
public static eventType = 2; // 1.remove 2.subscription(suggested)
private static TAG = 'ChatClient';
private static _instance: ChatClient;
private _connectionSubscriptions: Map<string, EmitterSubscription>;
public static getInstance(): ChatClient {
if (ChatClient._instance == null || ChatClient._instance === undefined) {
ChatClient._instance = new ChatClient();
}
return ChatClient._instance;
}
private setEventEmitter(): void {
console.log(`${ChatClient.TAG}: setEventEmitter: `);
this.setNativeListener(this.getEventEmitter());
this._chatManager.setNativeListener(this.getEventEmitter());
console.log('eventEmitter has finished.');
}
public getEventEmitter(): EventEmitter {
return eventEmitter;
}
// private _eventEmitter: NativeEventEmitter;
private _chatManager: ChatManager;
// todo: no implement
// private _contactManager: ChatContactManager;
// private _chatRoomManager: ChatChatRoomManager;
// private _groupManager: ChatGroupManager;`
// private _pushManager: ChatPushManager;
// private _userInfoManager: ChatUserInfoManager;
// private _conversationManager: ChatConversationManager;
private _connectionListeners: Set<ChatConnectionListener>;
// todo: no implement
private _multiDeviceListeners: Set<ChatMultiDeviceListener>;
private _customListeners: Set<ChatCustomListener>;
// 1.主动登录 2.主动退出 3.被动退出
private _options?: ChatOptions;
private _sdkVersion: string = '1.0.0';
private _isInit: boolean = false;
private _currentUsername: string = '';
private constructor() {
super();
this._chatManager = new ChatManager();
// todo: no implement
// this._contactManager = new ChatContactManager();
// this._chatRoomManager = new ChatChatRoomManager();
// this._groupManager = new ChatGroupManager();
// this._pushManager = new ChatPushManager();
// this._userInfoManager = new ChatUserInfoManager();
// this._conversationManager = new ChatConversationManager();
this._connectionListeners = new Set<ChatConnectionListener>();
this._connectionSubscriptions = new Map<string, EmitterSubscription>();
this._multiDeviceListeners = new Set<ChatMultiDeviceListener>();
this._customListeners = new Set<ChatCustomListener>();
this.setEventEmitter();
}
private setConnectNativeListener(event: EventEmitter): void {
console.log(`${ChatClient.TAG}: setConnectNativeListener: `);
this._connectionSubscriptions.forEach(
(
value: EmitterSubscription,
key: string,
map: Map<string, EmitterSubscription>
) => {
console.log(
`${ChatClient.TAG}: setConnectNativeListener: ${key}, ${value}, ${map}`
);
value.remove();
}
);
this._connectionSubscriptions.clear();
// let s: EmitterSubscription[] | undefined = eventEmitter?.listeners(
// MethodTypeonConnected
// );
// console.log(`${s?.length}`);
// let s: EmitterSubscription = event.addListener(
// MethodTypeonConnected,
// (params: any[]): any => {
// console.log('etst', params);
// s.remove();
// }
// );
this._connectionSubscriptions.set(
MethodTypeonConnected,
event.addListener(MethodTypeonConnected, this.onConnected.bind(this))
);
this._connectionSubscriptions.set(
MethodTypeonDisconnected,
event.addListener(
MethodTypeonDisconnected,
this.onDisconnected.bind(this)
)
);
this._connectionSubscriptions.set(
MethodTypeonTokenDidExpire,
event.addListener(
MethodTypeonTokenDidExpire,
this.onTokenDidExpire.bind(this)
)
);
this._connectionSubscriptions.set(
MethodTypeonTokenWillExpire,
event.addListener(
MethodTypeonTokenWillExpire,
this.onTokenWillExpire.bind(this)
)
);
this._connectionSubscriptions.set(
MethodTypeonMultiDeviceEvent,
event.addListener(
MethodTypeonMultiDeviceEvent,
this.onMultiDeviceEvent.bind(this)
)
);
this._connectionSubscriptions.set(
MethodTypeonSendDataToFlutter,
event.addListener(
MethodTypeonSendDataToFlutter,
this.onCustomEvent.bind(this)
)
);
console.log(`${ChatClient.TAG}: setConnectNativeListener: `, event);
}
private setNativeListener(event: NativeEventEmitter): void {
console.log(
`${ChatClient.TAG}: setNativeListener: ${ChatClient.eventType}`
);
if (ChatClient.eventType === 1) {
event.removeAllListeners(MethodTypeonConnected);
event.addListener(MethodTypeonConnected, this.onConnected.bind(this));
event.removeAllListeners(MethodTypeonDisconnected);
event.addListener(
MethodTypeonDisconnected,
this.onDisconnected.bind(this)
);
event.removeAllListeners(MethodTypeonTokenDidExpire);
event.addListener(
MethodTypeonTokenDidExpire,
this.onTokenDidExpire.bind(this)
);
event.removeAllListeners(MethodTypeonTokenWillExpire);
event.addListener(
MethodTypeonTokenWillExpire,
this.onTokenWillExpire.bind(this)
);
event.removeAllListeners(MethodTypeonMultiDeviceEvent);
event.addListener(
MethodTypeonMultiDeviceEvent,
this.onMultiDeviceEvent.bind(this)
);
event.removeAllListeners(MethodTypeonSendDataToFlutter);
event.addListener(
MethodTypeonSendDataToFlutter,
this.onCustomEvent.bind(this)
);
} else if (ChatClient.eventType === 2) {
this.setConnectNativeListener(event);
} else {
throw new Error('This type is not supported.');
}
}
public onConnected(): void {
console.log(`${ChatClient.TAG}: onConnected: `);
this._connectionListeners.forEach((element) => {
element.onConnected();
});
}
private onDisconnected(params?: any): void {
console.log(`${ChatClient.TAG}: onDisconnected: ${params}`);
this._connectionListeners.forEach((element) => {
let ec = params?.errorCode as number;
element.onDisconnected(ec);
});
}
private onTokenWillExpire(params?: any): void {
console.log(`${ChatClient.TAG}: onTokenWillExpire: ${params}`);
this._connectionListeners.forEach((element) => {
element.onTokenWillExpire();
});
}
private onTokenDidExpire(params?: any): void {
console.log(`${ChatClient.TAG}: onTokenDidExpire: ${params}`);
this._connectionListeners.forEach((element) => {
element.onTokenDidExpire();
});
}
private onMultiDeviceEvent(params?: any): void {
console.log(`${ChatClient.TAG}: onMultiDeviceEvent: ${params}`);
this._multiDeviceListeners.forEach((element) => {
let event = params?.event as number;
if (event > 10) {
element.onGroupEvent(
ChatContactGroupEventFromNumber(event),
params?.target,
params?.userNames
);
} else {
element.onContactEvent(
ChatContactGroupEventFromNumber(event),
params?.target,
params?.userNames
);
}
});
}
private onCustomEvent(params: any): void {
console.log(`${ChatClient.TAG}: onCustomEvent: ${params}`);
this._customListeners.forEach((element) => {
element.onDataReceived(params);
});
}
private reset(): void {
this._currentUsername = '';
}
/**
* Get SDK Configurations. Make sure to set the param, see {@link EMOptions}.
*
* @returns The configurations.
*/
public get options(): ChatOptions | undefined {
return this._options;
}
/**
* Get SDK version.
*/
public get sdkVersion(): string {
return this._sdkVersion;
}
/**
* Get current user name.
*
* The value is valid after successful login.
*/
public get currentUserName(): string {
return this._currentUsername;
}
/**
* Get React-native SDK version.
*/
public get rnSdkVersion(): string {
return this._sdkVersion;
}
/**
* Make sure to initialize the SDK in the main thread. Make sure to set the param, see {@link ChatOptions}.
*
* **note**
*
* **This method must be called before any method can be called.**
*
* @param options The configurations.
*
* @throws Error, see {@link ChatError}
*/
public async init(options: ChatOptions): Promise<void> {
console.log(`${ChatClient.TAG}: init: ${options}`);
this._options = options;
await Native._callMethod(MethodTypeinit, { options });
this._isInit = true;
}
/**
* Check whether you have successfully connected to the server.
*
* @returns
* - `true`: The server has been connected.
* - `false`: The server is not connected.
*
* @throws Error, see {@link ChatError}
*/
public async isConnected(): Promise<boolean> {
console.log(`${ChatClient.TAG}: isConnected: `);
let result: any = await Native._callMethod(MethodTypeisConnected);
ChatClient.hasErrorFromResult(result);
let _connected = result?.[MethodTypeisConnected] as boolean;
return _connected;
}
/**
* Get current user name from native. Get cache see {@link currentUserName}
* @returns The user name.
*
* @throws Error, see {@link ChatError}
*/
public async getCurrentUsername(): Promise<string> {
console.log(`${ChatClient.TAG}: getCurrentUsername: `);
let result: any = await Native._callMethod(MethodTypegetCurrentUser);
ChatClient.hasErrorFromResult(result);
let userName = result?.[MethodTypegetCurrentUser] as string;
if (userName && userName.length !== 0) {
if (userName !== this._currentUsername) {
this._currentUsername = userName;
}
}
return this._currentUsername;
}
/**
* Get login state.
*
* @returns
* - `true`: In automatic login mode, the value is true before successful login and false otherwise.
* - `false`: In non-automatic login mode, the value is false.
*
* @throws Error, see {@link ChatError}
*/
public async isLoginBefore(): Promise<boolean> {
console.log(`${ChatClient.TAG}: isLoginBefore: `);
let result: any = await Native._callMethod(MethodTypeisLoggedInBefore);
ChatClient.hasErrorFromResult(result);
let _isLoginBefore = result?.[MethodTypeisLoggedInBefore] as boolean;
return _isLoginBefore;
}
/**
* Get login access token.
*
* @returns The token value.
*
* @throws Error, see {@link ChatError}
*/
public async getAccessToken(): Promise<string> {
console.log(`${ChatClient.TAG}: isLoginBefore: `);
let result: any = await Native._callMethod(MethodTypegetToken);
ChatClient.hasErrorFromResult(result);
let _token = result?.[MethodTypegetToken] as string;
return _token;
}
/**
* Register a new user with your chat network.
*
* @param username The username. The maximum length is 64 characters. Ensure that you set this parameter. Supported characters include the 26 English letters (a-z), the ten numbers (0-9), the underscore (_), the hyphen (-), and the English period (.). This parameter is case insensitive, and upper-case letters are automatically changed to low-case ones. If you want to set this parameter as a regular expression, set it as ^[a-zA-Z0-9_-]+$.
* @param password The password. The maximum length is 64 characters. Ensure that you set this parameter.
*
* @throws Error, see {@link ChatError}
*/
public async createAccount(
username: string,
password: string
): Promise<void> {
console.log(`${ChatClient.TAG}: createAccount: ${username}, ${password}`);
let result: any = await Native._callMethod(MethodTypecreateAccount, {
[MethodTypecreateAccount]: {
username: username,
password: password,
},
});
ChatClient.hasErrorFromResult(result);
}
/**
* An app user logs in to the chat server with a password or token.
*
* @param userName The username, see {@link createAccount}.
* @param pwdOrToken The password or token, see {@link createAccount} or {@link getAccessToken}
* @param isPassword The password or token flag. true is the password, otherwise it is the token.
*
* @throws Error, see {@link ChatError}
*/
public async login(
userName: string,
pwdOrToken: string,
isPassword: boolean = true
): Promise<void> {
console.log(
`${ChatClient.TAG}: login: ${userName}, ${pwdOrToken}, ${isPassword}`
);
let result: any = await Native._callMethod(MethodTypelogin, {
[MethodTypelogin]: {
username: userName,
pwdOrToken: pwdOrToken,
isPassword: isPassword,
},
});
ChatClient.hasErrorFromResult(result);
result = result?.[MethodTypelogin];
this._currentUsername = result?.username;
console.log(
`${ChatClient.TAG}: login: ${result?.username}, ${result?.token}`
);
}
/**
* An app user logs in to the chat server with a agora token.
*
* @param userName The username, see {@link createAccount}.
* @param agoraToken The token from agora api.
*
* @throws Error, see {@link ChatError}
*/
public async loginWithAgoraToken(
userName: string,
agoraToken: string
): Promise<void> {
console.log(
`${ChatClient.TAG}: loginWithAgoraToken: ${userName}, ${agoraToken}`
);
let result: any = await Native._callMethod(MethodTypeloginWithAgoraToken, {
[MethodTypeloginWithAgoraToken]: {
username: userName,
agoraToken: agoraToken,
},
});
ChatClient.hasErrorFromResult(result);
this._currentUsername = result?.username;
}
/**
* Renew token.
*
* When a user is in the Agora token login state and receives a callback
* notification of the token is to be expired in the {@link ChatConnectionListener}
* implementation class, this API can be called to update the token to
* avoid unknown problems caused by the token invalidation.
*
* @param agoraToken The new token.
*
* @throws Error, see {@link ChatError}
*/
public async renewAgoraToken(agoraToken: string): Promise<void> {
console.log(`${ChatClient.TAG}: renewAgoraToken: ${agoraToken}`);
let result: any = await Native._callMethod(MethodTyperefreshAgoraToken, {
[MethodTyperefreshAgoraToken]: {
agoraToken: agoraToken,
},
});
ChatClient.hasErrorFromResult(result);
}
/**
* An app user logs out and returns the result.
*
* @param unbindDeviceToken Whether to unbind the token.
* - `true`: means to unbind the device token when logout.
* - `false`: means to not unbind the device token when logout.
* @throws Error, see {@link ChatError}
*/
public async logout(unbindDeviceToken: boolean = true): Promise<void> {
console.log(`${ChatClient.TAG}: logout: ${unbindDeviceToken}`);
let result: any = await Native._callMethod(MethodTypelogout, {
[MethodTypelogout]: {
unbindToken: unbindDeviceToken,
},
});
ChatClient.hasErrorFromResult(result);
this.reset();
}
/**
* Update the App Key, which is the unique identifier used to access Agora Chat.
*
* You retrieve the new App Key from Agora Console.
*
* As this key controls all access to Agora Chat for your app, you can only update the key when the current user is logged out.
*
* Also, you can set App Key by the following method when logged out {@link ChatOptions#appKey}.
*
* @param newAppKey The App Key, make sure to set the param.
*
* @throws Error, see {@link ChatError}
*/
public async changeAppKey(newAppKey: string): Promise<void> {
console.log(`${ChatClient.TAG}: changeAppKey: ${newAppKey}`);
let r: any = await Native._callMethod(MethodTypechangeAppKey, {
appKey: newAppKey,
});
ChatClient.hasErrorFromResult(r);
}
/**
* Compresses the debug log into a gzip archive.
*
* Best practice is to delete this debug archive as soon as it is no longer used.
*
* @returns The path of the compressed gz file.
*
* @throws Error, see {@link ChatError}
*/
public async compressLogs(): Promise<string | undefined> {
console.log(`${ChatClient.TAG}: compressLogs:`);
let r: any = await Native._callMethod(MethodTypecompressLogs);
ChatClient.hasErrorFromResult(r);
return r?.[MethodTypecompressLogs];
}
/**
* Gets all the information about the logged in devices under the specified account.
*
* @param username The user ID you want to get the device information.
* @param password The password.
* @returns The list of the online devices.
*
* @throws Error, see {@link ChatError}
*/
public async getLoggedInDevicesFromServer(
username: string,
password: string
): Promise<Array<ChatDeviceInfo>> {
console.log(
`${ChatClient.TAG}: getLoggedInDevicesFromServer: ${username}, ${password}`
);
let result: any = await Native._callMethod(
MethodTypegetLoggedInDevicesFromServer,
{
[MethodTypegetLoggedInDevicesFromServer]: {
username: username,
password: password,
},
}
);
ChatClient.hasErrorFromResult(result);
let r = new Array<ChatDeviceInfo>(1);
let list: Array<any> = result?.[MethodTypegetLoggedInDevicesFromServer];
list.forEach((element) => {
r.push(new ChatDeviceInfo(element));
});
return r;
}
/**
* Force the specified account to logout from the specified device, to fetch the device ID: {@link ChatDeviceInfo#resource}.
*
* @param username The account you want to force logout.
* @param password The account's password.
* @param resource The device ID, see {@link ChatDeviceInfo#resource}.
*
* @throws Error, see {@link ChatError}
*/
public async kickDevice(
username: string,
password: string,
resource: string
): Promise<void> {
console.log(
`${ChatClient.TAG}: kickDevice: ${username}, ${password}, ${resource}`
);
let r: any = await Native._callMethod(MethodTypekickDevice, {
[MethodTypekickDevice]: {
username: username,
password: password,
resource: resource,
},
});
ChatClient.hasErrorFromResult(r);
}
/**
* Kicks out all the devices logged in under the specified account.
*
* @param username The account you want to log out from all the devices.
* @param password The account's password.
*
* @throws Error, see {@link ChatError}
*/
public async kickAllDevices(
username: string,
password: string
): Promise<void> {
console.log(`${ChatClient.TAG}: kickAllDevices: ${username}, ${password}`);
let r: any = await Native._callMethod(MethodTypekickAllDevices, {
[MethodTypekickAllDevices]: {
username: username,
password: password,
},
});
ChatClient.hasErrorFromResult(r);
}
public addConnectionListener(listener: ChatConnectionListener): void {
console.log(`${ChatClient.TAG}: addConnectionListener: `);
this._connectionListeners.add(listener);
}
public removeConnectionListener(listener: ChatConnectionListener): void {
console.log(`${ChatClient.TAG}: removeConnectionListener: `);
this._connectionListeners.delete(listener);
}
public removeAllConnectionListener(): void {
console.log(`${ChatClient.TAG}: removeAllConnectionListener: `);
this._connectionListeners.clear();
}
public addMultiDeviceListener(listener: ChatMultiDeviceListener): void {
this._multiDeviceListeners.add(listener);
}
public removeMultiDeviceListener(listener: ChatMultiDeviceListener): void {
this._multiDeviceListeners.delete(listener);
}
public removeAllMultiDeviceListener(): void {
this._multiDeviceListeners.clear();
}
public addCustomListener(listener: ChatCustomListener): void {
this._customListeners.add(listener);
}
public removeCustomListener(listener: ChatCustomListener): void {
this._customListeners.delete(listener);
}
public removeAllCustomListener(): void {
this._customListeners.clear();
}
public get chatManager(): ChatManager {
return this._chatManager;
}
}