diff --git a/.gitignore b/.gitignore index 8cfd857..0ffeec4 100755 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ config.js +/Concepts/ /temp -app/services/mopidy/mopidy.pid @eaDir Thumbs.db diff --git a/app/app.js b/app/app.js index 2221559..2683eea 100755 --- a/app/app.js +++ b/app/app.js @@ -69,7 +69,11 @@ angular.module('spotmop', [ // fetch this instance's best thumbnail $scope.image = getThumbnailImage( $scope.images ); - // get the best thumbnail image, please and thankyou + /** + * Get the most appropriate thumbnail image + * @param images = array of image urls + * @return string (image url) + **/ function getThumbnailImage( images ){ // what if there are no images? then nada @@ -138,12 +142,12 @@ angular.module('spotmop', [ $scope.text = $scope.defaultText; // bind to document-wide click events - $(document).on('click', function(evt){ + $(document).on('click', function(event){ - // if we've clicked on THIS confirmation button - if( evt.target == $element[0] ){ + // if we've left-clicked on THIS confirmation button + if( event.target == $element[0] && event.which == 1 ){ if( $scope.confirming ){ - + // if the function exists, perform the on-confirmation function from the directive's template if( typeof( $scope.$parent[ $scope.onConfirmation ]() ) === 'function' ) $scope.$parent[ $scope.onConfirmation ](); @@ -172,7 +176,7 @@ angular.module('spotmop', [ }, replace: true, // Replace with the template below transclude: true, // we want to insert custom content inside the directive - template: '' + template: '' }; }) @@ -234,7 +238,11 @@ angular.module('spotmop', [ **/ .controller('ApplicationController', function ApplicationController( $scope, $rootScope, $state, $localStorage, $timeout, $location, SpotifyService, MopidyService, EchonestService, SettingsService ){ - $scope.isTouchDevice = function(){ return !!('ontouchstart' in window); } + $scope.isTouchDevice = function(){ + if( SettingsService.getSetting('emulateTouchDevice',false) ) + return true; + return !!('ontouchstart' in window); + } $scope.isSameDomainAsMopidy = function(){ var mopidyhost = SettingsService.getSetting('mopidyhost','localhost'); @@ -255,13 +263,16 @@ angular.module('spotmop', [ window.location.reload(); } $scope.playlistsMenu = []; + $scope.myPlaylists = {}; // update the playlists menu $scope.updatePlaylists = function(){ SpotifyService.getPlaylists( $scope.spotifyUser.id, 50 ) .success(function( response ) { - + + $scope.myPlaylists = response.items; + var newPlaylistsMenu = []; // loop all of our playlists, and set up a menu item for each @@ -302,7 +313,6 @@ angular.module('spotmop', [ } angular.element(window).resize(function () { - $scope.resquarePanels(); $scope.windowWidth = $(document).width(); // if we're a small or medium screen, re-hide the sidebar and reset the body sliding @@ -317,14 +327,6 @@ angular.module('spotmop', [ } }); - // make all the square panels really square - $scope.resquarePanels = function(){ - $(document).find('.square-panel').each( function(index, value){ - var realWidth = value.getBoundingClientRect().width; - $(value).find('.image-container').css('height', realWidth +'px'); - }); - } - $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams){ $scope.hideMenu(); }); @@ -419,16 +421,6 @@ angular.module('spotmop', [ var notificationItem = $(document).find('#notifications .notification-item[data-id="'+data.id+'"]'); notificationItem.fadeOut(200, function(){ notificationItem.remove() }); }); - - // the page content has been updated - $scope.$on('spotmop:pageUpdated', function(){ - - // wait for $digest - $timeout( function(){ - $scope.resquarePanels(); - }, - 0); - }); diff --git a/app/browse/album/controller.js b/app/browse/album/controller.js index 8bddbd6..e0c8a2d 100755 --- a/app/browse/album/controller.js +++ b/app/browse/album/controller.js @@ -17,10 +17,10 @@ angular.module('spotmop.browse.album', []) /** * Main controller **/ -.controller('AlbumController', function AlbumController( $scope, $rootScope, SpotifyService, $stateParams, $filter ){ +.controller('AlbumController', function AlbumController( $scope, $rootScope, $stateParams, $filter, MopidyService, SpotifyService ){ $scope.album = {}; - $scope.tracklist = {}; + $scope.tracklist = {type: 'track'}; $scope.convertedDate = function(){ if( $scope.album.release_date_precision == 'day' ) return $filter('date')($scope.album.release_date, "MMMM d, yyyy"); @@ -41,6 +41,26 @@ angular.module('spotmop.browse.album', []) } return Math.round(totalTime / 100000); } + + // play the whole album + $scope.playAlbum = function(){ + MopidyService.playStream( $scope.album.uri ); + } + + // add album to library + $scope.addToLibrary = function(){ + $rootScope.$broadcast('spotmop:notifyUser', {type: 'loading', id: 'adding-to-library', message: 'Adding to library'}); + + var trackids = []; + angular.forEach( $scope.tracklist.tracks, function( track ){ + trackids.push( SpotifyService.getFromUri( 'trackid', track.uri ) ); + }); + + SpotifyService.addTracksToLibrary( trackids ) + .success( function(response){ + $rootScope.$broadcast('spotmop:notifyUserRemoval', {id: 'adding-to-library'}); + }); + } $rootScope.$broadcast('spotmop:notifyUser', {type: 'loading', id: 'loading-album', message: 'Loading'}); @@ -50,9 +70,9 @@ angular.module('spotmop.browse.album', []) $scope.album = response; $scope.tracklist = response.tracks; + $scope.tracklist.type = 'track'; $scope.tracklist.tracks = response.tracks.items; - $rootScope.$broadcast('spotmop:pageUpdated'); $rootScope.$broadcast('spotmop:notifyUserRemoval', {id: 'loading-album'}); }) .error(function( error ){ diff --git a/app/browse/album/template.html b/app/browse/album/template.html index aa1aeca..2e4290f 100755 --- a/app/browse/album/template.html +++ b/app/browse/album/template.html @@ -1,7 +1,5 @@
- -
@@ -33,10 +31,14 @@

-
- +
+

+ +   Play album + Add to library +
diff --git a/app/browse/artist/controller.js b/app/browse/artist/controller.js index 1cc1dbe..6792f68 100755 --- a/app/browse/artist/controller.js +++ b/app/browse/artist/controller.js @@ -10,7 +10,7 @@ angular.module('spotmop.browse.artist', []) $stateProvider .state('browse.artist', { url: "/artist/:uri", - //abstract: true, + abstract: true, templateUrl: "app/browse/artist/template.html", controller: ['$scope', '$state', function( $scope, $state) { @@ -21,7 +21,7 @@ angular.module('spotmop.browse.artist', []) }] }) .state('browse.artist.overview', { - url: "/overview", + url: "", templateUrl: "app/browse/artist/overview.template.html", controller: 'ArtistOverviewController' }) @@ -61,7 +61,7 @@ angular.module('spotmop.browse.artist', []) .controller('ArtistController', function ArtistController( $scope, $rootScope, $timeout, SpotifyService, $stateParams, $sce ){ $scope.artist = {}; - $scope.tracklist = {}; + $scope.tracklist = {type: 'track'}; $scope.albums = {}; $scope.relatedArtists = {}; @@ -95,7 +95,7 @@ angular.module('spotmop.browse.artist', []) SpotifyService.getAlbums( $stateParams.uri ) .success( function( response ){ $scope.albums = response; - + // get the artist's top tracks SpotifyService.getTopTracks( $stateParams.uri ) .success( function( response ){ @@ -157,18 +157,7 @@ angular.module('spotmop.browse.artist', []) /** * Related artists controller **/ -.controller('RelatedArtistsController', function RelatedArtistsController( $scope, $timeout, $rootScope ){ - - // when the related artists array changes (ie on API response, page load, etc) - $scope.$watch('relatedArtists', function(){ - - // wait for $digest - $timeout( function(){ - $scope.resquarePanels(); - }, - 0); - }); - +.controller('RelatedArtistsController', function RelatedArtistsController( $scope, $timeout, $rootScope ){ }) diff --git a/app/browse/artist/overview.template.html b/app/browse/artist/overview.template.html index 7ae77ef..7bc3ab5 100755 --- a/app/browse/artist/overview.template.html +++ b/app/browse/artist/overview.template.html @@ -1,6 +1,4 @@
- -

Top tracks

diff --git a/app/browse/playlist/controller.js b/app/browse/playlist/controller.js index 605df02..4949e65 100755 --- a/app/browse/playlist/controller.js +++ b/app/browse/playlist/controller.js @@ -17,11 +17,11 @@ angular.module('spotmop.browse.playlist', []) /** * Main controller **/ -.controller('PlaylistController', function PlaylistController( $scope, $rootScope, $filter, $state, $stateParams, $sce, SpotifyService, SettingsService, DialogService ){ +.controller('PlaylistController', function PlaylistController( $scope, $rootScope, $filter, $state, $stateParams, $sce, SpotifyService, MopidyService, SettingsService, DialogService ){ // setup base variables - $scope.playlist = {}; - $scope.tracklist = { tracks: [] }; + $scope.playlist = {images: []}; + $scope.tracklist = { tracks: [], type: 'track' }; $scope.totalTime = 0; $scope.following = false; $scope.followPlaylist = function(){ @@ -52,6 +52,11 @@ angular.module('spotmop.browse.playlist', []) DialogService.create('editPlaylist', $scope); } + // play the whole playlist + $scope.playPlaylist = function(){ + MopidyService.playStream( $scope.playlist.uri ); + } + // figure out the total time for all tracks $scope.totalTime = function(){ var totalTime = 0; diff --git a/app/browse/playlist/template.html b/app/browse/playlist/template.html index 4fc3955..5bf9627 100755 --- a/app/browse/playlist/template.html +++ b/app/browse/playlist/template.html @@ -1,60 +1,69 @@
- -
-

+ +
+ +
+

- +
+ 0 tracks +  |  +0 minutes +  |    +
- +

Loading

- - - - Edit - - - +
+ +   Play playlist + + - - - Recover playlist - + + + + + Edit + + + + + + + Recover playlist + + - - -

-

Loading

-
- 0 tracks -  |  +0 minutes -  |    +
+ +
+
diff --git a/app/browse/user/controller.js b/app/browse/user/controller.js index 018b204..adea5a3 100755 --- a/app/browse/user/controller.js +++ b/app/browse/user/controller.js @@ -35,7 +35,6 @@ angular.module('spotmop.browse.user', []) $scope.playlists = response.items; $scope.next = response.next; $scope.totalPlaylists = response.total; - $rootScope.$broadcast('spotmop:pageUpdated'); $rootScope.$broadcast('spotmop:notifyUserRemoval', {id: 'loading-user'}); }) .error(function( error ){ diff --git a/app/common/contextmenu/directive.js b/app/common/contextmenu/directive.js index fa13268..2d45958 100755 --- a/app/common/contextmenu/directive.js +++ b/app/common/contextmenu/directive.js @@ -17,7 +17,7 @@ angular.module('spotmop.common.contextmenu', [ **/ $scope.play = function(){ $rootScope.$broadcast('spotmop:tracklist:playSelectedTracks'); - $element.hide(); + $element.fadeOut('fast'); // if we're a touch device, hide the menu now we're done with it (aka unselect all) if( $scope.isTouchDevice() ) @@ -26,7 +26,7 @@ angular.module('spotmop.common.contextmenu', [ $scope.enqueue = function(){ $rootScope.$broadcast('spotmop:tracklist:enqueueSelectedTracks'); - $element.hide(); + $element.fadeOut('fast'); // if we're a touch device, hide the menu now we're done with it (aka unselect all) if( $scope.isTouchDevice() ) @@ -35,7 +35,7 @@ angular.module('spotmop.common.contextmenu', [ $scope.unqueue = function(){ $rootScope.$broadcast('spotmop:tracklist:unqueueSelectedTracks'); - $element.hide(); + $element.fadeOut('fast'); // if we're a touch device, hide the menu now we're done with it (aka unselect all) if( $scope.isTouchDevice() ) @@ -44,7 +44,7 @@ angular.module('spotmop.common.contextmenu', [ $scope.playNext = function(){ $rootScope.$broadcast('spotmop:tracklist:enqueueSelectedTracks', true); - $element.hide(); + $element.fadeOut('fast'); // if we're a touch device, hide the menu now we're done with it (aka unselect all) if( $scope.isTouchDevice() ) @@ -53,26 +53,32 @@ angular.module('spotmop.common.contextmenu', [ $scope.addToPlaylist = function(){ $rootScope.$broadcast('spotmop:tracklist:addSelectedTracksToPlaylist'); - $element.hide(); + $element.fadeOut('fast'); + } + + $scope.removeFromPlaylist = function(){ + $rootScope.$broadcast('spotmop:tracklist:removeSelectedTracksFromPlaylist'); + $element.fadeOut('fast'); } $scope.addToLibrary = function(){ $rootScope.$broadcast('spotmop:tracklist:addSelectedTracksToLibrary'); - $element.hide(); + $element.fadeOut('fast'); } $scope.selectAll = function(){ $rootScope.$broadcast('spotmop:tracklist:selectAll'); - $element.hide(); } $scope.unselectAll = function(){ $rootScope.$broadcast('spotmop:tracklist:unselectAll'); - $element.hide(); + $element.fadeOut('fast'); } /** - * Show the context menu + * Show the standard context menu + * This is typically triggered by a right-click on a track + * * @param context = string (track|tltrack) * @param reverse = boolean (optional) to reverse position of context menu, ie when you're on the right-boundary of the page **/ @@ -98,12 +104,29 @@ angular.module('spotmop.common.contextmenu', [ }); }); + /** + * Show the touch-device specific context menu + * This is triggered when our tracklist has selected tracks + * + * @param context = string (track|tltrack) + **/ + $scope.$on('spotmop:touchContextMenu:show', function(event, context){ + + // position and reveal our element + $element.show(); + + // use the clicked element to define what kind of context menu to show + $scope.$apply( function(){ + $scope.context = context; + }); + }); + /** * Hide the context menu **/ $scope.$on('spotmop:contextMenu:hide', function(event){ - $element.hide(); + $element.fadeOut('fast'); }); } } diff --git a/app/common/contextmenu/template.html b/app/common/contextmenu/template.html index 02463ea..8543244 100755 --- a/app/common/contextmenu/template.html +++ b/app/common/contextmenu/template.html @@ -1,9 +1,48 @@
- Play - Play next - Add to queue - Add to playlist - Add to library - Delete - Delete + +
+ + + Play + + + + Play next + + + + Queue + + + + Playlist + + + + Library + + + + Delete + + + + Delete + + + + Cancel + +
+ +
+ Play + Play next + Add to queue + Add to playlist + Add to library + Delete + Delete +
+
\ No newline at end of file diff --git a/app/common/contextmenu/tltracklist.template.html b/app/common/contextmenu/tltracklist.template.html deleted file mode 100755 index 1913be9..0000000 --- a/app/common/contextmenu/tltracklist.template.html +++ /dev/null @@ -1,26 +0,0 @@ -
- - - Play - - - - Delete - - - - Playlist - - - - Library - - - - Select all - - - - Cancel - -
\ No newline at end of file diff --git a/app/common/contextmenu/tracklist.template.html b/app/common/contextmenu/tracklist.template.html deleted file mode 100755 index 3d439e9..0000000 --- a/app/common/contextmenu/tracklist.template.html +++ /dev/null @@ -1,30 +0,0 @@ -
- - - Play - - - - Play next - - - - Queue - - - - Playlist - - - - Library - - - - Delete - - - - Cancel - -
\ No newline at end of file diff --git a/app/common/tracklist/controller.js b/app/common/tracklist/controller.js index 27bc5ac..d76654b 100755 --- a/app/common/tracklist/controller.js +++ b/app/common/tracklist/controller.js @@ -19,7 +19,10 @@ angular.module('spotmop.common.tracklist', [ // left click if( event.which === 1 ){ - $scope.$emit('spotmop:contextMenu:hide'); + + if( !$scope.isTouchDevice() ) + $scope.$emit('spotmop:contextMenu:hide'); + $scope.$emit('spotmop:track:clicked', $scope); // right click (only when selected) @@ -82,7 +85,10 @@ angular.module('spotmop.common.tracklist', [ // left click if( event.which === 1 ){ - $scope.$emit('spotmop:contextMenu:hide'); + + if( !$scope.isTouchDevice() ) + $scope.$emit('spotmop:contextMenu:hide'); + $scope.$emit('spotmop:track:clicked', $scope); // right click (only when selected) @@ -194,6 +200,16 @@ angular.module('spotmop.common.tracklist', [ // save this item to our last-clicked (used for shift-click) $scope.lastSelectedTrack = $track; + + /** + * Hide/show mobile version of the context menu + **/ + if( $scope.isTouchDevice() ){ + if( $filter('filter')($scope.tracklist.tracks, {selected: true}).length > 0 ) + $rootScope.$broadcast('spotmop:touchContextMenu:show', $scope.tracklist.type ); + else + $rootScope.$broadcast('spotmop:contextMenu:hide' ); + } }); @@ -255,17 +271,11 @@ angular.module('spotmop.common.tracklist', [ function playSelectedTracks(){ var selectedTracks = $filter('filter')( $scope.tracklist.tracks, {selected: true} ); - var firstSelectedTrack = selectedTracks[0]; - - // detect tracklist context - if( $element.data('type') === 'queue' ) - var context = 'queue'; - else - var context = 'generic'; - + var firstSelectedTrack = selectedTracks[0]; + console.log( $scope.tracklist ); // depending on context, make the selected track(s) play // queue - if( context === 'queue' ){ + if( $scope.tracklist.type == 'tltrack' ){ // get the queue's tracks // we need to re-get the queue because at this point some tracks may not have tlids @@ -283,7 +293,7 @@ angular.module('spotmop.common.tracklist', [ }); // generic tracklist (playlist, top-tracks, album, etc) - }else if( context === 'generic' ){ + }else if( $scope.tracklist.type == 'track' ){ // build an array of track uris (and subtract the first one, as we play him immediately) var selectedTracksUris = []; @@ -300,14 +310,14 @@ angular.module('spotmop.common.tracklist', [ // play the first track immediately MopidyService.playTrack( [ firstSelectedTrack.uri ], 0 ).then( function(){ - // no other tracks to add - if( selectedTracks.length <= 1 ){ - $rootScope.$broadcast('spotmop:notifyUserRemoval', {id: 'playing-from-tracklist'}); - }else{ + // more tracks to add + if( selectedTracksUris.length > 0 ){ // add the following tracks to the tracklist MopidyService.addToTrackList( selectedTracksUris ).then( function(response){ $rootScope.$broadcast('spotmop:notifyUserRemoval', {id: 'playing-from-tracklist'}); }); + }else{ + $rootScope.$broadcast('spotmop:notifyUserRemoval', {id: 'playing-from-tracklist'}); } }); } @@ -375,7 +385,7 @@ angular.module('spotmop.common.tracklist', [ * Selected Tracks >> Add to library **/ $scope.$on('spotmop:tracklist:addSelectedTracksToLibrary', function(event){ - + $scope.$broadcast('spotmop:notifyUser', {type: 'loading', id: 'adding-to-library', message: 'Adding to library'}); var selectedTracks = $filter('filter')( $scope.tracklist.tracks, {selected: true} ); diff --git a/app/discover/controller.js b/app/discover/controller.js index 2065931..0174049 100755 --- a/app/discover/controller.js +++ b/app/discover/controller.js @@ -36,7 +36,6 @@ angular.module('spotmop.discover', []) SpotifyService.discoverCategories() .success(function( response ) { $scope.categories = response.categories; - $rootScope.$broadcast('spotmop:pageUpdated'); $rootScope.$broadcast('spotmop:notifyUserRemoval', {id: 'loading-categories'}); }) .error(function( error ){ @@ -75,7 +74,6 @@ angular.module('spotmop.discover', []) // update loader and re-open for further pagination objects $rootScope.$broadcast('spotmop:notifyUserRemoval', {id: 'loading-more-categories'}); - $rootScope.$broadcast('spotmop:pageUpdated'); loadingMoreCategories = false; }) .error(function( error ){ @@ -111,7 +109,6 @@ angular.module('spotmop.discover', []) SpotifyService.getCategoryPlaylists( $stateParams.categoryid ) .success(function( response ) { $scope.playlists = response.playlists; - $rootScope.$broadcast('spotmop:pageUpdated'); $rootScope.$broadcast('spotmop:notifyUserRemoval', {id: 'loading-category'}); }) .error(function( error ){ @@ -155,7 +152,6 @@ angular.module('spotmop.discover', []) // update loader and re-open for further pagination objects $rootScope.$broadcast('spotmop:notifyUserRemoval', {id: 'loading-more-playlists'}); - $rootScope.$broadcast('spotmop:pageUpdated'); loadingMorePlaylists = false; }) .error(function( error ){ diff --git a/app/discover/featured/controller.js b/app/discover/featured/controller.js index bf527c6..e8a375a 100755 --- a/app/discover/featured/controller.js +++ b/app/discover/featured/controller.js @@ -25,7 +25,6 @@ angular.module('spotmop.discover.featured', []) SpotifyService.featuredPlaylists() .success(function( response ) { $scope.playlists = response.playlists.items; - $rootScope.$broadcast('spotmop:pageUpdated'); $rootScope.$broadcast('spotmop:notifyUserRemoval', {id: 'loading-featured-playlists'}); }) .error(function( error ){ diff --git a/app/discover/new/controller.js b/app/discover/new/controller.js index 200945e..1bef688 100755 --- a/app/discover/new/controller.js +++ b/app/discover/new/controller.js @@ -25,7 +25,6 @@ angular.module('spotmop.discover.new', []) SpotifyService.newReleases() .success(function( response ) { $scope.albums = response.albums; - $rootScope.$broadcast('spotmop:pageUpdated'); $rootScope.$broadcast('spotmop:notifyUserRemoval', {id: 'loading-new-releases'}); }) .error(function( error ){ @@ -64,7 +63,6 @@ angular.module('spotmop.discover.new', []) // update loader and re-open for further pagination objects $rootScope.$broadcast('spotmop:notifyUserRemoval', {id: 'loading-more-new-releases'}); - $rootScope.$broadcast('spotmop:pageUpdated'); loadingMoreNewReleases = false; }) .error(function( error ){ diff --git a/app/library/controller.js b/app/library/controller.js index 77d4760..a81d4bb 100755 --- a/app/library/controller.js +++ b/app/library/controller.js @@ -30,7 +30,6 @@ angular.module('spotmop.library', []) $scope.tracklist = response.data; $scope.tracklist.tracks = reformatTracks( response.data.items ); - $rootScope.$broadcast('spotmop:pageUpdated'); $rootScope.$broadcast('spotmop:notifyUserRemoval', {id: 'loading-library'}); }, function( response ){ // error diff --git a/app/library/template.html b/app/library/template.html index a29d1cb..4677c1a 100755 --- a/app/library/template.html +++ b/app/library/template.html @@ -1,6 +1,4 @@
- -

My Music Library diff --git a/app/my-playlists/controller.js b/app/my-playlists/controller.js index 1cae2ac..8e564df 100755 --- a/app/my-playlists/controller.js +++ b/app/my-playlists/controller.js @@ -17,33 +17,9 @@ angular.module('spotmop.myplaylists', []) **/ .controller('MyPlaylistsController', function MyPlaylistsController( $scope, $rootScope, SpotifyService, SettingsService, DialogService ){ - // set the default items - $scope.playlists = []; + // note: we use the existing playlist list to show playlists on this page + $scope.createPlaylist = function(){ DialogService.create('createPlaylist', $scope); - } - - // if we've got a userid already in storage, use that - var userid = SettingsService.getSetting('spotifyuserid',$scope.$parent.spotifyUser.id); - - $rootScope.$broadcast('spotmop:notifyUser', {type: 'loading', id: 'loading-playlists', message: 'Loading'}); - - SpotifyService.getPlaylists( userid ) - .then( - function( response ){ // successful - $scope.playlists = response.data.items; - $rootScope.$broadcast('spotmop:pageUpdated'); - $rootScope.$broadcast('spotmop:notifyUserRemoval', {id: 'loading-playlists'}); - }, - function( response ){ // error - - // if it was 401, refresh token - if( error.error.status == 401 ) - Spotify.refreshToken(); - - $rootScope.$broadcast('spotmop:notifyUserRemoval', {id: 'loading-playlists'}); - $rootScope.$broadcast('spotmop:notifyUser', {type: 'bad', id: 'loading-playlists', message: error.error.message}); - } - ); - + } }); \ No newline at end of file diff --git a/app/my-playlists/template.html b/app/my-playlists/template.html index 33220a0..e5d93c4 100755 --- a/app/my-playlists/template.html +++ b/app/my-playlists/template.html @@ -10,7 +10,7 @@

-
+
-
+
No items
diff --git a/app/player/controller.js b/app/player/controller.js index 2a5085b..6a3781a 100755 --- a/app/player/controller.js +++ b/app/player/controller.js @@ -5,7 +5,7 @@ angular.module('spotmop.player', [ 'spotmop.services.mopidy' ]) -.controller('PlayerController', function PlayerController( $scope, $rootScope, $timeout, $interval, $element, MopidyService, SpotifyService, EchonestService ){ +.controller('PlayerController', function PlayerController( $scope, $rootScope, $timeout, $interval, $element, MopidyService, SpotifyService, EchonestService, SettingsService ){ // setup template containers $scope.muted = false; @@ -37,7 +37,8 @@ angular.module('spotmop.player', [ $scope.next = function(){ // log this skip (we do this BEFORE moving to the next, as the skip is on the OLD track) - EchonestService.addToTasteProfile( 'skip', $scope.currentTlTrack.track.uri ); + if( SettingsService.getSetting('echonestenabled',false) ) + EchonestService.addToTasteProfile( 'skip', $scope.currentTlTrack.track.uri ); MopidyService.next(); } @@ -127,20 +128,24 @@ angular.module('spotmop.player', [ $scope.previous(); }); $scope.$on('spotmop:keyboardShortcut:up', function( event ){ - $scope.volume += 10; - - // don't let the volume exceed maximum possible, 100% - if( $scope.volume >= 100 ) - $scope.volume = 100; - MopidyService.setVolume( $scope.volume ); + if( $rootScope.ctrlKeyHeld ){ + $scope.volume += 10; + + // don't let the volume exceed maximum possible, 100% + if( $scope.volume >= 100 ) + $scope.volume = 100; + MopidyService.setVolume( $scope.volume ); + } }); $scope.$on('spotmop:keyboardShortcut:down', function( event ){ - $scope.volume -= 10; - - // don't let the volume below minimum possible, 0% - if( $scope.volume < 0 ) - $scope.volume = 0; - MopidyService.setVolume( $scope.volume ); + if( $rootScope.ctrlKeyHeld ){ + $scope.volume -= 10; + + // don't let the volume below minimum possible, 0% + if( $scope.volume < 0 ) + $scope.volume = 0; + MopidyService.setVolume( $scope.volume ); + } }); @@ -266,7 +271,8 @@ angular.module('spotmop.player', [ updatePlayerState(); // log this play - EchonestService.addToTasteProfile( 'play', tlTrack.tl_track.track.uri ); + if( SettingsService.getSetting('echonestenabled',false) ) + EchonestService.addToTasteProfile( 'play', tlTrack.tl_track.track.uri ); }); diff --git a/app/queue/controller.js b/app/queue/controller.js index f94c298..ff1d012 100755 --- a/app/queue/controller.js +++ b/app/queue/controller.js @@ -18,7 +18,7 @@ angular.module('spotmop.queue', []) .controller('QueueController', function QueueController( $scope, $rootScope, $filter, $timeout, $state, MopidyService, SpotifyService ){ $scope.totalTime = 0; - $scope.tracklist = { tracks: $scope.$parent.currentTracklist }; + $scope.tracklist = { type: 'tltrack', tracks: $scope.$parent.currentTracklist }; /** * Watch the current tracklist diff --git a/app/queue/template.html b/app/queue/template.html index f835562..92d710a 100755 --- a/app/queue/template.html +++ b/app/queue/template.html @@ -2,8 +2,6 @@
- -

Queue

diff --git a/app/search/controller.js b/app/search/controller.js index 8028b83..d554bc1 100755 --- a/app/search/controller.js +++ b/app/search/controller.js @@ -23,7 +23,7 @@ angular.module('spotmop.search', []) **/ .controller('SearchController', function SearchController( $scope, $rootScope, $state, $stateParams, $timeout, SpotifyService ){ - $scope.tracklist = {tracks: []}; + $scope.tracklist = {tracks: [], type: 'track'}; $scope.albums = []; $scope.artists = []; $scope.playlists = []; @@ -56,7 +56,7 @@ angular.module('spotmop.search', []) if( newValue != oldValue && newValue && newValue != '' ){ $scope.loading = true; - $scope.tracklist = {tracks: []}; + $scope.tracklist = {tracks: [], type: 'track'}; $scope.albums = []; $scope.artists = []; $scope.playlists = []; @@ -92,8 +92,8 @@ angular.module('spotmop.search', []) .success( function(response){ $scope.tracklist = response.tracks; $scope.tracklist.tracks = response.tracks.items; + $scope.tracklist.type = 'track'; $scope.next = response.tracks.next; - $rootScope.$broadcast('spotmop:pageUpdated'); $scope.loading = false; }); break; @@ -104,7 +104,6 @@ angular.module('spotmop.search', []) .success( function(response){ $scope.albums = response.albums; $scope.next = response.albums.next; - $rootScope.$broadcast('spotmop:pageUpdated'); $scope.loading = false; }); break; @@ -115,7 +114,6 @@ angular.module('spotmop.search', []) .success( function(response){ $scope.artists = response.artists; $scope.next = response.artists.next; - $rootScope.$broadcast('spotmop:pageUpdated'); $scope.loading = false; }); break; @@ -126,7 +124,6 @@ angular.module('spotmop.search', []) .success( function(response){ $scope.playlists = response.playlists; $scope.next = response.playlists.next; - $rootScope.$broadcast('spotmop:pageUpdated'); $scope.loading = false; }); break; @@ -136,12 +133,11 @@ angular.module('spotmop.search', []) SpotifyService.getSearchResults( 'track', query, 20 ) .success( function(response){ $scope.tracklist = response.tracks; + $scope.tracklist.type = 'track'; $scope.tracklist.tracks = response.tracks.items; // handle loading (remembering that these queries may finish in a different order) $scope.loading = $scope.loading - 1; - if( $scope.loading <= 0 ) - $rootScope.$broadcast('spotmop:pageUpdated'); }); SpotifyService.getSearchResults( 'album', query, 6 ) @@ -150,8 +146,6 @@ angular.module('spotmop.search', []) // handle loading (remembering that these queries may finish in a different order) $scope.loading = $scope.loading - 1; - if( $scope.loading <= 0 ) - $rootScope.$broadcast('spotmop:pageUpdated'); }); SpotifyService.getSearchResults( 'artist', query, 6 ) @@ -160,8 +154,6 @@ angular.module('spotmop.search', []) // handle loading (remembering that these queries may finish in a different order) $scope.loading = $scope.loading - 1; - if( $scope.loading <= 0 ) - $rootScope.$broadcast('spotmop:pageUpdated'); }); SpotifyService.getSearchResults( 'playlist', query, 6 ) @@ -170,8 +162,6 @@ angular.module('spotmop.search', []) // handle loading (remembering that these queries may finish in a different order) $scope.loading = $scope.loading - 1; - if( $scope.loading <= 0 ) - $rootScope.$broadcast('spotmop:pageUpdated'); }); break; diff --git a/app/services/dialog/service.js b/app/services/dialog/service.js index 6d59a16..84c26e9 100755 --- a/app/services/dialog/service.js +++ b/app/services/dialog/service.js @@ -108,7 +108,6 @@ angular.module('spotmop.services.dialog', []) // and finally remove this dialog DialogService.remove(); $rootScope.$broadcast('spotmop:notifyUser', {id: 'saved', message: 'Saved', autoremove: true}); - $rootScope.$broadcast('spotmop:pageUpdated'); }); } } @@ -184,7 +183,6 @@ angular.module('spotmop.services.dialog', []) SpotifyService.getPlaylists( spotifyUserID, 50 ) .success(function( response ) { $scope.playlists = $filter('filter')( response.items, { owner: { id: spotifyUserID } } ); - $scope.$emit('spotmop:pageUpdated'); }) .error(function( error ){ $scope.status = 'Unable to load your playlists'; diff --git a/app/services/spotify/service.js b/app/services/spotify/service.js index 955a686..2e3bc85 100755 --- a/app/services/spotify/service.js +++ b/app/services/spotify/service.js @@ -138,6 +138,7 @@ angular.module('spotmop.services.spotify', []) getMe: function(){ return $http({ + cache: true, method: 'GET', url: urlBase+'me/', headers: { @@ -151,6 +152,7 @@ angular.module('spotmop.services.spotify', []) var userid = this.getFromUri( 'userid', useruri ); return $http({ + cache: true, method: 'GET', url: urlBase+'users/'+userid, headers: { @@ -182,6 +184,7 @@ angular.module('spotmop.services.spotify', []) var trackid = this.getFromUri('trackid', trackuri); return $http({ + cache: true, method: 'GET', url: urlBase+'tracks/'+trackid, headers: { @@ -198,6 +201,7 @@ angular.module('spotmop.services.spotify', []) getMyTracks: function( userid ){ return $http({ + cache: true, method: 'GET', url: urlBase+'me/tracks/', headers: { @@ -243,6 +247,7 @@ angular.module('spotmop.services.spotify', []) limit = 40; return $http({ + cache: true, method: 'GET', url: urlBase+'users/'+userid+'/playlists?limit='+limit, headers: { @@ -258,6 +263,7 @@ angular.module('spotmop.services.spotify', []) var playlistid = this.getFromUri( 'playlistid', playlisturi ); return $http({ + cache: true, method: 'GET', url: urlBase+'users/'+userid+'/playlists/'+playlistid, headers: { @@ -310,6 +316,7 @@ angular.module('spotmop.services.spotify', []) featuredPlaylists: function(){ return $http({ + cache: true, method: 'GET', url: urlBase+'browse/featured-playlists', headers: { @@ -426,6 +433,7 @@ angular.module('spotmop.services.spotify', []) limit = 40; return $http({ + cache: true, method: 'GET', url: urlBase+'browse/new-releases?country='+ country +'&limit='+limit, headers: { @@ -440,6 +448,7 @@ angular.module('spotmop.services.spotify', []) limit = 40; return $http({ + cache: true, method: 'GET', url: urlBase+'browse/categories?limit='+limit, headers: { @@ -450,6 +459,7 @@ angular.module('spotmop.services.spotify', []) getCategory: function( categoryid ){ return $http({ + cache: true, method: 'GET', url: urlBase+'browse/categories/'+categoryid, headers: { @@ -464,6 +474,7 @@ angular.module('spotmop.services.spotify', []) limit = 40; return $http({ + cache: true, method: 'GET', url: urlBase+'browse/categories/'+categoryid+'/playlists?limit='+limit, headers: { @@ -481,6 +492,7 @@ angular.module('spotmop.services.spotify', []) var artistid = this.getFromUri( 'artistid', artisturi ); return $http({ + cache: true, method: 'GET', url: urlBase+'artists/'+artistid, headers: { @@ -494,6 +506,7 @@ angular.module('spotmop.services.spotify', []) var artistid = this.getFromUri( 'artistid', artisturi ); return $http({ + cache: true, method: 'GET', url: urlBase+'artists/'+artistid+'/albums?album_type=album,single&market='+country, headers: { @@ -507,6 +520,7 @@ angular.module('spotmop.services.spotify', []) var albumid = this.getFromUri( 'albumid', albumuri ); return $http({ + cache: true, method: 'GET', url: urlBase+'albums/'+albumid, headers: { @@ -520,6 +534,7 @@ angular.module('spotmop.services.spotify', []) var artistid = this.getFromUri( 'artistid', artisturi ); return $http({ + cache: true, method: 'GET', url: urlBase+'artists/'+artistid+'/top-tracks?country='+country, headers: { @@ -533,6 +548,7 @@ angular.module('spotmop.services.spotify', []) var artistid = this.getFromUri( 'artistid', artisturi ); return $http({ + cache: true, method: 'GET', url: urlBase+'artists/'+artistid+'/related-artists', headers: { @@ -552,6 +568,7 @@ angular.module('spotmop.services.spotify', []) if( typeof( limit ) === 'undefined' ) limit = 10; return $http({ + cache: true, method: 'GET', url: urlBase+'search?q='+query+'&type='+type+'&country='+country+'&limit='+limit, headers: { diff --git a/app/settings/controller.js b/app/settings/controller.js index c4be995..6639e0a 100755 --- a/app/settings/controller.js +++ b/app/settings/controller.js @@ -10,7 +10,7 @@ angular.module('spotmop.settings', []) $stateProvider .state('settings', { url: "/settings", - //abstract: true, + abstract: true, templateUrl: "app/settings/template.html", controller: ['$scope', '$state', function( $scope, $state) { @@ -21,7 +21,7 @@ angular.module('spotmop.settings', []) }] }) .state('settings.mopidy', { - url: "/mopidy", + url: "", templateUrl: "app/settings/mopidy.template.html" }) .state('settings.spotify', { @@ -79,6 +79,13 @@ angular.module('spotmop.settings', []) SettingsService.setSetting('keyboardShortcutsEnabled',true); } }; + $scope.toggleEmulateTouchDevice = function(){ + if( SettingsService.getSetting('emulateTouchDevice',true) ){ + SettingsService.setSetting('emulateTouchDevice',false); + }else{ + SettingsService.setSetting('emulateTouchDevice',true); + } + }; // commands to parse to the mopidy server $scope.startMopidyServer = function(){ @@ -167,12 +174,10 @@ angular.module('spotmop.settings', []) EchonestService.stop(); } }; - $scope.resetSettings = function( confirmed ){ - if( confirmed ){ - $rootScope.$broadcast('spotmop:notifyUser', {id: 'reset-settings', message: "All settings reset... reloading"}); - localStorage.clear(); - window.location = window.location; - } + $scope.resetSettings = function(){ + $rootScope.$broadcast('spotmop:notifyUser', {id: 'reset-settings', message: "All settings reset... reloading"}); + localStorage.clear(); + window.location = window.location; }; SettingsService.getVersion() diff --git a/app/settings/echonest.template.html b/app/settings/echonest.template.html index f706212..cf63c71 100755 --- a/app/settings/echonest.template.html +++ b/app/settings/echonest.template.html @@ -24,7 +24,7 @@
-
Up arrow
+
CTRL + Up arrow
Volume up by 10%
-
Down arrow
+
CTRL + Down arrow
Volume down by 10%
@@ -61,14 +61,19 @@
+
+
Emulate touch device
+
+ +
+
+
\ No newline at end of file diff --git a/app/settings/mopidy.template.html b/app/settings/mopidy.template.html index 003ae7b..365322c 100755 --- a/app/settings/mopidy.template.html +++ b/app/settings/mopidy.template.html @@ -36,13 +36,13 @@
- +   Start - +   Restart - +   Stop
diff --git a/app/settings/spotify.template.html b/app/settings/spotify.template.html index 8711d70..16cec46 100755 --- a/app/settings/spotify.template.html +++ b/app/settings/spotify.template.html @@ -50,8 +50,8 @@
diff --git a/assets/css/context-menu.css b/assets/css/context-menu.css index 5a1cab3..4072749 100755 --- a/assets/css/context-menu.css +++ b/assets/css/context-menu.css @@ -33,4 +33,47 @@ contextmenu .menu-item:hover { } +/** + * Touch-device friendly + * This provides a sticky toolbar while tracks are selected + **/ +.touchDevice contextmenu { + padding-left: 5px; + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 99; +} + +.touchDevice contextmenu .menu-item { + display: block; + float: left; + padding: 14px 0 12px; + width: 45px; + text-align: center; + border: 0; +} + +.touchDevice contextmenu .menu-item.cancel { + opacity: 0.5; + float: right; +} + +.touchDevice contextmenu .menu-item .icon { + font-size: 16px; +} + +.touchDevice contextmenu .menu-item .text { + font-size: 8px; + display: block; + text-transform: uppercase; + padding-top: 5px; +} + + + + + + diff --git a/assets/css/core.css b/assets/css/core.css index 2ad2094..a2db976 100755 --- a/assets/css/core.css +++ b/assets/css/core.css @@ -383,13 +383,15 @@ h4.section-title { * Text colour variations **/ -.black-text { color: #000000; } -.red-text { color: rgb(214,47,47); } -.green-text { color: rgb(53,204,53); } +.black-text { color: #000000; } +.red-text { color: rgb(214,47,47); } +.green-text { color: rgb(53,204,53); } +.primary-colour-text { color: #1cf4c4; } -.black-background { background-color: #000000; } -.red-background { background-color: rgb(214,47,47); } -.green-background { background-color: rgb(53,204,53); } +.black-background { background-color: #000000; } +.red-background { background-color: rgb(214,47,47); } +.green-background { background-color: rgb(53,204,53); } +.primary-colour-background { background-color: #1cf4c4; } /** @@ -479,81 +481,46 @@ h1 .flag { /** * Clickable buttons + * These are all within the .body element (ie the main app frame) **/ -body .button.default, -body .button.pill { - padding: 8px 18px; - background: #333333; - color: #FFFFFF; +.body .button { + padding: 8px 16px; display: inline-block; border: 0; cursor: pointer; - margin-right: 10px; + margin-right: 4px; + border-radius: 3px; + color: #FFFFFF; + border: 0; } -body .button.bordered { - border: 1px solid #333333; - color: #333333; - background: none; - text-transform: uppercase; - display: inline-block; - vertical-align: top; -} +.body .button { background: #9b9b9b; } +.body .button:hover { background: #8B8B8B; } -body .button.bordered:not(.disabled):hover { - background: #FAFAFA; +.body .button .fa { + margin-top: 2px; + margin-left: -2px; + vertical-align: top; } -body:not(.touchDevice) .button.bordered:not(.disabled):hover { - border-bottom-width: 4px; - padding-top: 16px; - padding-bottom: 16px; - background: #FAFAFA; -} -.button.pill:not(.disabled):hover, -.button.default:not(.disabled):hover { - background: #202020; -} +/* variations */ -.button.pill { - border-radius: 20px; -} +.body .button.primary { background: #08d58f; } +.body .button.primary:hover { background: #03c784; } -.button.default > .fa { - margin-top: 2px; - vertical-align: top; -} +.body .button.destructive { background: #cf2d2d; } +.body .button.destructive:hover { background: #bc1f1f; } -/* disabled */ -body .button.disabled { +.body .button.disabled { cursor: not-allowed; color: #DDDDDD !important; border-color: #DDDDDD !important; } -/* variations */ - -.button.large { padding: 18px 26px; } - -.button.light { background: #EEEEEE; color: #222222; } -.button.light:not(.disabled):hover { background: #DDDDDD; } - -.button.red { background-color: #D62F2F; } -.button.red:not(.disabled):hover, -.button.red-hover:not(.disabled):hover { background-color: #c42424; } -.button.bordered.red { border-color: #D62F2F; color: #D62F2F; } - -.button.green { background-color: #35CC35; } -.button.green:not(.disabled):hover, -.button.green-hover:not(.disabled):hover { background-color: #28b428; } -.button.bordered.green { border-color: #35CC35; color: #35CC35; } - - - /** * Switch buttons **/ @@ -681,7 +648,8 @@ body .button.disabled { .square-panel .image-container { position: relative; - height: 180px; + width: 100%; + padding-bottom: 100%; } .square-panel .image-container .image { @@ -903,17 +871,35 @@ body:not(.dragging):not(.touchDevice) .tracklist .track:not(.selected):hover { } .playlist-intro .inner { - background: rgba(0,0,0,0.5); + background: rgba(0,0,0,0.65); color: #FFFFFF; padding: 50px; } +.playlist-intro .content { + padding-left: 220px; +} + +.playlist-intro .thumbnail { + width: 200px; + height: 200px; + float: left; + margin: -20px 0 -20px -20px; + background-position: 50% 50%; + background-size: cover; +} + .playlist-intro h1 { - padding-bottom: 8px; + padding-bottom: 0; + margin-bottom: -5px; } .playlist-intro .description { - padding-bottom: 20px; + padding: 24px 0 8px; +} + +.playlist-intro .buttons { + padding-top: 20px; } @@ -1075,15 +1061,6 @@ body:not(.dragging):not(.touchDevice) .tracklist .track:not(.selected):hover { width: 29%; } -.search-results-section .square-panel .image-container { - height: 100px; - min-width: 100%; -} - -.search-results-section .square-panel .image-container .image { - min-width: 100%; -} - .search-results-section.playlists .square-panel .info > .name { white-space: nowrap; overflow: hidden; diff --git a/assets/css/forms.css b/assets/css/forms.css index d178b3b..b55ea52 100755 --- a/assets/css/forms.css +++ b/assets/css/forms.css @@ -68,16 +68,6 @@ } -/** - * Form actions - * Also used in settings - **/ - -.actions .button { - margin-right: 15px; -} - - /** * Search form diff --git a/assets/css/responsive.css b/assets/css/responsive.css index a03d61b..0ba89bb 100755 --- a/assets/css/responsive.css +++ b/assets/css/responsive.css @@ -275,6 +275,21 @@ } + /** + * Playlist page + **/ + .playlist-intro .thumbnail { + width: 120px; + height: 120px; + margin-left: -10px; + margin-top: -10px; + } + + .playlist-intro .content { + padding-left: 150px; + } + + /** * Square panels **/ @@ -284,21 +299,6 @@ padding-bottom: 8%; font-size: 13px; } - - /** - * Context menu - **/ - .tracklist-context-menu { - padding-left: 5px; - } - - .tracklist-context-menu .menu-item { - padding: 14px 5px 12px; - } - - .tracklist-context-menu .menu-item .text { - font-size: 8px; - } /** @@ -334,14 +334,8 @@ padding-left: 0; } - .button.large { - padding: 12px 16px; - margin-right: 10px; - } - - body .button.bordered:hover:not(.disabled) { - padding-bottom: 12px; - padding-top: 12px; + .body .button { + padding: 6px 12px; } @@ -388,6 +382,30 @@ padding-bottom: 20px; } + + /** + * Playlist page + **/ + .playlist-intro .thumbnail { + width: 80px; + height: 80px; + margin-top: 5px; + margin-left: -5px; + } + + .playlist-intro .content { + padding-left: 100px; + } + + .playlist-intro .description { + padding-top: 10px; + } + + .playlist-intro .buttons { + padding-top: 10px; + } + + /** * Square panels **/ @@ -398,10 +416,10 @@ /** - * Touch-device context menus - **/ - .tracklist-context-menu .status { - display: none; + * Buttons and interactive panels + **/ + .body .button { + padding: 4px 8px; } @@ -418,6 +436,7 @@ width: 29.3333%; } + /** * Player **/ diff --git a/assets/progress-background.psd b/assets/progress-background.psd deleted file mode 100644 index 6899e3d..0000000 Binary files a/assets/progress-background.psd and /dev/null differ diff --git a/assets/search page concept.psd b/assets/search page concept.psd deleted file mode 100755 index fecfa04..0000000 Binary files a/assets/search page concept.psd and /dev/null differ diff --git a/index.html b/index.html index 5249096..3efb6c9 100755 --- a/index.html +++ b/index.html @@ -61,7 +61,6 @@
-
@@ -70,6 +69,8 @@
+ +