MOBILE-3807 user: Edit avatar only from details
parent
434a2a90f2
commit
0bbfb898d1
|
@ -18,7 +18,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@if ($core-user-hide-siteinfo) {
|
@if ($core-user-hide-siteinfo) {
|
||||||
.core-usermenu-siteinfo {
|
.core-usermenu-siteinfo {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<ion-buttons slot="start">
|
<ion-buttons slot="start">
|
||||||
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
|
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
|
||||||
</ion-buttons>
|
</ion-buttons>
|
||||||
<h1 *ngIf="title">{{ title }}</h1>
|
<h1>{{ 'core.user.details' | translate }}</h1>
|
||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content>
|
<ion-content>
|
||||||
|
@ -12,6 +12,25 @@
|
||||||
</ion-refresher>
|
</ion-refresher>
|
||||||
<core-loading [hideUntil]="userLoaded">
|
<core-loading [hideUntil]="userLoaded">
|
||||||
<ion-list *ngIf="user">
|
<ion-list *ngIf="user">
|
||||||
|
<ion-item class="ion-text-center core-user-profile-maininfo">
|
||||||
|
<core-user-avatar [user]="user" [userId]="user.id" [linkProfile]="false" [checkOnline]="true">
|
||||||
|
<ion-button
|
||||||
|
class="edit-avatar"
|
||||||
|
*ngIf="canChangeProfilePicture"
|
||||||
|
(click)="changeProfilePicture()"
|
||||||
|
[attr.aria-label]="'core.user.newpicture' | translate"
|
||||||
|
fill="clear"
|
||||||
|
color="dark"
|
||||||
|
>
|
||||||
|
<ion-icon slot="icon-only" name="fas-pen" aria-hidden="true"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
</core-user-avatar>
|
||||||
|
<ion-label>
|
||||||
|
<h2>{{ user.fullname }}</h2>
|
||||||
|
<p *ngIf="user.address"><ion-icon name="fas-map-marker-alt" [attr.aria-hidden]="true"></ion-icon> {{ user.address }}</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
<ion-item-group *ngIf="hasContact">
|
<ion-item-group *ngIf="hasContact">
|
||||||
<ion-item-divider><ion-label><h2>{{ 'core.user.contact' | translate}}</h2></ion-label></ion-item-divider>
|
<ion-item-divider><ion-label><h2>{{ 'core.user.contact' | translate}}</h2></ion-label></ion-item-divider>
|
||||||
<ion-item class="ion-text-wrap" *ngIf="user.email">
|
<ion-item class="ion-text-wrap" *ngIf="user.email">
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// 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 { Component, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { SafeUrl } from '@angular/platform-browser';
|
import { SafeUrl } from '@angular/platform-browser';
|
||||||
import { IonRefresher } from '@ionic/angular';
|
import { IonRefresher } from '@ionic/angular';
|
||||||
|
|
||||||
|
@ -20,10 +20,15 @@ import { CoreSites } from '@services/sites';
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { CoreTextUtils } from '@services/utils/text';
|
import { CoreTextUtils } from '@services/utils/text';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
import { CoreEvents } from '@singletons/events';
|
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||||
import { CoreUser, CoreUserProfile, CoreUserProvider } from '@features/user/services/user';
|
import { CoreUser, CoreUserProfile, CoreUserProvider } from '@features/user/services/user';
|
||||||
import { CoreUserHelper } from '@features/user/services/user-helper';
|
import { CoreUserHelper } from '@features/user/services/user-helper';
|
||||||
import { CoreNavigator } from '@services/navigator';
|
import { CoreNavigator } from '@services/navigator';
|
||||||
|
import { CoreIonLoadingElement } from '@classes/ion-loading';
|
||||||
|
import { CoreSite } from '@classes/site';
|
||||||
|
import { CoreFileUploaderHelper } from '@features/fileuploader/services/fileuploader-helper';
|
||||||
|
import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
||||||
|
import { Translate } from '@singletons';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays info about a user.
|
* Page that displays info about a user.
|
||||||
|
@ -31,11 +36,9 @@ import { CoreNavigator } from '@services/navigator';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'page-core-user-about',
|
selector: 'page-core-user-about',
|
||||||
templateUrl: 'about.html',
|
templateUrl: 'about.html',
|
||||||
|
styleUrls: ['about.scss'],
|
||||||
})
|
})
|
||||||
export class CoreUserAboutPage implements OnInit {
|
export class CoreUserAboutPage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
protected userId!: number;
|
|
||||||
protected siteId: string;
|
|
||||||
|
|
||||||
courseId!: number;
|
courseId!: number;
|
||||||
userLoaded = false;
|
userLoaded = false;
|
||||||
|
@ -45,20 +48,46 @@ export class CoreUserAboutPage implements OnInit {
|
||||||
title?: string;
|
title?: string;
|
||||||
formattedAddress?: string;
|
formattedAddress?: string;
|
||||||
encodedAddress?: SafeUrl;
|
encodedAddress?: SafeUrl;
|
||||||
|
canChangeProfilePicture = false;
|
||||||
|
|
||||||
|
protected userId!: number;
|
||||||
|
protected site!: CoreSite;
|
||||||
|
protected obsProfileRefreshed?: CoreEventObserver;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.siteId = CoreSites.getCurrentSiteId();
|
try {
|
||||||
|
this.site = CoreSites.getRequiredCurrentSite();
|
||||||
|
} catch (error) {
|
||||||
|
CoreDomUtils.showErrorModal(error);
|
||||||
|
CoreNavigator.back();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.obsProfileRefreshed = CoreEvents.on(CoreUserProvider.PROFILE_REFRESHED, (data) => {
|
||||||
|
if (!this.user || !data.user) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.user.email = data.user.email;
|
||||||
|
this.user.address = CoreUserHelper.formatAddress('', data.user.city, data.user.country);
|
||||||
|
}, CoreSites.getCurrentSiteId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On init.
|
* @inheritdoc
|
||||||
*
|
|
||||||
* @return Promise resolved when done.
|
|
||||||
*/
|
*/
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
this.userId = CoreNavigator.getRouteNumberParam('userId') || 0;
|
this.userId = CoreNavigator.getRouteNumberParam('userId') || 0;
|
||||||
this.courseId = CoreNavigator.getRouteNumberParam('courseId') || 0;
|
this.courseId = CoreNavigator.getRouteNumberParam('courseId') || 0;
|
||||||
|
|
||||||
|
// Allow to change the profile image only in the app profile page.
|
||||||
|
this.canChangeProfilePicture =
|
||||||
|
!this.courseId &&
|
||||||
|
this.userId == this.site.getUserId() &&
|
||||||
|
this.site.canUploadFiles() &&
|
||||||
|
!CoreUser.isUpdatePictureDisabledInSite(this.site);
|
||||||
|
|
||||||
this.fetchUser().finally(() => {
|
this.fetchUser().finally(() => {
|
||||||
this.userLoaded = true;
|
this.userLoaded = true;
|
||||||
});
|
});
|
||||||
|
@ -83,11 +112,85 @@ export class CoreUserAboutPage implements OnInit {
|
||||||
|
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.title = user.fullname;
|
this.title = user.fullname;
|
||||||
|
|
||||||
|
this.user.address = CoreUserHelper.formatAddress('', user.city, user.country);
|
||||||
|
|
||||||
|
await this.checkUserImageUpdated();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
CoreDomUtils.showErrorModalDefault(error, 'core.user.errorloaduser', true);
|
CoreDomUtils.showErrorModalDefault(error, 'core.user.errorloaduser', true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if current user image has changed.
|
||||||
|
*
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async checkUserImageUpdated(): Promise<void> {
|
||||||
|
if (!this.site || !this.site.getInfo() || !this.user) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.userId != this.site.getUserId() || !this.isUserAvatarDirty()) {
|
||||||
|
// Not current user or hasn't changed.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The current user image received is different than the one stored in site info. Assume the image was updated.
|
||||||
|
// Update the site info to get the right avatar in there.
|
||||||
|
try {
|
||||||
|
await CoreSites.updateSiteInfo(this.site.getId());
|
||||||
|
} catch {
|
||||||
|
// Cannot update site info. Assume the profile image is the right one.
|
||||||
|
CoreEvents.trigger(CoreUserProvider.PROFILE_PICTURE_UPDATED, {
|
||||||
|
userId: this.userId,
|
||||||
|
picture: this.user.profileimageurl,
|
||||||
|
}, this.site.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isUserAvatarDirty()) {
|
||||||
|
// The image is still different, this means that the good one is the one in site info.
|
||||||
|
await this.refreshUser();
|
||||||
|
} else {
|
||||||
|
// Now they're the same, send event to use the right avatar in the rest of the app.
|
||||||
|
CoreEvents.trigger(CoreUserProvider.PROFILE_PICTURE_UPDATED, {
|
||||||
|
userId: this.userId,
|
||||||
|
picture: this.user.profileimageurl,
|
||||||
|
}, this.site.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens dialog to change profile picture.
|
||||||
|
*/
|
||||||
|
async changeProfilePicture(): Promise<void> {
|
||||||
|
const maxSize = -1;
|
||||||
|
const title = Translate.instant('core.user.newpicture');
|
||||||
|
const mimetypes = CoreMimetypeUtils.getGroupMimeInfo('image', 'mimetypes');
|
||||||
|
let modal: CoreIonLoadingElement | undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await CoreFileUploaderHelper.selectAndUploadFile(maxSize, title, mimetypes);
|
||||||
|
|
||||||
|
modal = await CoreDomUtils.showModalLoading('core.sending', true);
|
||||||
|
|
||||||
|
const profileImageURL = await CoreUser.changeProfilePicture(result.itemid, this.userId, this.site.getId());
|
||||||
|
|
||||||
|
CoreEvents.trigger(CoreUserProvider.PROFILE_PICTURE_UPDATED, {
|
||||||
|
userId: this.userId,
|
||||||
|
picture: profileImageURL,
|
||||||
|
}, this.site.getId());
|
||||||
|
|
||||||
|
CoreSites.updateSiteInfo(this.site.getId());
|
||||||
|
|
||||||
|
this.refreshUser();
|
||||||
|
} catch (error) {
|
||||||
|
CoreDomUtils.showErrorModal(error);
|
||||||
|
} finally {
|
||||||
|
modal?.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refresh the user data.
|
* Refresh the user data.
|
||||||
*
|
*
|
||||||
|
@ -106,8 +209,52 @@ export class CoreUserAboutPage implements OnInit {
|
||||||
courseId: this.courseId,
|
courseId: this.courseId,
|
||||||
userId: this.userId,
|
userId: this.userId,
|
||||||
user: this.user,
|
user: this.user,
|
||||||
}, this.siteId);
|
}, this.site.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the user avatar is not up to date with site info.
|
||||||
|
*
|
||||||
|
* @return Whether the user avatar differs from site info cache.
|
||||||
|
*/
|
||||||
|
protected isUserAvatarDirty(): boolean {
|
||||||
|
if (!this.user || !this.site) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const courseAvatarUrl = this.normalizeAvatarUrl(this.user.profileimageurl);
|
||||||
|
const siteAvatarUrl = this.normalizeAvatarUrl(this.site.getInfo()?.userpictureurl);
|
||||||
|
|
||||||
|
return courseAvatarUrl !== siteAvatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize an avatar url regardless of theme.
|
||||||
|
*
|
||||||
|
* Given that the default image is the only one that can be changed per theme, any other url will stay the same. Note that
|
||||||
|
* the values returned by this function may not be valid urls, given that they are intended for string comparison.
|
||||||
|
*
|
||||||
|
* @param avatarUrl Avatar url.
|
||||||
|
* @return Normalized avatar string (may not be a valid url).
|
||||||
|
*/
|
||||||
|
protected normalizeAvatarUrl(avatarUrl?: string): string {
|
||||||
|
if (!avatarUrl) {
|
||||||
|
return 'undefined';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avatarUrl.startsWith(`${this.site?.siteUrl}/theme/image.php`)) {
|
||||||
|
return 'default';
|
||||||
|
}
|
||||||
|
|
||||||
|
return avatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.obsProfileRefreshed?.off();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
:host {
|
||||||
|
|
||||||
|
.core-user-profile-maininfo::part(native) {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
::ng-deep {
|
||||||
|
core-user-avatar {
|
||||||
|
display: block;
|
||||||
|
--core-avatar-size: var(--core-large-avatar-size);
|
||||||
|
height: calc(var(--core-avatar-size) + 16px);
|
||||||
|
|
||||||
|
img {
|
||||||
|
margin: 8px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-status {
|
||||||
|
width: 24px !important;
|
||||||
|
height: 24px !important;
|
||||||
|
right: calc(50% - 12px - var(--core-avatar-size) / 2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-avatar {
|
||||||
|
position: absolute;
|
||||||
|
right: calc(50% - 15px - var(--core-avatar-size) / 2);
|
||||||
|
bottom: -12px;
|
||||||
|
|
||||||
|
:host-context([dir="rtl"]) & {
|
||||||
|
left: 0;
|
||||||
|
right: unset;
|
||||||
|
}
|
||||||
|
&::part(native) {
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--ion-item-background);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
:host-context([dir="rtl"]) ::ng-deep core-user-avatar .edit-avatar {
|
||||||
|
left: -24px;
|
||||||
|
right: unset;
|
||||||
|
}
|
|
@ -14,20 +14,10 @@
|
||||||
<ion-list *ngIf="user && !isDeleted && isEnrolled">
|
<ion-list *ngIf="user && !isDeleted && isEnrolled">
|
||||||
<ion-item class="ion-text-center core-user-profile-maininfo">
|
<ion-item class="ion-text-center core-user-profile-maininfo">
|
||||||
<core-user-avatar [user]="user" [userId]="user.id" [linkProfile]="false" [checkOnline]="true">
|
<core-user-avatar [user]="user" [userId]="user.id" [linkProfile]="false" [checkOnline]="true">
|
||||||
<ion-button
|
|
||||||
class="edit-avatar"
|
|
||||||
*ngIf="canChangeProfilePicture"
|
|
||||||
(click)="changeProfilePicture()"
|
|
||||||
[attr.aria-label]="'core.user.newpicture' | translate"
|
|
||||||
fill="clear"
|
|
||||||
color="dark"
|
|
||||||
>
|
|
||||||
<ion-icon slot="icon-only" name="fas-pen" aria-hidden="true"></ion-icon>
|
|
||||||
</ion-button>
|
|
||||||
</core-user-avatar>
|
</core-user-avatar>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<h2>{{ user.fullname }}</h2>
|
<h2>{{ user.fullname }}</h2>
|
||||||
<p *ngIf="user.address">{{ user.address }}</p>
|
<p *ngIf="user.address"><ion-icon name="fas-map-marker-alt" [attr.aria-hidden]="true"></ion-icon> {{ user.address }}</p>
|
||||||
<p *ngIf="rolesFormatted" class="ion-text-wrap">
|
<p *ngIf="rolesFormatted" class="ion-text-wrap">
|
||||||
<strong>{{ 'core.user.roles' | translate}}</strong>{{'core.labelsep' | translate}}
|
<strong>{{ 'core.user.roles' | translate}}</strong>{{'core.labelsep' | translate}}
|
||||||
{{ rolesFormatted }}
|
{{ rolesFormatted }}
|
||||||
|
|
|
@ -19,8 +19,6 @@ import { Subscription } from 'rxjs';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite } from '@classes/site';
|
||||||
import { CoreSites } from '@services/sites';
|
import { CoreSites } from '@services/sites';
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
|
||||||
import { Translate } from '@singletons';
|
|
||||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||||
import {
|
import {
|
||||||
CoreUser,
|
CoreUser,
|
||||||
|
@ -29,8 +27,6 @@ import {
|
||||||
} from '@features/user/services/user';
|
} from '@features/user/services/user';
|
||||||
import { CoreUserHelper } from '@features/user/services/user-helper';
|
import { CoreUserHelper } from '@features/user/services/user-helper';
|
||||||
import { CoreUserDelegate, CoreUserDelegateService, CoreUserProfileHandlerData } from '@features/user/services/user-delegate';
|
import { CoreUserDelegate, CoreUserDelegateService, CoreUserProfileHandlerData } from '@features/user/services/user-delegate';
|
||||||
import { CoreFileUploaderHelper } from '@features/fileuploader/services/fileuploader-helper';
|
|
||||||
import { CoreIonLoadingElement } from '@classes/ion-loading';
|
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
import { CoreNavigator } from '@services/navigator';
|
import { CoreNavigator } from '@services/navigator';
|
||||||
import { CoreCourses } from '@features/courses/services/courses';
|
import { CoreCourses } from '@features/courses/services/courses';
|
||||||
|
@ -54,7 +50,6 @@ export class CoreUserProfilePage implements OnInit, OnDestroy {
|
||||||
title?: string;
|
title?: string;
|
||||||
isDeleted = false;
|
isDeleted = false;
|
||||||
isEnrolled = true;
|
isEnrolled = true;
|
||||||
canChangeProfilePicture = false;
|
|
||||||
rolesFormatted?: string;
|
rolesFormatted?: string;
|
||||||
actionHandlers: CoreUserProfileHandlerData[] = [];
|
actionHandlers: CoreUserProfileHandlerData[] = [];
|
||||||
newPageHandlers: CoreUserProfileHandlerData[] = [];
|
newPageHandlers: CoreUserProfileHandlerData[] = [];
|
||||||
|
@ -72,7 +67,7 @@ export class CoreUserProfilePage implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On init.
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
@ -91,13 +86,6 @@ export class CoreUserProfilePage implements OnInit, OnDestroy {
|
||||||
this.courseId = undefined;
|
this.courseId = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow to change the profile image only in the app profile page.
|
|
||||||
this.canChangeProfilePicture =
|
|
||||||
!this.courseId &&
|
|
||||||
this.userId == this.site.getUserId() &&
|
|
||||||
this.site.canUploadFiles() &&
|
|
||||||
!CoreUser.isUpdatePictureDisabledInSite(this.site);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.fetchUser();
|
await this.fetchUser();
|
||||||
|
|
||||||
|
@ -154,84 +142,12 @@ export class CoreUserProfilePage implements OnInit, OnDestroy {
|
||||||
this.isLoadingHandlers = !CoreUserDelegate.areHandlersLoaded(user.id);
|
this.isLoadingHandlers = !CoreUserDelegate.areHandlersLoaded(user.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.checkUserImageUpdated();
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Error is null for deleted users, do not show the modal.
|
// Error is null for deleted users, do not show the modal.
|
||||||
CoreDomUtils.showErrorModal(error);
|
CoreDomUtils.showErrorModal(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if current user image has changed.
|
|
||||||
*
|
|
||||||
* @return Promise resolved when done.
|
|
||||||
*/
|
|
||||||
protected async checkUserImageUpdated(): Promise<void> {
|
|
||||||
if (!this.site || !this.site.getInfo() || !this.user) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.userId != this.site.getUserId() || !this.isUserAvatarDirty()) {
|
|
||||||
// Not current user or hasn't changed.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The current user image received is different than the one stored in site info. Assume the image was updated.
|
|
||||||
// Update the site info to get the right avatar in there.
|
|
||||||
try {
|
|
||||||
await CoreSites.updateSiteInfo(this.site.getId());
|
|
||||||
} catch {
|
|
||||||
// Cannot update site info. Assume the profile image is the right one.
|
|
||||||
CoreEvents.trigger(CoreUserProvider.PROFILE_PICTURE_UPDATED, {
|
|
||||||
userId: this.userId,
|
|
||||||
picture: this.user.profileimageurl,
|
|
||||||
}, this.site.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isUserAvatarDirty()) {
|
|
||||||
// The image is still different, this means that the good one is the one in site info.
|
|
||||||
await this.refreshUser();
|
|
||||||
} else {
|
|
||||||
// Now they're the same, send event to use the right avatar in the rest of the app.
|
|
||||||
CoreEvents.trigger(CoreUserProvider.PROFILE_PICTURE_UPDATED, {
|
|
||||||
userId: this.userId,
|
|
||||||
picture: this.user.profileimageurl,
|
|
||||||
}, this.site.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens dialog to change profile picture.
|
|
||||||
*/
|
|
||||||
async changeProfilePicture(): Promise<void> {
|
|
||||||
const maxSize = -1;
|
|
||||||
const title = Translate.instant('core.user.newpicture');
|
|
||||||
const mimetypes = CoreMimetypeUtils.getGroupMimeInfo('image', 'mimetypes');
|
|
||||||
let modal: CoreIonLoadingElement | undefined;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await CoreFileUploaderHelper.selectAndUploadFile(maxSize, title, mimetypes);
|
|
||||||
|
|
||||||
modal = await CoreDomUtils.showModalLoading('core.sending', true);
|
|
||||||
|
|
||||||
const profileImageURL = await CoreUser.changeProfilePicture(result.itemid, this.userId, this.site.getId());
|
|
||||||
|
|
||||||
CoreEvents.trigger(CoreUserProvider.PROFILE_PICTURE_UPDATED, {
|
|
||||||
userId: this.userId,
|
|
||||||
picture: profileImageURL,
|
|
||||||
}, this.site.getId());
|
|
||||||
|
|
||||||
CoreSites.updateSiteInfo(this.site.getId());
|
|
||||||
|
|
||||||
this.refreshUser();
|
|
||||||
} catch (error) {
|
|
||||||
CoreDomUtils.showErrorModal(error);
|
|
||||||
} finally {
|
|
||||||
modal?.dismiss();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refresh the user.
|
* Refresh the user.
|
||||||
*
|
*
|
||||||
|
@ -285,48 +201,11 @@ export class CoreUserProfilePage implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page destroyed.
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.subscription?.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
this.obsProfileRefreshed.off();
|
this.obsProfileRefreshed.off();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether the user avatar is not up to date with site info.
|
|
||||||
*
|
|
||||||
* @return Whether the user avatar differs from site info cache.
|
|
||||||
*/
|
|
||||||
private isUserAvatarDirty(): boolean {
|
|
||||||
if (!this.user || !this.site) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const courseAvatarUrl = this.normalizeAvatarUrl(this.user.profileimageurl);
|
|
||||||
const siteAvatarUrl = this.normalizeAvatarUrl(this.site.getInfo()?.userpictureurl);
|
|
||||||
|
|
||||||
return courseAvatarUrl !== siteAvatarUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalize an avatar url regardless of theme.
|
|
||||||
*
|
|
||||||
* Given that the default image is the only one that can be changed per theme, any other url will stay the same. Note that
|
|
||||||
* the values returned by this function may not be valid urls, given that they are intended for string comparison.
|
|
||||||
*
|
|
||||||
* @param avatarUrl Avatar url.
|
|
||||||
* @return Normalized avatar string (may not be a valid url).
|
|
||||||
*/
|
|
||||||
private normalizeAvatarUrl(avatarUrl?: string): string {
|
|
||||||
if (!avatarUrl) {
|
|
||||||
return 'undefined';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (avatarUrl.startsWith(`${this.site?.siteUrl}/theme/image.php`)) {
|
|
||||||
return 'default';
|
|
||||||
}
|
|
||||||
|
|
||||||
return avatarUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,21 +18,6 @@
|
||||||
height: 24px !important;
|
height: 24px !important;
|
||||||
right: calc(50% - 12px - var(--core-avatar-size) / 2) !important;
|
right: calc(50% - 12px - var(--core-avatar-size) / 2) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-avatar {
|
|
||||||
position: absolute;
|
|
||||||
right: calc(50% - 15px - var(--core-avatar-size) / 2);
|
|
||||||
bottom: -12px;
|
|
||||||
|
|
||||||
:host-context([dir="rtl"]) & {
|
|
||||||
left: 0;
|
|
||||||
right: unset;
|
|
||||||
}
|
|
||||||
&::part(native) {
|
|
||||||
border-radius: 50%;
|
|
||||||
background: var(--ion-item-background);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue