Merge pull request #1715 from crazyserver/MOBILE-2795

Mobile 2795
main
Juan Leyva 2019-01-10 11:10:10 +01:00 committed by GitHub
commit d580036cbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 150 additions and 34 deletions

View File

@ -21,12 +21,21 @@
</core-empty-box> </core-empty-box>
<ion-list *ngIf="filteredEvents && filteredEvents.length" no-margin> <ion-list *ngIf="filteredEvents && filteredEvents.length" no-margin>
<a ion-item text-wrap *ngFor="let event of filteredEvents" [title]="event.name" (click)="gotoEvent(event.id)" [class.core-split-item-selected]="event.id == eventId"> <ng-container *ngFor="let event of filteredEvents">
<ion-item-divider *ngIf="event.showDate">
{{ event.timestart * 1000 | coreFormatDate: "strftimedayshort" }}
</ion-item-divider>
<a ion-item text-wrap [title]="event.name" (click)="gotoEvent(event.id)" [class.core-split-item-selected]="event.id == eventId">
<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>
<p>{{ event.timestart | coreToLocaleString }}</p> <p>
{{ event.timestart * 1000 | coreFormatDate: "strftimetime" }}
<span *ngIf="event.timeduration && event.endsSameDay"> - {{ (event.timestart + event.timeduration) * 1000 | coreFormatDate: "strftimetime" }}</span>
<span *ngIf="event.timeduration && !event.endsSameDay"> - {{ (event.timestart + event.timeduration) | coreToLocaleString }}</span>
</p>
</a> </a>
</ng-container>
</ion-list> </ion-list>
<core-infinite-loading [enabled]="canLoadMore" (action)="loadMoreEvents($event)" [error]="loadMoreError"></core-infinite-loading> <core-infinite-loading [enabled]="canLoadMore" (action)="loadMoreEvents($event)" [error]="loadMoreError"></core-infinite-loading>

View File

@ -26,6 +26,7 @@ import { CoreCoursePickerMenuPopoverComponent } from '@components/course-picker-
import { CoreEventsProvider } from '@providers/events'; import { CoreEventsProvider } from '@providers/events';
import { CoreAppProvider } from '@providers/app'; import { CoreAppProvider } from '@providers/app';
import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreSplitViewComponent } from '@components/split-view/split-view';
import * as moment from 'moment';
/** /**
* Page that displays the list of calendar events. * Page that displays the list of calendar events.
@ -147,6 +148,10 @@ export class AddonCalendarListPage implements OnDestroy {
} else { } else {
// Sort the events by timestart, they're ordered by id. // Sort the events by timestart, they're ordered by id.
events.sort((a, b) => { events.sort((a, b) => {
if (a.timestart == b.timestart) {
return a.timeduration - b.timeduration;
}
return a.timestart - b.timestart; return a.timestart - b.timestart;
}); });
@ -160,6 +165,12 @@ export class AddonCalendarListPage implements OnDestroy {
this.events = this.utils.mergeArraysWithoutDuplicates(this.events, events, 'id'); this.events = this.utils.mergeArraysWithoutDuplicates(this.events, events, 'id');
} }
this.filteredEvents = this.getFilteredEvents(); 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; this.canLoadMore = true;
// Schedule notifications for the events retrieved (might have new events). // 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. * Show the context menu.
* *

View File

@ -138,6 +138,37 @@ export class AddonCalendarProvider {
this.sitesProvider.createTablesFromSchema(this.tablesSchema); 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<void>} Promise resolved when done.
*/
cleanExpiredEvents(siteId?: string): Promise<void> {
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. * 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. * @return {Promise} Promise resolved when all the notifications have been scheduled.
*/ */
scheduleAllSitesEventsNotifications(): Promise<any[]> { scheduleAllSitesEventsNotifications(): Promise<any[]> {
if (this.localNotificationsProvider.isAvailable()) { const notificationsEnabled = this.localNotificationsProvider.isAvailable();
return this.sitesProvider.getSitesIds().then((siteIds) => { return this.sitesProvider.getSitesIds().then((siteIds) => {
const promises = []; const promises = [];
siteIds.forEach((siteId) => { siteIds.forEach((siteId) => {
promises.push(this.cleanExpiredEvents(siteId).then(() => {
if (notificationsEnabled) {
// Check if calendar is disabled for the site. // Check if calendar is disabled for the site.
promises.push(this.isDisabled(siteId).then((disabled) => { return this.isDisabled(siteId).then((disabled) => {
if (!disabled) { if (!disabled) {
// Get first events. // Get first events.
return this.getEventsList(undefined, undefined, siteId).then((events) => { return this.getEventsList(undefined, undefined, siteId).then((events) => {
return this.scheduleEventsNotifications(events, siteId); return this.scheduleEventsNotifications(events, siteId);
}); });
} }
});
}
})); }));
}); });
return Promise.all(promises); return Promise.all(promises);
}); });
} else {
return Promise.resolve([]);
}
} }
/** /**

View File

@ -21,6 +21,11 @@ ion-app.app-root.md {
@include margin-horizontal($item-md-padding-start + ($item-md-padding-start / 2) - 1, null); @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) { @each $color-name, $color-base, $color-contrast in get-colors($colors-md) {
.core-#{$color-name}-card { .core-#{$color-name}-card {
@extend .card-md; @extend .card-md;

View File

@ -943,6 +943,12 @@ details summary {
pointer-events: auto; pointer-events: auto;
} }
.icon.fa-graduation-cap{
font-size: 21px;
width: 21px;
line-height: 28px;
}
// Fix iframes in fullscreen mode. // Fix iframes in fullscreen mode.
// //
// Ionic sets "contain: strict" to some elements. This enables paint containment, // Ionic sets "contain: strict" to some elements. This enables paint containment,

View File

@ -17,7 +17,9 @@ ion-app.app-root.wp {
} }
.item-wp ion-spinner[item-start] + .item-inner, .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); @include margin-horizontal(($item-wp-padding-start / 2), null);
} }

View File

@ -23,8 +23,11 @@
<!-- Course summary. By default we only display the course progress. --> <!-- Course summary. By default we only display the course progress. -->
<core-dynamic-component [component]="courseSummaryComponent" [data]="data"> <core-dynamic-component [component]="courseSummaryComponent" [data]="data">
<ion-list no-lines *ngIf="selectedSection && selectedSection.id == allSectionsId && course.progress != null && course.progress >= 0" class="core-format-progress-list"> <ion-list no-lines *ngIf="course.imageThumb || (selectedSection && selectedSection.id == allSectionsId && course.progress != null && course.progress >= 0)" class="core-format-progress-list">
<ion-item class="core-course-progress"> <div *ngIf="course.imageThumb" class="core-course-thumb">
<img [src]="course.imageThumb" core-external-content alt=""/>
</div>
<ion-item class="core-course-progress" *ngIf="selectedSection && selectedSection.id == allSectionsId && course.progress != null && course.progress >= 0">
<core-progress-bar [progress]="course.progress"></core-progress-bar> <core-progress-bar [progress]="course.progress"></core-progress-bar>
</ion-item> </ion-item>
</ion-list> </ion-list>

View File

@ -34,6 +34,7 @@ ion-app.app-root core-course-format {
} }
.core-course-thumb { .core-course-thumb {
display: none;
height: 150px; height: 150px;
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;

View File

@ -139,3 +139,7 @@ ion-app.app-root.wp core-course-module {
margin-bottom: 8px; margin-bottom: 8px;
} }
} }
ion-app.app-root a.core-course-module-handler.item [item-start] + .item-inner {
@include margin-horizontal(4px, null);
}

View File

@ -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. // Load the course handlers.
promises.push(this.courseOptionsDelegate.getHandlersToDisplay(this.injector, this.course, refresh, false) promises.push(this.courseOptionsDelegate.getHandlersToDisplay(this.injector, this.course, refresh, false)
.then((handlers) => { .then((handlers) => {

View File

@ -97,14 +97,13 @@ export class CoreSitePluginsHelperProvider {
this.logger = logger.getInstance('CoreSitePluginsHelperProvider'); this.logger = logger.getInstance('CoreSitePluginsHelperProvider');
// Fetch the plugins on login. // Fetch the plugins on login.
eventsProvider.on(CoreEventsProvider.LOGIN, () => { eventsProvider.on(CoreEventsProvider.LOGIN, (data) => {
const siteId = this.sitesProvider.getCurrentSiteId(); this.fetchSitePlugins(data.siteId).then((plugins) => {
this.fetchSitePlugins(siteId).then((plugins) => {
// Plugins fetched, check that site hasn't changed. // 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. // Site is still the same. Load the plugins and trigger the event.
this.loadSitePlugins(plugins).then(() => { this.loadSitePlugins(plugins).then(() => {
eventsProvider.trigger(CoreEventsProvider.SITE_PLUGINS_LOADED, {}, siteId); eventsProvider.trigger(CoreEventsProvider.SITE_PLUGINS_LOADED, {}, data.siteId);
}); });
} }

View File

@ -489,8 +489,10 @@ export class CoreLocalNotificationsProvider {
// Remove from triggered, since the notification could be in there with a different time. // Remove from triggered, since the notification could be in there with a different time.
this.removeTriggered(notification.id); this.removeTriggered(notification.id);
this.localNotifications.cancel(notification.id).finally(() => {
this.localNotifications.schedule(notification); this.localNotifications.schedule(notification);
}); });
});
} }
}); });
} }

View File

@ -1062,7 +1062,9 @@ export class CoreSitesProvider {
updateSiteToken(siteUrl: string, username: string, token: string, privateToken: string = ''): Promise<any> { updateSiteToken(siteUrl: string, username: string, token: string, privateToken: string = ''): Promise<any> {
const siteId = this.createSiteID(siteUrl, username); const siteId = this.createSiteID(siteUrl, username);
return this.updateSiteTokenBySiteId(siteId, token, privateToken); return this.updateSiteTokenBySiteId(siteId, token, privateToken).then(() => {
return this.login(siteId);
});
} }
/** /**