From 9698c341b2f0ffc67e7cfd1a233edda902eb9854 Mon Sep 17 00:00:00 2001 From: Maksim Sukharev Date: Fri, 25 Oct 2024 21:00:29 +0200 Subject: [PATCH] fix: show e-mail in subline of e-mail guests for moderators - show e-mail in toast when resend invitation - refactor tests Signed-off-by: Maksim Sukharev --- .../LinkShareSettings.vue | 7 +-- .../Participants/Participant.spec.js | 50 +++++++++++-------- .../RightSidebar/Participants/Participant.vue | 18 +++---- .../Participants/ParticipantsTab.vue | 5 +- src/services/participantsService.js | 4 +- src/store/participantsStore.js | 22 ++++++-- src/store/participantsStore.spec.js | 2 +- 7 files changed, 64 insertions(+), 44 deletions(-) diff --git a/src/components/ConversationSettings/LinkShareSettings.vue b/src/components/ConversationSettings/LinkShareSettings.vue index f2e5c229618..e64dd30a7fe 100644 --- a/src/components/ConversationSettings/LinkShareSettings.vue +++ b/src/components/ConversationSettings/LinkShareSettings.vue @@ -214,12 +214,7 @@ export default { async handleResendInvitations() { this.isSendingInvitations = true - try { - await this.$store.dispatch('resendInvitations', { token: this.token }) - showSuccess(t('spreed', 'Invitations sent')) - } catch (e) { - showError(t('spreed', 'Error occurred when sending invitations')) - } + await this.$store.dispatch('resendInvitations', { token: this.token }) this.isSendingInvitations = false }, }, diff --git a/src/components/RightSidebar/Participants/Participant.spec.js b/src/components/RightSidebar/Participants/Participant.spec.js index c89f053179e..6802f2ef45c 100644 --- a/src/components/RightSidebar/Participants/Participant.spec.js +++ b/src/components/RightSidebar/Participants/Participant.spec.js @@ -227,39 +227,43 @@ describe('Participant.vue', () => { /** * Check which status is currently rendered * @param {object} participant participant object - * @param {string|null} status status which expected to be rendered + * @param {string} [status] status which expected to be rendered */ async function checkUserSubnameRendered(participant, status) { const wrapper = mountParticipant(participant) await flushPromises() + const userSubname = wrapper.find('.participant__status') if (status) { - expect(wrapper.find('.participant__status').exists()).toBeTruthy() - expect(wrapper.find('.participant__status').text()).toBe(status) + expect(userSubname.exists()).toBeTruthy() + expect(userSubname.text()).toBe(status) } else { - expect(wrapper.find('.participant__status').exists()).toBeFalsy() + expect(userSubname.exists()).toBeFalsy() } } - test('renders user status', async () => { - await checkUserSubnameRendered(participant, '🌧️ rainy') - }) - - test('does not render user status when not set', async () => { - participant.statusIcon = '' - participant.statusMessage = '' - await checkUserSubnameRendered(participant, null) - }) + const testCases = [ + ['online', '', '', undefined], + ['online', '🌧️', 'Rainy', '🌧️ Rainy'], + ['dnd', '🌧️', 'Rainy', '🌧️ Rainy'], + ['dnd', '🌧️', '', '🌧️ Do not disturb'], + ['away', '🌧️', '', '🌧️ Away'], + ] - test('renders dnd status', async () => { - participant.statusMessage = '' - participant.status = 'dnd' - await checkUserSubnameRendered(participant, '🌧️ Do not disturb') - }) + it.each(testCases)('renders status for participant \'%s\', \'%s\', \'%s\' - \'%s\'', + (status, statusIcon, statusMessage, result) => { + checkUserSubnameRendered({ + ...participant, + status, + statusIcon, + statusMessage, + }, result) + }) - test('renders away status', async () => { - participant.statusMessage = '' - participant.status = 'away' - await checkUserSubnameRendered(participant, '🌧️ Away') + it('renders e-mail as status for e-mail guest', async () => { + participant.actorType = ATTENDEE.ACTOR_TYPE.EMAILS + participant.participantType = PARTICIPANT.TYPE.GUEST + participant.invitedActorId = 'test@mail.com' + await checkUserSubnameRendered(participant, 'test@mail.com') }) }) @@ -522,6 +526,7 @@ describe('Participant.vue', () => { test('allows moderators to resend invitations for email participants', async () => { conversation.participantType = PARTICIPANT.TYPE.MODERATOR participant.actorType = ATTENDEE.ACTOR_TYPE.EMAILS + participant.invitedActorId = 'alice@mail.com' const wrapper = mountParticipant(participant) const actionButton = findNcActionButton(wrapper, 'Resend invitation') expect(actionButton.exists()).toBe(true) @@ -531,6 +536,7 @@ describe('Participant.vue', () => { expect(resendInvitationsAction).toHaveBeenCalledWith(expect.anything(), { token: 'current-token', attendeeId: 'alice-attendee-id', + actorId: 'alice@mail.com', }) }) diff --git a/src/components/RightSidebar/Participants/Participant.vue b/src/components/RightSidebar/Participants/Participant.vue index b1ae97a57b2..3dd10c18489 100644 --- a/src/components/RightSidebar/Participants/Participant.vue +++ b/src/components/RightSidebar/Participants/Participant.vue @@ -512,6 +512,10 @@ export default { : '💬 ' + t('spreed', '{time} talking time', { time: formattedTime(this.timeSpeaking, true) }) } + if (this.isEmailActor && this.participant?.invitedActorId) { + return this.participant.invitedActorId + } + return getStatusMessage(this.participant) }, @@ -815,15 +819,11 @@ export default { }, async resendInvitation() { - try { - await this.$store.dispatch('resendInvitations', { - token: this.token, - attendeeId: this.attendeeId, - }) - showSuccess(t('spreed', 'Invitation was sent to {actorId}', { actorId: this.participant.actorId })) - } catch (error) { - showError(t('spreed', 'Could not send invitation to {actorId}', { actorId: this.participant.actorId })) - } + await this.$store.dispatch('resendInvitations', { + token: this.token, + attendeeId: this.attendeeId, + actorId: this.participant.invitedActorId ?? this.participant.actorId, + }) }, async sendCallNotification() { diff --git a/src/components/RightSidebar/Participants/ParticipantsTab.vue b/src/components/RightSidebar/Participants/ParticipantsTab.vue index 95058066a01..ce866f90b5d 100644 --- a/src/components/RightSidebar/Participants/ParticipantsTab.vue +++ b/src/components/RightSidebar/Participants/ParticipantsTab.vue @@ -65,7 +65,7 @@ import { ref, toRefs } from 'vue' import IconInformationOutline from 'vue-material-design-icons/InformationOutline.vue' -import { showError } from '@nextcloud/dialogs' +import { showError, showSuccess } from '@nextcloud/dialogs' import { subscribe, unsubscribe } from '@nextcloud/event-bus' import { t } from '@nextcloud/l10n' @@ -311,6 +311,9 @@ export default { async addParticipants(item) { try { await addParticipant(this.token, item.id, item.source) + if (item.source === ATTENDEE.ACTOR_TYPE.EMAILS) { + showSuccess(t('spreed', 'Invitation was sent to {actorId}', { actorId: item.id })) + } this.abortSearch() this.cancelableGetParticipants() } catch (exception) { diff --git a/src/services/participantsService.js b/src/services/participantsService.js index 1cc6be514e1..cc44122cde3 100644 --- a/src/services/participantsService.js +++ b/src/services/participantsService.js @@ -158,9 +158,9 @@ const setGuestUserName = async (token, userName) => { * If no userId is set, send to all applicable participants. * * @param {string} token conversation token - * @param {number} attendeeId attendee id to target, or null for all + * @param {number|null} [attendeeId] attendee id to target, or null for all */ -const resendInvitations = async (token, { attendeeId = null }) => { +const resendInvitations = async (token, attendeeId = null) => { await axios.post(generateOcsUrl('apps/spreed/api/v4/room/{token}/participants/resend-invitations', { token }), { attendeeId, }) diff --git a/src/store/participantsStore.js b/src/store/participantsStore.js index 9badbed70b6..576d572d94a 100644 --- a/src/store/participantsStore.js +++ b/src/store/participantsStore.js @@ -883,10 +883,26 @@ const actions = { * @param {object} _ - unused. * @param {object} data - the wrapping object. * @param {string} data.token - conversation token. - * @param {number} data.attendeeId - attendee id to target, or null for all. + * @param {number} [data.attendeeId] - attendee id to target, or null for all. + * @param {string} [data.actorId] - if attendee is provided, the actorId (e-mail) to show in the message. */ - async resendInvitations(_, { token, attendeeId }) { - await resendInvitations(token, { attendeeId }) + async resendInvitations(_, { token, attendeeId, actorId }) { + if (attendeeId) { + try { + await resendInvitations(token, attendeeId) + showSuccess(t('spreed', 'Invitation was sent to {actorId}', { actorId })) + } catch (error) { + showError(t('spreed', 'Could not send invitation to {actorId}', { actorId })) + } + } else { + try { + await resendInvitations(token) + showSuccess(t('spreed', 'Invitations sent')) + } catch (e) { + showError(t('spreed', 'Error occurred when sending invitations')) + } + } + }, /** diff --git a/src/store/participantsStore.spec.js b/src/store/participantsStore.spec.js index 06c0df290a7..8ff40b7be38 100644 --- a/src/store/participantsStore.spec.js +++ b/src/store/participantsStore.spec.js @@ -693,7 +693,7 @@ describe('participantsStore', () => { attendeeId: 1, }) - expect(resendInvitations).toHaveBeenCalledWith(TOKEN, { attendeeId: 1 }) + expect(resendInvitations).toHaveBeenCalledWith(TOKEN, 1) }) describe('joining conversation', () => {