MOBILE-4009 core: Create a generic password modal

main
Pau Ferrer Ocaña 2023-07-06 10:14:57 +02:00
parent c819f2eedd
commit 2804951810
11 changed files with 113 additions and 139 deletions

View File

@ -18,13 +18,11 @@ import { CoreSharedModule } from '@/core/shared.module';
import { CoreCourseComponentsModule } from '@features/course/components/components.module';
import { AddonModLessonIndexComponent } from './index/index';
import { AddonModLessonMenuModalPage } from './menu-modal/menu-modal';
import { AddonModLessonPasswordModalComponent } from './password-modal/password-modal';
@NgModule({
declarations: [
AddonModLessonIndexComponent,
AddonModLessonMenuModalPage,
AddonModLessonPasswordModalComponent,
],
imports: [
CoreSharedModule,
@ -35,7 +33,6 @@ import { AddonModLessonPasswordModalComponent } from './password-modal/password-
exports: [
AddonModLessonIndexComponent,
AddonModLessonMenuModalPage,
AddonModLessonPasswordModalComponent,
],
})
export class AddonModLessonComponentsModule {}

View File

@ -13,7 +13,6 @@
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreCanceledError } from '@classes/errors/cancelederror';
import { CoreError } from '@classes/errors/error';
import { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler';
@ -26,7 +25,6 @@ import { CoreDomUtils } from '@services/utils/dom';
import { CoreUtils } from '@services/utils/utils';
import { CoreWSFile } from '@services/ws';
import { makeSingleton, Translate } from '@singletons';
import { AddonModLessonPasswordModalComponent } from '../../components/password-modal/password-modal';
import {
AddonModLesson,
AddonModLessonGetAccessInformationWSResponse,
@ -55,15 +53,11 @@ export class AddonModLessonPrefetchHandlerService extends CoreCourseActivityPref
*/
protected async askUserPassword(): Promise<string> {
// Create and show the modal.
const modalData = await CoreDomUtils.openModal<string>({
component: AddonModLessonPasswordModalComponent,
return CoreDomUtils.promptPassword({
title: 'addon.mod_lesson.enterpassword',
placeholder: 'core.login.password',
submit: 'addon.mod_lesson.continue',
});
if (typeof modalData != 'string') {
throw new CoreCanceledError();
}
return modalData;
}
/**

View File

@ -1,7 +1,7 @@
<ion-header>
<ion-toolbar>
<ion-title>
<h1>{{ 'core.login.password' | translate }}</h1>
<h1>{{ title | translate }}</h1>
</ion-title>
<ion-buttons slot="end">
<ion-button fill="clear" (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
@ -10,19 +10,18 @@
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding addon-mod_lesson-password-modal">
<form (ngSubmit)="submitPassword($event, passwordinput)" #passwordForm>
<ion-content class="ion-padding">
<form (ngSubmit)="submitPassword($event)" #passwordForm class="ion-padding-vertical">
<ion-item>
<ion-label>{{ 'addon.mod_lesson.enterpassword' | translate }}</ion-label>
<ion-label class="sr-only">{{ placeholder | translate }}</ion-label>
<core-show-password name="password">
<ion-input name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" core-auto-focus
#passwordinput [clearOnEdit]="false">
<ion-input class="ion-text-wrap core-ioninput-password" name="password" type="password"
placeholder="{{ placeholder | translate }}" [(ngModel)]="password" core-auto-focus [clearOnEdit]="false">
</ion-input>
</core-show-password>
</ion-item>
<ion-button expand="block" type="submit">
{{ 'addon.mod_lesson.continue' | translate }}
<ion-icon slot="end" name="fas-chevron-right" aria-hidden="true"></ion-icon>
<ion-button expand="block" type="submit" [disabled]="!password">
{{ submit | translate }}
</ion-button>
<!-- Remove this once Ionic fixes this bug: https://github.com/ionic-team/ionic-framework/issues/19368 -->
<input type="submit" class="core-submit-hidden-enter" />

View File

@ -0,0 +1,29 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { NgModule } from '@angular/core';
import { CorePasswordModalComponent } from './password-modal';
import { CoreSharedModule } from '@/core/shared.module';
export { CorePasswordModalComponent };
@NgModule({
declarations: [
CorePasswordModalComponent,
],
imports: [
CoreSharedModule,
],
})
export class CorePasswordModalModule {}

View File

@ -12,37 +12,42 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, ViewChild, ElementRef } from '@angular/core';
import { IonInput } from '@ionic/angular';
import { Component, ViewChild, ElementRef, Input } from '@angular/core';
import { CoreSites } from '@services/sites';
import { CoreForms } from '@singletons/form';
import { ModalController } from '@singletons';
/**
* Modal that asks the password for a lesson.
* Modal that asks the password.
*
* WARNING: This component is not loaded with components.module.ts.
*/
@Component({
selector: 'page-addon-mod-lesson-password-modal',
selector: 'core-password-modal',
templateUrl: 'password-modal.html',
})
export class AddonModLessonPasswordModalComponent {
export class CorePasswordModalComponent {
@ViewChild('passwordForm') formElement?: ElementRef;
@Input() title? = 'core.login.password'; // Translatable string to be shown on modal title.
@Input() placeholder? = 'core.login.password'; // Translatable string to be shown on password input as placeholder.
@Input() submit? = 'core.submit'; // Translatable string to be shown on submit button.
@Input() password? = ''; // Previous entered password.
/**
* Send the password back.
*
* @param e Event.
* @param password The input element.
*/
submitPassword(e: Event, password: IonInput): void {
submitPassword(e: Event): void {
e.preventDefault();
e.stopPropagation();
CoreForms.triggerFormSubmittedEvent(this.formElement, false, CoreSites.getCurrentSiteId());
ModalController.dismiss(password.value);
ModalController.dismiss(this.password);
}
/**
@ -55,3 +60,5 @@ export class AddonModLessonPasswordModalComponent {
}
}
export type CorePasswordModalParams = Pick<CorePasswordModalComponent, 'title' | 'placeholder' | 'submit' | 'password'>;

View File

@ -31,7 +31,6 @@ import {
} from '@features/course/services/course-options-delegate';
import { CoreCourseHelper } from '@features/course/services/course-helper';
import { ActionSheetController, ModalController, NgZone, Translate } from '@singletons';
import { CoreCoursesSelfEnrolPasswordComponent } from '../../../courses/components/self-enrol-password/self-enrol-password';
import { CoreNavigator } from '@services/navigator';
import { CoreUtils } from '@services/utils/utils';
import { CoreCoursesHelper, CoreCourseWithImageAndColor } from '@features/courses/services/courses-helper';
@ -374,25 +373,23 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
modal?.dismiss();
if (error && error.errorcode === CoreCoursesProvider.ENROL_INVALID_KEY) {
// Initialize the self enrol modal.
// Invalid password, show the modal to enter the password.
const modalData = await CoreDomUtils.openModal<string>(
{
component: CoreCoursesSelfEnrolPasswordComponent,
componentProps: { password },
},
);
if (modalData !== undefined) {
try {
// Initialize the self enrol modal.
// Invalid password, show the modal to enter the password.
const modalData = await CoreDomUtils.promptPassword({
password,
title: 'core.courses.selfenrolment',
placeholder: 'core.courses.password',
submit: 'core.courses.enrolme',
});
this.selfEnrolInCourse(instanceId, modalData);
return;
}
if (!password) {
} catch {
// No password entered, don't show error.
return;
}
return;
}
CoreDomUtils.showErrorModalDefault(error, 'core.courses.errorselfenrol', true);

View File

@ -18,14 +18,12 @@ import { CoreSharedModule } from '@/core/shared.module';
import { CoreCoursesCourseListItemComponent } from './course-list-item/course-list-item';
import { CoreCoursesCourseProgressComponent } from './course-progress/course-progress';
import { CoreCoursesCourseOptionsMenuComponent } from './course-options-menu/course-options-menu';
import { CoreCoursesSelfEnrolPasswordComponent } from './self-enrol-password/self-enrol-password';
@NgModule({
declarations: [
CoreCoursesCourseListItemComponent,
CoreCoursesCourseProgressComponent,
CoreCoursesCourseOptionsMenuComponent,
CoreCoursesSelfEnrolPasswordComponent,
],
imports: [
CoreSharedModule,
@ -34,7 +32,6 @@ import { CoreCoursesSelfEnrolPasswordComponent } from './self-enrol-password/sel
CoreCoursesCourseListItemComponent,
CoreCoursesCourseProgressComponent,
CoreCoursesCourseOptionsMenuComponent,
CoreCoursesSelfEnrolPasswordComponent,
],
})
export class CoreCoursesComponentsModule {}

View File

@ -1,27 +0,0 @@
<ion-header>
<ion-toolbar>
<ion-title>
<h1>{{ 'core.courses.selfenrolment' | translate }}</h1>
</ion-title>
<ion-buttons slot="end">
<ion-button fill="clear" (click)="close()" [attr.aria-label]="'core.close' | translate">
<ion-icon name="fas-xmark" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<form (ngSubmit)="submitPassword($event)" #enrolPasswordForm>
<ion-item>
<ion-label class="sr-only">{{ 'core.courses.password' | translate }}</ion-label>
<core-show-password name="password">
<ion-input class="ion-text-wrap core-ioninput-password" name="password" type="password"
placeholder="{{ 'core.courses.password' | translate }}" [(ngModel)]="password" core-auto-focus [clearOnEdit]="false">
</ion-input>
</core-show-password>
</ion-item>
<div class="ion-padding">
<ion-button expand="block" [disabled]="!password" type="submit">{{ 'core.courses.enrolme' | translate }}</ion-button>
</div>
</form>
</ion-content>

View File

@ -1,62 +0,0 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, ViewChild, ElementRef } from '@angular/core';
import { NavParams } from '@ionic/angular';
import { CoreSites } from '@services/sites';
import { ModalController } from '@singletons';
import { CoreForms } from '@singletons/form';
/**
* Modal that displays a form to enter a password to self enrol in a course.
*/
@Component({
selector: 'page-core-courses-self-enrol-password',
templateUrl: 'self-enrol-password.html',
})
export class CoreCoursesSelfEnrolPasswordComponent {
@ViewChild('enrolPasswordForm') formElement!: ElementRef;
password = '';
constructor(
navParams: NavParams,
) {
this.password = navParams.get('password') || '';
}
/**
* Close help modal.
*/
close(): void {
CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId());
ModalController.dismiss();
}
/**
* Submit password.
*
* @param e Event.
*/
submitPassword(e: Event): void {
e.preventDefault();
e.stopPropagation();
CoreForms.triggerFormSubmittedEvent(this.formElement, false, CoreSites.getCurrentSiteId());
ModalController.dismiss(this.password);
}
}

View File

@ -59,6 +59,7 @@ import { CoreErrorInfoComponent } from '@components/error-info/error-info';
import { CorePlatform } from '@services/platform';
import { CoreCancellablePromise } from '@classes/cancellable-promise';
import { CoreLang } from '@services/lang';
import { CorePasswordModalParams } from '@components/password-modal/password-modal';
/*
* "Utils" service with helper functions for UI, DOM elements and HTML code.
@ -1880,6 +1881,33 @@ export class CoreDomUtilsProvider {
}
}
/**
* Prompts password to the user and returns the entered text.
*
* @param passwordParams Params to show the modal.
* @returns Entered password.
*/
async promptPassword(passwordParams?: CorePasswordModalParams): Promise<CorePasswordModalResponse> {
const { CorePasswordModalComponent } =
await import('@/core/components/password-modal/password-modal.module');
const modalData = await CoreDomUtils.openModal<string>(
{
cssClass: 'core-password-modal',
showBackdrop: true,
backdropDismiss: true,
component: CorePasswordModalComponent,
componentProps: passwordParams,
},
);
if (typeof modalData !== 'string') {
throw new CoreCanceledError();
}
return modalData;
}
/**
* View an image in a modal.
*

View File

@ -729,6 +729,21 @@ body.core-iframe-fullscreen ion-router-outlet {
}
}
.core-password-modal {
--border-radius: var(--medium-radius);
--min-width: auto;
--min-height: 260px;
--width: 320px;
--height: auto;
form {
display: flex;
flex-direction: column;
height: 100%;
justify-content: space-between;
}
}
// Hidden submit button.
.core-submit-hidden-enter {
position: absolute;
@ -871,7 +886,7 @@ img.large-avatar,
max-width: var(--core-large-avatar-size);
max-height: var(--core-large-avatar-size);
margin-bottom: 10px;
border-radius : 50%;
border-radius: 50%;
padding: 4px;
border: 1px solid var(--stroke);
background-color: transparent;