fix(*): Composer horizontal scroll, fix archive-lots-and-scroll-down, draft-store cleanup exception,

This commit is contained in:
Ben Gotow 2015-04-28 17:33:46 -07:00
parent a0dc6612d8
commit 1efe193d12
8 changed files with 62 additions and 48 deletions

View file

@ -30,6 +30,7 @@
border-bottom: 0;
max-width: @compose-width;
margin: 0 auto;
overflow:hidden; // necessary because of negative margins below
.composer-action-bar-content {
display:flex;

View file

@ -41,20 +41,15 @@ describe "DatabaseView", ->
view = new DatabaseView(Message)
expect(view.count()).toBe(-1)
it "should immediately start fetching a row count and page one", ->
it "should immediately start fetching a row count", ->
config =
matchers: [Message.attributes.namespaceId.equal('asd')]
view = new DatabaseView(Message, config)
# Count query
expect(@queries.length).toEqual(2)
expect(@queries[0]._count).toEqual(true)
expect(@queries[0]._matchers).toEqual(config.matchers)
# Items query
expect(@queries[1]._matchers).toEqual(config.matchers)
expect(@queries[1]._range).toEqual({offset:0, limit:view._pageSize})
describe "instance methods", ->
beforeEach ->
config =
@ -206,7 +201,7 @@ describe "DatabaseView", ->
describe "when items have been removed", ->
beforeEach ->
spyOn(@view._emitter, 'emit')
@start = @view._pages[1].loadingStart
@start = @view._pages[1].lastTouchTime
runs ->
b = new Thread(@b)
b.tags = []
@ -218,9 +213,9 @@ describe "DatabaseView", ->
expect(@view._pages[0].items).toEqual([@a, @c, @d])
expect(@view._pages[1].items).toEqual([@e, @f])
it "should change the loadingStart date of changed pages so that refreshes started before the replacement do not revert it's changes", ->
expect(@view._pages[0].loadingStart isnt @start).toEqual(true)
expect(@view._pages[1].loadingStart isnt @start).toEqual(true)
it "should change the lastTouchTime date of changed pages so that refreshes started before the replacement do not revert it's changes", ->
expect(@view._pages[0].lastTouchTime isnt @start).toEqual(true)
expect(@view._pages[1].lastTouchTime isnt @start).toEqual(true)
describe "cullPages", ->
beforeEach ->
@ -342,4 +337,3 @@ describe "DatabaseView", ->
expect(@view._pages[0].items[0].metadata).toEqual('metadata-for-model-a')
expect(@view._pages[0].items[1].metadata).toEqual('metadata-for-model-b')
expect(@view._emitter.emit).toHaveBeenCalled()

View file

@ -16,7 +16,7 @@ describe "ContactStore", ->
it "initializes the cache from the DB", ->
spyOn(DatabaseStore, "findAll").andCallFake -> Promise.resolve([])
ContactStore.init()
ContactStore.constructor()
expect(ContactStore._contactCache.length).toBe 0
expect(ContactStore._fetchOffset).toBe 0

View file

@ -41,7 +41,7 @@ class ContactStore
@_refreshCache()
# Public: Search the user's contact list for the given search term.
# Public: Search the user's contact list for the given search term.
# This method compares the `search` string against each Contact's
# `name` and `email`.
#
@ -107,4 +107,4 @@ class ContactStore
@_resetCache()
module.exports = new ContactStore()
module.exports = new ContactStore()

View file

@ -118,7 +118,7 @@ inspect the changes that are sent to your listener method.
_onDataChanged: (change) ->
return unless change.objectClass is Message
return unless @_myMessageID in _.map change.objects, (m) -> m.id
return unless @_myMessageID in _.map change.objects, (m) -> m.id
# Refresh Data

View file

@ -4,12 +4,15 @@ DatabaseStore = require './database-store'
ModelView = require './model-view'
EventEmitter = require('events').EventEmitter
# DatabaseView abstracts away the process of paginating a query
verbose = true
# Public: DatabaseView abstracts away the process of paginating a query
# and loading ranges of data. It's very smart about deciding when
# results need to be refreshed. There are a few core concepts that
# make it flexible:
#
# matchers: The where clauses that should be applied to queries.
# - `matchers`: The where clauses that should be applied to queries.
# - `includes`: The include clauses that should be applied to queries.
#
# metadataProvider: For each item loaded, you can provide a promise
# that resolves with additional data for that item. The DatabaseView
@ -24,10 +27,6 @@ EventEmitter = require('events').EventEmitter
# DatabaseView may internally keep a larger set of items loaded
# for performance.
#
#
verbose = true
class DatabaseView extends ModelView
constructor: (@klass, config = {}, @_itemMetadataProvider) ->
@ -108,6 +107,8 @@ class DatabaseView extends ModelView
sortAttribute = items[0].constructor.naturalSortOrder()?.attribute()
indexes = []
touchTime = Date.now()
spliceItem = (idx) =>
page = Math.floor(idx / @_pageSize)
pageIdx = idx - page * @_pageSize
@ -115,10 +116,10 @@ class DatabaseView extends ModelView
# Remove the item in question from the page
@_pages[page]?.items.splice(pageIdx, 1)
# Update the page's `loadingStart`. This causes pending refreshes
# Update the page's `lastTouchTime`. This causes pending refreshes
# of page data to be cancelled. This is important because these refreshes
# would actually roll back this optimistic change.
@_pages[page]?.loadingStart = Date.now()
@_pages[page]?.lastTouchTime = touchTime
# Iterate through the remaining pages. Take the first
# item from the next page, remove it, and put it at the
@ -127,7 +128,7 @@ class DatabaseView extends ModelView
item = @_pages[page + 1].items[0]
break unless item
@_pages[page + 1].items.splice(0, 1)
@_pages[page + 1].loadingStart = Date.now()
@_pages[page + 1].lastTouchTime = touchTime
@_pages[page].items.push(item)
page += 1
@ -195,7 +196,6 @@ class DatabaseView extends ModelView
@selection.updateModelReferences(items)
@_emitter.emit('trigger')
invalidateMetadataFor: (ids = []) ->
# This method should be called when you know that only the metadata for
# a given set of items has been dirtied. For example, when we have a view
@ -220,18 +220,31 @@ class DatabaseView extends ModelView
@_count = count
@_emitter.emit('trigger')
retrievePage: (idx) ->
start = Date.now()
invalidateRetainedRange: _.debounce ->
for idx in @pagesRetained()
@retrievePage(idx)
,10
retrieveDirtyInRetainedRange: ->
for idx in @pagesRetained()
if not @_pages[idx] or @_pages[idx].lastTouchTime > @_pages[idx].lastLoadTime
@retrievePage(idx)
retrievePage: (idx) ->
page = @_pages[idx] ? {
lastTouchTime: 0
lastLoadTime: 0
metadata: {}
items: []
}
page.loadingStart = start
page.loading = true
@_pages[idx] = page
# Even though we won't touch the items array for another 100msec, the data
# will reflect "now" since we make the query now.
touchTime = Date.now()
query = DatabaseStore.findAll(@klass).where(@_matchers)
query.offset(idx * @_pageSize).limit(@_pageSize)
query.include(attr) for attr in @_includes
@ -241,9 +254,10 @@ class DatabaseView extends ModelView
# retained range and been cleaned up.
return unless @_pages[idx]
# If we've started reloading since we made our query, don't do any more work
if page.loadingStart isnt start
@log("Retrieval cancelled — out of date.")
# The data has been changed and is now "newer" than our query result. Applying
# our version of the items would roll it back. Abort!
if page.lastTouchTime >= touchTime
@log("Version #{touchTime} fetched, but out of date (current is #{page.lastTouchTime})")
return
# Now, fetch the messages for each thread. We could do this with a
@ -253,10 +267,11 @@ class DatabaseView extends ModelView
@retrievePageMetadata(idx, items)
retrievePageMetadata: (idx, items) ->
start = Date.now()
page = @_pages[idx]
page.loadingStart = start
# Even though we won't touch the items array for another 100msec, the data
# will reflect "now" since we make the query now.
touchTime = Date.now()
# This method can only be used once the page is loaded. If no page is present,
# go ahead and retrieve it in full.
@ -275,8 +290,8 @@ class DatabaseView extends ModelView
Promise.props(metadataPromises).then (results) =>
# If we've started reloading since we made our query, don't do any more work
if page.loadingStart isnt start
@log("Metadata retrieval cancelled — out of date.")
if page.lastTouchTime >= touchTime
@log("Metadata version #{touchTime} fetched, but out of date (current is #{page.lastTouchTime})")
return
for item in items
@ -292,6 +307,8 @@ class DatabaseView extends ModelView
page.items = items
page.loading = false
page.lastLoadTime = touchTime
page.lastTouchTime = touchTime
# Trigger if this is the last page that needed to be loaded
@_emitter.emit('trigger') if @loaded()

View file

@ -125,16 +125,17 @@ class DraftStore
########### PRIVATE ####################################################
cleanupSessionForLocalId: (localId) =>
return unless @_draftSessions[localId]
session = @_draftSessions[localId]
return unless session
draft = @_draftSessions[localId].draft()
draft = session.draft()
Actions.queueTask(new DestroyDraftTask(localId)) if draft.pristine
if atom.getWindowType() is "composer"
atom.close()
if atom.isMainWindow()
@_draftSessions[localId].cleanup()
session.cleanup()
delete @_draftSessions[localId]
_onBeforeUnload: =>

View file

@ -4,15 +4,15 @@ ModelViewSelection = require './model-view-selection'
module.exports =
class ModelView
constructor: ->
@_pageSize = 100
@_retainedRange = {start: 0, end: 50}
@_pages = {}
@_emitter = new EventEmitter()
@selection = new ModelViewSelection @, => @_emitter.emit('trigger')
@
# Accessing Data
@ -39,7 +39,7 @@ class ModelView
getStub: ->
@_sample ?= new klass
getById: (id) ->
return null unless id
for pageIdx, page of @_pages
@ -53,7 +53,7 @@ class ModelView
for item, itemIdx in page.items
return pageIdx * @_pageSize + itemIdx if item.id is id
return -1
count: ->
throw new Error("ModelView base class does not implement count()")
@ -72,11 +72,7 @@ class ModelView
end is @_retainedRange.end
@_retainedRange = {start, end}
for idx in @pagesRetained()
if not @_pages[idx]
@retrievePage(idx)
@retrieveDirtyInRetainedRange()
@cullPages()
# Optionally implement this method in subclasses to expand the retained range provided
@ -101,5 +97,10 @@ class ModelView
for idx in @pagesRetained()
@retrievePage(idx)
retrieveDirtyInRetainedRange: ->
for idx in @pagesRetained()
if not @_pages[idx]
@retrievePage(idx)
retrievePage: (page) ->
throw new Error("ModelView base class does not implement retrievePage()")