MOBILE-3625 blocks: Recent accessed items block
parent
fd67797ff1
commit
15bb71b49b
|
@ -37,6 +37,7 @@ import { AddonBlockSiteMainMenuModule } from './sitemainmenu/sitemainmenu.module
|
|||
import { AddonBlockStarredCoursesModule } from './starredcourses/starredcourses.module';
|
||||
import { AddonBlockTagsModule } from './tags/tags.module';
|
||||
import { AddonBlockActivityModulesModule } from './activitymodules/activitymodules.module';
|
||||
import { AddonBlockRecentlyAccessedItemsModule } from './recentlyaccesseditems/recentlyaccesseditems.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
|
@ -64,6 +65,7 @@ import { AddonBlockActivityModulesModule } from './activitymodules/activitymodul
|
|||
AddonBlockStarredCoursesModule,
|
||||
AddonBlockTagsModule,
|
||||
AddonBlockActivityModulesModule,
|
||||
AddonBlockRecentlyAccessedItemsModule,
|
||||
],
|
||||
providers: [],
|
||||
exports: [],
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
// (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 { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { CoreSharedModule } from '@/core/shared.module';
|
||||
import { CoreCoursesComponentsModule } from '@features/courses/components/components.module';
|
||||
|
||||
import { AddonBlockRecentlyAccessedItemsComponent } from './recentlyaccesseditems/recentlyaccesseditems';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AddonBlockRecentlyAccessedItemsComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreSharedModule,
|
||||
CoreCoursesComponentsModule,
|
||||
],
|
||||
exports: [
|
||||
AddonBlockRecentlyAccessedItemsComponent,
|
||||
],
|
||||
entryComponents: [
|
||||
AddonBlockRecentlyAccessedItemsComponent,
|
||||
],
|
||||
})
|
||||
export class AddonBlockRecentlyAccessedItemsComponentsModule {}
|
|
@ -0,0 +1,29 @@
|
|||
<ion-item-divider sticky="true">
|
||||
<ion-label><h2>{{ 'addon.block_recentlyaccesseditems.pluginname' | translate }}</h2></ion-label>
|
||||
</ion-item-divider>
|
||||
<core-loading [hideUntil]="loaded" class="core-loading-center safe-area-page">
|
||||
<div class="core-horizontal-scroll" *ngIf="items && items.length > 0">
|
||||
<div *ngFor="let item of items">
|
||||
<ion-card>
|
||||
<ion-item class="core-course-module-handler item-media ion-text-wrap" detail="false" (click)="action($event, item)"
|
||||
[title]="item.name">
|
||||
<img slot="start" [src]="item.iconUrl" alt="" role="presentation" *ngIf="item.iconUrl" class="core-module-icon">
|
||||
<ion-label>
|
||||
<h2>
|
||||
<core-format-text [text]="item.name" contextLevel="module" [contextInstanceId]="item.cmid"
|
||||
[courseId]="item.courseid"></core-format-text>
|
||||
</h2>
|
||||
<p>
|
||||
<core-format-text [text]="item.coursename" contextLevel="course" [contextInstanceId]="item.courseid">
|
||||
</core-format-text>
|
||||
</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<core-empty-box *ngIf="items.length <= 0" image="assets/img/icons/activities.svg"
|
||||
[message]="'addon.block_recentlyaccesseditems.noitems' | translate"></core-empty-box>
|
||||
|
||||
</core-loading>
|
|
@ -0,0 +1,7 @@
|
|||
@import "~theme/globals";
|
||||
|
||||
:host {
|
||||
.core-horizontal-scroll > div {
|
||||
@include horizontal_scroll_item(80%, 250px, 300px);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
// (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 { Component, OnInit } from '@angular/core';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreBlockBaseComponent } from '@features/block/classes/base-block-component';
|
||||
import {
|
||||
AddonBlockRecentlyAccessedItems,
|
||||
AddonBlockRecentlyAccessedItemsItem,
|
||||
} from '../../services/recentlyaccesseditems';
|
||||
import { CoreTextUtils } from '@services/utils/text';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper';
|
||||
|
||||
/**
|
||||
* Component to render a recently accessed items block.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'addon-block-recentlyaccesseditems',
|
||||
templateUrl: 'addon-block-recentlyaccesseditems.html',
|
||||
styleUrls: ['recentlyaccesseditems.scss'],
|
||||
})
|
||||
export class AddonBlockRecentlyAccessedItemsComponent extends CoreBlockBaseComponent implements OnInit {
|
||||
|
||||
items: AddonBlockRecentlyAccessedItemsItem[] = [];
|
||||
|
||||
protected fetchContentDefaultError = 'Error getting recently accessed items data.';
|
||||
|
||||
constructor() {
|
||||
super('AddonBlockRecentlyAccessedItemsComponent');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Perform the invalidate content function.
|
||||
*
|
||||
* @return Resolved when done.
|
||||
*/
|
||||
protected async invalidateContent(): Promise<void> {
|
||||
await AddonBlockRecentlyAccessedItems.instance.invalidateRecentItems();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the data to render the block.
|
||||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async fetchContent(): Promise<void> {
|
||||
this.items = await AddonBlockRecentlyAccessedItems.instance.getRecentItems();
|
||||
}
|
||||
|
||||
/**
|
||||
* Event clicked.
|
||||
*
|
||||
* @param e Click event.
|
||||
* @param item Activity item info.
|
||||
*/
|
||||
async action(e: Event, item: AddonBlockRecentlyAccessedItemsItem): Promise<void> {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const url = CoreTextUtils.instance.decodeHTMLEntities(item.viewurl);
|
||||
const modal = await CoreDomUtils.instance.showModalLoading();
|
||||
|
||||
try {
|
||||
const treated = await CoreContentLinksHelper.instance.handleLink(url);
|
||||
if (!treated) {
|
||||
return CoreSites.instance.getCurrentSite()?.openInBrowserWithAutoLoginIfSameSite(url);
|
||||
}
|
||||
} finally {
|
||||
modal.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"noitems": "No recent items",
|
||||
"pluginname": "Recently accessed items"
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
// (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 { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreSharedModule } from '@/core/shared.module';
|
||||
import { CoreBlockDelegate } from '@features/block/services/block-delegate';
|
||||
import { AddonBlockRecentlyAccessedItemsComponentsModule } from './components/components.module';
|
||||
import { AddonBlockRecentlyAccessedItemsHandler } from './services/block-handler';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
IonicModule,
|
||||
CoreSharedModule,
|
||||
AddonBlockRecentlyAccessedItemsComponentsModule,
|
||||
TranslateModule.forChild(),
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
multi: true,
|
||||
useValue: () => {
|
||||
CoreBlockDelegate.instance.registerHandler(AddonBlockRecentlyAccessedItemsHandler.instance);
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
export class AddonBlockRecentlyAccessedItemsModule {}
|
|
@ -0,0 +1,50 @@
|
|||
// (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 { CoreBlockHandlerData } from '@features/block/services/block-delegate';
|
||||
import { AddonBlockRecentlyAccessedItemsComponent } from '../components/recentlyaccesseditems/recentlyaccesseditems';
|
||||
import { CoreBlockBaseHandler } from '@features/block/classes/base-block-handler';
|
||||
import { makeSingleton } from '@singletons';
|
||||
|
||||
/**
|
||||
* Block handler.
|
||||
*/
|
||||
@Injectable( { providedIn: 'root' })
|
||||
export class AddonBlockRecentlyAccessedItemsHandlerService extends CoreBlockBaseHandler {
|
||||
|
||||
name = 'AddonBlockRecentlyAccessedItems';
|
||||
blockName = 'recentlyaccesseditems';
|
||||
|
||||
/**
|
||||
* Returns the data needed to render the block.
|
||||
*
|
||||
* @param injector Injector.
|
||||
* @param block The block to render.
|
||||
* @param contextLevel The context where the block will be used.
|
||||
* @param instanceId The instance ID associated with the context level.
|
||||
* @return Data or promise resolved with the data.
|
||||
*/
|
||||
getDisplayData(): CoreBlockHandlerData{
|
||||
|
||||
return {
|
||||
title: 'addon.block_recentlyaccesseditems.pluginname',
|
||||
class: 'addon-block-recentlyaccesseditems',
|
||||
component: AddonBlockRecentlyAccessedItemsComponent,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class AddonBlockRecentlyAccessedItemsHandler extends makeSingleton(AddonBlockRecentlyAccessedItemsHandlerService) {}
|
|
@ -0,0 +1,102 @@
|
|||
// (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 { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreCourse } from '@features/course/services/course';
|
||||
import { CoreSiteWSPreSets } from '@classes/site';
|
||||
import { makeSingleton } from '@singletons';
|
||||
|
||||
const ROOT_CACHE_KEY = 'AddonBlockRecentlyAccessedItems:';
|
||||
|
||||
/**
|
||||
* Service that provides some features regarding recently accessed items.
|
||||
*/
|
||||
@Injectable( { providedIn: 'root' })
|
||||
export class AddonBlockRecentlyAccessedItemsProvider {
|
||||
|
||||
/**
|
||||
* Get cache key for get last accessed items value WS call.
|
||||
*
|
||||
* @return Cache key.
|
||||
*/
|
||||
protected getRecentItemsCacheKey(): string {
|
||||
return ROOT_CACHE_KEY + ':recentitems';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last accessed items.
|
||||
*
|
||||
* @param siteId Site ID. If not defined, use current site.
|
||||
* @return Promise resolved when the info is retrieved.
|
||||
*/
|
||||
async getRecentItems(siteId?: string): Promise<AddonBlockRecentlyAccessedItemsItem[]> {
|
||||
const site = await CoreSites.instance.getSite(siteId);
|
||||
|
||||
const preSets: CoreSiteWSPreSets = {
|
||||
cacheKey: this.getRecentItemsCacheKey(),
|
||||
};
|
||||
|
||||
const items: AddonBlockRecentlyAccessedItemsItem[] =
|
||||
await site.read('block_recentlyaccesseditems_get_recent_items', undefined, preSets);
|
||||
|
||||
return items.map((item) => {
|
||||
const modicon = item.icon && CoreDomUtils.instance.getHTMLElementAttribute(item.icon, 'src');
|
||||
|
||||
item.iconUrl = CoreCourse.instance.getModuleIconSrc(item.modname, modicon || undefined);
|
||||
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates get last accessed items WS call.
|
||||
*
|
||||
* @param siteId Site ID to invalidate. If not defined, use current site.
|
||||
* @return Promise resolved when the data is invalidated.
|
||||
*/
|
||||
async invalidateRecentItems(siteId?: string): Promise<void> {
|
||||
const site = await CoreSites.instance.getSite(siteId);
|
||||
|
||||
await site.invalidateWsCacheForKey(this.getRecentItemsCacheKey());
|
||||
}
|
||||
|
||||
}
|
||||
export class AddonBlockRecentlyAccessedItems extends makeSingleton(AddonBlockRecentlyAccessedItemsProvider) {}
|
||||
|
||||
|
||||
/**
|
||||
* Result of WS block_recentlyaccesseditems_get_recent_items.
|
||||
*/
|
||||
export type AddonBlockRecentlyAccessedItemsItem = {
|
||||
id: number; // Id.
|
||||
courseid: number; // Courseid.
|
||||
cmid: number; // Cmid.
|
||||
userid: number; // Userid.
|
||||
modname: string; // Modname.
|
||||
name: string; // Name.
|
||||
coursename: string; // Coursename.
|
||||
timeaccess: number; // Timeaccess.
|
||||
viewurl: string; // Viewurl.
|
||||
courseviewurl: string; // Courseviewurl.
|
||||
icon: string; // Icon.
|
||||
} & AddonBlockRecentlyAccessedItemsItemCalculatedData;
|
||||
|
||||
/**
|
||||
* Calculated data for recently accessed item.
|
||||
*/
|
||||
export type AddonBlockRecentlyAccessedItemsItemCalculatedData = {
|
||||
iconUrl: string; // Icon URL. Calculated by the app.
|
||||
};
|
|
@ -1,3 +1,5 @@
|
|||
@import "~theme/globals";
|
||||
|
||||
:host {
|
||||
ion-card {
|
||||
display: flex;
|
||||
|
@ -107,21 +109,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
// @todo
|
||||
:host-context(.core-horizontal-scroll) {
|
||||
flex: 0 0 80%;
|
||||
min-width: 250px;
|
||||
max-width: 300px;
|
||||
align-self: stretch;
|
||||
display: block;
|
||||
|
||||
[text-wrap] .label {
|
||||
h2, p {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
@include horizontal_scroll_item(80%, 250px, 300px);
|
||||
|
||||
ion-card {
|
||||
.core-course-thumb {
|
||||
|
|
|
@ -185,3 +185,32 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin horizontal_scroll_item($width, $min-width, $max-width) {
|
||||
flex: 0 0 $width;
|
||||
min-width: $min-width;
|
||||
max-width: $max-width;
|
||||
align-self: stretch;
|
||||
display: block;
|
||||
|
||||
ion-card {
|
||||
height: calc(100% - 20px);
|
||||
width: calc(100% - 20px);
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
@media (max-width: 360px) {
|
||||
margin-left: 6px;
|
||||
margin-right: 6px;
|
||||
width: calc(100% - 12px);
|
||||
}
|
||||
}
|
||||
|
||||
.ion-text-wrap ion-label {
|
||||
h2, p {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue