Skip to content

Commit edd6214

Browse files
committed
MOBILE-4313 notifications: Show warnings if permissions missing
1 parent 09a2bf3 commit edd6214

File tree

14 files changed

+294
-12
lines changed

14 files changed

+294
-12
lines changed

package-lock.json

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
"@moodlehq/cordova-plugin-inappbrowser": "5.0.0-moodle.3",
8282
"@moodlehq/cordova-plugin-intent": "2.2.0-moodle.3",
8383
"@moodlehq/cordova-plugin-ionic-webview": "5.0.0-moodle.3",
84-
"@moodlehq/cordova-plugin-local-notification": "0.9.0-moodle.11",
84+
"@moodlehq/cordova-plugin-local-notification": "0.9.0-moodle.12",
8585
"@moodlehq/cordova-plugin-qrscanner": "3.0.1-moodle.5",
8686
"@moodlehq/cordova-plugin-statusbar": "4.0.0-moodle.3",
8787
"@moodlehq/cordova-plugin-zip": "3.1.0-moodle.1",

scripts/langindex.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,6 +1752,8 @@
17521752
"core.errorsyncblocked": "local_moodlemobileapp",
17531753
"core.errorurlschemeinvalidscheme": "local_moodlemobileapp",
17541754
"core.errorurlschemeinvalidsite": "local_moodlemobileapp",
1755+
"core.exactalarmsturnedoff": "local_moodlemobileapp",
1756+
"core.exactalarmsturnedoffmessage": "local_moodlemobileapp",
17551757
"core.expand": "moodle",
17561758
"core.explanationdigitalminor": "moodle",
17571759
"core.favourites": "moodle",
@@ -2225,6 +2227,7 @@
22252227
"core.notenrolledprofile": "moodle",
22262228
"core.notice": "moodle",
22272229
"core.notingroup": "moodle",
2230+
"core.notnow": "local_moodlemobileapp",
22282231
"core.notsent": "local_moodlemobileapp",
22292232
"core.now": "moodle",
22302233
"core.nummore": "local_moodlemobileapp",
@@ -2499,6 +2502,10 @@
24992502
"core.today": "moodle",
25002503
"core.toggledelete": "local_moodlemobileapp",
25012504
"core.tryagain": "local_moodlemobileapp",
2505+
"core.turnon": "local_moodlemobileapp",
2506+
"core.turnonexactalarms": "local_moodlemobileapp",
2507+
"core.turnonnotifications": "local_moodlemobileapp",
2508+
"core.turnonnotificationsmessage": "local_moodlemobileapp",
25022509
"core.twoparagraphs": "local_moodlemobileapp",
25032510
"core.type": "repository",
25042511
"core.uhoh": "local_moodlemobileapp",

src/addons/calendar/pages/event/event.html

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,23 @@ <h1>
120120
</ion-item>
121121
</ion-list>
122122

123+
<ion-card class="core-warning-card core-card-with-buttons"
124+
*ngIf="remindersEnabled && event && !canScheduleExactAlarms && !scheduleExactWarningHidden">
125+
<ion-item class="ion-text-wrap">
126+
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true" />
127+
<ion-label>
128+
<p><strong>{{ 'core.exactalarmsturnedoff' | translate }}</strong></p>
129+
<p>{{ 'core.exactalarmsturnedoffmessage' | translate }}</p>
130+
</ion-label>
131+
</ion-item>
132+
<div class="core-card-buttons">
133+
<ion-button fill="clear" (click)="hideAlarmWarning()">
134+
{{ 'core.dontshowagain' | translate | coreNoPeriod }}
135+
</ion-button>
136+
<ion-button fill="outline" (click)="openAlarmSettings()">{{ 'core.turnon' | translate }}</ion-button>
137+
</div>
138+
</ion-card>
139+
123140
<ion-card *ngIf="remindersEnabled && event">
124141
<ion-item>
125142
<ion-label>

src/addons/calendar/pages/event/event.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ import { AddonCalendarEventsSource } from '@addons/calendar/classes/events-sourc
4040
import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager';
4141
import { CoreReminders, CoreRemindersService } from '@features/reminders/services/reminders';
4242
import { CoreRemindersSetReminderMenuComponent } from '@features/reminders/components/set-reminder-menu/set-reminder-menu';
43+
import { CoreLocalNotifications } from '@services/local-notifications';
44+
import { CorePlatform } from '@services/platform';
45+
import { CoreConfig } from '@services/config';
4346

4447
/**
4548
* Page that displays a single calendar event.
@@ -61,6 +64,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
6164
protected defaultTimeChangedObserver: CoreEventObserver;
6265
protected currentSiteId: string;
6366
protected updateCurrentTime?: number;
67+
protected appResumeSubscription: Subscription;
6468

6569
eventLoaded = false;
6670
event?: AddonCalendarEventToDisplay;
@@ -78,6 +82,8 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
7882
hasOffline = false;
7983
isOnline = false;
8084
syncIcon = CoreConstants.ICON_LOADING; // Sync icon.
85+
canScheduleExactAlarms = true;
86+
scheduleExactWarningHidden = false;
8187

8288
constructor(
8389
protected route: ActivatedRoute,
@@ -138,6 +144,11 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
138144
this.updateCurrentTime = window.setInterval(() => {
139145
this.currentTime = CoreTimeUtils.timestamp();
140146
}, 5000);
147+
148+
this.checkExactAlarms();
149+
this.appResumeSubscription = CorePlatform.resume.subscribe(() => {
150+
this.checkExactAlarms();
151+
});
141152
}
142153

143154
/**
@@ -153,6 +164,14 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
153164
this.reminders = await AddonCalendarHelper.getEventReminders(this.eventId, this.event.timestart, this.currentSiteId);
154165
}
155166

167+
/**
168+
* Check if the app can schedule exact alarms.
169+
*/
170+
protected async checkExactAlarms(): Promise<void> {
171+
this.scheduleExactWarningHidden = !!(await CoreConfig.get(CoreConstants.DONT_SHOW_EXACT_ALARMS_WARNING, 0));
172+
this.canScheduleExactAlarms = await CoreLocalNotifications.canScheduleExactAlarms();
173+
}
174+
156175
/**
157176
* @inheritdoc
158177
*/
@@ -616,6 +635,21 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
616635
}
617636
}
618637

638+
/**
639+
* Open alarm settings.
640+
*/
641+
openAlarmSettings(): void {
642+
CoreLocalNotifications.openAlarmSettings();
643+
}
644+
645+
/**
646+
* Hide alarm warning.
647+
*/
648+
hideAlarmWarning(): void {
649+
CoreConfig.set(CoreConstants.DONT_SHOW_EXACT_ALARMS_WARNING, 1);
650+
this.scheduleExactWarningHidden = true;
651+
}
652+
619653
/**
620654
* @inheritdoc
621655
*/
@@ -626,6 +660,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
626660
this.onlineObserver.unsubscribe();
627661
this.newEventObserver.off();
628662
this.events?.destroy();
663+
this.appResumeSubscription.unsubscribe();
629664
clearInterval(this.updateCurrentTime);
630665
}
631666

src/addons/notifications/pages/list/list.html

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,23 @@ <h1>{{ 'addon.notifications.notifications' | translate }}</h1>
1717
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
1818
</ion-refresher>
1919
<core-loading [hideUntil]="notifications.loaded">
20+
<ion-card class="core-warning-card core-card-with-buttons" *ngIf="!hasNotificationsPermission && !permissionWarningHidden">
21+
<ion-item class="ion-text-wrap">
22+
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true" />
23+
<ion-label>
24+
<p><strong>{{ 'core.turnonnotifications' | translate }}</strong></p>
25+
<p>{{ 'core.turnonnotificationsmessage' | translate }}</p>
26+
</ion-label>
27+
</ion-item>
28+
<div class="core-card-buttons">
29+
<ion-button fill="clear" (click)="hidePermissionWarning()">
30+
{{ 'core.dontshowagain' | translate | coreNoPeriod }}
31+
</ion-button>
32+
<ion-button fill="outline" (click)="openSettings()">{{ 'core.turnon' | translate }}</ion-button>
33+
</div>
34+
</ion-card>
2035

21-
<ion-item *ngFor="let notification of notifications.items" class="ion-text-wrap"
36+
<ion-item *ngFor="let notification of notifications.items" class="ion-text-wrap addon-notification-item"
2237
[attr.aria-current]="notifications.getItemAriaCurrent(notification)" (click)="notifications.select(notification)" button
2338
[detail]="false" lines="full">
2439

src/addons/notifications/pages/list/list.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@use "theme/globals" as *;
22

3-
ion-item {
3+
ion-item.addon-notification-item {
44
ion-label {
55
margin-top: 8px;
66
margin-bottom: 8px;

src/addons/notifications/pages/list/list.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ import { CoreTimeUtils } from '@services/utils/time';
3131
import { AddonNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source';
3232
import { CoreListItemsManager } from '@classes/items-management/list-items-manager';
3333
import { AddonLegacyNotificationsNotificationsSource } from '@addons/notifications/classes/legacy-notifications-source';
34+
import { CoreLocalNotifications } from '@services/local-notifications';
35+
import { CoreConfig } from '@services/config';
36+
import { CoreConstants } from '@/core/constants';
37+
import { CorePlatform } from '@services/platform';
3438

3539
/**
3640
* Page that displays the list of notifications.
@@ -47,12 +51,15 @@ export class AddonNotificationsListPage implements AfterViewInit, OnDestroy {
4751
fetchMoreNotificationsFailed = false;
4852
canMarkAllNotificationsAsRead = false;
4953
loadingMarkAllNotificationsAsRead = false;
54+
hasNotificationsPermission = true;
55+
permissionWarningHidden = false;
5056

5157
protected isCurrentView?: boolean;
5258
protected cronObserver?: CoreEventObserver;
5359
protected readObserver?: CoreEventObserver;
5460
protected pushObserver?: Subscription;
5561
protected pendingRefresh = false;
62+
protected appResumeSubscription?: Subscription;
5663

5764
constructor() {
5865
try {
@@ -67,7 +74,14 @@ export class AddonNotificationsListPage implements AfterViewInit, OnDestroy {
6774
} catch(error) {
6875
CoreDomUtils.showErrorModal(error);
6976
CoreNavigator.back();
77+
78+
return;
7079
}
80+
81+
this.checkPermission();
82+
this.appResumeSubscription = CorePlatform.resume.subscribe(() => {
83+
this.checkPermission();
84+
});
7185
}
7286

7387
/**
@@ -120,6 +134,14 @@ export class AddonNotificationsListPage implements AfterViewInit, OnDestroy {
120134
deepLinkManager.treatLink();
121135
}
122136

137+
/**
138+
* Check if the app has permission to display notifications.
139+
*/
140+
protected async checkPermission(): Promise<void> {
141+
this.permissionWarningHidden = !!(await CoreConfig.get(CoreConstants.DONT_SHOW_NOTIFICATIONS_PERMISSION_WARNING, 0));
142+
this.hasNotificationsPermission = await CoreLocalNotifications.hasNotificationsPermission();
143+
}
144+
123145
/**
124146
* Convenience function to get notifications. Gets unread notifications first.
125147
*
@@ -211,6 +233,21 @@ export class AddonNotificationsListPage implements AfterViewInit, OnDestroy {
211233
refresher?.complete();
212234
}
213235

236+
/**
237+
* Open notification settings.
238+
*/
239+
openSettings(): void {
240+
CoreLocalNotifications.openNotificationSettings();
241+
}
242+
243+
/**
244+
* Hide permission warning.
245+
*/
246+
hidePermissionWarning(): void {
247+
CoreConfig.set(CoreConstants.DONT_SHOW_NOTIFICATIONS_PERMISSION_WARNING, 1);
248+
this.permissionWarningHidden = true;
249+
}
250+
214251
/**
215252
* User entered the page.
216253
*/
@@ -241,6 +278,7 @@ export class AddonNotificationsListPage implements AfterViewInit, OnDestroy {
241278
this.readObserver?.off();
242279
this.pushObserver?.unsubscribe();
243280
this.notifications?.destroy();
281+
this.appResumeSubscription?.unsubscribe();
244282
}
245283

246284
}

src/core/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ export class CoreConstants {
155155

156156
// Other constants.
157157
static readonly CALENDAR_DEFAULT_STARTING_WEEKDAY = 1;
158+
static readonly DONT_SHOW_NOTIFICATIONS_PERMISSION_WARNING = 'CoreDontShowNotificationsPermissionWarning';
159+
static readonly DONT_SHOW_EXACT_ALARMS_WARNING = 'CoreDontShowScheduleExactWarning';
160+
static readonly EXACT_ALARMS_WARNING_DISPLAYED = 'CoreScheduleExactWarningModalDisplayed';
158161

159162
// Config & environment constants.
160163
static readonly CONFIG = { ...envJson.config } as unknown as EnvironmentConfig; // Data parsed from config.json files.

src/core/lang.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@
124124
"errorsyncblocked": "This {{$a}} cannot be synchronised right now because of an ongoing process. Please try again later. If the problem persists, try restarting the app.",
125125
"errorurlschemeinvalidscheme": "This URL is meant to be used in another app: {{$a}}.",
126126
"errorurlschemeinvalidsite": "This site URL cannot be opened in this app.",
127+
"exactalarmsturnedoff": "Real-time notifications are turned off",
128+
"exactalarmsturnedoffmessage": "To make sure you don't miss any important alerts, turn on 'Alarms and reminders' in your device's settings.",
127129
"expand": "Expand",
128130
"explanationdigitalminor": "This information is required to determine if your age is over the digital age of consent. This is the age when an individual can consent to terms and conditions and their data being legally stored and processed.",
129131
"favourites": "Starred",
@@ -225,6 +227,7 @@
225227
"notenrolledprofile": "This profile is not available because this user is not enrolled in this course.",
226228
"notice": "Notice",
227229
"notingroup": "Sorry, but you need to be part of a group to see this page.",
230+
"notnow": "Not now",
228231
"notsent": "Not sent",
229232
"now": "now",
230233
"nummore": "{{$a}} more",
@@ -333,6 +336,10 @@
333336
"today": "Today",
334337
"toggledelete": "Toggle delete buttons",
335338
"tryagain": "Try again",
339+
"turnon": "Turn on",
340+
"turnonexactalarms": "Turn on real-time alerts",
341+
"turnonnotifications": "Turn on notifications",
342+
"turnonnotificationsmessage": "Would you like to receive notifications about activities and assignments?",
336343
"twoparagraphs": "{{p1}}<br><br>{{p2}}",
337344
"type": "Type",
338345
"uhoh": "Uh oh!",

0 commit comments

Comments
 (0)