More things

This commit is contained in:
2026-10-01 02:29:21 +02:00
parent e1df4d3ae1
commit 13b659662e
54 changed files with 4409 additions and 911 deletions

View File

@@ -1,14 +1,16 @@
<script setup lang="ts">
/* Centered profile popup, opened by the account row in the sidebar (and closed
with esc, a backdrop click, or navigation — the same shape as the settings
popup). It holds the account itself: the profile picture, the username and
the password, and the way out. */
popup). It holds the account itself: the profile picture, the username, the
password, the interface language the account reads the wiki in, and the
way out. */
const { open, hide } = useProfileMenu()
const { hide: hideSearch } = useSearch()
const { hide: hideSettings } = useSettings()
const { hide: hideSignIn, show: openSignIn } = useSignInPopup()
const { user, signOut, updateUsername, changePassword, setAvatar } = useAuth()
const { t } = useI18n()
const { user, signOut, updateUsername, changePassword, setAvatar, updateLocale } = useAuth()
const { t, locale } = useI18n()
const { choose } = useAppLocale()
const route = useRoute()
const panelEl = ref<HTMLElement | null>(null)
@@ -166,6 +168,24 @@ function leave() {
hide()
}
/* --- the interface language ---
Turn the language Mathew speaks in. The device's own choice (the cookie
setLocale keeps) lands at once, and the account saves it too, so the next
device says the same hello. An account that won't take it — a session gone
quiet — doesn't undo the switch made here. */
const language = ref('en')
async function setLanguage(code: string) {
if (language.value === code) return
language.value = code
await choose(code)
try {
await updateLocale(code)
} catch {
/* the device-level switch stands on its own */
}
}
/* sync the form from the account whenever the popup opens */
watch(
open,
@@ -178,6 +198,7 @@ watch(
username.value = user.value?.username ?? ''
currentPassword.value = ''
newPassword.value = ''
language.value = locale.value
avatarBusy.value = false
avatarError.value = ''
nameError.value = ''
@@ -340,6 +361,30 @@ watch(
<p v-else-if="passwordSaved" class="profile-saved">{{ t('profile.passwordChanged') }}</p>
</section>
<!-- the interface language -->
<section class="profile-group" aria-labelledby="profile-language">
<div class="profile-group-head">
<span id="profile-language" class="profile-group-title">{{ t('profile.language') }}</span>
<span class="profile-group-hint">{{ t('profile.languageHint') }}</span>
</div>
<div class="theme-switch" role="radiogroup" :aria-label="t('profile.languageAria')">
<button
v-for="l in APP_LANGUAGES"
:key="l.code"
type="button"
role="radio"
class="theme-option"
:class="{ active: language === l.code }"
:aria-checked="language === l.code"
:lang="l.code"
@click="setLanguage(l.code)"
>
<span>{{ l.name }}</span>
</button>
</div>
</section>
<section class="profile-group">
<button type="button" class="btn btn-danger btn-sm profile-signout" @click="leave()">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">