Locations können nun auch Posten - bugfixes im Feed
Some checks failed
Host-Based Deploy (Java 21 Fix) / build-and-run (push) Has been cancelled
Some checks failed
Host-Based Deploy (Java 21 Fix) / build-and-run (push) Has been cancelled
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
<title>Profil – xXx Sphere</title>
|
||||
<link rel="stylesheet" href="/css/variables.css">
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<link rel="stylesheet" href="/css/community.css">
|
||||
<style>
|
||||
/* ── Profile Header ── */
|
||||
.profil-header {
|
||||
@@ -487,50 +488,13 @@
|
||||
.vorliebe-chip.bw-EHER_NICHT { border-color:#fb8c00; color:#fb8c00; }
|
||||
.vorliebe-chip.bw-GEHT_GAR_NICHT { border-color:#e53935; color:#e53935; }
|
||||
|
||||
/* ── Post cards (profile posts tab) ── */
|
||||
.post-card { background:var(--color-card); border:1px solid var(--color-secondary); border-radius:10px; padding:1rem; margin-bottom:0.9rem; }
|
||||
.post-header { display:flex; align-items:center; gap:0.7rem; margin-bottom:0.6rem; }
|
||||
.post-avatar { width:36px; height:36px; border-radius:50%; background:var(--color-secondary); display:flex; align-items:center; justify-content:center; font-size:0.95rem; flex-shrink:0; overflow:hidden; }
|
||||
.post-avatar img { width:100%; height:100%; object-fit:cover; }
|
||||
.post-author { font-weight:600; font-size:0.9rem; }
|
||||
.post-date { font-size:0.75rem; color:var(--color-muted); margin-left:auto; }
|
||||
.post-text { font-size:0.95rem; line-height:1.5; white-space:pre-wrap; word-break:break-word; }
|
||||
.post-bild { width:100%; max-height:360px; object-fit:contain; border-radius:6px; margin-top:0.5rem; display:block; transition:opacity 0.2s; }
|
||||
/* ── Post-Bild-Wrap (Profil-spezifisch) ── */
|
||||
.post-bild-wrap { position:relative; cursor:pointer; display:block; }
|
||||
.post-bild-wrap:hover .post-bild { opacity:0.82; }
|
||||
.post-bild-hover-icon { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; opacity:0; transition:opacity 0.2s; pointer-events:none; font-size:1.6rem; }
|
||||
.post-bild-wrap:hover .post-bild-hover-icon { opacity:1; }
|
||||
.post-actions { display:flex; gap:1rem; margin-top:0.75rem; align-items:center; flex-wrap:wrap; }
|
||||
.post-action-btn { background:none; border:none; color:var(--color-muted); cursor:pointer; font-size:0.85rem; padding:0; display:flex; align-items:center; gap:0.3rem; margin:0; width:auto; }
|
||||
.post-action-btn:hover { color:var(--color-primary); background:none; }
|
||||
.post-action-btn.active { color:var(--color-primary); }
|
||||
.post-delete { margin-left:auto; }
|
||||
.post-delete:hover { color:#c0392b !important; }
|
||||
.umfrage-option-bar { margin:0.3rem 0; cursor:pointer; border-radius:6px; overflow:hidden; border:1px solid var(--color-secondary); position:relative; transition:border-color 0.15s; }
|
||||
.umfrage-option-bar:hover { border-color:var(--color-primary); }
|
||||
.umfrage-option-bar.voted { border-color:var(--color-primary); }
|
||||
.umfrage-bar-fill { position:absolute; inset:0; background:rgba(var(--color-primary-rgb,180,0,60),0.15); transition:width 0.4s; }
|
||||
.umfrage-bar-content { position:relative; display:flex; justify-content:space-between; padding:0.45rem 0.75rem; font-size:0.88rem; }
|
||||
.umfrage-total { font-size:0.78rem; color:var(--color-muted); margin-top:0.3rem; }
|
||||
|
||||
/* ── Post / Bild Lightbox ── */
|
||||
.lightbox { display:none; position:fixed; inset:0; background:rgba(0,0,0,0.88); z-index:400; align-items:center; justify-content:center; }
|
||||
.lightbox.open { display:flex; }
|
||||
.lb-layout { display:flex; max-width:min(1340px, calc(100vw - 2rem)); width:95vw; height:min(90vh, 1100px); background:var(--color-card); border-radius:12px; overflow:hidden; position:relative; }
|
||||
.lb-post-side .post-bild { max-height:1024px; }
|
||||
.lb-close { position:absolute; top:0.6rem; right:0.6rem; background:rgba(0,0,0,0.55); border:none; color:#fff; font-size:1.1rem; width:2rem; height:2rem; border-radius:50%; cursor:pointer; z-index:10; display:flex; align-items:center; justify-content:center; padding:0; margin:0; }
|
||||
.lb-post-side { flex:1; overflow-y:auto; padding:1.25rem; border-right:1px solid var(--color-secondary); min-width:0; }
|
||||
.lb-comments-panel { width:300px; flex-shrink:0; display:flex; flex-direction:column; }
|
||||
.lb-comments-header { font-size:0.78rem; font-weight:600; color:var(--color-muted); text-transform:uppercase; letter-spacing:0.06em; padding:0.7rem 1rem; border-bottom:1px solid var(--color-secondary); flex-shrink:0; }
|
||||
.lb-comments-list { flex:1; overflow-y:auto; padding:0.75rem; }
|
||||
.lb-comment-compose { padding:0.75rem; border-top:1px solid var(--color-secondary); display:flex; flex-direction:column; gap:0.5rem; flex-shrink:0; }
|
||||
.lb-comment-compose textarea { width:100%; font-size:0.85rem; padding:0.35rem 0.6rem; resize:none; background:var(--color-secondary); border:1px solid var(--color-secondary); border-radius:6px; color:var(--color-text); font-family:inherit; outline:none; transition:border-color 0.2s; box-sizing:border-box; }
|
||||
.lb-comment-compose textarea:focus { border-color:var(--color-primary); }
|
||||
.lb-comment-compose-actions { display:flex; gap:0.5rem; justify-content:flex-end; }
|
||||
.lb-comment-compose button { width:auto; margin:0; padding:0.35rem 0.75rem; font-size:0.8rem; }
|
||||
/* ── Bild-Navigation in Lightbox ── */
|
||||
.lb-img-nav { display:flex; gap:0.75rem; align-items:center; justify-content:center; margin-top:0.75rem; flex-wrap:wrap; }
|
||||
.compose-action-btn { background:none; border:1px solid var(--color-secondary); color:var(--color-muted); border-radius:6px; padding:0.35rem 0.6rem; font-size:0.95rem; cursor:pointer; margin:0; width:auto; }
|
||||
@media (max-width:650px) { .lb-layout { flex-direction:column; height:95vh; } .lb-post-side { border-right:none; border-bottom:1px solid var(--color-secondary); max-height:55vh; } .lb-comments-panel { width:100%; } }
|
||||
</style>
|
||||
</head>
|
||||
<body class="app">
|
||||
@@ -652,8 +616,9 @@
|
||||
<script src="/js/shared.js"></script>
|
||||
<script src="/js/image-viewer.js"></script>
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script src="/js/hashtag.js"></script>
|
||||
<script src="/js/meldung.js"></script>
|
||||
<script>
|
||||
// ── State ──
|
||||
@@ -680,6 +645,9 @@
|
||||
let activeLbPostId = null;
|
||||
let activeLbMode = null; // 'post' | 'image'
|
||||
let activeLbImageIdx = null;
|
||||
const profilPostBilder = new Map(); // postId → bilder[] für Lightbox-Carousel
|
||||
const profilPostCache = {}; // postId → post-Objekt für Edit
|
||||
const profilEditBilder = new Map(); // postId → bilder[] während Bearbeitung
|
||||
|
||||
// ── Label maps ──
|
||||
const GESCHLECHT_LABEL = { WEIBLICH: 'weiblich', DIVERS: 'divers', MAENNLICH: 'männlich' };
|
||||
@@ -719,6 +687,7 @@
|
||||
}
|
||||
|
||||
myUserId = me ? me.userId : null;
|
||||
initLb(myUserId);
|
||||
isOwnProfile = !previewMode && me && me.userId === profile.userId;
|
||||
profileData = profile;
|
||||
allImages = images;
|
||||
@@ -1248,6 +1217,7 @@
|
||||
}
|
||||
|
||||
function renderLbImageBody() {
|
||||
document.querySelector('#postLightbox .lb-layout')?.classList.remove('lb-text-only');
|
||||
const img = allImages[activeLbImageIdx];
|
||||
const total = allImages.length;
|
||||
const prevDisabled = activeLbImageIdx === 0 ? 'disabled' : '';
|
||||
@@ -1589,42 +1559,48 @@
|
||||
const avatarHtml = p.authorPicture
|
||||
? `<img src="data:image/png;base64,${p.authorPicture}" alt="">`
|
||||
: '◉';
|
||||
const bildRaw = bilderCarousel(p.bilder);
|
||||
const bildHtml = bildRaw
|
||||
? `<div class="post-bild-wrap" data-post-id="${p.postId}">${bildRaw}</div>`
|
||||
: '';
|
||||
profilPostCache[p.postId] = p;
|
||||
profilPostBilder.set(p.postId, p.bilder || []);
|
||||
const bildHtml = bilderGrid(p.bilder);
|
||||
const privacyLabel = p.isPublic ? '' : '<span style="font-size:0.72rem;color:var(--color-muted);margin-left:0.4rem;">🔒 privat</span>';
|
||||
const editedLabel = p.editedAt ? ' <span class="edited-label" style="font-size:0.75rem;color:var(--color-muted);">(bearbeitet)</span>' : '';
|
||||
|
||||
let umfrageHtml = '';
|
||||
if (p.beitragTyp === 'UMFRAGE' && p.optionen && p.optionen.length > 0) {
|
||||
const totalVotes = p.optionen.reduce((s, o) => s + o.stimmenCount, 0);
|
||||
umfrageHtml = '<div style="margin-top:0.5rem;">' + p.optionen.map(o => {
|
||||
umfrageHtml = p.optionen.map(o => {
|
||||
const pct = totalVotes > 0 ? Math.round(o.stimmenCount / totalVotes * 100) : 0;
|
||||
const voted = p.myVoteOptionIds && p.myVoteOptionIds.includes(o.optionId);
|
||||
return `<div class="umfrage-option-bar${voted ? ' voted' : ''}" onclick="voteProfilPost('${p.postId}','${o.optionId}')">
|
||||
<div class="umfrage-bar-fill" style="width:${pct}%"></div>
|
||||
<div class="umfrage-bar-content"><span>${esc(o.text)}</span><span>${pct}%</span></div>
|
||||
</div>`;
|
||||
}).join('') + `<div class="umfrage-total">${totalVotes} Stimme${totalVotes !== 1 ? 'n' : ''}</div></div>`;
|
||||
}).join('') + `<div class="umfrage-total">${totalVotes} Stimme${totalVotes !== 1 ? 'n' : ''}</div>`;
|
||||
}
|
||||
|
||||
const canDelete = p.authorId === myUserId;
|
||||
const deleteBtn = canDelete
|
||||
? `<button class="post-action-btn post-delete" onclick="deleteProfilPost('${p.postId}')">🗑</button>`
|
||||
const isOwn = p.authorId === myUserId;
|
||||
const rightBtns = isOwn
|
||||
? `<div style="margin-left:auto;display:flex;gap:0.25rem;">
|
||||
<button class="post-action-btn" onclick="event.stopPropagation();startProfilEdit('${p.postId}')" title="Bearbeiten" style="color:var(--color-muted)">✏</button>
|
||||
<button class="post-action-btn post-delete" onclick="event.stopPropagation();deleteProfilPost('${p.postId}')">🗑</button>
|
||||
</div>`
|
||||
: '';
|
||||
|
||||
return `<div class="post-card" id="pp-${p.postId}">
|
||||
<div class="post-header">
|
||||
<div class="post-avatar">${avatarHtml}</div>
|
||||
<div class="post-avatar"><a href="/community/benutzer.html?userId=${p.authorId}" onclick="event.stopPropagation()" style="display:contents;">${avatarHtml}</a></div>
|
||||
<div>
|
||||
<div class="post-author">${esc(p.authorName)}${privacyLabel}</div>
|
||||
<div class="post-date">${fmtDate(p.createdAt)}</div>
|
||||
<div class="post-author"><a href="/community/benutzer.html?userId=${p.authorId}" style="color:inherit;text-decoration:none;" onclick="event.stopPropagation()">${esc(p.authorName)}</a>${privacyLabel}</div>
|
||||
<div class="post-date" id="ppm-${p.postId}">${fmtDate(p.createdAt)}${editedLabel}</div>
|
||||
</div>
|
||||
${deleteBtn}
|
||||
${rightBtns}
|
||||
</div>
|
||||
<div class="post-text">${esc(p.text)}</div>
|
||||
${bildHtml}
|
||||
${umfrageHtml}
|
||||
<div id="ppva-${p.postId}">
|
||||
<div class="post-text">${esc(p.text)}</div>
|
||||
<div id="ppbi-${p.postId}" class="post-bild-wrap" data-post-id="${p.postId}">${bildHtml}</div>
|
||||
</div>
|
||||
<div id="ppea-${p.postId}" style="display:none;"></div>
|
||||
<div id="ppum-${p.postId}">${umfrageHtml}</div>
|
||||
<div class="post-actions">
|
||||
<button class="post-action-btn${p.likedByMe ? ' active' : ''}" id="pp-like-${p.postId}" onclick="likeProfilPost('${p.postId}')">
|
||||
♥ <span id="pp-lc-${p.postId}">${p.likeCount}</span>
|
||||
@@ -1665,6 +1641,73 @@
|
||||
if (res.ok) document.getElementById('pp-' + postId)?.remove();
|
||||
}
|
||||
|
||||
// ── Profil-Post-Bearbeitung ──
|
||||
|
||||
function startProfilEdit(postId) {
|
||||
const post = profilPostCache[postId];
|
||||
if (!post) return;
|
||||
startPostEdit({
|
||||
postId,
|
||||
prefix: 'pp',
|
||||
data: post,
|
||||
editBilderMap: profilEditBilder,
|
||||
saveFn: 'saveProfilEdit',
|
||||
cancelFn: 'cancelProfilEdit',
|
||||
addImgFn: 'profilEditAddImg',
|
||||
addOptionFn: 'profilEditAddOption',
|
||||
rmImgFn: 'profilEditRmImg'
|
||||
});
|
||||
}
|
||||
|
||||
function cancelProfilEdit(postId) {
|
||||
cancelPostEdit(postId, 'pp', profilEditBilder);
|
||||
}
|
||||
|
||||
function profilEditRmImg(postId, idx) {
|
||||
profilEditBilder.get(postId)?.splice(idx, 1);
|
||||
_renderEditThumbs(profilEditBilder, postId, 'pp', 'profilEditRmImg');
|
||||
}
|
||||
|
||||
function profilEditAddImg(input, postId) {
|
||||
const arr = profilEditBilder.get(postId);
|
||||
if (!arr) return;
|
||||
[...input.files].forEach(f => processImageFile(f, arr, () => {
|
||||
_renderEditThumbs(profilEditBilder, postId, 'pp', 'profilEditRmImg');
|
||||
}));
|
||||
input.value = '';
|
||||
}
|
||||
|
||||
function profilEditAddOption(postId) {
|
||||
editAddOptionRow('ppeo-' + postId);
|
||||
}
|
||||
|
||||
async function saveProfilEdit(postId) {
|
||||
const post = profilPostCache[postId];
|
||||
await savePostEdit({
|
||||
postId,
|
||||
prefix: 'pp',
|
||||
endpoint: '/feed/posts/' + postId,
|
||||
isUmfrage: post?.beitragTyp === 'UMFRAGE',
|
||||
editBilderMap: profilEditBilder,
|
||||
onSuccess: updated => {
|
||||
profilPostCache[postId] = { ...profilPostCache[postId], text: updated.text, bilder: updated.bilder || [], optionen: updated.optionen || [] };
|
||||
profilPostBilder.set(postId, updated.bilder || []);
|
||||
let umfrageHtml = '';
|
||||
if (post?.beitragTyp === 'UMFRAGE' && updated.optionen) {
|
||||
const totalVotes = updated.optionen.reduce((s, o) => s + o.stimmenCount, 0);
|
||||
umfrageHtml = updated.optionen.map(o => {
|
||||
const pct = totalVotes > 0 ? Math.round(o.stimmenCount / totalVotes * 100) : 0;
|
||||
return `<div class="umfrage-option-bar" onclick="voteProfilPost('${postId}','${o.optionId}')">
|
||||
<div class="umfrage-bar-fill" style="width:${pct}%"></div>
|
||||
<div class="umfrage-bar-content"><span>${esc(o.text)}</span><span>${pct}%</span></div>
|
||||
</div>`;
|
||||
}).join('') + `<div class="umfrage-total">${totalVotes} Stimme${totalVotes !== 1 ? 'n' : ''}</div>`;
|
||||
}
|
||||
applyPostEditDom(postId, 'pp', updated, umfrageHtml);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ── Lightbox (Post + Bild) ──
|
||||
function openPostLb(postId) {
|
||||
activeLbMode = 'post';
|
||||
@@ -1674,7 +1717,9 @@
|
||||
if (card) {
|
||||
const clone = card.cloneNode(true);
|
||||
clone.querySelectorAll('.post-actions').forEach(el => el.remove());
|
||||
clone.querySelectorAll('[id^="ppea-"]').forEach(el => el.remove());
|
||||
document.getElementById('lbPostBody').innerHTML = clone.innerHTML;
|
||||
_lbSetupContent(postId, 'pp', profilPostBilder.get(postId) || []);
|
||||
}
|
||||
loadLbComments();
|
||||
document.getElementById('postLightbox').classList.add('open');
|
||||
@@ -1728,7 +1773,6 @@
|
||||
if (e.target === document.getElementById('postLightbox')) closeLb();
|
||||
});
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.key === 'Escape' && document.getElementById('postLightbox').classList.contains('open')) closeLb();
|
||||
if (activeLbMode === 'image') {
|
||||
if (e.key === 'ArrowLeft') lbGalleryNav(-1);
|
||||
if (e.key === 'ArrowRight') lbGalleryNav(1);
|
||||
|
||||
Reference in New Issue
Block a user