MOBILE-3021 calendar: Implement day view
This commit is contained in:
		
							parent
							
								
									8dd0108907
								
							
						
					
					
						commit
						083ca7cd7e
					
				| @ -90,6 +90,8 @@ | |||||||
|   "addon.calendar.calendarreminders": "local_moodlemobileapp", |   "addon.calendar.calendarreminders": "local_moodlemobileapp", | ||||||
|   "addon.calendar.confirmeventdelete": "calendar", |   "addon.calendar.confirmeventdelete": "calendar", | ||||||
|   "addon.calendar.confirmeventseriesdelete": "calendar", |   "addon.calendar.confirmeventseriesdelete": "calendar", | ||||||
|  |   "addon.calendar.daynext": "calendar", | ||||||
|  |   "addon.calendar.dayprev": "calendar", | ||||||
|   "addon.calendar.defaultnotificationtime": "local_moodlemobileapp", |   "addon.calendar.defaultnotificationtime": "local_moodlemobileapp", | ||||||
|   "addon.calendar.deleteallevents": "calendar", |   "addon.calendar.deleteallevents": "calendar", | ||||||
|   "addon.calendar.deleteevent": "calendar", |   "addon.calendar.deleteevent": "calendar", | ||||||
|  | |||||||
| @ -31,7 +31,7 @@ | |||||||
|         <ion-row *ngFor="let week of weeks"> |         <ion-row *ngFor="let week of weeks"> | ||||||
|             <ion-col *ngFor="let value of week.prepadding" class="dayblank"></ion-col> <!-- Empty slots (first week). --> |             <ion-col *ngFor="let value of week.prepadding" class="dayblank"></ion-col> <!-- Empty slots (first week). --> | ||||||
|             <ion-col text-center *ngFor="let day of week.days" [ngClass]='{"hasevents": day.hasevents, "today": day.istoday, "weekend": day.isweekend, "duration_finish": day.haslastdayofevent}' > |             <ion-col text-center *ngFor="let day of week.days" [ngClass]='{"hasevents": day.hasevents, "today": day.istoday, "weekend": day.isweekend, "duration_finish": day.haslastdayofevent}' > | ||||||
|                 <p>{{ day.mday }}</p> |                 <p class="addon-calendar-day-number" (click)="dayClicked(day.mday)">{{ day.mday }}</p> | ||||||
| 
 | 
 | ||||||
|                 <!-- In phone, display some dots to indicate the type of events. --> |                 <!-- In phone, display some dots to indicate the type of events. --> | ||||||
|                 <p class="hidden-tablet"><span *ngFor="let type of day.calendareventtypes" class="calendar_event_type calendar_event_{{type}}"></span></p> |                 <p class="hidden-tablet"><span *ngFor="let type of day.calendareventtypes" class="calendar_event_type calendar_event_{{type}}"></span></p> | ||||||
| @ -47,7 +47,7 @@ | |||||||
|                             {{event.name}} |                             {{event.name}} | ||||||
|                         </p> |                         </p> | ||||||
|                     </ng-container> |                     </ng-container> | ||||||
|                     <p *ngIf="day.filteredEvents.length > 4"><b>{{ 'core.nummore' | translate:{$a: day.filteredEvents.length - 3} }}</b></p> |                     <p *ngIf="day.filteredEvents.length > 4" class="addon-calendar-day-more" (click)="dayClicked(day.mday)"><b>{{ 'core.nummore' | translate:{$a: day.filteredEvents.length - 3} }}</b></p> | ||||||
|                 </div> |                 </div> | ||||||
|             </ion-col> |             </ion-col> | ||||||
|             <ion-col *ngFor="let value of week.postpadding" class="dayblank"></ion-col> <!-- Empty slots (last week). --> |             <ion-col *ngFor="let value of week.postpadding" class="dayblank"></ion-col> <!-- Empty slots (last week). --> | ||||||
|  | |||||||
| @ -20,7 +20,7 @@ ion-app.app-root addon-calendar-calendar { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     .addon-calendar-event { |     .addon-calendar-event, .addon-calendar-day-number, .addon-calendar-day-more { | ||||||
|         cursor: pointer; |         cursor: pointer; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -37,6 +37,7 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest | |||||||
|     @Input() categoryId: number | string; // Category ID the course belongs to.
 |     @Input() categoryId: number | string; // Category ID the course belongs to.
 | ||||||
|     @Input() canNavigate?: string | boolean; // Whether to include arrows to change the month. Defaults to true.
 |     @Input() canNavigate?: string | boolean; // Whether to include arrows to change the month. Defaults to true.
 | ||||||
|     @Output() onEventClicked = new EventEmitter<number>(); |     @Output() onEventClicked = new EventEmitter<number>(); | ||||||
|  |     @Output() onDayClicked = new EventEmitter<{day: number, month: number, year: number}>(); | ||||||
| 
 | 
 | ||||||
|     periodName: string; |     periodName: string; | ||||||
|     weekDays: any[]; |     weekDays: any[]; | ||||||
| @ -288,6 +289,15 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest | |||||||
|         this.onEventClicked.emit(event.id); |         this.onEventClicked.emit(event.id); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * A day was clicked. | ||||||
|  |      * | ||||||
|  |      * @param {number} day Day. | ||||||
|  |      */ | ||||||
|  |     dayClicked(day: number): void { | ||||||
|  |         this.onDayClicked.emit({day: day, month: this.month, year: this.year}); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Decrease the current month. |      * Decrease the current month. | ||||||
|      */ |      */ | ||||||
|  | |||||||
| @ -34,7 +34,6 @@ export class AddonCalendarUpcomingEventsComponent implements OnInit, OnChanges, | |||||||
|     @Input() categoryId: number | string; // Category ID the course belongs to.
 |     @Input() categoryId: number | string; // Category ID the course belongs to.
 | ||||||
|     @Output() onEventClicked = new EventEmitter<number>(); |     @Output() onEventClicked = new EventEmitter<number>(); | ||||||
| 
 | 
 | ||||||
|     events = []; // Events (both online and offline).
 |  | ||||||
|     filteredEvents = []; |     filteredEvents = []; | ||||||
|     loaded = false; |     loaded = false; | ||||||
| 
 | 
 | ||||||
| @ -43,6 +42,7 @@ export class AddonCalendarUpcomingEventsComponent implements OnInit, OnChanges, | |||||||
|     protected categoriesRetrieved = false; |     protected categoriesRetrieved = false; | ||||||
|     protected categories = {}; |     protected categories = {}; | ||||||
|     protected currentSiteId: string; |     protected currentSiteId: string; | ||||||
|  |     protected events = []; // Events (both online and offline).
 | ||||||
|     protected onlineEvents = []; |     protected onlineEvents = []; | ||||||
|     protected offlineEvents = []; // Offline events.
 |     protected offlineEvents = []; // Offline events.
 | ||||||
|     protected deletedEvents = []; // Events deleted in offline.
 |     protected deletedEvents = []; // Events deleted in offline.
 | ||||||
|  | |||||||
| @ -6,6 +6,8 @@ | |||||||
|     "calendarreminders": "Calendar reminders", |     "calendarreminders": "Calendar reminders", | ||||||
|     "confirmeventdelete": "Are you sure you want to delete the \"{{$a}}\" event?", |     "confirmeventdelete": "Are you sure you want to delete the \"{{$a}}\" event?", | ||||||
|     "confirmeventseriesdelete": "The \"{{$a.name}}\" event is part of a series. Do you want to delete just this event, or all {{$a.count}} events in the series?", |     "confirmeventseriesdelete": "The \"{{$a.name}}\" event is part of a series. Do you want to delete just this event, or all {{$a.count}} events in the series?", | ||||||
|  |     "daynext": "Next day", | ||||||
|  |     "dayprev": "Previous day", | ||||||
|     "defaultnotificationtime": "Default notification time", |     "defaultnotificationtime": "Default notification time", | ||||||
|     "deleteallevents": "Delete all events", |     "deleteallevents": "Delete all events", | ||||||
|     "deleteevent": "Delete event", |     "deleteevent": "Delete event", | ||||||
|  | |||||||
							
								
								
									
										71
									
								
								src/addon/calendar/pages/day/day.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/addon/calendar/pages/day/day.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | |||||||
|  | <ion-header> | ||||||
|  |     <ion-navbar core-back-button> | ||||||
|  |         <ion-title>{{ 'addon.calendar.calendarevents' | translate }}</ion-title> | ||||||
|  |         <ion-buttons end> | ||||||
|  |             <button *ngIf="courses && courses.length" ion-button icon-only (click)="openCourseFilter($event)" [attr.aria-label]="'core.courses.filter' | translate"> | ||||||
|  |                 <ion-icon name="funnel"></ion-icon> | ||||||
|  |             </button> | ||||||
|  |             <core-context-menu> | ||||||
|  |                 <core-context-menu-item [hidden]="!loaded || !hasOffline || !isOnline"  [priority]="400" [content]="'core.settings.synchronizenow' | translate" (action)="doRefresh(null, $event, true)" [iconAction]="syncIcon" [closeOnClick]="false"></core-context-menu-item> | ||||||
|  |             </core-context-menu> | ||||||
|  |         </ion-buttons> | ||||||
|  |     </ion-navbar> | ||||||
|  | </ion-header> | ||||||
|  | <ion-content> | ||||||
|  |     <ion-refresher [enabled]="loaded" (ionRefresh)="doRefresh($event)"> | ||||||
|  |         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||||
|  |     </ion-refresher> | ||||||
|  | 
 | ||||||
|  |     <!-- Period name and arrows to navigate. --> | ||||||
|  |     <ion-grid padding-top> | ||||||
|  |         <ion-row> | ||||||
|  |             <ion-col text-start *ngIf="currentMoment"> | ||||||
|  |                 <a ion-button icon-only clear (click)="loadPrevious()" [title]="'addon.calendar.dayprev' | translate"> | ||||||
|  |                     <ion-icon name="arrow-back" md="ios-arrow-back"></ion-icon> | ||||||
|  |                 </a> | ||||||
|  |             </ion-col> | ||||||
|  |             <ion-col text-center> | ||||||
|  |                 <p>{{ periodName }}</p> | ||||||
|  |             </ion-col> | ||||||
|  |             <ion-col text-end *ngIf="currentMoment"> | ||||||
|  |                 <a ion-button icon-only clear (click)="loadNext()" [title]="'addon.calendar.daynext' | translate"> | ||||||
|  |                     <ion-icon name="arrow-forward" md="ios-arrow-forward"></ion-icon> | ||||||
|  |                 </a> | ||||||
|  |             </ion-col> | ||||||
|  |         </ion-row> | ||||||
|  |     </ion-grid> | ||||||
|  | 
 | ||||||
|  |     <!-- There is data to be synchronized --> | ||||||
|  |     <ion-card class="core-warning-card" icon-start *ngIf="hasOffline"> | ||||||
|  |         <ion-icon name="warning"></ion-icon> {{ 'core.hasdatatosync' | translate:{$a: 'core.day' | translate} }} | ||||||
|  |     </ion-card> | ||||||
|  | 
 | ||||||
|  |     <core-empty-box *ngIf="!filteredEvents || !filteredEvents.length" icon="calendar" [message]="'addon.calendar.noevents' | translate"> | ||||||
|  |     </core-empty-box> | ||||||
|  | 
 | ||||||
|  |     <ion-list *ngIf="filteredEvents && filteredEvents.length" no-margin> | ||||||
|  |         <ng-container *ngFor="let event of filteredEvents"> | ||||||
|  |             <a ion-item text-wrap [title]="event.name" (click)="gotoEvent(event.id)" [class.core-split-item-selected]="event.id == eventId"> | ||||||
|  |                 <img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" item-start class="core-module-icon"> | ||||||
|  |                 <core-icon *ngIf="event.icon && !event.moduleIcon" [name]="event.icon" item-start></core-icon> | ||||||
|  |                 <h2><core-format-text [text]="event.name"></core-format-text></h2> | ||||||
|  |                 <p [innerHTML]="event.formattedtime"></p> | ||||||
|  |                 <ion-note *ngIf="event.offline && !event.deleted" item-end> | ||||||
|  |                     <ion-icon name="time"></ion-icon> | ||||||
|  |                     <span text-wrap>{{ 'core.notsent' | translate }}</span> | ||||||
|  |                 </ion-note> | ||||||
|  |                 <ion-note *ngIf="event.deleted" item-end> | ||||||
|  |                     <ion-icon name="trash"></ion-icon> | ||||||
|  |                     <span text-wrap>{{ 'core.deletedoffline' | translate }}</span> | ||||||
|  |                 </ion-note> | ||||||
|  |             </a> | ||||||
|  |         </ng-container> | ||||||
|  |     </ion-list> | ||||||
|  | 
 | ||||||
|  |     <!-- Create a calendar event. --> | ||||||
|  |     <ion-fab core-fab bottom end *ngIf="canCreate"> | ||||||
|  |         <button ion-fab (click)="openEdit()" [attr.aria-label]="'addon.calendar.newevent' | translate"> | ||||||
|  |             <ion-icon name="add"></ion-icon> | ||||||
|  |         </button> | ||||||
|  |     </ion-fab> | ||||||
|  | </ion-content> | ||||||
							
								
								
									
										35
									
								
								src/addon/calendar/pages/day/day.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/addon/calendar/pages/day/day.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | // (C) Copyright 2015 Martin Dougiamas
 | ||||||
|  | //
 | ||||||
|  | // 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 { NgModule } from '@angular/core'; | ||||||
|  | import { IonicPageModule } from 'ionic-angular'; | ||||||
|  | import { TranslateModule } from '@ngx-translate/core'; | ||||||
|  | import { CoreComponentsModule } from '@components/components.module'; | ||||||
|  | import { CoreDirectivesModule } from '@directives/directives.module'; | ||||||
|  | import { CorePipesModule } from '@pipes/pipes.module'; | ||||||
|  | import { AddonCalendarDayPage } from './day'; | ||||||
|  | 
 | ||||||
|  | @NgModule({ | ||||||
|  |     declarations: [ | ||||||
|  |         AddonCalendarDayPage, | ||||||
|  |     ], | ||||||
|  |     imports: [ | ||||||
|  |         CoreComponentsModule, | ||||||
|  |         CoreDirectivesModule, | ||||||
|  |         CorePipesModule, | ||||||
|  |         IonicPageModule.forChild(AddonCalendarDayPage), | ||||||
|  |         TranslateModule.forChild() | ||||||
|  |     ], | ||||||
|  | }) | ||||||
|  | export class AddonCalendarDayPageModule {} | ||||||
							
								
								
									
										607
									
								
								src/addon/calendar/pages/day/day.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										607
									
								
								src/addon/calendar/pages/day/day.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,607 @@ | |||||||
|  | // (C) Copyright 2015 Martin Dougiamas
 | ||||||
|  | //
 | ||||||
|  | // 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 OFx ANY KIND, either express or implied.
 | ||||||
|  | // See the License for the specific language governing permissions and
 | ||||||
|  | // limitations under the License.
 | ||||||
|  | 
 | ||||||
|  | import { Component, OnInit, OnDestroy, NgZone } from '@angular/core'; | ||||||
|  | import { IonicPage, NavParams, NavController } from 'ionic-angular'; | ||||||
|  | import { CoreAppProvider } from '@providers/app'; | ||||||
|  | import { CoreEventsProvider } from '@providers/events'; | ||||||
|  | import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; | ||||||
|  | import { CoreSitesProvider } from '@providers/sites'; | ||||||
|  | import { CoreDomUtilsProvider } from '@providers/utils/dom'; | ||||||
|  | import { CoreTimeUtilsProvider } from '@providers/utils/time'; | ||||||
|  | import { AddonCalendarProvider } from '../../providers/calendar'; | ||||||
|  | import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline'; | ||||||
|  | import { AddonCalendarHelperProvider } from '../../providers/helper'; | ||||||
|  | import { AddonCalendarSyncProvider } from '../../providers/calendar-sync'; | ||||||
|  | import { CoreCoursesProvider } from '@core/courses/providers/courses'; | ||||||
|  | import { CoreCoursesHelperProvider } from '@core/courses/providers/helper'; | ||||||
|  | import { Network } from '@ionic-native/network'; | ||||||
|  | import * as moment from 'moment'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Page that displays the calendar events for a certain day. | ||||||
|  |  */ | ||||||
|  | @IonicPage({ segment: 'addon-calendar-day' }) | ||||||
|  | @Component({ | ||||||
|  |     selector: 'page-addon-calendar-day', | ||||||
|  |     templateUrl: 'day.html', | ||||||
|  | }) | ||||||
|  | export class AddonCalendarDayPage implements OnInit, OnDestroy { | ||||||
|  | 
 | ||||||
|  |     protected currentSiteId: string; | ||||||
|  |     protected year: number; | ||||||
|  |     protected month: number; | ||||||
|  |     protected day: number; | ||||||
|  |     protected categories = {}; | ||||||
|  |     protected events = []; // Events (both online and offline).
 | ||||||
|  |     protected onlineEvents = []; | ||||||
|  |     protected offlineEvents = {}; // Offline events.
 | ||||||
|  |     protected offlineEditedEventsIds = []; // IDs of events edited in offline.
 | ||||||
|  |     protected deletedEvents = []; // Events deleted in offline.
 | ||||||
|  |     protected timeFormat: string; | ||||||
|  |     protected currentMoment: moment.Moment; | ||||||
|  | 
 | ||||||
|  |     // Observers.
 | ||||||
|  |     protected newEventObserver: any; | ||||||
|  |     protected discardedObserver: any; | ||||||
|  |     protected editEventObserver: any; | ||||||
|  |     protected deleteEventObserver: any; | ||||||
|  |     protected undeleteEventObserver: any; | ||||||
|  |     protected syncObserver: any; | ||||||
|  |     protected manualSyncObserver: any; | ||||||
|  |     protected onlineObserver: any; | ||||||
|  | 
 | ||||||
|  |     periodName: string; | ||||||
|  |     filteredEvents = []; | ||||||
|  |     courseId: number; | ||||||
|  |     categoryId: number; | ||||||
|  |     canCreate = false; | ||||||
|  |     courses: any[]; | ||||||
|  |     loaded = false; | ||||||
|  |     hasOffline = false; | ||||||
|  |     isOnline = false; | ||||||
|  |     syncIcon: string; | ||||||
|  | 
 | ||||||
|  |     constructor(localNotificationsProvider: CoreLocalNotificationsProvider, | ||||||
|  |             navParams: NavParams, | ||||||
|  |             network: Network, | ||||||
|  |             zone: NgZone, | ||||||
|  |             sitesProvider: CoreSitesProvider, | ||||||
|  |             private navCtrl: NavController, | ||||||
|  |             private domUtils: CoreDomUtilsProvider, | ||||||
|  |             private timeUtils: CoreTimeUtilsProvider, | ||||||
|  |             private calendarProvider: AddonCalendarProvider, | ||||||
|  |             private calendarOffline: AddonCalendarOfflineProvider, | ||||||
|  |             private calendarHelper: AddonCalendarHelperProvider, | ||||||
|  |             private calendarSync: AddonCalendarSyncProvider, | ||||||
|  |             private eventsProvider: CoreEventsProvider, | ||||||
|  |             private coursesProvider: CoreCoursesProvider, | ||||||
|  |             private coursesHelper: CoreCoursesHelperProvider, | ||||||
|  |             private appProvider: CoreAppProvider) { | ||||||
|  | 
 | ||||||
|  |         const now = new Date(); | ||||||
|  | 
 | ||||||
|  |         this.year = navParams.get('year') || now.getFullYear(); | ||||||
|  |         this.month = navParams.get('month') || (now.getMonth() + 1); | ||||||
|  |         this.day = navParams.get('day') || now.getDate(); | ||||||
|  |         this.courseId = navParams.get('courseId'); | ||||||
|  |         this.currentSiteId = sitesProvider.getCurrentSiteId(); | ||||||
|  | 
 | ||||||
|  |         // Listen for events added. When an event is added, reload the data.
 | ||||||
|  |         this.newEventObserver = eventsProvider.on(AddonCalendarProvider.NEW_EVENT_EVENT, (data) => { | ||||||
|  |             if (data && data.event) { | ||||||
|  |                 this.loaded = false; | ||||||
|  |                 this.refreshData(true, false); | ||||||
|  |             } | ||||||
|  |         }, this.currentSiteId); | ||||||
|  | 
 | ||||||
|  |         // Listen for new event discarded event. When it does, reload the data.
 | ||||||
|  |         this.discardedObserver = eventsProvider.on(AddonCalendarProvider.NEW_EVENT_DISCARDED_EVENT, () => { | ||||||
|  |             this.loaded = false; | ||||||
|  |             this.refreshData(true, false); | ||||||
|  |         }, this.currentSiteId); | ||||||
|  | 
 | ||||||
|  |         // Listen for events edited. When an event is edited, reload the data.
 | ||||||
|  |         this.editEventObserver = eventsProvider.on(AddonCalendarProvider.EDIT_EVENT_EVENT, (data) => { | ||||||
|  |             if (data && data.event) { | ||||||
|  |                 this.loaded = false; | ||||||
|  |                 this.refreshData(true, false); | ||||||
|  |             } | ||||||
|  |         }, this.currentSiteId); | ||||||
|  | 
 | ||||||
|  |         // Refresh data if calendar events are synchronized automatically.
 | ||||||
|  |         this.syncObserver = eventsProvider.on(AddonCalendarSyncProvider.AUTO_SYNCED, (data) => { | ||||||
|  |             this.loaded = false; | ||||||
|  |             this.refreshData(); | ||||||
|  |         }, this.currentSiteId); | ||||||
|  | 
 | ||||||
|  |         // Refresh data if calendar events are synchronized manually but not by this page.
 | ||||||
|  |         this.manualSyncObserver = eventsProvider.on(AddonCalendarSyncProvider.MANUAL_SYNCED, (data) => { | ||||||
|  |             if (data && (data.source != 'day' || data.year != this.year || data.month != this.month || data.day != this.day)) { | ||||||
|  |                 this.loaded = false; | ||||||
|  |                 this.refreshData(); | ||||||
|  |             } | ||||||
|  |         }, this.currentSiteId); | ||||||
|  | 
 | ||||||
|  |         // Update the events when an event is deleted.
 | ||||||
|  |         this.deleteEventObserver = eventsProvider.on(AddonCalendarProvider.DELETED_EVENT_EVENT, (data) => { | ||||||
|  |             if (data && !data.sent) { | ||||||
|  |                 // Event was deleted in offline. Just mark it as deleted, no need to refresh.
 | ||||||
|  |                 this.hasOffline = this.markAsDeleted(data.eventId, true) || this.hasOffline; | ||||||
|  |                 this.deletedEvents.push(data.eventId); | ||||||
|  |             } else { | ||||||
|  |                 this.loaded = false; | ||||||
|  |                 this.refreshData(); | ||||||
|  |             } | ||||||
|  |         }, this.currentSiteId); | ||||||
|  | 
 | ||||||
|  |         // Listen for events "undeleted" (offline).
 | ||||||
|  |         this.undeleteEventObserver = eventsProvider.on(AddonCalendarProvider.UNDELETED_EVENT_EVENT, (data) => { | ||||||
|  |             if (data && data.eventId) { | ||||||
|  |                 // Mark it as undeleted, no need to refresh.
 | ||||||
|  |                 const found = this.markAsDeleted(data.eventId, false); | ||||||
|  | 
 | ||||||
|  |                 // Remove it from the list of deleted events if it's there.
 | ||||||
|  |                 const index = this.deletedEvents.indexOf(data.eventId); | ||||||
|  |                 if (index != -1) { | ||||||
|  |                     this.deletedEvents.splice(index, 1); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (found) { | ||||||
|  |                     // The deleted event belongs to current list. Re-calculate "hasOffline".
 | ||||||
|  |                     this.hasOffline = false; | ||||||
|  | 
 | ||||||
|  |                     if (this.events.length != this.onlineEvents.length) { | ||||||
|  |                         this.hasOffline = true; | ||||||
|  |                     } else { | ||||||
|  |                         const event = this.events.find((event) => { | ||||||
|  |                             return event.deleted || event.offline; | ||||||
|  |                         }); | ||||||
|  | 
 | ||||||
|  |                         this.hasOffline = !!event; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }, this.currentSiteId); | ||||||
|  | 
 | ||||||
|  |         // Refresh online status when changes.
 | ||||||
|  |         this.onlineObserver = network.onchange().subscribe(() => { | ||||||
|  |             // Execute the callback in the Angular zone, so change detection doesn't stop working.
 | ||||||
|  |             zone.run(() => { | ||||||
|  |                 this.isOnline = this.appProvider.isOnline(); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * View loaded. | ||||||
|  |      */ | ||||||
|  |     ngOnInit(): void { | ||||||
|  |         this.currentMoment = moment().year(this.year).month(this.month - 1).day(this.day); | ||||||
|  | 
 | ||||||
|  |         this.fetchData(true, false); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Fetch all the data required for the view. | ||||||
|  |      * | ||||||
|  |      * @param {boolean} [sync] Whether it should try to synchronize offline events. | ||||||
|  |      * @param {boolean} [showErrors] Whether to show sync errors to the user. | ||||||
|  |      * @return {Promise<any>} Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     fetchData(sync?: boolean, showErrors?: boolean): Promise<any> { | ||||||
|  | 
 | ||||||
|  |         this.syncIcon = 'spinner'; | ||||||
|  |         this.isOnline = this.appProvider.isOnline(); | ||||||
|  | 
 | ||||||
|  |         const promise = sync ? this.sync() : Promise.resolve(); | ||||||
|  | 
 | ||||||
|  |         return promise.then(() => { | ||||||
|  |             const promises = []; | ||||||
|  | 
 | ||||||
|  |             // Load courses for the popover.
 | ||||||
|  |             promises.push(this.coursesHelper.getCoursesForPopover(this.courseId).then((data) => { | ||||||
|  |                 this.courses = data.courses; | ||||||
|  |                 this.categoryId = data.categoryId; | ||||||
|  |             })); | ||||||
|  | 
 | ||||||
|  |             // Get categories.
 | ||||||
|  |             promises.push(this.loadCategories()); | ||||||
|  | 
 | ||||||
|  |             // Get offline events.
 | ||||||
|  |             promises.push(this.calendarOffline.getAllEditedEvents().then((events) => { | ||||||
|  |                 // Format data.
 | ||||||
|  |                 events.forEach((event) => { | ||||||
|  |                     event.offline = true; | ||||||
|  |                     this.calendarHelper.formatEventData(event); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |                 // Classify them by month & day.
 | ||||||
|  |                 this.offlineEvents = this.calendarHelper.classifyIntoMonths(events); | ||||||
|  | 
 | ||||||
|  |                 // // Get the IDs of events edited in offline.
 | ||||||
|  |                 const filtered = events.filter((event) => { | ||||||
|  |                     return event.id > 0; | ||||||
|  |                 }); | ||||||
|  |                 this.offlineEditedEventsIds = filtered.map((event) => { | ||||||
|  |                     return event.id; | ||||||
|  |                 }); | ||||||
|  |             })); | ||||||
|  | 
 | ||||||
|  |             // Get events deleted in offline.
 | ||||||
|  |             promises.push(this.calendarOffline.getAllDeletedEventsIds().then((ids) => { | ||||||
|  |                 this.deletedEvents = ids; | ||||||
|  |             })); | ||||||
|  | 
 | ||||||
|  |             // Check if user can create events.
 | ||||||
|  |             promises.push(this.calendarHelper.canEditEvents(this.courseId).then((canEdit) => { | ||||||
|  |                 this.canCreate = canEdit; | ||||||
|  |             })); | ||||||
|  | 
 | ||||||
|  |             // Get user preferences.
 | ||||||
|  |             promises.push(this.calendarProvider.getCalendarTimeFormat().then((value) => { | ||||||
|  |                 this.timeFormat = value; | ||||||
|  |             })); | ||||||
|  | 
 | ||||||
|  |             return Promise.all(promises); | ||||||
|  |         }).then(() => { | ||||||
|  |             return this.fetchEvents(); | ||||||
|  |         }).catch((error) => { | ||||||
|  |             this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true); | ||||||
|  |         }).finally(() => { | ||||||
|  |             this.loaded = true; | ||||||
|  |             this.syncIcon = 'sync'; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Fetch the events for current day. | ||||||
|  |      * | ||||||
|  |      * @return {Promise<any>} Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     fetchEvents(): Promise<any> { | ||||||
|  |         // Don't pass courseId and categoryId, we'll filter them locally.
 | ||||||
|  |         return this.calendarProvider.getDayEvents(this.year, this.month, this.day).then((result) => { | ||||||
|  | 
 | ||||||
|  |             // Calculate the period name. We don't use the one in result because it's in server's language.
 | ||||||
|  |             this.periodName = this.timeUtils.userDate(new Date(this.year, this.month - 1, this.day).getTime(), | ||||||
|  |                     'core.strftimedaydate'); | ||||||
|  | 
 | ||||||
|  |             this.onlineEvents = result.events; | ||||||
|  |             this.onlineEvents.forEach(this.calendarHelper.formatEventData.bind(this.calendarHelper)); | ||||||
|  | 
 | ||||||
|  |             // Merge the online events with offline data.
 | ||||||
|  |             this.events = this.mergeEvents(); | ||||||
|  | 
 | ||||||
|  |             // Filter events by course.
 | ||||||
|  |             this.filterEvents(); | ||||||
|  | 
 | ||||||
|  |             // Re-calculate the formatted time so it uses the device date.
 | ||||||
|  |             this.events.forEach((event) => { | ||||||
|  |                 event.formattedtime = this.calendarProvider.formatEventTime(event, this.timeFormat); | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Merge online events with the offline events of that period. | ||||||
|  |      * | ||||||
|  |      * @return {any[]} Merged events. | ||||||
|  |      */ | ||||||
|  |     protected mergeEvents(): any[] { | ||||||
|  |         this.hasOffline = false; | ||||||
|  | 
 | ||||||
|  |         if (!this.offlineEditedEventsIds.length && !this.deletedEvents.length) { | ||||||
|  |             // No offline events, nothing to merge.
 | ||||||
|  |             return this.onlineEvents; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const monthOfflineEvents = this.offlineEvents[this.calendarHelper.getMonthId(this.year, this.month)], | ||||||
|  |             dayOfflineEvents = monthOfflineEvents && monthOfflineEvents[this.day]; | ||||||
|  |         let result = this.onlineEvents; | ||||||
|  | 
 | ||||||
|  |         if (this.deletedEvents.length) { | ||||||
|  |             // Mark as deleted the events that were deleted in offline.
 | ||||||
|  |             result.forEach((event) => { | ||||||
|  |                 event.deleted = this.deletedEvents.indexOf(event.id) != -1; | ||||||
|  | 
 | ||||||
|  |                 if (event.deleted) { | ||||||
|  |                     this.hasOffline = true; | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (this.offlineEditedEventsIds.length) { | ||||||
|  |             // Remove the online events that were modified in offline.
 | ||||||
|  |             result = result.filter((event) => { | ||||||
|  |                 return this.offlineEditedEventsIds.indexOf(event.id) == -1; | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             if (result.length != this.onlineEvents.length) { | ||||||
|  |                 this.hasOffline = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (dayOfflineEvents && dayOfflineEvents.length) { | ||||||
|  |             // Add the offline events (either new or edited).
 | ||||||
|  |             this.hasOffline = true; | ||||||
|  |             result = this.sortEvents(result.concat(dayOfflineEvents)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Filter events to only display events belonging to a certain course. | ||||||
|  |      */ | ||||||
|  |     protected filterEvents(): void { | ||||||
|  |         if (!this.courseId || this.courseId < 0) { | ||||||
|  |             this.filteredEvents = this.events; | ||||||
|  |         } else { | ||||||
|  |             this.filteredEvents = this.events.filter((event) => { | ||||||
|  |                 return this.calendarHelper.shouldDisplayEvent(event, this.courseId, this.categoryId, this.categories); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Sort events by timestart. | ||||||
|  |      * | ||||||
|  |      * @param {any[]} events List to sort. | ||||||
|  |      */ | ||||||
|  |     protected sortEvents(events: any[]): any[] { | ||||||
|  |         return events.sort((a, b) => { | ||||||
|  |             if (a.timestart == b.timestart) { | ||||||
|  |                 return a.timeduration - b.timeduration; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return a.timestart - b.timestart; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Refresh the data. | ||||||
|  |      * | ||||||
|  |      * @param {any} [refresher] Refresher. | ||||||
|  |      * @param {Function} [done] Function to call when done. | ||||||
|  |      * @param {boolean} [showErrors] Whether to show sync errors to the user. | ||||||
|  |      * @return {Promise<any>} Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     doRefresh(refresher?: any, done?: () => void, showErrors?: boolean): Promise<any> { | ||||||
|  |         if (this.loaded) { | ||||||
|  |             return this.refreshData(true, showErrors).finally(() => { | ||||||
|  |                 refresher && refresher.complete(); | ||||||
|  |                 done && done(); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return Promise.resolve(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Refresh the data. | ||||||
|  |      * | ||||||
|  |      * @param {boolean} [sync] Whether it should try to synchronize offline events. | ||||||
|  |      * @param {boolean} [showErrors] Whether to show sync errors to the user. | ||||||
|  |      * @return {Promise<any>} Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     refreshData(sync?: boolean, showErrors?: boolean): Promise<any> { | ||||||
|  |         this.syncIcon = 'spinner'; | ||||||
|  | 
 | ||||||
|  |         const promises = []; | ||||||
|  | 
 | ||||||
|  |         promises.push(this.calendarProvider.invalidateAllowedEventTypes()); | ||||||
|  |         promises.push(this.calendarProvider.invalidateDayEvents(this.year, this.month, this.day)); | ||||||
|  |         promises.push(this.coursesProvider.invalidateCategories(0, true)); | ||||||
|  |         promises.push(this.calendarProvider.invalidateTimeFormat()); | ||||||
|  | 
 | ||||||
|  |         return Promise.all(promises).finally(() => { | ||||||
|  |             return this.fetchData(sync, showErrors); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Load categories to be able to filter events. | ||||||
|  |      * | ||||||
|  |      * @return {Promise<any>} Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     protected loadCategories(): Promise<any> { | ||||||
|  |         return this.coursesProvider.getCategories(0, true).then((cats) => { | ||||||
|  |             this.categories = {}; | ||||||
|  | 
 | ||||||
|  |             // Index categories by ID.
 | ||||||
|  |             cats.forEach((category) => { | ||||||
|  |                 this.categories[category.id] = category; | ||||||
|  |             }); | ||||||
|  |         }).catch(() => { | ||||||
|  |             // Ignore errors.
 | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Try to synchronize offline events. | ||||||
|  |      * | ||||||
|  |      * @param {boolean} [showErrors] Whether to show sync errors to the user. | ||||||
|  |      * @return {Promise<any>} Promise resolved when done. | ||||||
|  |      */ | ||||||
|  |     protected sync(showErrors?: boolean): Promise<any> { | ||||||
|  |         return this.calendarSync.syncEvents().then((result) => { | ||||||
|  |             if (result.warnings && result.warnings.length) { | ||||||
|  |                 this.domUtils.showErrorModal(result.warnings[0]); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (result.updated) { | ||||||
|  |                 // Trigger a manual sync event.
 | ||||||
|  |                 result.source = 'day'; | ||||||
|  |                 result.day = this.day; | ||||||
|  |                 result.month = this.month; | ||||||
|  |                 result.year = this.year; | ||||||
|  | 
 | ||||||
|  |                 this.eventsProvider.trigger(AddonCalendarSyncProvider.MANUAL_SYNCED, result, this.currentSiteId); | ||||||
|  |             } | ||||||
|  |         }).catch((error) => { | ||||||
|  |             if (showErrors) { | ||||||
|  |                 this.domUtils.showErrorModalDefault(error, 'core.errorsync', true); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Navigate to a particular event. | ||||||
|  |      * | ||||||
|  |      * @param {number} eventId Event to load. | ||||||
|  |      */ | ||||||
|  |     gotoEvent(eventId: number): void { | ||||||
|  |         if (eventId < 0) { | ||||||
|  |             // It's an offline event, go to the edit page.
 | ||||||
|  |             this.openEdit(eventId); | ||||||
|  |         } else { | ||||||
|  |             this.navCtrl.push('AddonCalendarEventPage', { | ||||||
|  |                 id: eventId | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Show the context menu. | ||||||
|  |      * | ||||||
|  |      * @param {MouseEvent} event Event. | ||||||
|  |      */ | ||||||
|  |     openCourseFilter(event: MouseEvent): void { | ||||||
|  |         this.coursesHelper.selectCourse(event, this.courses, this.courseId).then((result) => { | ||||||
|  |             if (typeof result.courseId != 'undefined') { | ||||||
|  |                 this.courseId = result.courseId > 0 ? result.courseId : undefined; | ||||||
|  |                 this.categoryId = result.courseId > 0 ? result.categoryId : undefined; | ||||||
|  | 
 | ||||||
|  |                 // Course viewed has changed, check if the user can create events for this course calendar.
 | ||||||
|  |                 this.calendarHelper.canEditEvents(this.courseId).then((canEdit) => { | ||||||
|  |                     this.canCreate = canEdit; | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |                 this.filterEvents(); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Open page to create/edit an event. | ||||||
|  |      * | ||||||
|  |      * @param {number} [eventId] Event ID to edit. | ||||||
|  |      */ | ||||||
|  |     openEdit(eventId?: number): void { | ||||||
|  |         const params: any = {}; | ||||||
|  | 
 | ||||||
|  |         if (eventId) { | ||||||
|  |             params.eventId = eventId; | ||||||
|  |         } else { | ||||||
|  |             // It's a new event, set the time.
 | ||||||
|  |             params.timestamp = moment().year(this.year).month(this.month - 1).date(this.day).unix() * 1000; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (this.courseId) { | ||||||
|  |             params.courseId = this.courseId; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.navCtrl.push('AddonCalendarEditEventPage', params); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Load next month. | ||||||
|  |      */ | ||||||
|  |     loadNext(): void { | ||||||
|  |         this.increaseDay(); | ||||||
|  | 
 | ||||||
|  |         this.loaded = false; | ||||||
|  | 
 | ||||||
|  |         this.fetchEvents().catch((error) => { | ||||||
|  |             this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true); | ||||||
|  |             this.decreaseDay(); | ||||||
|  |         }).finally(() => { | ||||||
|  |             this.loaded = true; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Load previous month. | ||||||
|  |      */ | ||||||
|  |     loadPrevious(): void { | ||||||
|  |         this.decreaseDay(); | ||||||
|  | 
 | ||||||
|  |         this.loaded = false; | ||||||
|  | 
 | ||||||
|  |         this.fetchEvents().catch((error) => { | ||||||
|  |             this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true); | ||||||
|  |             this.increaseDay(); | ||||||
|  |         }).finally(() => { | ||||||
|  |             this.loaded = true; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Decrease the current day. | ||||||
|  |      */ | ||||||
|  |     protected decreaseDay(): void { | ||||||
|  |         this.currentMoment.subtract(1, 'day'); | ||||||
|  | 
 | ||||||
|  |         this.year = this.currentMoment.year(); | ||||||
|  |         this.month = this.currentMoment.month() + 1; | ||||||
|  |         this.day = this.currentMoment.date(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Increase the current day. | ||||||
|  |      */ | ||||||
|  |     protected increaseDay(): void { | ||||||
|  |         this.currentMoment.add(1, 'day'); | ||||||
|  | 
 | ||||||
|  |         this.year = this.currentMoment.year(); | ||||||
|  |         this.month = this.currentMoment.month() + 1; | ||||||
|  |         this.day = this.currentMoment.date(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Find an event and mark it as deleted. | ||||||
|  |      * | ||||||
|  |      * @param {number} eventId Event ID. | ||||||
|  |      * @param {boolean} deleted Whether to mark it as deleted or not. | ||||||
|  |      * @return {boolean} Whether the event was found. | ||||||
|  |      */ | ||||||
|  |     protected markAsDeleted(eventId: number, deleted: boolean): boolean { | ||||||
|  |         const event = this.onlineEvents.find((event) => { | ||||||
|  |             return event.id == eventId; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         if (event) { | ||||||
|  |             event.deleted = deleted; | ||||||
|  | 
 | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Page destroyed. | ||||||
|  |      */ | ||||||
|  |     ngOnDestroy(): void { | ||||||
|  |         this.newEventObserver && this.newEventObserver.off(); | ||||||
|  |         this.discardedObserver && this.discardedObserver.off(); | ||||||
|  |         this.editEventObserver && this.editEventObserver.off(); | ||||||
|  |         this.deleteEventObserver && this.deleteEventObserver.off(); | ||||||
|  |         this.undeleteEventObserver && this.undeleteEventObserver.off(); | ||||||
|  |         this.syncObserver && this.syncObserver.off(); | ||||||
|  |         this.manualSyncObserver && this.manualSyncObserver.off(); | ||||||
|  |         this.onlineObserver && this.onlineObserver.unsubscribe(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -99,6 +99,8 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy { | |||||||
|         this.courseId = navParams.get('courseId'); |         this.courseId = navParams.get('courseId'); | ||||||
|         this.title = this.eventId ? 'addon.calendar.editevent' : 'addon.calendar.newevent'; |         this.title = this.eventId ? 'addon.calendar.editevent' : 'addon.calendar.newevent'; | ||||||
| 
 | 
 | ||||||
|  |         const timestamp = navParams.get('timestamp'); | ||||||
|  | 
 | ||||||
|         this.currentSite = sitesProvider.getCurrentSite(); |         this.currentSite = sitesProvider.getCurrentSite(); | ||||||
|         this.errors = { |         this.errors = { | ||||||
|             required: this.translate.instant('core.required') |             required: this.translate.instant('core.required') | ||||||
| @ -114,7 +116,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy { | |||||||
|         this.groupControl = this.fb.control(''); |         this.groupControl = this.fb.control(''); | ||||||
|         this.descriptionControl = this.fb.control(''); |         this.descriptionControl = this.fb.control(''); | ||||||
| 
 | 
 | ||||||
|         const currentDate = this.timeUtils.toDatetimeFormat(); |         const currentDate = this.timeUtils.toDatetimeFormat(timestamp); | ||||||
| 
 | 
 | ||||||
|         this.eventForm.addControl('name', this.fb.control('', Validators.required)); |         this.eventForm.addControl('name', this.fb.control('', Validators.required)); | ||||||
|         this.eventForm.addControl('timestart', this.fb.control(currentDate, Validators.required)); |         this.eventForm.addControl('timestart', this.fb.control(currentDate, Validators.required)); | ||||||
|  | |||||||
| @ -493,7 +493,6 @@ export class AddonCalendarEventPage implements OnDestroy { | |||||||
|                 eventId: this.eventId |                 eventId: this.eventId | ||||||
|             }, this.sitesProvider.getCurrentSiteId()); |             }, this.sitesProvider.getCurrentSiteId()); | ||||||
| 
 | 
 | ||||||
|             this.domUtils.showToast('addon.calendar.eventcalendareventdeleted', true, 3000, undefined, false); |  | ||||||
|             this.event.deleted = false; |             this.event.deleted = false; | ||||||
| 
 | 
 | ||||||
|         }).catch((error) => { |         }).catch((error) => { | ||||||
|  | |||||||
| @ -28,7 +28,7 @@ | |||||||
|         <ion-icon name="warning"></ion-icon> {{ 'core.hasdatatosync' | translate:{$a: 'addon.calendar.calendar' | translate} }} |         <ion-icon name="warning"></ion-icon> {{ 'core.hasdatatosync' | translate:{$a: 'addon.calendar.calendar' | translate} }} | ||||||
|     </ion-card> |     </ion-card> | ||||||
| 
 | 
 | ||||||
|     <addon-calendar-calendar [hidden]="!showCalendar" [courseId]="courseId" [categoryId]="categoryId" (onEventClicked)="gotoEvent($event)"></addon-calendar-calendar> |     <addon-calendar-calendar [hidden]="!showCalendar" [courseId]="courseId" [categoryId]="categoryId" (onEventClicked)="gotoEvent($event)" (onDayClicked)="gotoDay($event)"></addon-calendar-calendar> | ||||||
| 
 | 
 | ||||||
|     <addon-calendar-upcoming-events *ngIf="loadUpcoming" [hidden]="showCalendar" [courseId]="courseId" [categoryId]="categoryId" (onEventClicked)="gotoEvent($event)"></addon-calendar-upcoming-events> |     <addon-calendar-upcoming-events *ngIf="loadUpcoming" [hidden]="showCalendar" [courseId]="courseId" [categoryId]="categoryId" (onEventClicked)="gotoEvent($event)"></addon-calendar-upcoming-events> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -13,7 +13,7 @@ | |||||||
| // limitations under the License.
 | // limitations under the License.
 | ||||||
| 
 | 
 | ||||||
| import { Component, OnInit, OnDestroy, ViewChild, NgZone } from '@angular/core'; | import { Component, OnInit, OnDestroy, ViewChild, NgZone } from '@angular/core'; | ||||||
| import { IonicPage, NavParams, NavController, PopoverController } from 'ionic-angular'; | import { IonicPage, NavParams, NavController } from 'ionic-angular'; | ||||||
| import { CoreAppProvider } from '@providers/app'; | import { CoreAppProvider } from '@providers/app'; | ||||||
| import { CoreEventsProvider } from '@providers/events'; | import { CoreEventsProvider } from '@providers/events'; | ||||||
| import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; | import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; | ||||||
| @ -25,9 +25,7 @@ import { AddonCalendarHelperProvider } from '../../providers/helper'; | |||||||
| import { AddonCalendarCalendarComponent } from '../../components/calendar/calendar'; | import { AddonCalendarCalendarComponent } from '../../components/calendar/calendar'; | ||||||
| import { AddonCalendarUpcomingEventsComponent } from '../../components/upcoming-events/upcoming-events'; | import { AddonCalendarUpcomingEventsComponent } from '../../components/upcoming-events/upcoming-events'; | ||||||
| import { AddonCalendarSyncProvider } from '../../providers/calendar-sync'; | import { AddonCalendarSyncProvider } from '../../providers/calendar-sync'; | ||||||
| import { CoreCoursesProvider } from '@core/courses/providers/courses'; | import { CoreCoursesHelperProvider } from '@core/courses/providers/helper'; | ||||||
| import { CoreCoursePickerMenuPopoverComponent } from '@components/course-picker-menu/course-picker-menu-popover'; |  | ||||||
| import { TranslateService } from '@ngx-translate/core'; |  | ||||||
| import { Network } from '@ionic-native/network'; | import { Network } from '@ionic-native/network'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -42,11 +40,6 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy { | |||||||
|     @ViewChild(AddonCalendarCalendarComponent) calendarComponent: AddonCalendarCalendarComponent; |     @ViewChild(AddonCalendarCalendarComponent) calendarComponent: AddonCalendarCalendarComponent; | ||||||
|     @ViewChild(AddonCalendarUpcomingEventsComponent) upcomingEventsComponent: AddonCalendarUpcomingEventsComponent; |     @ViewChild(AddonCalendarUpcomingEventsComponent) upcomingEventsComponent: AddonCalendarUpcomingEventsComponent; | ||||||
| 
 | 
 | ||||||
|     protected allCourses = { |  | ||||||
|         id: -1, |  | ||||||
|         fullname: this.translate.instant('core.fulllistofcourses'), |  | ||||||
|         category: -1 |  | ||||||
|     }; |  | ||||||
|     protected eventId: number; |     protected eventId: number; | ||||||
|     protected currentSiteId: string; |     protected currentSiteId: string; | ||||||
| 
 | 
 | ||||||
| @ -83,10 +76,8 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy { | |||||||
|             private calendarOffline: AddonCalendarOfflineProvider, |             private calendarOffline: AddonCalendarOfflineProvider, | ||||||
|             private calendarHelper: AddonCalendarHelperProvider, |             private calendarHelper: AddonCalendarHelperProvider, | ||||||
|             private calendarSync: AddonCalendarSyncProvider, |             private calendarSync: AddonCalendarSyncProvider, | ||||||
|             private translate: TranslateService, |  | ||||||
|             private eventsProvider: CoreEventsProvider, |             private eventsProvider: CoreEventsProvider, | ||||||
|             private coursesProvider: CoreCoursesProvider, |             private coursesHelper: CoreCoursesHelperProvider, | ||||||
|             private popoverCtrl: PopoverController, |  | ||||||
|             private appProvider: CoreAppProvider) { |             private appProvider: CoreAppProvider) { | ||||||
| 
 | 
 | ||||||
|         this.courseId = navParams.get('courseId'); |         this.courseId = navParams.get('courseId'); | ||||||
| @ -206,21 +197,9 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy { | |||||||
|             this.hasOffline = false; |             this.hasOffline = false; | ||||||
| 
 | 
 | ||||||
|             // Load courses for the popover.
 |             // Load courses for the popover.
 | ||||||
|             promises.push(this.coursesProvider.getUserCourses(false).then((courses) => { |             promises.push(this.coursesHelper.getCoursesForPopover(this.courseId).then((data) => { | ||||||
|                 // Add "All courses".
 |                 this.courses = data.courses; | ||||||
|                 courses.unshift(this.allCourses); |                 this.categoryId = data.categoryId; | ||||||
|                 this.courses = courses; |  | ||||||
| 
 |  | ||||||
|                 if (this.courseId) { |  | ||||||
|                     // Search the course to get the category.
 |  | ||||||
|                     const course = this.courses.find((course) => { |  | ||||||
|                         return course.id == this.courseId; |  | ||||||
|                     }); |  | ||||||
| 
 |  | ||||||
|                     if (course) { |  | ||||||
|                         this.categoryId = course.category; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             })); |             })); | ||||||
| 
 | 
 | ||||||
|             // Check if user can create events.
 |             // Check if user can create events.
 | ||||||
| @ -273,9 +252,7 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy { | |||||||
| 
 | 
 | ||||||
|         const promises = []; |         const promises = []; | ||||||
| 
 | 
 | ||||||
|         promises.push(this.calendarProvider.invalidateAllowedEventTypes().then(() => { |         promises.push(this.calendarProvider.invalidateAllowedEventTypes()); | ||||||
|             return this.fetchData(); |  | ||||||
|         })); |  | ||||||
| 
 | 
 | ||||||
|         // Refresh the sub-component.
 |         // Refresh the sub-component.
 | ||||||
|         if (this.showCalendar && this.calendarComponent) { |         if (this.showCalendar && this.calendarComponent) { | ||||||
| @ -305,21 +282,35 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * View a certain day. | ||||||
|  |      * | ||||||
|  |      * @param {any} data Data with the year, month and day. | ||||||
|  |      */ | ||||||
|  |     gotoDay(data: any): void { | ||||||
|  |         const params: any = { | ||||||
|  |             day: data.day, | ||||||
|  |             month: data.month, | ||||||
|  |             year: data.year | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         if (this.courseId) { | ||||||
|  |             params.courseId = this.courseId; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.navCtrl.push('AddonCalendarDayPage', params); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Show the context menu. |      * Show the context menu. | ||||||
|      * |      * | ||||||
|      * @param {MouseEvent} event Event. |      * @param {MouseEvent} event Event. | ||||||
|      */ |      */ | ||||||
|     openCourseFilter(event: MouseEvent): void { |     openCourseFilter(event: MouseEvent): void { | ||||||
|         const popover = this.popoverCtrl.create(CoreCoursePickerMenuPopoverComponent, { |         this.coursesHelper.selectCourse(event, this.courses, this.courseId).then((result) => { | ||||||
|             courses: this.courses, |             if (typeof result.courseId != 'undefined') { | ||||||
|             courseId: this.courseId |                 this.courseId = result.courseId > 0 ? result.courseId : undefined; | ||||||
|         }); |                 this.categoryId = result.courseId > 0 ? result.categoryId : undefined; | ||||||
| 
 |  | ||||||
|         popover.onDidDismiss((course) => { |  | ||||||
|             if (course) { |  | ||||||
|                 this.courseId = course.id > 0 ? course.id : undefined; |  | ||||||
|                 this.categoryId = course.id > 0 ? course.category : undefined; |  | ||||||
| 
 | 
 | ||||||
|                 // Course viewed has changed, check if the user can create events for this course calendar.
 |                 // Course viewed has changed, check if the user can create events for this course calendar.
 | ||||||
|                 this.calendarHelper.canEditEvents(this.courseId).then((canEdit) => { |                 this.calendarHelper.canEditEvents(this.courseId).then((canEdit) => { | ||||||
| @ -327,9 +318,6 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy { | |||||||
|                 }); |                 }); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|         popover.present({ |  | ||||||
|             ev: event |  | ||||||
|         }); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -13,19 +13,18 @@ | |||||||
| // limitations under the License.
 | // limitations under the License.
 | ||||||
| 
 | 
 | ||||||
| import { Component, ViewChild, OnDestroy, NgZone } from '@angular/core'; | import { Component, ViewChild, OnDestroy, NgZone } from '@angular/core'; | ||||||
| import { IonicPage, Content, PopoverController, NavParams, NavController } from 'ionic-angular'; | import { IonicPage, Content, NavParams, NavController } from 'ionic-angular'; | ||||||
| import { TranslateService } from '@ngx-translate/core'; |  | ||||||
| import { AddonCalendarProvider } from '../../providers/calendar'; | import { AddonCalendarProvider } from '../../providers/calendar'; | ||||||
| import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline'; | import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline'; | ||||||
| import { AddonCalendarHelperProvider } from '../../providers/helper'; | import { AddonCalendarHelperProvider } from '../../providers/helper'; | ||||||
| import { AddonCalendarSyncProvider } from '../../providers/calendar-sync'; | import { AddonCalendarSyncProvider } from '../../providers/calendar-sync'; | ||||||
| import { CoreCoursesProvider } from '@core/courses/providers/courses'; | import { CoreCoursesProvider } from '@core/courses/providers/courses'; | ||||||
|  | import { CoreCoursesHelperProvider } from '@core/courses/providers/helper'; | ||||||
| import { CoreDomUtilsProvider } from '@providers/utils/dom'; | import { CoreDomUtilsProvider } from '@providers/utils/dom'; | ||||||
| import { CoreTimeUtilsProvider } from '@providers/utils/time'; | import { CoreTimeUtilsProvider } from '@providers/utils/time'; | ||||||
| import { CoreUtilsProvider } from '@providers/utils/utils'; | import { CoreUtilsProvider } from '@providers/utils/utils'; | ||||||
| import { CoreSitesProvider } from '@providers/sites'; | import { CoreSitesProvider } from '@providers/sites'; | ||||||
| import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; | import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; | ||||||
| import { CoreCoursePickerMenuPopoverComponent } from '@components/course-picker-menu/course-picker-menu-popover'; |  | ||||||
| import { CoreEventsProvider } from '@providers/events'; | import { CoreEventsProvider } from '@providers/events'; | ||||||
| import { CoreAppProvider } from '@providers/app'; | import { CoreAppProvider } from '@providers/app'; | ||||||
| import { CoreSplitViewComponent } from '@components/split-view/split-view'; | import { CoreSplitViewComponent } from '@components/split-view/split-view'; | ||||||
| @ -50,11 +49,6 @@ export class AddonCalendarListPage implements OnDestroy { | |||||||
|     protected emptyEventsTimes = 0; // Variable to identify consecutive calls returning 0 events.
 |     protected emptyEventsTimes = 0; // Variable to identify consecutive calls returning 0 events.
 | ||||||
|     protected categoriesRetrieved = false; |     protected categoriesRetrieved = false; | ||||||
|     protected getCategories = false; |     protected getCategories = false; | ||||||
|     protected allCourses = { |  | ||||||
|         id: -1, |  | ||||||
|         fullname: this.translate.instant('core.fulllistofcourses'), |  | ||||||
|         category: -1 |  | ||||||
|     }; |  | ||||||
|     protected categories = {}; |     protected categories = {}; | ||||||
|     protected siteHomeId: number; |     protected siteHomeId: number; | ||||||
|     protected obsDefaultTimeChange: any; |     protected obsDefaultTimeChange: any; | ||||||
| @ -80,18 +74,17 @@ export class AddonCalendarListPage implements OnDestroy { | |||||||
|     filteredEvents = []; |     filteredEvents = []; | ||||||
|     canLoadMore = false; |     canLoadMore = false; | ||||||
|     loadMoreError = false; |     loadMoreError = false; | ||||||
|     filter = { |     courseId: number; | ||||||
|         course: this.allCourses |     categoryId: number; | ||||||
|     }; |  | ||||||
|     canCreate = false; |     canCreate = false; | ||||||
|     hasOffline = false; |     hasOffline = false; | ||||||
|     isOnline = false; |     isOnline = false; | ||||||
|     syncIcon: string; // Sync icon.
 |     syncIcon: string; // Sync icon.
 | ||||||
| 
 | 
 | ||||||
|     constructor(private translate: TranslateService, private calendarProvider: AddonCalendarProvider, navParams: NavParams, |     constructor(private calendarProvider: AddonCalendarProvider, navParams: NavParams, | ||||||
|             private domUtils: CoreDomUtilsProvider, private coursesProvider: CoreCoursesProvider, private utils: CoreUtilsProvider, |             private domUtils: CoreDomUtilsProvider, private coursesProvider: CoreCoursesProvider, private utils: CoreUtilsProvider, | ||||||
|             private calendarHelper: AddonCalendarHelperProvider, sitesProvider: CoreSitesProvider, zone: NgZone, |             private calendarHelper: AddonCalendarHelperProvider, sitesProvider: CoreSitesProvider, zone: NgZone, | ||||||
|             localNotificationsProvider: CoreLocalNotificationsProvider, private popoverCtrl: PopoverController, |             localNotificationsProvider: CoreLocalNotificationsProvider, private coursesHelper: CoreCoursesHelperProvider, | ||||||
|             private eventsProvider: CoreEventsProvider, private navCtrl: NavController, private appProvider: CoreAppProvider, |             private eventsProvider: CoreEventsProvider, private navCtrl: NavController, private appProvider: CoreAppProvider, | ||||||
|             private calendarOffline: AddonCalendarOfflineProvider, private calendarSync: AddonCalendarSyncProvider, |             private calendarOffline: AddonCalendarOfflineProvider, private calendarSync: AddonCalendarSyncProvider, | ||||||
|             network: Network, private timeUtils: CoreTimeUtilsProvider) { |             network: Network, private timeUtils: CoreTimeUtilsProvider) { | ||||||
| @ -177,6 +170,7 @@ export class AddonCalendarListPage implements OnDestroy { | |||||||
|             if (data && !data.sent) { |             if (data && !data.sent) { | ||||||
|                 // Event was deleted in offline. Just mark it as deleted, no need to refresh.
 |                 // Event was deleted in offline. Just mark it as deleted, no need to refresh.
 | ||||||
|                 this.markAsDeleted(data.eventId, true); |                 this.markAsDeleted(data.eventId, true); | ||||||
|  |                 this.deletedEvents.push(data.eventId); | ||||||
|                 this.hasOffline = true; |                 this.hasOffline = true; | ||||||
|             } else { |             } else { | ||||||
|                 // Event deleted, clear the details if needed and refresh the view.
 |                 // Event deleted, clear the details if needed and refresh the view.
 | ||||||
| @ -278,19 +272,17 @@ export class AddonCalendarListPage implements OnDestroy { | |||||||
|         return promise.then(() => { |         return promise.then(() => { | ||||||
| 
 | 
 | ||||||
|             const promises = []; |             const promises = []; | ||||||
|             const courseId = this.filter.course.id != this.allCourses.id ? this.filter.course.id : undefined; |  | ||||||
| 
 | 
 | ||||||
|             this.hasOffline = false; |             this.hasOffline = false; | ||||||
| 
 | 
 | ||||||
|             promises.push(this.calendarHelper.canEditEvents(courseId).then((canEdit) => { |             promises.push(this.calendarHelper.canEditEvents(this.courseId).then((canEdit) => { | ||||||
|                 this.canCreate = canEdit; |                 this.canCreate = canEdit; | ||||||
|             })); |             })); | ||||||
| 
 | 
 | ||||||
|             // Load courses for the popover.
 |             // Load courses for the popover.
 | ||||||
|             promises.push(this.coursesProvider.getUserCourses(false).then((courses) => { |             promises.push(this.coursesHelper.getCoursesForPopover(this.courseId).then((result) => { | ||||||
|                 // Add "All courses".
 |                 this.courses = result.courses; | ||||||
|                 courses.unshift(this.allCourses); |                 this.categoryId = result.categoryId; | ||||||
|                 this.courses = courses; |  | ||||||
| 
 | 
 | ||||||
|                 if (this.preSelectedCourseId) { |                 if (this.preSelectedCourseId) { | ||||||
|                     this.filter.course = courses.find((course) => { |                     this.filter.course = courses.find((course) => { | ||||||
| @ -418,14 +410,13 @@ export class AddonCalendarListPage implements OnDestroy { | |||||||
|      * @return {any[]} Filtered events. |      * @return {any[]} Filtered events. | ||||||
|      */ |      */ | ||||||
|     protected getFilteredEvents(): any[] { |     protected getFilteredEvents(): any[] { | ||||||
|         if (this.filter.course.id == -1) { |         if (!this.courseId) { | ||||||
|             // No filter, display everything.
 |             // No filter, display everything.
 | ||||||
|             return this.events; |             return this.events; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return this.events.filter((event) => { |         return this.events.filter((event) => { | ||||||
|             return this.calendarHelper.shouldDisplayEvent(event, this.filter.course.id, this.filter.course.category, |             return this.calendarHelper.shouldDisplayEvent(event, this.courseId, this.categoryId, this.categories); | ||||||
|                     this.categories); |  | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -613,28 +604,21 @@ export class AddonCalendarListPage implements OnDestroy { | |||||||
|      * @param {MouseEvent} event Event. |      * @param {MouseEvent} event Event. | ||||||
|      */ |      */ | ||||||
|     openCourseFilter(event: MouseEvent): void { |     openCourseFilter(event: MouseEvent): void { | ||||||
|         const popover = this.popoverCtrl.create(CoreCoursePickerMenuPopoverComponent, { |         this.coursesHelper.selectCourse(event, this.courses, this.courseId).then((result) => { | ||||||
|             courses: this.courses, |             if (typeof result.courseId != 'undefined') { | ||||||
|             courseId: this.filter.course.id |                 this.courseId = result.courseId > 0 ? result.courseId : undefined; | ||||||
|         }); |                 this.categoryId = result.courseId > 0 ? result.categoryId : undefined; | ||||||
|         popover.onDidDismiss((course) => { | 
 | ||||||
|             if (course) { |                 // Course viewed has changed, check if the user can create events for this course calendar.
 | ||||||
|                 this.filter.course = course; |                 this.calendarHelper.canEditEvents(this.courseId).then((canEdit) => { | ||||||
|                 this.domUtils.scrollToTop(this.content); |                     this.canCreate = canEdit; | ||||||
|  |                 }); | ||||||
| 
 | 
 | ||||||
|                 this.filteredEvents = this.getFilteredEvents(); |                 this.filteredEvents = this.getFilteredEvents(); | ||||||
| 
 | 
 | ||||||
|                 // Course viewed has changed, check if the user can create events for this course calendar.
 |                 this.domUtils.scrollToTop(this.content); | ||||||
|                 const courseId = this.filter.course.id != this.allCourses.id ? this.filter.course.id : undefined; |  | ||||||
| 
 |  | ||||||
|                 this.calendarHelper.canEditEvents(courseId).then((canEdit) => { |  | ||||||
|                     this.canCreate = canEdit; |  | ||||||
|                 }); |  | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|         popover.present({ |  | ||||||
|             ev: event |  | ||||||
|         }); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -650,8 +634,8 @@ export class AddonCalendarListPage implements OnDestroy { | |||||||
|         if (eventId) { |         if (eventId) { | ||||||
|             params.eventId = eventId; |             params.eventId = eventId; | ||||||
|         } |         } | ||||||
|         if (this.filter.course.id != this.allCourses.id) { |         if (this.courseId) { | ||||||
|             params.courseId = this.filter.course.id; |             params.courseId = this.courseId; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         this.splitviewCtrl.push('AddonCalendarEditEventPage', params); |         this.splitviewCtrl.push('AddonCalendarEditEventPage', params); | ||||||
|  | |||||||
| @ -858,6 +858,79 @@ export class AddonCalendarProvider { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Get calendar events for a certain day. | ||||||
|  |      * | ||||||
|  |      * @param {number} year Year to get. | ||||||
|  |      * @param {number} month Month to get. | ||||||
|  |      * @param {number} day Day to get. | ||||||
|  |      * @param {number} [courseId] Course to get. | ||||||
|  |      * @param {number} [categoryId] Category to get. | ||||||
|  |      * @param {string} [siteId] Site ID. If not defined, current site. | ||||||
|  |      * @return {Promise<any>} Promise resolved with the response. | ||||||
|  |      */ | ||||||
|  |     getDayEvents(year: number, month: number, day: number, courseId?: number, categoryId?: number, siteId?: string): Promise<any> { | ||||||
|  | 
 | ||||||
|  |         return this.sitesProvider.getSite(siteId).then((site) => { | ||||||
|  | 
 | ||||||
|  |             const data: any = { | ||||||
|  |                 year: year, | ||||||
|  |                 month: month, | ||||||
|  |                 day: day | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             if (courseId) { | ||||||
|  |                 data.courseid = courseId; | ||||||
|  |             } | ||||||
|  |             if (categoryId) { | ||||||
|  |                 data.categoryid = categoryId; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             const preSets = { | ||||||
|  |                 cacheKey: this.getDayEventsCacheKey(year, month, day, courseId, categoryId), | ||||||
|  |                 updateFrequency: CoreSite.FREQUENCY_SOMETIMES | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             return site.read('core_calendar_get_calendar_day_view', data, preSets); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get prefix cache key for day events WS calls. | ||||||
|  |      * | ||||||
|  |      * @return {string} Prefix Cache key. | ||||||
|  |      */ | ||||||
|  |     protected getDayEventsPrefixCacheKey(): string { | ||||||
|  |         return this.ROOT_CACHE_KEY + 'day:'; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get prefix cache key for a certain day for day events WS calls. | ||||||
|  |      * | ||||||
|  |      * @param {number} year Year to get. | ||||||
|  |      * @param {number} month Month to get. | ||||||
|  |      * @param {number} day Day to get. | ||||||
|  |      * @return {string} Prefix Cache key. | ||||||
|  |      */ | ||||||
|  |     protected getDayEventsDayPrefixCacheKey(year: number, month: number, day: number): string { | ||||||
|  |         return this.getDayEventsPrefixCacheKey() + year + ':' + month + ':' + day + ':'; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get cache key for day events WS calls. | ||||||
|  |      * | ||||||
|  |      * @param {number} year Year to get. | ||||||
|  |      * @param {number} month Month to get. | ||||||
|  |      * @param {number} day Day to get. | ||||||
|  |      * @param {number} [courseId] Course to get. | ||||||
|  |      * @param {number} [categoryId] Category to get. | ||||||
|  |      * @return {string} Cache key. | ||||||
|  |      */ | ||||||
|  |     protected getDayEventsCacheKey(year: number, month: number, day: number, courseId?: number, categoryId?: number): string { | ||||||
|  |         return this.getDayEventsDayPrefixCacheKey(year, month, day) + (courseId ? courseId : '') + ':' + | ||||||
|  |                 (categoryId ? categoryId : ''); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Get a calendar reminders from local Db. |      * Get a calendar reminders from local Db. | ||||||
|      * |      * | ||||||
| @ -1120,6 +1193,32 @@ export class AddonCalendarProvider { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Invalidates day events for all days. | ||||||
|  |      * | ||||||
|  |      * @param {string} [siteId] Site Id. If not defined, use current site. | ||||||
|  |      * @return {Promise<any>} Promise resolved when the data is invalidated. | ||||||
|  |      */ | ||||||
|  |     invalidateAllDayEvents(siteId?: string): Promise<any> { | ||||||
|  |         return this.sitesProvider.getSite(siteId).then((site) => { | ||||||
|  |             return site.invalidateWsCacheForKeyStartingWith(this.getDayEventsPrefixCacheKey()); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Invalidates day events for a certain day. | ||||||
|  |      * | ||||||
|  |      * @param {number} year Year. | ||||||
|  |      * @param {number} month Month. | ||||||
|  |      * @param {number} day Day. | ||||||
|  |      * @return {Promise<any>} Promise resolved when the data is invalidated. | ||||||
|  |      */ | ||||||
|  |     invalidateDayEvents(year: number, month: number, day: number, siteId?: string): Promise<any> { | ||||||
|  |         return this.sitesProvider.getSite(siteId).then((site) => { | ||||||
|  |             return site.invalidateWsCacheForKeyStartingWith(this.getDayEventsDayPrefixCacheKey(year, month, day)); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Invalidates events list and all the single events and related info. |      * Invalidates events list and all the single events and related info. | ||||||
|      * |      * | ||||||
|  | |||||||
| @ -90,6 +90,8 @@ | |||||||
|     "addon.calendar.calendarreminders": "Calendar reminders", |     "addon.calendar.calendarreminders": "Calendar reminders", | ||||||
|     "addon.calendar.confirmeventdelete": "Are you sure you want to delete the \"{{$a}}\" event?", |     "addon.calendar.confirmeventdelete": "Are you sure you want to delete the \"{{$a}}\" event?", | ||||||
|     "addon.calendar.confirmeventseriesdelete": "The \"{{$a.name}}\" event is part of a series. Do you want to delete just this event, or all {{$a.count}} events in the series?", |     "addon.calendar.confirmeventseriesdelete": "The \"{{$a.name}}\" event is part of a series. Do you want to delete just this event, or all {{$a.count}} events in the series?", | ||||||
|  |     "addon.calendar.daynext": "Next day", | ||||||
|  |     "addon.calendar.dayprev": "Previous day", | ||||||
|     "addon.calendar.defaultnotificationtime": "Default notification time", |     "addon.calendar.defaultnotificationtime": "Default notification time", | ||||||
|     "addon.calendar.deleteallevents": "Delete all events", |     "addon.calendar.deleteallevents": "Delete all events", | ||||||
|     "addon.calendar.deleteevent": "Delete event", |     "addon.calendar.deleteevent": "Delete event", | ||||||
|  | |||||||
| @ -13,9 +13,12 @@ | |||||||
| // limitations under the License.
 | // limitations under the License.
 | ||||||
| 
 | 
 | ||||||
| import { Injectable } from '@angular/core'; | import { Injectable } from '@angular/core'; | ||||||
|  | import { PopoverController } from 'ionic-angular'; | ||||||
| import { CoreUtilsProvider } from '@providers/utils/utils'; | import { CoreUtilsProvider } from '@providers/utils/utils'; | ||||||
| import { CoreCoursesProvider } from './courses'; | import { CoreCoursesProvider } from './courses'; | ||||||
| import { AddonCourseCompletionProvider } from '@addon/coursecompletion/providers/coursecompletion'; | import { AddonCourseCompletionProvider } from '@addon/coursecompletion/providers/coursecompletion'; | ||||||
|  | import { TranslateService } from '@ngx-translate/core'; | ||||||
|  | import { CoreCoursePickerMenuPopoverComponent } from '@components/course-picker-menu/course-picker-menu-popover'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Helper to gather some common courses functions. |  * Helper to gather some common courses functions. | ||||||
| @ -23,8 +26,46 @@ import { AddonCourseCompletionProvider } from '@addon/coursecompletion/providers | |||||||
| @Injectable() | @Injectable() | ||||||
| export class CoreCoursesHelperProvider { | export class CoreCoursesHelperProvider { | ||||||
| 
 | 
 | ||||||
|     constructor(private coursesProvider: CoreCoursesProvider, private utils: CoreUtilsProvider, |     constructor(private coursesProvider: CoreCoursesProvider, | ||||||
|         private courseCompletionProvider: AddonCourseCompletionProvider) { } |             private utils: CoreUtilsProvider, | ||||||
|  |             private courseCompletionProvider: AddonCourseCompletionProvider, | ||||||
|  |             private translate: TranslateService, | ||||||
|  |             private popoverCtrl: PopoverController) { } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the courses to display the course picker popover. If a courseId is specified, it will also return its categoryId. | ||||||
|  |      * | ||||||
|  |      * @param {number} [courseId] Course ID to get the category. | ||||||
|  |      * @return {Promise<{courses: any[], categoryId: number}>} Promise resolved with the list of courses and the category. | ||||||
|  |      */ | ||||||
|  |     getCoursesForPopover(courseId?: number): Promise<{courses: any[], categoryId: number}> { | ||||||
|  |         return this.coursesProvider.getUserCourses(false).then((courses) => { | ||||||
|  |             // Add "All courses".
 | ||||||
|  |             courses.unshift({ | ||||||
|  |                 id: -1, | ||||||
|  |                 fullname: this.translate.instant('core.fulllistofcourses'), | ||||||
|  |                 category: -1 | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             let categoryId; | ||||||
|  | 
 | ||||||
|  |             if (courseId) { | ||||||
|  |                 // Search the course to get the category.
 | ||||||
|  |                 const course = courses.find((course) => { | ||||||
|  |                     return course.id == courseId; | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |                 if (course) { | ||||||
|  |                     categoryId = course.category; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return { | ||||||
|  |                 courses: courses, | ||||||
|  |                 categoryId: categoryId | ||||||
|  |             }; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Given a course object returned by core_enrol_get_users_courses and another one returned by core_course_get_courses_by_field, |      * Given a course object returned by core_enrol_get_users_courses and another one returned by core_course_get_courses_by_field, | ||||||
| @ -174,4 +215,33 @@ export class CoreCoursesHelperProvider { | |||||||
|             }); |             }); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Show a context menu to select a course, and return the courseId and categoryId of the selected course (-1 for all courses). | ||||||
|  |      * Returns an empty object if popover closed without picking a course. | ||||||
|  |      * | ||||||
|  |      * @param {MouseEvent} event Click event. | ||||||
|  |      * @param {any[]} courses List of courses, from CoreCoursesHelperProvider.getCoursesForPopover. | ||||||
|  |      * @param {number} courseId The course to select at start. | ||||||
|  |      * @return {Promise<{courseId?: number, categoryId?: number}>} Promise resolved with the course ID and category ID. | ||||||
|  |      */ | ||||||
|  |     selectCourse(event: MouseEvent, courses: any[], courseId: number): Promise<{courseId?: number, categoryId?: number}> { | ||||||
|  |         return new Promise((resolve, reject): any => { | ||||||
|  |             const popover = this.popoverCtrl.create(CoreCoursePickerMenuPopoverComponent, { | ||||||
|  |                 courses: courses, | ||||||
|  |                 courseId: courseId | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |             popover.onDidDismiss((course) => { | ||||||
|  |                 if (course) { | ||||||
|  |                     resolve({courseId: course.id, categoryId: course.category}); | ||||||
|  |                 } else { | ||||||
|  |                     resolve({}); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             popover.present({ | ||||||
|  |                 ev: event | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user