Skip to content

Commit

Permalink
MOBILE-3371 search: Support blocks and sections
Browse files Browse the repository at this point in the history
  • Loading branch information
NoelDeMartin committed Sep 14, 2023
1 parent e4755af commit 254dcb3
Show file tree
Hide file tree
Showing 15 changed files with 153 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { OnInit, Component } from '@angular/core';
import { OnInit, Component, HostBinding } from '@angular/core';
import { CoreBlockBaseComponent } from '../../classes/base-block-component';

/**
Expand All @@ -26,6 +26,8 @@ export class CoreBlockPreRenderedComponent extends CoreBlockBaseComponent implem

courseId?: number;

@HostBinding('attr.id') id?: string;

constructor() {
super('CoreBlockPreRenderedComponent');
}
Expand All @@ -39,6 +41,8 @@ export class CoreBlockPreRenderedComponent extends CoreBlockBaseComponent implem
this.courseId = this.contextLevel == 'course' ? this.instanceId : undefined;

this.fetchContentDefaultError = 'Error getting ' + this.block.contents?.title + ' data.';

this.id = `block-${this.block.instanceid}`;
}

}
24 changes: 23 additions & 1 deletion src/core/features/block/components/side-blocks/side-blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { Component, ViewChildren, Input, OnInit, QueryList } from '@angular/core';
import { Component, ViewChildren, Input, OnInit, QueryList, ElementRef } from '@angular/core';
import { ModalController } from '@singletons';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreCourse, CoreCourseBlock } from '@features/course/services/course';
Expand All @@ -22,6 +22,7 @@ import { CoreUtils } from '@services/utils/utils';
import { IonRefresher } from '@ionic/angular';
import { CoreCoursesDashboard } from '@features/courses/services/dashboard';
import { CoreTextUtils } from '@services/utils/text';
import { CoreDom } from '@singletons/dom';

/**
* Component that displays the list of side blocks.
Expand All @@ -35,19 +36,24 @@ export class CoreBlockSideBlocksComponent implements OnInit {

@Input() contextLevel!: string;
@Input() instanceId!: number;
@Input() initialBlockInstanceId?: number;
@Input() myDashboardPage?: string;

@ViewChildren(CoreBlockComponent) blocksComponents?: QueryList<CoreBlockComponent>;

loaded = false;
blocks: CoreCourseBlock[] = [];

constructor(protected elementRef: ElementRef<HTMLElement>) {}

/**
* @inheritdoc
*/
async ngOnInit(): Promise<void> {
this.loadContent().finally(() => {
this.loaded = true;

this.focusInitialBlock();
});
}

Expand Down Expand Up @@ -119,4 +125,20 @@ export class CoreBlockSideBlocksComponent implements OnInit {
ModalController.dismiss();
}

/**
* Focus the initial block, if any.
*/
private async focusInitialBlock(): Promise<void> {
if (!this.initialBlockInstanceId) {
return;
}

const selector = '#block-' + this.initialBlockInstanceId;

await CoreUtils.waitFor(() => !!this.elementRef.nativeElement.querySelector(selector));
await CoreUtils.wait(200);

CoreDom.scrollToElement(this.elementRef.nativeElement, selector, { addYAxis: -10 });
}

}
20 changes: 16 additions & 4 deletions src/core/features/course/components/course-format/course-format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import { CoreDom } from '@singletons/dom';
import { CoreUserTourDirectiveOptions } from '@directives/user-tour';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { CorePlatform } from '@services/platform';
import { CoreBlockSideBlocksComponent } from '@features/block/components/side-blocks/side-blocks';

/**
* Component to display course contents using a certain format. If the format isn't found, use default one.
Expand All @@ -76,6 +77,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
@Input() sections: CoreCourseSectionToDisplay[] = []; // List of course sections.
@Input() initialSectionId?: number; // The section to load first (by ID).
@Input() initialSectionNumber?: number; // The section to load first (by number).
@Input() initialBlockInstanceId?: number; // The instance to focus.
@Input() moduleId?: number; // The module ID to scroll to. Must be inside the initial selected section.
@Input() isGuest?: boolean; // If user is accessing using an ACCESS_GUEST enrolment method.

Expand Down Expand Up @@ -298,16 +300,26 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
// Always load "All sections" to display the section title. If it isn't there just load the section.
this.loaded = true;
this.sectionChanged(sections[0]);
} else if (this.initialSectionId || this.initialSectionNumber) {
} else if (this.initialSectionId || this.initialSectionNumber !== undefined) {
// We have an input indicating the section ID to load. Search the section.
const section = sections.find((section) =>
section.id == this.initialSectionId || (section.section && section.section == this.initialSectionNumber));
section.id == this.initialSectionId ||
(section.section !== undefined && section.section == this.initialSectionNumber));

// Don't load the section if it cannot be viewed by the user.
if (section && this.canViewSection(section)) {
this.loaded = true;
this.sectionChanged(section);
}
} else if (this.initialBlockInstanceId && this.displayBlocks && this.hasBlocks) {
CoreDomUtils.openSideModal({
component: CoreBlockSideBlocksComponent,
componentProps: {
contextLevel: 'course',
instanceId: this.course.id,
initialBlockInstanceId: this.initialBlockInstanceId,
},
});
}

if (!this.loaded) {
Expand Down Expand Up @@ -666,8 +678,8 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
CoreCourse.logView(this.course.id, sectionNumber),
);

let extraParams = sectionNumber ? `&section=${sectionNumber}` : '';
if (firstLoad && sectionNumber) {
let extraParams = sectionNumber !== undefined ? `&section=${sectionNumber}` : '';
if (firstLoad && sectionNumber !== undefined) {
// If course is configured to show all sections in one page, don't include section in URL in first load.
const courseDisplay = 'courseformatoptions' in this.course &&
this.course.courseformatoptions?.find(option => option.name === 'coursedisplay');
Expand Down
3 changes: 2 additions & 1 deletion src/core/features/course/pages/contents/contents.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

<core-loading [hideUntil]="dataLoaded && !updatingData">
<core-course-format [course]="course" [sections]="sections" [initialSectionId]="sectionId" [initialSectionNumber]="sectionNumber"
[moduleId]="moduleId" class="core-course-format-{{course.format}}" *ngIf="dataLoaded && sections" [isGuest]="isGuest">
[initialBlockInstanceId]="blockInstanceId" [moduleId]="moduleId" class="core-course-format-{{course.format}}"
*ngIf="dataLoaded && sections" [isGuest]="isGuest">
</core-course-format>
</core-loading>
</ion-content>
2 changes: 2 additions & 0 deletions src/core/features/course/pages/contents/contents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy, CoreRefreshCon
sections?: CoreCourseSection[];
sectionId?: number;
sectionNumber?: number;
blockInstanceId?: number;
dataLoaded = false;
updatingData = false;
downloadCourseEnabled = false;
Expand Down Expand Up @@ -92,6 +93,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy, CoreRefreshCon

this.sectionId = CoreNavigator.getRouteNumberParam('sectionId');
this.sectionNumber = CoreNavigator.getRouteNumberParam('sectionNumber');
this.blockInstanceId = CoreNavigator.getRouteNumberParam('blockInstanceId');
this.moduleId = CoreNavigator.getRouteNumberParam('moduleId');
this.isGuest = CoreNavigator.getRouteBooleanParam('isGuest');

Expand Down
5 changes: 3 additions & 2 deletions src/core/features/course/pages/index/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
if (data.sectionId) {
this.contentsTab.pageParams.sectionId = data.sectionId;
}
if (data.sectionNumber) {
if (data.sectionNumber !== undefined) {
this.contentsTab.pageParams.sectionNumber = data.sectionNumber;
}

Expand Down Expand Up @@ -162,12 +162,13 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
course: this.course,
sectionId: CoreNavigator.getRouteNumberParam('sectionId'),
sectionNumber: CoreNavigator.getRouteNumberParam('sectionNumber'),
blockInstanceId: CoreNavigator.getRouteNumberParam('blockInstanceId'),
isGuest: this.isGuest,
};

if (this.module) {
this.contentsTab.pageParams.moduleId = this.module.id;
if (!this.contentsTab.pageParams.sectionId && !this.contentsTab.pageParams.sectionNumber) {
if (!this.contentsTab.pageParams.sectionId && this.contentsTab.pageParams.sectionNumber === undefined) {
// No section specified, use module section.
this.contentsTab.pageParams.sectionId = this.module.section;
}
Expand Down
8 changes: 7 additions & 1 deletion src/core/features/courses/services/handlers/course-link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ export class CoreCoursesCourseLinkHandlerService extends CoreContentLinksHandler

if (!isNaN(sectionNumber)) {
pageParams.sectionNumber = sectionNumber;
} else {
const matches = url.match(/#inst(\d+)/);

if (matches && matches[1]) {
pageParams.blockInstanceId = parseInt(matches[1], 10);
}
}

return [{
Expand Down Expand Up @@ -136,7 +142,7 @@ export class CoreCoursesCourseLinkHandlerService extends CoreContentLinksHandler
// Direct access.
const course = await CoreUtils.ignoreErrors(CoreCourses.getUserCourse(courseId), { id: courseId });

CoreCourseHelper.openCourse(course, pageParams);
CoreCourseHelper.openCourse(course, { params: pageParams });
} else {
this.navigateCourseSummary(courseId, pageParams);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ <h3 *ngIf="result.title">
<ion-icon *ngIf="renderedIcon" [name]="renderedIcon" aria-hidden="true"></ion-icon>
<core-mod-icon *ngIf="!renderedIcon && result.module" [modicon]="result.module.iconurl"
[modname]="result.module.name"></core-mod-icon>
<img *ngIf="!renderedIcon && !result.module && result.component" [src]="result.component.iconurl" alt="" class="result-icon"
core-external-content [component]="result.component.name">
<core-format-text [text]="result.title"></core-format-text>
</h3>
<core-format-text *ngIf="result.content && !result.course && !result.user" [text]="result.content"></core-format-text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
--core-global-search-result-title-color: var(--text);
--core-global-search-result-content-color: var(--gray-700);
--core-global-search-result-context-color: var(--gray-600);
--core-global-search-result-icon-size: 16px;
--mod-icon-filter: brightness(0);

h3 {
Expand All @@ -12,7 +13,7 @@
color: var(--core-global-search-result-title-color);

core-mod-icon {
--size: 16px;
--size: var(--core-global-search-result-icon-size);
--filter: var(--mod-icon-filter);

margin-inline-end: var(--spacing-2);
Expand All @@ -22,9 +23,9 @@
background: transparent;
}

ion-icon {
width: 16px;
height: 16px;
ion-icon, .result-icon {
width: var(--core-global-search-result-icon-size);
height: var(--core-global-search-result-icon-size);

margin-inline-end: var(--spacing-2);
}
Expand Down Expand Up @@ -61,6 +62,9 @@
}

.result-context {
display: flex;
justify-items: center;
align-items: center;
color: var(--core-global-search-result-context-color);
margin-top: var(--spacing-2);
font-size: 12px;
Expand Down
29 changes: 21 additions & 8 deletions src/core/features/search/services/global-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export type CoreSearchGlobalSearchResult = {
content?: string;
context?: CoreSearchGlobalSearchResultContext;
module?: CoreSearchGlobalSearchResultModule;
component?: CoreSearchGlobalSearchResultComponent;
course?: CoreCourseListItem;
user?: CoreUserWithAvatar;
};
Expand All @@ -59,6 +60,11 @@ export type CoreSearchGlobalSearchResultModule = {
area: string;
};

export type CoreSearchGlobalSearchResultComponent = {
name: string;
iconurl: string;
};

export type CoreSearchGlobalSearchSearchAreaCategory = {
id: string;
name: string;
Expand Down Expand Up @@ -225,8 +231,8 @@ export class CoreSearchGlobalSearchService {
const user = await CoreUser.getProfile(wsResult.itemid);

result.user = user;
} else if (wsResult.componentname === 'core_course') {
const course = await CoreCourses.getCourse(wsResult.itemid);
} else if (wsResult.componentname === 'core_course' && wsResult.areaname === 'course') {
const course = await CoreCourses.getCourseByField('id', wsResult.itemid);

result.course = course;
} else {
Expand All @@ -237,12 +243,19 @@ export class CoreSearchGlobalSearchService {
};
}

if (wsResult.iconurl && wsResult.componentname.startsWith('mod_')) {
result.module = {
name: wsResult.componentname.substring(4),
iconurl: wsResult.iconurl,
area: wsResult.areaname,
};
if (wsResult.iconurl) {
if (wsResult.componentname.startsWith('mod_')) {
result.module = {
name: wsResult.componentname.substring(4),
iconurl: wsResult.iconurl,
area: wsResult.areaname,
};
} else {
result.component = {
name: wsResult.componentname,
iconurl: wsResult.iconurl,
};
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,16 @@ import { CommonModule } from '@angular/common';
import {
CoreSearchGlobalSearchResultsPageComponent,
} from '@features/search/stories/components/global-search-results-page/global-search-results-page';
import { CoreSearchGlobalSearchResultComponent } from '@features/search/components/global-search-result/global-search-result';
import { CoreSharedModule } from '@/core/shared.module';

@NgModule({
declarations: [
CoreSearchGlobalSearchResultsPageComponent,
CoreSearchGlobalSearchResultComponent,
],
imports: [
CoreSharedModule,
CommonModule,
StorybookModule,
CoreComponentsModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,30 @@ export class CoreSearchGlobalSearchResultsPageComponent {
area: 'post',
},
},
{
id: 6,
url: '',
title: 'Side block',
context: {
courseName: 'Moodle Site',
},
component: {
name: 'block_html',
iconurl: 'https://master.mm.moodledemo.net/theme/image.php?theme=boost&component=core&image=e%2Fanchor',
},
},
{
id: 7,
url: '',
title: 'Course section',
context: {
courseName: 'Course 101',
},
component: {
name: 'core_course',
iconurl: 'https://master.mm.moodledemo.net/theme/image.php?theme=boost&component=core&image=i%2Fsection',
},
},
];

/**
Expand Down
Loading

0 comments on commit 254dcb3

Please sign in to comment.