diff --git a/app/controllers/Api/Web/ImagesController.php b/app/controllers/Api/Web/ImagesController.php new file mode 100644 index 00000000..acdd5554 --- /dev/null +++ b/app/controllers/Api/Web/ImagesController.php @@ -0,0 +1,29 @@ +id); + $images = []; + foreach ($query->get() as $image) { + $images[] = [ + 'id' => $image->id, + 'url' => $image->getUrl(Image::SMALL), + 'filename' => $image->filename + ]; + } + + return Response::json($images, 200); + } + } \ No newline at end of file diff --git a/app/controllers/Api/Web/TracksController.php b/app/controllers/Api/Web/TracksController.php index 3f81b320..05525eae 100644 --- a/app/controllers/Api/Web/TracksController.php +++ b/app/controllers/Api/Web/TracksController.php @@ -92,7 +92,8 @@ 'description' => $track->description, 'lyrics' => $track->lyrics, 'released_at' => $track->released_at, - 'cover_url' => $track->hasCover() ? $track->getCoverUrl(Image::NORMAL) : null + 'cover_url' => $track->hasCover() ? $track->getCoverUrl(Image::NORMAL) : null, + 'real_cover_url' => $track->getCoverUrl(Image::NORMAL) ], 200); } diff --git a/app/controllers/ImagesController.php b/app/controllers/ImagesController.php index 0a45053d..9ed78b02 100644 --- a/app/controllers/ImagesController.php +++ b/app/controllers/ImagesController.php @@ -15,6 +15,17 @@ if (!$image) App::abort(404); - return File::inline($image->getFile($coverType['id']), $image->mime, $image->filename); + $filename = $image->getFile($coverType['id']); + $lastModified = filemtime($filename); + + if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $lastModified == $_SERVER['HTTP_IF_MODIFIED_SINCE']) { + header('HTTP/1.0 304 Not Modified'); + exit(); + } + + header('Last-Modified: ' . $lastModified); + header('Cache-Control: max-age=' . (60 * 60 * 24 * 7)); + + return File::inline($filename, $image->mime, $image->filename); } } \ No newline at end of file diff --git a/app/models/Commands/EditTrackCommand.php b/app/models/Commands/EditTrackCommand.php index 8979c19a..f14419cd 100644 --- a/app/models/Commands/EditTrackCommand.php +++ b/app/models/Commands/EditTrackCommand.php @@ -42,7 +42,8 @@ 'genre_id' => 'required|exists:genres,id', 'cover' => 'image|mimes:png|min_width:350|min_height:350', 'track_type_id' => 'required|exists:track_types,id', - 'songs' => 'required_when:track_type,2|exists:songs,id' + 'songs' => 'required_when:track_type,2|exists:songs,id', + 'cover_id' => 'exists:images,id' ]); if ($validator->fails()) @@ -64,10 +65,13 @@ $track->published_at = new \DateTime(); } - if (isset($this->_input['cover'])) { + if (isset($this->_input['cover_id'])) { + $track->cover_id = $this->_input['cover_id']; + } + else if (isset($this->_input['cover'])) { $cover = $this->_input['cover']; $track->cover_id = Image::Upload($cover, Auth::user())->id; - } else + } else if ($this->_input['remove_cover'] == 'true') $track->cover_id = null; $track->save(); diff --git a/app/routes.php b/app/routes.php index 7734eb1d..e96b4296 100644 --- a/app/routes.php +++ b/app/routes.php @@ -39,6 +39,7 @@ }); Route::group(['before' => 'auth'], function() { + Route::get('/images/owned', 'Api\Web\ImagesController@getOwned'); Route::get('/tracks/owned', 'Api\Web\TracksController@getOwned'); Route::get('/tracks/edit/{id}', 'Api\Web\TracksController@getEdit'); }); diff --git a/public/scripts/app/controllers/account-content-tracks.coffee b/public/scripts/app/controllers/account-content-tracks.coffee index 3b489188..b246cd7a 100644 --- a/public/scripts/app/controllers/account-content-tracks.coffee +++ b/public/scripts/app/controllers/account-content-tracks.coffee @@ -8,6 +8,7 @@ angular.module('ponyfm').controller "account-content-tracks", [ $scope.isCoverLoaded = false $scope.selectedTrack = null $scope.isDirty = false + $scope.isSaving = false $scope.taxonomies = trackTypes: taxonomies.trackTypes licenses: taxonomies.licenses @@ -18,11 +19,22 @@ angular.module('ponyfm').controller "account-content-tracks", [ $scope.previewCover = () -> return if !$scope.edit.cover - lightbox.openDataUrl $('#coverPreview').attr 'src' + if typeof($scope.edit.cover) == 'object' + lightbox.openDataUrl $('#coverPreview').attr 'src' + else + lightbox.openImageUrl $scope.edit.cover + + $scope.selectGalleryImage = (image) -> + $('#coverPreview').attr 'src', image.url + $scope.edit.cover_id = image.id + $scope.edit.remove_cover = false + $scope.edit.cover = null + $scope.isDirty = true $scope.updateTrack = (track) -> xhr = new XMLHttpRequest() xhr.onload = -> $scope.$apply -> + $scope.isSaving = false if xhr.status != 200 errors = if xhr.getResponseHeader('content-type') == 'application/json' @@ -40,12 +52,15 @@ angular.module('ponyfm').controller "account-content-tracks", [ formData = new FormData(); _.each $scope.edit, (value, name) -> if name == 'cover' - formData.append name, value, value.name + return if value == null + if typeof(value) == 'object' + formData.append name, value, value.name else formData.append name, value xhr.open 'POST', '/api/web/tracks/edit/' + $scope.edit.id, true xhr.setRequestHeader 'X-Token', pfm.token + $scope.isSaving = true xhr.send formData $scope.uploadTrackCover = () -> @@ -53,6 +68,7 @@ angular.module('ponyfm').controller "account-content-tracks", [ $scope.setCoverImage = (input) -> $scope.$apply -> + delete $scope.edit.cover_id previewElement = $('#coverPreview')[0] file = input.files[0] @@ -71,6 +87,9 @@ angular.module('ponyfm').controller "account-content-tracks", [ $scope.clearTrackCover = () -> $scope.isCoverLoaded = false + $scope.isDirty = true + $scope.edit.remove_cover = true + delete $scope.edit.cover_id delete $scope.edit.cover $scope.filters = @@ -162,6 +181,20 @@ angular.module('ponyfm').controller "account-content-tracks", [ genre_id: track.genre_id track_type_id: track.track_type_id released_at: if track.released_at then track.released_at.date else '' + remove_cover: false + cover: track.cover_url + + trackDbItem = tracksDb[t.id] + trackDbItem.title = track.title + trackDbItem.is_explicit = track.is_explicit + trackDbItem.is_vocal = track.is_vocal + trackDbItem.genre_id = track.genre_id + trackDbItem.is_published = track.is_published + trackDbItem.cover_url = track.real_cover_url + + if track.cover_url + $('#coverPreview').attr 'src', track.cover_url + $scope.isCoverLoaded = true $scope.touchModel = -> $scope.isDirty = true diff --git a/public/scripts/app/controllers/account-image-select.coffee b/public/scripts/app/controllers/account-image-select.coffee new file mode 100644 index 00000000..6ec95c22 --- /dev/null +++ b/public/scripts/app/controllers/account-image-select.coffee @@ -0,0 +1,10 @@ +angular.module('ponyfm').controller "account-image-select", [ + '$scope' + ($scope) -> + $scope.images = [] + $scope.isLoading = true + + $.getJSON('/api/web/images/owned').done (images) -> $scope.$apply -> + $scope.images = images + $scope.isLoading = false +] \ No newline at end of file diff --git a/public/scripts/app/directives/popup.coffee b/public/scripts/app/directives/popup.coffee new file mode 100644 index 00000000..119c23df --- /dev/null +++ b/public/scripts/app/directives/popup.coffee @@ -0,0 +1,36 @@ +angular.module('ponyfm').directive 'pfmPopup', () -> + (scope, element, attrs) -> + $popup = $ '#' + attrs.pfmPopup + $element = $ element + $popup.remove() + open = false + + documentClickHandler = () -> + return if !open + $popup.removeClass 'open' + open = false + + $(document.body).bind 'click', documentClickHandler + + $(document.body).append $popup + + $(element).click (e) -> + e.preventDefault() + e.stopPropagation() + + if open + open = false + $popup.removeClass 'open' + return + + position = $element.offset() + $popup.addClass 'open' + $popup.css + top: position.top + $element.height() + 10 + left: position.left + + open = true + + scope.$on '$destroy', () -> + $(document.body).unbind 'click', documentClickHandler + $popup.remove() \ No newline at end of file diff --git a/public/scripts/app/services/lightbox.coffee b/public/scripts/app/services/lightbox.coffee index 94e8293d..b4598e73 100644 --- a/public/scripts/app/services/lightbox.coffee +++ b/public/scripts/app/services/lightbox.coffee @@ -4,5 +4,10 @@ angular.module('ponyfm').factory('lightbox', [ $.colorbox html: '' transition: 'none' + + openImageUrl: (src) -> + $.colorbox + href: src + transition: 'none' ]) diff --git a/public/styles/account-tracks.less b/public/styles/account-tracks.less index e5280324..0ac6f3b9 100644 --- a/public/styles/account-tracks.less +++ b/public/styles/account-tracks.less @@ -15,6 +15,38 @@ } } +.image-selector { + width: 500px; + max-height: 300px; + overflow-y: auto; + + ul { + list-style: none; + padding: 0px; + margin: 0px; + + li { + margin: 0px; + float: left; + width: 20%; + cursor: pointer; + + img { + .transition(all 400ms); + display: block; + width: 100px; + height: 100px; + } + + &:hover { + img { + opacity: .8; + } + } + } + } +} + .account-tracks-listing { overflow-y: auto; margin: 0px; diff --git a/public/styles/components.less b/public/styles/components.less index ce02027a..28a40340 100644 --- a/public/styles/components.less +++ b/public/styles/components.less @@ -80,4 +80,16 @@ html body { } } } +} + +.pfm-popup { + .box-shadow(0 5px 10px rgba(0, 0, 0, 0.2)); + position: absolute; + display: none; + border: 1px solid rgba(0, 0, 0, 0.2); + background: #fff; + + &.open { + display: block; + } } \ No newline at end of file diff --git a/public/templates/account/content/tracks.html b/public/templates/account/content/tracks.html index 7333f5f8..f9f2aece 100644 --- a/public/templates/account/content/tracks.html +++ b/public/templates/account/content/tracks.html @@ -58,42 +58,48 @@
- +
{{errors.title}}
- +
{{errors.description}}
- - + +
{{errors.lyrics}}
-
{{errors.track_type_id}}
-
{{errors.genre_id}}
@@ -109,24 +115,32 @@

+
+
    +
  • + +
  • +
{{errors.cover}}
- +
{{errors.released_at}}
- +
- +
@@ -136,7 +150,7 @@
{{license.title}}

{{license.description}}

- + Select Selected