diff --git a/src/addon/messages/pages/search/search.html b/src/addon/messages/pages/search/search.html index 77dd31713..86e701fe4 100644 --- a/src/addon/messages/pages/search/search.html +++ b/src/addon/messages/pages/search/search.html @@ -33,7 +33,7 @@

- +

@@ -41,7 +41,7 @@

{{ 'addon.messages.you' | translate }} - +

diff --git a/src/addon/messages/pages/search/search.ts b/src/addon/messages/pages/search/search.ts index 892b26c28..14d5b6ab1 100644 --- a/src/addon/messages/pages/search/search.ts +++ b/src/addon/messages/pages/search/search.ts @@ -175,17 +175,20 @@ export class AddonMessagesSearchPage implements OnDestroy { if (!loadMore || loadMore == 'contacts') { this.contacts.results.push(...newContacts); this.contacts.canLoadMore = canLoadMoreContacts; + this.setHighlight(newContacts, true); } if (!loadMore || loadMore == 'noncontacts') { this.nonContacts.results.push(...newNonContacts); this.nonContacts.canLoadMore = canLoadMoreNonContacts; + this.setHighlight(newNonContacts, true); } if (!loadMore || loadMore == 'messages') { this.messages.results.push(...newMessages); this.messages.canLoadMore = canLoadMoreMessages; this.messages.loadMoreError = false; + this.setHighlight(newMessages, false); } if (!loadMore) { @@ -238,6 +241,19 @@ export class AddonMessagesSearchPage implements OnDestroy { } } + /** + * Set the highlight values for each entry. + * + * @param {any[]} results Results to highlight. + * @param {boolean} isUser Whether the results are from a user search or from a message search. + */ + setHighlight(results: any[], isUser: boolean): void { + results.forEach((result) => { + result.highlightName = isUser ? this.query : undefined; + result.highlightMessage = !isUser ? this.query : undefined; + }); + } + /** * Component destroyed. */ diff --git a/src/app/app.scss b/src/app/app.scss index f9c07d4f9..9c1b3803b 100644 --- a/src/app/app.scss +++ b/src/app/app.scss @@ -1050,6 +1050,11 @@ ion-modal, contain: size layout style; } +// Highlight text. +.matchtext { + background-color: lighten($blue, 40%); +} + // Styles for desktop apps only. ion-app.platform-desktop { video::-webkit-media-text-track-display { diff --git a/src/directives/format-text.ts b/src/directives/format-text.ts index d840c8a84..df0a5aeb1 100644 --- a/src/directives/format-text.ts +++ b/src/directives/format-text.ts @@ -55,6 +55,7 @@ export class CoreFormatTextDirective implements OnChanges { // If you want to avoid this use class="inline" at the same time to use display: inline-block. @Input() fullOnClick?: boolean | string; // Whether it should open a new page with the full contents on click. @Input() fullTitle?: string; // Title to use in full view. Defaults to "Description". + @Input() highlight?: string; // Text to highlight. @Output() afterRender?: EventEmitter; // Called when the data is rendered. protected element: HTMLElement; @@ -348,7 +349,7 @@ export class CoreFormatTextDirective implements OnChanges { // Apply format text function. return this.textUtils.formatText(this.text, this.utils.isTrueOrOne(this.clean), - this.utils.isTrueOrOne(this.singleLine)); + this.utils.isTrueOrOne(this.singleLine), undefined, this.highlight); }).then((formatted) => { const div = document.createElement('div'), canTreatVimeo = site && site.isVersionGreaterEqualThan(['3.3.4', '3.4']); diff --git a/src/providers/utils/text.ts b/src/providers/utils/text.ts index 9bef61b2f..5bdb70ca4 100644 --- a/src/providers/utils/text.ts +++ b/src/providers/utils/text.ts @@ -396,9 +396,10 @@ export class CoreTextUtilsProvider { * @param {boolean} [clean] Whether HTML tags should be removed. * @param {boolean} [singleLine] Whether new lines should be removed. Only valid if clean is true. * @param {number} [shortenLength] Number of characters to shorten the text. + * @param {number} [highlight] Text to highlight. * @return {Promise} Promise resolved with the formatted text. */ - formatText(text: string, clean?: boolean, singleLine?: boolean, shortenLength?: number): Promise { + formatText(text: string, clean?: boolean, singleLine?: boolean, shortenLength?: number, highlight?: string): Promise { return this.treatMultilangTags(text).then((formatted) => { if (clean) { formatted = this.cleanTags(formatted, singleLine); @@ -406,6 +407,9 @@ export class CoreTextUtilsProvider { if (shortenLength > 0) { formatted = this.shortenText(formatted, shortenLength); } + if (highlight) { + formatted = this.highlightText(formatted, highlight); + } return formatted; }); @@ -452,6 +456,25 @@ export class CoreTextUtilsProvider { return /<[a-z][\s\S]*>/i.test(text); } + /** + * Highlight all occurrences of a certain text inside another text. It will add some HTML code to highlight it. + * + * @param {string} text Full text. + * @param {string} searchText Text to search and highlight. + * @return {string} Highlighted text. + */ + highlightText(text: string, searchText: string): string { + if (!text || typeof text != 'string') { + return ''; + } else if (!searchText) { + return text; + } + + const regex = new RegExp('(' + searchText + ')', 'gi'); + + return text.replace(regex, '$1'); + } + /** * Check if HTML content is blank. *