fix: Improve websocket reliability (#1470)

* check connection on page visibility change

* fix: SocketPresence account for socket changing
This commit is contained in:
Tom Moor
2020-08-20 20:37:54 -07:00
committed by GitHub
parent 26c574ab58
commit ec55299c8b
3 changed files with 56 additions and 11 deletions

View File

@ -3,7 +3,7 @@ import { find } from "lodash";
import { observable } from "mobx"; import { observable } from "mobx";
import { inject, observer } from "mobx-react"; import { inject, observer } from "mobx-react";
import * as React from "react"; import * as React from "react";
import io from "socket.io-client"; import io, { Socket } from "socket.io-client";
import AuthStore from "stores/AuthStore"; import AuthStore from "stores/AuthStore";
import CollectionsStore from "stores/CollectionsStore"; import CollectionsStore from "stores/CollectionsStore";
import DocumentPresenceStore from "stores/DocumentPresenceStore"; import DocumentPresenceStore from "stores/DocumentPresenceStore";
@ -13,6 +13,7 @@ import MembershipsStore from "stores/MembershipsStore";
import PoliciesStore from "stores/PoliciesStore"; import PoliciesStore from "stores/PoliciesStore";
import UiStore from "stores/UiStore"; import UiStore from "stores/UiStore";
import ViewsStore from "stores/ViewsStore"; import ViewsStore from "stores/ViewsStore";
import { getVisibilityListener, getPageVisible } from "utils/pageVisibility";
export const SocketContext: any = React.createContext(); export const SocketContext: any = React.createContext();
@ -31,9 +32,35 @@ type Props = {
@observer @observer
class SocketProvider extends React.Component<Props> { class SocketProvider extends React.Component<Props> {
@observable socket; @observable socket: Socket;
componentDidMount() { componentDidMount() {
this.createConnection();
document.addEventListener(getVisibilityListener(), this.checkConnection);
}
componentWillUnmount() {
if (this.socket) {
this.socket.authenticated = false;
this.socket.disconnect();
}
document.removeEventListener(getVisibilityListener(), this.checkConnection);
}
checkConnection = () => {
if (this.socket && this.socket.disconnected && getPageVisible()) {
// null-ifying this reference is important, do not remove. Without it
// references to old sockets are potentially held in context
this.socket.close();
this.socket = null;
this.createConnection();
}
};
createConnection = () => {
this.socket = io(window.location.origin, { this.socket = io(window.location.origin, {
path: "/realtime", path: "/realtime",
transports: ["websocket"], transports: ["websocket"],
@ -264,14 +291,7 @@ class SocketProvider extends React.Component<Props> {
this.socket.on("user.presence", (event) => { this.socket.on("user.presence", (event) => {
presence.touch(event.documentId, event.userId, event.isEditing); presence.touch(event.documentId, event.userId, event.isEditing);
}); });
} };
componentWillUnmount() {
if (this.socket) {
this.socket.disconnect();
this.socket.authenticated = false;
}
}
render() { render() {
return ( return (

View File

@ -41,7 +41,7 @@ export default class SocketPresence extends React.Component<Props> {
} }
setupOnce = () => { setupOnce = () => {
if (this.context && !this.previousContext) { if (this.context && this.context !== this.previousContext) {
this.previousContext = this.context; this.previousContext = this.context;
if (this.context.authenticated) { if (this.context.authenticated) {

View File

@ -0,0 +1,25 @@
// @flow
let hidden = "hidden";
let visibilityChange = "visibilitychange";
if ("hidden" in document) {
hidden = "hidden";
visibilityChange = "visibilitychange";
} else if ("mozHidden" in document) {
// Firefox up to v17
hidden = "mozHidden";
visibilityChange = "mozvisibilitychange";
} else if ("webkitHidden" in document) {
// Chrome up to v32, Android up to v4.4, Blackberry up to v10
hidden = "webkitHidden";
visibilityChange = "webkitvisibilitychange";
}
export function getVisibilityListener(): string {
return visibilityChange;
}
export function getPageVisible(): boolean {
// $FlowFixMe
return !document[hidden];
}