Skip to content

Commit

Permalink
accounts reducer (web): handle account disabling edge cases, #316
Browse files Browse the repository at this point in the history
  • Loading branch information
vladimiry committed Oct 16, 2020
1 parent fcec295 commit 9b717a2
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export abstract class AccountViewAbstractComponent extends NgChangesObservableCo
type: "action",
payload: this.injector
.get(AccountsService)
.generateNotificationsStateResetAction({login: this.account.accountConfig.login, ignoreNoAccount: true}),
.generateNotificationsStateResetAction({login: this.account.accountConfig.login, optionalAccount: true}),
});
}

Expand Down
15 changes: 9 additions & 6 deletions src/web/browser-window/app/_accounts/accounts.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export class AccountsEffects {
const {login} = pk;
const dispose$ = from(finishPromise).pipe(
tap(() => {
this.store.dispatch(ACCOUNTS_ACTIONS.Patch({login, patch: {syncingActivated: false}, ignoreNoAccount: true}));
this.store.dispatch(ACCOUNTS_ACTIONS.Patch({login, patch: {syncingActivated: false}, optionalAccount: true}));
logger.info("dispose");
}),
);
Expand Down Expand Up @@ -192,10 +192,11 @@ export class AccountsEffects {
);
return selectMailOnlineInput$.pipe(
mergeMap(() => EMPTY),
finalize(() => this.store.dispatch(ACCOUNTS_ACTIONS.PatchProgress({
login,
patch: {selectingMailOnline: false},
}))),
finalize(() => {
this.store.dispatch(
ACCOUNTS_ACTIONS.PatchProgress({login, patch: {selectingMailOnline: false}}),
);
}),
);
}),
),
Expand Down Expand Up @@ -287,7 +288,9 @@ export class AccountsEffects {
),
catchError((error) => of(NOTIFICATION_ACTIONS.Error(error))),
finalize(() => {
this.store.dispatch(ACCOUNTS_ACTIONS.PatchProgress({login, patch: {syncing: false}}));
this.store.dispatch(
ACCOUNTS_ACTIONS.PatchProgress({login, patch: {syncing: false}, optionalAccount: true}),
);
}),
);

Expand Down
4 changes: 2 additions & 2 deletions src/web/browser-window/app/_accounts/accounts.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ export class AccountsService {
) {}

generateNotificationsStateResetAction(
{login, ignoreNoAccount}: { login: string; ignoreNoAccount?: boolean }
{login, optionalAccount}: { login: string; optionalAccount?: boolean }
): ReturnType<typeof ACCOUNTS_ACTIONS.Patch> {
return ACCOUNTS_ACTIONS.Patch({login, patch: {notifications: {unread: 0, loggedIn: false}}, ignoreNoAccount});
return ACCOUNTS_ACTIONS.Patch({login, patch: {notifications: {unread: 0, loggedIn: false}}, optionalAccount});
}

buildLoginDelaysResetAction(
Expand Down
4 changes: 2 additions & 2 deletions src/web/browser-window/app/store/actions/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {WebAccount, WebAccountProgress} from "src/web/browser-window/app/model";
export const ACCOUNTS_ACTIONS = unionize({
Select: ofType<{ login: string }>(),
DeSelect: ofType<{ login: string }>(),
PatchProgress: ofType<{ login: string; patch: WebAccountProgress }>(),
PatchProgress: ofType<{ login: string; patch: WebAccountProgress; optionalAccount?: boolean; }>(),
Patch: ofType<{
login: string;
patch: Partial<{
Expand All @@ -20,7 +20,7 @@ export const ACCOUNTS_ACTIONS = unionize({
| "loginDelayedSeconds"
| "loginDelayedUntilSelected">]: Partial<WebAccount[k]>
}>;
ignoreNoAccount?: boolean;
optionalAccount?: boolean;
}>(),
PatchDbExportProgress: ofType<{ pk: DbAccountPk; uuid: string; progress?: number }>(),
ToggleDatabaseView: ofType<{ login: string; forced?: Pick<WebAccount, "databaseView"> }>(),
Expand Down
74 changes: 39 additions & 35 deletions src/web/browser-window/app/store/reducers/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,6 @@ const logger = getZoneNameBoundWebLogger("[reducers/accounts]");

export const featureName = "accounts";

const notFoundAccountError = new Error(`Failed to resolve account`);

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function pickAccountBundle(accounts: WebAccount[], criteria: LoginFieldContainer, strict = true) {
const index = accounts
.map(({accountConfig}) => accountConfig)
.findIndex(accountPickingPredicate(criteria));

if (strict && index === -1) {
throw notFoundAccountError;
}

return {index, account: accounts[index]};
}

export interface State extends fromRoot.State {
selectedLogin?: string;
initialized?: boolean;
Expand All @@ -42,6 +27,22 @@ const initialState: State = {
globalProgress: {},
};

const resolveAccountByLogin = <T extends boolean>(
accounts: WebAccount[],
filterCriteria: LoginFieldContainer,
strict: T,
): typeof strict extends true ? WebAccount : WebAccount | undefined => {
const filterPredicate = accountPickingPredicate(filterCriteria);
const webAccount: ReturnType<typeof resolveAccountByLogin> = accounts.find(({accountConfig}) => filterPredicate(accountConfig));

if (!webAccount && strict) {
throw new Error("Failed to resolve account");
}

// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any
return webAccount as any;
};

export function reducer(state = initialState, action: UnionOf<typeof ACCOUNTS_ACTIONS> & UnionOf<typeof NAVIGATION_ACTIONS>): State {
if (NAVIGATION_ACTIONS.is.Logout(action)) {
return initialState;
Expand All @@ -52,7 +53,7 @@ export function reducer(state = initialState, action: UnionOf<typeof ACCOUNTS_AC
const webAccounts = accountConfigs
.filter((accountConfig) => !accountConfig.disabled)
.reduce((accounts: WebAccount[], accountConfig) => {
const {account} = pickAccountBundle(draftState.accounts, accountConfig, false);
const account = resolveAccountByLogin(draftState.accounts, accountConfig, false);

if (account) {
account.accountConfig = accountConfig;
Expand Down Expand Up @@ -109,23 +110,26 @@ export function reducer(state = initialState, action: UnionOf<typeof ACCOUNTS_AC
delete draftState.selectedLogin;
}
},
PatchProgress(payload) {
const {account} = pickAccountBundle(draftState.accounts, payload);
account.progress = {...account.progress, ...payload.patch};
PatchProgress({login, patch, optionalAccount}) {
logger.verbose("(PatchProgress)", JSON.stringify({patch, optionalAccount}));

const account = resolveAccountByLogin(draftState.accounts, {login}, !optionalAccount);

if (!account) {
logger.verbose("(PatchProgress) reducing skipped");
return;
}

account.progress = {...account.progress, ...patch};
},
Patch({login, patch, ignoreNoAccount}) {
logger.verbose("(Patch)", JSON.stringify({patch}));
Patch({login, patch, optionalAccount}) {
logger.verbose("(Patch)", JSON.stringify({patch, optionalAccount}));

let account: WebAccount | undefined;
const account = resolveAccountByLogin(draftState.accounts, {login}, !optionalAccount);

try {
const bundle = pickAccountBundle(draftState.accounts, {login});
account = bundle.account; // eslint-disable-line prefer-destructuring
} catch (error) {
if (error === notFoundAccountError && ignoreNoAccount) {
return;
}
throw error;
if (!account) {
logger.verbose("(Patch) reducing skipped");
return;
}

if ("notifications" in patch) {
Expand All @@ -149,7 +153,7 @@ export function reducer(state = initialState, action: UnionOf<typeof ACCOUNTS_AC
}
},
PatchDbExportProgress({pk: {login}, uuid, progress}) {
const {account} = pickAccountBundle(draftState.accounts, {login});
const account = resolveAccountByLogin(draftState.accounts, {login}, true);
const item = account.dbExportProgress.find((_) => _.uuid === uuid);

if (typeof progress === "number") {
Expand All @@ -166,7 +170,7 @@ export function reducer(state = initialState, action: UnionOf<typeof ACCOUNTS_AC
}
},
ToggleDatabaseView({login, forced}) {
const {account} = pickAccountBundle(draftState.accounts, {login});
const account = resolveAccountByLogin(draftState.accounts, {login}, true);

account.databaseView = forced
? forced.databaseView
Expand All @@ -176,15 +180,15 @@ export function reducer(state = initialState, action: UnionOf<typeof ACCOUNTS_AC
draftState.globalProgress = {...draftState.globalProgress, ...patch};
},
FetchSingleMailSetParams({pk, mailPk}) {
const {account} = pickAccountBundle(draftState.accounts, {login: pk.login});
const account = resolveAccountByLogin(draftState.accounts, {login: pk.login}, true);

account.fetchSingleMailParams = mailPk
? {mailPk}
: null;
},
MakeMailReadSetParams({pk, ...rest}) {
const key = "makeReadMailParams";
const {account} = pickAccountBundle(draftState.accounts, {login: pk.login});
const account = resolveAccountByLogin(draftState.accounts, {login: pk.login}, true);

if ("messageIds" in rest) {
const {messageIds} = rest;
Expand All @@ -196,7 +200,7 @@ export function reducer(state = initialState, action: UnionOf<typeof ACCOUNTS_AC
},
SetMailFolderParams({pk, ...rest}) {
const key = "setMailFolderParams";
const {account} = pickAccountBundle(draftState.accounts, {login: pk.login});
const account = resolveAccountByLogin(draftState.accounts, {login: pk.login}, true);

if ("messageIds" in rest) {
const {folderId, messageIds} = rest;
Expand Down

0 comments on commit 9b717a2

Please sign in to comment.