Skip to content

Commit d7e2cd9

Browse files
authored
Merge pull request #4579 from crazyserver/MOBILE-4842
Mobile 4842
2 parents 5e3d07b + ef2285e commit d7e2cd9

File tree

8 files changed

+86
-60
lines changed

8 files changed

+86
-60
lines changed

src/addons/mod/forum/components/post/post.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges
102102
@Input({ transform: toBoolean }) highlight = false;
103103
@Output() onPostChange: EventEmitter<void> = new EventEmitter<void>(); // Event emitted when a reply is posted or modified.
104104

105-
readonly formElement = viewChild.required<ElementRef>('replyFormEl');
105+
readonly formElement = viewChild<ElementRef>('replyFormEl');
106106

107107
messageControl = new FormControl<string | null>(null);
108108

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@if ((alwaysShow || (isMainScreen && menuPage?.tabsPlacement === 'bottom')) && siteInfo) {
2-
<core-user-avatar [site]="siteInfo" class="core-bar-button-image clickable" [linkProfile]="false"
1+
@if (showButton()) {
2+
<core-user-avatar [site]="siteInfo()" class="core-bar-button-image clickable" [linkProfile]="false"
33
(ariaButtonClick)="openUserMenu($event)" [attr.aria-label]="'core.user.useraccount' | translate" />
44
}

src/core/features/mainmenu/components/user-menu-button/user-menu-button.ts

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
import { Component, Input, OnInit, inject } from '@angular/core';
15+
import { Component, OnInit, inject, input, linkedSignal, signal } from '@angular/core';
1616
import { CoreSiteInfo } from '@classes/sites/unauthenticated-site';
1717
import { IonRouterOutlet } from '@ionic/angular';
1818
import { CoreSites } from '@services/sites';
1919
import { CoreModals } from '@services/overlays/modals';
2020
import CoreMainMenuPage from '@features/mainmenu/pages/menu/menu';
2121
import { toBoolean } from '@/core/transforms/boolean';
2222
import { CoreSharedModule } from '@/core/shared.module';
23+
import { CoreMainMenuPlacement } from '@features/mainmenu/constants';
2324

2425
/**
2526
* Component to display an avatar on the header to open user menu.
@@ -36,23 +37,43 @@ import { CoreSharedModule } from '@/core/shared.module';
3637
})
3738
export class CoreMainMenuUserButtonComponent implements OnInit {
3839

39-
@Input({ transform: toBoolean }) alwaysShow = false;
40+
readonly alwaysShow = input(false, { transform: toBoolean });
4041

41-
siteInfo?: CoreSiteInfo;
42-
isMainScreen = false;
42+
readonly siteInfo = signal<CoreSiteInfo | undefined>(undefined);
43+
readonly showButton = linkedSignal(() => this.shouldShowButton());
4344

4445
protected routerOutlet = inject(IonRouterOutlet);
4546
protected menuPage = inject(CoreMainMenuPage, { optional: true });
4647

4748
constructor() {
48-
this.siteInfo = CoreSites.getCurrentSite()?.getInfo();
49+
this.siteInfo.set(CoreSites.getCurrentSite()?.getInfo());
4950
}
5051

5152
/**
5253
* @inheritdoc
5354
*/
5455
ngOnInit(): void {
55-
this.isMainScreen = !this.routerOutlet.canGoBack();
56+
this.showButton.set(this.shouldShowButton());
57+
}
58+
59+
/**
60+
* Determine if the button should be shown.
61+
*
62+
* @returns True if the button should be shown, false otherwise.
63+
*/
64+
protected shouldShowButton(): boolean {
65+
if (!this.siteInfo()) {
66+
return false;
67+
}
68+
69+
if (this.alwaysShow()) {
70+
return true;
71+
}
72+
73+
const isMainScreen = !this.routerOutlet.canGoBack();
74+
const tabsPlacement = this.menuPage?.tabsPlacement();
75+
76+
return isMainScreen && tabsPlacement === CoreMainMenuPlacement.BOTTOM;
5677
}
5778

5879
/**

src/core/features/mainmenu/pages/menu/menu.html

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
<ion-tabs #mainTabs [hidden]="!showTabs" [class]="'placement-' + tabsPlacement"
2-
[class.tabshidden]="!isMainScreen && tabsPlacement === 'bottom'" (ionTabsDidChange)="tabChanged($event)">
3-
<ion-tab-bar [slot]="tabsPlacement === 'side' ? 'top' : 'bottom'" class="mainmenu-tabs"
4-
[class.slide-in-bottom-animation]="visibility() === 'visible'" [class.slide-out-bottom-animation]="visibility() === 'hidden'"
5-
[hidden]="hiddenAnimationFinished()">
6-
<core-loading [hideUntil]="loaded" [placeholderType]="tabsPlacement === 'bottom' ? 'rowwrap' : 'column'"
1+
<ion-tabs #mainTabs [hidden]="!showTabs" [class]="'placement-' + tabsPlacement()" (ionTabsDidChange)="tabChanged($event)"
2+
[class.tabshidden]="hiddenAnimationFinished()">
3+
<ion-tab-bar [slot]="tabsPlacement() === 'side' ? 'top' : 'bottom'" class="mainmenu-tabs"
4+
[class.slide-in-bottom-animation]="visibility() === 'visible'" [class.slide-out-bottom-animation]="visibility() === 'hidden'">
5+
<core-loading [hideUntil]="loaded()" [placeholderType]="tabsPlacement() === 'bottom' ? 'rowwrap' : 'column'"
76
[placeholderLimit]="loadingTabsLength" />
8-
@if (loaded && tabsPlacement === 'side') {
7+
8+
@if (loaded() && tabsPlacement() === 'side') {
99
<core-user-menu-button [alwaysShow]="true" />
1010
}
1111

1212
<ion-tab-button *ngFor="let tab of tabs" (keydown)="tabAction.keyDown(tab.page, $event)" (keyup)="tabAction.keyUp(tab.page, $event)"
13-
[hidden]="!loaded && tab.hide" [tab]="tab.page" [disabled]="tab.hide" layout="label-hide" class="{{tab.class}}"
13+
[hidden]="!loaded() && tab.hide" [tab]="tab.page" [disabled]="tab.hide" layout="label-hide" class="{{tab.class}}"
1414
[selected]="tab.page === selectedTab" [tabindex]="selectedTab === tab.page ? 0 : -1" [attr.aria-controls]="tab.id">
1515
<ion-icon class="core-tab-icon" [name]="tab.icon" aria-hidden="true" />
1616
<ion-label aria-hidden="true">{{ tab.title | translate }}</ion-label>
@@ -26,7 +26,7 @@
2626
</ion-tab-button>
2727

2828
<ion-tab-button (keydown)="tabAction.keyDown(morePageName, $event)" (keyup)="tabAction.keyUp(morePageName, $event)"
29-
[hidden]="!loaded" [tab]="morePageName" layout="label-hide" [tabindex]="selectedTab === morePageName ? 0 : -1"
29+
[hidden]="!loaded()" [tab]="morePageName" layout="label-hide" [tabindex]="selectedTab === morePageName ? 0 : -1"
3030
[attr.aria-controls]="morePageName">
3131
<ion-icon class="core-tab-icon" name="ellipsis-horizontal" aria-hidden="true" />
3232
<ion-label aria-hidden="true">{{ 'core.more' | translate }}</ion-label>

src/core/features/mainmenu/pages/menu/menu.scss

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,8 @@ ion-tabs {
142142
} @else {
143143
&.tabshidden ion-tab-bar {
144144
pointer-events: none;
145-
146-
ion-tab-button {
147-
height: auto;
148-
}
145+
height: 0px;
146+
border: 0px;
149147
}
150148
}
151149
}

src/core/features/mainmenu/pages/menu/menu.ts

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
import { Component, OnInit, OnDestroy, effect, viewChild, signal, ElementRef, inject } from '@angular/core';
15+
import { Component, OnInit, OnDestroy, effect, viewChild, signal, ElementRef, inject, computed } from '@angular/core';
1616
import { IonTabs } from '@ionic/angular';
1717
import { BackButtonEvent } from '@ionic/core';
1818
import { Subscription } from 'rxjs';
@@ -59,17 +59,28 @@ import { CoreKeyboard } from '@singletons/keyboard';
5959
})
6060
export default class CoreMainMenuPage implements OnInit, OnDestroy {
6161

62+
readonly tabsPlacement = signal<CoreMainMenuPlacement>(CoreMainMenuPlacement.BOTTOM);
63+
readonly isMainScreen = signal(false);
64+
readonly visibility = computed(() => {
65+
const tabsPlacement = this.tabsPlacement();
66+
const isMainScreen = this.isMainScreen();
67+
68+
const visibility = tabsPlacement === CoreMainMenuPlacement.SIDE
69+
? ''
70+
: (isMainScreen ? 'visible' : 'hidden');
71+
72+
return visibility;
73+
});
74+
75+
readonly hiddenAnimationFinished = signal(false);
76+
6277
tabs: CoreMainMenuHandlerToDisplay[] = [];
6378
allHandlers?: CoreMainMenuHandlerToDisplay[];
64-
loaded = false;
79+
readonly loaded = signal(false);
6580
showTabs = false;
66-
tabsPlacement: CoreMainMenuPlacement = CoreMainMenuPlacement.BOTTOM;
6781
morePageName = MAIN_MENU_MORE_PAGE_NAME;
6882
selectedTab?: string;
69-
isMainScreen = false;
7083
moreBadge = false;
71-
readonly visibility = signal('hidden');
72-
readonly hiddenAnimationFinished = signal(true);
7384
loadingTabsLength = this.getLoadingTabsLength();
7485

7586
protected subscription?: Subscription;
@@ -96,8 +107,7 @@ export default class CoreMainMenuPage implements OnInit, OnDestroy {
96107
this.navSubscription = Router.events
97108
.pipe(filter(event => event instanceof NavigationEnd))
98109
.subscribe(() => {
99-
this.isMainScreen = !this.mainTabs().outlet?.canGoBack();
100-
this.updateVisibility();
110+
this.isMainScreen.set(!this.mainTabs().outlet?.canGoBack());
101111
});
102112

103113
if (CorePlatform.isIOS()) {
@@ -115,6 +125,12 @@ export default class CoreMainMenuPage implements OnInit, OnDestroy {
115125
}
116126
});
117127
}
128+
129+
effect(() => {
130+
this.visibility();
131+
// Tabs changed visibility, reset hidden animation.
132+
this.hiddenAnimationFinished.set(false);
133+
});
118134
}
119135

120136
/**
@@ -125,8 +141,7 @@ export default class CoreMainMenuPage implements OnInit, OnDestroy {
125141

126142
this.initAfterLoginNavigations();
127143

128-
this.isMainScreen = !this.mainTabs().outlet?.canGoBack();
129-
this.updateVisibility();
144+
this.isMainScreen.set(!this.mainTabs().outlet?.canGoBack());
130145

131146
this.subscription = CoreMainMenuDelegate.getHandlersObservable().subscribe((handlers) => {
132147
const previousHandlers = this.allHandlers;
@@ -150,7 +165,8 @@ export default class CoreMainMenuPage implements OnInit, OnDestroy {
150165

151166
const tabBar = this.hostElement.querySelector('ion-tab-bar');
152167
tabBar?.addEventListener('animationend', (ev) => {
153-
if (ev.animationName == 'slideOutBottom') {
168+
if (ev.animationName === 'slideOutBottom' &&
169+
!this.isMainScreen() && this.tabsPlacement() === CoreMainMenuPlacement.BOTTOM) {
154170
this.hiddenAnimationFinished.set(true);
155171
}
156172

@@ -167,8 +183,8 @@ export default class CoreMainMenuPage implements OnInit, OnDestroy {
167183
if (!this.allHandlers) {
168184
return;
169185
}
170-
this.tabsPlacement = CoreMainMenu.getTabPlacement();
171-
this.updateVisibility();
186+
187+
this.tabsPlacement.set(CoreMainMenu.getTabPlacement());
172188

173189
this.loadingTabsLength = this.getLoadingTabsLength();
174190

@@ -207,9 +223,9 @@ export default class CoreMainMenuPage implements OnInit, OnDestroy {
207223
}
208224

209225
const mainMenuTab = CoreNavigator.getCurrentMainMenuTab();
210-
this.loaded = CoreMainMenuDelegate.areHandlersLoaded();
226+
this.loaded.set(CoreMainMenuDelegate.areHandlersLoaded());
211227

212-
if (this.loaded && (!mainMenuTab || removedHandlersPages.includes(mainMenuTab))) {
228+
if (this.loaded() && (!mainMenuTab || removedHandlersPages.includes(mainMenuTab))) {
213229
// No tab selected or handler no longer available, select the first one.
214230
await CoreWait.nextTick();
215231

@@ -231,8 +247,9 @@ export default class CoreMainMenuPage implements OnInit, OnDestroy {
231247
* @returns The total number of loading tabs to display.
232248
*/
233249
protected getLoadingTabsLength(): number {
234-
return CoreMainMenu.getNumItems() +
235-
(this.tabsPlacement === CoreMainMenuPlacement.BOTTOM ? 1 : 2); // +1 for the "More" tab and user button.
250+
const isBottomPlacement = this.tabsPlacement() === CoreMainMenuPlacement.BOTTOM;
251+
252+
return CoreMainMenu.getNumItems() + (isBottomPlacement ? 1 : 2); // +1 for the "More" tab and user button.
236253
}
237254

238255
/**
@@ -307,22 +324,6 @@ export default class CoreMainMenuPage implements OnInit, OnDestroy {
307324
this.selectHistory.push(event.tab);
308325
}
309326

310-
/**
311-
* Update menu visibility.
312-
*/
313-
protected updateVisibility(): void {
314-
const visibility = this.tabsPlacement === CoreMainMenuPlacement.SIDE
315-
? ''
316-
: (this.isMainScreen ? 'visible' : 'hidden');
317-
318-
if (visibility === this.visibility()) {
319-
return;
320-
}
321-
322-
this.hiddenAnimationFinished.set(false);
323-
this.visibility.set(visibility);
324-
}
325-
326327
/**
327328
* Back button clicked.
328329
*
@@ -401,7 +402,7 @@ class CoreMainMenuRoleTab extends CoreAriaRoleTab<CoreMainMenuPage> {
401402
* @inheritdoc
402403
*/
403404
isHorizontal(): boolean {
404-
return this.componentInstance.tabsPlacement === CoreMainMenuPlacement.BOTTOM;
405+
return this.componentInstance.tabsPlacement() === CoreMainMenuPlacement.BOTTOM;
405406
}
406407

407408
/**

src/theme/animations/slide.scss

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,12 @@
9090
@keyframes slideInBottom {
9191
0% {
9292
height: 0px;
93-
visibility: hidden;
93+
opacity: 0;
9494
transform: translateY(100%);
95-
border: 0px;
9695
}
9796

9897
100% {
99-
visibility: visible;
98+
opacity: 1;
10099
}
101100
}
102101

@@ -107,11 +106,13 @@
107106
@keyframes slideOutBottom {
108107
0% {
109108
transform: translateY(0);
109+
opacity: 1;
110+
110111
}
111112

112113
100% {
113114
height: 0px;
114-
visibility: hidden;
115+
opacity: 0;
115116
transform: translateY(100%);
116117
}
117118
}

src/theme/theme.base.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,11 @@ ion-tabs.placement-side .tabs-inner {
172172
--ion-safe-area-left: 0px;
173173
}
174174

175+
:root[dir=rtl] ion-tabs.placement-side .tabs-inner {
176+
--ion-safe-area-right: 0px;
177+
--ion-safe-area-left: var(--root-safe-area-left);
178+
}
179+
175180
ion-tabs.placement-bottom .tabs-inner {
176181
--ion-safe-area-bottom: 0px;
177182
}

0 commit comments

Comments
 (0)