Merge pull request #1886 from crazyserver/MOBILE-2895

Mobile 2895
main
Juan Leyva 2019-05-07 16:25:50 +02:00 committed by GitHub
commit be34610f25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 144 additions and 71 deletions

View File

@ -1333,6 +1333,7 @@
"core.dismiss": "local_moodlemobileapp",
"core.done": "survey",
"core.download": "moodle",
"core.downloaded": "local_moodlemobileapp",
"core.downloading": "local_moodlemobileapp",
"core.edit": "moodle",
"core.emptysplit": "local_moodlemobileapp",

View File

@ -115,6 +115,7 @@ export class AddonModResourceHelperProvider {
isDisplayedEmbedded(module: any, display: number): boolean {
if ((!module.contents.length && !module.contentsinfo) || !this.fileProvider.isAvailable() ||
(!this.sitesProvider.getCurrentSite().isVersionGreaterEqualThan('3.7') && this.isNextcloudFile(module))) {
return false;
}
let ext;

View File

@ -1333,6 +1333,7 @@
"core.dismiss": "Dismiss",
"core.done": "Done",
"core.download": "Download",
"core.downloaded": "Downloaded",
"core.downloading": "Downloading",
"core.edit": "Edit",
"core.emptysplit": "This page will appear blank if the left panel is empty or is loading.",

View File

@ -34,6 +34,7 @@ import { CoreContextMenuPopoverComponent } from './context-menu/context-menu-pop
import { CoreCoursePickerMenuPopoverComponent } from './course-picker-menu/course-picker-menu-popover';
import { CoreChartComponent } from './chart/chart';
import { CoreChronoComponent } from './chrono/chrono';
import { CoreDownloadRefreshComponent } from './download-refresh/download-refresh';
import { CoreLocalFileComponent } from './local-file/local-file';
import { CoreSitePickerComponent } from './site-picker/site-picker';
import { CoreTabsComponent } from './tabs/tabs';
@ -72,6 +73,7 @@ import { CoreStyleComponent } from './style/style';
CoreCoursePickerMenuPopoverComponent,
CoreChartComponent,
CoreChronoComponent,
CoreDownloadRefreshComponent,
CoreLocalFileComponent,
CoreSitePickerComponent,
CoreTabsComponent,
@ -118,6 +120,7 @@ import { CoreStyleComponent } from './style/style';
CoreContextMenuItemComponent,
CoreChartComponent,
CoreChronoComponent,
CoreDownloadRefreshComponent,
CoreLocalFileComponent,
CoreSitePickerComponent,
CoreTabsComponent,

View File

@ -0,0 +1,17 @@
<ng-container *ngIf="enabled && !(loading || status === statusDownloading)">
<!-- Download button. -->
<button *ngIf="status == statusNotDownloaded" ion-button icon-only clear (click)="download($event, false)" color="dark" class="core-animate-show-hide" [attr.aria-label]="'core.download' | translate">
<ion-icon name="cloud-download"></ion-icon>
</button>
<!-- Refresh button. -->
<button *ngIf="status == statusOutdated || (status == statusDownloaded && !canTrustDownload)" ion-button icon-only clear (click)="download($event, true)" color="dark" class="core-animate-show-hide" [attr.aria-label]="'core.refresh' | translate">
<ion-icon name="refresh"></ion-icon>
</button>
<!-- Downloaded status icon. -->
<ion-icon *ngIf="status == statusDownloaded && canTrustDownload" class="core-icon-downloaded" color="success" name="cloud-done" [attr.aria-label]="'core.downloaded' | translate" role="status"></ion-icon>
</ng-container>
<!-- Spinner. -->
<ion-spinner *ngIf="loading || status === statusDownloading" class="core-animate-show-hide"></ion-spinner>

View File

@ -0,0 +1,13 @@
ion-app.app-root core-download-refresh {
font-size: 1.4rem;
button, ion-icon {
cursor: pointer;
pointer-events: auto;
text-align: center;
}
ion-icon, .core-icon-downloaded {
font-size: 1.8em;
}
}

View File

@ -0,0 +1,55 @@
// (C) Copyright 2015 Martin Dougiamas
//
// 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, Output, EventEmitter } from '@angular/core';
import { CoreConstants } from '@core/constants';
/**
* Component to show a download button with refresh option, the spinner and the status of it.
*
* Usage:
* <core-download-refresh [status]="status" enabled="true" canCheckUpdates="true" action="download()"></core-download-refresh>
*/
@Component({
selector: 'core-download-refresh',
templateUrl: 'core-download-refresh.html'
})
export class CoreDownloadRefreshComponent {
@Input() status: string; // Download status.
@Input() enabled = false; // Whether the download is enabled.
@Input() loading = true; // Force loading status when is not downloading.
@Input() canTrustDownload = false; // If false, refresh will be shown if downloaded.
@Output() action: EventEmitter<boolean>; // Will emit an event when the item clicked.
statusDownloaded = CoreConstants.DOWNLOADED;
statusNotDownloaded = CoreConstants.NOT_DOWNLOADED;
statusOutdated = CoreConstants.OUTDATED;
statusDownloading = CoreConstants.DOWNLOADING;
constructor() {
this.action = new EventEmitter();
}
/**
* Download clicked.
*
* @param {Event} e Click event.
* @param {boolean} refresh Whether it's refreshing.
*/
download(e: Event, refresh: boolean): void {
e.preventDefault();
e.stopPropagation();
this.action.emit(refresh);
}
}

View File

@ -3,13 +3,12 @@
<h2>{{fileName}}</h2>
<p *ngIf="fileSizeReadable">{{ fileSizeReadable }}</p>
<p *ngIf="showTime">{{ timemodified * 1000 | coreFormatDate }}</p>
<div class="buttons" item-end>
<button ion-button clear icon-only (click)="download($event)" *ngIf="!isDownloading && showDownload" [attr.aria-label]="'core.download' | translate" color="dark">
<ion-icon [name]="isDownloaded ? 'refresh' : 'cloud-download'"></ion-icon>
</button>
<core-download-refresh [status]="state" [enabled]="canDownload" [loading]="isDownloading" [canTrustDownload]="!alwaysDownload" (action)="download()"></core-download-refresh>
<button ion-button clear icon-only (click)="delete($event)" *ngIf="!isDownloading && canDelete" [attr.aria-label]="'core.delete' | translate" color="danger">
<ion-icon name="trash"></ion-icon>
</button>
</div>
<ion-spinner *ngIf="isDownloading" item-end></ion-spinner>
</a>

View File

@ -7,6 +7,10 @@ ion-app.app-root {
.card-ios core-file + core-file > .item-ios.item-block > .item-inner,
core-file + core-file > .item-ios.item-block > .item-inner {
border-top: $hairlines-width solid $list-ios-border-color;
.buttons {
min-height: 53px;
min-width: 58px;
}
}
.card-wp core-file + core-file > .item-wp.item-block > .item-inner,
@ -16,5 +20,16 @@ ion-app.app-root {
core-file > .item.item-block > .item-inner {
border-bottom: 0;
@include padding(null, 0, null, null);
.buttons {
display: flex;
flex-flow: row;
align-items: center;
z-index: 1;
justify-content: space-around;
align-content: center;
min-height: 52px;
min-width: 53px;
}
}
}

View File

@ -44,9 +44,7 @@ export class CoreFileComponent implements OnInit, OnDestroy {
@Input() showTime?: boolean | string = true; // Whether show file time modified.
@Output() onDelete?: EventEmitter<void>; // Will notify when the delete button is clicked.
isDownloaded: boolean;
isDownloading: boolean;
showDownload: boolean;
fileIcon: string;
fileName: string;
fileSizeReadable: string;
@ -110,13 +108,10 @@ export class CoreFileComponent implements OnInit, OnDestroy {
*/
protected calculateState(): Promise<void> {
return this.filepoolProvider.getFileStateByUrl(this.siteId, this.fileUrl, this.timemodified).then((state) => {
const canDownload = this.sitesProvider.getCurrentSite().canDownloadFiles();
this.canDownload = this.sitesProvider.getCurrentSite().canDownloadFiles();
this.state = state;
this.isDownloaded = state === CoreConstants.DOWNLOADED || state === CoreConstants.OUTDATED;
this.isDownloading = canDownload && state === CoreConstants.DOWNLOADING;
this.showDownload = canDownload && (state === CoreConstants.NOT_DOWNLOADED || state === CoreConstants.OUTDATED ||
(this.alwaysDownload && state === CoreConstants.DOWNLOADED));
this.isDownloading = this.canDownload && state === CoreConstants.DOWNLOADING;
});
}
@ -139,12 +134,12 @@ export class CoreFileComponent implements OnInit, OnDestroy {
/**
* Download a file and, optionally, open it afterwards.
*
* @param {Event} e Click event.
* @param {Event} [e] Click event.
* @param {boolean} openAfterDownload Whether the file should be opened after download.
*/
download(e: Event, openAfterDownload: boolean): void {
e.preventDefault();
e.stopPropagation();
download(e?: Event, openAfterDownload: boolean = false): void {
e && e.preventDefault();
e && e.stopPropagation();
let promise;
@ -168,7 +163,8 @@ export class CoreFileComponent implements OnInit, OnDestroy {
return;
}
if (!this.appProvider.isOnline() && (!openAfterDownload || (openAfterDownload && !this.isDownloaded))) {
if (!this.appProvider.isOnline() && (!openAfterDownload || (openAfterDownload &&
!(this.state === CoreConstants.DOWNLOADED || this.state === CoreConstants.OUTDATED)))) {
this.domUtils.showErrorModal('core.networkerrormsg', true);
return;

View File

@ -90,17 +90,9 @@
<!-- Template to render a section download button/progress. -->
<ng-template #sectionDownloadTemplate let-section="section">
<div *ngIf="section && downloadEnabled" class="core-button-spinner" float-end>
<!-- Download button. -->
<button *ngIf="section.showDownload && !section.isDownloading && !section.isCalculating" (click)="prefetch($event, section)" ion-button icon-only clear color="dark" [attr.aria-label]="'core.download' | translate">
<ion-icon name="cloud-download"></ion-icon>
</button>
<!-- Refresh button. -->
<button *ngIf="section.showRefresh && !section.isDownloading && !section.isCalculating" (click)="prefetch($event, section)" ion-button icon-only clear color="dark" [attr.aria-label]="'core.refresh' | translate">
<ion-icon name="refresh"></ion-icon>
</button>
<!-- Download progress. -->
<ion-badge class="core-course-download-section-progress" *ngIf="section.isDownloading && section.total > 0 && section.count < section.total">{{section.count}} / {{section.total}}</ion-badge>
<!-- Spinner (downloading or calculating status). -->
<ion-spinner *ngIf="section.isDownloading || section.isCalculating"></ion-spinner>
<core-download-refresh [status]="section.downloadStatus" [enabled]="downloadEnabled" [loading]="section.isDownloading || section.isCalculating" [canTrustDownload]="section.canCheckUpdates" (action)="prefetch(section, $event)"></core-download-refresh>
</div>
</ng-template>

View File

@ -119,7 +119,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
this.courseHelper.calculateSectionStatus(section, this.course.id, false).then(() => {
if (section.isDownloading && !prefetchDelegate.isBeingDownloaded(downloadId)) {
// All the modules are now downloading, set a download all promise.
this.prefetch(section, false);
this.prefetch(section);
}
});
}
@ -339,13 +339,10 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
/**
* Confirm and prefetch a section. If the section is "all sections", prefetch all the sections.
*
* @param {Event} e Click event.
* @param {any} section Section to download.
* @param {boolean} refresh Refresh clicked (not used).
*/
prefetch(e: Event, section: any): void {
e.preventDefault();
e.stopPropagation();
prefetch(section: any, refresh: boolean = false): void {
section.isCalculating = true;
this.courseHelper.confirmDownloadSizeSection(this.course.id, section, this.sections).then(() => {
this.prefetchSection(section, true);

View File

@ -1,4 +1,6 @@
ion-app.app-root core-course-module-completion a {
display: block;
img {
padding: 5px;
width: 30px;

View File

@ -10,23 +10,12 @@
<core-course-module-completion *ngIf="module.completiondata" [completion]="module.completiondata" [moduleName]="module.name" (completionChanged)="completionChanged.emit($event)"></core-course-module-completion>
<div class="core-module-buttons-more">
<!-- Download button. -->
<button *ngIf="downloadEnabled && showDownload" [hidden]="spinner || module.handlerData.spinner" ion-button icon-only clear (click)="download($event, false)" color="dark" class="core-animate-show-hide" [attr.aria-label]="'core.download' | translate">
<ion-icon name="cloud-download"></ion-icon>
</button>
<!-- Refresh button. -->
<button *ngIf="downloadEnabled && showRefresh" [hidden]="spinner || module.handlerData.spinner" ion-button icon-only clear (click)="download($event, true)" color="dark" class="core-animate-show-hide" [attr.aria-label]="'core.refresh' | translate">
<ion-icon name="refresh"></ion-icon>
</button>
<core-download-refresh [status]="downloadStatus" [enabled]="downloadEnabled" [loading]="spinner || module.handlerData.spinner" [canTrustDownload]="canCheckUpdates" (action)="download($event)"></core-download-refresh>
<!-- Buttons defined by the module handler. -->
<button ion-button icon-only clear *ngFor="let button of module.handlerData.buttons" [hidden]="button.hidden || spinner || module.handlerData.spinner" (click)="buttonClicked($event, button)" color="dark" class="core-animate-show-hide" [attr.aria-label]="button.label | translate">
<core-icon [name]="button.icon" [ios]="button.iosIcon || ''" [md]="button.mdIcon || ''"></core-icon>
</button>
<!-- Spinner. -->
<ion-spinner *ngIf="spinner || module.handlerData.spinner" class="core-animate-show-hide"></ion-spinner>
</div>
</div>
</div>

View File

@ -36,6 +36,8 @@ ion-app.app-root core-course-module {
flex-flow: row;
align-items: center;
z-index: 1;
justify-content: space-around;
align-content: center;
}
.core-module-buttons core-course-module-completion,
@ -44,9 +46,8 @@ ion-app.app-root core-course-module {
pointer-events: auto;
}
.core-module-buttons-more .spinner {
@include position(null, 13px, null, null);
position: absolute;
.core-module-buttons core-course-module-completion {
text-align: center;
}
}

View File

@ -21,7 +21,6 @@ import { CoreCourseHelperProvider } from '../../providers/helper';
import { CoreCourseProvider } from '../../providers/course';
import { CoreCourseModuleHandlerButton } from '../../providers/module-delegate';
import { CoreCourseModulePrefetchDelegate, CoreCourseModulePrefetchHandler } from '../../providers/module-prefetch-delegate';
import { CoreConstants } from '../../../constants';
/**
* Component to display a module entry in a list of modules.
@ -52,9 +51,9 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
}
@Output() completionChanged?: EventEmitter<any>; // Will emit an event when the module completion changes.
showDownload: boolean; // Whether to display the download button.
showRefresh: boolean; // Whether to display the refresh button.
spinner: boolean; // Whether to display a spinner.
downloadStatus: string;
canCheckUpdates: boolean;
spinner: boolean; // Whether to display a loading spinner.
downloadEnabled: boolean; // Whether the download of sections and modules is enabled.
protected prefetchHandler: CoreCourseModulePrefetchHandler;
@ -81,6 +80,7 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
if (this.module.handlerData.showDownloadButton) {
// Listen for changes on this module status, even if download isn't enabled.
this.prefetchHandler = this.prefetchDelegate.getPrefetchHandlerFor(this.module);
this.canCheckUpdates = this.prefetchDelegate.canCheckUpdates();
this.statusObserver = this.eventsProvider.on(CoreEventsProvider.PACKAGE_STATUS_CHANGED, (data) => {
if (data.componentId === this.module.id && this.prefetchHandler &&
@ -135,13 +135,9 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
/**
* Download the module.
*
* @param {Event} event Click event.
* @param {boolean} refresh Whether it's refreshing.
*/
download(event: Event, refresh: boolean): void {
event.preventDefault();
event.stopPropagation();
download(refresh: boolean): void {
if (!this.prefetchHandler) {
return;
}
@ -168,10 +164,8 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
*/
protected showStatus(status: string): void {
if (status) {
this.spinner = status === CoreConstants.DOWNLOADING;
this.showDownload = status === CoreConstants.NOT_DOWNLOADED;
this.showRefresh = status === CoreConstants.OUTDATED ||
(!this.prefetchDelegate.canCheckUpdates() && status === CoreConstants.DOWNLOADED);
this.spinner = false;
this.downloadStatus = status;
if (this.module.handlerData.updateStatus) {
this.module.handlerData.updateStatus(status);

View File

@ -197,11 +197,9 @@ export class CoreCourseHelperProvider {
result.status = CoreConstants.DOWNLOADING;
}
section.downloadStatus = result.status;
section.canCheckUpdates = this.prefetchDelegate.canCheckUpdates();
// Set this section data.
section.showDownload = result.status === CoreConstants.NOT_DOWNLOADED;
section.showRefresh = result.status === CoreConstants.OUTDATED ||
(!this.prefetchDelegate.canCheckUpdates() && result.status === CoreConstants.DOWNLOADED);
if (result.status !== CoreConstants.DOWNLOADING || !this.prefetchDelegate.isBeingDownloaded(section.id)) {
section.isDownloading = false;
section.total = 0;
@ -250,9 +248,8 @@ export class CoreCourseHelperProvider {
return Promise.all(promises).then(() => {
if (allSectionsSection) {
// Set "All sections" data.
allSectionsSection.showDownload = allSectionsStatus === CoreConstants.NOT_DOWNLOADED;
allSectionsSection.showRefresh = allSectionsStatus === CoreConstants.OUTDATED ||
(!this.prefetchDelegate.canCheckUpdates() && allSectionsStatus === CoreConstants.DOWNLOADED);
allSectionsSection.downloadStatus = allSectionsStatus;
allSectionsSection.canCheckUpdates = this.prefetchDelegate.canCheckUpdates();
allSectionsSection.isDownloading = allSectionsStatus === CoreConstants.DOWNLOADING;
}
}).finally(() => {
@ -981,7 +978,7 @@ export class CoreCourseHelperProvider {
*/
getCourseStatusIconAndTitleFromStatus(status: string): {icon: string, title: string} {
if (status == CoreConstants.DOWNLOADED) {
// Always show refresh icon, we cannot knew if there's anything new in course options.
// Always show refresh icon, we cannot know if there's anything new in course options.
return {
icon: 'refresh',
title: 'core.course.refreshcourse'
@ -1330,9 +1327,8 @@ export class CoreCourseHelperProvider {
return this.utils.allPromises(promises).then(() => {
// Set "All sections" data.
section.showDownload = allSectionsStatus === CoreConstants.NOT_DOWNLOADED;
section.showRefresh = allSectionsStatus === CoreConstants.OUTDATED ||
(!this.prefetchDelegate.canCheckUpdates() && allSectionsStatus === CoreConstants.DOWNLOADED);
section.downloadStatus = allSectionsStatus;
section.canCheckUpdates = this.prefetchDelegate.canCheckUpdates();
section.isDownloading = allSectionsStatus === CoreConstants.DOWNLOADING;
}).finally(() => {
section.isDownloading = false;

View File

@ -72,6 +72,7 @@
"dismiss": "Dismiss",
"done": "Done",
"download": "Download",
"downloaded": "Downloaded",
"downloading": "Downloading",
"edit": "Edit",
"emptysplit": "This page will appear blank if the left panel is empty or is loading.",