commit
8684e12732
|
@ -23,6 +23,7 @@ import { AddonCalendarProvider } from '../../providers/calendar';
|
||||||
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
import { AddonCalendarHelperProvider } from '../../providers/helper';
|
||||||
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline';
|
||||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||||
|
import { CoreAppProvider } from '@providers/app';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays a calendar.
|
* Component that displays a calendar.
|
||||||
|
@ -70,7 +71,8 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest
|
||||||
private domUtils: CoreDomUtilsProvider,
|
private domUtils: CoreDomUtilsProvider,
|
||||||
private timeUtils: CoreTimeUtilsProvider,
|
private timeUtils: CoreTimeUtilsProvider,
|
||||||
private utils: CoreUtilsProvider,
|
private utils: CoreUtilsProvider,
|
||||||
private coursesProvider: CoreCoursesProvider) {
|
private coursesProvider: CoreCoursesProvider,
|
||||||
|
private appProvider: CoreAppProvider) {
|
||||||
|
|
||||||
this.currentSiteId = sitesProvider.getCurrentSiteId();
|
this.currentSiteId = sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
@ -184,8 +186,14 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest
|
||||||
*/
|
*/
|
||||||
fetchEvents(): Promise<any> {
|
fetchEvents(): Promise<any> {
|
||||||
// Don't pass courseId and categoryId, we'll filter them locally.
|
// Don't pass courseId and categoryId, we'll filter them locally.
|
||||||
return this.calendarProvider.getMonthlyEvents(this.year, this.month).then((result) => {
|
return this.calendarProvider.getMonthlyEvents(this.year, this.month).catch((error) => {
|
||||||
|
if (!this.appProvider.isOnline()) {
|
||||||
|
// Allow navigating to non-cached months in offline (behave as if using emergency cache).
|
||||||
|
return this.calendarHelper.getOfflineMonthWeeks(this.year, this.month);
|
||||||
|
} else {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
}).then((result) => {
|
||||||
// Calculate the period name. We don't use the one in result because it's in server's language.
|
// Calculate the period name. We don't use the one in result because it's in server's language.
|
||||||
this.periodName = this.timeUtils.userDate(new Date(this.year, this.month - 1).getTime(), 'core.strftimemonthyear');
|
this.periodName = this.timeUtils.userDate(new Date(this.year, this.month - 1).getTime(), 'core.strftimemonthyear');
|
||||||
|
|
||||||
|
|
|
@ -264,7 +264,7 @@ export class AddonCalendarUpcomingEventsComponent implements OnInit, OnChanges,
|
||||||
return this.onlineEvents;
|
return this.onlineEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
const start = Date.now(),
|
const start = Date.now() / 1000,
|
||||||
end = start + (CoreConstants.SECONDS_DAY * this.lookAhead);
|
end = start + (CoreConstants.SECONDS_DAY * this.lookAhead);
|
||||||
let result = this.onlineEvents;
|
let result = this.onlineEvents;
|
||||||
|
|
||||||
|
|
|
@ -282,7 +282,14 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy {
|
||||||
*/
|
*/
|
||||||
fetchEvents(): Promise<any> {
|
fetchEvents(): Promise<any> {
|
||||||
// Don't pass courseId and categoryId, we'll filter them locally.
|
// Don't pass courseId and categoryId, we'll filter them locally.
|
||||||
return this.calendarProvider.getDayEvents(this.year, this.month, this.day).then((result) => {
|
return this.calendarProvider.getDayEvents(this.year, this.month, this.day).catch((error) => {
|
||||||
|
if (!this.appProvider.isOnline()) {
|
||||||
|
// Allow navigating to non-cached days in offline (behave as if using emergency cache).
|
||||||
|
return Promise.resolve({ events: [] });
|
||||||
|
} else {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
}).then((result) => {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
// Calculate the period name. We don't use the one in result because it's in server's language.
|
// Calculate the period name. We don't use the one in result because it's in server's language.
|
||||||
|
@ -321,7 +328,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy {
|
||||||
protected mergeEvents(): any[] {
|
protected mergeEvents(): any[] {
|
||||||
this.hasOffline = false;
|
this.hasOffline = false;
|
||||||
|
|
||||||
if (!this.offlineEditedEventsIds.length && !this.deletedEvents.length) {
|
if (!Object.keys(this.offlineEvents).length && !this.deletedEvents.length) {
|
||||||
// No offline events, nothing to merge.
|
// No offline events, nothing to merge.
|
||||||
return this.onlineEvents;
|
return this.onlineEvents;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<ion-header>
|
<ion-header>
|
||||||
<ion-navbar core-back-button>
|
<ion-navbar core-back-button>
|
||||||
<ion-title>{{ 'addon.calendar.calendarevents' | 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">
|
<button *ngIf="showCalendar" ion-button icon-only (click)="toggleDisplay()" [attr.aria-label]="'addon.calendar.upcomingevents' | translate">
|
||||||
<ion-icon name="list"></ion-icon>
|
<ion-icon name="list"></ion-icon>
|
||||||
|
|
|
@ -43,6 +43,7 @@ export class AddonCalendarProvider {
|
||||||
static DEFAULT_NOTIFICATION_TIME_CHANGED = 'AddonCalendarDefaultNotificationTimeChangedEvent';
|
static DEFAULT_NOTIFICATION_TIME_CHANGED = 'AddonCalendarDefaultNotificationTimeChangedEvent';
|
||||||
static DEFAULT_NOTIFICATION_TIME_SETTING = 'mmaCalendarDefaultNotifTime';
|
static DEFAULT_NOTIFICATION_TIME_SETTING = 'mmaCalendarDefaultNotifTime';
|
||||||
static DEFAULT_NOTIFICATION_TIME = 60;
|
static DEFAULT_NOTIFICATION_TIME = 60;
|
||||||
|
static STARTING_WEEK_DAY = 'addon_calendar_starting_week_day';
|
||||||
static NEW_EVENT_EVENT = 'addon_calendar_new_event';
|
static NEW_EVENT_EVENT = 'addon_calendar_new_event';
|
||||||
static NEW_EVENT_DISCARDED_EVENT = 'addon_calendar_new_event_discarded';
|
static NEW_EVENT_DISCARDED_EVENT = 'addon_calendar_new_event_discarded';
|
||||||
static EDIT_EVENT_EVENT = 'addon_calendar_edit_event';
|
static EDIT_EVENT_EVENT = 'addon_calendar_edit_event';
|
||||||
|
@ -726,7 +727,7 @@ export class AddonCalendarProvider {
|
||||||
return this.userProvider.getUserPreference('calendar_lookahead').catch((error) => {
|
return this.userProvider.getUserPreference('calendar_lookahead').catch((error) => {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
}).then((value): any => {
|
}).then((value): any => {
|
||||||
if (typeof value != 'undefined') {
|
if (value != null) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1198,6 +1199,11 @@ export class AddonCalendarProvider {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Store starting week day preference, we need it in offline to show months that are not in cache.
|
||||||
|
if (this.appProvider.isOnline()) {
|
||||||
|
this.configProvider.set(AddonCalendarProvider.STARTING_WEEK_DAY, response.daynames[0].dayno);
|
||||||
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,6 +18,8 @@ import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||||
import { AddonCalendarProvider } from './calendar';
|
import { AddonCalendarProvider } from './calendar';
|
||||||
import { CoreConstants } from '@core/constants';
|
import { CoreConstants } from '@core/constants';
|
||||||
|
import { CoreConfigProvider } from '@providers/config';
|
||||||
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,7 +40,9 @@ export class AddonCalendarHelperProvider {
|
||||||
constructor(logger: CoreLoggerProvider,
|
constructor(logger: CoreLoggerProvider,
|
||||||
private courseProvider: CoreCourseProvider,
|
private courseProvider: CoreCourseProvider,
|
||||||
private sitesProvider: CoreSitesProvider,
|
private sitesProvider: CoreSitesProvider,
|
||||||
private calendarProvider: AddonCalendarProvider) {
|
private calendarProvider: AddonCalendarProvider,
|
||||||
|
private configProvider: CoreConfigProvider,
|
||||||
|
private utils: CoreUtilsProvider) {
|
||||||
this.logger = logger.getInstance('AddonCalendarHelperProvider');
|
this.logger = logger.getInstance('AddonCalendarHelperProvider');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,6 +193,66 @@ export class AddonCalendarHelperProvider {
|
||||||
return year + '#' + month;
|
return year + '#' + month;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get weeks of a month in offline (with no events).
|
||||||
|
*
|
||||||
|
* The result has the same structure than getMonthlyEvents, but it only contains fields that are actually used by the app.
|
||||||
|
*
|
||||||
|
* @param {number} year Year to get.
|
||||||
|
* @param {number} month Month to get.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved with the response.
|
||||||
|
*/
|
||||||
|
getOfflineMonthWeeks(year: number, month: number, siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
// Get starting week day user preference, fallback to site configuration.
|
||||||
|
const startWeekDay = site.getStoredConfig('calendar_startwday');
|
||||||
|
|
||||||
|
return this.configProvider.get(AddonCalendarProvider.STARTING_WEEK_DAY, startWeekDay);
|
||||||
|
}).then((startWeekDay) => {
|
||||||
|
const today = moment();
|
||||||
|
const isCurrentMonth = today.year() == year && today.month() == month - 1;
|
||||||
|
const weeks = [];
|
||||||
|
|
||||||
|
let date = moment({year, month: month - 1, date: 1});
|
||||||
|
for (let mday = 1; mday <= date.daysInMonth(); mday++) {
|
||||||
|
date = moment({year, month: month - 1, date: mday});
|
||||||
|
|
||||||
|
// Add new week and calculate prepadding.
|
||||||
|
if (!weeks.length || date.day() == startWeekDay) {
|
||||||
|
const prepaddingLength = (date.day() - startWeekDay + 7) % 7;
|
||||||
|
const prepadding = [];
|
||||||
|
for (let i = 0; i < prepaddingLength; i++) {
|
||||||
|
prepadding.push(i);
|
||||||
|
}
|
||||||
|
weeks.push({ prepadding, postpadding: [], days: []});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate postpadding of last week.
|
||||||
|
if (mday == date.daysInMonth()) {
|
||||||
|
const postpaddingLength = (startWeekDay - date.day() + 6) % 7;
|
||||||
|
const postpadding = [];
|
||||||
|
for (let i = 0; i < postpaddingLength; i++) {
|
||||||
|
postpadding.push(i);
|
||||||
|
}
|
||||||
|
weeks[weeks.length - 1].postpadding = postpadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add day to current week.
|
||||||
|
weeks[weeks.length - 1].days.push({
|
||||||
|
events: [],
|
||||||
|
hasevents: false,
|
||||||
|
mday: date.date(),
|
||||||
|
isweekend: date.day() == 0 || date.day() == 6,
|
||||||
|
istoday: isCurrentMonth && today.date() == date.date(),
|
||||||
|
calendareventtypes: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {weeks, daynames: [{dayno: startWeekDay}]};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the data of an event has changed.
|
* Check if the data of an event has changed.
|
||||||
*
|
*
|
||||||
|
@ -286,20 +350,20 @@ export class AddonCalendarHelperProvider {
|
||||||
* @return {Promise<any>} REsolved when done.
|
* @return {Promise<any>} REsolved when done.
|
||||||
*/
|
*/
|
||||||
invalidateRepeatedEventsOnCalendar(event: any, repeated: number, siteId?: string): Promise<any> {
|
invalidateRepeatedEventsOnCalendar(event: any, repeated: number, siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
let invalidatePromise;
|
let invalidatePromise;
|
||||||
const timestarts = [];
|
const timestarts = [];
|
||||||
|
|
||||||
if (repeated > 1) {
|
if (repeated > 1) {
|
||||||
if (event.repeatid) {
|
if (event.repeatid) {
|
||||||
// Being edited or deleted.
|
// Being edited or deleted.
|
||||||
invalidatePromise = this.calendarProvider.getLocalEventsByRepeatIdFromLocalDb(event.repeatid, siteId)
|
invalidatePromise = this.calendarProvider.getLocalEventsByRepeatIdFromLocalDb(event.repeatid, site.id)
|
||||||
.then((events) => {
|
.then((events) => {
|
||||||
return events.map((event) => {
|
return this.utils.allPromises(events.map((event) => {
|
||||||
timestarts.push(event.timestart);
|
timestarts.push(event.timestart);
|
||||||
|
|
||||||
return this.calendarProvider.invalidateEvent(event.id);
|
return this.calendarProvider.invalidateEvent(event.id);
|
||||||
});
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Being added.
|
// Being added.
|
||||||
|
@ -318,12 +382,14 @@ export class AddonCalendarHelperProvider {
|
||||||
invalidatePromise = this.calendarProvider.invalidateEvent(event.id);
|
invalidatePromise = this.calendarProvider.invalidateEvent(event.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return invalidatePromise.then(() => {
|
return invalidatePromise.finally(() => {
|
||||||
let lastMonth, lastYear;
|
let lastMonth, lastYear;
|
||||||
|
|
||||||
return Promise.all([
|
return this.utils.allPromises([
|
||||||
this.calendarProvider.invalidateAllUpcomingEvents(),
|
this.calendarProvider.invalidateAllUpcomingEvents(),
|
||||||
timestarts.map((time) => {
|
|
||||||
|
// Invalidate months.
|
||||||
|
this.utils.allPromises(timestarts.map((time) => {
|
||||||
const day = moment(new Date(time * 1000));
|
const day = moment(new Date(time * 1000));
|
||||||
|
|
||||||
if (lastMonth && (lastMonth == day.month() + 1 && lastYear == day.year())) {
|
if (lastMonth && (lastMonth == day.month() + 1 && lastYear == day.year())) {
|
||||||
|
@ -334,9 +400,17 @@ export class AddonCalendarHelperProvider {
|
||||||
lastMonth = day.month() + 1;
|
lastMonth = day.month() + 1;
|
||||||
lastYear = day.year();
|
lastYear = day.year();
|
||||||
|
|
||||||
return this.calendarProvider.invalidateMonthlyEvents(lastYear, lastMonth, siteId);
|
return this.calendarProvider.invalidateMonthlyEvents(lastYear, lastMonth, site.id);
|
||||||
})
|
})),
|
||||||
|
|
||||||
|
// Invalidate days.
|
||||||
|
this.utils.allPromises(timestarts.map((time) => {
|
||||||
|
const day = moment(new Date(time * 1000));
|
||||||
|
|
||||||
|
return this.calendarProvider.invalidateDayEvents(day.year(), day.month() + 1, day.date(), site.id);
|
||||||
|
})),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1079,6 +1079,11 @@ details summary {
|
||||||
contain: none !important;
|
contain: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lower z-index for ion-item-divider so it is displayed below video menus.
|
||||||
|
ion-item-divider {
|
||||||
|
z-index: 2; // Ionic default is 100.
|
||||||
|
}
|
||||||
|
|
||||||
// Highlight text.
|
// Highlight text.
|
||||||
.matchtext {
|
.matchtext {
|
||||||
background-color: $core-text-hightlight-background-color;
|
background-color: $core-text-hightlight-background-color;
|
||||||
|
|
|
@ -1391,7 +1391,7 @@
|
||||||
"core.course.warningmanualcompletionmodified": "The manual completion of an activity was modified on the site.",
|
"core.course.warningmanualcompletionmodified": "The manual completion of an activity was modified on the site.",
|
||||||
"core.course.warningofflinemanualcompletiondeleted": "Some offline manual completion of course '{{name}}' has been deleted. {{error}}",
|
"core.course.warningofflinemanualcompletiondeleted": "Some offline manual completion of course '{{name}}' has been deleted. {{error}}",
|
||||||
"core.coursedetails": "Course details",
|
"core.coursedetails": "Course details",
|
||||||
"core.coursenogroups": "This course doesn't have any group.",
|
"core.coursenogroups": "You are not a member of any group of this course.",
|
||||||
"core.courses.addtofavourites": "Star this course",
|
"core.courses.addtofavourites": "Star this course",
|
||||||
"core.courses.allowguests": "This course allows guest users to enter",
|
"core.courses.allowguests": "This course allows guest users to enter",
|
||||||
"core.courses.availablecourses": "Available courses",
|
"core.courses.availablecourses": "Available courses",
|
||||||
|
|
|
@ -25,7 +25,7 @@ ion-app.app-root core-ion-tabs {
|
||||||
|
|
||||||
&[tabsplacement="bottom"] {
|
&[tabsplacement="bottom"] {
|
||||||
.ion-page > ion-content > .scroll-content {
|
.ion-page > ion-content > .scroll-content {
|
||||||
margin-bottom: $navbar-md-height !important;
|
margin-bottom: $navbar-md-height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
"copiedtoclipboard": "Text copied to clipboard",
|
"copiedtoclipboard": "Text copied to clipboard",
|
||||||
"course": "Course",
|
"course": "Course",
|
||||||
"coursedetails": "Course details",
|
"coursedetails": "Course details",
|
||||||
"coursenogroups": "This course doesn't have any group.",
|
"coursenogroups": "You are not a member of any group of this course.",
|
||||||
"currentdevice": "Current device",
|
"currentdevice": "Current device",
|
||||||
"datastoredoffline": "Data stored in the device because it couldn't be sent. It will be sent automatically later.",
|
"datastoredoffline": "Data stored in the device because it couldn't be sent. It will be sent automatically later.",
|
||||||
"date": "Date",
|
"date": "Date",
|
||||||
|
|
Loading…
Reference in New Issue