diff --git a/public/mobile-app/package-lock.json b/public/mobile-app/package-lock.json
index 24980a2b..2f5a878f 100644
--- a/public/mobile-app/package-lock.json
+++ b/public/mobile-app/package-lock.json
@@ -19,6 +19,7 @@
"@sveltejs/vite-plugin-svelte": "6.2.1",
"@testing-library/jest-dom": "6.9.1",
"@testing-library/svelte": "5.2.9",
+ "dompurify": "3.4.5",
"eslint": "9.39.1",
"eslint-config-prettier": "10.1.8",
"eslint-plugin-svelte": "3.13.1",
@@ -2630,6 +2631,15 @@
"integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==",
"license": "MIT"
},
+ "node_modules/dompurify": {
+ "version": "3.4.5",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.5.tgz",
+ "integrity": "sha512-OrwIBKsdNSVEeubdJ1HBv/wNENRM9ytAVCv7YXt//A3vPdVMNuACRqK9mXCGCBW2ln7BT/A4X0jXHo2Gu89miA==",
+ "license": "(MPL-2.0 OR Apache-2.0)",
+ "optionalDependencies": {
+ "@types/trusted-types": "^2.0.7"
+ }
+ },
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
diff --git a/public/mobile-app/package.json b/public/mobile-app/package.json
index 44e2fc5f..f2999afa 100644
--- a/public/mobile-app/package.json
+++ b/public/mobile-app/package.json
@@ -29,6 +29,7 @@
"@sveltejs/vite-plugin-svelte": "6.2.1",
"@testing-library/jest-dom": "6.9.1",
"@testing-library/svelte": "5.2.9",
+ "dompurify": "3.4.5",
"eslint": "9.39.1",
"eslint-config-prettier": "10.1.8",
"eslint-plugin-svelte": "3.13.1",
diff --git a/public/mobile-app/src/lib/agenda.test.ts b/public/mobile-app/src/lib/agenda.test.ts
index a7cee635..0c78cd56 100644
--- a/public/mobile-app/src/lib/agenda.test.ts
+++ b/public/mobile-app/src/lib/agenda.test.ts
@@ -1,5 +1,6 @@
import { afterEach, describe, expect, test, vi } from 'vitest';
import '@testing-library/jest-dom/vitest';
+import { Address } from '$lib/address';
import { Agenda, buildAgenda, Item } from '$lib/agenda';
import * as catalogMethods from '$lib/api-catalog';
import * as scheduledNotificationsMethods from '$lib/scheduled-notifications';
@@ -125,6 +126,39 @@ describe('/agenda.ts', () => {
expect(name2).equal('Novembre');
});
});
+ describe('endDate', () => {
+ test('should return the max endDate of subitems', async () => {
+ // Given
+ const item1 = new Item('holiday', 'title', 'description', null, null, null);
+ const item2 = new Item(
+ 'holiday',
+ 'title',
+ 'description',
+ null,
+ null,
+ new Date('2025-11-15')
+ );
+ const item3 = new Item(
+ 'holiday',
+ 'title',
+ 'description',
+ null,
+ null,
+ new Date('2025-11-15')
+ );
+ item3.addSubItem('description', null, null, new Date('2025-11-14'));
+
+ // When
+ const endDate1 = item1.endDate;
+ const endDate2 = item2.endDate;
+ const endDate3 = item3.endDate;
+
+ // Then
+ expect(endDate1).equal(null);
+ expect(endDate2?.getTime()).equal(new Date('2025-11-15').getTime());
+ expect(endDate3?.getTime()).equal(new Date('2025-11-15').getTime());
+ });
+ });
describe('period', () => {
test('should not mention start date year', async () => {
// Given
@@ -535,18 +569,17 @@ describe('/agenda.ts', () => {
new Item(
'holiday',
'Holiday 5',
- 'Paris (75) 🏠',
+ 'Zone C : Paris (75) 🏠',
null,
holiday5.start_date,
- holiday5.end_date,
- true
+ holiday5.end_date
)
)
).toBe(true);
});
});
describe('Zones', () => {
- test('should mark holidays as custom if zones match user zone', async () => {
+ test('should ignore some zones', async () => {
// Given
vi.stubEnv('TZ', 'Europe/Paris');
localStorage.setItem('user_identity', JSON.stringify(mockUserIdentity));
@@ -557,7 +590,7 @@ describe('/agenda.ts', () => {
date: null,
start_date: new Date('2026-02-06T23:00:00Z'),
end_date: new Date('2026-02-22T23:00:00Z'),
- zones: ['Zone A'],
+ zones: ['Zone A', 'Zone foo'],
emoji: 'foo',
};
const holiday2 = {
@@ -567,65 +600,32 @@ describe('/agenda.ts', () => {
date: null,
start_date: new Date('2026-02-13T23:00:00Z'),
end_date: new Date('2026-03-01T23:00:00Z'),
- zones: ['Zone C'],
+ zones: ['Zone foo'],
emoji: 'foo',
};
- const holiday3 = {
- kind: 'holiday',
- title: 'Summer Holiday',
- description: '',
- date: null,
- start_date: new Date('2026-07-01T23:00:00Z'),
- end_date: new Date('2026-08-31T23:00:00Z'),
- zones: ['Zone A', 'Zone B', 'Zone C'],
- emoji: 'bar',
- };
- const holiday4 = {
- kind: 'holiday',
- title: 'Day',
- description: '',
- date: new Date('2026-02-15T23:00:00Z'),
- start_date: null,
- end_date: null,
- zones: [],
- emoji: '',
- };
await userStore.login(mockUserInfo);
const spyIsConcerned = vi
.spyOn(Preferences.prototype, 'isSchoolHolidayConcerned')
- .mockReturnValue(true);
+ .mockReturnValueOnce(true)
+ .mockReturnValueOnce(false);
const spyGetDescription = vi
.spyOn(Preferences.prototype, 'getSchoolHolidayDescription')
- .mockReturnValueOnce('desc 1')
- .mockReturnValueOnce('desc 2')
- .mockReturnValueOnce('desc 3');
+ .mockReturnValueOnce('desc 1');
// When
const agenda = new Agenda(
{
- school_holidays: [holiday1, holiday2, holiday3],
- public_holidays: [holiday4],
+ school_holidays: [holiday1, holiday2],
+ public_holidays: [],
elections: [],
},
new Date('2026-02-01T12:00:00Z')
);
// Then
- expect(agenda.now.length).equal(4);
+ expect(agenda.now.length).equal(1);
expect(
agenda.now[0].equals(
- new Item(
- 'otv',
- 'Opération Tranquillité Vacances 🏠',
- 'Inscrivez-vous pour protéger votre domicile pendant votre absence',
- null,
- new Date('2026-01-23T23:00:00Z'),
- null
- )
- )
- ).toBe(true);
- expect(
- agenda.now[1].equals(
new Item(
'holiday',
'Holiday foo',
@@ -636,61 +636,23 @@ describe('/agenda.ts', () => {
)
)
).toBe(true);
- expect(
- agenda.now[2].equals(
- new Item(
- 'holiday',
- 'Holiday foo',
- 'desc 2',
- null,
- holiday2.start_date,
- holiday2.end_date,
- true
- )
- )
- ).toBe(true);
- expect(
- agenda.now[3].equals(
- new Item('holiday', 'Day', null, holiday4.date, null, null)
- )
- ).toBe(true);
- expect(agenda.next.length).equal(1);
- expect(
- agenda.next[0].equals(
- new Item(
- 'holiday',
- 'Summer Holiday bar',
- 'desc 3',
- null,
- holiday3.start_date,
- holiday3.end_date,
- true
- )
- )
- ).toBe(true);
- expect(spyIsConcerned).toHaveBeenCalledTimes(3);
+ expect(agenda.next.length).equal(0);
+ expect(spyIsConcerned).toHaveBeenCalledTimes(2);
expect(spyIsConcerned).toHaveBeenCalledWith(holiday1);
expect(spyIsConcerned).toHaveBeenCalledWith(holiday2);
- expect(spyIsConcerned).toHaveBeenCalledWith(holiday3);
- expect(spyGetDescription).toHaveBeenCalledTimes(3);
+ expect(spyGetDescription).toHaveBeenCalledTimes(1);
expect(spyGetDescription).toHaveBeenCalledWith(
holiday1,
userStore.connected?.identity.address
);
- expect(spyGetDescription).toHaveBeenCalledWith(
- holiday2,
- userStore.connected?.identity.address
- );
- expect(spyGetDescription).toHaveBeenCalledWith(
- holiday3,
- userStore.connected?.identity.address
- );
// Cleanup
spyIsConcerned.mockRestore();
spyGetDescription.mockRestore();
});
- test('should ignore some zones', async () => {
+ });
+ describe('Multitiles', () => {
+ test('should stack holidays with the same title - but only for the same year', async () => {
// Given
vi.stubEnv('TZ', 'Europe/Paris');
localStorage.setItem('user_identity', JSON.stringify(mockUserIdentity));
@@ -701,7 +663,7 @@ describe('/agenda.ts', () => {
date: null,
start_date: new Date('2026-02-06T23:00:00Z'),
end_date: new Date('2026-02-22T23:00:00Z'),
- zones: ['Zone A', 'Zone foo'],
+ zones: ['Zone A'],
emoji: 'foo',
};
const holiday2 = {
@@ -711,22 +673,35 @@ describe('/agenda.ts', () => {
date: null,
start_date: new Date('2026-02-13T23:00:00Z'),
end_date: new Date('2026-03-01T23:00:00Z'),
- zones: ['Zone foo'],
+ zones: ['Zone B'],
+ emoji: 'foo',
+ };
+ const holiday3 = {
+ kind: 'holiday',
+ title: 'Holiday',
+ description: '',
+ date: null,
+ start_date: new Date('2027-02-06T23:00:00Z'),
+ end_date: new Date('2027-02-22T23:00:00Z'),
+ zones: ['Zone A'],
+ emoji: 'foo',
+ };
+ const holiday4 = {
+ kind: 'holiday',
+ title: 'Holiday',
+ description: '',
+ date: null,
+ start_date: new Date('2027-02-06T23:00:00Z'),
+ end_date: new Date('2027-02-21T23:00:00Z'),
+ zones: ['Zone B'],
emoji: 'foo',
};
await userStore.login(mockUserInfo);
- const spyIsConcerned = vi
- .spyOn(Preferences.prototype, 'isSchoolHolidayConcerned')
- .mockReturnValueOnce(true)
- .mockReturnValueOnce(false);
- const spyGetDescription = vi
- .spyOn(Preferences.prototype, 'getSchoolHolidayDescription')
- .mockReturnValueOnce('desc 1');
// When
const agenda = new Agenda(
{
- school_holidays: [holiday1, holiday2],
+ school_holidays: [holiday1, holiday2, holiday3, holiday4],
public_holidays: [],
elections: [],
},
@@ -735,31 +710,29 @@ describe('/agenda.ts', () => {
// Then
expect(agenda.now.length).equal(1);
- expect(
- agenda.now[0].equals(
- new Item(
- 'holiday',
- 'Holiday foo',
- 'desc 1',
- null,
- holiday1.start_date,
- holiday1.end_date
- )
- )
- ).toBe(true);
- expect(agenda.next.length).equal(0);
- expect(spyIsConcerned).toHaveBeenCalledTimes(2);
- expect(spyIsConcerned).toHaveBeenCalledWith(holiday1);
- expect(spyIsConcerned).toHaveBeenCalledWith(holiday2);
- expect(spyGetDescription).toHaveBeenCalledTimes(1);
- expect(spyGetDescription).toHaveBeenCalledWith(
- holiday1,
- userStore.connected?.identity.address
+ const item1 = new Item(
+ 'holiday',
+ 'Holiday foo',
+ 'Zone A',
+ null,
+ holiday1.start_date,
+ holiday1.end_date
);
-
- // Cleanup
- spyIsConcerned.mockRestore();
- spyGetDescription.mockRestore();
+ item1.addSubItem('Zone B', null, holiday2.start_date, holiday2.end_date);
+ expect(agenda.now[0].equals(item1)).toBe(true);
+ expect(agenda.next.length).equal(1);
+ const item2 = new Item(
+ 'holiday',
+ 'Holiday foo',
+ 'Zone A',
+ null,
+ holiday3.start_date,
+ holiday3.end_date
+ );
+ item2.addSubItem('Zone B', null, holiday4.start_date, holiday4.end_date);
+ expect(agenda.next[0].equals(item2)).toBe(true);
+ expect(agenda.next[0].subitems[0].period).toEqual('Du 7 au 22 février 2027');
+ expect(agenda.next[0].subitems[1].period).toEqual('Du 7 au 23 février 2027');
});
});
describe('OTV', () => {
@@ -767,7 +740,14 @@ describe('/agenda.ts', () => {
// Given
vi.stubEnv('TZ', 'Europe/Paris');
const newMockUserIdentity = JSON.parse(JSON.stringify(mockUserIdentity));
- newMockUserIdentity.dataDetails.address.postcode = '20000'; // Corse
+ newMockUserIdentity.address = new Address(
+ 'Bastia',
+ '2B, Haute-Corse, Corse',
+ '2B033',
+ 'Bastia',
+ 'Bastia',
+ '20200'
+ );
localStorage.setItem('user_identity', JSON.stringify(newMockUserIdentity));
const holiday1 = {
kind: 'holiday',
@@ -807,6 +787,47 @@ describe('/agenda.ts', () => {
).toBe(true);
expect(agenda.next.length).equal(0);
});
+ test('should not display OTV if holiday zones does not match user preferences', async () => {
+ // Given
+ vi.stubEnv('TZ', 'Europe/Paris');
+ const preferences = new Preferences(['Zone A'], []);
+ const newMockUserIdentity = JSON.parse(JSON.stringify(mockUserIdentity));
+ newMockUserIdentity.preferences = preferences;
+ newMockUserIdentity.address = new Address(
+ 'Bastia',
+ '2B, Haute-Corse, Corse',
+ '2B033',
+ 'Bastia',
+ 'Bastia',
+ '20200'
+ );
+ localStorage.setItem('user_identity', JSON.stringify(newMockUserIdentity));
+ const holiday1 = {
+ kind: 'holiday',
+ title: 'Holiday',
+ description: '',
+ date: null,
+ start_date: new Date('2026-02-06T23:00:00Z'),
+ end_date: new Date('2026-02-22T23:00:00Z'),
+ zones: ['Corse'],
+ emoji: 'foo',
+ };
+ await userStore.login(mockUserInfo);
+
+ // When
+ const agenda = new Agenda(
+ {
+ school_holidays: [holiday1],
+ public_holidays: [],
+ elections: [],
+ },
+ new Date('2026-02-01T12:00:00Z')
+ );
+
+ // Then
+ expect(agenda.now.length).equal(0);
+ expect(agenda.next.length).equal(0);
+ });
test('should not display past items or OTV related to past holidays', async () => {
// Given
vi.stubEnv('TZ', 'Europe/Paris');
@@ -904,7 +925,9 @@ describe('/agenda.ts', () => {
);
// Then
- expect(agenda.now.length).equal(3);
+ expect(agenda.now.length).equal(2);
+ expect(agenda.now[0].title).toEqual('Opération Tranquillité Vacances 🏠');
+ expect(agenda.now[1].title).toEqual('Holiday foo');
});
});
describe('Scheduled notifications', () => {
@@ -914,7 +937,11 @@ describe('/agenda.ts', () => {
const spy = vi
.spyOn(scheduledNotificationsMethods, 'createScheduledNotification')
.mockResolvedValue(true);
- localStorage.setItem('user_identity', JSON.stringify(mockUserIdentity));
+ // holidays are not displayed but otv have to be sent
+ const preferences = new Preferences(['Réunion'], []);
+ const newMockUserIdentity = JSON.parse(JSON.stringify(mockUserIdentity));
+ newMockUserIdentity.preferences = preferences;
+ localStorage.setItem('user_identity', JSON.stringify(newMockUserIdentity));
const holiday1 = {
kind: 'holiday',
title: 'Holiday',
@@ -958,7 +985,7 @@ describe('/agenda.ts', () => {
);
// Then
- expect(agenda.now.length).equal(3);
+ expect(agenda.now.length).equal(0);
expect(spy).toHaveBeenCalledTimes(2);
expect(spy).toHaveBeenCalledWith({
content_body:
@@ -1035,7 +1062,9 @@ describe('/agenda.ts', () => {
);
// Then
- expect(agenda.now.length).equal(3);
+ expect(agenda.now.length).equal(2);
+ expect(agenda.now[0].title).toEqual('Opération Tranquillité Vacances 🏠');
+ expect(agenda.now[1].title).toEqual('Holiday foo');
expect(spy).toHaveBeenCalledTimes(0);
});
});
diff --git a/public/mobile-app/src/lib/agenda.ts b/public/mobile-app/src/lib/agenda.ts
index 35e1d0bb..8916f05e 100644
--- a/public/mobile-app/src/lib/agenda.ts
+++ b/public/mobile-app/src/lib/agenda.ts
@@ -27,36 +27,19 @@ const slugify = (str: string): string => {
const oneday_in_ms = 24 * 60 * 60 * 1000;
-export class Item {
+export class SubItem {
constructor(
- private _kind: Kind,
- private _title: string,
private _description: string | null,
private _date: Date | null = null,
private _start_date: Date | null = null,
- private _end_date: Date | null = null,
- private _custom: boolean = false
+ private _end_date: Date | null = null
) {}
- equals(other: Item): boolean {
- if (!(other instanceof Item)) {
+ equals(other: SubItem): boolean {
+ if (!(other instanceof SubItem)) {
return false;
}
- return Object.entries(this).every(([key, thisValue]) => {
- const otherValue = other[key as keyof Item];
- // Special handling for Date objects
- if (thisValue instanceof Date || otherValue instanceof Date) {
- return (
- (thisValue as Date | null)?.getTime() ===
- (otherValue as Date | null)?.getTime()
- );
- }
- return thisValue === otherValue;
- });
- }
-
- get title(): string {
- return this._title;
+ return JSON.stringify(this) === JSON.stringify(other);
}
get description(): string | null {
@@ -67,28 +50,8 @@ export class Item {
return this._start_date || this._date;
}
- get dayName(): string | null {
- return this.date
- ? capitalizeFirstLetter(
- this.date.toLocaleDateString('fr-FR', { weekday: 'short' }).replace('.', '')
- )
- : null;
- }
-
- get fullDayName(): string | null {
- return this.date
- ? capitalizeFirstLetter(
- this.date.toLocaleDateString('fr-FR', { weekday: 'long' })
- )
- : null;
- }
-
- get dayNum(): number | null {
- return this.date ? this.date.getDate() : null;
- }
-
- get monthName(): string | null {
- return this.date ? monthName(this.date) : null;
+ get endDate(): Date | null {
+ return this._end_date;
}
get period(): string | undefined {
@@ -121,6 +84,101 @@ export class Item {
const date = this._date?.toLocaleDateString(locale, dateFormat);
return date;
}
+}
+
+export class Item {
+ private _subitems: SubItem[];
+
+ constructor(
+ private _kind: Kind,
+ private _title: string,
+ _description: string | null,
+ _date: Date | null = null,
+ _start_date: Date | null = null,
+ _end_date: Date | null = null
+ ) {
+ this._subitems = [];
+ this.addSubItem(_description, _date, _start_date, _end_date);
+ }
+
+ addSubItem(
+ _description: string | null,
+ _date: Date | null = null,
+ _start_date: Date | null = null,
+ _end_date: Date | null = null
+ ) {
+ this._subitems.push(new SubItem(_description, _date, _start_date, _end_date));
+ this._subitems.sort((a, b) => {
+ const dateComparison = (a.date?.getTime() || 0) - (b.date?.getTime() || 0);
+ if (dateComparison !== 0) {
+ return dateComparison;
+ }
+ return (a.endDate?.getTime() || 0) - (b.endDate?.getTime() || 0);
+ });
+ }
+
+ equals(other: Item): boolean {
+ if (!(other instanceof Item)) {
+ return false;
+ }
+ return JSON.stringify(this) === JSON.stringify(other);
+ }
+
+ get title(): string {
+ return this._title;
+ }
+
+ get description(): string | null {
+ return this._subitems[0].description;
+ }
+
+ get subitems(): SubItem[] {
+ return this._subitems;
+ }
+
+ get date(): Date | null {
+ return this._subitems[0].date;
+ }
+
+ get endDate(): Date | null {
+ const endDates = this._subitems
+ .map((subitem) => subitem.endDate)
+ .filter((date): date is Date => date != null);
+
+ if (!endDates.length) {
+ return null;
+ }
+
+ return new Date(Math.max(...endDates.map((d) => d.getTime())));
+ }
+
+ get dayName(): string | null {
+ return this.date
+ ? capitalizeFirstLetter(
+ this.date.toLocaleDateString('fr-FR', { weekday: 'short' }).replace('.', '')
+ )
+ : null;
+ }
+
+ get fullDayName(): string | null {
+ return this.date
+ ? capitalizeFirstLetter(
+ this.date.toLocaleDateString('fr-FR', { weekday: 'long' })
+ )
+ : null;
+ }
+
+ get dayNum(): number | null {
+ return this.date ? this.date.getDate() : null;
+ }
+
+ get monthName(): string | null {
+ return this.date ? monthName(this.date) : null;
+ }
+
+ get period(): string | undefined {
+ return this._subitems[0].period;
+ }
private static readonly KindInfo: Record<
Kind,
@@ -147,10 +205,6 @@ export class Item {
return this._kind;
}
- get custom(): boolean {
- return this._custom;
- }
-
get label(): string {
const info = Item.KindInfo[this._kind];
if (info === undefined) {
@@ -226,9 +280,39 @@ export class Agenda {
school_holidays: CatalogItem[],
date: Date
) {
+ const result: Item[] = [];
school_holidays.forEach((holiday) => {
- const item = this.createSchoolHolidayItem(holiday, date);
+ const item = this.createSchoolHolidayItem(holiday);
if (item !== null) {
+ // check if an item whith this description already exists
+ const key = JSON.stringify({
+ desc: item.title,
+ year: item.date?.getFullYear(),
+ });
+ let seen = false;
+ result.forEach((_item) => {
+ const _key = JSON.stringify({
+ desc: _item.title,
+ year: _item.date?.getFullYear(),
+ });
+ if (key === _key) {
+ _item.addSubItem(
+ item.description,
+ null,
+ holiday.start_date,
+ holiday.end_date
+ );
+ seen = true;
+ }
+ });
+ if (!seen) {
+ result.push(item);
+ }
+ }
+ });
+ result.forEach((item) => {
+ if (item.endDate !== null && item.endDate >= date) {
+ // exclude past school holiday
items.push(item);
}
});
@@ -241,23 +325,11 @@ export class Agenda {
return userStore.connected.getSchoolHolidayDescriptionFromPreferences(holiday);
}
- private getSchoolHolidayItemCustom(holiday: CatalogItem): boolean {
- const userZone = userStore.connected?.identity.address?.zone;
- if (userZone !== undefined && holiday.zones.includes(userZone)) {
- return true;
- }
- return false;
- }
-
- private createSchoolHolidayItem(holiday: CatalogItem, date: Date): Item | null {
+ private createSchoolHolidayItem(holiday: CatalogItem): Item | null {
if (!holiday.start_date || !holiday.end_date) {
// should not happen for school holiday
return null;
}
- if (holiday.end_date < date) {
- // exclude past school holiday
- return null;
- }
let title = holiday.title;
if (holiday.emoji) {
title += ` ${holiday.emoji}`;
@@ -271,8 +343,7 @@ export class Agenda {
this.getSchoolHolidayItemDescription(holiday),
null,
holiday.start_date,
- holiday.end_date,
- this.getSchoolHolidayItemCustom(holiday)
+ holiday.end_date
);
}
@@ -302,7 +373,7 @@ export class Agenda {
if (holiday.emoji) {
title += ` ${holiday.emoji}`;
}
- return new Item('holiday', title, null, holiday.date, null, null, false);
+ return new Item('holiday', title, null, holiday.date, null, null);
}
private createOTVItems(items: Item[], school_holidays: CatalogItem[], date: Date) {
@@ -356,8 +427,7 @@ export class Agenda {
'Inscrivez-vous pour protéger votre domicile pendant votre absence',
null,
startDate,
- null,
- false
+ null
);
const scheduledNotificationKey = `ami-otv:d-3w:${holiday.start_date.getFullYear()}:${slugify(holiday.title)}`;
if (!scheduledNotificationsCreatedKeys.has(scheduledNotificationKey)) {
@@ -372,6 +442,10 @@ export class Agenda {
});
userStore.connected?.addScheduledNotificationCreatedKey(scheduledNotificationKey);
}
+ if (!userStore.connected?.isSchoolHolidayConcernedByPreferences(holiday)) {
+ // don't display OTV if holiday match user preferences
+ return null;
+ }
if (startDate > date) {
// don't display OTV too early, only display them when they're close enough to their associated holiday
return null;
@@ -401,15 +475,7 @@ export class Agenda {
if (election.emoji) {
title += ` ${election.emoji}`;
}
- return new Item(
- 'election',
- title,
- election.description,
- election.date,
- null,
- null,
- false
- );
+ return new Item('election', title, election.description, election.date, null, null);
}
get now(): Item[] {
diff --git a/public/mobile-app/src/lib/components/AgendaItem.svelte b/public/mobile-app/src/lib/components/AgendaItem.svelte
index 7e9114dc..da302211 100644
--- a/public/mobile-app/src/lib/components/AgendaItem.svelte
+++ b/public/mobile-app/src/lib/components/AgendaItem.svelte
@@ -1,4 +1,5 @@