mirror of
https://github.com/nextcloud/spreed.git
synced 2025-12-17 21:12:20 +01:00
- disable new jsdoc rules - wrap some jsdoc inline tags - cleanup redundant close-on-click-outside Signed-off-by: Maksim Sukharev <antreesy.web@gmail.com>
185 lines
5.4 KiB
Vue
185 lines
5.4 KiB
Vue
<!--
|
|
- SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
|
|
- SPDX-License-Identifier: AGPL-3.0-or-later
|
|
-->
|
|
|
|
<template>
|
|
<div class="username-form">
|
|
<!-- eslint-disable-next-line vue/no-v-html -->
|
|
<h3 v-if="!compact" v-html="displayNameLabel" />
|
|
|
|
<NcButton
|
|
v-if="!isEditingUsername && !compact"
|
|
@click="toggleEdit">
|
|
{{ t('spreed', 'Edit display name') }}
|
|
<template #icon>
|
|
<IconPencilOutline :size="20" />
|
|
</template>
|
|
</NcButton>
|
|
|
|
<div v-else class="username-form__display-name">
|
|
<IconAccountOutline class="username-form__display-name-icon" :size="20" />
|
|
<NcTextField
|
|
ref="usernameInput"
|
|
v-model="guestUserName"
|
|
:placeholder="t('spreed', 'Guest')"
|
|
class="username-form__input"
|
|
:label="t('spreed', 'Display name (required)')"
|
|
:show-trailing-button="!!guestUserName && !compact"
|
|
trailing-button-icon="arrowEnd"
|
|
:trailing-button-label="t('spreed', 'Save name')"
|
|
@trailing-button-click="!compact ? updateDisplayName() : null"
|
|
@keydown.enter="!compact ? updateDisplayName() : null"
|
|
@keydown.esc="toggleEdit" />
|
|
</div>
|
|
|
|
<div class="login-info">
|
|
<span> {{ t('spreed', 'Do you already have an account?') }}</span>
|
|
<NcButton
|
|
class="login-info__button"
|
|
variant="secondary"
|
|
:href="loginUrl">
|
|
{{ t('spreed', 'Log in') }}
|
|
</NcButton>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { NextcloudUser } from '@nextcloud/auth'
|
|
|
|
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
|
|
import { t } from '@nextcloud/l10n'
|
|
import { generateUrl } from '@nextcloud/router'
|
|
import debounce from 'debounce'
|
|
import escapeHtml from 'escape-html'
|
|
import { computed, nextTick, onBeforeUnmount, ref, useTemplateRef, watch } from 'vue'
|
|
import NcButton from '@nextcloud/vue/components/NcButton'
|
|
import NcTextField from '@nextcloud/vue/components/NcTextField'
|
|
import IconAccountOutline from 'vue-material-design-icons/AccountOutline.vue'
|
|
import IconPencilOutline from 'vue-material-design-icons/PencilOutline.vue'
|
|
import { useGetToken } from '../composables/useGetToken.ts'
|
|
import { EventBus } from '../services/EventBus.ts'
|
|
import { useActorStore } from '../stores/actor.ts'
|
|
import { useGuestNameStore } from '../stores/guestName.ts'
|
|
|
|
const { compact = false } = defineProps<{
|
|
compact?: boolean
|
|
}>()
|
|
|
|
const loginUrl = `${generateUrl('/login')}?redirect_url=${encodeURIComponent(window.location.pathname)}`
|
|
|
|
const actorStore = useActorStore()
|
|
const guestNameStore = useGuestNameStore()
|
|
const token = useGetToken()
|
|
|
|
const usernameInput = useTemplateRef('usernameInput')
|
|
|
|
const guestUserName = computed({
|
|
get: () => guestNameStore.guestUserName,
|
|
set: (newValue: string) => {
|
|
guestNameStore.guestUserName = newValue
|
|
debounceUpdateDisplayName()
|
|
},
|
|
})
|
|
const isEditingUsername = ref(false)
|
|
|
|
const actorDisplayName = computed<string>(() => actorStore.displayName || guestUserName.value)
|
|
const displayNameLabel = computed(() => t('spreed', 'Display name: {name}', {
|
|
name: `<strong>${escapeHtml(actorDisplayName.value)}</strong>`,
|
|
}, { escape: false }))
|
|
const debounceUpdateDisplayName = debounce(updateDisplayName, 10_000)
|
|
|
|
watch(actorDisplayName, (newValue) => {
|
|
if (newValue && newValue !== guestUserName.value) {
|
|
guestUserName.value = newValue
|
|
}
|
|
})
|
|
|
|
/** Initially set displayName in store, if available from BrowserStorage */
|
|
let delayUpdateDisplayName = false
|
|
if (guestUserName.value && !actorStore.displayName) {
|
|
actorStore.setDisplayName(guestUserName.value)
|
|
delayUpdateDisplayName = true
|
|
}
|
|
|
|
/** Update guest displayName for other attendees */
|
|
EventBus.once('joined-conversation', () => {
|
|
if (guestUserName.value && delayUpdateDisplayName) {
|
|
console.debug('Saving guest name from browser storage to the session')
|
|
updateDisplayName()
|
|
}
|
|
})
|
|
|
|
/** Update guest displayName from public event (e.g. `@nextcloud/auth`) */
|
|
subscribe('user:info:changed', updateDisplayNameFromPublicEvent)
|
|
onBeforeUnmount(() => {
|
|
unsubscribe('user:info:changed', updateDisplayNameFromPublicEvent)
|
|
debounceUpdateDisplayName.flush?.()
|
|
})
|
|
|
|
/**
|
|
* Update guest username from public page user menu
|
|
*
|
|
* @param payload
|
|
*/
|
|
function updateDisplayNameFromPublicEvent(payload: NextcloudUser) {
|
|
if (payload.displayName && payload.displayName !== guestUserName.value) {
|
|
guestUserName.value = payload.displayName
|
|
updateDisplayName()
|
|
}
|
|
}
|
|
|
|
/** 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
|
|
}
|
|
|
|
/** Toggle editing state of username */
|
|
function toggleEdit() {
|
|
isEditingUsername.value = !isEditingUsername.value
|
|
if (isEditingUsername.value) {
|
|
nextTick(() => {
|
|
usernameInput.value!.focus()
|
|
})
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.username-form {
|
|
|
|
&__display-name {
|
|
display: flex;
|
|
gap: var(--default-grid-baseline);
|
|
flex-direction: row;
|
|
margin-block-start: 6px; // moved from NcTextField
|
|
|
|
&-icon {
|
|
flex-shrink: 0;
|
|
margin-inline-end: var(--default-grid-baseline);
|
|
}
|
|
}
|
|
}
|
|
|
|
.login-info {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: calc(var(--default-grid-baseline) * 2);
|
|
padding: calc(var(--default-grid-baseline) * 2) calc(var(--default-grid-baseline) * 2) 0;
|
|
margin-inline-start: calc(var(--default-grid-baseline) + 20px); // 20px for checkbox alignment
|
|
|
|
&__button {
|
|
flex-shrink: 0;
|
|
}
|
|
}
|
|
|
|
:deep(.input-field) {
|
|
margin-block-start: 0;
|
|
}
|
|
|
|
</style>
|