Skip to content

Commit d542935

Browse files
authored
Ensure conversation-thread scrolls to bottom after socket update close #1637 (#1690)
1 parent be88d9f commit d542935

File tree

8 files changed

+63
-23
lines changed

8 files changed

+63
-23
lines changed

app/models/conversation.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Model from 'ember-data/model';
22
import attr from 'ember-data/attr';
3+
import { sort } from '@ember/object/computed';
34
import { belongsTo, hasMany } from 'ember-data/relationships';
45

56
export default Model.extend({
@@ -11,5 +12,8 @@ export default Model.extend({
1112
conversationParts: hasMany('conversation-part', { async: true }),
1213
message: belongsTo('message', { async: true }),
1314
project: belongsTo('project', { async: true }),
14-
user: belongsTo('user', { async: true })
15+
user: belongsTo('user', { async: true }),
16+
17+
partsAsc: ['insertedAt:asc'],
18+
sortedConversationParts: sort('conversationParts', 'partsAsc')
1519
});

app/templates/components/conversations/conversation-thread.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
sentAt=conversation.message.insertedAt
99
}}
1010
{{/if}}
11-
{{#each (sort-by "insertedAt:asc" conversation.conversationParts) as |conversationPart|}}
11+
{{#each sortedConversationParts as |conversationPart|}}
1212
{{#if conversationPart.isLoaded}}
1313
{{conversations/conversation-part
1414
author=conversationPart.author

app/templates/conversations/conversation.hbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{{conversations/conversation-thread
22
conversation=conversation
3+
sortedConversationParts=conversation.sortedConversationParts
34
send=(action send)
45
}}
56
{{conversations/project-details

app/templates/project/conversations/conversation.hbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{{conversations/conversation-thread
22
conversation=conversation
3+
sortedConversationParts=conversation.sortedConversationParts
34
send=(action send)
45
}}
56
{{conversations/user-details

tests/acceptance/project-conversations-test.js

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const hour = 3600 * 1000;
99
function createConversations(count, project, user) {
1010
return [...Array(count)].map(() => {
1111
let message = server.create('message', { project });
12-
return server.create('conversation', { message, user });
12+
return server.create('conversation', { message, user }, 'withConversationParts');
1313
});
1414
}
1515

@@ -88,8 +88,9 @@ test('Project admin can view list of conversations', async function(assert) {
8888
});
8989

9090
test('Project admin can view single conversations', async function(assert) {
91-
assert.expect(1);
91+
assert.expect(5);
9292

93+
let store = this.application.__container__.lookup('service:store');
9394
let { project, user } = server.create('project-user', { role: 'admin' });
9495
authenticateSession(this.application, { user_id: user.id });
9596

@@ -100,9 +101,19 @@ test('Project admin can view single conversations', async function(assert) {
100101
project: project.slug
101102
});
102103

103-
page.conversations(1).click();
104+
await page.conversations(1).click();
104105

105-
assert.equal(currentRouteName(), 'project.conversations.conversation');
106+
andThen(() => {
107+
assert.equal(currentRouteName(), 'project.conversations.conversation');
108+
109+
let conversation = store.peekRecord('conversation', server.db.conversations[1].id);
110+
let firstPart = conversation.get('sortedConversationParts').get('firstObject');
111+
let lastPart = conversation.get('sortedConversationParts').get('lastObject');
112+
assert.equal(page.conversationThread.conversationParts().count, 11, 'Message head and conversation parts rendered');
113+
assert.equal(page.conversationThread.conversationParts(1).body.text, firstPart.get('body'), 'first conversation part is rendered correctly');
114+
assert.equal(page.conversationThread.conversationParts(10).body.text, lastPart.get('body'), 'last conversation part is rendered correctly');
115+
assert.ok(firstPart.get('insertedAt') < lastPart.get('insertedAt'), 'conversations are sorted correctly');
116+
});
106117
});
107118

108119
test('System is notified of new conversation part', async function(assert) {
@@ -120,17 +131,17 @@ test('System is notified of new conversation part', async function(assert) {
120131

121132
page.conversations(0).click();
122133

123-
assert.equal(page.conversationThread.conversationParts().count, 1, 'Just the message head is rendered.');
134+
assert.equal(page.conversationThread.conversationParts().count, 11, 'Just the message head and conversation parts is rendered.');
124135
server.create('conversation-part', { conversation });
125136

126-
assert.equal(page.conversationThread.conversationParts().count, 1, 'No notification yet, so new part was not rendered.');
137+
assert.equal(page.conversationThread.conversationParts().count, 11, 'No notification yet, so new part was not rendered.');
127138
let conversationChannelService = this.application.__container__.lookup('service:conversation-channel');
128139
let socket = get(conversationChannelService, 'socket.socket');
129140
let [channel] = socket.channels;
130141
channel.trigger('new:conversation-part', {});
131142

132143
andThen(() => {
133-
assert.equal(page.conversationThread.conversationParts().count, 2, 'Notification was sent. New part is rendered.');
144+
assert.equal(page.conversationThread.conversationParts().count, 12, 'Notification was sent. New part is rendered.');
134145
});
135146
});
136147

tests/acceptance/user-conversations-test.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,13 @@ test('User can view list of their own conversations', function(assert) {
5959
});
6060

6161
test('User can view single conversations', function(assert) {
62-
assert.expect(1);
62+
assert.expect(5);
6363

64+
let store = this.application.__container__.lookup('service:store');
6465
let user = server.create('user');
6566
authenticateSession(this.application, { user_id: user.id });
6667

67-
server.create('conversation', { user });
68+
server.create('conversation', { user }, 'withConversationParts');
6869

6970
page.visit();
7071

@@ -74,6 +75,14 @@ test('User can view single conversations', function(assert) {
7475

7576
andThen(() => {
7677
assert.equal(currentRouteName(), 'conversations.conversation');
78+
79+
let conversation = store.peekRecord('conversation', server.db.conversations[0].id);
80+
let firstPart = conversation.get('sortedConversationParts').get('firstObject');
81+
let lastPart = conversation.get('sortedConversationParts').get('lastObject');
82+
assert.equal(page.conversationThread.conversationParts().count, 11, 'Message head and conversation parts rendered');
83+
assert.equal(page.conversationThread.conversationParts(1).body.text, firstPart.get('body'), 'first conversation part is rendered correctly');
84+
assert.equal(page.conversationThread.conversationParts(10).body.text, lastPart.get('body'), 'last conversation part is rendered correctly');
85+
assert.ok(firstPart.get('insertedAt') < lastPart.get('insertedAt'), 'conversations are sorted correctly');
7786
});
7887
});
7988

tests/integration/components/conversations/conversation-thread-test.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ let page = PageObject.create(component);
1010
function renderPage() {
1111
page.render(hbs`
1212
{{conversations/conversation-thread
13+
sortedConversationParts=conversation.sortedConversationParts
1314
conversation=conversation
1415
send=send
1516
}}
@@ -60,32 +61,32 @@ test('it delays rendering head until loaded', function(assert) {
6061
assert.equal(page.conversationParts().count, 0, 'No part is rendered.');
6162
});
6263

63-
test('it renders each conversation part, sorted by insertedAt', function(assert) {
64+
test('it renders each conversation part', function(assert) {
6465
assert.expect(4);
6566

66-
let conversationParts = [
67+
let sortedConversationParts = [
6768
{ isLoaded: true, body: 'foo 1', insertedAt: new Date('2017-11-01') },
6869
{ isLoaded: true, body: 'foo 2', insertedAt: new Date('2017-12-01') },
6970
{ isLoaded: true, body: 'foo 3', insertedAt: new Date('2017-10-01') }
7071
];
71-
set(this, 'conversation', { conversationParts });
72+
set(this, 'conversation', { sortedConversationParts });
7273
renderPage();
7374

7475
assert.equal(page.conversationParts().count, 3, 'Each part is rendered.');
75-
assert.equal(page.conversationParts(1).body.text, 'foo 1', 'Part 1 is rendered second to last.');
76-
assert.equal(page.conversationParts(2).body.text, 'foo 2', 'Part 2 is rendered last (newest).');
77-
assert.equal(page.conversationParts(0).body.text, 'foo 3', 'Part 3 is rendered first (earliest date).');
76+
assert.equal(page.conversationParts(0).body.text, 'foo 1', 'Part 1 is rendered first');
77+
assert.equal(page.conversationParts(1).body.text, 'foo 2', 'Part 2 is rendered second');
78+
assert.equal(page.conversationParts(2).body.text, 'foo 3', 'Part 3 is rendered last');
7879
});
7980

8081
test('it delays rendering conversation parts not yet loaded', function(assert) {
8182
assert.expect(1);
8283

83-
let conversationParts = [
84+
let sortedConversationParts = [
8485
{ isLoaded: true, body: 'foo 1' },
8586
{ isLoaded: false, body: 'foo 2' },
8687
{ isLoaded: false, body: 'foo 3' }
8788
];
88-
set(this, 'conversation', { conversationParts });
89+
set(this, 'conversation', { sortedConversationParts });
8990
renderPage();
9091

9192
assert.equal(page.conversationParts().count, 1, 'Only loaded parts are rendered.');

tests/unit/models/conversation-test.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
11
import { moduleForModel, test } from 'ember-qunit';
22
import { testForAttributes } from 'code-corps-ember/tests/helpers/attributes';
33
import { testForBelongsTo } from 'code-corps-ember/tests/helpers/relationship';
4+
import { run } from '@ember/runloop';
45

56
moduleForModel('conversation', 'Unit | Model | conversation', {
6-
// Specify the other units that are required for this test.
77
needs: [
8+
'model:conversation',
9+
'model:conversation-part',
810
'model:message',
911
'model:user'
1012
]
1113
});
1214

13-
test('it exists', function(assert) {
14-
let model = this.subject();
15-
assert.ok(!!model);
15+
test('sortedConversationParts sorts insertedAt by asc', function(assert) {
16+
let laterPart, earlierPart;
17+
run(() => {
18+
laterPart = this.store().createRecord('conversation-part', { body: 'wat', insertedAt: new Date('2017-10-07') });
19+
earlierPart = this.store().createRecord('conversation-part', { body: 'foo', insertedAt: new Date('2017-10-06') });
20+
});
21+
let model = this.subject({ conversationParts: [laterPart, earlierPart] });
22+
assert.equal(model.get('sortedConversationParts.length'), 2);
23+
assert.equal(model.get('sortedConversationParts').get('firstObject').get('body'),
24+
earlierPart.get('body'),
25+
'the first conversation part is the earlier one');
26+
assert.equal(model.get('sortedConversationParts').get('lastObject').get('body'),
27+
laterPart.get('body'),
28+
'the secont conversation part is the later one');
1629
});
1730

1831
testForAttributes('conversation', ['insertedAt', 'readAt', 'status', 'updatedAt']);

0 commit comments

Comments
 (0)