Commit 9d0242c1 authored by Gemini's avatar Gemini Committed by Jakob Moser
Browse files

fix: Correct message display for filtered favorites

Restores the correct messaging logic when a search term is applied
within the favorites view.

- Ensures that if a search filters the list of favorites, a message
  indicates how many of the total favorites are being shown or if
  no favorites match the search.
- Refines message object structure in getProcessedSongsForView.
- Updates view logic to correctly interpret and display these messages,
  including the 'clear search' link for favorites.
parent 1f836b47
Loading
Loading
Loading
Loading
+95 −69
Original line number Diff line number Diff line
// frontend/SongList.mjs
import SongItem from './SongItem.mjs';
import PaginationControls from './PaginationControls.mjs'; // NEU: Import für Paginierungs-Komponente
import PaginationControls from './PaginationControls.mjs';

const FAVORITES_STORAGE_KEY = 'songAppFavorites';
const API_URL_SONGS = '/api/songs';
@@ -9,8 +9,8 @@ const SongList = {
    allSongs: [],
    isLoading: true,
    currentPage: 1,
    itemsPerPage: 60,
    totalPagesComputed: 1, // Wird dynamisch berechnet
    itemsPerPage: 60, // Wie vom Benutzer angegeben
    totalPagesComputed: 1,

    _loadFavoriteIdsFromStorage: function() {
        try {
@@ -34,7 +34,7 @@ const SongList = {

    loadAllSongsFromServer: function() {
        this.isLoading = true;
        this.currentPage = 1; // Reset current page on full load
        this.currentPage = 1;
        const favoriteIdsFromStorage = this._loadFavoriteIdsFromStorage();
        m.request({
            method: "GET",
@@ -61,8 +61,6 @@ const SongList = {
        this.loadAllSongsFromServer();
    },

    // Wird aufgerufen, bevor die Komponente aktualisiert wird.
    // Setzt die aktuelle Seite zurück, wenn sich Filter ändern.
    onbeforeupdate: function(vnode, old) {
        if (vnode.attrs.currentView !== old.attrs.currentView ||
            vnode.attrs.searchTerm !== old.attrs.searchTerm) {
@@ -71,34 +69,32 @@ const SongList = {
        return true;
    },

    // Wird von PaginationControls aufgerufen, um die Seite zu wechseln.
    onPageChange: function(newPage) {
        if (newPage >= 1 && newPage <= this.totalPagesComputed) {
            this.currentPage = newPage;
            // m.redraw(); // Mithril handhabt das Redraw normalerweise automatisch
        }
    },

    getProcessedSongsForView: function(viewFilter, searchTerm) {
        let initialSongsForView = [];
        let messageFromTabFilter = null;
        let messageFromTabFilter = null; // Nachricht rein basierend auf Tab-Auswahl

        // 1. Songs basierend auf dem aktuellen Tab (View) filtern
        switch (viewFilter) {
            case 'favorites':
                initialSongsForView = this.allSongs.filter(song => song.isFavorite);
                if (initialSongsForView.length === 0 && !searchTerm) { // Nachricht nur, wenn kein Suchbegriff aktiv ist
                if (initialSongsForView.length === 0) { // Überhaupt keine Favoriten
                    messageFromTabFilter = "Du hast noch keine Favoriten markiert.";
                }
                break;
            case 'popular':
                initialSongsForView = [];
                // initialSongsForView bleibt leer
                messageFromTabFilter = "Die Liste der beliebten Songs ist bald verfügbar!";
                break;
            case 'all':
            default:
                initialSongsForView = this.allSongs;
                if (initialSongsForView.length === 0 && !this.isLoading && !searchTerm) {
                if (initialSongsForView.length === 0 && !this.isLoading) {
                    messageFromTabFilter = "Keine Songs in der Bibliothek.";
                }
                break;
@@ -107,68 +103,86 @@ const SongList = {
        let songsAfterSearch = initialSongsForView.slice();
        let composedMessage = null; // Endgültige Nachricht für den Benutzer

        // 2. Suchbegriff anwenden
        if (searchTerm && searchTerm.trim() !== '') {
            const lowerSearchTerm = searchTerm.trim().toLowerCase();
        // 2. Suchbegriff anwenden und Nachrichten erstellen
        const trimmedSearchTerm = searchTerm ? searchTerm.trim() : '';
        if (trimmedSearchTerm !== '') {
            const lowerSearchTerm = trimmedSearchTerm.toLowerCase();
            songsAfterSearch = initialSongsForView.filter(song =>
                (song.title && song.title.toLowerCase().includes(lowerSearchTerm)) ||
                (song.artist && song.artist.toLowerCase().includes(lowerSearchTerm))
            );

            if (songsAfterSearch.length === 0) {
                if (viewFilter === 'favorites' && initialSongsForView.length > 0) {
                    // Suchbegriff hat keine Favoriten gefunden, obwohl welche da sind
                    composedMessage = `Deine Suche ergab keine Treffer unter deinen ${initialSongsForView.length} Favoriten.`;
            if (viewFilter === 'favorites') {
                const totalFavoritesOverall = initialSongsForView.length; // Anzahl Favoriten vor der Suche
                const favoritesFoundBySearch = songsAfterSearch.length;

                if (totalFavoritesOverall > 0) { // Nur wenn es überhaupt Favoriten gab
                    if (favoritesFoundBySearch < totalFavoritesOverall) {
                        if (favoritesFoundBySearch === 0) {
                            // Suche aktiv, Favoriten vorhanden, aber Suche findet keine davon
                            composedMessage = { type: 'filtered_favorites_none_found_by_search', total: totalFavoritesOverall };
                        } else {
                    composedMessage = "Keine Songs für deine Suche gefunden.";
                            // Suche aktiv, Favoriten vorhanden, Suche findet einige, aber nicht alle
                            composedMessage = { type: 'filtered_favorites_some_hidden', total: totalFavoritesOverall, showing: favoritesFoundBySearch };
                        }
                    }
                    // Wenn favoritesFoundBySearch === totalFavoritesOverall, hat die Suche nichts ausgeblendet -> keine spezielle Nachricht hier.
                } else {
            // Kein Suchbegriff aktiv, verwende die Nachricht vom Tab-Filter, falls vorhanden
                    // Keine Favoriten vorhanden, aber Suche ist aktiv.
                    // messageFromTabFilter ("Du hast noch keine Favoriten markiert.") ist hier passend.
                    composedMessage = messageFromTabFilter;
                }
            }
            
        const totalFilteredItems = songsAfterSearch.length;
            // Allgemeine Nachricht, wenn die Suche keine Ergebnisse liefert (und nicht schon durch Favoriten-Logik abgedeckt)
            if (songsAfterSearch.length === 0 && !composedMessage) {
                 composedMessage = "Keine Songs für deine Suche gefunden.";
            }

        } else { // Kein Suchbegriff aktiv
            composedMessage = messageFromTabFilter;
        }

        const totalFilteredItems = songsAfterSearch.length; // Gesamtanzahl der Items nach Filterung/Suche (über alle Seiten)

        // 3. Paginierungslogik
        const totalPages = Math.ceil(totalFilteredItems / this.itemsPerPage) || 1;
        this.totalPagesComputed = totalPages; // Speichern für onPageChange
        this.totalPagesComputed = totalPages;

        // Sicherstellen, dass currentPage innerhalb gültiger Grenzen liegt
        if (this.currentPage > totalPages) {
            this.currentPage = totalPages;
        }
        if (this.currentPage < 1) {
            this.currentPage = 1;
        }
        if (this.currentPage > totalPages) this.currentPage = totalPages;
        if (this.currentPage < 1) this.currentPage = 1;

        const startIndex = (this.currentPage - 1) * this.itemsPerPage;
        const paginatedSongs = songsAfterSearch.slice(startIndex, startIndex + this.itemsPerPage);

        // 4. Songs für die aktuelle Seite nach Künstler gruppieren
        const groups = {};
        if (paginatedSongs.length > 0) {
            paginatedSongs.forEach(song => {
                groups[song.artist] = groups[song.artist] || [];
                groups[song.artist].push(song);
            });
        }
        const sortedArtistNames = Object.keys(groups).sort((a, b) => a.localeCompare(b));
        const finalGroupedSongsOnPage = {};
        sortedArtistNames.forEach(artist => {
            finalGroupedSongsOnPage[artist] = groups[artist].sort((a, b) => a.title.localeCompare(b.title));
        });
        
        // 5. Endgültige Nachricht, falls nach Filterung/Suche gar keine Items da sind
        // 5. Endgültige Nachricht, falls nach Filterung/Suche gar keine Items da sind (über alle Seiten)
        //    und noch keine spezifischere Nachricht (z.B. Favoriten-Logik) gesetzt wurde.
        if (totalFilteredItems === 0 && !composedMessage && !this.isLoading) {
             composedMessage = "Keine Songs entsprechen den aktuellen Kriterien.";
        }


        return {
            groupedSongs: finalGroupedSongsOnPage,
            message: composedMessage, // Dies ist jetzt eine einfache Zeichenkette oder null
            groupedSongs: finalGroupedSongsOnPage, // Songs für die aktuelle Seite
            message: composedMessage, // Kann null, string oder Objekt sein
            hasContentOnPage: paginatedSongs.length > 0,
            currentPage: this.currentPage,
            totalPages: this.totalPagesComputed
            totalPages: this.totalPagesComputed,
            totalFilteredItems: totalFilteredItems // Gesamtanzahl gefilterter Items
        };
    },

@@ -183,7 +197,6 @@ const SongList = {
                }
            });
            this._saveFavoriteIdsToStorage(currentFavoriteIds);
            // Kein m.redraw() hier, da die Ansicht sowieso aktualisiert wird, wenn nötig.
        }
    },

@@ -194,49 +207,63 @@ const SongList = {
            return m("div.song-list-outer-container", m("div.loading-placeholder", "Lade Songs vom Server..."));
        }

        const { groupedSongs, message, hasContentOnPage, currentPage, totalPages } =
        const { groupedSongs, message, hasContentOnPage, currentPage, totalPages, totalFilteredItems } =
            this.getProcessedSongsForView(currentView, searchTerm);
        
        const artists = Object.keys(groupedSongs);
        const artistsOnPage = Object.keys(groupedSongs);
        let messageElement = null;

        // Logik für "Suche zurücksetzen"-Link
        const onClearSearch = (e) => {
            e.preventDefault();
            if (onSearchTermChange) {
                onSearchTermChange(''); // Dies löst onbeforeupdate aus und setzt currentPage zurück
            }
        // Hilfsfunktion für den "Suche zurücksetzen"-Link
        const createClearSearchLink = (totalFavs) => {
            return m("a.clear-search-link", {
                onclick: (e) => { e.preventDefault(); if (onSearchTermChange) onSearchTermChange(''); },
                href: "#", role: "button", tabindex: 0,
                onkeypress: (e) => { if ((e.key === 'Enter' || e.key === ' ') && onSearchTermChange) { e.preventDefault(); onSearchTermChange(''); } }
            }, `alle ${totalFavs} Favoriten anzeigen`);
        };
        
        if (!hasContentOnPage && message) {
            let messageContent = [message];
            // Füge "Suche zurücksetzen"-Link hinzu, wenn die Nachricht aufgrund der Suche im Favoriten-Tab angezeigt wird
            // und es überhaupt Favoriten gibt.
            const totalFavoritesOverall = this.allSongs.filter(s => s.isFavorite).length;
            if (searchTerm && currentView === 'favorites' && totalFavoritesOverall > 0 && message.toLowerCase().includes("suche")) {
                 messageContent.push(` Möchtest du `);
                 messageContent.push(m("a.clear-search-link", {
                    onclick: onClearSearch,
                    href: "#", role: "button", tabindex:0,
                    onkeypress: (e) => { if(e.key === 'Enter' || e.key === ' ') onClearSearch(e); }
                 }, `alle ${totalFavoritesOverall} Favoriten anzeigen`));
                 messageContent.push(`?`);
        // Nachrichten-Rendering basierend auf dem `message`-Objekt oder String
        if (message && typeof message === 'object') {
            if (message.type === 'filtered_favorites_some_hidden') {
                messageElement = m("p.view-message", [
                    `Deine Suche zeigt ${message.showing} von deinen insgesamt ${message.total} Favoriten. `,
                    `Möchtest du `,
                    createClearSearchLink(message.total),
                    `?`
                ]);
            } else if (message.type === 'filtered_favorites_none_found_by_search') {
                 messageElement = m("p.view-message", [
                    `Deine Suche ergab keine Treffer unter deinen ${message.total} Favoriten. `,
                    `Möchtest du `,
                    createClearSearchLink(message.total),
                    `?`
                ]);
            }
            // Hier könnten weitere Objekt-Nachrichtentypen behandelt werden
        } else if (message && typeof message === 'string') {
            // String-Nachrichten werden angezeigt, wenn totalFilteredItems 0 ist
            // ODER wenn es eine spezifische Ansicht wie 'popular' ist, die immer eine Nachricht hat.
            if (totalFilteredItems === 0 || currentView === 'popular') {
                messageElement = m("p.view-message", message);
            }
            messageElement = m("p.view-message", messageContent);
        } else if (!hasContentOnPage && artists.length === 0 && !this.isLoading && !message) {
            // Fallback-Nachricht, wenn keine andere Nachricht gesetzt wurde und keine Inhalte da sind.
        }

        // Fallback-Nachricht, wenn keine spezifische Nachricht erstellt wurde,
        // aber absolut keine Items vorhanden sind (über alle Seiten).
        // Dies sollte durch getProcessedSongsForView abgedeckt sein, ist aber ein Sicherheitsnetz.
        if (!messageElement && totalFilteredItems === 0 && !this.isLoading) {
             messageElement = m("p.view-message", "Keine Songs entsprechen den aktuellen Kriterien.");
        }

        const elementsToRender = [];
        if (messageElement) {
        if (messageElement) { // Wenn eine Nachricht zum Anzeigen vorhanden ist
            elementsToRender.push(messageElement);
        }

        if (hasContentOnPage) {
            elementsToRender.push(
                artists.map(artist =>
                    m("div.artist-group", { key: `${currentView}-${searchTerm || 'all'}-${artist}-${currentPage}` }, [ // currentPage in key
                artistsOnPage.map(artist =>
                    m("div.artist-group", { key: `${currentView}-${searchTerm || 'all'}-${artist}-${currentPage}` }, [
                        m("h2.artist-name", artist),
                        groupedSongs[artist].map(s =>
                            m(SongItem, {
@@ -250,7 +277,6 @@ const SongList = {
            );
        }
        
        // Füge Paginierungs-Steuerelemente hinzu, wenn mehr als eine Seite vorhanden ist
        if (totalPages > 1) {
            elementsToRender.push(m(PaginationControls, {
                currentPage: currentPage,