Smaller thread replies and system messages (#14099)

* Add preliminar version of smaller thread reply messages

* Add thread reply preview

* Add fix for compact message list view

* Remove edited avatar, use pencil icon and red color for non owner editions

* Remove uncessary information from collapsed messages

* Reduce presence of collapsed messages

* Improve design of collapsed messages

* Add collapsed style to system messages

Co-authored-by: vynmera <39674991+vynmera@users.noreply.github.com>

* RTL fixes

* Auto break title line and always keep thread relply in a new row on mobiles

* Do not show scrollbar ticks for replyes of messages that contains mentions

* Move styles

* Update tests

* Fix message loading from server

* Fix tests

* Save screenshots of all test steps

* Try to fix tests

* Limit thread’s title to 200px in relply’s preview and fix render of multiline replies

* Fix chimp config

* Try to fix tests
pull/14147/head
Tasso Evangelista 7 years ago committed by Rodrigo Nascimento
parent f36236af15
commit 4ea7efcd99
  1. 77
      app/theme/client/imports/general/base_old.css
  2. 25
      app/theme/client/imports/general/rtl.css
  3. 78
      app/threads/client/threads.css
  4. 53
      app/ui-message/client/message.html
  5. 38
      app/ui-message/client/message.js
  6. 2
      app/ui-utils/client/lib/RoomManager.js
  7. 12
      app/ui-utils/client/lib/SideNav.js
  8. 7
      app/ui/client/views/app/room.js
  9. 2
      tests/chimp-config.js
  10. 1
      tests/end-to-end/ui/04-main-elements-render.js
  11. 16
      tests/end-to-end/ui/08-resolutions.js
  12. 11
      tests/pageobjects/discussion.page.js
  13. 2
      tests/pageobjects/main-content.page.js

@ -2766,9 +2766,10 @@ rc-old select,
margin-right: 5px;
font-family: inherit;
font-size: inherit;
font-size: 0.875rem;
font-weight: 600;
line-height: inherit;
}
& .thumb {
@ -2802,24 +2803,30 @@ rc-old select,
}
}
& .info {
font-size: 12px;
& .title {
display: flex;
flex-direction: row;
font-size: 0.75rem;
line-height: 1.25rem;
align-items: center;
& .edited {
margin-left: 3px;
padding-left: 3px;
border-left: 1px dotted;
}
& .is-bot,
& .role-tag {
margin: 0 3px;
padding: 1px 4px;
border-width: 1px;
border-radius: var(--border-radius);
background: #ffffff;
line-height: initial;
}
}
@ -2841,13 +2848,14 @@ rc-old select,
display: none;
}
& .info {
& .title {
position: absolute;
left: 5px;
width: 60px;
text-align: right;
justify-content: flex-end;
& .time,
& .role-tag,
@ -3008,6 +3016,51 @@ rc-old select,
display: none;
}
}
&.collapsed {
min-height: 25px;
padding: 2px 50px 2px 70px;
font-size: 12px;
& > .thumb {
top: 3px;
width: 20px;
height: 20px;
margin-left: 16px;
& .avatar {
width: 100%;
height: 100%;
}
}
& .user {
font-size: 0.75rem;
font-weight: initial;
}
& .body,
& .message-oembed,
& .attachment,
& .reactions,
& .edited,
& .role-tag,
&:hover .message-actions {
display: none;
}
&.system {
& .title {
display: inline-flex;
}
& .body {
display: inline-flex;
}
}
}
}
.rc-old .image-labels {
@ -3079,7 +3132,7 @@ rc-old select,
}
}
& .info {
& .title {
& .avatar-image {
border-radius: 2px;
}
@ -3094,6 +3147,7 @@ rc-old select,
width: 20px;
height: 20px;
margin-left: 0;
& .avatar {
width: 20px;
@ -3107,11 +3161,14 @@ rc-old select,
display: inline-block;
}
& .info {
& .title {
position: relative;
left: 0;
text-align: right;
width: auto;
text-align: left;
justify-content: initial;
& .time,
& .edited {

@ -54,6 +54,16 @@
right: 50%;
left: auto;
}
&.compact {
& .message {
padding: 5px 45px 0 15px;
&.collapsed .thumb {
margin: 0;
}
}
}
}
& .terminal {
@ -390,14 +400,9 @@
left: auto;
}
& .info .edited {
& .title .edited {
margin-right: 3px;
margin-left: auto;
padding-right: 3px;
padding-left: auto;
border-right: 1px dotted #bab8d8;
border-left: unset;
}
& .private {
@ -408,7 +413,7 @@
&.sequential {
padding-top: 4px;
& .info {
& .title {
right: 5px;
left: auto;
@ -431,6 +436,12 @@
}
}
}
&.collapsed {
& > .thumb {
margin: 0 16px 0 0;
}
}
}
& blockquote {

@ -25,32 +25,80 @@
flex-shrink: 1;
}
.thread-replied {
overflow: hidden;
.message {
& .thread-replied {
white-space: nowrap;
text-overflow: ellipsis;
display: inline-flex;
overflow: hidden;
color: var(--color-gray);
margin-left: 3px;
font-size: 14px;
cursor: pointer;
white-space: nowrap;
text-overflow: ellipsis;
color: var(--rc-color-link-active);
align-items: baseline;
flex-flow: row nowrap;
& .rc-icon {
position: relative;
bottom: -0.15rem;
min-width: 18px;
}
& .thread-quote {
overflow: hidden;
max-width: 200px;
white-space: nowrap;
text-overflow: ellipsis;
}
& .thread-reply-preview {
display: none;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
&.collapsed .thread-reply-preview {
display: initial;
}
}
.message.sequential > .body > .thread-replied {
display: none;
.compact .message.collapsed {
& .thread-replied {
& .thread-reply-switch,
& .thread-reply-preview {
display: none;
}
}
}
.thread-quote {
cursor: pointer;
@media (width < 500px) {
.message {
& .title {
flex-wrap: wrap;
}
color: var(--rc-color-link-active);
& .thread-replied {
flex-basis: 100%;
}
}
}
.message.sequential > .body > .thread-replied {
display: none;
}
.contextual-bar__content.thread,
.contextual-bar__content.threads {
padding: 0;
}
.js-open-thread {
cursor: pointer;
}

@ -1,5 +1,5 @@
<template name="message">
<li id="{{templatePrefix}}{{msg._id}}" data-id="{{msg._id}}" data-context={{actionContext}} class="message background-transparent-dark-hover {{ignoredClass}} {{sequentialClass}} {{system}} {{t}} {{own}} {{isTemp}} {{chatops}} {{customClass}}" data-username="{{msg.u.username}}" data-tmid="{{msg.tmid}}" data-groupable="{{isGroupable}}" data-date="{{date}}" data-timestamp="{{timestamp}}">
<li id="{{templatePrefix}}{{msg._id}}" data-id="{{msg._id}}" data-context={{actionContext}} class="message background-transparent-dark-hover {{ignoredClass}} {{sequentialClass}} {{system}} {{t}} {{own}} {{isTemp}} {{chatops}} {{collapsed}} {{customClass}}" data-username="{{msg.u.username}}" data-tmid="{{msg.tmid}}" data-groupable="{{isGroupable}}" data-date="{{date}}" data-timestamp="{{timestamp}}">
{{#if avatar}}
{{#if avatarFromUsername}}
<button class="thumb user-card-message" data-username="{{msg.u.username}}" tabindex="1">{{> avatar username=avatarFromUsername}}</button>
@ -21,8 +21,8 @@
<button class="thumb user-card-message" data-username="{{msg.u.username}}" tabindex="1">{{> avatar username=msg.u.username}}</button>
{{/if}}
{{/if}}
<button type="button" class="user user-card-message color-primary-font-color" data-username="{{msg.u.username}}" tabindex="1">{{getName}}{{#if showUsername}} <span class="message-alias border-component-color color-info-font-color">@{{msg.u.username}}</span>{{/if}}</button>
<span class="info border-component-color color-info-font-color">
<div class="title border-component-color color-info-font-color">
<button type="button" class="user user-card-message color-primary-font-color" data-username="{{msg.u.username}}" tabindex="1">{{getName}}{{#if showUsername}} <span class="message-alias border-component-color color-info-font-color">@{{msg.u.username}}</span>{{/if}}</button>
{{#each role in roleTags}}
<span class="role-tag color-secondary-color border-component-color" data-role="{{role.description}}">{{role.description}}</span>
{{/each}}
@ -42,23 +42,34 @@
{{/if}}
{{#if edited}}
<span class="edited" title='{{_ "edited"}} {{_ "at"}} {{editTime}} {{_ "by"}} {{editedBy}}'>
<i class="icon-edit" aria-label="{{_ "Edited"}}"></i>
<button class="thumb thumb-small user-card-message" data-username="{{editedBy}}" tabindex="1">{{> avatar username=editedBy}}</button>
<i class="icon-pencil{{#if $neq editedBy msg.u.username}} error-color{{/if}}" aria-label="{{_ "Edited"}}"></i>
</span>
{{/if}}
{{#if msg.alert}}<div aria-label="{{_ msg.alert }}" class="rc-tooltip message-unread"></div> {{/if}}
{{#if private}}
<span class="private">{{_ "Only_you_can_see_this_message"}}</span>
{{/if}}
</span>
{{#if fromSearch}}
<span class="user color-info-font-color">
{{>icon icon=roomIcon}}{{channelName}}
</span>
{{/if}}
{{#if isIgnored}}
<span class="toggle-hidden icon-right-dir" data-message="{{msg._id}}"> {{_ "Message_Ignored"}} </span>
{{/if}}
{{#if isThreadReply}}
<span class="thread-replied">
{{> icon icon="thread" classes="js-open-thread"}}
<q role="button" aria-label="{{_ "Open_thread"}}" class="js-open-thread thread-quote">
<span class="message-body--unstyled">{{{parentMessage}}}</span>
</q>
<span class="js-toggle-thread-reply thread-reply-switch {{collapseSwitchClass}}"></span>
<span role="button" class="js-toggle-thread-reply thread-reply-preview color-primary-font-color">
<span class="message-body--unstyled">{{{normalizedBody}}}</span>
</span>
</span>
{{/if}}
{{#if fromSearch}}
<span class="user color-info-font-color">
{{>icon icon=roomIcon}}{{channelName}}
</span>
{{/if}}
{{#if isIgnored}}
<span class="toggle-hidden icon-right-dir" data-message="{{msg._id}}"> {{_ "Message_Ignored"}} </span>
{{/if}}
</div>
<div class="body {{bodyClass}}" dir="auto" data-unread-text="{{_ "Unread_Messages"}}">
{{#if isSnippet}}
<div class="snippet-name">{{_ "Snippet_name"}}: {{snippetName}}</div>
@ -66,19 +77,7 @@
{{#if isDecrypting}}
<span>******</span>
{{else}}
{{#if and settings.showreply msg.tmid}}
<p class="thread-replied">
{{_ "Replied_on"}}:
<q role="button" aria-label="{{_ "Open_thread"}}" class="thread-quote js-open-thread">
<span class="message-body--unstyled">{{{parentMessage}}}</span>
</q>
</p>
<blockquote>
{{{body}}}
</blockquote>
{{else}}
{{{body}}}
{{/if}}
{{{body}}}
{{/if}}
{{#if hasOembed}}
{{#each msg.urls}}

@ -146,7 +146,10 @@ Template.message.helpers({
}
},
sequentialClass() {
const { msg, groupable } = this;
const { msg, groupable, settings: { showreply } } = this;
if (msg.tmid && showreply) {
return;
}
return groupable !== false && msg.groupable !== false && 'sequential';
},
avatarFromUsername() {
@ -204,6 +207,10 @@ Template.message.helpers({
body() {
return Template.instance().body;
},
normalizedBody() {
const { msg } = this;
return normalizeThreadMessage(msg);
},
bodyClass() {
const { msg } = this;
return MessageTypes.isSystemMessage(msg) ? 'color-info-font-color' : 'color-primary-font-color';
@ -368,6 +375,22 @@ Template.message.helpers({
const { msg } = this;
return msg.actionContext === 'snippeted';
},
isThreadReply() {
const { msg: { tmid }, settings: { showreply } } = this;
return !!(tmid && showreply);
},
collapsed() {
const { msg, msg: { tmid, collapsed }, settings: { showreply } } = this;
const isCollapsedThreadReply = tmid && showreply && collapsed !== false;
const isSystemMessage = MessageTypes.isSystemMessage(msg);
if (isCollapsedThreadReply || isSystemMessage) {
return 'collapsed';
}
},
collapseSwitchClass() {
const { msg: { collapsed = true } } = this;
return collapsed ? 'icon-right-dir' : 'icon-down-dir';
},
parentMessage() {
const { msg: { threadMsg } } = this;
return threadMsg;
@ -394,7 +417,16 @@ const findParentMessage = (() => {
repliesCount: msg.tcount,
},
}, { multi: true });
Messages.upsert({ _id }, msg);
if (!Messages.findOne({ _id })) {
/**
* Delete rid from message to not render it and to not be considred in last message
* find from load history method what was preveting the load of some messages in
* between the reals last loaded message and this one if this one is older than
* the real last loaded message.
*/
delete msg.rid;
Messages.upsert({ _id }, msg);
}
});
}, 500);
@ -529,7 +561,7 @@ Template.message.onViewRendered = function() {
if (nextDataset.groupable !== 'false') {
if (nextDataset.tmid !== currentDataset.tmid || nextDataset.username !== currentDataset.username || parseInt(nextDataset.timestamp) - parseInt(currentDataset.timestamp) > settings.Message_GroupingPeriod) {
nextNode.classList.remove('sequential');
} else if (!nextNode.classList.contains('new-day') && !currentNode.classList.contains('temp')) {
} else if (!nextNode.classList.contains('new-day') && !currentNode.classList.contains('temp') && !currentNode.dataset.tmid) {
nextNode.classList.add('sequential');
}
}

@ -235,7 +235,7 @@ export const RoomManager = new function() {
const scrollTop = $('> .wrapper', messagesBox).scrollTop() - 50;
const totalHeight = $(' > .wrapper > ul', messagesBox).height() + 40;
ticksBar.innerHTML = Array.from(messagesBox.querySelectorAll('.mention-link--me, .mention-link--group'))
ticksBar.innerHTML = Array.from(messagesBox.querySelectorAll('.message .body .mention-link--me, .message .body .mention-link--group'))
.map((mentionLink) => {
const topOffset = $(mentionLink).offset().top + scrollTop;
const percent = (100 / totalHeight) * topOffset;

@ -23,8 +23,18 @@ export const SideNav = new class {
this.flexNav.addClass('animated-hidden');
} else {
this.flexNav.opened = true;
setTimeout(() => this.flexNav.removeClass('animated-hidden'), 50);
if (window.DISABLE_ANIMATION === true) {
this.flexNav.removeClass('animated-hidden');
} else {
setTimeout(() => this.flexNav.removeClass('animated-hidden'), 50);
}
}
if (window.DISABLE_ANIMATION === true) {
this.animating = false;
return typeof callback === 'function' && callback();
}
return setTimeout(() => {
this.animating = false;
return typeof callback === 'function' && callback();

@ -780,6 +780,13 @@ Template.room.events({
}
},
'click .js-toggle-thread-reply'(e) {
e.preventDefault();
e.stopPropagation();
const { msg: { _id, collapsed = true } } = messageArgs(this);
ChatMessage.update({ _id }, { $set: { collapsed: !collapsed } });
},
'dragenter .dropzone'(e) {
const types = e.originalEvent && e.originalEvent.dataTransfer && e.originalEvent.dataTransfer.types;
if (types != null && types.length > 0 && _.every(types, (type) => type.indexOf('text/') === -1 || type.indexOf('text/uri-list') !== -1) && userCanDrop(this._id)) {

@ -27,7 +27,7 @@ module.exports = {
// chai: false,
screenshotsOnError: true,
screenshotsPath: '.screenshots',
captureAllStepScreenshots: false,
captureAllStepScreenshots: true,
saveScreenshotsToDisk: true,
// // Note: With a large viewport size and captureAllStepScreenshots enabled,
// // you may run out of memory. Use browser.setViewportSize to make the

@ -41,6 +41,7 @@ describe('[Main Elements Render]', function() {
it('it should click the spotlight and show the channel list', () => {
sideNav.spotlightSearch.waitForVisible(5000);
browser.pause(100);
sideNav.spotlightSearch.click();
sideNav.spotlightSearchPopUp.waitForVisible(5000);
sideNav.spotlightSearchPopUp.isVisible().should.be.true;

@ -22,7 +22,7 @@ describe('[Resolution]', () => {
});
describe('moving elements:', () => {
it('it should close de sidenav', () => {
it('it should close the sidenav', () => {
mainContent.mainContent.getLocation().should.deep.include({ x:0 });
});
@ -48,7 +48,7 @@ describe('[Resolution]', () => {
sideNav.openChannel('general');
});
it('it should close de sidenav', () => {
it('it should close the sidenav', () => {
mainContent.mainContent.getLocation().should.deep.include({ x:0 });
});
@ -69,7 +69,7 @@ describe('[Resolution]', () => {
sideNav.preferences.click();
});
it('it should close de sidenav', () => {
it('it should close the sidenav', () => {
mainContent.mainContent.getLocation().should.deep.include({ x:0 });
});
@ -83,7 +83,7 @@ describe('[Resolution]', () => {
sideNav.profile.click();
});
it('it should close de sidenav', () => {
it('it should close the sidenav', () => {
mainContent.mainContent.getLocation().should.deep.include({ x:0 });
});
@ -92,15 +92,11 @@ describe('[Resolution]', () => {
sideNav.burgerBtn.click();
});
it('it should close de sidenav', () => {
it('it should close the preferences nav', () => {
sideNav.preferencesClose.waitForVisible(10000);
sideNav.preferencesClose.click();
sideNav.sidebarWrap.click();
});
it('it should press the navbar button', () => {
sideNav.burgerBtn.waitForVisible(10000);
sideNav.burgerBtn.click();
sideNav.preferencesClose.waitForVisible(10000, true);
});
});
});

@ -55,14 +55,17 @@ class Discussion extends Page {
this.createDiscussionModal.waitForVisible(1000);
this.discussionName.setValue(name);
this.discussionMessage.setValue(message);
browser.pause(100);
this.parentChannelName.waitForVisible(1000);
this.parentChannelName.setValue(parentChannelName);
const list = browser.element('.rc-popup-list__list');
list.waitForVisible(2000);
const listItem = browser.element('.rc-popup-list__list .rc-popup-list__item');
listItem.waitForVisible(2000);
list.element('.rc-popup-list__item').waitForVisible(10000);
list.element('.rc-popup-list__item').click();
listItem.click();
browser.waitUntil(function() {
return browser.isEnabled('.js-save-discussion');

@ -24,7 +24,7 @@ class MainContent extends Page {
get joinChannelBtn() { return browser.element('.button.join'); }
// Messages
get lastMessageUser() { return browser.element('.message:last-child .user-card-message:nth-of-type(2)'); }
get lastMessageUser() { return browser.element('.message:last-child .title .user-card-message'); }
get lastMessage() { return browser.element('.message:last-child .body'); }
get lastMessageDesc() { return browser.element('.message:last-child .body .attachment-description'); }
get lastMessageRoleAdded() { return browser.element('.message:last-child.subscription-role-added .body'); }

Loading…
Cancel
Save