diff --git a/code.js b/code.js index 8c88b6d..9e139c7 100644 --- a/code.js +++ b/code.js @@ -1,24 +1,112 @@ -const instanceColor = getComputedStyle(document.documentElement).getPropertyValue('--instance-color').trim() || '#ff6347'; -const backgroundColor = getComputedStyle(document.documentElement).getPropertyValue('--background-color').trim() || '#ff6347'; - document.addEventListener('DOMContentLoaded', function() { - const accountsContainer = document.querySelector('#account-list'); - let accounts = Array.from(document.querySelectorAll('.account')); - - // Separate Favoriten und gemischte Accounts - const favoriteAccounts = accounts.filter(account => account.dataset.favorite === "true"); - const otherAccounts = accounts.filter(account => !account.dataset.favorite); - - // Mische übrige Accounts + const shuffleContainers = document.querySelectorAll('.shuffle-container'); const seed = Date.now(); - const shuffledOtherAccounts = seededShuffle(otherAccounts, seed); - const sortedAccounts = [...favoriteAccounts, ...shuffledOtherAccounts]; + shuffleContainers.forEach(container => { + seededShuffleChildren(container, seed); + }); - accountsContainer.innerHTML = ''; - sortedAccounts.forEach(account => accountsContainer.appendChild(account)); + const starterkits = document.querySelectorAll('.starterkit'); - sortedAccounts.forEach(function(account) { + starterkits.forEach(kit => { + const authorAccount = kit.querySelector('.autor .account'); + if (authorAccount) { + try { + const url = new URL(authorAccount.getAttribute('href')); + const pathParts = url.pathname.split('/'); + + if (pathParts.length > 1 && pathParts[1].startsWith('@')) { + const handle = pathParts[1].substring(1); + const instance = url.hostname; + const lookupUrl = `https://${config.homeInstance}/api/v1/accounts/lookup?acct=${handle}@${instance}`; + + fetch(lookupUrl) + .then(response => response.json()) + .then(data => { + const avatarImage = document.createElement('img'); + avatarImage.classList.add('account-avatar'); + avatarImage.alt = data.avatar ? `Profilbild von ${data.username}` : 'Profilbild nicht verfügbar'; + avatarImage.src = data.avatar || ''; + + authorAccount.prepend(avatarImage); + + // Ergänze die Klasse basierend auf der Instanz + const serverClass = getServerClass(instance); + if (serverClass) { + authorAccount.classList.add(serverClass); + } + }) + .catch(error => { + console.error('Fehler beim Abrufen des Autoren-Profils:', error); + }); + } else { + console.error('Ungültiger Benutzer-Handle in URL:', url.href); + } + } catch (error) { + console.error('Fehler bei der Verarbeitung der URL:', error); + } + } + + const accounts = kit.querySelectorAll('.account'); + accounts.forEach(account => { + const parent = account.closest('.autor'); + if (!parent) { + account.style.display = 'none'; + } + }); + + // Zeige die Anzahl der Accounts auf der StarterKit-Kachel + const accountCount = kit.querySelectorAll('.account:not(.autor .account)').length; + const accountCountElement = document.createElement('div'); + accountCountElement.classList.add('account-count'); + accountCountElement.textContent = `${accountCount} Profile`; + kit.appendChild(accountCountElement); + + // Füge Event-Listener für StarterKit-Kacheln hinzu + kit.addEventListener('click', function() { + const title = kit.querySelector('h3').textContent; + const accountsForPopup = Array.from(kit.querySelectorAll('.account')).filter(account => !account.closest('.autor')); + const popup = createRecommendationPopup(accountsForPopup, title); + document.body.appendChild(popup); + }); + }); +}); + +/** + * Funktion zum Erstellen eines Pop-ups. + * @param {NodeList} accounts - Die Liste der Accounts für das Pop-up. + * @param {string} title - Der Titel des StarterKits. + * @returns {HTMLElement} - Das erzeugte Pop-up-Element. + */ +function createRecommendationPopup(accounts, title) { + // Overlay erstellen + const overlay = document.createElement('div'); + overlay.classList.add('recommendation-popup'); + + // Pop-up-Inhalt + const content = document.createElement('div'); + content.classList.add('recommendation-popup-content'); + + // Header des Pop-ups + const header = document.createElement('div'); + header.classList.add('recommendation-popup-header'); + + const titleElement = document.createElement('h3'); + titleElement.textContent = title; + + const closeButton = document.createElement('button'); + closeButton.classList.add('close-popup'); + closeButton.textContent = '×'; + closeButton.addEventListener('click', () => overlay.remove()); // Schließen-Funktion + + header.appendChild(titleElement); + header.appendChild(closeButton); + + // Body des Pop-ups + const body = document.createElement('div'); + body.classList.add('recommendation-popup-body'); + + accounts.forEach(account => { try { const url = new URL(account.getAttribute('href')); const pathParts = url.pathname.split('/'); @@ -31,61 +119,8 @@ document.addEventListener('DOMContentLoaded', function() { fetch(lookupUrl) .then(response => response.json()) .then(data => { - const link = document.createElement('a'); - link.classList.add('account-card-link'); - link.href = account.href; - link.target = '_blank'; - - const card = document.createElement('div'); - card.classList.add('account-card'); - - const avatarImage = document.createElement('img'); - avatarImage.classList.add('account-avatar'); - avatarImage.alt = data.avatar ? `Profilbild von ${data.username}` : 'Profilbild nicht verfügbar'; - avatarImage.src = data.avatar || ''; - - const infoContainer = document.createElement('div'); - infoContainer.classList.add('account-info'); - - const displayNameElement = document.createElement('p'); - displayNameElement.classList.add('display-name'); - displayNameElement.textContent = data.display_name || handle; - - const handleElement = document.createElement('p'); - handleElement.classList.add('handle'); - - const serverHandleElement = document.createElement('span'); - serverHandleElement.classList.add('server-handle'); - serverHandleElement.textContent = `@${instance}`; - - if (instance === config.homeInstance) { - serverHandleElement.style.backgroundColor = instanceColor; - } - - handleElement.innerHTML = `@${data.username}`; - handleElement.appendChild(serverHandleElement); - - // QR-Code Button - const qrButton = document.createElement('button'); - qrButton.classList.add('qr-code-button'); - qrButton.innerHTML = ` - - - - `; - qrButton.addEventListener('click', function(event) { - event.preventDefault(); - showQrPopup(link.href); - }); - - infoContainer.appendChild(displayNameElement); - infoContainer.appendChild(handleElement); - card.appendChild(avatarImage); - card.appendChild(infoContainer); - card.appendChild(qrButton); - link.appendChild(card); - - account.replaceWith(link); + const card = createAccountCard(data, account.href, instance); + body.appendChild(card); }) .catch(error => { console.error('Fehler beim Abrufen des Profils:', error); @@ -97,96 +132,122 @@ document.addEventListener('DOMContentLoaded', function() { console.error('Fehler bei der Verarbeitung der URL:', error); } }); -}); -// QR-Code anzeigen -function showQrPopup(profileUrl) { - // QR-Overlay für Abdunkelung - const overlay = document.createElement('div'); - overlay.classList.add('qr-overlay'); + // Pop-up zusammenfügen + content.appendChild(header); + content.appendChild(body); + overlay.appendChild(content); - // Popup-Container - const popup = document.createElement('div'); - popup.classList.add('qr-popup'); - - // Schließen-Button - const closeButton = document.createElement('button'); - closeButton.classList.add('close-popup'); - closeButton.textContent = 'x'; - closeButton.innerHTML = ` - - `; - closeButton.addEventListener('click', function () { - overlay.remove(); - }); - - // Event-Listener für das Overlay (schließt Popup bei Klick auf Hintergrund) - overlay.addEventListener('click', function (event) { - if (event.target === overlay) { // Nur schließen, wenn außerhalb des Popups geklickt wurde + // Event-Listener für Overlay (schließt Pop-up bei Klick auf Hintergrund) + overlay.addEventListener('click', function(event) { + if (event.target === overlay) { overlay.remove(); } }); - // QR-Code-Container - const qrContainer = document.createElement('div'); - qrContainer.classList.add('qr-code-container'); + return overlay; +} - // QR-Code mit kjua generieren - const qrCode = kjua({ - render: 'canvas', - text: profileUrl, - size: 200, - quiet: 2, - ecLevel: 'H', - }); +/** + * Funktion zum Erstellen einer Account-Kachel. + * @param {Object} data - Die Account-Daten von der API. + * @param {string} href - Die URL des Accounts. + * @param {string} instance - Der Instanz-Name. + * @returns {HTMLElement} - Die erzeugte Account-Kachel. + */ +function createAccountCard(data, href, instance) { + const link = document.createElement('a'); + link.classList.add('account-card-link'); + link.href = href; + link.target = '_blank'; - qrContainer.appendChild(qrCode); + const card = document.createElement('div'); + card.classList.add('account-card'); - // Extrahiere User-Handle und Server-Handle - const url = new URL(profileUrl); - const pathParts = url.pathname.split('/'); - const userHandle = pathParts[1] || '@unbekannt'; - const serverHandle = url.hostname || 'unbekannt'; + const avatarImage = document.createElement('img'); + avatarImage.classList.add('account-avatar'); + avatarImage.alt = data.avatar ? `Profilbild von ${data.username}` : 'Profilbild nicht verfügbar'; + avatarImage.src = data.avatar || ''; - // Handle unter dem QR-Code - const handleContainer = document.createElement('p'); - handleContainer.classList.add('qr-handle'); + const infoContainer = document.createElement('div'); + infoContainer.classList.add('account-info'); - const userHandleText = document.createTextNode(userHandle); + const displayNameElement = document.createElement('p'); + displayNameElement.classList.add('display-name'); + displayNameElement.textContent = data.display_name || data.username; - const serverHandleSpan = document.createElement('span'); - serverHandleSpan.classList.add('qr-server-handle'); - serverHandleSpan.textContent = `@${serverHandle}`; + const handleElement = document.createElement('p'); + handleElement.classList.add('handle'); - // Dynamisch die Hintergrundfarbe für Variable "config.homeInstance" setzen - if (serverHandle === config.homeInstance) { - serverHandleSpan.style.backgroundColor = instanceColor; - qrCode.style.borderColor = instanceColor; + const serverHandleElement = document.createElement('span'); + serverHandleElement.classList.add('server-handle'); + serverHandleElement.textContent = `@${instance}`; + + // Ergänze die Klasse basierend auf der Instanz + const serverClass = getServerClass(instance); + if (serverClass) { + serverHandleElement.classList.add(serverClass); } - handleContainer.appendChild(userHandleText); - handleContainer.appendChild(serverHandleSpan); + handleElement.textContent = `@${data.username}`; + handleElement.appendChild(serverHandleElement); - // Popup zusammenfügen - popup.appendChild(closeButton); - popup.appendChild(qrContainer); - popup.appendChild(handleContainer); + infoContainer.appendChild(displayNameElement); + infoContainer.appendChild(handleElement); - // Overlay zusammenfügen - overlay.appendChild(popup); - document.body.appendChild(overlay); + card.appendChild(avatarImage); + card.appendChild(infoContainer); + link.appendChild(card); + + return link; } -function seededRandom(seed) { - let x = Math.sin(seed++) * 10000; - return x - Math.floor(x); +/** + * Funktion, um die Kinder eines Containers deterministisch neu anzuordnen. + * @param {HTMLElement} container - Der Container, dessen Kinder durchmischt werden sollen. + * @param {number} seed - Der Seed für das deterministische Shuffle. + */ +function seededShuffleChildren(container, seed) { + const children = Array.from(container.children); + const shuffled = seededShuffle(children, seed); + shuffled.forEach(child => container.appendChild(child)); } +/** + * Funktion für einen deterministischen Shuffle-Algorithmus. + * @param {Array} array - Das Array, das gemischt werden soll. + * @param {number} seed - Der Seed für das deterministische Shuffle. + * @returns {Array} - Das gemischte Array. + */ function seededShuffle(array, seed) { for (let i = array.length - 1; i > 0; i--) { - const j = Math.floor(seededRandom(seed) * (i + 1)); + const j = Math.floor(seededRandom(seed++) * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; - seed++; } return array; } + +/** + * Funktion für einen deterministischen Zufallswert. + * @param {number} seed - Der Seed für den Zufallswert. + * @returns {number} - Der generierte Zufallswert. + */ +function seededRandom(seed) { + const x = Math.sin(seed++) * 10000; + return x - Math.floor(x); +} + +/** + * Funktion zur Zuordnung von Instanzen zu CSS-Klassen. + * @param {string} instance - Der Name der Instanz. + * @returns {string|null} - Die zugehörige CSS-Klasse oder null, wenn keine Übereinstimmung vorliegt. + */ +function getServerClass(instance) { + const serverClasses = { + 'libori.social': 'liboriSocial', + 'reliverse.social': 'reliverseSocial', + 'kirche.social': 'kircheSocial', + 'katholisch.social': 'katholischSocial' + }; + return serverClasses[instance] || null; +} diff --git a/index.html b/index.html index 8a72d19..2d7d52d 100644 --- a/index.html +++ b/index.html @@ -36,7 +36,7 @@

Wir sind #FediKirche

-
+
@@ -124,24 +124,132 @@
-

Empfehlungen für dich

-
-

💡 Tipp

-

Um anderen mit deinem Account zu folgen, klicke auf der Profilseite auf 'Folgen'. Neue Beiträge erscheinen dann automatisch auf deiner Startseite.

-

📱 für Nutzer der (Android) Mastodon-App

-

Mit der App kannst du Profile über einen QR-Code erreichen. Klicke für den QR-Code einfach auf den - - -Button.
In der Mastodon-App klickst du auf die 🔎 (Entdecken). Den QR-Code-Scanner findest du in der Suchleiste.

- -
-
- - - - - - - +

unsere StarterKits für dich 🚀

+
+
+

#FediKirche

+

Accounts rund um den Hashtag #FediKirche

+
zusammengestellt von
+ +
+ + +
+

tägliche Impulse

+

tägliche Impulse

+
zusammengestellt von
+ +
+
+

Vereine und Initiativen

+

Vereine und Initiativen

+
zusammengestellt von
+ +
diff --git a/style.css b/style.css index 4f872d4..bcf36fb 100644 --- a/style.css +++ b/style.css @@ -159,11 +159,83 @@ section { box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); } +/* StarterKit Grid Darstellung */ .recommendations { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 20px; + padding: 0; +} + +.starterkit { + background-color: var(--secondary-background-color); + border-radius: 15px; + padding: 20px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); + transition: transform 0.3s ease, box-shadow 0.3s ease; + cursor: pointer; + position: relative; +} + +.starterkit:hover { + transform: translateY(-5px); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.4); +} + +/* Pop-up Styling */ +.recommendation-popup { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: rgba(0, 0, 0, 0.8); + display: flex; + justify-content: center; + align-items: center; + z-index: 9999; +} + +.recommendation-popup-content { + background-color: var(--secondary-background-color); + border-radius: 15px; + padding: 20px; + width: 90%; + max-width: 800px; + height: 80%; + overflow: hidden; + display: flex; + flex-direction: column; +} + +.recommendation-popup-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; +} + +.recommendation-popup-header h3 { + color: #ffffff; + margin: 0; +} + +.close-popup { + background-color: transparent; + border: none; + color: #ffffff; + font-size: 1.5rem; + cursor: pointer; +} + +.recommendation-popup-body { + flex: 1; + overflow-y: auto; + padding: 10px 10px 0 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: 20px; - padding: 0; + align-items: start; margin-top: 20px; list-style-type: none; } @@ -406,7 +478,7 @@ footer p { border: solid 1px var(--liboriSocial-color); } -.instance-card.liboriSocial a.bigger-button { +.instance-card.liboriSocial a.bigger-button, .server-handle.liboriSocial { background-color: var(--liboriSocial-color); } @@ -414,13 +486,15 @@ footer p { background-color: var(--liboriSocial-secondary-color); } + + /* --- reliverse.social --- */ .instance-card.reliverseSocial { border: solid 1px var(--reliverseSocial-color); } -.instance-card.reliverseSocial a.bigger-button { +.instance-card.reliverseSocial a.bigger-button, .server-handle.reliverseSocial { background-color: var(--reliverseSocial-color); } @@ -430,7 +504,7 @@ footer p { border: solid 1px var(--kircheSocial-color); } -.instance-card.kircheSocial a.bigger-button { +.instance-card.kircheSocial a.bigger-button, .server-handle.kircheSocial { background-color: var(--kircheSocial-color); } @@ -444,7 +518,7 @@ footer p { border: solid 1px var(--katholischSocial-color); } -.instance-card.katholischSocial a.bigger-button { +.instance-card.katholischSocial a.bigger-button, .server-handle.katholischSocial { background-color: var(--katholischSocial-color); }