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>
<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">
<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>
<h2><core-format-text [text]="event.name"></core-format-text></h2>
<p>{{ event.timestart | coreToLocaleString }}</p>
</a>
<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">
<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>
<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>
</ng-container>
</ion-list>
<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 { 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.
*

View File

@ -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<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.
*
@ -442,27 +473,29 @@ export class AddonCalendarProvider {
* @return {Promise} Promise resolved when all the notifications have been scheduled.
*/
scheduleAllSitesEventsNotifications(): Promise<any[]> {
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);
});
}
/**

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);
}
.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;

View File

@ -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,

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-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);
}

View File

@ -23,8 +23,11 @@
<!-- Course summary. By default we only display the course progress. -->
<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-item class="core-course-progress">
<ion-list no-lines *ngIf="course.imageThumb || (selectedSection && selectedSection.id == allSectionsId && course.progress != null && course.progress >= 0)" class="core-format-progress-list">
<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>
</ion-item>
</ion-list>

View File

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

View File

@ -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);
}

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

View File

@ -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);
});
}

View File

@ -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);
});
});
}
});

View File

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