Merge pull request #2797 from dpalou/MOBILE-3320

Mobile 3320
main
Pau Ferrer Ocaña 2021-05-27 16:03:32 +02:00 committed by GitHub
commit 134e2d7203
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 79 additions and 47 deletions

View File

@ -44,12 +44,6 @@ export class AddonModAssignSubmissionFileComponent extends AddonModAssignSubmiss
* Component being initialized. * Component being initialized.
*/ */
async ngOnInit(): Promise<void> { async ngOnInit(): Promise<void> {
// Get the offline data.
const filesData = await CoreUtils.ignoreErrors(
AddonModAssignOffline.getSubmission(this.assign.id),
undefined,
);
this.acceptedTypes = this.configs?.filetypeslist; this.acceptedTypes = this.configs?.filetypeslist;
this.maxSize = this.configs?.maxsubmissionsizebytes this.maxSize = this.configs?.maxsubmissionsizebytes
? parseInt(this.configs.maxsubmissionsizebytes, 10) ? parseInt(this.configs.maxsubmissionsizebytes, 10)
@ -58,6 +52,12 @@ export class AddonModAssignSubmissionFileComponent extends AddonModAssignSubmiss
? parseInt(this.configs.maxfilesubmissions, 10) ? parseInt(this.configs.maxfilesubmissions, 10)
: undefined; : undefined;
// Get the offline data.
const filesData = await CoreUtils.ignoreErrors(
AddonModAssignOffline.getSubmission(this.assign.id),
undefined,
);
try { try {
if (filesData && filesData.plugindata && filesData.plugindata.files_filemanager) { if (filesData && filesData.plugindata && filesData.plugindata.files_filemanager) {
const offlineDataFiles = <CoreFileUploaderStoreFilesResult>filesData.plugindata.files_filemanager; const offlineDataFiles = <CoreFileUploaderStoreFilesResult>filesData.plugindata.files_filemanager;

View File

@ -41,7 +41,7 @@
<ion-label position="stacked" id="addon-mod-glossary-aliases-label"> <ion-label position="stacked" id="addon-mod-glossary-aliases-label">
{{ 'addon.mod_glossary.aliases' | translate }} {{ 'addon.mod_glossary.aliases' | translate }}
</ion-label> </ion-label>
<ion-textarea [(ngModel)]="options.aliases" rows="1" core-auto-rows <ion-textarea [(ngModel)]="options.aliases" rows="1" [core-auto-rows]="options.aliases"
aria-labelledby="addon-mod-glossary-aliases-label" name="aliases"> aria-labelledby="addon-mod-glossary-aliases-label" name="aliases">
</ion-textarea> </ion-textarea>
</ion-item> </ion-item>

View File

@ -19,6 +19,7 @@ import { CoreCourseContentsPage } from '@features/course/pages/contents/contents
import { CoreCourse } from '@features/course/services/course'; import { CoreCourse } from '@features/course/services/course';
import { IonContent } from '@ionic/angular'; import { IonContent } from '@ionic/angular';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreSync } from '@services/sync';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
import { Translate } from '@singletons'; import { Translate } from '@singletons';
@ -584,9 +585,17 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom
/** /**
* Performs the sync of the activity. * Performs the sync of the activity.
* *
* @param retries Number of retries done.
* @return Promise resolved when done. * @return Promise resolved when done.
*/ */
protected async sync(): Promise<AddonModScormSyncResult> { protected async sync(retries = 0): Promise<AddonModScormSyncResult> {
if (CoreSync.isBlocked(AddonModScormProvider.COMPONENT, this.scorm!.id) && retries < 5) {
// Sync is currently blocked, this can happen when SCORM player is left. Retry in a bit.
await CoreUtils.wait(400);
return this.sync(retries + 1);
}
const result = await AddonModScormSync.syncScorm(this.scorm!); const result = await AddonModScormSync.syncScorm(this.scorm!);
if (!result.updated && this.dataSent) { if (!result.updated && this.dataSent) {

View File

@ -15,7 +15,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CoreError } from '@classes/errors/error'; import { CoreError } from '@classes/errors/error';
import { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler'; import { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler';
import { CoreCourseAnyModuleData, CoreCourseCommonModWSOptions } from '@features/course/services/course'; import { CoreCourse, CoreCourseAnyModuleData, CoreCourseCommonModWSOptions } from '@features/course/services/course';
import { CoreApp } from '@services/app'; import { CoreApp } from '@services/app';
import { CoreFile } from '@services/file'; import { CoreFile } from '@services/file';
import { CoreFilepool } from '@services/filepool'; import { CoreFilepool } from '@services/filepool';
@ -316,8 +316,13 @@ export class AddonModScormPrefetchHandlerService extends CoreCourseActivityPrefe
* @param siteId Site ID. * @param siteId Site ID.
* @returns Promise resolved with the SCORM. * @returns Promise resolved with the SCORM.
*/ */
protected getScorm(module: CoreCourseAnyModuleData, courseId: number, siteId?: string): Promise<AddonModScormScorm> { protected async getScorm(module: CoreCourseAnyModuleData, courseId: number, siteId?: string): Promise<AddonModScormScorm> {
const moduleUrl = 'url' in module ? module.url : undefined; let moduleUrl = 'url' in module ? module.url : undefined;
if (!moduleUrl) {
module = await CoreCourse.getModule(module.id, module.course, undefined, true, false, siteId);
moduleUrl = module.url;
}
return AddonModScorm.getScorm(courseId, module.id, { moduleUrl, siteId }); return AddonModScorm.getScorm(courseId, module.id, { moduleUrl, siteId });
} }

View File

@ -198,7 +198,7 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide
if (updated) { if (updated) {
try { try {
// Update downloaded data. // Update downloaded data.
const module = await CoreCourse.getModuleBasicInfoByInstance(scorm.id, 'scorm', siteId); const module = await CoreCourse.getModule(scorm.coursemodule, scorm.course, undefined, false, false, siteId);
await this.prefetchAfterUpdate(AddonModScormPrefetchHandler.instance, module, scorm.course, undefined, siteId); await this.prefetchAfterUpdate(AddonModScormPrefetchHandler.instance, module, scorm.course, undefined, siteId);
} catch { } catch {

View File

@ -32,7 +32,8 @@
<ion-label position="stacked"> <ion-label position="stacked">
{{ 'addon.mod_workshop_assessment_accumulative.dimensioncommentfor' | translate : {'$a': field.dimtitle } }} {{ 'addon.mod_workshop_assessment_accumulative.dimensioncommentfor' | translate : {'$a': field.dimtitle } }}
</ion-label> </ion-label>
<ion-textarea [(ngModel)]="selectedValues[n].peercomment" core-auto-rows></ion-textarea> <ion-textarea [(ngModel)]="selectedValues[n].peercomment" [core-auto-rows]="selectedValues[n].peercomment">
</ion-textarea>
</ion-item> </ion-item>
<ion-item *ngIf="!edit" class="ion-text-wrap"> <ion-item *ngIf="!edit" class="ion-text-wrap">
<ion-label> <ion-label>

View File

@ -14,7 +14,8 @@
{{ 'addon.mod_workshop_assessment_comments.dimensioncommentfor' | translate : {'$a': field.dimtitle } }} {{ 'addon.mod_workshop_assessment_comments.dimensioncommentfor' | translate : {'$a': field.dimtitle } }}
</span> </span>
</ion-label> </ion-label>
<ion-textarea [(ngModel)]="selectedValues[n].peercomment" core-auto-rows></ion-textarea> <ion-textarea [(ngModel)]="selectedValues[n].peercomment" [core-auto-rows]="selectedValues[n].peercomment">
</ion-textarea>
<core-input-errors *ngIf="fieldErrors['peercomment_' + n]" [errorText]="fieldErrors['peercomment_' + n]"> <core-input-errors *ngIf="fieldErrors['peercomment_' + n]" [errorText]="fieldErrors['peercomment_' + n]">
</core-input-errors> </core-input-errors>
</ion-item> </ion-item>

View File

@ -36,7 +36,7 @@
{{ 'addon.mod_workshop_assessment_numerrors.dimensioncommentfor' | translate : {'$a': field.dimtitle } }} {{ 'addon.mod_workshop_assessment_numerrors.dimensioncommentfor' | translate : {'$a': field.dimtitle } }}
</ion-label> </ion-label>
<ion-textarea [(ngModel)]="selectedValues[n].peercomment" [name]="'peercomment_' + n" <ion-textarea [(ngModel)]="selectedValues[n].peercomment" [name]="'peercomment_' + n"
core-auto-rows> [core-auto-rows]="selectedValues[n].peercomment">
</ion-textarea> </ion-textarea>
</ion-item> </ion-item>
<ion-item *ngIf="!edit" class="ion-text-wrap"> <ion-item *ngIf="!edit" class="ion-text-wrap">

View File

@ -6,7 +6,7 @@
</iframe> </iframe>
<ion-button <ion-button
*ngIf="!loading" *ngIf="!loading && displayHelp"
color="dark" expand="block" fill="clear" color="dark" expand="block" fill="clear"
(click)="openIframeHelpModal()" (click)="openIframeHelpModal()"
aria-haspopup="dialog" aria-haspopup="dialog"

View File

@ -4,7 +4,7 @@
[core-auto-focus]="showKeyboard" [core-auto-focus]="showKeyboard"
[placeholder]="placeholder" [placeholder]="placeholder"
rows="1" rows="1"
core-auto-rows [core-auto-rows]="message"
[(ngModel)]="message" [(ngModel)]="message"
name="message" name="message"
(onResize)="textareaResized()" (onResize)="textareaResized()"

View File

@ -12,41 +12,31 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { Directive, ElementRef, HostListener, Output, EventEmitter, AfterViewInit } from '@angular/core'; import { Directive, ElementRef, Output, EventEmitter, AfterViewInit, Input, OnChanges } from '@angular/core';
/** /**
* Directive to adapt a textarea rows depending on the input text. It's based on Moodle's data-auto-rows. * Directive to adapt a textarea rows depending on the input text. It's based on Moodle's data-auto-rows.
* *
* @description * @description
* Usage: * Usage:
* <textarea class="core-textarea" [(ngModel)]="message" rows="1" core-auto-rows></textarea> * <textarea class="core-textarea" [(ngModel)]="message" rows="1" [core-auto-rows]="message"></textarea>
*/ */
@Directive({ @Directive({
selector: 'textarea[core-auto-rows], ion-textarea[core-auto-rows]', selector: 'textarea[core-auto-rows], ion-textarea[core-auto-rows]',
}) })
export class CoreAutoRowsDirective implements AfterViewInit { export class CoreAutoRowsDirective implements AfterViewInit, OnChanges {
protected height = 0; protected height = 0;
@Input('core-auto-rows') value?: string; // eslint-disable-line @angular-eslint/no-input-rename
@Output() onResize: EventEmitter<void>; // Emit when resizing the textarea. @Output() onResize: EventEmitter<void>; // Emit when resizing the textarea.
constructor(protected element: ElementRef) { constructor(protected element: ElementRef) {
this.onResize = new EventEmitter(); this.onResize = new EventEmitter();
} }
@HostListener('input') onInput(): void {
this.resize();
}
@HostListener('change') onChange(): void {
// Fired on reset. Wait to the change to be finished.
setTimeout(() => {
this.resize();
}, 300);
}
/** /**
* Resize after content. * Resize after initialized.
*/ */
ngAfterViewInit(): void { ngAfterViewInit(): void {
// Wait for rendering of child views. // Wait for rendering of child views.
@ -55,6 +45,18 @@ export class CoreAutoRowsDirective implements AfterViewInit {
}, 300); }, 300);
} }
/**
* Resize when content changes.
*/
ngOnChanges(): void {
this.resize();
if (this.value === '') {
// Maybe the form was resetted. In that case it takes a bit to update the height.
setTimeout(() => this.resize(), 300);
}
}
/** /**
* Resize the textarea. * Resize the textarea.
*/ */

View File

@ -206,7 +206,7 @@ export class CoreCoursesMyCoursesPage implements OnInit, OnDestroy {
* Go to search courses. * Go to search courses.
*/ */
openSearch(): void { openSearch(): void {
CoreNavigator.navigate('courses/search'); CoreNavigator.navigateToSitePath('courses/search');
} }
/** /**

View File

@ -19,7 +19,7 @@ import { LangChangeEvent } from '@ngx-translate/core';
import { CoreAppProvider } from '@services/app'; import { CoreAppProvider } from '@services/app';
import { CoreConfig } from '@services/config'; import { CoreConfig } from '@services/config';
import { CoreSubscriptions } from '@singletons/subscriptions'; import { CoreSubscriptions } from '@singletons/subscriptions';
import { makeSingleton, Translate, Platform } from '@singletons'; import { makeSingleton, Translate, Platform, Http } from '@singletons';
import * as moment from 'moment'; import * as moment from 'moment';
import { CoreSite } from '../classes/site'; import { CoreSite } from '../classes/site';
@ -142,24 +142,24 @@ export class CoreLangProvider {
// Change the language, resolving the promise when we receive the first value. // Change the language, resolving the promise when we receive the first value.
promises.push(new Promise((resolve, reject) => { promises.push(new Promise((resolve, reject) => {
CoreSubscriptions.once(Translate.use(language), data => { CoreSubscriptions.once(Translate.use(language), async data => {
// It's a language override, load the original one first. // Check if it has a parent language.
const fallbackLang = this.getParentLanguage(language); const fallbackLang = this.getParentLanguage(language);
if (fallbackLang) { if (fallbackLang) {
CoreSubscriptions.once( try {
Translate.use(fallbackLang), // Merge parent translations with the child ones.
fallbackData => { const parentTranslations = Translate.translations[fallbackLang] ?? await this.readLangFile(fallbackLang);
data = Object.assign(fallbackData, data);
const mergedData = Object.assign(parentTranslations, data);
Object.assign(data, mergedData);
} catch {
// Ignore errors.
}
}
resolve(data); resolve(data);
},
// Resolve with the original language.
() => resolve(data),
);
} else {
resolve(data);
}
}, reject); }, reject);
})); }));
@ -474,6 +474,20 @@ export class CoreLangProvider {
} }
} }
/**
* Read a language file.
*
* @param lang Language code.
* @return Promise resolved with the file contents.
*/
async readLangFile(lang: string): Promise<Record<string, string>> {
const observable = Http.get(`assets/lang/${lang}.json`, {
responseType: 'json',
});
return <Record<string, string>> await observable.toPromise();
}
/** /**
* Unload custom or site plugin strings, removing them from the translations table. * Unload custom or site plugin strings, removing them from the translations table.
* *