Track searching stuff
|
@ -6,14 +6,15 @@
|
||||||
use Entities\License;
|
use Entities\License;
|
||||||
use Entities\ShowSong;
|
use Entities\ShowSong;
|
||||||
use Entities\TrackType;
|
use Entities\TrackType;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class TaxonomiesController extends \ApiControllerBase {
|
class TaxonomiesController extends \ApiControllerBase {
|
||||||
public function getAll() {
|
public function getAll() {
|
||||||
return \Response::json([
|
return \Response::json([
|
||||||
'licenses' => License::all()->toArray(),
|
'licenses' => License::all()->toArray(),
|
||||||
'genres' => Genre::orderBy('name')->get()->toArray(),
|
'genres' => Genre::select('genres.*', DB::raw('(SELECT COUNT(id) FROM tracks WHERE tracks.genre_id = genres.id AND tracks.published_at IS NOT NULL) AS track_count'))->orderBy('name')->get()->toArray(),
|
||||||
'track_types' => TrackType::all()->toArray(),
|
'track_types' => TrackType::select('track_types.*', DB::raw('(SELECT COUNT(id) FROM tracks WHERE tracks.track_type_id = track_types.id AND tracks.published_at IS NOT NULL) AS track_count'))->get()->toArray(),
|
||||||
'show_songs' => ShowSong::select('title', 'id', 'slug')->get()->toArray()
|
'show_songs' => ShowSong::select('title', 'id', 'slug', DB::raw('(SELECT COUNT(tracks.id) FROM show_song_track INNER JOIN tracks ON tracks.id = show_song_track.track_id WHERE show_song_track.show_song_id = show_songs.id AND tracks.published_at IS NOT NULL) AS track_count'))->get()->toArray()
|
||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -34,39 +34,32 @@
|
||||||
$tracks = [];
|
$tracks = [];
|
||||||
|
|
||||||
foreach ($query->get() as $track) {
|
foreach ($query->get() as $track) {
|
||||||
$tracks[] = [
|
$tracks[] = $this->mapPublicTrack($track);
|
||||||
'id' => $track->id,
|
|
||||||
'title' => $track->title,
|
|
||||||
'user' => [
|
|
||||||
'id' => $track->user->id,
|
|
||||||
'name' => $track->user->display_name,
|
|
||||||
'url' => $track->user->url
|
|
||||||
],
|
|
||||||
'url' => $track->url,
|
|
||||||
'slug' => $track->slug,
|
|
||||||
'is_vocal' => $track->is_vocal,
|
|
||||||
'is_explicit' => $track->is_explicit,
|
|
||||||
'is_downloadable' => $track->is_downloadable,
|
|
||||||
'is_published' => $track->isPublished(),
|
|
||||||
'published_at' => $track->published_at,
|
|
||||||
'duration' => $track->duration,
|
|
||||||
'genre' => [
|
|
||||||
'id' => $track->genre->id,
|
|
||||||
'slug' => $track->genre->slug,
|
|
||||||
'name' => $track->genre->name
|
|
||||||
],
|
|
||||||
'track_type_id' => $track->track_type_id,
|
|
||||||
'covers' => [
|
|
||||||
'thumbnail' => $track->getCoverUrl(Image::THUMBNAIL),
|
|
||||||
'small' => $track->getCoverUrl(Image::SMALL),
|
|
||||||
'normal' => $track->getCoverUrl(Image::NORMAL)
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response::json($tracks, 200);
|
return Response::json($tracks, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getIndex() {
|
||||||
|
$page = 1;
|
||||||
|
|
||||||
|
if (Input::has('page'))
|
||||||
|
$page = Input::get('page');
|
||||||
|
|
||||||
|
$query = Track::summary()->whereNotNull('published_at');
|
||||||
|
$this->applyFilters($query);
|
||||||
|
|
||||||
|
$totalCount = $query->count();
|
||||||
|
$query->take(30)->skip(30 * ($page - 1));
|
||||||
|
$tracks = [];
|
||||||
|
|
||||||
|
foreach ($query->get() as $track) {
|
||||||
|
$tracks[] = $this->mapPublicTrack($track);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response::json(["tracks" => $tracks, "current_page" => $page, "total_pages" => ceil($totalCount / 30)], 200);
|
||||||
|
}
|
||||||
|
|
||||||
public function getOwned() {
|
public function getOwned() {
|
||||||
$query = Track::summary()->where('user_id', \Auth::user()->id);
|
$query = Track::summary()->where('user_id', \Auth::user()->id);
|
||||||
|
|
||||||
|
@ -78,24 +71,7 @@
|
||||||
$query->whereNull('published_at');
|
$query->whereNull('published_at');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Input::has('order')) {
|
$this->applyFilters($query);
|
||||||
$order = \Input::get('order');
|
|
||||||
$parts = explode(',', $order);
|
|
||||||
$query->orderBy($parts[0], $parts[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Input::has('in_album')) {
|
|
||||||
if (Input::get('in_album') == 'true')
|
|
||||||
$query->whereNotNull('album_id');
|
|
||||||
else
|
|
||||||
$query->whereNull('album_id');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Input::has('genres'))
|
|
||||||
$query->whereIn('genre_id', Input::get('genres'));
|
|
||||||
|
|
||||||
if (Input::has('types'))
|
|
||||||
$query->whereIn('track_type_id', Input::get('types'));
|
|
||||||
|
|
||||||
$dbTracks = $query->get();
|
$dbTracks = $query->get();
|
||||||
$tracks = [];
|
$tracks = [];
|
||||||
|
@ -159,4 +135,75 @@
|
||||||
'album_id' => $track->album_id
|
'album_id' => $track->album_id
|
||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function mapPublicTrack($track) {
|
||||||
|
return [
|
||||||
|
'id' => $track->id,
|
||||||
|
'title' => $track->title,
|
||||||
|
'user' => [
|
||||||
|
'id' => $track->user->id,
|
||||||
|
'name' => $track->user->display_name,
|
||||||
|
'url' => $track->user->url
|
||||||
|
],
|
||||||
|
'url' => $track->url,
|
||||||
|
'slug' => $track->slug,
|
||||||
|
'is_vocal' => $track->is_vocal,
|
||||||
|
'is_explicit' => $track->is_explicit,
|
||||||
|
'is_downloadable' => $track->is_downloadable,
|
||||||
|
'is_published' => $track->isPublished(),
|
||||||
|
'published_at' => $track->published_at,
|
||||||
|
'duration' => $track->duration,
|
||||||
|
'genre' => $track->genre != null
|
||||||
|
?
|
||||||
|
[
|
||||||
|
'id' => $track->genre->id,
|
||||||
|
'slug' => $track->genre->slug,
|
||||||
|
'name' => $track->genre->name
|
||||||
|
] : null,
|
||||||
|
'track_type_id' => $track->track_type_id,
|
||||||
|
'covers' => [
|
||||||
|
'thumbnail' => $track->getCoverUrl(Image::THUMBNAIL),
|
||||||
|
'small' => $track->getCoverUrl(Image::SMALL),
|
||||||
|
'normal' => $track->getCoverUrl(Image::NORMAL)
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyFilters($query) {
|
||||||
|
if (Input::has('order')) {
|
||||||
|
$order = \Input::get('order');
|
||||||
|
$parts = explode(',', $order);
|
||||||
|
$query->orderBy($parts[0], $parts[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Input::has('is_vocal')) {
|
||||||
|
$isVocal = \Input::get('is_vocal');
|
||||||
|
if ($isVocal == 'true')
|
||||||
|
$query->whereIsVocal(true);
|
||||||
|
else
|
||||||
|
$query->whereIsVocal(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Input::has('in_album')) {
|
||||||
|
if (Input::get('in_album') == 'true')
|
||||||
|
$query->whereNotNull('album_id');
|
||||||
|
else
|
||||||
|
$query->whereNull('album_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Input::has('genres'))
|
||||||
|
$query->whereIn('genre_id', Input::get('genres'));
|
||||||
|
|
||||||
|
if (Input::has('types'))
|
||||||
|
$query->whereIn('track_type_id', Input::get('types'));
|
||||||
|
|
||||||
|
if (Input::has('songs')) {
|
||||||
|
$query->join('show_song_track', 'tracks.id', '=', 'show_song_track.track_id')
|
||||||
|
->whereIn('show_song_track.show_song_id', Input::get('songs'));
|
||||||
|
|
||||||
|
$query->select('tracks.*');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -40,6 +40,7 @@
|
||||||
Route::get('/playlists/show/{id}', 'Api\Web\PlaylistsController@getShow');
|
Route::get('/playlists/show/{id}', 'Api\Web\PlaylistsController@getShow');
|
||||||
|
|
||||||
Route::get('/tracks/recent', 'Api\Web\TracksController@getRecent');
|
Route::get('/tracks/recent', 'Api\Web\TracksController@getRecent');
|
||||||
|
Route::get('/tracks', 'Api\Web\TracksController@getIndex');
|
||||||
|
|
||||||
Route::get('/dashboard', 'Api\Web\DashboardController@getIndex');
|
Route::get('/dashboard', 'Api\Web\DashboardController@getIndex');
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,6 @@
|
||||||
@else
|
@else
|
||||||
<li ng-class="{selected: $state.includes('home')}"><a href="/">Home</a></li>
|
<li ng-class="{selected: $state.includes('home')}"><a href="/">Home</a></li>
|
||||||
@endif
|
@endif
|
||||||
<li><a href="/tracks">Now Playing</a></li>
|
|
||||||
<li><h3>Discover</h3></li>
|
<li><h3>Discover</h3></li>
|
||||||
<li ng-class="{selected: $state.includes('tracks')}"><a href="/tracks">Music <i class="icon-music"></i></a></li>
|
<li ng-class="{selected: $state.includes('tracks')}"><a href="/tracks">Music <i class="icon-music"></i></a></li>
|
||||||
<li ng-class="{selected: $state.includes('albums')}"><a href="/albums">Albums <i class="icon-music"></i></a></li>
|
<li ng-class="{selected: $state.includes('albums')}"><a href="/albums">Albums <i class="icon-music"></i></a></li>
|
||||||
|
|
BIN
public/images/pattern1.jpg
Normal file
After Width: | Height: | Size: 106 KiB |
BIN
public/images/pattern10.jpg
Normal file
After Width: | Height: | Size: 140 KiB |
BIN
public/images/pattern2.jpg
Normal file
After Width: | Height: | Size: 108 KiB |
BIN
public/images/pattern3.jpg
Normal file
After Width: | Height: | Size: 143 KiB |
BIN
public/images/pattern4.jpg
Normal file
After Width: | Height: | Size: 218 KiB |
BIN
public/images/pattern5.jpg
Normal file
After Width: | Height: | Size: 139 KiB |
BIN
public/images/pattern6.jpg
Normal file
After Width: | Height: | Size: 185 KiB |
BIN
public/images/pattern7.jpg
Normal file
After Width: | Height: | Size: 129 KiB |
BIN
public/images/pattern8.jpg
Normal file
After Width: | Height: | Size: 118 KiB |
BIN
public/images/pattern9.jpg
Normal file
After Width: | Height: | Size: 214 KiB |
BIN
public/images/sidebar-background.jpg
Normal file
After Width: | Height: | Size: 260 KiB |
BIN
public/images/test_pattern.jpg
Normal file
After Width: | Height: | Size: 88 KiB |
|
@ -69,7 +69,26 @@ angular.module 'ponyfm', ['ui.bootstrap', 'ui.state', 'ui.date', 'ui.sortable'],
|
||||||
|
|
||||||
state.state 'tracks',
|
state.state 'tracks',
|
||||||
url: '/tracks'
|
url: '/tracks'
|
||||||
templateUrl: '/templates/tracks/index.html'
|
templateUrl: '/templates/tracks/_layout.html'
|
||||||
|
abstract: true
|
||||||
|
|
||||||
|
state.state 'tracks.search',
|
||||||
|
templateUrl: '/templates/tracks/search.html'
|
||||||
|
controller: 'tracks'
|
||||||
|
|
||||||
|
state.state 'tracks.search.list',
|
||||||
|
url: '?filter&page'
|
||||||
|
templateUrl: '/templates/tracks/search-list.html'
|
||||||
|
controller: 'tracks-list'
|
||||||
|
|
||||||
|
state.state 'tracks.popular',
|
||||||
|
url: '/popular'
|
||||||
|
templateUrl: '/templates/tracks/search.html'
|
||||||
|
controller: 'tracks'
|
||||||
|
|
||||||
|
state.state 'tracks.random',
|
||||||
|
url: '/random'
|
||||||
|
templateUrl: '/templates/tracks/search.html'
|
||||||
controller: 'tracks'
|
controller: 'tracks'
|
||||||
|
|
||||||
# Albums
|
# Albums
|
||||||
|
@ -121,7 +140,7 @@ angular.module 'ponyfm', ['ui.bootstrap', 'ui.state', 'ui.date', 'ui.sortable'],
|
||||||
if window.pfm.auth.isLogged
|
if window.pfm.auth.isLogged
|
||||||
state.state 'home',
|
state.state 'home',
|
||||||
url: '/'
|
url: '/'
|
||||||
templateUrl: '/templates/dashboard.html'
|
templateUrl: '/templates/dashboard/index.html'
|
||||||
controller: 'dashboard'
|
controller: 'dashboard'
|
||||||
else
|
else
|
||||||
state.state 'home',
|
state.state 'home',
|
||||||
|
|
|
@ -137,10 +137,6 @@ angular.module('ponyfm').controller "account-albums-edit", [
|
||||||
title: ''
|
title: ''
|
||||||
description: ''
|
description: ''
|
||||||
|
|
||||||
window.onbeforeunload = ->
|
|
||||||
return if !$scope.isDirty
|
|
||||||
"Are you sure you want to leave this page without saving your changes?"
|
|
||||||
|
|
||||||
$scope.$on '$locationChangeStart', (e) ->
|
$scope.$on '$locationChangeStart', (e) ->
|
||||||
return if !$scope.isDirty
|
return if !$scope.isDirty
|
||||||
e.preventDefault() if !confirm('Are you sure you want to leave this page without saving your changes?')
|
e.preventDefault() if !confirm('Are you sure you want to leave this page without saving your changes?')
|
||||||
|
|
|
@ -138,10 +138,6 @@ angular.module('ponyfm').controller "account-tracks-edit", [
|
||||||
$scope.$emit 'track-deleted'
|
$scope.$emit 'track-deleted'
|
||||||
$state.transitionTo 'account-content.tracks'
|
$state.transitionTo 'account-content.tracks'
|
||||||
|
|
||||||
window.onbeforeunload = ->
|
|
||||||
return if !$scope.isDirty
|
|
||||||
"Are you sure you want to leave this page without saving your changes?"
|
|
||||||
|
|
||||||
$scope.$on '$locationChangeStart', (e) ->
|
$scope.$on '$locationChangeStart', (e) ->
|
||||||
return if !$scope.isDirty
|
return if !$scope.isDirty
|
||||||
e.preventDefault() if !confirm('Are you sure you want to leave this page without saving your changes?')
|
e.preventDefault() if !confirm('Are you sure you want to leave this page without saving your changes?')
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
|
window.pfm.preloaders['dashboard'] = [
|
||||||
|
'dashboard'
|
||||||
|
(dashboard) -> dashboard.refresh(true)
|
||||||
|
]
|
||||||
|
|
||||||
angular.module('ponyfm').controller "dashboard", [
|
angular.module('ponyfm').controller "dashboard", [
|
||||||
'$scope'
|
'$scope', 'dashboard'
|
||||||
($scope) ->
|
($scope, dashboard) ->
|
||||||
$scope.recentTracks = null
|
$scope.recentTracks = null
|
||||||
$scope.popularTracks = null
|
$scope.popularTracks = null
|
||||||
|
|
||||||
$scope.refresh = () ->
|
dashboard.refresh().done (res) ->
|
||||||
$.getJSON('/api/web/dashboard')
|
$scope.recentTracks = res.recent_tracks
|
||||||
.done (res) -> $scope.$apply ->
|
$scope.popularTracks = res.popular_tracks
|
||||||
$scope.recentTracks = res.recent_tracks
|
|
||||||
$scope.popularTracks = res.popular_tracks
|
|
||||||
|
|
||||||
$scope.refresh()
|
|
||||||
]
|
]
|
19
public/scripts/app/controllers/tracks-list.coffee
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
window.pfm.preloaders['tracks-list'] = [
|
||||||
|
'tracks', '$state'
|
||||||
|
(tracks, $state) ->
|
||||||
|
$.when.all [tracks.loadFilters().then(->
|
||||||
|
if !tracks.mainQuery.hasLoadedFilters
|
||||||
|
tracks.mainQuery.fromFilterString($state.params.filter)
|
||||||
|
if $state.params.page
|
||||||
|
tracks.mainQuery.setPage $state.params.page
|
||||||
|
|
||||||
|
tracks.mainQuery.fetch()
|
||||||
|
)]
|
||||||
|
]
|
||||||
|
|
||||||
|
angular.module('ponyfm').controller "tracks-list", [
|
||||||
|
'$scope', 'tracks', '$state',
|
||||||
|
($scope, tracks, $state) ->
|
||||||
|
tracks.mainQuery.fetch().done (searchResults) ->
|
||||||
|
$scope.tracks = searchResults.tracks
|
||||||
|
]
|
|
@ -1,12 +1,33 @@
|
||||||
angular.module('ponyfm').controller "tracks", [
|
angular.module('ponyfm').controller "tracks", [
|
||||||
'$scope'
|
'$scope', 'tracks', '$state'
|
||||||
($scope) ->
|
($scope, tracks, $state) ->
|
||||||
$scope.recentTracks = null
|
$scope.recentTracks = null
|
||||||
|
$scope.query = tracks.mainQuery
|
||||||
|
$scope.filters = tracks.filters
|
||||||
|
|
||||||
$scope.refresh = () ->
|
$scope.toggleListFilter = (filter, id) ->
|
||||||
$.getJSON('/api/web/tracks/recent')
|
$scope.query.toggleListFilter filter, id
|
||||||
.done (res) -> $scope.$apply ->
|
$state.transitionTo 'tracks.search.list', {filter: $scope.query.toFilterString()}
|
||||||
$scope.recentTracks = res
|
|
||||||
|
|
||||||
$scope.refresh()
|
$scope.setFilter = (filter, value) ->
|
||||||
|
$scope.query.setFilter filter, value
|
||||||
|
$state.transitionTo 'tracks.search.list', {filter: $scope.query.toFilterString()}
|
||||||
|
|
||||||
|
$scope.setListFilter = (filter, id) ->
|
||||||
|
$scope.query.setListFilter filter, id
|
||||||
|
$state.transitionTo 'tracks.search.list', {filter: $scope.query.toFilterString()}
|
||||||
|
|
||||||
|
tracks.mainQuery.listen (searchResults) ->
|
||||||
|
$scope.tracks = searchResults.tracks
|
||||||
|
$scope.currentPage = parseInt(searchResults.current_page)
|
||||||
|
$scope.totalPages = parseInt(searchResults.total_pages)
|
||||||
|
delete $scope.nextPage
|
||||||
|
delete $scope.prevPage
|
||||||
|
|
||||||
|
$scope.nextPage = $scope.currentPage + 1 if $scope.currentPage < $scope.totalPages
|
||||||
|
$scope.prevPage = $scope.currentPage - 1 if $scope.currentPage > 1
|
||||||
|
$scope.pages = [1..$scope.totalPages]
|
||||||
|
|
||||||
|
$scope.gotoPage = (page) ->
|
||||||
|
$state.transitionTo 'tracks.search.list', {filter: $state.params.filter, page: page}
|
||||||
]
|
]
|
16
public/scripts/app/services/dashboard.coffee
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
angular.module('ponyfm').factory('dashboard', [
|
||||||
|
'$rootScope', '$http'
|
||||||
|
($rootScope, $http) ->
|
||||||
|
def = null
|
||||||
|
|
||||||
|
self =
|
||||||
|
refresh: (force) ->
|
||||||
|
force = force || false
|
||||||
|
return def if !force && def
|
||||||
|
def = new $.Deferred()
|
||||||
|
$http.get('/api/web/dashboard').success (dashboardContent) ->
|
||||||
|
def.resolve(dashboardContent)
|
||||||
|
def.promise()
|
||||||
|
|
||||||
|
self
|
||||||
|
])
|
|
@ -5,19 +5,31 @@ angular.module('ponyfm').factory('taxonomies', [
|
||||||
|
|
||||||
self =
|
self =
|
||||||
trackTypes: []
|
trackTypes: []
|
||||||
|
trackTypesWithTracks: []
|
||||||
licenses: []
|
licenses: []
|
||||||
genres: []
|
genres: []
|
||||||
|
genresWithTracks: []
|
||||||
showSongs: []
|
showSongs: []
|
||||||
|
showSongsWithTracks: []
|
||||||
refresh: () ->
|
refresh: () ->
|
||||||
return def.promise() if def != null
|
return def.promise() if def != null
|
||||||
|
|
||||||
def = new $.Deferred()
|
def = new $.Deferred()
|
||||||
$http.get('/api/web/taxonomies/all')
|
$http.get('/api/web/taxonomies/all')
|
||||||
.success (taxonomies) ->
|
.success (taxonomies) ->
|
||||||
self.trackTypes.push t for t in taxonomies.track_types
|
for t in taxonomies.track_types
|
||||||
|
self.trackTypes.push t
|
||||||
|
self.trackTypesWithTracks.push t if t.track_count > 0
|
||||||
|
|
||||||
|
for t in taxonomies.genres
|
||||||
|
self.genres.push t
|
||||||
|
self.genresWithTracks.push t if t.track_count > 0
|
||||||
|
|
||||||
|
for t in taxonomies.show_songs
|
||||||
|
self.showSongs.push t
|
||||||
|
self.showSongsWithTracks.push t if t.track_count > 0
|
||||||
|
|
||||||
self.licenses.push t for t in taxonomies.licenses
|
self.licenses.push t for t in taxonomies.licenses
|
||||||
self.genres.push t for t in taxonomies.genres
|
|
||||||
self.showSongs.push t for t in taxonomies.show_songs
|
|
||||||
def.resolve self
|
def.resolve self
|
||||||
|
|
||||||
def.promise()
|
def.promise()
|
||||||
|
|
182
public/scripts/app/services/tracks.coffee
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
angular.module('ponyfm').factory('tracks', [
|
||||||
|
'$rootScope', '$http', 'taxonomies'
|
||||||
|
($rootScope, $http, taxonomies) ->
|
||||||
|
def = null
|
||||||
|
|
||||||
|
class Query
|
||||||
|
cachedDef: null
|
||||||
|
page: 1
|
||||||
|
listeners: []
|
||||||
|
|
||||||
|
constructor: (@availableFilters) ->
|
||||||
|
@filters = {}
|
||||||
|
@hasLoadedFilters = false
|
||||||
|
|
||||||
|
_.each @availableFilters, (filter, name) =>
|
||||||
|
if filter.type == 'single'
|
||||||
|
@filters[name] = _.find filter.values, (f) -> f.isDefault
|
||||||
|
else
|
||||||
|
@filters[name] = {title: 'Any', selectedArray: [], selectedObject: {}}
|
||||||
|
|
||||||
|
isIdSelected: (type, id) ->
|
||||||
|
@filters[type].selectedObject[id] != undefined
|
||||||
|
|
||||||
|
listen: (listener) ->
|
||||||
|
@listeners.push listener
|
||||||
|
@cachedDef.done listener if @cachedDef
|
||||||
|
|
||||||
|
setListFilter: (type, id) ->
|
||||||
|
@cachedDef = null
|
||||||
|
@page = 1
|
||||||
|
filterToAdd = _.find @availableFilters[type].values, (f) -> `f.id == id`
|
||||||
|
return if !filterToAdd
|
||||||
|
|
||||||
|
filter = @filters[type]
|
||||||
|
filter.selectedArray = [filterToAdd]
|
||||||
|
filter.selectedObject = {}
|
||||||
|
filter.selectedObject[id] = filterToAdd
|
||||||
|
filter.title = filterToAdd.title
|
||||||
|
|
||||||
|
toggleListFilter: (type, id) ->
|
||||||
|
@cachedDef = null
|
||||||
|
@page = 1
|
||||||
|
filter = @filters[type]
|
||||||
|
|
||||||
|
if filter.selectedObject[id]
|
||||||
|
delete filter.selectedObject[id]
|
||||||
|
filter.selectedArray.splice _.indexOf(filter.selectedArray, (f) -> f.id == id), 1
|
||||||
|
else
|
||||||
|
filterToAdd = _.find @availableFilters[type].values, (f) -> `f.id == id`
|
||||||
|
return if !filterToAdd
|
||||||
|
filter.selectedObject[id] = filterToAdd
|
||||||
|
filter.selectedArray.push filterToAdd
|
||||||
|
|
||||||
|
if filter.selectedArray.length == 0
|
||||||
|
filter.title = 'Any'
|
||||||
|
else if filter.selectedArray.length == 1
|
||||||
|
filter.title = filter.selectedArray[0].title
|
||||||
|
else
|
||||||
|
filter.title = filter.selectedArray.length + ' selected'
|
||||||
|
|
||||||
|
setPage: (page) ->
|
||||||
|
@page = page
|
||||||
|
@cachedDef = null
|
||||||
|
|
||||||
|
setFilter: (type, value) ->
|
||||||
|
@cachedDef = null
|
||||||
|
@page = 1
|
||||||
|
@filters[type] = value
|
||||||
|
|
||||||
|
toFilterString: ->
|
||||||
|
parts = []
|
||||||
|
_.each @availableFilters, (filter, name) =>
|
||||||
|
if filter.type == 'single'
|
||||||
|
return if @filters[name].query == ''
|
||||||
|
parts.push(name + '-' + @filters[name].query)
|
||||||
|
else
|
||||||
|
return if @filters[name].selectedArray.length == 0
|
||||||
|
parts.push(name + '-' + _.map(@filters[name].selectedArray, (f) -> f.id).join '-')
|
||||||
|
|
||||||
|
return parts.join '!'
|
||||||
|
|
||||||
|
fromFilterString: (str) ->
|
||||||
|
@hasLoadedFilters = true
|
||||||
|
return if !str
|
||||||
|
filters = str.split '!'
|
||||||
|
for filter in filters
|
||||||
|
parts = filter.split '-'
|
||||||
|
name = parts[0]
|
||||||
|
return if !@availableFilters[name]
|
||||||
|
|
||||||
|
if @availableFilters[name].type == 'single'
|
||||||
|
filterToSet = _.find @availableFilters[name].values, (f) -> f.query == parts[1]
|
||||||
|
filterToSet = _.find @availableFilters[name].values, (f) -> f.isDefault if filterToSet == null
|
||||||
|
else
|
||||||
|
@toggleListFilter name, id for id in _.rest parts, 1
|
||||||
|
|
||||||
|
fetch: () ->
|
||||||
|
return @cachedDef if @cachedDef
|
||||||
|
@cachedDef = new $.Deferred()
|
||||||
|
def = @cachedDef
|
||||||
|
|
||||||
|
query = '/api/web/tracks?'
|
||||||
|
parts = ['page=' + @page]
|
||||||
|
_.each @availableFilters, (filter, name) =>
|
||||||
|
if filter.type == 'single'
|
||||||
|
parts.push @filters[name].filter
|
||||||
|
else
|
||||||
|
queryName = filter.filterName
|
||||||
|
for item in @filters[name].selectedArray
|
||||||
|
parts.push queryName + "[]=" + item.id
|
||||||
|
|
||||||
|
query += parts.join '&'
|
||||||
|
$http.get(query).success (tracks) =>
|
||||||
|
@tracks = tracks
|
||||||
|
for listener in @listeners
|
||||||
|
listener tracks
|
||||||
|
|
||||||
|
def.resolve tracks
|
||||||
|
|
||||||
|
def.promise()
|
||||||
|
|
||||||
|
self =
|
||||||
|
filters: {}
|
||||||
|
|
||||||
|
createQuery: -> new Query self.filters
|
||||||
|
loadFilters: ->
|
||||||
|
return def if def
|
||||||
|
|
||||||
|
def = new $.Deferred()
|
||||||
|
self.filters.isVocal =
|
||||||
|
type: 'single'
|
||||||
|
values: [
|
||||||
|
{title: 'Either', query: '', isDefault: true, filter: ''},
|
||||||
|
{title: 'Yes', query: 'yes', isDefault: false, filter: 'is_vocal=true'},
|
||||||
|
{title: 'No', query: 'no', isDefault: false, filter: 'is_vocal=false'}
|
||||||
|
]
|
||||||
|
|
||||||
|
self.filters.sort =
|
||||||
|
type: 'single'
|
||||||
|
values: [
|
||||||
|
{title: 'Newest to Oldest', query: '', isDefault: true, filter: 'order=created_at,desc'},
|
||||||
|
{title: 'Oldest to Newest', query: 'created_at,asc', isDefault: true, filter: 'order=created_at,asc'}
|
||||||
|
]
|
||||||
|
|
||||||
|
self.filters.genres =
|
||||||
|
type: 'list'
|
||||||
|
values: []
|
||||||
|
filterName: 'genres'
|
||||||
|
|
||||||
|
self.filters.trackTypes =
|
||||||
|
type: 'list'
|
||||||
|
values: []
|
||||||
|
filterName: 'types'
|
||||||
|
|
||||||
|
self.filters.showSongs =
|
||||||
|
type: 'list'
|
||||||
|
values: []
|
||||||
|
filterName: 'songs'
|
||||||
|
|
||||||
|
taxonomies.refresh().done (taxes) ->
|
||||||
|
for genre in taxes.genresWithTracks
|
||||||
|
self.filters.genres.values.push
|
||||||
|
title: genre.name
|
||||||
|
id: genre.id
|
||||||
|
|
||||||
|
for type in taxes.trackTypesWithTracks
|
||||||
|
self.filters.trackTypes.values.push
|
||||||
|
title: type.title
|
||||||
|
id: type.id
|
||||||
|
|
||||||
|
for song in taxes.showSongsWithTracks
|
||||||
|
self.filters.showSongs.values.push
|
||||||
|
title: song.title
|
||||||
|
id: song.id
|
||||||
|
|
||||||
|
self.mainQuery = self.createQuery()
|
||||||
|
def.resolve self
|
||||||
|
|
||||||
|
def.promise()
|
||||||
|
|
||||||
|
self
|
||||||
|
])
|
|
@ -1,5 +1,5 @@
|
||||||
@import 'base/bootstrap/bootstrap';
|
@import-once 'base/bootstrap/bootstrap';
|
||||||
@import 'mixins';
|
@import-once 'mixins';
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
html {
|
html {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
@import 'base/bootstrap/bootstrap';
|
@import-once 'base/bootstrap/bootstrap';
|
||||||
@import 'mixins';
|
@import-once 'mixins';
|
||||||
|
|
||||||
.slide-down-enter, .slide-down-leave,
|
.slide-down-enter, .slide-down-leave,
|
||||||
.slide-up-enter, .slide-up-leave,
|
.slide-up-enter, .slide-up-leave,
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
@import 'base/bootstrap/bootstrap';
|
@import-once 'base/bootstrap/bootstrap';
|
||||||
@import 'base/font-awesome/font-awesome';
|
@import-once 'base/font-awesome/font-awesome';
|
||||||
@import 'mixins';
|
@import-once 'mixins';
|
||||||
@import 'layout';
|
@import-once 'layout';
|
||||||
@import 'home';
|
@import-once 'home';
|
||||||
@import 'account-content';
|
@import-once 'account-content';
|
||||||
@import 'components';
|
@import-once 'components';
|
||||||
@import 'forms';
|
@import-once 'forms';
|
||||||
@import 'tracks';
|
@import-once 'tracks';
|
||||||
@import 'animations';
|
@import-once 'animations';
|
|
@ -1,5 +1,5 @@
|
||||||
@import 'base/bootstrap/bootstrap';
|
@import-once 'base/bootstrap/bootstrap';
|
||||||
@import 'mixins';
|
@import-once 'mixins';
|
||||||
|
|
||||||
.fade-hide, .fade-show {
|
.fade-hide, .fade-show {
|
||||||
.transition(all cubic-bezier(0.250, 0.460, 0.450, 0.940) 350ms);
|
.transition(all cubic-bezier(0.250, 0.460, 0.450, 0.940) 350ms);
|
||||||
|
@ -51,6 +51,25 @@ html .dropdown-menu {
|
||||||
padding: 3px 10px;
|
padding: 3px 10px;
|
||||||
font-size: 8pt;
|
font-size: 8pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> .dont-close {
|
||||||
|
float: right;
|
||||||
|
display: block;
|
||||||
|
width: 24px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 3px 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .dont-close + a {
|
||||||
|
margin-right: 30px;
|
||||||
|
clear: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
> .dont-close i:before {
|
||||||
|
content: "\f068";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,12 +98,11 @@ html .dropdown-menu {
|
||||||
|
|
||||||
li.selected {
|
li.selected {
|
||||||
a {
|
a {
|
||||||
#gradient>.vertical(@green, darken(@green, 5%));
|
background: @green;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
#gradient>.vertical(fadeout(@green, 20%), fadeout(darken(@green, 5%), 20%));
|
background: fadeout(@green, 20%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,4 +188,25 @@ html .dropdown-menu {
|
||||||
.btn {
|
.btn {
|
||||||
.border-radius(0px);
|
.border-radius(0px);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
.pagination {
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
&.pagination-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
li a {
|
||||||
|
.border-radius(0px);
|
||||||
|
border: none;
|
||||||
|
padding: 1px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.active a {
|
||||||
|
background: #444;
|
||||||
|
color: #ddd;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
@import 'base/bootstrap/bootstrap';
|
@import-once 'base/bootstrap/bootstrap';
|
||||||
@import 'mixins';
|
@import-once 'mixins';
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -13,6 +13,7 @@ html body {
|
||||||
|
|
||||||
.background-color {
|
.background-color {
|
||||||
background: rgba(42, 42, 42, 1);
|
background: rgba(42, 42, 42, 1);
|
||||||
|
background-image: url('/images/pattern4.jpg');
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0px;
|
left: 0px;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
|
@ -32,7 +33,7 @@ ui-view {
|
||||||
content: ' ';
|
content: ' ';
|
||||||
top: 0px;
|
top: 0px;
|
||||||
left: 0px;
|
left: 0px;
|
||||||
z-index: 1000;
|
z-index: 999;
|
||||||
width: 0px;
|
width: 0px;
|
||||||
height: 0px;
|
height: 0px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -53,6 +54,7 @@ ui-view {
|
||||||
|
|
||||||
header {
|
header {
|
||||||
.clearfix();
|
.clearfix();
|
||||||
|
.box-shadow(0px 1px 8px rgba(0, 0, 0, 0.2));
|
||||||
background: #222;
|
background: #222;
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
@import 'base/bootstrap/bootstrap';
|
@import-once 'base/bootstrap/bootstrap';
|
||||||
@import 'mixins';
|
@import-once 'mixins';
|
||||||
|
|
||||||
.dashboard {
|
.dashboard {
|
||||||
section {
|
section {
|
||||||
|
@ -24,25 +24,72 @@
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
|
||||||
|
&.two-column {
|
||||||
|
li {
|
||||||
|
.box-sizing(border-box);
|
||||||
|
|
||||||
|
width: 50%;
|
||||||
|
float: left;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin: 5px 0px;
|
margin: 10px 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
padding-bottom: 5px;
|
|
||||||
|
|
||||||
img {
|
&:hover {
|
||||||
.img-polaroid();
|
background: #eee;
|
||||||
padding: 3px;
|
|
||||||
padding: 0px;
|
.image {
|
||||||
display: block;
|
.play-button {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
float: left;
|
float: left;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.play-button {
|
||||||
|
.transition(background 250ms ease-out);
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
line-height: 38px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12pt;
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
background: rgba(0, 0, 0, .4);
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(0, 0, 0, .8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
.img-polaroid();
|
||||||
|
padding: 3px;
|
||||||
|
padding: 0px;
|
||||||
|
display: block;
|
||||||
|
height: 38px;
|
||||||
|
width: 38px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icons {
|
.icons {
|
||||||
float: right;
|
float: right;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
margin-right: 2px;
|
||||||
|
|
||||||
a, span {
|
a, span {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -56,7 +103,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
margin-left: 51px;
|
text-decoration: none;
|
||||||
|
display: block;
|
||||||
|
padding: 3px 0px;
|
||||||
|
margin-left: 40px;
|
||||||
|
padding-left: 5px;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
@ -71,10 +122,6 @@
|
||||||
display: block;
|
display: block;
|
||||||
color: #777;
|
color: #777;
|
||||||
font-size: 8pt;
|
font-size: 8pt;
|
||||||
|
|
||||||
a {
|
|
||||||
color: fadeOut(@blue, 10%);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,18 +10,23 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="stretch-to-bottom">
|
<div class="stretch-to-bottom">
|
||||||
<div class="form-row" ng-class="{'has-error': errors.display_name != null}">
|
<div class="row-fluid">
|
||||||
<label for="sync_names" class="strong"><input ng-disabled="isSaving" ng-change="touchModel();" id="sync_names" type="checkbox" ng-model="settings.sync_names" /> Sync my MLP Forums display name with Pony.fm</label>
|
<div class="form-row span6" ng-class="{'has-error': errors.display_name != null}">
|
||||||
<input type="text" ng-disabled="isSaving" ng-change="touchModel()" ng-show="!settings.sync_names" placeholder="Display Name" id="display_name" ng-model="settings.display_name" />
|
<label for="sync_names" class="strong"><input ng-disabled="isSaving" ng-change="touchModel();" id="sync_names" type="checkbox" ng-model="settings.sync_names" /> Sync my MLP Forums display name with Pony.fm</label>
|
||||||
<div ng-show="settings.sync_names" class="alert alert-info">Your current MLP Forums display name is <strong>{{settings.mlpforums_name}}</strong></div>
|
<input type="text" ng-disabled="isSaving" ng-change="touchModel()" ng-show="!settings.sync_names" placeholder="Display Name" id="display_name" ng-model="settings.display_name" />
|
||||||
<div class="error">{{errors.display_name}}</div>
|
<div ng-show="settings.sync_names" class="alert alert-info">Your current MLP Forums display name is <strong>{{settings.mlpforums_name}}</strong></div>
|
||||||
</div>
|
<div class="error">{{errors.display_name}}</div>
|
||||||
<div class="form-row" ng-class="{'has-error': errors.bio != null}">
|
</div>
|
||||||
<label class="strong" for="bio">Bio</label>
|
<div class="form-row span6">
|
||||||
<textarea id="bio" placeholder="bio (optional)" ng-model="settings.bio" ng-disabled="isLoading" ng-change="touchModel()"></textarea>
|
<label for="can_see_explicit_content"><input ng-change="touchModel()" ng-disabled="isLoading" id="can_see_explicit_content" type="checkbox" ng-model="settings.can_see_explicit_content" /> Can See Explicit Content</label>
|
||||||
<div class="error">{{errors.description}}</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
|
<div class="form-row span6" ng-class="{'has-error': errors.bio != null}">
|
||||||
|
<label class="strong" for="bio">Bio</label>
|
||||||
|
<textarea id="bio" placeholder="bio (optional)" ng-model="settings.bio" ng-disabled="isLoading" ng-change="touchModel()"></textarea>
|
||||||
|
<div class="error">{{errors.description}}</div>
|
||||||
|
</div>
|
||||||
<div class="form-row span6" ng-class="{'has-error': errors.avatar != null || errors.gravatar != null}">
|
<div class="form-row span6" ng-class="{'has-error': errors.avatar != null || errors.gravatar != null}">
|
||||||
<label for="uses_gravatar">
|
<label for="uses_gravatar">
|
||||||
<input ng-change="touchModel()" ng-disabled="isLoading" id="uses_gravatar" type="checkbox" ng-model="settings.uses_gravatar" /> Use Gravatar
|
<input ng-change="touchModel()" ng-disabled="isLoading" id="uses_gravatar" type="checkbox" ng-model="settings.uses_gravatar" /> Use Gravatar
|
||||||
|
@ -33,9 +38,6 @@
|
||||||
<div class="error" ng-show="errors.avatar != null">{{errors.avatar}}</div>
|
<div class="error" ng-show="errors.avatar != null">{{errors.avatar}}</div>
|
||||||
<div class="error" ng-show="errors.gravatar != null">{{errors.gravatar}}</div>
|
<div class="error" ng-show="errors.gravatar != null">{{errors.gravatar}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row span6">
|
|
||||||
<label for="can_see_explicit_content"><input ng-change="touchModel()" ng-disabled="isLoading" id="can_see_explicit_content" type="checkbox" ng-model="settings.can_see_explicit_content" /> Can See Explicit Content</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
<div class="dashboard">
|
|
||||||
<section class="recent-tracks">
|
|
||||||
<div>
|
|
||||||
<h1>
|
|
||||||
<a href="#"><i class="icon-music"></i> see more</a>
|
|
||||||
The Newest Tunes
|
|
||||||
</h1>
|
|
||||||
<ul class="tracks-listing stretch-to-bottom">
|
|
||||||
<li ng-repeat="track in recentTracks">
|
|
||||||
<img ng-src="{{track.covers.thumbnail}}" />
|
|
||||||
<div class="icons">
|
|
||||||
<span><i ng-class="{'icon-microphone-off': !track.is_vocal, 'icon-microphone': track.is_vocal}"></i></span>
|
|
||||||
<a href="#"><i class="icon-star-empty"></i></a>
|
|
||||||
</div>
|
|
||||||
<div class="info">
|
|
||||||
<a href="{{track.url}}" class="title">{{track.title}}</a>
|
|
||||||
<span class="metadata">
|
|
||||||
by: <a href="{{track.user.url}}">{{track.user.name}}</a> /
|
|
||||||
<a href="#">{{track.genre.name}}</a> /
|
|
||||||
{{track.published_at.date | momentFromNow}}
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section class="popular">
|
|
||||||
<div>
|
|
||||||
<h1>
|
|
||||||
<a href="#"><i class="icon-star"></i> see more</a>
|
|
||||||
What's Popular Today
|
|
||||||
</h1>
|
|
||||||
<ul class="tracks-listing stretch-to-bottom">
|
|
||||||
<li ng-repeat="track in recentTracks">
|
|
||||||
<img ng-src="{{track.covers.thumbnail}}" />
|
|
||||||
<div class="icons">
|
|
||||||
<span><i ng-class="{'icon-microphone-off': !track.is_vocal, 'icon-microphone': track.is_vocal}"></i></span>
|
|
||||||
<a href="#"><i class="icon-star-empty"></i></a>
|
|
||||||
</div>
|
|
||||||
<div class="info">
|
|
||||||
<a href="{{track.url}}" class="title">{{track.title}}</a>
|
|
||||||
<span class="metadata">
|
|
||||||
by: <a href="{{track.user.url}}">{{track.user.name}}</a> /
|
|
||||||
<a href="#">{{track.genre.name}}</a> /
|
|
||||||
{{track.published_at.date | momentFromNow}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
|
@ -7,19 +7,22 @@
|
||||||
</h1>
|
</h1>
|
||||||
<ul class="tracks-listing stretch-to-bottom">
|
<ul class="tracks-listing stretch-to-bottom">
|
||||||
<li ng-repeat="track in recentTracks">
|
<li ng-repeat="track in recentTracks">
|
||||||
<img ng-src="{{track.covers.thumbnail}}" />
|
<div class="image">
|
||||||
|
<a href="#" class="play-button"><i class="icon-play"></i></a>
|
||||||
|
<img ng-src="{{track.covers.thumbnail}}" />
|
||||||
|
</div>
|
||||||
<div class="icons">
|
<div class="icons">
|
||||||
<span><i ng-class="{'icon-microphone-off': !track.is_vocal, 'icon-microphone': track.is_vocal}"></i></span>
|
<span><i ng-class="{'icon-microphone-off': !track.is_vocal, 'icon-microphone': track.is_vocal}"></i></span>
|
||||||
<a href="#"><i class="icon-star-empty"></i></a>
|
<a href="#"><i class="icon-star-empty"></i></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="info">
|
<a class="info" href="{{track.url}}">
|
||||||
<a href="{{track.url}}" class="title">{{track.title}}</a>
|
<span class="title">{{track.title}}</span>
|
||||||
<span class="metadata">
|
<span class="metadata">
|
||||||
by: <a href="{{track.user.url}}">{{track.user.name}}</a> /
|
by: <span class="artist">{{track.user.name}}</span> /
|
||||||
<a href="#">{{track.genre.name}}</a> /
|
<span class="genre">{{track.genre.name}}</span> /
|
||||||
{{track.published_at.date | momentFromNow}}
|
{{track.published_at.date | momentFromNow}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -32,19 +35,22 @@
|
||||||
</h1>
|
</h1>
|
||||||
<ul class="tracks-listing stretch-to-bottom">
|
<ul class="tracks-listing stretch-to-bottom">
|
||||||
<li ng-repeat="track in recentTracks">
|
<li ng-repeat="track in recentTracks">
|
||||||
<img ng-src="{{track.covers.thumbnail}}" />
|
<div class="image">
|
||||||
|
<a href="#" class="play-button"><i class="icon-play"></i></a>
|
||||||
|
<img ng-src="{{track.covers.thumbnail}}" />
|
||||||
|
</div>
|
||||||
<div class="icons">
|
<div class="icons">
|
||||||
<span><i ng-class="{'icon-microphone-off': !track.is_vocal, 'icon-microphone': track.is_vocal}"></i></span>
|
<span><i ng-class="{'icon-microphone-off': !track.is_vocal, 'icon-microphone': track.is_vocal}"></i></span>
|
||||||
<a href="#"><i class="icon-star-empty"></i></a>
|
<a href="#"><i class="icon-star-empty"></i></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="info">
|
<a class="info" href="{{track.url}}">
|
||||||
<a href="{{track.url}}" class="title">{{track.title}}</a>
|
<span class="title">{{track.title}}</span>
|
||||||
<span class="metadata">
|
<span class="metadata">
|
||||||
by: <a href="{{track.user.url}}">{{track.user.name}}</a> /
|
by: <span class="artist">{{track.user.name}}</span> /
|
||||||
<a href="#">{{track.genre.name}}</a> /
|
<span class="genre">{{track.genre.name}}</span> /
|
||||||
{{track.published_at.date | momentFromNow}}
|
{{track.published_at.date | momentFromNow}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
9
public/templates/tracks/_layout.html
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<div>
|
||||||
|
<ul class="tabs">
|
||||||
|
<li ng-class="{active: $state.includes('tracks.search')}"><a href="/tracks">Search</a></li>
|
||||||
|
<li ng-class="{active: $state.includes('tracks.popular')}"><a href="/tracks/popular">Popular</a></li>
|
||||||
|
<li ng-class="{active: $state.includes('tracks.random')}"><a href="/tracks/random">Random</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ui-view></ui-view>
|
||||||
|
</div>
|
20
public/templates/tracks/search-list.html
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<ul class="tracks-listing two-column stretch-to-bottom">
|
||||||
|
<li ng-repeat="track in tracks">
|
||||||
|
<div class="image">
|
||||||
|
<a href="#" class="play-button"><i class="icon-play"></i></a>
|
||||||
|
<img ng-src="{{track.covers.thumbnail}}" />
|
||||||
|
</div>
|
||||||
|
<div class="icons">
|
||||||
|
<span><i ng-class="{'icon-microphone-off': !track.is_vocal, 'icon-microphone': track.is_vocal}"></i></span>
|
||||||
|
<a href="#"><i class="icon-star-empty"></i></a>
|
||||||
|
</div>
|
||||||
|
<a class="info" href="{{track.url}}">
|
||||||
|
<span class="title">{{track.title}}</span>
|
||||||
|
<span class="metadata">
|
||||||
|
by: <span class="artist">{{track.user.name}}</span> /
|
||||||
|
<span class="genre">{{track.genre.name}}</span> /
|
||||||
|
{{track.published_at.date | momentFromNow}}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
67
public/templates/tracks/search.html
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<ul class="dropdowns">
|
||||||
|
<li class="dropdown">
|
||||||
|
<a class="dropdown-toggle btn">
|
||||||
|
Type: <strong>{{query.filters.trackTypes.title}}</strong>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li ng-repeat="type in filters.trackTypes.values" ng-class="{selected: query.isIdSelected('trackTypes', type.id)}">
|
||||||
|
<a class="dont-close" pfm-eat-click href="#" ng-click="toggleListFilter('trackTypes', type.id); $event.stopPropagation();"><i class="icon-plus"></i></a>
|
||||||
|
<a pfm-eat-click href="#" ng-click="setListFilter('trackTypes', type.id);">{{type.title}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="dropdown">
|
||||||
|
<a class="dropdown-toggle btn">
|
||||||
|
Show Songs: <strong>{{query.filters.showSongs.title}}</strong>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li ng-repeat="song in filters.showSongs.values" ng-class="{selected: query.isIdSelected('showSongs', song.id)}">
|
||||||
|
<a class="dont-close" pfm-eat-click href="#" ng-click="toggleListFilter('showSongs', song.id); $event.stopPropagation();"><i class="icon-plus"></i></a>
|
||||||
|
<a pfm-eat-click href="#" ng-click="setListFilter('showSongs', song.id);">{{song.title}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="dropdown">
|
||||||
|
<a class="dropdown-toggle btn">
|
||||||
|
Genre: <strong>{{query.filters.genres.title}}</strong>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li ng-repeat="genre in filters.genres.values" ng-class="{selected: query.isIdSelected('genres', genre.id)}">
|
||||||
|
<a class="dont-close" pfm-eat-click href="#" ng-click="toggleListFilter('genres', genre.id); $event.stopPropagation();"><i class="icon-plus"></i></a>
|
||||||
|
<a pfm-eat-click href="#" ng-click="setListFilter('genres', genre.id);">{{genre.title}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="dropdown">
|
||||||
|
<a class="dropdown-toggle btn">
|
||||||
|
Is Vocal: <strong>{{query.filters.isVocal.title}}</strong>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li ng-repeat="item in filters.isVocal.values" ng-class="{selected: item == query.filters.isVocal}">
|
||||||
|
<a pfm-eat-click href="#" ng-click="setFilter('isVocal', item);">{{item.title}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="dropdown">
|
||||||
|
<a class="dropdown-toggle btn">
|
||||||
|
Order: <strong>{{query.filters.sort.title}}</strong>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li ng-repeat="filter in filters.sort.values" ng-class="{selected: filter == query.filters.sort}">
|
||||||
|
<a pfm-eat-click href="#" ng-click="setFilter('sort', filter)">{{filter.title}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="pagination" ng-show="totalPages > 0">
|
||||||
|
<ul>
|
||||||
|
<li ng-class="{disabled: !prevPage}"><a href="#" ng-click="gotoPage(prevPage);" pfm-eat-click>Prev</a></li>
|
||||||
|
<li ng-repeat="page in pages" ng-class="{active: page == currentPage}">
|
||||||
|
<a href="#" ng-click="gotoPage(page);" pfm-eat-click>{{page}}</a>
|
||||||
|
</li>
|
||||||
|
<li ng-class="{disabled: !nextPage}"><a href="#" ng-click="gotoPage(nextPage);" pfm-eat-click>Next</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ui-view></ui-view>
|