From 289e8e1ce7a453be76ff6828c5df2ef2f0314220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Wed, 20 Oct 2021 15:15:12 +0200 Subject: [PATCH 01/13] MOBILE-3807 menu: Change hamburger to meatballs more menu icon --- src/core/features/mainmenu/pages/menu/menu.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/features/mainmenu/pages/menu/menu.html b/src/core/features/mainmenu/pages/menu/menu.html index b888971dc..893737e2f 100644 --- a/src/core/features/mainmenu/pages/menu/menu.html +++ b/src/core/features/mainmenu/pages/menu/menu.html @@ -18,7 +18,7 @@ - + {{ 'core.more' | translate }} From 81fdd00902dd460b77299b9667cf38bbc101e95a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Fri, 22 Oct 2021 12:13:43 +0200 Subject: [PATCH 02/13] MOBILE-3807 mainmenu: Add new user menu modal --- .../mainmenu/components/components.module.ts | 33 ++++ .../user-menu-button/user-menu-button.html | 9 ++ .../user-menu-button/user-menu-button.ts | 55 +++++++ .../components/user-menu/user-menu.html | 63 ++++++++ .../components/user-menu/user-menu.scss | 38 +++++ .../components/user-menu/user-menu.ts | 153 ++++++++++++++++++ .../features/mainmenu/pages/more/more.html | 147 ++++++++--------- .../mainmenu/pages/more/more.module.ts | 2 + .../features/mainmenu/pages/more/more.scss | 18 --- src/core/features/mainmenu/pages/more/more.ts | 33 +--- src/core/features/user/lang.json | 2 + src/theme/globals.variables.scss | 10 +- 12 files changed, 431 insertions(+), 132 deletions(-) create mode 100644 src/core/features/mainmenu/components/components.module.ts create mode 100644 src/core/features/mainmenu/components/user-menu-button/user-menu-button.html create mode 100644 src/core/features/mainmenu/components/user-menu-button/user-menu-button.ts create mode 100644 src/core/features/mainmenu/components/user-menu/user-menu.html create mode 100644 src/core/features/mainmenu/components/user-menu/user-menu.scss create mode 100644 src/core/features/mainmenu/components/user-menu/user-menu.ts diff --git a/src/core/features/mainmenu/components/components.module.ts b/src/core/features/mainmenu/components/components.module.ts new file mode 100644 index 000000000..9a688ae75 --- /dev/null +++ b/src/core/features/mainmenu/components/components.module.ts @@ -0,0 +1,33 @@ +// (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 { CoreSharedModule } from '@/core/shared.module'; +import { CoreMainMenuUserButtonComponent } from './user-menu-button/user-menu-button'; +import { CoreMainMenuUserMenuComponent } from './user-menu/user-menu'; + +@NgModule({ + declarations: [ + CoreMainMenuUserButtonComponent, + CoreMainMenuUserMenuComponent, + ], + imports: [ + CoreSharedModule, + ], + exports: [ + CoreMainMenuUserButtonComponent, + CoreMainMenuUserMenuComponent, + ], +}) +export class CoreMainMenuComponentsModule {} diff --git a/src/core/features/mainmenu/components/user-menu-button/user-menu-button.html b/src/core/features/mainmenu/components/user-menu-button/user-menu-button.html new file mode 100644 index 000000000..1d9f61bde --- /dev/null +++ b/src/core/features/mainmenu/components/user-menu-button/user-menu-button.html @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/src/core/features/mainmenu/components/user-menu-button/user-menu-button.ts b/src/core/features/mainmenu/components/user-menu-button/user-menu-button.ts new file mode 100644 index 000000000..f8d3291f7 --- /dev/null +++ b/src/core/features/mainmenu/components/user-menu-button/user-menu-button.ts @@ -0,0 +1,55 @@ +// (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 } from '@angular/core'; +import { CoreSiteInfo } from '@classes/site'; +import { CoreSites } from '@services/sites'; +import { CoreDomUtils } from '@services/utils/dom'; +import { CoreMainMenuUserMenuComponent } from '../user-menu/user-menu'; + +/** + * Component to display an avatar on the header to open user menu. + * + * Example: + */ +@Component({ + selector: 'core-user-menu-button', + templateUrl: 'user-menu-button.html', +}) +export class CoreMainMenuUserButtonComponent { + + siteInfo?: CoreSiteInfo; + + constructor() { + const currentSite = CoreSites.getRequiredCurrentSite(); + + // @TODO: Check if the page where I currently am is at level 0. + this.siteInfo = currentSite.getInfo(); + } + + /** + * Open User menu + * + * @param event Click event. + */ + openUserMenu(event: Event): void { + event.preventDefault(); + event.stopPropagation(); + + CoreDomUtils.openModal({ + component: CoreMainMenuUserMenuComponent, + }); + } + +} diff --git a/src/core/features/mainmenu/components/user-menu/user-menu.html b/src/core/features/mainmenu/components/user-menu/user-menu.html new file mode 100644 index 000000000..8c89de390 --- /dev/null +++ b/src/core/features/mainmenu/components/user-menu/user-menu.html @@ -0,0 +1,63 @@ + + + + + + + +

+ {{'core.user.account' | translate}} +

+
+
+ + + + + + + + +

{{ 'core.user.details' | translate }}

+
+
+ + + + + + + + +

{{ handler.title | translate }}

+
+
+ + + + +

{{ 'core.settings.preferences' | translate }}

+
+
+
+
+
+ + + + {{ logoutLabel | translate }} + + \ No newline at end of file diff --git a/src/core/features/mainmenu/components/user-menu/user-menu.scss b/src/core/features/mainmenu/components/user-menu/user-menu.scss new file mode 100644 index 000000000..7466e6dd8 --- /dev/null +++ b/src/core/features/mainmenu/components/user-menu/user-menu.scss @@ -0,0 +1,38 @@ +@import "~theme/globals"; + +: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; + } + } + } +} + + +@if ($core-user-hide-siteinfo) { + .core-usermenu-siteinfo { + display: none; + } +} + +@if ($core-user-hide-sitename) { + .core-usermenu-sitename { + display: none; + } +} + +@if ($core-user-hide-siteurl) { + .core-usermenu-siteurl { + display: none; + } +} diff --git a/src/core/features/mainmenu/components/user-menu/user-menu.ts b/src/core/features/mainmenu/components/user-menu/user-menu.ts new file mode 100644 index 000000000..71c8cc9f7 --- /dev/null +++ b/src/core/features/mainmenu/components/user-menu/user-menu.ts @@ -0,0 +1,153 @@ +// (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, OnDestroy, OnInit } from '@angular/core'; +import { CoreSiteInfo } from '@classes/site'; +import { CoreLoginHelper } from '@features/login/services/login-helper'; +import { CoreUser, CoreUserProfile } from '@features/user/services/user'; +import { CoreUserProfileHandlerData, CoreUserDelegate, CoreUserDelegateService } from '@features/user/services/user-delegate'; +import { CoreNavigator } from '@services/navigator'; +import { CoreSites } from '@services/sites'; +import { ModalController } from '@singletons'; +import { Subscription } from 'rxjs'; + +/** + * Component to display a user menu. + */ +@Component({ + selector: 'core-main-menu-user-menu', + templateUrl: 'user-menu.html', + styleUrls: ['user-menu.scss'], +}) +export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy { + + siteInfo?: CoreSiteInfo; + siteName?: string; + logoutLabel = 'core.mainmenu.changesite'; + siteUrl?: string; + handlers: CoreUserProfileHandlerData[] = []; + handlersLoaded = false; + user?: CoreUserProfile; + + protected subscription!: Subscription; + + /** + * @inheritdoc + */ + async ngOnInit(): Promise { + + const currentSite = CoreSites.getRequiredCurrentSite(); + + this.siteInfo = currentSite.getInfo(); + this.siteName = currentSite.getSiteName(); + this.siteUrl = currentSite.getURL(); + this.logoutLabel = CoreLoginHelper.getLogoutLabel(currentSite); + + // Load the handlers. + if (this.siteInfo) { + this.user = await CoreUser.getProfile(this.siteInfo.userid); + + this.subscription = CoreUserDelegate.getProfileHandlersFor(this.user).subscribe((handlers) => { + if (!handlers || !this.user) { + return; + } + + this.handlers = []; + handlers.forEach((handler) => { + if (handler.type == CoreUserDelegateService.TYPE_NEW_PAGE) { + this.handlers.push(handler.data); + } + }); + + this.handlersLoaded = CoreUserDelegate.areHandlersLoaded(this.user.id); + }); + + } + } + + /** + * Opens User profile page. + * + * @param event Click event. + */ + async openUserProfile(event: Event): Promise { + if (!this.siteInfo) { + return; + } + + await this.close(event); + + CoreNavigator.navigateToSitePath('user/about', { + params: { + userId: this.siteInfo.userid, + }, + }); + } + + /** + * Opens preferences. + * + * @param event Click event. + */ + async openPreferences(event: Event): Promise { + await this.close(event); + + CoreNavigator.navigateToSitePath('preferences'); + } + + /** + * A handler was clicked. + * + * @param event Click event. + * @param handler Handler that was clicked. + */ + async handlerClicked(event: Event, handler: CoreUserProfileHandlerData): Promise { + if (!this.user) { + return; + } + + await this.close(event); + + handler.action(event, this.user); + } + + /** + * Logout the user. + * + * @param event Click event + */ + async logout(event: Event): Promise { + await this.close(event); + + CoreSites.logout(); + } + + /** + * Close modal. + */ + async close(event: Event): Promise { + event.preventDefault(); + event.stopPropagation(); + + await ModalController.dismiss(); + } + + /** + * @inheritdoc + */ + ngOnDestroy(): void { + this.subscription?.unsubscribe(); + } + +} diff --git a/src/core/features/mainmenu/pages/more/more.html b/src/core/features/mainmenu/pages/more/more.html index 3f3fcff68..69a4e046c 100644 --- a/src/core/features/mainmenu/pages/more/more.html +++ b/src/core/features/mainmenu/pages/more/more.html @@ -5,97 +5,76 @@

+ + + + - - - - + + + + + + + +

{{ handler.title | translate}}

+
+ + + {{ handler.badgeA11yText | translate: {$a : handler.badge } }} + + + +
+ + + -

{{siteInfo.fullname}}

-

- - -

-

{{ siteUrl }}

+

{{item.label}}

- - - - - - + + -

{{ handler.title | translate}}

-
- - - {{ handler.badgeA11yText | translate: {$a : handler.badge } }} - - - -
- - - - -

{{item.label}}

-
-
- - - -

{{item.label}}

-
-
-
- - - -

{{ 'core.scanqr' | translate }}

+

{{item.label}}

- - - -

{{ 'core.mainmenu.website' | translate }}

-
-
- - - -

{{ 'core.mainmenu.help' | translate }}

-
-
- - - -

{{ 'core.settings.preferences' | translate }}

-
-
- - - -

{{ logoutLabel | translate }}

-
-
- - - - -

{{ 'core.settings.appsettings' | translate }}

-
-
-
-
+ + + + +

{{ 'core.scanqr' | translate }}

+
+
+ + + +

{{ 'core.mainmenu.website' | translate }}

+
+
+ + + +

{{ 'core.mainmenu.help' | translate }}

+
+
+
+ + + + +

{{ 'core.settings.appsettings' | translate }}

+
+
+
diff --git a/src/core/features/mainmenu/pages/more/more.module.ts b/src/core/features/mainmenu/pages/more/more.module.ts index 1999f1b49..770fa3d9b 100644 --- a/src/core/features/mainmenu/pages/more/more.module.ts +++ b/src/core/features/mainmenu/pages/more/more.module.ts @@ -19,10 +19,12 @@ import { CoreSharedModule } from '@/core/shared.module'; import { CoreMainMenuMorePage } from './more'; import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module'; import { CoreMainMenuProvider } from '@features/mainmenu/services/mainmenu'; +import { CoreMainMenuComponentsModule } from '@features/mainmenu/components/components.module'; @NgModule({ imports: [ CoreSharedModule, + CoreMainMenuComponentsModule, ], providers: [ { diff --git a/src/core/features/mainmenu/pages/more/more.scss b/src/core/features/mainmenu/pages/more/more.scss index c5b62e00a..9b1b544d2 100644 --- a/src/core/features/mainmenu/pages/more/more.scss +++ b/src/core/features/mainmenu/pages/more/more.scss @@ -17,21 +17,3 @@ ion-item { color: var(--core-more-icon, inherit); } } - -@if ($core-more-hide-siteinfo) { - .core-moremenu-siteinfo { - display: none; - } -} - -@if ($core-more-hide-sitename) { - .core-moremenu-sitename { - display: none; - } -} - -@if ($core-more-hide-siteurl) { - .core-moremenu-siteurl { - display: none; - } -} diff --git a/src/core/features/mainmenu/pages/more/more.ts b/src/core/features/mainmenu/pages/more/more.ts index 733c8a0aa..a8b93f285 100644 --- a/src/core/features/mainmenu/pages/more/more.ts +++ b/src/core/features/mainmenu/pages/more/more.ts @@ -18,7 +18,6 @@ import { Subscription } from 'rxjs'; import { CoreSites } from '@services/sites'; import { CoreUtils } from '@services/utils/utils'; import { CoreSiteInfo } from '@classes/site'; -import { CoreLoginHelper } from '@features/login/services/login-helper'; import { CoreMainMenuDelegate, CoreMainMenuHandlerData } from '../../services/mainmenu-delegate'; import { CoreMainMenu, CoreMainMenuCustomItem } from '../../services/mainmenu'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; @@ -39,19 +38,16 @@ import { Translate } from '@singletons'; export class CoreMainMenuMorePage implements OnInit, OnDestroy { handlers?: CoreMainMenuHandlerData[]; - allHandlers?: CoreMainMenuHandlerData[]; handlersLoaded = false; siteInfo?: CoreSiteInfo; siteName?: string; - logoutLabel = 'core.mainmenu.changesite'; showScanQR: boolean; showWeb?: boolean; showHelp?: boolean; docsUrl?: string; customItems?: CoreMainMenuCustomItem[]; - siteUrl?: string; - loggedOut = false; + protected allHandlers?: CoreMainMenuHandlerData[]; protected subscription!: Subscription; protected langObserver: CoreEventObserver; protected updateSiteObserver: CoreEventObserver; @@ -70,7 +66,7 @@ export class CoreMainMenuMorePage implements OnInit, OnDestroy { } /** - * Initialize component. + * @inheritdoc */ ngOnInit(): void { // Load the handlers. @@ -84,7 +80,7 @@ export class CoreMainMenuMorePage implements OnInit, OnDestroy { } /** - * Page destroyed. + * @inheritdoc */ ngOnDestroy(): void { window.removeEventListener('resize', this.initHandlers.bind(this)); @@ -116,16 +112,10 @@ export class CoreMainMenuMorePage implements OnInit, OnDestroy { * Load the site info required by the view. */ protected async loadSiteInfo(): Promise { - const currentSite = CoreSites.getCurrentSite(); - - if (!currentSite) { - return; - } + const currentSite = CoreSites.getRequiredCurrentSite(); this.siteInfo = currentSite.getInfo(); this.siteName = currentSite.getSiteName(); - this.siteUrl = currentSite.getURL(); - this.logoutLabel = CoreLoginHelper.getLogoutLabel(currentSite); this.showWeb = !currentSite.isFeatureDisabled('CoreMainMenuDelegate_website'); this.showHelp = !currentSite.isFeatureDisabled('CoreMainMenuDelegate_help'); @@ -154,13 +144,6 @@ export class CoreMainMenuMorePage implements OnInit, OnDestroy { CoreNavigator.navigateToSitePath('viewer/iframe', { params: { title: item.label, url: item.url } }); } - /** - * Open preferences. - */ - openPreferences(): void { - CoreNavigator.navigateToSitePath('preferences'); - } - /** * Open settings. */ @@ -200,12 +183,4 @@ export class CoreMainMenuMorePage implements OnInit, OnDestroy { } } - /** - * Logout the user. - */ - logout(): void { - this.loggedOut = true; - CoreSites.logout(); - } - } diff --git a/src/core/features/user/lang.json b/src/core/features/user/lang.json index 610e0fae4..3435a4c4a 100644 --- a/src/core/features/user/lang.json +++ b/src/core/features/user/lang.json @@ -1,5 +1,6 @@ { "address": "Address", + "account": "Account", "city": "City/town", "contact": "Contact", "country": "Country", @@ -14,6 +15,7 @@ "interests": "Interests", "lastname": "Surname", "manager": "Manager", + "myprofile": "My profile", "newpicture": "New picture", "noparticipants": "No participants found for this course", "participants": "Participants", diff --git a/src/theme/globals.variables.scss b/src/theme/globals.variables.scss index e2a39b886..abc13bfce 100644 --- a/src/theme/globals.variables.scss +++ b/src/theme/globals.variables.scss @@ -127,7 +127,15 @@ $core-login-loading-color-dark: $text-color-dark !default; $core-login-hide-forgot-password: false !default; $core-login-hide-need-help: false !default; -// Configuration options for more page. +// Configuration options for more page. (deprecated on 4.0) $core-more-hide-siteinfo: false !default; $core-more-hide-sitename: false !default; $core-more-hide-siteurl: false !default; + +// Configuration options for user page. +$core-user-hide-siteinfo: $core-more-hide-siteinfo !default; +$core-user-hide-sitename: $core-more-hide-sitename !default; +$core-user-hide-siteurl: $core-more-hide-siteurl !default; + + + From 806e29ce103469d62b550ca41404e63fd4d08ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 21 Oct 2021 15:09:23 +0200 Subject: [PATCH 03/13] MOBILE-3807 core: Add some delegates to user menu --- .../privatefiles/services/block-handler.ts | 4 +- .../privatefiles/privatefiles-lazy.module.ts | 4 +- .../privatefiles/privatefiles.module.ts | 8 +-- .../handlers/{mainmenu.ts => user.ts} | 39 ++++++++----- .../features/grades/grades-lazy.module.ts | 6 +- src/core/features/grades/grades.module.ts | 7 +-- .../grades/services/handlers/mainmenu.ts | 56 ------------------- .../features/grades/services/handlers/user.ts | 55 +++++++++++++----- 8 files changed, 79 insertions(+), 100 deletions(-) rename src/addons/privatefiles/services/handlers/{mainmenu.ts => user.ts} (50%) delete mode 100644 src/core/features/grades/services/handlers/mainmenu.ts diff --git a/src/addons/block/privatefiles/services/block-handler.ts b/src/addons/block/privatefiles/services/block-handler.ts index 73c160eaf..bf4c9aaf7 100644 --- a/src/addons/block/privatefiles/services/block-handler.ts +++ b/src/addons/block/privatefiles/services/block-handler.ts @@ -16,7 +16,7 @@ import { Injectable } from '@angular/core'; import { CoreBlockHandlerData } from '@features/block/services/block-delegate'; import { CoreBlockOnlyTitleComponent } from '@features/block/components/only-title-block/only-title-block'; import { CoreBlockBaseHandler } from '@features/block/classes/base-block-handler'; -import { AddonPrivateFilesMainMenuHandlerService } from '@/addons/privatefiles/services/handlers/mainmenu'; +import { AddonPrivateFilesUserHandlerService } from '@addons/privatefiles/services/handlers/user'; import { makeSingleton } from '@singletons'; /** @@ -39,7 +39,7 @@ export class AddonBlockPrivateFilesHandlerService extends CoreBlockBaseHandler { title: 'addon.block_privatefiles.pluginname', class: 'addon-block-private-files', component: CoreBlockOnlyTitleComponent, - link: AddonPrivateFilesMainMenuHandlerService.PAGE_NAME, + link: AddonPrivateFilesUserHandlerService.PAGE_NAME, linkParams: { root: 'my' }, navOptions: { preferCurrentTab: false, diff --git a/src/addons/privatefiles/privatefiles-lazy.module.ts b/src/addons/privatefiles/privatefiles-lazy.module.ts index fa78f9da5..5791c5dc0 100644 --- a/src/addons/privatefiles/privatefiles-lazy.module.ts +++ b/src/addons/privatefiles/privatefiles-lazy.module.ts @@ -16,14 +16,14 @@ import { Injector, NgModule } from '@angular/core'; import { RouterModule, ROUTES, Routes } from '@angular/router'; import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module'; -import { AddonPrivateFilesMainMenuHandlerService } from './services/handlers/mainmenu'; +import { AddonPrivateFilesUserHandlerService } from './services/handlers/user'; function buildRoutes(injector: Injector): Routes { return [ { path: 'root', data: { - mainMenuTabRoot: AddonPrivateFilesMainMenuHandlerService.PAGE_NAME, + mainMenuTabRoot: AddonPrivateFilesUserHandlerService.PAGE_NAME, }, loadChildren: () => import('./pages/index/index.module').then(m => m.AddonPrivateFilesIndexPageModule), }, diff --git a/src/addons/privatefiles/privatefiles.module.ts b/src/addons/privatefiles/privatefiles.module.ts index ca91c9d3f..635a80afb 100644 --- a/src/addons/privatefiles/privatefiles.module.ts +++ b/src/addons/privatefiles/privatefiles.module.ts @@ -15,12 +15,12 @@ import { APP_INITIALIZER, NgModule, Type } from '@angular/core'; import { Routes } from '@angular/router'; -import { CoreMainMenuDelegate } from '@features/mainmenu/services/mainmenu-delegate'; import { CoreMainMenuRoutingModule } from '@features/mainmenu/mainmenu-routing.module'; -import { AddonPrivateFilesMainMenuHandler, AddonPrivateFilesMainMenuHandlerService } from './services/handlers/mainmenu'; import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module'; import { AddonPrivateFilesProvider } from './services/privatefiles'; import { AddonPrivateFilesHelperProvider } from './services/privatefiles-helper'; +import { CoreUserDelegate } from '@features/user/services/user-delegate'; +import { AddonPrivateFilesUserHandler, AddonPrivateFilesUserHandlerService } from './services/handlers/user'; export const ADDON_PRIVATEFILES_SERVICES: Type[] = [ AddonPrivateFilesProvider, @@ -29,7 +29,7 @@ export const ADDON_PRIVATEFILES_SERVICES: Type[] = [ const routes: Routes = [ { - path: AddonPrivateFilesMainMenuHandlerService.PAGE_NAME, + path: AddonPrivateFilesUserHandlerService.PAGE_NAME, loadChildren: () => import('@/addons/privatefiles/privatefiles-lazy.module').then(m => m.AddonPrivateFilesLazyModule), }, ]; @@ -45,7 +45,7 @@ const routes: Routes = [ provide: APP_INITIALIZER, multi: true, useValue: () => { - CoreMainMenuDelegate.registerHandler(AddonPrivateFilesMainMenuHandler.instance); + CoreUserDelegate.registerHandler(AddonPrivateFilesUserHandler.instance); }, }, ], diff --git a/src/addons/privatefiles/services/handlers/mainmenu.ts b/src/addons/privatefiles/services/handlers/user.ts similarity index 50% rename from src/addons/privatefiles/services/handlers/mainmenu.ts rename to src/addons/privatefiles/services/handlers/user.ts index 0ec18da4c..4672385b3 100644 --- a/src/addons/privatefiles/services/handlers/mainmenu.ts +++ b/src/addons/privatefiles/services/handlers/user.ts @@ -14,44 +14,57 @@ import { Injectable } from '@angular/core'; -import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@features/mainmenu/services/mainmenu-delegate'; import { AddonPrivateFiles } from '@/addons/privatefiles/services/privatefiles'; import { makeSingleton } from '@singletons'; +import { CoreUserDelegateService, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@features/user/services/user-delegate'; +import { CoreUserProfile } from '@features/user/services/user'; +import { CoreNavigator } from '@services/navigator'; +import { CoreSites } from '@services/sites'; /** - * Handler to inject an option into main menu. + * Handler to inject an option into user menu. */ @Injectable({ providedIn: 'root' }) -export class AddonPrivateFilesMainMenuHandlerService implements CoreMainMenuHandler { +export class AddonPrivateFilesUserHandlerService implements CoreUserProfileHandler { static readonly PAGE_NAME = 'private'; name = 'AddonPrivateFiles'; - priority = 400; + priority = 300; + type = CoreUserDelegateService.TYPE_NEW_PAGE; + cacheEnabled = true; /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. + * @inheritdoc */ async isEnabled(): Promise { return AddonPrivateFiles.isPluginEnabled(); } /** - * Returns the data needed to render the handler. - * - * @return Data needed to render the handler. + * @inheritdoc */ - getDisplayData(): CoreMainMenuHandlerData { + async isEnabledForUser(user: CoreUserProfile): Promise { + // Private files only available for the current user. + return user.id == CoreSites.getCurrentSiteUserId(); + } + + /** + * @inheritdoc + */ + getDisplayData(): CoreUserProfileHandlerData { return { icon: 'fas-folder', title: 'addon.privatefiles.files', - page: AddonPrivateFilesMainMenuHandlerService.PAGE_NAME, class: 'addon-privatefiles-handler', + action: (event): void => { + event.preventDefault(); + event.stopPropagation(); + CoreNavigator.navigateToSitePath(AddonPrivateFilesUserHandlerService.PAGE_NAME); + }, }; } } -export const AddonPrivateFilesMainMenuHandler = makeSingleton(AddonPrivateFilesMainMenuHandlerService); +export const AddonPrivateFilesUserHandler = makeSingleton(AddonPrivateFilesUserHandlerService); diff --git a/src/core/features/grades/grades-lazy.module.ts b/src/core/features/grades/grades-lazy.module.ts index e60242302..d825a2621 100644 --- a/src/core/features/grades/grades-lazy.module.ts +++ b/src/core/features/grades/grades-lazy.module.ts @@ -23,13 +23,13 @@ import { CoreGradesCoursePage } from './pages/course/course.page'; import { CoreGradesCoursePageModule } from './pages/course/course.module'; import { CoreGradesCoursesPage } from './pages/courses/courses.page'; import { CoreGradesGradePage } from './pages/grade/grade.page'; -import { CoreGradesMainMenuHandlerService } from './services/handlers/mainmenu'; +import { CoreGradesUserHandlerService } from './services/handlers/user'; const mobileRoutes: Routes = [ { path: '', data: { - mainMenuTabRoot: CoreGradesMainMenuHandlerService.PAGE_NAME, + mainMenuTabRoot: CoreGradesUserHandlerService.PAGE_NAME, }, component: CoreGradesCoursesPage, }, @@ -47,7 +47,7 @@ const tabletRoutes: Routes = [ { path: '', data: { - mainMenuTabRoot: CoreGradesMainMenuHandlerService.PAGE_NAME, + mainMenuTabRoot: CoreGradesUserHandlerService.PAGE_NAME, }, component: CoreGradesCoursesPage, children: [ diff --git a/src/core/features/grades/grades.module.ts b/src/core/features/grades/grades.module.ts index b359d575c..d7ca58cf3 100644 --- a/src/core/features/grades/grades.module.ts +++ b/src/core/features/grades/grades.module.ts @@ -19,14 +19,12 @@ import { CoreCourseIndexRoutingModule } from '@features/course/pages/index/index import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate'; import { CoreMainMenuRoutingModule } from '@features/mainmenu/mainmenu-routing.module'; import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module'; -import { CoreMainMenuDelegate } from '@features/mainmenu/services/mainmenu-delegate'; import { CoreUserDelegate } from '@features/user/services/user-delegate'; import { CoreGradesProvider } from './services/grades'; import { CoreGradesHelperProvider } from './services/grades-helper'; import { CoreGradesCourseOptionHandler } from './services/handlers/course-option'; -import { CoreGradesMainMenuHandler, CoreGradesMainMenuHandlerService } from './services/handlers/mainmenu'; import { CoreGradesOverviewLinkHandler } from './services/handlers/overview-link'; -import { CoreGradesUserHandler } from './services/handlers/user'; +import { CoreGradesUserHandler, CoreGradesUserHandlerService } from './services/handlers/user'; import { CoreGradesUserLinkHandler } from './services/handlers/user-link'; export const CORE_GRADES_SERVICES: Type[] = [ @@ -36,7 +34,7 @@ export const CORE_GRADES_SERVICES: Type[] = [ const routes: Routes = [ { - path: CoreGradesMainMenuHandlerService.PAGE_NAME, + path: CoreGradesUserHandlerService.PAGE_NAME, loadChildren: () => import('@features/grades/grades-lazy.module').then(m => m.CoreGradesLazyModule), }, { @@ -63,7 +61,6 @@ const courseIndexRoutes: Routes = [ provide: APP_INITIALIZER, multi: true, useValue: () => { - CoreMainMenuDelegate.registerHandler(CoreGradesMainMenuHandler.instance); CoreUserDelegate.registerHandler(CoreGradesUserHandler.instance); CoreContentLinksDelegate.registerHandler(CoreGradesUserLinkHandler.instance); CoreContentLinksDelegate.registerHandler(CoreGradesOverviewLinkHandler.instance); diff --git a/src/core/features/grades/services/handlers/mainmenu.ts b/src/core/features/grades/services/handlers/mainmenu.ts deleted file mode 100644 index 6e7a7b915..000000000 --- a/src/core/features/grades/services/handlers/mainmenu.ts +++ /dev/null @@ -1,56 +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 { Injectable } from '@angular/core'; -import { CoreGrades } from '@features/grades/services/grades'; -import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@features/mainmenu/services/mainmenu-delegate'; -import { makeSingleton } from '@singletons'; - -/** - * Handler to inject an option into main menu. - */ -@Injectable({ providedIn: 'root' }) -export class CoreGradesMainMenuHandlerService implements CoreMainMenuHandler { - - static readonly PAGE_NAME = 'grades'; - - name = 'CoreGrades'; - priority = 600; - - /** - * Check if the handler is enabled on a site level. - * - * @return Whether or not the handler is enabled on a site level. - */ - isEnabled(): Promise { - return CoreGrades.isCourseGradesEnabled(); - } - - /** - * Returns the data needed to render the handler. - * - * @return Data needed to render the handler. - */ - getDisplayData(): CoreMainMenuHandlerData { - return { - icon: 'fas-chart-bar', - title: 'core.grades.grades', - page: CoreGradesMainMenuHandlerService.PAGE_NAME, - class: 'core-grades-coursesgrades-handler', - }; - } - -} - -export const CoreGradesMainMenuHandler = makeSingleton(CoreGradesMainMenuHandlerService); diff --git a/src/core/features/grades/services/handlers/user.ts b/src/core/features/grades/services/handlers/user.ts index 544044018..fab0752f6 100644 --- a/src/core/features/grades/services/handlers/user.ts +++ b/src/core/features/grades/services/handlers/user.ts @@ -22,6 +22,7 @@ import { CoreUserProfileHandlerData, } from '@features/user/services/user-delegate'; import { CoreNavigator } from '@services/navigator'; +import { CoreSites } from '@services/sites'; import { CoreUtils } from '@services/utils/utils'; import { makeSingleton } from '@singletons'; @@ -31,6 +32,8 @@ import { makeSingleton } from '@singletons'; @Injectable({ providedIn: 'root' }) export class CoreGradesUserHandlerService implements CoreUserProfileHandler { + static readonly PAGE_NAME = 'grades'; + name = 'CoreGrades:viewGrades'; priority = 400; type = CoreUserDelegateService.TYPE_NEW_PAGE; @@ -47,32 +50,54 @@ export class CoreGradesUserHandlerService implements CoreUserProfileHandler { * @inheritdoc */ async isEnabledForCourse(courseId?: number): Promise { - return CoreUtils.ignoreErrors(CoreGrades.isPluginEnabledForCourse(courseId), false); + if (courseId) { + return CoreUtils.ignoreErrors(CoreGrades.isPluginEnabledForCourse(courseId), false); + } else { + return CoreGrades.isCourseGradesEnabled(); + } } /** * @inheritdoc */ async isEnabledForUser(user: CoreUserProfile, courseId?: number): Promise { - return CoreUtils.promiseWorks(CoreGrades.getCourseGradesTable(courseId!, user.id)); + if (courseId) { + return CoreUtils.promiseWorks(CoreGrades.getCourseGradesTable(courseId, user.id)); + } + + // All course grades only available for the current user. + return user.id == CoreSites.getCurrentSiteUserId(); } /** * @inheritdoc */ - getDisplayData(): CoreUserProfileHandlerData { - return { - icon: 'fas-chart-bar', - title: 'core.grades.grades', - class: 'core-grades-user-handler', - action: (event, user, courseId): void => { - event.preventDefault(); - event.stopPropagation(); - CoreNavigator.navigateToSitePath(`/user-grades/${courseId}`, { - params: { userId: user.id }, - }); - }, - }; + getDisplayData(user: CoreUserProfile, courseId?: number): CoreUserProfileHandlerData { + if (courseId) { + return { + icon: 'fas-chart-bar', + title: 'core.grades.grades', + class: 'core-grades-user-handler', + action: (event, user, courseId): void => { + event.preventDefault(); + event.stopPropagation(); + CoreNavigator.navigateToSitePath(`/user-grades/${courseId}`, { + params: { userId: user.id }, + }); + }, + }; + } else { + return { + icon: 'fas-chart-bar', + title: 'core.grades.grades', + class: 'core-grades-coursesgrades-handler', + action: (event): void => { + event.preventDefault(); + event.stopPropagation(); + CoreNavigator.navigateToSitePath(CoreGradesUserHandlerService.PAGE_NAME); + }, + }; + } } } From 434a2a90f201dbe1afefcf651d7f6475b8157705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 21 Oct 2021 15:06:45 +0200 Subject: [PATCH 04/13] MOBILE-3807 core: Add user menu button to all level 1 pages --- src/addons/blog/blog-lazy.module.ts | 2 ++ src/addons/blog/pages/entries/entries.html | 4 +++- src/addons/calendar/pages/index/index.html | 1 + .../calendar/pages/index/index.module.ts | 2 ++ .../pages/discussions-35/discussions.html | 1 + .../discussions-35/discussions.module.ts | 2 ++ .../group-conversations.html | 1 + .../group-conversations.module.ts | 2 ++ src/addons/notifications/pages/list/list.html | 3 +++ .../notifications/pages/list/list.module.ts | 2 ++ .../navbar-buttons/navbar-buttons.ts | 12 +++++++--- .../components/user-avatar/user-avatar.scss | 8 ++++++- .../courses/pages/dashboard/dashboard.html | 2 +- .../features/courses/pages/list/list.html | 22 +++++++++---------- .../courses/pages/list/list.module.ts | 2 ++ .../features/mainmenu/pages/home/home.html | 1 + .../mainmenu/pages/home/home.module.ts | 2 ++ .../features/sitehome/pages/index/index.html | 2 +- .../features/tag/pages/search/search.html | 3 +++ .../tag/pages/search/search.page.module.ts | 2 ++ src/theme/theme.base.scss | 3 ++- 21 files changed, 60 insertions(+), 19 deletions(-) diff --git a/src/addons/blog/blog-lazy.module.ts b/src/addons/blog/blog-lazy.module.ts index 5fab11220..2bd801405 100644 --- a/src/addons/blog/blog-lazy.module.ts +++ b/src/addons/blog/blog-lazy.module.ts @@ -22,6 +22,7 @@ import { CoreCommentsComponentsModule } from '@features/comments/components/comp import { CoreTagComponentsModule } from '@features/tag/components/components.module'; import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module'; import { AddonBlogMainMenuHandlerService } from './services/handlers/mainmenu'; +import { CoreMainMenuComponentsModule } from '@features/mainmenu/components/components.module'; function buildRoutes(injector: Injector): Routes { return [ @@ -39,6 +40,7 @@ function buildRoutes(injector: Injector): Routes { CoreSharedModule, CoreCommentsComponentsModule, CoreTagComponentsModule, + CoreMainMenuComponentsModule, ], exports: [RouterModule], providers: [ diff --git a/src/addons/blog/pages/entries/entries.html b/src/addons/blog/pages/entries/entries.html index 6dd13b87c..60d027fcb 100644 --- a/src/addons/blog/pages/entries/entries.html +++ b/src/addons/blog/pages/entries/entries.html @@ -4,7 +4,9 @@

{{ title | translate }}

- + + + diff --git a/src/addons/calendar/pages/index/index.html b/src/addons/calendar/pages/index/index.html index 1533cc434..b67f85700 100644 --- a/src/addons/calendar/pages/index/index.html +++ b/src/addons/calendar/pages/index/index.html @@ -22,6 +22,7 @@ [content]="'core.settings.synchronizenow' | translate" (action)="doRefresh(undefined, $event, true)" [iconAction]="syncIcon" [closeOnClick]="false"> + diff --git a/src/addons/calendar/pages/index/index.module.ts b/src/addons/calendar/pages/index/index.module.ts index be80e63e6..0ecbc7b6e 100644 --- a/src/addons/calendar/pages/index/index.module.ts +++ b/src/addons/calendar/pages/index/index.module.ts @@ -19,6 +19,7 @@ import { CoreSharedModule } from '@/core/shared.module'; import { AddonCalendarComponentsModule } from '../../components/components.module'; import { AddonCalendarIndexPage } from './index.page'; +import { CoreMainMenuComponentsModule } from '@features/mainmenu/components/components.module'; const routes: Routes = [ { @@ -32,6 +33,7 @@ const routes: Routes = [ RouterModule.forChild(routes), CoreSharedModule, AddonCalendarComponentsModule, + CoreMainMenuComponentsModule, ], declarations: [ AddonCalendarIndexPage, diff --git a/src/addons/messages/pages/discussions-35/discussions.html b/src/addons/messages/pages/discussions-35/discussions.html index 5c0b2b5e3..8824fe578 100644 --- a/src/addons/messages/pages/discussions-35/discussions.html +++ b/src/addons/messages/pages/discussions-35/discussions.html @@ -5,6 +5,7 @@

{{ 'addon.messages.messages' | translate }}

+ diff --git a/src/addons/messages/pages/discussions-35/discussions.module.ts b/src/addons/messages/pages/discussions-35/discussions.module.ts index 15ec2604e..f26263d62 100644 --- a/src/addons/messages/pages/discussions-35/discussions.module.ts +++ b/src/addons/messages/pages/discussions-35/discussions.module.ts @@ -23,6 +23,7 @@ import { CoreSearchComponentsModule } from '@features/search/components/componen import { AddonMessagesDiscussions35Page } from './discussions.page'; import { AddonMessagesMainMenuHandlerService } from '@addons/messages/services/handlers/mainmenu'; +import { CoreMainMenuComponentsModule } from '@features/mainmenu/components/components.module'; const mobileRoutes: Routes = [ { @@ -58,6 +59,7 @@ const routes: Routes = [ RouterModule.forChild(routes), CoreSharedModule, CoreSearchComponentsModule, + CoreMainMenuComponentsModule, ], declarations: [ AddonMessagesDiscussions35Page, diff --git a/src/addons/messages/pages/group-conversations/group-conversations.html b/src/addons/messages/pages/group-conversations/group-conversations.html index 7d02aa119..d55475d6c 100644 --- a/src/addons/messages/pages/group-conversations/group-conversations.html +++ b/src/addons/messages/pages/group-conversations/group-conversations.html @@ -11,6 +11,7 @@ + diff --git a/src/addons/messages/pages/group-conversations/group-conversations.module.ts b/src/addons/messages/pages/group-conversations/group-conversations.module.ts index a4390583e..8fbcc9bae 100644 --- a/src/addons/messages/pages/group-conversations/group-conversations.module.ts +++ b/src/addons/messages/pages/group-conversations/group-conversations.module.ts @@ -22,6 +22,7 @@ import { CoreSharedModule } from '@/core/shared.module'; import { AddonMessagesGroupConversationsPage } from './group-conversations.page'; import { AddonMessagesMainMenuHandlerService } from '@addons/messages/services/handlers/mainmenu'; +import { CoreMainMenuComponentsModule } from '@features/mainmenu/components/components.module'; const mobileRoutes: Routes = [ { @@ -56,6 +57,7 @@ const routes: Routes = [ imports: [ RouterModule.forChild(routes), CoreSharedModule, + CoreMainMenuComponentsModule, ], declarations: [ AddonMessagesGroupConversationsPage, diff --git a/src/addons/notifications/pages/list/list.html b/src/addons/notifications/pages/list/list.html index aa11a1300..2863ef416 100644 --- a/src/addons/notifications/pages/list/list.html +++ b/src/addons/notifications/pages/list/list.html @@ -4,6 +4,9 @@

{{ 'addon.notifications.notifications' | translate }}

+ + + diff --git a/src/addons/notifications/pages/list/list.module.ts b/src/addons/notifications/pages/list/list.module.ts index 5a4f6144d..b592079e7 100644 --- a/src/addons/notifications/pages/list/list.module.ts +++ b/src/addons/notifications/pages/list/list.module.ts @@ -18,6 +18,7 @@ import { RouterModule, Routes } from '@angular/router'; import { CoreSharedModule } from '@/core/shared.module'; import { AddonNotificationsComponentsModule } from '../../components/components.module'; import { AddonNotificationsListPage } from './list'; +import { CoreMainMenuComponentsModule } from '@features/mainmenu/components/components.module'; const routes: Routes = [ { @@ -31,6 +32,7 @@ const routes: Routes = [ RouterModule.forChild(routes), CoreSharedModule, AddonNotificationsComponentsModule, + CoreMainMenuComponentsModule, ], declarations: [ AddonNotificationsListPage, diff --git a/src/core/components/navbar-buttons/navbar-buttons.ts b/src/core/components/navbar-buttons/navbar-buttons.ts index 39b69167e..48439908e 100644 --- a/src/core/components/navbar-buttons/navbar-buttons.ts +++ b/src/core/components/navbar-buttons/navbar-buttons.ts @@ -56,7 +56,7 @@ const BUTTON_HIDDEN_CLASS = 'core-navbar-button-hidden'; }) export class CoreNavBarButtonsComponent implements OnInit, OnDestroy { - @ViewChild('contextMenuContainer', { read: ViewContainerRef }) container?: ViewContainerRef; + @ViewChild('contextMenuContainer', { read: ViewContainerRef }) container!: ViewContainerRef; // If the hidden input is true, hide all buttons. // eslint-disable-next-line @angular-eslint/no-input-rename @@ -113,7 +113,13 @@ export class CoreNavBarButtonsComponent implements OnInit, OnDestroy { // Make sure that context-menu is always at the end of buttons if any. const contextMenu = buttonsContainer.querySelector('core-context-menu'); - contextMenu?.parentElement?.appendChild(contextMenu); + const userMenu = buttonsContainer.querySelector('core-user-menu-button'); + + if (userMenu) { + contextMenu?.parentElement?.insertBefore(contextMenu, userMenu); + } else { + contextMenu?.parentElement?.appendChild(contextMenu); + } } else { this.logger.warn('The header was found, but it didn\'t have the right ion-buttons.', selector); } @@ -177,7 +183,7 @@ export class CoreNavBarButtonsComponent implements OnInit, OnDestroy { */ protected createMainContextMenu(): CoreContextMenuComponent { const factory = this.factoryResolver.resolveComponentFactory(CoreContextMenuComponent); - const componentRef = this.container!.createComponent(factory); + const componentRef = this.container.createComponent(factory); this.createdMainContextMenuElement = componentRef.location.nativeElement; diff --git a/src/core/components/user-avatar/user-avatar.scss b/src/core/components/user-avatar/user-avatar.scss index 5d99ba41d..f0e1ff20d 100644 --- a/src/core/components/user-avatar/user-avatar.scss +++ b/src/core/components/user-avatar/user-avatar.scss @@ -36,13 +36,19 @@ content: ""; } } - &.core-bar-button-image img { + &.core-bar-button-image { padding: 0; 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; + + img { + padding: 4px; + border-radius: 50%; + } } .contact-status { diff --git a/src/core/features/courses/pages/dashboard/dashboard.html b/src/core/features/courses/pages/dashboard/dashboard.html index c4315e7d1..c79854f7a 100644 --- a/src/core/features/courses/pages/dashboard/dashboard.html +++ b/src/core/features/courses/pages/dashboard/dashboard.html @@ -1,4 +1,4 @@ - + diff --git a/src/core/features/courses/pages/list/list.html b/src/core/features/courses/pages/list/list.html index 114a5a78b..ffb6b4b22 100644 --- a/src/core/features/courses/pages/list/list.html +++ b/src/core/features/courses/pages/list/list.html @@ -5,20 +5,20 @@

{{ 'core.courses.availablecourses' | translate }}

{{ 'core.courses.mycourses' | translate }}

- + + + + + + + - - - - - - diff --git a/src/core/features/courses/pages/list/list.module.ts b/src/core/features/courses/pages/list/list.module.ts index c0d3a2c36..23dd52438 100644 --- a/src/core/features/courses/pages/list/list.module.ts +++ b/src/core/features/courses/pages/list/list.module.ts @@ -20,6 +20,7 @@ import { CoreCoursesComponentsModule } from '../../components/components.module' import { CoreSearchComponentsModule } from '@features/search/components/components.module'; import { CoreCoursesListPage } from './list'; +import { CoreMainMenuComponentsModule } from '@features/mainmenu/components/components.module'; const routes: Routes = [ { @@ -34,6 +35,7 @@ const routes: Routes = [ CoreSharedModule, CoreCoursesComponentsModule, CoreSearchComponentsModule, + CoreMainMenuComponentsModule, ], declarations: [ CoreCoursesListPage, diff --git a/src/core/features/mainmenu/pages/home/home.html b/src/core/features/mainmenu/pages/home/home.html index 903bcb8ff..e953b24c2 100644 --- a/src/core/features/mainmenu/pages/home/home.html +++ b/src/core/features/mainmenu/pages/home/home.html @@ -8,6 +8,7 @@ + diff --git a/src/core/features/mainmenu/pages/home/home.module.ts b/src/core/features/mainmenu/pages/home/home.module.ts index c60828634..7dcde146c 100644 --- a/src/core/features/mainmenu/pages/home/home.module.ts +++ b/src/core/features/mainmenu/pages/home/home.module.ts @@ -22,6 +22,7 @@ import { CoreMainMenuHomePage } from './home'; import { MAIN_MENU_HOME_ROUTES } from './home-routing.module'; import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module'; import { CoreMainMenuHomeHandlerService } from '@features/mainmenu/services/handlers/mainmenu'; +import { CoreMainMenuComponentsModule } from '@features/mainmenu/components/components.module'; function buildRoutes(injector: Injector): Routes { const routes = resolveModuleRoutes(injector, MAIN_MENU_HOME_ROUTES); @@ -42,6 +43,7 @@ function buildRoutes(injector: Injector): Routes { @NgModule({ imports: [ CoreSharedModule, + CoreMainMenuComponentsModule, ], providers: [ { provide: ROUTES, multi: true, useFactory: buildRoutes, deps: [Injector] }, diff --git a/src/core/features/sitehome/pages/index/index.html b/src/core/features/sitehome/pages/index/index.html index 4dd2ce8d5..f96e2ea06 100644 --- a/src/core/features/sitehome/pages/index/index.html +++ b/src/core/features/sitehome/pages/index/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/core/features/tag/pages/search/search.html b/src/core/features/tag/pages/search/search.html index 5afa3e371..1f6ff37a9 100644 --- a/src/core/features/tag/pages/search/search.html +++ b/src/core/features/tag/pages/search/search.html @@ -4,6 +4,9 @@

{{ 'core.tag.searchtags' | translate }}

+ + + diff --git a/src/core/features/tag/pages/search/search.page.module.ts b/src/core/features/tag/pages/search/search.page.module.ts index 3da4821d2..5519cb3ac 100644 --- a/src/core/features/tag/pages/search/search.page.module.ts +++ b/src/core/features/tag/pages/search/search.page.module.ts @@ -19,6 +19,7 @@ import { CoreSharedModule } from '@/core/shared.module'; import { CoreSearchComponentsModule } from '@features/search/components/components.module'; import { CoreTagSearchPage } from './search.page'; +import { CoreMainMenuComponentsModule } from '@features/mainmenu/components/components.module'; const routes: Routes = [ { @@ -34,6 +35,7 @@ const routes: Routes = [ RouterModule.forChild(routes), CoreSharedModule, CoreSearchComponentsModule, + CoreMainMenuComponentsModule, ], exports: [RouterModule], }) diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index c040c18d3..f202afd2d 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -655,12 +655,13 @@ ion-card ion-item:only-child { ion-toolbar h1 img.core-bar-button-image, ion-toolbar h1 .core-bar-button-image img { - padding: 0; + 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; } // Action sheet. From 0bbfb898d1c59508c201ec0e9b5ca6efb8e757a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 21 Oct 2021 15:44:53 +0200 Subject: [PATCH 05/13] MOBILE-3807 user: Edit avatar only from details --- .../components/user-menu/user-menu.scss | 1 - src/core/features/user/pages/about/about.html | 21 ++- .../features/user/pages/about/about.page.ts | 169 ++++++++++++++++-- src/core/features/user/pages/about/about.scss | 44 +++++ .../features/user/pages/profile/profile.html | 12 +- .../user/pages/profile/profile.page.ts | 125 +------------ .../features/user/pages/profile/profile.scss | 15 -- 7 files changed, 225 insertions(+), 162 deletions(-) create mode 100644 src/core/features/user/pages/about/about.scss diff --git a/src/core/features/mainmenu/components/user-menu/user-menu.scss b/src/core/features/mainmenu/components/user-menu/user-menu.scss index 7466e6dd8..e6d9fd3fd 100644 --- a/src/core/features/mainmenu/components/user-menu/user-menu.scss +++ b/src/core/features/mainmenu/components/user-menu/user-menu.scss @@ -18,7 +18,6 @@ } } - @if ($core-user-hide-siteinfo) { .core-usermenu-siteinfo { display: none; diff --git a/src/core/features/user/pages/about/about.html b/src/core/features/user/pages/about/about.html index fb08137f1..b37d038f4 100644 --- a/src/core/features/user/pages/about/about.html +++ b/src/core/features/user/pages/about/about.html @@ -3,7 +3,7 @@ -

{{ title }}

+

{{ 'core.user.details' | translate }}

@@ -12,6 +12,25 @@ + +

{{ 'core.user.contact' | translate}}

diff --git a/src/core/features/user/pages/about/about.page.ts b/src/core/features/user/pages/about/about.page.ts index 2c3e5808f..33bbdb8f9 100644 --- a/src/core/features/user/pages/about/about.page.ts +++ b/src/core/features/user/pages/about/about.page.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; import { SafeUrl } from '@angular/platform-browser'; import { IonRefresher } from '@ionic/angular'; @@ -20,10 +20,15 @@ import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreTextUtils } from '@services/utils/text'; 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 { CoreUserHelper } from '@features/user/services/user-helper'; 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. @@ -31,11 +36,9 @@ import { CoreNavigator } from '@services/navigator'; @Component({ selector: 'page-core-user-about', templateUrl: 'about.html', + styleUrls: ['about.scss'], }) -export class CoreUserAboutPage implements OnInit { - - protected userId!: number; - protected siteId: string; +export class CoreUserAboutPage implements OnInit, OnDestroy { courseId!: number; userLoaded = false; @@ -45,20 +48,46 @@ export class CoreUserAboutPage implements OnInit { title?: string; formattedAddress?: string; encodedAddress?: SafeUrl; + canChangeProfilePicture = false; + + protected userId!: number; + protected site!: CoreSite; + protected obsProfileRefreshed?: CoreEventObserver; 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. - * - * @return Promise resolved when done. + * @inheritdoc */ async ngOnInit(): Promise { this.userId = CoreNavigator.getRouteNumberParam('userId') || 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.userLoaded = true; }); @@ -83,11 +112,85 @@ export class CoreUserAboutPage implements OnInit { this.user = user; this.title = user.fullname; + + this.user.address = CoreUserHelper.formatAddress('', user.city, user.country); + + await this.checkUserImageUpdated(); } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'core.user.errorloaduser', true); } } + /** + * Check if current user image has changed. + * + * @return Promise resolved when done. + */ + protected async checkUserImageUpdated(): Promise { + 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 { + 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. * @@ -106,8 +209,52 @@ export class CoreUserAboutPage implements OnInit { courseId: this.courseId, userId: this.userId, 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(); + } + } diff --git a/src/core/features/user/pages/about/about.scss b/src/core/features/user/pages/about/about.scss new file mode 100644 index 000000000..c5c1284cb --- /dev/null +++ b/src/core/features/user/pages/about/about.scss @@ -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; +} diff --git a/src/core/features/user/pages/profile/profile.html b/src/core/features/user/pages/profile/profile.html index 2bedd26e5..a5c7112e8 100644 --- a/src/core/features/user/pages/profile/profile.html +++ b/src/core/features/user/pages/profile/profile.html @@ -14,20 +14,10 @@ + [attr.aria-label]="'core.user.details' | translate" detail="true" lines="none">

{{ 'core.user.details' | translate }}

- + + [attr.aria-label]="handler.title | translate" detail="true" lines="none">

{{ handler.title | translate }}

- +

{{ 'core.settings.preferences' | translate }}

diff --git a/src/core/features/mainmenu/components/user-menu/user-menu.scss b/src/core/features/mainmenu/components/user-menu/user-menu.scss index e6d9fd3fd..bae1b1d30 100644 --- a/src/core/features/mainmenu/components/user-menu/user-menu.scss +++ b/src/core/features/mainmenu/components/user-menu/user-menu.scss @@ -16,6 +16,11 @@ } } } + + .core-user-menu-preferences { + --inner-border-width: 0; + --border-width: 1px 0 0 0; + } } @if ($core-user-hide-siteinfo) { diff --git a/src/core/features/mainmenu/lang.json b/src/core/features/mainmenu/lang.json index a6558e06e..0d188b98d 100644 --- a/src/core/features/mainmenu/lang.json +++ b/src/core/features/mainmenu/lang.json @@ -1,7 +1,5 @@ { "changesite": "Change site", - "help": "Help", "home": "Home", - "logout": "Log out", - "website": "Website" + "logout": "Log out" } diff --git a/src/core/features/mainmenu/pages/more/more.html b/src/core/features/mainmenu/pages/more/more.html index 69a4e046c..2d111d407 100644 --- a/src/core/features/mainmenu/pages/more/more.html +++ b/src/core/features/mainmenu/pages/more/more.html @@ -4,7 +4,7 @@ -

+

{{ 'core.more' | translate }}

@@ -54,20 +54,6 @@

{{ 'core.scanqr' | translate }}

- - - -

{{ 'core.mainmenu.website' | translate }}

-
-
- - - -

{{ 'core.mainmenu.help' | translate }}

-
-
diff --git a/src/core/features/mainmenu/pages/more/more.ts b/src/core/features/mainmenu/pages/more/more.ts index a8b93f285..626a18d27 100644 --- a/src/core/features/mainmenu/pages/more/more.ts +++ b/src/core/features/mainmenu/pages/more/more.ts @@ -17,7 +17,6 @@ import { Subscription } from 'rxjs'; import { CoreSites } from '@services/sites'; import { CoreUtils } from '@services/utils/utils'; -import { CoreSiteInfo } from '@classes/site'; import { CoreMainMenuDelegate, CoreMainMenuHandlerData } from '../../services/mainmenu-delegate'; import { CoreMainMenu, CoreMainMenuCustomItem } from '../../services/mainmenu'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; @@ -28,7 +27,7 @@ import { CoreTextUtils } from '@services/utils/text'; import { Translate } from '@singletons'; /** - * Page that displays the main menu of the app. + * Page that displays the more page of the app. */ @Component({ selector: 'page-core-mainmenu-more', @@ -39,12 +38,7 @@ export class CoreMainMenuMorePage implements OnInit, OnDestroy { handlers?: CoreMainMenuHandlerData[]; handlersLoaded = false; - siteInfo?: CoreSiteInfo; - siteName?: string; showScanQR: boolean; - showWeb?: boolean; - showHelp?: boolean; - docsUrl?: string; customItems?: CoreMainMenuCustomItem[]; protected allHandlers?: CoreMainMenuHandlerData[]; @@ -53,14 +47,14 @@ export class CoreMainMenuMorePage implements OnInit, OnDestroy { protected updateSiteObserver: CoreEventObserver; constructor() { + this.langObserver = CoreEvents.on(CoreEvents.LANGUAGE_CHANGED, this.loadCustomMenuItems.bind(this)); + + this.updateSiteObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, async () => { + this.customItems = await CoreMainMenu.getCustomMenuItems(); + }, CoreSites.getCurrentSiteId()); + + this.loadCustomMenuItems(); - this.langObserver = CoreEvents.on(CoreEvents.LANGUAGE_CHANGED, this.loadSiteInfo.bind(this)); - this.updateSiteObserver = CoreEvents.on( - CoreEvents.SITE_UPDATED, - this.loadSiteInfo.bind(this), - CoreSites.getCurrentSiteId(), - ); - this.loadSiteInfo(); this.showScanQR = CoreUtils.canScanQR() && !CoreSites.getCurrentSite()?.isFeatureDisabled('CoreMainMenuDelegate_QrReader'); } @@ -109,18 +103,9 @@ export class CoreMainMenuMorePage implements OnInit, OnDestroy { } /** - * Load the site info required by the view. + * Load custom menu items. */ - protected async loadSiteInfo(): Promise { - const currentSite = CoreSites.getRequiredCurrentSite(); - - this.siteInfo = currentSite.getInfo(); - this.siteName = currentSite.getSiteName(); - this.showWeb = !currentSite.isFeatureDisabled('CoreMainMenuDelegate_website'); - this.showHelp = !currentSite.isFeatureDisabled('CoreMainMenuDelegate_help'); - - this.docsUrl = await currentSite.getDocsUrl(); - + protected async loadCustomMenuItems(): Promise { this.customItems = await CoreMainMenu.getCustomMenuItems(); } diff --git a/src/core/features/settings/pages/site/site.html b/src/core/features/settings/pages/site/site.html index b28da0563..af29f5ee0 100644 --- a/src/core/features/settings/pages/site/site.html +++ b/src/core/features/settings/pages/site/site.html @@ -15,18 +15,6 @@ - - -

{{siteInfo!.fullname}}

-

- -

-

{{ siteUrl }}

-
-
- - @@ -36,38 +24,37 @@

{{ handler.title | translate}}

- - - - -

{{ 'core.settings.spaceusage' | translate }}

-

{{ spaceUsage.spaceUsage | coreBytesToSize }}

-
- - - - - - -
- - -

{{ 'core.settings.synchronizenow' | translate }}

-
- - - - - - - - -
-
+ + + +

{{ 'core.settings.spaceusage' | translate }}

+

{{ spaceUsage.spaceUsage | coreBytesToSize }}

+
+ + + + + + +
+ + +

{{ 'core.settings.synchronizenow' | translate }}

+
+ + + + + + + + +
+
diff --git a/src/core/features/settings/pages/site/site.ts b/src/core/features/settings/pages/site/site.ts index be7d0e313..77a366b73 100644 --- a/src/core/features/settings/pages/site/site.ts +++ b/src/core/features/settings/pages/site/site.ts @@ -22,7 +22,6 @@ import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreSettingsHelper, CoreSiteSpaceUsage } from '../../services/settings-helper'; import { CoreApp } from '@services/app'; -import { CoreSiteInfo } from '@classes/site'; import { Translate } from '@singletons'; import { CoreNavigator } from '@services/navigator'; import { CorePageItemsListManager } from '@classes/page-items-list-manager'; @@ -43,9 +42,6 @@ export class CoreSitePreferencesPage implements AfterViewInit, OnDestroy { isIOS: boolean; siteId: string; - siteInfo?: CoreSiteInfo; - siteName?: string; - siteUrl?: string; spaceUsage: CoreSiteSpaceUsage = { cacheEntries: 0, spaceUsage: 0, @@ -60,11 +56,9 @@ export class CoreSitePreferencesPage implements AfterViewInit, OnDestroy { this.siteId = CoreSites.getCurrentSiteId(); this.handlers = new CoreSettingsSitePreferencesManager(CoreSitePreferencesPage); - this.sitesObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, (data) => { - if (data.siteId == this.siteId) { - this.refreshData(); - } - }); + this.sitesObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, () => { + this.refreshData(); + }, this.siteId); } /** @@ -94,11 +88,6 @@ export class CoreSitePreferencesPage implements AfterViewInit, OnDestroy { protected async fetchData(): Promise { this.handlers.setItems(CoreSettingsDelegate.getHandlers()); - const currentSite = CoreSites.getCurrentSite(); - this.siteInfo = currentSite!.getInfo(); - this.siteName = currentSite!.getSiteName(); - this.siteUrl = currentSite!.getURL(); - this.spaceUsage = await CoreSettingsHelper.getSiteSpaceUsage(this.siteId); } @@ -145,7 +134,9 @@ export class CoreSitePreferencesPage implements AfterViewInit, OnDestroy { */ async deleteSiteStorage(): Promise { try { - this.spaceUsage = await CoreSettingsHelper.deleteSiteStorage(this.siteName || '', this.siteId); + const siteName = CoreSites.getRequiredCurrentSite().getSiteName(); + + this.spaceUsage = await CoreSettingsHelper.deleteSiteStorage(siteName, this.siteId); } catch { // Ignore cancelled confirmation modal. } diff --git a/src/core/features/user/pages/about/about.html b/src/core/features/user/pages/about/about.html index b37d038f4..cb828fb03 100644 --- a/src/core/features/user/pages/about/about.html +++ b/src/core/features/user/pages/about/about.html @@ -12,7 +12,7 @@ -
- + - {{ logoutLabel | translate }} + {{ 'core.mainmenu.logout' | translate }} - \ No newline at end of file + diff --git a/src/core/features/mainmenu/components/user-menu/user-menu.ts b/src/core/features/mainmenu/components/user-menu/user-menu.ts index 71c8cc9f7..1ec0e39ad 100644 --- a/src/core/features/mainmenu/components/user-menu/user-menu.ts +++ b/src/core/features/mainmenu/components/user-menu/user-menu.ts @@ -14,11 +14,13 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { CoreSiteInfo } from '@classes/site'; +import { CoreLoginSitesComponent } from '@features/login/components/sites/sites'; import { CoreLoginHelper } from '@features/login/services/login-helper'; import { CoreUser, CoreUserProfile } from '@features/user/services/user'; import { CoreUserProfileHandlerData, CoreUserDelegate, CoreUserDelegateService } from '@features/user/services/user-delegate'; import { CoreNavigator } from '@services/navigator'; import { CoreSites } from '@services/sites'; +import { CoreDomUtils } from '@services/utils/dom'; import { ModalController } from '@singletons'; import { Subscription } from 'rxjs'; @@ -34,11 +36,12 @@ export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy { siteInfo?: CoreSiteInfo; siteName?: string; - logoutLabel = 'core.mainmenu.changesite'; siteUrl?: string; handlers: CoreUserProfileHandlerData[] = []; handlersLoaded = false; + loaded = false; user?: CoreUserProfile; + moreSites = false; protected subscription!: Subscription; @@ -46,13 +49,16 @@ export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy { * @inheritdoc */ async ngOnInit(): Promise { + // Check if there are more sites to switch. + const sites = await CoreSites.getSites(); + this.moreSites = sites.length > 1; const currentSite = CoreSites.getRequiredCurrentSite(); - this.siteInfo = currentSite.getInfo(); this.siteName = currentSite.getSiteName(); this.siteUrl = currentSite.getURL(); - this.logoutLabel = CoreLoginHelper.getLogoutLabel(currentSite); + + this.loaded = true; // Load the handlers. if (this.siteInfo) { @@ -133,6 +139,38 @@ export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy { CoreSites.logout(); } + /** + * Show account selector. + * + * @param event Click event + */ + async switchAccounts(event: Event): Promise { + const thisModal = await ModalController.getTop(); + + event.preventDefault(); + event.stopPropagation(); + + const closeAll = await CoreDomUtils.openSideModal({ + component: CoreLoginSitesComponent, + cssClass: 'core-modal-lateral-sm', + }); + + if (closeAll) { + await ModalController.dismiss(undefined, undefined, thisModal.id); + } + } + + /** + * Add account. + * + * @param event Click event + */ + async addAccount(event: Event): Promise { + await this.close(event); + + await CoreLoginHelper.goToAddSite(true, true); + } + /** * Close modal. */ diff --git a/src/core/features/mainmenu/lang.json b/src/core/features/mainmenu/lang.json index 0d188b98d..9e9977545 100644 --- a/src/core/features/mainmenu/lang.json +++ b/src/core/features/mainmenu/lang.json @@ -1,5 +1,5 @@ { - "changesite": "Change site", "home": "Home", - "logout": "Log out" + "logout": "Log out", + "switchaccount": "Switch account" } diff --git a/src/core/features/tag/tag-lazy.module.ts b/src/core/features/tag/tag-lazy.module.ts index e0c6f7b0e..6ced82dfe 100644 --- a/src/core/features/tag/tag-lazy.module.ts +++ b/src/core/features/tag/tag-lazy.module.ts @@ -35,7 +35,7 @@ function buildRoutes(injector: Injector): Routes { data: { mainMenuTabRoot: CoreTagMainMenuHandlerService.PAGE_NAME, }, - loadChildren: () => import('@features/tag//pages/search/search.page.module').then(m => m.CoreTagSearchPageModule), + loadChildren: () => import('@features/tag/pages/search/search.page.module').then(m => m.CoreTagSearchPageModule), }, CoreTagIndexAreaRoute, ...buildTabMainRoutes(injector, { diff --git a/src/core/features/user/lang.json b/src/core/features/user/lang.json index 3435a4c4a..61d41e287 100644 --- a/src/core/features/user/lang.json +++ b/src/core/features/user/lang.json @@ -15,12 +15,12 @@ "interests": "Interests", "lastname": "Surname", "manager": "Manager", - "myprofile": "My profile", "newpicture": "New picture", "noparticipants": "No participants found for this course", "participants": "Participants", "phone1": "Phone", "phone2": "Mobile phone", + "profile": "Profile", "roles": "Roles", "sendemail": "Email", "student": "Student", diff --git a/src/core/services/app.ts b/src/core/services/app.ts index 4c5cfc1e2..b3419ce9e 100644 --- a/src/core/services/app.ts +++ b/src/core/services/app.ts @@ -606,7 +606,7 @@ export class CoreAppProvider { }; localStorage.setItem('CoreRedirect', JSON.stringify(redirect)); - } catch (ex) { + } catch { // Ignore errors. } } diff --git a/src/core/services/sites.ts b/src/core/services/sites.ts index dd0c39a2b..3b77bf38b 100644 --- a/src/core/services/sites.ts +++ b/src/core/services/sites.ts @@ -1095,7 +1095,7 @@ export class CoreSitesProvider { async getSortedSites(ids?: string[]): Promise { const sites = await this.getSites(ids); - // Sort sites by url and ful lname. + // Sort sites by url and fullname. sites.sort((a, b) => { // First compare by site url without the protocol. const urlA = a.siteUrl.replace(/^https?:\/\//, '').toLowerCase(); @@ -1754,40 +1754,13 @@ export type CoreSiteUserTokenResponse = { * Site's basic info. */ export type CoreSiteBasicInfo = { - /** - * Site ID. - */ - id: string; - - /** - * Site URL. - */ - siteUrl: string; - - /** - * User's full name. - */ - fullName?: string; - - /** - * Site's name. - */ - siteName?: string; - - /** - * User's avatar. - */ - avatar?: string; - - /** - * Badge to display in the site. - */ - badge?: number; - - /** - * Site home ID. - */ - siteHomeId?: number; + id: string; // Site ID. + siteUrl: string; // Site URL. + fullName?: string; // User's full name. + siteName?: string; // Site's name. + avatar?: string; // User's avatar. + badge?: number; // Badge to display in the site. + siteHomeId?: number; // Site home ID. }; /** From ed19df5c8e1020a12358efe55e9afa1f27fdb22b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 28 Oct 2021 11:14:49 +0200 Subject: [PATCH 10/13] MOBILE-3807 usermenu: Hide usermenu button when the path is not main --- .../user-menu-button/user-menu-button.html | 12 +++--------- .../user-menu-button/user-menu-button.ts | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/core/features/mainmenu/components/user-menu-button/user-menu-button.html b/src/core/features/mainmenu/components/user-menu-button/user-menu-button.html index 1d9f61bde..998f7e7c0 100644 --- a/src/core/features/mainmenu/components/user-menu-button/user-menu-button.html +++ b/src/core/features/mainmenu/components/user-menu-button/user-menu-button.html @@ -1,9 +1,3 @@ - \ No newline at end of file + + diff --git a/src/core/features/mainmenu/components/user-menu-button/user-menu-button.ts b/src/core/features/mainmenu/components/user-menu-button/user-menu-button.ts index c1acf9857..d95001748 100644 --- a/src/core/features/mainmenu/components/user-menu-button/user-menu-button.ts +++ b/src/core/features/mainmenu/components/user-menu-button/user-menu-button.ts @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { CoreSiteInfo } from '@classes/site'; +import { IonRouterOutlet } from '@ionic/angular'; import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreMainMenuUserMenuComponent } from '../user-menu/user-menu'; @@ -27,17 +28,24 @@ import { CoreMainMenuUserMenuComponent } from '../user-menu/user-menu'; selector: 'core-user-menu-button', templateUrl: 'user-menu-button.html', }) -export class CoreMainMenuUserButtonComponent { +export class CoreMainMenuUserButtonComponent implements OnInit { siteInfo?: CoreSiteInfo; + isMainScreen = false; - constructor() { + constructor(protected routerOutlet: IonRouterOutlet) { const currentSite = CoreSites.getRequiredCurrentSite(); - // @TODO: Check if the page where I currently am is at level 0. this.siteInfo = currentSite.getInfo(); } + /** + * @inheritdoc + */ + ngOnInit(): void { + this.isMainScreen = !this.routerOutlet.canGoBack(); + } + /** * Open User menu * From b9bf0189662f487461427e39b1662a65bf7ad09e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 28 Oct 2021 16:15:27 +0200 Subject: [PATCH 11/13] MOBILE-3807 usermenu: Show on side main menu bar --- .../components/user-menu-button/user-menu-button.scss | 3 +++ .../components/user-menu-button/user-menu-button.ts | 1 + src/core/features/mainmenu/mainmenu-lazy.module.ts | 2 ++ src/core/features/mainmenu/pages/menu/menu.html | 2 ++ src/core/features/mainmenu/pages/menu/menu.scss | 8 +++++++- 5 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 src/core/features/mainmenu/components/user-menu-button/user-menu-button.scss diff --git a/src/core/features/mainmenu/components/user-menu-button/user-menu-button.scss b/src/core/features/mainmenu/components/user-menu-button/user-menu-button.scss new file mode 100644 index 000000000..15aa50df5 --- /dev/null +++ b/src/core/features/mainmenu/components/user-menu-button/user-menu-button.scss @@ -0,0 +1,3 @@ +:host-context(ion-tabs.placement-side div.tabs-inner) { + display: none; +} \ No newline at end of file diff --git a/src/core/features/mainmenu/components/user-menu-button/user-menu-button.ts b/src/core/features/mainmenu/components/user-menu-button/user-menu-button.ts index d95001748..a27782b73 100644 --- a/src/core/features/mainmenu/components/user-menu-button/user-menu-button.ts +++ b/src/core/features/mainmenu/components/user-menu-button/user-menu-button.ts @@ -27,6 +27,7 @@ import { CoreMainMenuUserMenuComponent } from '../user-menu/user-menu'; @Component({ selector: 'core-user-menu-button', templateUrl: 'user-menu-button.html', + styleUrls: ['user-menu-button.scss'], }) export class CoreMainMenuUserButtonComponent implements OnInit { diff --git a/src/core/features/mainmenu/mainmenu-lazy.module.ts b/src/core/features/mainmenu/mainmenu-lazy.module.ts index ff9c67189..22f30d18b 100644 --- a/src/core/features/mainmenu/mainmenu-lazy.module.ts +++ b/src/core/features/mainmenu/mainmenu-lazy.module.ts @@ -22,6 +22,7 @@ import { MAIN_MENU_ROUTES } from './mainmenu-routing.module'; import { CoreMainMenuPage } from './pages/menu/menu'; import { CoreMainMenuHomeHandlerService } from './services/handlers/mainmenu'; import { CoreMainMenuProvider } from './services/mainmenu'; +import { CoreMainMenuComponentsModule } from './components/components.module'; function buildRoutes(injector: Injector): Routes { const routes = resolveModuleRoutes(injector, MAIN_MENU_ROUTES); @@ -54,6 +55,7 @@ function buildRoutes(injector: Injector): Routes { @NgModule({ imports: [ CoreSharedModule, + CoreMainMenuComponentsModule, ], declarations: [ CoreMainMenuPage, diff --git a/src/core/features/mainmenu/pages/menu/menu.html b/src/core/features/mainmenu/pages/menu/menu.html index 893737e2f..18a5bb380 100644 --- a/src/core/features/mainmenu/pages/menu/menu.html +++ b/src/core/features/mainmenu/pages/menu/menu.html @@ -4,6 +4,8 @@ [@menuShowHideAnimation]="tabsPlacement == 'side' ? '' : (isMainScreen ? 'visible' : 'hidden')"> + + diff --git a/src/core/features/mainmenu/pages/menu/menu.scss b/src/core/features/mainmenu/pages/menu/menu.scss index 7f362ec1d..b359d98f0 100644 --- a/src/core/features/mainmenu/pages/menu/menu.scss +++ b/src/core/features/mainmenu/pages/menu/menu.scss @@ -71,7 +71,7 @@ @include padding(var(--ion-safe-area-top), 0px, var(--ion-safe-area-bottom), var(--ion-safe-area-left)); - ion-tab-button { + ion-tab-button, core-user-menu-button { width: 100%; min-height: var(--menutabbar-size); flex: 0; @@ -83,6 +83,12 @@ } } + core-user-menu-button { + align-items: center; + display: flex; + justify-content: center; + } + .core-network-message { --network-message-height: 16px; position: absolute; From d3ffc45be334637dd65faef48f8967576b3b5eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Fri, 29 Oct 2021 12:36:42 +0200 Subject: [PATCH 12/13] MOBILE-3807 usermenu: Change UI and texts --- scripts/langindex.json | 1 + .../login/components/sites/sites.html | 5 +- .../features/login/pages/sites/sites.html | 2 +- .../features/login/services/login-helper.ts | 19 +++--- .../components/user-menu/user-menu.html | 37 ++++------- .../components/user-menu/user-menu.scss | 16 ----- .../pages/space-usage/space-usage.html | 2 +- .../synchronization/synchronization.html | 2 +- src/core/features/user/pages/about/about.html | 63 +++++++++++-------- src/core/services/sites.ts | 12 ++-- 10 files changed, 71 insertions(+), 88 deletions(-) diff --git a/scripts/langindex.json b/scripts/langindex.json index 093646fcf..b269f6b62 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -2255,6 +2255,7 @@ "core.user.participants": "moodle", "core.user.phone1": "moodle", "core.user.phone2": "moodle", + "core.user.profile": "moodle", "core.user.roles": "moodle", "core.user.sendemail": "local_moodlemobileapp", "core.user.student": "moodle/defaultcoursestudent", diff --git a/src/core/features/login/components/sites/sites.html b/src/core/features/login/components/sites/sites.html index 20ee59b87..5cf1c7fae 100644 --- a/src/core/features/login/components/sites/sites.html +++ b/src/core/features/login/components/sites/sites.html @@ -26,7 +26,8 @@ -

{{ accountsList.currentSite.siteUrl }} +

{{ + accountsList.currentSite.siteUrlWithoutProtocol }}

@@ -52,7 +53,7 @@

-

{{ sites[0].siteUrl }}

+

{{ sites[0].siteUrlWithoutProtocol }}

diff --git a/src/core/features/login/pages/sites/sites.html b/src/core/features/login/pages/sites/sites.html index b9d3ed2b7..a47a65f2f 100644 --- a/src/core/features/login/pages/sites/sites.html +++ b/src/core/features/login/pages/sites/sites.html @@ -26,7 +26,7 @@

-

{{ sites[0].siteUrl }}

+

{{ sites[0].siteUrlWithoutProtocol }}

diff --git a/src/core/features/login/services/login-helper.ts b/src/core/features/login/services/login-helper.ts index 545898b2b..627547671 100644 --- a/src/core/features/login/services/login-helper.ts +++ b/src/core/features/login/services/login-helper.ts @@ -1357,25 +1357,23 @@ export class CoreLoginHelperProvider { const index = sites.findIndex((site) => site.id == currentSiteId); accountsList.currentSite = sites.splice(index, 1)[0]; - siteUrl = accountsList.currentSite.siteUrl.replace(/^https?:\/\//, '').toLowerCase(); - accountsList.currentSite.siteUrl = siteUrl; + siteUrl = accountsList.currentSite.siteUrlWithoutProtocol; } const otherSites: Record = {}; // Add site counter and classify sites. await Promise.all(sites.map(async (site) => { - site.siteUrl = site.siteUrl.replace(/^https?:\/\//, '').toLowerCase(); site.badge = await CoreUtils.ignoreErrors(CorePushNotifications.getSiteCounter(site.id)) || 0; - if (site.siteUrl == siteUrl) { + if (site.siteUrlWithoutProtocol == siteUrl) { accountsList.sameSite.push(site); } else { - if (!otherSites[site.siteUrl]) { - otherSites[site.siteUrl] = []; + if (!otherSites[site.siteUrlWithoutProtocol]) { + otherSites[site.siteUrlWithoutProtocol] = []; } - otherSites[site.siteUrl].push(site); + otherSites[site.siteUrlWithoutProtocol].push(site); } return; @@ -1396,11 +1394,11 @@ export class CoreLoginHelperProvider { async deleteAccountFromList(accountsList: CoreAccountsList, site: CoreSiteBasicInfo): Promise { await CoreSites.deleteSite(site.id); - const siteUrl = site.siteUrl; + const siteUrl = site.siteUrlWithoutProtocol; let index = 0; // Found on same site. - if (accountsList.sameSite.length > 0 && accountsList.sameSite[0].siteUrl == siteUrl) { + if (accountsList.sameSite.length > 0 && accountsList.sameSite[0].siteUrlWithoutProtocol == siteUrl) { index = accountsList.sameSite.findIndex((listedSite) => listedSite.id == site.id); if (index >= 0) { accountsList.sameSite.splice(index, 1); @@ -1410,7 +1408,8 @@ export class CoreLoginHelperProvider { return; } - const otherSiteIndex = accountsList.otherSites.findIndex((sites) => sites.length > 0 && sites[0].siteUrl == siteUrl); + const otherSiteIndex = accountsList.otherSites.findIndex((sites) => + sites.length > 0 && sites[0].siteUrlWithoutProtocol == siteUrl); if (otherSiteIndex < 0) { // Site Url not found. return; diff --git a/src/core/features/mainmenu/components/user-menu/user-menu.html b/src/core/features/mainmenu/components/user-menu/user-menu.html index 4cb01be69..c36894446 100644 --- a/src/core/features/mainmenu/components/user-menu/user-menu.html +++ b/src/core/features/mainmenu/components/user-menu/user-menu.html @@ -8,32 +8,19 @@

{{'core.user.account' | translate}}

- - - - - - - - - @@ -51,17 +38,17 @@ - +

{{ handler.title | translate }}

- +

{{ 'core.settings.preferences' | translate }}

@@ -70,8 +57,8 @@
- + {{ 'core.mainmenu.logout' | translate }} diff --git a/src/core/features/mainmenu/components/user-menu/user-menu.scss b/src/core/features/mainmenu/components/user-menu/user-menu.scss index bae1b1d30..74e3b93a4 100644 --- a/src/core/features/mainmenu/components/user-menu/user-menu.scss +++ b/src/core/features/mainmenu/components/user-menu/user-menu.scss @@ -1,22 +1,6 @@ @import "~theme/globals"; :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; - } - } - } - .core-user-menu-preferences { --inner-border-width: 0; --border-width: 1px 0 0 0; diff --git a/src/core/features/settings/pages/space-usage/space-usage.html b/src/core/features/settings/pages/space-usage/space-usage.html index aa17b099a..6d1343a40 100644 --- a/src/core/features/settings/pages/space-usage/space-usage.html +++ b/src/core/features/settings/pages/space-usage/space-usage.html @@ -25,7 +25,7 @@

{{ site.fullName }}

-

{{ site.siteUrl }}

+

{{ site.siteUrlWithoutProtocol }}

{{ site.spaceUsage | coreBytesToSize }} diff --git a/src/core/features/settings/pages/synchronization/synchronization.html b/src/core/features/settings/pages/synchronization/synchronization.html index 625d67b88..31fe30857 100644 --- a/src/core/features/settings/pages/synchronization/synchronization.html +++ b/src/core/features/settings/pages/synchronization/synchronization.html @@ -37,7 +37,7 @@

{{ site.fullName }}

-

{{ site.siteUrl }}

+

{{ site.siteUrlWithoutProtocol }}

-

{{ 'core.user.details' | translate }}

+

{{ 'core.user.profile' | translate }}

@@ -14,56 +14,55 @@ -

{{ 'core.user.contact' | translate}}

+ + +

{{ 'core.user.contact' | translate}}

+
+

{{ 'core.user.email' | translate }}

-

- {{ user.email }} -

+

+ {{ user.email }} +

{{ 'core.user.phone1' | translate}}

- {{ user.phone1 }} -

+ {{ user.phone1 }} +

{{ 'core.user.phone2' | translate}}

- {{ user.phone2 }} -

+ {{ user.phone2 }} +

{{ 'core.user.address' | translate}}

- {{ formattedAddress }} -

+ {{ formattedAddress }} +

@@ -80,13 +79,17 @@
-

{{ 'core.userdetails' | translate}}

+ + +

{{ 'core.userdetails' | translate}}

+
+

{{ 'core.user.webpage' | translate}}

- {{ user.url }} -

+ {{ user.url }} +

@@ -100,11 +103,17 @@
-

{{ 'core.user.description' | translate}}

+ + +

{{ 'core.user.description' | translate}}

+
+
-

-

+

+ + +

diff --git a/src/core/services/sites.ts b/src/core/services/sites.ts index 3b77bf38b..abbcc0de6 100644 --- a/src/core/services/sites.ts +++ b/src/core/services/sites.ts @@ -1052,8 +1052,10 @@ export class CoreSitesProvider { * @param siteId The site ID. If not defined, current site (if available). * @return Promise resolved with site home ID. */ - getSiteHomeId(siteId?: string): Promise { - return this.getSite(siteId).then((site) => site.getSiteHomeId()); + async getSiteHomeId(siteId?: string): Promise { + const site = await this.getSite(siteId); + + return site.getSiteHomeId(); } /** @@ -1074,6 +1076,7 @@ export class CoreSitesProvider { const basicInfo: CoreSiteBasicInfo = { id: site.id, siteUrl: site.siteUrl, + siteUrlWithoutProtocol: site.siteUrl.replace(/^https?:\/\//, '').toLowerCase(), fullName: siteInfo?.fullname, siteName: CoreConstants.CONFIG.sitename == '' ? siteInfo?.sitename: CoreConstants.CONFIG.sitename, avatar: siteInfo?.userpictureurl, @@ -1098,9 +1101,7 @@ export class CoreSitesProvider { // Sort sites by url and fullname. sites.sort((a, b) => { // First compare by site url without the protocol. - const urlA = a.siteUrl.replace(/^https?:\/\//, '').toLowerCase(); - const urlB = b.siteUrl.replace(/^https?:\/\//, '').toLowerCase(); - const compare = urlA.localeCompare(urlB); + const compare = a.siteUrlWithoutProtocol.localeCompare(b.siteUrlWithoutProtocol); if (compare !== 0) { return compare; @@ -1756,6 +1757,7 @@ export type CoreSiteUserTokenResponse = { export type CoreSiteBasicInfo = { id: string; // Site ID. siteUrl: string; // Site URL. + siteUrlWithoutProtocol: string; // Site URL without protocol. fullName?: string; // User's full name. siteName?: string; // Site's name. avatar?: string; // User's avatar. From 898f01632d77aba4689512e29715f1351efb81b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 2 Nov 2021 11:42:28 +0100 Subject: [PATCH 13/13] MOBILE-3807 usermenu: Add badges coming from more menu --- .../components/user-menu/user-menu.html | 8 ++++++++ .../features/user/pages/profile/profile.html | 19 ++++++++++++++---- .../features/user/services/user-delegate.ts | 20 +++++++++++++++++++ 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/core/features/mainmenu/components/user-menu/user-menu.html b/src/core/features/mainmenu/components/user-menu/user-menu.html index c36894446..34a9184f7 100644 --- a/src/core/features/mainmenu/components/user-menu/user-menu.html +++ b/src/core/features/mainmenu/components/user-menu/user-menu.html @@ -45,6 +45,14 @@

{{ handler.title | translate }}

+ + + {{ handler.badgeA11yText | translate: {$a : handler.badge } }} + + +

{{ user.fullname }}

-

{{ user.address }}

+

+ {{ user.address }} +

{{ 'core.user.roles' | translate}}{{'core.labelsep' | translate}} {{ rolesFormatted }} @@ -51,7 +53,9 @@ - + + +

{{ handler.title | translate }}

+ + + {{ handler.badgeA11yText | translate: {$a : handler.badge } }} + + +
@@ -76,8 +88,7 @@
- + diff --git a/src/core/features/user/services/user-delegate.ts b/src/core/features/user/services/user-delegate.ts index 5901fa17d..b16a36787 100644 --- a/src/core/features/user/services/user-delegate.ts +++ b/src/core/features/user/services/user-delegate.ts @@ -111,6 +111,26 @@ export interface CoreUserProfileHandlerData { */ spinner?: boolean; + /** + * If the handler has badge to show or not. Only for TYPE_NEW_PAGE. + */ + showBadge?: boolean; + + /** + * Text to display on the badge. Only used if showBadge is true and only for TYPE_NEW_PAGE. + */ + badge?: string; + + /** + * Accessibility text to add on the badge. Only used if showBadge is true and only for TYPE_NEW_PAGE. + */ + badgeA11yText?: string; + + /** + * If true, the badge number is being loaded. Only used if showBadge is true and only for TYPE_NEW_PAGE. + */ + loading?: boolean; + /** * Action to do when clicked. *