contact messages: messaging header = flag selector + fused enable toggle

The site settings' messaging block now opens with the article editor's
LangSelect (the flag dropdown on the shared shell-wide editorLang, same
option construction) instead of a plain select, and the enable checkbox
is fused into the "messaging" section header — one line for the whole
header. The separate enabled field row is gone.
This commit is contained in:
LeoVasanko committed 2026-09-28 15:28:03 +00:00
1 parent 719546a0ee
commit 59fe426e5e
1 file changed
+55 -38
+55 -38
View File
@@ -12,6 +12,9 @@ import { html } from '@codemirror/lang-html'
import { cmHighlight, cmTheme } from './cmtheme'
import { dropPageCache, loadPlain, runScripts } from './swapdoc'
import { apiFetch, apiJson } from 'paskia'
import { editorLang } from './editorLang'
import LangSelect from './LangSelect.vue'
import { flagFor, langName, langSort } from './langs'
const props = defineProps({
pagePath: { type: String, default: '' },
@@ -59,22 +62,38 @@ const brand = ref('')
const theme = ref('')
// --- Messaging (contact) ---------------------------------------------------
// Site-wide contact settings: the enabled toggle and mailto address are
// global; the label (the {contact:} buttons' default and the visitor
// dialog's title) and the email subject are per-language strings — the
// selector switches the two text fields between the original and a
// translation override (stored in the chunk-keyed trans store; auto
// translators fill the same store, so this is the manual-override UI).
// Site-wide contact settings: the enabled toggle (combined with the
// section header) and mailto address are global; the label (the
// {contact:} buttons' default and the visitor dialog's title) and the
// email subject are per-language strings — the LangSelect (the article
// editor's flag dropdown, on the shared shell-wide editorLang) switches
// the two text fields between the original and a translation override
// (stored in the chunk-keyed trans store; auto translators fill the same
// store, so this is the manual-override UI).
const contactEnabled = ref(true)
const contactEmail = ref('')
const contactLabel = ref('')
const contactSubject = ref('')
const contactLabelOrig = ref('')
const contactSubjectOrig = ref('')
const contactLang = ref('') // '' = the original strings
const lang = editorLang // '' = the original strings (the primary language)
const primaryLang = ref('en')
const translateLangs = ref([])
const translating = computed(() => contactLang.value && contactLang.value !== primaryLang.value)
const translating = computed(() => lang.value && lang.value !== primaryLang.value)
// The flag selector's options, built like the article editor's: the
// primary language first (tag ''), then the configured targets in the
// geographic grouping.
const contactLangOptions = computed(() => {
const others = langSort(translateLangs.value.filter((l) => l && l !== primaryLang.value))
return [primaryLang.value, ...others].map((code) => ({
tag: code === primaryLang.value ? '' : code,
code,
name: langName(code),
flag: flagFor(code),
primary: code === primaryLang.value,
}))
})
async function onContactLangChange() {
if (!translating.value) {
@@ -83,27 +102,28 @@ async function onContactLangChange() {
return
}
try {
const s = await apiJson(`/_api/settings?lang=${contactLang.value}`)
const s = await apiJson(`/_api/settings?lang=${lang.value}`)
contactLabel.value = s.contact_label_tr || ''
contactSubject.value = s.contact_subject_tr || ''
} catch { /* keep empty */ }
}
watch(lang, onContactLangChange)
function onContactInput() {
// Capture the language being edited: a debounced save must not land in
// whatever language happens to be selected when the timer fires.
const lang = contactLang.value
if (!lang || lang === primaryLang.value) {
const tag = lang.value
if (!tag || tag === primaryLang.value) {
contactLabelOrig.value = contactLabel.value
contactSubjectOrig.value = contactSubject.value
}
debounce('contact', () => saveContact(lang, contactLabel.value, contactSubject.value))
debounce('contact', () => saveContact(tag, contactLabel.value, contactSubject.value))
}
async function saveContact(lang = contactLang.value, label = '', subject = '') {
if (lang && lang !== primaryLang.value) {
async function saveContact(tag = lang.value, label = '', subject = '') {
if (tag && tag !== primaryLang.value) {
// Translation override: only the label/subject apply (empty = drop).
const res = await apiFetch(`/_api/settings?lang=${lang}`, {
const res = await apiFetch(`/_api/settings?lang=${tag}`, {
method: 'PUT',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
@@ -607,6 +627,9 @@ onMounted(async () => {
parent: brandEl.value,
})
await loadSettings()
// The shared editorLang may already point at a translation (picked in
// another tab): load its label/subject overrides.
await onContactLangChange()
parseFonts(customCss.value)
setCssDocument(customCss.value)
applyCustomCss(customCss.value)
@@ -736,28 +759,20 @@ onUnmounted(() => {
<section class="block">
<div class="block-head">
<span class="field-label">messaging</span>
<select
v-if="translateLangs.length"
v-model="contactLang"
class="text-input theme-select contact-lang"
<LangSelect
v-model="lang"
:options="contactLangOptions"
title="Language of the label/subject strings (auto translators fill these too; this edits the manual override)"
@change="onContactLangChange"
/>
<label
class="msg-toggle"
title="Visitor messaging ({contact:} buttons, corner icon)"
>
<option value="">original</option>
<option v-for="l in translateLangs" :key="l" :value="l">{{ l }}</option>
</select>
<input v-model="contactEnabled" type="checkbox" @change="saveSettings()" />
<span class="field-label">messaging</span>
</label>
</div>
<div class="field-grid">
<label class="field">
<span class="field-label">enabled</span>
<input
v-model="contactEnabled"
type="checkbox"
title="Visitor messaging ({contact:} buttons, corner icon)"
@change="saveSettings()"
/>
</label>
<label class="field">
<span class="field-label">email</span>
<input
@@ -921,11 +936,13 @@ onUnmounted(() => {
font-size: 1rem;
}
/* The messaging block's language selector sits at the block head's right. */
.block-head .contact-lang {
margin-left: auto;
flex: 0 0 auto;
width: auto;
/* The messaging block's single-line header: the article editor's flag
selector, then the enabled toggle fused with the section label. */
.msg-toggle {
display: flex;
align-items: center;
gap: 0.4rem;
cursor: pointer;
}
.text-input {