Implement password prompt UI and logic in MultiChatDialog: Add a password entry panel with validation and error handling for room access. Update i18n files for localized password prompts in English, German, and Spanish.

This commit is contained in:
Torsten Schulz (local)
2026-03-04 23:34:55 +01:00
parent a48e907e50
commit fd41a53404
4 changed files with 110 additions and 31 deletions

View File

@@ -172,6 +172,23 @@
</div> </div>
</div> </div>
</div> </div>
<div v-if="!showColorPicker && passwordPromptVisible" class="room-password-panel">
<div class="room-password-title">{{ $t('chat.multichat.password.title') }}</div>
<div class="room-password-message">
{{ passwordPromptInvalid ? $t('chat.multichat.password.invalidPrompt', { room: passwordPromptRoom }) : $t('chat.multichat.password.requiredPrompt', { room: passwordPromptRoom }) }}
</div>
<div class="room-password-controls">
<input
v-model="passwordPromptValue"
class="room-password-input"
type="password"
:placeholder="$t('chat.multichat.password.inputLabel')"
@keyup.enter="submitRoomPassword"
/>
<button type="button" class="send-btn" @click="submitRoomPassword">{{ $t('chat.multichat.password.submit') }}</button>
<button type="button" class="create-room-reset-btn" @click="cancelRoomPassword">{{ $t('chat.multichat.password.cancel') }}</button>
</div>
</div>
<div v-if="!showColorPicker" class="multi-chat-input"> <div v-if="!showColorPicker" class="multi-chat-input">
<input v-model="input" @keyup.enter="sendMessage" class="chat-input" <input v-model="input" @keyup.enter="sendMessage" class="chat-input"
:placeholder="$t('chat.multichat.placeholder')" /> :placeholder="$t('chat.multichat.placeholder')" />
@@ -322,7 +339,10 @@ export default {
pendingRoomCreateAttempts: 0, pendingRoomCreateAttempts: 0,
pendingRoomCreateTimer: null, pendingRoomCreateTimer: null,
roomPasswords: {}, roomPasswords: {},
passwordPromptActive: false, passwordPromptVisible: false,
passwordPromptInvalid: false,
passwordPromptRoom: '',
passwordPromptValue: '',
// Palette state // Palette state
paletteWidth: 420, paletteWidth: 420,
paletteHeight: 220, paletteHeight: 220,
@@ -371,6 +391,10 @@ export default {
this.messages = []; this.messages = [];
this.usersInRoom = []; this.usersInRoom = [];
this.selectedTargetUser = null; this.selectedTargetUser = null;
this.passwordPromptVisible = false;
this.passwordPromptInvalid = false;
this.passwordPromptRoom = '';
this.passwordPromptValue = '';
} }
} }
}, },
@@ -411,38 +435,47 @@ export default {
if (!roomName) return ''; if (!roomName) return '';
return this.roomPasswords[roomName] || ''; return this.roomPasswords[roomName] || '';
}, },
async handleRoomPasswordError(errorCode) { handleRoomPasswordError(errorCode) {
const room = this.getSelectedRoomName(); const room = this.getSelectedRoomName();
if (!room || this.passwordPromptActive) return; if (!room || this.passwordPromptVisible) return;
this.passwordPromptActive = true; this.passwordPromptRoom = room;
try { this.passwordPromptInvalid = errorCode === 'room_password_invalid';
const isInvalid = errorCode === 'room_password_invalid'; this.passwordPromptValue = '';
const promptText = isInvalid this.passwordPromptVisible = true;
? this.$t('chat.multichat.password.invalidPrompt', { room }) },
: this.$t('chat.multichat.password.requiredPrompt', { room }); submitRoomPassword() {
const entered = window.prompt(promptText, ''); const room = this.passwordPromptRoom || this.getSelectedRoomName();
if (entered === null) { if (!room) {
this.messages.push({ this.passwordPromptVisible = false;
id: Date.now(), return;
user: 'System',
text: this.$t('chat.multichat.password.cancelled', { room })
});
return;
}
const password = entered.trim();
if (!password) {
this.messages.push({
id: Date.now(),
user: 'System',
text: this.$t('chat.multichat.password.empty')
});
return;
}
this.roomPasswords[room] = password;
this.sendWithToken({ type: 'join', room, password });
} finally {
this.passwordPromptActive = false;
} }
const password = (this.passwordPromptValue || '').trim();
if (!password) {
this.messages.push({
id: Date.now(),
user: 'System',
text: this.$t('chat.multichat.password.empty')
});
return;
}
this.roomPasswords[room] = password;
this.passwordPromptVisible = false;
this.passwordPromptInvalid = false;
this.passwordPromptRoom = '';
this.passwordPromptValue = '';
this.sendWithToken({ type: 'join', room, password });
},
cancelRoomPassword() {
const room = this.passwordPromptRoom || this.getSelectedRoomName();
this.passwordPromptVisible = false;
this.passwordPromptInvalid = false;
this.passwordPromptRoom = '';
this.passwordPromptValue = '';
this.messages.push({
id: Date.now(),
user: 'System',
text: this.$t('chat.multichat.password.cancelled', { room })
});
}, },
tryConfirmRoomCreateSuccess() { tryConfirmRoomCreateSuccess() {
if (!this.pendingRoomCreateName) return false; if (!this.pendingRoomCreateName) return false;
@@ -2162,6 +2195,40 @@ export default {
margin-top: 0.5em; margin-top: 0.5em;
} }
.room-password-panel {
margin-top: 0.6em;
margin-bottom: 0.6em;
padding: 0.6em;
border: 1px solid #d7d7d7;
border-radius: 6px;
background: #f8f9fb;
}
.room-password-title {
font-weight: bold;
margin-bottom: 0.25em;
}
.room-password-message {
font-size: 0.9em;
color: #444;
margin-bottom: 0.45em;
}
.room-password-controls {
display: flex;
align-items: center;
gap: 0.45em;
}
.room-password-input {
flex: 1;
min-width: 0;
border: 1px solid #bbb;
border-radius: 3px;
padding: 0.35em 0.5em;
}
.chat-message { .chat-message {
margin-bottom: 0.3em; margin-bottom: 0.3em;
} }

View File

@@ -116,6 +116,10 @@
"types": {} "types": {}
}, },
"password": { "password": {
"title": "Passwort erforderlich",
"inputLabel": "Passwort eingeben",
"submit": "Beitreten",
"cancel": "Abbrechen",
"requiredPrompt": "Der Raum \"{room}\" ist passwortgeschützt. Bitte Passwort eingeben:", "requiredPrompt": "Der Raum \"{room}\" ist passwortgeschützt. Bitte Passwort eingeben:",
"invalidPrompt": "Falsches Passwort für \"{room}\". Bitte erneut eingeben:", "invalidPrompt": "Falsches Passwort für \"{room}\". Bitte erneut eingeben:",
"cancelled": "Beitritt zu \"{room}\" abgebrochen.", "cancelled": "Beitritt zu \"{room}\" abgebrochen.",

View File

@@ -116,6 +116,10 @@
"types": {} "types": {}
}, },
"password": { "password": {
"title": "Password required",
"inputLabel": "Enter password",
"submit": "Join room",
"cancel": "Cancel",
"requiredPrompt": "Room \"{room}\" is password-protected. Please enter password:", "requiredPrompt": "Room \"{room}\" is password-protected. Please enter password:",
"invalidPrompt": "Wrong password for \"{room}\". Please try again:", "invalidPrompt": "Wrong password for \"{room}\". Please try again:",
"cancelled": "Join to \"{room}\" was cancelled.", "cancelled": "Join to \"{room}\" was cancelled.",

View File

@@ -115,6 +115,10 @@
"types": {} "types": {}
}, },
"password": { "password": {
"title": "Contraseña requerida",
"inputLabel": "Introduce la contraseña",
"submit": "Entrar en sala",
"cancel": "Cancelar",
"requiredPrompt": "La sala \"{room}\" está protegida por contraseña. Introduce la contraseña:", "requiredPrompt": "La sala \"{room}\" está protegida por contraseña. Introduce la contraseña:",
"invalidPrompt": "Contraseña incorrecta para \"{room}\". Inténtalo de nuevo:", "invalidPrompt": "Contraseña incorrecta para \"{room}\". Inténtalo de nuevo:",
"cancelled": "Se canceló el acceso a \"{room}\".", "cancelled": "Se canceló el acceso a \"{room}\".",