Skip to content

Commit 68d3d04

Browse files
committed
MOBILE-4842 keyboard: Change keyboard events to signals
1 parent ae34d22 commit 68d3d04

File tree

7 files changed

+95
-54
lines changed

7 files changed

+95
-54
lines changed

src/core/features/comments/pages/viewer/viewer.ts

Lines changed: 7 additions & 6 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, OnDestroy, OnInit, ViewChild, AfterViewInit, inject } from '@angular/core';
15+
import { Component, OnDestroy, OnInit, ViewChild, AfterViewInit, effect, inject } from '@angular/core';
1616
import { CoreEventObserver, CoreEvents } from '@singletons/events';
1717
import { ActivatedRoute } from '@angular/router';
1818
import { CoreSites } from '@services/sites';
@@ -49,6 +49,7 @@ import { CoreDom } from '@singletons/dom';
4949
import { CoreSharedModule } from '@/core/shared.module';
5050
import { ADDON_MOD_ASSIGN_COMMENTS_COMPONENT_NAME } from '@addons/mod/assign/submission/comments/constants';
5151
import { CoreCourses } from '@features/courses/services/courses';
52+
import { CoreKeyboard } from '@singletons/keyboard';
5253

5354
/**
5455
* Page that displays comments.
@@ -93,7 +94,6 @@ export default class CoreCommentsViewerPage implements OnInit, OnDestroy, AfterV
9394
protected addDeleteCommentsAvailable = false;
9495
protected syncObserver?: CoreEventObserver;
9596
protected onlineObserver: Subscription;
96-
protected keyboardObserver: CoreEventObserver;
9797
protected viewDestroyed = false;
9898
protected scrollBottom = true;
9999
protected scrollElement?: HTMLElement;
@@ -128,9 +128,11 @@ export default class CoreCommentsViewerPage implements OnInit, OnDestroy, AfterV
128128
});
129129
});
130130

131-
this.keyboardObserver = CoreEvents.on(CoreEvents.KEYBOARD_CHANGE, (keyboardHeight: number) => {
132-
// Force when opening.
133-
this.scrollToBottom(keyboardHeight > 0);
131+
effect(() => {
132+
const shown = CoreKeyboard.getKeyboardShownSignal();
133+
134+
/// Force when opening.
135+
this.scrollToBottom(shown());
134136
});
135137
}
136138

@@ -700,7 +702,6 @@ export default class CoreCommentsViewerPage implements OnInit, OnDestroy, AfterV
700702
this.syncObserver?.off();
701703
this.onlineObserver.unsubscribe();
702704
this.viewDestroyed = true;
703-
this.keyboardObserver.off();
704705
}
705706

706707
}

src/core/features/editor/components/rich-text-editor/rich-text-editor.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
Output,
2626
EventEmitter,
2727
inject,
28+
effect,
2829
} from '@angular/core';
2930
import { IonContent } from '@ionic/angular';
3031
import { CoreSharedModule } from '@/core/shared.module';
@@ -50,6 +51,7 @@ import { CoreLoadingComponent } from '@components/loading/loading';
5051
import { CoreToasts } from '@services/overlays/toasts';
5152
import { CorePromiseUtils } from '@singletons/promise-utils';
5253
import { convertTextToHTMLElement } from '@/core/utils/create-html-element';
54+
import { CoreKeyboard } from '@singletons/keyboard';
5355

5456
/**
5557
* Component that displays a rich text editor.
@@ -85,7 +87,6 @@ export class CoreEditorRichTextEditorComponent implements AfterViewInit, OnDestr
8587

8688
@ViewChild(CoreDynamicComponent) dynamicComponent!: CoreDynamicComponent<CoreEditorBaseComponent>;
8789

88-
protected keyboardObserver?: CoreEventObserver;
8990
protected resizeListener?: CoreEventObserver;
9091
protected editorComponentClass?: Type<CoreEditorBaseComponent>;
9192
protected editorComponentData: Record<string, unknown> = {};
@@ -107,6 +108,15 @@ export class CoreEditorRichTextEditorComponent implements AfterViewInit, OnDestr
107108
constructor() {
108109
// Generate a "unique" ID based on timestamp.
109110
this.pageInstance = `app_${Date.now()}`;
111+
112+
effect(() => {
113+
// Signal will be triggered when the keyboard is shown or hidden.
114+
CoreKeyboard.getKeyboardShownSignal();
115+
116+
// Opening or closing the keyboard also calls the resize function, but sometimes the resize is called too soon.
117+
// Check the height again, now the window height should have been updated.
118+
this.maximizeEditorSize();
119+
});
110120
}
111121

112122
/**
@@ -188,20 +198,13 @@ export class CoreEditorRichTextEditorComponent implements AfterViewInit, OnDestr
188198
this.controlSubscription = this.control?.valueChanges.subscribe((newValue) => {
189199
this.onControlValueChange(newValue);
190200
});
191-
192-
// Opening or closing the keyboard also calls the resize function, but sometimes the resize is called too soon.
193-
// Check the height again, now the window height should have been updated.
194-
this.keyboardObserver = CoreEvents.on(CoreEvents.KEYBOARD_CHANGE, () => {
195-
this.maximizeEditorSize();
196-
});
197201
}
198202

199203
/**
200204
* @inheritdoc
201205
*/
202206
ngOnDestroy(): void {
203207
this.resizeListener?.off();
204-
this.keyboardObserver?.off();
205208
this.controlSubscription?.unsubscribe();
206209
this.resetObserver?.off();
207210
this.labelObserver?.disconnect();

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

Lines changed: 18 additions & 15 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, ViewChild } from '@angular/core';
15+
import { Component, OnInit, OnDestroy, ViewChild, effect } from '@angular/core';
1616
import { IonTabs } from '@ionic/angular';
1717
import { BackButtonEvent } from '@ionic/core';
1818
import { Subscription } from 'rxjs';
@@ -44,6 +44,7 @@ import {
4444
import { CoreSharedModule } from '@/core/shared.module';
4545
import { CoreMainMenuUserButtonComponent } from '../../components/user-menu-button/user-menu-button';
4646
import { BackButtonPriority } from '@/core/constants';
47+
import { CoreKeyboard } from '@singletons/keyboard';
4748

4849
const ANIMATION_DURATION = 500;
4950

@@ -119,6 +120,22 @@ export default class CoreMainMenuPage implements OnInit, OnDestroy {
119120
this.isMainScreen = !this.mainTabs?.outlet.canGoBack();
120121
this.updateVisibility();
121122
});
123+
124+
if (CorePlatform.isIOS()) {
125+
effect(() => {
126+
const shown = CoreKeyboard.getKeyboardShownSignal();
127+
// In iOS, the resize event is triggered before the keyboard is opened/closed and not triggered again once done.
128+
// Init handlers again once keyboard is closed since the resize event doesn't have the updated height.
129+
if (!shown) {
130+
this.updateHandlers();
131+
132+
// If the device is slow it can take a bit more to update the window height. Retry in a few ms.
133+
setTimeout(() => {
134+
this.updateHandlers();
135+
}, 250);
136+
}
137+
});
138+
}
122139
}
123140

124141
/**
@@ -150,20 +167,6 @@ export default class CoreMainMenuPage implements OnInit, OnDestroy {
150167
});
151168
document.addEventListener('ionBackButton', this.backButtonFunction);
152169

153-
if (CorePlatform.isIOS()) {
154-
// In iOS, the resize event is triggered before the keyboard is opened/closed and not triggered again once done.
155-
// Init handlers again once keyboard is closed since the resize event doesn't have the updated height.
156-
this.keyboardObserver = CoreEvents.on(CoreEvents.KEYBOARD_CHANGE, (kbHeight: number) => {
157-
if (kbHeight === 0) {
158-
this.updateHandlers();
159-
160-
// If the device is slow it can take a bit more to update the window height. Retry in a few ms.
161-
setTimeout(() => {
162-
this.updateHandlers();
163-
}, 250);
164-
}
165-
});
166-
}
167170
CoreEvents.trigger(CoreEvents.MAIN_HOME_LOADED);
168171
}
169172

src/core/initializers/subscribe-to-keyboard-events.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ export default function(): void {
2525
// Execute callbacks in the Angular zone, so change detection doesn't stop working.
2626
keyboard.onKeyboardShow().subscribe(data => zone.run(() => CoreKeyboard.onKeyboardShow(data.keyboardHeight)));
2727
keyboard.onKeyboardHide().subscribe(() => zone.run(() => CoreKeyboard.onKeyboardHide()));
28-
keyboard.onKeyboardWillShow().subscribe(() => zone.run(() => CoreKeyboard.onKeyboardWillShow()));
28+
keyboard.onKeyboardWillShow().subscribe((data) => zone.run(() => CoreKeyboard.onKeyboardWillShow(data.keyboardHeight)));
2929
keyboard.onKeyboardWillHide().subscribe(() => zone.run(() => CoreKeyboard.onKeyboardWillHide()));
3030
}

src/core/services/app.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export class CoreAppProvider {
9090
/**
9191
* Closes the keyboard.
9292
*
93-
* @deprecated sinde 4.5.0. Use CoreKeyboard.closeKeyboard instead.
93+
* @deprecated since 4.5.0. Use CoreKeyboard.closeKeyboard instead.
9494
*/
9595
closeKeyboard(): void {
9696
CoreKeyboard.close();

src/core/singletons/events.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ export class CoreEvents {
107107
static readonly IAB_MESSAGE = 'inappbrowser_message';
108108
static readonly APP_LAUNCHED_URL = 'app_launched_url'; // App opened with a certain URL (custom URL scheme).
109109
static readonly FILE_SHARED = 'file_shared';
110+
/**
111+
* @deprecated since 5.0.0. Use CoreKeyboard.getKeyboardShownSignal signal.
112+
*/
110113
static readonly KEYBOARD_CHANGE = 'keyboard_change';
111114
static readonly ORIENTATION_CHANGE = 'orientation_change';
112115
static readonly SEND_ON_ENTER_CHANGED = 'send_on_enter_changed';

src/core/singletons/keyboard.ts

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

15+
import { effect, Signal, signal } from '@angular/core';
1516
import { CorePlatform } from '@services/platform';
1617
import { Keyboard } from '@singletons';
1718
import { CoreEvents } from '@singletons/events';
@@ -21,13 +22,16 @@ import { CoreEvents } from '@singletons/events';
2122
*/
2223
export class CoreKeyboard {
2324

24-
protected static isKeyboardShown = false;
25-
protected static keyboardOpening = false;
26-
protected static keyboardClosing = false;
25+
protected static readonly IS_KEYBOARD_SHOWN = signal(false);
26+
protected static readonly KEYBOARD_OPENING = signal(false);
27+
protected static readonly KEYBOARD_CLOSING = signal(false);
28+
protected static readonly KEYBOARD_HEIGHT = signal(0);
2729

2830
// Avoid creating singleton instances.
2931
private constructor() {
30-
// Nothing to do.
32+
effect(() => {
33+
document.body.classList.toggle('keyboard-is-open', CoreKeyboard.IS_KEYBOARD_SHOWN());
34+
});
3135
}
3236

3337
/**
@@ -49,53 +53,80 @@ export class CoreKeyboard {
4953
}
5054
}
5155

56+
/**
57+
* Get a signal that indicates whether the keyboard is shown or not.
58+
*
59+
* @returns Signal indicating whether the keyboard is shown.
60+
*/
61+
static getKeyboardShownSignal(): Signal<boolean> {
62+
return CoreKeyboard.IS_KEYBOARD_SHOWN.asReadonly();
63+
}
64+
65+
/**
66+
* Get a signal that indicates the keyboard height.
67+
*
68+
* @returns Signal indicating the keyboard height.
69+
*/
70+
static getKeyboardHeightSignal(): Signal<number> {
71+
return CoreKeyboard.KEYBOARD_HEIGHT.asReadonly();
72+
}
73+
5274
/**
5375
* Notify that Keyboard has been shown.
5476
*
5577
* @param keyboardHeight Keyboard height.
5678
*/
5779
static onKeyboardShow(keyboardHeight: number): void {
58-
document.body.classList.add('keyboard-is-open');
59-
CoreKeyboard.setKeyboardShown(true);
6080
// Error on iOS calculating size.
61-
// More info: https://github.com/ionic-team/ionic-plugin-keyboard/issues/276 .
62-
CoreEvents.trigger(CoreEvents.KEYBOARD_CHANGE, keyboardHeight);
81+
// More info: https://github.com/ionic-team/ionic-plugin-keyboard/issues/276
82+
CoreKeyboard.setKeyboardShown(true, keyboardHeight);
6383
}
6484

6585
/**
6686
* Notify that Keyboard has been hidden.
6787
*/
6888
static onKeyboardHide(): void {
69-
document.body.classList.remove('keyboard-is-open');
70-
CoreKeyboard.setKeyboardShown(false);
71-
CoreEvents.trigger(CoreEvents.KEYBOARD_CHANGE, 0);
89+
CoreKeyboard.setKeyboardShown(false, 0);
7290
}
7391

7492
/**
7593
* Notify that Keyboard is about to be shown.
94+
*
95+
* @param keyboardHeight Keyboard height.
7696
*/
77-
static onKeyboardWillShow(): void {
78-
CoreKeyboard.keyboardOpening = true;
79-
CoreKeyboard.keyboardClosing = false;
97+
static onKeyboardWillShow(keyboardHeight?: number): void {
98+
CoreKeyboard.KEYBOARD_OPENING.set(true);
99+
CoreKeyboard.KEYBOARD_CLOSING.set(false);
100+
101+
if (keyboardHeight !== undefined) {
102+
this.KEYBOARD_HEIGHT.set(keyboardHeight);
103+
}
80104
}
81105

82106
/**
83107
* Notify that Keyboard is about to be hidden.
84108
*/
85109
static onKeyboardWillHide(): void {
86-
CoreKeyboard.keyboardOpening = false;
87-
CoreKeyboard.keyboardClosing = true;
110+
CoreKeyboard.KEYBOARD_OPENING.set(false);
111+
CoreKeyboard.KEYBOARD_CLOSING.set(true);
112+
113+
this.KEYBOARD_HEIGHT.set(0);
88114
}
89115

90116
/**
91117
* Set keyboard shown or hidden.
92118
*
93119
* @param shown Whether the keyboard is shown or hidden.
120+
* @param keyboardHeight Keyboard height.
94121
*/
95-
protected static setKeyboardShown(shown: boolean): void {
96-
CoreKeyboard.isKeyboardShown = shown;
97-
CoreKeyboard.keyboardOpening = false;
98-
CoreKeyboard.keyboardClosing = false;
122+
protected static setKeyboardShown(shown: boolean, keyboardHeight: number): void {
123+
CoreKeyboard.IS_KEYBOARD_SHOWN.set(shown);
124+
CoreKeyboard.KEYBOARD_OPENING.set(false);
125+
CoreKeyboard.KEYBOARD_CLOSING.set(false);
126+
this.KEYBOARD_HEIGHT.set(keyboardHeight);
127+
128+
// eslint-disable-next-line @typescript-eslint/no-deprecated
129+
CoreEvents.trigger(CoreEvents.KEYBOARD_CHANGE, keyboardHeight);
99130
}
100131

101132
/**
@@ -104,7 +135,7 @@ export class CoreKeyboard {
104135
* @returns Whether keyboard is closing (animating).
105136
*/
106137
static isKeyboardClosing(): boolean {
107-
return CoreKeyboard.keyboardClosing;
138+
return CoreKeyboard.KEYBOARD_CLOSING();
108139
}
109140

110141
/**
@@ -113,7 +144,7 @@ export class CoreKeyboard {
113144
* @returns Whether keyboard is opening (animating).
114145
*/
115146
static isKeyboardOpening(): boolean {
116-
return CoreKeyboard.keyboardOpening;
147+
return CoreKeyboard.KEYBOARD_OPENING();
117148
}
118149

119150
/**
@@ -122,7 +153,7 @@ export class CoreKeyboard {
122153
* @returns Whether keyboard is visible.
123154
*/
124155
static isKeyboardVisible(): boolean {
125-
return CoreKeyboard.isKeyboardShown;
156+
return CoreKeyboard.IS_KEYBOARD_SHOWN();
126157
}
127158

128159
}

0 commit comments

Comments
 (0)