MOBILE-3814 module: Adapt module preview page

main
Pau Ferrer Ocaña 2022-02-18 12:41:04 +01:00
parent 5c2eb8fc0a
commit a34736bc60
10 changed files with 91 additions and 81 deletions

View File

@ -1517,10 +1517,8 @@
"core.course": "moodle", "core.course": "moodle",
"core.course.activitydisabled": "local_moodlemobileapp", "core.course.activitydisabled": "local_moodlemobileapp",
"core.course.activitynotyetviewableremoteaddon": "local_moodlemobileapp", "core.course.activitynotyetviewableremoteaddon": "local_moodlemobileapp",
"core.course.activitynotyetviewablesiteupgradeneeded": "local_moodlemobileapp",
"core.course.allsections": "local_moodlemobileapp", "core.course.allsections": "local_moodlemobileapp",
"core.course.aria:sectionprogress": "local_moodlemobileapp", "core.course.aria:sectionprogress": "local_moodlemobileapp",
"core.course.askadmintosupport": "local_moodlemobileapp",
"core.course.availablespace": "local_moodlemobileapp", "core.course.availablespace": "local_moodlemobileapp",
"core.course.cannotdeletewhiledownloading": "local_moodlemobileapp", "core.course.cannotdeletewhiledownloading": "local_moodlemobileapp",
"core.course.completion_automatic:done": "course", "core.course.completion_automatic:done": "course",
@ -2357,7 +2355,6 @@
"core.whatisyourage": "moodle", "core.whatisyourage": "moodle",
"core.wheredoyoulive": "moodle", "core.wheredoyoulive": "moodle",
"core.whoissiteadmin": "local_moodlemobileapp", "core.whoissiteadmin": "local_moodlemobileapp",
"core.whoops": "local_moodlemobileapp",
"core.whyisthishappening": "local_moodlemobileapp", "core.whyisthishappening": "local_moodlemobileapp",
"core.whyisthisrequired": "moodle", "core.whyisthisrequired": "moodle",
"core.wsfunctionnotavailable": "local_moodlemobileapp", "core.wsfunctionnotavailable": "local_moodlemobileapp",

View File

@ -7,6 +7,8 @@
<core-format-text [text]="module.name" contextLevel="module" [component]="component" [componentId]="componentId" <core-format-text [text]="module.name" contextLevel="module" [component]="component" [componentId]="componentId"
[contextInstanceId]="module.id" [courseId]="courseId"> [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text> </core-format-text>
<ion-icon name="fas-lock" *ngIf="module.visible === 0 || module.uservisible === false"
[attr.aria-label]="'core.restricted' | translate"></ion-icon>
</h1> </h1>
<ng-content select="[title]"></ng-content> <ng-content select="[title]"></ng-content>
</ion-label> </ion-label>
@ -20,22 +22,33 @@
</ion-item> </ion-item>
<ng-content select="[description]"></ng-content> <ng-content select="[description]"></ng-content>
<ion-item class="ion-text-wrap" *ngIf="showCompletion && (module.dates?.length || <!-- Module completion. -->
(module.completiondata && module.completiondata.isautomatic && module.uservisible))"> <ion-item class="ion-text-wrap" *ngIf="showCompletion && module.completiondata && module.completiondata.isautomatic">
<ion-label> <ion-label>
<!-- Activity dates. -->
<div *ngIf="module.dates?.length" class="core-module-dates">
<p *ngFor="let date of module.dates">
<ion-icon name="fas-calendar" aria-hidden="true"></ion-icon><strong>{{ date.label }}</strong> {{ date.timestamp * 1000 |
coreFormatDate:'strftimedatetime' }}
</p>
</div>
<!-- Module completion. -->
<core-course-module-completion [completion]="module.completiondata" [moduleName]="module.name" [moduleId]="module.id" <core-course-module-completion [completion]="module.completiondata" [moduleName]="module.name" [moduleId]="module.id"
[showCompletionConditions]="true"> [showCompletionConditions]="true">
</core-course-module-completion> </core-course-module-completion>
</ion-label> </ion-label>
</ion-item> </ion-item>
<div class="core-module-dates-availabilityinfo"
*ngIf="(module.dates && module.dates.length) || (showAvailabilityInfo && module.availabilityinfo)">
<!-- Activity dates. -->
<div *ngIf="module.dates && module.dates.length" class="core-module-dates">
<p *ngFor="let date of module.dates">
<ion-icon name="fas-calendar" aria-hidden="true"></ion-icon><strong>{{ date.label }}</strong> {{ date.timestamp
*
1000 | coreFormatDate:'strftimedatetime' }}
</p>
</div>
<!-- Availability info space. -->
<div class="core-module-availabilityinfo" *ngIf="showAvailabilityInfo">
<ion-icon name="fas-lock" [attr.aria-label]="'core.restricted' | translate"></ion-icon>
<core-format-text [text]="module.availabilityinfo" contextLevel="module" [contextInstanceId]="module.id" [courseId]="module.course">
</core-format-text>
</div>
</div>
<ng-content></ng-content> <ng-content></ng-content>
<!-- Activity has something offline. --> <!-- Activity has something offline. -->

View File

@ -16,16 +16,38 @@
align-self: flex-start; align-self: flex-start;
} }
.core-module-dates { h1 ion-icon {
color: var(--medium);
@include margin-horizontal(8px, null);
font-size: 80%;
}
.core-module-dates-availabilityinfo {
background: var(--light); background: var(--light);
border-radius: var(--small-radius); border-radius: var(--small-radius);
padding: 8px; padding: 8px;
margin: 8px;
font-size: 90%;
ion-icon { ion-icon {
position: static;
@include margin-horizontal(null, 8px); @include margin-horizontal(null, 8px);
} }
p,
ul {
margin-top: 4px;
margin-bottom: 4px;
}
} }
.core-module-dates + .core-module-availabilityinfo {
border-top: 1px solid var(--stroke);
padding-top: 8px;
}
} }
:host-context(.core-iframe-fullscreen) { :host-context(.core-iframe-fullscreen) {

View File

@ -44,6 +44,8 @@ export class CoreCourseModuleInfoComponent implements OnInit {
@Input() description?: string | false; // The description to display. If false, no description will be shown. @Input() description?: string | false; // The description to display. If false, no description will be shown.
@Input() expandDescription = false; // If the description should be expanded by default. @Input() expandDescription = false; // If the description should be expanded by default.
@Input() showAvailabilityInfo = false; // If show availability info on the box.
@Input() hasDataToSync = false; // If the activity has any data to be synced. @Input() hasDataToSync = false; // If the activity has any data to be synced.
modicon = ''; modicon = '';

View File

@ -1,21 +1,22 @@
<div class="ion-padding"> <ion-card class="core-danger-card">
<h2 *ngIf="!isDisabledInSite && isSupportedByTheApp">{{ 'core.whoops' | translate }}</h2> <ion-item>
<h2 *ngIf="isDisabledInSite || !isSupportedByTheApp">{{ 'core.uhoh' | translate }}</h2> <ion-icon name="fas-exclamation-triangle" slot="start" aria-hidden="true"></ion-icon>
<ion-label>
<p class="core-big" *ngIf="isDisabledInSite">{{ 'core.course.activitydisabled' | translate }}</p> <p><strong>{{ 'core.uhoh' | translate }} </strong>
<p class="core-big" *ngIf="!isDisabledInSite && isSupportedByTheApp"> <ng-container *ngIf="isDisabledInSite">{{ 'core.course.activitydisabled' | translate }}</ng-container>
{{ 'core.course.activitynotyetviewablesiteupgradeneeded' | translate }} <ng-container *ngIf="!isDisabledInSite">
</p> {{ 'core.course.activitynotyetviewableremoteaddon' | translate }}
<p class="core-big" *ngIf="!isDisabledInSite && !isSupportedByTheApp"> </ng-container>
{{ 'core.course.activitynotyetviewableremoteaddon' | translate }} </p>
</p> </ion-label>
<p *ngIf="isDisabledInSite || !isSupportedByTheApp"><strong>{{ 'core.course.askadmintosupport' | translate }}</strong></p> </ion-item>
</ion-card>
<div *ngIf="module && module.url"> <ion-item lines="none" class="ion-text-wrap" *ngIf="module?.url && module?.uservisible">
<p><strong>{{ 'core.course.useactivityonbrowser' | translate }}</strong></p> <ion-label>
<ion-button expand="block" [href]="module.url" core-link [showBrowserWarning]="false"> <p>{{ 'core.course.useactivityonbrowser' | translate }}</p>
<ion-button expand="block" [href]="module?.url" core-link [showBrowserWarning]="false">
{{ 'core.openinbrowser' | translate }} {{ 'core.openinbrowser' | translate }}
<ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon> <ion-icon name="fas-external-link-alt" slot="end" aria-hidden="true"></ion-icon>
</ion-button> </ion-button>
</div> </ion-label>
</div> </ion-item>

View File

@ -14,7 +14,6 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { CoreCourse } from '@features/course/services/course';
import { CoreCourseModuleData } from '@features/course/services/course-helper'; import { CoreCourseModuleData } from '@features/course/services/course-helper';
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate'; import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
@ -27,12 +26,10 @@ import { CoreCourseModuleDelegate } from '@features/course/services/module-deleg
}) })
export class CoreCourseUnsupportedModuleComponent implements OnInit { export class CoreCourseUnsupportedModuleComponent implements OnInit {
@Input() courseId?: number; // The course to module belongs to. @Input() courseId?: number; // The course to module belongs to (unused).
@Input() module?: CoreCourseModuleData; // The module to render. @Input() module?: CoreCourseModuleData; // The module to render.
isDisabledInSite?: boolean; isDisabledInSite = false; // It is implicit than if not disabled it will be unsupported.
isSupportedByTheApp?: boolean;
moduleName?: string;
/** /**
* Component being initialized. * Component being initialized.
@ -43,8 +40,6 @@ export class CoreCourseUnsupportedModuleComponent implements OnInit {
} }
this.isDisabledInSite = CoreCourseModuleDelegate.isModuleDisabledInSite(this.module.modname); this.isDisabledInSite = CoreCourseModuleDelegate.isModuleDisabledInSite(this.module.modname);
this.isSupportedByTheApp = CoreCourseModuleDelegate.hasHandler(this.module.modname);
this.moduleName = CoreCourse.translateModuleName(this.module.modname);
} }
} }

View File

@ -1,10 +1,8 @@
{ {
"activitydisabled": "Your organisation has disabled this activity in the mobile app.", "activitydisabled": "Your organisation has disabled this activity in the mobile app.",
"activitynotyetviewableremoteaddon": "Your organisation installed a plugin that is not yet supported.", "activitynotyetviewableremoteaddon": "Your organisation installed a plugin that is not yet supported.",
"activitynotyetviewablesiteupgradeneeded": "Your organisation's Moodle installation needs to be updated.",
"allsections": "All sections", "allsections": "All sections",
"aria:sectionprogress": "Section progress:", "aria:sectionprogress": "Section progress:",
"askadmintosupport": "Contact the site administrator and tell them you want to use this activity with the Moodle Mobile app.",
"availablespace": " You currently have about {{available}} free space.", "availablespace": " You currently have about {{available}} free space.",
"cannotdeletewhiledownloading": "Files cannot be deleted while the activity is being downloaded. Please wait for the download to finish.", "cannotdeletewhiledownloading": "Files cannot be deleted while the activity is being downloaded. Please wait for the download to finish.",
"completion_automatic:done": "Done:", "completion_automatic:done": "Done:",

View File

@ -11,8 +11,8 @@
</ion-title> </ion-title>
<ion-buttons slot="end"> <ion-buttons slot="end">
<ion-button fill="clear" *ngIf="module.url" [href]="module.url" core-link [showBrowserWarning]="false" color="dark" <ion-button fill="clear" *ngIf="module.url && module.uservisible && !unsupported" [href]="module.url" core-link
[attr.aria-label]="'core.openinbrowser' | translate"> [showBrowserWarning]="false" color="dark" [attr.aria-label]="'core.openinbrowser' | translate">
<ion-icon name="fas-external-link-alt" slot="icon-only" aria-hidden="true"></ion-icon> <ion-icon name="fas-external-link-alt" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button> </ion-button>
</ion-buttons> </ion-buttons>
@ -24,44 +24,24 @@
</ion-refresher> </ion-refresher>
<core-loading [hideUntil]="loaded"> <core-loading [hideUntil]="loaded">
<core-course-module-info [module]="module" [courseId]="courseId" [description]="module.description" [component]="module.modname" <core-course-module-info [module]="module" [courseId]="courseId" [description]="module.description" [component]="module.modname"
[componentId]="module.id" [expandDescription]="true"> [componentId]="module.id" [expandDescription]="true" [showAvailabilityInfo]="true">
<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>
<ion-item class="ion-text-wrap" *ngIf="module.handlerData?.extraBadge || <!-- Hidden badges -->
(module.visible === 0 && (!section || section.visible)) || <ion-badge color="warning" *ngIf="module.visible === 0">
(module.visible !== 0 && module.isStealth) || {{ 'core.course.hiddenfromstudents' | translate }}
module.availabilityinfo"> </ion-badge>
<ion-label> <ion-badge color="warning" *ngIf="module.visible !== 0 && module.isStealth">
<div class="ion-padding" *ngIf="module.handlerData?.extraBadge"> {{ 'core.course.hiddenoncoursepage' | translate }}
<ion-chip *ngIf="module.handlerData?.extraBadge" [color]="module.handlerData?.extraBadgeColor" </ion-badge>
class="ion-text-wrap ion-text-start" [outline]="true"> </div>
<ion-label><span [innerHTML]="module.handlerData?.extraBadge"></span></ion-label>
</ion-chip>
</div>
<!-- Hidden badges -->
<div *ngIf="module.visible === 0 && (!section || section.visible)">
<ion-badge color="warning">
{{ 'core.course.hiddenfromstudents' | translate }}
</ion-badge>
</div>
<div *ngIf="module.visible !== 0 && module.isStealth">
<ion-badge color="warning">
{{ 'core.course.hiddenoncoursepage' | translate }}
</ion-badge>
</div>
<!-- Availability info -->
<div *ngIf="module.availabilityinfo" class="core-module-availabilityinfo">
<ion-icon name="fas-lock" [attr.aria-label]="'core.restricted' | translate"></ion-icon>
<core-format-text [text]="module.availabilityinfo" contextLevel="module" [contextInstanceId]="module.id"
[courseId]="module.course">
</core-format-text>
</div>
</ion-label>
</ion-item>
<core-course-unsupported-module *ngIf="unsupported" [module]="module" [courseId]="courseId"></core-course-unsupported-module>
</core-course-module-info> </core-course-module-info>
<core-course-unsupported-module *ngIf="unsupported" [module]="module"></core-course-unsupported-module>
</core-loading> </core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" <core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module"

View File

@ -37,6 +37,7 @@ export class CoreCourseModulePreviewPage implements OnInit {
courseId!: number; courseId!: number;
loaded = false; loaded = false;
unsupported = false; unsupported = false;
isDisabledInSite = false;
showManualCompletion = false; showManualCompletion = false;
protected debouncedUpdateModule?: () => void; // Update the module after a certain time. protected debouncedUpdateModule?: () => void; // Update the module after a certain time.
@ -80,6 +81,8 @@ export class CoreCourseModulePreviewPage implements OnInit {
if (!this.unsupported) { if (!this.unsupported) {
this.module.handlerData = this.module.handlerData =
await CoreCourseModuleDelegate.getModuleDataFor(this.module.modname, this.module, this.courseId); await CoreCourseModuleDelegate.getModuleDataFor(this.module.modname, this.module, this.courseId);
} else {
this.isDisabledInSite = CoreCourseModuleDelegate.isModuleDisabledInSite(this.module.modname);
} }
this.title = this.module.name; this.title = this.module.name;

View File

@ -342,7 +342,6 @@
"whatisyourage": "What is your age?", "whatisyourage": "What is your age?",
"wheredoyoulive": "In which country do you live?", "wheredoyoulive": "In which country do you live?",
"whoissiteadmin": "\"Site Administrators\" are the people who manage the Moodle at your school/university/company or learning organisation. If you don't know how to contact them, please contact your teachers/trainers.", "whoissiteadmin": "\"Site Administrators\" are the people who manage the Moodle at your school/university/company or learning organisation. If you don't know how to contact them, please contact your teachers/trainers.",
"whoops": "Oops!",
"whyisthishappening": "Why is this happening?", "whyisthishappening": "Why is this happening?",
"whyisthisrequired": "Why is this required?", "whyisthisrequired": "Why is this required?",
"wsfunctionnotavailable": "The web service function is not available.", "wsfunctionnotavailable": "The web service function is not available.",