Merge pull request #16386 from nextcloud/fix/noid/guest-store-ts

This commit is contained in:
Maksim Sukharev 2025-11-24 11:19:04 +01:00 committed by GitHub
commit ead2a61dea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 138 additions and 129 deletions

View file

@ -40,7 +40,7 @@ import Hex from 'crypto-js/enc-hex.js'
import SHA1 from 'crypto-js/sha1.js'
import TransitionWrapper from '../../UIShared/TransitionWrapper.vue'
import { useActorStore } from '../../../stores/actor.ts'
import { useGuestNameStore } from '../../../stores/guestName.js'
import { useGuestNameStore } from '../../../stores/guestName.ts'
const reactions = {
'❤️': 'Heart.gif',

View file

@ -32,7 +32,7 @@ import SHA1 from 'crypto-js/sha1.js'
import panzoom from 'panzoom'
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import VideoBottomBar from './VideoBottomBar.vue'
import { useGuestNameStore } from '../../../stores/guestName.js'
import { useGuestNameStore } from '../../../stores/guestName.ts'
import attachMediaStream from '../../../utils/attachmediastream.js'
const ZOOM_MIN = 1

View file

@ -107,7 +107,7 @@ import VideoBottomBar from './VideoBottomBar.vue'
import { ATTENDEE, AVATAR } from '../../../constants.ts'
import { EventBus } from '../../../services/EventBus.ts'
import { useCallViewStore } from '../../../stores/callView.ts'
import { useGuestNameStore } from '../../../stores/guestName.js'
import { useGuestNameStore } from '../../../stores/guestName.ts'
import attachMediaStream from '../../../utils/attachmediastream.js'
import { getDisplayNameWithFallback } from '../../../utils/getDisplayName.ts'
import { ConnectionState } from '../../../utils/webrtc/models/CallParticipantModel.js'

View file

@ -64,7 +64,7 @@ import NcModal from '@nextcloud/vue/components/NcModal'
import NcTextField from '@nextcloud/vue/components/NcTextField'
import Check from 'vue-material-design-icons/CheckBold.vue'
import ConversationIcon from './ConversationIcon.vue'
import { useGuestNameStore } from '../stores/guestName.js'
import { useGuestNameStore } from '../stores/guestName.ts'
export default {
name: 'GuestWelcomeWindow',

View file

@ -264,7 +264,7 @@ import { ATTENDEE, AVATAR, CALL, CONFIG, PARTICIPANT, VIRTUAL_BACKGROUND } from
import BrowserStorage from '../../services/BrowserStorage.js'
import { getTalkConfig } from '../../services/CapabilitiesManager.ts'
import { useActorStore } from '../../stores/actor.ts'
import { useGuestNameStore } from '../../stores/guestName.js'
import { useGuestNameStore } from '../../stores/guestName.ts'
import { useSettingsStore } from '../../stores/settings.ts'
import { localMediaModel } from '../../utils/webrtc/index.js'

View file

@ -56,7 +56,7 @@ import NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon'
import IconHeartOutline from 'vue-material-design-icons/HeartOutline.vue'
import AvatarWrapper from '../../../../AvatarWrapper/AvatarWrapper.vue'
import { ATTENDEE, AVATAR } from '../../../../../constants.ts'
import { useGuestNameStore } from '../../../../../stores/guestName.js'
import { useGuestNameStore } from '../../../../../stores/guestName.ts'
import { getDisplayNameWithFallback } from '../../../../../utils/getDisplayName.ts'
export default {

View file

@ -93,7 +93,7 @@ import IconHeartOutline from 'vue-material-design-icons/HeartOutline.vue'
import ReactionsList from './ReactionsList.vue'
import { ATTENDEE } from '../../../../../constants.ts'
import { useActorStore } from '../../../../../stores/actor.ts'
import { useGuestNameStore } from '../../../../../stores/guestName.js'
import { useGuestNameStore } from '../../../../../stores/guestName.ts'
import { useReactionsStore } from '../../../../../stores/reactions.js'
import { getDisplayNameWithFallback } from '../../../../../utils/getDisplayName.ts'

View file

@ -13,7 +13,7 @@ import MessagesGroup from './MessagesGroup.vue'
import { ATTENDEE, MESSAGE } from '../../../constants.ts'
import storeConfig from '../../../store/storeConfig.js'
import { useActorStore } from '../../../stores/actor.ts'
import { useGuestNameStore } from '../../../stores/guestName.js'
import { useGuestNameStore } from '../../../stores/guestName.ts'
describe('MessagesGroup.vue', () => {
const TOKEN = 'XXTOKENXX'

View file

@ -38,7 +38,7 @@ import MessageItem from './Message/MessageItem.vue'
import { useMessageInfo } from '../../../composables/useMessageInfo.ts'
import { ATTENDEE, AVATAR } from '../../../constants.ts'
import { useActorStore } from '../../../stores/actor.ts'
import { useGuestNameStore } from '../../../stores/guestName.js'
import { useGuestNameStore } from '../../../stores/guestName.ts'
export default {
name: 'MessagesGroup',

View file

@ -32,7 +32,7 @@ import escapeHtml from 'escape-html'
import AvatarWrapper from '../AvatarWrapper/AvatarWrapper.vue'
import { AVATAR } from '../../constants.ts'
import { useActorStore } from '../../stores/actor.ts'
import { useGuestNameStore } from '../../stores/guestName.js'
import { useGuestNameStore } from '../../stores/guestName.ts'
export default {
name: 'NewMessageTypingIndicator',

View file

@ -61,7 +61,7 @@ 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.js'
import { useGuestNameStore } from '../stores/guestName.ts'
const { compact = false } = defineProps<{
compact?: boolean

View file

@ -9,7 +9,7 @@ import { computed, ref } from 'vue'
import { useStore } from 'vuex'
import { ATTENDEE, CONVERSATION, MESSAGE } from '../../constants.ts'
import { useActorStore } from '../../stores/actor.ts'
import { useGuestNameStore } from '../../stores/guestName.js'
import { useGuestNameStore } from '../../stores/guestName.ts'
import { useConversationInfo } from '../useConversationInfo.ts'
import { useMessageInfo } from '../useMessageInfo.ts'

View file

@ -12,7 +12,7 @@ import { useStore } from 'vuex'
import { ATTENDEE, CONVERSATION, MESSAGE } from '../constants.ts'
import { hasTalkFeature } from '../services/CapabilitiesManager.ts'
import { useActorStore } from '../stores/actor.ts'
import { useGuestNameStore } from '../stores/guestName.js'
import { useGuestNameStore } from '../stores/guestName.ts'
import { ONE_DAY_IN_MS, ONE_HOUR_IN_MS } from '../utils/formattedTime.ts'
import { getDisplayNameWithFallback } from '../utils/getDisplayName.ts'
import { useConversationInfo } from './useConversationInfo.ts'

View file

@ -29,7 +29,7 @@ import { useActorStore } from '../stores/actor.ts'
import { useCallViewStore } from '../stores/callView.ts'
import { useChatStore } from '../stores/chat.ts'
import { useChatExtrasStore } from '../stores/chatExtras.ts'
import { useGuestNameStore } from '../stores/guestName.js'
import { useGuestNameStore } from '../stores/guestName.ts'
import { usePollsStore } from '../stores/polls.ts'
import { useReactionsStore } from '../stores/reactions.js'
import { useSharedItemsStore } from '../stores/sharedItems.ts'

View file

@ -30,7 +30,7 @@ import {
} from '../services/messagesService.ts'
import { useActorStore } from '../stores/actor.ts'
import { useChatStore } from '../stores/chat.ts'
import { useGuestNameStore } from '../stores/guestName.js'
import { useGuestNameStore } from '../stores/guestName.ts'
import { useReactionsStore } from '../stores/reactions.js'
import { generateOCSErrorResponse, generateOCSResponse } from '../test-helpers.js'
import CancelableRequest from '../utils/cancelableRequest.js'

View file

@ -34,7 +34,7 @@ import SessionStorage from '../services/SessionStorage.js'
import { talkBroadcastChannel } from '../services/talkBroadcastChannel.js'
import { useActorStore } from '../stores/actor.ts'
import { useCallViewStore } from '../stores/callView.ts'
import { useGuestNameStore } from '../stores/guestName.js'
import { useGuestNameStore } from '../stores/guestName.ts'
import pinia from '../stores/pinia.ts'
import { useSessionStore } from '../stores/session.ts'
import { useTokenStore } from '../stores/token.ts'

View file

@ -31,7 +31,7 @@ import {
} from '../services/participantsService.js'
import SessionStorage from '../services/SessionStorage.js'
import { useActorStore } from '../stores/actor.ts'
import { useGuestNameStore } from '../stores/guestName.js'
import { useGuestNameStore } from '../stores/guestName.ts'
import { useSessionStore } from '../stores/session.ts'
import { useTokenStore } from '../stores/token.ts'
import { generateOCSErrorResponse, generateOCSResponse } from '../test-helpers.js'

View file

@ -10,7 +10,7 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
import { setGuestUserName } from '../../services/participantsService.js'
import { generateOCSErrorResponse } from '../../test-helpers.js'
import { useActorStore } from '../actor.ts'
import { useGuestNameStore } from '../guestName.js'
import { useGuestNameStore } from '../guestName.ts'
vi.mock('../../services/participantsService', () => ({
setGuestUserName: vi.fn(),

View file

@ -7,7 +7,7 @@ import { createPinia, setActivePinia } from 'pinia'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { ATTENDEE, PARTICIPANT } from '../../constants.ts'
import vuexStore from '../../store/index.js'
import { useGuestNameStore } from '../guestName.js'
import { useGuestNameStore } from '../guestName.ts'
import { useSessionStore } from '../session.ts'
describe('sessionStore', () => {

View file

@ -1,109 +0,0 @@
/**
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { getGuestNickname, setGuestNickname } from '@nextcloud/auth'
import { t } from '@nextcloud/l10n'
import { defineStore } from 'pinia'
import { setGuestUserName } from '../services/participantsService.js'
import { useActorStore } from './actor.ts'
export const useGuestNameStore = defineStore('guestName', {
state: () => ({
guestNames: {},
guestUserName: getGuestNickname() || '',
}),
actions: {
/**
* Gets the participant display name
*
* @param {string} token the conversation's token
* @param {string} actorId the participant actorId
* @return {string} the participant name
*/
getGuestName(token, actorId) {
return this.guestNames[token]?.[actorId] ?? t('spreed', 'Guest')
},
/**
* Gets the participant display name with suffix
* if the display name is not default translatable Guest
*
* @param {string} token the conversation's token
* @param {string} actorId the participant actorId
* @return {string} the participant name with/without suffix
*/
getGuestNameWithGuestSuffix(token, actorId) {
const displayName = this.getGuestName(token, actorId)
if (displayName === t('spreed', 'Guest')) {
return displayName
}
return t('spreed', '{guest} (guest)', {
guest: displayName,
})
},
/**
* Adds a guest name to the store
*
* @param {object} data the wrapping object
* @param {string} data.token the token of the conversation
* @param {string} data.actorId the guest
* @param {string} data.actorDisplayName the display name to set
* @param {object} options options
* @param {boolean} options.noUpdate Override the display name or set it if it is empty
*/
addGuestName({ token, actorId, actorDisplayName }, { noUpdate }) {
if (!this.guestNames[token]) {
this.guestNames[token] = {}
}
if (!this.guestNames[token][actorId] || actorDisplayName === '') {
this.guestNames[token][actorId] = t('spreed', 'Guest')
} else if (noUpdate) {
return
}
if (actorDisplayName) {
this.guestNames[token][actorId] = actorDisplayName
}
},
/**
* Add the submitted guest name to the store
*
* @param {string} token the token of the conversation
* @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)
try {
actorStore.setDisplayName(name)
this.addGuestName({
token,
actorId,
actorDisplayName: name,
}, { noUpdate: false })
await setGuestUserName(token, name)
setGuestNickname(name)
} catch (error) {
actorStore.setDisplayName(previousName)
this.addGuestName({
token,
actorId,
actorDisplayName: previousName,
}, { noUpdate: false })
console.error(error)
}
},
},
})

118
src/stores/guestName.ts Normal file
View file

@ -0,0 +1,118 @@
/**
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { getGuestNickname, setGuestNickname } from '@nextcloud/auth'
import { t } from '@nextcloud/l10n'
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { setGuestUserName } from '../services/participantsService.js'
import { useActorStore } from './actor.ts'
type AddGuestNamePayload = { token: string, actorId: string, actorDisplayName: string }
export const useGuestNameStore = defineStore('guestName', () => {
const LOCALIZED_GUEST = t('spreed', 'Guest')
/** A map of guest names per conversation token and actorId */
const guestNames = ref<Record<string, Record<string, string>>>({})
/** An own display name of a current guest-user */
const guestUserName = ref(getGuestNickname() || '')
/**
* Gets the participant display name
*
* @param token the conversation's token
* @param actorId the participant actorId
*/
function getGuestName(token: string, actorId: string): string {
return guestNames.value[token]?.[actorId] ?? LOCALIZED_GUEST
}
/**
* Gets the participant display name with suffix
* if the display name is not default, gets localized 'Guest'
*
* @param token the conversation's token
* @param actorId the participant actorId
*/
function getGuestNameWithGuestSuffix(token: string, actorId: string): string {
const guest = getGuestName(token, actorId)
if (guest === LOCALIZED_GUEST) {
return guest
}
return t('spreed', '{guest} (guest)', { guest })
}
/**
* Adds a guest name to the store
*
* @param payload the wrapping object
* @param payload.token the token of the conversation
* @param payload.actorId the guest id
* @param payload.actorDisplayName the display name to set
* @param options options
* @param options.noUpdate Override the display name or set it, if it is empty
*/
function addGuestName({ token, actorId, actorDisplayName }: AddGuestNamePayload, { noUpdate }: { noUpdate: boolean }) {
if (!guestNames.value[token]) {
guestNames.value[token] = {}
}
if (!guestNames.value[token][actorId] || actorDisplayName === '') {
guestNames.value[token][actorId] = LOCALIZED_GUEST
} else if (noUpdate) {
return
}
if (actorDisplayName) {
guestNames.value[token][actorId] = actorDisplayName
}
}
/**
* Add the submitted guest name to the store
*
* @param token the token of the conversation
* @param name the new guest name
*/
async function submitGuestUsername(token: string, name: string) {
if (!name) {
return
}
const actorStore = useActorStore()
const actorId = actorStore.actorId!
const previousName = getGuestName(token, actorId)
try {
actorStore.setDisplayName(name)
addGuestName({
token,
actorId,
actorDisplayName: name,
}, { noUpdate: false })
await setGuestUserName(token, name)
setGuestNickname(name)
} catch (error) {
actorStore.setDisplayName(previousName)
addGuestName({
token,
actorId,
actorDisplayName: previousName,
}, { noUpdate: false })
console.error(error)
}
}
return {
guestUserName,
getGuestName,
getGuestNameWithGuestSuffix,
addGuestName,
submitGuestUsername,
}
})

View file

@ -17,7 +17,7 @@ import SHA1 from 'crypto-js/sha1.js'
import { defineStore } from 'pinia'
import { ATTENDEE, PARTICIPANT } from '../constants.ts'
import store from '../store/index.js'
import { useGuestNameStore } from './guestName.js'
import { useGuestNameStore } from './guestName.ts'
type Session = {
attendeeId: number | undefined