UNPKG

fastcomments-react-native-sdk

Version:

React Native FastComments Components. Add live commenting to any React Native application.

159 lines (158 loc) 6.69 kB
import { iterateCommentsTree } from "./comment-trees"; import { createURLQueryString, makeRequest } from "./http"; /** * @typedef {number} UserPresencePollState **/ /** * @enum {UserPresencePollState} */ const UserPresencePollStateEnum = { Disabled: 0, Poll: 1 }; const STEALTH = { stealth: true }; /** * @typedef {Object} UserPresenceState * @property {boolean} [heartbeatActive] * @property {UserPresencePollStateEnum} [presencePollState] * @property {Object.<string, boolean>} usersOnlineMap * @property {Object.<string, Array.<string>>} userIdsToCommentIds */ export async function setupUserPresenceState(state, urlIdWS) { if (state.config.disableLiveCommenting.get() || state.userPresenceState.presencePollState.get() === UserPresencePollStateEnum.Disabled) { return; } const userIdsToCheck = {}; // TODO add optimization back - remove these two lines state.userPresenceState.userIdsToCommentIds.set({}); state.userPresenceState.usersOnlineMap.set({}); iterateCommentsTree(state.commentsTree.get(), function (comment) { addCommentToUserPresenceState(state.userPresenceState, comment, userIdsToCheck); }); const userIds = Object.keys(userIdsToCheck); if (userIds.length > 0) { await getAndUpdateUserStatuses(state.apiHost.get(STEALTH), state.config.tenantId.get(STEALTH), state.ssoConfigString.get(STEALTH), urlIdWS, state.userPresenceState, userIds); } else if (!state.userPresenceState.heartbeatActive.get()) { setupUserPresenceHeartbeat(state.apiHost.get(STEALTH), state.config.tenantId.get(STEALTH), state.ssoConfigString.get(STEALTH), urlIdWS); setupUserPresencePolling(state.apiHost.get(STEALTH), state.config.tenantId.get(STEALTH), state.ssoConfigString.get(STEALTH), urlIdWS, state.userPresenceState, userIds); state.userPresenceState.heartbeatActive.set(true); } } async function getAndUpdateUserStatuses(apiHost, tenantId, ssoConfigString, urlIdWS, userPresenceState, userIds) { const response = await makeRequest({ apiHost, method: 'GET', url: '/user-presence-status' + createURLQueryString({ tenantId, urlIdWS: urlIdWS, userIds: userIds.join(',') }) }); if (response.status === 'success' && response.userIdsOnline) { for (const userId in response.userIdsOnline) { const isOnline = response.userIdsOnline[userId]; if (userPresenceState.usersOnlineMap[userId].get() !== isOnline) { userPresenceState.usersOnlineMap[userId].set(isOnline); } } } if (!userPresenceState.heartbeatActive.get()) { setupUserPresenceHeartbeat(apiHost, tenantId, ssoConfigString, urlIdWS); setupUserPresencePolling(apiHost, tenantId, ssoConfigString, urlIdWS, userPresenceState, userIds); userPresenceState.heartbeatActive.set(true); } } // TODO OPTIMIZE don't take whole config object due to required get() call in callers export async function handleNewRemoteUser(config, urlIdWS, state, userIds) { const response = await makeRequest({ apiHost: config.apiHost, method: 'GET', url: '/user-presence-status' + createURLQueryString({ tenantId: config.tenantId, urlIdWS: urlIdWS, userIds: userIds }) }); if (response.status === 'success' && response.userIdsOnline) { for (const userId in response.userIdsOnline) { const isOnline = response.userIdsOnline[userId]; if (state.usersOnlineMap[userId].get() !== isOnline) { state.usersOnlineMap[userId].set(isOnline); } } } } /** * * @param {UserPresenceState} state * @param {comment} comment * @param {Object.<string, boolean>} [changes] */ export function addCommentToUserPresenceState(state, comment, changes) { if (comment.userId || comment.anonUserId) { // OPTIMIZATION state.userIdsToCommentIds.set((userIdsToCommentIds) => { if (comment.userId) { if (!userIdsToCommentIds[comment.userId]) { userIdsToCommentIds[comment.userId] = []; if (changes && state.usersOnlineMap[comment.userId] === undefined) { changes[comment.userId] = true; } } if (!userIdsToCommentIds[comment.userId].includes(comment._id)) { userIdsToCommentIds[comment.userId].push(comment._id); } } if (comment.anonUserId) { if (!userIdsToCommentIds[comment.anonUserId]) { userIdsToCommentIds[comment.anonUserId] = []; if (changes && state.usersOnlineMap[comment.anonUserId] === undefined) { changes[comment.anonUserId] = true; } } if (!userIdsToCommentIds[comment.anonUserId].includes(comment._id)) { userIdsToCommentIds[comment.anonUserId].push(comment._id); } } return userIdsToCommentIds; }); } } function setupUserPresenceHeartbeat(apiHost, tenantId, ssoConfigString, urlIdWS) { async function next() { try { await makeRequest({ apiHost, method: 'PUT', url: '/user-presence-heartbeat' + createURLQueryString({ tenantId, urlIdWS: urlIdWS, sso: ssoConfigString }) }); } catch (e) { } setTimeout(next, 1800000); // every 30 minutes on success or failure } setTimeout(next, 1800000); // every 30 minutes } function setupUserPresencePolling(apiHost, tenantId, ssoConfigString, urlIdWS, userPresenceState, userIds) { if (userPresenceState.presencePollState?.get() === UserPresencePollStateEnum.Poll) { const offset = Math.ceil(10000 * Math.random()); const timeout = 30000 + offset; // every 30 seconds + a random offset async function next() { await getAndUpdateUserStatuses(apiHost, tenantId, ssoConfigString, urlIdWS, userPresenceState, userIds); setTimeout(next, timeout); } setTimeout(next, timeout); } } // export function updateUserActivityMonitors(config: FastCommentsCommentWidgetConfig, state: FastCommentsState, userId: string, isOnline: boolean) { // if (config.disableLiveCommenting) { // return; // } // if (typeof isOnline === 'boolean') { // state.usersOnlineMap[userId] = isOnline; // } // }