forked from CIT/Vmeda.Online
		
	MOBILE-1927 calendar: Allow creating events in offline
This commit is contained in:
		
							parent
							
								
									5a9a7b1a11
								
							
						
					
					
						commit
						7ccfade21e
					
				| @ -14,6 +14,7 @@ | |||||||
| 
 | 
 | ||||||
| import { NgModule } from '@angular/core'; | import { NgModule } from '@angular/core'; | ||||||
| import { AddonCalendarProvider } from './providers/calendar'; | import { AddonCalendarProvider } from './providers/calendar'; | ||||||
|  | import { AddonCalendarOfflineProvider } from './providers/calendar-offline'; | ||||||
| import { AddonCalendarHelperProvider } from './providers/helper'; | import { AddonCalendarHelperProvider } from './providers/helper'; | ||||||
| import { AddonCalendarMainMenuHandler } from './providers/mainmenu-handler'; | import { AddonCalendarMainMenuHandler } from './providers/mainmenu-handler'; | ||||||
| import { CoreMainMenuDelegate } from '@core/mainmenu/providers/delegate'; | import { CoreMainMenuDelegate } from '@core/mainmenu/providers/delegate'; | ||||||
| @ -25,6 +26,7 @@ import { CoreUpdateManagerProvider } from '@providers/update-manager'; | |||||||
| // List of providers (without handlers).
 | // List of providers (without handlers).
 | ||||||
| export const ADDON_CALENDAR_PROVIDERS: any[] = [ | export const ADDON_CALENDAR_PROVIDERS: any[] = [ | ||||||
|     AddonCalendarProvider, |     AddonCalendarProvider, | ||||||
|  |     AddonCalendarOfflineProvider, | ||||||
|     AddonCalendarHelperProvider |     AddonCalendarHelperProvider | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| @ -35,6 +37,7 @@ export const ADDON_CALENDAR_PROVIDERS: any[] = [ | |||||||
|     ], |     ], | ||||||
|     providers: [ |     providers: [ | ||||||
|         AddonCalendarProvider, |         AddonCalendarProvider, | ||||||
|  |         AddonCalendarOfflineProvider, | ||||||
|         AddonCalendarHelperProvider, |         AddonCalendarHelperProvider, | ||||||
|         AddonCalendarMainMenuHandler |         AddonCalendarMainMenuHandler | ||||||
|     ] |     ] | ||||||
|  | |||||||
| @ -44,7 +44,7 @@ | |||||||
|             <ion-item text-wrap *ngIf="eventTypeControl.value == 'course'"> |             <ion-item text-wrap *ngIf="eventTypeControl.value == 'course'"> | ||||||
|                 <ion-label id="addon-calendar-course-label"><h2 [core-mark-required]="true">{{ 'core.course' | translate }}</h2></ion-label> |                 <ion-label id="addon-calendar-course-label"><h2 [core-mark-required]="true">{{ 'core.course' | translate }}</h2></ion-label> | ||||||
|                 <ion-select [formControlName]="'courseid'" aria-labelledby="addon-calendar-course-label" interface="action-sheet" [placeholder]="'core.noselection' | translate"> |                 <ion-select [formControlName]="'courseid'" aria-labelledby="addon-calendar-course-label" interface="action-sheet" [placeholder]="'core.noselection' | translate"> | ||||||
|                     <ion-option *ngFor="let course of courses" [value]="course.id">{{ course.fullname }}</ion-option> |                     <ion-option *ngFor="let course of courses" [value]="course.id"><core-format-text [text]="course.fullname"></core-format-text></ion-option> | ||||||
|                 </ion-select> |                 </ion-select> | ||||||
|             </ion-item> |             </ion-item> | ||||||
| 
 | 
 | ||||||
| @ -54,7 +54,7 @@ | |||||||
|                 <ion-item text-wrap> |                 <ion-item text-wrap> | ||||||
|                     <ion-label id="addon-calendar-groupcourse-label"><h2 [core-mark-required]="true">{{ 'core.course' | translate }}</h2></ion-label> |                     <ion-label id="addon-calendar-groupcourse-label"><h2 [core-mark-required]="true">{{ 'core.course' | translate }}</h2></ion-label> | ||||||
|                     <ion-select [formControlName]="'groupcourseid'" aria-labelledby="addon-calendar-groupcourse-label" interface="action-sheet" [placeholder]="'core.noselection' | translate" (ionChange)="groupCourseSelected($event)"> |                     <ion-select [formControlName]="'groupcourseid'" aria-labelledby="addon-calendar-groupcourse-label" interface="action-sheet" [placeholder]="'core.noselection' | translate" (ionChange)="groupCourseSelected($event)"> | ||||||
|                         <ion-option *ngFor="let course of courses" [value]="course.id">{{ course.fullname }}</ion-option> |                         <ion-option *ngFor="let course of courses" [value]="course.id"><core-format-text [text]="course.fullname"></core-format-text></ion-option> | ||||||
|                     </ion-select> |                     </ion-select> | ||||||
|                 </ion-item> |                 </ion-item> | ||||||
|                 <!-- The course has no groups. --> |                 <!-- The course has no groups. --> | ||||||
|  | |||||||
| @ -26,6 +26,7 @@ import { CoreCoursesProvider } from '@core/courses/providers/courses'; | |||||||
| import { CoreSplitViewComponent } from '@components/split-view/split-view'; | import { CoreSplitViewComponent } from '@components/split-view/split-view'; | ||||||
| import { CoreRichTextEditorComponent } from '@components/rich-text-editor/rich-text-editor.ts'; | import { CoreRichTextEditorComponent } from '@components/rich-text-editor/rich-text-editor.ts'; | ||||||
| import { AddonCalendarProvider } from '../../providers/calendar'; | import { AddonCalendarProvider } from '../../providers/calendar'; | ||||||
|  | import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline'; | ||||||
| import { AddonCalendarHelperProvider } from '../../providers/helper'; | import { AddonCalendarHelperProvider } from '../../providers/helper'; | ||||||
| import { CoreSite } from '@classes/site'; | import { CoreSite } from '@classes/site'; | ||||||
| 
 | 
 | ||||||
| @ -79,6 +80,7 @@ export class AddonCalendarEditEventPage implements OnInit { | |||||||
|             private coursesProvider: CoreCoursesProvider, |             private coursesProvider: CoreCoursesProvider, | ||||||
|             private utils: CoreUtilsProvider, |             private utils: CoreUtilsProvider, | ||||||
|             private calendarProvider: AddonCalendarProvider, |             private calendarProvider: AddonCalendarProvider, | ||||||
|  |             private calendarOffline: AddonCalendarOfflineProvider, | ||||||
|             private calendarHelper: AddonCalendarHelperProvider, |             private calendarHelper: AddonCalendarHelperProvider, | ||||||
|             private fb: FormBuilder, |             private fb: FormBuilder, | ||||||
|             @Optional() private svComponent: CoreSplitViewComponent) { |             @Optional() private svComponent: CoreSplitViewComponent) { | ||||||
| @ -102,8 +104,10 @@ export class AddonCalendarEditEventPage implements OnInit { | |||||||
|         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(); | ||||||
|  | 
 | ||||||
|         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(new Date().toISOString(), Validators.required)); |         this.eventForm.addControl('timestart', this.fb.control(currentDate, Validators.required)); | ||||||
|         this.eventForm.addControl('eventtype', this.eventTypeControl); |         this.eventForm.addControl('eventtype', this.eventTypeControl); | ||||||
|         this.eventForm.addControl('categoryid', this.fb.control('')); |         this.eventForm.addControl('categoryid', this.fb.control('')); | ||||||
|         this.eventForm.addControl('courseid', this.fb.control(this.courseId)); |         this.eventForm.addControl('courseid', this.fb.control(this.courseId)); | ||||||
| @ -112,7 +116,7 @@ export class AddonCalendarEditEventPage implements OnInit { | |||||||
|         this.eventForm.addControl('description', this.descriptionControl); |         this.eventForm.addControl('description', this.descriptionControl); | ||||||
|         this.eventForm.addControl('location', this.fb.control('')); |         this.eventForm.addControl('location', this.fb.control('')); | ||||||
|         this.eventForm.addControl('duration', this.fb.control(0)); |         this.eventForm.addControl('duration', this.fb.control(0)); | ||||||
|         this.eventForm.addControl('timedurationuntil', this.fb.control(new Date().toISOString())); |         this.eventForm.addControl('timedurationuntil', this.fb.control(currentDate)); | ||||||
|         this.eventForm.addControl('timedurationminutes', this.fb.control('')); |         this.eventForm.addControl('timedurationminutes', this.fb.control('')); | ||||||
|         this.eventForm.addControl('repeat', this.fb.control(false)); |         this.eventForm.addControl('repeat', this.fb.control(false)); | ||||||
|         this.eventForm.addControl('repeats', this.fb.control('1')); |         this.eventForm.addControl('repeats', this.fb.control('1')); | ||||||
| @ -131,9 +135,10 @@ export class AddonCalendarEditEventPage implements OnInit { | |||||||
|     /** |     /** | ||||||
|      * Fetch the data needed to render the form. |      * Fetch the data needed to render the form. | ||||||
|      * |      * | ||||||
|  |      * @param {boolean} [refresh] Whether it's refreshing data. | ||||||
|      * @return {Promise<any>} Promise resolved when done. |      * @return {Promise<any>} Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     protected fetchData(): Promise<any> { |     protected fetchData(refresh?: boolean): Promise<any> { | ||||||
|         let accessInfo; |         let accessInfo; | ||||||
| 
 | 
 | ||||||
|         // Get access info.
 |         // Get access info.
 | ||||||
| @ -151,6 +156,33 @@ export class AddonCalendarEditEventPage implements OnInit { | |||||||
|                 return Promise.reject(this.translate.instant('addon.calendar.nopermissiontoupdatecalendar')); |                 return Promise.reject(this.translate.instant('addon.calendar.nopermissiontoupdatecalendar')); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             if (this.eventId && !refresh) { | ||||||
|  |                 // Get the event data if there's any.
 | ||||||
|  |                 promises.push(this.calendarOffline.getEvent(this.eventId).then((event) => { | ||||||
|  |                     this.hasOffline = true; | ||||||
|  | 
 | ||||||
|  |                     // Load the data in the form.
 | ||||||
|  |                     this.eventForm.controls.name.setValue(event.name); | ||||||
|  |                     this.eventForm.controls.timestart.setValue(this.timeUtils.toDatetimeFormat(event.timestart * 1000)); | ||||||
|  |                     this.eventForm.controls.eventtype.setValue(event.eventtype); | ||||||
|  |                     this.eventForm.controls.categoryid.setValue(event.categoryid || ''); | ||||||
|  |                     this.eventForm.controls.courseid.setValue(event.courseid || ''); | ||||||
|  |                     this.eventForm.controls.groupcourseid.setValue(event.groupcourseid || ''); | ||||||
|  |                     this.eventForm.controls.groupid.setValue(event.groupid || ''); | ||||||
|  |                     this.eventForm.controls.description.setValue(event.description); | ||||||
|  |                     this.eventForm.controls.location.setValue(event.location); | ||||||
|  |                     this.eventForm.controls.duration.setValue(event.duration); | ||||||
|  |                     this.eventForm.controls.timedurationuntil.setValue( | ||||||
|  |                             this.timeUtils.toDatetimeFormat((event.timedurationuntil * 1000) || Date.now())); | ||||||
|  |                     this.eventForm.controls.timedurationminutes.setValue(event.timedurationminutes || ''); | ||||||
|  |                     this.eventForm.controls.repeat.setValue(!!event.repeat); | ||||||
|  |                     this.eventForm.controls.repeats.setValue(event.repeats || '1'); | ||||||
|  |                 }).catch(() => { | ||||||
|  |                     // No offline data.
 | ||||||
|  |                     this.hasOffline = false; | ||||||
|  |                 })); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             if (types.category) { |             if (types.category) { | ||||||
|                 // Get the categories.
 |                 // Get the categories.
 | ||||||
|                 promises.push(this.coursesProvider.getCategories(0, true).then((cats) => { |                 promises.push(this.coursesProvider.getCategories(0, true).then((cats) => { | ||||||
| @ -185,12 +217,14 @@ export class AddonCalendarEditEventPage implements OnInit { | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return Promise.all(promises).then(() => { |             return Promise.all(promises).then(() => { | ||||||
|                 // Set event types. If course is allowed, select it first.
 |                 if (!this.eventTypeControl.value) { | ||||||
|  |                     // Initialize event type value. If course is allowed, select it first.
 | ||||||
|                     if (types.course) { |                     if (types.course) { | ||||||
|                         this.eventTypeControl.setValue(AddonCalendarProvider.TYPE_COURSE); |                         this.eventTypeControl.setValue(AddonCalendarProvider.TYPE_COURSE); | ||||||
|                     } else { |                     } else { | ||||||
|                         this.eventTypeControl.setValue(eventTypes[0].value); |                         this.eventTypeControl.setValue(eventTypes[0].value); | ||||||
|                     } |                     } | ||||||
|  |                 } | ||||||
| 
 | 
 | ||||||
|                 this.eventTypes = eventTypes; |                 this.eventTypes = eventTypes; | ||||||
|             }); |             }); | ||||||
| @ -227,7 +261,7 @@ export class AddonCalendarEditEventPage implements OnInit { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         Promise.all(promises).finally(() => { |         Promise.all(promises).finally(() => { | ||||||
|             this.fetchData().finally(() => { |             this.fetchData(true).finally(() => { | ||||||
|                 refresher.complete(); |                 refresher.complete(); | ||||||
|             }); |             }); | ||||||
|         }); |         }); | ||||||
| @ -333,8 +367,8 @@ export class AddonCalendarEditEventPage implements OnInit { | |||||||
|         // Send the data.
 |         // Send the data.
 | ||||||
|         const modal = this.domUtils.showModalLoading('core.sending'); |         const modal = this.domUtils.showModalLoading('core.sending'); | ||||||
| 
 | 
 | ||||||
|         this.calendarProvider.submitEvent(this.eventId, data).then((event) => { |         this.calendarProvider.submitEvent(this.eventId, data).then((result) => { | ||||||
|             this.returnToList(event); |             this.returnToList(result.event); | ||||||
|         }).catch((error) => { |         }).catch((error) => { | ||||||
|             this.domUtils.showErrorModalDefault(error, 'Error sending data.'); |             this.domUtils.showErrorModalDefault(error, 'Error sending data.'); | ||||||
|         }).finally(() => { |         }).finally(() => { | ||||||
| @ -348,10 +382,14 @@ export class AddonCalendarEditEventPage implements OnInit { | |||||||
|      * @param {number} [event] Event. |      * @param {number} [event] Event. | ||||||
|      */ |      */ | ||||||
|     protected returnToList(event?: any): void { |     protected returnToList(event?: any): void { | ||||||
|  |         if (event) { | ||||||
|             const data: any = { |             const data: any = { | ||||||
|                 event: event |                 event: event | ||||||
|             }; |             }; | ||||||
|             this.eventsProvider.trigger(AddonCalendarProvider.NEW_EVENT_EVENT, data, this.currentSite.getId()); |             this.eventsProvider.trigger(AddonCalendarProvider.NEW_EVENT_EVENT, data, this.currentSite.getId()); | ||||||
|  |         } else { | ||||||
|  |             this.eventsProvider.trigger(AddonCalendarProvider.NEW_EVENT_DISCARDED_EVENT, {}, this.currentSite.getId()); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         if (this.svComponent && this.svComponent.isOn()) { |         if (this.svComponent && this.svComponent.isOn()) { | ||||||
|             // Empty form.
 |             // Empty form.
 | ||||||
| @ -369,7 +407,12 @@ export class AddonCalendarEditEventPage implements OnInit { | |||||||
|      */ |      */ | ||||||
|     discard(): void { |     discard(): void { | ||||||
|         this.domUtils.showConfirm(this.translate.instant('core.areyousure')).then(() => { |         this.domUtils.showConfirm(this.translate.instant('core.areyousure')).then(() => { | ||||||
|             // @todo.
 |             this.calendarOffline.deleteEvent(this.eventId).then(() => { | ||||||
|  |                 this.returnToList(); | ||||||
|  |             }).catch(() => { | ||||||
|  |                 // Shouldn't happen.
 | ||||||
|  |                 this.domUtils.showErrorModal('Error discarding event.'); | ||||||
|  |             }); | ||||||
|         }).catch(() => { |         }).catch(() => { | ||||||
|             // Cancelled.
 |             // Cancelled.
 | ||||||
|         }); |         }); | ||||||
|  | |||||||
| @ -17,9 +17,28 @@ | |||||||
|             <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> |             <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||||
|         </ion-refresher> |         </ion-refresher> | ||||||
|         <core-loading [hideUntil]="eventsLoaded"> |         <core-loading [hideUntil]="eventsLoaded"> | ||||||
|  |             <!-- 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: 'addon.calendar.calendar' | translate} }} | ||||||
|  |             </ion-card> | ||||||
|  | 
 | ||||||
|             <core-empty-box *ngIf="!filteredEvents || !filteredEvents.length" icon="calendar" [message]="'addon.calendar.noevents' | translate"> |             <core-empty-box *ngIf="!filteredEvents || !filteredEvents.length" icon="calendar" [message]="'addon.calendar.noevents' | translate"> | ||||||
|             </core-empty-box> |             </core-empty-box> | ||||||
| 
 | 
 | ||||||
|  |             <ion-list *ngIf="offlineEvents && offlineEvents.length" no-margin> | ||||||
|  |                 <ng-container *ngFor="let event of offlineEvents"> | ||||||
|  |                     <ion-item-divider> {{ 'core.notsent' | translate }}</ion-item-divider> | ||||||
|  |                     <a ion-item text-wrap [title]="event.name" (click)="gotoEvent(event.id)" [class.core-split-item-selected]="event.id == eventId"> | ||||||
|  |                         <core-icon *ngIf="event.icon" [name]="event.icon" item-start></core-icon> | ||||||
|  |                         <h2><core-format-text [text]="event.name"></core-format-text></h2> | ||||||
|  |                         <p> | ||||||
|  |                             {{ event.timestart * 1000 | coreFormatDate: "strftimedatetimeshort" }} | ||||||
|  |                             <span *ngIf="event.timeduration"> - {{ (event.timestart + event.timeduration) * 1000 | coreFormatDate: "strftimedatetimeshort" }}</span> | ||||||
|  |                         </p> | ||||||
|  |                     </a> | ||||||
|  |                 </ng-container> | ||||||
|  |             </ion-list> | ||||||
|  | 
 | ||||||
|             <ion-list *ngIf="filteredEvents && filteredEvents.length" no-margin> |             <ion-list *ngIf="filteredEvents && filteredEvents.length" no-margin> | ||||||
|                 <ng-container *ngFor="let event of filteredEvents"> |                 <ng-container *ngFor="let event of filteredEvents"> | ||||||
|                     <ion-item-divider *ngIf="event.showDate"> |                     <ion-item-divider *ngIf="event.showDate"> | ||||||
| @ -43,7 +62,7 @@ | |||||||
| 
 | 
 | ||||||
|         <!-- Create a calendar event. --> |         <!-- Create a calendar event. --> | ||||||
|         <ion-fab core-fab bottom end *ngIf="canCreate"> |         <ion-fab core-fab bottom end *ngIf="canCreate"> | ||||||
|             <button ion-fab (click)="openCreate()" [attr.aria-label]="'addon.calendar.newevent' | translate"> |             <button ion-fab (click)="openEdit()" [attr.aria-label]="'addon.calendar.newevent' | translate"> | ||||||
|                 <ion-icon name="add"></ion-icon> |                 <ion-icon name="add"></ion-icon> | ||||||
|             </button> |             </button> | ||||||
|         </ion-fab> |         </ion-fab> | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ import { Component, ViewChild, OnDestroy } from '@angular/core'; | |||||||
| import { IonicPage, Content, PopoverController, NavParams, NavController } from 'ionic-angular'; | import { IonicPage, Content, PopoverController, NavParams, NavController } from 'ionic-angular'; | ||||||
| import { TranslateService } from '@ngx-translate/core'; | import { TranslateService } from '@ngx-translate/core'; | ||||||
| import { AddonCalendarProvider } from '../../providers/calendar'; | import { AddonCalendarProvider } from '../../providers/calendar'; | ||||||
|  | import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline'; | ||||||
| import { AddonCalendarHelperProvider } from '../../providers/helper'; | import { AddonCalendarHelperProvider } from '../../providers/helper'; | ||||||
| import { CoreCoursesProvider } from '@core/courses/providers/courses'; | import { CoreCoursesProvider } from '@core/courses/providers/courses'; | ||||||
| import { CoreDomUtilsProvider } from '@providers/utils/dom'; | import { CoreDomUtilsProvider } from '@providers/utils/dom'; | ||||||
| @ -55,10 +56,12 @@ export class AddonCalendarListPage implements OnDestroy { | |||||||
|     protected eventId: number; |     protected eventId: number; | ||||||
|     protected preSelectedCourseId: number; |     protected preSelectedCourseId: number; | ||||||
|     protected newEventObserver: any; |     protected newEventObserver: any; | ||||||
|  |     protected discardedObserver: any; | ||||||
| 
 | 
 | ||||||
|     courses: any[]; |     courses: any[]; | ||||||
|     eventsLoaded = false; |     eventsLoaded = false; | ||||||
|     events = []; |     events = []; | ||||||
|  |     offlineEvents = []; | ||||||
|     notificationsEnabled = false; |     notificationsEnabled = false; | ||||||
|     filteredEvents = []; |     filteredEvents = []; | ||||||
|     canLoadMore = false; |     canLoadMore = false; | ||||||
| @ -67,12 +70,14 @@ export class AddonCalendarListPage implements OnDestroy { | |||||||
|         course: this.allCourses |         course: this.allCourses | ||||||
|     }; |     }; | ||||||
|     canCreate = false; |     canCreate = false; | ||||||
|  |     hasOffline = false; | ||||||
| 
 | 
 | ||||||
|     constructor(private translate: TranslateService, private calendarProvider: AddonCalendarProvider, navParams: NavParams, |     constructor(private translate: TranslateService, 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, |             private calendarHelper: AddonCalendarHelperProvider, sitesProvider: CoreSitesProvider, | ||||||
|             localNotificationsProvider: CoreLocalNotificationsProvider, private popoverCtrl: PopoverController, |             localNotificationsProvider: CoreLocalNotificationsProvider, private popoverCtrl: PopoverController, | ||||||
|             eventsProvider: CoreEventsProvider, private navCtrl: NavController, appProvider: CoreAppProvider) { |             eventsProvider: CoreEventsProvider, private navCtrl: NavController, appProvider: CoreAppProvider, | ||||||
|  |             private calendarOffline: AddonCalendarOfflineProvider) { | ||||||
| 
 | 
 | ||||||
|         this.siteHomeId = sitesProvider.getCurrentSite().getSiteHomeId(); |         this.siteHomeId = sitesProvider.getCurrentSite().getSiteHomeId(); | ||||||
|         this.notificationsEnabled = localNotificationsProvider.isAvailable(); |         this.notificationsEnabled = localNotificationsProvider.isAvailable(); | ||||||
| @ -87,7 +92,7 @@ export class AddonCalendarListPage implements OnDestroy { | |||||||
|         this.eventId = navParams.get('eventId') || false; |         this.eventId = navParams.get('eventId') || false; | ||||||
|         this.preSelectedCourseId = navParams.get('courseId') || null; |         this.preSelectedCourseId = navParams.get('courseId') || null; | ||||||
| 
 | 
 | ||||||
|         // Listen for events added. When an event is added, we reload the data.
 |         // Listen for events added. When an event is added, reload the data.
 | ||||||
|         this.newEventObserver = eventsProvider.on(AddonCalendarProvider.NEW_EVENT_EVENT, (data) => { |         this.newEventObserver = eventsProvider.on(AddonCalendarProvider.NEW_EVENT_EVENT, (data) => { | ||||||
|             if (data && data.event) { |             if (data && data.event) { | ||||||
|                 if (this.splitviewCtrl.isOn()) { |                 if (this.splitviewCtrl.isOn()) { | ||||||
| @ -97,19 +102,25 @@ export class AddonCalendarListPage implements OnDestroy { | |||||||
| 
 | 
 | ||||||
|                 this.eventsLoaded = false; |                 this.eventsLoaded = false; | ||||||
|                 this.refreshEvents(false).finally(() => { |                 this.refreshEvents(false).finally(() => { | ||||||
|                     this.eventsLoaded = true; |  | ||||||
| 
 | 
 | ||||||
|                     // In tablet mode try to open the event.
 |                     // In tablet mode try to open the event (only if it's an online event).
 | ||||||
|                     if (this.splitviewCtrl.isOn()) { |                     if (this.splitviewCtrl.isOn() && data.event.id > 0) { | ||||||
|                         if (data.event.id) { |  | ||||||
|                         this.gotoEvent(data.event.id); |                         this.gotoEvent(data.event.id); | ||||||
|                         } else { |  | ||||||
|                             // It's an offline event.
 |  | ||||||
|                         } |  | ||||||
|                     } |                     } | ||||||
|                 }); |                 }); | ||||||
|             } |             } | ||||||
|         }, sitesProvider.getCurrentSiteId()); |         }, sitesProvider.getCurrentSiteId()); | ||||||
|  | 
 | ||||||
|  |         // Listen for new event discarded event. When it does, reload the data.
 | ||||||
|  |         this.discardedObserver = eventsProvider.on(AddonCalendarProvider.NEW_EVENT_DISCARDED_EVENT, () => { | ||||||
|  |             if (this.splitviewCtrl.isOn()) { | ||||||
|  |                 // Discussion added, clear details page.
 | ||||||
|  |                 this.splitviewCtrl.emptyDetails(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             this.eventsLoaded = false; | ||||||
|  |             this.refreshEvents(false); | ||||||
|  |         }, sitesProvider.getCurrentSiteId()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -126,8 +137,6 @@ export class AddonCalendarListPage implements OnDestroy { | |||||||
|                 // Take first and load it.
 |                 // Take first and load it.
 | ||||||
|                 this.gotoEvent(this.events[0].id); |                 this.gotoEvent(this.events[0].id); | ||||||
|             } |             } | ||||||
|         }).finally(() => { |  | ||||||
|             this.eventsLoaded = true; |  | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -167,7 +176,18 @@ export class AddonCalendarListPage implements OnDestroy { | |||||||
|             return this.fetchEvents(refresh); |             return this.fetchEvents(refresh); | ||||||
|         })); |         })); | ||||||
| 
 | 
 | ||||||
|         return Promise.all(promises); |         // Get offline events.
 | ||||||
|  |         promises.push(this.calendarOffline.getAllEvents().then((events) => { | ||||||
|  |             this.hasOffline = !!events.length; | ||||||
|  | 
 | ||||||
|  |             // Format data and sort by timestart.
 | ||||||
|  |             events.forEach(this.calendarHelper.formatEventData.bind(this.calendarHelper)); | ||||||
|  |             this.offlineEvents = events.sort((a, b) => a.timestart - b.timestart); | ||||||
|  |         })); | ||||||
|  | 
 | ||||||
|  |         return Promise.all(promises).finally(() => { | ||||||
|  |             this.eventsLoaded = true; | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -194,6 +214,8 @@ export class AddonCalendarListPage implements OnDestroy { | |||||||
|                     return this.fetchEvents(); |                     return this.fetchEvents(); | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|  |                 events.forEach(this.calendarHelper.formatEventData.bind(this.calendarHelper)); | ||||||
|  | 
 | ||||||
|                 // Sort the events by timestart, they're ordered by id.
 |                 // Sort the events by timestart, they're ordered by id.
 | ||||||
|                 events.sort((a, b) => { |                 events.sort((a, b) => { | ||||||
|                     if (a.timestart == b.timestart) { |                     if (a.timestart == b.timestart) { | ||||||
| @ -203,7 +225,6 @@ export class AddonCalendarListPage implements OnDestroy { | |||||||
|                     return a.timestart - b.timestart; |                     return a.timestart - b.timestart; | ||||||
|                 }); |                 }); | ||||||
| 
 | 
 | ||||||
|                 events.forEach(this.calendarHelper.formatEventData.bind(this.calendarHelper)); |  | ||||||
|                 this.getCategories = this.shouldLoadCategories(events); |                 this.getCategories = this.shouldLoadCategories(events); | ||||||
| 
 | 
 | ||||||
|                 if (refresh) { |                 if (refresh) { | ||||||
| @ -427,10 +448,16 @@ export class AddonCalendarListPage implements OnDestroy { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Open page to create an event. |      * Open page to create/edit an event. | ||||||
|  |      * | ||||||
|  |      * @param {number} [eventId] Event ID to edit. | ||||||
|      */ |      */ | ||||||
|     openCreate(): void { |     openEdit(eventId?: number): void { | ||||||
|         const params: any = {}; |         const params: any = {}; | ||||||
|  | 
 | ||||||
|  |         if (eventId) { | ||||||
|  |             params.eventId = eventId; | ||||||
|  |         } | ||||||
|         if (this.filter.course.id != this.allCourses.id) { |         if (this.filter.course.id != this.allCourses.id) { | ||||||
|             params.courseId = this.filter.course.id; |             params.courseId = this.filter.course.id; | ||||||
|         } |         } | ||||||
| @ -452,7 +479,15 @@ export class AddonCalendarListPage implements OnDestroy { | |||||||
|      */ |      */ | ||||||
|     gotoEvent(eventId: number): void { |     gotoEvent(eventId: number): void { | ||||||
|         this.eventId = eventId; |         this.eventId = eventId; | ||||||
|         this.splitviewCtrl.push('AddonCalendarEventPage', { id: eventId }); | 
 | ||||||
|  |         if (eventId < 0) { | ||||||
|  |             // It's an offline event, go to the edit page.
 | ||||||
|  |             this.openEdit(eventId); | ||||||
|  |         } else { | ||||||
|  |             this.splitviewCtrl.push('AddonCalendarEventPage', { | ||||||
|  |                 id: eventId | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
							
								
								
									
										215
									
								
								src/addon/calendar/providers/calendar-offline.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								src/addon/calendar/providers/calendar-offline.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,215 @@ | |||||||
|  | // (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 { Injectable } from '@angular/core'; | ||||||
|  | import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; | ||||||
|  | import { AddonCalendarProvider } from './calendar'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Service to handle offline calendar events. | ||||||
|  |  */ | ||||||
|  | @Injectable() | ||||||
|  | export class AddonCalendarOfflineProvider { | ||||||
|  | 
 | ||||||
|  |     // Variables for database.
 | ||||||
|  |     static EVENTS_TABLE = 'addon_calendar_offline_events'; | ||||||
|  | 
 | ||||||
|  |     protected siteSchema: CoreSiteSchema = { | ||||||
|  |         name: 'AddonCalendarOfflineProvider', | ||||||
|  |         version: 1, | ||||||
|  |         tables: [ | ||||||
|  |             { | ||||||
|  |                 name: AddonCalendarOfflineProvider.EVENTS_TABLE, | ||||||
|  |                 columns: [ | ||||||
|  |                     { | ||||||
|  |                         name: 'id', // Negative for offline entries.
 | ||||||
|  |                         type: 'INTEGER', | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         name: 'name', | ||||||
|  |                         type: 'TEXT', | ||||||
|  |                         notNull: true | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         name: 'timestart', | ||||||
|  |                         type: 'INTEGER', | ||||||
|  |                         notNull: true | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         name: 'eventtype', | ||||||
|  |                         type: 'TEXT', | ||||||
|  |                         notNull: true | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         name: 'categoryid', | ||||||
|  |                         type: 'INTEGER', | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         name: 'courseid', | ||||||
|  |                         type: 'INTEGER', | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         name: 'groupcourseid', | ||||||
|  |                         type: 'INTEGER', | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         name: 'groupid', | ||||||
|  |                         type: 'INTEGER', | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         name: 'description', | ||||||
|  |                         type: 'TEXT', | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         name: 'location', | ||||||
|  |                         type: 'TEXT', | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         name: 'duration', | ||||||
|  |                         type: 'INTEGER', | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         name: 'timedurationuntil', | ||||||
|  |                         type: 'INTEGER', | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         name: 'timedurationminutes', | ||||||
|  |                         type: 'INTEGER', | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         name: 'repeat', | ||||||
|  |                         type: 'INTEGER', | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         name: 'repeats', | ||||||
|  |                         type: 'TEXT', | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         name: 'userid', | ||||||
|  |                         type: 'INTEGER', | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         name: 'timecreated', | ||||||
|  |                         type: 'INTEGER', | ||||||
|  |                     } | ||||||
|  |                 ], | ||||||
|  |                 primaryKeys: ['id'] | ||||||
|  |             } | ||||||
|  |         ] | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     constructor(private sitesProvider: CoreSitesProvider) { | ||||||
|  |         this.sitesProvider.registerSiteSchema(this.siteSchema); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Delete an offline event. | ||||||
|  |      * | ||||||
|  |      * @param {number} eventId Event ID. | ||||||
|  |      * @param {string} [siteId] Site ID. If not defined, current site. | ||||||
|  |      * @return {Promise<any>} Promise resolved if deleted, rejected if failure. | ||||||
|  |      */ | ||||||
|  |     deleteEvent(eventId: number, siteId?: string): Promise<any> { | ||||||
|  |         return this.sitesProvider.getSite(siteId).then((site) => { | ||||||
|  |             const conditions: any = { | ||||||
|  |                 id: eventId | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             return site.getDb().deleteRecords(AddonCalendarOfflineProvider.EVENTS_TABLE, conditions); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get all offline events. | ||||||
|  |      * | ||||||
|  |      * @param {string} [siteId] Site ID. If not defined, current site. | ||||||
|  |      * @return {Promise<any[]>} Promise resolved with events. | ||||||
|  |      */ | ||||||
|  |     getAllEvents(siteId?: string): Promise<any[]> { | ||||||
|  |         return this.sitesProvider.getSite(siteId).then((site) => { | ||||||
|  |             return site.getDb().getRecords(AddonCalendarOfflineProvider.EVENTS_TABLE); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get an offline event. | ||||||
|  |      * | ||||||
|  |      * @param {number} eventId Event ID. | ||||||
|  |      * @param {string} [siteId] Site ID. If not defined, current site. | ||||||
|  |      * @return {Promise<any>} Promise resolved with the event. | ||||||
|  |      */ | ||||||
|  |     getEvent(eventId: number, siteId?: string): Promise<any> { | ||||||
|  |         return this.sitesProvider.getSite(siteId).then((site) => { | ||||||
|  |             const conditions: any = { | ||||||
|  |                 id: eventId | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             return site.getDb().getRecord(AddonCalendarOfflineProvider.EVENTS_TABLE, conditions); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Check if there are offline events to send. | ||||||
|  |      * | ||||||
|  |      * @param {string} [siteId] Site ID. If not defined, current site. | ||||||
|  |      * @return {Promise<boolean>} Promise resolved with boolean: true if has offline events, false otherwise. | ||||||
|  |      */ | ||||||
|  |     hasEvents(siteId?: string): Promise<boolean> { | ||||||
|  |         return this.getAllEvents(siteId).then((events) => { | ||||||
|  |             return !!events.length; | ||||||
|  |         }).catch(() => { | ||||||
|  |             // No offline data found, return false.
 | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Offline version for adding a new discussion to a forum. | ||||||
|  |      * | ||||||
|  |      * @param {number} eventId Event ID. If it's a new event, set it to undefined/null. | ||||||
|  |      * @param {any} data Event data. | ||||||
|  |      * @param {number} [timeCreated] The time the event was created. If not defined, current time. | ||||||
|  |      * @param {string} [siteId] Site ID. If not defined, current site. | ||||||
|  |      * @return {Promise<any>} Promise resolved with the stored event. | ||||||
|  |      */ | ||||||
|  |     saveEvent(eventId: number, data: any, timeCreated?: number, siteId?: string): Promise<any> { | ||||||
|  |         return this.sitesProvider.getSite(siteId).then((site) => { | ||||||
|  |             timeCreated = timeCreated || Date.now(); | ||||||
|  | 
 | ||||||
|  |             const event = { | ||||||
|  |                 id: eventId || -timeCreated, | ||||||
|  |                 name: data.name, | ||||||
|  |                 timestart: data.timestart, | ||||||
|  |                 eventtype: data.eventtype, | ||||||
|  |                 categoryid: data.categoryid || null, | ||||||
|  |                 courseid: data.courseid || null, | ||||||
|  |                 groupcourseid: data.groupcourseid || null, | ||||||
|  |                 groupid: data.groupid || null, | ||||||
|  |                 description: data.description && data.description.text, | ||||||
|  |                 location: data.location, | ||||||
|  |                 duration: data.duration, | ||||||
|  |                 timedurationuntil: data.timedurationuntil, | ||||||
|  |                 timedurationminutes: data.timedurationminutes, | ||||||
|  |                 repeat: data.repeat ? 1 : 0, | ||||||
|  |                 repeats: data.repeats, | ||||||
|  |                 timecreated: timeCreated, | ||||||
|  |                 userid: site.getUserId() | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             return site.getDb().insertRecord(AddonCalendarOfflineProvider.EVENTS_TABLE, event).then(() => { | ||||||
|  |                 return event; | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -17,6 +17,7 @@ import { CoreLoggerProvider } from '@providers/logger'; | |||||||
| import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; | import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; | ||||||
| import { CoreSite } from '@classes/site'; | import { CoreSite } from '@classes/site'; | ||||||
| import { CoreCoursesProvider } from '@core/courses/providers/courses'; | import { CoreCoursesProvider } from '@core/courses/providers/courses'; | ||||||
|  | import { CoreAppProvider } from '@providers/app'; | ||||||
| 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 { CoreGroupsProvider } from '@providers/groups'; | import { CoreGroupsProvider } from '@providers/groups'; | ||||||
| @ -25,6 +26,7 @@ import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; | |||||||
| import { CoreConfigProvider } from '@providers/config'; | import { CoreConfigProvider } from '@providers/config'; | ||||||
| import { ILocalNotification } from '@ionic-native/local-notifications'; | import { ILocalNotification } from '@ionic-native/local-notifications'; | ||||||
| import { SQLiteDB } from '@classes/sqlitedb'; | import { SQLiteDB } from '@classes/sqlitedb'; | ||||||
|  | import { AddonCalendarOfflineProvider } from './calendar-offline'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Service to handle calendar events. |  * Service to handle calendar events. | ||||||
| @ -37,6 +39,7 @@ export class AddonCalendarProvider { | |||||||
|     static DEFAULT_NOTIFICATION_TIME_SETTING = 'mmaCalendarDefaultNotifTime'; |     static DEFAULT_NOTIFICATION_TIME_SETTING = 'mmaCalendarDefaultNotifTime'; | ||||||
|     static DEFAULT_NOTIFICATION_TIME = 60; |     static DEFAULT_NOTIFICATION_TIME = 60; | ||||||
|     static NEW_EVENT_EVENT = 'addon_calendar_new_event'; |     static NEW_EVENT_EVENT = 'addon_calendar_new_event'; | ||||||
|  |     static NEW_EVENT_DISCARDED_EVENT = 'addon_calendar_new_event_discarded'; | ||||||
|     static TYPE_CATEGORY = 'category'; |     static TYPE_CATEGORY = 'category'; | ||||||
|     static TYPE_COURSE = 'course'; |     static TYPE_COURSE = 'course'; | ||||||
|     static TYPE_GROUP = 'group'; |     static TYPE_GROUP = 'group'; | ||||||
| @ -214,7 +217,8 @@ export class AddonCalendarProvider { | |||||||
|     constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private groupsProvider: CoreGroupsProvider, |     constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private groupsProvider: CoreGroupsProvider, | ||||||
|             private coursesProvider: CoreCoursesProvider, private timeUtils: CoreTimeUtilsProvider, |             private coursesProvider: CoreCoursesProvider, private timeUtils: CoreTimeUtilsProvider, | ||||||
|             private localNotificationsProvider: CoreLocalNotificationsProvider, private configProvider: CoreConfigProvider, |             private localNotificationsProvider: CoreLocalNotificationsProvider, private configProvider: CoreConfigProvider, | ||||||
|             private utils: CoreUtilsProvider) { |             private utils: CoreUtilsProvider, private calendarOffline: AddonCalendarOfflineProvider, | ||||||
|  |             private appProvider: CoreAppProvider) { | ||||||
|         this.logger = logger.getInstance('AddonCalendarProvider'); |         this.logger = logger.getInstance('AddonCalendarProvider'); | ||||||
|         this.sitesProvider.registerSiteSchema(this.siteSchema); |         this.sitesProvider.registerSiteSchema(this.siteSchema); | ||||||
|     } |     } | ||||||
| @ -918,14 +922,58 @@ export class AddonCalendarProvider { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Submit an event, either to create it or to edit it. |      * Submit a calendar event. | ||||||
|  |      * | ||||||
|  |      * @param {number} eventId ID of the event. If undefined/null, create a new event. | ||||||
|  |      * @param {any} formData Form data. | ||||||
|  |      * @param {number} [timeCreated] The time the event was created. Only if modifying a new offline event. | ||||||
|  |      * @param {boolean} [forceOffline] True to always save it in offline. | ||||||
|  |      * @param {string} [siteId] Site ID. If not defined, current site. | ||||||
|  |      * @return {Promise<{sent: boolean, event: any}>} Promise resolved with the event and a boolean indicating if data was | ||||||
|  |      *                                                sent to server or stored in offline. | ||||||
|  |      */ | ||||||
|  |     submitEvent(eventId: number, formData: any, timeCreated?: number, forceOffline?: boolean, siteId?: string): | ||||||
|  |             Promise<{sent: boolean, event: any}> { | ||||||
|  | 
 | ||||||
|  |         siteId = siteId || this.sitesProvider.getCurrentSiteId(); | ||||||
|  | 
 | ||||||
|  |         // Function to store the event to be synchronized later.
 | ||||||
|  |         const storeOffline = (): Promise<{sent: boolean, event: any}> => { | ||||||
|  |             return this.calendarOffline.saveEvent(eventId, formData, timeCreated, siteId).then((event) => { | ||||||
|  |                 return {sent: false, event: event}; | ||||||
|  |             }); | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         if (forceOffline || !this.appProvider.isOnline()) { | ||||||
|  |             // App is offline, store the event.
 | ||||||
|  |             return storeOffline(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // If the event is already stored, discard it first.
 | ||||||
|  |         return this.calendarOffline.deleteEvent(eventId, siteId).then(() => { | ||||||
|  |             return this.submitEventOnline(eventId, formData, siteId).then((event) => { | ||||||
|  |                 return {sent: true, event: event}; | ||||||
|  |             }).catch((error) => { | ||||||
|  |                 if (error && !this.utils.isWebServiceError(error)) { | ||||||
|  |                     // Couldn't connect to server, store in offline.
 | ||||||
|  |                     return storeOffline(); | ||||||
|  |                 } else { | ||||||
|  |                     // The WebService has thrown an error, reject.
 | ||||||
|  |                     return Promise.reject(error); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Submit an event, either to create it or to edit it. It will fail if offline or cannot connect. | ||||||
|      * |      * | ||||||
|      * @param {number} eventId ID of the event. If undefined/null, create a new event. |      * @param {number} eventId ID of the event. If undefined/null, create a new event. | ||||||
|      * @param {any} formData Form data. |      * @param {any} formData Form data. | ||||||
|      * @param {string} [siteId] Site ID. If not provided, current site. |      * @param {string} [siteId] Site ID. If not provided, current site. | ||||||
|      * @return {Promise<any>} Promise resolved when done. |      * @return {Promise<any>} Promise resolved when done. | ||||||
|      */ |      */ | ||||||
|     submitEvent(eventId: number, formData: any, siteId?: string): Promise<any> { |     submitEventOnline(eventId: number, formData: any, siteId?: string): Promise<any> { | ||||||
|         return this.sitesProvider.getSite(siteId).then((site) => { |         return this.sitesProvider.getSite(siteId).then((site) => { | ||||||
|             // Add data that is "hidden" in web.
 |             // Add data that is "hidden" in web.
 | ||||||
|             formData.id = eventId || 0; |             formData.id = eventId || 0; | ||||||
| @ -940,7 +988,7 @@ export class AddonCalendarProvider { | |||||||
| 
 | 
 | ||||||
|             return site.write('core_calendar_submit_create_update_form', params).then((result) => { |             return site.write('core_calendar_submit_create_update_form', params).then((result) => { | ||||||
|                 if (result.validationerror) { |                 if (result.validationerror) { | ||||||
|                     return Promise.reject(null); |                     return Promise.reject(this.utils.createFakeWSError('')); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 return result.event; |                 return result.event; | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ import { Injectable } from '@angular/core'; | |||||||
| import { CoreLoggerProvider } from '@providers/logger'; | import { CoreLoggerProvider } from '@providers/logger'; | ||||||
| import { CoreCourseProvider } from '@core/course/providers/course'; | import { CoreCourseProvider } from '@core/course/providers/course'; | ||||||
| import { AddonCalendarProvider } from './calendar'; | import { AddonCalendarProvider } from './calendar'; | ||||||
|  | import { CoreConstants } from '@core/constants'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Service that provides some features regarding lists of courses and categories. |  * Service that provides some features regarding lists of courses and categories. | ||||||
| @ -47,6 +48,20 @@ export class AddonCalendarHelperProvider { | |||||||
|             e.icon = this.courseProvider.getModuleIconSrc(e.modulename); |             e.icon = this.courseProvider.getModuleIconSrc(e.modulename); | ||||||
|             e.moduleIcon = e.icon; |             e.moduleIcon = e.icon; | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         if (e.id < 0) { | ||||||
|  |             // It's an offline event, add some calculated data.
 | ||||||
|  |             e.format = 1; | ||||||
|  |             e.visible = 1; | ||||||
|  | 
 | ||||||
|  |             if (e.duration == 1) { | ||||||
|  |                 e.timeduration = e.timedurationuntil - e.timestart; | ||||||
|  |             } else if (e.duration == 2) { | ||||||
|  |                 e.timeduration = e.timedurationminutes * CoreConstants.SECONDS_MINUTE; | ||||||
|  |             } else { | ||||||
|  |                 e.timeduration = 0; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -51,10 +51,10 @@ export class AddonModDataFieldDateComponent extends AddonModDataFieldPluginCompo | |||||||
|             val = this.search['f_' + this.field.id + '_y'] ? new Date(this.search['f_' + this.field.id + '_y'] + '-' + |             val = this.search['f_' + this.field.id + '_y'] ? new Date(this.search['f_' + this.field.id + '_y'] + '-' + | ||||||
|                 this.search['f_' + this.field.id + '_m'] + '-' + this.search['f_' + this.field.id + '_d']) : new Date(); |                 this.search['f_' + this.field.id + '_m'] + '-' + this.search['f_' + this.field.id + '_d']) : new Date(); | ||||||
| 
 | 
 | ||||||
|             this.search['f_' + this.field.id] = val.toISOString(); |             this.search['f_' + this.field.id] = this.timeUtils.toDatetimeFormat(val.getTime()); | ||||||
|         } else { |         } else { | ||||||
|             val = this.value && this.value.content ? new Date(parseInt(this.value.content, 10) * 1000) : new Date(); |             val = this.value && this.value.content ? new Date(parseInt(this.value.content, 10) * 1000) : new Date(); | ||||||
|             val = val.toISOString(); |             val = this.timeUtils.toDatetimeFormat(val.getTime()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         this.addControl('f_' + this.field.id, val); |         this.addControl('f_' + this.field.id, val); | ||||||
|  | |||||||
| @ -15,6 +15,7 @@ import { Injector, Injectable } from '@angular/core'; | |||||||
| import { TranslateService } from '@ngx-translate/core'; | import { TranslateService } from '@ngx-translate/core'; | ||||||
| import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; | import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; | ||||||
| import { AddonModDataFieldDateComponent } from '../component/date'; | import { AddonModDataFieldDateComponent } from '../component/date'; | ||||||
|  | import { CoreTimeUtilsProvider } from '@providers/utils/time'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Handler for date data field plugin. |  * Handler for date data field plugin. | ||||||
| @ -24,7 +25,7 @@ export class AddonModDataFieldDateHandler implements AddonModDataFieldHandler { | |||||||
|     name = 'AddonModDataFieldDateHandler'; |     name = 'AddonModDataFieldDateHandler'; | ||||||
|     type = 'date'; |     type = 'date'; | ||||||
| 
 | 
 | ||||||
|     constructor(private translate: TranslateService) { } |     constructor(private translate: TranslateService, private timeUtils: CoreTimeUtilsProvider) { } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Return the Component to use to display the plugin data. |      * Return the Component to use to display the plugin data. | ||||||
| @ -129,7 +130,7 @@ export class AddonModDataFieldDateHandler implements AddonModDataFieldHandler { | |||||||
|             input = inputData[fieldName] && inputData[fieldName].substr(0, 10) || ''; |             input = inputData[fieldName] && inputData[fieldName].substr(0, 10) || ''; | ||||||
| 
 | 
 | ||||||
|         originalFieldData = (originalFieldData && originalFieldData.content && |         originalFieldData = (originalFieldData && originalFieldData.content && | ||||||
|                 new Date(originalFieldData.content * 1000).toISOString().substr(0, 10)) || ''; |                 this.timeUtils.toDatetimeFormat(originalFieldData.content * 1000).substr(0, 10)) || ''; | ||||||
| 
 | 
 | ||||||
|         return input != originalFieldData; |         return input != originalFieldData; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -1389,6 +1389,7 @@ | |||||||
|     "core.deleteduser": "Deleted user", |     "core.deleteduser": "Deleted user", | ||||||
|     "core.deleting": "Deleting", |     "core.deleting": "Deleting", | ||||||
|     "core.description": "Description", |     "core.description": "Description", | ||||||
|  |     "core.dfdatetimeinput": "YYYY-MM-DDThh:mm:ss.SSS", | ||||||
|     "core.dfdaymonthyear": "MM-DD-YYYY", |     "core.dfdaymonthyear": "MM-DD-YYYY", | ||||||
|     "core.dfdayweekmonth": "ddd, D MMM", |     "core.dfdayweekmonth": "ddd, D MMM", | ||||||
|     "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", |     "core.dffulldate": "dddd, D MMMM YYYY h[:]mm A", | ||||||
|  | |||||||
| @ -64,6 +64,7 @@ | |||||||
|     "deleteduser": "Deleted user", |     "deleteduser": "Deleted user", | ||||||
|     "deleting": "Deleting", |     "deleting": "Deleting", | ||||||
|     "description": "Description", |     "description": "Description", | ||||||
|  |     "dfdatetimeinput": "YYYY-MM-DDThh:mm:ss.SSS", | ||||||
|     "dfdaymonthyear": "MM-DD-YYYY", |     "dfdaymonthyear": "MM-DD-YYYY", | ||||||
|     "dfdayweekmonth": "ddd, D MMM", |     "dfdayweekmonth": "ddd, D MMM", | ||||||
|     "dffulldate": "dddd, D MMMM YYYY h[:]mm A", |     "dffulldate": "dddd, D MMMM YYYY h[:]mm A", | ||||||
|  | |||||||
| @ -299,6 +299,18 @@ export class CoreTimeUtilsProvider { | |||||||
|         return moment(timestamp).format(format); |         return moment(timestamp).format(format); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Convert a timestamp to the format to set to a datetime input. | ||||||
|  |      * | ||||||
|  |      * @param {number} [timestamp] Timestamp to convert (in ms). If not provided, current time. | ||||||
|  |      * @return {string} Formatted time. | ||||||
|  |      */ | ||||||
|  |     toDatetimeFormat(timestamp?: number): string { | ||||||
|  |         timestamp = timestamp || Date.now(); | ||||||
|  | 
 | ||||||
|  |         return this.userDate(timestamp, 'core.dfdatetimeinput', false); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Convert a text into user timezone timestamp. |      * Convert a text into user timezone timestamp. | ||||||
|      * |      * | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user