fix(guests): ease display name submission requirement flow

Signed-off-by: Dorra Jaouad <dorra.jaoued7@gmail.com>
This commit is contained in:
Dorra Jaouad 2025-08-14 13:03:20 +02:00
parent b24178fb21
commit 7fa33faeb4
5 changed files with 34 additions and 41 deletions

View file

@ -40,7 +40,6 @@ import moment from '@nextcloud/moment'
import NcRichText from '@nextcloud/vue/components/NcRichText'
import RoomService from 'vue-material-design-icons/RoomService.vue'
import MediaSettings from '../components/MediaSettings/MediaSettings.vue'
import GuestWelcomeWindow from './GuestWelcomeWindow.vue'
import { useGetToken } from '../composables/useGetToken.ts'
import { futureRelativeTime, ONE_DAY_IN_MS } from '../utils/formattedTime.ts'

View file

@ -171,7 +171,9 @@
</MediaSettingsTabs>
<!-- Guest display name setting-->
<SetGuestUsername v-if="isGuest" compact />
<SetGuestUsername v-if="isGuest"
compact
@update="guestUserName = $event" />
<!-- Moderator options before starting a call-->
<NcCheckboxRadioSwitch v-if="showStartRecordingOption"
@ -382,6 +384,7 @@ export default {
skipBlurVirtualBackground: false,
mediaLoading: false,
isDeviceCheck: false,
guestUserName: '',
}
},
@ -557,11 +560,11 @@ export default {
disabledCallButton() {
return (this.isRecordingConsentRequired && !this.recordingConsentGiven)
|| (this.isGuest && !this.actorStore.displayName.length)
|| (this.isGuest && !this.guestUserName.length)
},
forceShowMediaSettings() {
return this.isGuest && this.hasCall && this.isDialog
return !this.isInCall && this.isGuest && this.isDialog
},
},
@ -630,20 +633,17 @@ export default {
}
},
forceShowMediaSettings: {
intermediate: true,
handler(value) {
if (value) {
this.showMediaSettings()
}
},
},
connectionFailed(value) {
if (value) {
this.skipBlurVirtualBackground = false
}
},
hasCall(value) {
if (value && this.forceShowMediaSettings) {
this.showMediaSettings()
}
},
},
beforeMount() {

View file

@ -24,11 +24,11 @@
:placeholder="t('spreed', 'Guest')"
class="username-form__input"
:label="t('spreed', 'Display name (required)')"
:show-trailing-button="!!guestUserName"
:show-trailing-button="!!guestUserName && !compact"
trailing-button-icon="arrowEnd"
:trailing-button-label="t('spreed', 'Save name')"
@trailing-button-click="updateDisplayName"
@keydown.enter="updateDisplayName"
@trailing-button-click="!compact ? updateDisplayName() : null"
@keydown.enter="!compact ? updateDisplayName() : null"
@keydown.esc="toggleEdit" />
</div>
@ -63,7 +63,11 @@ import { useActorStore } from '../stores/actor.ts'
import { useGuestNameStore } from '../stores/guestName.js'
const { compact = false } = defineProps<{
compact: boolean
compact?: boolean
}>()
const emit = defineEmits<{
(event: 'update', value: string): void
}>()
const loginUrl = `${generateUrl('/login')}?redirect_url=${encodeURIComponent(window.location.pathname)}`
@ -76,7 +80,7 @@ const usernameInput = useTemplateRef('usernameInput')
const guestUserName = ref(getGuestNickname() || '')
const isEditingUsername = ref(false)
const actorDisplayName = computed<string>(() => actorStore.displayName || guestUserName.value || t('spreed', 'Guest'))
const actorDisplayName = computed<string>(() => actorStore.displayName || guestUserName.value)
const displayNameLabel = computed(() => t('spreed', 'Display name: {name}', {
name: `<strong>${escapeHtml(actorDisplayName.value)}</strong>`,
}, { escape: false }))
@ -104,6 +108,7 @@ EventBus.once('joined-conversation', () => {
subscribe('user:info:changed', updateDisplayNameFromPublicEvent)
onBeforeUnmount(() => {
unsubscribe('user:info:changed', updateDisplayNameFromPublicEvent)
updateDisplayName()
})
/** Update guest username from public page user menu */
@ -116,6 +121,9 @@ function updateDisplayNameFromPublicEvent(payload: NextcloudUser) {
/** Set guest username locally and send request to server to update for other attendees */
function updateDisplayName() {
if (!guestUserName.value) {
return
}
guestNameStore.submitGuestUsername(token.value, guestUserName.value)
isEditingUsername.value = false
}
@ -129,6 +137,11 @@ function toggleEdit() {
})
}
}
// One-way binding to parent component
watch(guestUserName, (newValue) => {
emit('update', newValue)
}, { immediate: true })
</script>
<style lang="scss" scoped>

View file

@ -170,28 +170,6 @@ describe('guestNameStore', () => {
expect(actorStore.displayName).toBe('actor 1')
})
test('removes display name from local storage when user sumbits an empty new name', async () => {
// Arrange
const actor1 = {
token: 'token-1',
actorId: 'actor-id1',
actorDisplayName: 'actor 1',
}
const newName = ''
actorStore.setCurrentUser({ uid: 'actor-id1' })
// Mock implementation of setGuestUserName
setGuestUserName.mockResolvedValue()
// Act
await store.submitGuestUsername(actor1.token, newName)
// Assert
expect(setGuestUserName).toHaveBeenCalledWith(actor1.token, newName)
expect(setGuestNickname).toHaveBeenCalledWith('Guest')
})
test('resets to previous display name if there is an error in setting the new one', async () => {
// Arrange
const actor1 = {

View file

@ -76,6 +76,9 @@ export const useGuestNameStore = defineStore('guestName', {
* @param {string} name the new guest name
*/
async submitGuestUsername(token, name) {
if (!name) {
return
}
const actorStore = useActorStore()
const actorId = actorStore.actorId
const previousName = this.getGuestName(token, actorId)
@ -90,7 +93,7 @@ export const useGuestNameStore = defineStore('guestName', {
await setGuestUserName(token, name)
setGuestNickname(name || t('spreed', 'Guest'))
setGuestNickname(name)
} catch (error) {
actorStore.setDisplayName(previousName)
this.addGuestName({