From 065b3a66474e67df035b6397e669b74ef3ed89ed Mon Sep 17 00:00:00 2001 From: heyarne Date: Sun, 10 Mar 2019 12:09:17 +0100 Subject: [PATCH] Remove current song --- karma.conf.js | 7 ++++- src/cljs/airsonic_ui/audio/core.cljs | 3 +- src/cljs/airsonic_ui/audio/playlist.cljs | 14 +++++++-- .../components/audio_player/events.cljs | 8 +++++ .../components/current_queue/views.cljs | 7 +++-- .../cljs/airsonic_ui/audio/playlist_test.cljs | 30 +++++++++++++++++++ .../components/audio_player/events_test.cljs | 10 +++++++ 7 files changed, 72 insertions(+), 7 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index 237f2b7..3cdfa96 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,7 +1,12 @@ module.exports = function (config) { const configuration = { browsers: ['ChromeHeadless'], - autoWatchBatchDelay: 1000, + // The tests are sometimes run before the tests were completely written + // to disc; this is a known problem unfortunately. This is a hack to at + // least keep the browsers connected so the tests are compiled and run + // again even if a developer isn't aware of this + autoWatchBatchDelay: 100, + browserNoActivityTimeout: 60 * 1000 * 10, // The directory where the output file lives basePath: 'public/test', // The file itself diff --git a/src/cljs/airsonic_ui/audio/core.cljs b/src/cljs/airsonic_ui/audio/core.cljs index d9f2139..132bc2e 100644 --- a/src/cljs/airsonic_ui/audio/core.cljs +++ b/src/cljs/airsonic_ui/audio/core.cljs @@ -53,7 +53,8 @@ (fn [_] (when-let [audio @audio] (.pause audio) - (set! (.-currentTime audio) 0)))) + (set! (.-currentTime audio) 0) + (set! (.-src audio) "")))) (rf/reg-fx :audio/toggle-play-pause diff --git a/src/cljs/airsonic_ui/audio/playlist.cljs b/src/cljs/airsonic_ui/audio/playlist.cljs index f51b34c..f86ac6e 100644 --- a/src/cljs/airsonic_ui/audio/playlist.cljs +++ b/src/cljs/airsonic_ui/audio/playlist.cljs @@ -22,7 +22,9 @@ (enqueue-next [this song]) (move-song [this from-idx to-idx] - "Allows you to move a song in a playlist")) + "Allows you to move a song in a playlist") + (remove-song [this song-idx] + "Removes a song from the playlist")) ;; helpers to manage creating playlists @@ -142,7 +144,15 @@ (= from-idx current-idx) (assoc result :current-idx to-idx) (<= to-idx current-idx from-idx) (update result :current-idx inc) (>= to-idx current-idx from-idx) (update result :current-idx dec) - :else result)))) + :else result))) + + (remove-song [this song-idx] + (cond-> (update this :items #(let [n-items (count %)] + (-> (reduce (fn [items idx] + (assoc items idx (get items (inc idx)))) + % (range song-idx n-items)) + (dissoc (dec n-items))))) + (= song-idx current-idx) (assoc :current-idx -1)))) ;; constructor wrapper diff --git a/src/cljs/airsonic_ui/components/audio_player/events.cljs b/src/cljs/airsonic_ui/components/audio_player/events.cljs index 4ce845f..fc8d87b 100644 --- a/src/cljs/airsonic_ui/components/audio_player/events.cljs +++ b/src/cljs/airsonic_ui/components/audio_player/events.cljs @@ -66,6 +66,14 @@ (fn [_ _] {:audio/toggle-play-pause nil})) +(defn remove-song [{:keys [db]} [_ song-idx]] + (let [song-removed (update-in db [:audio :current-playlist] #(playlist/remove-song % song-idx))] + (cond-> {:db song-removed} + (nil? (playlist/current-song (get-in song-removed [:audio :current-playlist]))) + (assoc :audio/stop nil)))) + +(rf/reg-event-fx :audio-player/remove-song remove-song) + (defn audio-update "Reacts to audio events fired by the HTML5 audio player and plays the next track if necessary." diff --git a/src/cljs/airsonic_ui/components/current_queue/views.cljs b/src/cljs/airsonic_ui/components/current_queue/views.cljs index 5ee6cee..27c15dc 100644 --- a/src/cljs/airsonic_ui/components/current_queue/views.cljs +++ b/src/cljs/airsonic_ui/components/current_queue/views.cljs @@ -21,10 +21,10 @@ (r/as-element [:span.is-size-7.has-text-grey-lighter [icon :elevator]])))) -(defn song-actions [] +(defn song-actions [{:keys [song idx]}] ;; TODO: Implement both of these [dropdown {:items [{:label "Remove from queue" - :event []} + :event [:audio-player/remove-song idx]} {:label "Go to source" :event []}]}]) @@ -62,7 +62,8 @@ [:td.song-artist [artist-link song]] [:td.song-title [song-link song idx]] [:td.song-duration (helpers/format-duration (:duration song) :brief? true)] - [:td.song-actions.is-narrow [song-actions]]]) + [:td.song-actions.is-narrow [song-actions {:song song + :idx idx}]]]) :on-sort-end (fn [{:keys [old-idx new-idx]}] diff --git a/test/cljs/airsonic_ui/audio/playlist_test.cljs b/test/cljs/airsonic_ui/audio/playlist_test.cljs index 58ae6e6..c640d63 100644 --- a/test/cljs/airsonic_ui/audio/playlist_test.cljs +++ b/test/cljs/airsonic_ui/audio/playlist_test.cljs @@ -309,3 +309,33 @@ (is (= 3 (-> (playlist/set-current-song playlist 3) (playlist/move-song 4 7) :current-idx)))))))) + +(deftest remove-song + (with-redefs [shuffle identity] + (testing "Should remove a single song from the playlist" + (doseq [playback-mode [:linear :shuffled] + repeat-mode [:repeat-none :repeat-all :repeat-single]] + (let [n-songs 10 + queue (song-queue n-songs) + playlist (playlist/->playlist queue :repeat-mode repeat-mode :playback-mode playback-mode) + first-removed (playlist/remove-song playlist 0) + middle-removed (playlist/remove-song playlist 5) + last-removed (playlist/remove-song playlist 9) + song-not-in-list? (fn [song playlist] + (every? #(not (same-song? % song)) + (vals (:items playlist))))] + (is (= 9 (count first-removed) (count middle-removed) (count last-removed))) + (is (song-not-in-list? (first queue) first-removed)) + (is (same-song? (second queue) (get (:items first-removed) 0))) + (is (song-not-in-list? (nth queue 5) middle-removed)) + (is (same-song? (nth queue 6) (get (:items middle-removed) 5))) + (is (song-not-in-list? (last queue) last-removed))))) + (testing "Should pause if the currently playing song is removed" + (doseq [playback-mode [:linear :shuffled] + repeat-mode [:repeat-none :repeat-all :repeat-single]] + (let [n-songs 10 + queue (song-queue n-songs)] + (is (nil? (-> (playlist/->playlist queue :repeat-mode repeat-mode :playback-mode playback-mode) + (playlist/set-current-song 5) + (playlist/remove-song 5) + (playlist/current-song))))))))) diff --git a/test/cljs/airsonic_ui/components/audio_player/events_test.cljs b/test/cljs/airsonic_ui/components/audio_player/events_test.cljs index 9ca8e9f..a4e0e20 100644 --- a/test/cljs/airsonic_ui/components/audio_player/events_test.cljs +++ b/test/cljs/airsonic_ui/components/audio_player/events_test.cljs @@ -27,3 +27,13 @@ (:current-idx))) (str "for playback-mode " playback-mode " and repeat-mode " repeat-mode)) (is (contains? effects :audio/play)))))) + +(deftest removing-currently-playing-song + (testing "Should stop all audio when removing the currently playing song" + (doseq [playback-mode [:linear :shuffled] + repeat-mode [:repeat-none :repeat-single :repeat-all]] + (let [n-songs 100 + fixture {:db {:credentials fixtures/credentials + :audio {:current-playlist (playlist/->playlist (song-queue n-songs) :playback-mode playback-mode :repeat-mode repeat-mode)}}}] + (is (contains? (events/remove-song fixture [:audio/remove-song 0]) :audio/stop)) + (is (not (contains? (events/remove-song fixture [:audio/remove-song 99]) :audio/stop)))))))