diff --git a/src/addon/calendar/components/calendar/calendar.ts b/src/addon/calendar/components/calendar/calendar.ts index 5507a389f..a620fe6ee 100644 --- a/src/addon/calendar/components/calendar/calendar.ts +++ b/src/addon/calendar/components/calendar/calendar.ts @@ -23,6 +23,7 @@ import { AddonCalendarProvider } from '../../providers/calendar'; import { AddonCalendarHelperProvider } from '../../providers/helper'; import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline'; import { CoreCoursesProvider } from '@core/courses/providers/courses'; +import { CoreAppProvider } from '@providers/app'; /** * Component that displays a calendar. @@ -70,7 +71,8 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest private domUtils: CoreDomUtilsProvider, private timeUtils: CoreTimeUtilsProvider, private utils: CoreUtilsProvider, - private coursesProvider: CoreCoursesProvider) { + private coursesProvider: CoreCoursesProvider, + private appProvider: CoreAppProvider) { this.currentSiteId = sitesProvider.getCurrentSiteId(); @@ -184,8 +186,14 @@ export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDest */ fetchEvents(): Promise { // 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. this.periodName = this.timeUtils.userDate(new Date(this.year, this.month - 1).getTime(), 'core.strftimemonthyear'); diff --git a/src/addon/calendar/pages/day/day.ts b/src/addon/calendar/pages/day/day.ts index f5a302997..73202cfd3 100644 --- a/src/addon/calendar/pages/day/day.ts +++ b/src/addon/calendar/pages/day/day.ts @@ -282,7 +282,14 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { */ fetchEvents(): Promise { // 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 = []; // Calculate the period name. We don't use the one in result because it's in server's language. diff --git a/src/addon/calendar/providers/calendar.ts b/src/addon/calendar/providers/calendar.ts index 9ecc4effd..5ffe6d29d 100644 --- a/src/addon/calendar/providers/calendar.ts +++ b/src/addon/calendar/providers/calendar.ts @@ -43,6 +43,7 @@ export class AddonCalendarProvider { static DEFAULT_NOTIFICATION_TIME_CHANGED = 'AddonCalendarDefaultNotificationTimeChangedEvent'; static DEFAULT_NOTIFICATION_TIME_SETTING = 'mmaCalendarDefaultNotifTime'; 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_DISCARDED_EVENT = 'addon_calendar_new_event_discarded'; static EDIT_EVENT_EVENT = 'addon_calendar_edit_event'; @@ -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; }); }); diff --git a/src/addon/calendar/providers/helper.ts b/src/addon/calendar/providers/helper.ts index 4ac36ec70..c3b24c956 100644 --- a/src/addon/calendar/providers/helper.ts +++ b/src/addon/calendar/providers/helper.ts @@ -18,6 +18,7 @@ import { CoreSitesProvider } from '@providers/sites'; import { CoreCourseProvider } from '@core/course/providers/course'; import { AddonCalendarProvider } from './calendar'; import { CoreConstants } from '@core/constants'; +import { CoreConfigProvider } from '@providers/config'; import { CoreUtilsProvider } from '@providers/utils/utils'; import * as moment from 'moment'; @@ -40,6 +41,7 @@ export class AddonCalendarHelperProvider { private courseProvider: CoreCourseProvider, private sitesProvider: CoreSitesProvider, private calendarProvider: AddonCalendarProvider, + private configProvider: CoreConfigProvider, private utils: CoreUtilsProvider) { this.logger = logger.getInstance('AddonCalendarHelperProvider'); } @@ -191,6 +193,66 @@ export class AddonCalendarHelperProvider { 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} Promise resolved with the response. + */ + getOfflineMonthWeeks(year: number, month: number, siteId?: string): Promise { + 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. *