/** * FediStream Library JavaScript * * @package WP_FediStream */ ( function( $ ) { 'use strict'; const Library = { currentTab: 'favorites', currentPage: { favorites: 1, artists: 1, history: 1 }, currentFilter: 'all', isLoading: false, init: function() { this.bindEvents(); this.loadInitialTab(); }, bindEvents: function() { const self = this; // Tab navigation. $( '.fedistream-library-nav .tab-btn' ).on( 'click', function() { const tab = $( this ).data( 'tab' ); self.switchTab( tab ); } ); // Filter change. $( '.fedistream-library-filters .filter-type' ).on( 'change', function() { self.currentFilter = $( this ).val(); self.currentPage.favorites = 1; self.loadFavorites(); } ); // Clear history. $( '.btn-clear-history' ).on( 'click', function() { self.clearHistory(); } ); // Pagination clicks. $( document ).on( 'click', '.library-pagination .page-btn', function() { const page = $( this ).data( 'page' ); const tab = $( this ).closest( '.library-pagination' ).data( 'tab' ); self.goToPage( tab, page ); } ); // Unfavorite button. $( document ).on( 'click', '.unfavorite-btn', function() { const btn = $( this ); const contentType = btn.data( 'content-type' ); const contentId = btn.data( 'content-id' ); self.toggleFavorite( contentType, contentId, btn.closest( '.library-item' ) ); } ); // Unfollow button. $( document ).on( 'click', '.unfollow-btn', function() { const btn = $( this ); const artistId = btn.data( 'artist-id' ); self.toggleFollow( artistId, btn.closest( '.library-item' ) ); } ); // Play button. $( document ).on( 'click', '.play-btn', function( e ) { e.preventDefault(); const trackId = $( this ).data( 'track-id' ); self.playTrack( trackId ); } ); }, loadInitialTab: function() { const initialTab = $( '.fedistream-library' ).data( 'initial-tab' ) || 'favorites'; this.switchTab( initialTab ); }, switchTab: function( tab ) { this.currentTab = tab; // Update nav. $( '.fedistream-library-nav .tab-btn' ).removeClass( 'active' ); $( '.fedistream-library-nav .tab-btn[data-tab="' + tab + '"]' ).addClass( 'active' ); // Update content. $( '.tab-content' ).removeClass( 'active' ); $( '#tab-' + tab ).addClass( 'active' ); // Show/hide filters. if ( tab === 'favorites' ) { $( '.fedistream-library-filters' ).show(); } else { $( '.fedistream-library-filters' ).hide(); } // Load content. this.loadTabContent( tab ); }, loadTabContent: function( tab ) { switch ( tab ) { case 'favorites': this.loadFavorites(); break; case 'artists': this.loadArtists(); break; case 'history': this.loadHistory(); break; } }, loadFavorites: function() { const self = this; if ( this.isLoading ) { return; } this.showLoading(); $.ajax( { url: fedistreamLibrary.ajaxUrl, type: 'POST', data: { action: 'fedistream_get_library', nonce: fedistreamLibrary.nonce, type: this.currentFilter, page: this.currentPage.favorites }, success: function( response ) { self.hideLoading(); if ( response.success ) { self.renderFavorites( response.data ); } else { self.showError( response.data.message ); } }, error: function() { self.hideLoading(); self.showError( fedistreamLibrary.i18n.error ); } } ); }, loadArtists: function() { const self = this; if ( this.isLoading ) { return; } this.showLoading(); $.ajax( { url: fedistreamLibrary.ajaxUrl, type: 'POST', data: { action: 'fedistream_get_followed_artists', nonce: fedistreamLibrary.nonce, page: this.currentPage.artists }, success: function( response ) { self.hideLoading(); if ( response.success ) { self.renderArtists( response.data ); } else { self.showError( response.data.message ); } }, error: function() { self.hideLoading(); self.showError( fedistreamLibrary.i18n.error ); } } ); }, loadHistory: function() { const self = this; if ( this.isLoading ) { return; } this.showLoading(); $.ajax( { url: fedistreamLibrary.ajaxUrl, type: 'POST', data: { action: 'fedistream_get_history', nonce: fedistreamLibrary.nonce, page: this.currentPage.history }, success: function( response ) { self.hideLoading(); if ( response.success ) { self.renderHistory( response.data ); } else { self.showError( response.data.message ); } }, error: function() { self.hideLoading(); self.showError( fedistreamLibrary.i18n.error ); } } ); }, renderFavorites: function( data ) { const container = $( '.favorites-grid' ); container.empty(); if ( ! data.items || data.items.length === 0 ) { container.html( '

' + fedistreamLibrary.i18n.noFavorites + '

' ); $( '.library-pagination[data-tab="favorites"]' ).empty(); return; } data.items.forEach( function( item ) { container.append( this.createFavoriteItem( item ) ); }, this ); this.renderPagination( 'favorites', data ); }, renderArtists: function( data ) { const container = $( '.artists-grid' ); container.empty(); if ( ! data.artists || data.artists.length === 0 ) { container.html( '

' + fedistreamLibrary.i18n.noArtists + '

' ); $( '.library-pagination[data-tab="artists"]' ).empty(); return; } data.artists.forEach( function( artist ) { container.append( this.createArtistItem( artist ) ); }, this ); this.renderPagination( 'artists', data ); }, renderHistory: function( data ) { const container = $( '.history-list' ); container.empty(); if ( ! data.tracks || data.tracks.length === 0 ) { container.html( '

' + fedistreamLibrary.i18n.noHistory + '

' ); $( '.library-pagination[data-tab="history"]' ).empty(); return; } data.tracks.forEach( function( track ) { container.append( this.createHistoryItem( track ) ); }, this ); this.renderPagination( 'history', data ); }, createFavoriteItem: function( item ) { const thumbnail = item.thumbnail ? '' + this.escapeHtml( item.title ) + '' : '
'; const playBtn = item.type === 'track' ? '' : ''; const artistInfo = item.artist ? '

' + this.escapeHtml( item.artist ) + '

' : ''; let metaInfo = ''; if ( item.type === 'track' && item.duration ) { metaInfo = '

' + this.formatDuration( item.duration ) + '

'; } else if ( item.type === 'album' && item.track_count ) { metaInfo = '

' + item.track_count + ' tracks

'; } return `
${thumbnail} ${playBtn}

${this.escapeHtml( item.title )}

${artistInfo} ${metaInfo}
`; }, createArtistItem: function( artist ) { const thumbnail = artist.thumbnail ? '' + this.escapeHtml( artist.name ) + '' : '
'; const typeLabel = artist.type === 'band' ? 'Band' : 'Artist'; return `
${thumbnail}

${this.escapeHtml( artist.name )}

${typeLabel}

`; }, createHistoryItem: function( track ) { const thumbnail = track.thumbnail ? '' + this.escapeHtml( track.title ) + '' : '
'; const artistInfo = track.artist ? '

' + this.escapeHtml( track.artist ) + '

' : ''; const duration = track.duration ? '' + this.formatDuration( track.duration ) + '' : ''; return `
${thumbnail}

${this.escapeHtml( track.title )}

${artistInfo}

${this.formatPlayedTime( track.played_at )}

${duration}
`; }, renderPagination: function( tab, data ) { const container = $( '.library-pagination[data-tab="' + tab + '"]' ); container.empty(); if ( data.total_pages <= 1 ) { return; } let html = '
'; // Previous button. if ( data.page > 1 ) { html += ''; } // Page numbers. html += 'Page ' + data.page + ' of ' + data.total_pages + ''; // Next button. if ( data.page < data.total_pages ) { html += ''; } html += '
'; container.html( html ); }, goToPage: function( tab, page ) { this.currentPage[ tab ] = page; this.loadTabContent( tab ); }, toggleFavorite: function( contentType, contentId, element ) { const self = this; $.ajax( { url: fedistreamLibrary.ajaxUrl, type: 'POST', data: { action: 'fedistream_toggle_favorite', nonce: fedistreamLibrary.nonce, content_type: contentType, content_id: contentId }, success: function( response ) { if ( response.success && response.data.action === 'removed' ) { element.fadeOut( 300, function() { $( this ).remove(); self.updateFavoriteCount(); } ); } }, error: function() { self.showError( fedistreamLibrary.i18n.error ); } } ); }, toggleFollow: function( artistId, element ) { const self = this; $.ajax( { url: fedistreamLibrary.ajaxUrl, type: 'POST', data: { action: 'fedistream_toggle_follow', nonce: fedistreamLibrary.nonce, artist_id: artistId }, success: function( response ) { if ( response.success && response.data.action === 'unfollowed' ) { element.fadeOut( 300, function() { $( this ).remove(); self.updateFollowingCount(); } ); } }, error: function() { self.showError( fedistreamLibrary.i18n.error ); } } ); }, clearHistory: function() { const self = this; if ( ! confirm( fedistreamLibrary.i18n.confirmClear ) ) { return; } $.ajax( { url: fedistreamLibrary.ajaxUrl, type: 'POST', data: { action: 'fedistream_clear_history', nonce: fedistreamLibrary.nonce }, success: function( response ) { if ( response.success ) { $( '.history-list' ).html( '

' + fedistreamLibrary.i18n.noHistory + '

' ); $( '.library-pagination[data-tab="history"]' ).empty(); } else { self.showError( response.data.message ); } }, error: function() { self.showError( fedistreamLibrary.i18n.error ); } } ); }, playTrack: function( trackId ) { // Trigger global player if available. if ( window.FediStreamPlayer && typeof window.FediStreamPlayer.playTrack === 'function' ) { window.FediStreamPlayer.playTrack( trackId ); } else { // Fallback: redirect to track page. window.location.href = '?p=' + trackId; } }, updateFavoriteCount: function() { const count = $( '.favorites-grid .library-item' ).length; $( '.tab-btn[data-tab="favorites"] .count' ).text( count ); }, updateFollowingCount: function() { const count = $( '.artists-grid .library-item' ).length; $( '.tab-btn[data-tab="artists"] .count' ).text( count ); }, showLoading: function() { this.isLoading = true; $( '.fedistream-library-loading' ).show(); }, hideLoading: function() { this.isLoading = false; $( '.fedistream-library-loading' ).hide(); }, showError: function( message ) { alert( message ); }, formatDuration: function( seconds ) { const mins = Math.floor( seconds / 60 ); const secs = seconds % 60; return mins + ':' + ( secs < 10 ? '0' : '' ) + secs; }, formatPlayedTime: function( dateString ) { const date = new Date( dateString ); const now = new Date(); const diff = Math.floor( ( now - date ) / 1000 ); if ( diff < 60 ) { return 'Just now'; } else if ( diff < 3600 ) { const mins = Math.floor( diff / 60 ); return mins + ' minute' + ( mins !== 1 ? 's' : '' ) + ' ago'; } else if ( diff < 86400 ) { const hours = Math.floor( diff / 3600 ); return hours + ' hour' + ( hours !== 1 ? 's' : '' ) + ' ago'; } else { const days = Math.floor( diff / 86400 ); return days + ' day' + ( days !== 1 ? 's' : '' ) + ' ago'; } }, escapeHtml: function( text ) { const div = document.createElement( 'div' ); div.textContent = text; return div.innerHTML; } }; // Initialize on document ready. $( document ).ready( function() { if ( $( '.fedistream-library' ).length ) { Library.init(); } } ); } )( jQuery );