Amazing. I did this all in a HOC… sharing incase anyone is curious
import React, { createContext, useEffect, useContext, useState } from 'react';
import { AppState } from 'react-native';
import { connect } from 'react-redux';
import * as actions from 'actions';
import SendBird from 'sendbird';
import _throttle from 'lodash.throttle';
// saved on the outer scope because this is used in react-native-navigation, the HOC has a new instance for every scene, this caches it
const Register = {
AppState: null,
Connection: null,
ChannelEventHandler: null,
};
// prettier-ignore
// throttled because this is used in react-native-navigation, the HOC has a new instance for every scene
const throttledEventInitializer = _throttle(
async (props, SB) => {
if (!Register.Connection) {
console.log('[Sendbird] Registering Connection Handler');
Register.Connection = new SB.ConnectionHandler();
Register.Connection.onReconnectFailed = () => {
console.log("onReconnectFailed");
SB.reconnect();
};
Register.Connection.onReconnectStarted = () => {
console.log('onReconnectStarted');
};
Register.Connection.onReconnectSucceeded = () => {
console.log('onReconnectSucceeded');
props.fetchChannels()
};
SB.addConnectionHandler('connection', Register.Connection);
}
if (!Register.ChannelEventHandler) {
console.log('[Sendbird] Registering Channel Handler');
Register.ChannelEventHandler = new SB.ChannelHandler();
Register.ChannelEventHandler.onMessageReceived = (channel, message) => { props.messageEventHandler('insert', channel, [message]); };
Register.ChannelEventHandler.onMessageUpdated = (channel, message) => { props.messageEventHandler('update', channel, [message]); };
Register.ChannelEventHandler.onMessageDeleted = (channel, messageId) => { props.messageEventHandler('remove', channel, [{ messageId }]); };
Register.ChannelEventHandler.onMentionReceived = (channel,message) => {};
Register.ChannelEventHandler.onDeliveryReceiptUpdated = groupChannel => {};
Register.ChannelEventHandler.onReadReceiptUpdated = groupChannel => { console.log('someone read a message'); };
Register.ChannelEventHandler.onTypingStatusUpdated = groupChannel => { console.log('someone is typing'); };
Register.ChannelEventHandler.onChannelHidden = groupChannel => { props.channelEventHandler('remove', [groupChannel]); };
Register.ChannelEventHandler.onChannelChanged = channel => { props.channelEventHandler('update', [channel]); };
Register.ChannelEventHandler.onChannelDeleted = (channelUrl, channelType) => { props.channelEventHandler('remove', [{ url: channelUrl }]); };
Register.ChannelEventHandler.onChannelFrozen = channel => { props.channelEventHandler('remove', [channel]); };
Register.ChannelEventHandler.onChannelUnfrozen = channel => { props.channelEventHandler('insert', [channel]); };
Register.ChannelEventHandler.onChannelMemberCountChanged = channels => {};
Register.ChannelEventHandler.onChannelParticipantCountChanged = channels => {};
Register.ChannelEventHandler.onMetaDataCreated = (channel, metaData) => {};
Register.ChannelEventHandler.onMetaDataUpdated = (channel, metaData) => {};
Register.ChannelEventHandler.onMetaDataDeleted = (channel, metaDataKeys) => {};
Register.ChannelEventHandler.onMetaCountersCreated = ( channel, metaCounter) => {};
Register.ChannelEventHandler.onMetaCountersUpdated = (channel, metaCounter) => {};
Register.ChannelEventHandler.onMetaCountersDeleted = (channel, metaCounterKeys) => {};
Register.ChannelEventHandler.onUserEntered = (openChannel, user) => { console.log('user entered'); };
Register.ChannelEventHandler.onUserExited = (openChannel, user) => { console.log('user exited'); };
Register.ChannelEventHandler.onUserMuted = (channel, user) => { props.channelEventHandler('remove', [channel]); };
Register.ChannelEventHandler.onUserUnmuted = (channel, user) => { props.channelEventHandler('insert', [channel]); };
Register.ChannelEventHandler.onUserBanned = (channel, user) => { props.channelEventHandler('remove', [channel]); };
Register.ChannelEventHandler.onUserUnbanned = (channel, user) => { props.channelEventHandler('insert', [channel]); };
Register.ChannelEventHandler.onUserReceivedInvitation = (groupChannel, inviter, invitees) => { props.channelEventHandler('insert', [groupChannel]) };
Register.ChannelEventHandler.onUserDeclinedInvitation = (groupChannel, inviter, invitee) => {};
Register.ChannelEventHandler.onUserJoined = (groupChannel, user) => { console.log('user joined', groupChannel); };
Register.ChannelEventHandler.onUserLeft = (groupChannel, user) => { console.log('user left', groupChannel); };
SB.addChannelHandler('channels', Register.ChannelEventHandler);
}
if (!Register.AppState) {
Register.AppState = AppState.addEventListener(
"change",
nextAppState => {
const SB = SendBird.getInstance();
if (
nextAppState === "active"
) {
SB.setForegroundState();
} else {
SB.setBackgroundState();
}
}
);
}
},
10000,
{
leading: true,
trailing: false,
},
);
// throttled because this is used in react-native-navigation, the HOC has a new instance for every scene
const throttledEventCleaner = _throttle(
async SB => {
console.log('[Sendbird] Unregistering (Cleaner)');
Register.AppState = null;
Register.Connection = null;
SB.removeChannelHandler('channels');
Register.ChannelEventHandler = null;
},
3000,
{
leading: true,
trailing: false,
},
);
const SendbirdContext = createContext();
function useSendbird() {
const context = useContext(SendbirdContext);
if (context === undefined) {
throw new Error('useSendbird must be used within a SendbirdProvider');
}
return context;
}
function SendbirdProvider(props) {
const SB = SendBird.getInstance();
useEffect(() => {
if (props.chat.user.userId && props.auth.user_id) {
throttledEventInitializer(props, SB); // SET event handlers
} else if (!props.auth.user_id) {
throttledEventCleaner(SB); // logged out, DELETE event handlers
}
}, [props.chat.session_token.expires_at]); // creation or update of token expiration
return (
<SendbirdContext.Provider value={{}}>
{props.children}
</SendbirdContext.Provider>
);
}
const mapStateToProps = ({ auth, device, chat }) => ({
auth,
device,
chat,
});
const SendbirdHOC = connect(mapStateToProps, actions)(SendbirdProvider);
export { SendbirdHOC, useSendbird, SendbirdContext };
These handlers correspond to the actions described in the react-native-redux-syncmanager-sample, and should plug into the reducers as is.