From bd30adec1592628c7d38c3f4faf250d41d6fdcc9 Mon Sep 17 00:00:00 2001 From: Jackie Luo Date: Fri, 2 Dec 2016 11:00:12 -0800 Subject: [PATCH] :coffee:(account-store): Convert to ES6 --- src/flux/models/category.es6 | 2 +- src/flux/nylas-api.es6 | 10 +- src/flux/stores/account-store.coffee | 365 --------------- src/flux/stores/account-store.es6 | 434 ++++++++++++++++++ src/flux/stores/category-store.coffee | 2 +- src/flux/stores/contact-ranking-store.coffee | 2 +- src/flux/stores/contact-store.coffee | 2 +- src/flux/stores/draft-editing-session.coffee | 2 +- src/flux/stores/draft-factory.coffee | 2 +- src/flux/stores/focused-contacts-store.coffee | 2 +- src/flux/stores/focused-content-store.coffee | 2 +- src/flux/stores/mail-rules-store.coffee | 2 +- .../stores/nylas-sync-status-store.coffee | 2 +- .../stores/task-queue-status-store.coffee | 2 +- src/flux/stores/workspace-store.coffee | 2 +- src/mailbox-perspective.coffee | 2 +- 16 files changed, 453 insertions(+), 382 deletions(-) delete mode 100644 src/flux/stores/account-store.coffee create mode 100644 src/flux/stores/account-store.es6 diff --git a/src/flux/models/category.es6 b/src/flux/models/category.es6 index e4fd847bd..d5122bfee 100644 --- a/src/flux/models/category.es6 +++ b/src/flux/models/category.es6 @@ -116,7 +116,7 @@ export default class Category extends Model { } displayType = () => { - AccountStore = AccountStore || require('../stores/account-store'); + AccountStore = AccountStore || require('../stores/account-store').default; if (AccountStore.accountForId(this.accountId).usesLabels()) { return 'label'; } diff --git a/src/flux/nylas-api.es6 b/src/flux/nylas-api.es6 index 93dcedaa7..7971e039e 100644 --- a/src/flux/nylas-api.es6 +++ b/src/flux/nylas-api.es6 @@ -127,9 +127,11 @@ class NylasAPI { handleAuthenticationFailure = (modelUrl, apiToken, apiName) => { // 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 localMatch = AccountStore.tokensForAccountId(acc.id).localSync === apiToken; const cloudMatch = AccountStore.tokensForAccountId(acc.id).n1Cloud === apiToken; @@ -319,7 +321,7 @@ class NylasAPI { } accessTokenForAccountId = (aid) => { - AccountStore = AccountStore || require('./stores/account-store') + AccountStore = AccountStore || require('./stores/account-store').default return AccountStore.tokensForAccountId(aid).localSync } @@ -369,7 +371,7 @@ class NylasAPI { let account = accountOrId if (!(accountOrId instanceof Account)) { - AccountStore = AccountStore || require('./stores/account-store') + AccountStore = AccountStore || require('./stores/account-store').default account = AccountStore.accountForId(accountOrId) } diff --git a/src/flux/stores/account-store.coffee b/src/flux/stores/account-store.coffee deleted file mode 100644 index 19d9577b7..000000000 --- a/src/flux/stores/account-store.coffee +++ /dev/null @@ -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() diff --git a/src/flux/stores/account-store.es6 b/src/flux/stores/account-store.es6 new file mode 100644 index 000000000..73c57ff2b --- /dev/null +++ b/src/flux/stores/account-store.es6 @@ -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() diff --git a/src/flux/stores/category-store.coffee b/src/flux/stores/category-store.coffee index 9e1e791b1..1307dadb4 100644 --- a/src/flux/stores/category-store.coffee +++ b/src/flux/stores/category-store.coffee @@ -1,7 +1,7 @@ _ = require 'underscore' Rx = require 'rx-lite' NylasStore = require 'nylas-store' -AccountStore = require './account-store' +AccountStore = require('./account-store').default NylasSyncStatusStore = require './nylas-sync-status-store' Account = require('../models/account').default {StandardCategoryNames} = require('../models/category').default diff --git a/src/flux/stores/contact-ranking-store.coffee b/src/flux/stores/contact-ranking-store.coffee index 7a8bda9b3..771a00938 100644 --- a/src/flux/stores/contact-ranking-store.coffee +++ b/src/flux/stores/contact-ranking-store.coffee @@ -1,7 +1,7 @@ Rx = require 'rx-lite' NylasStore = require 'nylas-store' DatabaseStore = require('./database-store').default -AccountStore = require './account-store' +AccountStore = require('./account-store').default class ContactRankingStore extends NylasStore diff --git a/src/flux/stores/contact-store.coffee b/src/flux/stores/contact-store.coffee index 721f8526a..5e13f5e7f 100644 --- a/src/flux/stores/contact-store.coffee +++ b/src/flux/stores/contact-store.coffee @@ -8,7 +8,7 @@ Utils = require '../models/utils' NylasStore = require 'nylas-store' RegExpUtils = require '../../regexp-utils' DatabaseStore = require('./database-store').default -AccountStore = require './account-store' +AccountStore = require('./account-store').default ComponentRegistry = require('../../registries/component-registry') ContactRankingStore = require './contact-ranking-store' _ = require 'underscore' diff --git a/src/flux/stores/draft-editing-session.coffee b/src/flux/stores/draft-editing-session.coffee index c4a5becc3..473577124 100644 --- a/src/flux/stores/draft-editing-session.coffee +++ b/src/flux/stores/draft-editing-session.coffee @@ -1,7 +1,7 @@ Message = require('../models/message').default Actions = require('../actions').default NylasAPI = require '../nylas-api' -AccountStore = require './account-store' +AccountStore = require('./account-store').default ContactStore = require './contact-store' DatabaseStore = require('./database-store').default UndoStack = require('../../undo-stack').default diff --git a/src/flux/stores/draft-factory.coffee b/src/flux/stores/draft-factory.coffee index 2ee518a15..ae3c7ce46 100644 --- a/src/flux/stores/draft-factory.coffee +++ b/src/flux/stores/draft-factory.coffee @@ -2,7 +2,7 @@ _ = require 'underscore' Actions = require('../actions').default DatabaseStore = require('./database-store').default -AccountStore = require './account-store' +AccountStore = require('./account-store').default ContactStore = require './contact-store' MessageStore = require './message-store' FocusedPerspectiveStore = require('./focused-perspective-store').default diff --git a/src/flux/stores/focused-contacts-store.coffee b/src/flux/stores/focused-contacts-store.coffee index 68e260043..83ac111cc 100644 --- a/src/flux/stores/focused-contacts-store.coffee +++ b/src/flux/stores/focused-contacts-store.coffee @@ -7,7 +7,7 @@ NylasStore = require 'nylas-store' Thread = require('../models/thread').default Contact = require('../models/contact').default MessageStore = require './message-store' -AccountStore = require './account-store' +AccountStore = require('./account-store').default DatabaseStore = require('./database-store').default FocusedContentStore = require './focused-content-store' diff --git a/src/flux/stores/focused-content-store.coffee b/src/flux/stores/focused-content-store.coffee index fa7abe8cf..a69fa4d6f 100644 --- a/src/flux/stores/focused-content-store.coffee +++ b/src/flux/stores/focused-content-store.coffee @@ -1,6 +1,6 @@ _ = require 'underscore' Reflux = require 'reflux' -AccountStore = require './account-store' +AccountStore = require('./account-store').default WorkspaceStore = require './workspace-store' DatabaseStore = require('./database-store').default FocusedPerspectiveStore = require('./focused-perspective-store').default diff --git a/src/flux/stores/mail-rules-store.coffee b/src/flux/stores/mail-rules-store.coffee index 8b26cb61d..cf62d4521 100644 --- a/src/flux/stores/mail-rules-store.coffee +++ b/src/flux/stores/mail-rules-store.coffee @@ -1,7 +1,7 @@ NylasStore = require 'nylas-store' _ = require 'underscore' Rx = require 'rx-lite' -AccountStore = require './account-store' +AccountStore = require('./account-store').default DatabaseStore = require('./database-store').default TaskQueueStatusStore = require './task-queue-status-store' ReprocessMailRulesTask = require('../tasks/reprocess-mail-rules-task').default diff --git a/src/flux/stores/nylas-sync-status-store.coffee b/src/flux/stores/nylas-sync-status-store.coffee index ee5ebf795..961f0bd32 100644 --- a/src/flux/stores/nylas-sync-status-store.coffee +++ b/src/flux/stores/nylas-sync-status-store.coffee @@ -1,6 +1,6 @@ _ = require 'underscore' Rx = require 'rx-lite' -AccountStore = require './account-store' +AccountStore = require('./account-store').default DatabaseStore = require('./database-store').default NylasStore = require 'nylas-store' NylasObservables = require 'nylas-observables' diff --git a/src/flux/stores/task-queue-status-store.coffee b/src/flux/stores/task-queue-status-store.coffee index b2586f777..a4e5dd4c6 100644 --- a/src/flux/stores/task-queue-status-store.coffee +++ b/src/flux/stores/task-queue-status-store.coffee @@ -2,7 +2,7 @@ _ = require 'underscore' Rx = require 'rx-lite' NylasStore = require 'nylas-store' DatabaseStore = require('./database-store').default -AccountStore = require './account-store' +AccountStore = require('./account-store').default TaskQueue = require './task-queue' # Public: The TaskQueueStatusStore allows you to inspect the task queue from diff --git a/src/flux/stores/workspace-store.coffee b/src/flux/stores/workspace-store.coffee index 4d6fddd04..f87b6daed 100644 --- a/src/flux/stores/workspace-store.coffee +++ b/src/flux/stores/workspace-store.coffee @@ -1,6 +1,6 @@ _ = require 'underscore' Actions = require('../actions').default -AccountStore = require './account-store' +AccountStore = require('./account-store').default CategoryStore = require './category-store' MailboxPerspective = require '../../mailbox-perspective' NylasStore = require 'nylas-store' diff --git a/src/mailbox-perspective.coffee b/src/mailbox-perspective.coffee index 87ed8dd3b..8e542b814 100644 --- a/src/mailbox-perspective.coffee +++ b/src/mailbox-perspective.coffee @@ -2,7 +2,7 @@ _ = require 'underscore' Utils = require './flux/models/utils' 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' DatabaseStore = require('./flux/stores/database-store').default OutboxStore = require('./flux/stores/outbox-store').default