Hashtags eingeführt
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:
@@ -68,6 +68,11 @@
|
||||
.empty-hint { color:var(--color-muted); font-size:0.9rem; margin-top:0.5rem; }
|
||||
.sentinel { height:1px; }
|
||||
|
||||
.hashtag-banner { display:flex; align-items:center; gap:0.75rem; margin-bottom:1.25rem; padding:0.65rem 1rem; background:var(--color-card); border:1px solid var(--color-secondary); border-radius:8px; }
|
||||
.hashtag-banner-tag { font-size:1.05rem; font-weight:700; color:var(--color-primary); }
|
||||
.hashtag-banner-back { margin-left:auto; font-size:0.82rem; color:var(--color-muted); text-decoration:none; }
|
||||
.hashtag-banner-back:hover { color:var(--color-primary); }
|
||||
|
||||
/* Lightbox */
|
||||
.lightbox { display:none; position:fixed; inset:0; background:rgba(0,0,0,0.88); z-index:300; align-items:center; justify-content:center; }
|
||||
.lightbox.open { display:flex; }
|
||||
@@ -92,7 +97,13 @@
|
||||
<div class="main">
|
||||
<div class="content">
|
||||
|
||||
<div class="tabs">
|
||||
<!-- Hashtag-Banner (nur sichtbar wenn ?tag=… gesetzt) -->
|
||||
<div class="hashtag-banner" id="hashtagBanner" style="display:none;">
|
||||
<span>Posts mit </span><span class="hashtag-banner-tag" id="hashtagBannerLabel"></span>
|
||||
<a class="hashtag-banner-back" href="/community/feed.html">× Zurück zum Feed</a>
|
||||
</div>
|
||||
|
||||
<div class="tabs" id="feedTabs">
|
||||
<button class="tab-btn active" id="tabMine" data-tab="mine" onclick="switchTab('mine', this)">Mein Feed</button>
|
||||
<button class="tab-btn" id="tabPublic" data-tab="public" onclick="switchTab('public', this)">Öffentlicher Feed</button>
|
||||
</div>
|
||||
@@ -140,6 +151,13 @@
|
||||
<div class="sentinel" id="publicSentinel"></div>
|
||||
</div>
|
||||
|
||||
<!-- Hashtag-Feed (wird angezeigt wenn ?tag=… gesetzt) -->
|
||||
<div id="tab-hashtag" style="display:none;">
|
||||
<div id="hashtagFeed"></div>
|
||||
<p class="empty-hint" id="hashtagEmpty" style="display:none;">Keine Posts mit diesem Hashtag.</p>
|
||||
<div class="sentinel" id="hashtagSentinel"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -165,37 +183,68 @@
|
||||
|
||||
<script src="/js/shared.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/meldung.js"></script>
|
||||
<script src="/js/hashtag.js"></script>
|
||||
<script>
|
||||
// ── State ──
|
||||
let myUserId = null;
|
||||
let activeLbPostId = null;
|
||||
let activeLbPostId = null;
|
||||
let activeLbPostType = null;
|
||||
let activeHashtag = null; // set when ?tag=... is in URL
|
||||
|
||||
const feedState = {
|
||||
mine: { page:0, hasMore:true, loading:false, loaded:false },
|
||||
public: { page:0, hasMore:true, loading:false, loaded:false }
|
||||
mine: { page:0, hasMore:true, loading:false, loaded:false },
|
||||
public: { page:0, hasMore:true, loading:false, loaded:false },
|
||||
hashtag: { page:0, hasMore:true, loading:false, loaded:false }
|
||||
};
|
||||
|
||||
let composeBilderArr = [];
|
||||
|
||||
// ── Hashtag-Modus prüfen ──
|
||||
const _urlTag = new URLSearchParams(window.location.search).get('tag');
|
||||
if (_urlTag) {
|
||||
activeHashtag = _urlTag.replace(/^#/, '').toLowerCase();
|
||||
document.getElementById('hashtagBanner').style.display = '';
|
||||
document.getElementById('hashtagBannerLabel').textContent = '#' + activeHashtag;
|
||||
document.getElementById('feedTabs').style.display = 'none';
|
||||
document.getElementById('tab-mine').style.display = 'none';
|
||||
document.getElementById('tab-public').style.display = 'none';
|
||||
document.getElementById('tab-hashtag').style.display = '';
|
||||
document.getElementById('compose').style.display = 'none';
|
||||
}
|
||||
|
||||
// ── Boot ──
|
||||
fetch('/login/me').then(r => r.ok ? r.json() : null).then(async user => {
|
||||
if (user) {
|
||||
myUserId = user.userId;
|
||||
const raw = sessionStorage.getItem('feedOpenPost');
|
||||
if (raw) {
|
||||
sessionStorage.removeItem('feedOpenPost');
|
||||
loadFeed('mine');
|
||||
openLbWithData(JSON.parse(raw));
|
||||
if (activeHashtag) {
|
||||
await loadFeed('hashtag');
|
||||
} else {
|
||||
await loadFeed('mine');
|
||||
const raw = sessionStorage.getItem('feedOpenPost');
|
||||
if (raw) {
|
||||
sessionStorage.removeItem('feedOpenPost');
|
||||
loadFeed('mine');
|
||||
openLbWithData(JSON.parse(raw));
|
||||
} else {
|
||||
await loadFeed('mine');
|
||||
}
|
||||
}
|
||||
}
|
||||
}).catch(() => {});
|
||||
|
||||
// ── Autocomplete für Compose ──
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const ta = document.getElementById('composeText');
|
||||
if (ta) attachHashtagAutocomplete(ta);
|
||||
});
|
||||
// Fallback falls DOMContentLoaded bereits gefeuert
|
||||
if (document.readyState !== 'loading') {
|
||||
const ta = document.getElementById('composeText');
|
||||
if (ta) attachHashtagAutocomplete(ta);
|
||||
}
|
||||
|
||||
// ── Tab switching ──
|
||||
function switchTab(name, btn) {
|
||||
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
||||
@@ -218,8 +267,14 @@
|
||||
state.loading = true;
|
||||
state.loaded = true;
|
||||
try {
|
||||
const endpoint = tab === 'mine' ? '/feed/mine' : '/feed/public';
|
||||
const res = await fetch(`${endpoint}?page=${state.page}&size=10`);
|
||||
let url;
|
||||
if (tab === 'hashtag') {
|
||||
url = `/feed/hashtag?tag=${encodeURIComponent(activeHashtag)}&page=${state.page}&size=10`;
|
||||
} else {
|
||||
const base = tab === 'mine' ? '/feed/mine' : '/feed/public';
|
||||
url = `${base}?page=${state.page}&size=10`;
|
||||
}
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) return;
|
||||
const data = await res.json();
|
||||
const feedEl = document.getElementById(tab + 'Feed');
|
||||
@@ -238,12 +293,14 @@
|
||||
const observer = new IntersectionObserver(entries => {
|
||||
entries.forEach(e => {
|
||||
if (!e.isIntersecting) return;
|
||||
if (e.target.id === 'mineSentinel') loadFeed('mine');
|
||||
if (e.target.id === 'publicSentinel') loadFeed('public');
|
||||
if (e.target.id === 'mineSentinel') loadFeed('mine');
|
||||
if (e.target.id === 'publicSentinel') loadFeed('public');
|
||||
if (e.target.id === 'hashtagSentinel') loadFeed('hashtag');
|
||||
});
|
||||
}, { threshold: 0.5 });
|
||||
observer.observe(document.getElementById('mineSentinel'));
|
||||
observer.observe(document.getElementById('publicSentinel'));
|
||||
observer.observe(document.getElementById('hashtagSentinel'));
|
||||
|
||||
// bilderCarousel und carNav kommen aus shared.js
|
||||
|
||||
@@ -297,7 +354,7 @@
|
||||
</div>
|
||||
${deleteBtn}
|
||||
</div>
|
||||
<div class="post-text">${esc(p.text)}</div>
|
||||
<div class="post-text">${renderTextWithHashtags(p.text)}</div>
|
||||
${bildHtml}
|
||||
${umfrageHtml}
|
||||
<div class="post-actions">
|
||||
|
||||
Reference in New Issue
Block a user