Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
1447867
MOBILE-4595 a11y: Fix placeholder color contrast
albertgasset Jan 22, 2025
58853f7
MOBILE-4595 a11y: Fix alert dialog color contrast
albertgasset Jan 22, 2025
d4218e6
MOBILE-4595 a11y: Add alt attribute to logo images
albertgasset Jan 22, 2025
1479a15
MOBILE-4595 a11y: Allow focusing buttons inside ion-input
albertgasset Jan 22, 2025
1605d3b
MOBILE-4595 a11y: Use heading tags for items styled as headings
albertgasset Jan 22, 2025
20a19ac
MOBILE-4595 a11y: Fix level of nested headings
albertgasset Jan 22, 2025
b11d621
MOBILE-4065 directive: Stop click propagation in ariaButtonClick
albertgasset Jan 22, 2025
d24b7ef
MOBILE-4595 a11y: Fix course list items
albertgasset Jan 22, 2025
e02e719
MOBILE-4595 a11y: Fix hidding search box history and error
albertgasset Jan 22, 2025
4633b98
MOBILE-4595 a11y: Fix course modules in screen readers
albertgasset Jan 22, 2025
e756bca
MOBILE-4595 a11y: Fix focused completion button
albertgasset Jan 22, 2025
0a8241a
MOBILE-4595 a11y: Fix focus trap in course summary modal
albertgasset Jan 22, 2025
f7cedda
MOBILE-4595 a11y: Remove ARIA label from profile button
albertgasset Jan 22, 2025
aa3f853
MOBILE-4595 a11y: Fix course index in screen readers
albertgasset Jan 22, 2025
e8f6b46
MOBILE-4595 a11y: Remvoe resize handle from course index
albertgasset Jan 22, 2025
002579e
MOBILE-4595 a11y: Fix alert dialog color contrast
albertgasset Jan 22, 2025
9e770e9
MOBILE-4595 a11y: Remove ARIA label from profile buttons
albertgasset Jan 22, 2025
9cde7c8
MOBILE-4595 a11y: Use bolder font and thicker border for selected tabs
albertgasset Jan 22, 2025
c026007
MOBILE-4595 a11y: Use default text color in action sheet buttons
albertgasset Jan 22, 2025
e29e145
MOBILE-4595 a11y: Use text color for star icons
albertgasset Jan 22, 2025
25be400
MOBILE-4595 a11y: Use blue color for focus outline of links and buttons
albertgasset Jan 22, 2025
5a82b32
MOBILE-4595 a11y: Fix files in screen readers
albertgasset Jan 22, 2025
ecc19f9
MOBILE-4595 a11y: Fix forum index in screen readers
albertgasset Jan 22, 2025
dda24cd
MOBILE-4595 a11y: Fix focus trap in RTE
albertgasset Jan 22, 2025
0815ed8
MOBILE-4595 a11y: Notify RTE info messages
albertgasset Jan 22, 2025
3aa7eb7
MOBILE-4595 a11y: Status messages of file deletions
albertgasset Jan 22, 2025
eacc040
MOBILE-4595 storagemanager: Fix download course button not updating
albertgasset Jan 22, 2025
22c805b
MOBILE-4595 a11y: Status messages of course storage actions
albertgasset Jan 22, 2025
a2011a7
MOBILE-4595 a11y: Method to dismiss loader with a status message
albertgasset Jan 22, 2025
1af25b4
MOBILE-4595 a11y: Present status message when submitting quiz
albertgasset Jan 22, 2025
7165114
MOBILE-4595 a11y: Do not hide collapsible footer when it has focus
albertgasset Jan 22, 2025
13c92b9
MOBILE-4595 a11y: New design of selected main menu tab
albertgasset Jan 22, 2025
d332ef0
MOBILE-4595 a11y: Fix contrast of focused buttons
albertgasset Jan 22, 2025
0b89b69
MOBILE-4595 a11y: Fix color contrast in forms
albertgasset Jan 22, 2025
53c10fe
MOBILE-4595 a11y: Fix contrast of loading spinner
albertgasset Jan 22, 2025
c7a2392
MOBILE-4595 a11y: Fix contrast of today border
albertgasset Jan 22, 2025
f9e0554
MOBILE-4595 a11y: Removed CSS variables in Ionic 8
albertgasset Jan 22, 2025
29125a0
MOBILE-4595 a11y: Fix focus ion-radio and ion-checkbox with links
albertgasset Jan 22, 2025
5350c05
MOBILE-4595 a11y: Use accordion for advanced section of post form
albertgasset Jan 22, 2025
f4934c0
MOBILE-4595 a11y: Fix color contrast in menus and tabs
albertgasset Jan 22, 2025
7a9eb90
MOBILE-4595 a11y: Pinch-to-zoom setting
albertgasset Jan 22, 2025
560d25a
MOBILE-4595 a11y: Fix storage manager in screen readers
albertgasset Jan 22, 2025
fb3b692
MOBILE-4595 a11y: Present status message when searching courses
albertgasset Jan 22, 2025
6fa9d50
MOBILE-4595 behat: Update snapshots
albertgasset Jan 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@
<preference name="prerendered-icon" value="true" />
<preference name="AppendUserAgent" value="MoodleMobile 5.0.0 (50000)" />
<preference name="BackupWebStorage" value="none" />
<preference name="ScrollEnabled" value="false" />
<preference name="ScrollEnabled" value="true" />
<preference name="KeyboardDisplayRequiresUserAction" value="false" />
<preference name="HideKeyboardFormAccessoryBar" value="false" />
<preference name="KeyboardResizeMode" value="ionic" />
<preference name="AllowInlineMediaPlayback" value="true" />
<preference name="LoadUrlTimeoutValue" value="60000" />
<preference name="load-url-timeout" value="60000" />
Expand Down
5 changes: 5 additions & 0 deletions cordova-plugin-moodleapp/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
<param name="android-package" value="com.moodle.moodlemobile.Diagnostic_Location"/>
<param name="onload" value="true" />
</feature>
<feature name="PinchToZoom">
<param name="android-package" value="com.moodle.moodlemobile.PinchToZoom"/>
<param name="onload" value="true" />
</feature>
</config-file>

<source-file src="src/android/Diagnostic.java" target-dir="src/cordova/plugins" />
Expand All @@ -37,6 +41,7 @@

<source-file src="src/android/SecureStorage.java" target-dir="src/com/moodle/moodlemobile" />
<source-file src="src/android/InstallReferrer.java" target-dir="src/com/moodle/moodlemobile" />
<source-file src="src/android/PinchToZoom.java" target-dir="src/com/moodle/moodlemobile" />
</platform>

<platform name="ios">
Expand Down
42 changes: 42 additions & 0 deletions cordova-plugin-moodleapp/src/android/PinchToZoom.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {},
Expand All @@ -238,4 +241,4 @@
"nl.kingsquare.cordova.background-audio": {}
}
}
}
}
45 changes: 45 additions & 0 deletions patches/@ionic+core+8.4.1.patch
Original file line number Diff line number Diff line change
Expand Up @@ -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 <ion-input>.
+ 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 <ion-input>.
+ inputEl.addEventListener('focusout', focusOut);
};
- componentEl.addEventListener('focusin', focusIn);
+ // Patched: Attach focusin/focusout events to inputEl instead of componentEl to allow focusing buttons inside <ion-input>.
+ 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
4 changes: 4 additions & 0 deletions scripts/langindex.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,12 @@ <h2>{{ 'addon.block_myoverview.pluginname' | translate }}</h2>
</ion-row>

<core-empty-box *ngIf="filteredCourses.length === 0" image="assets/img/icons/courses.svg">
<p *ngIf="hasCourses" class="item-heading">
<h3 *ngIf="hasCourses" class="item-heading">
{{'addon.block_myoverview.noresult' | translate}}
</p>
<p *ngIf="!hasCourses" class="item-heading">
</h3>
<h3 *ngIf="!hasCourses" class="item-heading">
{{'addon.block_myoverview.nocoursesenrolled' | translate}}
</p>
</h3>
<ng-container *ngIf="searchEnabled">
<p *ngIf="hasCourses" class="subdued">
{{'addon.block_myoverview.noresultdescription' | translate}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

core-empty-box {
.item-heading {
font-size: 1rem;
font-weight: bold;
margin-bottom: 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@ <h3>
<ion-item-group *ngFor="let dayEvents of events">
<ion-item>
<ion-label>
<h4>{{ dayEvents.dayTimestamp * 1000 | coreFormatDate:"strftimedaydate" }}</h4>
@if (course) {
<h3><ng-container *ngTemplateOutlet="date" /></h3>
} @else {
<h4><ng-container *ngTemplateOutlet="date" /></h4>
}
<ng-template #date>
{{ dayEvents.dayTimestamp * 1000 | coreFormatDate:"strftimedaydate" }}
</ng-template>
</ion-label>
</ion-item>
<ng-container *ngFor="let event of dayEvents.events">
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
131 changes: 68 additions & 63 deletions src/addons/mod/forum/components/index/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,70 +84,75 @@
<p>{{ 'addon.mod_forum.errorloadingsortingorderdetails' | translate }}</p>
</core-empty-box>

<ion-item *ngFor="let discussion of discussionsItems" class="addon-mod-forum-discussion" [detail]="true"
[lines]="discussion.groupname && 'none'" [attr.aria-current]="discussions?.getItemAriaCurrent(discussion)"
(click)="discussions?.select(discussion)" button>
<ion-label>
<p class="addon-mod-forum-discussion-title ion-text-wrap item-heading">
<ion-icon name="fas-map-pin" *ngIf="discussion.pinned"
[attr.aria-label]="'addon.mod_forum.discussionpinned' | translate" />
<ion-icon name="fas-star" class="addon-forum-star" *ngIf="discussion.starred"
[attr.aria-label]="'addon.mod_forum.favourites' | translate" />
<core-format-text [text]="discussion.subject" contextLevel="module" [contextInstanceId]="module && module.id"
[courseId]="courseId" />
<ion-icon name="fas-lock" *ngIf="discussion.locked" class="addon-mod-forum-locked-icon"
[attr.aria-label]="'addon.mod_forum.discussionlocked' | translate" />
</p>
<div class="addon-mod-forum-discussion-info">
<core-user-avatar *ngIf="discussion.userfullname" [user]="discussion" slot="start" [courseId]="courseId"
[linkProfile]="false" />
<div class="addon-mod-forum-discussion-author">
<span *ngIf="discussion.userfullname" class="core-discussionusername">{{discussion.userfullname}}</span>
<p *ngIf="discussion.groupname" class="core-groupname">
<ion-icon name="fas-users" [attr.aria-label]="'addon.mod_forum.group' | translate" />
<core-format-text [text]="discussion.groupname" contextLevel="course" [contextInstanceId]="courseId"
[wsNotFiltered]="true" />
</p>
<p *ngIf="isOnlineDiscussion(discussion)">
{{discussion.created * 1000 | coreFormatDate: "strftimerecentfull"}}
</p>
<p *ngIf="isOfflineDiscussion(discussion)">
<ion-icon name="fas-clock" aria-hidden="true" />
{{ 'core.notsent' | translate }}
</p>
<div *ngFor="let discussion of discussionsItems" class="ion-activatable ripple-parent">
<ion-item class="addon-mod-forum-discussion" [detail]="true" [lines]="discussion.groupname && 'none'"
[attr.aria-current]="discussions?.getItemAriaCurrent(discussion)" (click)="discussions?.select(discussion)" tappable>
<ion-label>
<p class="addon-mod-forum-discussion-title ion-text-wrap item-heading">
<ion-icon name="fas-map-pin" *ngIf="discussion.pinned"
[attr.aria-label]="'addon.mod_forum.discussionpinned' | translate" />
<ion-icon name="fas-star" class="addon-forum-star" *ngIf="discussion.starred"
[attr.aria-label]="'addon.mod_forum.favourites' | translate" />
<span (ariaButtonClick)="discussions?.select(discussion)">
<core-format-text [text]="discussion.subject" contextLevel="module"
[contextInstanceId]="module && module.id" [courseId]="courseId" />
</span>
<ion-icon name="fas-lock" *ngIf="discussion.locked" class="addon-mod-forum-locked-icon"
[attr.aria-label]="'addon.mod_forum.discussionlocked' | translate" />
</p>
<div class="addon-mod-forum-discussion-info">
<core-user-avatar *ngIf="discussion.userfullname" [user]="discussion" slot="start" [courseId]="courseId"
[linkProfile]="false" />
<div class="addon-mod-forum-discussion-author">
<span *ngIf="discussion.userfullname" class="core-discussionusername">{{discussion.userfullname}}</span>
<p *ngIf="discussion.groupname" class="core-groupname">
<ion-icon name="fas-users" [attr.aria-label]="'addon.mod_forum.group' | translate" />
<core-format-text [text]="discussion.groupname" contextLevel="course" [contextInstanceId]="courseId"
[wsNotFiltered]="true" />
</p>
<p *ngIf="isOnlineDiscussion(discussion)">
{{discussion.created * 1000 | coreFormatDate: "strftimerecentfull"}}
</p>
<p *ngIf="isOfflineDiscussion(discussion)">
<ion-icon name="fas-clock" aria-hidden="true" />
{{ 'core.notsent' | translate }}
</p>
</div>
</div>
</div>
<ion-row *ngIf="isOnlineDiscussion(discussion)" class="ion-text-center addon-mod-forum-discussion-more-info">
<ion-col class="ion-text-start">
<ion-note>
<ion-icon name="fas-clock" aria-hidden="true" /> {{ 'addon.mod_forum.lastpost' | translate }}
<ng-container *ngIf="discussion.timemodified > discussion.created">
{{ discussion.timemodified | coreTimeAgo }}
</ng-container>
<ng-container *ngIf="discussion.timemodified <= discussion.created">
{{ discussion.created | coreTimeAgo }}
</ng-container>
</ion-note>
</ion-col>
<ion-col class="ion-text-end">
<ion-note>
<ion-icon name="fas-comments" aria-hidden="true" />
{{ 'addon.mod_forum.numreplies' | translate:{numreplies: discussion.numreplies} }}
<ion-badge *ngIf="discussion.numunread" class="ion-text-center">
<span aria-hidden="true">{{ discussion.numunread }}</span>
<span class="sr-only">
{{ 'addon.mod_forum.unreadpostsnumber' | translate:{ '$a' : discussion.numunread} }}
</span>
</ion-badge>
</ion-note>
</ion-col>
</ion-row>
</ion-label>
<ion-button *ngIf="canPin || discussion.canlock || discussion.canfavourite" fill="clear"
[ariaLabel]="('core.displayoptions' | translate)" (click)="showOptionsMenu($event, discussion)" slot="end">
<ion-icon name="ellipsis-vertical" slot="icon-only" aria-hidden="true" />
</ion-button>
</ion-item>
<ion-row *ngIf="isOnlineDiscussion(discussion)" class="ion-text-center addon-mod-forum-discussion-more-info">
<ion-col class="ion-text-start">
<ion-note>
<ion-icon name="fas-clock" aria-hidden="true" /> {{ 'addon.mod_forum.lastpost' | translate }}
<ng-container *ngIf="discussion.timemodified > discussion.created">
{{ discussion.timemodified | coreTimeAgo }}
</ng-container>
<ng-container *ngIf="discussion.timemodified <= discussion.created">
{{ discussion.created | coreTimeAgo }}
</ng-container>
</ion-note>
</ion-col>
<ion-col class="ion-text-end">
<ion-note>
<ion-icon name="fas-comments" aria-hidden="true" />
{{ 'addon.mod_forum.numreplies' | translate:{numreplies: discussion.numreplies} }}
<ion-badge *ngIf="discussion.numunread" class="ion-text-center">
<span aria-hidden="true">{{ discussion.numunread }}</span>
<span class="sr-only">
{{ 'addon.mod_forum.unreadpostsnumber' | translate:{ '$a' : discussion.numunread} }}
</span>
</ion-badge>
</ion-note>
</ion-col>
</ion-row>
</ion-label>
<ion-button *ngIf="canPin || discussion.canlock || discussion.canfavourite" fill="clear"
[ariaLabel]="('core.displayoptions' | translate)" (click)="showOptionsMenu($event, discussion)" slot="end">
<ion-icon name="ellipsis-vertical" slot="icon-only" aria-hidden="true" />
</ion-button>
</ion-item>
<ion-ripple-effect />
</div>


<core-infinite-loading [enabled]="discussions && discussions.loaded && !discussions.completed" [error]="fetchFailed"
(action)="fetchMoreDiscussions($event)" />
Expand Down
Loading
Loading