MOBILE-3936 calendar: Use new reminder selector

main
Pau Ferrer Ocaña 2022-09-14 17:17:07 +02:00
parent cefd0248fc
commit 9f154a7bb6
20 changed files with 514 additions and 363 deletions

View File

@ -19,25 +19,20 @@ import { CoreSharedModule } from '@/core/shared.module';
import { AddonCalendarCalendarComponent } from './calendar/calendar';
import { AddonCalendarUpcomingEventsComponent } from './upcoming-events/upcoming-events';
import { AddonCalendarFilterComponent } from './filter/filter';
import { AddonCalendarReminderTimeModalComponent } from './reminder-time-modal/reminder-time-modal';
@NgModule({
declarations: [
AddonCalendarCalendarComponent,
AddonCalendarUpcomingEventsComponent,
AddonCalendarFilterComponent,
AddonCalendarReminderTimeModalComponent,
],
imports: [
CoreSharedModule,
],
providers: [
],
exports: [
AddonCalendarCalendarComponent,
AddonCalendarUpcomingEventsComponent,
AddonCalendarFilterComponent,
AddonCalendarReminderTimeModalComponent,
],
})
export class AddonCalendarComponentsModule {}

View File

@ -1,62 +0,0 @@
<ion-header>
<ion-toolbar>
<ion-title>
<h2>{{ 'addon.calendar.reminders' | translate }}</h2>
</ion-title>
<ion-buttons slot="end">
<ion-button fill="clear" (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
<ion-icon slot="icon-only" name="fas-times" aria-hidden="true"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<form (ngSubmit)="saveReminder()">
<ion-radio-group name="radiovalue" [(ngModel)]="radioValue" class="ion-text-wrap">
<!-- Preset options. -->
<ion-item *ngIf="allowDisable">
<ion-label>
<p>{{ 'core.settings.disabled' | translate }}</p>
</ion-label>
<ion-radio slot="end" value="disabled"></ion-radio>
</ion-item>
<ion-item *ngFor="let option of presetOptions">
<ion-label>
<p>{{ option.label }}</p>
</ion-label>
<ion-radio slot="end" [value]="option.radioValue"></ion-radio>
</ion-item>
<!-- Custom value. -->
<ion-item class="ion-text-wrap">
<ion-label>
<p>{{ 'core.custom' | translate }}</p>
</ion-label>
<ion-radio slot="end" value="custom"></ion-radio>
</ion-item>
<ion-item class="ion-text-wrap" (click)="customInputClicked($event)">
<ion-label></ion-label>
<div class="flex-row">
<!-- Input to enter the value. -->
<ion-input type="number" name="customvalue" [(ngModel)]="customValue" [disabled]="radioValue != 'custom'"
placeholder="10" (click)="customInputClicked($event)">
</ion-input>
<!-- Units. -->
<label class="accesshide" for="reminderUnits">{{ 'addon.calendar.units' | translate }}</label>
<ion-select id="reminderUnits" name="customunits" [(ngModel)]="customUnits" interface="action-sheet"
[disabled]="radioValue != 'custom'" slot="end" [interfaceOptions]="{header: 'addon.calendar.units' | translate}">
<ion-select-option *ngFor="let option of customUnitsOptions" [value]="option.value">
{{ option.label | translate }}
</ion-select-option>
</ion-select>
</div>
</ion-item>
</ion-radio-group>
<ion-button type="submit" class="ion-margin" expand="block" [disabled]="radioValue == 'custom' && !customValue">
{{ 'core.done' | translate }}
</ion-button>
</form>
</ion-content>

View File

@ -1,174 +0,0 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { AddonCalendar, AddonCalendarReminderUnits, AddonCalendarValueAndUnit } from '@addons/calendar/services/calendar';
import { Component, Input, OnInit } from '@angular/core';
import { CoreDomUtils } from '@services/utils/dom';
import { ModalController } from '@singletons';
/**
* Modal to choose a reminder time.
*/
@Component({
selector: 'addon-calendar-new-reminder-modal',
templateUrl: 'reminder-time-modal.html',
})
export class AddonCalendarReminderTimeModalComponent implements OnInit {
@Input() initialValue?: AddonCalendarValueAndUnit;
@Input() allowDisable?: boolean;
radioValue = '5m';
customValue = '10';
customUnits = AddonCalendarReminderUnits.MINUTE;
presetOptions = [
{
radioValue: '5m',
value: 5,
unit: AddonCalendarReminderUnits.MINUTE,
label: '',
},
{
radioValue: '10m',
value: 10,
unit: AddonCalendarReminderUnits.MINUTE,
label: '',
},
{
radioValue: '30m',
value: 30,
unit: AddonCalendarReminderUnits.MINUTE,
label: '',
},
{
radioValue: '1h',
value: 1,
unit: AddonCalendarReminderUnits.HOUR,
label: '',
},
{
radioValue: '12h',
value: 12,
unit: AddonCalendarReminderUnits.HOUR,
label: '',
},
{
radioValue: '1d',
value: 1,
unit: AddonCalendarReminderUnits.DAY,
label: '',
},
];
customUnitsOptions = [
{
value: AddonCalendarReminderUnits.MINUTE,
label: 'core.minutes',
},
{
value: AddonCalendarReminderUnits.HOUR,
label: 'core.hours',
},
{
value: AddonCalendarReminderUnits.DAY,
label: 'core.days',
},
{
value: AddonCalendarReminderUnits.WEEK,
label: 'core.weeks',
},
];
/**
* @inheritdoc
*/
async ngOnInit(): Promise<void> {
this.presetOptions.forEach((option) => {
option.label = AddonCalendar.getUnitValueLabel(option.value, option.unit);
});
if (!this.initialValue) {
return;
}
if (this.initialValue.value === 0) {
this.radioValue = 'disabled';
} else {
// Search if it's one of the preset options.
const option = this.presetOptions.find(option =>
option.value === this.initialValue?.value && option.unit === this.initialValue.unit);
if (option) {
this.radioValue = option.radioValue;
} else {
// It's a custom value.
this.radioValue = 'custom';
this.customValue = String(this.initialValue.value);
this.customUnits = this.initialValue.unit;
}
}
}
/**
* Close the modal.
*/
closeModal(): void {
ModalController.dismiss();
}
/**
* Save the reminder.
*/
saveReminder(): void {
if (this.radioValue === 'disabled') {
ModalController.dismiss(0);
} else if (this.radioValue === 'custom') {
const value = parseInt(this.customValue, 10);
if (!value) {
CoreDomUtils.showErrorModal('core.errorinvalidform', true);
return;
}
ModalController.dismiss(Math.abs(value) * this.customUnits);
} else {
const option = this.presetOptions.find(option => option.radioValue === this.radioValue);
if (!option) {
return;
}
ModalController.dismiss(option.unit * option.value);
}
}
/**
* Custom value input clicked.
*
* @param ev Click event.
*/
async customInputClicked(ev: Event): Promise<void> {
if (this.radioValue === 'custom') {
return;
}
this.radioValue = 'custom';
const target = <HTMLInputElement | HTMLElement | null> ev.target;
if (target) {
CoreDomUtils.focusElement(target);
}
}
}

View File

@ -41,7 +41,6 @@
"noevents": "There are no events",
"nopermissiontoupdatecalendar": "Sorry, but you do not have permission to update the calendar event.",
"reminders": "Reminders",
"units": "Units",
"repeatedevents": "Repeated events",
"repeateditall": "Also apply changes to the other {{$a}} events in this repeat series",
"repeateditthis": "Apply changes to this event only",
@ -55,7 +54,6 @@
"sunday": "Sunday",
"thu": "Thu",
"thursday": "Thursday",
"timebefore": "{{value}} {{units}} before",
"today": "Today",
"tomorrow": "Tomorrow",
"tue": "Tue",

View File

@ -31,7 +31,6 @@ import {
AddonCalendarEventType,
AddonCalendar,
AddonCalendarSubmitCreateUpdateFormDataWSParams,
AddonCalendarReminderUnits,
} from '../../services/calendar';
import { AddonCalendarOffline } from '../../services/calendar-offline';
import { AddonCalendarEventTypeOption, AddonCalendarHelper } from '../../services/calendar-helper';
@ -45,7 +44,8 @@ import { CoreNavigator } from '@services/navigator';
import { CanLeave } from '@guards/can-leave';
import { CoreForms } from '@singletons/form';
import { CoreLocalNotifications } from '@services/local-notifications';
import { AddonCalendarReminderTimeModalComponent } from '@addons/calendar/components/reminder-time-modal/reminder-time-modal';
import { CoreReminders, CoreRemindersService, CoreRemindersUnits } from '@features/reminders/services/reminders';
import { CoreRemindersSetReminderMenuComponent } from '@features/reminders/components/set-reminder-menu/set-reminder-menu';
/**
* Page that displays a form to create/edit an event.
@ -134,7 +134,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
}
/**
* Component being initialized.
* @inheritdoc
*/
ngOnInit(): void {
this.eventId = CoreNavigator.getRouteNumberParam('eventId') || undefined;
@ -657,13 +657,13 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
return;
}
const data = AddonCalendarProvider.convertSecondsToValueAndUnit(defaultTime);
const data = CoreRemindersService.convertSecondsToValueAndUnit(defaultTime);
// Add default reminder.
this.reminders.push({
value: data.value,
unit: data.unit,
label: AddonCalendar.getUnitValueLabel(data.value, data.unit, true),
label: CoreReminders.getUnitValueLabel(data.value, data.unit, true),
});
}
@ -671,8 +671,9 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
* Add a reminder.
*/
async addReminder(): Promise<void> {
const reminderTime = await CoreDomUtils.openModal<number>({
component: AddonCalendarReminderTimeModalComponent,
const reminderTime = await CoreDomUtils.openPopover<{timeBefore: number}>({
component: CoreRemindersSetReminderMenuComponent,
// TODO: Add event to open the popover in place.
});
if (reminderTime === undefined) {
@ -680,14 +681,14 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
return;
}
const data = AddonCalendarProvider.convertSecondsToValueAndUnit(reminderTime);
const data = CoreRemindersService.convertSecondsToValueAndUnit(reminderTime.timeBefore);
// Add reminder.
this.reminders.push({
time: reminderTime,
time: reminderTime.timeBefore,
value: data.value,
unit: data.unit,
label: AddonCalendar.getUnitValueLabel(data.value, data.unit),
label: CoreReminders.getUnitValueLabel(data.value, data.unit),
});
}
@ -704,7 +705,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
}
/**
* Page destroyed.
* @inheritdoc
*/
ngOnDestroy(): void {
this.unblockSync();
@ -716,6 +717,6 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
type AddonCalendarEventCandidateReminder = {
time?: number; // Undefined for default reminder.
value: number; // Amount of time.
unit: AddonCalendarReminderUnits; // Units.
unit: CoreRemindersUnits; // Units.
label: string; // Label to represent the reminder.
};

View File

@ -38,11 +38,11 @@ import { CoreNavigator } from '@services/navigator';
import { CoreUtils } from '@services/utils/utils';
import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router';
import { CoreConstants } from '@/core/constants';
import { AddonCalendarReminderTimeModalComponent } from '@addons/calendar/components/reminder-time-modal/reminder-time-modal';
import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker';
import { AddonCalendarEventsSource } from '@addons/calendar/classes/events-source';
import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager';
import { CoreReminders } from '@features/reminders/services/reminders';
import { CoreRemindersSetReminderMenuComponent } from '@features/reminders/components/set-reminder-menu/set-reminder-menu';
/**
* Page that displays a single calendar event.
@ -157,7 +157,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
}
/**
* View loaded.
* @inheritdoc
*/
async ngOnInit(): Promise<void> {
try {
@ -387,8 +387,9 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
return;
}
const reminderTime = await CoreDomUtils.openModal<number>({
component: AddonCalendarReminderTimeModalComponent,
const reminderTime = await CoreDomUtils.openPopover<{timeBefore: number}>({
component: CoreRemindersSetReminderMenuComponent,
// TODO: Add event to open the popover in place.
});
if (reminderTime === undefined) {
@ -396,7 +397,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
return;
}
await AddonCalendar.addEventReminder(this.event, reminderTime, this.currentSiteId);
await AddonCalendar.addEventReminder(this.event, reminderTime.timeBefore, this.currentSiteId);
await this.loadReminders();
}
@ -636,7 +637,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
}
/**
* Page destroyed.
* @inheritdoc
*/
ngOnDestroy(): void {
this.editEventObserver.off();

View File

@ -16,13 +16,17 @@ import { Component, OnInit } from '@angular/core';
import {
AddonCalendar,
AddonCalendarProvider,
AddonCalendarReminderUnits,
AddonCalendarValueAndUnit,
} from '../../services/calendar';
import { CoreEvents } from '@singletons/events';
import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
import { AddonCalendarReminderTimeModalComponent } from '@addons/calendar/components/reminder-time-modal/reminder-time-modal';
import {
CoreReminders,
CoreRemindersService,
CoreRemindersUnits,
CoreReminderValueAndUnit,
} from '@features/reminders/services/reminders';
import { CoreRemindersSetReminderMenuComponent } from '@features/reminders/components/set-reminder-menu/set-reminder-menu';
/**
* Page that displays the calendar settings.
@ -35,19 +39,16 @@ export class AddonCalendarSettingsPage implements OnInit {
defaultTimeLabel = '';
protected defaultTime: AddonCalendarValueAndUnit = {
protected defaultTime: CoreReminderValueAndUnit = {
value: 0,
unit: AddonCalendarReminderUnits.MINUTE,
unit: CoreRemindersUnits.MINUTE,
};
/**
* View loaded.
* @inheritdoc
*/
async ngOnInit(): Promise<void> {
const defaultTime = await AddonCalendar.getDefaultNotificationTime();
this.defaultTime = AddonCalendarProvider.convertSecondsToValueAndUnit(defaultTime);
this.defaultTimeLabel = AddonCalendar.getUnitValueLabel(this.defaultTime.value, this.defaultTime.unit);
this.updateDefaultTimeLabel();
}
/**
@ -61,12 +62,13 @@ export class AddonCalendarSettingsPage implements OnInit {
e.stopImmediatePropagation();
e.preventDefault();
const reminderTime = await CoreDomUtils.openModal<number>({
component: AddonCalendarReminderTimeModalComponent,
const reminderTime = await CoreDomUtils.openPopover<{timeBefore: number}>({
component: CoreRemindersSetReminderMenuComponent,
componentProps: {
initialValue: this.defaultTime,
allowDisable: true,
noReminderLabel: 'core.settings.disabled',
},
event: e,
});
if (reminderTime === undefined) {
@ -74,25 +76,24 @@ export class AddonCalendarSettingsPage implements OnInit {
return;
}
this.defaultTime = AddonCalendarProvider.convertSecondsToValueAndUnit(reminderTime);
this.defaultTimeLabel = AddonCalendar.getUnitValueLabel(this.defaultTime.value, this.defaultTime.unit);
this.updateDefaultTime(reminderTime);
}
/**
* Update default time.
*
* @param newTime New time.
*/
updateDefaultTime(newTime: number): void {
AddonCalendar.setDefaultNotificationTime(newTime);
await AddonCalendar.setDefaultNotificationTime(reminderTime.timeBefore);
this.updateDefaultTimeLabel();
CoreEvents.trigger(
AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED,
{ time: newTime },
{ time: reminderTime.timeBefore },
CoreSites.getCurrentSiteId(),
);
}
/**
* Update default time label.
*/
async updateDefaultTimeLabel(): Promise<void> {
const defaultTime = await AddonCalendar.getDefaultNotificationTime();
this.defaultTime = CoreRemindersService.convertSecondsToValueAndUnit(defaultTime);
this.defaultTimeLabel = CoreReminders.getUnitValueLabel(this.defaultTime.value, this.defaultTime.unit);
}
}

View File

@ -337,8 +337,8 @@ export class AddonCalendarHelperProvider {
let defaultLabel: string | undefined;
if (defaultTime > AddonCalendarProvider.DEFAULT_NOTIFICATION_DISABLED) {
const data = AddonCalendarProvider.convertSecondsToValueAndUnit(defaultTime);
defaultLabel = AddonCalendar.getUnitValueLabel(data.value, data.unit, true);
const data = CoreRemindersService.convertSecondsToValueAndUnit(defaultTime);
defaultLabel = CoreReminders.getUnitValueLabel(data.value, data.unit, true);
}
return reminders.map((reminder) => {
@ -353,8 +353,8 @@ export class AddonCalendarHelperProvider {
formatted.timestamp = eventTimestart - defaultTime;
}
} else {
const data = AddonCalendarProvider.convertSecondsToValueAndUnit(reminder.timebefore);
formatted.label = AddonCalendar.getUnitValueLabel(data.value, data.unit, false);
const data = CoreRemindersService.convertSecondsToValueAndUnit(reminder.timebefore);
formatted.label = CoreReminders.getUnitValueLabel(data.value, data.unit, false);
formatted.timestamp = eventTimestart - reminder.timebefore;
}

View File

@ -46,6 +46,8 @@ import {
CoreReminders,
CoreRemindersPushNotificationData,
CoreRemindersService,
CoreRemindersUnits,
CoreReminderValueAndUnit,
} from '@features/reminders/services/reminders';
import { CoreReminderDBRecord } from '@features/reminders/services/database/reminders';
@ -64,6 +66,8 @@ export enum AddonCalendarEventType {
/**
* Units to set a reminder.
*
* @deprecated since 4.1 Use CoreReminderUnits instead.
*/
export enum AddonCalendarReminderUnits {
MINUTE = CoreConstants.SECONDS_MINUTE,
@ -91,21 +95,6 @@ declare module '@singletons/events' {
}
const REMINDER_UNITS_LABELS = {
single: {
[AddonCalendarReminderUnits.MINUTE]: 'core.minute',
[AddonCalendarReminderUnits.HOUR]: 'core.hour',
[AddonCalendarReminderUnits.DAY]: 'core.day',
[AddonCalendarReminderUnits.WEEK]: 'core.week',
},
multi: {
[AddonCalendarReminderUnits.MINUTE]: 'core.minutes',
[AddonCalendarReminderUnits.HOUR]: 'core.hours',
[AddonCalendarReminderUnits.DAY]: 'core.days',
[AddonCalendarReminderUnits.WEEK]: 'core.weeks',
},
};
/**
* Service to handle calendar events.
*/
@ -127,7 +116,7 @@ export class AddonCalendarProvider {
static readonly CALENDAR_TF_24 = '%H:%M'; // Calendar time in 24 hours format.
static readonly CALENDAR_TF_12 = '%I:%M %p'; // Calendar time in 12 hours format.
static readonly DEFAULT_NOTIFICATION_DISABLED = 0;
static readonly DEFAULT_NOTIFICATION_DISABLED = -1;
protected weekDays: AddonCalendarWeekDaysTranslationKeys[] = [
{
@ -196,34 +185,10 @@ export class AddonCalendarProvider {
*
* @param seconds Number of seconds.
* @return Value and unit.
* @deprecated since 4.1 Use CoreRemindersService.convertSecondsToValueAndUnit instead.
*/
static convertSecondsToValueAndUnit(seconds: number): AddonCalendarValueAndUnit {
if (seconds <= 0) {
return {
value: 0,
unit: AddonCalendarReminderUnits.MINUTE,
};
} else if (seconds % AddonCalendarReminderUnits.WEEK === 0) {
return {
value: seconds / AddonCalendarReminderUnits.WEEK,
unit: AddonCalendarReminderUnits.WEEK,
};
} else if (seconds % AddonCalendarReminderUnits.DAY === 0) {
return {
value: seconds / AddonCalendarReminderUnits.DAY,
unit: AddonCalendarReminderUnits.DAY,
};
} else if (seconds % AddonCalendarReminderUnits.HOUR === 0) {
return {
value: seconds / AddonCalendarReminderUnits.HOUR,
unit: AddonCalendarReminderUnits.HOUR,
};
} else {
return {
value: seconds / AddonCalendarReminderUnits.MINUTE,
unit: AddonCalendarReminderUnits.MINUTE,
};
}
static convertSecondsToValueAndUnit(seconds: number): CoreReminderValueAndUnit {
return CoreRemindersService.convertSecondsToValueAndUnit(seconds);
}
/**
@ -1140,26 +1105,10 @@ export class AddonCalendarProvider {
* @param unit Unit.
* @param addDefaultLabel Whether to add the "Default" text.
* @return Translated label.
* @deprecated since 4.1 Use CoreReminders.getUnitValueLabel instead.
*/
getUnitValueLabel(value: number, unit: AddonCalendarReminderUnits, addDefaultLabel = false): string {
if (value === 0) {
return Translate.instant('core.settings.disabled');
}
const unitsLabel = value === 1 ?
REMINDER_UNITS_LABELS.single[unit] :
REMINDER_UNITS_LABELS.multi[unit];
const label = Translate.instant('addon.calendar.timebefore', {
units: Translate.instant(unitsLabel),
value: value,
});
if (addDefaultLabel) {
return Translate.instant('core.defaultvalue', { $a: label });
}
return label;
getUnitValueLabel(value: number, unit: CoreRemindersUnits, addDefaultLabel = false): string {
return CoreReminders.getUnitValueLabel(value, unit, addDefaultLabel);
}
/**
@ -2318,11 +2267,10 @@ export type AddonCalendarUpdatedEventEvent = {
/**
* Value and unit for reminders.
*
* @deprecated since 4.1, use CoreReminderValueAndUnit instead.
*/
export type AddonCalendarValueAndUnit = {
value: number;
unit: AddonCalendarReminderUnits;
};
export type AddonCalendarValueAndUnit = CoreReminderValueAndUnit;
/**
* Options to pass to submit event.

View File

@ -98,7 +98,6 @@ export class CoreContentLinksHelperProvider {
* @param pageName Name of the page to go.
* @param pageParams Params to send to the page.
* @param siteId Site ID. If not defined, current site.
* @param checkMenu If true, check if the root page of a main menu tab. Only the page name will be checked.
* @return Promise resolved when done.
* @deprecated since 3.9.5. Use CoreNavigator.navigateToSitePath instead.
*/

View File

@ -0,0 +1,33 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { CoreSharedModule } from '@/core/shared.module';
import { NgModule } from '@angular/core';
import { CoreRemindersSetReminderCustomComponent } from './set-reminder-custom/set-reminder-custom';
import { CoreRemindersSetReminderMenuComponent } from './set-reminder-menu/set-reminder-menu';
@NgModule({
declarations: [
CoreRemindersSetReminderMenuComponent,
CoreRemindersSetReminderCustomComponent,
],
imports: [
CoreSharedModule,
],
exports: [
CoreRemindersSetReminderMenuComponent,
CoreRemindersSetReminderCustomComponent,
],
})
export class CoreRemindersComponentsModule {}

View File

@ -0,0 +1,37 @@
<ion-header>
<ion-toolbar>
<ion-title>
<h2>{{ 'core.reminders.customreminder' | translate }}</h2>
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-item class="ion-text-wrap">
<div class="flex-row">
<!-- Input to enter the value. -->
<ion-input type="number" name="customvalue" [(ngModel)]="customValue" placeholder="10">
</ion-input>
<!-- Units. -->
<label class="accesshide" for="reminderUnits">{{ 'core.reminders.units' | translate }}</label>
<ion-select id="reminderUnits" name="customunits" [(ngModel)]="customUnits" interface="action-sheet" slot="end"
[interfaceOptions]="{header: 'core.reminders.units' | translate}">
<ion-select-option *ngFor="let option of customUnitsOptions" [value]="option.value">
{{ option.label | translate }}
</ion-select-option>
</ion-select>
</div>
</ion-item>
<ion-grid>
<ion-row>
<ion-col>
<ion-button expand="block" fill="outline" (click)="cancel()">{{ 'core.cancel' | translate }}</ion-button>
</ion-col>
<ion-col>
<ion-button expand="block" (click)="set()">
{{ 'core.done' | translate }}
</ion-button>
</ion-col>
</ion-row>
</ion-grid>
</ion-content>

View File

@ -0,0 +1,64 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, Input } from '@angular/core';
import { CoreRemindersUnits } from '@features/reminders/services/reminders';
import { PopoverController } from '@singletons';
/**
* This component is meant to set a custom reminder
*/
@Component({
templateUrl: 'set-reminder-custom.html',
})
export class CoreRemindersSetReminderCustomComponent {
@Input() customValue = 10;
@Input() customUnits = CoreRemindersUnits.MINUTE;
customUnitsOptions = [
{
value: CoreRemindersUnits.MINUTE,
label: 'core.minutes',
},
{
value: CoreRemindersUnits.HOUR,
label: 'core.hours',
},
{
value: CoreRemindersUnits.DAY,
label: 'core.days',
},
{
value: CoreRemindersUnits.WEEK,
label: 'core.weeks',
},
];
/**
* Set custom reminder.
*/
set(): void {
// Return it as an object because 0 means undefined if not.
PopoverController.dismiss({ value: this.customValue, unit: this.customUnits });
}
/**
* Close popup.
*/
cancel(): void {
PopoverController.dismiss();
}
}

View File

@ -0,0 +1,35 @@
<ion-header>
<ion-toolbar>
<ion-title>
<h2>{{ 'core.reminders.setareminder' | translate }}</h2>
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-content>
<!-- Preset options. -->
<ion-item button class="ion-text-wrap" (click)="setReminder(option.radioValue)" detail="false" *ngFor="let option of presetOptions">
<ion-label>
<p class="item-heading">{{ option.label }}</p>
</ion-label>
<ion-icon name="fas-check" *ngIf="currentValue === option.radioValue" slot="end"></ion-icon>
</ion-item>
<!-- Custom value. -->
<ion-item button class="ion-text-wrap" (click)="setCustom($event)" detail="false">
<ion-label>
<p class="item-heading">{{ 'core.reminders.custom' | translate }}</p>
<p>{{ customLabel }}</p>
</ion-label>
<ion-icon name="fas-check" *ngIf="currentValue === 'custom'" slot="end"></ion-icon>
</ion-item>
<ion-item *ngIf="noReminderLabel" button class="ion-text-wrap text-danger border-top" (click)="setReminder('disabled')"
detail="false">
<ion-label>
<p class="item-heading">{{ noReminderLabel | translate }}</p>
</ion-label>
<ion-icon name="fas-check" *ngIf="currentValue === 'disabled'" slot="end"></ion-icon>
</ion-item>
</ion-content>

View File

@ -0,0 +1,3 @@
ion-item.border-top {
border-top: 1px solid var(--stroke);
}

View File

@ -0,0 +1,152 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { AddonCalendarProvider } from '@addons/calendar/services/calendar';
import { Component, Input, OnInit } from '@angular/core';
import { CoreReminders, CoreRemindersUnits, CoreReminderValueAndUnit } from '@features/reminders/services/reminders';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreUtils } from '@services/utils/utils';
import { PopoverController } from '@singletons';
import { CoreRemindersSetReminderCustomComponent } from '../set-reminder-custom/set-reminder-custom';
/**
* This component is meant to display a popover with the reminder options.
*/
@Component({
templateUrl: 'set-reminder-menu.html',
styleUrls: ['set-reminder-menu.scss'],
})
export class CoreRemindersSetReminderMenuComponent implements OnInit {
@Input() initialValue?: CoreReminderValueAndUnit;
@Input() noReminderLabel = '';
currentValue = '0m';
customLabel = '';
protected customValue = 10;
protected customUnits = CoreRemindersUnits.MINUTE;
presetOptions = [
{
radioValue: '0m',
value: 0,
unit: CoreRemindersUnits.MINUTE,
label: '',
},
{
radioValue: '1h',
value: 1,
unit: CoreRemindersUnits.HOUR,
label: '',
},
{
radioValue: '12h',
value: 12,
unit: CoreRemindersUnits.HOUR,
label: '',
},
{
radioValue: '1d',
value: 1,
unit: CoreRemindersUnits.DAY,
label: '',
},
];
/**
* @inheritdoc
*/
async ngOnInit(): Promise<void> {
this.presetOptions.forEach((option) => {
option.label = CoreReminders.getUnitValueLabel(option.value, option.unit);
});
if (!this.initialValue) {
return;
}
if (this.initialValue.value === AddonCalendarProvider.DEFAULT_NOTIFICATION_DISABLED) {
this.currentValue = 'disabled';
} else {
// Search if it's one of the preset options.
const option = this.presetOptions.find(option =>
option.value === this.initialValue?.value && option.unit === this.initialValue.unit);
if (option) {
this.currentValue = option.radioValue;
} else {
// It's a custom value.
this.currentValue = 'custom';
this.customValue = this.initialValue.value;
this.customUnits = this.initialValue.unit;
this.customLabel = CoreReminders.getUnitValueLabel(this.customValue, this.customUnits);
}
}
}
/**
* Set the reminder.
*
* @param value Value to set.
*/
setReminder(value: string): void {
// Return it as an object because 0 means undefined if not.
if (value === 'disabled') {
PopoverController.dismiss({ timeBefore: AddonCalendarProvider.DEFAULT_NOTIFICATION_DISABLED });
return;
}
const option = this.presetOptions.find(option => option.radioValue === value);
if (!option) {
return;
}
PopoverController.dismiss({ timeBefore: option.unit * option.value });
}
/**
* Custom value input clicked.
*
* @param ev Click event.
*/
async setCustom(ev: Event): Promise<void> {
const reminderTime = await CoreDomUtils.openPopover<CoreReminderValueAndUnit>({
component: CoreRemindersSetReminderCustomComponent,
componentProps: {
customValue: this.customValue,
customUnits: this.customUnits,
},
waitForDismissCompleted: true, // To be able to close parent popup.
event: ev,
});
if (reminderTime === undefined) {
// User canceled.
return;
}
this.currentValue = 'custom';
this.customValue = reminderTime.value;
this.customUnits = reminderTime.unit;
this.customLabel = CoreReminders.getUnitValueLabel(this.customValue, this.customUnits);
// Let the dimissed popover to be removed.
await CoreUtils.nextTick();
PopoverController.dismiss({ timeBefore: Math.abs(this.customValue) * this.customUnits });
}
}

View File

@ -0,0 +1,11 @@
{
"atthetime": "At the time of the event",
"custom": "Custom...",
"customreminder": "Custom reminder",
"setareminder": "Set a reminder",
"daybefore": "{{time}} day before",
"daysbefore": "{{time}} days before",
"delete": "Delete reminder",
"timebefore": "{{value}} {{units}} before",
"units": "units"
}

View File

@ -14,6 +14,7 @@
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
import { CORE_SITE_SCHEMAS } from '@services/sites';
import { CoreRemindersComponentsModule } from './components/components.module';
import { REMINDERS_SITE_SCHEMA } from './services/database/reminders';
import { CoreReminders, CoreRemindersService } from './services/reminders';
@ -23,6 +24,7 @@ export const CORE_REMINDERS_SERVICES: Type<unknown>[] = [
@NgModule({
imports: [
CoreRemindersComponentsModule,
],
providers: [
{

View File

@ -17,10 +17,36 @@ import { Injectable } from '@angular/core';
import { CoreLocalNotifications } from '@services/local-notifications';
import { CoreSites } from '@services/sites';
import { CoreTimeUtils } from '@services/utils/time';
import { makeSingleton } from '@singletons';
import { makeSingleton, Translate } from '@singletons';
import { CoreReminderDBRecord, REMINDERS_TABLE } from './database/reminders';
import { ILocalNotification } from '@ionic-native/local-notifications';
import { CorePlatform } from '@services/platform';
import { CoreConstants } from '@/core/constants';
/**
* Units to set a reminder.
*/
export enum CoreRemindersUnits {
MINUTE = CoreConstants.SECONDS_MINUTE,
HOUR = CoreConstants.SECONDS_HOUR,
DAY = CoreConstants.SECONDS_DAY,
WEEK = CoreConstants.SECONDS_WEEK,
}
const REMINDER_UNITS_LABELS = {
single: {
[CoreRemindersUnits.MINUTE]: 'core.minute',
[CoreRemindersUnits.HOUR]: 'core.hour',
[CoreRemindersUnits.DAY]: 'core.day',
[CoreRemindersUnits.WEEK]: 'core.week',
},
multi: {
[CoreRemindersUnits.MINUTE]: 'core.minutes',
[CoreRemindersUnits.HOUR]: 'core.hours',
[CoreRemindersUnits.DAY]: 'core.days',
[CoreRemindersUnits.WEEK]: 'core.weeks',
},
};
/**
* Service to handle reminders.
@ -274,6 +300,80 @@ export class CoreRemindersService {
}));
}
/**
* Given a value and a unit, return the translated label.
*
* @param value Value.
* @param unit Unit.
* @param addDefaultLabel Whether to add the "Default" text.
* @return Translated label.
*/
getUnitValueLabel(value: number, unit: CoreRemindersUnits, addDefaultLabel = false): string {
if (value === AddonCalendarProvider.DEFAULT_NOTIFICATION_DISABLED) {
// TODO: It will need a migration of date to set 0 to -1 when needed.
return Translate.instant('core.settings.disabled');
}
if (value === 0) {
return Translate.instant('core.reminders.atthetime');
}
const unitsLabel = value === 1 ?
REMINDER_UNITS_LABELS.single[unit] :
REMINDER_UNITS_LABELS.multi[unit];
const label = Translate.instant('core.reminders.timebefore', {
units: Translate.instant(unitsLabel),
value: value,
});
if (addDefaultLabel) {
return Translate.instant('core.defaultvalue', { $a: label });
}
return label;
}
/**
* Given a number of seconds, convert it to a unit&value format compatible with reminders.
*
* @param seconds Number of seconds.
* @return Value and unit.
*/
static convertSecondsToValueAndUnit(seconds?: number): CoreReminderValueAndUnit {
if (seconds === undefined || seconds < 0) {
return {
value: AddonCalendarProvider.DEFAULT_NOTIFICATION_DISABLED,
unit: CoreRemindersUnits.MINUTE,
};
} else if (seconds === 0) {
return {
value: 0,
unit: CoreRemindersUnits.MINUTE,
};
} else if (seconds % CoreRemindersUnits.WEEK === 0) {
return {
value: seconds / CoreRemindersUnits.WEEK,
unit: CoreRemindersUnits.WEEK,
};
} else if (seconds % CoreRemindersUnits.DAY === 0) {
return {
value: seconds / CoreRemindersUnits.DAY,
unit: CoreRemindersUnits.DAY,
};
} else if (seconds % CoreRemindersUnits.HOUR === 0) {
return {
value: seconds / CoreRemindersUnits.HOUR,
unit: CoreRemindersUnits.HOUR,
};
} else {
return {
value: seconds / CoreRemindersUnits.MINUTE,
unit: CoreRemindersUnits.MINUTE,
};
}
}
}
export const CoreReminders = makeSingleton(CoreRemindersService);
@ -293,6 +393,14 @@ export type CoreReminderNotificationOptions = {
title: string;
};
/**
* Value and unit for reminders.
*/
export type CoreReminderValueAndUnit = {
value: number;
unit: CoreRemindersUnits;
};
export type CoreReminderSelector = {
instanceId: number;
component: string;

View File

@ -1138,7 +1138,7 @@ export class CoreDomUtilsProvider {
/**
* Show an alert modal with a button to close it.
*
* @param title Title to show.
* @param header Title to show.
* @param message Message to show.
* @param buttonText Text of the button.
* @param autocloseTime Number of milliseconds to wait to close the modal. If not defined, modal won't be closed.
@ -1230,7 +1230,7 @@ export class CoreDomUtilsProvider {
/**
* Show an alert modal with a button to close it, translating the values supplied.
*
* @param title Title to show.
* @param header Title to show.
* @param message Message to show.
* @param buttonText Text of the button.
* @param autocloseTime Number of milliseconds to wait to close the modal. If not defined, modal won't be closed.
@ -1669,7 +1669,6 @@ export class CoreDomUtilsProvider {
* @param needsTranslate Whether the 'text' needs to be translated.
* @param duration Duration in ms of the dimissable toast.
* @param cssClass Class to add to the toast.
* @param dismissOnPageChange Dismiss the Toast on page change.
* @return Toast instance.
*/
async showToast(