Merge pull request #3302 from NoelDeMartin/MOBILE-4042
MOBILE-4042 calendar: Swipe navigation in event
This commit is contained in:
		
						commit
						11c1b2a7da
					
				
							
								
								
									
										64
									
								
								src/addons/calendar/classes/events-source.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/addons/calendar/classes/events-source.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | ||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { AddonCalendarEventToDisplay } from '@addons/calendar/services/calendar'; | ||||
| import { Params } from '@angular/router'; | ||||
| import { CoreRoutedItemsManagerSource } from '@classes/items-management/routed-items-manager-source'; | ||||
| 
 | ||||
| /** | ||||
|  * Provides a collection of calendar events. | ||||
|  */ | ||||
| export class AddonCalendarEventsSource extends CoreRoutedItemsManagerSource<AddonCalendarEventToDisplay> { | ||||
| 
 | ||||
|     readonly DATE: string; | ||||
| 
 | ||||
|     private events: AddonCalendarEventToDisplay[] = []; | ||||
| 
 | ||||
|     constructor(date: string) { | ||||
|         super(); | ||||
| 
 | ||||
|         this.DATE = date; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Set events. | ||||
|      * | ||||
|      * @param events Events. | ||||
|      */ | ||||
|     setEvents(events: AddonCalendarEventToDisplay[]): void { | ||||
|         this.events = events; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     protected async loadPageItems(): Promise<{ items: AddonCalendarEventToDisplay[] }> { | ||||
|         return { items: this.events.slice(0) }; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     getItemPath(event: AddonCalendarEventToDisplay): string { | ||||
|         return event.id.toString(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     getItemQueryParams(): Params { | ||||
|         return { date: this.DATE }; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -67,7 +67,7 @@ | ||||
|                             <ng-container *ngFor="let event of day.filteredEvents"> | ||||
|                                 <ion-card> | ||||
|                                     <ion-item class="addon-calendar-event ion-text-wrap" [attr.aria-label]="event.name" | ||||
|                                         (click)="gotoEvent(event.id)" [class.item-dimmed]="event.ispast" | ||||
|                                         (click)="gotoEvent(event.id, day)" [class.item-dimmed]="event.ispast" | ||||
|                                         [ngClass]="['addon-calendar-eventtype-'+event.eventtype]" button [detail]="false"> | ||||
|                                         <core-mod-icon *ngIf="event.moduleIcon" [modicon]="event.moduleIcon" slot="start" [showAlt]="false" | ||||
|                                             [modname]="event.modulename" [componentId]="event.instance"> | ||||
|  | ||||
| @ -45,6 +45,8 @@ import { | ||||
|     CoreSwipeSlidesDynamicItem, | ||||
|     CoreSwipeSlidesDynamicItemsManagerSource, | ||||
| } from '@classes/items-management/swipe-slides-dynamic-items-manager-source'; | ||||
| import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker'; | ||||
| import { AddonCalendarEventsSource } from '@addons/calendar/classes/events-source'; | ||||
| 
 | ||||
| /** | ||||
|  * Page that displays the calendar events for a certain day. | ||||
| @ -342,9 +344,10 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { | ||||
|      * Navigate to a particular event. | ||||
|      * | ||||
|      * @param eventId Event to load. | ||||
|      * @param day Day. | ||||
|      */ | ||||
|     gotoEvent(eventId: number): void { | ||||
|         CoreNavigator.navigateToSitePath(`/calendar/event/${eventId}`); | ||||
|     gotoEvent(eventId: number, day: PreloadedDay): void { | ||||
|         CoreNavigator.navigateToSitePath(`/calendar/event/${eventId}`, { params: { date: day.moment.format('MMDDY') } }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -452,6 +455,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { | ||||
|         this.manualSyncObserver?.off(); | ||||
|         this.onlineObserver?.unsubscribe(); | ||||
|         this.filterChangedObserver?.off(); | ||||
|         this.manager?.getSource().forgetRelatedSources(); | ||||
|         this.manager?.destroy(); | ||||
|         this.managerUnsubscribe && this.managerUnsubscribe(); | ||||
|     } | ||||
| @ -483,6 +487,7 @@ type PreloadedDay = DayBasicData & CoreSwipeSlidesDynamicItem & { | ||||
| class AddonCalendarDaySlidesItemsManagerSource extends CoreSwipeSlidesDynamicItemsManagerSource<PreloadedDay> { | ||||
| 
 | ||||
|     courses: Partial<CoreEnrolledCourseData>[] = []; | ||||
|     eventsSources: Set<AddonCalendarEventsSource> = new Set(); | ||||
|     // Offline events classified in month & day.
 | ||||
|     offlineEvents: Record<string, Record<number, AddonCalendarEventToDisplay[]>> = {}; | ||||
|     offlineEditedEventsIds: number[] = []; // IDs of events edited in offline.
 | ||||
| @ -533,6 +538,8 @@ class AddonCalendarDaySlidesItemsManagerSource extends CoreSwipeSlidesDynamicIte | ||||
|      */ | ||||
|     filterEvents(day: PreloadedDay, filter: AddonCalendarFilter): void { | ||||
|         day.filteredEvents = AddonCalendarHelper.getFilteredEvents(day.events || [], filter, this.categories || {}); | ||||
| 
 | ||||
|         this.rememberEventsList(day); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -816,4 +823,34 @@ class AddonCalendarDaySlidesItemsManagerSource extends CoreSwipeSlidesDynamicIte | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Forget other sources that where created whilst using this one. | ||||
|      */ | ||||
|     forgetRelatedSources(): void { | ||||
|         for (const source of this.eventsSources) { | ||||
|             CoreRoutedItemsManagerSourcesTracker.removeReference(source, this); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Remember the list of events in a day to be used in a different context. | ||||
|      * | ||||
|      * @param day Day containing the events list. | ||||
|      */ | ||||
|     private async rememberEventsList(day: PreloadedDay): Promise<void> { | ||||
|         const source = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource(AddonCalendarEventsSource, [ | ||||
|             day.moment.format('MMDDY'), | ||||
|         ]); | ||||
| 
 | ||||
|         if (!this.eventsSources.has(source)) { | ||||
|             this.eventsSources.add(source); | ||||
| 
 | ||||
|             CoreRoutedItemsManagerSourcesTracker.addReference(source, this); | ||||
|         } | ||||
| 
 | ||||
|         source.setEvents(day.filteredEvents ?? []); | ||||
| 
 | ||||
|         await source.reload(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -26,7 +26,7 @@ | ||||
|         </ion-buttons> | ||||
|     </ion-toolbar> | ||||
| </ion-header> | ||||
| <ion-content> | ||||
| <ion-content [core-swipe-navigation]="events"> | ||||
|     <ion-refresher slot="fixed" [disabled]="!eventLoaded" (ionRefresh)="doRefresh($event.target)"> | ||||
|         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||
|     </ion-refresher> | ||||
|  | ||||
| @ -36,9 +36,12 @@ import { Network, NgZone, Translate } from '@singletons'; | ||||
| import { Subscription } from 'rxjs'; | ||||
| import { CoreNavigator } from '@services/navigator'; | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| import { ActivatedRoute } from '@angular/router'; | ||||
| import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router'; | ||||
| import { CoreConstants } from '@/core/constants'; | ||||
| import { AddonCalendarReminderTimeModalComponent } from '@addons/calendar/components/reminder-time-modal/reminder-time-modal'; | ||||
| import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker'; | ||||
| import { AddonCalendarEventsSource } from '@addons/calendar/classes/events-source'; | ||||
| import { CoreSwipeNavigationItemsManager } from '@classes/items-management/swipe-navigation-items-manager'; | ||||
| 
 | ||||
| /** | ||||
|  * Page that displays a single calendar event. | ||||
| @ -63,6 +66,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { | ||||
| 
 | ||||
|     eventLoaded = false; | ||||
|     event?: AddonCalendarEventToDisplay; | ||||
|     events?: CoreSwipeNavigationItemsManager; | ||||
|     courseId?: number; | ||||
|     courseName = ''; | ||||
|     groupName?: string; | ||||
| @ -155,7 +159,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { | ||||
|     /** | ||||
|      * View loaded. | ||||
|      */ | ||||
|     ngOnInit(): void { | ||||
|     async ngOnInit(): Promise<void> { | ||||
|         try { | ||||
|             this.eventId = CoreNavigator.getRequiredRouteNumberParam('id'); | ||||
|         } catch (error) { | ||||
| @ -168,7 +172,8 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { | ||||
| 
 | ||||
|         this.syncIcon = CoreConstants.ICON_LOADING; | ||||
| 
 | ||||
|         this.fetchEvent(); | ||||
|         await this.initializeSwipeManager(); | ||||
|         await this.fetchEvent(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -292,6 +297,25 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { | ||||
|         this.syncIcon = CoreConstants.ICON_SYNC; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Initialize swipe manager if enabled. | ||||
|      */ | ||||
|     protected async initializeSwipeManager(): Promise<void> { | ||||
|         const date = CoreNavigator.getRouteParam('date'); | ||||
|         const source = date && CoreRoutedItemsManagerSourcesTracker.getSource( | ||||
|             AddonCalendarEventsSource, | ||||
|             [date], | ||||
|         ); | ||||
| 
 | ||||
|         if (!source) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         this.events = new AddonCalendarEventsSwipeItemsManager(source); | ||||
| 
 | ||||
|         await this.events.start(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sync offline events. | ||||
|      * | ||||
| @ -620,7 +644,22 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy { | ||||
|         this.manualSyncObserver.off(); | ||||
|         this.onlineObserver.unsubscribe(); | ||||
|         this.newEventObserver.off(); | ||||
|         this.events?.destroy(); | ||||
|         clearInterval(this.updateCurrentTime); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Helper to manage swiping within a collection of events. | ||||
|  */ | ||||
| class AddonCalendarEventsSwipeItemsManager extends CoreSwipeNavigationItemsManager { | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     protected getSelectedItemPathFromRoute(route: ActivatedRouteSnapshot): string | null { | ||||
|         return route.params.id; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -30,6 +30,26 @@ export class CoreRoutedItemsManagerSourcesTracker { | ||||
|     private static instances: WeakMap<SourceConstructor, Instances> = new WeakMap(); | ||||
|     private static instanceIds: WeakMap<CoreRoutedItemsManagerSource, string> = new WeakMap(); | ||||
| 
 | ||||
|     /** | ||||
|      * Retrieve an instance given the constructor arguments or id. | ||||
|      * | ||||
|      * @param constructor Source constructor. | ||||
|      * @param constructorArgumentsOrId Arguments to create a new instance, or the id if it's known. | ||||
|      * @returns Source. | ||||
|      */ | ||||
|     static getSource<T extends CoreRoutedItemsManagerSource, C extends SourceConstructor<T>>( | ||||
|         constructor: C, | ||||
|         constructorArgumentsOrId: ConstructorParameters<C> | string, | ||||
|     ): SourceConstuctorInstance<C> | null { | ||||
|         const id = typeof constructorArgumentsOrId === 'string' | ||||
|             ? constructorArgumentsOrId | ||||
|             : constructor.getSourceId(...constructorArgumentsOrId); | ||||
|         const constructorInstances = this.getConstructorInstances(constructor); | ||||
| 
 | ||||
|         return constructorInstances[id]?.instance as SourceConstuctorInstance<C> | ||||
|             ?? null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create an instance of the given source or retrieve one if it's already in use. | ||||
|      * | ||||
| @ -42,9 +62,9 @@ export class CoreRoutedItemsManagerSourcesTracker { | ||||
|         constructorArguments: ConstructorParameters<C>, | ||||
|     ): SourceConstuctorInstance<C> { | ||||
|         const id = constructor.getSourceId(...constructorArguments); | ||||
|         const constructorInstances = this.getConstructorInstances(constructor); | ||||
| 
 | ||||
|         return constructorInstances[id]?.instance as SourceConstuctorInstance<C> | ||||
|         // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | ||||
|         return this.getSource(constructor, id) as any | ||||
|             ?? this.createInstance(id, constructor, constructorArguments); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user