forked from CIT/Vmeda.Online
		
	
						commit
						0c578d8e5d
					
				| @ -91,6 +91,7 @@ | |||||||
|   "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.currentmonth": "local_moodlemobileapp", | ||||||
|   "addon.calendar.daynext": "calendar", |   "addon.calendar.daynext": "calendar", | ||||||
|   "addon.calendar.dayprev": "calendar", |   "addon.calendar.dayprev": "calendar", | ||||||
|   "addon.calendar.defaultnotificationtime": "local_moodlemobileapp", |   "addon.calendar.defaultnotificationtime": "local_moodlemobileapp", | ||||||
|  | |||||||
| @ -4,9 +4,9 @@ | |||||||
|     </ion-refresher> |     </ion-refresher> | ||||||
|     <core-loading [hideUntil]="loaded" class="core-loading-center"> |     <core-loading [hideUntil]="loaded" class="core-loading-center"> | ||||||
|         <div class="safe-padding-horizontal"> |         <div class="safe-padding-horizontal"> | ||||||
|             <ion-item *ngIf="showMyIssuesToggle"> |             <ion-item *ngIf="showMyEntriesToggle"> | ||||||
|                 <ion-label>{{ 'addon.blog.showonlyyourentries' | translate }}</ion-label> |                 <ion-label>{{ 'addon.blog.showonlyyourentries' | translate }}</ion-label> | ||||||
|                 <ion-toggle [(ngModel)]="onlyMyEntries"></ion-toggle> |                 <ion-toggle [(ngModel)]="onlyMyEntries" (ionChange)="onlyMyEntriesToggleChanged(onlyMyEntries)">></ion-toggle> | ||||||
|             </ion-item> |             </ion-item> | ||||||
|         </div> |         </div> | ||||||
|         <core-empty-box *ngIf="entries && entries.length == 0" icon="fa-newspaper-o" [message]="'addon.blog.noentriesyet' | translate"></core-empty-box> |         <core-empty-box *ngIf="entries && entries.length == 0" icon="fa-newspaper-o" [message]="'addon.blog.noentriesyet' | translate"></core-empty-box> | ||||||
|  | |||||||
| @ -15,6 +15,7 @@ | |||||||
| import { Component, Input, OnInit, ViewChild } from '@angular/core'; | import { Component, Input, OnInit, ViewChild } from '@angular/core'; | ||||||
| import { Content } from 'ionic-angular'; | import { Content } from 'ionic-angular'; | ||||||
| import { CoreDomUtilsProvider } from '@providers/utils/dom'; | import { CoreDomUtilsProvider } from '@providers/utils/dom'; | ||||||
|  | import { CoreUtilsProvider } from '@providers/utils/utils'; | ||||||
| import { CoreSitesProvider } from '@providers/sites'; | import { CoreSitesProvider } from '@providers/sites'; | ||||||
| import { CoreUserProvider } from '@core/user/providers/user'; | import { CoreUserProvider } from '@core/user/providers/user'; | ||||||
| import { AddonBlogProvider } from '../../providers/blog'; | import { AddonBlogProvider } from '../../providers/blog'; | ||||||
| @ -38,6 +39,9 @@ export class AddonBlogEntriesComponent implements OnInit { | |||||||
| 
 | 
 | ||||||
|     protected filter = {}; |     protected filter = {}; | ||||||
|     protected pageLoaded = 0; |     protected pageLoaded = 0; | ||||||
|  |     protected userPageLoaded = 0; | ||||||
|  |     protected canLoadMoreEntries = false; | ||||||
|  |     protected canLoadMoreUserEntries = true; | ||||||
| 
 | 
 | ||||||
|     @ViewChild(Content) content: Content; |     @ViewChild(Content) content: Content; | ||||||
| 
 | 
 | ||||||
| @ -46,14 +50,14 @@ export class AddonBlogEntriesComponent implements OnInit { | |||||||
|     loadMoreError = false; |     loadMoreError = false; | ||||||
|     entries = []; |     entries = []; | ||||||
|     currentUserId: number; |     currentUserId: number; | ||||||
|     showMyIssuesToggle = false; |     showMyEntriesToggle = false; | ||||||
|     onlyMyEntries = false; |     onlyMyEntries = false; | ||||||
|     component = AddonBlogProvider.COMPONENT; |     component = AddonBlogProvider.COMPONENT; | ||||||
|     commentsEnabled: boolean; |     commentsEnabled: boolean; | ||||||
|     tagsEnabled: boolean; |     tagsEnabled: boolean; | ||||||
| 
 | 
 | ||||||
|     constructor(protected blogProvider: AddonBlogProvider, protected domUtils: CoreDomUtilsProvider, |     constructor(protected blogProvider: AddonBlogProvider, protected domUtils: CoreDomUtilsProvider, | ||||||
|             protected userProvider: CoreUserProvider, sitesProvider: CoreSitesProvider, |             protected userProvider: CoreUserProvider, sitesProvider: CoreSitesProvider, protected utils: CoreUtilsProvider, | ||||||
|             protected commentsProvider: CoreCommentsProvider, private tagProvider: CoreTagProvider) { |             protected commentsProvider: CoreCommentsProvider, private tagProvider: CoreTagProvider) { | ||||||
|         this.currentUserId = sitesProvider.getCurrentSiteUserId(); |         this.currentUserId = sitesProvider.getCurrentSiteUserId(); | ||||||
|     } |     } | ||||||
| @ -65,6 +69,7 @@ export class AddonBlogEntriesComponent implements OnInit { | |||||||
|         if (this.userId) { |         if (this.userId) { | ||||||
|             this.filter['userid'] = this.userId; |             this.filter['userid'] = this.userId; | ||||||
|         } |         } | ||||||
|  |         this.showMyEntriesToggle = !this.userId; | ||||||
| 
 | 
 | ||||||
|         if (this.courseId) { |         if (this.courseId) { | ||||||
|             this.filter['courseid'] = this.courseId; |             this.filter['courseid'] = this.courseId; | ||||||
| @ -107,9 +112,12 @@ export class AddonBlogEntriesComponent implements OnInit { | |||||||
| 
 | 
 | ||||||
|         if (refresh) { |         if (refresh) { | ||||||
|             this.pageLoaded = 0; |             this.pageLoaded = 0; | ||||||
|  |             this.userPageLoaded = 0; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return this.blogProvider.getEntries(this.filter, this.pageLoaded).then((result) => { |         const loadPage = this.onlyMyEntries ? this.userPageLoaded : this.pageLoaded; | ||||||
|  | 
 | ||||||
|  |         return this.blogProvider.getEntries(this.filter, loadPage).then((result) => { | ||||||
|             const promises = result.entries.map((entry) => { |             const promises = result.entries.map((entry) => { | ||||||
|                 switch (entry.publishstate) { |                 switch (entry.publishstate) { | ||||||
|                     case 'draft': |                     case 'draft': | ||||||
| @ -134,16 +142,25 @@ export class AddonBlogEntriesComponent implements OnInit { | |||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|             if (refresh) { |             if (refresh) { | ||||||
|                 this.showMyIssuesToggle = false; |  | ||||||
|                 this.entries = result.entries; |                 this.entries = result.entries; | ||||||
|             } else { |             } else { | ||||||
|                 this.entries = this.entries.concat(result.entries); |                 this.entries = this.utils.uniqueArray(this.entries.concat(result.entries), 'id').sort((a, b) => { | ||||||
|  |                     return b.created - a.created; | ||||||
|  |                 }); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             this.canLoadMore = result.totalentries > this.entries.length; |             if (this.onlyMyEntries) { | ||||||
|  |                 const count = this.entries.filter((entry) => { | ||||||
|  |                     return entry.userid == this.currentUserId; | ||||||
|  |                 }).length; | ||||||
|  |                 this.canLoadMoreUserEntries = result.totalentries > count; | ||||||
|  |                 this.canLoadMore = this.canLoadMoreUserEntries; | ||||||
|  |                 this.userPageLoaded++; | ||||||
|  |             } else { | ||||||
|  |                 this.canLoadMoreEntries = result.totalentries > this.entries.length; | ||||||
|  |                 this.canLoadMore = this.canLoadMoreEntries; | ||||||
|                 this.pageLoaded++; |                 this.pageLoaded++; | ||||||
| 
 |             } | ||||||
|             this.showMyIssuesToggle = !this.userId; |  | ||||||
| 
 | 
 | ||||||
|             return Promise.all(promises); |             return Promise.all(promises); | ||||||
|         }).catch((message) => { |         }).catch((message) => { | ||||||
| @ -154,6 +171,30 @@ export class AddonBlogEntriesComponent implements OnInit { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Toggle between showing only my entries or not. | ||||||
|  |      * | ||||||
|  |      * @param {boolean} enabled If true, filter my entries. False otherwise. | ||||||
|  |      */ | ||||||
|  |     onlyMyEntriesToggleChanged(enabled: boolean): void { | ||||||
|  |         if (enabled) { | ||||||
|  |             const count = this.entries.filter((entry) => { | ||||||
|  |                 return entry.userid == this.currentUserId; | ||||||
|  |             }).length; | ||||||
|  |             this.userPageLoaded = Math.floor(count / AddonBlogProvider.ENTRIES_PER_PAGE); | ||||||
|  |             this.filter['userid'] = this.currentUserId; | ||||||
|  | 
 | ||||||
|  |             if (count == 0 && this.canLoadMoreUserEntries) { | ||||||
|  |                 // First time but no entry loaded. Try to load some.
 | ||||||
|  |                 this.loadMore(); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             delete this.filter['userid']; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.canLoadMore = enabled ? this.canLoadMoreUserEntries : this.canLoadMoreEntries; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Function to load more entries. |      * Function to load more entries. | ||||||
|      * |      * | ||||||
| @ -178,7 +219,17 @@ export class AddonBlogEntriesComponent implements OnInit { | |||||||
| 
 | 
 | ||||||
|         promises.push(this.blogProvider.invalidateEntries(this.filter)); |         promises.push(this.blogProvider.invalidateEntries(this.filter)); | ||||||
| 
 | 
 | ||||||
|         Promise.all(promises).finally(() => { |         if (this.showMyEntriesToggle) { | ||||||
|  |             this.filter['userid'] = this.currentUserId; | ||||||
|  |             promises.push(this.blogProvider.invalidateEntries(this.filter)); | ||||||
|  | 
 | ||||||
|  |             if (!this.showMyEntriesToggle) { | ||||||
|  |                 delete this.filter['userid']; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.utils.allPromises(promises).finally(() => { | ||||||
|             this.fetchEntries(true).finally(() => { |             this.fetchEntries(true).finally(() => { | ||||||
|                 if (refresher) { |                 if (refresher) { | ||||||
|                     refresher.complete(); |                     refresher.complete(); | ||||||
|  | |||||||
| @ -1,9 +1,9 @@ | |||||||
| 
 | 
 | ||||||
| <!-- Add buttons to the nav bar. --> | <!-- Add buttons to the nav bar. --> | ||||||
| <core-navbar-buttons end prepend> | <core-navbar-buttons end prepend> | ||||||
|     <button [hidden]="!canNavigate || isCurrentMonth || !displayNavButtons" ion-button icon-only clear (click)="goToCurrentMonth()"> |     <core-context-menu> | ||||||
|         <core-icon  name="fa-calendar-times-o"></core-icon> |         <core-context-menu-item *ngIf="canNavigate && !isCurrentMonth && displayNavButtons" [priority]="900" [content]="'addon.calendar.currentmonth' | translate" [iconAction]="'fa-calendar-times-o'" (action)="goToCurrentMonth()"></core-context-menu-item> | ||||||
|     </button> |     </core-context-menu> | ||||||
| </core-navbar-buttons> | </core-navbar-buttons> | ||||||
| 
 | 
 | ||||||
| <core-loading [hideUntil]="loaded" class="core-loading-center"> | <core-loading [hideUntil]="loaded" class="core-loading-center"> | ||||||
| @ -31,15 +31,16 @@ | |||||||
|         <!-- List of days. --> |         <!-- List of days. --> | ||||||
|         <ion-row> |         <ion-row> | ||||||
|             <ion-col text-center *ngFor="let day of weekDays" class="addon-calendar-weekday"> |             <ion-col text-center *ngFor="let day of weekDays" class="addon-calendar-weekday"> | ||||||
|                 {{ day.shortname | translate }} |                 <span class="hidden-tablet" [title]="day.fullname | translate">{{ day.shortname | translate }}</span> | ||||||
|  |                 <span class="hidden-phone">{{ day.fullname | translate }}</span> | ||||||
|             </ion-col> |             </ion-col> | ||||||
|         </ion-row> |         </ion-row> | ||||||
| 
 | 
 | ||||||
|         <!-- Weeks. --> |         <!-- Weeks. --> | ||||||
|         <ion-row *ngFor="let week of weeks" class="addon-calendar-week"> |         <ion-row *ngFor="let week of weeks" class="addon-calendar-week"> | ||||||
|             <ion-col *ngFor="let value of week.prepadding" class="dayblank addon-calendar-day"></ion-col> <!-- Empty slots (first week). --> |             <ion-col *ngFor="let value of week.prepadding" class="dayblank addon-calendar-day"></ion-col> <!-- Empty slots (first week). --> | ||||||
|             <ion-col text-center *ngFor="let day of week.days"  (click)="dayClicked(day.mday)" [ngClass]='{"hasevents": day.hasevents, "today": day.istoday, "weekend": day.isweekend, "duration_finish": day.haslastdayofevent}' class="addon-calendar-day"> |             <ion-col text-center *ngFor="let day of week.days"  (click)="dayClicked(day.mday)" [ngClass]='{"hasevents": day.hasevents, "today": day.istoday, "weekend": day.isweekend, "duration_finish": day.haslastdayofevent}' class="addon-calendar-day" [class.addon-calendar-event-past-day]="isPastMonth || day.ispast"> | ||||||
|                 <p class="addon-calendar-day-number">{{ day.mday }}</p> |                 <p class="addon-calendar-day-number"><span>{{ day.mday }}</span></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 addon-calendar-dot-types"><span *ngFor="let type of day.calendareventtypes" class="calendar_event_type calendar_event_{{type}}"></span></p> |                 <p class="hidden-tablet addon-calendar-dot-types"><span *ngFor="let type of day.calendareventtypes" class="calendar_event_type calendar_event_{{type}}"></span></p> | ||||||
| @ -47,12 +48,12 @@ | |||||||
|                 <!-- In tablet, display list of events. --> |                 <!-- In tablet, display list of events. --> | ||||||
|                 <div class="hidden-phone addon-calendar-day-events"> |                 <div class="hidden-phone addon-calendar-day-events"> | ||||||
|                     <ng-container *ngFor="let event of day.filteredEvents | slice:0:4; let index = index"> |                     <ng-container *ngFor="let event of day.filteredEvents | slice:0:4; let index = index"> | ||||||
|                         <p *ngIf="index < 3 || day.filteredEvents.length == 4" class="addon-calendar-event" (click)="eventClicked(event, $event)"> |                         <p *ngIf="index < 3 || day.filteredEvents.length == 4" class="addon-calendar-event" (click)="eventClicked(event, $event)" [class.addon-calendar-event-past]="event.ispast"> | ||||||
|                             <span class="calendar_event_type calendar_event_{{event.formattedType}}"></span> |                             <span class="calendar_event_type calendar_event_{{event.formattedType}}"></span> | ||||||
|                             <ion-icon *ngIf="event.offline && !event.deleted" name="time"></ion-icon> |                             <ion-icon *ngIf="event.offline && !event.deleted" name="time"></ion-icon> | ||||||
|                             <ion-icon *ngIf="event.deleted" name="trash"></ion-icon> |                             <ion-icon *ngIf="event.deleted" name="trash"></ion-icon> | ||||||
|                             <img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" alt="" role="presentation" class="core-module-icon"> |  | ||||||
|                             <span class="addon-calendar-event-time">{{ event.timestart * 1000 | coreFormatDate: timeFormat }}</span> |                             <span class="addon-calendar-event-time">{{ event.timestart * 1000 | coreFormatDate: timeFormat }}</span> | ||||||
|  |                             <img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" alt="" role="presentation" class="core-module-icon"> | ||||||
|                             <span class="addon-calendar-event-name">{{event.name}}</span> |                             <span class="addon-calendar-event-name">{{event.name}}</span> | ||||||
|                         </p> |                         </p> | ||||||
|                     </ng-container> |                     </ng-container> | ||||||
|  | |||||||
| @ -32,17 +32,34 @@ ion-app.app-root addon-calendar-calendar { | |||||||
|             @include border-end(0, null, null); |             @include border-end(0, null, null); | ||||||
|             @include padding(null, 8px, null, null); |             @include padding(null, 8px, null, null); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         &.addon-calendar-event-past-day > .addon-calendar-dot-types, | ||||||
|  |         &.addon-calendar-event-past-day > .addon-calendar-day-events { | ||||||
|  |             opacity: 0.5; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         .addon-calendar-day-number { |         .addon-calendar-day-number { | ||||||
|             height: 24px; |             margin: 0; | ||||||
|  | 
 | ||||||
|  |             span { | ||||||
|                 line-height: 24px; |                 line-height: 24px; | ||||||
|             width: max-content; |  | ||||||
|             min-width: 24px; |  | ||||||
|             text-align: center; |  | ||||||
|                 font-weight: 500; |                 font-weight: 500; | ||||||
|                 display: inline-block; |                 display: inline-block; | ||||||
|                 margin: 3px; |                 margin: 3px; | ||||||
|  |                 width: max-content; | ||||||
|  |                 width: 24px; | ||||||
|  |                 height: 24px; | ||||||
|  |                 text-align: center; | ||||||
|             } |             } | ||||||
|         &.today .addon-calendar-day-number { |         } | ||||||
|  | 
 | ||||||
|  |         @include media-breakpoint-up(md) { | ||||||
|  |             .addon-calendar-day-number { | ||||||
|  |                 text-align: left; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         &.today .addon-calendar-day-number span { | ||||||
|             background-color: $calendar-today-bgcolor; |             background-color: $calendar-today-bgcolor; | ||||||
|             color: $calendar-today-color; |             color: $calendar-today-color; | ||||||
| 
 | 
 | ||||||
| @ -58,6 +75,10 @@ ion-app.app-root addon-calendar-calendar { | |||||||
|             overflow: hidden; |             overflow: hidden; | ||||||
|             white-space: nowrap; |             white-space: nowrap; | ||||||
| 
 | 
 | ||||||
|  |             &.addon-calendar-event-past { | ||||||
|  |                 opacity: 0.5; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             .addon-calendar-event-name { |             .addon-calendar-event-name { | ||||||
|                 font-weight: 500; |                 font-weight: 500; | ||||||
|             } |             } | ||||||
| @ -81,7 +102,6 @@ ion-app.app-root addon-calendar-calendar { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     .addon-calendar-weekday { |     .addon-calendar-weekday { | ||||||
|         color: $gray-dark; |  | ||||||
|         border-bottom: 1px solid $list-md-border-color; |         border-bottom: 1px solid $list-md-border-color; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -130,6 +150,6 @@ ion-app.app-root addon-calendar-calendar { | |||||||
|         width: 16px; |         width: 16px; | ||||||
|         height: 16px; |         height: 16px; | ||||||
|         display: inline-block; |         display: inline-block; | ||||||
|         vertical-align: middle; |         vertical-align: bottom; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -48,6 +48,7 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest | |||||||
|     loaded = false; |     loaded = false; | ||||||
|     timeFormat: string; |     timeFormat: string; | ||||||
|     isCurrentMonth: boolean; |     isCurrentMonth: boolean; | ||||||
|  |     isPastMonth: boolean; | ||||||
| 
 | 
 | ||||||
|     protected year: number; |     protected year: number; | ||||||
|     protected month: number; |     protected month: number; | ||||||
| @ -57,6 +58,7 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest | |||||||
|     protected offlineEvents: {[monthId: string]: {[day: number]: any[]}} = {}; // Offline events classified in month & day.
 |     protected offlineEvents: {[monthId: string]: {[day: number]: any[]}} = {}; // Offline events classified in month & day.
 | ||||||
|     protected offlineEditedEventsIds = []; // IDs of events edited in offline.
 |     protected offlineEditedEventsIds = []; // IDs of events edited in offline.
 | ||||||
|     protected deletedEvents = []; // Events deleted in offline.
 |     protected deletedEvents = []; // Events deleted in offline.
 | ||||||
|  |     protected currentTime: number; | ||||||
| 
 | 
 | ||||||
|     // Observers.
 |     // Observers.
 | ||||||
|     protected undeleteEventObserver: any; |     protected undeleteEventObserver: any; | ||||||
| @ -200,6 +202,28 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest | |||||||
|             this.weekDays = this.calendarProvider.getWeekDays(result.daynames[0].dayno); |             this.weekDays = this.calendarProvider.getWeekDays(result.daynames[0].dayno); | ||||||
|             this.weeks = result.weeks; |             this.weeks = result.weeks; | ||||||
| 
 | 
 | ||||||
|  |             this.calculateIsCurrentMonth(); | ||||||
|  | 
 | ||||||
|  |             if (this.isCurrentMonth) { | ||||||
|  |                 let isPast = true; | ||||||
|  |                 this.weeks.forEach((week) => { | ||||||
|  |                     week.days.some((day) => { | ||||||
|  |                         day.ispast = isPast && !day.istoday; | ||||||
|  |                         isPast = day.ispast; | ||||||
|  | 
 | ||||||
|  |                         if (day.istoday) { | ||||||
|  |                             day.events.forEach((event) => { | ||||||
|  |                                 event.ispast = this.isEventPast(event); | ||||||
|  |                             }); | ||||||
|  | 
 | ||||||
|  |                             return true; | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         return day.istoday; | ||||||
|  |                     }); | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             // Merge the online events with offline data.
 |             // Merge the online events with offline data.
 | ||||||
|             this.mergeEvents(); |             this.mergeEvents(); | ||||||
| 
 | 
 | ||||||
| @ -288,7 +312,6 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest | |||||||
|             this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true); |             this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true); | ||||||
|             this.decreaseMonth(); |             this.decreaseMonth(); | ||||||
|         }).finally(() => { |         }).finally(() => { | ||||||
|             this.calculateIsCurrentMonth(); |  | ||||||
|             this.loaded = true; |             this.loaded = true; | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| @ -305,7 +328,6 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest | |||||||
|             this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true); |             this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true); | ||||||
|             this.increaseMonth(); |             this.increaseMonth(); | ||||||
|         }).finally(() => { |         }).finally(() => { | ||||||
|             this.calculateIsCurrentMonth(); |  | ||||||
|             this.loaded = true; |             this.loaded = true; | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| @ -336,7 +358,10 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest | |||||||
|     calculateIsCurrentMonth(): void { |     calculateIsCurrentMonth(): void { | ||||||
|         const now = new Date(); |         const now = new Date(); | ||||||
| 
 | 
 | ||||||
|  |         this.currentTime = this.timeUtils.timestamp(); | ||||||
|  | 
 | ||||||
|         this.isCurrentMonth = this.year == now.getFullYear() && this.month == now.getMonth() + 1; |         this.isCurrentMonth = this.year == now.getFullYear() && this.month == now.getMonth() + 1; | ||||||
|  |         this.isPastMonth = this.year < now.getFullYear() || (this.year == now.getFullYear() && this.month < now.getMonth() + 1); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -466,6 +491,15 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns if the event is in the past or not. | ||||||
|  |      * @param  {any}     event Event object. | ||||||
|  |      * @return {boolean}       True if it's in the past. | ||||||
|  |      */ | ||||||
|  |     isEventPast(event: any): boolean { | ||||||
|  |         return (event.timestart + event.timeduration) < this.currentTime; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Component destroyed. |      * Component destroyed. | ||||||
|      */ |      */ | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ | |||||||
|     "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?", | ||||||
|  |     "currentmonth": "Current Month", | ||||||
|     "daynext": "Next day", |     "daynext": "Next day", | ||||||
|     "dayprev": "Previous day", |     "dayprev": "Previous day", | ||||||
|     "defaultnotificationtime": "Default notification time", |     "defaultnotificationtime": "Default notification time", | ||||||
|  | |||||||
| @ -2,13 +2,12 @@ | |||||||
|     <ion-navbar core-back-button> |     <ion-navbar core-back-button> | ||||||
|         <ion-title>{{ 'addon.calendar.calendarevents' | translate }}</ion-title> |         <ion-title>{{ 'addon.calendar.calendarevents' | translate }}</ion-title> | ||||||
|         <ion-buttons end> |         <ion-buttons end> | ||||||
|             <button *ngIf="!isCurrentDay" ion-button icon-only clear (click)="goToCurrentDay()"> |  | ||||||
|                 <core-icon name="fa-calendar-times-o"></core-icon> |  | ||||||
|             </button> |  | ||||||
|             <button *ngIf="courses && courses.length" ion-button icon-only (click)="openCourseFilter($event)" [attr.aria-label]="'core.courses.filter' | translate"> |             <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> |                 <ion-icon name="funnel" *ngIf="courseId"></ion-icon> | ||||||
|  |                 <ion-icon name="ios-funnel-outline" *ngIf="!courseId"></ion-icon> | ||||||
|             </button> |             </button> | ||||||
|             <core-context-menu> |             <core-context-menu> | ||||||
|  |                 <core-context-menu-item *ngIf="!isCurrentDay" [priority]="900" [content]="'addon.calendar.today' | translate" [iconAction]="'fa-calendar-times-o'" (action)="goToCurrentDay()"></core-context-menu-item> | ||||||
|                 <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-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> |             </core-context-menu> | ||||||
|         </ion-buttons> |         </ion-buttons> | ||||||
| @ -49,7 +48,7 @@ | |||||||
| 
 | 
 | ||||||
|         <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"> | ||||||
|                 <a ion-item text-wrap [title]="event.name" (click)="gotoEvent(event.id)"> |                 <ion-item text-wrap [title]="event.name" (click)="gotoEvent(event.id)" [class.item-dimmed]="event.ispast"> | ||||||
|                     <img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" item-start class="core-module-icon"> |                     <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> |                     <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> |                     <h2><core-format-text [text]="event.name"></core-format-text></h2> | ||||||
| @ -62,7 +61,7 @@ | |||||||
|                         <ion-icon name="trash"></ion-icon> |                         <ion-icon name="trash"></ion-icon> | ||||||
|                         <span text-wrap>{{ 'core.deletedoffline' | translate }}</span> |                         <span text-wrap>{{ 'core.deletedoffline' | translate }}</span> | ||||||
|                     </ion-note> |                     </ion-note> | ||||||
|                 </a> |                 </ion-item> | ||||||
|             </ng-container> |             </ng-container> | ||||||
|         </ion-list> |         </ion-list> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -51,6 +51,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { | |||||||
|     protected deletedEvents = []; // Events deleted in offline.
 |     protected deletedEvents = []; // Events deleted in offline.
 | ||||||
|     protected timeFormat: string; |     protected timeFormat: string; | ||||||
|     protected currentMoment: moment.Moment; |     protected currentMoment: moment.Moment; | ||||||
|  |     protected currentTime: number; | ||||||
| 
 | 
 | ||||||
|     // Observers.
 |     // Observers.
 | ||||||
|     protected newEventObserver: any; |     protected newEventObserver: any; | ||||||
| @ -74,6 +75,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { | |||||||
|     isOnline = false; |     isOnline = false; | ||||||
|     syncIcon: string; |     syncIcon: string; | ||||||
|     isCurrentDay: boolean; |     isCurrentDay: boolean; | ||||||
|  |     isPastDay: boolean; | ||||||
| 
 | 
 | ||||||
|     constructor(localNotificationsProvider: CoreLocalNotificationsProvider, |     constructor(localNotificationsProvider: CoreLocalNotificationsProvider, | ||||||
|             navParams: NavParams, |             navParams: NavParams, | ||||||
| @ -308,9 +310,12 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { | |||||||
|             // Filter events by course.
 |             // Filter events by course.
 | ||||||
|             this.filterEvents(); |             this.filterEvents(); | ||||||
| 
 | 
 | ||||||
|  |             this.calculateIsCurrentDay(); | ||||||
|  | 
 | ||||||
|             // Re-calculate the formatted time so it uses the device date.
 |             // Re-calculate the formatted time so it uses the device date.
 | ||||||
|             const dayTime = this.currentMoment.unix() * 1000; |             const dayTime = this.currentMoment.unix() * 1000; | ||||||
|             this.events.forEach((event) => { |             this.events.forEach((event) => { | ||||||
|  |                 event.ispast = this.isPastDay || (this.isCurrentDay && this.isEventPast(event)); | ||||||
|                 promises.push(this.calendarProvider.formatEventTime(event, this.timeFormat, true, dayTime).then((time) => { |                 promises.push(this.calendarProvider.formatEventTime(event, this.timeFormat, true, dayTime).then((time) => { | ||||||
|                     event.formattedtime = time; |                     event.formattedtime = time; | ||||||
|                 })); |                 })); | ||||||
| @ -555,7 +560,11 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { | |||||||
|     calculateIsCurrentDay(): void { |     calculateIsCurrentDay(): void { | ||||||
|         const now = new Date(); |         const now = new Date(); | ||||||
| 
 | 
 | ||||||
|  |         this.currentTime = this.timeUtils.timestamp(); | ||||||
|  | 
 | ||||||
|         this.isCurrentDay = this.year == now.getFullYear() && this.month == now.getMonth() + 1 && this.day == now.getDate(); |         this.isCurrentDay = this.year == now.getFullYear() && this.month == now.getMonth() + 1 && this.day == now.getDate(); | ||||||
|  |         this.isPastDay = this.year < now.getFullYear() || (this.year == now.getFullYear() && this.month < now.getMonth()) || | ||||||
|  |             (this.year == now.getFullYear() && this.month == now.getMonth() + 1 && this.day < now.getDate()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -600,7 +609,6 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { | |||||||
|             this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true); |             this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true); | ||||||
|             this.decreaseDay(); |             this.decreaseDay(); | ||||||
|         }).finally(() => { |         }).finally(() => { | ||||||
|             this.calculateIsCurrentDay(); |  | ||||||
|             this.loaded = true; |             this.loaded = true; | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| @ -617,7 +625,6 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { | |||||||
|             this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true); |             this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true); | ||||||
|             this.increaseDay(); |             this.increaseDay(); | ||||||
|         }).finally(() => { |         }).finally(() => { | ||||||
|             this.calculateIsCurrentDay(); |  | ||||||
|             this.loaded = true; |             this.loaded = true; | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| @ -665,6 +672,15 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { | |||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Returns if the event is in the past or not. | ||||||
|  |      * @param  {any}     event Event object. | ||||||
|  |      * @return {boolean}       True if it's in the past. | ||||||
|  |      */ | ||||||
|  |     isEventPast(event: any): boolean { | ||||||
|  |         return (event.timestart + event.timeduration) < this.currentTime; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Page destroyed. |      * Page destroyed. | ||||||
|      */ |      */ | ||||||
|  | |||||||
| @ -2,16 +2,13 @@ | |||||||
|     <ion-navbar core-back-button> |     <ion-navbar core-back-button> | ||||||
|         <ion-title>{{ (showCalendar ? 'addon.calendar.calendarevents' : 'addon.calendar.upcomingevents') | translate }}</ion-title> |         <ion-title>{{ (showCalendar ? 'addon.calendar.calendarevents' : 'addon.calendar.upcomingevents') | translate }}</ion-title> | ||||||
|         <ion-buttons end> |         <ion-buttons end> | ||||||
|             <button *ngIf="showCalendar" ion-button icon-only (click)="toggleDisplay()" [attr.aria-label]="'addon.calendar.upcomingevents' | translate"> |  | ||||||
|                 <ion-icon name="list"></ion-icon> |  | ||||||
|             </button> |  | ||||||
|             <button *ngIf="!showCalendar" ion-button icon-only (click)="toggleDisplay()" [attr.aria-label]="'addon.calendar.monthlyview' | translate"> |  | ||||||
|                 <core-icon name="fa-calendar-o"></core-icon> |  | ||||||
|             </button> |  | ||||||
|             <button *ngIf="courses && courses.length" ion-button icon-only (click)="openCourseFilter($event)" [attr.aria-label]="'core.courses.filter' | translate"> |             <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> |                 <ion-icon name="funnel" *ngIf="courseId"></ion-icon> | ||||||
|  |                 <ion-icon name="ios-funnel-outline" *ngIf="!courseId"></ion-icon> | ||||||
|             </button> |             </button> | ||||||
|             <core-context-menu> |             <core-context-menu> | ||||||
|  |                 <core-context-menu-item *ngIf="showCalendar" [priority]="800" [content]="'addon.calendar.upcomingevents' | translate" [iconAction]="'list'" (action)="toggleDisplay()"></core-context-menu-item> | ||||||
|  |                 <core-context-menu-item *ngIf="!showCalendar" [priority]="800" [content]="'addon.calendar.monthlyview' | translate" [iconAction]="'fa-calendar-o'" (action)="toggleDisplay()"></core-context-menu-item> | ||||||
|                 <core-context-menu-item [hidden]="!notificationsEnabled" [priority]="600" [content]="'core.settings.settings' | translate" (action)="openSettings()" [iconAction]="'cog'"></core-context-menu-item> |                 <core-context-menu-item [hidden]="!notificationsEnabled" [priority]="600" [content]="'core.settings.settings' | translate" (action)="openSettings()" [iconAction]="'cog'"></core-context-menu-item> | ||||||
|                 <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-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> |             </core-context-menu> | ||||||
|  | |||||||
| @ -1,3 +0,0 @@ | |||||||
| page-addon-calendar-index .toolbar-ios ion-title { |  | ||||||
|     @include padding-horizontal(null, 120px); |  | ||||||
| } |  | ||||||
| @ -3,7 +3,8 @@ | |||||||
|         <ion-title>{{ 'addon.calendar.calendarevents' | translate }}</ion-title> |         <ion-title>{{ 'addon.calendar.calendarevents' | translate }}</ion-title> | ||||||
|         <ion-buttons end> |         <ion-buttons end> | ||||||
|             <button *ngIf="courses && courses.length" ion-button icon-only (click)="openCourseFilter($event)" [attr.aria-label]="'core.courses.filter' | translate"> |             <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> |                 <ion-icon name="funnel" *ngIf="courseId"></ion-icon> | ||||||
|  |                 <ion-icon name="ios-funnel-outline" *ngIf="!courseId"></ion-icon> | ||||||
|             </button> |             </button> | ||||||
|             <core-context-menu> |             <core-context-menu> | ||||||
|                 <core-context-menu-item [hidden]="!notificationsEnabled" [priority]="600" [content]="'core.settings.settings' | translate" (action)="openSettings()" [iconAction]="'cog'"></core-context-menu-item> |                 <core-context-menu-item [hidden]="!notificationsEnabled" [priority]="600" [content]="'core.settings.settings' | translate" (action)="openSettings()" [iconAction]="'cog'"></core-context-menu-item> | ||||||
|  | |||||||
| @ -90,6 +90,7 @@ | |||||||
|     "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.currentmonth": "Current Month", | ||||||
|     "addon.calendar.daynext": "Next day", |     "addon.calendar.daynext": "Next day", | ||||||
|     "addon.calendar.dayprev": "Previous day", |     "addon.calendar.dayprev": "Previous day", | ||||||
|     "addon.calendar.defaultnotificationtime": "Default notification time", |     "addon.calendar.defaultnotificationtime": "Default notification time", | ||||||
|  | |||||||
| @ -234,7 +234,15 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges, OnDe | |||||||
|      */ |      */ | ||||||
|     calculateTabBarHeight(): void { |     calculateTabBarHeight(): void { | ||||||
|         this.tabBarHeight = this.topTabsElement.offsetHeight; |         this.tabBarHeight = this.topTabsElement.offsetHeight; | ||||||
|         this.originalTabsContainer.style.paddingBottom = this.tabBarHeight + 'px'; | 
 | ||||||
|  |         if (this.tabsShown) { | ||||||
|  |             // Smooth translation.
 | ||||||
|  |             this.topTabsElement.style.transform = 'translateY(-' + this.lastScroll + 'px)'; | ||||||
|  |             this.originalTabsContainer.style.transform = 'translateY(-' + this.lastScroll + 'px)'; | ||||||
|  |             this.originalTabsContainer.style.paddingBottom = this.tabBarHeight - this.lastScroll + 'px'; | ||||||
|  |         } else { | ||||||
|  |             this.tabBarElement.classList.add('tabs-hidden'); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -3,7 +3,6 @@ | |||||||
| </div> | </div> | ||||||
| 
 | 
 | ||||||
| <div *ngIf="blocks && blocks.length > 0" [class.core-hide-blocks]="hideBlocks" class="core-course-blocks-side"> | <div *ngIf="blocks && blocks.length > 0" [class.core-hide-blocks]="hideBlocks" class="core-course-blocks-side"> | ||||||
|     <div class="core-course-blocks-side-scroll"> |  | ||||||
|     <core-loading [hideUntil]="dataLoaded" class="core-loading-center"> |     <core-loading [hideUntil]="dataLoaded" class="core-loading-center"> | ||||||
|         <ion-list> |         <ion-list> | ||||||
|             <!-- Course blocks. --> |             <!-- Course blocks. --> | ||||||
| @ -12,5 +11,4 @@ | |||||||
|             </ng-container> |             </ng-container> | ||||||
|         </ion-list> |         </ion-list> | ||||||
|     </core-loading> |     </core-loading> | ||||||
|     </div> |  | ||||||
| </div> | </div> | ||||||
|  | |||||||
| @ -13,10 +13,6 @@ ion-app.app-root core-block-course-blocks { | |||||||
| 
 | 
 | ||||||
|     &.core-has-blocks { |     &.core-has-blocks { | ||||||
|         @include media-breakpoint-up(md) { |         @include media-breakpoint-up(md) { | ||||||
|             @include position(0, 0, 0, 0); |  | ||||||
| 
 |  | ||||||
|             position: absolute; |  | ||||||
| 
 |  | ||||||
|             display: flex; |             display: flex; | ||||||
| 
 | 
 | ||||||
|             flex-direction: row; |             flex-direction: row; | ||||||
| @ -29,46 +25,20 @@ ion-app.app-root core-block-course-blocks { | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             div.core-course-blocks-side { |             div.core-course-blocks-side { | ||||||
|                 position: relative; |  | ||||||
|                 @include position(auto, 0, auto, auto); |  | ||||||
|                 max-width: $core-side-blocks-max-width; |                 max-width: $core-side-blocks-max-width; | ||||||
|                 min-width: $core-side-blocks-min-width; |                 min-width: $core-side-blocks-min-width; | ||||||
|                 @include border-start(1px, solid, $list-md-border-color); |                 @include border-start(1px, solid, $list-md-border-color); | ||||||
| 
 |  | ||||||
|                 .core-course-blocks-side-scroll { |  | ||||||
|                     position: absolute; |  | ||||||
|                     top: 0; |  | ||||||
|                     max-width: 100%; |  | ||||||
|                     min-width: 100%; |  | ||||||
| 
 |  | ||||||
|                     &.core-course-blocks-fixed-bottom { |  | ||||||
|                         position: fixed; |  | ||||||
|                         bottom: 0; |  | ||||||
|                         top: auto; |  | ||||||
|                         transform: none !important; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     core-block { |  | ||||||
|                         max-width: $core-side-blocks-max-width; |  | ||||||
|                         min-width: $core-side-blocks-min-width; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         @include media-breakpoint-down(sm) { |         @include media-breakpoint-down(sm) { | ||||||
|             // Disable scroll on individual columns. |             // Disable scroll on individual columns. | ||||||
|             div.core-course-blocks-side { |             div.core-course-blocks-side { | ||||||
|                 transform: none !important; |  | ||||||
|                 height: auto; |                 height: auto; | ||||||
| 
 | 
 | ||||||
|                 &.core-hide-blocks { |                 &.core-hide-blocks { | ||||||
|                     display: none; |                     display: none; | ||||||
|                 } |                 } | ||||||
| 
 |  | ||||||
|                 .core-course-blocks-side-scroll { |  | ||||||
|                     transform: none !important; |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -12,10 +12,9 @@ | |||||||
| // See the License for the specific language governing permissions and
 | // See the License for the specific language governing permissions and
 | ||||||
| // limitations under the License.
 | // limitations under the License.
 | ||||||
| 
 | 
 | ||||||
| import { Component, ViewChildren, Input, OnInit, QueryList, ElementRef, OnDestroy } from '@angular/core'; | import { Component, ViewChildren, Input, OnInit, QueryList, ElementRef } from '@angular/core'; | ||||||
| import { Content } from 'ionic-angular'; | import { Content } from 'ionic-angular'; | ||||||
| import { CoreDomUtilsProvider } from '@providers/utils/dom'; | import { CoreDomUtilsProvider } from '@providers/utils/dom'; | ||||||
| import { CoreAppProvider } from '@providers/app'; |  | ||||||
| import { CoreCourseProvider } from '@core/course/providers/course'; | import { CoreCourseProvider } from '@core/course/providers/course'; | ||||||
| import { CoreBlockComponent } from '../block/block'; | import { CoreBlockComponent } from '../block/block'; | ||||||
| import { CoreBlockHelperProvider } from '../../providers/helper'; | import { CoreBlockHelperProvider } from '../../providers/helper'; | ||||||
| @ -27,7 +26,7 @@ import { CoreBlockHelperProvider } from '../../providers/helper'; | |||||||
|     selector: 'core-block-course-blocks', |     selector: 'core-block-course-blocks', | ||||||
|     templateUrl: 'core-block-course-blocks.html', |     templateUrl: 'core-block-course-blocks.html', | ||||||
| }) | }) | ||||||
| export class CoreBlockCourseBlocksComponent implements OnInit, OnDestroy { | export class CoreBlockCourseBlocksComponent implements OnInit { | ||||||
| 
 | 
 | ||||||
|     @Input() courseId: number; |     @Input() courseId: number; | ||||||
|     @Input() hideBlocks = false; |     @Input() hideBlocks = false; | ||||||
| @ -39,16 +38,10 @@ export class CoreBlockCourseBlocksComponent implements OnInit, OnDestroy { | |||||||
|     blocks = []; |     blocks = []; | ||||||
| 
 | 
 | ||||||
|     protected element: HTMLElement; |     protected element: HTMLElement; | ||||||
|     protected lastScroll; |  | ||||||
|     protected translationY = 0; |  | ||||||
|     protected blocksScrollHeight = 0; |  | ||||||
|     protected sideScroll: HTMLElement; |  | ||||||
|     protected vpHeight = 0; // Viewport height.
 |  | ||||||
|     protected scrollWorking = false; |  | ||||||
| 
 | 
 | ||||||
|     constructor(private domUtils: CoreDomUtilsProvider, private courseProvider: CoreCourseProvider, |     constructor(private domUtils: CoreDomUtilsProvider, private courseProvider: CoreCourseProvider, | ||||||
|             protected blockHelper: CoreBlockHelperProvider, element: ElementRef, |             protected blockHelper: CoreBlockHelperProvider, element: ElementRef, | ||||||
|             protected content: Content, protected appProvider: CoreAppProvider) { |             protected content: Content) { | ||||||
|         this.element = element.nativeElement; |         this.element = element.nativeElement; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -58,83 +51,9 @@ export class CoreBlockCourseBlocksComponent implements OnInit, OnDestroy { | |||||||
|     ngOnInit(): void { |     ngOnInit(): void { | ||||||
|         this.loadContent().finally(() => { |         this.loadContent().finally(() => { | ||||||
|             this.dataLoaded = true; |             this.dataLoaded = true; | ||||||
| 
 |  | ||||||
|             window.addEventListener('resize', this.initScroll.bind(this)); |  | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * Setup scrolling. |  | ||||||
|      */ |  | ||||||
|     protected initScroll(): void { |  | ||||||
|         if (this.blocks.length <= 0) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         const scroll: HTMLElement = this.content && this.content.getScrollElement(); |  | ||||||
| 
 |  | ||||||
|         this.domUtils.waitElementToExist(() => scroll && scroll.querySelector('.core-course-blocks-side')).then((sideElement) => { |  | ||||||
|             const contentHeight = parseInt(this.content.getNativeElement().querySelector('.scroll-content').scrollHeight, 10); |  | ||||||
| 
 |  | ||||||
|             this.sideScroll = scroll.querySelector('.core-course-blocks-side-scroll'); |  | ||||||
|             this.blocksScrollHeight = this.sideScroll.scrollHeight; |  | ||||||
|             this.vpHeight = sideElement.clientHeight; |  | ||||||
| 
 |  | ||||||
|             // Check if needed and event was not init before.
 |  | ||||||
|             if (this.appProvider.isWide() && this.vpHeight && contentHeight > this.vpHeight && |  | ||||||
|                     this.blocksScrollHeight > this.vpHeight) { |  | ||||||
|                 if (typeof this.lastScroll == 'undefined') { |  | ||||||
|                     this.lastScroll = 0; |  | ||||||
|                     scroll.addEventListener('scroll', this.scrollFunction.bind(this)); |  | ||||||
|                 } |  | ||||||
|                 this.scrollWorking = true; |  | ||||||
|             } else { |  | ||||||
|                 this.sideScroll.style.transform = 'translate(0, 0)'; |  | ||||||
|                 this.sideScroll.classList.remove('core-course-blocks-fixed-bottom'); |  | ||||||
|                 this.scrollWorking = false; |  | ||||||
|             } |  | ||||||
|         }).catch(() => { |  | ||||||
|             // Ignore errors.
 |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Scroll function that moves the sidebar if needed. |  | ||||||
|      * |  | ||||||
|      * @param {Event} e Event to get the target from. |  | ||||||
|      */ |  | ||||||
|     protected scrollFunction(e: Event): void { |  | ||||||
|         if (!this.scrollWorking) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         const target: any = e.target, |  | ||||||
|             top = parseInt(target.scrollTop, 10), |  | ||||||
|             goingUp = top < this.lastScroll; |  | ||||||
|         if (goingUp) { |  | ||||||
|             this.sideScroll.classList.remove('core-course-blocks-fixed-bottom'); |  | ||||||
|             if (top <= this.translationY ) { |  | ||||||
|                 // Fixed to top.
 |  | ||||||
|                 this.translationY = top; |  | ||||||
|                 this.sideScroll.style.transform = 'translate(0, ' + this.translationY + 'px)'; |  | ||||||
|             } |  | ||||||
|         } else if (top - this.translationY >= (this.blocksScrollHeight - this.vpHeight)) { |  | ||||||
|             // Fixed to bottom.
 |  | ||||||
|             this.sideScroll.classList.add('core-course-blocks-fixed-bottom'); |  | ||||||
|             this.translationY = top - (this.blocksScrollHeight - this.vpHeight); |  | ||||||
|             this.sideScroll.style.transform = 'translate(0, ' + this.translationY + 'px)'; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         this.lastScroll = top; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Component destroyed. |  | ||||||
|      */ |  | ||||||
|     ngOnDestroy(): void { |  | ||||||
|         window.removeEventListener('resize', this.initScroll); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |     /** | ||||||
|      * Invalidate blocks data. |      * Invalidate blocks data. | ||||||
|      * |      * | ||||||
| @ -175,8 +94,6 @@ export class CoreBlockCourseBlocksComponent implements OnInit, OnDestroy { | |||||||
|                 this.element.classList.remove('core-no-blocks'); |                 this.element.classList.remove('core-no-blocks'); | ||||||
| 
 | 
 | ||||||
|                 this.content.getElementRef().nativeElement.classList.add('core-course-block-with-blocks'); |                 this.content.getElementRef().nativeElement.classList.add('core-course-block-with-blocks'); | ||||||
| 
 |  | ||||||
|                 this.initScroll(); |  | ||||||
|             } else { |             } else { | ||||||
|                 this.element.classList.remove('core-has-blocks'); |                 this.element.classList.remove('core-has-blocks'); | ||||||
|                 this.element.classList.add('core-no-blocks'); |                 this.element.classList.add('core-no-blocks'); | ||||||
|  | |||||||
| @ -96,7 +96,9 @@ export class CoreCourseModule { | |||||||
|         eventsProvider.on(CoreEventsProvider.LOGIN, () => { |         eventsProvider.on(CoreEventsProvider.LOGIN, () => { | ||||||
|             // Log the app is open to keep user in online status.
 |             // Log the app is open to keep user in online status.
 | ||||||
|             setTimeout(() => { |             setTimeout(() => { | ||||||
|                 cronDelegate.forceCronHandlerExecution(logHandler.name); |                 cronDelegate.forceCronHandlerExecution(logHandler.name).catch((e) => { | ||||||
|  |                     // Ignore errors here, since probably login is not complete: it happens on token invalid.
 | ||||||
|  |                 }); | ||||||
|             }, 1000); |             }, 1000); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -114,6 +114,8 @@ export class CoreSitePluginsHelperProvider { | |||||||
|                         eventsProvider.trigger(CoreEventsProvider.SITE_PLUGINS_LOADED, {}, data.siteId); |                         eventsProvider.trigger(CoreEventsProvider.SITE_PLUGINS_LOADED, {}, data.siteId); | ||||||
|                     }); |                     }); | ||||||
|                 } |                 } | ||||||
|  |             }).catch((e) => { | ||||||
|  |                 // Ignore errors here.
 | ||||||
|             }).finally(() => { |             }).finally(() => { | ||||||
|                 this.sitePluginsProvider.setPluginsFetched(); |                 this.sitePluginsProvider.setPluginsFetched(); | ||||||
|             }); |             }); | ||||||
|  | |||||||
| @ -1385,6 +1385,8 @@ export class CoreSitesProvider { | |||||||
|                         this.eventsProvider.trigger(CoreEventsProvider.SITE_UPDATED, info, siteId); |                         this.eventsProvider.trigger(CoreEventsProvider.SITE_UPDATED, info, siteId); | ||||||
|                     }); |                     }); | ||||||
|                 }); |                 }); | ||||||
|  |             }).catch((error) => { | ||||||
|  |                 // Ignore that we cannot fetch site info. Probably the auth token is invalid.
 | ||||||
|             }); |             }); | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -96,8 +96,6 @@ ion-app.app-root core-rich-text-editor .core-rte-editor { | |||||||
|   .atto_image_button_right { |   .atto_image_button_right { | ||||||
|     vertical-align: middle; |     vertical-align: middle; | ||||||
|     max-width: 100%; |     max-width: 100%; | ||||||
|     height: auto; |  | ||||||
|     width: auto; |  | ||||||
|     display: inline-block; |     display: inline-block; | ||||||
|     margin: 0 0.5em; |     margin: 0 0.5em; | ||||||
| 
 | 
 | ||||||
| @ -105,8 +103,6 @@ ion-app.app-root core-rich-text-editor .core-rte-editor { | |||||||
|       /* If the image is display: block then linking the image to URLs won't work. */ |       /* If the image is display: block then linking the image to URLs won't work. */ | ||||||
|       /*display: inline-block;*/ |       /*display: inline-block;*/ | ||||||
|       max-width: 100%; |       max-width: 100%; | ||||||
|       height: auto; |  | ||||||
|       width: auto; |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -179,6 +175,24 @@ ion-app.app-root core-rich-text-editor .core-rte-editor { | |||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Those styles are omitted on RTE. | ||||||
|  | ion-app.app-root core-format-text, | ||||||
|  | ion-app.app-root .item core-format-text { | ||||||
|  |   .atto_image_button_text-top, | ||||||
|  |   .atto_image_button_middle, | ||||||
|  |   .atto_image_button_text-bottom, | ||||||
|  |   .atto_image_button_left, | ||||||
|  |   .atto_image_button_right { | ||||||
|  |     height: auto; | ||||||
|  |     width: auto; | ||||||
|  | 
 | ||||||
|  |     &.img-responsive { | ||||||
|  |       height: auto; | ||||||
|  |       width: auto; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Special fixes | // Special fixes | ||||||
| // ------------------------- | // ------------------------- | ||||||
| ion-app.app-root { | ion-app.app-root { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user