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:
1 parent
719546a0ee
commit
59fe426e5e
1 file changed
+55
-38
+55
-38
@@ -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 {
|
||||
|
||||
Reference in new issue
Block a user