From 0c69cbddad78473543e47ca333b91c236a14cf53 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Tue, 12 Dec 2017 15:28:01 +0100 Subject: [PATCH] MOBILE-2302 courses: Implement search courses --- src/assets/img/icons/paypal.png | Bin 0 -> 7181 bytes src/components/components.module.ts | 7 +- src/components/search-box/search-box.html | 10 +++ src/components/search-box/search-box.scss | 6 ++ src/components/search-box/search-box.ts | 67 ++++++++++++++ .../courses/components/components.module.ts | 7 +- .../course-list-item/course-list-item.html | 15 ++++ .../course-list-item/course-list-item.scss | 6 ++ .../course-list-item/course-list-item.ts | 82 ++++++++++++++++++ src/core/courses/pages/search/search.html | 18 ++++ .../courses/pages/search/search.module.ts | 33 +++++++ src/core/courses/pages/search/search.scss | 3 + src/core/courses/pages/search/search.ts | 82 ++++++++++++++++++ src/core/login/pages/site/site.html | 2 +- src/directives/auto-focus.ts | 25 +++++- 15 files changed, 354 insertions(+), 9 deletions(-) create mode 100644 src/assets/img/icons/paypal.png create mode 100644 src/components/search-box/search-box.html create mode 100644 src/components/search-box/search-box.scss create mode 100644 src/components/search-box/search-box.ts create mode 100644 src/core/courses/components/course-list-item/course-list-item.html create mode 100644 src/core/courses/components/course-list-item/course-list-item.scss create mode 100644 src/core/courses/components/course-list-item/course-list-item.ts create mode 100644 src/core/courses/pages/search/search.html create mode 100644 src/core/courses/pages/search/search.module.ts create mode 100644 src/core/courses/pages/search/search.scss create mode 100644 src/core/courses/pages/search/search.ts diff --git a/src/assets/img/icons/paypal.png b/src/assets/img/icons/paypal.png new file mode 100644 index 0000000000000000000000000000000000000000..058f6e4cb52350ea214da2e444f36ef56b7eecbb GIT binary patch literal 7181 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 literal 0 HcmV?d00001 diff --git a/src/components/components.module.ts b/src/components/components.module.ts index ab0b08f84..c814ce0ec 100644 --- a/src/components/components.module.ts +++ b/src/components/components.module.ts @@ -23,6 +23,7 @@ import { CoreShowPasswordComponent } from './show-password/show-password'; import { CoreIframeComponent } from './iframe/iframe'; import { CoreProgressBarComponent } from './progress-bar/progress-bar'; import { CoreEmptyBoxComponent } from './empty-box/empty-box'; +import { CoreSearchBoxComponent } from './search-box/search-box'; @NgModule({ declarations: [ @@ -32,7 +33,8 @@ import { CoreEmptyBoxComponent } from './empty-box/empty-box'; CoreShowPasswordComponent, CoreIframeComponent, CoreProgressBarComponent, - CoreEmptyBoxComponent + CoreEmptyBoxComponent, + CoreSearchBoxComponent ], imports: [ IonicModule, @@ -46,7 +48,8 @@ import { CoreEmptyBoxComponent } from './empty-box/empty-box'; CoreShowPasswordComponent, CoreIframeComponent, CoreProgressBarComponent, - CoreEmptyBoxComponent + CoreEmptyBoxComponent, + CoreSearchBoxComponent ] }) export class CoreComponentsModule {} diff --git a/src/components/search-box/search-box.html b/src/components/search-box/search-box.html new file mode 100644 index 000000000..bc0b8a5da --- /dev/null +++ b/src/components/search-box/search-box.html @@ -0,0 +1,10 @@ + +
+ + + + +
+
diff --git a/src/components/search-box/search-box.scss b/src/components/search-box/search-box.scss new file mode 100644 index 000000000..d451c7559 --- /dev/null +++ b/src/components/search-box/search-box.scss @@ -0,0 +1,6 @@ +core-search-box { + .button.item-button[icon-only] { + margin: 0; + padding: ($content-padding / 2) $content-padding; + } +} diff --git a/src/components/search-box/search-box.ts b/src/components/search-box/search-box.ts new file mode 100644 index 000000000..a6e70b3f9 --- /dev/null +++ b/src/components/search-box/search-box.ts @@ -0,0 +1,67 @@ +// (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, OnInit } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { CoreUtilsProvider } from '../../providers/utils/utils'; + +/** + * Component to display a "search box". + * + * @description + * This component will display a standalone search box with its search button in order to have a better UX. + * + * Example usage: + * + */ +@Component({ + selector: 'core-search-box', + templateUrl: 'search-box.html' +}) +export class CoreSearchBoxComponent implements OnInit { + @Input() initialValue?: string = ''; // Initial value for search text. + @Input() searchLabel?: string ; // Label to be used on action button. + @Input() placeholder?: string; // Placeholder text for search text input. + @Input() autocorrect?: string = 'on'; // Enables/disable Autocorrection on search text input. + @Input() spellcheck?: string|boolean = true; // Enables/disable Spellchecker on search text input. + @Input() autoFocus?: string|boolean; // Enables/disable Autofocus when entering view. + @Input() lengthCheck?: number = 3; // Check value length before submit. If 0, any string will be submitted. + @Output() onSubmit: EventEmitter; // Send data when submitting the search form. + + constructor(private translate: TranslateService, private utils: CoreUtilsProvider) { + this.onSubmit = new EventEmitter(); + } + + ngOnInit() { + this.searchLabel = this.searchLabel || this.translate.instant('core.search'); + this.placeholder = this.placeholder || this.translate.instant('core.search'); + this.spellcheck = this.utils.isTrueOrOne(this.spellcheck); + } + + /** + * Form submitted. + * + * @param {string} value Entered value. + */ + submitForm(value: string) { + if (value.length < this.lengthCheck) { + // The view should handle this case, but we check it here too just in case. + return; + } + + this.onSubmit.emit(value); + } + +} diff --git a/src/core/courses/components/components.module.ts b/src/core/courses/components/components.module.ts index f6a66ff29..6cddb3414 100644 --- a/src/core/courses/components/components.module.ts +++ b/src/core/courses/components/components.module.ts @@ -19,10 +19,12 @@ import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '../../../components/components.module'; import { CoreDirectivesModule } from '../../../directives/directives.module'; import { CoreCoursesCourseProgressComponent } from '../components/course-progress/course-progress'; +import { CoreCoursesCourseListItemComponent } from '../components/course-list-item/course-list-item'; @NgModule({ declarations: [ - CoreCoursesCourseProgressComponent + CoreCoursesCourseProgressComponent, + CoreCoursesCourseListItemComponent ], imports: [ CommonModule, @@ -34,7 +36,8 @@ import { CoreCoursesCourseProgressComponent } from '../components/course-progres providers: [ ], exports: [ - CoreCoursesCourseProgressComponent + CoreCoursesCourseProgressComponent, + CoreCoursesCourseListItemComponent ] }) export class CoreCoursesComponentsModule {} diff --git a/src/core/courses/components/course-list-item/course-list-item.html b/src/core/courses/components/course-list-item/course-list-item.html new file mode 100644 index 000000000..1c105977b --- /dev/null +++ b/src/core/courses/components/course-list-item/course-list-item.html @@ -0,0 +1,15 @@ + + +

+
+ + + + + + + + + +
+
diff --git a/src/core/courses/components/course-list-item/course-list-item.scss b/src/core/courses/components/course-list-item/course-list-item.scss new file mode 100644 index 000000000..2487cb6e9 --- /dev/null +++ b/src/core/courses/components/course-list-item/course-list-item.scss @@ -0,0 +1,6 @@ +core-courses-course-list-item { + .mm-course-enrollment-img { + max-width: 16px; + max-height: 16px; + } +} diff --git a/src/core/courses/components/course-list-item/course-list-item.ts b/src/core/courses/components/course-list-item/course-list-item.ts new file mode 100644 index 000000000..9e67707f1 --- /dev/null +++ b/src/core/courses/components/course-list-item/course-list-item.ts @@ -0,0 +1,82 @@ +// (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, OnInit } from '@angular/core'; +import { NavController } from 'ionic-angular'; +import { TranslateService } from '@ngx-translate/core'; +import { CoreCoursesProvider } from '../../providers/courses'; + +/** + * This directive is meant to display an item for a list of courses. + * + * Example usage: + * + * + */ +@Component({ + selector: 'core-courses-course-list-item', + templateUrl: 'course-list-item.html' +}) +export class CoreCoursesCourseListItemComponent implements OnInit { + @Input() course: any; // The course to render. + + constructor(private navCtrl: NavController, private translate: TranslateService, private coursesProvider: CoreCoursesProvider) {} + + /** + * Component being initialized. + */ + ngOnInit() { + // Check if the user is enrolled in the course. + return this.coursesProvider.getUserCourse(this.course.id).then(() => { + this.course.isEnrolled = true; + }).catch(() => { + this.course.isEnrolled = false; + this.course.enrollment = []; + + this.course.enrollmentmethods.forEach((instance) => { + if (instance === 'self') { + this.course.enrollment.push({ + name: this.translate.instant('core.courses.selfenrolment'), + icon: 'unlock' + }); + } else if (instance === 'guest') { + this.course.enrollment.push({ + name: this.translate.instant('core.courses.allowguests'), + icon: 'person' + }); + } else if (instance === 'paypal') { + this.course.enrollment.push({ + name: this.translate.instant('core.courses.paypalaccepted'), + img: 'assets/img/icons/paypal.png' + }); + } + }); + + if (this.course.enrollment.length == 0) { + this.course.enrollment.push({ + name: this.translate.instant('core.courses.notenrollable'), + icon: 'lock' + }); + } + }); + } + + /** + * Open a course. + */ + openCourse(course) { + this.navCtrl.push('CoreCoursesCoursePreviewPage', {course: course}); + } + +} diff --git a/src/core/courses/pages/search/search.html b/src/core/courses/pages/search/search.html new file mode 100644 index 000000000..4df0a3845 --- /dev/null +++ b/src/core/courses/pages/search/search.html @@ -0,0 +1,18 @@ + + + {{ 'core.courses.searchcourses' | translate }} + + + + + +
+ {{ 'core.courses.totalcoursesearchresults' | translate:{$a: total} }} + + + + + +
+
+ diff --git a/src/core/courses/pages/search/search.module.ts b/src/core/courses/pages/search/search.module.ts new file mode 100644 index 000000000..c74212c21 --- /dev/null +++ b/src/core/courses/pages/search/search.module.ts @@ -0,0 +1,33 @@ +// (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 { NgModule } from '@angular/core'; +import { IonicPageModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { CoreCoursesSearchPage } from './search'; +import { CoreComponentsModule } from '../../../../components/components.module'; +import { CoreCoursesComponentsModule } from '../../components/components.module'; + +@NgModule({ + declarations: [ + CoreCoursesSearchPage, + ], + imports: [ + CoreComponentsModule, + CoreCoursesComponentsModule, + IonicPageModule.forChild(CoreCoursesSearchPage), + TranslateModule.forChild() + ], +}) +export class CoreCoursesSearchPageModule {} diff --git a/src/core/courses/pages/search/search.scss b/src/core/courses/pages/search/search.scss new file mode 100644 index 000000000..1bf3fe798 --- /dev/null +++ b/src/core/courses/pages/search/search.scss @@ -0,0 +1,3 @@ +page-core-courses-search { + +} diff --git a/src/core/courses/pages/search/search.ts b/src/core/courses/pages/search/search.ts new file mode 100644 index 000000000..654b36cb5 --- /dev/null +++ b/src/core/courses/pages/search/search.ts @@ -0,0 +1,82 @@ +// (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 } from '@angular/core'; +import { IonicPage } from 'ionic-angular'; +import { CoreDomUtilsProvider } from '../../../../providers/utils/dom'; +import { CoreCoursesProvider } from '../../providers/courses'; + +/** + * Page that allows searching for courses. + */ +@IonicPage() +@Component({ + selector: 'page-core-courses-search', + templateUrl: 'search.html', +}) +export class CoreCoursesSearchPage { + total = 0; + courses: any[]; + canLoadMore: boolean; + + protected page = 0; + protected currentSearch = ''; + + constructor(private domUtils: CoreDomUtilsProvider, private coursesProvider: CoreCoursesProvider) {} + + /** + * Search a new text. + * + * @param {string} text The text to search. + */ + search(text: string) { + this.currentSearch = text; + this.courses = undefined; + this.page = 0; + + let modal = this.domUtils.showModalLoading('core.searching', true); + this.searchCourses().finally(() => { + modal.dismiss(); + }); + } + + /** + * Load more results. + */ + loadMoreResults(infiniteScroll) { + this.searchCourses().finally(() => { + infiniteScroll.complete(); + }); + } + + /** + * Search courses or load the next page of current search. + */ + protected searchCourses() { + return this.coursesProvider.search(this.currentSearch, this.page).then((response) => { + if (this.page === 0) { + this.courses = response.courses; + } else { + this.courses = this.courses.concat(response.courses); + } + this.total = response.total; + + this.page++; + this.canLoadMore = this.courses.length < this.total; + }).catch((error) => { + this.canLoadMore = false; + this.domUtils.showErrorModalDefault(error, 'core.courses.errorsearching', true); + }); + } +} diff --git a/src/core/login/pages/site/site.html b/src/core/login/pages/site/site.html index d8f15beac..dc5c1dda1 100644 --- a/src/core/login/pages/site/site.html +++ b/src/core/login/pages/site/site.html @@ -19,7 +19,7 @@

{{ 'core.login.newsitedescription' | translate }}

- +
diff --git a/src/directives/auto-focus.ts b/src/directives/auto-focus.ts index 5cded27fa..9b41e7a3c 100644 --- a/src/directives/auto-focus.ts +++ b/src/directives/auto-focus.ts @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Directive, Input, AfterViewInit, ElementRef } from '@angular/core'; +import { Directive, Input, OnInit, ElementRef } from '@angular/core'; +import { NavController } from 'ionic-angular'; import { CoreDomUtilsProvider } from '../providers/utils/dom'; import { CoreUtilsProvider } from '../providers/utils/utils'; @@ -24,19 +25,35 @@ import { CoreUtilsProvider } from '../providers/utils/utils'; @Directive({ selector: '[core-auto-focus]' }) -export class CoreAutoFocusDirective implements AfterViewInit { +export class CoreAutoFocusDirective implements OnInit { @Input('core-auto-focus') coreAutoFocus: boolean|string = true; protected element: HTMLElement; - constructor(element: ElementRef, private domUtils: CoreDomUtilsProvider, private utils: CoreUtilsProvider) { + constructor(element: ElementRef, private domUtils: CoreDomUtilsProvider, private utils: CoreUtilsProvider, + private navCtrl: NavController) { this.element = element.nativeElement || element; } + /** + * Component being initialized. + */ + ngOnInit() { + if (this.navCtrl.isTransitioning()) { + // Navigating to a new page. Wait for the transition to be over. + let subscription = this.navCtrl.viewDidEnter.subscribe(() => { + this.autoFocus(); + subscription.unsubscribe(); + }); + } else { + this.autoFocus(); + } + } + /** * Function after the view is initialized. */ - ngAfterViewInit() { + protected autoFocus() { const autoFocus = this.utils.isTrueOrOne(this.coreAutoFocus); if (autoFocus) { // If it's a ion-input or ion-textarea, search the right input to use.