MOBILE-2963 message: Highlight search results

main
Dani Palou 2019-04-05 13:17:54 +02:00
parent af25634bd2
commit 4490dc94c8
5 changed files with 49 additions and 4 deletions

View File

@ -33,7 +33,7 @@
<a ion-item text-wrap *ngFor="let result of item.results" [title]="result.fullname" (click)="openConversation(result)" [class.core-split-item-selected]="result == selectedResult" class="addon-message-discussion"> <a ion-item text-wrap *ngFor="let result of item.results" [title]="result.fullname" (click)="openConversation(result)" [class.core-split-item-selected]="result == selectedResult" class="addon-message-discussion">
<ion-avatar item-start core-user-avatar [user]="result" [checkOnline]="true" [linkProfile]="false"></ion-avatar> <ion-avatar item-start core-user-avatar [user]="result" [checkOnline]="true" [linkProfile]="false"></ion-avatar>
<h2> <h2>
<core-format-text [text]="result.fullname"></core-format-text> <core-format-text [text]="result.fullname" [highlight]="result.highlightName"></core-format-text>
<core-icon name="fa-ban" *ngIf="result.isblocked" [label]="'addon.messages.contactblocked' | translate"></core-icon> <core-icon name="fa-ban" *ngIf="result.isblocked" [label]="'addon.messages.contactblocked' | translate"></core-icon>
</h2> </h2>
<ion-note *ngIf="result.lastmessagedate > 0"> <ion-note *ngIf="result.lastmessagedate > 0">
@ -41,7 +41,7 @@
</ion-note> </ion-note>
<p class="addon-message-last-message"> <p class="addon-message-last-message">
<span *ngIf="result.sentfromcurrentuser" class="addon-message-last-message-user">{{ 'addon.messages.you' | translate }}</span> <span *ngIf="result.sentfromcurrentuser" class="addon-message-last-message-user">{{ 'addon.messages.you' | translate }}</span>
<core-format-text clean="true" singleLine="true" [text]="result.lastmessage" class="addon-message-last-message-text"></core-format-text> <core-format-text clean="true" singleLine="true" [text]="result.lastmessage" [highlight]="result.highlightMessage" class="addon-message-last-message-text"></core-format-text>
</p> </p>
</a> </a>

View File

@ -175,17 +175,20 @@ export class AddonMessagesSearchPage implements OnDestroy {
if (!loadMore || loadMore == 'contacts') { if (!loadMore || loadMore == 'contacts') {
this.contacts.results.push(...newContacts); this.contacts.results.push(...newContacts);
this.contacts.canLoadMore = canLoadMoreContacts; this.contacts.canLoadMore = canLoadMoreContacts;
this.setHighlight(newContacts, true);
} }
if (!loadMore || loadMore == 'noncontacts') { if (!loadMore || loadMore == 'noncontacts') {
this.nonContacts.results.push(...newNonContacts); this.nonContacts.results.push(...newNonContacts);
this.nonContacts.canLoadMore = canLoadMoreNonContacts; this.nonContacts.canLoadMore = canLoadMoreNonContacts;
this.setHighlight(newNonContacts, true);
} }
if (!loadMore || loadMore == 'messages') { if (!loadMore || loadMore == 'messages') {
this.messages.results.push(...newMessages); this.messages.results.push(...newMessages);
this.messages.canLoadMore = canLoadMoreMessages; this.messages.canLoadMore = canLoadMoreMessages;
this.messages.loadMoreError = false; this.messages.loadMoreError = false;
this.setHighlight(newMessages, false);
} }
if (!loadMore) { 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. * Component destroyed.
*/ */

View File

@ -1050,6 +1050,11 @@ ion-modal,
contain: size layout style; contain: size layout style;
} }
// Highlight text.
.matchtext {
background-color: lighten($blue, 40%);
}
// Styles for desktop apps only. // Styles for desktop apps only.
ion-app.platform-desktop { ion-app.platform-desktop {
video::-webkit-media-text-track-display { video::-webkit-media-text-track-display {

View File

@ -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. // 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() 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() fullTitle?: string; // Title to use in full view. Defaults to "Description".
@Input() highlight?: string; // Text to highlight.
@Output() afterRender?: EventEmitter<any>; // Called when the data is rendered. @Output() afterRender?: EventEmitter<any>; // Called when the data is rendered.
protected element: HTMLElement; protected element: HTMLElement;
@ -348,7 +349,7 @@ export class CoreFormatTextDirective implements OnChanges {
// Apply format text function. // Apply format text function.
return this.textUtils.formatText(this.text, this.utils.isTrueOrOne(this.clean), 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) => { }).then((formatted) => {
const div = document.createElement('div'), const div = document.createElement('div'),
canTreatVimeo = site && site.isVersionGreaterEqualThan(['3.3.4', '3.4']); canTreatVimeo = site && site.isVersionGreaterEqualThan(['3.3.4', '3.4']);

View File

@ -396,9 +396,10 @@ export class CoreTextUtilsProvider {
* @param {boolean} [clean] Whether HTML tags should be removed. * @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 {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} [shortenLength] Number of characters to shorten the text.
* @param {number} [highlight] Text to highlight.
* @return {Promise<string>} Promise resolved with the formatted text. * @return {Promise<string>} Promise resolved with the formatted text.
*/ */
formatText(text: string, clean?: boolean, singleLine?: boolean, shortenLength?: number): Promise<string> { formatText(text: string, clean?: boolean, singleLine?: boolean, shortenLength?: number, highlight?: string): Promise<string> {
return this.treatMultilangTags(text).then((formatted) => { return this.treatMultilangTags(text).then((formatted) => {
if (clean) { if (clean) {
formatted = this.cleanTags(formatted, singleLine); formatted = this.cleanTags(formatted, singleLine);
@ -406,6 +407,9 @@ export class CoreTextUtilsProvider {
if (shortenLength > 0) { if (shortenLength > 0) {
formatted = this.shortenText(formatted, shortenLength); formatted = this.shortenText(formatted, shortenLength);
} }
if (highlight) {
formatted = this.highlightText(formatted, highlight);
}
return formatted; return formatted;
}); });
@ -452,6 +456,25 @@ export class CoreTextUtilsProvider {
return /<[a-z][\s\S]*>/i.test(text); 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, '<span class="matchtext">$1</span>');
}
/** /**
* Check if HTML content is blank. * Check if HTML content is blank.
* *