A music player that connects to your cloud/distributed storage.
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add tracks context menu

+319 -12
+91 -2
src/Applications/UI.elm
··· 8 8 import Color 9 9 import Color.Ext as Color 10 10 import Common exposing (Switch(..)) 11 + import Conditional exposing (..) 12 + import ContextMenu exposing (ContextMenu) 11 13 import Css exposing (url) 12 14 import Css.Global 15 + import Css.Transitions 13 16 import Dict.Ext as Dict 14 17 import File 15 18 import File.Download 16 19 import File.Select 17 20 import Html.Events.Extra.Pointer as Pointer 18 21 import Html.Styled as Html exposing (Html, div, section, text, toUnstyled) 19 - import Html.Styled.Attributes exposing (id, style) 22 + import Html.Styled.Attributes exposing (css, id, style) 23 + import Html.Styled.Events exposing (onClick) 20 24 import Html.Styled.Lazy as Lazy 21 25 import Json.Decode 22 26 import Json.Encode 27 + import Maybe.Extra as Maybe 23 28 import Notifications 24 29 import Process 25 30 import Replying as N5 exposing (do, return) ··· 33 38 import UI.Authentication as Authentication 34 39 import UI.Backdrop as Backdrop 35 40 import UI.Console 41 + import UI.ContextMenu 36 42 import UI.Core as Core exposing (Flags, Model, Msg(..)) 37 43 import UI.Equalizer as Equalizer 38 44 import UI.Kit ··· 50 56 import UI.Sources.Page 51 57 import UI.Svg.Elements 52 58 import UI.Tracks as Tracks 59 + import UI.Tracks.ContextMenu as Tracks 53 60 import UI.Tracks.Core as Tracks 54 61 import UI.UserData as UserData 55 62 import Url exposing (Url) ··· 80 87 ( ----------------------------------------- 81 88 -- Initial model 82 89 ----------------------------------------- 83 - { currentTime = Time.millisToPosix flags.initialTime 90 + { contextMenu = Nothing 91 + , currentTime = Time.millisToPosix flags.initialTime 84 92 , isAuthenticated = False 85 93 , isLoading = True 86 94 , navKey = key ··· 373 381 model 374 382 375 383 ----------------------------------------- 384 + -- Context Menu 385 + ----------------------------------------- 386 + Core.HideContextMenu -> 387 + ( { model | contextMenu = Nothing } 388 + , Cmd.none 389 + ) 390 + 391 + Core.ShowTracksContextMenu coordinates tracks -> 392 + ( { model | contextMenu = Just (Tracks.trackMenu tracks coordinates) } 393 + , Cmd.none 394 + ) 395 + 396 + ----------------------------------------- 376 397 -- Import / Export 377 398 ----------------------------------------- 378 399 Core.Export -> ··· 541 562 Reply.SaveTracks -> 542 563 Core.SaveTracks 543 564 565 + Reply.ShowTracksContextMenu coordinates tracks -> 566 + Core.ShowTracksContextMenu coordinates tracks 567 + 544 568 Reply.ToggleLoadingScreen state -> 545 569 Core.ToggleLoadingScreen state 546 570 ··· 683 707 |> Html.map BackdropMsg 684 708 685 709 ----------------------------------------- 710 + -- Context Menu 711 + ----------------------------------------- 712 + , model.contextMenu 713 + |> Lazy.lazy UI.ContextMenu.view 714 + 715 + ----------------------------------------- 686 716 -- Notifications 687 717 ----------------------------------------- 688 718 , model.notifications 689 719 |> Lazy.lazy UI.Notifications.view 690 720 691 721 ----------------------------------------- 722 + -- Overlay 723 + ----------------------------------------- 724 + , model.contextMenu 725 + |> Lazy.lazy overlay 726 + 727 + ----------------------------------------- 692 728 -- Content 693 729 ----------------------------------------- 694 730 , content ··· 783 819 Html.map never UI.Svg.Elements.loading 784 820 785 821 822 + overlay : Maybe (ContextMenu Msg) -> Html Msg 823 + overlay maybeContextMenu = 824 + let 825 + isVisible = 826 + Maybe.isJust maybeContextMenu 827 + in 828 + brick 829 + [ css (overlayStyles { clickable = isVisible }) 830 + 831 + -- 832 + , ifThenElse isVisible (onClick HideContextMenu) (onClick Bypass) 833 + ] 834 + [ T.absolute 835 + , T.absolute__fill 836 + , T.z_999 837 + 838 + -- 839 + , ifThenElse isVisible T.o_100 T.o_0 840 + ] 841 + [] 842 + 843 + 786 844 787 845 -- 🖼 ░░ GLOBAL 788 846 ··· 812 870 , Css.Global.selector ":-ms-input-placeholder" placeholderStyles 813 871 , Css.Global.selector ":-moz-placeholder" placeholderStyles 814 872 , Css.Global.selector "::placeholder" placeholderStyles 873 + 874 + ----------------------------------------- 875 + -- Bits & Pieces 876 + ----------------------------------------- 877 + , Css.Global.selector ".lh-0" [ Css.lineHeight Css.zero ] 815 878 ] 816 879 817 880 ··· 820 883 [ Css.color (Css.rgb 0 0 0) 821 884 , Css.opacity (Css.num 0.2) 822 885 ] 886 + 887 + 888 + 889 + -- 🖼 ░░ OTHER 890 + 891 + 892 + overlayStyles : { clickable : Bool } -> List Css.Style 893 + overlayStyles { clickable } = 894 + [ Css.backgroundColor (Css.rgba 0 0 0 0.25) 895 + 896 + -- 897 + , ifThenElse 898 + clickable 899 + (Css.pointerEvents Css.auto) 900 + (Css.pointerEvents Css.none) 901 + 902 + -- 903 + , ifThenElse 904 + clickable 905 + (Css.cursor Css.pointer) 906 + (Css.cursor Css.inherit) 907 + 908 + -- 909 + , Css.Transitions.transition 910 + [ Css.Transitions.opacity3 1000 0 Css.Transitions.ease ] 911 + ]
+97
src/Applications/UI/ContextMenu.elm
··· 1 + module UI.ContextMenu exposing (view) 2 + 3 + import Chunky exposing (..) 4 + import Classes as C 5 + import Color exposing (Color) 6 + import Conditional exposing (..) 7 + import ContextMenu exposing (..) 8 + import Coordinates exposing (Coordinates) 9 + import Css 10 + import Html.Styled exposing (Html, fromUnstyled, text) 11 + import Html.Styled.Attributes exposing (css) 12 + import Html.Styled.Events exposing (onClick) 13 + import Json.Decode 14 + import Svg exposing (Svg) 15 + import Tachyons.Classes as T 16 + import UI.Core 17 + import UI.Kit 18 + 19 + 20 + 21 + -- 🗺 22 + 23 + 24 + view : Maybe (ContextMenu UI.Core.Msg) -> Html UI.Core.Msg 25 + view m = 26 + case m of 27 + Just (ContextMenu items coordinates) -> 28 + brick 29 + [ css (menuStyles coordinates) 30 + 31 + -- 32 + , Html.Styled.Events.custom 33 + "click" 34 + (Json.Decode.succeed 35 + { message = UI.Core.HideContextMenu 36 + , stopPropagation = True 37 + , preventDefault = True 38 + } 39 + ) 40 + ] 41 + [ T.absolute 42 + , T.br2 43 + , T.bg_white 44 + , T.f7 45 + , T.overflow_hidden 46 + , T.z_9999 47 + ] 48 + (let 49 + lastIndex = 50 + List.length items - 1 51 + in 52 + List.indexedMap 53 + (itemView lastIndex) 54 + items 55 + ) 56 + 57 + Nothing -> 58 + nothing 59 + 60 + 61 + itemView : Int -> Int -> ( Color -> Int -> Svg msg, String, msg ) -> Html msg 62 + itemView lastIndex index ( icon, label, msg ) = 63 + let 64 + isLast = 65 + index == lastIndex 66 + in 67 + brick 68 + [ onClick msg ] 69 + [ T.b__near_white 70 + , T.pa3 71 + , T.pointer 72 + 73 + -- 74 + , ifThenElse isLast "" T.bb 75 + ] 76 + [ inline [ T.dib, C.lh_0, T.v_mid ] [ fromUnstyled (icon UI.Kit.colors.text 14) ] 77 + , inline [ T.dib, T.ml2, T.v_mid ] [ text label ] 78 + ] 79 + 80 + 81 + 82 + -- 🖼 83 + 84 + 85 + menuStyles : Coordinates -> List Css.Style 86 + menuStyles { x, y } = 87 + [ Css.fontSize (Css.px 12.5) 88 + , Css.left (Css.px x) 89 + , Css.minWidth (Css.px 170) 90 + , Css.transform (Css.translate2 (Css.pct -50) (Css.pct -50)) 91 + , Css.top (Css.px y) 92 + 93 + -- 94 + , Css.property 95 + "box-shadow" 96 + "0 1px 3px 0 rgba(0, 0, 0, 0.225), 0 3px 15px 0 rgba(0, 0, 0, 0.1)" 97 + ]
+10 -1
src/Applications/UI/Core.elm
··· 4 4 import Browser 5 5 import Browser.Navigation as Nav 6 6 import Common exposing (Switch(..)) 7 + import ContextMenu exposing (ContextMenu) 8 + import Coordinates exposing (Coordinates) 7 9 import File exposing (File) 8 10 import Json.Encode as Json 9 11 import Notifications exposing (..) 10 12 import Queue 11 13 import Time 14 + import Tracks exposing (IdentifiedTrack) 12 15 import UI.Authentication as Authentication 13 16 import UI.Backdrop as Backdrop 14 17 import UI.Equalizer as Equalizer ··· 34 37 35 38 36 39 type alias Model = 37 - { currentTime : Time.Posix 40 + { contextMenu : Maybe (ContextMenu Msg) 41 + , currentTime : Time.Posix 38 42 , isAuthenticated : Bool 39 43 , isLoading : Bool 40 44 , navKey : Nav.Key ··· 114 118 ----------------------------------------- 115 119 | ActiveQueueItemChanged (Maybe Queue.Item) 116 120 | FillQueue 121 + ----------------------------------------- 122 + -- Context Menu 123 + ----------------------------------------- 124 + | HideContextMenu 125 + | ShowTracksContextMenu Coordinates (List IdentifiedTrack) 117 126 ----------------------------------------- 118 127 -- Import / Export 119 128 -----------------------------------------
+2 -1
src/Applications/UI/Equalizer.elm
··· 3 3 import Chunky exposing (..) 4 4 import Color exposing (Color) 5 5 import Color.Ext as Color 6 + import Coordinates exposing (Coordinates) 6 7 import Css 7 8 import Equalizer exposing (..) 8 9 import Html.Events.Extra.Pointer as Pointer ··· 40 41 41 42 -- 42 43 , activeKnob : Maybe Knob 43 - , startCoordinates : { x : Float, y : Float } 44 + , startCoordinates : Coordinates 44 45 } 45 46 46 47
+3 -1
src/Applications/UI/Kit.elm
··· 603 603 604 604 vesselStyles : List Css.Style 605 605 vesselStyles = 606 - [ Css.maxWidth (px insulationWidth) ] 606 + [ Css.boxShadow5 (Css.px 0) (Css.px 2) (Css.px 4) (Css.px 0) (Css.rgba 0 0 0 0.2) 607 + , Css.maxWidth (px insulationWidth) 608 + ] 607 609 608 610 609 611
+2
src/Applications/UI/Reply.elm
··· 2 2 3 3 import Alien 4 4 import Common exposing (Switch(..)) 5 + import Coordinates exposing (Coordinates) 5 6 import Json.Decode as Json 6 7 import Notifications exposing (Notification) 7 8 import Queue ··· 32 33 | SaveSettings 33 34 | SaveSources 34 35 | SaveTracks 36 + | ShowTracksContextMenu Coordinates (List IdentifiedTrack) 35 37 | ToggleLoadingScreen Switch
+19 -1
src/Applications/UI/Tracks.elm
··· 74 74 R3.withNothing { model | infiniteList = infiniteList } 75 75 76 76 Reply replies -> 77 - ( model, Cmd.none, Just replies ) 77 + ( model 78 + , Cmd.none 79 + , Just replies 80 + ) 81 + 82 + ShowContextMenu track mouseEvent -> 83 + let 84 + ( x, y ) = 85 + mouseEvent.clientPos 86 + 87 + tracks = 88 + [ track ] 89 + in 90 + ( model 91 + , Cmd.none 92 + , Just [ ShowTracksContextMenu { x = x, y = y } tracks ] 93 + ) 78 94 79 95 ScrollToNowPlaying -> 80 96 case model.nowPlaying of ··· 491 507 "/sources/new" 492 508 UI.Kit.Normal 493 509 (inline 510 + [] 494 511 [ UI.Kit.inlineIcon Icons.add 495 512 , text "Add some music" 496 513 ] ··· 505 522 UI.Kit.Normal 506 523 (Reply [ InsertDemo ]) 507 524 (inline 525 + [] 508 526 [ UI.Kit.inlineIcon Icons.music_note 509 527 , text "Insert demo" 510 528 ]
+34
src/Applications/UI/Tracks/ContextMenu.elm
··· 1 + module UI.Tracks.ContextMenu exposing (trackMenu) 2 + 3 + import ContextMenu exposing (..) 4 + import Coordinates exposing (Coordinates) 5 + import Material.Icons.Action as Icons 6 + import Tracks exposing (IdentifiedTrack) 7 + import UI.Core exposing (Msg(..)) 8 + import UI.Queue.Core as Queue 9 + 10 + 11 + 12 + -- 🔱 13 + 14 + 15 + trackMenu : List IdentifiedTrack -> Coordinates -> ContextMenu Msg 16 + trackMenu tracks = 17 + ContextMenu (queueActions tracks) 18 + 19 + 20 + 21 + -- ACTIONS 22 + 23 + 24 + queueActions : List IdentifiedTrack -> ContextMenuItems Msg 25 + queueActions identifiedTracks = 26 + [ ( Icons.event_seat 27 + , "Play next" 28 + , QueueMsg (Queue.InjectFirst identifiedTracks) 29 + ) 30 + , ( Icons.event_seat 31 + , "Add to queue" 32 + , QueueMsg (Queue.InjectLast identifiedTracks) 33 + ) 34 + ]
+2
src/Applications/UI/Tracks/Core.elm
··· 1 1 module UI.Tracks.Core exposing (Model, Msg(..), Scene(..)) 2 2 3 + import Html.Events.Extra.Mouse as Mouse 3 4 import InfiniteList 4 5 import Json.Encode as Json 5 6 import Tracks exposing (..) ··· 37 38 | ScrollToNowPlaying 38 39 | SetEnabledSourceIds (List String) 39 40 | SetNowPlaying (Maybe IdentifiedTrack) 41 + | ShowContextMenu IdentifiedTrack Mouse.Event 40 42 | SortBy SortBy 41 43 | ToggleHideDuplicates 42 44 -----------------------------------------
+18 -1
src/Applications/UI/Tracks/Scene/List.elm
··· 8 8 import Css 9 9 import Html as UnstyledHtml 10 10 import Html.Attributes as UnstyledHtmlAttributes 11 + import Html.Events.Extra.Mouse as Mouse exposing (onWithOptions) 11 12 import Html.Styled as Html exposing (Html, text) 12 13 import Html.Styled.Attributes exposing (css, fromUnstyled, id) 13 14 import Html.Styled.Events exposing (onClick, onDoubleClick) ··· 343 344 slab 344 345 Html.li 345 346 [ css (rowStyles idx identifiers) 346 - , onDoubleClick (Reply [ UI.Reply.PlayTrack ( identifiers, track ) ]) 347 + 348 + -- Play 349 + ------- 350 + , [ UI.Reply.PlayTrack ( identifiers, track ) ] 351 + |> Reply 352 + |> onDoubleClick 353 + 354 + -- Context Menu 355 + --------------- 356 + , ( identifiers, track ) 357 + |> ShowContextMenu 358 + |> onWithOptions 359 + "contextmenu" 360 + { stopPropagation = True 361 + , preventDefault = True 362 + } 363 + |> Html.Styled.Attributes.fromUnstyled 347 364 ] 348 365 [ T.dt_row 349 366
+5 -5
src/Library/Chunky.elm
··· 68 68 List.singleton >> chunk a 69 69 70 70 71 + inline : List String -> List (Html msg) -> Html msg 72 + inline = 73 + slab Html.span [] 74 + 75 + 71 76 72 77 -- 4 73 78 ··· 84 89 85 90 86 91 -- 5 87 - 88 - 89 - inline : List (Html msg) -> Html msg 90 - inline = 91 - Html.span [] 92 92 93 93 94 94 nothing : Html msg
+12
src/Library/Classes.elm
··· 1 + module Classes exposing (lh_0) 2 + 3 + {-| Some class names used for global css. 4 + 5 + This is an extension of Tachyons. 6 + 7 + -} 8 + 9 + 10 + lh_0 : String 11 + lh_0 = 12 + "lh-0"
+17
src/Library/ContextMenu.elm
··· 1 + module ContextMenu exposing (ContextMenu(..), ContextMenuItems) 2 + 3 + import Color exposing (Color) 4 + import Coordinates exposing (Coordinates) 5 + import Svg exposing (Svg) 6 + 7 + 8 + 9 + -- 🌳 10 + 11 + 12 + type ContextMenu msg 13 + = ContextMenu (ContextMenuItems msg) Coordinates 14 + 15 + 16 + type alias ContextMenuItems msg = 17 + List ( Color -> Int -> Svg msg, String, msg )
+7
src/Library/Coordinates.elm
··· 1 + module Coordinates exposing (Coordinates) 2 + 3 + -- 🌳 4 + 5 + 6 + type alias Coordinates = 7 + { x : Float, y : Float }