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) {
|
||||||
this.pageLoaded++;
|
const count = this.entries.filter((entry) => {
|
||||||
|
return entry.userid == this.currentUserId;
|
||||||
this.showMyIssuesToggle = !this.userId;
|
}).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++;
|
||||||
|
}
|
||||||
|
|
||||||
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-day-number {
|
|
||||||
height: 24px;
|
&.addon-calendar-event-past-day > .addon-calendar-dot-types,
|
||||||
line-height: 24px;
|
&.addon-calendar-event-past-day > .addon-calendar-day-events {
|
||||||
width: max-content;
|
opacity: 0.5;
|
||||||
min-width: 24px;
|
|
||||||
text-align: center;
|
|
||||||
font-weight: 500;
|
|
||||||
display: inline-block;
|
|
||||||
margin: 3px;
|
|
||||||
}
|
}
|
||||||
&.today .addon-calendar-day-number {
|
|
||||||
|
.addon-calendar-day-number {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
span {
|
||||||
|
line-height: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 3px;
|
||||||
|
width: max-content;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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,14 +3,12 @@
|
||||||
</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. -->
|
<ng-container *ngFor="let block of blocks">
|
||||||
<ng-container *ngFor="let block of blocks">
|
<core-block [block]="block" contextLevel="course" [instanceId]="courseId" [extraData]="{'downloadEnabled': downloadEnabled}"></core-block>
|
||||||
<core-block [block]="block" contextLevel="course" [instanceId]="courseId" [extraData]="{'downloadEnabled': downloadEnabled}"></core-block>
|
</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…
Reference in New Issue