From c8f7b753a882028b97640f771f77e0273fefde11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Wed, 24 Nov 2021 15:40:43 +0100 Subject: [PATCH] MOBILE-3913 timeline: Search activities on timeline block --- scripts/langindex.json | 1 + .../timeline/components/components.module.ts | 2 + .../timeline/components/events/events.ts | 2 +- .../timeline/addon-block-timeline.html | 16 ++++++- .../timeline/components/timeline/timeline.ts | 23 ++++++++-- src/addons/block/timeline/lang.json | 3 +- .../block/timeline/services/timeline.ts | 46 ++++++++++++------- src/addons/calendar/services/calendar.ts | 3 ++ src/core/components/combobox/combobox.scss | 1 + src/core/directives/auto-focus.ts | 2 +- .../components/side-blocks/side-blocks.scss | 13 ++++++ .../components/side-blocks/side-blocks.ts | 1 + .../components/search-box/search-box.scss | 14 ++++-- .../components/search-box/search-box.ts | 4 +- src/theme/theme.base.scss | 1 + 15 files changed, 101 insertions(+), 31 deletions(-) create mode 100644 src/core/features/block/components/side-blocks/side-blocks.scss diff --git a/scripts/langindex.json b/scripts/langindex.json index 6f9b71abf..a8d9ddc2b 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -73,6 +73,7 @@ "addon.block_timeline.noevents": "block_timeline", "addon.block_timeline.overdue": "block_timeline", "addon.block_timeline.pluginname": "block_timeline", + "addon.block_timeline.searchevents": "block_timeline", "addon.block_timeline.sortbycourses": "block_timeline", "addon.block_timeline.sortbydates": "block_timeline", "addon.blog.blog": "blog", diff --git a/src/addons/block/timeline/components/components.module.ts b/src/addons/block/timeline/components/components.module.ts index f54298186..00c06106d 100644 --- a/src/addons/block/timeline/components/components.module.ts +++ b/src/addons/block/timeline/components/components.module.ts @@ -18,6 +18,7 @@ import { CoreSharedModule } from '@/core/shared.module'; import { AddonBlockTimelineComponent } from './timeline/timeline'; import { AddonBlockTimelineEventsComponent } from './events/events'; +import { CoreSearchComponentsModule } from '@features/search/components/components.module'; @NgModule({ declarations: [ @@ -26,6 +27,7 @@ import { AddonBlockTimelineEventsComponent } from './events/events'; ], imports: [ CoreSharedModule, + CoreSearchComponentsModule, ], exports: [ AddonBlockTimelineComponent, diff --git a/src/addons/block/timeline/components/events/events.ts b/src/addons/block/timeline/components/events/events.ts index b9047ff2b..4e0155873 100644 --- a/src/addons/block/timeline/components/events/events.ts +++ b/src/addons/block/timeline/components/events/events.ts @@ -52,7 +52,7 @@ export class AddonBlockTimelineEventsComponent implements OnChanges { this.showCourse = !this.course; if (changes.events || changes.from || changes.to) { - if (this.events && this.events.length > 0) { + if (this.events) { const filteredEvents = await this.filterEventsByTime(this.from, this.to); this.empty = !filteredEvents || filteredEvents.length <= 0; diff --git a/src/addons/block/timeline/components/timeline/addon-block-timeline.html b/src/addons/block/timeline/components/timeline/addon-block-timeline.html index dbf981cdb..a0774f1d9 100644 --- a/src/addons/block/timeline/components/timeline/addon-block-timeline.html +++ b/src/addons/block/timeline/components/timeline/addon-block-timeline.html @@ -4,7 +4,7 @@ - + @@ -30,6 +30,12 @@ + + + + @@ -42,6 +48,14 @@ + + + + + + { try { if (course) { - const courseEvents = await AddonBlockTimeline.getActionEventsByCourse(course.id, course.canLoadMore); + const courseEvents = + await AddonBlockTimeline.getActionEventsByCourse(course.id, course.canLoadMore, this.searchText); course.events = course.events?.concat(courseEvents.events); course.canLoadMore = courseEvents.canLoadMore; } else { @@ -153,7 +159,7 @@ export class AddonBlockTimelineComponent extends CoreBlockBaseComponent implemen * @return Promise resolved when done. */ protected async fetchMyOverviewTimeline(afterEventId?: number): Promise { - const events = await AddonBlockTimeline.getActionEventsByTimesort(afterEventId); + const events = await AddonBlockTimeline.getActionEventsByTimesort(afterEventId, this.searchText); this.timeline.events = events.events; this.timeline.canLoadMore = events.canLoadMore; @@ -174,7 +180,7 @@ export class AddonBlockTimelineComponent extends CoreBlockBaseComponent implemen if (this.timelineCourses.courses.length > 0) { this.courseIds = this.timelineCourses.courses.map((course) => course.id); - const courseEvents = await AddonBlockTimeline.getActionEventsByCourses(this.courseIds); + const courseEvents = await AddonBlockTimeline.getActionEventsByCourses(this.courseIds, this.searchText); this.timelineCourses.courses = this.timelineCourses.courses.filter((course) => { if (courseEvents[course.id].events.length == 0) { @@ -243,6 +249,17 @@ export class AddonBlockTimelineComponent extends CoreBlockBaseComponent implemen } } + /** + * Search text changed. + * + * @param searchValue Search value + */ + searchTextChanged(searchValue = ''): void { + this.searchText = searchValue || ''; + + this.fetchContent(); + } + } export type AddonBlockTimelineCourse = CoreEnrolledCourseDataWithOptions & { diff --git a/src/addons/block/timeline/lang.json b/src/addons/block/timeline/lang.json index fbd482f47..10c8b2859 100644 --- a/src/addons/block/timeline/lang.json +++ b/src/addons/block/timeline/lang.json @@ -8,6 +8,7 @@ "noevents": "No upcoming activities due", "overdue": "Overdue", "pluginname": "Timeline", + "searchevents": "Search by activity type or name", "sortbycourses": "Sort by courses", "sortbydates": "Sort by dates" -} \ No newline at end of file +} diff --git a/src/addons/block/timeline/services/timeline.ts b/src/addons/block/timeline/services/timeline.ts index c2e646f00..0dbddde01 100644 --- a/src/addons/block/timeline/services/timeline.ts +++ b/src/addons/block/timeline/services/timeline.ts @@ -26,7 +26,6 @@ import { import moment from 'moment'; import { makeSingleton } from '@singletons'; import { CoreSiteWSPreSets } from '@classes/site'; -import { CoreError } from '@classes/errors/error'; // Cache key was maintained from block myoverview when blocks were splitted. const ROOT_CACHE_KEY = 'myoverview:'; @@ -45,12 +44,14 @@ export class AddonBlockTimelineProvider { * * @param courseId Only events in this course. * @param afterEventId The last seen event id. + * @param searchValue The value a user wishes to search against. * @param siteId Site ID. If not defined, use current site. * @return Promise resolved when the info is retrieved. */ async getActionEventsByCourse( courseId: number, afterEventId?: number, + searchValue = '', siteId?: string, ): Promise<{ events: AddonCalendarEvent[]; canLoadMore?: number }> { const site = await CoreSites.getSite(siteId); @@ -70,17 +71,18 @@ export class AddonBlockTimelineProvider { cacheKey: this.getActionEventsByCourseCacheKey(courseId), }; + if (searchValue != '') { + data.searchvalue = searchValue; + preSets.getFromCache = false; + } + const courseEvents = await site.read( 'core_calendar_get_action_events_by_course', data, preSets, ); - if (courseEvents && courseEvents.events) { - return this.treatCourseEvents(courseEvents, time); - } - - throw new CoreError('No events returned on core_calendar_get_action_events_by_course.'); + return this.treatCourseEvents(courseEvents, time); } /** @@ -98,10 +100,12 @@ export class AddonBlockTimelineProvider { * * @param courseIds Course IDs. * @param siteId Site ID. If not defined, use current site. + * @param searchValue The value a user wishes to search against. * @return Promise resolved when the info is retrieved. */ async getActionEventsByCourses( courseIds: number[], + searchValue = '', siteId?: string, ): Promise<{[courseId: string]: { events: AddonCalendarEvent[]; canLoadMore?: number } }> { const site = await CoreSites.getSite(siteId); @@ -117,6 +121,11 @@ export class AddonBlockTimelineProvider { cacheKey: this.getActionEventsByCoursesCacheKey(), }; + if (searchValue != '') { + data.searchvalue = searchValue; + preSets.getFromCache = false; + } + const events = await site.read( 'core_calendar_get_action_events_by_courses', data, @@ -145,11 +154,13 @@ export class AddonBlockTimelineProvider { * Get calendar action events based on the timesort value. * * @param afterEventId The last seen event id. + * @param searchValue The value a user wishes to search against. * @param siteId Site ID. If not defined, use current site. * @return Promise resolved when the info is retrieved. */ async getActionEventsByTimesort( afterEventId?: number, + searchValue = '', siteId?: string, ): Promise<{ events: AddonCalendarEvent[]; canLoadMore?: number }> { const site = await CoreSites.getSite(siteId); @@ -171,25 +182,26 @@ export class AddonBlockTimelineProvider { uniqueCacheKey: true, }; + if (searchValue != '') { + data.searchvalue = searchValue; + preSets.getFromCache = false; + } + const result = await site.read( 'core_calendar_get_action_events_by_timesort', data, preSets, ); - if (result && result.events) { - const canLoadMore = result.events.length >= limitnum ? result.lastid : undefined; + const canLoadMore = result.events.length >= limitnum ? result.lastid : undefined; - // Filter events by time in case it uses cache. - const events = result.events.filter((element) => element.timesort >= timesortfrom); + // Filter events by time in case it uses cache. + const events = result.events.filter((element) => element.timesort >= timesortfrom); - return { - events, - canLoadMore, - }; - } - - throw new CoreError('No events returned on core_calendar_get_action_events_by_timesort.'); + return { + events, + canLoadMore, + }; } /** diff --git a/src/addons/calendar/services/calendar.ts b/src/addons/calendar/services/calendar.ts index 815254572..17c60c3bf 100644 --- a/src/addons/calendar/services/calendar.ts +++ b/src/addons/calendar/services/calendar.ts @@ -1785,6 +1785,7 @@ export type AddonCalendarGetActionEventsByCoursesWSParams = { timesortfrom?: number; // Time sort from. timesortto?: number; // Time sort to. limitnum?: number; // Limit number. + searchvalue?: string; // The value a user wishes to search against. }; /** @@ -1804,6 +1805,7 @@ export type AddonCalendarGetActionEventsByCourseWSParams = { timesortto?: number; // Time sort to. aftereventid?: number; // The last seen event id. limitnum?: number; // Limit number. + searchvalue?: string; // The value a user wishes to search against. }; /** @@ -1816,6 +1818,7 @@ export type AddonCalendarGetActionEventsByTimesortWSParams = { limitnum?: number; // Limit number. limittononsuspendedevents?: boolean; // Limit the events to courses the user is not suspended in. userid?: number; // The user id. + searchvalue?: string; // The value a user wishes to search against. }; /** diff --git a/src/core/components/combobox/combobox.scss b/src/core/components/combobox/combobox.scss index 110a97d5f..566c1274e 100644 --- a/src/core/components/combobox/combobox.scss +++ b/src/core/components/combobox/combobox.scss @@ -2,6 +2,7 @@ :host { max-width: 100%; + display: block; @include margin-horizontal(var(--ion-safe-area-left), var(--ion-safe-area-right)); ion-select, diff --git a/src/core/directives/auto-focus.ts b/src/core/directives/auto-focus.ts index 190f90d19..6aeb4d829 100644 --- a/src/core/directives/auto-focus.ts +++ b/src/core/directives/auto-focus.ts @@ -52,7 +52,7 @@ export class CoreAutoFocusDirective implements AfterViewInit { /** * Function to focus the element. * - * @param retries Internal param to stop retrying then 0. + * @param retries Internal param to stop retrying on 0. */ protected setFocus(retries = 10): void { if (retries == 0) { diff --git a/src/core/features/block/components/side-blocks/side-blocks.scss b/src/core/features/block/components/side-blocks/side-blocks.scss new file mode 100644 index 000000000..fc2f730f2 --- /dev/null +++ b/src/core/features/block/components/side-blocks/side-blocks.scss @@ -0,0 +1,13 @@ +@import "~theme/globals"; + +:host ::ng-deep core-block { + @include media-breakpoint-up(md) { + .ion-hide-md-down { + display: none !important; + } + + .ion-hide-md-up { + display: block !important; + } + } +} diff --git a/src/core/features/block/components/side-blocks/side-blocks.ts b/src/core/features/block/components/side-blocks/side-blocks.ts index eefc8f527..0f817a4a0 100644 --- a/src/core/features/block/components/side-blocks/side-blocks.ts +++ b/src/core/features/block/components/side-blocks/side-blocks.ts @@ -28,6 +28,7 @@ import { CoreCoursesDashboard } from '@features/courses/services/dashboard'; @Component({ selector: 'core-block-side-blocks', templateUrl: 'side-blocks.html', + styleUrls: ['side-blocks.scss'], }) export class CoreBlockSideBlocksComponent implements OnInit { diff --git a/src/core/features/search/components/search-box/search-box.scss b/src/core/features/search/components/search-box/search-box.scss index e3d84473a..4848d54b9 100644 --- a/src/core/features/search/components/search-box/search-box.scss +++ b/src/core/features/search/components/search-box/search-box.scss @@ -1,5 +1,5 @@ :host { - height: 73px; + min-height: 61px; display: block; position: relative; @@ -8,13 +8,12 @@ left: 0; right: 0; z-index: 4; - margin-top: 10px; - margin-bottom: 10px; + margin-top: 8px; + margin-bottom: 8px; } ion-button.button { - margin-left: 0; - margin-right: 0; + margin: 0; } .core-search-history { @@ -37,4 +36,9 @@ padding-left: 0; padding-right: 0; } + + + ion-item { + --min-height: var(--a11y-min-target-size); + } } diff --git a/src/core/features/search/components/search-box/search-box.ts b/src/core/features/search/components/search-box/search-box.ts index 5270c4f1b..275647525 100644 --- a/src/core/features/search/components/search-box/search-box.ts +++ b/src/core/features/search/components/search-box/search-box.ts @@ -41,8 +41,8 @@ export class CoreSearchBoxComponent implements OnInit { @Input() searchLabel?: string; // Label to be used on action button. @Input() placeholder?: string; // Placeholder text for search text input. @Input() autocorrect = '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() spellcheck: string | boolean = true; // Enables/disable Spellchecker on search text input. + @Input() autoFocus: string | boolean = false; // Enables/disable Autofocus when entering view. @Input() lengthCheck = 3; // Check value length before submit. If 0, any string will be submitted. @Input() showClear = true; // Show/hide clear button. @Input() disabled = false; // Disables the input text. diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index 45f9de0ab..c4c8a8d00 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -172,6 +172,7 @@ ion-app.ios ion-header h2 { .item.ion-text-wrap ion-label core-format-text .core-format-text-content > *, .fake-ion-item.ion-text-wrap core-format-text .core-format-text-content > * { white-space: normal; + overflow: inherit; } .item.ion-text-wrap ion-label {