@strategies/collaborate-on-fire
TypeScript icon, indicating that this package has built-in type declarations

1.2.3 • Public • Published

collaborate-on-fire

Use collaborate-on-fire to enable multi-user shared-state environments using mobx-keystone and firebase.

Motivation

Firebase provides many mechanisms required for creating multi-user environments. It is trivial to create a chat-client or a simple game where each user controls only their own state. However, true collaboration in web-based tools often requires operating on the same set of elements simultaneously. This creates conflicts when trying to centralize state in firestore or realtime db. This solution uses the patch feature of mobx-keystone to send patches from actions performed by one user to all other users.

Features

  • Synchronize shared file state between multiple users:
    • Watch for changes to the shared file data and push these changes to all other users using realtime-db.
    • Apply incoming patches from other users to the local document state.
    • Collect patches to be sent using a configurable interval, to reduce traffic for more intensive operations.
  • Synchronize independent user state - such as cursor position or selection.
  • Load a file from cloud firestore, and track any incoming patches while it loads to ensure consistency.
  • Save the file to cloud firestore based on the most recent user to update.
  • Undo/Redo support: incoming changes do not get added to the stack. Users will only undo/redo their own changes.

Example

https://github.com/sasakiassociates/collaborate-on-fire-example

Installation

Run the following command to install collaborate-on-fire in your project:

yarn add collaborate-on-fire

OR

npm install collaborate-on-fire

Usage

Initialize Firebase

Firebase should be initialized using initializeApp before creating the MultiUserPersistence instance.

firebase.initializeApp(firebaseConfig);

Create a RootStore class with a file, userStore and undoManager that implement the IRootStore interface. Once the user id has been determined (via login) the MultiUserPersistence instance can be created.

const stateContainer: IStateContainer = new KeystoneContainer();//see example for KeystoneContainer
const rootStore: IRootStore = new RootStore();
firebase.auth().onAuthStateChanged(user => {
    new MultiUserPersistence(rootStore, stateContainer, user.uid);
});

Considerations

Think carefully about what needs to persist in your file, what can go in temporary state (e.g. interface state) and what (if anything) needs to be synced as user state.

Be careful applying snapshots e.g. to undo a change because that will undo globally and delete all actions by another user. e.g. if one were to use the practice of reverting a modal's changes to a previous snapshot on cancel this would not work well in a multi-user setup: if one user left a modal open, came back and cancelled it later, it would revert all changes by other users while that modal was open. The built-in mobx-keystone undo middleware uses patches instead of snapshots and therefore works well with this patch-based approach.

Realtime Transaction Counter

The Realtime Transaction Counter (RTC) is a centralized counter used to keep track of patches and the order in which they are applied. The same counter is saved on the firestore file's metadata so that patches since the last save can be applied when loading.

IRootStore.undoManager (optional)

It is best practice to exclude user patches from other users from the undo/redo stack. If using the mobx-keystone undo middleware, this can be specified on the rootStore object and withoutUndo will be wrapped around incoming patches.

import {UndoManager, undoMiddleware} from "mobx-keystone";
import IRootStore from "collaborate-on-fire";

class RootStore implements IRootStore {
    persist: FileStore;
    undoManager: UndoManager;
    userStore: UserStore;

    constructor(persist: FileStore, userStore: UserStore) {
        this.persist = persist;
        this.userStore = userStore;
        this.undoManager = undoMiddleware(persist);
    }
};

SingleUserPersistence

If you want to disable the multi-user behavior - for example while testing or for a demo version, you can simply use SingleUserPersistence instead (the user id is not needed)

new SingleUserPersistence(rootStore, stateContainer);

Keystone

For an example using keystone, take a look at collaborate-on-fire-example.

License

MIT

Readme

Keywords

none

Package Sidebar

Install

npm i @strategies/collaborate-on-fire

Weekly Downloads

1

Version

1.2.3

License

MIT

Unpacked Size

146 kB

Total Files

17

Last publish

Collaborators

  • scottdpenman
  • tadiraman
  • sasaki-dev
  • arminakvn
  • eyoungberg
  • sasaki-strategies