forked from
tokono.ma/diffuse
A music player that connects to your cloud/distributed storage.
1module UI.Playlists.State exposing (..)
2
3import Conditional exposing (ifThenElse)
4import Coordinates
5import Html.Events.Extra.Mouse as Mouse
6import List.Ext as List
7import List.Extra as List
8import Maybe.Extra as Maybe
9import Notifications
10import Playlists exposing (..)
11import Return exposing (andThen)
12import Tracks exposing (IdentifiedTrack)
13import Tracks.Collection
14import UI.Alfred.State as Alfred
15import UI.Common.State as Common
16import UI.Page as Page
17import UI.Playlists.Alfred
18import UI.Playlists.ContextMenu as Playlists
19import UI.Playlists.Page exposing (..)
20import UI.Tracks.State as Tracks
21import UI.Types exposing (..)
22import UI.User.State.Export as User
23
24
25
26-- 🔱
27
28
29activate : Playlist -> Manager
30activate playlist model =
31 model
32 |> select playlist
33 |> andThen (Common.changeUrlUsingPage Page.Index)
34
35
36addTracksToPlaylist : { collection : Bool, playlistName : String, tracks : List PlaylistTrackWithoutMetadata } -> Manager
37addTracksToPlaylist { collection, playlistName, tracks } model =
38 let
39 properPlaylistName =
40 String.trim playlistName
41
42 playlistIndex =
43 List.findIndex
44 (\p -> Maybe.isNothing p.autoGenerated && p.name == properPlaylistName)
45 model.playlists
46
47 ( tracksAlreadyInPlaylist, newTracks ) =
48 playlistIndex
49 |> Maybe.andThen
50 (\a ->
51 if collection then
52 Just a
53
54 else
55 Nothing
56 )
57 |> Maybe.andThen (\idx -> List.getAt idx model.playlists)
58 |> Maybe.map
59 (\p ->
60 List.foldl
61 (\track ( a, b, c ) ->
62 case
63 List.findIndex
64 (\x ->
65 track.title == x.title && track.album == x.album && track.artist == x.artist
66 )
67 c
68 of
69 Just idx ->
70 ( track :: a, b, List.removeAt idx c )
71
72 Nothing ->
73 ( a, track :: b, c )
74 )
75 ( [], [], p.tracks )
76 tracks
77 )
78 |> Maybe.map (\( a, b, _ ) -> ( a, b ))
79 |> Maybe.withDefault ( [], tracks )
80 |> Tuple.mapSecond
81 (List.map
82 (\track ->
83 let
84 newTrack : PlaylistTrack
85 newTrack =
86 { album = track.album
87 , artist = track.artist
88 , title = track.title
89
90 --
91 , insertedAt = model.currentTime
92 }
93 in
94 newTrack
95 )
96 )
97
98 newInventory =
99 case playlistIndex of
100 Just idx ->
101 List.updateAt
102 idx
103 (\p -> { p | tracks = p.tracks ++ newTracks })
104 model.playlists
105
106 Nothing ->
107 { autoGenerated = Nothing
108 , collection = collection
109 , name = properPlaylistName
110 , public = False
111 , tracks = newTracks
112 }
113 :: model.playlists
114
115 newModel =
116 { model
117 | playlists = newInventory
118 , lastModifiedPlaylist =
119 Just
120 { collection = collection
121 , name = properPlaylistName
122 }
123 }
124
125 subject =
126 ifThenElse collection "collection" "playlist"
127 in
128 case newTracks of
129 [] ->
130 if collection then
131 (case tracksAlreadyInPlaylist of
132 [ t ] ->
133 "__" ++ t.title ++ "__ was"
134
135 l ->
136 "__" ++ String.fromInt (List.length l) ++ " tracks__ were"
137 )
138 |> (\s -> s ++ " already added to the __" ++ properPlaylistName ++ "__ collection")
139 |> Notifications.casual
140 |> Common.showNotificationWithModel model
141
142 else
143 Return.singleton model
144
145 _ ->
146 (case newTracks of
147 [ t ] ->
148 "Added __" ++ t.title ++ "__"
149
150 l ->
151 "Added __" ++ String.fromInt (List.length l) ++ " tracks__"
152 )
153 |> (\s -> s ++ " to the __" ++ properPlaylistName ++ "__ " ++ subject)
154 |> Notifications.success
155 |> Common.showNotificationWithModel newModel
156 |> andThen User.savePlaylists
157
158
159assistWithAddingTracksToCollection : List IdentifiedTrack -> Manager
160assistWithAddingTracksToCollection tracks model =
161 model.playlists
162 |> List.filter (\p -> p.autoGenerated == Nothing && p.collection == True)
163 |> UI.Playlists.Alfred.create { collectionMode = True } tracks
164 |> (\a -> Alfred.assign a model)
165
166
167assistWithAddingTracksToPlaylist : List IdentifiedTrack -> Manager
168assistWithAddingTracksToPlaylist tracks model =
169 model.playlists
170 |> List.filter (\p -> p.autoGenerated == Nothing && p.collection == False)
171 |> UI.Playlists.Alfred.create { collectionMode = False } tracks
172 |> (\a -> Alfred.assign a model)
173
174
175assistWithSelectingPlaylist : Manager
176assistWithSelectingPlaylist model =
177 model.playlists
178 |> UI.Playlists.Alfred.select
179 |> (\a -> Alfred.assign a model)
180
181
182convertCollectionToPlaylist : { name : String } -> Manager
183convertCollectionToPlaylist { name } model =
184 case
185 List.findIndex
186 (\p -> Maybe.isNothing p.autoGenerated && p.name == name)
187 model.playlists
188 of
189 Just playlistIndex ->
190 model.playlists
191 |> List.updateAt
192 playlistIndex
193 (\p -> { p | collection = False })
194 |> (\newInventory ->
195 { model
196 | playlists = newInventory
197 , selectedPlaylist =
198 Maybe.map
199 (\p ->
200 if p.name == name then
201 { p | collection = False }
202
203 else
204 p
205 )
206 model.selectedPlaylist
207 }
208 )
209 |> Return.singleton
210 |> andThen User.savePlaylists
211
212 Nothing ->
213 Return.singleton model
214
215
216convertPlaylistToCollection : { name : String } -> Manager
217convertPlaylistToCollection { name } model =
218 case
219 List.findIndex
220 (\p -> Maybe.isNothing p.autoGenerated && p.name == name)
221 model.playlists
222 of
223 Just playlistIndex ->
224 model.playlists
225 |> List.updateAt
226 playlistIndex
227 (\p -> { p | collection = True })
228 |> (\newInventory ->
229 { model
230 | playlists = newInventory
231 , selectedPlaylist =
232 Maybe.map
233 (\p ->
234 if p.name == name then
235 { p | collection = True }
236
237 else
238 p
239 )
240 model.selectedPlaylist
241 }
242 )
243 |> Return.singleton
244 |> andThen User.savePlaylists
245
246 Nothing ->
247 Return.singleton model
248
249
250create : { collection : Bool } -> Manager
251create { collection } model =
252 case model.newPlaylistContext of
253 Just playlistName ->
254 let
255 alreadyExists =
256 List.find
257 (.name >> (==) playlistName)
258 (List.filterNot (.autoGenerated >> Maybe.isJust) model.playlists)
259
260 playlist =
261 { autoGenerated = Nothing
262 , collection = collection
263 , name = playlistName
264 , public = False
265 , tracks = []
266 }
267 in
268 case alreadyExists of
269 Just existingPlaylist ->
270 (if existingPlaylist.collection then
271 "There's already a collection using this name"
272
273 else
274 "There's already a playlist using this name"
275 )
276 |> Notifications.error
277 |> Common.showNotificationWithModel model
278
279 Nothing ->
280 { model
281 | lastModifiedPlaylist =
282 Just
283 { collection = playlist.collection
284 , name = playlist.name
285 }
286 , newPlaylistContext = Nothing
287 , playlists = playlist :: model.playlists
288 }
289 |> User.savePlaylists
290 |> andThen redirectToPlaylistIndexPage
291
292 Nothing ->
293 Return.singleton model
294
295
296createCollection : Manager
297createCollection =
298 create { collection = True }
299
300
301createPlaylist : Manager
302createPlaylist =
303 create { collection = False }
304
305
306deactivate : Manager
307deactivate =
308 deselect
309
310
311deselect : Manager
312deselect model =
313 { model | selectedPlaylist = Nothing }
314 |> Tracks.reviseCollection Tracks.Collection.arrange
315 |> andThen User.saveEnclosedUserData
316
317
318delete : { playlistName : String } -> Manager
319delete { playlistName } model =
320 let
321 selectedPlaylist =
322 Maybe.map
323 (\p -> ( p.autoGenerated, p.name ))
324 model.selectedPlaylist
325
326 ( selectedPlaylistChanged, newSelectedPlaylist ) =
327 if selectedPlaylist == Just ( Nothing, playlistName ) then
328 ( True, Nothing )
329
330 else
331 ( False, model.selectedPlaylist )
332 in
333 model.playlists
334 |> List.filter
335 (\p ->
336 if Maybe.isJust p.autoGenerated then
337 True
338
339 else
340 p.name /= playlistName
341 )
342 |> (\col ->
343 { model
344 | playlists = col
345 , selectedPlaylist = newSelectedPlaylist
346 }
347 )
348 |> (if selectedPlaylistChanged then
349 Tracks.reviseCollection Tracks.Collection.arrange
350
351 else
352 Return.singleton
353 )
354 |> andThen User.savePlaylists
355
356
357modify : Manager
358modify model =
359 case model.editPlaylistContext of
360 Just { oldName, newName } ->
361 let
362 properName =
363 String.trim newName
364
365 validName =
366 String.isEmpty properName == False
367
368 ( autoGenerated, notAutoGenerated ) =
369 List.partition (.autoGenerated >> Maybe.isJust) model.playlists
370
371 alreadyExists =
372 List.find
373 (.name >> (==) properName)
374 notAutoGenerated
375
376 newCollection =
377 List.map
378 (\p -> ifThenElse (p.name == oldName) { p | name = properName } p)
379 notAutoGenerated
380 in
381 case alreadyExists of
382 Just existingPlaylist ->
383 (if existingPlaylist.collection then
384 "There's already a collection using this name"
385
386 else
387 "There's already a playlist using this name"
388 )
389 |> Notifications.error
390 |> Common.showNotificationWithModel
391 { model | editPlaylistContext = Nothing }
392
393 Nothing ->
394 if validName then
395 { model
396 | editPlaylistContext = Nothing
397 , lastModifiedPlaylist =
398 case model.lastModifiedPlaylist of
399 Just l ->
400 if l.name == oldName then
401 Just { l | name = newName }
402
403 else
404 Just l
405
406 Nothing ->
407 Nothing
408 , playlists = newCollection ++ autoGenerated
409 }
410 |> User.savePlaylists
411 |> andThen redirectToPlaylistIndexPage
412
413 else
414 redirectToPlaylistIndexPage model
415
416 Nothing ->
417 redirectToPlaylistIndexPage model
418
419
420moveTrackInSelected : { to : Int } -> Manager
421moveTrackInSelected { to } model =
422 case model.selectedPlaylist of
423 Just playlist ->
424 let
425 moveParams =
426 { from = Maybe.withDefault 0 (List.head model.selectedTrackIndexes)
427 , to = to
428 , amount = List.length model.selectedTrackIndexes
429 }
430
431 updatedPlaylist =
432 { playlist | tracks = List.move moveParams playlist.tracks }
433
434 updatedPlaylistCollection =
435 List.map
436 (\p ->
437 ifThenElse
438 (p.autoGenerated == Nothing && p.name == updatedPlaylist.name)
439 updatedPlaylist
440 p
441 )
442 model.playlists
443 in
444 { model
445 | playlists = updatedPlaylistCollection
446 , selectedPlaylist = Just updatedPlaylist
447 }
448 |> Tracks.reviseCollection Tracks.Collection.arrange
449 |> andThen User.savePlaylists
450
451 Nothing ->
452 Return.singleton model
453
454
455removeTracks : Playlist -> List IdentifiedTrack -> Manager
456removeTracks playlist tracks model =
457 let
458 updatedPlaylist =
459 Tracks.removeFromPlaylist tracks playlist
460 in
461 model.playlists
462 |> List.map
463 (\p ->
464 if p.name == playlist.name then
465 updatedPlaylist
466
467 else
468 p
469 )
470 |> (\c -> { model | playlists = c })
471 |> select updatedPlaylist
472 |> andThen User.savePlaylists
473
474
475select : Playlist -> Manager
476select playlist model =
477 { model | page = Page.Index, selectedPlaylist = Just playlist }
478 |> Tracks.reviseCollection Tracks.Collection.arrange
479 |> andThen User.saveEnclosedUserData
480
481
482setCreationContext : String -> Manager
483setCreationContext playlistName model =
484 Return.singleton { model | newPlaylistContext = Just playlistName }
485
486
487setModificationContext : String -> String -> Manager
488setModificationContext oldName newName model =
489 let
490 context =
491 { oldName = oldName
492 , newName = newName
493 }
494 in
495 Return.singleton { model | editPlaylistContext = Just context }
496
497
498showListMenu : Playlist -> Mouse.Event -> Manager
499showListMenu playlist mouseEvent model =
500 let
501 coordinates =
502 Coordinates.fromTuple mouseEvent.clientPos
503
504 contextMenu =
505 Playlists.listMenu
506 playlist
507 model.tracks.identified
508 model.confirmation
509 coordinates
510 in
511 Return.singleton { model | contextMenu = Just contextMenu }
512
513
514toggleVisibility : Playlist -> Manager
515toggleVisibility playlist model =
516 let
517 updatedPlaylist =
518 { playlist | public = not playlist.public }
519 in
520 model.playlists
521 |> List.map
522 (\p ->
523 if p.name == playlist.name then
524 updatedPlaylist
525
526 else
527 p
528 )
529 |> (\c -> { model | playlists = c })
530 |> User.savePlaylists
531
532
533
534-- ㊙️
535
536
537redirectToPlaylistIndexPage : Manager
538redirectToPlaylistIndexPage =
539 Common.changeUrlUsingPage (Page.Playlists Index)