Skip to content

Commit 3e87369

Browse files
committed
use database to support "unread" value calculation, see #30
1 parent 4f8f26c commit 3e87369

File tree

6 files changed

+42
-17
lines changed

6 files changed

+42
-17
lines changed

src/electron-main/api/endpoints-builders/database/index.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,12 @@ export async function buildEndpoints(ctx: Context): Promise<Pick<Endpoints, Meth
5050
await ctx.db.saveToFile();
5151
}
5252

53-
if (entitiesModified) {
54-
NOTIFICATION_SUBJECT.next(IPC_MAIN_API_NOTIFICATION_ACTIONS.DbPatchAccount({key, stat: ctx.db.accountStat(account)}));
55-
}
53+
NOTIFICATION_SUBJECT.next(IPC_MAIN_API_NOTIFICATION_ACTIONS.DbPatchAccount({
54+
key,
55+
entitiesModified,
56+
metadataModified,
57+
stat: ctx.db.accountStat(account),
58+
}));
5659

5760
return account.metadata;
5861
})()),

src/electron-main/database/index.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,15 @@ export class Database {
164164
return stat;
165165
}
166166

167-
accountStat(account: MemoryDbAccount): { conversationEntries: number, mails: number, folders: number; contacts: number } {
167+
accountStat(
168+
account: MemoryDbAccount,
169+
): { conversationEntries: number, mails: number, folders: number; contacts: number; unread: number } {
168170
return {
169171
conversationEntries: account.conversationEntries.size,
170172
mails: account.mails.size,
171173
folders: account.folders.size,
172174
contacts: account.contacts.size,
175+
unread: [...account.mails.values()].reduce((unread, mail) => unread += Number(mail.unread), 0),
173176
};
174177
}
175178

src/electron-preload/webview/tutanota/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,8 @@ function bootstrapApi(api: Unpacked<ReturnType<typeof resolveApi>>) {
246246
setInterval(notifyUnreadValue, ONE_SECOND_MS * 60);
247247
setTimeout(notifyUnreadValue, ONE_SECOND_MS * 15);
248248
}).pipe(
249-
distinctUntilChanged(({unread: prev}, {unread: curr}) => curr === prev),
249+
// all the values need to be sent to stream to get proper unread value after disabling database syncing
250+
// distinctUntilChanged(({unread: prev}, {unread: curr}) => curr === prev),
250251
),
251252

252253
Observable.create((subscriber: Subscriber<BatchEntityUpdatesCounterOutput>) => {

src/shared/api/main.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,9 @@ export const IPC_MAIN_API_NOTIFICATION_ACTIONS = unionize({
9797
ActivateBrowserWindow: ofType<{}>(),
9898
DbPatchAccount: ofType<{
9999
key: { type: keyof FsDb, login: string };
100-
stat: { mails: number, folders: number; contacts: number };
100+
entitiesModified: boolean;
101+
metadataModified: boolean;
102+
stat: { mails: number, folders: number; contacts: number; unread: number; };
101103
}>(),
102104
},
103105
{

src/web/src/app/_accounts/accounts.effects.ts

+26-11
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import {Actions, Effect} from "@ngrx/effects";
22
import {EMPTY, Subject, from, fromEvent, merge, of, timer} from "rxjs";
33
import {Injectable} from "@angular/core";
4-
import {Store} from "@ngrx/store";
5-
import {catchError, concatMap, debounce, filter, finalize, map, mergeMap, takeUntil, tap} from "rxjs/operators";
4+
import {Store, select} from "@ngrx/store";
5+
import {catchError, concatMap, debounce, filter, finalize, map, mergeMap, takeUntil, tap, withLatestFrom} from "rxjs/operators";
66

77
import {ACCOUNTS_ACTIONS, CORE_ACTIONS, OPTIONS_ACTIONS, unionizeActionFilter} from "src/web/src/app/store/actions";
88
import {AccountTypeAndLoginFieldContainer} from "src/shared/model/container";
9+
import {AccountsSelectors} from "src/web/src/app/store/selectors";
910
import {ElectronService} from "src/web/src/app/_core/electron.service";
11+
import {IPC_MAIN_API_NOTIFICATION_ACTIONS} from "src/shared/api/main";
1012
import {ONE_SECOND_MS} from "src/shared/constants";
1113
import {State} from "src/web/src/app/store/reducers/accounts";
1214
import {TutanotaNotificationOutput} from "src/shared/api/webview/tutanota";
@@ -38,8 +40,8 @@ export class AccountsEffects {
3840
unionizeActionFilter(ACCOUNTS_ACTIONS.is.SetupNotificationChannel),
3941
map(logActionTypeAndBoundLoggerWithActionType({_logger})),
4042
mergeMap(({payload, logger}) => {
41-
const {account, webView, finishPromise} = payload;
42-
const {type, login, entryUrl} = account.accountConfig;
43+
const {account: payloadAccount, webView, finishPromise} = payload;
44+
const {type, login, entryUrl} = payloadAccount.accountConfig;
4345
const dispose$ = from(finishPromise).pipe(tap(() => logger.info("dispose")));
4446

4547
logger.info("setup");
@@ -48,16 +50,23 @@ export class AccountsEffects {
4850
merged$,
4951
this.api.webViewClient(webView, type, {finishPromise}).pipe(
5052
mergeMap((webViewClient) => webViewClient("notification")({entryUrl, zoneName: logger.zoneName()})),
51-
mergeMap((notification) => {
52-
if (type !== "tutanota"
53-
|| typeof (notification as TutanotaNotificationOutput).batchEntityUpdatesCounter === "undefined") {
54-
return of(ACCOUNTS_ACTIONS.Patch({login, patch: {notifications: notification}}));
53+
withLatestFrom(this.store.pipe(select(AccountsSelectors.ACCOUNTS.pickAccount({login})))),
54+
mergeMap(([notification, account]) => {
55+
const batchEntityUpdatesNotification = type === "tutanota"
56+
&& typeof (notification as TutanotaNotificationOutput).batchEntityUpdatesCounter === "number";
57+
58+
if (batchEntityUpdatesNotification) {
59+
this.fireSyncingIteration$.next({type, login});
60+
return EMPTY;
5561
}
5662

57-
this.fireSyncingIteration$.next({type, login});
58-
// skipping patching "notifications" by the "batchEntityUpdatesCounter" value
59-
return EMPTY;
63+
// app derives "unread" value form the database in case of activated database syncing
64+
// as "unread" notification should be ignored
65+
if (account && account.syncingActivated && typeof notification.unread === "number") {
66+
return EMPTY;
67+
}
6068

69+
return of(ACCOUNTS_ACTIONS.Patch({login, patch: {notifications: notification}}));
6170
}),
6271
takeUntil(dispose$),
6372
),
@@ -99,6 +108,12 @@ export class AccountsEffects {
99108
this.api.webViewClient(webView, type, {finishPromise}).pipe(
100109
mergeMap((webViewClient) => merge(
101110
of(ACCOUNTS_ACTIONS.Patch({login, patch: {syncingActivated: true}})),
111+
this.api.ipcMainClient({finishPromise})("notification")().pipe(
112+
filter(IPC_MAIN_API_NOTIFICATION_ACTIONS.is.DbPatchAccount),
113+
mergeMap(({payload: statPayload}) => {
114+
return of(ACCOUNTS_ACTIONS.Patch({login, patch: {notifications: {unread: statPayload.stat.unread}}}));
115+
}),
116+
),
102117
merge(
103118
timer(0, ONE_SECOND_MS * 60 * 5).pipe(
104119
tap(() => logger.verbose(`triggered by: timer`)),

src/web/src/app/_db-view/db-view.effects.ts

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export class DbViewEffects {
2727
apiClient("dbGetAccountDataView")(dbAccountPk), // initial load
2828
apiClient("notification")().pipe(
2929
filter(IPC_MAIN_API_NOTIFICATION_ACTIONS.is.DbPatchAccount),
30+
filter(({payload: patchPayload}) => patchPayload.entitiesModified),
3031
// tslint:disable-next-line:ban
3132
switchMap(() => apiClient("dbGetAccountDataView")(dbAccountPk)),
3233
),

0 commit comments

Comments
 (0)