MOBILE-3745 buttons: Implement user avatar as a button

main
Pau Ferrer Ocaña 2021-05-06 14:38:48 +02:00
parent f11fd7b08d
commit 833f65c628
3 changed files with 115 additions and 13 deletions

View File

@ -0,0 +1,68 @@
// (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.
export abstract class CoreAriaRoleButton<T = unknown> {
componentInstance: T;
constructor(componentInstance: T) {
this.componentInstance = componentInstance;
}
/**
* A11y key functionallity that prevents keyDown events.
*
* @param event Event.
*/
keyDown(event: KeyboardEvent): void {
if ((event.key == ' ' || event.key == 'Enter') && this.isAllowed()) {
event.preventDefault();
event.stopPropagation();
}
}
/**
* A11y key functionallity that translates space and enter keys to click action.
*
* @param event Event.
*/
keyUp(event: KeyboardEvent): void {
if ((event.key == ' ' || event.key == 'Enter') && this.isAllowed()) {
event.preventDefault();
event.stopPropagation();
this.click(event);
}
}
/**
* A11y click functionallity.
*
* @param event Event.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
click(event?: Event): void {
// Nothing defined here.
}
/**
* Checks if action is allowed in class.
*
* @returns If allowed.
*/
isAllowed(): boolean {
return true;
}
}

View File

@ -1,10 +1,16 @@
<img *ngIf="avatarUrl" [src]="avatarUrl" [alt]="'core.pictureof' | translate:{$a: fullname}" core-external-content <img *ngIf="avatarUrl" [src]="avatarUrl" [alt]="'core.pictureof' | translate:{$a: fullname}" core-external-content
onError="this.src='assets/img/user-avatar.png'" (click)="gotoProfile($event)"> onError="this.src='assets/img/user-avatar.png'" (click)="gotoProfile($event)" [attr.aria-hidden]="!linkProfile"
[attr.role]="linkProfile ? 'button' : null" (keydown)="buttonAction.keyDown($event)"
(keyup)="buttonAction.keyUp($event)"
[attr.tabindex]="linkProfile ? 0 : null">
<img *ngIf="!avatarUrl" src="assets/img/user-avatar.png" [alt]="'core.pictureof' | translate:{$a: fullname}" <img *ngIf="!avatarUrl" src="assets/img/user-avatar.png" [alt]="'core.pictureof' | translate:{$a: fullname}"
(click)="gotoProfile($event)"> (click)="gotoProfile($event)" [attr.aria-hidden]="!linkProfile" [attr.role]="linkProfile ? 'button' : null"
(keydown)="buttonAction.keyDown($event)" (keyup)="buttonAction.keyUp($event)"
[attr.tabindex]="linkProfile ? 0 : null">
<span *ngIf="checkOnline && isOnline()" class="contact-status online"></span> <span *ngIf="checkOnline && isOnline()" class="contact-status online" role="status" [attr.aria-label]="'core.online' | translate">
</span>
<img *ngIf="extraIcon" [src]="extraIcon" alt="" role="presentation" class="core-avatar-extra-icon"> <img *ngIf="extraIcon" [src]="extraIcon" alt="" role="presentation" class="core-avatar-extra-icon">

View File

@ -20,6 +20,7 @@ import { CoreUtils } from '@services/utils/utils';
import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreUserProvider, CoreUserBasicData } from '@features/user/services/user'; import { CoreUserProvider, CoreUserBasicData } from '@features/user/services/user';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreAriaRoleButton } from '@classes/aria-role-button';
/** /**
* Component to display a "user avatar". * Component to display a "user avatar".
@ -36,15 +37,17 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy {
@Input() user?: CoreUserWithAvatar; @Input() user?: CoreUserWithAvatar;
// The following params will override the ones in user object. // The following params will override the ones in user object.
@Input() profileUrl?: string; @Input() profileUrl?: string;
@Input() protected linkProfile = true; // Avoid linking to the profile if wanted. @Input() linkProfile = true; // Avoid linking to the profile if wanted.
@Input() fullname?: string; @Input() fullname?: string;
@Input() protected userId?: number; // If provided or found it will be used to link the image to the profile. @Input() userId?: number; // If provided or found it will be used to link the image to the profile.
@Input() protected courseId?: number; @Input() courseId?: number;
@Input() checkOnline = false; // If want to check and show online status. @Input() checkOnline = false; // If want to check and show online status.
@Input() extraIcon?: string; // Extra icon to show near the avatar. @Input() extraIcon?: string; // Extra icon to show near the avatar.
avatarUrl?: string; avatarUrl?: string;
buttonAction: CoreUserAvatarButton;
// Variable to check if we consider this user online or not. // Variable to check if we consider this user online or not.
// @TODO: Use setting when available (see MDL-63972) so we can use site setting. // @TODO: Use setting when available (see MDL-63972) so we can use site setting.
protected timetoshowusers = 300000; // Miliseconds default. protected timetoshowusers = 300000; // Miliseconds default.
@ -52,6 +55,7 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy {
protected pictureObserver: CoreEventObserver; protected pictureObserver: CoreEventObserver;
constructor() { constructor() {
this.currentUserId = CoreSites.getCurrentSiteUserId(); this.currentUserId = CoreSites.getCurrentSiteUserId();
this.pictureObserver = CoreEvents.on( this.pictureObserver = CoreEvents.on(
@ -63,12 +67,15 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy {
}, },
CoreSites.getCurrentSiteId(), CoreSites.getCurrentSiteId(),
); );
this.buttonAction = new CoreUserAvatarButton(this);
} }
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit(): void { ngOnInit(): void {
this.setFields(); this.setFields();
} }
@ -130,19 +137,14 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy {
* @param event Click event. * @param event Click event.
*/ */
gotoProfile(event: Event): void { gotoProfile(event: Event): void {
if (!this.linkProfile || !this.userId) { if (!this.buttonAction.isAllowed()) {
return; return;
} }
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
CoreNavigator.navigateToSitePath('user', { this.buttonAction.click();
params: {
userId: this.userId,
courseId: this.courseId,
},
});
} }
/** /**
@ -154,6 +156,32 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy {
} }
/**
* Helper class to manage rol button.
*/
class CoreUserAvatarButton extends CoreAriaRoleButton<CoreUserAvatarComponent> {
/**
* @inheritdoc
*/
click(): void {
CoreNavigator.navigateToSitePath('user', {
params: {
userId: this.componentInstance.userId,
courseId: this.componentInstance.courseId,
},
});
}
/**
* @inheritdoc
*/
isAllowed(): boolean {
return this.componentInstance.linkProfile && !!this.componentInstance.userId;
}
}
/** /**
* Type with all possible formats of user. * Type with all possible formats of user.
*/ */