mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-09-22 16:26:08 +08:00
105 lines
3.4 KiB
CoffeeScript
105 lines
3.4 KiB
CoffeeScript
|
{Message, Actions,DraftStore} = require 'inbox-exports'
|
||
|
EventEmitter = require('events').EventEmitter
|
||
|
_ = require 'underscore-plus'
|
||
|
|
||
|
# As the user interacts with the draft, changes are accumulated in the
|
||
|
# DraftChangeSet associated with the store proxy. The DraftChangeSet does two things:
|
||
|
#
|
||
|
# 1. It debounces changes and calls Actions.saveDraft() at a reasonable interval.
|
||
|
#
|
||
|
# 2. It exposes `applyToModel`, which allows you to optimistically apply changes
|
||
|
# to a draft object. When the proxy vends the draft, it passes it through this
|
||
|
# function to apply uncommitted changes. This means the Draft provided by the
|
||
|
# DraftStoreProxy will always relfect recent changes, even though they're
|
||
|
# written to the database intermittently.
|
||
|
#
|
||
|
class DraftChangeSet
|
||
|
constructor: (@localId, @_onChange) ->
|
||
|
@_pending = {}
|
||
|
@_timer = null
|
||
|
|
||
|
add: (changes, immediate) ->
|
||
|
@_pending = _.extend(@_pending, changes)
|
||
|
@_onChange()
|
||
|
if immediate
|
||
|
@commit()
|
||
|
else
|
||
|
clearTimeout(@_timer) if @_timer
|
||
|
@_timer = setTimeout(@commit, 750)
|
||
|
|
||
|
commit: =>
|
||
|
@_pending.localId = @localId
|
||
|
if Object.keys(@_pending).length > 1
|
||
|
Actions.saveDraft(@_pending)
|
||
|
@_pending = {}
|
||
|
|
||
|
applyToModel: (model) ->
|
||
|
model.fromJSON(@_pending) if model
|
||
|
model
|
||
|
|
||
|
# DraftStoreProxy is a small class that makes it easy to implement components
|
||
|
# that display Draft objects or allow for interactive editing of Drafts.
|
||
|
#
|
||
|
# 1. It synchronously provides an instance of a draft via `draft()`, and
|
||
|
# triggers whenever that draft instance has changed.
|
||
|
#
|
||
|
# 2. It provides an interface for modifying the draft that transparently
|
||
|
# batches changes, and ensures that the draft provided via `draft()`
|
||
|
# always has pending changes applied.
|
||
|
#
|
||
|
module.exports =
|
||
|
class DraftStoreProxy
|
||
|
|
||
|
constructor: (@draftLocalId) ->
|
||
|
@unlisteners = []
|
||
|
@unlisteners.push DraftStore.listen(@_onDraftChanged, @)
|
||
|
@unlisteners.push Actions.didSwapModel.listen(@_onDraftSwapped, @)
|
||
|
@_emitter = new EventEmitter()
|
||
|
@_draft = false
|
||
|
@_reloadDraft()
|
||
|
|
||
|
@changes = new DraftChangeSet @draftLocalId, =>
|
||
|
@_emitter.emit('trigger')
|
||
|
|
||
|
draft: ->
|
||
|
@changes.applyToModel(@_draft)
|
||
|
@_draft
|
||
|
|
||
|
listen: (callback, bindContext) ->
|
||
|
eventHandler = (args) ->
|
||
|
callback.apply(bindContext, args)
|
||
|
@_emitter.addListener('trigger', eventHandler)
|
||
|
return =>
|
||
|
@_emitter.removeListener('trigger', eventHandler)
|
||
|
if @_emitter.listeners('trigger').length == 0
|
||
|
# Unlink ourselves from the stores/actions we were listening to
|
||
|
# so that we can be garbage collected
|
||
|
unlisten() for unlisten in @unlisteners
|
||
|
|
||
|
_onDraftChanged: (change) ->
|
||
|
# We don't accept changes unless our draft object is loaded
|
||
|
return unless @_draft
|
||
|
|
||
|
# Is this change an update to our draft?
|
||
|
myDraft = _.find(change.objects, (obj) => obj.id == @_draft.id)
|
||
|
if myDraft
|
||
|
@_draft = myDraft
|
||
|
@_emitter.emit('trigger')
|
||
|
|
||
|
_onDraftSwapped: (change) ->
|
||
|
# A draft was saved with a new ID. Since we use the draft ID to
|
||
|
# watch for changes to our draft, we need to pull again using our
|
||
|
# localId.
|
||
|
if change.oldModel.id is @_draft.id
|
||
|
@_draft = change.newModel
|
||
|
@_emitter.emit('trigger')
|
||
|
|
||
|
_reloadDraft: ->
|
||
|
promise = DraftStore.findByLocalId(@draftLocalId)
|
||
|
promise.catch (err) ->
|
||
|
console.log(err)
|
||
|
promise.then (draft) =>
|
||
|
@_draft = draft
|
||
|
@_emitter.emit('trigger')
|
||
|
|