MOBILE-4616 keyboard: Fix dom-app circular dep by separating keyboard

main
Pau Ferrer Ocaña 2024-07-12 16:20:43 +02:00
parent 746e60f51e
commit a9f4a48356
17 changed files with 185 additions and 72 deletions

View File

@ -22,13 +22,13 @@ import {
AddonMessages,
} from '../../services/messages';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreApp } from '@services/app';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { ActivatedRoute } from '@angular/router';
import { Translate } from '@singletons';
import { CoreScreen } from '@services/screen';
import { CoreNavigator } from '@services/navigator';
import { CoreSplitViewComponent } from '@components/split-view/split-view';
import { CoreKeyboard } from '@singletons/keyboard';
/**
* Page that displays the list of contacts.
@ -205,15 +205,17 @@ export class AddonMessagesContacts35Page implements OnInit, OnDestroy {
* @param query Text to search for.
* @returns Resolved when done.
*/
search(query: string): Promise<void> {
CoreApp.closeKeyboard();
async search(query: string): Promise<void> {
CoreKeyboard.close();
this.loaded = false;
this.loadingMessage = this.searchingMessages;
return this.performSearch(query).finally(() => {
try {
await this.performSearch(query);
} finally {
this.loaded = true;
});
}
}
/**

View File

@ -34,7 +34,6 @@ import { CoreDomUtils } from '@services/utils/dom';
import { CoreUtils } from '@services/utils/utils';
import { CoreTextUtils } from '@services/utils/text';
import { CoreLogger } from '@singletons/logger';
import { CoreApp } from '@services/app';
import { CoreInfiniteLoadingComponent } from '@components/infinite-loading/infinite-loading';
import { Md5 } from 'ts-md5/dist/md5';
import moment from 'moment-timezone';
@ -45,6 +44,7 @@ import { CoreIonLoadingElement } from '@classes/ion-loading';
import { ActivatedRoute } from '@angular/router';
import { CoreConstants } from '@/core/constants';
import { CoreDom } from '@singletons/dom';
import { CoreKeyboard } from '@singletons/keyboard';
/**
* Page that displays a message discussion page.
@ -1177,7 +1177,7 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView
// Only close the keyboard if an error happens.
// We want the user to be able to send multiple messages without the keyboard being closed.
CoreApp.closeKeyboard();
CoreKeyboard.close();
CoreDomUtils.showErrorModalDefault(error, 'addon.messages.messagenotsent', true);
this.removeMessage(message.hash!);

View File

@ -23,7 +23,6 @@ import {
} from '../../services/messages';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreUtils } from '@services/utils/utils';
import { CoreApp } from '@services/app';
import { ActivatedRoute, Params } from '@angular/router';
import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications';
import { CorePushNotificationsDelegate } from '@features/pushnotifications/services/push-delegate';
@ -34,6 +33,7 @@ import { CoreScreen } from '@services/screen';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
import { CorePlatform } from '@services/platform';
import { CoreSplitViewComponent } from '@components/split-view/split-view';
import { CoreKeyboard } from '@singletons/keyboard';
/**
* Page that displays the list of discussions.
@ -234,7 +234,7 @@ export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy {
* @returns Resolved when done.
*/
async searchMessage(query: string): Promise<void> {
CoreApp.closeKeyboard();
CoreKeyboard.close();
this.loaded = false;
this.loadingMessage = this.search.loading;

View File

@ -22,10 +22,10 @@ import {
AddonMessages,
} from '../../services/messages';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreApp } from '@services/app';
import { CoreNavigator } from '@services/navigator';
import { CoreScreen } from '@services/screen';
import { CoreSplitViewComponent } from '@components/split-view/split-view';
import { CoreKeyboard } from '@singletons/keyboard';
/**
* Page for searching users.
@ -124,7 +124,7 @@ export class AddonMessagesSearchPage implements OnDestroy {
* @returns Resolved when done.
*/
async search(query: string, loadMore?: 'contacts' | 'noncontacts' | 'messages', infiniteComplete?: () => void): Promise<void> {
CoreApp.closeKeyboard();
CoreKeyboard.close();
this.query = query;
this.disableSearch = true;

View File

@ -16,7 +16,6 @@ import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core';
import { CoreSendMessageFormComponent } from '@components/send-message-form/send-message-form';
import { CanLeave } from '@guards/can-leave';
import { IonContent } from '@ionic/angular';
import { CoreApp } from '@services/app';
import { CoreNetwork } from '@services/network';
import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites';
@ -30,6 +29,7 @@ import { AddonModChat, AddonModChatUser } from '../../services/chat';
import { AddonModChatFormattedMessage, AddonModChatHelper } from '../../services/chat-helper';
import { CoreTime } from '@singletons/time';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { CoreKeyboard } from '@singletons/keyboard';
/**
* Page that displays a chat session.
@ -323,7 +323,7 @@ export class AddonModChatChatPage implements OnInit, OnDestroy, CanLeave {
} catch (error) {
// Only close the keyboard if an error happens, we want the user to be able to send multiple
// messages without the keyboard being closed.
CoreApp.closeKeyboard();
CoreKeyboard.close();
this.newMessage = text;
CoreDomUtils.showErrorModalDefault(error, 'addon.mod_chat.errorwhilesendingmessage', true);

View File

@ -14,11 +14,11 @@
import { AddonNotes, AddonNotesPublishState } from '@addons/notes/services/notes';
import { Component, ViewChild, ElementRef, Input } from '@angular/core';
import { CoreApp } from '@services/app';
import { CoreSites } from '@services/sites';
import { CoreDomUtils, ToastDuration } from '@services/utils/dom';
import { CoreForms } from '@singletons/form';
import { ModalController } from '@singletons';
import { CoreKeyboard } from '@singletons/keyboard';
/**
* Component that displays a text area for composing a note.
@ -45,7 +45,7 @@ export class AddonNotesAddComponent {
e.preventDefault();
e.stopPropagation();
CoreApp.closeKeyboard();
CoreKeyboard.close();
const loadingModal = await CoreDomUtils.showModalLoading('core.sending', true);
// Freeze the add note button.

View File

@ -38,11 +38,11 @@ import { CoreError } from '@classes/errors/error';
import { CoreCommentsOffline } from '@features/comments/services/comments-offline';
import { CoreCommentsDBRecord } from '@features/comments/services/database/comments';
import { CoreTimeUtils } from '@services/utils/time';
import { CoreApp } from '@services/app';
import { CoreNetwork } from '@services/network';
import moment from 'moment-timezone';
import { Subscription } from 'rxjs';
import { CoreAnimations } from '@components/animations';
import { CoreKeyboard } from '@singletons/keyboard';
/**
* Page that displays comments.
@ -309,7 +309,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy {
* @param text Comment text to add.
*/
async addComment(text: string): Promise<void> {
CoreApp.closeKeyboard();
CoreKeyboard.close();
const loadingModal = await CoreDomUtils.showModalLoading('core.sending', true);
// Freeze the add comment button.
this.sending = true;

View File

@ -36,6 +36,7 @@ import { CoreSitesFactory } from '@services/sites-factory';
import { EMAIL_SIGNUP_FEATURE_NAME, FORGOTTEN_PASSWORD_FEATURE_NAME } from '@features/login/constants';
import { CoreCustomURLSchemes } from '@services/urlschemes';
import { CoreSiteError } from '@classes/errors/siteerror';
import { CoreKeyboard } from '@singletons/keyboard';
/**
* Page to enter the user credentials.
@ -253,7 +254,7 @@ export class CoreLoginCredentialsPage implements OnInit, OnDestroy {
e?.preventDefault();
e?.stopPropagation();
CoreApp.closeKeyboard();
CoreKeyboard.close();
// Get input data.
const siteUrl = this.site.getURL();

View File

@ -15,7 +15,6 @@
import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { CoreApp } from '@services/app';
import { CoreNetwork } from '@services/network';
import { CoreSiteBasicInfo, CoreSites, CoreSitesReadingStrategy } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
@ -33,6 +32,7 @@ import { Translate } from '@singletons';
import { SafeHtml } from '@angular/platform-browser';
import { CoreSitePublicConfigResponse } from '@classes/sites/unauthenticated-site';
import { FORGOTTEN_PASSWORD_FEATURE_NAME } from '@features/login/constants';
import { CoreKeyboard } from '@singletons/keyboard';
/**
* Page to enter the user password to reconnect to a site.
@ -217,7 +217,7 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy {
e.preventDefault();
e.stopPropagation();
CoreApp.closeKeyboard();
CoreKeyboard.close();
// Get input data.
const password = this.credForm.value.password;

View File

@ -15,7 +15,6 @@
import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import { FormBuilder, FormGroup, ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms';
import { CoreApp } from '@services/app';
import { CoreNetwork } from '@services/network';
import { CoreConfig } from '@services/config';
import { CoreSites, CoreSiteCheckResponse, CoreLoginSiteInfo, CoreSitesDemoSiteData } from '@services/sites';
@ -49,6 +48,7 @@ import { CoreReferrer } from '@services/referrer';
import { CoreSitesFactory } from '@services/sites-factory';
import { ONBOARDING_DONE } from '@features/login/constants';
import { CoreUnauthenticatedSite } from '@classes/sites/unauthenticated-site';
import { CoreKeyboard } from '@singletons/keyboard';
/**
* Site (url) chooser when adding a new site.
@ -285,7 +285,7 @@ export class CoreLoginSitePage implements OnInit {
e?.preventDefault();
e?.stopPropagation();
CoreApp.closeKeyboard();
CoreKeyboard.close();
if (!url) {
CoreDomUtils.showErrorModal('core.login.siteurlrequired', true);

View File

@ -14,7 +14,6 @@
import { Component, OnInit } from '@angular/core';
import { CoreApp } from '@services/app';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreUtils } from '@services/utils/utils';
import { CoreTextUtils } from '@services/utils/text';
@ -25,6 +24,7 @@ import { CoreNavigator } from '@services/navigator';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
import { CoreTime } from '@singletons/time';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { CoreKeyboard } from '@singletons/keyboard';
/**
* Page that displays most used tags and allows searching.
@ -153,7 +153,7 @@ export class CoreTagSearchPage implements OnInit {
}
this.logSearch = CoreTime.once(() => this.performLogSearch());
CoreApp.closeKeyboard();
CoreKeyboard.close();
return this.fetchTags().catch((error) => {
CoreDomUtils.showErrorModalDefault(error, 'Error loading tags.');

View File

@ -14,7 +14,6 @@
import { AfterViewInit, Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { CoreApp } from '@services/app';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreNavigator } from '@services/navigator';
import { CoreListItemsManager } from '@classes/items-management/list-items-manager';
@ -25,6 +24,7 @@ import { CoreUserParticipantsSource } from '@features/user/classes/participants-
import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { Translate } from '@singletons';
import { CoreKeyboard } from '@singletons/keyboard';
/**
* Page that displays the list of course participants.
@ -114,7 +114,7 @@ export class CoreUserParticipantsPage implements OnInit, AfterViewInit, OnDestro
* @param query Text to search for.
*/
async search(query: string): Promise<void> {
CoreApp.closeKeyboard();
CoreKeyboard.close();
const newSource = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource(
CoreUserParticipantsSource,

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { CoreApp } from '@services/app';
import { CoreKeyboard } from '@singletons/keyboard';
import { NgZone, Keyboard } from '@singletons';
/**
@ -20,12 +20,11 @@ import { NgZone, Keyboard } from '@singletons';
*/
export default function(): void {
const zone = NgZone.instance;
const app = CoreApp.instance;
const keyboard = Keyboard.instance;
// Execute callbacks in the Angular zone, so change detection doesn't stop working.
keyboard.onKeyboardShow().subscribe(data => zone.run(() => app.onKeyboardShow(data.keyboardHeight)));
keyboard.onKeyboardHide().subscribe(() => zone.run(() => app.onKeyboardHide()));
keyboard.onKeyboardWillShow().subscribe(() => zone.run(() => app.onKeyboardWillShow()));
keyboard.onKeyboardWillHide().subscribe(() => zone.run(() => app.onKeyboardWillHide()));
keyboard.onKeyboardShow().subscribe(data => zone.run(() => CoreKeyboard.onKeyboardShow(data.keyboardHeight)));
keyboard.onKeyboardHide().subscribe(() => zone.run(() => CoreKeyboard.onKeyboardHide()));
keyboard.onKeyboardWillShow().subscribe(() => zone.run(() => CoreKeyboard.onKeyboardWillShow()));
keyboard.onKeyboardWillHide().subscribe(() => zone.run(() => CoreKeyboard.onKeyboardWillHide()));
}

View File

@ -18,7 +18,7 @@ import { CoreDB } from '@services/db';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb';
import { makeSingleton, Keyboard, StatusBar } from '@singletons';
import { makeSingleton, StatusBar } from '@singletons';
import { CoreLogger } from '@singletons/logger';
import { CoreColors } from '@singletons/colors';
import { DBNAME, SCHEMA_VERSIONS_TABLE_NAME, SCHEMA_VERSIONS_TABLE_SCHEMA, SchemaVersionsDBEntry } from '@services/database/app';
@ -32,6 +32,7 @@ import { Subscription } from 'rxjs';
import { CorePlatform } from '@services/platform';
import { CoreNetwork, CoreNetworkConnection } from '@services/network';
import { CoreMainMenuProvider } from '@features/mainmenu/services/mainmenu';
import { CoreKeyboard } from '@singletons/keyboard';
/**
* Factory to provide some global functionalities, like access to the global app database.
@ -52,9 +53,6 @@ export class CoreAppProvider {
protected db?: SQLiteDB;
protected logger: CoreLogger;
protected ssoAuthenticationDeferred?: CorePromisedValue<void>;
protected isKeyboardShown = false;
protected keyboardOpening = false;
protected keyboardClosing = false;
protected redirect?: CoreRedirectData;
protected schemaVersionsTable = asyncInstance<CoreDatabaseTable<SchemaVersionsDBEntry, 'name'>>();
protected mainMenuListener?: CoreEventObserver;
@ -129,11 +127,11 @@ export class CoreAppProvider {
/**
* Closes the keyboard.
*
* @deprecated sinde 4.5.0. Use CoreKeyboard.closeKeyboard instead.
*/
closeKeyboard(): void {
if (CorePlatform.isMobile()) {
Keyboard.hide();
}
CoreKeyboard.close();
}
/**
@ -246,27 +244,30 @@ export class CoreAppProvider {
* Check if the keyboard is closing.
*
* @returns Whether keyboard is closing (animating).
* @deprecated since 4.5.0. Use CoreKeyboard.isKeyboardClosing instead.
*/
isKeyboardClosing(): boolean {
return this.keyboardClosing;
return CoreKeyboard.isKeyboardClosing();
}
/**
* Check if the keyboard is being opened.
*
* @returns Whether keyboard is opening (animating).
* @deprecated since 4.5.0. Use CoreKeyboard.isKeyboardOpening instead.
*/
isKeyboardOpening(): boolean {
return this.keyboardOpening;
return CoreKeyboard.isKeyboardOpening();
}
/**
* Check if the keyboard is visible.
*
* @returns Whether keyboard is visible.
* @deprecated since 4.5.0. Use CoreKeyboard.isKeyboardVisible instead.
*/
isKeyboardVisible(): boolean {
return this.isKeyboardShown;
return CoreKeyboard.isKeyboardVisible();
}
/**
@ -320,61 +321,48 @@ export class CoreAppProvider {
/**
* Open the keyboard.
*
* @deprecated since 4.5.0. Use CoreKeyboard.openKeyboard instead.
*/
openKeyboard(): void {
// Open keyboard is not supported in desktop and in iOS.
if (CorePlatform.isAndroid()) {
Keyboard.show();
}
CoreKeyboard.open();
}
/**
* Notify that Keyboard has been shown.
*
* @param keyboardHeight Keyboard height.
* @deprecated since 4.5.0. Use CoreKeyboard.onKeyboardShow instead.
*/
onKeyboardShow(keyboardHeight: number): void {
document.body.classList.add('keyboard-is-open');
this.setKeyboardShown(true);
// Error on iOS calculating size.
// More info: https://github.com/ionic-team/ionic-plugin-keyboard/issues/276 .
CoreEvents.trigger(CoreEvents.KEYBOARD_CHANGE, keyboardHeight);
CoreKeyboard.onKeyboardShow(keyboardHeight);
}
/**
* Notify that Keyboard has been hidden.
*
* @deprecated since 4.5.0. Use CoreKeyboard.onKeyboardHide instead.
*/
onKeyboardHide(): void {
document.body.classList.remove('keyboard-is-open');
this.setKeyboardShown(false);
CoreEvents.trigger(CoreEvents.KEYBOARD_CHANGE, 0);
CoreKeyboard.onKeyboardHide();
}
/**
* Notify that Keyboard is about to be shown.
*
* @deprecated since 4.5.0. Use CoreKeyboard.onKeyboardWillShow instead.
*/
onKeyboardWillShow(): void {
this.keyboardOpening = true;
this.keyboardClosing = false;
CoreKeyboard.onKeyboardWillShow();
}
/**
* Notify that Keyboard is about to be hidden.
*
* @deprecated since 4.5.0. Use CoreKeyboard.onKeyboardWillHide instead.
*/
onKeyboardWillHide(): void {
this.keyboardOpening = false;
this.keyboardClosing = true;
}
/**
* Set keyboard shown or hidden.
*
* @param shown Whether the keyboard is shown or hidden.
*/
protected setKeyboardShown(shown: boolean): void {
this.isKeyboardShown = shown;
this.keyboardOpening = false;
this.keyboardClosing = false;
CoreKeyboard.onKeyboardWillHide();
}
/**

View File

@ -75,7 +75,7 @@ export class CoreConfigProvider {
async initializeDatabase(): Promise<void> {
try {
await CoreApp.createTablesFromSchema(APP_SCHEMA);
} catch (e) {
} catch {
// Ignore errors.
}

View File

@ -17,7 +17,6 @@ import { IonContent } from '@ionic/angular';
import { ModalOptions, PopoverOptions, AlertOptions, AlertButton, TextFieldTypes, ToastOptions } from '@ionic/core';
import { Md5 } from 'ts-md5';
import { CoreApp } from '@services/app';
import { CoreConfig } from '@services/config';
import { CoreFile } from '@services/file';
import { CoreWSExternalWarning } from '@services/ws';
@ -60,6 +59,7 @@ import { CoreLang } from '@services/lang';
import { CorePasswordModalParams, CorePasswordModalResponse } from '@components/password-modal/password-modal';
import { CoreWSError } from '@classes/errors/wserror';
import { CoreErrorLogs } from '@singletons/error-logs';
import { CoreKeyboard } from '@singletons/keyboard';
/*
* "Utils" service with helper functions for UI, DOM elements and HTML code.
@ -337,7 +337,7 @@ export class CoreDomUtilsProvider {
await CoreUtils.nextTick();
if (CorePlatform.isAndroid() && this.supportsInputKeyboard(elementToFocus)) {
// On some Android versions the keyboard doesn't open automatically.
CoreApp.openKeyboard();
CoreKeyboard.open();
}
break;
}

View File

@ -0,0 +1,123 @@
// (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 { CorePlatform } from '@services/platform';
import { Keyboard } from '@singletons';
import { CoreEvents } from '@singletons/events';
/**
* Singleton with helper functions for keybard management.
*/
export class CoreKeyboard {
protected static isKeyboardShown = false;
protected static keyboardOpening = false;
protected static keyboardClosing = false;
/**
* Closes the keyboard.
*/
static close(): void {
if (CorePlatform.isMobile()) {
Keyboard.hide();
}
}
/**
* Open the keyboard.
*/
static open(): void {
// Open keyboard is not supported in desktop and in iOS.
if (CorePlatform.isAndroid()) {
Keyboard.show();
}
}
/**
* Notify that Keyboard has been shown.
*
* @param keyboardHeight Keyboard height.
*/
static onKeyboardShow(keyboardHeight: number): void {
document.body.classList.add('keyboard-is-open');
this.setKeyboardShown(true);
// Error on iOS calculating size.
// More info: https://github.com/ionic-team/ionic-plugin-keyboard/issues/276 .
CoreEvents.trigger(CoreEvents.KEYBOARD_CHANGE, keyboardHeight);
}
/**
* Notify that Keyboard has been hidden.
*/
static onKeyboardHide(): void {
document.body.classList.remove('keyboard-is-open');
this.setKeyboardShown(false);
CoreEvents.trigger(CoreEvents.KEYBOARD_CHANGE, 0);
}
/**
* Notify that Keyboard is about to be shown.
*/
static onKeyboardWillShow(): void {
CoreKeyboard.keyboardOpening = true;
CoreKeyboard.keyboardClosing = false;
}
/**
* Notify that Keyboard is about to be hidden.
*/
static onKeyboardWillHide(): void {
CoreKeyboard.keyboardOpening = false;
CoreKeyboard.keyboardClosing = true;
}
/**
* Set keyboard shown or hidden.
*
* @param shown Whether the keyboard is shown or hidden.
*/
protected static setKeyboardShown(shown: boolean): void {
CoreKeyboard.isKeyboardShown = shown;
CoreKeyboard.keyboardOpening = false;
CoreKeyboard.keyboardClosing = false;
}
/**
* Check if the keyboard is closing.
*
* @returns Whether keyboard is closing (animating).
*/
static isKeyboardClosing(): boolean {
return CoreKeyboard.keyboardClosing;
}
/**
* Check if the keyboard is being opened.
*
* @returns Whether keyboard is opening (animating).
*/
static isKeyboardOpening(): boolean {
return CoreKeyboard.keyboardOpening;
}
/**
* Check if the keyboard is visible.
*
* @returns Whether keyboard is visible.
*/
static isKeyboardVisible(): boolean {
return CoreKeyboard.isKeyboardShown;
}
}