Websocket Support (#937)
* Atom / RSS meta link * Spike * Feeling good about this spike now * Remove document.collection * Remove koa.ctx from all presenters to make them portable outside requests * Remove full serialized model from events Move events.add to controllers for now, will eventually be in commands * collections.create event parentDocument -> parentDocumentId * Fix up deprecated tests * Fixed: Doc creation * documents.move * Handle collection deleted * 💚 * Authorize room join requests * Move starred data structure Account for documents with no context on sockets * Add socket.io-redis * Add WEBSOCKETS_ENABLED env variable to disable websockets entirely for self hosted New installations will default to true, existing installations to false * 💚 No need for promise response here * Reload notice
This commit is contained in:
96
app/components/SocketProvider.js
Normal file
96
app/components/SocketProvider.js
Normal file
@ -0,0 +1,96 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { inject } from 'mobx-react';
|
||||
import io from 'socket.io-client';
|
||||
import DocumentsStore from 'stores/DocumentsStore';
|
||||
import CollectionsStore from 'stores/CollectionsStore';
|
||||
import AuthStore from 'stores/AuthStore';
|
||||
import UiStore from 'stores/UiStore';
|
||||
|
||||
const SocketContext = React.createContext();
|
||||
|
||||
type Props = {
|
||||
children: React.Node,
|
||||
documents: DocumentsStore,
|
||||
collections: CollectionsStore,
|
||||
auth: AuthStore,
|
||||
ui: UiStore,
|
||||
};
|
||||
|
||||
class SocketProvider extends React.Component<Props> {
|
||||
socket;
|
||||
|
||||
componentDidMount() {
|
||||
if (!process.env.WEBSOCKETS_ENABLED) return;
|
||||
|
||||
this.socket = io(window.location.origin, {
|
||||
path: '/realtime',
|
||||
});
|
||||
|
||||
const { auth, ui, documents, collections } = this.props;
|
||||
if (!auth.token) return;
|
||||
|
||||
this.socket.on('connect', () => {
|
||||
this.socket.emit('authentication', {
|
||||
token: auth.token,
|
||||
});
|
||||
this.socket.on('unauthorized', err => {
|
||||
ui.showToast(err.message);
|
||||
});
|
||||
this.socket.on('entities', event => {
|
||||
if (event.documents) {
|
||||
event.documents.forEach(doc => {
|
||||
documents.add(doc);
|
||||
|
||||
// TODO: Move this to the document scene once data loading
|
||||
// has been refactored to be friendlier there.
|
||||
if (
|
||||
auth.user &&
|
||||
doc.id === ui.activeDocumentId &&
|
||||
doc.updatedBy.id !== auth.user.id
|
||||
) {
|
||||
ui.showToast(`Document updated by ${doc.updatedBy.name}`, {
|
||||
timeout: 30 * 1000,
|
||||
action: {
|
||||
text: 'Refresh',
|
||||
onClick: () => window.location.reload(),
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if (event.collections) {
|
||||
event.collections.forEach(collections.add);
|
||||
}
|
||||
});
|
||||
this.socket.on('documents.star', event => {
|
||||
documents.starredIds.set(event.documentId, true);
|
||||
});
|
||||
this.socket.on('documents.unstar', event => {
|
||||
documents.starredIds.set(event.documentId, false);
|
||||
});
|
||||
|
||||
// received a message from the API server that we should request
|
||||
// to join a specific room. Forward that to the ws server.
|
||||
this.socket.on('join', event => {
|
||||
this.socket.emit('join', event);
|
||||
});
|
||||
|
||||
// received a message from the API server that we should request
|
||||
// to leave a specific room. Forward that to the ws server.
|
||||
this.socket.on('leave', event => {
|
||||
this.socket.emit('leave', event);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SocketContext.Provider value={this.socket}>
|
||||
{this.props.children}
|
||||
</SocketContext.Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default inject('auth', 'ui', 'documents', 'collections')(SocketProvider);
|
Reference in New Issue
Block a user