fix: cropper mime bug + loadSiteIdentity crash + logo|name header
1. applyCrop() — mime variable was declared INSIDE toBlob callback but used as an argument to toBlob() (outer scope) → ReferenceError. Fix: declare _mime, _quality, _ext BEFORE out.toBlob() call. 2. loadSiteIdentity() — crashed when identity section had no rows (data was null/non-array). Fix: safe Array.isArray guard + catch. 3. Header logo: show logo image + | + site name side by side when logo is configured (was showing one OR the other). Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -139,15 +139,13 @@
|
|||||||
<body>
|
<body>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<a class="logo" href="/">
|
<a class="logo" href="/" style="display:flex;align-items:center;gap:.6rem">
|
||||||
@if (!string.IsNullOrEmpty(_logoUrl))
|
@if (!string.IsNullOrEmpty(_logoUrl))
|
||||||
{
|
{
|
||||||
<img src="@_logoUrl" alt="@(ViewData["SiteName"] ?? "دکتر سوسن آلطه")" style="height:38px;width:auto;object-fit:contain;vertical-align:middle" />
|
<img src="@_logoUrl" alt="@(ViewData["SiteName"] ?? "دکتر سوسن آلطه")" style="height:36px;width:auto;object-fit:contain;flex-shrink:0" />
|
||||||
}
|
<span style="color:var(--border);font-weight:300;font-size:1.1rem;line-height:1">|</span>
|
||||||
else
|
|
||||||
{
|
|
||||||
@(ViewData["SiteName"] ?? "دکتر سوسن آلطه")
|
|
||||||
}
|
}
|
||||||
|
<span>@(ViewData["SiteName"] ?? "دکتر سوسن آلطه")</span>
|
||||||
</a>
|
</a>
|
||||||
<nav>
|
<nav>
|
||||||
<a href="/#about">درباره من</a>
|
<a href="/#about">درباره من</a>
|
||||||
|
|||||||
@@ -1375,9 +1375,9 @@ async function deleteReq(id){if(!confirm('حذف؟'))return;await api(`/api/heal
|
|||||||
// ── Comments ──────────────────────────────────────────────────────────────────
|
// ── Comments ──────────────────────────────────────────────────────────────────
|
||||||
// ── Site Identity (logo / favicon) ────────────────────────────────────────────
|
// ── Site Identity (logo / favicon) ────────────────────────────────────────────
|
||||||
async function loadSiteIdentity() {
|
async function loadSiteIdentity() {
|
||||||
const data = await api('/api/settings/identity') || [];
|
const data = await api('/api/settings/identity').catch(()=>[]) || [];
|
||||||
const vals = {};
|
const vals = {};
|
||||||
data.forEach(s => vals[s.key] = s.value);
|
(Array.isArray(data) ? data : []).forEach(s => vals[s.key] = s.value);
|
||||||
// Set hidden inputs
|
// Set hidden inputs
|
||||||
document.getElementById('si-logo').value = vals.logo || '';
|
document.getElementById('si-logo').value = vals.logo || '';
|
||||||
document.getElementById('si-favicon').value = vals.favicon || '';
|
document.getElementById('si-favicon').value = vals.favicon || '';
|
||||||
@@ -1823,8 +1823,13 @@ async function applyCrop() {
|
|||||||
const out = document.createElement('canvas');
|
const out = document.createElement('canvas');
|
||||||
out.width = Math.round(sw); out.height = Math.round(sh);
|
out.width = Math.round(sw); out.height = Math.round(sh);
|
||||||
out.getContext('2d').drawImage(cropper.img, sx, sy, sw, sh, 0, 0, out.width, out.height);
|
out.getContext('2d').drawImage(cropper.img, sx, sy, sw, sh, 0, 0, out.width, out.height);
|
||||||
// Capture targets BEFORE closing (closeCropper doesn't clear them, but be safe)
|
// Capture targets and mime type BEFORE the async callback
|
||||||
const _inputId = cropper.inputId, _previewId = cropper.previewId;
|
const _inputId = cropper.inputId;
|
||||||
|
const _previewId = cropper.previewId;
|
||||||
|
const _mime = cropper.mimeType || 'image/jpeg';
|
||||||
|
const _quality = (_mime === 'image/jpeg' || _mime === 'image/webp') ? 0.92 : undefined;
|
||||||
|
const _ext = _mime.split('/')[1].replace('jpeg','jpg').replace('vnd.microsoft.icon','ico').replace('x-icon','ico').replace('svg+xml','svg');
|
||||||
|
|
||||||
out.toBlob(async blob => {
|
out.toBlob(async blob => {
|
||||||
closeCropper();
|
closeCropper();
|
||||||
const inputId = _inputId, previewId = _previewId;
|
const inputId = _inputId, previewId = _previewId;
|
||||||
@@ -1834,8 +1839,7 @@ async function applyCrop() {
|
|||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
try {
|
try {
|
||||||
const fd = new FormData();
|
const fd = new FormData();
|
||||||
const ext = _inputId && cropper.mimeType ? cropper.mimeType.split('/')[1].replace('jpeg','jpg') : 'jpg';
|
fd.append('file', new File([blob], `crop.${_ext}`, {type: _mime}));
|
||||||
fd.append('file', new File([blob], `crop.${ext}`, {type: cropper.mimeType || 'image/jpeg'}));
|
|
||||||
const r = await fetch('/api/upload', {method:'POST', headers:{'Authorization':`Bearer ${token}`}, body:fd});
|
const r = await fetch('/api/upload', {method:'POST', headers:{'Authorization':`Bearer ${token}`}, body:fd});
|
||||||
if (!r.ok) throw new Error(await r.text());
|
if (!r.ok) throw new Error(await r.text());
|
||||||
const { url } = await r.json();
|
const { url } = await r.json();
|
||||||
@@ -1844,10 +1848,7 @@ async function applyCrop() {
|
|||||||
toast('تصویر با موفقیت آپلود شد ✓');
|
toast('تصویر با موفقیت آپلود شد ✓');
|
||||||
} catch(e) { toast('خطا در آپلود: '+e.message,'error'); }
|
} catch(e) { toast('خطا در آپلود: '+e.message,'error'); }
|
||||||
finally { btn.innerHTML=orig; btn.disabled=false; }
|
finally { btn.innerHTML=orig; btn.disabled=false; }
|
||||||
// Use original mime type; only pass quality for lossy formats
|
}, _mime, _quality);
|
||||||
const mime = cropper.mimeType || 'image/jpeg';
|
|
||||||
const quality = (mime === 'image/jpeg' || mime === 'image/webp') ? 0.92 : undefined;
|
|
||||||
}, mime, quality);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadImage(inputId, previewId) {
|
async function uploadImage(inputId, previewId) {
|
||||||
|
|||||||
Reference in New Issue
Block a user