MOBILE-3021 calendar: Implement day view
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",
|
||||||
|
|
|
@ -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>
|
|
@ -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 {}
|
|
@ -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…
Reference in New Issue