From aff813617974221f5782533536c13d33f91bc4ab Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Thu, 8 Aug 2019 13:01:02 +0200 Subject: [PATCH 1/8] MOBILE-3068 styles: Remove bottom padding when keyboard is open --- src/components/ion-tabs/ion-tabs.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ion-tabs/ion-tabs.scss b/src/components/ion-tabs/ion-tabs.scss index 8e8a0ebd3..31f667650 100644 --- a/src/components/ion-tabs/ion-tabs.scss +++ b/src/components/ion-tabs/ion-tabs.scss @@ -25,7 +25,7 @@ ion-app.app-root core-ion-tabs { &[tabsplacement="bottom"] { .ion-page > ion-content > .scroll-content { - margin-bottom: $navbar-md-height !important; + margin-bottom: $navbar-md-height; } } From 184faa4a6db10ed9ccac3e7f4a3fe44d0752a126 Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Thu, 8 Aug 2019 13:00:03 +0200 Subject: [PATCH 2/8] MOBILE-3042 styles: Fix ion-item-divider displayed over video menus --- src/app/app.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/app/app.scss b/src/app/app.scss index 2d0a1baa0..cf34c2ee7 100644 --- a/src/app/app.scss +++ b/src/app/app.scss @@ -1079,6 +1079,11 @@ details summary { 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. .matchtext { background-color: $core-text-hightlight-background-color; From 2b72b65ba501d33d9a58337cb9f2e92dc5508273 Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Thu, 8 Aug 2019 10:49:35 +0200 Subject: [PATCH 3/8] MOBILE-1927 calendar: Invalidate day when creating/editing an event --- src/addon/calendar/providers/helper.ts | 104 ++++++++++++++----------- 1 file changed, 58 insertions(+), 46 deletions(-) diff --git a/src/addon/calendar/providers/helper.ts b/src/addon/calendar/providers/helper.ts index 00005972a..4ac36ec70 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 { CoreUtilsProvider } from '@providers/utils/utils'; import * as moment from 'moment'; /** @@ -38,7 +39,8 @@ export class AddonCalendarHelperProvider { constructor(logger: CoreLoggerProvider, private courseProvider: CoreCourseProvider, private sitesProvider: CoreSitesProvider, - private calendarProvider: AddonCalendarProvider) { + private calendarProvider: AddonCalendarProvider, + private utils: CoreUtilsProvider) { this.logger = logger.getInstance('AddonCalendarHelperProvider'); } @@ -286,57 +288,67 @@ export class AddonCalendarHelperProvider { * @return {Promise} REsolved when done. */ invalidateRepeatedEventsOnCalendar(event: any, repeated: number, siteId?: string): Promise { - let invalidatePromise; - const timestarts = []; + return this.sitesProvider.getSite(siteId).then((site) => { + let invalidatePromise; + const timestarts = []; - if (repeated > 1) { - if (event.repeatid) { - // Being edited or deleted. - invalidatePromise = this.calendarProvider.getLocalEventsByRepeatIdFromLocalDb(event.repeatid, siteId) - .then((events) => { - return events.map((event) => { - timestarts.push(event.timestart); + if (repeated > 1) { + if (event.repeatid) { + // Being edited or deleted. + invalidatePromise = this.calendarProvider.getLocalEventsByRepeatIdFromLocalDb(event.repeatid, site.id) + .then((events) => { + return this.utils.allPromises(events.map((event) => { + timestarts.push(event.timestart); - return this.calendarProvider.invalidateEvent(event.id); + return this.calendarProvider.invalidateEvent(event.id); + })); }); - - }); - } else { - // Being added. - let time = event.timestart; - while (repeated > 0) { - timestarts.push(time); - time += CoreConstants.SECONDS_DAY * 7; - repeated--; - } - - invalidatePromise = Promise.resolve(); - } - } else { - // Not repeated. - timestarts.push(event.timestart); - invalidatePromise = this.calendarProvider.invalidateEvent(event.id); - } - - return invalidatePromise.then(() => { - let lastMonth, lastYear; - - return Promise.all([ - this.calendarProvider.invalidateAllUpcomingEvents(), - timestarts.map((time) => { - const day = moment(new Date(time * 1000)); - - if (lastMonth && (lastMonth == day.month() + 1 && lastYear == day.year())) { - return Promise.resolve(); + } else { + // Being added. + let time = event.timestart; + while (repeated > 0) { + timestarts.push(time); + time += CoreConstants.SECONDS_DAY * 7; + repeated--; } - // Invalidate once. - lastMonth = day.month() + 1; - lastYear = day.year(); + invalidatePromise = Promise.resolve(); + } + } else { + // Not repeated. + timestarts.push(event.timestart); + invalidatePromise = this.calendarProvider.invalidateEvent(event.id); + } - return this.calendarProvider.invalidateMonthlyEvents(lastYear, lastMonth, siteId); - }) - ]); + return invalidatePromise.finally(() => { + let lastMonth, lastYear; + + return this.utils.allPromises([ + this.calendarProvider.invalidateAllUpcomingEvents(), + + // Invalidate months. + this.utils.allPromises(timestarts.map((time) => { + const day = moment(new Date(time * 1000)); + + if (lastMonth && (lastMonth == day.month() + 1 && lastYear == day.year())) { + return Promise.resolve(); + } + + // Invalidate once. + lastMonth = day.month() + 1; + lastYear = day.year(); + + 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); + })), + ]); + }); }); } } From 4e3f57533b6900848c1223642072074691c389e0 Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Thu, 8 Aug 2019 12:01:01 +0200 Subject: [PATCH 4/8] MOBILE-1927 calendar: Fix offline events not displayed in day view --- src/addon/calendar/pages/day/day.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/addon/calendar/pages/day/day.ts b/src/addon/calendar/pages/day/day.ts index a8c1e90a1..f5a302997 100644 --- a/src/addon/calendar/pages/day/day.ts +++ b/src/addon/calendar/pages/day/day.ts @@ -321,7 +321,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy { protected mergeEvents(): any[] { 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. return this.onlineEvents; } From de1207f8beeed9e1657e34ef40a99eafe24b9b9b Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Thu, 8 Aug 2019 12:54:10 +0200 Subject: [PATCH 5/8] MOBILE-1927 calendar: Fix no groups message --- src/assets/lang/en.json | 2 +- src/lang/en.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json index 1ea76b5ba..6d03d0b22 100644 --- a/src/assets/lang/en.json +++ b/src/assets/lang/en.json @@ -1391,7 +1391,7 @@ "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.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.allowguests": "This course allows guest users to enter", "core.courses.availablecourses": "Available courses", diff --git a/src/lang/en.json b/src/lang/en.json index da6737465..8c5698b50 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -48,7 +48,7 @@ "copiedtoclipboard": "Text copied to clipboard", "course": "Course", "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", "datastoredoffline": "Data stored in the device because it couldn't be sent. It will be sent automatically later.", "date": "Date", From 56d8aba3b40c4e020c3ccf1e09e9094c25d5450c Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Fri, 9 Aug 2019 11:50:08 +0200 Subject: [PATCH 6/8] NOBILE-3087 calendar: Allow navigating to all days and months in offline --- .../calendar/components/calendar/calendar.ts | 14 ++++- src/addon/calendar/pages/day/day.ts | 9 ++- src/addon/calendar/providers/calendar.ts | 6 ++ src/addon/calendar/providers/helper.ts | 62 +++++++++++++++++++ 4 files changed, 87 insertions(+), 4 deletions(-) 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. * From e217234f9e03340a52a16ec530f7f0bb54884ec8 Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Fri, 9 Aug 2019 13:09:07 +0200 Subject: [PATCH 7/8] MOBILE-3087 calendar: Fix offline events not displayed in upcoming events page --- .../calendar/components/upcoming-events/upcoming-events.ts | 2 +- src/addon/calendar/providers/calendar.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/addon/calendar/components/upcoming-events/upcoming-events.ts b/src/addon/calendar/components/upcoming-events/upcoming-events.ts index d540dae6e..74db1aebc 100644 --- a/src/addon/calendar/components/upcoming-events/upcoming-events.ts +++ b/src/addon/calendar/components/upcoming-events/upcoming-events.ts @@ -264,7 +264,7 @@ export class AddonCalendarUpcomingEventsComponent implements OnInit, OnChanges, return this.onlineEvents; } - const start = Date.now(), + const start = Date.now() / 1000, end = start + (CoreConstants.SECONDS_DAY * this.lookAhead); let result = this.onlineEvents; diff --git a/src/addon/calendar/providers/calendar.ts b/src/addon/calendar/providers/calendar.ts index 5ffe6d29d..7b09df4a8 100644 --- a/src/addon/calendar/providers/calendar.ts +++ b/src/addon/calendar/providers/calendar.ts @@ -727,7 +727,7 @@ export class AddonCalendarProvider { return this.userProvider.getUserPreference('calendar_lookahead').catch((error) => { // Ignore errors. }).then((value): any => { - if (typeof value != 'undefined') { + if (value != null) { return value; } From 798f3b2d53827c2d4db56a5d73bdeafcb67d6e20 Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Fri, 9 Aug 2019 13:15:49 +0200 Subject: [PATCH 8/8] MOBILE-3087 calendar: Changed title of upcoming events page --- src/addon/calendar/pages/index/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/addon/calendar/pages/index/index.html b/src/addon/calendar/pages/index/index.html index adfbe89db..fc6386e36 100644 --- a/src/addon/calendar/pages/index/index.html +++ b/src/addon/calendar/pages/index/index.html @@ -1,6 +1,6 @@ - {{ 'addon.calendar.calendarevents' | translate }} + {{ (showCalendar ? 'addon.calendar.calendarevents' : 'addon.calendar.upcomingevents') | translate }}