fastcomments-react-native-sdk
Version:
React Native FastComments Components. Add live commenting to any React Native application.
170 lines (169 loc) • 7.71 kB
JavaScript
import { createURLQueryString, makeRequest } from "./http";
function extractCommentIdFromEvent(liveEvent) {
if (liveEvent.type === 'new-comment') {
return liveEvent.comment._id;
}
return 'undefined';
}
export function subscribeToChanges(config, wsHost, tenantIdWS, urlId, urlIdWS, userIdWS, checkBlockedComments, handleLiveEvent, onConnectionStatusChange, lastLiveEventTime) {
try {
if (config.disableLiveCommenting) {
return;
}
let isIntentionallyClosed = false;
// Normally live events (comments created/updated, new votes) are pushed to the client from the server.
// However, sometimes the client disconnects, some things happen, and then it reconnects.
// To keep the server-side architecture simple, we store an event log on the server, and when the client reconnects
// it simply fetches any events in the log that it would have missed.
async function fetchEventLog(startTime, endTime) {
// console.log('FastComments: fetchEventLog.', startTime, endTime);
const response = await makeRequest({
apiHost: config.apiHost, method: 'GET', url: '/event-log/' + config.tenantId + '/' + createURLQueryString({
urlId,
startTime,
endTime,
userIdWS // this will be used to validate the SSO session
})
});
if (response && response.status === 'success') {
if (response.events) {
// console.log('FastComments: fetchEventLog SUCCESS', response.events.length);
function handleEvents(eventsDataParsed, blockedCommentIdMap) {
for (const eventDataParsed of eventsDataParsed) {
lastLiveEventTime = Math.max(lastLiveEventTime || 0, eventDataParsed.timestamp + 1);
// the as WebsocketLiveNewOrUpdatedCommentEvent cast is kind of wrong here, but probably better than ts-ignore.
// we could check the type before we call the method, but that's kind of the point of having the method in the first place.
(!blockedCommentIdMap || !blockedCommentIdMap[extractCommentIdFromEvent(eventDataParsed)]) && handleLiveEvent(eventDataParsed);
}
}
const eventsParsed = response
.events
.map(function (event) {
return JSON.parse(event.data);
});
if (checkBlockedComments) {
const ids = [];
for (const eventParsed of eventsParsed) {
const extractedId = extractCommentIdFromEvent(eventParsed);
if (extractedId) {
ids.push(extractedId);
}
}
if (ids.length > 0) {
const blockedCommentIdMap = await checkBlockedComments(ids);
handleEvents(eventsParsed, blockedCommentIdMap);
}
else {
handleEvents(eventsParsed);
}
}
else {
handleEvents(eventsParsed);
}
}
else {
// console.log('FastComments: fetchEventLog SUCCESS - Empty response', response.events);
}
}
else {
console.error('FastComments: fetchEventLog FAILURE', response);
setTimeout(function () {
fetchEventLog(startTime, Date.now());
}, 5000 * Math.random());
}
}
if (config.usePolling) {
if (!lastLiveEventTime) {
lastLiveEventTime = Date.now();
}
async function pollNext() {
if (!isIntentionallyClosed) {
await fetchEventLog(lastLiveEventTime, Date.now());
timeNext();
}
}
function timeNext() {
if (!isIntentionallyClosed) {
setTimeout(function () {
// noinspection JSIgnoredPromiseFromCall
pollNext();
}, 2000);
}
}
timeNext();
return {
close: function () {
isIntentionallyClosed = true;
}
};
}
else {
if (lastLiveEventTime) {
console.log('FastComments: Detected potentially missed events, fetching log...');
// noinspection JSIgnoredPromiseFromCall
fetchEventLog(lastLiveEventTime, Date.now());
}
console.log('FastComments: connecting...');
const socket = new WebSocket(wsHost + '/sub' + createURLQueryString({
urlId: urlIdWS,
userIdWS,
tenantIdWS
}));
socket.onopen = async function () {
if (isIntentionallyClosed) {
return;
}
console.log('FastComments: connected.');
if (lastLiveEventTime) {
// noinspection ES6MissingAwait
fetchEventLog(lastLiveEventTime, Date.now());
}
onConnectionStatusChange && onConnectionStatusChange(true, lastLiveEventTime);
};
socket.onclose = function () {
console.log('FastComments: disconnected.');
if (!lastLiveEventTime) {
lastLiveEventTime = Date.now();
}
if (!isIntentionallyClosed) {
onConnectionStatusChange && onConnectionStatusChange(false, lastLiveEventTime);
setTimeout(function () {
subscribeToChanges(config, wsHost, tenantIdWS, urlId, urlIdWS, userIdWS, checkBlockedComments, handleLiveEvent, onConnectionStatusChange, lastLiveEventTime);
}, 2000 * Math.random());
}
};
socket.onmessage = async function (event) {
if (isIntentionallyClosed) {
return;
}
// console.log('FastComments: live event.');
const eventDataParsed = JSON.parse(decodeURIComponent(event.data));
lastLiveEventTime = Math.max(lastLiveEventTime || 0, eventDataParsed.timestamp);
if (checkBlockedComments) {
const id = extractCommentIdFromEvent(eventDataParsed);
if (id) {
const blockedCommentsInfo = await checkBlockedComments([id]);
if (!blockedCommentsInfo[id]) {
handleLiveEvent(eventDataParsed);
}
}
else {
handleLiveEvent(eventDataParsed);
}
}
else {
handleLiveEvent(eventDataParsed);
}
};
return {
close: function () {
isIntentionallyClosed = true;
socket && socket.close();
}
};
}
}
catch (e) {
console.error(e);
}
}