mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-12 02:58:20 +08:00
☕(account-store): Convert to ES6
This commit is contained in:
parent
30ce9be8d9
commit
bd30adec15
16 changed files with 453 additions and 382 deletions
|
@ -116,7 +116,7 @@ export default class Category extends Model {
|
||||||
}
|
}
|
||||||
|
|
||||||
displayType = () => {
|
displayType = () => {
|
||||||
AccountStore = AccountStore || require('../stores/account-store');
|
AccountStore = AccountStore || require('../stores/account-store').default;
|
||||||
if (AccountStore.accountForId(this.accountId).usesLabels()) {
|
if (AccountStore.accountForId(this.accountId).usesLabels()) {
|
||||||
return 'label';
|
return 'label';
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,9 +127,11 @@ class NylasAPI {
|
||||||
|
|
||||||
handleAuthenticationFailure = (modelUrl, apiToken, apiName) => {
|
handleAuthenticationFailure = (modelUrl, apiToken, apiName) => {
|
||||||
// Prevent /auth errors from presenting auth failure notices
|
// Prevent /auth errors from presenting auth failure notices
|
||||||
if (!apiToken) return Promise.resolve()
|
if (!apiToken) {
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
AccountStore = AccountStore || require('./stores/account-store')
|
AccountStore = AccountStore || require('./stores/account-store').default
|
||||||
const account = AccountStore.accounts().find((acc) => {
|
const account = AccountStore.accounts().find((acc) => {
|
||||||
const localMatch = AccountStore.tokensForAccountId(acc.id).localSync === apiToken;
|
const localMatch = AccountStore.tokensForAccountId(acc.id).localSync === apiToken;
|
||||||
const cloudMatch = AccountStore.tokensForAccountId(acc.id).n1Cloud === apiToken;
|
const cloudMatch = AccountStore.tokensForAccountId(acc.id).n1Cloud === apiToken;
|
||||||
|
@ -319,7 +321,7 @@ class NylasAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
accessTokenForAccountId = (aid) => {
|
accessTokenForAccountId = (aid) => {
|
||||||
AccountStore = AccountStore || require('./stores/account-store')
|
AccountStore = AccountStore || require('./stores/account-store').default
|
||||||
return AccountStore.tokensForAccountId(aid).localSync
|
return AccountStore.tokensForAccountId(aid).localSync
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,7 +371,7 @@ class NylasAPI {
|
||||||
|
|
||||||
let account = accountOrId
|
let account = accountOrId
|
||||||
if (!(accountOrId instanceof Account)) {
|
if (!(accountOrId instanceof Account)) {
|
||||||
AccountStore = AccountStore || require('./stores/account-store')
|
AccountStore = AccountStore || require('./stores/account-store').default
|
||||||
account = AccountStore.accountForId(accountOrId)
|
account = AccountStore.accountForId(accountOrId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,365 +0,0 @@
|
||||||
_ = require 'underscore'
|
|
||||||
NylasStore = require 'nylas-store'
|
|
||||||
Actions = require('../actions').default
|
|
||||||
Account = require('../models/account').default
|
|
||||||
Utils = require '../models/utils'
|
|
||||||
DatabaseStore = require('./database-store').default
|
|
||||||
keytar = require 'keytar'
|
|
||||||
NylasAPI = null
|
|
||||||
NylasAPIRequest = null
|
|
||||||
|
|
||||||
configAccountsKey = "nylas.accounts"
|
|
||||||
configVersionKey = "nylas.accountsVersion"
|
|
||||||
configTokensKey = "nylas.accountTokens"
|
|
||||||
keytarServiceName = 'Nylas'
|
|
||||||
|
|
||||||
###
|
|
||||||
Public: The AccountStore listens to changes to the available accounts in
|
|
||||||
the database and exposes the currently active Account via {::current}
|
|
||||||
|
|
||||||
Section: Stores
|
|
||||||
###
|
|
||||||
class AccountStore extends NylasStore
|
|
||||||
|
|
||||||
constructor: ->
|
|
||||||
@_loadAccounts()
|
|
||||||
@listenTo Actions.removeAccount, @_onRemoveAccount
|
|
||||||
@listenTo Actions.updateAccount, @_onUpdateAccount
|
|
||||||
@listenTo Actions.reorderAccount, @_onReorderAccount
|
|
||||||
|
|
||||||
NylasEnv.config.onDidChange configVersionKey, (change) =>
|
|
||||||
# If we already have this version of the accounts config, it means we
|
|
||||||
# are the ones who saved the change, and we don't need to reload.
|
|
||||||
return if @_version / 1 is change.newValue / 1
|
|
||||||
oldAccountIds = _.pluck(@_accounts, 'id')
|
|
||||||
@_loadAccounts()
|
|
||||||
accountIds = _.pluck(@_accounts, 'id')
|
|
||||||
newAccountIds = _.difference(accountIds, oldAccountIds)
|
|
||||||
|
|
||||||
if newAccountIds.length > 0
|
|
||||||
newId = newAccountIds[0]
|
|
||||||
CategoryStore = require './category-store'
|
|
||||||
CategoryStore.whenCategoriesReady(newId).then =>
|
|
||||||
Actions.focusDefaultMailboxPerspectiveForAccounts([newId], sidebarAccountIds: accountIds)
|
|
||||||
# TODO this Action is a hack, get rid of it in sidebar refactor
|
|
||||||
# Wait until the FocusedPerspectiveStore triggers and the sidebar is
|
|
||||||
# updated to uncollapse the inbox for the new account
|
|
||||||
_.defer =>
|
|
||||||
Actions.setCollapsedSidebarItem('Inbox', false)
|
|
||||||
|
|
||||||
_loadAccounts: =>
|
|
||||||
try
|
|
||||||
@_caches = {}
|
|
||||||
@_tokens = {}
|
|
||||||
@_version = NylasEnv.config.get(configVersionKey) || 0
|
|
||||||
|
|
||||||
@_accounts = []
|
|
||||||
for json in NylasEnv.config.get(configAccountsKey) || []
|
|
||||||
@_accounts.push((new Account).fromJSON(json))
|
|
||||||
|
|
||||||
# Run a few checks on account consistency. We want to display useful error
|
|
||||||
# messages and these can result in very strange exceptions downstream otherwise.
|
|
||||||
@_enforceAccountsValidity()
|
|
||||||
|
|
||||||
# Load tokens using the new keytar method
|
|
||||||
@_tokens = {}
|
|
||||||
for account in @_accounts
|
|
||||||
@_tokens[account.id] = {
|
|
||||||
n1Cloud: keytar.getPassword(keytarServiceName, "#{account.emailAddress}.n1Cloud"),
|
|
||||||
localSync: keytar.getPassword(keytarServiceName, "#{account.emailAddress}.localSync"),
|
|
||||||
}
|
|
||||||
catch error
|
|
||||||
NylasEnv.reportError(error)
|
|
||||||
|
|
||||||
@_trigger()
|
|
||||||
|
|
||||||
_enforceAccountsValidity: =>
|
|
||||||
seenIds = {}
|
|
||||||
seenEmails = {}
|
|
||||||
message = null
|
|
||||||
|
|
||||||
@_accounts = @_accounts.filter (account) =>
|
|
||||||
if not account.emailAddress
|
|
||||||
message = "Assertion failure: One of the accounts in config.json did not have an emailAddress, and was removed. You should re-link the account."
|
|
||||||
return false
|
|
||||||
if seenIds[account.id]
|
|
||||||
message = "Assertion failure: Two of the accounts in config.json had the same ID and one was removed. Please give each account a separate ID."
|
|
||||||
return false
|
|
||||||
if seenEmails[account.emailAddress]
|
|
||||||
message = "Assertion failure: Two of the accounts in config.json had the same email address and one was removed."
|
|
||||||
return false
|
|
||||||
|
|
||||||
seenIds[account.id] = true
|
|
||||||
seenEmails[account.emailAddress] = true
|
|
||||||
return true
|
|
||||||
|
|
||||||
if message and NylasEnv.isMainWindow()
|
|
||||||
NylasEnv.showErrorDialog("N1 was unable to load your account preferences.\n\n#{message}");
|
|
||||||
|
|
||||||
_trigger: ->
|
|
||||||
for account in @_accounts
|
|
||||||
if not account?.id
|
|
||||||
err = new Error("An invalid account was added to `this._accounts`")
|
|
||||||
NylasEnv.reportError(err)
|
|
||||||
@_accounts = _.compact(@_accounts)
|
|
||||||
@trigger()
|
|
||||||
|
|
||||||
_save: =>
|
|
||||||
@_version += 1
|
|
||||||
NylasEnv.config.set(configTokensKey, null)
|
|
||||||
NylasEnv.config.set(configAccountsKey, @_accounts)
|
|
||||||
NylasEnv.config.set(configVersionKey, @_version)
|
|
||||||
@_trigger()
|
|
||||||
|
|
||||||
# Inbound Events
|
|
||||||
|
|
||||||
_onUpdateAccount: (id, updated) =>
|
|
||||||
idx = _.findIndex @_accounts, (a) -> a.id is id
|
|
||||||
account = @_accounts[idx]
|
|
||||||
return unless account
|
|
||||||
account = _.extend(account, updated)
|
|
||||||
@_caches = {}
|
|
||||||
@_accounts[idx] = account
|
|
||||||
@_save()
|
|
||||||
|
|
||||||
_onRemoveAccount: (id) =>
|
|
||||||
account = _.findWhere(@_accounts, {id})
|
|
||||||
return unless account
|
|
||||||
keytar.deletePassword(keytarServiceName, account.emailAddress)
|
|
||||||
|
|
||||||
@_caches = {}
|
|
||||||
|
|
||||||
remainingAccounts = _.without(@_accounts, account)
|
|
||||||
# This action is called before saving because we need to unfocus the
|
|
||||||
# perspective of the account that is being removed before removing the
|
|
||||||
# account, otherwise when we trigger with the new set of accounts, the
|
|
||||||
# current perspective will still reference a stale accountId which will
|
|
||||||
# cause things to break
|
|
||||||
Actions.focusDefaultMailboxPerspectiveForAccounts(remainingAccounts)
|
|
||||||
_.defer =>
|
|
||||||
Actions.setCollapsedSidebarItem('Inbox', true)
|
|
||||||
|
|
||||||
@_accounts = remainingAccounts
|
|
||||||
@_save()
|
|
||||||
|
|
||||||
if remainingAccounts.length is 0
|
|
||||||
ipc = require('electron').ipcRenderer
|
|
||||||
ipc.send('command', 'application:relaunch-to-initial-windows', {
|
|
||||||
resetDatabase: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
_onReorderAccount: (id, newIdx) =>
|
|
||||||
existingIdx = _.findIndex @_accounts, (a) -> a.id is id
|
|
||||||
return if existingIdx is -1
|
|
||||||
account = @_accounts[existingIdx]
|
|
||||||
@_caches = {}
|
|
||||||
@_accounts.splice(existingIdx, 1)
|
|
||||||
@_accounts.splice(newIdx, 0, account)
|
|
||||||
@_save()
|
|
||||||
|
|
||||||
addAccountFromJSON: (json, localToken, cloudToken) =>
|
|
||||||
if not json.email_address or not json.provider
|
|
||||||
console.error("Returned account data is invalid", json)
|
|
||||||
console.log JSON.stringify(json)
|
|
||||||
throw new Error("Returned account data is invalid")
|
|
||||||
|
|
||||||
@_loadAccounts()
|
|
||||||
|
|
||||||
@_tokens[json.id] =
|
|
||||||
n1Cloud: cloudToken
|
|
||||||
localSync: localToken
|
|
||||||
keytar.replacePassword(keytarServiceName, "#{json.email_address}.n1Cloud", cloudToken)
|
|
||||||
keytar.replacePassword(keytarServiceName, "#{json.email_address}.localSync", localToken)
|
|
||||||
|
|
||||||
existingIdx = _.findIndex @_accounts, (a) ->
|
|
||||||
a.id is json.id or a.emailAddress is json.email_address
|
|
||||||
|
|
||||||
if existingIdx is -1
|
|
||||||
account = (new Account).fromJSON(json)
|
|
||||||
@_accounts.push(account)
|
|
||||||
else
|
|
||||||
account = @_accounts[existingIdx]
|
|
||||||
account.fromJSON(json)
|
|
||||||
|
|
||||||
@_save()
|
|
||||||
|
|
||||||
refreshHealthOfAccounts: (accountIds) =>
|
|
||||||
NylasAPI ?= require('../nylas-api').default
|
|
||||||
NylasAPIRequest ?= require('../nylas-api-request').default
|
|
||||||
Promise.all(accountIds.map (accountId) =>
|
|
||||||
return new NylasAPIRequest({
|
|
||||||
api: NylasAPI,
|
|
||||||
options: {
|
|
||||||
path: '/account',
|
|
||||||
accountId: accountId,
|
|
||||||
},
|
|
||||||
}).run().then (json) =>
|
|
||||||
return unless json
|
|
||||||
existing = @accountForId(accountId)
|
|
||||||
return unless existing # user may have deleted
|
|
||||||
existing.fromJSON(json)
|
|
||||||
).finally =>
|
|
||||||
@_caches = {}
|
|
||||||
@_save()
|
|
||||||
|
|
||||||
# Exposed Data
|
|
||||||
|
|
||||||
# Private: Helper which caches the results of getter functions often needed
|
|
||||||
# in the middle of React render cycles. (Performance critical!)
|
|
||||||
|
|
||||||
_cachedGetter: (key, fn) ->
|
|
||||||
@_caches[key] ?= fn()
|
|
||||||
@_caches[key]
|
|
||||||
|
|
||||||
# Public: Returns an {Array} of {Account} objects
|
|
||||||
accounts: =>
|
|
||||||
@_accounts
|
|
||||||
|
|
||||||
accountIds: =>
|
|
||||||
_.pluck(@_accounts, 'id')
|
|
||||||
|
|
||||||
accountsForItems: (items) =>
|
|
||||||
accounts = {}
|
|
||||||
items.forEach ({accountId}) =>
|
|
||||||
accounts[accountId] ?= @accountForId(accountId)
|
|
||||||
_.values(accounts)
|
|
||||||
|
|
||||||
accountForItems: (items) =>
|
|
||||||
accounts = @accountsForItems(items)
|
|
||||||
return null if accounts.length > 1
|
|
||||||
return accounts[0]
|
|
||||||
|
|
||||||
# Public: Returns the {Account} for the given email address, or null.
|
|
||||||
accountForEmail: (email) =>
|
|
||||||
for account in @accounts()
|
|
||||||
if Utils.emailIsEquivalent(email, account.emailAddress)
|
|
||||||
return account
|
|
||||||
for alias in @aliases()
|
|
||||||
if Utils.emailIsEquivalent(email, alias.email)
|
|
||||||
return @accountForId(alias.accountId)
|
|
||||||
return null
|
|
||||||
|
|
||||||
# Public: Returns the {Account} for the given account id, or null.
|
|
||||||
accountForId: (id) =>
|
|
||||||
@_cachedGetter "accountForId:#{id}", => _.findWhere(@_accounts, {id})
|
|
||||||
|
|
||||||
emailAddresses: ->
|
|
||||||
addresses = _.pluck((@accounts() ? []), "emailAddress")
|
|
||||||
addresses = addresses.concat(_.pluck((@aliases() ? []), "email"))
|
|
||||||
return _.unique(addresses)
|
|
||||||
|
|
||||||
aliases: =>
|
|
||||||
@_cachedGetter "aliases", =>
|
|
||||||
aliases = []
|
|
||||||
for acc in @_accounts
|
|
||||||
aliases.push(acc.me())
|
|
||||||
for alias in acc.aliases
|
|
||||||
aliasContact = acc.meUsingAlias(alias)
|
|
||||||
aliasContact.isAlias = true
|
|
||||||
aliases.push(aliasContact)
|
|
||||||
return aliases
|
|
||||||
|
|
||||||
aliasesFor: (accountsOrIds) =>
|
|
||||||
ids = accountsOrIds.map (accOrId) ->
|
|
||||||
if accOrId instanceof Account then accOrId.id else accOrId
|
|
||||||
@aliases().filter((contact) -> contact.accountId in ids)
|
|
||||||
|
|
||||||
# Public: Returns the currently active {Account}.
|
|
||||||
current: =>
|
|
||||||
throw new Error("AccountStore.current() has been deprecated.")
|
|
||||||
|
|
||||||
# Private: This method is going away soon, do not rely on it.
|
|
||||||
#
|
|
||||||
tokensForAccountId: (id) =>
|
|
||||||
@_tokens[id]
|
|
||||||
|
|
||||||
# Private: Load fake data from a directory for taking nice screenshots
|
|
||||||
#
|
|
||||||
_importFakeData: (dir) =>
|
|
||||||
fs = require 'fs-plus'
|
|
||||||
path = require 'path'
|
|
||||||
Message = require('../models/message').default
|
|
||||||
Account = require('../models/account').default
|
|
||||||
Thread = require('../models/thread').default
|
|
||||||
Label = require('../models/label').default
|
|
||||||
|
|
||||||
@_caches = {}
|
|
||||||
|
|
||||||
labels = []
|
|
||||||
threads = []
|
|
||||||
messages = []
|
|
||||||
|
|
||||||
account = @accountForEmail('nora@nylas.com')
|
|
||||||
unless account
|
|
||||||
account = new Account()
|
|
||||||
account.serverId = account.clientId
|
|
||||||
account.emailAddress = "nora@nylas.com"
|
|
||||||
account.organizationUnit = 'label'
|
|
||||||
account.label = "Nora's Email"
|
|
||||||
account.aliases = []
|
|
||||||
account.name = "Nora"
|
|
||||||
account.provider = "gmail"
|
|
||||||
json = account.toJSON()
|
|
||||||
json.token = 'nope'
|
|
||||||
@addAccountFromJSON(json)
|
|
||||||
|
|
||||||
filenames = fs.readdirSync(path.join(dir, 'threads'))
|
|
||||||
for filename in filenames
|
|
||||||
threadJSON = fs.readFileSync(path.join(dir, 'threads', filename))
|
|
||||||
threadMessages = JSON.parse(threadJSON).map (j) -> (new Message).fromJSON(j)
|
|
||||||
threadLabels = []
|
|
||||||
threadParticipants = []
|
|
||||||
threadAttachment = false
|
|
||||||
threadUnread = false
|
|
||||||
|
|
||||||
for m in threadMessages
|
|
||||||
m.accountId = account.id
|
|
||||||
for l in m.categories
|
|
||||||
l.accountId = account.id
|
|
||||||
for l in m.files
|
|
||||||
l.accountId = account.id
|
|
||||||
threadParticipants = threadParticipants.concat(m.participants())
|
|
||||||
threadLabels = threadLabels.concat(m.categories)
|
|
||||||
threadAttachment ||= m.files.length > 0
|
|
||||||
threadUnread ||= m.unread
|
|
||||||
|
|
||||||
threadParticipants = _.uniq threadParticipants, (p) -> p.email
|
|
||||||
threadLabels = _.uniq threadLabels, (l) -> l.id
|
|
||||||
labels = _.uniq labels.concat(threadLabels), (l) -> l.id
|
|
||||||
|
|
||||||
lastMsg = _.last(threadMessages)
|
|
||||||
thread = new Thread(
|
|
||||||
accountId: account.id
|
|
||||||
serverId: lastMsg.threadId
|
|
||||||
clientId: lastMsg.threadId
|
|
||||||
subject: lastMsg.subject
|
|
||||||
lastMessageReceivedTimestamp: lastMsg.date
|
|
||||||
hasAttachment: threadAttachment
|
|
||||||
categories: threadLabels
|
|
||||||
participants: threadParticipants
|
|
||||||
unread: threadUnread
|
|
||||||
snippet: lastMsg.snippet
|
|
||||||
starred: lastMsg.starred
|
|
||||||
)
|
|
||||||
messages = messages.concat(threadMessages)
|
|
||||||
threads.push(thread)
|
|
||||||
|
|
||||||
downloadsDir = path.join(dir, 'downloads')
|
|
||||||
if fs.existsSync(downloadsDir)
|
|
||||||
for filename in fs.readdirSync(downloadsDir)
|
|
||||||
destPath = path.join(NylasEnv.getConfigDirPath(), 'downloads', filename)
|
|
||||||
fs.removeSync(destPath) if fs.existsSync(destPath)
|
|
||||||
fs.copySync(path.join(downloadsDir, filename), destPath)
|
|
||||||
|
|
||||||
DatabaseStore.inTransaction (t) =>
|
|
||||||
Promise.all([
|
|
||||||
t.persistModel(account),
|
|
||||||
t.persistModels(labels),
|
|
||||||
t.persistModels(messages),
|
|
||||||
t.persistModels(threads)
|
|
||||||
])
|
|
||||||
.then =>
|
|
||||||
Actions.focusDefaultMailboxPerspectiveForAccounts([account.id])
|
|
||||||
.then -> new Promise (resolve, reject) -> setTimeout(resolve, 1000)
|
|
||||||
|
|
||||||
module.exports = new AccountStore()
|
|
434
src/flux/stores/account-store.es6
Normal file
434
src/flux/stores/account-store.es6
Normal file
|
@ -0,0 +1,434 @@
|
||||||
|
/* eslint global-require: 0 */
|
||||||
|
|
||||||
|
import _ from 'underscore'
|
||||||
|
import keytar from 'keytar'
|
||||||
|
|
||||||
|
import NylasStore from 'nylas-store'
|
||||||
|
import Actions from '../actions'
|
||||||
|
import Account from '../models/account'
|
||||||
|
import Utils from '../models/utils'
|
||||||
|
import DatabaseStore from './database-store'
|
||||||
|
|
||||||
|
let NylasAPI = null
|
||||||
|
let NylasAPIRequest = null
|
||||||
|
|
||||||
|
const configAccountsKey = "nylas.accounts"
|
||||||
|
const configVersionKey = "nylas.accountsVersion"
|
||||||
|
const configTokensKey = "nylas.accountTokens"
|
||||||
|
const keytarServiceName = 'Nylas'
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Public: The AccountStore listens to changes to the available accounts in
|
||||||
|
the database and exposes the currently active Account via {::current}
|
||||||
|
|
||||||
|
Section: Stores
|
||||||
|
*/
|
||||||
|
class AccountStore extends NylasStore {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this._loadAccounts()
|
||||||
|
this.listenTo(Actions.removeAccount, this._onRemoveAccount)
|
||||||
|
this.listenTo(Actions.updateAccount, this._onUpdateAccount)
|
||||||
|
this.listenTo(Actions.reorderAccount, this._onReorderAccount)
|
||||||
|
|
||||||
|
NylasEnv.config.onDidChange(configVersionKey, (change) => {
|
||||||
|
// If we already have this version of the accounts config, it means we
|
||||||
|
// are the ones who saved the change, and we don't need to reload.
|
||||||
|
if (this._version / 1 === change.newValue / 1) return
|
||||||
|
const oldAccountIds = _.pluck(this._accounts, 'id')
|
||||||
|
this._loadAccounts()
|
||||||
|
const accountIds = _.pluck(this._accounts, 'id')
|
||||||
|
const newAccountIds = _.difference(accountIds, oldAccountIds)
|
||||||
|
|
||||||
|
if (newAccountIds.length > 0) {
|
||||||
|
const newId = newAccountIds[0]
|
||||||
|
const CategoryStore = require('./category-store')
|
||||||
|
CategoryStore.whenCategoriesReady(newId).then(() => {
|
||||||
|
Actions.focusDefaultMailboxPerspectiveForAccounts([newId], {sidebarAccountIds: accountIds})
|
||||||
|
// TODO: This Action is a hack, get rid of it in sidebar refactor
|
||||||
|
// Wait until the FocusedPerspectiveStore triggers and the sidebar is
|
||||||
|
// updated to uncollapse the inbox for the new account
|
||||||
|
_.defer(() => {
|
||||||
|
Actions.setCollapsedSidebarItem('Inbox', false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadAccounts = () => {
|
||||||
|
try {
|
||||||
|
this._caches = {}
|
||||||
|
this._tokens = {}
|
||||||
|
this._version = NylasEnv.config.get(configVersionKey) || 0
|
||||||
|
|
||||||
|
this._accounts = []
|
||||||
|
for (const json of NylasEnv.config.get(configAccountsKey) || []) {
|
||||||
|
this._accounts.push((new Account()).fromJSON(json))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run a few checks on account consistency. We want to display useful error
|
||||||
|
// messages and these can result in very strange exceptions downstream otherwise.
|
||||||
|
this._enforceAccountsValidity()
|
||||||
|
|
||||||
|
// Load tokens using the new keytar method
|
||||||
|
this._tokens = {}
|
||||||
|
for (const account of this._accounts) {
|
||||||
|
this._tokens[account.id] = {
|
||||||
|
n1Cloud: keytar.getPassword(keytarServiceName, `${account.emailAddress}.n1Cloud`),
|
||||||
|
localSync: keytar.getPassword(keytarServiceName, `${account.emailAddress}.localSync`),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
NylasEnv.reportError(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
this._trigger()
|
||||||
|
}
|
||||||
|
|
||||||
|
_enforceAccountsValidity = () => {
|
||||||
|
const seenIds = {}
|
||||||
|
const seenEmails = {}
|
||||||
|
let message = null
|
||||||
|
|
||||||
|
this._accounts = this._accounts.filter((account) => {
|
||||||
|
if (!account.emailAddress) {
|
||||||
|
message = "Assertion failure: One of the accounts in config.json did not have an emailAddress, and was removed. You should re-link the account."
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (seenIds[account.id]) {
|
||||||
|
message = "Assertion failure: Two of the accounts in config.json had the same ID and one was removed. Please give each account a separate ID."
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (seenEmails[account.emailAddress]) {
|
||||||
|
message = "Assertion failure: Two of the accounts in config.json had the same email address and one was removed."
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
seenIds[account.id] = true
|
||||||
|
seenEmails[account.emailAddress] = true
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
if (message && NylasEnv.isMainWindow()) {
|
||||||
|
NylasEnv.showErrorDialog(`N1 was unable to load your account preferences.\n\n${message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_trigger() {
|
||||||
|
for (const account of this._accounts) {
|
||||||
|
if (!account || !account.id) {
|
||||||
|
const err = new Error("An invalid account was added to `this._accounts`")
|
||||||
|
NylasEnv.reportError(err)
|
||||||
|
this._accounts = _.compact(this._accounts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.trigger()
|
||||||
|
}
|
||||||
|
|
||||||
|
_save = () => {
|
||||||
|
this._version += 1
|
||||||
|
NylasEnv.config.set(configTokensKey, null)
|
||||||
|
NylasEnv.config.set(configAccountsKey, this._accounts)
|
||||||
|
NylasEnv.config.set(configVersionKey, this._version)
|
||||||
|
this._trigger()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inbound Events
|
||||||
|
|
||||||
|
_onUpdateAccount = (id, updated) => {
|
||||||
|
const idx = _.findIndex(this._accounts, (a) => a.id === id)
|
||||||
|
let account = this._accounts[idx]
|
||||||
|
if (!account) return
|
||||||
|
account = _.extend(account, updated)
|
||||||
|
this._caches = {}
|
||||||
|
this._accounts[idx] = account
|
||||||
|
this._save()
|
||||||
|
}
|
||||||
|
|
||||||
|
_onRemoveAccount = (id) => {
|
||||||
|
const account = _.findWhere(this._accounts, {id})
|
||||||
|
if (!account) return
|
||||||
|
keytar.deletePassword(keytarServiceName, account.emailAddress)
|
||||||
|
|
||||||
|
this._caches = {}
|
||||||
|
|
||||||
|
const remainingAccounts = _.without(this._accounts, account)
|
||||||
|
// This action is called before saving because we need to unfocus the
|
||||||
|
// perspective of the account that is being removed before removing the
|
||||||
|
// account, otherwise when we trigger with the new set of accounts, the
|
||||||
|
// current perspective will still reference a stale accountId which will
|
||||||
|
// cause things to break
|
||||||
|
Actions.focusDefaultMailboxPerspectiveForAccounts(remainingAccounts)
|
||||||
|
_.defer(() => {
|
||||||
|
Actions.setCollapsedSidebarItem('Inbox', true)
|
||||||
|
})
|
||||||
|
|
||||||
|
this._accounts = remainingAccounts
|
||||||
|
this._save()
|
||||||
|
|
||||||
|
if (remainingAccounts.length === 0) {
|
||||||
|
const ipc = require('electron').ipcRenderer
|
||||||
|
ipc.send('command', 'application:relaunch-to-initial-windows', {
|
||||||
|
resetDatabase: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onReorderAccount = (id, newIdx) => {
|
||||||
|
const existingIdx = _.findIndex(this._accounts, (a) => a.id === id)
|
||||||
|
if (existingIdx === -1) return
|
||||||
|
const account = this._accounts[existingIdx]
|
||||||
|
this._caches = {}
|
||||||
|
this._accounts.splice(existingIdx, 1)
|
||||||
|
this._accounts.splice(newIdx, 0, account)
|
||||||
|
this._save()
|
||||||
|
}
|
||||||
|
|
||||||
|
addAccountFromJSON = (json, localToken, cloudToken) => {
|
||||||
|
if (!json.email_address || !json.provider) {
|
||||||
|
console.error("Returned account data is invalid", json)
|
||||||
|
console.log(JSON.stringify(json))
|
||||||
|
throw new Error("Returned account data is invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
this._loadAccounts()
|
||||||
|
|
||||||
|
this._tokens[json.id] = {
|
||||||
|
n1Cloud: cloudToken,
|
||||||
|
localSync: localToken,
|
||||||
|
}
|
||||||
|
keytar.replacePassword(keytarServiceName, `${json.email_address}.n1Cloud`, cloudToken)
|
||||||
|
keytar.replacePassword(keytarServiceName, `${json.email_address}.localSync`, localToken)
|
||||||
|
|
||||||
|
const existingIdx = _.findIndex(this._accounts, (a) =>
|
||||||
|
a.id === json.id || a.emailAddress === json.email_address
|
||||||
|
)
|
||||||
|
|
||||||
|
if (existingIdx === -1) {
|
||||||
|
const account = (new Account()).fromJSON(json)
|
||||||
|
this._accounts.push(account)
|
||||||
|
} else {
|
||||||
|
const account = this._accounts[existingIdx]
|
||||||
|
account.fromJSON(json)
|
||||||
|
}
|
||||||
|
|
||||||
|
this._save()
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshHealthOfAccounts = (accountIds) => {
|
||||||
|
NylasAPI = require('../nylas-api').default
|
||||||
|
NylasAPIRequest = require('../nylas-api-request').default
|
||||||
|
Promise.all(accountIds.map((accountId) => {
|
||||||
|
return new NylasAPIRequest({
|
||||||
|
api: NylasAPI,
|
||||||
|
options: {
|
||||||
|
path: '/account',
|
||||||
|
accountId: accountId,
|
||||||
|
},
|
||||||
|
}).run().then((json) => {
|
||||||
|
if (!json) return
|
||||||
|
const existing = this.accountForId(accountId)
|
||||||
|
if (!existing) return // user may have deleted
|
||||||
|
existing.fromJSON(json)
|
||||||
|
})
|
||||||
|
})).finally(() => {
|
||||||
|
this._caches = {}
|
||||||
|
this._save()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exposed Data
|
||||||
|
|
||||||
|
// Private: Helper which caches the results of getter functions often needed
|
||||||
|
// in the middle of React render cycles. (Performance critical!)
|
||||||
|
|
||||||
|
_cachedGetter(key, fn) {
|
||||||
|
this._caches[key] = this._caches[key] || fn()
|
||||||
|
return this._caches[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public: Returns an {Array} of {Account} objects
|
||||||
|
accounts = () => {
|
||||||
|
return this._accounts
|
||||||
|
}
|
||||||
|
|
||||||
|
accountIds = () => {
|
||||||
|
return _.pluck(this._accounts, 'id')
|
||||||
|
}
|
||||||
|
|
||||||
|
accountsForItems = (items) => {
|
||||||
|
const accounts = {}
|
||||||
|
items.forEach(({accountId}) => {
|
||||||
|
accounts[accountId] = accounts[accountId] || this.accountForId(accountId)
|
||||||
|
})
|
||||||
|
return _.values(accounts)
|
||||||
|
}
|
||||||
|
|
||||||
|
accountForItems = (items) => {
|
||||||
|
const accounts = this.accountsForItems(items)
|
||||||
|
if (accounts.length > 1) return null
|
||||||
|
return accounts[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public: Returns the {Account} for the given email address, or null.
|
||||||
|
accountForEmail = (email) => {
|
||||||
|
for (const account of this.accounts()) {
|
||||||
|
if (Utils.emailIsEquivalent(email, account.emailAddress)) {
|
||||||
|
return account
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const alias of this.aliases()) {
|
||||||
|
if (Utils.emailIsEquivalent(email, alias.email)) {
|
||||||
|
return this.accountForId(alias.accountId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public: Returns the {Account} for the given account id, or null.
|
||||||
|
accountForId(id) {
|
||||||
|
return this._cachedGetter(`accountForId:${id}`, () => _.findWhere(this._accounts, {id}))
|
||||||
|
}
|
||||||
|
|
||||||
|
emailAddresses() {
|
||||||
|
let addresses = _.pluck((this.accounts() ? this.accounts() : []), "emailAddress")
|
||||||
|
addresses = addresses.concat(_.pluck((this.aliases() ? this.aliases() : []), "email"))
|
||||||
|
return _.unique(addresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
aliases() {
|
||||||
|
return this._cachedGetter("aliases", () => {
|
||||||
|
const aliases = []
|
||||||
|
for (const acc of this._accounts) {
|
||||||
|
aliases.push(acc.me())
|
||||||
|
for (const alias of acc.aliases) {
|
||||||
|
const aliasContact = acc.meUsingAlias(alias)
|
||||||
|
aliasContact.isAlias = true
|
||||||
|
aliases.push(aliasContact)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return aliases
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
aliasesFor(accountsOrIds) {
|
||||||
|
const ids = accountsOrIds.map((accOrId) => {
|
||||||
|
return accOrId instanceof Account ? accOrId.id : accOrId
|
||||||
|
})
|
||||||
|
return this.aliases().filter((contact) => ids.includes(contact.accountId))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public: Returns the currently active {Account}.
|
||||||
|
current() {
|
||||||
|
throw new Error("AccountStore.current() has been deprecated.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private: This method is going away soon, do not rely on it.
|
||||||
|
tokensForAccountId(id) {
|
||||||
|
return this._tokens[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private: Load fake data from a directory for taking nice screenshots
|
||||||
|
_importFakeData = (dir) => {
|
||||||
|
const fs = require('fs-plus')
|
||||||
|
const path = require('path')
|
||||||
|
const Message = require('../models/message').default
|
||||||
|
const Thread = require('../models/thread').default
|
||||||
|
|
||||||
|
this._caches = {}
|
||||||
|
|
||||||
|
let labels = []
|
||||||
|
const threads = []
|
||||||
|
let messages = []
|
||||||
|
|
||||||
|
let account = this.accountForEmail('nora@nylas.com')
|
||||||
|
if (!account) {
|
||||||
|
account = new Account()
|
||||||
|
account.serverId = account.clientId
|
||||||
|
account.emailAddress = "nora@nylas.com"
|
||||||
|
account.organizationUnit = 'label'
|
||||||
|
account.label = "Nora's Email"
|
||||||
|
account.aliases = []
|
||||||
|
account.name = "Nora"
|
||||||
|
account.provider = "gmail"
|
||||||
|
const json = account.toJSON()
|
||||||
|
json.token = 'nope'
|
||||||
|
this.addAccountFromJSON(json)
|
||||||
|
}
|
||||||
|
|
||||||
|
const filenames = fs.readdirSync(path.join(dir, 'threads'))
|
||||||
|
for (const filename of filenames) {
|
||||||
|
const threadJSON = fs.readFileSync(path.join(dir, 'threads', filename))
|
||||||
|
const threadMessages = JSON.parse(threadJSON).map((j) => (new Message()).fromJSON(j))
|
||||||
|
let threadLabels = []
|
||||||
|
let threadParticipants = []
|
||||||
|
let threadAttachment = false
|
||||||
|
let threadUnread = false
|
||||||
|
|
||||||
|
for (const m of threadMessages) {
|
||||||
|
m.accountId = account.id
|
||||||
|
for (const l of m.categories) {
|
||||||
|
l.accountId = account.id
|
||||||
|
}
|
||||||
|
for (const l of m.files) {
|
||||||
|
l.accountId = account.id
|
||||||
|
}
|
||||||
|
threadParticipants = threadParticipants.concat(m.participants())
|
||||||
|
threadLabels = threadLabels.concat(m.categories)
|
||||||
|
threadAttachment = threadAttachment || m.files.length > 0
|
||||||
|
threadUnread = threadUnread || m.unread
|
||||||
|
}
|
||||||
|
|
||||||
|
threadParticipants = _.uniq(threadParticipants, (p) => p.email)
|
||||||
|
threadLabels = _.uniq(threadLabels, (l) => l.id)
|
||||||
|
labels = _.uniq(labels.concat(threadLabels), (l) => l.id)
|
||||||
|
|
||||||
|
const lastMsg = _.last(threadMessages)
|
||||||
|
const thread = new Thread({
|
||||||
|
accountId: account.id,
|
||||||
|
serverId: lastMsg.threadId,
|
||||||
|
clientId: lastMsg.threadId,
|
||||||
|
subject: lastMsg.subject,
|
||||||
|
lastMessageReceivedTimestamp: lastMsg.date,
|
||||||
|
hasAttachment: threadAttachment,
|
||||||
|
categories: threadLabels,
|
||||||
|
participants: threadParticipants,
|
||||||
|
unread: threadUnread,
|
||||||
|
snippet: lastMsg.snippet,
|
||||||
|
starred: lastMsg.starred,
|
||||||
|
})
|
||||||
|
messages = messages.concat(threadMessages)
|
||||||
|
threads.push(thread)
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadsDir = path.join(dir, 'downloads')
|
||||||
|
if (fs.existsSync(downloadsDir)) {
|
||||||
|
for (const filename of fs.readdirSync(downloadsDir)) {
|
||||||
|
const destPath = path.join(NylasEnv.getConfigDirPath(), 'downloads', filename)
|
||||||
|
if (fs.existsSync(destPath)) {
|
||||||
|
fs.removeSync(destPath)
|
||||||
|
}
|
||||||
|
fs.copySync(path.join(downloadsDir, filename), destPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseStore.inTransaction((t) =>
|
||||||
|
Promise.all([
|
||||||
|
t.persistModel(account),
|
||||||
|
t.persistModels(labels),
|
||||||
|
t.persistModels(messages),
|
||||||
|
t.persistModels(threads),
|
||||||
|
])
|
||||||
|
).then(() => {
|
||||||
|
Actions.focusDefaultMailboxPerspectiveForAccounts([account.id])
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, 1000))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new AccountStore()
|
|
@ -1,7 +1,7 @@
|
||||||
_ = require 'underscore'
|
_ = require 'underscore'
|
||||||
Rx = require 'rx-lite'
|
Rx = require 'rx-lite'
|
||||||
NylasStore = require 'nylas-store'
|
NylasStore = require 'nylas-store'
|
||||||
AccountStore = require './account-store'
|
AccountStore = require('./account-store').default
|
||||||
NylasSyncStatusStore = require './nylas-sync-status-store'
|
NylasSyncStatusStore = require './nylas-sync-status-store'
|
||||||
Account = require('../models/account').default
|
Account = require('../models/account').default
|
||||||
{StandardCategoryNames} = require('../models/category').default
|
{StandardCategoryNames} = require('../models/category').default
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
Rx = require 'rx-lite'
|
Rx = require 'rx-lite'
|
||||||
NylasStore = require 'nylas-store'
|
NylasStore = require 'nylas-store'
|
||||||
DatabaseStore = require('./database-store').default
|
DatabaseStore = require('./database-store').default
|
||||||
AccountStore = require './account-store'
|
AccountStore = require('./account-store').default
|
||||||
|
|
||||||
class ContactRankingStore extends NylasStore
|
class ContactRankingStore extends NylasStore
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ Utils = require '../models/utils'
|
||||||
NylasStore = require 'nylas-store'
|
NylasStore = require 'nylas-store'
|
||||||
RegExpUtils = require '../../regexp-utils'
|
RegExpUtils = require '../../regexp-utils'
|
||||||
DatabaseStore = require('./database-store').default
|
DatabaseStore = require('./database-store').default
|
||||||
AccountStore = require './account-store'
|
AccountStore = require('./account-store').default
|
||||||
ComponentRegistry = require('../../registries/component-registry')
|
ComponentRegistry = require('../../registries/component-registry')
|
||||||
ContactRankingStore = require './contact-ranking-store'
|
ContactRankingStore = require './contact-ranking-store'
|
||||||
_ = require 'underscore'
|
_ = require 'underscore'
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
Message = require('../models/message').default
|
Message = require('../models/message').default
|
||||||
Actions = require('../actions').default
|
Actions = require('../actions').default
|
||||||
NylasAPI = require '../nylas-api'
|
NylasAPI = require '../nylas-api'
|
||||||
AccountStore = require './account-store'
|
AccountStore = require('./account-store').default
|
||||||
ContactStore = require './contact-store'
|
ContactStore = require './contact-store'
|
||||||
DatabaseStore = require('./database-store').default
|
DatabaseStore = require('./database-store').default
|
||||||
UndoStack = require('../../undo-stack').default
|
UndoStack = require('../../undo-stack').default
|
||||||
|
|
|
@ -2,7 +2,7 @@ _ = require 'underscore'
|
||||||
|
|
||||||
Actions = require('../actions').default
|
Actions = require('../actions').default
|
||||||
DatabaseStore = require('./database-store').default
|
DatabaseStore = require('./database-store').default
|
||||||
AccountStore = require './account-store'
|
AccountStore = require('./account-store').default
|
||||||
ContactStore = require './contact-store'
|
ContactStore = require './contact-store'
|
||||||
MessageStore = require './message-store'
|
MessageStore = require './message-store'
|
||||||
FocusedPerspectiveStore = require('./focused-perspective-store').default
|
FocusedPerspectiveStore = require('./focused-perspective-store').default
|
||||||
|
|
|
@ -7,7 +7,7 @@ NylasStore = require 'nylas-store'
|
||||||
Thread = require('../models/thread').default
|
Thread = require('../models/thread').default
|
||||||
Contact = require('../models/contact').default
|
Contact = require('../models/contact').default
|
||||||
MessageStore = require './message-store'
|
MessageStore = require './message-store'
|
||||||
AccountStore = require './account-store'
|
AccountStore = require('./account-store').default
|
||||||
DatabaseStore = require('./database-store').default
|
DatabaseStore = require('./database-store').default
|
||||||
FocusedContentStore = require './focused-content-store'
|
FocusedContentStore = require './focused-content-store'
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
_ = require 'underscore'
|
_ = require 'underscore'
|
||||||
Reflux = require 'reflux'
|
Reflux = require 'reflux'
|
||||||
AccountStore = require './account-store'
|
AccountStore = require('./account-store').default
|
||||||
WorkspaceStore = require './workspace-store'
|
WorkspaceStore = require './workspace-store'
|
||||||
DatabaseStore = require('./database-store').default
|
DatabaseStore = require('./database-store').default
|
||||||
FocusedPerspectiveStore = require('./focused-perspective-store').default
|
FocusedPerspectiveStore = require('./focused-perspective-store').default
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
NylasStore = require 'nylas-store'
|
NylasStore = require 'nylas-store'
|
||||||
_ = require 'underscore'
|
_ = require 'underscore'
|
||||||
Rx = require 'rx-lite'
|
Rx = require 'rx-lite'
|
||||||
AccountStore = require './account-store'
|
AccountStore = require('./account-store').default
|
||||||
DatabaseStore = require('./database-store').default
|
DatabaseStore = require('./database-store').default
|
||||||
TaskQueueStatusStore = require './task-queue-status-store'
|
TaskQueueStatusStore = require './task-queue-status-store'
|
||||||
ReprocessMailRulesTask = require('../tasks/reprocess-mail-rules-task').default
|
ReprocessMailRulesTask = require('../tasks/reprocess-mail-rules-task').default
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
_ = require 'underscore'
|
_ = require 'underscore'
|
||||||
Rx = require 'rx-lite'
|
Rx = require 'rx-lite'
|
||||||
AccountStore = require './account-store'
|
AccountStore = require('./account-store').default
|
||||||
DatabaseStore = require('./database-store').default
|
DatabaseStore = require('./database-store').default
|
||||||
NylasStore = require 'nylas-store'
|
NylasStore = require 'nylas-store'
|
||||||
NylasObservables = require 'nylas-observables'
|
NylasObservables = require 'nylas-observables'
|
||||||
|
|
|
@ -2,7 +2,7 @@ _ = require 'underscore'
|
||||||
Rx = require 'rx-lite'
|
Rx = require 'rx-lite'
|
||||||
NylasStore = require 'nylas-store'
|
NylasStore = require 'nylas-store'
|
||||||
DatabaseStore = require('./database-store').default
|
DatabaseStore = require('./database-store').default
|
||||||
AccountStore = require './account-store'
|
AccountStore = require('./account-store').default
|
||||||
TaskQueue = require './task-queue'
|
TaskQueue = require './task-queue'
|
||||||
|
|
||||||
# Public: The TaskQueueStatusStore allows you to inspect the task queue from
|
# Public: The TaskQueueStatusStore allows you to inspect the task queue from
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
_ = require 'underscore'
|
_ = require 'underscore'
|
||||||
Actions = require('../actions').default
|
Actions = require('../actions').default
|
||||||
AccountStore = require './account-store'
|
AccountStore = require('./account-store').default
|
||||||
CategoryStore = require './category-store'
|
CategoryStore = require './category-store'
|
||||||
MailboxPerspective = require '../../mailbox-perspective'
|
MailboxPerspective = require '../../mailbox-perspective'
|
||||||
NylasStore = require 'nylas-store'
|
NylasStore = require 'nylas-store'
|
||||||
|
|
|
@ -2,7 +2,7 @@ _ = require 'underscore'
|
||||||
|
|
||||||
Utils = require './flux/models/utils'
|
Utils = require './flux/models/utils'
|
||||||
TaskFactory = require('./flux/tasks/task-factory').default
|
TaskFactory = require('./flux/tasks/task-factory').default
|
||||||
AccountStore = require './flux/stores/account-store'
|
AccountStore = require('./flux/stores/account-store').default
|
||||||
CategoryStore = require './flux/stores/category-store'
|
CategoryStore = require './flux/stores/category-store'
|
||||||
DatabaseStore = require('./flux/stores/database-store').default
|
DatabaseStore = require('./flux/stores/database-store').default
|
||||||
OutboxStore = require('./flux/stores/outbox-store').default
|
OutboxStore = require('./flux/stores/outbox-store').default
|
||||||
|
|
Loading…
Reference in a new issue