diff --git a/src/addons/badges/pages/issued-badge/issued-badge.html b/src/addons/badges/pages/issued-badge/issued-badge.html index fde877cb2..5f233087f 100644 --- a/src/addons/badges/pages/issued-badge/issued-badge.html +++ b/src/addons/badges/pages/issued-badge/issued-badge.html @@ -9,11 +9,11 @@ - + - + diff --git a/src/addons/block/activitymodules/components/activitymodules/addon-block-activitymodules.html b/src/addons/block/activitymodules/components/activitymodules/addon-block-activitymodules.html index a22f3d549..b4f800003 100644 --- a/src/addons/block/activitymodules/components/activitymodules/addon-block-activitymodules.html +++ b/src/addons/block/activitymodules/components/activitymodules/addon-block-activitymodules.html @@ -3,7 +3,7 @@

{{ 'addon.block_activitymodules.pluginname' | translate }}

- + 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 8131987f4..3718ea8e7 100644 --- a/src/addons/block/myoverview/components/myoverview/addon-block-myoverview.html +++ b/src/addons/block/myoverview/components/myoverview/addon-block-myoverview.html @@ -20,7 +20,7 @@ - + diff --git a/src/addons/block/recentlyaccessedcourses/components/recentlyaccessedcourses/addon-block-recentlyaccessedcourses.html b/src/addons/block/recentlyaccessedcourses/components/recentlyaccessedcourses/addon-block-recentlyaccessedcourses.html index 93b5c04f7..d4b28ea36 100644 --- a/src/addons/block/recentlyaccessedcourses/components/recentlyaccessedcourses/addon-block-recentlyaccessedcourses.html +++ b/src/addons/block/recentlyaccessedcourses/components/recentlyaccessedcourses/addon-block-recentlyaccessedcourses.html @@ -7,7 +7,7 @@ - + diff --git a/src/addons/block/recentlyaccesseditems/components/recentlyaccesseditems/addon-block-recentlyaccesseditems.html b/src/addons/block/recentlyaccesseditems/components/recentlyaccesseditems/addon-block-recentlyaccesseditems.html index cacc174c9..bdab2ad19 100644 --- a/src/addons/block/recentlyaccesseditems/components/recentlyaccesseditems/addon-block-recentlyaccesseditems.html +++ b/src/addons/block/recentlyaccesseditems/components/recentlyaccesseditems/addon-block-recentlyaccesseditems.html @@ -7,7 +7,7 @@ - +
diff --git a/src/addons/block/sitemainmenu/components/sitemainmenu/addon-block-sitemainmenu.html b/src/addons/block/sitemainmenu/components/sitemainmenu/addon-block-sitemainmenu.html index fa38b0bc3..11bc7d2b8 100644 --- a/src/addons/block/sitemainmenu/components/sitemainmenu/addon-block-sitemainmenu.html +++ b/src/addons/block/sitemainmenu/components/sitemainmenu/addon-block-sitemainmenu.html @@ -3,7 +3,7 @@

{{ 'addon.block_sitemainmenu.pluginname' | translate }}

- + diff --git a/src/addons/block/starredcourses/components/starredcourses/addon-block-starredcourses.html b/src/addons/block/starredcourses/components/starredcourses/addon-block-starredcourses.html index 02398a668..88e7fdfce 100644 --- a/src/addons/block/starredcourses/components/starredcourses/addon-block-starredcourses.html +++ b/src/addons/block/starredcourses/components/starredcourses/addon-block-starredcourses.html @@ -7,7 +7,7 @@
- + diff --git a/src/addons/block/timeline/components/timeline/addon-block-timeline.html b/src/addons/block/timeline/components/timeline/addon-block-timeline.html index b652282e9..949692cb2 100644 --- a/src/addons/block/timeline/components/timeline/addon-block-timeline.html +++ b/src/addons/block/timeline/components/timeline/addon-block-timeline.html @@ -3,7 +3,7 @@

{{ 'addon.block_timeline.pluginname' | translate }}

- + @@ -57,11 +57,11 @@ - + - + diff --git a/src/addons/blog/pages/entries/entries.html b/src/addons/blog/pages/entries/entries.html index d1a56b3c0..06d703932 100644 --- a/src/addons/blog/pages/entries/entries.html +++ b/src/addons/blog/pages/entries/entries.html @@ -11,11 +11,11 @@ - + - + {{ 'addon.blog.showonlyyourentries' | translate }} diff --git a/src/addons/calendar/components/calendar/addon-calendar-calendar.html b/src/addons/calendar/components/calendar/addon-calendar-calendar.html index 39466f120..21d0bc83d 100644 --- a/src/addons/calendar/components/calendar/addon-calendar-calendar.html +++ b/src/addons/calendar/components/calendar/addon-calendar-calendar.html @@ -7,7 +7,7 @@ - +
diff --git a/src/addons/calendar/components/upcoming-events/addon-calendar-upcoming-events.html b/src/addons/calendar/components/upcoming-events/addon-calendar-upcoming-events.html index e5a295569..ef63f7816 100644 --- a/src/addons/calendar/components/upcoming-events/addon-calendar-upcoming-events.html +++ b/src/addons/calendar/components/upcoming-events/addon-calendar-upcoming-events.html @@ -1,8 +1,8 @@ - + - + - +
@@ -50,9 +50,9 @@ - + - + {{ 'core.hasdatatosync' | translate:{$a: 'core.day' | translate} }} @@ -63,7 +63,7 @@ [message]="'addon.calendar.noevents' | translate"> - + - + - + diff --git a/src/addons/competency/pages/competencysummary/competencysummary.html b/src/addons/competency/pages/competencysummary/competencysummary.html index b5314d334..57d59228e 100644 --- a/src/addons/competency/pages/competencysummary/competencysummary.html +++ b/src/addons/competency/pages/competencysummary/competencysummary.html @@ -10,11 +10,11 @@ - + - + diff --git a/src/addons/competency/pages/coursecompetencies/coursecompetencies.html b/src/addons/competency/pages/coursecompetencies/coursecompetencies.html index 6ca87f8af..daea95879 100644 --- a/src/addons/competency/pages/coursecompetencies/coursecompetencies.html +++ b/src/addons/competency/pages/coursecompetencies/coursecompetencies.html @@ -8,11 +8,11 @@ - + - + diff --git a/src/addons/competency/pages/plan/plan.html b/src/addons/competency/pages/plan/plan.html index 1f2275c70..055972527 100644 --- a/src/addons/competency/pages/plan/plan.html +++ b/src/addons/competency/pages/plan/plan.html @@ -8,11 +8,11 @@ - + - + diff --git a/src/addons/messages/pages/discussion/discussion.html b/src/addons/messages/pages/discussion/discussion.html index 2eb15f375..924b36f7b 100644 --- a/src/addons/messages/pages/discussion/discussion.html +++ b/src/addons/messages/pages/discussion/discussion.html @@ -57,8 +57,8 @@ - - + + diff --git a/src/addons/messages/pages/discussion/discussion.page.ts b/src/addons/messages/pages/discussion/discussion.page.ts index 0d4896837..a7be06c6e 100644 --- a/src/addons/messages/pages/discussion/discussion.page.ts +++ b/src/addons/messages/pages/discussion/discussion.page.ts @@ -445,13 +445,9 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView return; } - // Don't use domUtils.getScrollHeight because it gives an outdated value after receiving a new message. - const scrollHeight = this.scrollElement ? this.scrollElement.scrollHeight : 0; - // Check if we are at the bottom to scroll it after render. // Use a 5px error margin because in iOS there is 1px difference for some reason. - this.scrollBottom = Math.abs(scrollHeight - (this.scrollElement?.scrollTop || 0) - - (this.scrollElement?.clientHeight || 0)) < 5; + this.scrollBottom = CoreDom.scrollIsBottom(this.scrollElement, 5); if (this.messagesBeingSent > 0) { // Ignore polling due to a race condition. @@ -510,39 +506,39 @@ export class AddonMessagesDiscussionPage implements OnInit, OnDestroy, AfterView * The scroll was moved. Update new messages count. */ scrollFunction(): void { - if (this.newMessages > 0) { - const scrollBottom = (this.scrollElement?.scrollTop || 0) + (this.scrollElement?.clientHeight || 0); - const scrollHeight = (this.scrollElement?.scrollHeight || 0); - if (scrollBottom > scrollHeight - 40) { - // At the bottom, reset. - this.setNewMessagesBadge(0); + if (this.newMessages == 0) { + return; + } - return; + if (CoreDom.scrollIsBottom(this.scrollElement, 40)) { + // At the bottom, reset. + this.setNewMessagesBadge(0); + + return; + } + + const scrollElRect = this.scrollElement?.getBoundingClientRect(); + const scrollBottomPos = (scrollElRect && scrollElRect.bottom) || 0; + + if (scrollBottomPos == 0) { + return; + } + + const messages = Array.from(this.hostElement.querySelectorAll('.addon-message-not-mine')) + .slice(-this.newMessages) + .reverse(); + + const newMessagesUnread = messages.findIndex((message) => { + const elementRect = message.getBoundingClientRect(); + if (!elementRect) { + return false; } - const scrollElRect = this.scrollElement?.getBoundingClientRect(); - const scrollBottomPos = (scrollElRect && scrollElRect.bottom) || 0; + return elementRect.bottom <= scrollBottomPos; + }); - if (scrollBottomPos == 0) { - return; - } - - const messages = Array.from(this.hostElement.querySelectorAll('.addon-message-not-mine')) - .slice(-this.newMessages) - .reverse(); - - const newMessagesUnread = messages.findIndex((message) => { - const elementRect = message.getBoundingClientRect(); - if (!elementRect) { - return false; - } - - return elementRect.bottom <= scrollBottomPos; - }); - - if (newMessagesUnread > 0 && newMessagesUnread < this.newMessages) { - this.setNewMessagesBadge(newMessagesUnread); - } + if (newMessagesUnread > 0 && newMessagesUnread < this.newMessages) { + this.setNewMessagesBadge(newMessagesUnread); } } diff --git a/src/addons/mod/assign/components/index/addon-mod-assign-index.html b/src/addons/mod/assign/components/index/addon-mod-assign-index.html index d5aef9734..c58552c55 100644 --- a/src/addons/mod/assign/components/index/addon-mod-assign-index.html +++ b/src/addons/mod/assign/components/index/addon-mod-assign-index.html @@ -6,7 +6,7 @@ - + + +
+ + + + +

{{ user!.fullname }}

+ +
+
- - - - -

{{ user!.fullname }}

- -
-
+ + + +

{{ 'addon.mod_assign.hiddenuser' | translate }} {{blindId}}

+ +
+
- - - -

{{ 'addon.mod_assign.hiddenuser' | translate }} {{blindId}}

- -
-
+ + + +

{{ 'addon.mod_assign.submissionstatus' | translate }}

+ +
+
- - - -

{{ 'addon.mod_assign.submissionstatus' | translate }}

- -
-
+ + + + + + + - - - - - - - + + + +

{{ 'addon.mod_assign.timemodified' | translate }}

+

{{ userSubmission!.timemodified * 1000 | coreFormatDate }}

+
+
- - - -

{{ 'addon.mod_assign.timemodified' | translate }}

-

{{ userSubmission!.timemodified * 1000 | coreFormatDate }}

-
-
+ + +

{{ 'addon.mod_assign.timeremaining' | translate }}

+

+
+
- - -

{{ 'addon.mod_assign.timeremaining' | translate }}

-

-
-
- - - -

-

-

+ +

+

+

-

-
-
- - - -

{{ 'addon.mod_assign.duedate' | translate }}

-

{{ assign!.duedate * 1000 | coreFormatDate }}

-

{{ 'addon.mod_assign.duedateno' | translate }}

-
-
- - - -

{{ 'addon.mod_assign.cutoffdate' | translate }}

-

{{ assign!.cutoffdate * 1000 | coreFormatDate }}

-
-
- - - -

{{ 'addon.mod_assign.extensionduedate' | translate }}

-

{{ lastAttempt!.extensionduedate * 1000 | coreFormatDate }}

-
-
- - - -

{{ 'addon.mod_assign.attemptnumber' | translate }}

-

- {{ 'addon.mod_assign.outof' | translate : - {'$a': {'current': currentAttempt, 'total': maxAttemptsText} } }} -

-

- {{ 'addon.mod_assign.outof' | translate : - {'$a': {'current': currentAttempt, 'total': assign!.maxattempts} } }} -

-
-
- - - - -
- - - {{ 'addon.mod_assign.editsubmission' | translate }} - - - - {{ 'addon.mod_assign.addsubmission' | translate }} - - - - - {{ 'addon.mod_assign.addnewattemptfromprevious' | translate }} - - - {{ 'addon.mod_assign.addnewattempt' | translate }} - - - - - {{ 'addon.mod_assign.editsubmission' | translate }} - -
-
-

{{ 'addon.mod_assign.erroreditpluginsnotsupported' | translate }}

-

{{ name }}

-
-
-

{{ 'addon.mod_assign.cannoteditduetostatementsubmission' | translate }}

-
-
-
- - - - - - - - - - - - - - - {{ 'addon.mod_assign.submitassignment' | translate }} - -

{{ 'addon.mod_assign.submitassignment_help' | translate }}

-
-
- - - -

- {{ 'addon.mod_assign.cannotsubmitduetostatementsubmission' | translate }}

-
- - - -

{{ 'addon.mod_assign.userswhoneedtosubmit' | translate: {$a: ''} }}

-
-
- - - - - -

{{ user.fullname }}

-
-
-
-
- - - - {{ 'addon.mod_assign.hiddenuser' | translate }} {{ blindId }} - - - - - - - -

{{ 'addon.mod_assign.submissionslocked' | translate }}

-
-
- - - - -

{{ 'addon.mod_assign.editingstatus' | translate }}

-

{{ 'addon.mod_assign.submissioneditable' | translate }}

-

{{ 'addon.mod_assign.submissionnoteditable' | translate }}

-
-
-
-
- - - - - - - -

{{ 'addon.mod_assign.currentgrade' | translate }}

-

- -

-
- - - -
- - - - - -

{{ 'addon.mod_assign.gradeoutof' | translate: {$a: gradeInfo!.grade} }}

-
- - -

{{ 'addon.mod_assign.gradelocked' | translate }}

-
- - - + -

{{ 'addon.mod_assign.grade' | translate }}

-
- - - {{grade.label}} - - -
- - - - -

{{ outcome.name }}

-
- - - {{grade.label}} - - -

{{ outcome.selected }}

-
- - - - -

{{ 'addon.mod_assign.currentgrade' | translate }}

-

- {{ grade.gradebookGrade }} -

-

- {{ grade.scale[grade.gradebookGrade].label }} -

-

-

+

{{ 'addon.mod_assign.duedate' | translate }}

+

{{ assign!.duedate * 1000 | coreFormatDate }}

+

{{ 'addon.mod_assign.duedateno' | translate }}

-
- - - - - - - - -

{{ 'addon.mod_assign.markingworkflowstate' | translate }}

-

{{ workflowStatusTranslationId | translate }}

-
-
- - - - -

{{ 'addon.mod_assign.groupsubmissionsettings' | translate }}

-

{{ 'addon.mod_assign.applytoteam' | translate }}

-
- -
- - - - + -

{{ 'addon.mod_assign.attemptsettings' | translate }}

+

{{ 'addon.mod_assign.cutoffdate' | translate }}

+

{{ assign!.cutoffdate * 1000 | coreFormatDate }}

+
+
+ + + +

{{ 'addon.mod_assign.extensionduedate' | translate }}

+

{{ lastAttempt!.extensionduedate * 1000 | coreFormatDate }}

+
+
+ + + +

{{ 'addon.mod_assign.attemptnumber' | translate }}

{{ 'addon.mod_assign.outof' | translate : {'$a': {'current': currentAttempt, 'total': maxAttemptsText} } }} @@ -319,53 +95,279 @@ {{ 'addon.mod_assign.outof' | translate : {'$a': {'current': currentAttempt, 'total': assign!.maxattempts} } }}

+
+
+ + + + +
+ + + {{ 'addon.mod_assign.editsubmission' | translate }} + + + + {{ 'addon.mod_assign.addsubmission' | translate }} + + + + + {{ 'addon.mod_assign.addnewattemptfromprevious' | translate }} + + + {{ 'addon.mod_assign.addnewattempt' | translate }} + + + + + {{ 'addon.mod_assign.editsubmission' | translate }} + +
+
+

{{ 'addon.mod_assign.erroreditpluginsnotsupported' | translate }}

+

{{ name }}

+
+
+

{{ 'addon.mod_assign.cannoteditduetostatementsubmission' | translate }}

+
+
+
+ + + + + + + + + + + + + + + {{ 'addon.mod_assign.submitassignment' | translate }} + +

{{ 'addon.mod_assign.submitassignment_help' | translate }}

+
+
+ + + +

+ {{ 'addon.mod_assign.cannotsubmitduetostatementsubmission' | translate }} +

+
+
+
+ + + + +

{{ 'addon.mod_assign.userswhoneedtosubmit' | translate: {$a: ''} }}

+
+
+ + + + + +

{{ user.fullname }}

+
+
+
+
+ + + + {{ 'addon.mod_assign.hiddenuser' | translate }} {{ blindId }} + + + + + + + +

{{ 'addon.mod_assign.submissionslocked' | translate }}

+
+
+ + + + +

{{ 'addon.mod_assign.editingstatus' | translate }}

+

{{ 'addon.mod_assign.submissioneditable' | translate }}

+

{{ 'addon.mod_assign.submissionnoteditable' | translate }}

+
+
+
+
+ + + + + + + +

{{ 'addon.mod_assign.currentgrade' | translate }}

- {{ 'addon.mod_assign.attemptreopenmethod' | translate }}: - {{ 'addon.mod_assign.attemptreopenmethod_' + assign!.attemptreopenmethod | translate }} +

+ + +
- - {{ 'addon.mod_assign.addattempt' | translate }} - - - - - - - -

{{ 'addon.mod_assign.gradedby' | translate }}

-

{{ grader!.fullname }}

-

{{ feedback!.gradeddate * 1000 | coreFormatDate }}

-
-
+ + + + +

{{ 'addon.mod_assign.gradeoutof' | translate: {$a: gradeInfo!.grade} }}

+
+ + +

{{ 'addon.mod_assign.gradelocked' | translate }}

+
- - - -

{{ 'addon.mod_assign.gradedon' | translate }}

-

{{ feedback!.gradeddate * 1000 | coreFormatDate }}

-
-
+ + + +

{{ 'addon.mod_assign.grade' | translate }}

+
+ + + {{grade.label}} + + +
- - - - + + + +

{{ outcome.name }}

+
+ + + {{grade.label}} + + +

{{ outcome.selected }}

+
+ + + + +

{{ 'addon.mod_assign.currentgrade' | translate }}

+

+ {{ grade.gradebookGrade }} +

+

+ {{ grade.scale[grade.gradebookGrade].label }} +

+

-

+
+
+
+ + + + + + + + -

{{ 'addon.mod_assign.cannotgradefromapp' | translate }}

- - {{ 'core.openinbrowser' | translate }} - - +

{{ 'addon.mod_assign.markingworkflowstate' | translate }}

+

{{ workflowStatusTranslationId | translate }}

- -
-
-
+ + + + +

{{ 'addon.mod_assign.groupsubmissionsettings' | translate }}

+

{{ 'addon.mod_assign.applytoteam' | translate }}

+
+ +
+ + + + + +

{{ 'addon.mod_assign.attemptsettings' | translate }}

+

+ {{ 'addon.mod_assign.outof' | translate : + {'$a': {'current': currentAttempt, 'total': maxAttemptsText} } }} +

+

+ {{ 'addon.mod_assign.outof' | translate : + {'$a': {'current': currentAttempt, 'total': assign!.maxattempts} } }} +

+

+ {{ 'addon.mod_assign.attemptreopenmethod' | translate }}: + {{ 'addon.mod_assign.attemptreopenmethod_' + assign!.attemptreopenmethod | translate }} +

+
+
+ + {{ 'addon.mod_assign.addattempt' | translate }} + + +
+ + + + + +

{{ 'addon.mod_assign.gradedby' | translate }}

+

{{ grader!.fullname }}

+

{{ feedback!.gradeddate * 1000 | coreFormatDate }}

+
+
+ + + + +

{{ 'addon.mod_assign.gradedon' | translate }}

+

{{ feedback!.gradeddate * 1000 | coreFormatDate }}

+
+
+ + + + + + +

{{ 'addon.mod_assign.cannotgradefromapp' | translate }}

+ + {{ 'core.openinbrowser' | translate }} + + +
+
+
+
+
+
+
diff --git a/src/addons/mod/assign/pages/index/index.html b/src/addons/mod/assign/pages/index/index.html index 71f69b398..2d3f9be93 100644 --- a/src/addons/mod/assign/pages/index/index.html +++ b/src/addons/mod/assign/pages/index/index.html @@ -15,7 +15,7 @@ - + diff --git a/src/addons/mod/bigbluebuttonbn/components/index/index.html b/src/addons/mod/bigbluebuttonbn/components/index/index.html index f3047f77a..8003d0166 100644 --- a/src/addons/mod/bigbluebuttonbn/components/index/index.html +++ b/src/addons/mod/bigbluebuttonbn/components/index/index.html @@ -6,7 +6,7 @@ - + - + diff --git a/src/addons/mod/book/components/index/addon-mod-book-index.html b/src/addons/mod/book/components/index/addon-mod-book-index.html index 4727f8e6c..380582d58 100644 --- a/src/addons/mod/book/components/index/addon-mod-book-index.html +++ b/src/addons/mod/book/components/index/addon-mod-book-index.html @@ -6,7 +6,7 @@ - + - + diff --git a/src/addons/mod/chat/components/index/addon-mod-chat-index.html b/src/addons/mod/chat/components/index/addon-mod-chat-index.html index e9c2590d3..6e928b3fe 100644 --- a/src/addons/mod/chat/components/index/addon-mod-chat-index.html +++ b/src/addons/mod/chat/components/index/addon-mod-chat-index.html @@ -6,7 +6,7 @@ - + - - + + diff --git a/src/addons/mod/chat/pages/index/index.html b/src/addons/mod/chat/pages/index/index.html index bc5f254eb..5f2cec7ce 100644 --- a/src/addons/mod/chat/pages/index/index.html +++ b/src/addons/mod/chat/pages/index/index.html @@ -14,7 +14,7 @@ - + diff --git a/src/addons/mod/chat/pages/session-messages/session-messages.html b/src/addons/mod/chat/pages/session-messages/session-messages.html index 501e76cf8..9e18962a8 100644 --- a/src/addons/mod/chat/pages/session-messages/session-messages.html +++ b/src/addons/mod/chat/pages/session-messages/session-messages.html @@ -12,7 +12,7 @@ - + diff --git a/src/addons/mod/choice/components/index/addon-mod-choice-index.html b/src/addons/mod/choice/components/index/addon-mod-choice-index.html index cc554ee45..19cea320a 100644 --- a/src/addons/mod/choice/components/index/addon-mod-choice-index.html +++ b/src/addons/mod/choice/components/index/addon-mod-choice-index.html @@ -7,66 +7,63 @@ -
- - - + + + - - - - + + + + + +

{{ 'addon.mod_choice.previewonly' | translate:{$a: openTimeReadable} }}

+

{{ 'addon.mod_choice.notopenyet' | translate:{$a: openTimeReadable} }}

+
+
+
+ + + + + +

+ {{ 'addon.mod_choice.yourselection' | translate }} + + +

+

{{ 'addon.mod_choice.expired' | translate:{$a: closeTimeReadable} }}

+
+
+
+ + + + + + {{ publishInfo | translate }} + + + + + + + -

{{ 'addon.mod_choice.previewonly' | translate:{$a: openTimeReadable} }}

-

{{ 'addon.mod_choice.notopenyet' | translate:{$a: openTimeReadable} }}

+
+
-
- - - - + + + -

- {{ 'addon.mod_choice.yourselection' | translate }} - - -

-

{{ 'addon.mod_choice.expired' | translate:{$a: closeTimeReadable} }}

+
+
-
- - - - - - {{ publishInfo | translate }} - - - - - - - - - - - - - - - - - - - - - - -
+ +
diff --git a/src/addons/mod/choice/pages/index/index.html b/src/addons/mod/choice/pages/index/index.html index b5a04c2a6..82d3899af 100644 --- a/src/addons/mod/choice/pages/index/index.html +++ b/src/addons/mod/choice/pages/index/index.html @@ -14,7 +14,7 @@ - + diff --git a/src/addons/mod/data/components/index/addon-mod-data-index.html b/src/addons/mod/data/components/index/addon-mod-data-index.html index d8eef7e45..6a6b4724f 100644 --- a/src/addons/mod/data/components/index/addon-mod-data-index.html +++ b/src/addons/mod/data/components/index/addon-mod-data-index.html @@ -18,7 +18,7 @@ - + - + - + diff --git a/src/addons/mod/data/pages/index/index.html b/src/addons/mod/data/pages/index/index.html index ff9ed701e..4d0cd6995 100644 --- a/src/addons/mod/data/pages/index/index.html +++ b/src/addons/mod/data/pages/index/index.html @@ -15,7 +15,7 @@ - + diff --git a/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html b/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html index 38fab03e8..8a72abe19 100644 --- a/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html +++ b/src/addons/mod/feedback/components/index/addon-mod-feedback-index.html @@ -6,7 +6,7 @@ - + - - + + diff --git a/src/addons/mod/feedback/pages/index/index.html b/src/addons/mod/feedback/pages/index/index.html index 6996000e4..091b40c1c 100644 --- a/src/addons/mod/feedback/pages/index/index.html +++ b/src/addons/mod/feedback/pages/index/index.html @@ -14,7 +14,7 @@ - + diff --git a/src/addons/mod/folder/components/index/addon-mod-folder-index.html b/src/addons/mod/folder/components/index/addon-mod-folder-index.html index d9e0cf63d..eea0df71e 100644 --- a/src/addons/mod/folder/components/index/addon-mod-folder-index.html +++ b/src/addons/mod/folder/components/index/addon-mod-folder-index.html @@ -6,7 +6,7 @@ - + - + diff --git a/src/addons/mod/forum/components/post-options-menu/post-options-menu.scss b/src/addons/mod/forum/components/post-options-menu/post-options-menu.scss index 4ef3e574f..c09597ff5 100644 --- a/src/addons/mod/forum/components/post-options-menu/post-options-menu.scss +++ b/src/addons/mod/forum/components/post-options-menu/post-options-menu.scss @@ -1,11 +1,4 @@ -:host { - core-loading:not(.core-loading-loaded) > .core-loading-container { - position: relative !important; - padding-top: 10px !important; - padding-bottom: 10px !important; - overflow: hidden; - } - core-loading > .core-loading-container .core-loading-message { - display: none; - } +core-loading { + --loading-inline-min-height: 48px; + --loading-display-message: none; } diff --git a/src/addons/mod/forum/pages/discussion/discussion.html b/src/addons/mod/forum/pages/discussion/discussion.html index 37157efb5..542fefeda 100644 --- a/src/addons/mod/forum/pages/discussion/discussion.html +++ b/src/addons/mod/forum/pages/discussion/discussion.html @@ -55,12 +55,12 @@ - + - + diff --git a/src/addons/mod/glossary/pages/entry/entry.html b/src/addons/mod/glossary/pages/entry/entry.html index 3965b0f64..ee112af6e 100644 --- a/src/addons/mod/glossary/pages/entry/entry.html +++ b/src/addons/mod/glossary/pages/entry/entry.html @@ -11,12 +11,12 @@ - + - + diff --git a/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html b/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html index f12d47baf..41245df1a 100644 --- a/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html +++ b/src/addons/mod/h5pactivity/components/index/addon-mod-h5pactivity-index.html @@ -16,7 +16,7 @@ - + - + - + diff --git a/src/addons/mod/imscp/pages/view/view.html b/src/addons/mod/imscp/pages/view/view.html index 0611d34f8..e37142791 100644 --- a/src/addons/mod/imscp/pages/view/view.html +++ b/src/addons/mod/imscp/pages/view/view.html @@ -19,7 +19,7 @@ - + diff --git a/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html b/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html index ea987e830..d039f0303 100644 --- a/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html +++ b/src/addons/mod/lesson/components/index/addon-mod-lesson-index.html @@ -6,7 +6,7 @@ - + diff --git a/src/addons/mod/lesson/pages/index/index.html b/src/addons/mod/lesson/pages/index/index.html index fec01e323..f83508304 100644 --- a/src/addons/mod/lesson/pages/index/index.html +++ b/src/addons/mod/lesson/pages/index/index.html @@ -14,7 +14,7 @@ - + diff --git a/src/addons/mod/lesson/pages/player/player.html b/src/addons/mod/lesson/pages/player/player.html index 90b910843..f0a46750e 100644 --- a/src/addons/mod/lesson/pages/player/player.html +++ b/src/addons/mod/lesson/pages/player/player.html @@ -17,8 +17,8 @@ - - + + diff --git a/src/addons/mod/lesson/pages/user-retake/user-retake.html b/src/addons/mod/lesson/pages/user-retake/user-retake.html index 722c6a495..41e59c571 100644 --- a/src/addons/mod/lesson/pages/user-retake/user-retake.html +++ b/src/addons/mod/lesson/pages/user-retake/user-retake.html @@ -8,12 +8,12 @@ - + - +
diff --git a/src/addons/mod/lti/components/index/addon-mod-lti-index.html b/src/addons/mod/lti/components/index/addon-mod-lti-index.html index 1a9fb4164..db82dfce0 100644 --- a/src/addons/mod/lti/components/index/addon-mod-lti-index.html +++ b/src/addons/mod/lti/components/index/addon-mod-lti-index.html @@ -6,7 +6,7 @@ - + - + diff --git a/src/addons/mod/page/components/index/addon-mod-page-index.html b/src/addons/mod/page/components/index/addon-mod-page-index.html index b93d43391..c629090b0 100644 --- a/src/addons/mod/page/components/index/addon-mod-page-index.html +++ b/src/addons/mod/page/components/index/addon-mod-page-index.html @@ -6,7 +6,7 @@ - + div[padding] { - height: 100%; -} - -core-format-text > .no-overflow { - display: inline; -} diff --git a/src/addons/mod/page/components/index/index.ts b/src/addons/mod/page/components/index/index.ts index dd9dc2ff7..37d2f0b8e 100644 --- a/src/addons/mod/page/components/index/index.ts +++ b/src/addons/mod/page/components/index/index.ts @@ -27,7 +27,6 @@ import { AddonModPageHelper } from '../../services/page-helper'; @Component({ selector: 'addon-mod-page-index', templateUrl: 'addon-mod-page-index.html', - styleUrls: ['index.scss'], }) export class AddonModPageIndexComponent extends CoreCourseModuleMainResourceComponent implements OnInit { diff --git a/src/addons/mod/page/pages/index/index.html b/src/addons/mod/page/pages/index/index.html index fc6dc2f86..8cc1affc1 100644 --- a/src/addons/mod/page/pages/index/index.html +++ b/src/addons/mod/page/pages/index/index.html @@ -14,7 +14,7 @@ - + diff --git a/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html b/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html index a8b1b7988..d8feca57d 100644 --- a/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html +++ b/src/addons/mod/quiz/components/index/addon-mod-quiz-index.html @@ -6,7 +6,7 @@ - + - + - + diff --git a/src/addons/mod/quiz/pages/index/index.html b/src/addons/mod/quiz/pages/index/index.html index 233973f4f..83bd934c9 100644 --- a/src/addons/mod/quiz/pages/index/index.html +++ b/src/addons/mod/quiz/pages/index/index.html @@ -15,7 +15,7 @@ - + diff --git a/src/addons/mod/quiz/pages/player/player.html b/src/addons/mod/quiz/pages/player/player.html index 9e787ecaf..d74bcc8b2 100644 --- a/src/addons/mod/quiz/pages/player/player.html +++ b/src/addons/mod/quiz/pages/player/player.html @@ -30,8 +30,8 @@ - - + + {{ 'addon.mod_quiz.startattempt' | translate }} diff --git a/src/addons/mod/quiz/pages/review/review.html b/src/addons/mod/quiz/pages/review/review.html index 43fc2206f..0ab6d7c5d 100644 --- a/src/addons/mod/quiz/pages/review/review.html +++ b/src/addons/mod/quiz/pages/review/review.html @@ -15,11 +15,11 @@ - + - + diff --git a/src/addons/mod/resource/components/index/addon-mod-resource-index.html b/src/addons/mod/resource/components/index/addon-mod-resource-index.html index fd4d8b5b5..213d21523 100644 --- a/src/addons/mod/resource/components/index/addon-mod-resource-index.html +++ b/src/addons/mod/resource/components/index/addon-mod-resource-index.html @@ -6,7 +6,7 @@ - + - + diff --git a/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html b/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html index 5d3f06b43..d2b0c257f 100644 --- a/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html +++ b/src/addons/mod/scorm/components/index/addon-mod-scorm-index.html @@ -6,7 +6,7 @@ - + - + diff --git a/src/addons/mod/scorm/pages/player/player.html b/src/addons/mod/scorm/pages/player/player.html index 343397a9b..85093fb51 100644 --- a/src/addons/mod/scorm/pages/player/player.html +++ b/src/addons/mod/scorm/pages/player/player.html @@ -20,7 +20,7 @@ - + diff --git a/src/addons/mod/survey/components/index/addon-mod-survey-index.html b/src/addons/mod/survey/components/index/addon-mod-survey-index.html index b4370e1f6..6d04fab7c 100644 --- a/src/addons/mod/survey/components/index/addon-mod-survey-index.html +++ b/src/addons/mod/survey/components/index/addon-mod-survey-index.html @@ -6,7 +6,7 @@ - + - + diff --git a/src/addons/mod/url/components/index/addon-mod-url-index.html b/src/addons/mod/url/components/index/addon-mod-url-index.html index 6b29fa18c..0e7fa1baa 100644 --- a/src/addons/mod/url/components/index/addon-mod-url-index.html +++ b/src/addons/mod/url/components/index/addon-mod-url-index.html @@ -6,7 +6,7 @@ - + - + diff --git a/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html b/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html index f4fd30532..4c220849f 100644 --- a/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html +++ b/src/addons/mod/wiki/components/index/addon-mod-wiki-index.html @@ -27,7 +27,7 @@ - + - + diff --git a/src/addons/mod/workshop/components/assessment/addon-mod-workshop-assessment.html b/src/addons/mod/workshop/components/assessment/addon-mod-workshop-assessment.html index 90e393f77..8c0388f09 100644 --- a/src/addons/mod/workshop/components/assessment/addon-mod-workshop-assessment.html +++ b/src/addons/mod/workshop/components/assessment/addon-mod-workshop-assessment.html @@ -1,4 +1,4 @@ - + diff --git a/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html b/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html index 9ccdc53bc..9b98b2660 100644 --- a/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html +++ b/src/addons/mod/workshop/components/index/addon-mod-workshop-index.html @@ -6,7 +6,7 @@ - + diff --git a/src/addons/mod/workshop/components/submission/addon-mod-workshop-submission.html b/src/addons/mod/workshop/components/submission/addon-mod-workshop-submission.html index ee7f08609..f69d312a9 100644 --- a/src/addons/mod/workshop/components/submission/addon-mod-workshop-submission.html +++ b/src/addons/mod/workshop/components/submission/addon-mod-workshop-submission.html @@ -1,4 +1,4 @@ - +
diff --git a/src/addons/mod/workshop/pages/index/index.html b/src/addons/mod/workshop/pages/index/index.html index c9fdc573a..b28ffdd39 100644 --- a/src/addons/mod/workshop/pages/index/index.html +++ b/src/addons/mod/workshop/pages/index/index.html @@ -14,7 +14,7 @@ - + diff --git a/src/addons/mod/workshop/pages/submission/submission.html b/src/addons/mod/workshop/pages/submission/submission.html index 754b7456a..294fdb3af 100644 --- a/src/addons/mod/workshop/pages/submission/submission.html +++ b/src/addons/mod/workshop/pages/submission/submission.html @@ -20,7 +20,7 @@ - + diff --git a/src/addons/notes/pages/list/list.html b/src/addons/notes/pages/list/list.html index db1ee143d..6c58feff3 100644 --- a/src/addons/notes/pages/list/list.html +++ b/src/addons/notes/pages/list/list.html @@ -25,12 +25,12 @@ [closeOnClick]="false"> - + - + diff --git a/src/addons/notifications/components/actions/actions.ts b/src/addons/notifications/components/actions/actions.ts deleted file mode 100644 index 9ee3b221a..000000000 --- a/src/addons/notifications/components/actions/actions.ts +++ /dev/null @@ -1,90 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnInit } from '@angular/core'; - -import { CoreSites } from '@services/sites'; -import { CoreContentLinksDelegate, CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate'; - -/** - * Component that displays the actions for a notification. - */ -@Component({ - selector: 'addon-notifications-actions', - templateUrl: 'addon-notifications-actions.html', -}) -export class AddonNotificationsActionsComponent implements OnInit { - - @Input() contextUrl?: string; - @Input() courseId?: number; - @Input() data?: Record; // Extra data to handle the URL. - - actions: CoreContentLinksAction[] = []; - - /** - * Component being initialized. - */ - async ngOnInit(): Promise { - if (!this.contextUrl && (!this.data || !this.data.appurl)) { - // No URL, nothing to do. - return; - } - - let actions: CoreContentLinksAction[] = []; - - // Treat appurl first if any. - if (this.data?.appurl) { - actions = await CoreContentLinksDelegate.getActionsFor( - this.data.appurl, - this.courseId, - undefined, - this.data, - ); - } - - if (!actions.length && this.contextUrl) { - // No appurl or cannot handle it. Try with contextUrl. - actions = await CoreContentLinksDelegate.getActionsFor(this.contextUrl, this.courseId, undefined, this.data); - } - - if (!actions.length) { - // URL is not supported. Add an action to open it in browser. - actions.push({ - message: 'core.view', - icon: 'fas-eye', - action: this.openInBrowser.bind(this), - }); - } - - this.actions = actions; - } - - /** - * Default action. Open in browser. - * - * @param siteId Site ID to use. - */ - protected async openInBrowser(siteId?: string): Promise { - const url = this.data?.appurl || this.contextUrl; - - if (!url) { - return; - } - - const site = await CoreSites.getSite(siteId); - - site.openInBrowserWithAutoLogin(url); - } - -} diff --git a/src/addons/notifications/components/actions/addon-notifications-actions.html b/src/addons/notifications/components/actions/addon-notifications-actions.html deleted file mode 100644 index 348a2d701..000000000 --- a/src/addons/notifications/components/actions/addon-notifications-actions.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - {{ action.message | translate }} - - - diff --git a/src/addons/notifications/components/components.module.ts b/src/addons/notifications/components/components.module.ts deleted file mode 100644 index 06e36bdcd..000000000 --- a/src/addons/notifications/components/components.module.ts +++ /dev/null @@ -1,31 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreSharedModule } from '@/core/shared.module'; - -import { AddonNotificationsActionsComponent } from './actions/actions'; - -@NgModule({ - declarations: [ - AddonNotificationsActionsComponent, - ], - imports: [ - CoreSharedModule, - ], - exports: [ - AddonNotificationsActionsComponent, - ], -}) -export class AddonNotificationsComponentsModule {} diff --git a/src/addons/notifications/notifications.scss b/src/addons/notifications/notifications.scss index 5285ea7f0..0583e27fa 100644 --- a/src/addons/notifications/notifications.scss +++ b/src/addons/notifications/notifications.scss @@ -2,6 +2,7 @@ :host { --extra-icon-size: 16px; + --icon-size: 24px; ::ng-deep core-user-avatar { .core-avatar-extra-img, @@ -28,71 +29,18 @@ } } - .core-notification-icon { - width: var(--core-avatar-size); - height: var(--core-avatar-size); - @include margin(6px, 8px, 6px, 0px); + div.core-notification-icon { + img { + width: var(--icon-size); + height: var(--icon-size); + } + padding: 0.7rem; background: var(--background-color); border-radius: var(--small-radius); } - .item core-format-text ::ng-deep { - .forumpost { - border: 1px solid var(--gray-200); - width: 100%; - margin: 0 0 1em 0; - - td { - padding: 10px; - } - - .header { - background-color: var(--light); - - .picture { - width: 48px; - text-align: center; - @include padding-horizontal(4px, 0px); - padding-top: 8px; - - img { - width: 44px !important; - } - } - } - - .subject { - font-weight: 700; - margin-bottom: 1rem; - } - } - - a { - text-decoration: none; - } - - .userpicture { - border-radius: 50%; - } - - .mdl-right { - text-align: end; - a { - display: none; - } - font { - font-size: 0.9em; - } - } - - .commands { - display: none; - } - - hr { - margin-top: 1.5rem; - margin-bottom: 1.5rem; - background-color: var(--gray-200); - } + .core-notification-icon { + --module-icon-size: var(--icon-size); + @include margin(6px, 8px, 6px, 0px); } } diff --git a/src/addons/notifications/pages/list/list.html b/src/addons/notifications/pages/list/list.html index bac0b73c5..f208df1ae 100644 --- a/src/addons/notifications/pages/list/list.html +++ b/src/addons/notifications/pages/list/list.html @@ -11,69 +11,50 @@ - + -
-
- - - - - {{ 'addon.notifications.markallread' | translate }} - -
- - - - -
- -
- - -
+ : 'addon.notifications.unreadnotification' | translate: {$a: notification.subject}" + (click)="openNotification(notification)" button [detail]="false" lines="full"> + +
+ +
+ + +
- + +
+ +
+ + +
- -

- - -

-

{{ notification.userfromfullname }}

-
- - {{ notification.timecreated | coreDateDayOrTime }} - - - - -
- - - - - - - - -
-
+ +

+ + +

+

{{ notification.timecreated | coreTimeAgo }} · {{ + notification.userfromfullname }} +

+
+ + + +
@@ -81,4 +62,15 @@
+ + +
+ + + + + {{ 'addon.notifications.markallread' | translate }} + +
diff --git a/src/addons/notifications/pages/list/list.module.ts b/src/addons/notifications/pages/list/list.module.ts index b592079e7..bf8eb18ec 100644 --- a/src/addons/notifications/pages/list/list.module.ts +++ b/src/addons/notifications/pages/list/list.module.ts @@ -16,7 +16,6 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { CoreSharedModule } from '@/core/shared.module'; -import { AddonNotificationsComponentsModule } from '../../components/components.module'; import { AddonNotificationsListPage } from './list'; import { CoreMainMenuComponentsModule } from '@features/mainmenu/components/components.module'; @@ -31,7 +30,6 @@ const routes: Routes = [ imports: [ RouterModule.forChild(routes), CoreSharedModule, - AddonNotificationsComponentsModule, CoreMainMenuComponentsModule, ], declarations: [ diff --git a/src/addons/notifications/pages/list/list.scss b/src/addons/notifications/pages/list/list.scss new file mode 100644 index 000000000..4ac957b62 --- /dev/null +++ b/src/addons/notifications/pages/list/list.scss @@ -0,0 +1,72 @@ +@import "~theme/globals"; + +ion-item { + ion-label { + margin-top: 8px; + margin-bottom: 8px; + p.item-heading { + font-size: 14px; + -webkit-line-clamp: 3; + overflow: hidden; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + display: -webkit-box; + } + p { + font-size: 12px; + } + } + + ion-note { + margin: 0; + @include padding-horizontal(8px, 0); + padding-top: 12px; + + ion-icon { + font-size: 6px; + vertical-align: middle; + } + } + + [slot=start] { + align-self: start; + margin-top: 16px; + } + + --icon-size: 16px; + --extra-icon-size: 12px; + --core-avatar-size: 32px; + + div.core-notification-icon, + core-mod-icon.core-notification-icon { + padding: 8px; + } +} + +.mark-all-as-read { + padding-bottom: 16px; + bottom: 0; + left: 0; + right: 0; + display: flex; + background: transparent; + box-shadow: none; + pointer-events: none; + + ion-chip.ion-color { + pointer-events: all; + margin: 0 auto; + box-shadow: 0 2px 4px rgba(0, 0, 0, .4); + + ion-spinner { + width: 16px; + height: 16px; + @include margin-horizontal(0, 8px); + } + } +} + + +ion-content::part(scroll) { + padding-bottom: 44px; +} diff --git a/src/addons/notifications/pages/list/list.ts b/src/addons/notifications/pages/list/list.ts index f508814bf..d8b29c4de 100644 --- a/src/addons/notifications/pages/list/list.ts +++ b/src/addons/notifications/pages/list/list.ts @@ -30,6 +30,8 @@ import { AddonNotificationsNotificationToRender, } from '@addons/notifications/services/notifications-helper'; import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager'; +import { CoreNavigator } from '@services/navigator'; +import { CoreTimeUtils } from '@services/utils/time'; /** * Page that displays the list of notifications. @@ -37,7 +39,7 @@ import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-lin @Component({ selector: 'page-addon-notifications-list', templateUrl: 'list.html', - styleUrls: ['../../notifications.scss'], + styleUrls: ['list.scss', '../../notifications.scss'], }) export class AddonNotificationsListPage implements OnInit, OnDestroy { @@ -50,6 +52,7 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy { protected isCurrentView?: boolean; protected cronObserver?: CoreEventObserver; + protected readObserver?: CoreEventObserver; protected pushObserver?: Subscription; protected pendingRefresh = false; @@ -84,6 +87,21 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy { this.refreshNotifications(); }); + this.readObserver = CoreEvents.on(AddonNotificationsProvider.READ_CHANGED_EVENT, (data) => { + if (!data.id) { + return; + } + + const notification = this.notifications.find((notification) => notification.id === data.id); + if (!notification) { + return; + } + + notification.read = true; + notification.timeread = data.time; + this.loadMarkAllAsReadButton(); + }); + const deepLinkManager = new CoreMainMenuDeepLinkManager(); deepLinkManager.treatLink(); } @@ -91,7 +109,7 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy { /** * Convenience function to get notifications. Gets unread notifications first. * - * @param refreh Whether we're refreshing data. + * @param refresh Whether we're refreshing data. * @return Resolved when done. */ protected async fetchNotifications(refresh?: boolean): Promise { @@ -110,7 +128,7 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy { } this.canLoadMore = result.canLoadMore; - this.markNotificationsAsRead(notifications); + await this.loadMarkAllAsReadButton(); } catch (error) { CoreDomUtils.showErrorModalDefault(error, 'addon.notifications.errorgetnotifications', true); this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading. @@ -129,7 +147,9 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy { await CoreUtils.ignoreErrors(AddonNotifications.markAllNotificationsAsRead()); - CoreEvents.trigger(AddonNotificationsProvider.READ_CHANGED_EVENT, {}, CoreSites.getCurrentSiteId()); + CoreEvents.trigger(AddonNotificationsProvider.READ_CHANGED_EVENT, { + time: CoreTimeUtils.timestamp(), + }, CoreSites.getCurrentSiteId()); // All marked as read, refresh the list. this.notificationsLoaded = false; @@ -138,28 +158,11 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy { } /** - * Mark notifications as read. + * Load mark all notifications as read button. * - * @param notifications Array of notification objects. + * @return Promise resolved when done. */ - protected async markNotificationsAsRead(notifications: AddonNotificationsNotificationToRender[]): Promise { - if (notifications.length > 0) { - const promises = notifications.map(async (notification) => { - if (notification.read) { - // Already read, don't mark it. - return; - } - - await AddonNotifications.markNotificationRead(notification.id); - }); - - await CoreUtils.ignoreErrors(Promise.all(promises)); - - await CoreUtils.ignoreErrors(AddonNotifications.invalidateNotificationsList()); - - CoreEvents.trigger(AddonNotificationsProvider.READ_CHANGED_EVENT, {}, CoreSites.getCurrentSiteId()); - } - + protected async loadMarkAllAsReadButton(): Promise { // Check if mark all as read should be displayed (there are unread notifications). try { this.loadingMarkAllNotificationsAsRead = true; @@ -201,6 +204,15 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy { } } + /** + * Open Notification page. + * + * @param notification Notification to open. + */ + openNotification(notification: AddonNotificationsNotificationToRender): void { + CoreNavigator.navigate('../notification', { params: { notification } }); + } + /** * User entered the page. */ @@ -229,6 +241,7 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy { */ ngOnDestroy(): void { this.cronObserver?.off(); + this.readObserver?.off(); this.pushObserver?.unsubscribe(); } diff --git a/src/addons/notifications/pages/notification/notification.html b/src/addons/notifications/pages/notification/notification.html index c0566dee0..9c2254c43 100644 --- a/src/addons/notifications/pages/notification/notification.html +++ b/src/addons/notifications/pages/notification/notification.html @@ -4,34 +4,58 @@ -

{{ 'addon.notifications.notifications' | translate }}

+

{{ 'addon.notifications.notifications' | translate }}

- - -
- -
- - -
+
- + + +
+ +
+ + +
- -

{{ subject }}

-

{{ userFromFullName }}

-
-
- - - - - - + +
+ +
+ + +
+ + +

+ + +

+

{{ timecreated | coreTimeAgo }} · {{ + userFromFullName }} +

+
+ + + + + + + +
+ +
+
+ + + {{ action.message | translate }} + +
+
diff --git a/src/addons/notifications/pages/notification/notification.module.ts b/src/addons/notifications/pages/notification/notification.module.ts index b316d8bd4..e16e2860b 100644 --- a/src/addons/notifications/pages/notification/notification.module.ts +++ b/src/addons/notifications/pages/notification/notification.module.ts @@ -16,7 +16,6 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { CoreSharedModule } from '@/core/shared.module'; -import { AddonNotificationsComponentsModule } from '../../components/components.module'; import { AddonNotificationsNotificationPage } from './notification'; const routes: Routes = [ @@ -30,7 +29,6 @@ const routes: Routes = [ imports: [ RouterModule.forChild(routes), CoreSharedModule, - AddonNotificationsComponentsModule, ], declarations: [ AddonNotificationsNotificationPage, diff --git a/src/addons/notifications/pages/notification/notification.scss b/src/addons/notifications/pages/notification/notification.scss new file mode 100644 index 000000000..05f459851 --- /dev/null +++ b/src/addons/notifications/pages/notification/notification.scss @@ -0,0 +1,87 @@ +@import "~theme/globals"; + +:host { + + .core-notification-title { + [slot=start] { + align-self: start; + margin-top: 16px; + } + p.item-heading { + font-size: 16px; + } + p { + font-size: 12px; + } + } + + .core-notification-body { + core-format-text { + font-size: 14px; + } + + h2 { + font-weight: bold; + } + + core-format-text ::ng-deep { + .forumpost { + border: 1px solid var(--gray-200); + width: 100%; + margin: 0 0 1em 0; + + td { + padding: 10px; + } + + .header { + background-color: var(--light); + + .picture { + width: 48px; + text-align: center; + @include padding-horizontal(4px, 0px); + padding-top: 8px; + + img { + width: 44px !important; + } + } + } + + .subject { + font-weight: 700; + margin-bottom: 1rem; + } + } + + a { + text-decoration: none; + } + + .userpicture { + border-radius: 50%; + } + + .mdl-right { + text-align: end; + a { + display: none; + } + font { + font-size: 0.9em; + } + } + + .commands { + display: none; + } + + hr { + margin-top: 1.5rem; + margin-bottom: 1.5rem; + background-color: var(--gray-200); + } + } + } +} diff --git a/src/addons/notifications/pages/notification/notification.ts b/src/addons/notifications/pages/notification/notification.ts index 9fb04f294..23282d713 100644 --- a/src/addons/notifications/pages/notification/notification.ts +++ b/src/addons/notifications/pages/notification/notification.ts @@ -19,7 +19,9 @@ import { AddonNotificationsNotificationToRender, } from '@addons/notifications/services/notifications-helper'; import { Component, OnInit } from '@angular/core'; +import { CoreContentLinksAction, CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate'; import { CoreNavigator } from '@services/navigator'; +import { CoreSites } from '@services/sites'; import { CoreDomUtils } from '@services/utils/dom'; import { CoreUtils } from '@services/utils/utils'; @@ -29,7 +31,7 @@ import { CoreUtils } from '@services/utils/utils'; @Component({ selector: 'page-addon-notifications-notification', templateUrl: 'notification.html', - styleUrls: ['../../notifications.scss'], + styleUrls: ['../../notifications.scss', 'notification.scss'], }) export class AddonNotificationsNotificationPage implements OnInit { @@ -41,12 +43,19 @@ export class AddonNotificationsNotificationPage implements OnInit { iconUrl?: string; // Icon URL. modname?: string; // Module name. loaded = false; + timecreated = 0; + + // Actions data. + actions: CoreContentLinksAction[] = []; + contextUrl?: string; + courseId?: number; + actionsData?: Record; // Extra data to handle the URL. /** * @inheritdoc */ async ngOnInit(): Promise { - let notification: AddonNotificationsNotificationToRender | AddonNotificationsNotificationData; + let notification: AddonNotificationsNotification; try { notification = CoreNavigator.getRequiredRouteParam('notification'); @@ -85,6 +94,8 @@ export class AddonNotificationsNotificationPage implements OnInit { this.modname = modname; } } + this.timecreated = notification.timecreated; + } else { this.subject = notification.title || ''; this.content = notification.message || ''; @@ -93,7 +104,73 @@ export class AddonNotificationsNotificationPage implements OnInit { this.userFromFullName = notification.userfromfullname; } + await this.loadActions(notification); + AddonNotificationsHelper.markNotificationAsRead(notification); + this.loaded = true; } + /** + * Load notification actions + * + * @param notification Notification. + * @return Promise resolved when done. + */ + async loadActions(notification: AddonNotificationsNotification): Promise { + if (!notification.contexturl && (!notification.customdata || !notification.customdata.appurl)) { + // No URL, nothing to do. + return; + } + + let actions: CoreContentLinksAction[] = []; + this.actionsData = notification.customdata; + this.contextUrl = notification.contexturl; + this.courseId = 'courseid' in notification ? notification.courseid : undefined; + + // Treat appurl first if any. + if (this.actionsData?.appurl) { + actions = await CoreContentLinksDelegate.getActionsFor( + this.actionsData.appurl, + this.courseId, + undefined, + this.actionsData, + ); + } + + if (!actions.length && this.contextUrl) { + // No appurl or cannot handle it. Try with contextUrl. + actions = await CoreContentLinksDelegate.getActionsFor(this.contextUrl, this.courseId, undefined, this.actionsData); + } + + if (!actions.length) { + // URL is not supported. Add an action to open it in browser. + actions.push({ + message: 'core.view', + icon: 'fas-eye', + action: this.openInBrowser.bind(this), + }); + } + + this.actions = actions; + } + + /** + * Default action. Open in browser. + * + * @param siteId Site ID to use. + */ + protected async openInBrowser(siteId?: string): Promise { + const url = this.actionsData?.appurl || this.contextUrl; + + if (!url) { + return; + } + + const site = await CoreSites.getSite(siteId); + + site.openInBrowserWithAutoLogin(url); + } + } + +type AddonNotificationsNotification = AddonNotificationsNotificationToRender | AddonNotificationsNotificationData; diff --git a/src/addons/notifications/services/handlers/push-click.ts b/src/addons/notifications/services/handlers/push-click.ts index 2312b4e75..3fc1c4d0d 100644 --- a/src/addons/notifications/services/handlers/push-click.ts +++ b/src/addons/notifications/services/handlers/push-click.ts @@ -18,12 +18,12 @@ import { CoreNavigator } from '@services/navigator'; import { CoreTextUtils } from '@services/utils/text'; import { CoreUtils } from '@services/utils/utils'; import { makeSingleton } from '@singletons'; -import { CoreEvents } from '@singletons/events'; import { CorePushNotificationsClickHandler } from '@features/pushnotifications/services/push-delegate'; import { CorePushNotificationsNotificationBasicData } from '@features/pushnotifications/services/pushnotifications'; import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; -import { AddonNotifications, AddonNotificationsProvider } from '../notifications'; +import { AddonNotifications } from '../notifications'; import { AddonNotificationsMainMenuHandlerService } from './mainmenu'; +import { AddonNotificationsHelper } from '../notifications-helper'; /** * Handler for non-messaging push notifications clicks. @@ -64,15 +64,7 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi * @return Promise resolved when done. */ protected async markAsRead(notification: AddonNotificationsNotificationData): Promise { - const notifId = notification.savedmessageid || notification.id; - - if (!notifId) { - return; - } - - await CoreUtils.ignoreErrors(AddonNotifications.markNotificationRead(notifId, notification.site)); - - CoreEvents.trigger(AddonNotificationsProvider.READ_CHANGED_EVENT, {}, notification.site); + await CoreUtils.ignoreErrors(AddonNotificationsHelper.markNotificationAsRead(notification)); } /** diff --git a/src/addons/notifications/services/notifications-helper.ts b/src/addons/notifications/services/notifications-helper.ts index 8eb24bfdc..38d79ddc6 100644 --- a/src/addons/notifications/services/notifications-helper.ts +++ b/src/addons/notifications/services/notifications-helper.ts @@ -18,13 +18,18 @@ import { CoreUtils } from '@services/utils/utils'; import { makeSingleton } from '@singletons'; import { AddonMessageOutputDelegate } from '@addons/messageoutput/services/messageoutput-delegate'; import { + AddonNotifications, AddonNotificationsNotificationMessageFormatted, AddonNotificationsPreferences, AddonNotificationsPreferencesComponent, AddonNotificationsPreferencesNotification, AddonNotificationsPreferencesNotificationProcessor, AddonNotificationsPreferencesProcessor, + AddonNotificationsProvider, } from './notifications'; +import { CoreEvents } from '@singletons/events'; +import { AddonNotificationsNotificationData } from './handlers/push-click'; +import { CoreTimeUtils } from '@services/utils/time'; /** * Service that provides some helper functions for notifications. @@ -115,6 +120,46 @@ export class AddonNotificationsHelperProvider { return result; } + /** + * Mark notification as read, trigger event and invalidate data. + * + * @param notification Notification object. + * @return Promise resolved when done. + */ + async markNotificationAsRead( + notification: AddonNotificationsNotificationMessageFormatted | AddonNotificationsNotificationData, + siteId?: string, + ): Promise { + if ('read' in notification && (notification.read || notification.timeread > 0)) { + // Already read, don't mark it. + return false; + } + + const notifId = 'savedmessageid' in notification ? notification.savedmessageid || notification.id : notification.id; + if (!notifId) { + return false; + } + + siteId = 'site' in notification ? notification.site : siteId; + + await CoreUtils.ignoreErrors(AddonNotifications.markNotificationRead(notifId, siteId)); + + const time = CoreTimeUtils.timestamp(); + if ('read' in notification) { + notification.read = true; + notification.timeread = time; + } + + await CoreUtils.ignoreErrors(AddonNotifications.invalidateNotificationsList()); + + CoreEvents.trigger(AddonNotificationsProvider.READ_CHANGED_EVENT, { + id: notifId, + time, + }, siteId); + + return true; + } + } export const AddonNotificationsHelper = makeSingleton(AddonNotificationsHelperProvider); diff --git a/src/addons/notifications/services/notifications.ts b/src/addons/notifications/services/notifications.ts index 1fab101db..d6426b01a 100644 --- a/src/addons/notifications/services/notifications.ts +++ b/src/addons/notifications/services/notifications.ts @@ -24,6 +24,19 @@ import { CoreLogger } from '@singletons/logger'; import { makeSingleton } from '@singletons'; import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate'; +declare module '@singletons/events' { + + /** + * Augment CoreEventsData interface with events specific to this service. + * + * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation + */ + export interface CoreEventsData { + [AddonNotificationsProvider.READ_CHANGED_EVENT]: AddonNotificationsReadChangedEvent; + } + +} + const ROOT_CACHE_KEY = 'mmaNotifications:'; /** @@ -577,3 +590,11 @@ export enum AddonNotificationsGetReadType { READ = 1, BOTH = 2, } + +/** + * Event triggered when one or more notifications are read. + */ +export type AddonNotificationsReadChangedEvent = { + id?: number; // Set to the single id notification read. Undefined if multiple. + time: number; // Time of the change. +}; diff --git a/src/addons/privatefiles/pages/index/index.html b/src/addons/privatefiles/pages/index/index.html index a3555462f..ec245c9cd 100644 --- a/src/addons/privatefiles/pages/index/index.html +++ b/src/addons/privatefiles/pages/index/index.html @@ -8,13 +8,13 @@ - + - + diff --git a/src/addons/storagemanager/pages/course-storage/course-storage.html b/src/addons/storagemanager/pages/course-storage/course-storage.html index 35b3f2b8f..b6353dd6c 100644 --- a/src/addons/storagemanager/pages/course-storage/course-storage.html +++ b/src/addons/storagemanager/pages/course-storage/course-storage.html @@ -8,8 +8,8 @@ - - + +

{{ 'addon.storagemanager.courseinfo' | translate }}

diff --git a/src/addons/storagemanager/pages/courses-storage/courses-storage.html b/src/addons/storagemanager/pages/courses-storage/courses-storage.html index 4c4f3217f..a0d9bc1eb 100644 --- a/src/addons/storagemanager/pages/courses-storage/courses-storage.html +++ b/src/addons/storagemanager/pages/courses-storage/courses-storage.html @@ -8,8 +8,8 @@ - - + +

{{ 'addon.storagemanager.alldata' | translate }}

diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 7c361804a..275daf547 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -31,6 +31,7 @@ import { CoreUrlUtils } from '@services/utils/url'; import { CoreConstants } from '@/core/constants'; import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins'; import { CoreDomUtils } from '@services/utils/dom'; +import { CoreDom } from '@singletons/dom'; const MOODLE_VERSION_PREFIX = 'version-'; const MOODLEAPP_VERSION_PREFIX = 'moodleapp-'; @@ -90,9 +91,21 @@ export class AppComponent implements OnInit, AfterViewInit { }); // Listen to scroll to add style when scroll is not 0. - win.addEventListener('ionScroll', ({ detail, target }: CustomEvent) => { - const header = (target as HTMLElement).closest('.ion-page')?.querySelector('ion-header'); - header?.classList.toggle('core-header-shadow', detail.scrollTop > 0); + win.addEventListener('ionScroll', async ({ detail, target }: CustomEvent) => { + if ((target as HTMLElement).tagName != 'ION-CONTENT') { + return; + } + const content = (target as HTMLIonContentElement); + + const page = content.closest('.ion-page'); + if (!page) { + return; + } + + page.querySelector('ion-header')?.classList.toggle('core-header-shadow', detail.scrollTop > 0); + + const scrollElement = await content.getScrollElement(); + content.classList.toggle('core-footer-shadow', !CoreDom.scrollIsBottom(scrollElement)); }); // Listen for session expired events. diff --git a/src/core/components/attachments/attachments.scss b/src/core/components/attachments/attachments.scss index 4582f0e2b..c6955211f 100644 --- a/src/core/components/attachments/attachments.scss +++ b/src/core/components/attachments/attachments.scss @@ -1,5 +1,3 @@ -:host { - core-loading { - --loading-inline-min-height: 60px; - } +core-loading { + --loading-inline-min-height: 60px; } diff --git a/src/core/components/iframe/core-iframe.html b/src/core/components/iframe/core-iframe.html index e9664f6d3..28ce77c89 100644 --- a/src/core/components/iframe/core-iframe.html +++ b/src/core/components/iframe/core-iframe.html @@ -1,5 +1,4 @@ -
- + @@ -17,8 +16,4 @@ class="core-button-as-link core-iframe-help"> {{ 'core.iframehelp' | translate }} - - - - -
+
diff --git a/src/core/components/iframe/iframe.scss b/src/core/components/iframe/iframe.scss index 23c1cfd68..bc8a94691 100644 --- a/src/core/components/iframe/iframe.scss +++ b/src/core/components/iframe/iframe.scss @@ -9,27 +9,6 @@ max-width: 100%; background-color: var(--ion-background-color); } - - .core-loading-container { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - display: table; - height: 100%; - width: 100%; - z-index: 1; - margin: 0; - padding: 0; - clear: both; - - .core-loading-spinner { - display: table-cell; - text-align: center; - vertical-align: middle; - } - } } :host-context(.core-iframe-fullscreen) { diff --git a/src/core/components/loading/core-loading.html b/src/core/components/loading/core-loading.html index 569682040..cabe746af 100644 --- a/src/core/components/loading/core-loading.html +++ b/src/core/components/loading/core-loading.html @@ -2,8 +2,4 @@

{{message}}

-
- - -
+ diff --git a/src/core/components/loading/loading.scss b/src/core/components/loading/loading.scss index efa86a0de..82ab20289 100644 --- a/src/core/components/loading/loading.scss +++ b/src/core/components/loading/loading.scss @@ -1,5 +1,27 @@ @import "~theme/globals"; +@mixin inline() { + min-height: var(--internal-loading-inline-min-height); + max-height: 100vh; // In order show it on the page (content will be cut). + + &:not(.core-loading-loaded) { + position: relative; + --contents-display: flex; + flex-direction: column; + } + + .core-loading-container { + --loading-background: rgba(var(--loading-background-inline), 0.4); + flex-direction: row; + height: auto; + width: auto; + + .core-loading-message { + @include margin(0, 0, 0, 10px); + } + } +} + :host { --loading-background: var(--ion-background-color); --loading-background-inline: var(--ion-background-color-rgb); @@ -8,27 +30,28 @@ --loading-inline-margin: 0px; --loading-inline-min-height: 28px; --internal-loading-inline-min-height: var(--loading-inline-min-height); - --content-display: contents; + --loading-display: flex; + --loading-display-message: block; + --contents-display: contents; - position: static; - color: var(--loading-text-color); @include core-transition(all, 200ms); - - &.margin { - --loading-inline-margin: 10px; - --internal-loading-inline-min-height: calc(var(--loading-inline-min-height) + var(--loading-inline-margin) + var(--loading-inline-margin)); - } + pointer-events: none; + display: var(--contents-display); &.core-loading-loaded { + position: static; + pointer-events: auto; --internal-loading-inline-min-height: 0px; - } - ion-spinner { - --color: var(--loading-spinner); - color: var(--color); + &.has-spacer { + --contents-display: flex; + min-height: 100%; + flex-direction: column; + } } .core-loading-container { + pointer-events: none; position: absolute; @include position(0, 0, 0, 0); height: 100%; @@ -36,78 +59,38 @@ z-index: 3; margin: 0; padding: 0; + color: var(--loading-text-color); background-color: var(--loading-background); @include core-transition(all, 200ms); - display: flex; + display: var(--loading-display); justify-content: center; align-items: center; flex-direction: column; - } - - .core-loading-content { - @include core-transition(opacity, 200ms); - display: var(--content-display); - } - - .core-loading-message { - @include margin(10px, 0, 0, 0); - } - - &.core-loading-loaded { - position: unset; - display: contents; - } - - &.core-loading-inline { - --loading-background: rgba(var(--loading-background-inline), 0.5); - position: relative; - display: block; - min-height: var(--internal-loading-inline-min-height); .core-loading-message { - @include margin(0, 0, 0, 10px); + @include margin(10px, 0, 0, 0); + display: var(--loading-display-message); } - .core-loading-container { - flex-direction: row; + ion-spinner { + --color: var(--loading-spinner); + color: var(--color); } } - &.core-loading-full-height .core-loading-content { - height: 100%; - } - - &.has-spacer { - --content-display: flex; - - .core-loading-content { - min-height: 100%; - flex-direction: column; - } - } - - &.list-item-limited-width .core-loading-content { - max-width: var(--list-item-max-width); - margin-left: auto !important; - margin-right: auto !important; - } - - &.safe-area-padding:not(.core-loading-inline) .core-loading-content, - &.safe-area-padding-horizontal:not(.core-loading-inline) .core-loading-content { - @include safe-area-padding-horizontal(0px, 0px); - } - - &.safe-area-padding:not(.core-loading-inline) .core-loading-content { - padding-bottom: var(--ion-safe-area-bottom); - > * { - --ion-safe-area-bottom: 0px; - } - } -} - -:host-context(ion-item) { &.core-loading-inline { - position: static; - display: block; + @include inline(); } } + +// Force inline on some contexts. +:host-context(ion-item), +:host-context(core-block) { + // Implicit Inline. + @include inline(); +} + +:host-context(.limited-width > ):not([slot]) { + --contents-display: flex; + flex-direction: column; +} diff --git a/src/core/components/loading/loading.ts b/src/core/components/loading/loading.ts index d5be19770..7485ade46 100644 --- a/src/core/components/loading/loading.ts +++ b/src/core/components/loading/loading.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Input, OnInit, OnChanges, SimpleChange, ViewChild, ElementRef, AfterViewInit } from '@angular/core'; +import { Component, Input, OnInit, OnChanges, SimpleChange, ElementRef, AfterViewInit } from '@angular/core'; import { CoreEventLoadingChangedData, CoreEvents } from '@singletons/events'; import { CoreUtils } from '@services/utils/utils'; @@ -50,15 +50,14 @@ import { AsyncComponent } from '@classes/async-component'; }) export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, AsyncComponent { - @Input() hideUntil: unknown; // Determine when should the contents be shown. + @Input() hideUntil = false; // Determine when should the contents be shown. @Input() message?: string; // Message to show while loading. @Input() fullscreen = true; // Use the whole screen. - @ViewChild('content') content?: ElementRef; - uniqueId: string; - protected element: HTMLElement; // Current element. loaded = false; // Only comes true once. + + protected element: HTMLElement; // Current element. protected onReadyPromise = new CorePromisedValue(); constructor(element: ElementRef) { @@ -67,6 +66,7 @@ export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, A // Calculate the unique ID. this.uniqueId = 'core-loading-content-' + CoreUtils.getUniqueId('CoreLoadingComponent'); + this.element.setAttribute('id', this.uniqueId); } /** @@ -77,7 +77,6 @@ export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, A // Default loading message. this.message = Translate.instant('core.loading'); } - this.element.classList.toggle('core-loading-inline', !this.fullscreen); } @@ -85,7 +84,7 @@ export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, A * @inheritdoc */ ngAfterViewInit(): void { - this.changeState(!!this.hideUntil); + this.changeState(this.hideUntil); } /** @@ -93,7 +92,7 @@ export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, A */ ngOnChanges(changes: { [name: string]: SimpleChange }): void { if (changes.hideUntil) { - this.changeState(!!this.hideUntil); + this.changeState(this.hideUntil); } } @@ -105,7 +104,7 @@ export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, A */ async changeState(loaded: boolean): Promise { this.element.classList.toggle('core-loading-loaded', loaded); - this.content?.nativeElement.classList.toggle('core-loading-content', loaded); + this.element.setAttribute('aria-busy', loaded ? 'false' : 'true'); if (!this.loaded && loaded) { this.loaded = true; // Only comes true once. diff --git a/src/core/directives/collapsible-footer.ts b/src/core/directives/collapsible-footer.ts index f345a9b70..7ead926a9 100644 --- a/src/core/directives/collapsible-footer.ts +++ b/src/core/directives/collapsible-footer.ts @@ -126,6 +126,9 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy { const scroll = await this.content.getScrollElement(); this.content.scrollEvents = true; + // Init shadow. + this.content.classList.toggle('core-footer-shadow', !CoreDom.scrollIsBottom(scroll)); + this.content.addEventListener('ionScroll', this.contentScrollListener = (e: CustomEvent): void => { if (!this.content) { return; diff --git a/src/core/features/course/format/singleactivity/components/core-course-format-single-activity.html b/src/core/features/course/format/singleactivity/components/core-course-format-single-activity.html index aa42eeadb..1e960e8a8 100644 --- a/src/core/features/course/format/singleactivity/components/core-course-format-single-activity.html +++ b/src/core/features/course/format/singleactivity/components/core-course-format-single-activity.html @@ -1,4 +1,6 @@ - +
+ +
diff --git a/src/core/features/course/pages/module-preview/module-preview.html b/src/core/features/course/pages/module-preview/module-preview.html index e25f768db..642782ee5 100644 --- a/src/core/features/course/pages/module-preview/module-preview.html +++ b/src/core/features/course/pages/module-preview/module-preview.html @@ -18,11 +18,11 @@ - + - + diff --git a/src/core/features/grades/pages/course/course.html b/src/core/features/grades/pages/course/course.html index 30e043ec3..de30057bd 100644 --- a/src/core/features/grades/pages/course/course.html +++ b/src/core/features/grades/pages/course/course.html @@ -12,7 +12,7 @@ - +
diff --git a/src/core/features/h5p/components/h5p-iframe/core-h5p-iframe.html b/src/core/features/h5p/components/h5p-iframe/core-h5p-iframe.html index c60f30592..b09ea0ef2 100644 --- a/src/core/features/h5p/components/h5p-iframe/core-h5p-iframe.html +++ b/src/core/features/h5p/components/h5p-iframe/core-h5p-iframe.html @@ -1,4 +1,4 @@ - + diff --git a/src/core/features/login/pages/credentials/credentials.html b/src/core/features/login/pages/credentials/credentials.html index e8703bdfa..94e021f17 100644 --- a/src/core/features/login/pages/credentials/credentials.html +++ b/src/core/features/login/pages/credentials/credentials.html @@ -15,8 +15,8 @@ - - + +