From cb4f6c196a17f87da61c378657ad9985319b5b1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Wed, 9 Jan 2019 10:26:36 +0100 Subject: [PATCH 1/5] MOBILE-2795 ux: Fix margin on item-start img or icon --- src/app/app.md.scss | 5 +++++ src/app/app.scss | 6 ++++++ src/app/app.wp.scss | 4 +++- src/core/course/components/module/module.scss | 4 ++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/app/app.md.scss b/src/app/app.md.scss index 8fe5fb096..5322bbc19 100644 --- a/src/app/app.md.scss +++ b/src/app/app.md.scss @@ -21,6 +21,11 @@ ion-app.app-root.md { @include margin-horizontal($item-md-padding-start + ($item-md-padding-start / 2) - 1, null); } + .item-md img[item-start] + .item-inner, + .item-md img[item-start] + .item-input { + @include margin-horizontal($item-md-padding-start + ($item-md-padding-start / 2), null); + } + @each $color-name, $color-base, $color-contrast in get-colors($colors-md) { .core-#{$color-name}-card { @extend .card-md; diff --git a/src/app/app.scss b/src/app/app.scss index 0889d3c2f..2d719bee0 100644 --- a/src/app/app.scss +++ b/src/app/app.scss @@ -943,6 +943,12 @@ details summary { pointer-events: auto; } +.icon.fa-graduation-cap{ + font-size: 21px; + width: 21px; + line-height: 28px; +} + // Fix iframes in fullscreen mode. // // Ionic sets "contain: strict" to some elements. This enables paint containment, diff --git a/src/app/app.wp.scss b/src/app/app.wp.scss index e64c3726a..0acc39941 100644 --- a/src/app/app.wp.scss +++ b/src/app/app.wp.scss @@ -17,7 +17,9 @@ ion-app.app-root.wp { } .item-wp ion-spinner[item-start] + .item-inner, - .item-wp ion-spinner[item-start] + .item-input { + .item-wp ion-spinner[item-start] + .item-input, + .item-wp img[item-start] + .item-inner, + .item-wp img[item-start] + .item-input { @include margin-horizontal(($item-wp-padding-start / 2), null); } diff --git a/src/core/course/components/module/module.scss b/src/core/course/components/module/module.scss index 65f23d8b1..2541dd63a 100644 --- a/src/core/course/components/module/module.scss +++ b/src/core/course/components/module/module.scss @@ -138,4 +138,8 @@ ion-app.app-root.wp core-course-module { margin-top: 8px; margin-bottom: 8px; } +} + +ion-app.app-root a.core-course-module-handler.item [item-start] + .item-inner { + @include margin-horizontal(4px, null); } \ No newline at end of file From 5128880d6d6f3afa774c35eaca6e6d9fcda47199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Wed, 9 Jan 2019 15:44:25 +0100 Subject: [PATCH 2/5] MOBILE-2795 notifications: Handle duplication event detection --- src/addon/calendar/providers/calendar.ts | 69 +++++++++++++++++------- src/providers/local-notifications.ts | 4 +- 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/src/addon/calendar/providers/calendar.ts b/src/addon/calendar/providers/calendar.ts index af29d4f7d..3aadd52e0 100644 --- a/src/addon/calendar/providers/calendar.ts +++ b/src/addon/calendar/providers/calendar.ts @@ -138,6 +138,37 @@ export class AddonCalendarProvider { this.sitesProvider.createTablesFromSchema(this.tablesSchema); } + /** + * Removes expired events from local DB. + * + * @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site. + * @return {Promise} Promise resolved when done. + */ + cleanExpiredEvents(siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + let promise; + + // Cancel expired events notifications first. + if (this.localNotificationsProvider.isAvailable()) { + promise = site.getDb().getRecordsSelect(AddonCalendarProvider.EVENTS_TABLE, 'timestart < ?', + [this.timeUtils.timestamp()]).then((events) => { + events.forEach((event) => { + return this.localNotificationsProvider.cancel(event.id, AddonCalendarProvider.COMPONENT, site.getId()); + }); + }).catch(() => { + // Ignore errors. + }); + } else { + promise = Promise.resolve(); + } + + return promise.then(() => { + return site.getDb().deleteRecordsSelect(AddonCalendarProvider.EVENTS_TABLE, 'timestart < ?', + [this.timeUtils.timestamp()]); + }); + }); + } + /** * Get all calendar events from local Db. * @@ -442,27 +473,29 @@ export class AddonCalendarProvider { * @return {Promise} Promise resolved when all the notifications have been scheduled. */ scheduleAllSitesEventsNotifications(): Promise { - if (this.localNotificationsProvider.isAvailable()) { - return this.sitesProvider.getSitesIds().then((siteIds) => { - const promises = []; + const notificationsEnabled = this.localNotificationsProvider.isAvailable(); - siteIds.forEach((siteId) => { - // Check if calendar is disabled for the site. - promises.push(this.isDisabled(siteId).then((disabled) => { - if (!disabled) { - // Get first events. - return this.getEventsList(undefined, undefined, siteId).then((events) => { - return this.scheduleEventsNotifications(events, siteId); - }); - } - })); - }); + return this.sitesProvider.getSitesIds().then((siteIds) => { + const promises = []; - return Promise.all(promises); + siteIds.forEach((siteId) => { + promises.push(this.cleanExpiredEvents(siteId).then(() => { + if (notificationsEnabled) { + // Check if calendar is disabled for the site. + return this.isDisabled(siteId).then((disabled) => { + if (!disabled) { + // Get first events. + return this.getEventsList(undefined, undefined, siteId).then((events) => { + return this.scheduleEventsNotifications(events, siteId); + }); + } + }); + } + })); }); - } else { - return Promise.resolve([]); - } + + return Promise.all(promises); + }); } /** diff --git a/src/providers/local-notifications.ts b/src/providers/local-notifications.ts index 25e51da9e..8f6d66c60 100644 --- a/src/providers/local-notifications.ts +++ b/src/providers/local-notifications.ts @@ -489,7 +489,9 @@ export class CoreLocalNotificationsProvider { // Remove from triggered, since the notification could be in there with a different time. this.removeTriggered(notification.id); - this.localNotifications.schedule(notification); + this.localNotifications.cancel(notification.id).finally(() => { + this.localNotifications.schedule(notification); + }); }); } }); From 167d544949ba614f46ff188e2b67bf91b11bbec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Wed, 9 Jan 2019 16:46:58 +0100 Subject: [PATCH 3/5] MOBILE-2795 calendar: Show date dividers --- src/addon/calendar/pages/list/list.html | 21 ++++++++---- src/addon/calendar/pages/list/list.ts | 45 +++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/src/addon/calendar/pages/list/list.html b/src/addon/calendar/pages/list/list.html index 9ee1e90b7..393ae76be 100644 --- a/src/addon/calendar/pages/list/list.html +++ b/src/addon/calendar/pages/list/list.html @@ -21,12 +21,21 @@ - - - -

-

{{ event.timestart | coreToLocaleString }}

-
+ + + {{ event.timestart * 1000 | coreFormatDate: "strftimedayshort" }} + + + + +

+

+ {{ event.timestart * 1000 | coreFormatDate: "strftimetime" }} + - {{ (event.timestart + event.timeduration) * 1000 | coreFormatDate: "strftimetime" }} + - {{ (event.timestart + event.timeduration) | coreToLocaleString }} +

+
+
diff --git a/src/addon/calendar/pages/list/list.ts b/src/addon/calendar/pages/list/list.ts index 21d0d56ca..cd8523d90 100644 --- a/src/addon/calendar/pages/list/list.ts +++ b/src/addon/calendar/pages/list/list.ts @@ -26,6 +26,7 @@ import { CoreCoursePickerMenuPopoverComponent } from '@components/course-picker- import { CoreEventsProvider } from '@providers/events'; import { CoreAppProvider } from '@providers/app'; import { CoreSplitViewComponent } from '@components/split-view/split-view'; +import * as moment from 'moment'; /** * Page that displays the list of calendar events. @@ -147,6 +148,10 @@ export class AddonCalendarListPage implements OnDestroy { } else { // Sort the events by timestart, they're ordered by id. events.sort((a, b) => { + if (a.timestart == b.timestart) { + return a.timeduration - b.timeduration; + } + return a.timestart - b.timestart; }); @@ -160,6 +165,12 @@ export class AddonCalendarListPage implements OnDestroy { this.events = this.utils.mergeArraysWithoutDuplicates(this.events, events, 'id'); } this.filteredEvents = this.getFilteredEvents(); + + // Calculate which evemts need to display the date. + this.filteredEvents.forEach((event, index): any => { + event.showDate = this.showDate(event, this.filteredEvents[index - 1]); + event.endsSameDay = this.endsSameDay(event); + }); this.canLoadMore = true; // Schedule notifications for the events retrieved (might have new events). @@ -308,6 +319,40 @@ export class AddonCalendarListPage implements OnDestroy { }); } + /** + * Check date should be shown on event list for the current event. + * If date has changed from previous to current event it should be shown. + * + * @param {any} event Current event where to show the date. + * @param {any} [prevEvent] Previous event where to compare the date with. + * @return {boolean} If date has changed and should be shown. + */ + protected showDate(event: any, prevEvent?: any): boolean { + if (!prevEvent) { + // First event, show it. + return true; + } + + // Check if day has changed. + return !moment(event.timestart * 1000).isSame(prevEvent.timestart * 1000, 'day'); + } + + /** + * Check if event ends the same date or not. + * + * @param {any} event Event info. + * @return {boolean} If date has changed and should be shown. + */ + protected endsSameDay(event: any): boolean { + if (!event.timeduration) { + // No duration. + return true; + } + + // Check if day has changed. + return moment(event.timestart * 1000).isSame((event.timestart + event.timeduration) * 1000, 'day'); + } + /** * Show the context menu. * From 51b2c94cde5679fd9e67a6edb42211bee7e182b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 10 Jan 2019 08:59:40 +0100 Subject: [PATCH 4/5] MOBILE-2795 course: Load course image from course section without showing it --- src/core/course/components/format/core-course-format.html | 7 +++++-- src/core/course/components/format/format.scss | 1 + src/core/course/pages/section/section.ts | 5 +++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/core/course/components/format/core-course-format.html b/src/core/course/components/format/core-course-format.html index a7c21a84c..3377c9150 100644 --- a/src/core/course/components/format/core-course-format.html +++ b/src/core/course/components/format/core-course-format.html @@ -23,8 +23,11 @@ - - + +
+ +
+
diff --git a/src/core/course/components/format/format.scss b/src/core/course/components/format/format.scss index 3639d394c..2533ecdba 100644 --- a/src/core/course/components/format/format.scss +++ b/src/core/course/components/format/format.scss @@ -34,6 +34,7 @@ ion-app.app-root core-course-format { } .core-course-thumb { + display: none; height: 150px; width: 100%; overflow: hidden; diff --git a/src/core/course/pages/section/section.ts b/src/core/course/pages/section/section.ts index a246213b6..4a8bcf145 100644 --- a/src/core/course/pages/section/section.ts +++ b/src/core/course/pages/section/section.ts @@ -265,6 +265,11 @@ export class CoreCourseSectionPage implements OnDestroy { }); })); + // Get the overview files. + if (this.course.overviewfiles) { + this.course.imageThumb = this.course.overviewfiles[0] && this.course.overviewfiles[0].fileurl; + } + // Load the course handlers. promises.push(this.courseOptionsDelegate.getHandlersToDisplay(this.injector, this.course, refresh, false) .then((handlers) => { From 43d727a526cf638a45ebaf5adda9987108dad831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 10 Jan 2019 10:19:25 +0100 Subject: [PATCH 5/5] MOBILE-2795 login: Trigger login event on reconnect --- src/core/siteplugins/providers/helper.ts | 9 ++++----- src/providers/sites.ts | 4 +++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/core/siteplugins/providers/helper.ts b/src/core/siteplugins/providers/helper.ts index 666044bd0..a65bc96e1 100644 --- a/src/core/siteplugins/providers/helper.ts +++ b/src/core/siteplugins/providers/helper.ts @@ -97,14 +97,13 @@ export class CoreSitePluginsHelperProvider { this.logger = logger.getInstance('CoreSitePluginsHelperProvider'); // Fetch the plugins on login. - eventsProvider.on(CoreEventsProvider.LOGIN, () => { - const siteId = this.sitesProvider.getCurrentSiteId(); - this.fetchSitePlugins(siteId).then((plugins) => { + eventsProvider.on(CoreEventsProvider.LOGIN, (data) => { + this.fetchSitePlugins(data.siteId).then((plugins) => { // Plugins fetched, check that site hasn't changed. - if (siteId == this.sitesProvider.getCurrentSiteId() && plugins.length) { + if (data.siteId == this.sitesProvider.getCurrentSiteId() && plugins.length) { // Site is still the same. Load the plugins and trigger the event. this.loadSitePlugins(plugins).then(() => { - eventsProvider.trigger(CoreEventsProvider.SITE_PLUGINS_LOADED, {}, siteId); + eventsProvider.trigger(CoreEventsProvider.SITE_PLUGINS_LOADED, {}, data.siteId); }); } diff --git a/src/providers/sites.ts b/src/providers/sites.ts index 0d6a63aac..d1abf0bc9 100644 --- a/src/providers/sites.ts +++ b/src/providers/sites.ts @@ -1062,7 +1062,9 @@ export class CoreSitesProvider { updateSiteToken(siteUrl: string, username: string, token: string, privateToken: string = ''): Promise { const siteId = this.createSiteID(siteUrl, username); - return this.updateSiteTokenBySiteId(siteId, token, privateToken); + return this.updateSiteTokenBySiteId(siteId, token, privateToken).then(() => { + return this.login(siteId); + }); } /**