From af25634bd2072974a6a666c267be989015114850 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 5 Apr 2019 09:20:17 +0200 Subject: [PATCH 1/5] MOBILE-2963 message: Show only one message when no results found --- scripts/langindex.json | 3 -- src/addon/messages/lang/en.json | 3 -- src/addon/messages/pages/search/search.html | 59 +++++++++++---------- src/addon/messages/pages/search/search.ts | 3 -- src/assets/lang/en.json | 3 -- 5 files changed, 30 insertions(+), 41 deletions(-) diff --git a/scripts/langindex.json b/scripts/langindex.json index bd5af73bb..a0eddf6a0 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -216,9 +216,6 @@ "addon.messages.requests": "moodle", "addon.messages.requirecontacttomessage": "message", "addon.messages.searchcombined": "message", - "addon.messages.searchnocontactsfound": "message", - "addon.messages.searchnomessagesfound": "message", - "addon.messages.searchnononcontactsfound": "message", "addon.messages.selfconversation": "message", "addon.messages.selfconversationdefaultmessage": "message", "addon.messages.sendcontactrequest": "message", diff --git a/src/addon/messages/lang/en.json b/src/addon/messages/lang/en.json index f428c85c7..4767ed3ba 100644 --- a/src/addon/messages/lang/en.json +++ b/src/addon/messages/lang/en.json @@ -55,9 +55,6 @@ "requests": "Requests", "requirecontacttomessage": "You need to request {{$a}} to add you as a contact to be able to message them.", "searchcombined": "Search people and messages", - "searchnocontactsfound": "No contacts found", - "searchnomessagesfound": "No messages found", - "searchnononcontactsfound": "No non contacts found", "selfconversation": "Personal space", "selfconversationdefaultmessage": "Save draft messages, links, notes etc. to access later.", "sendcontactrequest": "Send contact request", diff --git a/src/addon/messages/pages/search/search.html b/src/addon/messages/pages/search/search.html index 4a9ba7621..77dd31713 100644 --- a/src/addon/messages/pages/search/search.html +++ b/src/addon/messages/pages/search/search.html @@ -18,42 +18,43 @@ + + - {{ item.titleString | translate }} - - {{ item.emptyString | translate }} - + + {{ item.titleString | translate }} - - - -

- - -

- - {{result.lastmessagedate | coreDateDayOrTime}} - -

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

-
+ + + +

+ + +

+ + {{result.lastmessagedate | coreDateDayOrTime}} + +

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

+
- - -
- -
-
- -
+ + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/src/addon/messages/pages/search/search.ts b/src/addon/messages/pages/search/search.ts index 6e51b1e20..892b26c28 100644 --- a/src/addon/messages/pages/search/search.ts +++ b/src/addon/messages/pages/search/search.ts @@ -38,7 +38,6 @@ export class AddonMessagesSearchPage implements OnDestroy { contacts = { type: 'contacts', titleString: 'addon.messages.contacts', - emptyString: 'addon.messages.searchnocontactsfound', results: [], canLoadMore: false, loadingMore: false @@ -46,7 +45,6 @@ export class AddonMessagesSearchPage implements OnDestroy { nonContacts = { type: 'noncontacts', titleString: 'addon.messages.noncontacts', - emptyString: 'addon.messages.searchnononcontactsfound', results: [], canLoadMore: false, loadingMore: false @@ -54,7 +52,6 @@ export class AddonMessagesSearchPage implements OnDestroy { messages = { type: 'messages', titleString: 'addon.messages.messages', - emptyString: 'addon.messages.searchnomessagesfound', results: [], canLoadMore: false, loadingMore: false, diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json index 55d53bf21..c8a128106 100644 --- a/src/assets/lang/en.json +++ b/src/assets/lang/en.json @@ -216,9 +216,6 @@ "addon.messages.requests": "Requests", "addon.messages.requirecontacttomessage": "You need to request {{$a}} to add you as a contact to be able to message them.", "addon.messages.searchcombined": "Search people and messages", - "addon.messages.searchnocontactsfound": "No contacts found", - "addon.messages.searchnomessagesfound": "No messages found", - "addon.messages.searchnononcontactsfound": "No non contacts found", "addon.messages.selfconversation": "Personal space", "addon.messages.selfconversationdefaultmessage": "Save draft messages, links, notes etc. to access later.", "addon.messages.sendcontactrequest": "Send contact request", From 4490dc94c827801b3778eed67ad44bad6b6a5313 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 5 Apr 2019 13:17:54 +0200 Subject: [PATCH 2/5] MOBILE-2963 message: Highlight search results --- src/addon/messages/pages/search/search.html | 4 ++-- src/addon/messages/pages/search/search.ts | 16 +++++++++++++ src/app/app.scss | 5 +++++ src/directives/format-text.ts | 3 ++- src/providers/utils/text.ts | 25 ++++++++++++++++++++- 5 files changed, 49 insertions(+), 4 deletions(-) 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. * From df20087b92313a34f943cc3437f0ba1a82cb1206 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 5 Apr 2019 15:36:52 +0200 Subject: [PATCH 3/5] MOBILE-2963 messages: Expand option with unread messages first --- .../group-conversations.ts | 63 +++++++++---------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/src/addon/messages/pages/group-conversations/group-conversations.ts b/src/addon/messages/pages/group-conversations/group-conversations.ts index 94af243d1..92b81f75e 100644 --- a/src/addon/messages/pages/group-conversations/group-conversations.ts +++ b/src/addon/messages/pages/group-conversations/group-conversations.ts @@ -266,7 +266,12 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { const promises = []; promises.push(this.fetchConversationCounts()); - promises.push(this.messagesProvider.getContactRequestsCount(this.siteId)); // View updated by the event observer. + + // View updated by the events observers. + promises.push(this.messagesProvider.getContactRequestsCount(this.siteId)); + if (refreshUnreadCounts) { + promises.push(this.messagesProvider.refreshUnreadConversationCounts(this.siteId)); + } return Promise.all(promises).then(() => { if (typeof this.favourites.expanded == 'undefined') { @@ -276,9 +281,9 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { // We don't know which option it belongs to, so we need to fetch the data for all of them. const promises = []; - promises.push(this.fetchDataForOption(this.favourites, false, refreshUnreadCounts)); - promises.push(this.fetchDataForOption(this.group, false, refreshUnreadCounts)); - promises.push(this.fetchDataForOption(this.individual, false, refreshUnreadCounts)); + promises.push(this.fetchDataForOption(this.favourites, false)); + promises.push(this.fetchDataForOption(this.group, false)); + promises.push(this.fetchDataForOption(this.individual, false)); return Promise.all(promises).then(() => { // All conversations have been loaded, find the one we need to load and expand its option. @@ -286,13 +291,13 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { if (conversation) { const option = this.getConversationOption(conversation); - return this.expandOption(option, refreshUnreadCounts); + return this.expandOption(option); } else { // Conversation not found, just open the default option. this.calculateExpandedStatus(); // Now load the data for the expanded option. - return this.fetchDataForExpandedOption(refreshUnreadCounts); + return this.fetchDataForExpandedOption(); } }); } @@ -302,7 +307,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { } // Now load the data for the expanded option. - return this.fetchDataForExpandedOption(refreshUnreadCounts); + return this.fetchDataForExpandedOption(); }).catch((error) => { this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingdiscussions', true); }).finally(() => { @@ -314,9 +319,9 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { * Calculate which option should be expanded initially. */ protected calculateExpandedStatus(): void { - this.favourites.expanded = this.favourites.count != 0; - this.group.expanded = this.favourites.count == 0 && this.group.count != 0; - this.individual.expanded = this.favourites.count == 0 && this.group.count == 0; + this.favourites.expanded = this.favourites.count != 0 && !this.group.unread && !this.individual.unread; + this.group.expanded = !this.favourites.expanded && this.group.count != 0 && !this.individual.unread; + this.individual.expanded = !this.favourites.expanded && !this.group.expanded; this.loadCurrentListElement(); } @@ -324,26 +329,16 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { /** * Fetch data for the expanded option. * - * @param {booleam} [refreshUnreadCounts=true] Whether to refresh unread counts. * @return {Promise} Promise resolved when done. */ - protected fetchDataForExpandedOption(refreshUnreadCounts: boolean = true): Promise { + protected fetchDataForExpandedOption(): Promise { const expandedOption = this.getExpandedOption(); if (expandedOption) { - return this.fetchDataForOption(expandedOption, false, refreshUnreadCounts); - } else { - // All options are collapsed, update the counts. - const promises = []; - - promises.push(this.fetchConversationCounts()); - if (refreshUnreadCounts) { - // View updated by event observer. - promises.push(this.messagesProvider.refreshUnreadConversationCounts(this.siteId)); - } - - return Promise.all(promises); + return this.fetchDataForOption(expandedOption, false); } + + return Promise.resolve(); } /** @@ -351,10 +346,10 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { * * @param {any} option The option to fetch data for. * @param {boolean} [loadingMore} Whether we are loading more data or just the first ones. - * @param {booleam} [refreshUnreadCounts=true] Whether to refresh unread counts. + * @param {booleam} [getCounts] Whether to get counts data. * @return {Promise} Promise resolved when done. */ - fetchDataForOption(option: any, loadingMore?: boolean, refreshUnreadCounts: boolean = true): Promise { + fetchDataForOption(option: any, loadingMore?: boolean, getCounts?: boolean): Promise { option.loadMoreError = false; const limitFrom = loadingMore ? option.conversations.length : 0, @@ -375,12 +370,11 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { promises.push(this.messagesOffline.getAllMessages().then((data) => { offlineMessages = data; })); + } + if (getCounts) { promises.push(this.fetchConversationCounts()); - if (refreshUnreadCounts) { - // View updated by the event observer. - promises.push(this.messagesProvider.refreshUnreadConversationCounts(this.siteId)); - } + promises.push(this.messagesProvider.refreshUnreadConversationCounts(this.siteId)); } return Promise.all(promises).then(() => { @@ -650,7 +644,8 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { option.expanded = false; this.loadCurrentListElement(); } else { - this.expandOption(option).catch((error) => { + // Pass getCounts=true to update the counts everytime the user expands an option. + this.expandOption(option, true).catch((error) => { this.domUtils.showErrorModalDefault(error, 'addon.messages.errorwhileretrievingdiscussions', true); }); } @@ -660,10 +655,10 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { * Expand a certain option. * * @param {any} option The option to expand. - * @param {booleam} [refreshUnreadCounts=true] Whether to refresh unread counts. + * @param {booleam} [getCounts] Whether to get counts data. * @return {Promise} Promise resolved when done. */ - protected expandOption(option: any, refreshUnreadCounts: boolean = true): Promise { + protected expandOption(option: any, getCounts?: boolean): Promise { // Collapse all and expand the right one. this.favourites.expanded = false; this.group.expanded = false; @@ -672,7 +667,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy { option.expanded = true; option.loading = true; - return this.fetchDataForOption(option, false, refreshUnreadCounts).then(() => { + return this.fetchDataForOption(option, false, getCounts).then(() => { this.loadCurrentListElement(); }).catch((error) => { option.expanded = false; From a43f0bb5fc48860c504cf577d5c55ea9ac73b12a Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 26 Apr 2019 09:02:17 +0200 Subject: [PATCH 4/5] MOBILE-2963 message: Improve accessibility in messaging --- scripts/langindex.json | 2 ++ src/addon/messages/lang/en.json | 2 ++ src/addon/messages/pages/discussion/discussion.html | 8 ++++---- .../pages/group-conversations/group-conversations.html | 2 +- src/assets/lang/en.json | 2 ++ src/components/context-menu/context-menu.ts | 6 +++--- .../context-menu/core-context-menu-popover.html | 2 +- 7 files changed, 15 insertions(+), 9 deletions(-) diff --git a/scripts/langindex.json b/scripts/langindex.json index a0eddf6a0..83f7a1edb 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -177,6 +177,7 @@ "addon.messages.contactname": "local_moodlemobileapp", "addon.messages.contactrequestsent": "message", "addon.messages.contacts": "message", + "addon.messages.conversationactions": "message", "addon.messages.decline": "message", "addon.messages.deleteallconfirm": "message", "addon.messages.deleteallselfconfirm": "message", @@ -198,6 +199,7 @@ "addon.messages.messagepreferences": "message", "addon.messages.messages": "message", "addon.messages.muteconversation": "message", + "addon.messages.mutedconversation": "message", "addon.messages.newmessage": "message", "addon.messages.newmessages": "local_moodlemobileapp", "addon.messages.nocontactrequests": "message", diff --git a/src/addon/messages/lang/en.json b/src/addon/messages/lang/en.json index 4767ed3ba..3a298fdff 100644 --- a/src/addon/messages/lang/en.json +++ b/src/addon/messages/lang/en.json @@ -16,6 +16,7 @@ "contactname": "Contact name", "contactrequestsent": "Contact request sent", "contacts": "Contacts", + "conversationactions": "Conversation actions menu", "decline": "Decline", "deleteallconfirm": "Are you sure you would like to delete this entire conversation? This will not delete it for other conversation participants.", "deleteallselfconfirm": "Are you sure you would like to delete this entire personal conversation?", @@ -37,6 +38,7 @@ "messagepreferences": "Message preferences", "messages": "Messages", "muteconversation": "Mute", + "mutedconversation": "Muted conversation", "newmessage": "New message", "newmessages": "New messages", "nocontactrequests": "No contact requests", diff --git a/src/addon/messages/pages/discussion/discussion.html b/src/addon/messages/pages/discussion/discussion.html index fb5602317..1d0566463 100644 --- a/src/addon/messages/pages/discussion/discussion.html +++ b/src/addon/messages/pages/discussion/discussion.html @@ -1,16 +1,16 @@ - + - - + + - + diff --git a/src/addon/messages/pages/group-conversations/group-conversations.html b/src/addon/messages/pages/group-conversations/group-conversations.html index ec74e6a41..c32cdec85 100644 --- a/src/addon/messages/pages/group-conversations/group-conversations.html +++ b/src/addon/messages/pages/group-conversations/group-conversations.html @@ -101,7 +101,7 @@

- +

{{ conversation.unreadcount }} diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json index c8a128106..e3028b828 100644 --- a/src/assets/lang/en.json +++ b/src/assets/lang/en.json @@ -177,6 +177,7 @@ "addon.messages.contactname": "Contact name", "addon.messages.contactrequestsent": "Contact request sent", "addon.messages.contacts": "Contacts", + "addon.messages.conversationactions": "Conversation actions menu", "addon.messages.decline": "Decline", "addon.messages.deleteallconfirm": "Are you sure you would like to delete this entire conversation? This will not delete it for other conversation participants.", "addon.messages.deleteallselfconfirm": "Are you sure you would like to delete this entire personal conversation?", @@ -198,6 +199,7 @@ "addon.messages.messagepreferences": "Message preferences", "addon.messages.messages": "Messages", "addon.messages.muteconversation": "Mute", + "addon.messages.mutedconversation": "Muted conversation", "addon.messages.newmessage": "New message", "addon.messages.newmessages": "New messages", "addon.messages.nocontactrequests": "No contact requests", diff --git a/src/components/context-menu/context-menu.ts b/src/components/context-menu/context-menu.ts index 1f674eebd..ff6413b67 100644 --- a/src/components/context-menu/context-menu.ts +++ b/src/components/context-menu/context-menu.ts @@ -31,10 +31,10 @@ import { Subject } from 'rxjs'; }) export class CoreContextMenuComponent implements OnInit, OnDestroy { @Input() icon?: string; // Icon to be shown on the navigation bar. Default: Kebab menu icon. - @Input() title?: string; // Aria label and text to be shown on the top of the popover. + @Input() title?: string; // Text to be shown on the top of the popover. + @Input('aria-label') ariaLabel?: string; // Aria label to be shown on the top of the popover. hideMenu = true; // It will be unhidden when items are added. - ariaLabel: string; expanded = false; protected items: CoreContextMenuItemComponent[] = []; protected itemsMovedToParent: CoreContextMenuItemComponent[] = []; @@ -70,7 +70,7 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy { */ ngOnInit(): void { this.icon = this.icon || 'more'; - this.ariaLabel = this.title || this.translate.instant('core.info'); + this.ariaLabel = this.ariaLabel || this.title || this.translate.instant('core.info'); } /** diff --git a/src/components/context-menu/core-context-menu-popover.html b/src/components/context-menu/core-context-menu-popover.html index 18905549d..d785687be 100644 --- a/src/components/context-menu/core-context-menu-popover.html +++ b/src/components/context-menu/core-context-menu-popover.html @@ -1,6 +1,6 @@ {{title}} - + From 758ecfd7a9246d31eeec162618006e09c25a1b56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Fri, 26 Apr 2019 17:36:27 +0200 Subject: [PATCH 5/5] MOBILE-2963 messages: Add message tails to match web --- .../messages/pages/discussion/discussion.html | 3 ++- .../messages/pages/discussion/discussion.scss | 24 +++++++++++++++++++ .../messages/pages/discussion/discussion.ts | 12 ++++++++++ src/app/app.scss | 2 +- src/theme/variables.scss | 2 ++ 5 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/addon/messages/pages/discussion/discussion.html b/src/addon/messages/pages/discussion/discussion.html index 1d0566463..74dba5553 100644 --- a/src/addon/messages/pages/discussion/discussion.html +++ b/src/addon/messages/pages/discussion/discussion.html @@ -47,7 +47,7 @@ -

+

{{ members[message.useridfrom].fullname }}
@@ -64,6 +64,7 @@ +
diff --git a/src/addon/messages/pages/discussion/discussion.scss b/src/addon/messages/pages/discussion/discussion.scss index b09449b24..2445c99e4 100644 --- a/src/addon/messages/pages/discussion/discussion.scss +++ b/src/addon/messages/pages/discussion/discussion.scss @@ -47,6 +47,9 @@ ion-app.app-root page-addon-messages-discussion { min-height: 0; position: relative; @include core-transition(width); + // This is needed to display bubble tails. + overflow: visible; + contain: none; core-format-text > p:only-child { display: inline; @@ -127,6 +130,15 @@ ion-app.app-root page-addon-messages-discussion { line-height: initial; } } + + .tail { + content: ''; + width: 0; + height: 0; + border: 0.5rem solid transparent; + position: absolute; + touch-action: none; + } } .addon-message.addon-message-mine + .addon-message-no-user.addon-message-mine, @@ -158,6 +170,18 @@ ion-app.app-root page-addon-messages-discussion { height: 16px; } } + + .tail { + @include position(null, 0, 0, null); + @include margin-horizontal(null, -0.5rem); + border-bottom-color: $item-message-mine-bg; + } + } + + .addon-message-not-mine .tail { + @include position(null, null, 0, 0); + @include margin-horizontal(-0.5rem, null); + border-bottom-color: $item-message-bg; } .toolbar-title { diff --git a/src/addon/messages/pages/discussion/discussion.ts b/src/addon/messages/pages/discussion/discussion.ts index dae1a0f12..3fecd7867 100644 --- a/src/addon/messages/pages/discussion/discussion.ts +++ b/src/addon/messages/pages/discussion/discussion.ts @@ -384,6 +384,7 @@ export class AddonMessagesDiscussionPage implements OnDestroy { this.messages.forEach((message, index): any => { message.showDate = this.showDate(message, this.messages[index - 1]); message.showUserData = this.showUserData(message, this.messages[index - 1]); + message.showTail = this.showTail(message, this.messages[index + 1]); }); // Call resize to recalculate the dimensions. @@ -1046,6 +1047,17 @@ export class AddonMessagesDiscussionPage implements OnDestroy { (!prevMessage || prevMessage.useridfrom != message.useridfrom || message.showDate); } + /** + * Check if a css tail should be shown. + * + * @param {any} message Current message where to show the user info. + * @param {any} [nextMessage] Next message. + * @return {boolean} Whether user data should be shown. + */ + showTail(message: any, nextMessage?: any): boolean { + return !nextMessage || nextMessage.useridfrom != message.useridfrom || nextMessage.showDate; + } + /** * Toggles delete state. */ diff --git a/src/app/app.scss b/src/app/app.scss index 9c1b3803b..2782088e0 100644 --- a/src/app/app.scss +++ b/src/app/app.scss @@ -1052,7 +1052,7 @@ ion-modal, // Highlight text. .matchtext { - background-color: lighten($blue, 40%); + background-color: $core-text-hightlight-background-color; } // Styles for desktop apps only. diff --git a/src/theme/variables.scss b/src/theme/variables.scss index 80a7b6d61..3cedb3059 100644 --- a/src/theme/variables.scss +++ b/src/theme/variables.scss @@ -263,6 +263,8 @@ $core-rte-min-height: 80px; $core-toolbar-button-image-width: 32px; +$core-text-hightlight-background-color: lighten($blue, 40%) !default; + // Timer variables. $core-timer-warn-color: $red !default; $core-timer-iterations: 15 !default;