MOBILE-3608 blocks: Add sitehome and dashboard blocks
parent
7d1d318afc
commit
c3372e8076
|
@ -19,7 +19,7 @@ import { CoreMainMenuHomeRoutingModule } from '@features/mainmenu/pages/home/hom
|
|||
import { CoreMainMenuHomeDelegate } from '@features/mainmenu/services/home-delegate';
|
||||
|
||||
import { CoreDashboardHomeHandler, CoreDashboardHomeHandlerService } from './services/handlers/dashboard-home';
|
||||
import { CoreCoursesMyCoursesHomeHandler, CoreCoursesMyCoursesHomeHandlerService } from './services/handlers/my-courses.home';
|
||||
import { CoreCoursesMyCoursesHomeHandler, CoreCoursesMyCoursesHomeHandlerService } from './services/handlers/my-courses-home';
|
||||
|
||||
const mainMenuHomeChildrenRoutes: Routes = [
|
||||
{
|
||||
|
|
|
@ -12,8 +12,19 @@
|
|||
</core-context-menu>
|
||||
</core-navbar-buttons>
|
||||
<ion-content>
|
||||
<!-- @todo -->
|
||||
<core-empty-box icon="fa-home" [message]="'core.courses.nocourses' | translate">
|
||||
<div>Dashboard</div>
|
||||
</core-empty-box>
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshDashboard($event)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
<core-loading [hideUntil]="loaded">
|
||||
<ion-list>
|
||||
<ng-container *ngFor="let block of blocks">
|
||||
<core-block *ngIf="block.visible" [block]="block" contextLevel="user" [instanceId]="userId"
|
||||
[extraData]="{'downloadEnabled': downloadEnabled}"></core-block>
|
||||
</ng-container>
|
||||
</ion-list>
|
||||
|
||||
<core-empty-box *ngIf="blocks.length == 0" icon="fas-th-large" [message]="'core.course.nocontentavailable' | translate">
|
||||
</core-empty-box>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
|
|
|
@ -20,6 +20,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
import { CoreBlockComponentsModule } from '@features/block/components/components.module';
|
||||
|
||||
import { CoreCoursesDashboardPage } from './dashboard';
|
||||
|
||||
|
@ -38,6 +39,7 @@ const routes: Routes = [
|
|||
TranslateModule.forChild(),
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
CoreBlockComponentsModule,
|
||||
],
|
||||
declarations: [
|
||||
CoreCoursesDashboardPage,
|
||||
|
|
|
@ -12,12 +12,16 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { NavController } from '@ionic/angular';
|
||||
import { Component, OnDestroy, OnInit, QueryList, ViewChildren } from '@angular/core';
|
||||
import { IonRefresher, NavController } from '@ionic/angular';
|
||||
|
||||
import { CoreCourses, CoreCoursesProvider } from '../../services/courses';
|
||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreCoursesDashboard } from '@features/courses/services/dashboard';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreCourseBlock } from '@features/course/services/course';
|
||||
import { CoreBlockComponent } from '@features/block/components/block/block';
|
||||
|
||||
/**
|
||||
* Page that displays the dashboard page.
|
||||
|
@ -29,16 +33,20 @@ import { CoreSites } from '@services/sites';
|
|||
})
|
||||
export class CoreCoursesDashboardPage implements OnInit, OnDestroy {
|
||||
|
||||
@ViewChildren(CoreBlockComponent) blocksComponents?: QueryList<CoreBlockComponent>;
|
||||
|
||||
|
||||
searchEnabled = false;
|
||||
downloadEnabled = false;
|
||||
downloadCourseEnabled = false;
|
||||
downloadCoursesEnabled = false;
|
||||
downloadEnabledIcon = 'far-square';
|
||||
userId?: number;
|
||||
blocks: Partial<CoreCourseBlock>[] = [];
|
||||
loaded = false;
|
||||
|
||||
protected updateSiteObserver?: CoreEventObserver;
|
||||
|
||||
siteName = 'Hello world';
|
||||
|
||||
constructor(
|
||||
protected navCtrl: NavController,
|
||||
) { }
|
||||
|
@ -59,8 +67,82 @@ export class CoreCoursesDashboardPage implements OnInit, OnDestroy {
|
|||
|
||||
this.switchDownload(this.downloadEnabled && this.downloadCourseEnabled && this.downloadCoursesEnabled);
|
||||
}, CoreSites.instance.getCurrentSiteId());
|
||||
|
||||
this.loadContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to fetch the dashboard data.
|
||||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async loadContent(): Promise<void> {
|
||||
const available = await CoreCoursesDashboard.instance.isAvailable();
|
||||
|
||||
if (available) {
|
||||
this.userId = CoreSites.instance.getCurrentSiteUserId();
|
||||
|
||||
try {
|
||||
this.blocks = await CoreCoursesDashboard.instance.getDashboardBlocks();
|
||||
} catch (error) {
|
||||
CoreDomUtils.instance.showErrorModal(error);
|
||||
|
||||
// Cannot get the blocks, just show dashboard if needed.
|
||||
this.loadFallbackBlocks();
|
||||
}
|
||||
} else if (!CoreCoursesDashboard.instance.isDisabledInSite()) {
|
||||
// Not available, but not disabled either. Use fallback.
|
||||
this.loadFallbackBlocks();
|
||||
} else {
|
||||
// Disabled.
|
||||
this.blocks = [];
|
||||
}
|
||||
|
||||
// this.dashboardEnabled = this.blockDelegate.hasSupportedBlock(this.blocks);
|
||||
this.loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load fallback blocks to shown before 3.6 when dashboard blocks are not supported.
|
||||
*/
|
||||
protected loadFallbackBlocks(): void {
|
||||
this.blocks = [
|
||||
{
|
||||
name: 'myoverview',
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
name: 'timeline',
|
||||
visible: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the dashboard data.
|
||||
*
|
||||
* @param refresher Refresher.
|
||||
*/
|
||||
refreshDashboard(refresher: CustomEvent<IonRefresher>): void {
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
promises.push(CoreCoursesDashboard.instance.invalidateDashboardBlocks());
|
||||
|
||||
// Invalidate the blocks.
|
||||
this.blocksComponents?.forEach((blockComponent) => {
|
||||
promises.push(blockComponent.invalidate().catch(() => {
|
||||
// Ignore errors.
|
||||
}));
|
||||
});
|
||||
|
||||
Promise.all(promises).finally(() => {
|
||||
this.loadContent().finally(() => {
|
||||
refresher?.detail.complete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Toggle download enabled.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||
import { CoreCourseBlock } from '@features/course/services/course';
|
||||
import { CoreStatusWithWarningsWSResponse } from '@services/ws';
|
||||
import { makeSingleton } from '@singletons';
|
||||
|
||||
const ROOT_CACHE_KEY = 'CoreCoursesDashboard:';
|
||||
|
||||
/**
|
||||
* Service that provides some features regarding course overview.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CoreCoursesDashboardProvider {
|
||||
|
||||
/**
|
||||
* Get cache key for dashboard blocks WS calls.
|
||||
*
|
||||
* @param userId User ID. Default, 0 means current user.
|
||||
* @return Cache key.
|
||||
*/
|
||||
protected getDashboardBlocksCacheKey(userId: number = 0): string {
|
||||
return ROOT_CACHE_KEY + 'blocks:' + userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dashboard blocks.
|
||||
*
|
||||
* @param userId User ID. Default, current user.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with the list of blocks.
|
||||
* @since 3.6
|
||||
*/
|
||||
async getDashboardBlocks(userId?: number, siteId?: string): Promise<CoreCourseBlock[]> {
|
||||
const site = await CoreSites.instance.getSite(siteId);
|
||||
|
||||
const params: CoreBlockGetDashboardBlocksWSParams = {
|
||||
returncontents: true,
|
||||
};
|
||||
const preSets: CoreSiteWSPreSets = {
|
||||
cacheKey: this.getDashboardBlocksCacheKey(userId),
|
||||
updateFrequency: CoreSite.FREQUENCY_RARELY,
|
||||
};
|
||||
if (userId) {
|
||||
params.userid = userId;
|
||||
}
|
||||
const result = await site.read<CoreBlockGetDashboardBlocksWSResponse>('core_block_get_dashboard_blocks', params, preSets);
|
||||
|
||||
return result.blocks || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates dashboard blocks WS call.
|
||||
*
|
||||
* @param userId User ID. Default, current user.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when the data is invalidated.
|
||||
*/
|
||||
async invalidateDashboardBlocks(userId?: number, siteId?: string): Promise<void> {
|
||||
const site = await CoreSites.instance.getSite(siteId);
|
||||
|
||||
return await site.invalidateWsCacheForKey(this.getDashboardBlocksCacheKey(userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not block based Dashboard is available for a certain site.
|
||||
*
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with true if available, resolved with false or rejected otherwise.
|
||||
* @since 3.6
|
||||
*/
|
||||
async isAvailable(siteId?: string): Promise<boolean> {
|
||||
const site = await CoreSites.instance.getSite(siteId);
|
||||
|
||||
// First check if it's disabled.
|
||||
if (this.isDisabledInSite(site)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return site.wsAvailable('core_block_get_dashboard_blocks');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Site Home is disabled in a certain site.
|
||||
*
|
||||
* @param siteId Site Id. If not defined, use current site.
|
||||
* @return Promise resolved with true if disabled, rejected or resolved with false otherwise.
|
||||
*/
|
||||
async isDisabled(siteId?: string): Promise<boolean> {
|
||||
const site = await CoreSites.instance.getSite(siteId);
|
||||
|
||||
return this.isDisabledInSite(site);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Site Home is disabled in a certain site.
|
||||
*
|
||||
* @param site Site. If not defined, use current site.
|
||||
* @return Whether it's disabled.
|
||||
*/
|
||||
isDisabledInSite(site?: CoreSite): boolean {
|
||||
site = site || CoreSites.instance.getCurrentSite();
|
||||
|
||||
return !!site?.isFeatureDisabled('CoreMainMenuDelegate_CoreCoursesDashboard');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class CoreCoursesDashboard extends makeSingleton(CoreCoursesDashboardProvider) {}
|
||||
|
||||
|
||||
/**
|
||||
* Params of core_block_get_dashboard_blocks WS.
|
||||
*/
|
||||
type CoreBlockGetDashboardBlocksWSParams = {
|
||||
userid?: number; // User id (optional), default is current user.
|
||||
returncontents?: boolean; // Whether to return the block contents.
|
||||
};
|
||||
|
||||
/**
|
||||
* Data returned by core_block_get_dashboard_blocks WS.
|
||||
*/
|
||||
type CoreBlockGetDashboardBlocksWSResponse = {
|
||||
blocks: CoreCourseBlock[]; // List of blocks in the course.
|
||||
warnings?: CoreStatusWithWarningsWSResponse[];
|
||||
};
|
|
@ -13,8 +13,10 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreBlockDelegate } from '@features/block/services/block-delegate';
|
||||
import { CoreMainMenuHomeHandler, CoreMainMenuHomeHandlerToDisplay } from '@features/mainmenu/services/home-delegate';
|
||||
import { makeSingleton } from '@singletons';
|
||||
import { CoreCoursesDashboard } from '../dashboard';
|
||||
|
||||
/**
|
||||
* Handler to add dashboard into home page.
|
||||
|
@ -42,10 +44,10 @@ export class CoreDashboardHomeHandlerService implements CoreMainMenuHomeHandler
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Whether or not the handler is enabled on a site level.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async isEnabledForSite(siteId?: string): Promise<boolean> {
|
||||
// @todo return this.blockDelegate.hasSupportedBlock(this.blocks);
|
||||
return true;
|
||||
const blocks = await CoreCoursesDashboard.instance.getDashboardBlocks(undefined, siteId);
|
||||
|
||||
return CoreBlockDelegate.instance.hasSupportedBlock(blocks);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,8 +13,11 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreBlockDelegate } from '@features/block/services/block-delegate';
|
||||
import { CoreMainMenuHomeHandler, CoreMainMenuHomeHandlerToDisplay } from '@features/mainmenu/services/home-delegate';
|
||||
import { CoreSiteHome } from '@features/sitehome/services/sitehome';
|
||||
import { makeSingleton } from '@singletons';
|
||||
import { CoreCoursesDashboard } from '../dashboard';
|
||||
|
||||
/**
|
||||
* Handler to add my courses into home page.
|
||||
|
@ -42,10 +45,10 @@ export class CoreCoursesMyCoursesHomeHandlerService implements CoreMainMenuHomeH
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Whether or not the handler is enabled on a site level.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async isEnabledForSite(siteId?: string): Promise<boolean> {
|
||||
// @todo return !this.blockDelegate.hasSupportedBlock(this.blocks) && !CoreSiteHome.instance.isAvailable(siteId);
|
||||
return true;
|
||||
const blocks = await CoreCoursesDashboard.instance.getDashboardBlocks(undefined, siteId);
|
||||
|
||||
return !CoreBlockDelegate.instance.hasSupportedBlock(blocks)&& !CoreSiteHome.instance.isAvailable(siteId);
|
||||
}
|
||||
|
||||
/**
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { IonRefresher, NavController } from '@ionic/angular';
|
||||
|
||||
|
@ -24,6 +24,7 @@ import { CoreSiteHome } from '@features/sitehome/services/sitehome';
|
|||
import { CoreCourses, CoreCoursesProvider } from '@features//courses/services/courses';
|
||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||
import { CoreCourseHelper } from '@features/course/services/course-helper';
|
||||
import { CoreBlockCourseBlocksComponent } from '@features/block/components/course-blocks/course-blocks';
|
||||
|
||||
/**
|
||||
* Page that displays site home index.
|
||||
|
@ -34,7 +35,7 @@ import { CoreCourseHelper } from '@features/course/services/course-helper';
|
|||
})
|
||||
export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
|
||||
|
||||
// @todo @ViewChild(CoreBlockCourseBlocksComponent) courseBlocksComponent: CoreBlockCourseBlocksComponent;
|
||||
@ViewChild(CoreBlockCourseBlocksComponent) courseBlocksComponent?: CoreBlockCourseBlocksComponent;
|
||||
|
||||
dataLoaded = false;
|
||||
section?: CoreCourseSection & {
|
||||
|
@ -158,13 +159,17 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
|
|||
// @todo promises.push(this.prefetchDelegate.invalidateModules(this.section.modules, this.siteHomeId));
|
||||
}
|
||||
|
||||
// @todo promises.push(this.courseBlocksComponent.invalidateBlocks());
|
||||
if (this.courseBlocksComponent) {
|
||||
promises.push(this.courseBlocksComponent.invalidateBlocks());
|
||||
}
|
||||
|
||||
Promise.all(promises).finally(async () => {
|
||||
const p2: Promise<unknown>[] = [];
|
||||
|
||||
p2.push(this.loadContent());
|
||||
// @todo p2.push(this.courseBlocksComponent.loadContent());
|
||||
if (this.courseBlocksComponent) {
|
||||
p2.push(this.courseBlocksComponent.loadContent());
|
||||
}
|
||||
|
||||
await Promise.all(p2).finally(() => {
|
||||
refresher?.detail.complete();
|
||||
|
|
Loading…
Reference in New Issue