diff --git a/config.xml b/config.xml
index a6c5369ec0e..cf8858bbddf 100644
--- a/config.xml
+++ b/config.xml
@@ -30,9 +30,10 @@
-
+
+
diff --git a/cordova-plugin-moodleapp/plugin.xml b/cordova-plugin-moodleapp/plugin.xml
index bbce2501cd8..eee9de23c2d 100644
--- a/cordova-plugin-moodleapp/plugin.xml
+++ b/cordova-plugin-moodleapp/plugin.xml
@@ -25,6 +25,10 @@
+
+
+
+
@@ -37,6 +41,7 @@
+
diff --git a/cordova-plugin-moodleapp/src/android/PinchToZoom.java b/cordova-plugin-moodleapp/src/android/PinchToZoom.java
new file mode 100644
index 00000000000..38eab6af8bd
--- /dev/null
+++ b/cordova-plugin-moodleapp/src/android/PinchToZoom.java
@@ -0,0 +1,42 @@
+// (C) Copyright 2025 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.
+
+package com.moodle.moodlemobile;
+
+import org.apache.cordova.CordovaInterface;
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.CordovaWebView;
+
+import android.util.Log;
+import android.webkit.WebSettings;
+import android.webkit.WebSettings.ZoomDensity;
+import android.webkit.WebView;
+
+public class PinchToZoom extends CordovaPlugin {
+
+ public static final String TAG = "PinchToZoom";
+
+ @Override
+ public void initialize(CordovaInterface cordova, CordovaWebView webView) {
+ Log.d(TAG, "Initializing pinch-to-zoom");
+
+ super.initialize(cordova, webView);
+
+ WebSettings settings = ((WebView) webView.getView()).getSettings();
+ settings.setBuiltInZoomControls(true);
+ settings.setDefaultZoom(WebSettings.ZoomDensity.MEDIUM);
+ settings.setDisplayZoomControls(false);
+ settings.setSupportZoom(true);
+ }
+}
diff --git a/package.json b/package.json
index c883fad1d6d..8de2866e17b 100644
--- a/package.json
+++ b/package.json
@@ -228,7 +228,10 @@
"cordova-plugin-local-notification": {
"ANDROID_SUPPORT_V4_VERSION": "26.+"
},
- "cordova-plugin-moodleapp": {},
+ "cordova-plugin-moodleapp": {
+ "ANDROIDX_VERSION": "1.0.0",
+ "ANDROIDX_APPCOMPAT_VERSION": "1.3.1"
+ },
"cordova-plugin-network-information": {},
"cordova-plugin-prevent-override": {},
"cordova-plugin-screen-orientation": {},
@@ -238,4 +241,4 @@
"nl.kingsquare.cordova.background-audio": {}
}
}
-}
+}
\ No newline at end of file
diff --git a/patches/@ionic+core+8.4.1.patch b/patches/@ionic+core+8.4.1.patch
index dae8830b13a..a79030a1560 100644
--- a/patches/@ionic+core+8.4.1.patch
+++ b/patches/@ionic+core+8.4.1.patch
@@ -152,3 +152,48 @@ index c3d2d8e..bc40d4f 100644
const root = getElementRoot(baseEl);
const contentEl = root.querySelector('.popover-content');
const referenceSizeEl = trigger || ((_a = ev === null || ev === void 0 ? void 0 : ev.detail) === null || _a === void 0 ? void 0 : _a.ionShadowTarget) || (ev === null || ev === void 0 ? void 0 : ev.target);
+diff --git a/node_modules/@ionic/core/dist/esm/input-shims-0314bbe5.js b/node_modules/@ionic/core/dist/esm/input-shims-0314bbe5.js
+index dd9d410..846146f 100644
+--- a/node_modules/@ionic/core/dist/esm/input-shims-0314bbe5.js
++++ b/node_modules/@ionic/core/dist/esm/input-shims-0314bbe5.js
+@@ -338,7 +338,8 @@ const enableScrollAssist = (componentEl, inputEl, contentEl, footerEl, keyboardH
+ const focusOut = () => {
+ hasKeyboardBeenPresentedForTextField = false;
+ win === null || win === void 0 ? void 0 : win.removeEventListener('ionKeyboardDidShow', keyboardShow);
+- componentEl.removeEventListener('focusout', focusOut);
++ // Patched: Attach focusin/focusout events to inputEl instead of componentEl to allow focusing buttons inside .
++ inputEl.removeEventListener('focusout', focusOut);
+ };
+ /**
+ * When the input is about to receive
+@@ -358,13 +358,15 @@ const enableScrollAssist = (componentEl, inputEl, contentEl, footerEl, keyboardH
+ }
+ jsSetFocus(componentEl, inputEl, contentEl, footerEl, keyboardHeight, addScrollPadding, disableClonedInput, platformHeight);
+ win === null || win === void 0 ? void 0 : win.addEventListener('ionKeyboardDidShow', keyboardShow);
+- componentEl.addEventListener('focusout', focusOut);
++ // Patched: Attach focusin/focusout events to inputEl instead of componentEl to allow focusing buttons inside .
++ inputEl.addEventListener('focusout', focusOut);
+ };
+- componentEl.addEventListener('focusin', focusIn);
++ // Patched: Attach focusin/focusout events to inputEl instead of componentEl to allow focusing buttons inside .
++ inputEl.addEventListener('focusin', focusIn);
+ return () => {
+- componentEl.removeEventListener('focusin', focusIn);
++ inputEl.removeEventListener('focusin', focusIn);
+ win === null || win === void 0 ? void 0 : win.removeEventListener('ionKeyboardDidShow', keyboardShow);
+- componentEl.removeEventListener('focusout', focusOut);
++ inputEl.removeEventListener('focusout', focusOut);
+ };
+ };
+ /**
+--- a/node_modules/@ionic/core/dist/esm/ion-item_8.entry.js
++++ b/node_modules/@ionic/core/dist/esm/ion-item_8.entry.js
+@@ -109,7 +109,7 @@ const Item = class {
+ // inputs, then those need to individually get each click
+ hasCover() {
+ const inputs = this.el.querySelectorAll('ion-checkbox, ion-datetime, ion-select, ion-radio');
+- return inputs.length === 1 && !this.multipleInputs;
++ return inputs.length === 1;
+ }
+ // If the item has an href or button property it will render a native
+ // anchor or button that is clickable
diff --git a/scripts/langindex.json b/scripts/langindex.json
index 66eb705688e..4a31ebf4178 100644
--- a/scripts/langindex.json
+++ b/scripts/langindex.json
@@ -1743,6 +1743,7 @@
"core.decsep": "langconfig",
"core.defaultvalue": "tool_usertours",
"core.delete": "moodle",
+ "core.deleted": "moodle",
"core.deletedoffline": "local_moodlemobileapp",
"core.deleteduser": "bulkusers",
"core.deleting": "local_moodlemobileapp",
@@ -2165,6 +2166,7 @@
"core.login.login": "moodle",
"core.login.loginbutton": "local_moodlemobileapp",
"core.login.loginsteps": "moodle",
+ "core.login.logoof": "moodle",
"core.login.missingemail": "moodle",
"core.login.missingfirstname": "moodle",
"core.login.missinglastname": "moodle",
@@ -2450,6 +2452,7 @@
"core.selectall": "moodle",
"core.send": "message",
"core.sending": "chat",
+ "core.sent": "moodle",
"core.serverconnection": "local_moodlemobileapp",
"core.settings.about": "local_moodlemobileapp",
"core.settings.accessstatement": "access",
@@ -2487,6 +2490,7 @@
"core.settings.enableanalytics": "local_moodlemobileapp",
"core.settings.enableanalyticsdescription": "local_moodlemobileapp",
"core.settings.enabledownloadsection": "local_moodlemobileapp",
+ "core.settings.enablepinchtozoom": "local_moodlemobileapp",
"core.settings.enablerichtexteditor": "local_moodlemobileapp",
"core.settings.enablerichtexteditordescription": "local_moodlemobileapp",
"core.settings.encryptedpushsupported": "local_moodlemobileapp",
diff --git a/src/addons/block/myoverview/components/myoverview/addon-block-myoverview.html b/src/addons/block/myoverview/components/myoverview/addon-block-myoverview.html
index b7a34cbaf3f..5d0a88dccb3 100644
--- a/src/addons/block/myoverview/components/myoverview/addon-block-myoverview.html
+++ b/src/addons/block/myoverview/components/myoverview/addon-block-myoverview.html
@@ -96,12 +96,12 @@ {{ 'addon.block_myoverview.pluginname' | translate }}
-
+
{{'addon.block_myoverview.noresult' | translate}}
-
-
+
+
{{'addon.block_myoverview.nocoursesenrolled' | translate}}
-
+
{{'addon.block_myoverview.noresultdescription' | translate}}
diff --git a/src/addons/block/myoverview/components/myoverview/myoverview.scss b/src/addons/block/myoverview/components/myoverview/myoverview.scss
index 50bb3c00af5..abfe545be56 100644
--- a/src/addons/block/myoverview/components/myoverview/myoverview.scss
+++ b/src/addons/block/myoverview/components/myoverview/myoverview.scss
@@ -33,6 +33,7 @@
core-empty-box {
.item-heading {
+ font-size: 1rem;
font-weight: bold;
margin-bottom: 0;
}
diff --git a/src/addons/block/timeline/components/events/addon-block-timeline-events.html b/src/addons/block/timeline/components/events/addon-block-timeline-events.html
index e9a6c41ec28..1f55dd71a34 100644
--- a/src/addons/block/timeline/components/events/addon-block-timeline-events.html
+++ b/src/addons/block/timeline/components/events/addon-block-timeline-events.html
@@ -9,7 +9,14 @@
- {{ dayEvents.dayTimestamp * 1000 | coreFormatDate:"strftimedaydate" }}
+ @if (course) {
+
+ } @else {
+
+ }
+
+ {{ dayEvents.dayTimestamp * 1000 | coreFormatDate:"strftimedaydate" }}
+
diff --git a/src/addons/messages/tests/behat/snapshots/test-basic-usage-of-messages-in-app-view-recent-conversations-and-contacts_22.png b/src/addons/messages/tests/behat/snapshots/test-basic-usage-of-messages-in-app-view-recent-conversations-and-contacts_22.png
index 368bf2370e7..5d58f7e7609 100644
Binary files a/src/addons/messages/tests/behat/snapshots/test-basic-usage-of-messages-in-app-view-recent-conversations-and-contacts_22.png and b/src/addons/messages/tests/behat/snapshots/test-basic-usage-of-messages-in-app-view-recent-conversations-and-contacts_22.png differ
diff --git a/src/addons/mod/forum/components/index/index.html b/src/addons/mod/forum/components/index/index.html
index 620db067f7b..df3ce64059c 100644
--- a/src/addons/mod/forum/components/index/index.html
+++ b/src/addons/mod/forum/components/index/index.html
@@ -84,70 +84,75 @@
{{ 'addon.mod_forum.errorloadingsortingorderdetails' | translate }}
-
-
-
-
-
-
-
-
-
-
-
-
{{discussion.userfullname}}
-
-
-
-
-
- {{discussion.created * 1000 | coreFormatDate: "strftimerecentfull"}}
-
-
-
- {{ 'core.notsent' | translate }}
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{discussion.userfullname}}
+
+
+
+
+
+ {{discussion.created * 1000 | coreFormatDate: "strftimerecentfull"}}
+
+
+
+ {{ 'core.notsent' | translate }}
+
+
-
-
-
-
- {{ 'addon.mod_forum.lastpost' | translate }}
- discussion.created">
- {{ discussion.timemodified | coreTimeAgo }}
-
-
- {{ discussion.created | coreTimeAgo }}
-
-
-
-
-
-
- {{ 'addon.mod_forum.numreplies' | translate:{numreplies: discussion.numreplies} }}
-
- {{ discussion.numunread }}
-
- {{ 'addon.mod_forum.unreadpostsnumber' | translate:{ '$a' : discussion.numunread} }}
-
-
-
-
-
-
-
-
-
-
+
+
+
+ {{ 'addon.mod_forum.lastpost' | translate }}
+ discussion.created">
+ {{ discussion.timemodified | coreTimeAgo }}
+
+
+ {{ discussion.created | coreTimeAgo }}
+
+
+
+
+
+
+ {{ 'addon.mod_forum.numreplies' | translate:{numreplies: discussion.numreplies} }}
+
+ {{ discussion.numunread }}
+
+ {{ 'addon.mod_forum.unreadpostsnumber' | translate:{ '$a' : discussion.numunread} }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/addons/mod/forum/components/index/index.scss b/src/addons/mod/forum/components/index/index.scss
index e764855371f..bd16c583394 100644
--- a/src/addons/mod/forum/components/index/index.scss
+++ b/src/addons/mod/forum/components/index/index.scss
@@ -70,4 +70,10 @@
border-top: 1px solid var(--spacer-color);
}
+ .ripple-parent {
+ position: relative;
+ ion-ripple-effect {
+ z-index: 1;
+ }
+ }
}
diff --git a/src/addons/mod/forum/components/post/post.html b/src/addons/mod/forum/components/post/post.html
index 1a35dd9eb53..01cb92d3c2b 100644
--- a/src/addons/mod/forum/components/post/post.html
+++ b/src/addons/mod/forum/components/post/post.html
@@ -111,20 +111,19 @@
0">
-
-
-
-
- {{ 'addon.mod_forum.advanced' | translate }}
-
-
-
-
-
+
+
+
+
+ {{ 'addon.mod_forum.advanced' | translate }}
+
+
+
+
+
+
+
diff --git a/src/addons/mod/forum/components/post/post.ts b/src/addons/mod/forum/components/post/post.ts
index c1ac9d34aeb..5d10ebe19ce 100644
--- a/src/addons/mod/forum/components/post/post.ts
+++ b/src/addons/mod/forum/components/post/post.ts
@@ -60,6 +60,7 @@ import { CoreWSFile } from '@services/ws';
import { CorePromiseUtils } from '@singletons/promise-utils';
import { CoreWSError } from '@classes/errors/wserror';
import { CoreAlerts } from '@services/overlays/alerts';
+import { AccordionGroupCustomEvent } from '@ionic/angular';
/**
* Components that shows a discussion post, its attachments and the action buttons allowed (reply, etc.).
@@ -232,7 +233,6 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges
// Show advanced fields if any of them has not the default value.
this.advanced = this.formData.files.length > 0;
-
if (!isEditing || !postId || postId <= 0) {
this.preparePostData = undefined;
}
@@ -620,10 +620,10 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges
}
/**
- * Show or hide advanced form fields.
+ * Function called when advanced accordion is toggled.
*/
- toggleAdvanced(): void {
- this.advanced = !this.advanced;
+ onAdvancedChanged(event: AccordionGroupCustomEvent): void {
+ this.advanced = event.detail.value === 'advanced';
}
/**
diff --git a/src/addons/mod/forum/tests/behat/snapshots/test-basic-usage-of-forum-activity-in-app-reply-a-post_14.png b/src/addons/mod/forum/tests/behat/snapshots/test-basic-usage-of-forum-activity-in-app-reply-a-post_14.png
index 24d1bc043d0..8b048339f05 100644
Binary files a/src/addons/mod/forum/tests/behat/snapshots/test-basic-usage-of-forum-activity-in-app-reply-a-post_14.png and b/src/addons/mod/forum/tests/behat/snapshots/test-basic-usage-of-forum-activity-in-app-reply-a-post_14.png differ
diff --git a/src/addons/mod/quiz/pages/player/player.ts b/src/addons/mod/quiz/pages/player/player.ts
index 0c977fad8f4..3eaadd32529 100644
--- a/src/addons/mod/quiz/pages/player/player.ts
+++ b/src/addons/mod/quiz/pages/player/player.ts
@@ -174,6 +174,8 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
try {
await this.processAttempt(false, false);
+
+ modal.dismissWithStatus('core.sent', true);
} catch (error) {
// Save attempt failed. Show confirmation.
modal.dismiss();
@@ -181,8 +183,6 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
await CoreAlerts.confirm(Translate.instant('addon.mod_quiz.confirmleavequizonerror'));
CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId());
- } finally {
- modal.dismiss();
}
return true;
@@ -252,10 +252,11 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
}, 50);
}
}
+
+ modal.dismissWithStatus('core.sent', true);
} catch (error) {
- CoreAlerts.showError(error, { default: 'Error performing action.' });
- } finally {
modal?.dismiss();
+ CoreAlerts.showError(error, { default: 'Error performing action.' });
}
}
@@ -301,7 +302,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
try {
await this.processAttempt(false, false);
- modal.dismiss();
+ modal.dismissWithStatus('core.sent', true);
} catch (error) {
CoreAlerts.showError(error, { default: Translate.instant('addon.mod_quiz.errorsaveattempt') });
modal.dismiss();
@@ -468,7 +469,10 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
await this.refreshAttempt();
await this.loadSummary();
}
+
+ modal.dismissWithStatus('core.sent', true);
} catch (error) {
+ modal?.dismiss();
// eslint-disable-next-line promise/catch-or-return
CoreAlerts
.showError(error, { default: Translate.instant('addon.mod_quiz.errorsaveattempt') })
@@ -481,8 +485,6 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
return;
});
- } finally {
- modal?.dismiss();
}
}
diff --git a/src/addons/privatefiles/components/file/file.html b/src/addons/privatefiles/components/file/file.html
index a21f68e8028..367ea8a0f5d 100644
--- a/src/addons/privatefiles/components/file/file.html
+++ b/src/addons/privatefiles/components/file/file.html
@@ -1,7 +1,7 @@
-
+
@if (file) {
-
+
@if (showCheckbox) {
@@ -13,7 +13,7 @@
- {{fileName}}
+ {{fileName}}
@if (state === statusDownloaded) {
+
}
diff --git a/src/addons/privatefiles/components/file/file.scss b/src/addons/privatefiles/components/file/file.scss
index 5fed5bdbfce..7d5e9632784 100644
--- a/src/addons/privatefiles/components/file/file.scss
+++ b/src/addons/privatefiles/components/file/file.scss
@@ -11,4 +11,8 @@
--inner-border-width: 0 !important;
}
+
+ ion-ripple-effect {
+ z-index: 1;
+ }
}
diff --git a/src/addons/storagemanager/pages/course-storage/course-storage.html b/src/addons/storagemanager/pages/course-storage/course-storage.html
index eb06b774445..50c6f6ec366 100644
--- a/src/addons/storagemanager/pages/course-storage/course-storage.html
+++ b/src/addons/storagemanager/pages/course-storage/course-storage.html
@@ -45,11 +45,9 @@ {{ 'addon.storagemanager.coursedownloads' | translate }}
-
-
-
-
-
+
+
+
@@ -57,10 +55,11 @@ {{ 'addon.storagemanager.coursedownloads' | translate }}
0" [id]="'addons-course-storage-'+section.id">
-
-
+
diff --git a/src/core/components/local-file/core-local-file.scss b/src/core/components/local-file/core-local-file.scss
new file mode 100644
index 00000000000..211f65a7801
--- /dev/null
+++ b/src/core/components/local-file/core-local-file.scss
@@ -0,0 +1,5 @@
+:host {
+ ion-ripple-effect {
+ z-index: 1;
+ }
+}
diff --git a/src/core/components/local-file/local-file.ts b/src/core/components/local-file/local-file.ts
index afce965af18..30f8b88443c 100644
--- a/src/core/components/local-file/local-file.ts
+++ b/src/core/components/local-file/local-file.ts
@@ -41,6 +41,7 @@ import { CoreAlerts } from '@services/overlays/alerts';
@Component({
selector: 'core-local-file',
templateUrl: 'core-local-file.html',
+ styleUrl: 'core-local-file.scss',
})
export class CoreLocalFileComponent implements OnInit {
diff --git a/src/core/components/site-logo/site-logo.html b/src/core/components/site-logo/site-logo.html
index c4fff46bead..445eb9c2bb3 100644
--- a/src/core/components/site-logo/site-logo.html
+++ b/src/core/components/site-logo/site-logo.html
@@ -1,7 +1,7 @@
diff --git a/src/core/components/site-logo/site-logo.ts b/src/core/components/site-logo/site-logo.ts
index ae88652c8ce..e38f7e9d30b 100644
--- a/src/core/components/site-logo/site-logo.ts
+++ b/src/core/components/site-logo/site-logo.ts
@@ -20,6 +20,7 @@ import { CoreSite } from '@classes/sites/site';
import { toBoolean } from '@/core/transforms/boolean';
import { CorePromiseUtils } from '@singletons/promise-utils';
import { CoreUnauthenticatedSite } from '@classes/sites/unauthenticated-site';
+import { CoreConstants } from '@/core/constants';
/**
* Component to render the current site logo.
@@ -46,6 +47,7 @@ export class CoreSiteLogoComponent implements OnInit, OnDestroy {
logoLoaded = false;
fallbackLogo = '';
showSiteName = true;
+ appName = CoreConstants.CONFIG.appname;
protected updateSiteObserver?: CoreEventObserver;
diff --git a/src/core/components/tabs/tabs.scss b/src/core/components/tabs/tabs.scss
index 5e9b48cb4b2..ab661b6a2bd 100644
--- a/src/core/components/tabs/tabs.scss
+++ b/src/core/components/tabs/tabs.scss
@@ -50,7 +50,7 @@
flex-grow: 1;
swiper-slide {
- border-bottom: 2px solid transparent;
+ border-bottom: 4px solid transparent;
min-width: 100px;
height: var(--height);
cursor: pointer;
diff --git a/src/core/constants.ts b/src/core/constants.ts
index f14df0ecf28..64c0065948a 100644
--- a/src/core/constants.ts
+++ b/src/core/constants.ts
@@ -142,6 +142,7 @@ export class CoreConstants {
static readonly SETTINGS_COLOR_SCHEME = 'CoreSettingsColorScheme';
static readonly SETTINGS_ANALYTICS_ENABLED = 'CoreSettingsAnalyticsEnabled';
static readonly SETTINGS_DONT_SHOW_EXTERNAL_LINK_WARN = 'CoreSettingsDontShowExtLinkWarn';
+ static readonly SETTINGS_PINCH_TO_ZOOM = 'CoreSettingsPinchToZoom';
// WS constants.
static readonly WS_TIMEOUT = 30000; // Timeout when not in WiFi.
diff --git a/src/core/directives/collapsible-footer.ts b/src/core/directives/collapsible-footer.ts
index d58f14a1d04..bbc01b2c13a 100644
--- a/src/core/directives/collapsible-footer.ts
+++ b/src/core/directives/collapsible-footer.ts
@@ -58,6 +58,7 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
protected pageDidEnterListener?: EventListener;
protected keyUpListener?: EventListener;
protected page?: HTMLElement;
+ protected moduleNav: HTMLElement | null = null;
constructor(el: ElementRef, protected ionContent: IonContent) {
this.element = el.nativeElement;
@@ -104,10 +105,11 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
// Set a minimum height value.
this.initialHeight = this.element.getBoundingClientRect().height || this.initialHeight;
- const moduleNav = this.element.querySelector('core-course-module-navigation');
- if (moduleNav) {
+ this.moduleNav = this.element.tagName === 'CORE-COURSE-MODULE-NAVIGATION' ?
+ this.element : this.element.querySelector('core-course-module-navigation');
+ if (this.moduleNav && this.moduleNav !== this.element) {
this.element.classList.add('has-module-nav');
- this.finalHeight = this.initialHeight - (moduleNav.getBoundingClientRect().height);
+ this.finalHeight = this.initialHeight - this.moduleNav.getBoundingClientRect().height;
}
this.previousHeight = this.initialHeight;
@@ -196,6 +198,11 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
document.activeElement.scrollIntoView({ block: 'center' });
}
});
+
+ // Show footer when it is focused,
+ this.moduleNav?.addEventListener('focusin', () => {
+ this.setBarHeight(this.initialHeight);
+ });
}
/**
@@ -213,7 +220,8 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
*/
protected onScroll(scrollDetail: ScrollDetail, scrollElement: HTMLElement): void {
const maxScroll = scrollElement.scrollHeight - scrollElement.offsetHeight;
- if (scrollDetail.scrollTop <= 0 || (this.appearOnBottom && scrollDetail.scrollTop >= maxScroll)) {
+ const footerHasFocus = this.moduleNav?.contains(document.activeElement);
+ if (scrollDetail.scrollTop <= 0 || (this.appearOnBottom && scrollDetail.scrollTop >= maxScroll) || footerHasFocus) {
// Reset.
this.setBarHeight(this.initialHeight);
} else {
diff --git a/src/core/features/course/components/course-format/course-format.ts b/src/core/features/course/components/course-format/course-format.ts
index 6638b96501a..c08b17b94b5 100644
--- a/src/core/features/course/components/course-format/course-format.ts
+++ b/src/core/features/course/components/course-format/course-format.ts
@@ -483,6 +483,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
component: CoreCourseCourseIndexComponent,
initialBreakpoint: 1,
breakpoints: [0, 1],
+ handle: false,
componentProps: {
course: this.course,
sections: this.sections,
diff --git a/src/core/features/course/components/course-index/course-index.html b/src/core/features/course/components/course-index/course-index.html
index 14b611e81ab..a9ef8818b2f 100644
--- a/src/core/features/course/components/course-index/course-index.html
+++ b/src/core/features/course/components/course-index/course-index.html
@@ -33,27 +33,33 @@
-
-
-
-
-
-
-
-
- {{highlighted}}
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ {{highlighted}}
+
+
+
+
+
+
diff --git a/src/core/features/course/components/course-index/course-index.scss b/src/core/features/course/components/course-index/course-index.scss
index 0ee1282631d..2d8d934d878 100644
--- a/src/core/features/course/components/course-index/course-index.scss
+++ b/src/core/features/course/components/course-index/course-index.scss
@@ -20,6 +20,9 @@ ion-item.item {
--background: var(--primary-tint);
--color: var(--gray-900);
border: 0px;
+ ion-label h2 {
+ font-weight: bold;
+ }
}
&.item-hightlighted {
@@ -84,10 +87,22 @@ ion-item.item {
&[role=button] {
min-height: auto;
min-width: auto;
+ @include core-focus-outline();
}
}
+
+ &::part(native) {
+ @include core-focus-inset-outline();
+ }
}
div.core-course-index-subsection {
@include padding-horizontal(16px, null);
}
+
+.ripple-parent {
+ position: relative;
+ ion-ripple-effect {
+ z-index: 1;
+ }
+}
diff --git a/src/core/features/course/components/module-completion/module-completion.scss b/src/core/features/course/components/module-completion/module-completion.scss
index 27c7e8302a1..2ebc5b6e001 100644
--- a/src/core/features/course/components/module-completion/module-completion.scss
+++ b/src/core/features/course/components/module-completion/module-completion.scss
@@ -11,6 +11,11 @@
color: var(--ion-color-shade);
}
+ ion-button.button-solid.ion-color-success.ion-focused::part(native) {
+ background: var(--ion-color);
+ color: var(--ion-color-contrast);
+ }
+
ion-button.button-outline::part(native){
border-color: var(--gray-400);
}
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 52a8bdf074d..51da2cb313d 100644
--- a/src/core/features/course/components/module/core-course-module.html
+++ b/src/core/features/course/components/module/core-course-module.html
@@ -1,7 +1,8 @@
@@ -12,8 +13,18 @@
[isBranded]="module.branded" />
-
+ @if (module.handlerData.action && module.uservisible) {
+
+
+
+ } @else {
+
+ }
+
+
+
{{ 'core.course.lastaccessedactivity' | translate }}
+
+ @if (module.handlerData.action && module.uservisible) {
+
+ }
diff --git a/src/core/features/course/pages/course-summary/course-summary.html b/src/core/features/course/pages/course-summary/course-summary.html
index 089c9328172..fdd1b7bbacc 100644
--- a/src/core/features/course/pages/course-summary/course-summary.html
+++ b/src/core/features/course/pages/course-summary/course-summary.html
@@ -1,6 +1,6 @@
-
+
@@ -84,7 +84,7 @@
+ [courseId]="isEnrolled ? course.id : null" [detail]="true">
diff --git a/src/core/features/course/tests/behat/snapshots/test-basic-usage-of-one-course-in-app-view-course-contents_47.png b/src/core/features/course/tests/behat/snapshots/test-basic-usage-of-one-course-in-app-view-course-contents_47.png
index 769b0bb7a86..26c54b22e96 100644
Binary files a/src/core/features/course/tests/behat/snapshots/test-basic-usage-of-one-course-in-app-view-course-contents_47.png and b/src/core/features/course/tests/behat/snapshots/test-basic-usage-of-one-course-in-app-view-course-contents_47.png differ
diff --git a/src/core/features/course/tests/behat/snapshots/test-basic-usage-of-one-course-in-app-view-course-contents_51.png b/src/core/features/course/tests/behat/snapshots/test-basic-usage-of-one-course-in-app-view-course-contents_51.png
index 65791fe88a5..1cae765db73 100644
Binary files a/src/core/features/course/tests/behat/snapshots/test-basic-usage-of-one-course-in-app-view-course-contents_51.png and b/src/core/features/course/tests/behat/snapshots/test-basic-usage-of-one-course-in-app-view-course-contents_51.png differ
diff --git a/src/core/features/courses/components/course-list-item/core-courses-course-list-item.html b/src/core/features/courses/components/course-list-item/core-courses-course-list-item.html
index 26103bc0ecd..30cd813e6a3 100644
--- a/src/core/features/courses/components/course-list-item/core-courses-course-list-item.html
+++ b/src/core/features/courses/components/course-list-item/core-courses-course-list-item.html
@@ -1,31 +1,12 @@
-
+ tappable [attr.aria-label]="course.displayname || course.fullname">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -47,8 +28,10 @@
{{ 'core.courses.aria:favourite' | translate }}
- {{ 'core.courses.aria:coursename' | translate }}
-
+
+ {{ 'core.courses.aria:coursename' | translate }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/core/features/courses/components/course-list-item/course-list-item.scss b/src/core/features/courses/components/course-list-item/course-list-item.scss
index 0f8ab669eab..ad59c55b6a1 100644
--- a/src/core/features/courses/components/course-list-item/course-list-item.scss
+++ b/src/core/features/courses/components/course-list-item/course-list-item.scss
@@ -45,6 +45,10 @@ button {
ion-card {
--border-radius: var(--card-radius);
+
+ ion-ripple-effect {
+ z-index: 1;
+ }
}
.core-button-spinner {
diff --git a/src/core/features/courses/pages/list/list.html b/src/core/features/courses/pages/list/list.html
index d8478dd39a1..1e6f429fd37 100644
--- a/src/core/features/courses/pages/list/list.html
+++ b/src/core/features/courses/pages/list/list.html
@@ -32,7 +32,7 @@ {{ 'core.courses.mycourses' | translate }}
0">
- {{ 'core.courses.totalcoursesearchresults' | translate:{$a: searchTotal} }}
+ {{ 'core.courses.totalcoursesearchresults' | translate:{$a: searchTotal} }}
diff --git a/src/core/features/editor/components/rich-text-editor/core-editor-rich-text-editor.html b/src/core/features/editor/components/rich-text-editor/core-editor-rich-text-editor.html
index 33f446b6d18..85e722582a0 100644
--- a/src/core/features/editor/components/rich-text-editor/core-editor-rich-text-editor.html
+++ b/src/core/features/editor/components/rich-text-editor/core-editor-rich-text-editor.html
@@ -8,7 +8,7 @@
[placeholder]="placeholder" [aria-labelledby]="ariaLabelledBy" (ionChange)="onChange()" (ionFocus)="focusRTE($event)"
(ionBlur)="blurRTE($event)" />
-
+
{{ infoMessage | translate }}
@@ -99,8 +99,8 @@
-
diff --git a/src/core/features/editor/components/rich-text-editor/rich-text-editor.scss b/src/core/features/editor/components/rich-text-editor/rich-text-editor.scss
index 5930d15d259..59f85d1fefc 100644
--- a/src/core/features/editor/components/rich-text-editor/rich-text-editor.scss
+++ b/src/core/features/editor/components/rich-text-editor/rich-text-editor.scss
@@ -22,12 +22,11 @@
display: flex;
flex-direction: column;
background: var(--background);
- border: 1px solid var(--stroke);
+ border: 2px solid var(--stroke);
border-radius: var(--mdl-shape-borderRadius-md);
&.has-focus {
- border-color: var(--a11y-focus-color);
- border-width: 2px;
+ border-color: var(--dark);
outline: none !important;
}
@@ -58,7 +57,7 @@
}
.core-rte-editor, .core-textarea {
- padding: 2px;
+ padding: 8px;
margin: 2px;
width: calc(100% - 4px);
resize: none;
diff --git a/src/core/features/login/components/site-onboarding/site-onboarding.html b/src/core/features/login/components/site-onboarding/site-onboarding.html
index 794b87d5637..fbcc6821c4e 100644
--- a/src/core/features/login/components/site-onboarding/site-onboarding.html
+++ b/src/core/features/login/components/site-onboarding/site-onboarding.html
@@ -16,7 +16,8 @@
-

+
diff --git a/src/core/features/login/components/site-onboarding/site-onboarding.ts b/src/core/features/login/components/site-onboarding/site-onboarding.ts
index 3bf0f10c3af..4ddb2fad29d 100644
--- a/src/core/features/login/components/site-onboarding/site-onboarding.ts
+++ b/src/core/features/login/components/site-onboarding/site-onboarding.ts
@@ -19,6 +19,7 @@ import { CoreOpener } from '@singletons/opener';
import { GET_STARTED_URL, ONBOARDING_DONE } from '@features/login/constants';
import { ModalController } from '@singletons';
import { CoreSharedModule } from '@/core/shared.module';
+import { CoreConstants } from '@/core/constants';
/**
* Component that displays onboarding help regarding the CoreLoginSitePage.
@@ -35,6 +36,7 @@ import { CoreSharedModule } from '@/core/shared.module';
export class CoreLoginSiteOnboardingComponent {
step = 0;
+ appName = CoreConstants.CONFIG.appname;
/**
* Go to next step.
diff --git a/src/core/features/login/lang.json b/src/core/features/login/lang.json
index b84f1bd5322..4b4f8627c56 100644
--- a/src/core/features/login/lang.json
+++ b/src/core/features/login/lang.json
@@ -67,6 +67,7 @@
"login": "Log in",
"loginbutton": "Log in",
"loginsteps": "For full access to this site, you first need to create an account.",
+ "logoof": "Logo of {{$a}}",
"missingemail": "Missing email address",
"missingfirstname": "Missing given name",
"missinglastname": "Missing last name",
diff --git a/src/core/features/login/pages/site/site.html b/src/core/features/login/pages/site/site.html
index 725d059e3ae..b5d70f22032 100644
--- a/src/core/features/login/pages/site/site.html
+++ b/src/core/features/login/pages/site/site.html
@@ -18,7 +18,8 @@ {{ 'core.login.connecttomoodle' | translate }}
-

+