mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-03-03 11:36:08 +08:00
feat(contacts): pull full list of contacts from API
Summary: Also allow to search by last name Add tests for the ContactStore Test Plan: edgehill --test Reviewers: bengotow Reviewed By: bengotow Differential Revision: https://review.inboxapp.com/D1308
This commit is contained in:
parent
cce71a38aa
commit
b020795b3b
4 changed files with 173 additions and 30 deletions
|
@ -1,6 +1,111 @@
|
||||||
|
_ = require 'underscore-plus'
|
||||||
|
proxyquire = require 'proxyquire'
|
||||||
|
Contact = require '../../src/flux/models/contact'
|
||||||
ContactStore = require '../../src/flux/stores/contact-store'
|
ContactStore = require '../../src/flux/stores/contact-store'
|
||||||
|
DatabaseStore = require '../../src/flux/stores/database-store'
|
||||||
|
NamespaceStore = require '../../src/flux/stores/namespace-store'
|
||||||
|
|
||||||
describe "ContactStore", ->
|
describe "ContactStore", ->
|
||||||
xit 'should return an empty array when there is no namespace', ->
|
beforeEach ->
|
||||||
r = ContactStore.searchContacts ''
|
ContactStore._contactCache = []
|
||||||
expect(r.length).toBe 0
|
ContactStore._fetchOffset = 0
|
||||||
|
ContactStore._namespaceId = null
|
||||||
|
ContactStore._lastNamespaceId = null
|
||||||
|
NamespaceStore._current =
|
||||||
|
id: "nsid"
|
||||||
|
|
||||||
|
it "initializes the cache from the DB", ->
|
||||||
|
spyOn(DatabaseStore, "findAll").andCallFake -> Promise.resolve([])
|
||||||
|
ContactStore.init()
|
||||||
|
expect(ContactStore._contactCache.length).toBe 0
|
||||||
|
expect(ContactStore._fetchOffset).toBe 0
|
||||||
|
|
||||||
|
describe "when the Namespace updates from null to valid", ->
|
||||||
|
beforeEach ->
|
||||||
|
spyOn(ContactStore, "_refreshDBFromAPI")
|
||||||
|
NamespaceStore.trigger()
|
||||||
|
|
||||||
|
it "triggers a database fetch", ->
|
||||||
|
expect(ContactStore._refreshDBFromAPI.calls.length).toBe 1
|
||||||
|
|
||||||
|
it "starts at the current offset", ->
|
||||||
|
args = ContactStore._refreshDBFromAPI.calls[0].args
|
||||||
|
expect(args[0].limit).toBe ContactStore.BATCH_SIZE
|
||||||
|
expect(args[0].offset).toBe 0
|
||||||
|
|
||||||
|
describe "when the Namespace updates but the ID doesn't change", ->
|
||||||
|
it "does nothing", ->
|
||||||
|
spyOn(ContactStore, "_refreshDBFromAPI")
|
||||||
|
ContactStore._contactCache = [1,2,3]
|
||||||
|
ContactStore._fetchOffset = 3
|
||||||
|
ContactStore._namespaceId = "nsid"
|
||||||
|
ContactStore._lastNamespaceId = "nsid"
|
||||||
|
NamespaceStore._current =
|
||||||
|
id: "nsid"
|
||||||
|
NamespaceStore.trigger()
|
||||||
|
expect(ContactStore._contactCache).toEqual [1,2,3]
|
||||||
|
expect(ContactStore._fetchOffset).toBe 3
|
||||||
|
expect(ContactStore._refreshDBFromAPI).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
describe "When fetching from the API", ->
|
||||||
|
it "makes a request for the first batch", ->
|
||||||
|
batches = [[4,5,6], []]
|
||||||
|
spyOn(atom.inbox, "getCollection").andCallFake (nsid, type, params, opts) ->
|
||||||
|
opts.success(batches.shift())
|
||||||
|
waitsForPromise ->
|
||||||
|
ContactStore._refreshDBFromAPI().then ->
|
||||||
|
expect(atom.inbox.getCollection.calls.length).toBe 2
|
||||||
|
|
||||||
|
it "makes additional requests for future batches", ->
|
||||||
|
batches = [[1,2,3], [4,5,6], []]
|
||||||
|
spyOn(atom.inbox, "getCollection").andCallFake (nsid, type, params, opts) ->
|
||||||
|
opts.success(batches.shift())
|
||||||
|
waitsForPromise ->
|
||||||
|
ContactStore._refreshDBFromAPI().then ->
|
||||||
|
expect(atom.inbox.getCollection.calls.length).toBe 3
|
||||||
|
|
||||||
|
describe "when searching for a contact", ->
|
||||||
|
beforeEach ->
|
||||||
|
@c1 = new Contact(name: "", email: "1test@nilas.com")
|
||||||
|
@c2 = new Contact(name: "First", email: "2test@nilas.com")
|
||||||
|
@c3 = new Contact(name: "First Last", email: "3test@nilas.com")
|
||||||
|
@c4 = new Contact(name: "Fit", email: "fit@nilas.com")
|
||||||
|
@c5 = new Contact(name: "Fins", email: "fins@nilas.com")
|
||||||
|
@c6 = new Contact(name: "Fill", email: "fill@nilas.com")
|
||||||
|
@c7 = new Contact(name: "Fin", email: "fin@nilas.com")
|
||||||
|
ContactStore._contactCache = [@c1,@c2,@c3,@c4,@c5,@c6,@c7]
|
||||||
|
|
||||||
|
it "can find by first name", ->
|
||||||
|
results = ContactStore.searchContacts("First")
|
||||||
|
expect(results.length).toBe 2
|
||||||
|
expect(results[0]).toBe @c2
|
||||||
|
expect(results[1]).toBe @c3
|
||||||
|
|
||||||
|
it "can find by last name", ->
|
||||||
|
results = ContactStore.searchContacts("Last")
|
||||||
|
expect(results.length).toBe 1
|
||||||
|
expect(results[0]).toBe @c3
|
||||||
|
|
||||||
|
it "can find by email", ->
|
||||||
|
results = ContactStore.searchContacts("1test")
|
||||||
|
expect(results.length).toBe 1
|
||||||
|
expect(results[0]).toBe @c1
|
||||||
|
|
||||||
|
it "is case insensitive", ->
|
||||||
|
results = ContactStore.searchContacts("FIrsT")
|
||||||
|
expect(results.length).toBe 2
|
||||||
|
expect(results[0]).toBe @c2
|
||||||
|
expect(results[1]).toBe @c3
|
||||||
|
|
||||||
|
it "only returns the number requested", ->
|
||||||
|
results = ContactStore.searchContacts("FIrsT", limit: 1)
|
||||||
|
expect(results.length).toBe 1
|
||||||
|
expect(results[0]).toBe @c2
|
||||||
|
|
||||||
|
it "returns no more than 5 by default", ->
|
||||||
|
results = ContactStore.searchContacts("fi")
|
||||||
|
expect(results.length).toBe 5
|
||||||
|
|
||||||
|
it "can return more than 5 if requested", ->
|
||||||
|
results = ContactStore.searchContacts("fi", limit: 6)
|
||||||
|
expect(results.length).toBe 6
|
||||||
|
|
|
@ -185,7 +185,7 @@ class InboxAPI
|
||||||
if objects.length > 0
|
if objects.length > 0
|
||||||
DatabaseStore.persistModels(objects)
|
DatabaseStore.persistModels(objects)
|
||||||
resolve(objects)
|
resolve(objects)
|
||||||
|
|
||||||
_shouldAcceptModel: (classname, model = null) ->
|
_shouldAcceptModel: (classname, model = null) ->
|
||||||
return Promise.resolve() unless model
|
return Promise.resolve() unless model
|
||||||
|
|
||||||
|
@ -238,9 +238,9 @@ class InboxAPI
|
||||||
getCalendars: (namespaceId) ->
|
getCalendars: (namespaceId) ->
|
||||||
@getCollection(namespaceId, 'calendars', {})
|
@getCollection(namespaceId, 'calendars', {})
|
||||||
|
|
||||||
getCollection: (namespaceId, collection, params={}) ->
|
getCollection: (namespaceId, collection, params={}, requestOptions={}) ->
|
||||||
throw (new Error "getCollection requires namespaceId") unless namespaceId
|
throw (new Error "getCollection requires namespaceId") unless namespaceId
|
||||||
@makeRequest
|
@makeRequest _.extend requestOptions,
|
||||||
path: "/n/#{namespaceId}/#{collection}"
|
path: "/n/#{namespaceId}/#{collection}"
|
||||||
qs: params
|
qs: params
|
||||||
returnsModel: true
|
returnsModel: true
|
||||||
|
|
|
@ -6,26 +6,67 @@ NamespaceStore = require './namespace-store'
|
||||||
_ = require 'underscore-plus'
|
_ = require 'underscore-plus'
|
||||||
|
|
||||||
module.exports = ContactStore = Reflux.createStore
|
module.exports = ContactStore = Reflux.createStore
|
||||||
|
|
||||||
|
BATCH_SIZE: 500 # Num contacts to pull at once
|
||||||
|
|
||||||
init: ->
|
init: ->
|
||||||
@namespaceId = null
|
@_contactCache = []
|
||||||
@listenTo NamespaceStore, @onNamespaceChanged
|
@_lastNamespaceId = null
|
||||||
@listenTo DatabaseStore, @onDataChanged
|
@_namespaceId = null
|
||||||
|
@listenTo DatabaseStore, @_onDBChanged
|
||||||
|
@listenTo NamespaceStore, @_onNamespaceChanged
|
||||||
|
|
||||||
onNamespaceChanged: ->
|
@_refreshCacheFromDB()
|
||||||
@onDataChanged()
|
|
||||||
|
|
||||||
onDataChanged: (change) ->
|
searchContacts: (search, {limit}={}) ->
|
||||||
return if change && change.objectClass != Contact.name
|
limit ?= 5
|
||||||
DatabaseStore.findAll(Contact).then (contacts) =>
|
limit = Math.max(limit, 0)
|
||||||
@_all = contacts
|
|
||||||
@trigger(@)
|
|
||||||
|
|
||||||
searchContacts: (search) ->
|
|
||||||
return [] if not search or search.length is 0
|
return [] if not search or search.length is 0
|
||||||
search = search.toLowerCase()
|
search = search.toLowerCase()
|
||||||
matches = _.filter @_all, (contact) ->
|
matches = _.filter @_contactCache, (contact) ->
|
||||||
return true if contact.email?.toLowerCase().indexOf(search) == 0
|
return true if contact.email?.toLowerCase().indexOf(search) is 0
|
||||||
return true if contact.name?.toLowerCase().indexOf(search) == 0
|
name = contact.name?.toLowerCase() ? ""
|
||||||
|
for namePart in name.split(/\s/)
|
||||||
|
return true if namePart.indexOf(search) is 0
|
||||||
false
|
false
|
||||||
matches = matches[0..4] if matches.length > 5
|
matches = matches[0..limit-1] if matches.length > limit
|
||||||
matches
|
matches
|
||||||
|
|
||||||
|
_refreshCacheFromDB: ->
|
||||||
|
new Promise (resolve, reject) =>
|
||||||
|
DatabaseStore.findAll(Contact)
|
||||||
|
.then (contacts=[]) =>
|
||||||
|
@_contactCache = contacts
|
||||||
|
@trigger()
|
||||||
|
resolve()
|
||||||
|
.catch(reject)
|
||||||
|
|
||||||
|
_refreshDBFromAPI: (params={}) ->
|
||||||
|
new Promise (resolve, reject) =>
|
||||||
|
requestOptions =
|
||||||
|
success: (json) =>
|
||||||
|
if json.length > 0
|
||||||
|
@_refreshDBFromAPI
|
||||||
|
limit: @BATCH_SIZE
|
||||||
|
offset: params.offset + json.length
|
||||||
|
.then(resolve).catch(reject)
|
||||||
|
else resolve(json)
|
||||||
|
error: reject
|
||||||
|
atom.inbox.getCollection(@_namespaceId, "contacts", params, requestOptions)
|
||||||
|
|
||||||
|
_onDBChanged: (change) ->
|
||||||
|
return unless change?.objectClass is Contact.name
|
||||||
|
@_refreshCacheFromDB()
|
||||||
|
|
||||||
|
_resetCache: ->
|
||||||
|
@_contactCache = []
|
||||||
|
@trigger(@)
|
||||||
|
|
||||||
|
_onNamespaceChanged: ->
|
||||||
|
@_namespaceId = NamespaceStore.current()?.id
|
||||||
|
if @_namespaceId?
|
||||||
|
if @_namespaceId isnt @_lastNamespaceId
|
||||||
|
@_refreshDBFromAPI(limit: @BATCH_SIZE, offset: 0)
|
||||||
|
else
|
||||||
|
@_resetCache()
|
||||||
|
@_lastNamespaceId = @_namespaceId
|
||||||
|
|
|
@ -18,13 +18,10 @@ NamespaceStore = Reflux.createStore
|
||||||
|
|
||||||
populateItems: ->
|
populateItems: ->
|
||||||
DatabaseStore.findAll(Namespace).then (namespaces) =>
|
DatabaseStore.findAll(Namespace).then (namespaces) =>
|
||||||
@_items = namespaces
|
@_namespaces = namespaces
|
||||||
@_current = _.find @_items, (n) -> n.id == @_current?.id
|
@_current = _.find @_namespaces, (n) -> n.id == @_current?.id
|
||||||
|
|
||||||
unless @_current
|
@_current = @_namespaces?[0] unless @_current
|
||||||
@_current = @_items?[0]
|
|
||||||
if @_current
|
|
||||||
atom.inbox.getCollection(@_current.id, "contacts")
|
|
||||||
|
|
||||||
@trigger(@)
|
@trigger(@)
|
||||||
|
|
||||||
|
@ -35,13 +32,13 @@ NamespaceStore = Reflux.createStore
|
||||||
@populateItems()
|
@populateItems()
|
||||||
|
|
||||||
onSelectNamespaceId: (id) ->
|
onSelectNamespaceId: (id) ->
|
||||||
@_current = _.find @_items, (n) -> n.id == @_current.id
|
@_current = _.find @_namespaces, (n) -> n.id == @_current.id
|
||||||
@trigger(@)
|
@trigger(@)
|
||||||
|
|
||||||
# Exposed Data
|
# Exposed Data
|
||||||
|
|
||||||
items: ->
|
items: ->
|
||||||
@_items
|
@_namespaces
|
||||||
|
|
||||||
current: ->
|
current: ->
|
||||||
@_current
|
@_current
|
||||||
|
|
Loading…
Reference in a new issue