Merge pull request from crazyserver/MOBILE-3213

Mobile 3213
This commit is contained in:
Juan Leyva 2019-12-18 12:45:40 +01:00 committed by GitHub
commit 62ce8a3536
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 125 additions and 45 deletions
src
addon
block
recentlyaccessedcourses/components/recentlyaccessedcourses
starredcourses/components/starredcourses
timeline/components/events
mod
assign/providers
data
components/index
data.scss
providers
workshop
theme

@ -1,6 +1,6 @@
<ion-item-divider> <ion-item-divider>
<h2>{{ 'addon.block_recentlyaccessedcourses.pluginname' | translate }}</h2> <h2>{{ 'addon.block_recentlyaccessedcourses.pluginname' | translate }}</h2>
<div *ngIf="downloadEnabled && courses && courses.length > 1" class="core-button-spinner" item-end> <div *ngIf="downloadCoursesEnabled && downloadEnabled && courses && courses.length > 1" class="core-button-spinner" item-end>
<button *ngIf="prefetchCoursesData.icon && prefetchCoursesData.icon != 'spinner'" ion-button icon-only clear color="dark" (click)="prefetchCourses()"> <button *ngIf="prefetchCoursesData.icon && prefetchCoursesData.icon != 'spinner'" ion-button icon-only clear color="dark" (click)="prefetchCourses()">
<core-icon [name]="prefetchCoursesData.icon"></core-icon> <core-icon [name]="prefetchCoursesData.icon"></core-icon>
</button> </button>
@ -13,7 +13,7 @@
<!-- List of courses. --> <!-- List of courses. -->
<div class="core-horizontal-scroll"> <div class="core-horizontal-scroll">
<ng-container *ngFor="let course of courses"> <ng-container *ngFor="let course of courses">
<core-courses-course-progress [course]="course" class="core-recentlyaccessedcourses" [showDownload]="downloadEnabled"></core-courses-course-progress> <core-courses-course-progress [course]="course" class="core-recentlyaccessedcourses" [showDownload]="downloadCourseEnabled && downloadEnabled"></core-courses-course-progress>
</ng-container> </ng-container>
</div> </div>
</core-loading> </core-loading>

@ -37,10 +37,13 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom
icon: '', icon: '',
badge: '' badge: ''
}; };
downloadCourseEnabled: boolean;
downloadCoursesEnabled: boolean;
protected prefetchIconsInitialized = false; protected prefetchIconsInitialized = false;
protected isDestroyed; protected isDestroyed;
protected coursesObserver; protected coursesObserver;
protected updateSiteObserver;
protected courseIds = []; protected courseIds = [];
protected fetchContentDefaultError = 'Error getting recent courses data.'; protected fetchContentDefaultError = 'Error getting recent courses data.';
@ -58,6 +61,17 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom
*/ */
ngOnInit(): void { ngOnInit(): void {
// Refresh the enabled flags if enabled.
this.downloadCourseEnabled = !this.coursesProvider.isDownloadCourseDisabledInSite();
this.downloadCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite();
// Refresh the enabled flags if site is updated.
this.updateSiteObserver = this.eventsProvider.on(CoreEventsProvider.SITE_UPDATED, () => {
this.downloadCourseEnabled = !this.coursesProvider.isDownloadCourseDisabledInSite();
this.downloadCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite();
}, this.sitesProvider.getCurrentSiteId());
this.coursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, () => { this.coursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, () => {
this.refreshContent(); this.refreshContent();
}, this.sitesProvider.getCurrentSiteId()); }, this.sitesProvider.getCurrentSiteId());
@ -154,5 +168,6 @@ export class AddonBlockRecentlyAccessedCoursesComponent extends CoreBlockBaseCom
ngOnDestroy(): void { ngOnDestroy(): void {
this.isDestroyed = true; this.isDestroyed = true;
this.coursesObserver && this.coursesObserver.off(); this.coursesObserver && this.coursesObserver.off();
this.updateSiteObserver && this.updateSiteObserver.off();
} }
} }

@ -1,6 +1,6 @@
<ion-item-divider> <ion-item-divider>
<h2>{{ 'addon.block_starredcourses.pluginname' | translate }}</h2> <h2>{{ 'addon.block_starredcourses.pluginname' | translate }}</h2>
<div *ngIf="downloadEnabled && courses && courses.length > 1" class="core-button-spinner" item-end> <div *ngIf="downloadCoursesEnabled && downloadEnabled && courses && courses.length > 1" class="core-button-spinner" item-end>
<button *ngIf="prefetchCoursesData.icon && prefetchCoursesData.icon != 'spinner'" ion-button icon-only clear color="dark" (click)="prefetchCourses()"> <button *ngIf="prefetchCoursesData.icon && prefetchCoursesData.icon != 'spinner'" ion-button icon-only clear color="dark" (click)="prefetchCourses()">
<core-icon [name]="prefetchCoursesData.icon"></core-icon> <core-icon [name]="prefetchCoursesData.icon"></core-icon>
</button> </button>
@ -13,7 +13,7 @@
<!-- List of courses. --> <!-- List of courses. -->
<div class="core-horizontal-scroll"> <div class="core-horizontal-scroll">
<ng-container *ngFor="let course of courses"> <ng-container *ngFor="let course of courses">
<core-courses-course-progress [course]="course" class="core-block_starredcourses" [showDownload]="downloadEnabled"></core-courses-course-progress> <core-courses-course-progress [course]="course" class="core-block_starredcourses" [showDownload]="downloadCourseEnabled && downloadEnabled"></core-courses-course-progress>
</ng-container> </ng-container>
</div> </div>
</core-loading> </core-loading>

@ -37,10 +37,13 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im
icon: '', icon: '',
badge: '' badge: ''
}; };
downloadCourseEnabled: boolean;
downloadCoursesEnabled: boolean;
protected prefetchIconsInitialized = false; protected prefetchIconsInitialized = false;
protected isDestroyed; protected isDestroyed;
protected coursesObserver; protected coursesObserver;
protected updateSiteObserver;
protected courseIds = []; protected courseIds = [];
protected fetchContentDefaultError = 'Error getting starred courses data.'; protected fetchContentDefaultError = 'Error getting starred courses data.';
@ -58,6 +61,17 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im
*/ */
ngOnInit(): void { ngOnInit(): void {
// Refresh the enabled flags if enabled.
this.downloadCourseEnabled = !this.coursesProvider.isDownloadCourseDisabledInSite();
this.downloadCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite();
// Refresh the enabled flags if site is updated.
this.updateSiteObserver = this.eventsProvider.on(CoreEventsProvider.SITE_UPDATED, () => {
this.downloadCourseEnabled = !this.coursesProvider.isDownloadCourseDisabledInSite();
this.downloadCoursesEnabled = !this.coursesProvider.isDownloadCoursesDisabledInSite();
}, this.sitesProvider.getCurrentSiteId());
this.coursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, () => { this.coursesObserver = this.eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, () => {
this.refreshContent(); this.refreshContent();
}, this.sitesProvider.getCurrentSiteId()); }, this.sitesProvider.getCurrentSiteId());
@ -154,5 +168,6 @@ export class AddonBlockStarredCoursesComponent extends CoreBlockBaseComponent im
ngOnDestroy(): void { ngOnDestroy(): void {
this.isDestroyed = true; this.isDestroyed = true;
this.coursesObserver && this.coursesObserver.off(); this.coursesObserver && this.coursesObserver.off();
this.updateSiteObserver && this.updateSiteObserver.off();
} }
} }

@ -14,3 +14,32 @@ ion-app.app-root addon-block-timeline-events {
pointer-events: auto; pointer-events: auto;
} }
} }
ion-app.app-root core-courses-course-progress addon-block-timeline-events {
@include media-breakpoint-up(md) {
.hidden-tablet {
display: block !important;
opacity: 1 !important;
&.button[disabled] {
opacity: .4 !important;
}
}
.hidden-phone {
display: none !important;
opacity: 0 !important;
}
}
@include media-breakpoint-up(lg) {
.hidden-tablet {
display: none !important;
opacity: 0 !important;
}
.hidden-phone {
display: block !important;
opacity: 1 !important;
&.button[disabled] {
opacity: .4 !important;
}
}
}
}

@ -29,6 +29,7 @@ import { CoreSyncBaseProvider } from '@classes/base-sync';
import { AddonModAssignProvider, AddonModAssignAssign } from './assign'; import { AddonModAssignProvider, AddonModAssignAssign } from './assign';
import { AddonModAssignOfflineProvider } from './assign-offline'; import { AddonModAssignOfflineProvider } from './assign-offline';
import { AddonModAssignSubmissionDelegate } from './submission-delegate'; import { AddonModAssignSubmissionDelegate } from './submission-delegate';
import { AddonModAssignFeedbackDelegate } from './feedback-delegate';
/** /**
* Data returned by an assign sync. * Data returned by an assign sync.
@ -55,13 +56,22 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider {
protected componentTranslate: string; protected componentTranslate: string;
constructor(loggerProvider: CoreLoggerProvider, sitesProvider: CoreSitesProvider, appProvider: CoreAppProvider, constructor(loggerProvider: CoreLoggerProvider,
syncProvider: CoreSyncProvider, textUtils: CoreTextUtilsProvider, translate: TranslateService, sitesProvider: CoreSitesProvider,
private courseProvider: CoreCourseProvider, private eventsProvider: CoreEventsProvider, appProvider: CoreAppProvider,
private assignProvider: AddonModAssignProvider, private assignOfflineProvider: AddonModAssignOfflineProvider, syncProvider: CoreSyncProvider,
private utils: CoreUtilsProvider, private submissionDelegate: AddonModAssignSubmissionDelegate, textUtils: CoreTextUtilsProvider,
private gradesHelper: CoreGradesHelperProvider, timeUtils: CoreTimeUtilsProvider, translate: TranslateService,
private logHelper: CoreCourseLogHelperProvider) { timeUtils: CoreTimeUtilsProvider,
protected courseProvider: CoreCourseProvider,
protected eventsProvider: CoreEventsProvider,
protected assignProvider: AddonModAssignProvider,
protected assignOfflineProvider: AddonModAssignOfflineProvider,
protected utils: CoreUtilsProvider,
protected submissionDelegate: AddonModAssignSubmissionDelegate,
protected feedbackDelegate: AddonModAssignFeedbackDelegate,
protected gradesHelper: CoreGradesHelperProvider,
protected logHelper: CoreCourseLogHelperProvider) {
super('AddonModAssignSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, super('AddonModAssignSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate,
timeUtils); timeUtils);
@ -403,9 +413,19 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider {
return this.assignProvider.submitGradingFormOnline(assign.id, userId, offlineData.grade, offlineData.attemptnumber, return this.assignProvider.submitGradingFormOnline(assign.id, userId, offlineData.grade, offlineData.attemptnumber,
offlineData.addattempt, offlineData.workflowstate, offlineData.applytoall, offlineData.outcomes, offlineData.addattempt, offlineData.workflowstate, offlineData.applytoall, offlineData.outcomes,
offlineData.plugindata, siteId).then(() => { offlineData.plugindata, siteId).then(() => {
// Grades sent.
// Discard grades drafts.
const promises = [];
if (status.feedback && status.feedback.plugins) {
status.feedback.plugins.forEach((plugin) => {
promises.push(this.feedbackDelegate.discardPluginFeedbackData(assign.id, userId, plugin, siteId));
});
}
// Grades sent, update cached data. No need to block the user for this. // Update cached data.
this.assignProvider.getSubmissionStatus(assign.id, userId, undefined, false, true, true, siteId); promises.push(this.assignProvider.getSubmissionStatus(assign.id, userId, undefined, false, true, true, siteId));
return Promise.all(promises);
}).catch((error) => { }).catch((error) => {
if (error && this.utils.isWebServiceError(error)) { if (error && this.utils.isWebServiceError(error)) {
// The WebService has thrown an error, this means it cannot be submitted. Discard the offline data. // The WebService has thrown an error, this means it cannot be submitted. Discard the offline data.

@ -286,6 +286,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
let entriesHTML = this.dataHelper.getTemplate(this.data, 'listtemplateheader', this.fieldsArray); let entriesHTML = this.dataHelper.getTemplate(this.data, 'listtemplateheader', this.fieldsArray);
console.error(entriesHTML);
// Get first entry from the whole list. // Get first entry from the whole list.
if (!this.search.searching || !this.firstEntry) { if (!this.search.searching || !this.firstEntry) {
this.firstEntry = this.entries[0].id; this.firstEntry = this.entries[0].id;
@ -305,7 +306,8 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
}); });
entriesHTML += this.dataHelper.getTemplate(this.data, 'listtemplatefooter', this.fieldsArray); entriesHTML += this.dataHelper.getTemplate(this.data, 'listtemplatefooter', this.fieldsArray);
this.entriesRendered = entriesHTML; this.entriesRendered = this.domUtils.fixHtml(entriesHTML);
console.error(entriesHTML);
// Pass the input data to the component. // Pass the input data to the component.
this.jsData = { this.jsData = {

@ -3,6 +3,7 @@
white-space: normal; white-space: normal;
word-break: break-word; word-break: break-word;
padding: $content-padding; padding: $content-padding;
@include safe-area-padding-horizontal($content-padding !important, $content-padding !important);
background-color: $white; background-color: $white;
border-top-width: 1px; border-top-width: 1px;
border-bottom-width: 1px; border-bottom-width: 1px;
@ -31,6 +32,18 @@
@extend .col; @extend .col;
min-height: auto; min-height: auto;
} }
// Do not let block elements to define widths or heights.
address, article, aside, blockquote, canvas, dd, div, dl, dt, fieldset, figcaption, figure, footer, form, h1, h2, h3, h4, h5, h6,
header, hr, li, main, nav, noscript, ol, p, pre, section, table, tfoot, ul, video {
width: auto !important;
height: auto !important;
min-width: auto !important;
min-height: auto !important;
// Avoid having one entry over another.
max-height: none !important;
}
} }
page-addon-mod-data-search, page-addon-mod-data-search,

@ -599,8 +599,10 @@ export class AddonModDataHelperProvider {
getTemplate(data: any, type: string, fields: any[]): string { getTemplate(data: any, type: string, fields: any[]): string {
let template = data[type] || this.getDefaultTemplate(type, fields); let template = data[type] || this.getDefaultTemplate(type, fields);
if (type != 'listtemplateheader' && type != 'listtemplatefooter') {
// Try to fix syntax errors so the template can be parsed by Angular. // Try to fix syntax errors so the template can be parsed by Angular.
template = this.domUtils.fixHtml(template); template = this.domUtils.fixHtml(template);
}
// Add core-link directive to links. // Add core-link directive to links.
template = template.replace(/<a ([^>]*href="[^>]*)>/ig, (match, attributes) => { template = template.replace(/<a ([^>]*href="[^>]*)>/ig, (match, attributes) => {

@ -154,6 +154,7 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity
promises.push(this.workshopProvider.invalidateReviewerAssesmentsData(this.workshop.id)); promises.push(this.workshopProvider.invalidateReviewerAssesmentsData(this.workshop.id));
} }
promises.push(this.workshopProvider.invalidateGradesData(this.workshop.id)); promises.push(this.workshopProvider.invalidateGradesData(this.workshop.id));
promises.push(this.workshopProvider.invalidateWorkshopWSData(this.workshop.id));
} }
return Promise.all(promises); return Promise.all(promises);

@ -9,7 +9,7 @@
</ion-navbar> </ion-navbar>
</ion-header> </ion-header>
<ion-content> <ion-content>
<ion-refresher [enabled]="loaded" (ionRefresh)="refreshAssessment($event)"> <ion-refresher [enabled]="loaded" (ionRefresh)="refreshAssessment($event)" *ngIf="!evaluating">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
</ion-refresher> </ion-refresher>
<core-loading [hideUntil]="loaded"> <core-loading [hideUntil]="loaded">

@ -9,9 +9,6 @@
</ion-navbar> </ion-navbar>
</ion-header> </ion-header>
<ion-content> <ion-content>
<ion-refresher [enabled]="loaded" (ionRefresh)="refreshSubmission($event)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
</ion-refresher>
<core-loading [hideUntil]="loaded"> <core-loading [hideUntil]="loaded">
<form ion-list [formGroup]="editForm" *ngIf="workshop"> <form ion-list [formGroup]="editForm" *ngIf="workshop">
<ion-item text-wrap> <ion-item text-wrap>

@ -274,26 +274,6 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy {
return false; return false;
} }
/**
* Pull to refresh.
*
* @param refresher Refresher.
*/
refreshSubmission(refresher: any): void {
if (this.loaded) {
const promises = [];
promises.push(this.workshopProvider.invalidateSubmissionData(this.workshopId, this.submission.id));
promises.push(this.workshopProvider.invalidateSubmissionsData(this.workshopId));
Promise.all(promises).finally(() => {
return this.fetchSubmissionData();
}).finally(() => {
refresher.complete();
});
}
}
/** /**
* Save the submission. * Save the submission.
*/ */

@ -12,7 +12,7 @@
</ion-navbar> </ion-navbar>
</ion-header> </ion-header>
<ion-content> <ion-content>
<ion-refresher [enabled]="loaded" (ionRefresh)="refreshSubmission($event)"> <ion-refresher [enabled]="loaded" (ionRefresh)="refreshSubmission($event)" *ngIf="!((assessmentId && access.assessingallowed) || canAddFeedback)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
</ion-refresher> </ion-refresher>
<core-loading [hideUntil]="loaded"> <core-loading [hideUntil]="loaded">

@ -373,6 +373,10 @@ export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy {
promises.push(this.workshopProvider.invalidateAssessmentData(this.workshopId, this.assessmentId)); promises.push(this.workshopProvider.invalidateAssessmentData(this.workshopId, this.assessmentId));
} }
if (this.assessmentUserId) {
promises.push(this.workshopProvider.invalidateReviewerAssesmentsData(this.workshopId, this.assessmentId));
}
return Promise.all(promises).finally(() => { return Promise.all(promises).finally(() => {
this.eventsProvider.trigger(AddonModWorkshopProvider.ASSESSMENT_INVALIDATED, this.siteId); this.eventsProvider.trigger(AddonModWorkshopProvider.ASSESSMENT_INVALIDATED, this.siteId);

@ -68,7 +68,7 @@ export class AddonModWorkshopHelperProvider {
const task = this.getTask(tasks, taskCode); const task = this.getTask(tasks, taskCode);
if (task) { if (task) {
return task.completed; return !!task.completed;
} }
// Task not found, assume true. // Task not found, assume true.

@ -33,9 +33,9 @@ export class AddonModWorkshopProvider {
static PHASE_ASSESSMENT = 30; static PHASE_ASSESSMENT = 30;
static PHASE_EVALUATION = 40; static PHASE_EVALUATION = 40;
static PHASE_CLOSED = 50; static PHASE_CLOSED = 50;
static EXAMPLES_VOLUNTARY: 0; static EXAMPLES_VOLUNTARY = 0;
static EXAMPLES_BEFORE_SUBMISSION: 1; static EXAMPLES_BEFORE_SUBMISSION = 1;
static EXAMPLES_BEFORE_ASSESSMENT: 2; static EXAMPLES_BEFORE_ASSESSMENT = 2;
static SUBMISSION_TYPE_DISABLED = 0; static SUBMISSION_TYPE_DISABLED = 0;
static SUBMISSION_TYPE_AVAILABLE = 1; static SUBMISSION_TYPE_AVAILABLE = 1;
static SUBMISSION_TYPE_REQUIRED = 2; static SUBMISSION_TYPE_REQUIRED = 2;

@ -25,6 +25,7 @@ ion-app.app-root .ion-page {
color: $core-dark-link-color; color: $core-dark-link-color;
} }
.core-tabs-bar,
.core-tabs-bar *, .core-tabs-bar *,
.core-tabs-bar .tab-slide, .core-tabs-bar .tab-slide,
.ion-page, .ion-page,

@ -474,6 +474,7 @@ $core-dd-question-colors: $white, $blue-light, #DCDCDC, #D8BFD8, #87CEFA, #DAA52
} }
@mixin safe-area-position($top: null, $end: null, $bottom: null, $start: null) { @mixin safe-area-position($top: null, $end: null, $bottom: null, $start: null) {
@include position-horizontal($start, $end);
@include safe-position-horizontal($start, $end); @include safe-position-horizontal($start, $end);
top: $top; top: $top;
bottom: $bottom; bottom: $bottom;