Skip to content

Commit 7dc7679

Browse files
authored
Merge pull request #1648 from code-corps/add-message-sounds
Adds message sounds and handle errors of sending
2 parents 18fc8fd + b597783 commit 7dc7679

File tree

15 files changed

+132
-20
lines changed

15 files changed

+132
-20
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
import Component from '@ember/component';
2+
import { get, set } from '@ember/object';
23
import { empty } from '@ember/object/computed';
34

45
export default Component.extend({
56
classNames: ['conversation-composer'],
67

78
body: null,
89

9-
submitDisabled: empty('body')
10+
submitDisabled: empty('body'),
11+
12+
send(body) {
13+
this._unsetBody();
14+
return get(this, 'onSend')(body).catch(() => this._resetBody(body));
15+
},
16+
17+
_resetBody(body) {
18+
set(this, 'body', body);
19+
},
20+
21+
_unsetBody() {
22+
set(this, 'body', null);
23+
}
1024
});

app/components/signup-form.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1+
import Ember from 'ember';
12
import Component from '@ember/component';
23
import { gte, and, alias } from '@ember/object/computed';
34
import { later } from '@ember/runloop';
45
import { set, get, computed } from '@ember/object';
56
import { task } from 'ember-concurrency';
67

8+
const { testing } = Ember;
9+
10+
const SHAKE_DELAY = testing ? 0 : 1000;
11+
712
export default Component.extend({
813
classNames: ['form--centered', 'signup-form'],
914
emailValid: false,
@@ -45,7 +50,7 @@ export default Component.extend({
4550
set(this, 'hasError', true);
4651
later(this, function() {
4752
set(this, 'hasError', false);
48-
}, 1000);
53+
}, SHAKE_DELAY);
4954
}
5055
},
5156

app/controllers/conversations/conversation.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Controller from '@ember/controller';
22
import { get } from '@ember/object';
33
import { alias } from '@ember/object/computed';
44
import { inject as service } from '@ember/service';
5+
import { reject } from 'rsvp';
56

67
export default Controller.extend({
78
currentUser: service(),
@@ -15,6 +16,13 @@ export default Controller.extend({
1516
let user = get(this, 'user');
1617

1718
let params = { author: user, body, conversation };
18-
return store.createRecord('conversation-part', params).save();
19+
let part = store.createRecord('conversation-part', params);
20+
21+
let onFailedSave = (reason) => {
22+
part.deleteRecord();
23+
return reject(reason);
24+
};
25+
26+
return part.save().catch(onFailedSave);
1927
}
2028
});

app/controllers/project/conversations/conversation.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Controller from '@ember/controller';
22
import { get } from '@ember/object';
33
import { alias } from '@ember/object/computed';
44
import { inject as service } from '@ember/service';
5+
import { reject } from 'rsvp';
56

67
export default Controller.extend({
78
currentUser: service(),
@@ -15,6 +16,13 @@ export default Controller.extend({
1516
let user = get(this, 'user');
1617

1718
let params = { author: user, body, conversation };
18-
return store.createRecord('conversation-part', params).save();
19+
let part = store.createRecord('conversation-part', params);
20+
21+
let onFailedSave = (reason) => {
22+
part.deleteRecord();
23+
return reject(reason);
24+
};
25+
26+
return part.save().catch(onFailedSave);
1927
}
2028
});

app/services/conversation-channel.js

+16-5
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,40 @@
11
import { get } from '@ember/object';
2+
import { alias } from '@ember/object/computed';
23
import Service, { inject as service } from '@ember/service';
34

45
export default Service.extend({
6+
currentUser: service(),
57
socket: service(),
8+
sounds: service(),
69
store: service(),
710

11+
user: alias('currentUser.user'),
12+
813
async join(conversation) {
914
let socket = get(this, 'socket');
1015

11-
await socket.connect();
12-
1316
let id = get(conversation, 'id');
1417
let channel = socket.joinChannel(`conversation:${id}`);
1518

16-
channel.on('new:conversation-part', () => this._onNewMessage(conversation));
19+
channel.on('new:conversation-part', (conversationPart) =>
20+
this._onNewPart(conversation, conversationPart));
1721
},
1822

1923
leave(conversation) {
2024
let id = get(conversation, 'id');
2125
return get(this, 'socket').leaveChannel(`conversation:${id}`);
2226
},
2327

24-
_onNewMessage(conversation) {
28+
_onNewPart(conversation, conversationPart) {
2529
let store = get(this, 'store');
2630
let id = get(conversation, 'id');
27-
return store.findRecord('conversation', id);
31+
32+
return store.findRecord('conversation', id).then((result) => {
33+
if (conversationPart.author_id != get(this, 'user.id')) {
34+
get(this, 'sounds').popup();
35+
}
36+
37+
return result;
38+
});
2839
}
2940
});

app/services/sounds.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import Ember from 'ember';
2+
import { get } from '@ember/object';
3+
import Service from '@ember/service';
4+
import { task, timeout } from 'ember-concurrency';
5+
6+
const { testing } = Ember;
7+
8+
const POPUP_URL = 'https://d3pgew4wbk2vb1.cloudfront.net/sounds/pop-up.ogg';
9+
const TIME_BETWEEN_SOUNDS = testing ? 0 : 10000;
10+
11+
export default Service.extend({
12+
popup() {
13+
return get(this, '_play').perform(POPUP_URL);
14+
},
15+
16+
_play: task(function* (url) {
17+
let audio = get(this, 'audio') || new Audio(url);
18+
audio.play();
19+
yield timeout(TIME_BETWEEN_SOUNDS);
20+
}).drop()
21+
});

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
autoresize=true
55
classNameBindings="textareaFocused:focused"
66
max-height=350
7-
modifiedSubmit=(action (pipe (action send body) (action (mut body) null)))
7+
modifiedSubmit=(action send body)
88
name="markdown"
99
onBlur="blurTextarea"
1010
placeholder=placeholder
1111
tabindex=tabindex
1212
value=body}}
1313
</div>
1414
<div class="conversation-composer__footer">
15-
<button {{action (pipe (action send body) (action (mut body) null))}} disabled={{submitDisabled}} class="button default {{if submitDisabled "disabled"}}" data-test-submit-button>{{fa-icon "paper-plane"}} Send</button>
15+
<button {{action send body}} disabled={{submitDisabled}} class="button default {{if submitDisabled "disabled"}}" data-test-submit-button>{{fa-icon "paper-plane"}} Send</button>
1616
</div>

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
</div>
2222
<div class="conversation-thread__composer">
2323
{{conversations/conversation-composer
24-
send=(pipe (action send) (action scrollBottom))
24+
onSend=(pipe (action send) (action scrollBottom))
2525
}}
2626
</div>
2727
</div>

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

+22-2
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,23 @@ import { run } from '@ember/runloop';
44
import hbs from 'htmlbars-inline-precompile';
55
import PageObject from 'ember-cli-page-object';
66
import component from 'code-corps-ember/tests/pages/components/conversations/conversation-composer';
7+
import { reject, resolve } from 'rsvp';
78

89
let page = PageObject.create(component);
910

1011
function renderPage() {
1112
page.render(hbs`
12-
{{conversations/conversation-composer body=body send=onSend}}
13+
{{conversations/conversation-composer body=body onSend=onSend}}
1314
`);
1415
}
1516

1617
moduleForComponent('conversations/conversation-composer', 'Integration | Component | conversations/conversation composer', {
1718
integration: true,
1819
beforeEach() {
1920
page.setContext(this);
20-
set(this, 'onSend', () => {});
21+
set(this, 'onSend', () => {
22+
return resolve();
23+
});
2124
},
2225
afterEach() {
2326
page.removeContext();
@@ -43,6 +46,7 @@ test('sends out action and blanks out body when clicking submit', function(asser
4346
set(this, 'body', 'foo');
4447
set(this, 'onSend', (body) => {
4548
assert.equal(body, 'foo', 'Correct value was sent with action.');
49+
return resolve();
4650
});
4751

4852
renderPage();
@@ -51,12 +55,28 @@ test('sends out action and blanks out body when clicking submit', function(asser
5155
assert.equal(page.submittableTextarea.value, '', 'Body was blanked out.');
5256
});
5357

58+
test('resets the body when promise rejects on clicking submit', async function(assert) {
59+
assert.expect(3);
60+
61+
set(this, 'body', 'foo');
62+
set(this, 'onSend', (body) => {
63+
assert.equal(body, 'foo', 'Correct value was sent with action.');
64+
return reject({ body });
65+
});
66+
67+
renderPage();
68+
assert.equal(page.submittableTextarea.value, 'foo', 'Body is rendered correctly.');
69+
await page.submitButton.click();
70+
assert.equal(page.submittableTextarea.value, 'foo', 'Body was reset.');
71+
});
72+
5473
test('sends out action and blanks out body when hitting enter key', function(assert) {
5574
assert.expect(3);
5675

5776
set(this, 'body', 'foo');
5877
set(this, 'onSend', (body) => {
5978
assert.equal(body, 'foo', 'Correct value was sent with action.');
79+
return resolve();
6080
});
6181

6282
renderPage();

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

+7-3
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ import { set } from '@ember/object';
33
import hbs from 'htmlbars-inline-precompile';
44
import PageObject from 'ember-cli-page-object';
55
import component from 'code-corps-ember/tests/pages/components/conversations/conversation-thread';
6+
import { resolve } from 'rsvp';
67

78
let page = PageObject.create(component);
89

910
function renderPage() {
1011
page.render(hbs`
1112
{{conversations/conversation-thread
1213
conversation=conversation
13-
send=onSend
14+
send=send
1415
}}
1516
`);
1617
}
@@ -19,7 +20,9 @@ moduleForComponent('conversations/conversation-thread', 'Integration | Component
1920
integration: true,
2021
beforeEach() {
2122
page.setContext(this);
22-
set(this, 'onSend', () => {});
23+
set(this, 'send', () => {
24+
return resolve();
25+
});
2326
},
2427
afterEach() {
2528
page.removeContext();
@@ -91,8 +94,9 @@ test('it delays rendering conversation parts not yet loaded', function(assert) {
9194
test('it delegates send action from composer', function(assert) {
9295
assert.expect(1);
9396

94-
set(this, 'onSend', (body) => {
97+
set(this, 'send', (body) => {
9598
assert.equal(body, 'foo', 'Correct value was send with action.');
99+
return resolve();
96100
});
97101

98102
renderPage();

tests/test-helper.js

+3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ import { setApplication } from '@ember/test-helpers';
33
import { start } from 'ember-qunit';
44
import './helpers/flash-message';
55
import loadEmberExam from 'ember-exam/test-support/load';
6+
import { run } from '@ember/runloop';
67

78
loadEmberExam();
89

10+
run.later = run.next;
11+
912
setApplication(Application.create({ autoboot: false }));
1013

1114
start();

tests/unit/routes/conversations/conversation-test.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ moduleFor('route:conversations/conversation', 'Unit | Route | conversations/conv
77
'service:can',
88
'service:conversation-channel',
99
'service:conversations',
10+
'service:current-user',
1011
'service:metrics',
1112
'service:router-scroll',
1213
'service:scheduler',
1314
'service:session',
14-
'service:socket'
15+
'service:socket',
16+
'service:sounds'
1517
]
1618
});
1719

tests/unit/routes/project/conversations/conversation-test.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ moduleFor('route:project/conversations/conversation', 'Unit | Route | project/co
77
'service:can',
88
'service:conversation-channel',
99
'service:conversations',
10+
'service:current-user',
1011
'service:metrics',
1112
'service:router-scroll',
1213
'service:scheduler',
1314
'service:session',
14-
'service:socket'
15+
'service:socket',
16+
'service:sounds'
1517
]
1618
});
1719

tests/unit/services/conversation-channel-test.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { moduleFor, test } from 'ember-qunit';
33
moduleFor('service:conversation-channel', 'Unit | Service | conversation channel', {
44
// Specify the other units that are required for this test.
55
needs: [
6-
'service:socket'
6+
'service:current-user',
7+
'service:socket',
8+
'service:sounds'
79
]
810
});
911

tests/unit/services/sounds-test.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { moduleFor, test } from 'ember-qunit';
2+
3+
moduleFor('service:sounds', 'Unit | Service | sounds', {
4+
// Specify the other units that are required for this test.
5+
// needs: ['service:foo']
6+
});
7+
8+
// Replace this with your real tests.
9+
test('it exists', function(assert) {
10+
let service = this.subject();
11+
assert.ok(service);
12+
});

0 commit comments

Comments
 (0)