Skip to content

Commit

Permalink
Merge pull request #4022 from crazyserver/MOBILE-4579
Browse files Browse the repository at this point in the history
Mobile 4579
  • Loading branch information
dpalou authored Apr 29, 2024
2 parents 52278a7 + 33d6d16 commit eaedb08
Show file tree
Hide file tree
Showing 17 changed files with 153 additions and 49 deletions.
12 changes: 12 additions & 0 deletions src/addons/messages/pages/discussion/discussion.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,21 @@
padding: 0px;

.core-bar-button-image {
--userpicture-padding: 4px;
@include margin-horizontal(null, 6px);
}

// Group avatar.
img.core-bar-button-image {
padding: var(--userpicture-padding);
width: var(--core-header-toolbar-button-image-size);
height: var(--core-header-toolbar-button-image-size);
max-width: var(--core-header-toolbar-button-image-size);
max-height: var(--core-header-toolbar-button-image-size);
border-radius: var(--core-avatar-radius);
display: block;
}

core-format-text {
overflow: hidden;
text-overflow: ellipsis;
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion src/addons/notifications/notifications.scss
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@
img {
width: var(--icon-size);
height: var(--icon-size);
padding: 4px;
}
ion-icon {
font-size: var(--icon-size);
}
padding: 0px;
background: var(--background-color);
border-radius: var(--mdl-shape-borderRadius-xs);
border-radius: var(--core-avatar-radius);
@include margin(6px, 8px, 6px, 0px);
}

Expand Down
11 changes: 7 additions & 4 deletions src/addons/notifications/services/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export class AddonNotificationsProvider {
notification.notif = 1;
notification.read = notification.timeread > 0;

if (typeof notification.customdata == 'string') {
if (typeof notification.customdata === 'string') {
notification.customdata = CoreTextUtils.parseJSON<Record<string, string|number>>(notification.customdata, {});
}

Expand All @@ -142,9 +142,6 @@ export class AddonNotificationsProvider {
}
}

const imgUrl = notification.customdata?.notificationpictureurl || notification.customdata?.notificationiconurl;
notification.imgUrl = imgUrl ? String(imgUrl) : undefined;

if (notification.useridfrom > 0) {
// Try to get the profile picture of the user.
try {
Expand All @@ -155,6 +152,12 @@ export class AddonNotificationsProvider {
} catch {
// Error getting user. This can happen if device is offline or the user is deleted.
}
} else {
// Do not assign avatar for newlogin notifications.
if (notification.eventtype !== 'newlogin') {
const imgUrl = notification.customdata?.notificationpictureurl || notification.customdata?.notificationiconurl;
notification.imgUrl = imgUrl ? String(imgUrl) : undefined;
}
}

return notification;
Expand Down
6 changes: 3 additions & 3 deletions src/core/components/tests/user-avatar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ describe('CoreUserAvatarComponent', () => {
// Assert.
expect(nativeElement.innerHTML.trim()).not.toHaveLength(0);

const image = nativeElement.querySelector('img');
expect(image).not.toBeNull();
expect(image?.src).toEqual(document.location.href + 'assets/img/user-avatar.png');
const initials = nativeElement.querySelector('.userinitials');
expect(initials).not.toBeNull();
expect(initials?.getAttribute('data-initials')?.trim()).toEqual('UNK');
});

});
15 changes: 5 additions & 10 deletions src/core/components/user-avatar/core-user-avatar.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,15 @@
</ng-container>
<ng-container *ngIf="!avatarUrl && initials">
<div class="userinitials" *ngIf="linkProfile" [attr.aria-label]="'core.pictureof' | translate:{$a: fullname}"
(ariaButtonClick)="gotoProfile($event)">
{{ initials }}
[title]="'core.pictureof' | translate:{$a: fullname}" (ariaButtonClick)="gotoProfile($event)" [attr.data-initials]="initials">
</div>

<div class="userinitials" *ngIf="!linkProfile" [attr.aria-label]="'core.pictureof' | translate:{$a: fullname}" aria-hidden="true">
{{ initials }}
<div class="userinitials" *ngIf="!linkProfile" [attr.aria-label]="'core.pictureof' | translate:{$a: fullname}"
[title]="'core.pictureof' | translate:{$a: fullname}" role="img" [attr.data-initials]="initials" aria-hidden="true">
</div>
</ng-container>
<ng-container *ngIf="!avatarUrl && !initials">
<img class="userpicture" *ngIf="linkProfile" src="assets/img/user-avatar.png" [alt]="'core.pictureof' | translate:{$a: fullname}"
(ariaButtonClick)="gotoProfile($event)">

<img class="userpicture" *ngIf="!linkProfile" src="assets/img/user-avatar.png" [alt]="'core.pictureof' | translate:{$a: fullname}"
aria-hidden="true">
<ng-container *ngIf="!avatarUrl && !initials"><!-- During loading -->
<img class="userpicture_loading" src="assets/img/user-avatar.png" role="presentation" aria-hidden="true" alt="">
</ng-container>

<span *ngIf="checkOnline && isOnline()" class="contact-status online" role="status" [attr.aria-label]="'core.online' | translate">
Expand Down
12 changes: 7 additions & 5 deletions src/core/components/user-avatar/user-avatar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,8 @@
height: var(--core-header-toolbar-button-image-size);
max-width: var(--core-header-toolbar-button-image-size);
max-height: var(--core-header-toolbar-button-image-size);
border-radius: 50%;
border-radius: var(--core-avatar-radius);
display: block;

img {
border-radius: 50%;
}
}

.contact-status {
Expand All @@ -83,8 +79,14 @@
font-weight: normal;
width: calc(var(--core-avatar-size) - var(--userpicture-padding) - var(--userpicture-padding));
height: calc(var(--core-avatar-size) - var(--userpicture-padding) - var(--userpicture-padding));
min-height: 0px;
min-width: 0px;
font-size: calc(var(--core-avatar-size)*0.3);
margin: var(--userpicture-padding);

&::after {
content: attr(data-initials);
}
}

&.large-avatar .userinitials {
Expand Down
16 changes: 10 additions & 6 deletions src/core/components/user-avatar/user-avatar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy {
/**
* Set fields from user.
*/
protected setFields(): void {
protected async setFields(): Promise<void> {
const profileUrl = this.profileUrl || (this.user && (this.user.profileimageurl || this.user.userprofileimageurl ||
this.user.userpictureurl || this.user.profileimageurlsmall || (this.user.urls && this.user.urls.profileimage)));

Expand All @@ -124,16 +124,20 @@ export class CoreUserAvatarComponent implements OnInit, OnChanges, OnDestroy {

this.fullname = this.fullname || (this.user && (this.user.fullname || this.user.userfullname));

if (this.user) {
this.initials = CoreUserHelper.getUserInitials(this.user);
}

if (this.initials && this.avatarUrl && CoreUrlUtils.isThemeImageUrl(this.avatarUrl)) {
if (this.avatarUrl && CoreUrlUtils.isThemeImageUrl(this.avatarUrl)) {
this.avatarUrl = undefined;
}

this.userId = this.userId || (this.user && (this.user.userid || this.user.id));
this.courseId = this.courseId || (this.user && this.user.courseid);

this.initials =
await CoreUserHelper.getUserInitialsFromParts({
firstname: this.user?.firstname,
lastname: this.user?.lastname,
fullname: this.fullname,
userId: this.userId,
});
}

/**
Expand Down
37 changes: 35 additions & 2 deletions src/core/features/user/services/user-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites';

import { makeSingleton, Translate } from '@singletons';
import { CoreUserProfile, CoreUserRole } from './user';
import { CoreUser, CoreUserProfile, CoreUserRole } from './user';

/**
* Service that provides some features regarding users information.
Expand Down Expand Up @@ -145,7 +145,8 @@ export class CoreUserHelperProvider {
* Get the user initials.
*
* @param user User object.
* @returns Promise resolved with the user data.
* @returns User initials.
* @deprecated since 4.4. Use getUserInitialsFromParts instead.
*/
getUserInitials(user: Partial<CoreUserProfile>): string {
if (!user.firstname && !user.lastname) {
Expand All @@ -156,6 +157,36 @@ export class CoreUserHelperProvider {
return (user.firstname?.charAt(0) || '') + (user.lastname?.charAt(0) || '');
}

/**
* Get the user initials.
*
* @param parts User name parts. Containing firstname, lastname, fullname and userId.
* @returns User initials.
*/
async getUserInitialsFromParts(parts: CoreUserNameParts): Promise<string> {
if (!parts.firstname && !parts.lastname) {
if (!parts.fullname && parts.userId) {
const user = await CoreUser.getProfile(parts.userId, undefined, true);
parts.fullname = user.fullname || '';
}

if (parts.fullname) {
const split = parts.fullname.split(' ');

parts.firstname = split[0];
if (split.length > 1) {
parts.lastname = split[split.length - 1];
}
}
}

if (!parts.firstname && !parts.lastname) {
return 'UNK';
}

return (parts.firstname?.charAt(0) || '') + (parts.lastname?.charAt(0) || '');
}

/**
* Translates legacy timezone names.
*
Expand All @@ -169,3 +200,5 @@ export class CoreUserHelperProvider {
}

export const CoreUserHelper = makeSingleton(CoreUserHelperProvider);

type CoreUserNameParts = { firstname?: string; lastname?: string; fullname?: string; userId?: number };
8 changes: 2 additions & 6 deletions src/core/features/user/services/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import { CoreEvents, CoreEventSiteData, CoreEventUserDeletedData, CoreEventUserS
import { CoreStatusWithWarningsWSResponse, CoreWSExternalWarning } from '@services/ws';
import { CoreError } from '@classes/errors/error';
import { USERS_TABLE_NAME, CoreUserDBRecord } from './database/user';
import { CoreUserHelper } from './user-helper';
import { CoreUrlUtils } from '@services/utils/url';
import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site';
import { CoreConstants } from '@/core/constants';
Expand Down Expand Up @@ -669,11 +668,8 @@ export class CoreUserProvider {
}

// Do not prefetch when initials are set and image is default.
if ('firstname' in entry || 'lastname' in entry) {
const initials = CoreUserHelper.getUserInitials(entry);
if (initials && imageUrl && CoreUrlUtils.isThemeImageUrl(imageUrl)) {
return;
}
if (imageUrl && CoreUrlUtils.isThemeImageUrl(imageUrl)) {
return;
}

treated[imageUrl] = true;
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 70 additions & 0 deletions src/core/features/user/tests/user-helper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// (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 { CoreUserHelper } from '../services/user-helper';

describe('getUserInitialsFromParts', () => {
it('should return initials based on firstname and lastname', async () => {
const parts = {
firstname: 'John',
lastname: 'Doe',
fullname: '',
userId: 123,
};

const result = await CoreUserHelper.getUserInitialsFromParts(parts);

expect(result).toEqual('JD');
});

it('should return initials based on fullname if firstname and lastname are missing', async () => {
let parts = {
firstname: '',
lastname: '',
fullname: 'John Doe',
userId: 123,
};

let result = await CoreUserHelper.getUserInitialsFromParts(parts);

expect(result).toEqual('JD');

parts = {
firstname: '',
lastname: '',
fullname: 'John Fitzgerald Doe',
userId: 123,
};

result = await CoreUserHelper.getUserInitialsFromParts(parts);

expect(result).toEqual('JD');
});

it('should return UNK string if empty parts', async () => {
const parts = {
firstname: '',
lastname: '',
fullname: '',
};

let result = await CoreUserHelper.getUserInitialsFromParts(parts);

expect(result).toEqual('UNK');

result = await CoreUserHelper.getUserInitialsFromParts({});

expect(result).toEqual('UNK');
});
});
12 changes: 0 additions & 12 deletions src/theme/theme.base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -636,18 +636,6 @@ ion-content.limited-width > :not([slot]) {
min-height: 100%;
}

ion-toolbar h1 img.core-bar-button-image,
ion-toolbar h1 .core-bar-button-image img {
padding: 4px;
--userpicture-padding: 4px;
width: var(--core-header-toolbar-button-image-size);
height: var(--core-header-toolbar-button-image-size);
max-width: var(--core-header-toolbar-button-image-size);
max-height: var(--core-header-toolbar-button-image-size);
border-radius: 50%;
display: block;
}

// Radio.
ion-radio,
input[type=radio],
Expand Down

0 comments on commit eaedb08

Please sign in to comment.