fix(identity): Always refresh accounts after identity

This fixes an issue where changing your Nylas ID didn’t refresh your accounts, and N1 would still think they were invalid.
This commit is contained in:
Ben Gotow 2016-09-28 11:29:22 -07:00
parent 306a5010fd
commit 6ddca404f5
6 changed files with 48 additions and 51 deletions

View file

@ -51,16 +51,13 @@ export default class AccountErrorHeader extends React.Component {
} }
_onCheckAgain = (event) => { _onCheckAgain = (event) => {
const errorAccounts = this.state.accounts.filter(a => a.hasSyncStateError());
this.setState({refreshing: true}); this.setState({refreshing: true});
event.stopPropagation(); event.stopPropagation();
IdentityStore.refreshStatus().finally(() => { IdentityStore.refreshIdentityAndAccounts().finally(() => {
AccountStore.refreshHealthOfAccounts(errorAccounts.map(a => a.id)).finally(() => { if (!this.mounted) { return; }
if (!this.mounted) { return; } this.setState({refreshing: false});
this.setState({refreshing: false});
});
}); });
} }

View file

@ -20,12 +20,9 @@ describe("AccountErrorHeader", function AccountErrorHeaderTests() {
it("allows the user to refresh the account", () => { it("allows the user to refresh the account", () => {
const header = mount(<AccountErrorHeader />); const header = mount(<AccountErrorHeader />);
spyOn(IdentityStore, 'refreshStatus').andReturn(Promise.resolve()); spyOn(IdentityStore, 'refreshIdentityAndAccounts').andReturn(Promise.resolve());
spyOn(AccountStore, 'refreshHealthOfAccounts').andReturn(Promise.resolve());
header.find('.action.refresh').simulate('click'); header.find('.action.refresh').simulate('click');
expect(IdentityStore.refreshStatus).toHaveBeenCalled(); expect(IdentityStore.refreshIdentityAndAccounts).toHaveBeenCalled();
advanceClock();
expect(AccountStore.refreshHealthOfAccounts).toHaveBeenCalledWith(['A']);
}); });
it("allows the user to reconnect the account", () => { it("allows the user to reconnect the account", () => {
@ -53,12 +50,9 @@ describe("AccountErrorHeader", function AccountErrorHeaderTests() {
it("allows the user to refresh the accounts", () => { it("allows the user to refresh the accounts", () => {
const header = mount(<AccountErrorHeader />); const header = mount(<AccountErrorHeader />);
spyOn(IdentityStore, 'refreshStatus').andReturn(Promise.resolve()); spyOn(IdentityStore, 'refreshIdentityAndAccounts').andReturn(Promise.resolve());
spyOn(AccountStore, 'refreshHealthOfAccounts').andReturn(Promise.resolve());
header.find('.action.refresh').simulate('click'); header.find('.action.refresh').simulate('click');
expect(IdentityStore.refreshStatus).toHaveBeenCalled(); expect(IdentityStore.refreshIdentityAndAccounts).toHaveBeenCalled();
advanceClock();
expect(AccountStore.refreshHealthOfAccounts).toHaveBeenCalledWith(['A', 'B']);
}); });
it("allows the user to open preferences", () => { it("allows the user to open preferences", () => {

View file

@ -83,7 +83,7 @@ class PreferencesIdentity extends React.Component {
_onRefresh = () => { _onRefresh = () => {
this.setState({refreshing: true}); this.setState({refreshing: true});
IdentityStore.refreshStatus().finally(() => { IdentityStore.refreshIdentityAndAccounts().finally(() => {
this.setState({refreshing: false}); this.setState({refreshing: false});
}); });
} }

View file

@ -79,21 +79,6 @@ describe "AccountStore", ->
expect(@instance.tokenForAccountId('A')).toEqual('A-TOKEN') expect(@instance.tokenForAccountId('A')).toEqual('A-TOKEN')
expect(@instance.tokenForAccountId('B')).toEqual('B-TOKEN') expect(@instance.tokenForAccountId('B')).toEqual('B-TOKEN')
describe "in the work window and running on production", ->
it "should refresh the accounts", ->
spyOn(NylasEnv, 'isWorkWindow').andReturn(true)
@instance = new @constructor
spyOn(@instance, 'refreshHealthOfAccounts')
advanceClock(10000)
expect(@instance.refreshHealthOfAccounts).toHaveBeenCalledWith(['A', 'B'])
describe "in the main window", ->
it "should not refresh the accounts", ->
@instance = new @constructor
spyOn(@instance, 'refreshHealthOfAccounts')
advanceClock(10000)
expect(@instance.refreshHealthOfAccounts).not.toHaveBeenCalled()
describe "accountForEmail", -> describe "accountForEmail", ->
beforeEach -> beforeEach ->
@instance = new @constructor @instance = new @constructor

View file

@ -26,11 +26,6 @@ class AccountStore extends NylasStore
@listenTo Actions.updateAccount, @_onUpdateAccount @listenTo Actions.updateAccount, @_onUpdateAccount
@listenTo Actions.reorderAccount, @_onReorderAccount @listenTo Actions.reorderAccount, @_onReorderAccount
if NylasEnv.isWorkWindow() and ['staging', 'production'].includes(NylasEnv.config.get('env'))
setTimeout( =>
@refreshHealthOfAccounts(@_accounts.map((a) -> a.id))
, 2000)
NylasEnv.config.onDidChange configVersionKey, (change) => NylasEnv.config.onDidChange configVersionKey, (change) =>
# If we already have this version of the accounts config, it means we # 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. # are the ones who saved the change, and we don't need to reload.

View file

@ -7,6 +7,7 @@ import Moment from 'moment-timezone';
import Actions from '../actions'; import Actions from '../actions';
import AccountStore from './account-store'; import AccountStore from './account-store';
import Utils from '../models/utils';
const configIdentityKey = "nylas.identity"; const configIdentityKey = "nylas.identity";
const keytarServiceName = 'Nylas'; const keytarServiceName = 'Nylas';
@ -35,13 +36,16 @@ class IdentityStore extends NylasStore {
NylasEnv.config.onDidChange(configIdentityKey, () => { NylasEnv.config.onDidChange(configIdentityKey, () => {
this._loadIdentity(); this._loadIdentity();
this.trigger(); this.trigger();
if (NylasEnv.isMainWindow()) {
this.refreshAccounts();
}
}); });
this._loadIdentity(); this._loadIdentity();
if (NylasEnv.isMainWindow() && ['staging', 'production'].includes(NylasEnv.config.get('env'))) { if (NylasEnv.isMainWindow() && ['staging', 'production'].includes(NylasEnv.config.get('env'))) {
setInterval(this.refreshStatus, 1000 * 60 * 60); setInterval(this.refreshIdentityAndAccounts, 1000 * 60 * 60); // 1 hour
this.refreshStatus(); this.refreshIdentityAndAccounts();
} }
} }
@ -102,20 +106,28 @@ class IdentityStore extends NylasStore {
return Math.max(0, requiredDayOfEpoch - nowDayOfEpoch); return Math.max(0, requiredDayOfEpoch - nowDayOfEpoch);
} }
refreshStatus = () => { refreshIdentityAndAccounts = () => {
return Promise.all([ return this.fetchIdentity().then(() =>
this.fetchIdentity(), this.refreshAccounts()
Promise.all(AccountStore.accounts().map((a) => ).catch((err) => {
this.fetchSubscriptionRequiredDate(a))
).then((subscriptionRequiredDates) => {
this._subscriptionRequiredAfter = subscriptionRequiredDates.sort().shift();
this.trigger();
}),
]).catch((err) => {
console.error(`Unable to refresh IdentityStore status: ${err.message}`) console.error(`Unable to refresh IdentityStore status: ${err.message}`)
}); });
} }
refreshAccounts = () => {
const accountIds = AccountStore.accounts().map((a) => a.id);
AccountStore.refreshHealthOfAccounts(accountIds);
return Promise.all(AccountStore.accounts().map((a) =>
this.fetchSubscriptionRequiredDate(a))
).then((subscriptionRequiredDates) => {
this._subscriptionRequiredAfter = subscriptionRequiredDates.sort().shift();
this.trigger();
}).catch((err) => {
console.error(`Unable to refresh IdentityStore accounts: ${err.message}`)
})
}
/** /**
* This passes utm_source, utm_campaign, and utm_content params to the * This passes utm_source, utm_campaign, and utm_content params to the
* N1 billing site. Please reference: * N1 billing site. Please reference:
@ -184,15 +196,29 @@ class IdentityStore extends NylasStore {
fetchPath = (path) => { fetchPath = (path) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
request({ const requestId = Utils.generateTempId();
const options = {
method: 'GET', method: 'GET',
url: `${this.URLRoot}${path}`, url: `${this.URLRoot}${path}`,
startTime: Date.now(),
auth: { auth: {
username: this._identity.token, username: this._identity.token,
password: '', password: '',
sendImmediately: true, sendImmediately: true,
}, },
}, (error, response = {}, body) => { };
Actions.willMakeAPIRequest({
request: options,
requestId: requestId,
});
request(options, (error, response = {}, body) => {
Actions.didMakeAPIRequest({
request: options,
statusCode: response.statusCode,
error: error,
requestId: requestId,
});
if (response.statusCode === 200) { if (response.statusCode === 200) {
try { try {
return resolve(JSON.parse(body)); return resolve(JSON.parse(body));