MOBILE-4348 module: Re-estructure activity card

main
Pau Ferrer Ocaña 2023-09-29 12:18:49 +02:00
parent 05786e94d3
commit 178e3eeb45
21 changed files with 298 additions and 210 deletions

View File

@ -21,7 +21,6 @@ import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/
import { CoreConstants, ModPurpose } from '@/core/constants';
import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler';
import { CoreCourseModuleData } from '@features/course/services/course-helper';
import { CoreIonicColorNames } from '@singletons/colors';
/**
* Handler to support forum modules.
@ -57,7 +56,6 @@ export class AddonModForumModuleHandlerService extends CoreModuleHandlerBase imp
const data = await super.getData(module, courseId);
if ('afterlink' in module && !!module.afterlink) {
data.extraBadgeColor = undefined;
const match = />(\d+)[^<]+/.exec(module.afterlink);
data.extraBadge = match ? Translate.instant('addon.mod_forum.unreadpostsnumber', { $a : match[1] }) : '';
} else {
@ -113,7 +111,6 @@ export class AddonModForumModuleHandlerService extends CoreModuleHandlerBase imp
}
data.extraBadge = Translate.instant('core.loading');
data.extraBadgeColor = CoreIonicColorNames.DARK;
try {
// Handle unread posts.
@ -122,7 +119,6 @@ export class AddonModForumModuleHandlerService extends CoreModuleHandlerBase imp
siteId,
});
data.extraBadgeColor = undefined;
data.extraBadge = forum.unreadpostscount
? Translate.instant(
'addon.mod_forum.unreadpostsnumber',
@ -131,7 +127,6 @@ export class AddonModForumModuleHandlerService extends CoreModuleHandlerBase imp
: '';
} catch {
// Ignore errors.
data.extraBadgeColor = undefined;
data.extraBadge = '';
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -59,7 +59,7 @@ export class AddonModLtiModuleHandlerService extends CoreModuleHandlerBase imple
): Promise<CoreCourseModuleHandlerData> {
const data = await super.getData(module, courseId, sectionId, forCoursePage);
data.showDownloadButton = false;
data.buttons = [{
data.button = {
icon: 'fas-up-right-from-square',
label: 'addon.mod_lti.launchactivity',
action: (event: Event, module: CoreCourseModuleData, courseId: number): void => {
@ -68,7 +68,7 @@ export class AddonModLtiModuleHandlerService extends CoreModuleHandlerBase imple
CoreCourse.storeModuleViewed(courseId, module.id);
},
}];
};
return data;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -73,14 +73,14 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
const handlerData = await super.getData(module, courseId, sectionId, forCoursePage);
handlerData.updateStatus = (status) => {
if (!handlerData.buttons) {
if (!handlerData.button) {
return;
}
handlerData.buttons[0].hidden = status !== CoreConstants.DOWNLOADED ||
handlerData.button.hidden = status !== CoreConstants.DOWNLOADED ||
AddonModResourceHelper.isDisplayedInIframe(module);
};
handlerData.buttons = [{
handlerData.button = {
hidden: true,
icon: openWithPicker ? 'fas-share-from-square' : 'fas-file',
label: module.name + ': ' + Translate.instant(openWithPicker ? 'core.openwith' : 'addon.mod_resource.openthefile'),
@ -92,7 +92,7 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
CoreCourse.storeModuleViewed(courseId, module.id);
}
},
}];
};
this.getResourceData(module, courseId, handlerData).then((extra) => {
handlerData.extraBadge = extra;
@ -144,11 +144,11 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
// Check if the button needs to be shown or not.
promises.push(this.hideOpenButton(module).then((hideOpenButton) => {
if (!handlerData.buttons) {
if (!handlerData.button) {
return;
}
handlerData.buttons[0].hidden = hideOpenButton;
handlerData.button.hidden = hideOpenButton;
return;
}));
@ -166,58 +166,61 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
await Promise.all(promises);
const extra: string[] = [];
if (module.contentsinfo) {
// No need to use the list of files.
extra.push(CoreTextUtils.cleanTags(module.afterlink));
} else if (module.contents && module.contents[0]) {
const files = module.contents;
const file = files[0];
return CoreTextUtils.cleanTags(module.afterlink);
}
if (options.showsize) {
const size = options.filedetails
? options.filedetails.size
: files.reduce((result, file) => result + (file.filesize || 0), 0);
if (!module.contents || !module.contents[0]) {
return '';
}
extra.push(CoreTextUtils.bytesToSize(size, 1));
}
const extra: string[] = [];
const files = module.contents;
const mainFile = files[0];
if (options.showtype) {
// We should take it from options.filedetails.size if available but it's already translated.
extra.push(CoreMimetypeUtils.getMimetypeDescription(file));
}
if (options.showsize) {
const size = options.filedetails
? options.filedetails.size
: files.reduce((result, file) => result + (file.filesize || 0), 0);
if (options.showdate) {
const timecreated = 'timecreated' in file ? file.timecreated : 0;
extra.push(CoreTextUtils.bytesToSize(size, 1));
}
if (options.filedetails && options.filedetails.modifieddate) {
extra.push(Translate.instant(
'addon.mod_resource.modifieddate',
{ $a: CoreTimeUtils.userDate(options.filedetails.modifieddate * 1000, 'core.strftimedatetimeshort') },
));
} else if (options.filedetails && options.filedetails.uploadeddate) {
extra.push(Translate.instant(
'addon.mod_resource.uploadeddate',
{ $a: CoreTimeUtils.userDate(options.filedetails.uploadeddate * 1000, 'core.strftimedatetimeshort') },
));
} else if ((file.timemodified || 0) > timecreated + CoreConstants.SECONDS_MINUTE * 5) {
/* Modified date may be up to several minutes later than uploaded date just because
teacher did not submit the form promptly. Give teacher up to 5 minutes to do it. */
extra.push(Translate.instant(
'addon.mod_resource.modifieddate',
{ $a: CoreTimeUtils.userDate((file.timemodified || 0) * 1000, 'core.strftimedatetimeshort') },
));
} else {
extra.push(Translate.instant(
'addon.mod_resource.uploadeddate',
{ $a: CoreTimeUtils.userDate(timecreated * 1000, 'core.strftimedatetimeshort') },
));
}
if (options.showtype) {
// We should take it from options.filedetails.size if available but it's already translated.
extra.push(CoreMimetypeUtils.getMimetypeDescription(mainFile));
}
if (options.showdate) {
const timecreated = 'timecreated' in mainFile ? mainFile.timecreated : 0;
if (options.filedetails && options.filedetails.modifieddate) {
extra.push(Translate.instant(
'addon.mod_resource.modifieddate',
{ $a: CoreTimeUtils.userDate(options.filedetails.modifieddate * 1000, 'core.strftimedatetimeshort') },
));
} else if (options.filedetails && options.filedetails.uploadeddate) {
extra.push(Translate.instant(
'addon.mod_resource.uploadeddate',
{ $a: CoreTimeUtils.userDate(options.filedetails.uploadeddate * 1000, 'core.strftimedatetimeshort') },
));
} else if ((mainFile.timemodified || 0) > timecreated + CoreConstants.SECONDS_MINUTE * 5) {
/* Modified date may be up to several minutes later than uploaded date just because
teacher did not submit the form promptly. Give teacher up to 5 minutes to do it. */
extra.push(Translate.instant(
'addon.mod_resource.modifieddate',
{ $a: CoreTimeUtils.userDate((mainFile.timemodified || 0) * 1000, 'core.strftimedatetimeshort') },
));
} else {
extra.push(Translate.instant(
'addon.mod_resource.uploadeddate',
{ $a: CoreTimeUtils.userDate(timecreated * 1000, 'core.strftimedatetimeshort') },
));
}
}
return extra.join(' ');
return extra.join(' · ');
}
/**

View File

@ -93,20 +93,20 @@ export class AddonModUrlModuleHandlerService extends CoreModuleHandlerBase imple
modal.dismiss();
}
},
buttons: [{
button: {
hidden: true, // Hide it until we calculate if it should be displayed or not.
icon: 'fas-link',
label: 'core.openmodinbrowser',
action: (event: Event, module: CoreCourseModuleData, courseId: number): void => {
openUrl(module, courseId);
},
}],
},
};
const hideButton = await CoreUtils.ignoreErrors(this.hideLinkButton(module));
if (handlerData.buttons && hideButton !== undefined) {
handlerData.buttons[0].hidden = hideButton;
if (handlerData.button && hideButton !== undefined) {
handlerData.button.hidden = hideButton;
}
try {

View File

@ -16,6 +16,5 @@
}
.course-section {
--padding-start: 12px;
--inner-padding-end: 12px;
}

View File

@ -10,14 +10,14 @@
core-mod-icon {
align-self: flex-start;
--padding: 12px;
--padding: 4px;
@include margin-horizontal(null, 8px);
}
h1 ion-icon {
color: var(--medium);
@include margin-horizontal(8px, null);
font-size: 80%;
}
.core-module-info-box {

View File

@ -1,84 +1,90 @@
<ion-card *ngIf="module.handlerData && module.visibleoncoursepage !== 0" [class.core-course-module-with-view]="moduleHasView"
(click)="moduleClicked($event)" [button]="module.handlerData.action && module.uservisible"
[attr.aria-label]="module.handlerData.a11yTitle ? module.handlerData.a11yTitle : null">
<ion-card *ngIf="module.handlerData && module.visibleoncoursepage !== 0"
class="activity-card core-course-module-handler {{module.handlerData.class}}" [class.core-course-module-with-view]="moduleHasView"
[class.item-dimmed]="module.visible === 0 || module.uservisible === false" (click)="moduleClicked($event)"
[button]="module.handlerData.action && module.uservisible"
[attr.aria-label]="module.handlerData.a11yTitle ? module.handlerData.a11yTitle : null" id="core-course-module-{{module.id}}">
<ng-container *ngIf="!module.handlerData.loading">
<ion-item id="core-course-module-{{module.id}}"
class="ion-text-wrap core-course-module-handler core-module-main-item {{module.handlerData.class}}" [ngClass]="{
'item-dimmed': module.visible === 0 || module.uservisible === false
}">
<ion-item class="ion-text-wrap">
<ion-label>
<div class="activity-main">
<core-mod-icon *ngIf="module.handlerData.icon" [modicon]="module.handlerData.icon" [modname]="module.modname"
[componentId]="module.instance" [fallbackTranslation]="module.modplural">
</core-mod-icon>
<div class="activity-title">
<p class="item-heading">
<core-format-text [text]="module.handlerData.title" contextLevel="module" [contextInstanceId]="module.id"
[courseId]="module.course" [attr.aria-label]="module.handlerData.a11yTitle + ', ' + modNameTranslated">
</core-format-text>
<ion-icon name="fas-lock" *ngIf="module.visible === 0 || module.uservisible === false"
[attr.aria-label]="'core.restricted' | translate"></ion-icon>
<ion-icon *ngIf="prefetchStatusIcon$ | async as prefetchStatusIcon" [name]="prefetchStatusIcon" color="success"
[attr.aria-label]="((prefetchStatusText$ | async) || '') | translate"></ion-icon>
</p>
<core-mod-icon slot="start" *ngIf="module.handlerData.icon" [modicon]="module.handlerData.icon" [modname]="module.modname"
[componentId]="module.instance" [fallbackTranslation]="module.modplural">
</core-mod-icon>
<div class="core-module-additional-info" *ngIf="module.visible === 0 || (module.visible !== 0 && module.isStealth)">
<!-- Hidden badges -->
<ion-badge color="secondary" *ngIf="module.visible === 0" class="ion-text-wrap">
<ion-icon name="far-eye-slash" aria-hidden="true"></ion-icon>
{{ 'core.course.hiddenfromstudents' | translate }}
</ion-badge>
<ion-badge color="secondary" *ngIf="module.visible !== 0 && module.isStealth" class="ion-text-wrap">
<ion-icon name="fas-low-vision" aria-hidden="true"></ion-icon>
{{ 'core.course.hiddenoncoursepage' | translate }}
</ion-badge>
</div>
</div>
<!-- Buttons. -->
<div *ngIf="module.uservisible !== false" class="core-module-buttons">
<!-- Module completion (legacy). -->
<core-course-module-completion-legacy *ngIf="module.completiondata && showLegacyCompletion"
[completion]="module.completiondata" [moduleName]="module.name" [moduleId]="module.id"
(completionChanged)="completionChanged.emit($event)">
</core-course-module-completion-legacy>
<ion-label class="core-module-title">
<p class="item-heading">
<core-format-text [text]="module.handlerData.title" contextLevel="module" [contextInstanceId]="module.id"
[courseId]="module.course" [attr.aria-label]="module.handlerData.a11yTitle + ', ' + modNameTranslated">
</core-format-text>
<ion-icon name="fas-lock" *ngIf="module.visible === 0 || module.uservisible === false"
[attr.aria-label]="'core.restricted' | translate"></ion-icon>
<ion-icon *ngIf="prefetchStatusIcon$ | async as prefetchStatusIcon" [name]="prefetchStatusIcon" color="success"
[attr.aria-label]="((prefetchStatusText$ | async) || '') | translate"></ion-icon>
</p>
<!-- Activity completion. For tablets -->
<core-course-module-completion *ngIf="hasCompletion && !showLegacyCompletion" [completion]="module.completiondata"
[moduleName]="module.name" [moduleId]="module.id" [showCompletionConditions]="showCompletionConditions"
[showManualCompletion]="showManualCompletion" (completionChanged)="completionChanged.emit($event)">
</core-course-module-completion>
<div class="core-module-additional-info">
<ion-chip *ngIf="module.handlerData.extraBadge" [color]="module.handlerData.extraBadgeColor"
class="ion-text-wrap ion-text-start" [outline]="true">
<ion-label><span [innerHTML]="module.handlerData.extraBadge"></span></ion-label>
</ion-chip>
<!-- Button defined by the module handler. -->
<ion-button fill="clear" class="core-module-button-more core-animate-show-hide" *ngIf="module.handlerData.button"
[hidden]="module.handlerData.button.hidden || module.handlerData.spinner" (click)="buttonClicked($event)"
[attr.aria-label]="module.handlerData.button.label | translate:{$a: module.handlerData.title}">
<ion-icon [name]="module.handlerData.button.icon" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<!-- Hidden badges -->
<ion-badge color="warning" *ngIf="module.visible === 0" class="ion-text-wrap">
{{ 'core.course.hiddenfromstudents' | translate }}
</ion-badge>
<ion-badge color="warning" *ngIf="module.visible !== 0 && module.isStealth" class="ion-text-wrap">
{{ 'core.course.hiddenoncoursepage' | translate }}
</ion-badge>
<!-- @deprecated since 4.3. Buttons defined by the module handler. -->
<ion-button fill="clear" class="core-module-button-more core-animate-show-hide"
*ngIf="!module.handlerData.button && module.handlerData.buttons && module.handlerData.buttons[0]"
[hidden]="module.handlerData.buttons[0].hidden || module.handlerData.spinner" (click)="buttonClicked($event)"
[attr.aria-label]="module.handlerData.buttons[0].label | translate:{$a: module.handlerData.title}">
<ion-icon [name]="module.handlerData.buttons[0].icon" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
</div>
</div>
</ion-label>
<!-- Buttons. -->
<div slot="end" *ngIf="module.uservisible !== false" class="buttons core-module-buttons"
[ngClass]="{'core-button-completion': module.completiondata && showLegacyCompletion}">
<!-- Module completion (legacy). -->
<core-course-module-completion-legacy *ngIf="module.completiondata && showLegacyCompletion"
[completion]="module.completiondata" [moduleName]="module.name" [moduleId]="module.id"
(completionChanged)="completionChanged.emit($event)">
</core-course-module-completion-legacy>
<div class="core-module-buttons-more">
<!-- Buttons defined by the module handler. -->
<ion-button fill="clear" *ngFor="let button of module.handlerData.buttons"
[hidden]="button.hidden || module.handlerData.spinner" class="core-animate-show-hide"
(click)="buttonClicked($event, button)" [attr.aria-label]="button.label | translate:{$a: module.handlerData.title}">
<ion-icon [name]="button.icon" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<!-- Activity dates. -->
<div *ngIf="showActivityDates && module.dates?.length" class="activity-dates activity-extra">
<core-reminders-date *ngFor="let date of module.dates" [type]="date.id" [label]="date.label" [time]="date.timestamp"
[relativeTo]="date.relativeto">
</core-reminders-date>
</div>
</div>
</ion-item>
<ion-item *ngIf="hasInfo" id="core-course-module-{{module.id}}-info" detail="false"
class="ion-text-wrap core-course-module-handler core-course-module-info {{module.handlerData.class}}" [ngClass]="{
'item-dimmed': module.visible === 0 || module.uservisible === false
}">
<ion-label [collapsible-item]="64">
<core-format-text class="core-module-description" *ngIf="module.description" [text]="module.description"
contextLevel="module" [contextInstanceId]="module.id" [courseId]="module.course">
</core-format-text>
<!-- Activity completion. -->
<core-course-module-completion *ngIf="hasCompletion && !showLegacyCompletion" [completion]="module.completiondata"
[moduleName]="module.name" [moduleId]="module.id" [showCompletionConditions]="showCompletionConditions"
[showManualCompletion]="showManualCompletion" (completionChanged)="completionChanged.emit($event)">
<core-course-module-completion class="activity-extra" *ngIf="hasCompletion && !showLegacyCompletion"
[completion]="module.completiondata" [moduleName]="module.name" [moduleId]="module.id"
[showCompletionConditions]="showCompletionConditions" [showManualCompletion]="showManualCompletion"
(completionChanged)="completionChanged.emit($event)">
</core-course-module-completion>
<div class="core-module-dates-availabilityinfo"
*ngIf="(showActivityDates && module.dates && module.dates.length) || module.availabilityinfo">
<!-- Activity dates. -->
<div *ngIf="showActivityDates && module.dates && module.dates.length" class="core-module-dates">
<core-reminders-date *ngFor="let date of module.dates" [type]="date.id" [label]="date.label" [time]="date.timestamp"
[relativeTo]="date.relativeto">
</core-reminders-date>
</div>
<!-- Description and restrictions -->
<div *ngIf="module.description || module.availabilityinfo" id="activity-{{module.id}}-collapsible"
class="ion-text-wrap activity-description-availabilityinfo activity-extra" [collapsible-item]="64">
<core-format-text class="core-module-description" *ngIf="module.description" [text]="module.description"
contextLevel="module" [contextInstanceId]="module.id" [courseId]="module.course">
</core-format-text>
<!-- Availability info -->
<div *ngIf="module.availabilityinfo" class="core-module-availabilityinfo">
@ -89,7 +95,10 @@
</div>
</div>
<div *ngIf="module.handlerData.extraBadge" class="ion-text-wrap activity-extrabadges activity-extra"
[innerHTML]="module.handlerData.extraBadge"></div>
</ion-label>
</ion-item>
<div class="core-course-last-module-viewed" *ngIf="isLastViewed">
@ -99,9 +108,8 @@
</ng-container>
<!-- Loading. -->
<ion-item *ngIf="module.handlerData.loading" role="status" class="ion-text-wrap" id="core-course-module-{{module.id}}"
[attr.aria-label]="module.handlerData.a11yTitle"
[ngClass]="['core-course-module-handler', 'core-module-loading', module.handlerData.class]" detail="false">
<ion-item *ngIf="module.handlerData.loading" role="status" class="ion-text-wrap core-module-loading"
[attr.aria-label]="module.handlerData.a11yTitle" detail="false">
<ion-label>
<ion-spinner [attr.aria-label]="'core.loading' | translate"></ion-spinner>
</ion-label>

View File

@ -1,73 +1,148 @@
@import "~theme/globals";
:host {
--horizontal-margin: 10px;
--vertical-margin: 10px;
--horizontal-margin: 12px;
--vertical-margin: 12px;
--card-padding: 16px;
ion-card {
margin: var(--vertical-margin) var(--horizontal-margin);
}
ion-item {
--padding-start: 12px;
--padding-start: var(--card-padding);
--inner-padding-end: var(--card-padding);
ion-label {
margin-top: var(--card-padding);
margin-bottom: var(--card-padding);
&>:last-child {
margin-bottom: 0px;
}
}
}
ion-item.core-module-main-item {
.activity-main {
--min-height: 52px;
display: flex;
flex-direction: row;
.core-module-title .item-heading ion-icon {
@include margin-horizontal(8px, null);
vertical-align: middle;
core-mod-icon {
margin-top: 0px;
margin-bottom: 0px;
--module-icon-padding: 4px;
--module-icon-radius: var(--radius-xs);
@include margin-horizontal(null, 8px);
align-self: self-start;
}
.core-module-buttons,
.buttons.core-module-buttons {
.activity-title {
flex-grow: 1;
align-self: center;
@include margin-horizontal(null, var(--card-padding));
.item-heading ion-icon {
@include margin-horizontal(8px, null);
vertical-align: middle;
}
}
.core-module-buttons {
align-self: self-start;
margin: 0;
}
.core-module-buttons,
.core-module-buttons-more {
display: flex;
flex-flow: row;
align-items: center;
z-index: 1;
justify-content: space-around;
align-content: center;
}
.core-module-buttons core-course-module-completion,
.core-module-buttons-more button {
cursor: pointer;
pointer-events: auto;
}
ion-button.core-module-button-more {
cursor: pointer;
pointer-events: auto;
margin: 0px 8px;
--a11y-target-min-size: 32px;
width: var(--a11y-target-min-size);
height: var(--a11y-target-min-size);
min-width: var(--a11y-target-min-size);
min-height: var(--a11y-target-min-size);
--padding-start: 0px;
--padding-end: 0px;
ion-icon {
font-size: 20px;
}
}
.core-module-buttons core-course-module-completion {
text-align: center;
core-course-module-completion {
--margin: 0px;
}
}
.core-module-additional-info {
display: flex;
align-items: center;
flex-wrap: wrap;
margin-top: 4px;
margin-bottom: 4px;
ion-badge {
@include margin-horizontal(null, 4px);
font-size: 12px;
font-weight: normal;
}
}
}
.core-course-module-info {
.core-module-dates-availabilityinfo {
background: var(--light);
border-radius: var(--radius-xs);
padding: 8px;
}
.core-module-description ::ng-deep img {
border-radius: var(--radius-lg);
}
.core-module-dates + .core-module-availabilityinfo {
border-top: 1px solid var(--stroke);
padding-top: 8px;
}
core-course-module-completion {
--margin: 8px 0px;
}
.activity-dates {
display: flex;
flex-direction: row;
flex-wrap: wrap;
core-reminders-date {
--display-icon: none;
}
}
.activity-description-availabilityinfo,
.activity-extrabadges {
margin-top: 8px;
padding-top: 8px;
border-top: 1px solid var(--stroke);
}
.activity-extrabadges {
color: var(--gray-700);
}
.activity-description-availabilityinfo {
.core-module-availabilityinfo {
font-size: 90%;
::ng-deep ul {
margin-top: 0.5em;
background: var(--gray-300);
border-radius: var(--radius-sm);
margin-top: 8px;
padding: 8px;
font-size: 14px;
line-height: 120%;
::ng-deep ul {
margin-top: 8px;
margin-bottom: 0px;
li {
margin-bottom: 4px;
}
}
ion-icon {
@include margin-horizontal(null, 8px);
}
}
}
@ -79,19 +154,6 @@
clear: both;
}
.core-module-main-item + .core-course-module-info ion-label {
margin-top: 0px;
}
.core-module-availabilityinfo ion-icon,
.core-module-dates ion-icon {
@include margin-horizontal(null, 8px);
}
.core-course-module-info ::ng-deep core-course-module-completion .core-module-automatic-completion-conditions .completioninfo.completion_complete {
display: none;
}
.core-course-last-module-viewed {
padding: 8px 12px;
color: var(--subdued-text-color);
@ -102,14 +164,25 @@
}
}
@include media-breakpoint-down(md) {
.core-module-buttons core-course-module-completion {
display: none;
}
}
@include media-breakpoint-up(md) {
core-course-module-completion.activity-extra {
display: none;
}
}
&.indented ion-card {
border: none;
border-radius: 0;
margin-inline-start: calc(var(--horizontal-margin) + 1rem);
--ion-card-radius: 0;
@include margin-horizontal(calc(var(--horizontal-margin) + 1rem), null);
}
&.indented + ::ng-deep core-course-module.indented ion-card {
border-top: 1px solid var(--border-color);
}
}

View File

@ -21,8 +21,8 @@ import {
CoreCourseSection,
CoreCourseHelper,
} from '@features/course/services/course-helper';
import { CoreCourse, CoreCourseModuleCompletionStatus, CoreCourseModuleCompletionTracking } from '@features/course/services/course';
import { CoreCourseModuleDelegate, CoreCourseModuleHandlerButton } from '@features/course/services/module-delegate';
import { CoreCourse } from '@features/course/services/course';
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
import {
CoreCourseModulePrefetchDelegate,
CoreCourseModulePrefetchHandler,
@ -55,7 +55,6 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
@HostBinding('class.indented') indented = false;
modNameTranslated = '';
hasInfo = false;
hasCompletion = false; // Whether activity has completion to be shown.
showManualCompletion = false; // Whether to show manual completion when completion conditions are disabled.
prefetchStatusIcon$ = new BehaviorSubject<string>(''); // Module prefetch status icon.
@ -87,13 +86,6 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
this.module.handlerData.a11yTitle = this.module.handlerData.a11yTitle ?? this.module.handlerData.title;
this.moduleHasView = CoreCourse.moduleHasView(this.module);
this.hasInfo = !!(
this.module.description ||
(this.showActivityDates && this.module.dates && this.module.dates.length) ||
(this.hasCompletion && !this.showLegacyCompletion) ||
(this.module.availabilityinfo)
);
if (this.module.handlerData?.showDownloadButton) {
const status = await CoreCourseModulePrefetchDelegate.getModuleStatus(this.module, this.module.course);
this.updateModuleStatus(status);
@ -177,9 +169,9 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
* Function called when a button is clicked.
*
* @param event Click event.
* @param button The clicked button.
*/
buttonClicked(event: Event, button: CoreCourseModuleHandlerButton): void {
buttonClicked(event: Event): void {
const button = this.module.handlerData?.button ?? this.module.handlerData?.buttons?.[0];
if (!button || !button.action) {
return;
}

View File

@ -29,17 +29,18 @@
<core-course-module-info [module]="module" [courseId]="courseId" [description]="module.description" [component]="module.modname"
[componentId]="module.id" [expandDescription]="true" [showAvailabilityInfo]="true" (completionChanged)="onCompletionChange()"
[showManualCompletion]="showManualCompletion">
<div class="core-module-additional-info" title>
<ion-chip *ngIf="module.handlerData?.extraBadge" [color]="module.handlerData?.extraBadgeColor"
class="ion-text-wrap ion-text-start" [outline]="true">
<ion-label><span [innerHTML]="module.handlerData?.extraBadge"></span></ion-label>
</ion-chip>
<div class="core-module-additional-info">
<p *ngIf="module.handlerData?.extraBadge" class="ion-text-wrap" [innerHTML]="module.handlerData?.extraBadge">
</p>
<!-- Hidden badges -->
<ion-badge color="warning" *ngIf="module.visible === 0" class="ion-text-wrap">
<ion-badge color="secondary" *ngIf="module.visible === 0" class="ion-text-wrap">
<ion-icon name="far-eye-slash" aria-hidden="true"></ion-icon>
{{ 'core.course.hiddenfromstudents' | translate }}
</ion-badge>
<ion-badge color="warning" *ngIf="module.visible !== 0 && module.isStealth" class="ion-text-wrap">
<ion-badge color="secondary" *ngIf="module.visible !== 0 && module.isStealth" class="ion-text-wrap">
<ion-icon name="fas-low-vision" aria-hidden="true"></ion-icon>
{{ 'core.course.hiddenoncoursepage' | translate }}
</ion-badge>
</div>

View File

@ -156,6 +156,8 @@ export interface CoreCourseModuleHandlerData {
/**
* The color of the extra badge. Default: primary.
*
* @deprecated since 4.3 Not used anymore.
*/
extraBadgeColor?: CoreIonicColorNames;
@ -168,9 +170,16 @@ export interface CoreCourseModuleHandlerData {
/**
* The buttons to display in the module item.
*
* @deprecated since 4.3 Use button instead. It will only display the first.
*/
buttons?: CoreCourseModuleHandlerButton[];
/**
* The button to display in the module item.
*/
button?: CoreCourseModuleHandlerButton;
/**
* Whether to display a spinner where the download button is displayed. The module icon, title, etc. will be displayed.
*/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -3,6 +3,8 @@
:host {
display: flex;
flex-direction: row;
margin-top: 8px;
@include margin-horizontal(null, 8px);
}
div {
@ -12,10 +14,7 @@ div {
align-self: center;
ion-icon {
display: var(--display-icon, inline-block);
@include margin-horizontal(0px, 4px);
}
}
core-reminders-date + :host {
margin-top: 12px;
}

View File

@ -1,8 +1,16 @@
.item.core-course-module-handler.addon-mod-label-handler {
.core-course-module-handler.addon-mod-label-handler {
align-items: center !important;
cursor: auto !important;
&:hover {
opacity: 1;
}
.activity-title {
display: flex;
flex-direction: column;
.item-heading {
order: 2;
}
}
}

View File

@ -92,9 +92,9 @@ html {
--subdued-text-color: var(--medium);
--ion-card-color: var(--text-color);
--ion-card-vertical-margin: 10px;
--ion-card-horizontal-margin: 10px;
--ion-card-radius: var(--radius-sm);
--ion-card-vertical-margin: 12px;
--ion-card-horizontal-margin: 12px;
--ion-card-radius: var(--radius-lg);
--ion-card-border-width: 1px;
--ion-card-border-color: var(--stroke);
ion-card {

View File

@ -7,6 +7,7 @@ information provided here is intended especially for developers.
- Font Awesome icon library has been updated to 6.4.0. But nothing has changed, only version number.
- The analytics system in the app has been refactored and some functions that could trigger analytics calls no longer do it, now you need to use CoreAnalytics instead. Some functions in CoreCourseLogHelper and CorePushNotificationsProvider have been deprecated.
- Due to the analytics refactor, the parameters of most log functions have changed.
- CoreCourseModuleHandlerData.buttons has been deprecated, now only one button in atribute button will be shown.
=== 4.2.0 ===