MOBILE-2963 message: Highlight search results
parent
af25634bd2
commit
4490dc94c8
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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']);
|
||||||
|
|
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue