diff --git a/webapp/src/ts/app.component.ts b/webapp/src/ts/app.component.ts index 90de9113fb3..195d53d6414 100644 --- a/webapp/src/ts/app.component.ts +++ b/webapp/src/ts/app.component.ts @@ -349,7 +349,11 @@ export class AppComponent implements OnInit, AfterViewInit { } private initAndroidTaskNotifications() { - if (typeof globalThis?.medicmobile_android?.updateTaskNotificationStore === 'function') { + const android = globalThis?.medicmobile_android; + if ( + typeof android?.updateTaskNotificationStoreWithSettings === 'function' || + typeof android?.updateTaskNotificationStore === 'function' + ) { this.taskNotificationService.initOnAndroid(); } } diff --git a/webapp/src/ts/services/rules-engine.service.ts b/webapp/src/ts/services/rules-engine.service.ts index 65e66dfa20d..2df55c79583 100644 --- a/webapp/src/ts/services/rules-engine.service.ts +++ b/webapp/src/ts/services/rules-engine.service.ts @@ -42,9 +42,11 @@ interface TaskDoc { _id: string; emission?: any; owner: string; - stateHistory: Array<{ state: string, timestamp: number }>; + stateHistory: StateHistory; } +export type StateHistory = Array<{ state: string, timestamp: number }>; + @Injectable({ providedIn: 'root' }) diff --git a/webapp/src/ts/services/task-notifications.service.ts b/webapp/src/ts/services/task-notifications.service.ts index a4c369c2a39..1fec337b10c 100644 --- a/webapp/src/ts/services/task-notifications.service.ts +++ b/webapp/src/ts/services/task-notifications.service.ts @@ -3,7 +3,7 @@ import { Subscription } from 'rxjs'; import { debounce as _debounce } from 'lodash-es'; import * as moment from 'moment'; -import { RulesEngineService } from '@mm-services/rules-engine.service'; +import { RulesEngineService, StateHistory } from '@mm-services/rules-engine.service'; import { TranslateService } from '@mm-services/translate.service'; import { FormatDateService } from '@mm-services/format-date.service'; import { SettingsService } from '@mm-services/settings.service'; @@ -11,6 +11,8 @@ import { AuthService } from '@mm-services/auth.service'; import { orderByDueDateAndPriority } from '@mm-reducers/tasks'; const DEFAULT_MAX_NOTIFICATIONS = 8; +const DEFAULT_WINDOW_START_TIME = '08:00'; +const DEFAULT_WINDOW_END_TIME = '19:00'; export interface Notification { _id: string, @@ -21,6 +23,14 @@ export interface Notification { readyAt: number } +export type notificationSettings = { + maxNotifications: number; + notificationWindow: { + start: string; + end: string; + } +}; + @Injectable({ providedIn: 'root' }) @@ -64,7 +74,15 @@ export class TasksNotificationService implements OnDestroy { private async updateAndroidStore(): Promise { const notifications = await this.fetchNotifications(); - const maxNotifications = await this.getMaxNotificationSettings(); + const { maxNotifications, notificationWindow } = await this.getNotificationSettings(); + + if (globalThis?.medicmobile_android?.updateTaskNotificationStoreWithSettings) { + globalThis.medicmobile_android.updateTaskNotificationStoreWithSettings( + JSON.stringify(notifications), + JSON.stringify({maxNotifications, ...notificationWindow}), + ); + return; + } globalThis?.medicmobile_android?.updateTaskNotificationStore(JSON.stringify(notifications), maxNotifications); } @@ -97,17 +115,28 @@ export class TasksNotificationService implements OnDestroy { } } - private getReadyStateTimestamp(stateHistory): number { + private getReadyStateTimestamp(stateHistory: StateHistory): number { const readyState = stateHistory.find(state => state.state === 'Ready'); return readyState ? readyState.timestamp : 0; } - private async getMaxNotificationSettings(): Promise { + private async getNotificationSettings(): Promise { const settings = await this.settingsService.get(); + const taskNotificationSettings: notificationSettings = { + maxNotifications: DEFAULT_MAX_NOTIFICATIONS, + notificationWindow: { + start: DEFAULT_WINDOW_START_TIME, + end: DEFAULT_WINDOW_END_TIME, + } + }; if (settings?.tasks?.max_task_notifications) { - return settings.tasks.max_task_notifications; + taskNotificationSettings.maxNotifications = settings.tasks.max_task_notifications; + } + + if (settings.tasks?.task_notification_window) { + taskNotificationSettings.notificationWindow = settings.tasks.task_notification_window; } - return DEFAULT_MAX_NOTIFICATIONS; + return taskNotificationSettings; } private translateContentText(taskTitle: string, contact: string): string { diff --git a/webapp/tests/karma/ts/app.component.spec.ts b/webapp/tests/karma/ts/app.component.spec.ts index 5443ecfd77b..18c8803a3da 100644 --- a/webapp/tests/karma/ts/app.component.spec.ts +++ b/webapp/tests/karma/ts/app.component.spec.ts @@ -340,6 +340,13 @@ describe('AppComponent', () => { expect(tasksNotificationService.initOnAndroid.callCount).to.equal(1); }); + it('should init task notifications on android if updateTaskNotificationStoreWithSettings', async () => { + window.medicmobile_android = { updateTaskNotificationStoreWithSettings: sinon.stub().returns(true) }; + await getComponent(); + await component.setupPromise; + expect(tasksNotificationService.initOnAndroid.callCount).to.equal(1); + }); + it('should show reload popup when service worker is updated', async () => { await getComponent(); await component.setupPromise; diff --git a/webapp/tests/karma/ts/services/task-notification.service.spec.ts b/webapp/tests/karma/ts/services/task-notification.service.spec.ts index 807b537ed35..44267312a61 100644 --- a/webapp/tests/karma/ts/services/task-notification.service.spec.ts +++ b/webapp/tests/karma/ts/services/task-notification.service.spec.ts @@ -127,22 +127,25 @@ describe('TasksNotificationService', () => { expect(service).to.be.ok; }); - it('should initialize max notifications value from settings', async () => { + it('should initialize notifications values from settings', async () => { settingsService.get.resolves({ tasks: { - max_task_notifications: 20 + max_task_notifications: 20, + task_notification_window: { start: '10:00', end: '18:00' } } }); - const maxNotifications = await service.getMaxNotificationSettings(); + const { maxNotifications, notificationWindow } = await service.getNotificationSettings(); expect(settingsService.get.callCount).to.equal(1); expect(maxNotifications).to.equal(20); + expect(notificationWindow).to.deep.equal({ start: '10:00', end: '18:00' }); }); - it('should return default max notifications value if settings value is not valid', async () => { + it('should return default notification values if settings value is not valid', async () => { settingsService.get.resolves({ max_task_notifications: '2o' }); - const maxNotifications = await service.getMaxNotificationSettings(); + const { maxNotifications, notificationWindow } = await service.getNotificationSettings(); expect(settingsService.get.callCount).to.equal(1); expect(maxNotifications).to.equal(8); + expect(notificationWindow).to.deep.equal({ start: '08:00', end: '19:00' }); }); it('should fetch notifications', async () => { @@ -259,4 +262,54 @@ describe('TasksNotificationService', () => { expect(updateAndroidStoreStub.callCount).to.equal(0); }); + describe('updateAndroidStore', () => { + let androidApi: any; + + beforeEach(() => { + androidApi = {}; + (globalThis as any).medicmobile_android = androidApi; + }); + + afterEach(() => { + delete (globalThis as any).medicmobile_android; + }); + + it('should update the android store without settings when only the basic api is available', async () => { + androidApi.updateTaskNotificationStore = sinon.stub(); + + await service.updateAndroidStore(); + + expect(androidApi.updateTaskNotificationStore.callCount).to.equal(1); + + const [notificationsJson, maxNotifications] = androidApi.updateTaskNotificationStore.args[0]; + expect(JSON.parse(notificationsJson)).to.be.an('array').that.has.lengthOf(3); + expect(maxNotifications).to.equal(8); + }); + + it('should update the android store with settings when the settings api is available', async () => { + androidApi.updateTaskNotificationStore = sinon.stub(); + androidApi.updateTaskNotificationStoreWithSettings = sinon.stub(); + settingsService.get.resolves({ + tasks: { + max_task_notifications: 20, + task_notification_window: { start: '10:00', end: '18:00' }, + }, + }); + + await service.updateAndroidStore(); + + expect(androidApi.updateTaskNotificationStore.callCount).to.equal(0); + expect(androidApi.updateTaskNotificationStoreWithSettings.callCount).to.equal(1); + + const [notificationsJson, settingsJson] = + androidApi.updateTaskNotificationStoreWithSettings.args[0]; + expect(JSON.parse(notificationsJson)).to.be.an('array').that.has.lengthOf(3); + expect(JSON.parse(settingsJson)).to.deep.equal({ + maxNotifications: 20, + start: '10:00', + end: '18:00' + }); + }); + }); + });