Merge pull request #3111 from crazyserver/MOBILE-3814

Mobile 3814
main
Dani Palou 2022-02-11 11:27:50 +01:00 committed by GitHub
commit ae496ef11a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 184 additions and 151 deletions

View File

@ -22,7 +22,7 @@
</ion-item-divider> </ion-item-divider>
<core-loading [hideUntil]="loaded" [fullscreen]="false"> <core-loading [hideUntil]="loaded" [fullscreen]="false">
<ion-row class="ion-no-padding ion-justify-content-between" *ngIf="hasCourses"> <ion-row class="ion-no-padding ion-justify-content-between ion-align-items-center" *ngIf="hasCourses">
<ion-col size="auto" class="ion-no-padding" *ngIf="filters.enabled"> <ion-col size="auto" class="ion-no-padding" *ngIf="filters.enabled">
<core-combobox interface="modal" [label]="'core.courses.filtermycourses' | translate" (onChange)="filterOptionsChanged($event)" <core-combobox interface="modal" [label]="'core.courses.filtermycourses' | translate" (onChange)="filterOptionsChanged($event)"
icon="fas-filter" [badge]="filters.count" [modalOptions]="filterModalOptions"> icon="fas-filter" [badge]="filters.count" [modalOptions]="filterModalOptions">
@ -48,14 +48,15 @@
</ion-select-option> </ion-select-option>
</core-combobox> </core-combobox>
</ion-col> </ion-col>
<ion-col size="auto" class="ion-no-padding" *ngIf="layouts.options.length > 1"> <ion-col size="auto" class="ion-no-padding" *ngIf="isLayoutSwitcherAvailable">
<!-- "Layouts" selector. --> <ion-button *ngIf="layout == 'card'" fill="outline" (click)="toggleLayout('list')"
<core-combobox [label]="'core.show' | translate" [selection]="layouts.selected" (onChange)="saveLayout($event)" icon="fas-th"> [attr.aria-label]="'addon.block_myoverview.list' | translate">
<ng-container *ngFor="let layout of layouts.options"> <ion-icon slot="icon-only" name="fas-list"></ion-icon>
<ion-select-option class="ion-text-wrap" [value]="layout">{{ 'addon.block_myoverview.'+layout | translate }} </ion-button>
</ion-select-option> <ion-button *ngIf="layout == 'list'" fill="outline" (click)="toggleLayout('card')"
</ng-container> [attr.aria-label]="'addon.block_myoverview.card' | translate">
</core-combobox> <ion-icon slot="icon-only" name="fas-th"></ion-icon>
</ion-button>
</ion-col> </ion-col>
</ion-row> </ion-row>
<ion-row class="ion-no-padding ion-hide-md-up" *ngIf="hasCourses"> <ion-row class="ion-no-padding ion-hide-md-up" *ngIf="hasCourses">
@ -73,13 +74,12 @@
<!-- List of courses. --> <!-- List of courses. -->
<div class="safe-area-padding" *ngIf="hasCourses"> <div class="safe-area-padding" *ngIf="hasCourses">
<ion-grid class="ion-no-padding" [class.core-no-grid]="layouts.selected != 'card'" <ion-grid class="ion-no-padding" [class.core-no-grid]="layout != 'card'" [class.list-item-limited-width]="layout != 'card'">
[class.list-item-limited-width]="layouts.selected != 'card'">
<ion-row class="ion-no-padding"> <ion-row class="ion-no-padding">
<ion-col *ngFor="let course of filteredCourses" class="ion-no-padding" size="12" size-sm="6" size-md="6" size-lg="4" <ion-col *ngFor="let course of filteredCourses" class="ion-no-padding" size="12" size-sm="6" size-md="6" size-lg="4"
size-xl="3"> size-xl="3">
<core-courses-course-list-item [course]="course" class="core-courseoverview" [showDownload]="downloadCourseEnabled" <core-courses-course-list-item [course]="course" class="core-courseoverview" [showDownload]="downloadCourseEnabled"
[layout]="layouts.selected"> [layout]="layout">
</core-courses-course-list-item> </core-courses-course-list-item>
</ion-col> </ion-col>
</ion-row> </ion-row>

View File

@ -76,10 +76,8 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
component: AddonBlockMyOverviewFilterOptionsComponent, component: AddonBlockMyOverviewFilterOptionsComponent,
}; };
layouts: AddonBlockMyOverviewLayoutOptions = { isLayoutSwitcherAvailable = false;
options: [], layout: AddonBlockMyOverviewLayouts = 'list';
selected: 'card',
};
sort: AddonBlockMyOverviewSortOptions = { sort: AddonBlockMyOverviewSortOptions = {
shortnameEnabled: false, shortnameEnabled: false,
@ -142,9 +140,9 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
promises.push(this.currentSite.getLocalSiteConfig( promises.push(this.currentSite.getLocalSiteConfig(
'AddonBlockMyOverviewLayout', 'AddonBlockMyOverviewLayout',
this.layouts.selected, this.layout,
).then((value) => { ).then((value) => {
this.layouts.selected = value; this.layout = value;
return; return;
})); }));
@ -340,10 +338,10 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
* @param layouts Config available layouts. * @param layouts Config available layouts.
*/ */
protected loadLayouts(layouts?: string[]): void { protected loadLayouts(layouts?: string[]): void {
this.layouts.options = []; const layoutsOptions: AddonBlockMyOverviewLayouts[] = [];
if (layouts === undefined) { if (layouts === undefined) {
this.layouts.options = ['card', 'list']; this.isLayoutSwitcherAvailable = true;
return; return;
} }
@ -354,19 +352,21 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
} }
const validLayout: AddonBlockMyOverviewLayouts = layout == 'summary' ? 'list' : layout as AddonBlockMyOverviewLayouts; const validLayout: AddonBlockMyOverviewLayouts = layout == 'summary' ? 'list' : layout as AddonBlockMyOverviewLayouts;
if (!this.layouts.options.includes(validLayout)) { if (!layoutsOptions.includes(validLayout)) {
this.layouts.options.push(validLayout); layoutsOptions.push(validLayout);
} }
}); });
// If no layout is available use card. // If no layout is available use list.
if (this.layouts.options.length == 0) { if (layoutsOptions.length == 0) {
this.layouts.options = ['card']; layoutsOptions.push('list');
} }
if (!this.layouts.options.includes(this.layouts.selected)) { if (!layoutsOptions.includes(this.layout)) {
this.layouts.selected = this.layouts.options[0]; this.layout = layoutsOptions[0];
} }
this.isLayoutSwitcherAvailable = layoutsOptions.length > 1;
} }
/** /**
@ -632,15 +632,15 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
} }
/** /**
* Saves layout value. * Toggle layout value.
* *
* @param layout New layout. * @param layout New layout.
* @return Promise resolved when done. * @return Promise resolved when done.
*/ */
async saveLayout(layout: AddonBlockMyOverviewLayouts): Promise<void> { async toggleLayout(layout: AddonBlockMyOverviewLayouts): Promise<void> {
this.layouts.selected = layout; this.layout = layout;
await this.currentSite.setLocalSiteConfig('AddonBlockMyOverviewLayout', this.layouts.selected); await this.currentSite.setLocalSiteConfig('AddonBlockMyOverviewLayout', this.layout);
} }
/** /**
@ -702,11 +702,6 @@ export type AddonBlockMyOverviewFilterOptions = {
count: number; count: number;
}; };
type AddonBlockMyOverviewLayoutOptions = {
options: AddonBlockMyOverviewLayouts[];
selected: AddonBlockMyOverviewLayouts;
};
type AddonBlockMyOverviewSortOptions = { type AddonBlockMyOverviewSortOptions = {
shortnameEnabled: boolean; shortnameEnabled: boolean;
selected: string; selected: string;

View File

@ -549,7 +549,14 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
* Prefetch the whole course. * Prefetch the whole course.
*/ */
async prefetchCourse(): Promise<void> { async prefetchCourse(): Promise<void> {
const course = await CoreCourses.getCourse(this.courseId); const courses = await CoreCourses.getUserCourses(true);
let course = courses.find((course) => course.id == this.courseId);
if (!course) {
course = await CoreCourses.getCourse(this.courseId);
}
if (!course) {
return;
}
try { try {
await CoreCourseHelper.confirmAndPrefetchCourse( await CoreCourseHelper.confirmAndPrefetchCourse(

View File

@ -20,7 +20,7 @@
{{ 'core.settings.spaceusagehelp' | translate }} {{ 'core.settings.spaceusagehelp' | translate }}
</p> </p>
</ion-label> </ion-label>
<ion-button fill="clear" color="danger" slot="end" (click)="deleteSiteStorage()" <ion-button fill="clear" color="danger" slot="end" (click)="deleteSiteStorage($event)"
[hidden]="spaceUsage.spaceUsage! + spaceUsage.cacheEntries! <= 0" [hidden]="spaceUsage.spaceUsage! + spaceUsage.cacheEntries! <= 0"
[attr.aria-label]="'core.settings.deletesitefilestitle' | translate" fill="outline"> [attr.aria-label]="'core.settings.deletesitefilestitle' | translate" fill="outline">
<ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon> <ion-icon name="fas-trash" slot="icon-only" aria-hidden="true"></ion-icon>
@ -31,8 +31,8 @@
<h2 class="ion-text-wrap">{{ 'addon.storagemanager.coursesspaceusage' | translate }}</h2> <h2 class="ion-text-wrap">{{ 'addon.storagemanager.coursesspaceusage' | translate }}</h2>
<ion-badge color="light">{{ totalSize | coreBytesToSize }}</ion-badge> <ion-badge color="light">{{ totalSize | coreBytesToSize }}</ion-badge>
</ion-label> </ion-label>
<ion-button slot="end" (click)="deleteCompletelyDownloadedCourses()" [disabled]="completelyDownloadedCourses.length === 0" <ion-button slot="end" (click)="deleteCompletelyDownloadedCourses($event)"
color="danger" fill="outline"> [disabled]="completelyDownloadedCourses.length === 0" color="danger" fill="outline">
<ion-icon name="fas-trash" slot="icon-only" ariaLabel="{{ 'addon.storagemanager.deletecourses' | translate }}"> <ion-icon name="fas-trash" slot="icon-only" ariaLabel="{{ 'addon.storagemanager.deletecourses' | translate }}">
</ion-icon> </ion-icon>
</ion-button> </ion-button>
@ -55,7 +55,8 @@
{{ course.totalSize | coreBytesToSize }} {{ course.totalSize | coreBytesToSize }}
</ion-badge> </ion-badge>
</ion-label> </ion-label>
<ion-button slot="end" (click)="deleteCourse(course)" [disabled]="course.isDownloading" color="danger" fill="clear"> <ion-button slot="end" (click)="deleteCourse($event, course)" [disabled]="course.isDownloading" color="danger"
fill="clear">
<ion-icon name="fas-trash" slot="icon-only" <ion-icon name="fas-trash" slot="icon-only"
[attr.aria-label]="'addon.storagemanager.deletedatafrom' | translate: { name: course.displayname }"> [attr.aria-label]="'addon.storagemanager.deletedatafrom' | translate: { name: course.displayname }">
</ion-icon> </ion-icon>

View File

@ -103,8 +103,13 @@ export class AddonStorageManagerCoursesStoragePage implements OnInit, OnDestroy
/** /**
* Delete all courses that have been downloaded. * Delete all courses that have been downloaded.
*
* @param event: Event Object.
*/ */
async deleteCompletelyDownloadedCourses(): Promise<void> { async deleteCompletelyDownloadedCourses(event: Event): Promise<void> {
event.preventDefault();
event.stopPropagation();
try { try {
await CoreDomUtils.showDeleteConfirm('core.course.confirmdeletestoreddata'); await CoreDomUtils.showDeleteConfirm('core.course.confirmdeletestoreddata');
} catch (error) { } catch (error) {
@ -132,9 +137,13 @@ export class AddonStorageManagerCoursesStoragePage implements OnInit, OnDestroy
/** /**
* Delete course. * Delete course.
* *
* @param event: Event Object.
* @param course Course to delete. * @param course Course to delete.
*/ */
async deleteCourse(course: DownloadedCourse): Promise<void> { async deleteCourse(event: Event, course: DownloadedCourse): Promise<void> {
event.preventDefault();
event.stopPropagation();
try { try {
await CoreDomUtils.showDeleteConfirm('core.course.confirmdeletestoreddata'); await CoreDomUtils.showDeleteConfirm('core.course.confirmdeletestoreddata');
} catch (error) { } catch (error) {
@ -242,9 +251,12 @@ export class AddonStorageManagerCoursesStoragePage implements OnInit, OnDestroy
/** /**
* Deletes files of a site and the tables that can be cleared. * Deletes files of a site and the tables that can be cleared.
* *
* @param siteData Site object with space usage. * @param event: Event Object.
*/ */
async deleteSiteStorage(): Promise<void> { async deleteSiteStorage(event: Event): Promise<void> {
event.preventDefault();
event.stopPropagation();
try { try {
const siteName = CoreSites.getRequiredCurrentSite().getSiteName(); const siteName = CoreSites.getRequiredCurrentSite().getSiteName();

View File

@ -55,9 +55,8 @@ ion-button {
color: var(--color); color: var(--color);
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
min-height: 26px; min-height: var(--a11y-min-target-size);
overflow: hidden; overflow: hidden;
margin: 8px;
box-shadow: var(--box-shadow); box-shadow: var(--box-shadow);
&:focus, &:focus,
@ -71,6 +70,7 @@ ion-select {
border-style: var(--border-style); border-style: var(--border-style);
border-width: var(--border-width); border-width: var(--border-width);
border-radius: var(--core-combobox-radius); border-radius: var(--core-combobox-radius);
margin: 8px;
&::part(icon) { &::part(icon) {
margin: var(--icon-margin); margin: var(--icon-margin);
@ -103,9 +103,9 @@ ion-select {
ion-button { ion-button {
border-radius: var(--core-combobox-radius); border-radius: var(--core-combobox-radius);
margin: 4px 8px;
flex: 1; flex: 1;
min-height: 45px;
&::part(native) { &::part(native) {
text-transform: none; text-transform: none;

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { Component, EventEmitter, Input, Output, ViewChild, ViewEncapsulation } from '@angular/core'; import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { Translate } from '@singletons'; import { Translate } from '@singletons';
import { ModalOptions } from '@ionic/core'; import { ModalOptions } from '@ionic/core';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
@ -40,7 +40,6 @@ import { IonSelect } from '@ionic/angular';
selector: 'core-combobox', selector: 'core-combobox',
templateUrl: 'core-combobox.html', templateUrl: 'core-combobox.html',
styleUrls: ['combobox.scss'], styleUrls: ['combobox.scss'],
encapsulation: ViewEncapsulation.ShadowDom,
}) })
export class CoreComboboxComponent { export class CoreComboboxComponent {

View File

@ -1,4 +1,4 @@
<ion-button (click)="openSelect($event)" color="light" *ngIf="interface != 'modal' && icon" [disabled]="disabled"> <ion-button (click)="openSelect($event)" *ngIf="interface != 'modal' && icon" [disabled]="disabled">
<ion-icon [name]="icon" [attr.aria-label]="label" slot="start"> <ion-icon [name]="icon" [attr.aria-label]="label" slot="start">
</ion-icon> </ion-icon>
<ion-badge *ngIf="badge && badge > 0" slot="start">{{badge}}</ion-badge> <ion-badge *ngIf="badge && badge > 0" slot="start">{{badge}}</ion-badge>

View File

@ -6,12 +6,27 @@
<img *ngIf="course.courseImage" [src]="course.courseImage" core-external-content alt="" /> <img *ngIf="course.courseImage" [src]="course.courseImage" core-external-content alt="" />
<ion-icon *ngIf="!course.courseImage" name="fas-graduation-cap" class="course-icon"> <ion-icon *ngIf="!course.courseImage" name="fas-graduation-cap" class="course-icon">
</ion-icon> </ion-icon>
<ng-container
*ngIf="isEnrolled && ((downloadCourseEnabled && !courseOptionMenuEnabled && showDownload) || courseOptionMenuEnabled)">
<ng-container *ngTemplateOutlet="download"></ng-container>
</ng-container>
</div> </div>
<ng-container *ngIf="isEnrolled && layout != 'summarycard'">
<div class="core-button-spinner" *ngIf="!courseOptionMenuEnabled && showDownload">
<core-download-refresh [status]="prefetchCourseData.status" [enabled]="showDownload"
[statusTranslatable]="prefetchCourseData.statusTranslatable" [canTrustDownload]="false"
[loading]="prefetchCourseData.loading" (action)="prefetchCourse()"></core-download-refresh>
</div>
<div class="core-button-spinner" *ngIf="courseOptionMenuEnabled">
<!-- Options menu. -->
<ion-button fill="clear" color="dark" (click)="showCourseOptionsMenu($event)" *ngIf="!showSpinner"
[attr.aria-label]="('core.displayoptions' | translate)">
<ion-icon name="ellipsis-vertical" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<!-- Loading options course spinner. -->
<ion-spinner *ngIf="showSpinner" [attr.aria-label]="'core.loading' | translate"></ion-spinner>
</div>
</ng-container>
<ion-item class="ion-text-wrap" button detail="false" (click)="openCourse()" [attr.aria-label]="course.displayname || course.fullname" <ion-item class="ion-text-wrap" button detail="false" (click)="openCourse()" [attr.aria-label]="course.displayname || course.fullname"
[class.item-disabled]="course.visible == 0"> [class.item-disabled]="course.visible == 0">
@ -46,8 +61,8 @@
</ion-icon> </ion-icon>
</span> </span>
<ion-icon *ngIf="downloadCourseEnabled && prefetchCourseData.downloadSucceeded" class="core-icon-downloaded" <ion-icon *ngIf="prefetchCourseData.downloadSucceeded" class="core-icon-downloaded" name="cloud-done" color="success"
name="cloud-done" color="success" role="status" [attr.aria-label]="'core.downloaded' | translate"></ion-icon> role="status" [attr.aria-label]="'core.downloaded' | translate"></ion-icon>
</p> </p>
<ion-chip color="brand" *ngIf="course.categoryname" <ion-chip color="brand" *ngIf="course.categoryname"
@ -63,30 +78,7 @@
class="core-course-progress"> class="core-course-progress">
<core-progress-bar [progress]="progress" a11yText="core.courses.aria:courseprogress"></core-progress-bar> <core-progress-bar [progress]="progress" a11yText="core.courses.aria:courseprogress"></core-progress-bar>
</div> </div>
<ng-container *ngIf="layout == 'list' || layout == 'listwithenrol' && isEnrolled">
<ng-container *ngTemplateOutlet="download"></ng-container>
</ng-container>
</ion-label> </ion-label>
</ion-item> </ion-item>
</ion-card> </ion-card>
<ng-template #download>
<div class="core-button-spinner" *ngIf="downloadCourseEnabled && !courseOptionMenuEnabled && showDownload">
<core-download-refresh [status]="prefetchCourseData.status" [enabled]="downloadCourseEnabled"
[statusTranslatable]="prefetchCourseData.statusTranslatable" [canTrustDownload]="false" [loading]="prefetchCourseData.loading"
(action)="prefetchCourse()"></core-download-refresh>
</div>
<div class="core-button-spinner" *ngIf="courseOptionMenuEnabled">
<!-- Options menu. -->
<ion-button fill="clear" color="dark" (click)="showCourseOptionsMenu($event)" *ngIf="!showSpinner"
[attr.aria-label]="('core.displayoptions' | translate)">
<ion-icon name="ellipsis-vertical" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<!-- Loading options course spinner. -->
<ion-spinner *ngIf="showSpinner" [attr.aria-label]="'core.loading' | translate"></ion-spinner>
</div>
</ng-template>

View File

@ -54,6 +54,7 @@ ion-card {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
z-index: 2;
ion-spinner { ion-spinner {
margin-top: 4px; margin-top: 4px;
@ -156,7 +157,7 @@ ion-card.core-course-list-card {
ion-icon.course-icon { ion-icon.course-icon {
color: white; color: white;
opacity: 50%; opacity: 0.5;
position: absolute; position: absolute;
left: 0; left: 0;
right: 0; right: 0;

View File

@ -55,7 +55,6 @@ export class CoreCoursesCourseListItemComponent implements OnInit, OnDestroy, On
}; };
showSpinner = false; showSpinner = false;
downloadCourseEnabled = false;
courseOptionMenuEnabled = false; courseOptionMenuEnabled = false;
progress = -1; progress = -1;
completionUserTracked: boolean | undefined = false; completionUserTracked: boolean | undefined = false;
@ -63,7 +62,6 @@ export class CoreCoursesCourseListItemComponent implements OnInit, OnDestroy, On
protected courseStatus = CoreConstants.NOT_DOWNLOADED; protected courseStatus = CoreConstants.NOT_DOWNLOADED;
protected isDestroyed = false; protected isDestroyed = false;
protected courseStatusObserver?: CoreEventObserver; protected courseStatusObserver?: CoreEventObserver;
protected siteUpdatedObserver?: CoreEventObserver;
protected element: HTMLElement; protected element: HTMLElement;
@ -93,31 +91,12 @@ export class CoreCoursesCourseListItemComponent implements OnInit, OnDestroy, On
} }
if (this.isEnrolled) { if (this.isEnrolled) {
if (this.showDownload) {
this.initPrefetchCourse();
}
this.downloadCourseEnabled = !CoreCourses.isDownloadCourseDisabledInSite();
if (this.downloadCourseEnabled) {
this.initPrefetchCourse();
}
// This field is only available from 3.6 onwards. // This field is only available from 3.6 onwards.
this.courseOptionMenuEnabled = (this.layout != 'listwithenrol' && this.layout != 'summarycard') && this.courseOptionMenuEnabled = (this.layout != 'listwithenrol' && this.layout != 'summarycard') &&
this.course.isfavourite !== undefined; this.course.isfavourite !== undefined;
// Refresh the enabled flag if site is updated. this.initPrefetchCourse();
this.siteUpdatedObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, () => {
const wasEnabled = this.downloadCourseEnabled;
this.downloadCourseEnabled = !CoreCourses.isDownloadCourseDisabledInSite();
if (!wasEnabled && this.downloadCourseEnabled) {
// Download course is enabled now, initialize it.
this.initPrefetchCourse();
}
}, CoreSites.getCurrentSiteId());
} else if ('enrollmentmethods' in this.course) { } else if ('enrollmentmethods' in this.course) {
this.enrolmentIcons = []; this.enrolmentIcons = [];
@ -169,9 +148,7 @@ export class CoreCoursesCourseListItemComponent implements OnInit, OnDestroy, On
* @inheritdoc * @inheritdoc
*/ */
ngOnChanges(): void { ngOnChanges(): void {
if (this.showDownload && this.isEnrolled) { this.initPrefetchCourse();
this.initPrefetchCourse();
}
this.updateCourseFields(); this.updateCourseFields();
} }
@ -203,7 +180,12 @@ export class CoreCoursesCourseListItemComponent implements OnInit, OnDestroy, On
/** /**
* Initialize prefetch course. * Initialize prefetch course.
*/ */
async initPrefetchCourse(): Promise<void> { async initPrefetchCourse(forceInit = false): Promise<void> {
if (!this.isEnrolled || !this.showDownload ||
(this.courseOptionMenuEnabled && !forceInit)) {
return;
}
if (this.courseStatusObserver !== undefined) { if (this.courseStatusObserver !== undefined) {
// Already initialized. // Already initialized.
return; return;
@ -306,6 +288,8 @@ export class CoreCoursesCourseListItemComponent implements OnInit, OnDestroy, On
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
this.initPrefetchCourse(true);
const popoverData = await CoreDomUtils.openPopover<string>({ const popoverData = await CoreDomUtils.openPopover<string>({
component: CoreCoursesCourseOptionsMenuComponent, component: CoreCoursesCourseOptionsMenuComponent,
componentProps: { componentProps: {
@ -414,7 +398,6 @@ export class CoreCoursesCourseListItemComponent implements OnInit, OnDestroy, On
ngOnDestroy(): void { ngOnDestroy(): void {
this.isDestroyed = true; this.isDestroyed = true;
this.courseStatusObserver?.off(); this.courseStatusObserver?.off();
this.siteUpdatedObserver?.off();
} }
} }

View File

@ -1,5 +1,5 @@
:host { :host {
min-height: 61px; min-height: var(--a11y-min-target-size);
display: block; display: block;
position: relative; position: relative;
@ -10,6 +10,10 @@
z-index: 4; z-index: 4;
margin-top: 8px; margin-top: 8px;
margin-bottom: 8px; margin-bottom: 8px;
--border-color: var(--core-search-box-border-color);
--border-radius: var(--core-search-box-border-radius);
--background: var(--core-search-box-background);
--color: var(--core-search-box-border-color);
} }
ion-button.button { ion-button.button {
@ -19,6 +23,8 @@
.core-search-history { .core-search-history {
max-height: calc(-120px + 80vh); max-height: calc(-120px + 80vh);
overflow-y: auto; overflow-y: auto;
background: var(--core-search-box-background);
--ion-item-background: var(--core-search-box-background);
.item:hover { .item:hover {
--background: var(--light); --background: var(--light);
@ -40,5 +46,7 @@
ion-item { ion-item {
--min-height: var(--a11y-min-target-size); --min-height: var(--a11y-min-target-size);
--border-width: 0px;
--background: var(--core-search-box-background);
} }
} }

View File

@ -119,7 +119,7 @@ export class CoreSettingsHelperProvider {
const title = Translate.instant('core.settings.deletesitefilestitle'); const title = Translate.instant('core.settings.deletesitefilestitle');
const message = Translate.instant('core.settings.deletesitefiles', { sitename: siteName }); const message = Translate.instant('core.settings.deletesitefiles', { sitename: siteName });
await CoreDomUtils.showConfirm(message, title); await CoreDomUtils.showConfirm(message, title, Translate.instant('core.delete'));
const site = await CoreSites.getSite(siteId); const site = await CoreSites.getSite(siteId);

View File

@ -201,6 +201,7 @@ ion-header ion-toolbar {
.in-toolbar.button-clear { .in-toolbar.button-clear {
--color: var(--core-header-toolbar-color); --color: var(--core-header-toolbar-color);
--ion-toolbar-color: var(--core-header-toolbar-color); --ion-toolbar-color: var(--core-header-toolbar-color);
--border-radius: var(--huge-radius);
} }
.button.button-clear, .button.button-clear,
@ -264,9 +265,15 @@ button,
min-width: var(--a11y-min-target-size); min-width: var(--a11y-min-target-size);
} }
ion-button {
margin: 4px 8px;
}
ion-button.button-outline { ion-button.button-outline {
--border-width: 1px; --border-width: var(--core-input-border-width);
--background: var(--contrast-background); --border-color: var(--core-input-stroke);
--background: var(--core-input-background);
--color: var(--text-color);
} }
ion-button.button-solid { ion-button.button-solid {
@ -313,12 +320,7 @@ ion-button.button.button-clear.button-has-icon-only {
ion-button.button.button-solid, ion-button.button.button-solid,
ion-button.button.button-outline { ion-button.button.button-outline {
--border-radius: var(--small-radius); --border-radius: var(--core-input-radius);
}
// Clear buttons will be black.
ion-button.button-clear {
--primary: var(--primary);
} }
[role="button"], [role="button"],

View File

@ -37,16 +37,19 @@
--ion-text-color: var(--text-color); --ion-text-color: var(--text-color);
--ion-text-color-rgb: #{$text-color-dark-rgb}; --ion-text-color-rgb: #{$text-color-dark-rgb};
--subdued-text-color: var(--gray-400); --subdued-text-color: var(--gray-400);
--stroke: var(--gray-700);
--contrast-background: black;
--ion-card-color: var(--text-color); --ion-card-color: var(--text-color);
--ion-card-background: var(--ion-item-background); --ion-card-background: var(--ion-item-background);
--contrast-background: black;
--stroke: var(--gray-700);
--ion-border-color: var(--stroke); --ion-border-color: var(--stroke);
--ion-item-border-color: var(--stroke); --ion-item-border-color: var(--stroke);
--core-input-stroke: var(--gray-700);
--core-input-background: var(--gray-900);
ion-content { ion-content {
--background: var(--ion-background-color); --background: var(--ion-background-color);
--background-alternative: var(--gray-900); --background-alternative: var(--gray-900);
@ -80,9 +83,18 @@
--item-divider-color: var(--text-color); --item-divider-color: var(--text-color);
--spacer-background: var(--gray-700); --spacer-background: var(--gray-700);
--core-combobox-background: var(--ion-item-background); --ion-searchbar-background: var(--ion-background-color);
--core-combobox-color: var(--gray-100); --ion-searchbar-border-color: var(--core-input-stroke);
--core-combobox-border-color: var(--stroke); --ion-searchbar-color: var(--text-color);
--ion-searchbar-icon-color: var(--core-input-stroke);
--core-search-box-background: var(--ion-background-color);
--core-search-box-border-color: var(--core-input-stroke);
--core-search-box-color: var(--text-color);
--core-combobox-background: var(--core-input-background);
--core-combobox-color: var(--text-color);
--core-combobox-border-color: var(--core-input-stroke);
--core-login-background: var(--gray-900); --core-login-background: var(--gray-900);
--core-login-text-color: var(--white); --core-login-text-color: var(--white);

View File

@ -43,40 +43,44 @@
--ion-color-step-950: var(--gray-900); --ion-color-step-950: var(--gray-900);
--ion-color-step-1000: var(--black); --ion-color-step-1000: var(--black);
--text-color: #{$text-color};
--background-color: #{$background-color};
--core-online-color: #5cb85c;
--stroke: var(--gray-300);
--ion-border-color: var(--stroke);
--ion-item-border-color: var(--stroke);
@each $color-name, $unused in $colors { @each $color-name, $unused in $colors {
@include generate-color($color-name, $colors); @include generate-color($color-name, $colors);
} }
--brand-color: var(--brand); --brand-color: var(--brand);
--small-radius: 4px;
--medium-radius: 8px;
--big-radius: 16px;
--huge-radius: 24px;
// Accessibility vars. // Accessibility vars.
--a11y-min-target-size: 44px; --a11y-min-target-size: 44px;
--a11y-focus-color: var(--primary); --a11y-focus-color: var(--primary);
--a11y-focus-width: 2px; --a11y-focus-width: 2px;
--zoom-level: 100%; --zoom-level: 100%;
--small-radius: 4px;
--medium-radius: 8px;
--big-radius: 16px;
--huge-radius: 24px;
--text-color: #{$text-color};
--background-color: #{$background-color};
--stroke: var(--gray-300);
--core-online-color: #5cb85c;
--ion-background-color: var(--background-color);
--ion-item-border-color: var(--stroke);
--ion-background-color-rgb: #{$background-color-rgb};
--ion-border-color: var(--stroke);
--core-input-stroke: var(--gray-400);
--core-input-background: var(--ion-background-color);
--core-input-radius: var(--small-radius);
--core-input-border-width: 1px;
--module-icon-size: 24px; --module-icon-size: 24px;
--module-icon-radius: var(--medium-radius); --module-icon-radius: var(--medium-radius);
--list-item--max-width: 768px; --list-item--max-width: 768px;
--ion-background-color: var(--background-color);
--ion-background-color-rgb: #{$background-color-rgb};
--contrast-background: white; --contrast-background: white;
--ion-text-color: var(--text-color); --ion-text-color: var(--text-color);
@ -155,16 +159,40 @@
--border-width: 0 0 var(--core-header-toolbar-border-width) 0; --border-width: 0 0 var(--core-header-toolbar-border-width) 0;
} }
--ion-searchbar-background: var(--ion-background-color);
--ion-searchbar-border-color: var(--core-input-stroke);
--ion-searchbar-border-radius: var(--core-input-radius);
--ion-searchbar-color: var(--text-color);
--ion-searchbar-icon-color: var(--core-input-stroke);
ion-searchbar { ion-searchbar {
--background: var(--ion-item-background); --background: var(--ion-searchbar-background);
--border-color: var(--ion-searchbar-border-color);
--icon-color: var(--ion-searchbar-icon-color);
.searchbar-input-container {
color: var(--ion-searchbar-color) !important;
}
.searchbar-input { .searchbar-input {
height: var(--a11y-min-target-size); height: var(--a11y-min-target-size);
border: 1px solid var(--stroke); border: 1px solid var(--ion-searchbar-border-color);
box-shadow: none; box-shadow: none;
border-radius: var(--medium-radius); border-radius: var(--ion-searchbar-border-radius);
background: var(--ion-searchbar-background) !important;
} }
} }
--core-search-box-background: var(--ion-background-color);
--core-search-box-border-color: var(--core-input-stroke);
--core-search-box-border-radius: var(--core-input-radius);
--core-search-box-color: var(--text-color);
--core-combobox-background: var(--core-input-background);
--core-combobox-color: var(--text-color);
--core-combobox-border-color: var(--core-input-stroke);
--core-combobox-border-width: var(--core-input-border-width);
--core-combobox-radius: var(--core-input-radius);
--core-combobox-box-shadow: none;
ion-action-sheet { ion-action-sheet {
--button-color: var(--ion-text-color); --button-color: var(--ion-text-color);
--button-color-selected: var(--ion-text-color); --button-color-selected: var(--ion-text-color);
@ -252,13 +280,6 @@
--min-width: var(--a11y-min-target-size); --min-width: var(--a11y-min-target-size);
} }
--core-combobox-background: var(--ion-item-background);
--core-combobox-color: var(--gray-900);
--core-combobox-border-color: var(--stroke);
--core-combobox-border-width: 1px;
--core-combobox-radius: var(--medium-radius);
--core-combobox-box-shadow: none;
--selected-item-color: var(--primary); --selected-item-color: var(--primary);
--selected-item-border-width: 5px; --selected-item-border-width: 5px;