On PUT, POST or DELETE to an API endpoint:
-
Get the next
tid
counter value from Redis -
Emit an event in the
changes
channel of Socket.io:{ name: 'create|update|delete', typeKey: 'product', id: '123', tid: ${counter value} }
-
Socket.io will need to be configured to use Redis as a backend so this will work with a cluster of Node processes
Create a ChangeNotificationService to subscribe to the changes
channel of Socket.io. On receipt of an event of type:
create
: can be ignored at this stageupdate
ordelete
: see if the updated model is in the store, and if it is then handle updating the in-memory model (see below)
There may be an Ember plugin that provides some of this functionality.
When the user is in the process of editing a model, we want to avoid overwriting their changes with the update from the server.
-
Add a simple locking mechanism so that the EditRoutes can indicate that a model is being edited
- It's possible that other models will need to be locked but at this stage all of our views send changes to a single API endpoint, so this isn't required (someone sanity check this please)
- Release the lock when the route is exited (be aware of the unload handling below)
-
If the model to be updated/deleted is locked then we need to inform the user:
-
update
: "Another user has made changes to this ${typeLabel}, please click here to load the updated details". The user can open this link in another tab if they want to compare their changes with the incoming change. The normalonunload
handler will fire and they -
delete
: "Another user has deleted this ${typeLabel}. You will need to {{link-to 'create'}}create a new ${typeLabel}{{/link-to}} in order to save your changes"
-
-
Otherwise, simple get the model from the store and call its
reload()
method to fetch the latest data from the server
If we miss any change notifications then our in-memory graph may be out of date. At some point we'll keep a buffer of events so that clients can catch up, but at this stage we should display a notification informing the user that a reload is required.
- On app startup and then periodically (every 30 seconds) the app should get the current
tid
value - If a new
tid
is received (either through periodic checking or from a received event) that is not equal tolast value + 1
then we've missed an event and the app should display a message: "Connection interrupted, you should reload to get the latest changes." - I think this should have some UX love because it's not an uncommon event
When a user is on an edit page and they navigate away we should prompt them to save their changes. This can be done using $(window).on('beforeunload')
. This should be implemented in FormRoute#deactivate
. If deactivate
is called and the model.get('isDirty')
display a message asking the user whether they want to save their changes.
- See this card for the UX design for this.
- There may already be an ember plugin that does this, but it's really simple so not hard to do it ourselves.
Add a check to the server side that adds WHERE last_updated = ${last_updated in request body}
to the UPDATE query.
- If the update results in zero records affected then get the current record from the database and compare the
last_updated
fields - If the record in the database has a later
last_updated
then return an appropriate error response to the client - On the client, check for this error code (probably in FormRoute) and display a message: "Another user has modified this ${typeLabel} since you started making changes to it, please click here to load the updated details."