diff --git a/app/components/create-status-form.hbs b/app/components/create-status-form.hbs index b878c07a6922b895e59a41c9ec9a052fb4718513..648fd2ed453eff711694ba5f617847d62bce4885 100644 --- a/app/components/create-status-form.hbs +++ b/app/components/create-status-form.hbs @@ -111,7 +111,7 @@ (get (service "error") "pipe") (scroll-to-bottom "[data-create-status-container]") )}} - {{focus-after-render}} + {{did-insert this.setCaretPosition}} {{class-list "compose__textarea compose--status__textarea" invalid=this.changeset.error.content }} diff --git a/app/components/create-status-form.js b/app/components/create-status-form.js index e4ca55d73cf128b5a29edf7a4335a453478923ca..b7337012f1efeabb08bc383cb77873f000c45bbf 100644 --- a/app/components/create-status-form.js +++ b/app/components/create-status-form.js @@ -72,7 +72,7 @@ export default Component.extend({ set(this.changeset, 'poll', this.createPoll()); } - if (this.compose === 'mention' && this.initialMentions) { + if ((this.compose === 'mention' || this.compose === 'reply') && this.initialMentions) { let content = ''; this.initialMentions.forEach((mention) => { @@ -137,6 +137,12 @@ export default Component.extend({ }, }, + setCaretPosition (textarea) { + let textLength = textarea.value.length; + textarea.focus(); + textarea.setSelectionRange(textLength, textLength); + }, + get statusValidations() { return { content: [ diff --git a/app/models/mention.js b/app/models/mention.js new file mode 100644 index 0000000000000000000000000000000000000000..1981008d3585732475a2b351fb3884c2079adc62 --- /dev/null +++ b/app/models/mention.js @@ -0,0 +1,8 @@ +import Model, { attr } from '@ember-data/model'; +import LoadableModel from 'ember-data-storefront/mixins/loadable-model'; + +export default Model.extend(LoadableModel, { + username: attr('string'), + url: attr('string'), + acct: attr('string'), +}); diff --git a/app/models/status.js b/app/models/status.js index 9704cddc057e151cbbcd2f164ef1008cfe9e704e..9b869756da726cc5b85b3ac93ec1bab4a288ad4c 100644 --- a/app/models/status.js +++ b/app/models/status.js @@ -46,6 +46,11 @@ export default Model.extend(LoadableModel, { inverse: null, }), + mentions: hasMany('mention', { + async: false, + inverse: null, + }), + ancestors: hasMany('status', { async: false, inverse: null }), descendants: hasMany('status', { async: false, inverse: null }), diff --git a/app/routes/application.js b/app/routes/application.js index d9506a988efdcc6688d46e4d767ae981bffeed0d..0aab4403a41a403b4ec71237b08dd710d5b10b40 100644 --- a/app/routes/application.js +++ b/app/routes/application.js @@ -121,10 +121,19 @@ export default Route.extend(ApplicationRouteMixin, { }, createReply(inReplyToStatus) { + let statusMentions = inReplyToStatus + .mentions.map(mention => mention.acct); + + let initialMentions = [inReplyToStatus.account.acct] + .concat(statusMentions) + .without(this.session.currentUser.acct) + .uniq(); + this.openStatusModal({ intent: 'reply', initialParticipants: [inReplyToStatus.account.id], inReplyToStatusId: inReplyToStatus.id, + initialMentions, }); }, diff --git a/app/serializers/status.js b/app/serializers/status.js index f95b90da2953c4640024f05c44a3f4023f50ed88..360c986228d6fdbb837b55f9554494b168cb8d51 100644 --- a/app/serializers/status.js +++ b/app/serializers/status.js @@ -15,6 +15,10 @@ export default ApplicationSerializer.extend(EmbeddedRecordsMixin, { deserialize: 'records', }, + mentions: { + deserialize: 'records', + }, + pleroma: { embedded: 'always', }, diff --git a/mirage/factories/mention.js b/mirage/factories/mention.js new file mode 100644 index 0000000000000000000000000000000000000000..a5cb6a7c21d518f86ae3fbcc54da9f6c4e04b7f1 --- /dev/null +++ b/mirage/factories/mention.js @@ -0,0 +1,9 @@ +import { Factory } from 'ember-cli-mirage'; +import faker from 'faker'; +import config from 'pleroma-pwa/config/environment'; + +export default Factory.extend({ + username() { return faker.internet.domainWord(); }, + url() { return `${config.APP.testApiBaseUrl}/users/${this.username}`; }, + acct() { return this.username }, +}); diff --git a/mirage/models/status.js b/mirage/models/status.js index 0269fa0f3cac00bf79a2935004da6e62a5fc4827..e0a2df29ac715092abf1488f917f78138a9e166b 100644 --- a/mirage/models/status.js +++ b/mirage/models/status.js @@ -6,4 +6,5 @@ export default Model.extend({ pleroma: belongsTo('status-extra'), reblog: belongsTo('status'), poll: belongsTo('poll'), + mentions: hasMany('mention'), }); diff --git a/mirage/serializers/status.js b/mirage/serializers/status.js index 318bb63ed3c5568b8c76c177d9ce8b009d191201..2b8335129d7c3ab4d73bd0f3c739a319a3267b30 100644 --- a/mirage/serializers/status.js +++ b/mirage/serializers/status.js @@ -8,6 +8,7 @@ export default ApplicationSerializer.extend({ 'pleroma', 'poll', 'reblog', + 'mentions', ]; }, diff --git a/tests/acceptance/reply-to-status-test.js b/tests/acceptance/reply-to-status-test.js index a7ff0f5e3ec3df81696fb7e18310b98ea0851a65..d5189ce93be95cef873a01bd6e75556fa23aef4f 100644 --- a/tests/acceptance/reply-to-status-test.js +++ b/tests/acceptance/reply-to-status-test.js @@ -265,4 +265,31 @@ module('Acceptance | replying to a status', function(hooks) { assert.dom(`${generateTestSelector('status-id', status.id)} ${generateTestSelector('replies-count')}`).hasText('1'); assert.dom(`${generateTestSelector('status-id', status.id)} ${generateTestSelector('replies-count')}`).hasAria('label', t('numReplies', { n: 1 })); }); + + test('it populates the form with the mentions of the thread participants', async function(assert) { + this.server.create('user', 'withToken', 'withSession'); + let statusAuthor = this.server.create('user'); + let mentions = this.server.createList('mention', 1); + let mentionedUser = mentions[0]; + + this.server.create('status', { + account: statusAuthor, + mentions, + }); + + await dangerouslyVisit('/feeds/public'); + + assert.dom(generateTestSelector('reply-action-item')).exists(); + + await click(generateTestSelector('reply-action-item')); + await settled(); + + assert.dom(generateTestSelector('create-status-modal')).exists(); + + await click(generateTestSelector('create-status-button')); + + assert.equal(currentURL(), '/feeds/public'); + + assert.dom(`${generateTestSelector('status')} ${generateTestSelector('feed-item-content')}`).hasText(`@${statusAuthor.acct} @${mentionedUser.acct}`); + }); });