added i18n for backend errors

This commit is contained in:
2026-01-28 19:31:14 +01:00
parent f029a322c4
commit 81d9624c9e
15 changed files with 316 additions and 162 deletions

View File

@@ -1,8 +1,8 @@
{ {
"name": "frangipane-client", "name": "frangipane-client",
"private": true, "private": true,
"version": "1.0.1", "version": "1.0.2",
"backendVersion": "1.0.4", "backendVersion": "1.0.5",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -1,50 +1,69 @@
import { UpdateUserResponse } from '../types'; import { UpdateUserResponse } from '../types';
import { apiFetch } from './client' import { apiFetch, ApiError } from './client'
// import { upload } from '@tauri-apps/plugin-upload';
import { getAuthData } from '../store'; import { getAuthData } from '../store';
import { API } from '../main.ts'; import { API } from '../main.ts';
export function updateSettings(username: string, email: string, password: string) { export function updateSettings(username: string, email: string, password: string) {
return apiFetch<UpdateUserResponse>('/account/settings', { return apiFetch<UpdateUserResponse>('/account/settings', {
method: 'PUT', method: 'PUT',
body: JSON.stringify({ username, email, password }), body: JSON.stringify({ username, email, password }),
}); });
} }
export async function uploadAvatar( export async function uploadAvatar(
fileData: Uint8Array, fileData: Uint8Array,
onProgress: (progress: number, total: number) => void onProgress: (progress: number, total: number) => void
) { ) {
const auth = await getAuthData(); const auth = await getAuthData();
const url = `${API}/account/upload-avatar`; const url = `${API}/account/upload-avatar`;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('POST', url); xhr.open('POST', url);
xhr.setRequestHeader('Authorization', `Bearer ${auth.token}`); xhr.setRequestHeader('Authorization', `Bearer ${auth.token}`);
xhr.setRequestHeader('Content-Type', 'application/octet-stream'); xhr.setRequestHeader('Content-Type', 'application/octet-stream');
// Handle Progress if (xhr.upload && onProgress) {
if (xhr.upload && onProgress) { xhr.upload.onprogress = (event) => {
xhr.upload.onprogress = (event) => { if (event.lengthComputable) {
if (event.lengthComputable) { onProgress(event.loaded, event.total);
onProgress(event.loaded, event.total); }
};
} }
};
}
// Handle Response // Handle Response
xhr.onload = () => { xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) { if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response); resolve(xhr.response);
} else { } else {
reject(new Error(`Upload failed with status ${xhr.status}: ${xhr.responseText}`)); if (xhr.status === 413) {
} reject(new ApiError('FILE_TOO_LARGE', 'File too large'));
}; return;
}
xhr.onerror = () => reject(new Error('Network error during upload')); try {
const res = JSON.parse(xhr.responseText);
if (res && res.code && res.message) {
reject(new ApiError(res.code, res.message));
return;
}
} catch (e) {
}
xhr.send(fileData); reject(new Error(`Upload failed with status ${xhr.status}: ${xhr.responseText}`));
}); }
};
xhr.onerror = () => {
if (xhr.status === 413) {
reject(new ApiError('FILE_TOO_LARGE', 'File too large'));
return;
}
reject(new ApiError("UPLOAD_FAILED", "Failed uploading file."));
};
xhr.send(fileData);
});
} }

View File

@@ -3,47 +3,68 @@ import { getAuthData, clearAuthData } from '../store'
import { API } from '../main.ts' import { API } from '../main.ts'
import router from '../router' import router from '../router'
export async function apiFetch<T>( // Custom Error class to hold the backend code
path: string, export class ApiError extends Error {
options: RequestInit = {} code: string;
): Promise<T> {
const auth = await getAuthData()
const isFormData = options.body instanceof FormData; constructor(code: string, message: string) {
super(message);
const res = await fetch(`${API}${path}`, { this.name = 'ApiError';
...options, this.code = code;
method: options.method || 'GET', }
headers: { }
// Only add json header if it's not formdata
...(!isFormData ? { 'Content-Type': 'application/json' } : {}), export async function apiFetch<T>(
...(auth.token ? { Authorization: `Bearer ${auth.token}` } : {}), path: string,
...options.headers, options: RequestInit = {}
}, ): Promise<T> {
}) const auth = await getAuthData()
if (res.status === 401 && auth.token) { const isFormData = options.body instanceof FormData;
await clearAuthData()
router.push('/login') const res = await fetch(`${API}${path}`, {
throw new Error("Session expired") ...options,
} method: options.method || 'GET',
headers: {
// Handle error responses ...(!isFormData ? { 'Content-Type': 'application/json' } : {}),
if (!res.ok) { ...(auth.token ? { Authorization: `Bearer ${auth.token}` } : {}),
const text = await res.text() ...options.headers,
throw new Error(text || res.statusText) },
} })
// Get the response as text first // Invalid token?
const responseText = await res.text() if (res.status === 401) {
if (auth.token) {
if (!responseText) { await clearAuthData()
return {} as T router.push('/login')
} }
}
try {
return JSON.parse(responseText) as T // Handle error responses
} catch (e) { if (!res.ok) {
return responseText as unknown as T const text = await res.text()
}
try {
const json = JSON.parse(text);
if (json && json.code && json.message) {
throw new ApiError(json.code, json.message);
}
} catch (e) {
if (e instanceof ApiError) throw e;
}
throw new Error(text || res.statusText)
}
const responseText = await res.text()
if (!responseText) {
return {} as T
}
try {
return JSON.parse(responseText) as T
} catch (e) {
return responseText as unknown as T
}
} }

View File

@@ -1,95 +1,106 @@
<template> <template>
<div class="backdrop" @click.self="emit('close')"> <div class="backdrop" @click.self="emit('close')">
<form class="modal" @submit.prevent="submit"> <form class="modal" @submit.prevent="submit">
<h2>{{ $t('chat-create-title') }}</h2> <h2>{{ $t('chat-create-title') }}</h2>
<div class="input-group"> <p v-if="errorMessage" class="error-message">{{ errorMessage }}</p>
<label>{{ $t('chat-create-name') }}</label>
<input v-model="name" :placeholder="$t('chat-create-name-placeholder')" autofocus />
</div>
<!-- <div class="input-group">
<label>{{ $t('chat-create-name') }}</label>
<input v-model="name" :placeholder="$t('chat-create-name-placeholder')" autofocus />
</div>
<!--
<label class="checkbox"> <label class="checkbox">
<input type="checkbox" v-model="global" /> <input type="checkbox" v-model="global" />
<span>{{ $t('chat-create-global') }}</span> <span>{{ $t('chat-create-global') }}</span>
</label> </label>
--> -->
<div class="actions"> <div class="actions">
<button type="button" @click="emit('close')" class="secondary"> <button type="button" @click="emit('close')" class="secondary">
{{ $t('shared-cancel') }} {{ $t('shared-cancel') }}
</button> </button>
<button type="submit"> <button type="submit">
{{ $t('chat-create-submit') }} {{ $t('chat-create-submit') }}
</button> </button>
</div> </div>
</form> </form>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { createRoom } from '../api/rooms' import { createRoom } from '../api/rooms'
import type { Room } from '../types' import type { Room } from '../types'
import { useErrorTranslator } from '../errors';
const { translateError } = useErrorTranslator();
const errorMessage = ref('')
const name = ref('') const name = ref('')
const global = ref(false) const global = ref(false)
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'created', room: Room): void (e: 'created', room: Room): void
(e: 'close'): void (e: 'close'): void
}>() }>()
async function submit() { async function submit() {
const room = await createRoom(name.value, global.value) try {
emit('created', room) const room = await createRoom(name.value, global.value)
emit('close') emit('created', room)
name.value = '' emit('close')
global.value = false name.value = ''
global.value = false
}
catch (err) {
errorMessage.value = translateError(err);
}
} }
</script> </script>
<style scoped> <style scoped>
.backdrop { .backdrop {
position: fixed; position: fixed;
inset: 0; inset: 0;
background: rgba(0, 0, 0, 0.6); background: rgba(0, 0, 0, 0.6);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
z-index: 1000; z-index: 1000;
} }
.modal { .modal {
background: var(--panel); background: var(--panel);
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: var(--radius); border-radius: var(--radius);
padding: 1.5rem; padding: 1.5rem;
width: 100%; width: 100%;
max-width: 420px; max-width: 420px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1rem; gap: 1rem;
} }
.modal h2 { .modal h2 {
font-size: 1.25rem; font-size: 1.25rem;
font-weight: 600; font-weight: 600;
} }
.checkbox { .checkbox {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.5rem; gap: 0.5rem;
color: var(--muted); color: var(--muted);
font-size: 0.9rem; font-size: 0.9rem;
} }
.actions { .actions {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
gap: 0.5rem; gap: 0.5rem;
} }
</style> </style>

View File

@@ -33,8 +33,10 @@ import { ref } from 'vue'
import { sendRoomInvite } from '../api/rooms' import { sendRoomInvite } from '../api/rooms'
import { sendFriendRequest } from '../api/friends'; import { sendFriendRequest } from '../api/friends';
import { useFluent } from 'fluent-vue'; import { useFluent } from 'fluent-vue';
import { useErrorTranslator } from '../errors';
const { $t } = useFluent(); const { $t } = useFluent();
const { translateError } = useErrorTranslator();
const props = defineProps<{ room_uuid: string }>(); const props = defineProps<{ room_uuid: string }>();
@@ -50,10 +52,7 @@ async function submit() {
errorMessage.value = '' errorMessage.value = ''
const username = receiverUsername.value.trim() const username = receiverUsername.value.trim()
if (!username) { if (!username) return
// errorMessage.value = 'Username is required.'
return
}
try { try {
await sendRoomInvite(username, props.room_uuid) await sendRoomInvite(username, props.room_uuid)
@@ -67,7 +66,7 @@ async function submit() {
emit('close') emit('close')
} catch (err: any) { } catch (err: any) {
errorMessage.value = err?.message || err || $t('shared-error'); errorMessage.value = translateError(err);
} }
} }
</script> </script>

View File

@@ -41,8 +41,10 @@ import { updateSettings } from '../api/account'
import { updateLocalUser } from '../store' import { updateLocalUser } from '../store'
import type { User } from '../types' import type { User } from '../types'
import { useFluent } from 'fluent-vue'; import { useFluent } from 'fluent-vue';
import { useErrorTranslator } from '../errors';
const { $t } = useFluent(); const { $t } = useFluent();
const { translateError } = useErrorTranslator();
const props = defineProps<{ user: User | null }>() const props = defineProps<{ user: User | null }>()
const emit = defineEmits(['close', 'updated']) const emit = defineEmits(['close', 'updated'])
@@ -84,7 +86,7 @@ async function submit() {
emit('updated') emit('updated')
emit('close') emit('close')
} catch (err: any) { } catch (err: any) {
errorMessage.value = err?.message || $t('settings-error-failed') errorMessage.value = translateError(err);
} finally { } finally {
isSubmitting.value = false isSubmitting.value = false
} }

View File

@@ -47,8 +47,10 @@ import { refreshLocalUser } from '../store.ts';
import { getAuthData } from '../store.ts'; import { getAuthData } from '../store.ts';
import { refreshAvatar } from '../store.ts'; import { refreshAvatar } from '../store.ts';
import { useFluent } from 'fluent-vue'; import { useFluent } from 'fluent-vue';
import { useErrorTranslator } from '../errors.ts';
const { $t } = useFluent(); const { $t } = useFluent();
const { translateError } = useErrorTranslator();
const emit = defineEmits(['close', 'updated']); const emit = defineEmits(['close', 'updated']);
@@ -166,7 +168,8 @@ async function handleUpload() {
emit('close'); emit('close');
} catch (err: any) { } catch (err: any) {
console.error("Upload failed:", err); console.error("Upload failed:", err);
errorMessage.value = $t('settings-error-upload-avatar-failed-upload'); const msg = translateError(err);
errorMessage.value = msg !== 'An error occurred' ? msg : $t('settings-error-upload-avatar-failed-upload');
isSubmitting.value = false; isSubmitting.value = false;
} }
} }

30
src/errors.ts Normal file
View File

@@ -0,0 +1,30 @@
import { useFluent } from 'fluent-vue';
import { ApiError } from './api/client';
export function useErrorTranslator() {
const { $t } = useFluent();
function translateError(err: unknown): string {
if (err instanceof ApiError) {
// Convert "AUTH_INVALID_CREDENTIALS" -> "error-auth-invalid-credentials"
const key = `error-${err.code.toLowerCase().replace(/_/g, '-')}`;
const translated = $t(key);
// Fallback to the message provided by backend.
if (translated === key) {
return err.message;
}
return translated;
}
if (err instanceof Error) {
return err.message;
}
return $t('shared-error');
}
return { translateError };
}

View File

@@ -18,7 +18,6 @@ auth-login-btn = Login
auth-register-btn = Create Account auth-register-btn = Create Account
auth-no-account = Don't have an account? auth-no-account = Don't have an account?
auth-has-account = Already have an account? auth-has-account = Already have an account?
auth-error-unknown = An unknown error occurred
auth-error-password-match = Passwords do not match auth-error-password-match = Passwords do not match
auth-error-password-length = Password must be at least 8 characters long auth-error-password-length = Password must be at least 8 characters long
auth-error-email-invalid = Please enter a valid email address auth-error-email-invalid = Please enter a valid email address
@@ -130,3 +129,34 @@ shared-confirm = Confirm
## Notifications ## Notifications
notifications-message-title = New {$messageType} message from {$senderUsername} notifications-message-title = New {$messageType} message from {$senderUsername}
## Errors (backend)
error-auth-invalid-credentials = Invalid email or password.
error-auth-missing-token = Authentication missing.
error-auth-invalid-token = Session expired or invalid.
error-user-not-found = User not found.
error-user-email-taken = Email is already in use.
error-user-username-taken = Username is already taken.
error-user-username-length = Username must be 1-35 characters long.
error-user-invalid-email = Invalid email format.
error-user-password-too-short = Password must be at least 8 characters.
error-user-empty-fields = Required fields are empty.
error-avatar-not-found = Avatar not found.
error-room-not-found = Room not found.
error-room-name-length = Room name must be 1-35 characters long.
error-room-not-member = You are not a member of this room.
error-room-already-member = This person is already a member.
error-room-owner-cannot-leave = Owner cannot leave the room without transferring ownership.
error-room-global-no-members = Cannot list members for global rooms.
error-invite-self = You cannot invite yourself.
error-invite-already-sent = Invite already sent.
error-invite-not-found = Invite not found.
error-friend-request-self = You cannot friend request yourself.
error-friend-already-exists = You are already friends.
error-friend-request-already-sent = Friend request already pending.
error-friend-request-not-found = Friend request not found.
error-friend-not-found = User is not in your friends list.
error-internal-server-error = An error occured.
error-internal-db-error = An error occured.
error-file-too-large = The file is too large (max 2MB).
error-upload-failed = Failed to upload the file.

View File

@@ -18,7 +18,6 @@ auth-login-btn = Se connecter
auth-register-btn = Créer un compte auth-register-btn = Créer un compte
auth-no-account = Pas encore de compte ? auth-no-account = Pas encore de compte ?
auth-has-account = Déjà un compte ? auth-has-account = Déjà un compte ?
auth-error-unknown = Une erreur inconnue est survenue
auth-error-password-match = Les mots de passe ne correspondent pas auth-error-password-match = Les mots de passe ne correspondent pas
auth-error-password-length = Le mot de passe doit faire au moins 8 caractères auth-error-password-length = Le mot de passe doit faire au moins 8 caractères
auth-error-email-invalid = Veuillez entrer une adresse email valide auth-error-email-invalid = Veuillez entrer une adresse email valide
@@ -128,3 +127,34 @@ shared-confirm = Confirmer
## Notifications ## Notifications
notifications-message-title = Nouveau message {$messageType} de {$senderUsername} notifications-message-title = Nouveau message {$messageType} de {$senderUsername}
## Errors (backend)
error-auth-invalid-credentials = Email ou mot de passe incorrect.
error-auth-missing-token = Authentification manquante.
error-auth-invalid-token = Session expirée ou invalide.
error-user-not-found = Utilisateur introuvable.
error-user-email-taken = L'adresse email est déjà utilisée.
error-user-username-taken = Ce nom d'utilisateur est déjà pris.
error-user-username-length = Le nom d'utilisateur doit faire 1-35 caractères.
error-user-invalid-email = Format d'email invalide.
error-user-password-too-short = Le mot de passe doit faire au moins 8 caractères.
error-user-empty-fields = Des champs requis sont vides.
error-avatar-not-found = Avatar introuvable.
error-room-not-found = Salon introuvable.
error-room-name-length = Le nom de la salle doit faire 1-35 caractères.
error-room-not-member = Vous n'êtes pas membre de ce salon.
error-room-already-member = Cette personne est déjà membre.
error-room-owner-cannot-leave = Le propriétaire ne peut pas quitter le salon sans transférer la propriété.
error-room-global-no-members = Impossible de lister les membres d'un salon global.
error-invite-self = Vous ne pouvez pas vous inviter vous-même.
error-invite-already-sent = Invitation déjà envoyée.
error-invite-not-found = Invitation introuvable.
error-friend-request-self = Vous ne pouvez pas vous ajouter en ami.
error-friend-already-exists = Vous êtes déjà amis.
error-friend-request-already-sent = Demande d'ami déjà en attente.
error-friend-request-not-found = Demande d'ami introuvable.
error-friend-not-found = L'utilisateur n'est pas dans votre liste d'amis.
error-internal-server-error = Une erreur est survenue.
error-internal-db-error = Une erreur est survenue.
error-file-too-large = Le fichier est trop volumineux (max 2Mo).
error-upload-failed = Erreur lors de l'envoi du fichier.

View File

@@ -54,9 +54,9 @@ async function init() {
init() init()
// export const API = 'http://127.0.0.1:8080' export const API = 'http://127.0.0.1:8080'
// export const API = 'http://192.168.1.183:8080' // export const API = 'http://192.168.1.183:8080'
export const API = 'https://alatreon.org/frangipane' // export const API = 'https://alatreon.org/frangipane'
// export const API_WS = 'ws://127.0.0.1:8080/ws' export const API_WS = 'ws://127.0.0.1:8080/ws'
// export const API_WS = 'ws://192.168.1.183:8080/ws' // export const API_WS = 'ws://192.168.1.183:8080/ws'
export const API_WS = 'wss://alatreon.org/frangipane/ws' // export const API_WS = 'wss://alatreon.org/frangipane/ws'

View File

@@ -50,12 +50,15 @@ import type { Friend } from '../types'
import { getAvatarUrl } from '../store' import { getAvatarUrl } from '../store'
import defaultAvatar from '../assets/default-avatar.png' import defaultAvatar from '../assets/default-avatar.png'
import UserProfileModal from '../components/UserProfileModal.vue' import UserProfileModal from '../components/UserProfileModal.vue'
import { useErrorTranslator } from '../errors'
const friends = ref<Friend[]>([]) const friends = ref<Friend[]>([])
const username = ref('') const username = ref('')
const errorMessage = ref('') const errorMessage = ref('')
const isLoading = ref(true) const isLoading = ref(true)
const { translateError } = useErrorTranslator();
const selectedFriend = ref<Friend | null>(null) const selectedFriend = ref<Friend | null>(null)
async function loadFriends() { async function loadFriends() {
@@ -91,7 +94,7 @@ async function send() {
errorMessage.value = '' errorMessage.value = ''
await loadFriends() await loadFriends()
} catch (err: any) { } catch (err: any) {
errorMessage.value = err errorMessage.value = translateError(err);
} }
} }
</script> </script>
@@ -143,7 +146,6 @@ async function send() {
.friends-list { .friends-list {
position: relative; position: relative;
min-width: 250px; min-width: 250px;
min-height: 200px;
background: var(--panel); background: var(--panel);
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: var(--radius); border-radius: var(--radius);

View File

@@ -24,6 +24,7 @@ import { ref } from "vue";
import { login } from '../store.ts' import { login } from '../store.ts'
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { useFluent } from 'fluent-vue'; import { useFluent } from 'fluent-vue';
import { useErrorTranslator } from "../errors.ts";
const email = ref(""); const email = ref("");
const password = ref(""); const password = ref("");
@@ -32,6 +33,7 @@ const errorMessage = ref("");
const router = useRouter(); const router = useRouter();
const { $t } = useFluent(); const { $t } = useFluent();
const { translateError } = useErrorTranslator();
async function submit() { async function submit() {
errorMessage.value = ""; errorMessage.value = "";
@@ -39,7 +41,8 @@ async function submit() {
await login(email.value, "", password.value); await login(email.value, "", password.value);
router.push("/"); router.push("/");
} catch (err: any) { } catch (err: any) {
errorMessage.value = err?.message || $t('auth-error-unknown'); errorMessage.value = translateError(err);
// errorMessage.value = err?.message || $t('auth-error-unknown');
} }
} }
</script> </script>

View File

@@ -47,8 +47,10 @@ import { fetchFriendRequests, acceptFriendRequest, declineFriendRequest } from '
import { fetchRoomInvites, acceptRoomInvite, declineRoomInvite } from '../api/rooms.ts' import { fetchRoomInvites, acceptRoomInvite, declineRoomInvite } from '../api/rooms.ts'
import { useNotifications } from '../store' import { useNotifications } from '../store'
import { useFluent } from 'fluent-vue'; import { useFluent } from 'fluent-vue';
import { useErrorTranslator } from '../errors.ts';
const { $t } = useFluent(); const { $t } = useFluent();
const { translateError } = useErrorTranslator();
const errorMessage = ref('') const errorMessage = ref('')
const { requests, invites, refreshNotifications } = useNotifications() const { requests, invites, refreshNotifications } = useNotifications()
@@ -65,7 +67,8 @@ async function acceptFriend(senderUuid: string) {
requests.value = requests.value.filter(r => r.sender_uuid !== senderUuid) requests.value = requests.value.filter(r => r.sender_uuid !== senderUuid)
// fetchFriends().then(f => (friends.value = f)) // fetchFriends().then(f => (friends.value = f))
} catch (err) { } catch (err) {
errorMessage.value = $t('notifications-error-friend-accept') const msg = translateError(err);
errorMessage.value = msg !== $t('shared-error') ? msg : $t('notifications-error-friend-accept')
} }
} }
@@ -75,7 +78,8 @@ async function declineFriend(senderUuid: string) {
requests.value = requests.value.filter(r => r.sender_uuid !== senderUuid) requests.value = requests.value.filter(r => r.sender_uuid !== senderUuid)
// fetchFriends().then(f => (friends.value = f)) // fetchFriends().then(f => (friends.value = f))
} catch (err) { } catch (err) {
errorMessage.value = $t('notifications-error-friend-decline') const msg = translateError(err);
errorMessage.value = msg !== $t('shared-error') ? msg : $t('notifications-error-friend-decline')
} }
} }
@@ -84,8 +88,8 @@ async function acceptRoom(senderUuid: string, roomUuid: string) {
await acceptRoomInvite(senderUuid, roomUuid) await acceptRoomInvite(senderUuid, roomUuid)
invites.value = invites.value.filter(r => r.room_uuid !== roomUuid) invites.value = invites.value.filter(r => r.room_uuid !== roomUuid)
} catch (err) { } catch (err) {
errorMessage.value = $t('notifications-error-room-accept') const msg = translateError(err);
throw err errorMessage.value = msg !== $t('shared-error') ? msg : $t('notifications-error-room-accept')
} }
} }
@@ -94,8 +98,8 @@ async function declineRoom(senderUuid: string, roomUuid: string) {
await declineRoomInvite(senderUuid, roomUuid) await declineRoomInvite(senderUuid, roomUuid)
invites.value = invites.value.filter(r => r.room_uuid !== roomUuid) invites.value = invites.value.filter(r => r.room_uuid !== roomUuid)
} catch (err) { } catch (err) {
errorMessage.value = $t('notifications-error-room-decline') const msg = translateError(err);
throw err errorMessage.value = msg !== $t('shared-error') ? msg : $t('notifications-error-room-decline')
} }
} }
</script> </script>

View File

@@ -22,7 +22,7 @@
<button type="submit">{{ $t('auth-register-btn') }}</button> <button type="submit">{{ $t('auth-register-btn') }}</button>
<p class="login-link"> <p class="login-link">
{{ $t('auth-has-settings') }} <router-link to="/login">{{ $t('auth-login-title') }}</router-link> {{ $t('auth-has-account') }} <router-link to="/login">{{ $t('auth-login-title') }}</router-link>
</p> </p>
<p v-if="errorMessage" class="error-message">{{ errorMessage }}</p> <p v-if="errorMessage" class="error-message">{{ errorMessage }}</p>
@@ -35,8 +35,10 @@ import { ref } from "vue";
import { register } from '../store.ts' import { register } from '../store.ts'
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { useFluent } from 'fluent-vue'; import { useFluent } from 'fluent-vue';
import { useErrorTranslator } from '../errors';
const { $t } = useFluent(); const { $t } = useFluent();
const { translateError } = useErrorTranslator();
const email = ref(""); const email = ref("");
const username = ref(""); const username = ref("");
@@ -51,17 +53,15 @@ async function submit(event: Event) {
const form = event.target as HTMLFormElement; const form = event.target as HTMLFormElement;
// Check password length and email // if (!form.checkValidity()) {
if (!form.checkValidity()) { // if (password.value.length < 8) {
if (password.value.length < 8) { // errorMessage.value = $t('auth-error-password-length');
errorMessage.value = $t('auth-error-password-length'); // } else {
} else { // errorMessage.value = $t('auth-error-email-invalid');
errorMessage.value = $t('auth-error-email-invalid'); // }
} // return;
return; // }
}
// Check password match
if (password.value !== confirmPassword.value) { if (password.value !== confirmPassword.value) {
errorMessage.value = $t('auth-error-password-match'); errorMessage.value = $t('auth-error-password-match');
return; return;
@@ -71,7 +71,7 @@ async function submit(event: Event) {
await register(email.value, username.value, password.value); await register(email.value, username.value, password.value);
router.push("/"); router.push("/");
} catch (err: any) { } catch (err: any) {
errorMessage.value = err?.message || "An unknown error occurred"; errorMessage.value = translateError(err);
} }
} }
</script> </script>