collab_client: Promisify deferred actions

This commit is contained in:
Richard Hansen 2021-03-30 17:57:43 -04:00
parent 5814b76aa4
commit 966ba54874

View file

@ -31,6 +31,18 @@ const browser = require('./vendors/browser');
let pad = undefined; let pad = undefined;
const getSocket = () => pad && pad.socket; const getSocket = () => pad && pad.socket;
// Gate is a normal Promise that resolves when its open() method is called.
class Gate extends Promise {
constructor(executor = null) {
let open;
super((resolve, reject) => {
open = resolve;
if (executor != null) executor(resolve, reject);
});
this.open = open;
}
}
/** Call this when the document is ready, and a new Ace2Editor() has been created and inited. /** Call this when the document is ready, and a new Ace2Editor() has been created and inited.
ACE's ready callback does not need to have fired yet. ACE's ready callback does not need to have fired yet.
"serverVars" are from calling doc.getCollabClientVars() on the server. */ "serverVars" are from calling doc.getCollabClientVars() on the server. */
@ -63,6 +75,12 @@ const getCollabClient = (ace2editor, serverVars, initialUserInfo, options, _pad)
onConnectionTrouble: () => {}, onConnectionTrouble: () => {},
onServerMessage: () => {}, onServerMessage: () => {},
}; };
// We need to present a working interface even before the socket is connected for the first time.
// Use a Gate to block actions until connected. Once connected, the Gate is opened which causes
// post-connect actions to start running.
let connectedGate = new Gate();
if (browser.firefox) { if (browser.firefox) {
// Prevent "escape" from taking effect and canceling a comet connection; // Prevent "escape" from taking effect and canceling a comet connection;
// doesn't work if focus is on an iframe. // doesn't work if focus is on an iframe.
@ -302,7 +320,8 @@ const getCollabClient = (ace2editor, serverVars, initialUserInfo, options, _pad)
hooks.callAll(`handleClientMessage_${msg.type}`, {payload: msg.payload}); hooks.callAll(`handleClientMessage_${msg.type}`, {payload: msg.payload});
}; };
const updateUserInfo = (userInfo) => { const updateUserInfo = async (userInfo) => {
await connectedGate;
userInfo.userId = userId; userInfo.userId = userId;
userSet[userId] = userInfo; userSet[userId] = userInfo;
tellAceActiveAuthorInfo(userInfo); tellAceActiveAuthorInfo(userInfo);
@ -352,6 +371,12 @@ const getCollabClient = (ace2editor, serverVars, initialUserInfo, options, _pad)
const setChannelState = (newChannelState, moreInfo) => { const setChannelState = (newChannelState, moreInfo) => {
if (newChannelState === channelState) return; if (newChannelState === channelState) return;
if (channelState === 'CONNECTED') {
// The old channel state is CONNECTED, which means we have just disconnected. Re-initialize
// connectedGate so that actions are deferred until connected again. Do this before calling
// onChannelStateChange() so that the event handler can create deferred actions if desired.
connectedGate = new Gate();
}
channelState = newChannelState; channelState = newChannelState;
callbacks.onChannelStateChange(channelState, moreInfo); callbacks.onChannelStateChange(channelState, moreInfo);
switch (channelState) { switch (channelState) {
@ -360,7 +385,7 @@ const getCollabClient = (ace2editor, serverVars, initialUserInfo, options, _pad)
startConnectTime = Date.now(); startConnectTime = Date.now();
break; break;
case 'CONNECTED': case 'CONNECTED':
doDeferredActions(); connectedGate.open();
break; break;
} }
}; };
@ -373,26 +398,6 @@ const getCollabClient = (ace2editor, serverVars, initialUserInfo, options, _pad)
return array; return array;
}; };
// We need to present a working interface even before the socket
// is connected for the first time.
let deferredActions = [];
const defer = (func) => function (...args) {
const action = () => {
func.call(this, ...args);
};
if (channelState !== 'CONNECTED') {
deferredActions.push(action);
} else {
action();
}
};
const doDeferredActions = () => {
for (const action of deferredActions) action();
deferredActions = [];
};
const sendClientMessage = (msg) => { const sendClientMessage = (msg) => {
sendMessage( sendMessage(
{ {
@ -470,7 +475,7 @@ const getCollabClient = (ace2editor, serverVars, initialUserInfo, options, _pad)
setOnConnectionTrouble: (cb) => { setOnConnectionTrouble: (cb) => {
callbacks.onConnectionTrouble = cb; callbacks.onConnectionTrouble = cb;
}, },
updateUserInfo: defer(updateUserInfo), updateUserInfo,
handleMessageFromServer, handleMessageFromServer,
getConnectedUsers, getConnectedUsers,
sendClientMessage, sendClientMessage,