From d0d2e0895ed8e17361b04f0fce21188cd45a323a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?=
Date: Wed, 16 Jul 2025 13:30:49 +0200
Subject: [PATCH 1/4] MOBILE-4842 network: Add signals
---
src/addons/blog/pages/index/index.ts | 11 +-
.../settings/pages/deviceinfo/deviceinfo.html | 8 +-
.../settings/pages/deviceinfo/deviceinfo.ts | 40 ++----
src/core/services/network.ts | 134 ++++++++++++------
4 files changed, 110 insertions(+), 83 deletions(-)
diff --git a/src/addons/blog/pages/index/index.ts b/src/addons/blog/pages/index/index.ts
index 293823fec74..f9f392a66d4 100644
--- a/src/addons/blog/pages/index/index.ts
+++ b/src/addons/blog/pages/index/index.ts
@@ -41,7 +41,6 @@ import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreTime } from '@singletons/time';
import { CorePopovers } from '@services/overlays/popovers';
import { CoreLoadings } from '@services/overlays/loadings';
-import { Subscription } from 'rxjs';
import { CoreAlerts } from '@services/overlays/alerts';
import { Translate } from '@singletons';
import { CoreCommentsCommentsComponent } from '@features/comments/components/comments/comments';
@@ -89,10 +88,9 @@ export default class AddonBlogIndexPage implements OnInit, OnDestroy {
contextInstanceId = 0;
entryUpdateObserver: CoreEventObserver;
syncObserver: CoreEventObserver;
- onlineObserver: Subscription;
optionsAvailable = false;
readonly hasOfflineDataToSync = signal(false);
- readonly isOnline = signal(false);
+ readonly isOnline = CoreNetwork.onlineSignal();
siteId: string;
syncIcon = CoreConstants.ICON_SYNC;
readonly syncHidden = computed(() => !this.loaded() || !this.isOnline() || !this.hasOfflineDataToSync());
@@ -101,7 +99,6 @@ export default class AddonBlogIndexPage implements OnInit, OnDestroy {
this.currentUserId = CoreSites.getCurrentSiteUserId();
this.siteHomeId = CoreSites.getCurrentSiteHomeId();
this.siteId = CoreSites.getCurrentSiteId();
- this.isOnline.set(CoreNetwork.isOnline());
this.logView = CoreTime.once(async () => {
await CorePromiseUtils.ignoreErrors(AddonBlog.logView(this.filter));
@@ -137,11 +134,6 @@ export default class AddonBlogIndexPage implements OnInit, OnDestroy {
await CorePromiseUtils.ignoreErrors(this.refresh(false));
this.loaded.set(true);
});
-
- // Refresh online status when changes.
- this.onlineObserver = CoreNetwork.onChange().subscribe(async () => {
- this.isOnline.set(CoreNetwork.isOnline());
- });
}
/**
@@ -516,7 +508,6 @@ export default class AddonBlogIndexPage implements OnInit, OnDestroy {
ngOnDestroy(): void {
this.entryUpdateObserver.off();
this.syncObserver.off();
- this.onlineObserver.unsubscribe();
}
}
diff --git a/src/core/features/settings/pages/deviceinfo/deviceinfo.html b/src/core/features/settings/pages/deviceinfo/deviceinfo.html
index 57d2ae90a37..e5386aabb0b 100644
--- a/src/core/features/settings/pages/deviceinfo/deviceinfo.html
+++ b/src/core/features/settings/pages/deviceinfo/deviceinfo.html
@@ -139,7 +139,7 @@
{{ 'core.settings.networkstatus' | translate}}
- @if (deviceInfo.isOnline) {
+ @if (deviceInfo.isOnline()) {
{{ 'core.online' | translate }}
} @else {
{{ 'core.offline' | translate }}
@@ -149,7 +149,11 @@
{{ 'core.settings.wificonnection' | translate}}
- {{ 'core.' + deviceInfo.wifiConnection | translate }}
+ @if (deviceInfo.wifiConnection()) {
+ {{ 'core.yes' | translate }}
+ } @else {
+ {{ 'core.no' | translate }}
+ }
@if (deviceInfo.cordovaVersion) {
diff --git a/src/core/features/settings/pages/deviceinfo/deviceinfo.ts b/src/core/features/settings/pages/deviceinfo/deviceinfo.ts
index 03e89d80c29..9824ac5b6b7 100644
--- a/src/core/features/settings/pages/deviceinfo/deviceinfo.ts
+++ b/src/core/features/settings/pages/deviceinfo/deviceinfo.ts
@@ -12,21 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { Component, OnDestroy } from '@angular/core';
+import { Component, computed, Signal } from '@angular/core';
import { CoreConstants } from '@/core/constants';
import { CoreLocalNotifications } from '@services/local-notifications';
-import { Device, Translate, NgZone } from '@singletons';
+import { Device, Translate } from '@singletons';
import { CoreLang } from '@services/lang';
import { CoreFile } from '@services/file';
import { CoreSites } from '@services/sites';
import { CorePromiseUtils } from '@singletons/promise-utils';
-import { Subscription } from 'rxjs';
import { CorePushNotifications } from '@features/pushnotifications/services/pushnotifications';
import { CoreConfig } from '@services/config';
import { CoreToasts } from '@services/overlays/toasts';
import { CoreNavigator } from '@services/navigator';
import { CorePlatform } from '@services/platform';
-import { CoreNetwork } from '@services/network';
+import { CoreNetwork, CoreNetworkConnection } from '@services/network';
import { CoreLoginHelper } from '@features/login/services/login-helper';
import { CoreSitesFactory } from '@services/sites-factory';
import { CoreText } from '@singletons/text';
@@ -53,8 +52,8 @@ interface CoreSettingsDeviceInfo {
locationHref?: string;
deviceType: string;
screen?: string;
- isOnline: boolean;
- wifiConnection: string;
+ isOnline: Signal;
+ wifiConnection: Signal;
cordovaVersion?: string;
platform?: string;
osVersion?: string;
@@ -76,7 +75,7 @@ interface CoreSettingsDeviceInfo {
CoreSharedModule,
],
})
-export default class CoreSettingsDeviceInfoPage implements OnDestroy {
+export default class CoreSettingsDeviceInfoPage {
deviceInfo: CoreSettingsDeviceInfo;
deviceOsTranslated?: string;
@@ -88,8 +87,6 @@ export default class CoreSettingsDeviceInfoPage implements OnDestroy {
protected devOptionsForced = false;
protected devOptionsClickTimeout?: number;
- protected onlineObserver?: Subscription;
-
constructor() {
const navigator = window.navigator;
@@ -98,8 +95,8 @@ export default class CoreSettingsDeviceInfoPage implements OnDestroy {
versionCode: CoreConstants.CONFIG.versioncode,
compilationTime: CoreConstants.BUILD.compilationTime || 0,
lastCommit: CoreConstants.BUILD.lastCommitHash || '',
- isOnline: CoreNetwork.isOnline(),
- wifiConnection: CoreNetwork.isWifi() ? 'yes' : 'no',
+ isOnline: CoreNetwork.onlineSignal(),
+ wifiConnection: computed(() => CoreNetwork.connectionTypeSignal()() === CoreNetworkConnection.WIFI),
localNotifAvailable: CoreLocalNotifications.isPluginAvailable() ? 'yes' : 'no',
pushId: CorePushNotifications.getPushId(),
deviceType: '',
@@ -172,14 +169,6 @@ export default class CoreSettingsDeviceInfoPage implements OnDestroy {
this.deviceInfo.siteId = currentSite?.getId();
this.deviceInfo.siteVersion = currentSite?.getInfo()?.release;
- // Refresh online status when changes.
- this.onlineObserver = CoreNetwork.onChange().subscribe(() => {
- // Execute the callback in the Angular zone, so change detection doesn't stop working.
- NgZone.run(() => {
- this.deviceInfo.isOnline = CoreNetwork.isOnline();
- });
- });
-
this.asyncInit();
}
@@ -220,6 +209,12 @@ export default class CoreSettingsDeviceInfoPage implements OnDestroy {
*/
copyInfo(): void {
CoreText.copyToClipboard(JSON.stringify(this.deviceInfo));
+ const deviceInfo = {
+ ...this.deviceInfo,
+ isOnline: this.deviceInfo.isOnline(),
+ wifiConnection: this.deviceInfo.wifiConnection(),
+ };
+ CoreText.copyToClipboard(JSON.stringify(deviceInfo));
}
/**
@@ -234,13 +229,6 @@ export default class CoreSettingsDeviceInfoPage implements OnDestroy {
text && CoreText.copyToClipboard(text);
}
- /**
- * Page destroyed.
- */
- ngOnDestroy(): void {
- this.onlineObserver && this.onlineObserver.unsubscribe();
- }
-
/**
* 5 clicks will enable dev options.
*/
diff --git a/src/core/services/network.ts b/src/core/services/network.ts
index 35597f4f51a..0d3493d2a61 100644
--- a/src/core/services/network.ts
+++ b/src/core/services/network.ts
@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { Injectable } from '@angular/core';
+import { computed, effect, Injectable, Signal, signal } from '@angular/core';
import { CorePlatform } from '@services/platform';
import { Network } from '@awesome-cordova-plugins/network/ngx';
-import { NgZone, makeSingleton } from '@singletons';
+import { makeSingleton } from '@singletons';
import { Observable, Subject, merge } from 'rxjs';
import { CoreHTMLClasses } from '@singletons/html-classes';
@@ -42,26 +42,44 @@ export class CoreNetworkService extends Network {
protected connectStableObservable = new Subject<'connected'>();
protected disconnectObservable = new Subject<'disconnected'>();
protected forceConnectionMode?: CoreNetworkConnection;
- protected online = false;
+ protected readonly online = signal(false);
protected connectStableTimeout?: number;
+ private readonly _connectionType = signal(CoreNetworkConnection.UNKNOWN);
+ private readonly _limitedConnection = computed(() => this.online() && CoreNetwork.isNetworkAccessLimited());
- get connectionType(): CoreNetworkConnection {
- if (this.forceConnectionMode !== undefined) {
- return this.forceConnectionMode;
- }
+ constructor() {
+ super();
- if (CorePlatform.isMobile()) {
- return this.type as CoreNetworkConnection;
- }
+ effect(() => {
+ const isOnline = this.online();
+
+ const hadOfflineMessage = CoreHTMLClasses.hasModeClass('core-offline');
+
+ CoreHTMLClasses.toggleModeClass('core-offline', !isOnline);
+
+ if (isOnline && hadOfflineMessage) {
+ CoreHTMLClasses.toggleModeClass('core-online', true);
+
+ setTimeout(() => {
+ CoreHTMLClasses.toggleModeClass('core-online', false);
+ }, 3000);
+ } else if (!isOnline) {
+ CoreHTMLClasses.toggleModeClass('core-online', false);
+ }
+ });
+ }
+
+ get connectionType(): CoreNetworkConnection {
+ CoreNetwork.updateConnectionType();
- return this.online ? CoreNetworkConnection.WIFI : CoreNetworkConnection.NONE;
+ return this._connectionType();
}
/**
* Initialize the service.
*/
initialize(): void {
- this.checkOnline();
+ this.updateOnline();
if (CorePlatform.isMobile()) {
// We cannot directly listen to onChange because it depends on
@@ -103,30 +121,7 @@ export class CoreNetworkService extends Network {
async onPlaformReady(): Promise {
await CorePlatform.ready();
- // Refresh online status when changes.
- CoreNetwork.onChange().subscribe(() => {
- // Execute the callback in the Angular zone, so change detection doesn't stop working.
- NgZone.run(() => {
- const isOnline = this.isOnline();
-
- const hadOfflineMessage = CoreHTMLClasses.hasModeClass('core-offline');
-
- CoreHTMLClasses.toggleModeClass('core-offline', !isOnline);
-
- if (isOnline && hadOfflineMessage) {
- CoreHTMLClasses.toggleModeClass('core-online', true);
-
- setTimeout(() => {
- CoreHTMLClasses.toggleModeClass('core-online', false);
- }, 3000);
- } else if (!isOnline) {
- CoreHTMLClasses.toggleModeClass('core-online', false);
- }
- });
- });
-
- const isOnline = this.isOnline();
- CoreHTMLClasses.toggleModeClass('core-offline', !isOnline);
+ CoreHTMLClasses.toggleModeClass('core-offline', !this.online());
}
/**
@@ -146,15 +141,18 @@ export class CoreNetworkService extends Network {
* @returns Whether the app is online.
*/
isOnline(): boolean {
- return this.online;
+ return this.online();
}
/**
- * Returns whether we are online.
+ * Updates online status.
*/
- checkOnline(): void {
+ protected updateOnline(): void {
+ // Recalculate connection type.
+ CoreNetwork.updateConnectionType();
+
if (this.forceConnectionMode === CoreNetworkConnection.NONE) {
- this.online = false;
+ this.online.set(false);
return;
}
@@ -162,12 +160,12 @@ export class CoreNetworkService extends Network {
// We cannot use navigator.onLine because it has issues in some devices.
// See https://bugs.chromium.org/p/chromium/issues/detail?id=811122
if (!CorePlatform.isAndroid()) {
- this.online = navigator.onLine;
+ this.online.set(navigator.onLine);
return;
}
- const type = this.connectionType;
+ const type = this._connectionType();
let online = type !== null && type !== CoreNetworkConnection.NONE && type !== CoreNetworkConnection.UNKNOWN;
// Double check we are not online because we cannot rely 100% in Cordova APIs.
@@ -175,7 +173,26 @@ export class CoreNetworkService extends Network {
online = true;
}
- this.online = online;
+ this.online.set(online);
+ }
+
+ /**
+ * Check and update the connection type.
+ */
+ protected updateConnectionType(): void {
+ if (this.forceConnectionMode !== undefined) {
+ this._connectionType.set(this.forceConnectionMode);
+
+ return;
+ }
+
+ if (CorePlatform.isMobile()) {
+ this._connectionType.set(this.type as CoreNetworkConnection);
+
+ return;
+ }
+
+ this._connectionType.set(this.online() ? CoreNetworkConnection.WIFI : CoreNetworkConnection.NONE);
}
/**
@@ -187,6 +204,33 @@ export class CoreNetworkService extends Network {
return merge(this.connectObservable, this.disconnectObservable);
}
+ /**
+ * Returns a signal to watch online status.
+ *
+ * @returns Signal.
+ */
+ onlineSignal(): Signal {
+ return this.online.asReadonly();
+ }
+
+ /**
+ * Returns a signal to watch limited connection status.
+ *
+ * @returns Signal.
+ */
+ limitedConnectionSignal(): Signal {
+ return this._limitedConnection;
+ }
+
+ /**
+ * Returns a signal to watch connection type.
+ *
+ * @returns Signal.
+ */
+ connectionTypeSignal(): Signal {
+ return this._connectionType.asReadonly();
+ }
+
/**
* Returns an observable to notify when the app is connected.
* It will also be fired when connection type changes.
@@ -224,9 +268,9 @@ export class CoreNetworkService extends Network {
*/
protected fireObservable(): void {
clearTimeout(this.connectStableTimeout);
- this.checkOnline();
+ this.updateOnline();
- if (this.online) {
+ if (this.online()) {
this.connectObservable.next('connected');
this.connectStableTimeout = window.setTimeout(() => {
this.connectStableObservable.next('connected');
From ae34d226821d5f89e5a7905cfc0518ff713a6270 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?=
Date: Wed, 29 Jan 2025 12:33:46 +0100
Subject: [PATCH 2/4] MOBILE-4842 module: Change BehaviorSubject to signal
---
.../mod/page/tests/behat/basic_usage.feature | 2 +-
.../components/module/core-course-module.html | 6 +++---
.../course/components/module/module.ts | 19 +++++++++----------
3 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/src/addons/mod/page/tests/behat/basic_usage.feature b/src/addons/mod/page/tests/behat/basic_usage.feature
index c9c8b85d69c..a44160b5ca2 100644
--- a/src/addons/mod/page/tests/behat/basic_usage.feature
+++ b/src/addons/mod/page/tests/behat/basic_usage.feature
@@ -24,7 +24,7 @@ Feature: Test basic usage of page activity in app
| name | activity | activityname | course |
| \mod_page\event\course_module_viewed | page | Test page title | Course 1 |
- Scenario: Prefecth page
+ Scenario: Prefetch page
Given I entered the course "Course 1" as "student1" in the app
When I press "Course downloads" in the app
And I press "Download" within "Test page title" "ion-item" in the app
diff --git a/src/core/features/course/components/module/core-course-module.html b/src/core/features/course/components/module/core-course-module.html
index 9685d51309e..640616f9f55 100644
--- a/src/core/features/course/components/module/core-course-module.html
+++ b/src/core/features/course/components/module/core-course-module.html
@@ -30,9 +30,9 @@
@if (module.visible === 0 || module.uservisible === false) {
}
- @if (prefetchStatusIcon$ | async; as prefetchStatusIcon) {
-
+ @if (prefetchStatusIcon()) {
+
}
diff --git a/src/core/features/course/components/module/module.ts b/src/core/features/course/components/module/module.ts
index 4e6b43e1cec..a097a80cf6b 100644
--- a/src/core/features/course/components/module/module.ts
+++ b/src/core/features/course/components/module/module.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, HostBinding } from '@angular/core';
+import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, HostBinding, signal } from '@angular/core';
import { CoreSites } from '@services/sites';
import {
@@ -29,7 +29,6 @@ import {
} from '@features/course/services/module-prefetch-delegate';
import { CoreConstants, DownloadStatus } from '@/core/constants';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
-import { BehaviorSubject } from 'rxjs';
import { toBoolean } from '@/core/transforms/boolean';
import { CoreRemindersDateComponent } from '../../../reminders/components/date/date';
import { CoreCourseModuleCompletionComponent } from '../module-completion/module-completion';
@@ -74,9 +73,9 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
modNameTranslated = '';
hasCompletion = false; // Whether activity has completion to be shown.
showManualCompletion = false; // Whether to show manual completion when completion conditions are disabled.
- prefetchStatusIcon$ = new BehaviorSubject(''); // Module prefetch status icon.
- prefetchStatusText$ = new BehaviorSubject(''); // Module prefetch status text.
moduleHasView = true;
+ readonly prefetchStatusIcon = signal(''); // Module prefetch status icon.
+ readonly prefetchStatusText = signal(''); // Module prefetch status text.
protected prefetchHandler?: CoreCourseModulePrefetchHandler;
@@ -152,16 +151,16 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
switch (prefetchStatus) {
case DownloadStatus.OUTDATED:
- this.prefetchStatusIcon$.next(CoreConstants.ICON_OUTDATED);
- this.prefetchStatusText$.next('core.outdated');
+ this.prefetchStatusIcon.set(CoreConstants.ICON_OUTDATED);
+ this.prefetchStatusText.set('core.outdated');
break;
case DownloadStatus.DOWNLOADED:
- this.prefetchStatusIcon$.next(CoreConstants.ICON_DOWNLOADED);
- this.prefetchStatusText$.next('core.downloaded');
+ this.prefetchStatusIcon.set(CoreConstants.ICON_DOWNLOADED);
+ this.prefetchStatusText.set('core.downloaded');
break;
default:
- this.prefetchStatusIcon$.next('');
- this.prefetchStatusText$.next('');
+ this.prefetchStatusIcon.set('');
+ this.prefetchStatusText.set('');
break;
}
From 0fe16318f4b9e7c3620ff6784abb7f776f4c701a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?=
Date: Wed, 29 Jan 2025 12:43:24 +0100
Subject: [PATCH 3/4] MOBILE-4842 keyboard: Change keyboard events to signals
---
.../features/comments/pages/viewer/viewer.ts | 13 ++--
.../rich-text-editor/rich-text-editor.ts | 19 +++---
src/core/features/mainmenu/pages/menu/menu.ts | 33 ++++-----
.../subscribe-to-keyboard-events.ts | 2 +-
src/core/services/app.ts | 2 +-
src/core/singletons/events.ts | 3 +
src/core/singletons/keyboard.ts | 67 ++++++++++++-------
7 files changed, 85 insertions(+), 54 deletions(-)
diff --git a/src/core/features/comments/pages/viewer/viewer.ts b/src/core/features/comments/pages/viewer/viewer.ts
index 96448472f83..e502b36de7c 100644
--- a/src/core/features/comments/pages/viewer/viewer.ts
+++ b/src/core/features/comments/pages/viewer/viewer.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { Component, OnDestroy, OnInit, ViewChild, AfterViewInit, inject } from '@angular/core';
+import { Component, OnDestroy, OnInit, ViewChild, AfterViewInit, effect, inject } from '@angular/core';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { ActivatedRoute } from '@angular/router';
import { CoreSites } from '@services/sites';
@@ -49,6 +49,7 @@ import { CoreDom } from '@singletons/dom';
import { CoreSharedModule } from '@/core/shared.module';
import { ADDON_MOD_ASSIGN_COMMENTS_COMPONENT_NAME } from '@addons/mod/assign/submission/comments/constants';
import { CoreCourses } from '@features/courses/services/courses';
+import { CoreKeyboard } from '@singletons/keyboard';
/**
* Page that displays comments.
@@ -93,7 +94,6 @@ export default class CoreCommentsViewerPage implements OnInit, OnDestroy, AfterV
protected addDeleteCommentsAvailable = false;
protected syncObserver?: CoreEventObserver;
protected onlineObserver: Subscription;
- protected keyboardObserver: CoreEventObserver;
protected viewDestroyed = false;
protected scrollBottom = true;
protected scrollElement?: HTMLElement;
@@ -128,9 +128,11 @@ export default class CoreCommentsViewerPage implements OnInit, OnDestroy, AfterV
});
});
- this.keyboardObserver = CoreEvents.on(CoreEvents.KEYBOARD_CHANGE, (keyboardHeight: number) => {
- // Force when opening.
- this.scrollToBottom(keyboardHeight > 0);
+ effect(() => {
+ const shown = CoreKeyboard.getKeyboardShownSignal();
+
+ /// Force when opening.
+ this.scrollToBottom(shown());
});
}
@@ -700,7 +702,6 @@ export default class CoreCommentsViewerPage implements OnInit, OnDestroy, AfterV
this.syncObserver?.off();
this.onlineObserver.unsubscribe();
this.viewDestroyed = true;
- this.keyboardObserver.off();
}
}
diff --git a/src/core/features/editor/components/rich-text-editor/rich-text-editor.ts b/src/core/features/editor/components/rich-text-editor/rich-text-editor.ts
index f85f0440c72..6c69ca584a3 100644
--- a/src/core/features/editor/components/rich-text-editor/rich-text-editor.ts
+++ b/src/core/features/editor/components/rich-text-editor/rich-text-editor.ts
@@ -25,6 +25,7 @@ import {
Output,
EventEmitter,
inject,
+ effect,
} from '@angular/core';
import { IonContent } from '@ionic/angular';
import { CoreSharedModule } from '@/core/shared.module';
@@ -50,6 +51,7 @@ import { CoreLoadingComponent } from '@components/loading/loading';
import { CoreToasts } from '@services/overlays/toasts';
import { CorePromiseUtils } from '@singletons/promise-utils';
import { convertTextToHTMLElement } from '@/core/utils/create-html-element';
+import { CoreKeyboard } from '@singletons/keyboard';
/**
* Component that displays a rich text editor.
@@ -85,7 +87,6 @@ export class CoreEditorRichTextEditorComponent implements AfterViewInit, OnDestr
@ViewChild(CoreDynamicComponent) dynamicComponent!: CoreDynamicComponent;
- protected keyboardObserver?: CoreEventObserver;
protected resizeListener?: CoreEventObserver;
protected editorComponentClass?: Type;
protected editorComponentData: Record = {};
@@ -107,6 +108,15 @@ export class CoreEditorRichTextEditorComponent implements AfterViewInit, OnDestr
constructor() {
// Generate a "unique" ID based on timestamp.
this.pageInstance = `app_${Date.now()}`;
+
+ effect(() => {
+ // Signal will be triggered when the keyboard is shown or hidden.
+ CoreKeyboard.getKeyboardShownSignal();
+
+ // Opening or closing the keyboard also calls the resize function, but sometimes the resize is called too soon.
+ // Check the height again, now the window height should have been updated.
+ this.maximizeEditorSize();
+ });
}
/**
@@ -188,12 +198,6 @@ export class CoreEditorRichTextEditorComponent implements AfterViewInit, OnDestr
this.controlSubscription = this.control?.valueChanges.subscribe((newValue) => {
this.onControlValueChange(newValue);
});
-
- // Opening or closing the keyboard also calls the resize function, but sometimes the resize is called too soon.
- // Check the height again, now the window height should have been updated.
- this.keyboardObserver = CoreEvents.on(CoreEvents.KEYBOARD_CHANGE, () => {
- this.maximizeEditorSize();
- });
}
/**
@@ -201,7 +205,6 @@ export class CoreEditorRichTextEditorComponent implements AfterViewInit, OnDestr
*/
ngOnDestroy(): void {
this.resizeListener?.off();
- this.keyboardObserver?.off();
this.controlSubscription?.unsubscribe();
this.resetObserver?.off();
this.labelObserver?.disconnect();
diff --git a/src/core/features/mainmenu/pages/menu/menu.ts b/src/core/features/mainmenu/pages/menu/menu.ts
index ca201752e0a..d8c80850d1b 100644
--- a/src/core/features/mainmenu/pages/menu/menu.ts
+++ b/src/core/features/mainmenu/pages/menu/menu.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
+import { Component, OnInit, OnDestroy, ViewChild, effect } from '@angular/core';
import { IonTabs } from '@ionic/angular';
import { BackButtonEvent } from '@ionic/core';
import { Subscription } from 'rxjs';
@@ -44,6 +44,7 @@ import {
import { CoreSharedModule } from '@/core/shared.module';
import { CoreMainMenuUserButtonComponent } from '../../components/user-menu-button/user-menu-button';
import { BackButtonPriority } from '@/core/constants';
+import { CoreKeyboard } from '@singletons/keyboard';
const ANIMATION_DURATION = 500;
@@ -119,6 +120,22 @@ export default class CoreMainMenuPage implements OnInit, OnDestroy {
this.isMainScreen = !this.mainTabs?.outlet.canGoBack();
this.updateVisibility();
});
+
+ if (CorePlatform.isIOS()) {
+ effect(() => {
+ const shown = CoreKeyboard.getKeyboardShownSignal();
+ // In iOS, the resize event is triggered before the keyboard is opened/closed and not triggered again once done.
+ // Init handlers again once keyboard is closed since the resize event doesn't have the updated height.
+ if (!shown) {
+ this.updateHandlers();
+
+ // If the device is slow it can take a bit more to update the window height. Retry in a few ms.
+ setTimeout(() => {
+ this.updateHandlers();
+ }, 250);
+ }
+ });
+ }
}
/**
@@ -150,20 +167,6 @@ export default class CoreMainMenuPage implements OnInit, OnDestroy {
});
document.addEventListener('ionBackButton', this.backButtonFunction);
- if (CorePlatform.isIOS()) {
- // In iOS, the resize event is triggered before the keyboard is opened/closed and not triggered again once done.
- // Init handlers again once keyboard is closed since the resize event doesn't have the updated height.
- this.keyboardObserver = CoreEvents.on(CoreEvents.KEYBOARD_CHANGE, (kbHeight: number) => {
- if (kbHeight === 0) {
- this.updateHandlers();
-
- // If the device is slow it can take a bit more to update the window height. Retry in a few ms.
- setTimeout(() => {
- this.updateHandlers();
- }, 250);
- }
- });
- }
CoreEvents.trigger(CoreEvents.MAIN_HOME_LOADED);
}
diff --git a/src/core/initializers/subscribe-to-keyboard-events.ts b/src/core/initializers/subscribe-to-keyboard-events.ts
index a160dabc0cc..b4f9f6684f1 100644
--- a/src/core/initializers/subscribe-to-keyboard-events.ts
+++ b/src/core/initializers/subscribe-to-keyboard-events.ts
@@ -25,6 +25,6 @@ export default function(): void {
// Execute callbacks in the Angular zone, so change detection doesn't stop working.
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.onKeyboardWillShow().subscribe((data) => zone.run(() => CoreKeyboard.onKeyboardWillShow(data.keyboardHeight)));
keyboard.onKeyboardWillHide().subscribe(() => zone.run(() => CoreKeyboard.onKeyboardWillHide()));
}
diff --git a/src/core/services/app.ts b/src/core/services/app.ts
index 1492fd5ea3b..ca7ff7f82a8 100644
--- a/src/core/services/app.ts
+++ b/src/core/services/app.ts
@@ -90,7 +90,7 @@ export class CoreAppProvider {
/**
* Closes the keyboard.
*
- * @deprecated sinde 4.5.0. Use CoreKeyboard.closeKeyboard instead.
+ * @deprecated since 4.5.0. Use CoreKeyboard.closeKeyboard instead.
*/
closeKeyboard(): void {
CoreKeyboard.close();
diff --git a/src/core/singletons/events.ts b/src/core/singletons/events.ts
index f5aba91d55b..dfd305c5864 100644
--- a/src/core/singletons/events.ts
+++ b/src/core/singletons/events.ts
@@ -107,6 +107,9 @@ export class CoreEvents {
static readonly IAB_MESSAGE = 'inappbrowser_message';
static readonly APP_LAUNCHED_URL = 'app_launched_url'; // App opened with a certain URL (custom URL scheme).
static readonly FILE_SHARED = 'file_shared';
+ /**
+ * @deprecated since 5.0.0. Use CoreKeyboard.getKeyboardShownSignal signal.
+ */
static readonly KEYBOARD_CHANGE = 'keyboard_change';
static readonly ORIENTATION_CHANGE = 'orientation_change';
static readonly SEND_ON_ENTER_CHANGED = 'send_on_enter_changed';
diff --git a/src/core/singletons/keyboard.ts b/src/core/singletons/keyboard.ts
index 2c3c0126943..87d0d4be27d 100644
--- a/src/core/singletons/keyboard.ts
+++ b/src/core/singletons/keyboard.ts
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import { effect, Signal, signal } from '@angular/core';
import { CorePlatform } from '@services/platform';
import { Keyboard } from '@singletons';
import { CoreEvents } from '@singletons/events';
@@ -21,13 +22,16 @@ import { CoreEvents } from '@singletons/events';
*/
export class CoreKeyboard {
- protected static isKeyboardShown = false;
- protected static keyboardOpening = false;
- protected static keyboardClosing = false;
+ protected static readonly IS_KEYBOARD_SHOWN = signal(false);
+ protected static readonly KEYBOARD_OPENING = signal(false);
+ protected static readonly KEYBOARD_CLOSING = signal(false);
+ protected static readonly KEYBOARD_HEIGHT = signal(0);
// Avoid creating singleton instances.
private constructor() {
- // Nothing to do.
+ effect(() => {
+ document.body.classList.toggle('keyboard-is-open', CoreKeyboard.IS_KEYBOARD_SHOWN());
+ });
}
/**
@@ -49,53 +53,70 @@ export class CoreKeyboard {
}
}
+ static getKeyboardShownSignal(): Signal {
+ return CoreKeyboard.IS_KEYBOARD_SHOWN.asReadonly();
+ }
+
+ static getKeyboardHeightSignal(): Signal {
+ return CoreKeyboard.KEYBOARD_HEIGHT.asReadonly();
+ }
+
/**
* Notify that Keyboard has been shown.
*
* @param keyboardHeight Keyboard height.
*/
static onKeyboardShow(keyboardHeight: number): void {
- document.body.classList.add('keyboard-is-open');
- CoreKeyboard.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);
+ // More info: https://github.com/ionic-team/ionic-plugin-keyboard/issues/276
+ CoreKeyboard.setKeyboardShown(true, keyboardHeight);
}
/**
* Notify that Keyboard has been hidden.
*/
static onKeyboardHide(): void {
- document.body.classList.remove('keyboard-is-open');
- CoreKeyboard.setKeyboardShown(false);
- CoreEvents.trigger(CoreEvents.KEYBOARD_CHANGE, 0);
+ CoreKeyboard.setKeyboardShown(false, 0);
}
/**
* Notify that Keyboard is about to be shown.
+ *
+ * @param keyboardHeight Keyboard height.
*/
- static onKeyboardWillShow(): void {
- CoreKeyboard.keyboardOpening = true;
- CoreKeyboard.keyboardClosing = false;
+ static onKeyboardWillShow(keyboardHeight?: number): void {
+ CoreKeyboard.KEYBOARD_OPENING.set(true);
+ CoreKeyboard.KEYBOARD_CLOSING.set(false);
+
+ if (keyboardHeight !== undefined) {
+ this.KEYBOARD_HEIGHT.set(keyboardHeight);
+ }
}
/**
* Notify that Keyboard is about to be hidden.
*/
static onKeyboardWillHide(): void {
- CoreKeyboard.keyboardOpening = false;
- CoreKeyboard.keyboardClosing = true;
+ CoreKeyboard.KEYBOARD_OPENING.set(false);
+ CoreKeyboard.KEYBOARD_CLOSING.set(true);
+
+ this.KEYBOARD_HEIGHT.set(0);
}
/**
* Set keyboard shown or hidden.
*
* @param shown Whether the keyboard is shown or hidden.
+ * @param keyboardHeight Keyboard height.
*/
- protected static setKeyboardShown(shown: boolean): void {
- CoreKeyboard.isKeyboardShown = shown;
- CoreKeyboard.keyboardOpening = false;
- CoreKeyboard.keyboardClosing = false;
+ protected static setKeyboardShown(shown: boolean, keyboardHeight: number): void {
+ CoreKeyboard.IS_KEYBOARD_SHOWN.set(shown);
+ CoreKeyboard.KEYBOARD_OPENING.set(false);
+ CoreKeyboard.KEYBOARD_CLOSING.set(false);
+ this.KEYBOARD_HEIGHT.set(keyboardHeight);
+
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
+ CoreEvents.trigger(CoreEvents.KEYBOARD_CHANGE, keyboardHeight);
}
/**
@@ -104,7 +125,7 @@ export class CoreKeyboard {
* @returns Whether keyboard is closing (animating).
*/
static isKeyboardClosing(): boolean {
- return CoreKeyboard.keyboardClosing;
+ return CoreKeyboard.KEYBOARD_CLOSING();
}
/**
@@ -113,7 +134,7 @@ export class CoreKeyboard {
* @returns Whether keyboard is opening (animating).
*/
static isKeyboardOpening(): boolean {
- return CoreKeyboard.keyboardOpening;
+ return CoreKeyboard.KEYBOARD_OPENING();
}
/**
@@ -122,7 +143,7 @@ export class CoreKeyboard {
* @returns Whether keyboard is visible.
*/
static isKeyboardVisible(): boolean {
- return CoreKeyboard.isKeyboardShown;
+ return CoreKeyboard.IS_KEYBOARD_SHOWN();
}
}
From c7465427453eab4e6ea34067688a583ed52c0409 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?=
Date: Wed, 29 Jan 2025 15:51:27 +0100
Subject: [PATCH 4/4] MOBILE-4842 network: Simplify network type management
---
.../tests/behat/behat_app.php | 7 +-
scripts/langindex.json | 2 +-
src/core/classes/sites/authenticated-site.ts | 2 +-
src/core/directives/external-content.ts | 3 +-
.../features/course/services/course-helper.ts | 2 +-
.../services/fileuploader-helper.ts | 2 +-
src/core/features/settings/lang.json | 2 +-
.../settings/pages/deviceinfo/deviceinfo.html | 4 +-
.../settings/pages/deviceinfo/deviceinfo.ts | 8 +-
src/core/features/settings/pages/site/site.ts | 4 +-
.../pages/synchronization/synchronization.ts | 4 +-
.../settings/services/settings-helper.ts | 2 +-
src/core/services/cron.ts | 2 +-
src/core/services/file-helper.ts | 2 +-
src/core/services/filepool.ts | 4 +-
src/core/services/network.ts | 108 ++++++++++--------
src/core/services/overlays/alerts.ts | 4 +-
src/core/services/ws.ts | 2 +-
18 files changed, 91 insertions(+), 73 deletions(-)
diff --git a/local_moodleappbehat/tests/behat/behat_app.php b/local_moodleappbehat/tests/behat/behat_app.php
index 20c957c2b2f..cdb7d95c898 100644
--- a/local_moodleappbehat/tests/behat/behat_app.php
+++ b/local_moodleappbehat/tests/behat/behat_app.php
@@ -1179,15 +1179,16 @@ public function i_switch_offline_mode(string $offline) {
public function i_switch_network_connection(string $mode) {
switch ($mode) {
case 'wifi':
- $this->runtime_js("network.setForceConnectionMode('$mode');");
+ $this->runtime_js("network.setForceConnectionMode('not_measured');");
break;
case 'cellular':
- $this->runtime_js("network.setForceConnectionMode('$mode');");
+ $this->runtime_js("network.setForceConnectionMode('measured');");
break;
case 'offline':
- $this->runtime_js("network.setForceConnectionMode('none');");
+ $this->runtime_js("network.setForceConnectionMode('offline');");
break;
default:
+ $this->runtime_js("network.setForceConnectionMode('unknown');");
break;
}
}
diff --git a/scripts/langindex.json b/scripts/langindex.json
index 777bb28b359..a81f93ba243 100644
--- a/scripts/langindex.json
+++ b/scripts/langindex.json
@@ -2537,6 +2537,7 @@
"core.settings.loggedin": "message",
"core.settings.loggedoff": "message",
"core.settings.logintosync": "local_moodlemobileapp",
+ "core.settings.measuredconnection": "local_moodlemobileapp",
"core.settings.navigatorlanguage": "local_moodlemobileapp",
"core.settings.navigatoruseragent": "local_moodlemobileapp",
"core.settings.networkstatus": "local_moodlemobileapp",
@@ -2560,7 +2561,6 @@
"core.settings.synchronizenowhelp": "local_moodlemobileapp",
"core.settings.syncsettings": "local_moodlemobileapp",
"core.settings.total": "moodle",
- "core.settings.wificonnection": "local_moodlemobileapp",
"core.settings.youradev": "local_moodlemobileapp",
"core.share": "moodle",
"core.sharedfiles.chooseaccountstorefile": "local_moodlemobileapp",
diff --git a/src/core/classes/sites/authenticated-site.ts b/src/core/classes/sites/authenticated-site.ts
index 345eb651cba..d39cca582ba 100644
--- a/src/core/classes/sites/authenticated-site.ts
+++ b/src/core/classes/sites/authenticated-site.ts
@@ -1592,7 +1592,7 @@ export class CoreAuthenticatedSite extends CoreUnauthenticatedSite {
let expirationDelay = CoreAuthenticatedSite.UPDATE_FREQUENCIES[updateFrequency] ||
CoreAuthenticatedSite.UPDATE_FREQUENCIES[CoreCacheUpdateFrequency.USUALLY];
- if (CoreNetwork.isNetworkAccessLimited()) {
+ if (CoreNetwork.connectionIsMeasured()) {
// Not WiFi, increase the expiration delay a 50% to decrease the data usage in this case.
expirationDelay *= 1.5;
}
diff --git a/src/core/directives/external-content.ts b/src/core/directives/external-content.ts
index 6345c16bd55..54921a7db64 100644
--- a/src/core/directives/external-content.ts
+++ b/src/core/directives/external-content.ts
@@ -518,7 +518,8 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges, O
clickableEl.addEventListener(eventName, () => {
// User played media or opened a downloadable link.
// Download the file if in wifi and it hasn't been downloaded already (for big files).
- if (state !== DownloadStatus.DOWNLOADED && state !== DownloadStatus.DOWNLOADING && CoreNetwork.isWifi()) {
+ if (state !== DownloadStatus.DOWNLOADED && state !== DownloadStatus.DOWNLOADING &&
+ CoreNetwork.connectionIsNotMeasured()) {
// We aren't using the result, so it doesn't matter which of the 2 functions we call.
CoreFilepool.getUrlByUrl(site.getId(), url, this.component, this.componentId, 0, false);
}
diff --git a/src/core/features/course/services/course-helper.ts b/src/core/features/course/services/course-helper.ts
index 7ea007c3c3d..5ce43462e39 100644
--- a/src/core/features/course/services/course-helper.ts
+++ b/src/core/features/course/services/course-helper.ts
@@ -890,7 +890,7 @@ export class CoreCourseHelperProvider {
}
// Start the download if in wifi, but return the URL right away so the file is opened.
- if (CoreNetwork.isWifi()) {
+ if (CoreNetwork.connectionIsNotMeasured()) {
this.downloadModule(module, courseId, component, componentId, files, siteId);
}
diff --git a/src/core/features/fileuploader/services/fileuploader-helper.ts b/src/core/features/fileuploader/services/fileuploader-helper.ts
index 06ddb44ba9f..7a38d1d7e78 100644
--- a/src/core/features/fileuploader/services/fileuploader-helper.ts
+++ b/src/core/features/fileuploader/services/fileuploader-helper.ts
@@ -140,7 +140,7 @@ export class CoreFileUploaderHelperProvider {
if (size < 0) {
return CoreAlerts.confirm(Translate.instant('core.fileuploader.confirmuploadunknownsize'));
- } else if (size >= wifiThreshold || (CoreNetwork.isNetworkAccessLimited() && size >= limitedThreshold)) {
+ } else if (size >= wifiThreshold || (CoreNetwork.connectionIsMeasured() && size >= limitedThreshold)) {
const readableSize = CoreText.bytesToSize(size, 2);
return CoreAlerts.confirm(Translate.instant('core.fileuploader.confirmuploadfile', { size: readableSize }));
diff --git a/src/core/features/settings/lang.json b/src/core/features/settings/lang.json
index 00202ecea47..f2df5c70b94 100644
--- a/src/core/features/settings/lang.json
+++ b/src/core/features/settings/lang.json
@@ -79,6 +79,6 @@
"synchronizenowhelp": "Synchronising a site will send pending changes and all offline activity stored in the device and will synchronise some data like messages and notifications.",
"syncsettings": "Synchronisation settings",
"total": "Total",
- "wificonnection": "Wi-Fi connection",
+ "measuredconnection": "Measured data connection",
"youradev": "You are now a developer"
}
diff --git a/src/core/features/settings/pages/deviceinfo/deviceinfo.html b/src/core/features/settings/pages/deviceinfo/deviceinfo.html
index e5386aabb0b..e665d0054b0 100644
--- a/src/core/features/settings/pages/deviceinfo/deviceinfo.html
+++ b/src/core/features/settings/pages/deviceinfo/deviceinfo.html
@@ -148,8 +148,8 @@
- {{ 'core.settings.wificonnection' | translate}}
- @if (deviceInfo.wifiConnection()) {
+ {{ 'core.settings.measuredconnection' | translate}}
+ @if (deviceInfo.measuredConnection()) {
{{ 'core.yes' | translate }}
} @else {
{{ 'core.no' | translate }}
diff --git a/src/core/features/settings/pages/deviceinfo/deviceinfo.ts b/src/core/features/settings/pages/deviceinfo/deviceinfo.ts
index 9824ac5b6b7..fa7cb93230d 100644
--- a/src/core/features/settings/pages/deviceinfo/deviceinfo.ts
+++ b/src/core/features/settings/pages/deviceinfo/deviceinfo.ts
@@ -25,7 +25,7 @@ import { CoreConfig } from '@services/config';
import { CoreToasts } from '@services/overlays/toasts';
import { CoreNavigator } from '@services/navigator';
import { CorePlatform } from '@services/platform';
-import { CoreNetwork, CoreNetworkConnection } from '@services/network';
+import { CoreNetwork, CoreNetworkConnectionType } from '@services/network';
import { CoreLoginHelper } from '@features/login/services/login-helper';
import { CoreSitesFactory } from '@services/sites-factory';
import { CoreText } from '@singletons/text';
@@ -53,7 +53,7 @@ interface CoreSettingsDeviceInfo {
deviceType: string;
screen?: string;
isOnline: Signal;
- wifiConnection: Signal;
+ measuredConnection: Signal;
cordovaVersion?: string;
platform?: string;
osVersion?: string;
@@ -96,7 +96,7 @@ export default class CoreSettingsDeviceInfoPage {
compilationTime: CoreConstants.BUILD.compilationTime || 0,
lastCommit: CoreConstants.BUILD.lastCommitHash || '',
isOnline: CoreNetwork.onlineSignal(),
- wifiConnection: computed(() => CoreNetwork.connectionTypeSignal()() === CoreNetworkConnection.WIFI),
+ measuredConnection: computed(() => CoreNetwork.connectionTypeSignal()() === CoreNetworkConnectionType.MEASURED),
localNotifAvailable: CoreLocalNotifications.isPluginAvailable() ? 'yes' : 'no',
pushId: CorePushNotifications.getPushId(),
deviceType: '',
@@ -212,7 +212,7 @@ export default class CoreSettingsDeviceInfoPage {
const deviceInfo = {
...this.deviceInfo,
isOnline: this.deviceInfo.isOnline(),
- wifiConnection: this.deviceInfo.wifiConnection(),
+ measuredConnection: this.deviceInfo.measuredConnection(),
};
CoreText.copyToClipboard(JSON.stringify(deviceInfo));
}
diff --git a/src/core/features/settings/pages/site/site.ts b/src/core/features/settings/pages/site/site.ts
index 7063aeee1e7..8b948be73d8 100644
--- a/src/core/features/settings/pages/site/site.ts
+++ b/src/core/features/settings/pages/site/site.ts
@@ -73,13 +73,13 @@ export default class CoreSitePreferencesPage implements AfterViewInit, OnDestroy
}, this.siteId);
this.isOnline = CoreNetwork.isOnline();
- this.limitedConnection = this.isOnline && CoreNetwork.isNetworkAccessLimited();
+ this.limitedConnection = CoreNetwork.connectionIsMeasured();
this.networkObserver = CoreNetwork.onChange().subscribe(() => {
// Execute the callback in the Angular zone, so change detection doesn't stop working.
NgZone.run(() => {
this.isOnline = CoreNetwork.isOnline();
- this.limitedConnection = this.isOnline && CoreNetwork.isNetworkAccessLimited();
+ this.limitedConnection = CoreNetwork.connectionIsMeasured();
});
});
}
diff --git a/src/core/features/settings/pages/synchronization/synchronization.ts b/src/core/features/settings/pages/synchronization/synchronization.ts
index 627ed67ef7a..4f67d2ef718 100644
--- a/src/core/features/settings/pages/synchronization/synchronization.ts
+++ b/src/core/features/settings/pages/synchronization/synchronization.ts
@@ -93,13 +93,13 @@ export default class CoreSettingsSynchronizationPage implements OnInit, OnDestro
});
this.isOnline = CoreNetwork.isOnline();
- this.limitedConnection = this.isOnline && CoreNetwork.isNetworkAccessLimited();
+ this.limitedConnection = CoreNetwork.connectionIsMeasured();
this.networkObserver = CoreNetwork.onChange().subscribe(() => {
// Execute the callback in the Angular zone, so change detection doesn't stop working.
NgZone.run(() => {
this.isOnline = CoreNetwork.isOnline();
- this.limitedConnection = this.isOnline && CoreNetwork.isNetworkAccessLimited();
+ this.limitedConnection = CoreNetwork.connectionIsMeasured();
});
});
diff --git a/src/core/features/settings/services/settings-helper.ts b/src/core/features/settings/services/settings-helper.ts
index 2dd2f95bcea..dceaf2486e9 100644
--- a/src/core/features/settings/services/settings-helper.ts
+++ b/src/core/features/settings/services/settings-helper.ts
@@ -241,7 +241,7 @@ export class CoreSettingsHelperProvider {
} else if (hasSyncHandlers && !CoreNetwork.isOnline()) {
// We need connection to execute sync.
throw new CoreError(Translate.instant('core.settings.cannotsyncoffline'));
- } else if (hasSyncHandlers && syncOnlyOnWifi && CoreNetwork.isNetworkAccessLimited()) {
+ } else if (hasSyncHandlers && syncOnlyOnWifi && CoreNetwork.connectionIsMeasured()) {
throw new CoreError(Translate.instant('core.settings.cannotsyncwithoutwifi'));
}
diff --git a/src/core/services/cron.ts b/src/core/services/cron.ts
index 15d3a8dc3a7..30d1b03049c 100644
--- a/src/core/services/cron.ts
+++ b/src/core/services/cron.ts
@@ -99,7 +99,7 @@ export class CoreCronDelegateService {
// Check network connection.
const syncOnlyOnWifi = await CoreConfig.get(CoreConstants.SETTINGS_SYNC_ONLY_ON_WIFI, false);
- if (syncOnlyOnWifi && !CoreNetwork.isWifi()) {
+ if (syncOnlyOnWifi && !CoreNetwork.connectionIsNotMeasured()) {
// Cannot execute in this network connection, retry soon.
this.logger.debug(`Cron job failed because your device has a limited internet connection: ${name}`);
this.scheduleNextExecution(name, CoreCronDelegateService.MIN_INTERVAL);
diff --git a/src/core/services/file-helper.ts b/src/core/services/file-helper.ts
index 53a94287cef..e51f044a792 100644
--- a/src/core/services/file-helper.ts
+++ b/src/core/services/file-helper.ts
@@ -165,7 +165,7 @@ export class CoreFileHelperProvider {
}
// The file system is available.
- const isWifi = CoreNetwork.isWifi();
+ const isWifi = CoreNetwork.connectionIsNotMeasured();
const isOnline = CoreNetwork.isOnline();
if (state === DownloadStatus.DOWNLOADED) {
diff --git a/src/core/services/filepool.ts b/src/core/services/filepool.ts
index 2ecbb520d34..88a9be0bb17 100644
--- a/src/core/services/filepool.ts
+++ b/src/core/services/filepool.ts
@@ -828,7 +828,7 @@ export class CoreFilepoolProvider {
}
// Calculate the size of the file.
- const isWifi = CoreNetwork.isWifi();
+ const isWifi = CoreNetwork.connectionIsNotMeasured();
const sizeUnknown = size <= 0;
if (!sizeUnknown) {
@@ -3027,7 +3027,7 @@ export class CoreFilepoolProvider {
*/
shouldDownload(size: number): boolean {
return size <= CoreFilepoolProvider.DOWNLOAD_THRESHOLD ||
- (CoreNetwork.isWifi() && size <= CoreFilepoolProvider.WIFI_DOWNLOAD_THRESHOLD);
+ (CoreNetwork.connectionIsNotMeasured() && size <= CoreFilepoolProvider.WIFI_DOWNLOAD_THRESHOLD);
}
/**
diff --git a/src/core/services/network.ts b/src/core/services/network.ts
index 0d3493d2a61..42d17b303db 100644
--- a/src/core/services/network.ts
+++ b/src/core/services/network.ts
@@ -12,14 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { computed, effect, Injectable, Signal, signal } from '@angular/core';
+import { effect, Injectable, Signal, signal } from '@angular/core';
import { CorePlatform } from '@services/platform';
import { Network } from '@awesome-cordova-plugins/network/ngx';
import { makeSingleton } from '@singletons';
import { Observable, Subject, merge } from 'rxjs';
import { CoreHTMLClasses } from '@singletons/html-classes';
-export enum CoreNetworkConnection {
+enum CoreNetworkConnection {
UNKNOWN = 'unknown',
ETHERNET = 'ethernet',
WIFI = 'wifi',
@@ -30,6 +30,13 @@ export enum CoreNetworkConnection {
NONE = 'none',
}
+export enum CoreNetworkConnectionType {
+ UNKNOWN = 'unknown',
+ NOT_MEASURED = 'not_measured',
+ MEASURED = 'measured',
+ OFFLINE = 'offline',
+}
+
/**
* Service to manage network connections.
*/
@@ -41,11 +48,10 @@ export class CoreNetworkService extends Network {
protected connectObservable = new Subject<'connected'>();
protected connectStableObservable = new Subject<'connected'>();
protected disconnectObservable = new Subject<'disconnected'>();
- protected forceConnectionMode?: CoreNetworkConnection;
- protected readonly online = signal(false);
+ protected forceConnectionMode?: CoreNetworkConnectionType;
protected connectStableTimeout?: number;
- private readonly _connectionType = signal(CoreNetworkConnection.UNKNOWN);
- private readonly _limitedConnection = computed(() => this.online() && CoreNetwork.isNetworkAccessLimited());
+ protected readonly online = signal(false);
+ private readonly _connectionType = signal(CoreNetworkConnectionType.UNKNOWN);
constructor() {
super();
@@ -69,7 +75,7 @@ export class CoreNetworkService extends Network {
});
}
- get connectionType(): CoreNetworkConnection {
+ get connectionType(): CoreNetworkConnectionType {
CoreNetwork.updateConnectionType();
return this._connectionType();
@@ -91,18 +97,6 @@ export class CoreNetworkService extends Network {
this.fireObservable();
});
} else {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- ( window).Connection = {
- UNKNOWN: CoreNetworkConnection.UNKNOWN, // eslint-disable-line @typescript-eslint/naming-convention
- ETHERNET: CoreNetworkConnection.ETHERNET, // eslint-disable-line @typescript-eslint/naming-convention
- WIFI: CoreNetworkConnection.WIFI, // eslint-disable-line @typescript-eslint/naming-convention
- CELL_2G: CoreNetworkConnection.CELL_2G, // eslint-disable-line @typescript-eslint/naming-convention
- CELL_3G: CoreNetworkConnection.CELL_3G, // eslint-disable-line @typescript-eslint/naming-convention
- CELL_4G: CoreNetworkConnection.CELL_4G, // eslint-disable-line @typescript-eslint/naming-convention
- CELL: CoreNetworkConnection.CELL, // eslint-disable-line @typescript-eslint/naming-convention
- NONE: CoreNetworkConnection.NONE, // eslint-disable-line @typescript-eslint/naming-convention
- };
-
window.addEventListener('online', () => {
this.fireObservable();
}, false);
@@ -130,7 +124,7 @@ export class CoreNetworkService extends Network {
*
* @param value Value to set.
*/
- setForceConnectionMode(value: CoreNetworkConnection): void {
+ setForceConnectionMode(value: CoreNetworkConnectionType): void {
this.forceConnectionMode = value;
this.fireObservable();
}
@@ -151,7 +145,7 @@ export class CoreNetworkService extends Network {
// Recalculate connection type.
CoreNetwork.updateConnectionType();
- if (this.forceConnectionMode === CoreNetworkConnection.NONE) {
+ if (this.forceConnectionMode === CoreNetworkConnectionType.OFFLINE) {
this.online.set(false);
return;
@@ -166,7 +160,7 @@ export class CoreNetworkService extends Network {
}
const type = this._connectionType();
- let online = type !== null && type !== CoreNetworkConnection.NONE && type !== CoreNetworkConnection.UNKNOWN;
+ let online = type !== null && type !== CoreNetworkConnectionType.OFFLINE && type !== CoreNetworkConnectionType.UNKNOWN;
// Double check we are not online because we cannot rely 100% in Cordova APIs.
if (!online && navigator.onLine) {
@@ -187,12 +181,32 @@ export class CoreNetworkService extends Network {
}
if (CorePlatform.isMobile()) {
- this._connectionType.set(this.type as CoreNetworkConnection);
-
- return;
+ switch (this.type) {
+ case CoreNetworkConnection.WIFI:
+ case CoreNetworkConnection.ETHERNET:
+ this._connectionType.set(CoreNetworkConnectionType.NOT_MEASURED);
+
+ return;
+ case CoreNetworkConnection.CELL:
+ case CoreNetworkConnection.CELL_2G:
+ case CoreNetworkConnection.CELL_3G:
+ case CoreNetworkConnection.CELL_4G:
+ this._connectionType.set(CoreNetworkConnectionType.MEASURED);
+
+ return;
+ case CoreNetworkConnection.NONE:
+ this._connectionType.set(CoreNetworkConnectionType.OFFLINE);
+
+ return;
+ default:
+ case CoreNetworkConnection.UNKNOWN:
+ this._connectionType.set(CoreNetworkConnectionType.UNKNOWN);
+
+ return;
+ }
}
- this._connectionType.set(this.online() ? CoreNetworkConnection.WIFI : CoreNetworkConnection.NONE);
+ this._connectionType.set(this.online() ? CoreNetworkConnectionType.NOT_MEASURED : CoreNetworkConnectionType.OFFLINE);
}
/**
@@ -213,21 +227,12 @@ export class CoreNetworkService extends Network {
return this.online.asReadonly();
}
- /**
- * Returns a signal to watch limited connection status.
- *
- * @returns Signal.
- */
- limitedConnectionSignal(): Signal {
- return this._limitedConnection;
- }
-
/**
* Returns a signal to watch connection type.
*
* @returns Signal.
*/
- connectionTypeSignal(): Signal {
+ connectionTypeSignal(): Signal {
return this._connectionType.asReadonly();
}
@@ -284,18 +289,29 @@ export class CoreNetworkService extends Network {
* Check if device uses a limited connection.
*
* @returns Whether the device uses a limited connection.
+ * @deprecated since 5.0. Use connectionIsMeasured instead.
*/
isNetworkAccessLimited(): boolean {
- const limited: CoreNetworkConnection[] = [
- CoreNetworkConnection.CELL_2G,
- CoreNetworkConnection.CELL_3G,
- CoreNetworkConnection.CELL_4G,
- CoreNetworkConnection.CELL,
- ];
+ return this.connectionIsMeasured();
+ }
- const type = this.connectionType;
+ /**
+ * Check if device uses a wifi connection.
+ *
+ * @returns Whether the device uses a wifi connection.
+ * @deprecated since 5.0. Use connectionIsNotMeasured instead.
+ */
+ isWifi(): boolean {
+ return this.connectionIsNotMeasured();
+ }
- return limited.indexOf(type) > -1;
+ /**
+ * Check if device uses a limited connection.
+ *
+ * @returns Whether the device uses a limited connection.
+ */
+ connectionIsMeasured(): boolean {
+ return this.connectionType === CoreNetworkConnectionType.MEASURED;
}
/**
@@ -303,8 +319,8 @@ export class CoreNetworkService extends Network {
*
* @returns Whether the device uses a wifi connection.
*/
- isWifi(): boolean {
- return this.isOnline() && !this.isNetworkAccessLimited();
+ connectionIsNotMeasured(): boolean {
+ return this.connectionType === CoreNetworkConnectionType.NOT_MEASURED;
}
}
diff --git a/src/core/services/overlays/alerts.ts b/src/core/services/overlays/alerts.ts
index c69f500057f..5759c9a7e6e 100644
--- a/src/core/services/overlays/alerts.ts
+++ b/src/core/services/overlays/alerts.ts
@@ -186,7 +186,7 @@ export class CoreAlertsService {
const limitedThreshold = options.limitedThreshold ?? CoreConstants.DOWNLOAD_THRESHOLD;
let wifiPrefix = '';
- if (CoreNetwork.isNetworkAccessLimited()) {
+ if (CoreNetwork.connectionIsMeasured()) {
wifiPrefix = Translate.instant('core.course.confirmlimiteddownload');
}
@@ -203,7 +203,7 @@ export class CoreAlertsService {
{ size: readableSize, availableSpace: availableSpace },
));
} else if (options.alwaysConfirm || size.size >= wifiThreshold ||
- (CoreNetwork.isNetworkAccessLimited() && size.size >= limitedThreshold)) {
+ (CoreNetwork.connectionIsMeasured() && size.size >= limitedThreshold)) {
return this.confirm(wifiPrefix + Translate.instant(
options.message ?? (size.size === 0 ? 'core.course.confirmdownloadzerosize' : 'core.course.confirmdownload'),
diff --git a/src/core/services/ws.ts b/src/core/services/ws.ts
index e038ceba1cd..5f94635c959 100644
--- a/src/core/services/ws.ts
+++ b/src/core/services/ws.ts
@@ -398,7 +398,7 @@ export class CoreWSProvider {
* @returns Timeout in ms.
*/
getRequestTimeout(): number {
- return CoreNetwork.isNetworkAccessLimited() ? CoreConstants.WS_TIMEOUT : CoreConstants.WS_TIMEOUT_WIFI;
+ return CoreNetwork.connectionIsMeasured() ? CoreConstants.WS_TIMEOUT : CoreConstants.WS_TIMEOUT_WIFI;
}
/**