fdas
This commit is contained in:
@@ -8925,7 +8925,7 @@ span.badge.badge-current {
|
|||||||
gap: 30px;
|
gap: 30px;
|
||||||
background: var(--metadata-bg);
|
background: var(--metadata-bg);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
@@ -13667,6 +13667,7 @@ i.iconset#a_oc {
|
|||||||
background: var(--nav-bg);
|
background: var(--nav-bg);
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
min-height: 140px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile_head::before {
|
.profile_head::before {
|
||||||
|
|||||||
@@ -390,8 +390,8 @@
|
|||||||
const canvasWrapper = document.createElement('div');
|
const canvasWrapper = document.createElement('div');
|
||||||
canvasWrapper.style.cssText = `
|
canvasWrapper.style.cssText = `
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 600px;
|
max-width: 400px;
|
||||||
aspect-ratio: 4 / 1;
|
aspect-ratio: 400 / 140;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid var(--nav-border-color);
|
border: 1px solid var(--nav-border-color);
|
||||||
@@ -402,8 +402,8 @@
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
canvas.width = 1200;
|
canvas.width = 400;
|
||||||
canvas.height = 300;
|
canvas.height = 140;
|
||||||
canvas.style.cssText = 'width:100%;height:100%;display:block;cursor:move;touch-action:none;pointer-events:auto !important;';
|
canvas.style.cssText = 'width:100%;height:100%;display:block;cursor:move;touch-action:none;pointer-events:auto !important;';
|
||||||
canvasWrapper.appendChild(canvas);
|
canvasWrapper.appendChild(canvas);
|
||||||
|
|
||||||
@@ -472,7 +472,7 @@
|
|||||||
const clamp = (val, min, max) => Math.min(Math.max(val, min), max);
|
const clamp = (val, min, max) => Math.min(Math.max(val, min), max);
|
||||||
|
|
||||||
const draw = () => {
|
const draw = () => {
|
||||||
ctx.clearRect(0, 0, 1200, 300);
|
ctx.clearRect(0, 0, 400, 140);
|
||||||
ctx.drawImage(img, panX, panY, img.naturalWidth * zoom, img.naturalHeight * zoom);
|
ctx.drawImage(img, panX, panY, img.naturalWidth * zoom, img.naturalHeight * zoom);
|
||||||
|
|
||||||
// Update hint with live coords/zoom
|
// Update hint with live coords/zoom
|
||||||
@@ -481,8 +481,8 @@
|
|||||||
|
|
||||||
const getBounds = (z) => {
|
const getBounds = (z) => {
|
||||||
return {
|
return {
|
||||||
minX: 1200 - img.naturalWidth * z,
|
minX: 400 - img.naturalWidth * z,
|
||||||
minY: 300 - img.naturalHeight * z
|
minY: 140 - img.naturalHeight * z
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -492,15 +492,15 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine minimum zoom so the image fully covers 1200x400 canvas without black edges
|
// Determine minimum zoom so the image fully covers 400x140 canvas without black edges
|
||||||
minZoom = Math.max(1200 / img.naturalWidth, 300 / img.naturalHeight);
|
minZoom = Math.max(400 / img.naturalWidth, 140 / img.naturalHeight);
|
||||||
maxZoom = minZoom * 4;
|
maxZoom = minZoom * 4;
|
||||||
|
|
||||||
if (existingConfig && existingConfig.bgSize && existingConfig.bgSize !== 'cover') {
|
if (existingConfig && existingConfig.bgSize && existingConfig.bgSize !== 'cover') {
|
||||||
const match = existingConfig.bgSize.match(/([\d.]+)%/);
|
const match = existingConfig.bgSize.match(/([\d.]+)%/);
|
||||||
if (match) {
|
if (match) {
|
||||||
const pct = parseFloat(match[1]);
|
const pct = parseFloat(match[1]);
|
||||||
const iw = (pct / 100) * 1200;
|
const iw = (pct / 100) * 400;
|
||||||
zoom = Math.max(minZoom, Math.min(maxZoom, iw / img.naturalWidth));
|
zoom = Math.max(minZoom, Math.min(maxZoom, iw / img.naturalWidth));
|
||||||
} else {
|
} else {
|
||||||
zoom = minZoom;
|
zoom = minZoom;
|
||||||
@@ -511,8 +511,8 @@
|
|||||||
|
|
||||||
const iw = img.naturalWidth * zoom;
|
const iw = img.naturalWidth * zoom;
|
||||||
const ih = img.naturalHeight * zoom;
|
const ih = img.naturalHeight * zoom;
|
||||||
const maxPanX = 1200 - iw;
|
const maxPanX = 400 - iw;
|
||||||
const maxPanY = 300 - ih;
|
const maxPanY = 140 - ih;
|
||||||
|
|
||||||
if (existingConfig && existingConfig.bgPos && existingConfig.bgPos !== 'center') {
|
if (existingConfig && existingConfig.bgPos && existingConfig.bgPos !== 'center') {
|
||||||
const parts = existingConfig.bgPos.split(' ');
|
const parts = existingConfig.bgPos.split(' ');
|
||||||
@@ -543,13 +543,13 @@
|
|||||||
const t = pct / 100;
|
const t = pct / 100;
|
||||||
const newZoom = minZoom + (maxZoom - minZoom) * t;
|
const newZoom = minZoom + (maxZoom - minZoom) * t;
|
||||||
|
|
||||||
const imgCenterX = (600 - panX) / oldZoom;
|
const imgCenterX = (200 - panX) / oldZoom;
|
||||||
const imgCenterY = (150 - panY) / oldZoom;
|
const imgCenterY = (70 - panY) / oldZoom;
|
||||||
|
|
||||||
zoom = newZoom;
|
zoom = newZoom;
|
||||||
const bounds = getBounds(zoom);
|
const bounds = getBounds(zoom);
|
||||||
panX = Math.max(bounds.minX, Math.min(0, 600 - imgCenterX * zoom));
|
panX = Math.max(bounds.minX, Math.min(0, 200 - imgCenterX * zoom));
|
||||||
panY = Math.max(bounds.minY, Math.min(0, 150 - imgCenterY * zoom));
|
panY = Math.max(bounds.minY, Math.min(0, 70 - imgCenterY * zoom));
|
||||||
|
|
||||||
draw();
|
draw();
|
||||||
};
|
};
|
||||||
@@ -626,13 +626,13 @@
|
|||||||
const iw = img.naturalWidth * zoom;
|
const iw = img.naturalWidth * zoom;
|
||||||
const ih = img.naturalHeight * zoom;
|
const ih = img.naturalHeight * zoom;
|
||||||
|
|
||||||
const maxPanX = 1200 - iw;
|
const maxPanX = 400 - iw;
|
||||||
const posXPercent = maxPanX === 0 ? 50 : (panX / maxPanX) * 100;
|
const posXPercent = maxPanX === 0 ? 50 : (panX / maxPanX) * 100;
|
||||||
|
|
||||||
const maxPanY = 300 - ih;
|
const maxPanY = 140 - ih;
|
||||||
const posYPercent = maxPanY === 0 ? 50 : (panY / maxPanY) * 100;
|
const posYPercent = maxPanY === 0 ? 50 : (panY / maxPanY) * 100;
|
||||||
|
|
||||||
const bgSize = `${(iw / 1200) * 100}% auto`;
|
const bgSize = `${(iw / 400) * 100}% auto`;
|
||||||
const bgPos = `${posXPercent.toFixed(2)}% ${posYPercent.toFixed(2)}%`;
|
const bgPos = `${posXPercent.toFixed(2)}% ${posYPercent.toFixed(2)}%`;
|
||||||
|
|
||||||
cleanup();
|
cleanup();
|
||||||
@@ -700,7 +700,7 @@
|
|||||||
const newDiv = document.createElement('div');
|
const newDiv = document.createElement('div');
|
||||||
newDiv.id = 'banner-preview';
|
newDiv.id = 'banner-preview';
|
||||||
newDiv.className = 'banner-preview-img';
|
newDiv.className = 'banner-preview-img';
|
||||||
newDiv.style.cssText = `width:100%;aspect-ratio:4/1;border-radius:4px;border:1px solid var(--nav-border-color);background-image:url('/a/${res.banner_file}?t=${Date.now()}');background-size:${payload.banner_size};background-position:${payload.banner_position};background-repeat:no-repeat;`;
|
newDiv.style.cssText = `width:100%;aspect-ratio:400/140;border-radius:4px;border:1px solid var(--nav-border-color);background-image:url('/a/${res.banner_file}?t=${Date.now()}');background-size:${payload.banner_size};background-position:${payload.banner_position};background-repeat:no-repeat;`;
|
||||||
a.appendChild(newDiv);
|
a.appendChild(newDiv);
|
||||||
bannerPreview.replaceWith(a);
|
bannerPreview.replaceWith(a);
|
||||||
} else {
|
} else {
|
||||||
@@ -857,8 +857,8 @@
|
|||||||
if (bannerPreview) {
|
if (bannerPreview) {
|
||||||
const ph = document.createElement('div');
|
const ph = document.createElement('div');
|
||||||
ph.id = 'banner-preview'; ph.className = 'banner-preview-img banner-placeholder';
|
ph.id = 'banner-preview'; ph.className = 'banner-preview-img banner-placeholder';
|
||||||
ph.style.cssText = 'width:100%;aspect-ratio:4/1;display:flex;align-items:center;justify-content:center;border:1px dashed var(--nav-border-color);border-radius:4px;color:var(--text-muted);font-size:0.85em;';
|
ph.style.cssText = 'width:100%;aspect-ratio:400/140;display:flex;align-items:center;justify-content:center;border:1px dashed var(--nav-border-color);border-radius:4px;color:var(--text-muted);font-size:0.85em;';
|
||||||
ph.textContent = 'No banner set';
|
ph.textContent = window.f0ckI18n?.settings_no_banner_set || 'No banner set';
|
||||||
|
|
||||||
// Replace the <a> wrapper if it exists, otherwise just the preview element
|
// Replace the <a> wrapper if it exists, otherwise just the preview element
|
||||||
const parentA = bannerPreview.closest('a');
|
const parentA = bannerPreview.closest('a');
|
||||||
@@ -1753,6 +1753,10 @@
|
|||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
showAccountStatus(descriptionStatus, 'Description updated successfully!', 'success');
|
showAccountStatus(descriptionStatus, 'Description updated successfully!', 'success');
|
||||||
|
const liveDesc = document.querySelector('.user-infobox-description');
|
||||||
|
if (liveDesc) {
|
||||||
|
liveDesc.textContent = description || 'Welcome to my profile! This is a live preview.';
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
alert(data.msg || 'Error saving description');
|
alert(data.msg || 'Error saving description');
|
||||||
}
|
}
|
||||||
@@ -1786,6 +1790,10 @@
|
|||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
showAccountStatus(descriptionStatus, 'Description cleared!', 'success');
|
showAccountStatus(descriptionStatus, 'Description cleared!', 'success');
|
||||||
|
const liveDesc = document.querySelector('.user-infobox-description');
|
||||||
|
if (liveDesc) {
|
||||||
|
liveDesc.textContent = 'Welcome to my profile! This is a live preview.';
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
alert(data.msg || 'Error clearing description');
|
alert(data.msg || 'Error clearing description');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,6 +139,9 @@
|
|||||||
"no_file_selected": "Keine Datei ausgewählt",
|
"no_file_selected": "Keine Datei ausgewählt",
|
||||||
"upload_btn": "Hochladen",
|
"upload_btn": "Hochladen",
|
||||||
"remove_custom": "Eigenen entfernen",
|
"remove_custom": "Eigenen entfernen",
|
||||||
|
"current_banner": "Aktuelles Banner",
|
||||||
|
"banner_hint": "Max. 5 MB. Erlaubt: gif, jpg, png, webp",
|
||||||
|
"no_banner_set": "Kein Banner gesetzt",
|
||||||
"custom_description": "Eigene Beschreibung",
|
"custom_description": "Eigene Beschreibung",
|
||||||
"description_placeholder": "Beschreibung zum Profil hinzufügen",
|
"description_placeholder": "Beschreibung zum Profil hinzufügen",
|
||||||
"save_description": "Beschreibung speichern",
|
"save_description": "Beschreibung speichern",
|
||||||
|
|||||||
@@ -139,6 +139,9 @@
|
|||||||
"no_file_selected": "No file selected",
|
"no_file_selected": "No file selected",
|
||||||
"upload_btn": "Upload",
|
"upload_btn": "Upload",
|
||||||
"remove_custom": "Remove Custom",
|
"remove_custom": "Remove Custom",
|
||||||
|
"current_banner": "Current Banner",
|
||||||
|
"banner_hint": "Max 5MB. Allowed: gif, jpg, png, webp",
|
||||||
|
"no_banner_set": "No banner set",
|
||||||
"custom_description": "Custom Description",
|
"custom_description": "Custom Description",
|
||||||
"description_placeholder": "Add a description to your profile",
|
"description_placeholder": "Add a description to your profile",
|
||||||
"save_description": "Save Description",
|
"save_description": "Save Description",
|
||||||
|
|||||||
@@ -139,6 +139,9 @@
|
|||||||
"no_file_selected": "Geen bestand geselecteerd",
|
"no_file_selected": "Geen bestand geselecteerd",
|
||||||
"upload_btn": "Uploaden",
|
"upload_btn": "Uploaden",
|
||||||
"remove_custom": "Aangepaste Verwijderen",
|
"remove_custom": "Aangepaste Verwijderen",
|
||||||
|
"current_banner": "Huidige Banner",
|
||||||
|
"banner_hint": "Max 5MB. Toegestaan: gif, jpg, png, webp",
|
||||||
|
"no_banner_set": "Geen banner ingesteld",
|
||||||
"custom_description": "Aangepaste Beschrijving",
|
"custom_description": "Aangepaste Beschrijving",
|
||||||
"description_placeholder": "Voeg een beschrijving toe aan je profiel",
|
"description_placeholder": "Voeg een beschrijving toe aan je profiel",
|
||||||
"save_description": "Beschrijving Opslaan",
|
"save_description": "Beschrijving Opslaan",
|
||||||
|
|||||||
@@ -139,6 +139,9 @@
|
|||||||
"no_file_selected": "Keine Datei ausgewählt",
|
"no_file_selected": "Keine Datei ausgewählt",
|
||||||
"upload_btn": "Aufladieren",
|
"upload_btn": "Aufladieren",
|
||||||
"remove_custom": "Benutzerdefiniertes entfernen",
|
"remove_custom": "Benutzerdefiniertes entfernen",
|
||||||
|
"current_banner": "Aktuelles Banner",
|
||||||
|
"banner_hint": "Max 5MB. Erlaubt: gif, jpg, png, webp",
|
||||||
|
"no_banner_set": "Kein Banner gesetzt",
|
||||||
"custom_description": "Benutzerdefinierte Beschreibung",
|
"custom_description": "Benutzerdefinierte Beschreibung",
|
||||||
"description_placeholder": "Fügen Sie Ihrem Profil eine Beschreibung hinzu",
|
"description_placeholder": "Fügen Sie Ihrem Profil eine Beschreibung hinzu",
|
||||||
"save_description": "Beschreibung speichern",
|
"save_description": "Beschreibung speichern",
|
||||||
|
|||||||
@@ -438,7 +438,7 @@ export default router => {
|
|||||||
return res.json({ success: false, msg: 'Invalid description format' }, 400);
|
return res.json({ success: false, msg: 'Invalid description format' }, 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
const cleanDescription = description ? description.trim().substring(0, 255) : null;
|
const cleanDescription = description ? description.trim().substring(0, 2000) : null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await db`
|
await db`
|
||||||
|
|||||||
@@ -126,7 +126,7 @@
|
|||||||
<legend style="width: auto; padding: 0 5px; font-size: 1.1em; font-weight: bold;">{{ t('settings.custom_description') }}</legend>
|
<legend style="width: auto; padding: 0 5px; font-size: 1.1em; font-weight: bold;">{{ t('settings.custom_description') }}</legend>
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
<textarea id="profile_description" class="input" placeholder="{{ t('settings.description_placeholder') }}"
|
<textarea id="profile_description" class="input" placeholder="{{ t('settings.description_placeholder') }}"
|
||||||
maxlength="255">{!! session.description || '' !!}</textarea>
|
maxlength="2000">{!! session.description || '' !!}</textarea>
|
||||||
<div class="profile-settings-actions">
|
<div class="profile-settings-actions">
|
||||||
<button type="button" id="btn-save-description" class="button">{{ t('settings.save_description') }}</button>
|
<button type="button" id="btn-save-description" class="button">{{ t('settings.save_description') }}</button>
|
||||||
<button type="button" id="btn-clear-description" class="button button-danger">{{ t('settings.clear') }}</button>
|
<button type="button" id="btn-clear-description" class="button button-danger">{{ t('settings.clear') }}</button>
|
||||||
@@ -141,21 +141,22 @@
|
|||||||
<legend style="width: auto; padding: 0 5px; font-size: 1.1em; font-weight: bold;">Profile Banner</legend>
|
<legend style="width: auto; padding: 0 5px; font-size: 1.1em; font-weight: bold;">Profile Banner</legend>
|
||||||
<div class="avatar-settings-wrapper">
|
<div class="avatar-settings-wrapper">
|
||||||
<div class="avatar-preview-container" style="width: 360px; max-width: 100%;">
|
<div class="avatar-preview-container" style="width: 360px; max-width: 100%;">
|
||||||
<div class="avatar-preview-label">Current Banner</div>
|
<div class="avatar-preview-label">{{ t('settings.current_banner') }}</div>
|
||||||
@if(banner_file)
|
@if(banner_file)
|
||||||
<a href="/user/{!! session.user !!}" target="_blank" style="display:block; width: 100%;">
|
<a href="/user/{!! session.user !!}" target="_blank" style="display:block; width: 100%;">
|
||||||
<div id="banner-preview" class="banner-preview-img" style="width:100%;aspect-ratio:4/1;border-radius:4px;border:1px solid var(--nav-border-color);background-image:url('/a/{{ banner_file }}');background-size:{{ banner_size || 'cover' }};background-position:{{ banner_position || 'center' }};background-repeat:no-repeat;"></div>
|
<div id="banner-preview" class="banner-preview-img" style="width:100%;aspect-ratio:400/140;border-radius:4px;border:1px solid var(--nav-border-color);background-image:url('/a/{{ banner_file }}');background-size:{{ banner_size || 'cover' }};background-position:{{ banner_position || 'center' }};background-repeat:no-repeat;"></div>
|
||||||
</a>
|
</a>
|
||||||
@else
|
@else
|
||||||
<div id="banner-preview" class="banner-preview-img banner-placeholder" style="width:100%;aspect-ratio:4/1;display:flex;align-items:center;justify-content:center;border:1px dashed var(--nav-border-color);border-radius:4px;color:var(--text-muted);font-size:0.85em;">No banner set</div>
|
<div id="banner-preview" class="banner-preview-img banner-placeholder" style="width:100%;aspect-ratio:400/140;display:flex;align-items:center;justify-content:center;border:1px dashed var(--nav-border-color);border-radius:4px;color:var(--text-muted);font-size:0.85em;">{{ t('settings.no_banner_set') }}</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="avatar-upload-section">
|
<div class="avatar-upload-section">
|
||||||
|
<p class="avatar-hint">{{ t('settings.banner_hint') }}</p>
|
||||||
<div class="avatar-upload-wrapper">
|
<div class="avatar-upload-wrapper">
|
||||||
<input type="file" id="banner-file-input" accept="image/gif,image/jpeg,image/png,image/webp" hidden>
|
<input type="file" id="banner-file-input" accept="image/gif,image/jpeg,image/png,image/webp" hidden>
|
||||||
<button type="button" id="banner-choose-btn" class="button">Choose File</button>
|
<button type="button" id="banner-choose-btn" class="button">{{ t('settings.choose_file') }}</button>
|
||||||
<span id="banner-filename" class="avatar-filename">No file selected</span>
|
<span id="banner-filename" class="avatar-filename">{{ t('settings.no_file_selected') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="avatar-progress-wrapper" id="banner-progress-wrapper" style="display: none;">
|
<div class="avatar-progress-wrapper" id="banner-progress-wrapper" style="display: none;">
|
||||||
|
|||||||
@@ -437,6 +437,7 @@
|
|||||||
upload_comment_placeholder: "{{ t('upload.item_comment_placeholder') || 'Comment (optional)...' }}",
|
upload_comment_placeholder: "{{ t('upload.item_comment_placeholder') || 'Comment (optional)...' }}",
|
||||||
upload_tags_placeholder: "{{ t('upload.item_tags_placeholder') || 'Tags...' }}",
|
upload_tags_placeholder: "{{ t('upload.item_tags_placeholder') || 'Tags...' }}",
|
||||||
upload_add_more: "{{ t('upload.add_more') || 'Add more' }}",
|
upload_add_more: "{{ t('upload.add_more') || 'Add more' }}",
|
||||||
|
settings_no_banner_set: "{{ t('settings.no_banner_set') || 'No banner set' }}",
|
||||||
// timeago
|
// timeago
|
||||||
timeago_just_now: "{{ t('timeago.just_now') }}",
|
timeago_just_now: "{{ t('timeago.just_now') }}",
|
||||||
timeago_year: "{{ t('timeago.year') }}",
|
timeago_year: "{{ t('timeago.year') }}",
|
||||||
|
|||||||
Reference in New Issue
Block a user