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

Configure Feed

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

Refactor Alfred state

+495 -432
+15 -18
src/Applications/UI.elm
··· 1 1 module UI exposing (main) 2 2 3 - import Alfred 4 3 import Alien 5 4 import Browser 6 5 import Browser.Events ··· 25 24 import Time 26 25 import Tracks 27 26 import Tracks.Encoding as Tracks 28 - import UI.Alfred as Alfred 27 + import UI.Alfred.State as Alfred 29 28 import UI.Audio.State as Audio 30 29 import UI.Audio.Types as Audio 31 30 import UI.Authentication as Authentication ··· 39 38 import UI.Page as Page 40 39 import UI.Playlists as Playlists 41 40 import UI.Playlists.ContextMenu as Playlists 41 + import UI.Playlists.State as Playlists 42 42 import UI.Ports as Ports 43 43 import UI.Queue as Queue 44 44 import UI.Queue.ContextMenu as Queue ··· 90 90 page = 91 91 Maybe.withDefault Page.Index maybePage 92 92 in 93 - { contextMenu = Nothing 93 + { alfred = Nothing 94 + , contextMenu = Nothing 94 95 , confirmation = Nothing 95 96 , currentTime = Time.millisToPosix flags.initialTime 96 97 , darkMode = flags.darkMode ··· 112 113 113 114 -- Children 114 115 ----------- 115 - , alfred = Alfred.initialModel 116 116 , authentication = Authentication.initialModel url 117 117 , backdrop = Backdrop.initialModel 118 118 , equalizer = Equalizer.initialModel ··· 160 160 Reply.translate reply 161 161 162 162 -- 163 + Alfred a -> 164 + Alfred.update a 165 + 163 166 Audio a -> 164 167 Audio.update a 165 168 ··· 181 184 ----------------------------------------- 182 185 -- Children 183 186 ----------------------------------------- 184 - AlfredMsg sub -> 185 - \model -> 186 - Return3.wieldNested 187 - Reply.translate 188 - { mapCmd = AlfredMsg 189 - , mapModel = \child -> { model | alfred = child } 190 - , update = Alfred.update 191 - } 192 - { model = model.alfred 193 - , msg = sub 194 - } 195 - 196 187 AuthenticationMsg sub -> 197 188 \model -> 198 189 Return3.wieldNested ··· 320 311 Interface.toggleLoadingScreen a 321 312 322 313 ----------------------------------------- 314 + -- Playlists 315 + ----------------------------------------- 316 + UI.AddTracksToPlaylist a -> 317 + Playlists.addTracksToPlaylist a 318 + 319 + ----------------------------------------- 323 320 -- Routing 324 321 ----------------------------------------- 325 322 ChangeUrlUsingPage a -> ··· 393 390 ----------------------------------------- 394 391 -- Alfred 395 392 ----------------------------------------- 396 - , case model.alfred.instance of 393 + , case model.alfred of 397 394 Just _ -> 398 - Sub.map AlfredMsg (Alfred.subscriptions model.alfred) 395 + Alfred.subscriptions 399 396 400 397 Nothing -> 401 398 Sub.none
-304
src/Applications/UI/Alfred.elm
··· 1 - module UI.Alfred exposing (Model, Msg(..), initialModel, subscriptions, update, view) 2 - 3 - import Alfred exposing (Alfred) 4 - import Browser.Dom as Dom 5 - import Chunky exposing (..) 6 - import Css.Classes as C 7 - import Html exposing (Html, text) 8 - import Html.Attributes exposing (autofocus, id, placeholder, type_) 9 - import Html.Events exposing (onInput) 10 - import Html.Ext exposing (onTapPreventDefault) 11 - import Json.Decode 12 - import Keyboard 13 - import List.Extra as List 14 - import Material.Icons as Icons 15 - import Material.Icons.Types exposing (Coloring(..)) 16 - import Return3 exposing (..) 17 - import String.Ext as String 18 - import Task 19 - import UI.Reply exposing (Reply) 20 - 21 - 22 - 23 - -- 🌳 24 - 25 - 26 - type alias Model = 27 - { instance : Maybe (Alfred Reply) } 28 - 29 - 30 - initialModel : Model 31 - initialModel = 32 - { instance = Nothing } 33 - 34 - 35 - 36 - -- 📣 37 - 38 - 39 - type Msg 40 - = Assign (Alfred Reply) 41 - | Bypass 42 - | DetermineResults String 43 - | Hide 44 - | RunAction Int 45 - ----------------------------------------- 46 - -- Keyboard 47 - ----------------------------------------- 48 - | KeyDown (Maybe Keyboard.Key) 49 - 50 - 51 - update : Msg -> Model -> Return Model Msg Reply 52 - update msg model = 53 - case msg of 54 - Assign instance -> 55 - returnCommandWithModel 56 - { model | instance = Just instance } 57 - (Task.attempt (always Bypass) (Dom.focus searchId)) 58 - 59 - Bypass -> 60 - return model 61 - 62 - DetermineResults searchTerm -> 63 - model.instance 64 - |> Maybe.map (determineResults searchTerm) 65 - |> (\a -> return { model | instance = a }) 66 - 67 - Hide -> 68 - return { model | instance = Nothing } 69 - 70 - RunAction index -> 71 - case model.instance of 72 - Just instance -> 73 - { result = List.getAt index instance.results 74 - , searchTerm = instance.searchTerm 75 - } 76 - |> instance.action 77 - |> returnRepliesWithModel model 78 - |> andThen (update Hide) 79 - 80 - Nothing -> 81 - update Hide model 82 - 83 - ----------------------------------------- 84 - -- Keyboard 85 - ----------------------------------------- 86 - KeyDown (Just Keyboard.ArrowDown) -> 87 - case model.instance of 88 - Just instance -> 89 - instance 90 - |> (\i -> { i | focus = min (i.focus + 1) (List.length i.results - 1) }) 91 - |> (\i -> { model | instance = Just i }) 92 - |> return 93 - 94 - Nothing -> 95 - return model 96 - 97 - KeyDown (Just Keyboard.ArrowUp) -> 98 - case model.instance of 99 - Just instance -> 100 - instance 101 - |> (\i -> { i | focus = max (i.focus - 1) 0 }) 102 - |> (\i -> { model | instance = Just i }) 103 - |> return 104 - 105 - Nothing -> 106 - return model 107 - 108 - KeyDown (Just Keyboard.Enter) -> 109 - case model.instance of 110 - Just instance -> 111 - update (RunAction instance.focus) model 112 - 113 - Nothing -> 114 - return model 115 - 116 - KeyDown _ -> 117 - return model 118 - 119 - 120 - determineResults : String -> Alfred Reply -> Alfred Reply 121 - determineResults searchTerm alfred = 122 - let 123 - lowerSearchTerm = 124 - searchTerm 125 - |> String.toLower 126 - |> String.trim 127 - in 128 - if String.length lowerSearchTerm > 0 then 129 - { alfred 130 - | searchTerm = 131 - Just searchTerm 132 - , results = 133 - alfred.index 134 - |> List.filter (String.toLower >> String.contains lowerSearchTerm) 135 - |> List.sort 136 - } 137 - 138 - else 139 - { alfred 140 - | searchTerm = Nothing 141 - , results = alfred.index 142 - } 143 - 144 - 145 - 146 - -- 📰 147 - 148 - 149 - subscriptions : Model -> Sub Msg 150 - subscriptions _ = 151 - Keyboard.downs (Keyboard.anyKeyUpper >> KeyDown) 152 - 153 - 154 - 155 - -- 🗺 156 - 157 - 158 - view : Model -> Html Msg 159 - view model = 160 - case model.instance of 161 - Just instance -> 162 - chunk 163 - [ C.inset_0 164 - , C.flex 165 - , C.flex_col 166 - , C.fixed 167 - , C.items_center 168 - , C.px_3 169 - , C.cursor_pointer 170 - , C.z_50 171 - ] 172 - [ ----------------------------------------- 173 - -- Message 174 - ----------------------------------------- 175 - chunk 176 - [ C.italic 177 - , C.leading_normal 178 - , C.mt_12 179 - , C.text_center 180 - , C.text_white 181 - 182 - -- Dark mode 183 - ------------ 184 - , C.dark__text_base07 185 - ] 186 - [ text instance.message ] 187 - 188 - ----------------------------------------- 189 - -- Search 190 - ----------------------------------------- 191 - , brick 192 - [ Html.Events.custom 193 - "tap" 194 - (Json.Decode.succeed 195 - { message = Bypass 196 - , stopPropagation = True 197 - , preventDefault = True 198 - } 199 - ) 200 - ] 201 - [ C.text_sm 202 - , C.max_w_md 203 - , C.mt_8 204 - , C.w_full 205 - ] 206 - [ slab 207 - Html.input 208 - [ autofocus True 209 - , id searchId 210 - , onInput DetermineResults 211 - , placeholder "Type to search or create" 212 - , type_ "text" 213 - ] 214 - [ C.border_none 215 - , C.bg_white 216 - , C.block 217 - , C.leading_normal 218 - , C.rounded 219 - , C.outline_none 220 - , C.p_4 221 - , C.shadow_md 222 - , C.text_2xl 223 - , C.tracking_tad_closer 224 - , C.w_full 225 - 226 - -- Dark mode 227 - ------------ 228 - , C.dark__bg_base00 229 - ] 230 - [] 231 - ] 232 - 233 - ----------------------------------------- 234 - -- Results 235 - ----------------------------------------- 236 - , chunk 237 - [ C.bg_white 238 - , C.rounded 239 - , C.text_sm 240 - , C.leading_none 241 - , C.max_w_md 242 - , C.mt_8 243 - , C.overflow_hidden 244 - , C.shadow_md 245 - , C.w_full 246 - 247 - -- Dark mode 248 - ------------ 249 - , C.dark__bg_base00 250 - ] 251 - (List.indexedMap 252 - (\idx result -> 253 - brick 254 - [ onTapPreventDefault (RunAction idx) ] 255 - [ C.p_4 256 - , C.relative 257 - , C.truncate 258 - 259 - -- 260 - , if idx == instance.focus then 261 - String.joinWithSpace [ C.text_white, C.dark__text_base07 ] 262 - 263 - else 264 - C.text_inherit 265 - 266 - -- 267 - , if idx == instance.focus then 268 - C.bg_accent 269 - 270 - else if modBy 2 idx == 0 then 271 - C.bg_transparent 272 - 273 - else 274 - String.joinWithSpace [ C.bg_gray_100, C.dark__bg_base01 ] 275 - ] 276 - [ text result 277 - 278 - -- 279 - , if idx == instance.focus then 280 - chunk 281 - [ C.absolute 282 - , C.leading_0 283 - , C.minus_translate_y_half 284 - , C.mr_3 285 - , C.right_0 286 - , C.top_half 287 - ] 288 - [ Icons.keyboard_return 13 Inherit 289 - ] 290 - 291 - else 292 - nothing 293 - ] 294 - ) 295 - instance.results 296 - ) 297 - ] 298 - 299 - Nothing -> 300 - nothing 301 - 302 - 303 - searchId = 304 - "diffuse__alfred"
+153
src/Applications/UI/Alfred/State.elm
··· 1 + module UI.Alfred.State exposing (..) 2 + 3 + import Alfred exposing (Alfred) 4 + import Browser.Dom as Dom 5 + import Chunky exposing (..) 6 + import Css.Classes as C 7 + import Html exposing (Html, text) 8 + import Html.Attributes exposing (autofocus, id, placeholder, type_) 9 + import Html.Events exposing (onInput) 10 + import Html.Ext exposing (onTapPreventDefault) 11 + import Json.Decode 12 + import Keyboard 13 + import List.Extra as List 14 + import Material.Icons as Icons 15 + import Material.Icons.Types exposing (Coloring(..)) 16 + import Return exposing (andThen, return) 17 + import Return.Ext as Return 18 + import String.Ext as String 19 + import Task 20 + import UI.Alfred.Types as Alfred exposing (..) 21 + import UI.Types as UI exposing (Manager) 22 + 23 + 24 + 25 + -- 📣 26 + 27 + 28 + update : Alfred.Msg UI.Msg -> Manager 29 + update msg = 30 + case msg of 31 + Assign a -> 32 + assign a 33 + 34 + DetermineResults a -> 35 + determineResults a 36 + 37 + KeyDown a -> 38 + keyDown a 39 + 40 + RunAction a -> 41 + runAction a 42 + 43 + 44 + 45 + -- 🔱 46 + 47 + 48 + assign : Alfred UI.Msg -> Manager 49 + assign instance model = 50 + return 51 + { model | alfred = Just instance } 52 + (Task.attempt 53 + (always UI.Bypass) 54 + (Dom.focus "diffuse__alfred") 55 + ) 56 + 57 + 58 + determineResults : String -> Manager 59 + determineResults searchTerm model = 60 + model.alfred 61 + |> Maybe.map (determineResults_ searchTerm) 62 + |> (\a -> Return.singleton { model | alfred = a }) 63 + 64 + 65 + runAction : Int -> Manager 66 + runAction index model = 67 + case model.alfred of 68 + Just instance -> 69 + { result = List.getAt index instance.results 70 + , searchTerm = instance.searchTerm 71 + } 72 + |> instance.action 73 + |> List.map Return.task 74 + |> Cmd.batch 75 + |> return { model | alfred = Nothing } 76 + 77 + Nothing -> 78 + Return.singleton { model | alfred = Nothing } 79 + 80 + 81 + keyDown : Maybe Keyboard.Key -> Manager 82 + keyDown maybeKey model = 83 + case maybeKey of 84 + Just Keyboard.ArrowDown -> 85 + case model.alfred of 86 + Just instance -> 87 + instance 88 + |> (\i -> { i | focus = min (i.focus + 1) (List.length i.results - 1) }) 89 + |> (\i -> { model | alfred = Just i }) 90 + |> Return.singleton 91 + 92 + Nothing -> 93 + Return.singleton model 94 + 95 + Just Keyboard.ArrowUp -> 96 + case model.alfred of 97 + Just instance -> 98 + instance 99 + |> (\i -> { i | focus = max (i.focus - 1) 0 }) 100 + |> (\i -> { model | alfred = Just i }) 101 + |> Return.singleton 102 + 103 + Nothing -> 104 + Return.singleton model 105 + 106 + Just Keyboard.Enter -> 107 + case model.alfred of 108 + Just instance -> 109 + update (RunAction instance.focus) model 110 + 111 + Nothing -> 112 + Return.singleton model 113 + 114 + _ -> 115 + Return.singleton model 116 + 117 + 118 + 119 + -- ⚗️ 120 + 121 + 122 + determineResults_ : String -> Alfred UI.Msg -> Alfred UI.Msg 123 + determineResults_ searchTerm alfred = 124 + let 125 + lowerSearchTerm = 126 + searchTerm 127 + |> String.toLower 128 + |> String.trim 129 + in 130 + if String.length lowerSearchTerm > 0 then 131 + { alfred 132 + | searchTerm = 133 + Just searchTerm 134 + , results = 135 + alfred.index 136 + |> List.filter (String.toLower >> String.contains lowerSearchTerm) 137 + |> List.sort 138 + } 139 + 140 + else 141 + { alfred 142 + | searchTerm = Nothing 143 + , results = alfred.index 144 + } 145 + 146 + 147 + 148 + -- 📰 149 + 150 + 151 + subscriptions : Sub UI.Msg 152 + subscriptions = 153 + Keyboard.downs (Keyboard.anyKeyUpper >> KeyDown >> UI.Alfred)
+19
src/Applications/UI/Alfred/Types.elm
··· 1 + module UI.Alfred.Types exposing (..) 2 + 3 + import Alfred exposing (Alfred) 4 + import Keyboard 5 + import UI.Reply exposing (Reply) 6 + 7 + 8 + 9 + -- 📣 10 + 11 + 12 + type Msg action 13 + = Assign (Alfred action) 14 + | DetermineResults String 15 + | RunAction Int 16 + ----------------------------------------- 17 + -- Keyboard 18 + ----------------------------------------- 19 + | KeyDown (Maybe Keyboard.Key)
+165
src/Applications/UI/Alfred/View.elm
··· 1 + module UI.Alfred.View exposing (view) 2 + 3 + import Alfred exposing (Alfred) 4 + import Chunky exposing (..) 5 + import Css.Classes as C 6 + import Html exposing (Html, text) 7 + import Html.Attributes exposing (autofocus, id, placeholder, type_) 8 + import Html.Events exposing (onInput) 9 + import Html.Ext exposing (onTapPreventDefault) 10 + import Json.Decode 11 + import List.Extra as List 12 + import Material.Icons as Icons 13 + import Material.Icons.Types exposing (Coloring(..)) 14 + import String.Ext as String 15 + import UI.Alfred.Types exposing (..) 16 + import UI.Types as UI 17 + 18 + 19 + 20 + -- 🗺 21 + 22 + 23 + view : Maybe (Alfred UI.Msg) -> Html UI.Msg 24 + view maybeInstance = 25 + case maybeInstance of 26 + Just instance -> 27 + chunk 28 + [ C.inset_0 29 + , C.flex 30 + , C.flex_col 31 + , C.fixed 32 + , C.items_center 33 + , C.px_3 34 + , C.cursor_pointer 35 + , C.z_50 36 + ] 37 + [ ----------------------------------------- 38 + -- Message 39 + ----------------------------------------- 40 + chunk 41 + [ C.italic 42 + , C.leading_normal 43 + , C.mt_12 44 + , C.text_center 45 + , C.text_white 46 + 47 + -- Dark mode 48 + ------------ 49 + , C.dark__text_base07 50 + ] 51 + [ text instance.message ] 52 + 53 + ----------------------------------------- 54 + -- Search 55 + ----------------------------------------- 56 + , brick 57 + [ Html.Events.custom 58 + "tap" 59 + (Json.Decode.succeed 60 + { message = UI.Bypass 61 + , stopPropagation = True 62 + , preventDefault = True 63 + } 64 + ) 65 + ] 66 + [ C.text_sm 67 + , C.max_w_md 68 + , C.mt_8 69 + , C.w_full 70 + ] 71 + [ slab 72 + Html.input 73 + [ autofocus True 74 + , id "diffuse__alfred" 75 + , onInput (UI.Alfred << DetermineResults) 76 + , placeholder "Type to search or create" 77 + , type_ "text" 78 + ] 79 + [ C.border_none 80 + , C.bg_white 81 + , C.block 82 + , C.leading_normal 83 + , C.rounded 84 + , C.outline_none 85 + , C.p_4 86 + , C.shadow_md 87 + , C.text_2xl 88 + , C.tracking_tad_closer 89 + , C.w_full 90 + 91 + -- Dark mode 92 + ------------ 93 + , C.dark__bg_base00 94 + ] 95 + [] 96 + ] 97 + 98 + ----------------------------------------- 99 + -- Results 100 + ----------------------------------------- 101 + , chunk 102 + [ C.bg_white 103 + , C.rounded 104 + , C.text_sm 105 + , C.leading_none 106 + , C.max_w_md 107 + , C.mt_8 108 + , C.overflow_hidden 109 + , C.shadow_md 110 + , C.w_full 111 + 112 + -- Dark mode 113 + ------------ 114 + , C.dark__bg_base00 115 + ] 116 + (List.indexedMap 117 + (\idx result -> 118 + brick 119 + [ onTapPreventDefault (UI.Alfred <| RunAction idx) ] 120 + [ C.p_4 121 + , C.relative 122 + , C.truncate 123 + 124 + -- 125 + , if idx == instance.focus then 126 + String.joinWithSpace [ C.text_white, C.dark__text_base07 ] 127 + 128 + else 129 + C.text_inherit 130 + 131 + -- 132 + , if idx == instance.focus then 133 + C.bg_accent 134 + 135 + else if modBy 2 idx == 0 then 136 + C.bg_transparent 137 + 138 + else 139 + String.joinWithSpace [ C.bg_gray_100, C.dark__bg_base01 ] 140 + ] 141 + [ text result 142 + 143 + -- 144 + , if idx == instance.focus then 145 + chunk 146 + [ C.absolute 147 + , C.leading_0 148 + , C.minus_translate_y_half 149 + , C.mr_3 150 + , C.right_0 151 + , C.top_half 152 + ] 153 + [ Icons.keyboard_return 13 Inherit 154 + ] 155 + 156 + else 157 + nothing 158 + ] 159 + ) 160 + instance.results 161 + ) 162 + ] 163 + 164 + Nothing -> 165 + nothing
-1
src/Applications/UI/Audio/Types.elm
··· 1 1 module UI.Audio.Types exposing (..) 2 2 3 3 import Dict exposing (Dict) 4 - import Management 5 4 6 5 7 6
+4 -4
src/Applications/UI/Authentication/State.elm
··· 20 20 -- 📣 21 21 22 22 23 - authenticationBootFailure : String -> UI.Manager 23 + authenticationBootFailure : String -> Manager 24 24 authenticationBootFailure err model = 25 25 model 26 26 |> showNotification (Notifications.error err) 27 27 |> andThen (Reply.translate LoadDefaultBackdrop) 28 28 29 29 30 - missingSecretKey : Json.Value -> UI.Manager 30 + missingSecretKey : Json.Value -> Manager 31 31 missingSecretKey _ model = 32 32 "There seems to be existing data that's encrypted, I will need the passphrase (ie. encryption key) to continue." 33 33 |> Notifications.error ··· 36 36 |> andThen (Reply.translate <| Reply.ToggleLoadingScreen Off) 37 37 38 38 39 - notAuthenticated : UI.Manager 39 + notAuthenticated : Manager 40 40 notAuthenticated model = 41 41 -- This is the message we get when the app initially 42 42 -- finds out we're not authenticated. ··· 57 57 ) 58 58 59 59 60 - remoteStorageWebfinger : RemoteStorage.Attributes -> Result Http.Error String -> UI.Manager 60 + remoteStorageWebfinger : RemoteStorage.Attributes -> Result Http.Error String -> Manager 61 61 remoteStorageWebfinger remoteStorage result model = 62 62 case result of 63 63 Ok oauthOrigin ->
+2 -2
src/Applications/UI/Common/State.elm
··· 5 5 import Return 6 6 import UI.Notifications 7 7 import UI.Reply exposing (Reply) 8 - import UI.Types as UI 8 + import UI.Types as UI exposing (Manager) 9 9 10 10 11 11 12 12 -- 📣 13 13 14 14 15 - showNotification : Notification Reply -> UI.Manager 15 + showNotification : Notification Reply -> Manager 16 16 showNotification notification model = 17 17 model.notifications 18 18 |> UI.Notifications.show notification
+3 -3
src/Applications/UI/EtCetera/State.elm
··· 32 32 -- 📣 33 33 34 34 35 - setIsOnline : Bool -> UI.Manager 35 + setIsOnline : Bool -> Manager 36 36 setIsOnline bool model = 37 37 if bool then 38 38 -- We're caching the user's data in the browser while offline. ··· 65 65 ) 66 66 67 67 68 - setCurrentTime : Time.Posix -> UI.Manager 68 + setCurrentTime : Time.Posix -> Manager 69 69 setCurrentTime time model = 70 70 model 71 71 |> (\m -> { m | currentTime = time }) ··· 77 77 -- ⚗️ 78 78 79 79 80 - syncHypaethralData : UI.Manager 80 + syncHypaethralData : Manager 81 81 syncHypaethralData model = 82 82 model 83 83 |> Common.showNotification (Notifications.warning "Syncing")
+13 -13
src/Applications/UI/Interface/State.elm
··· 32 32 -- 📣 33 33 34 34 35 - blur : UI.Manager 35 + blur : Manager 36 36 blur model = 37 37 Return.singleton { model | focusedOnInput = False } 38 38 39 39 40 - debounce : (Msg -> Model -> ( Model, Cmd Msg )) -> Debouncer.Msg Msg -> UI.Manager 40 + debounce : (Msg -> Model -> ( Model, Cmd Msg )) -> Debouncer.Msg Msg -> Manager 41 41 debounce update debouncerMsg model = 42 42 let 43 43 ( subModel, subCmd, emittedMsg ) = ··· 59 59 return updatedModel mappedCmd 60 60 61 61 62 - focusedOnInput : UI.Manager 62 + focusedOnInput : Manager 63 63 focusedOnInput model = 64 64 Return.singleton { model | focusedOnInput = True } 65 65 66 66 67 - hideOverlay : UI.Manager 67 + hideOverlay : Manager 68 68 hideOverlay model = 69 69 Return.singleton 70 70 { model 71 - | alfred = { instance = Nothing } 71 + | alfred = Nothing 72 72 , confirmation = Nothing 73 73 , contextMenu = Nothing 74 74 } 75 75 76 76 77 - keyboardMsg : Keyboard.Msg -> UI.Manager 77 + keyboardMsg : Keyboard.Msg -> Manager 78 78 keyboardMsg msg model = 79 79 (\m -> 80 80 let ··· 128 128 { model | pressedKeys = Keyboard.update msg model.pressedKeys } 129 129 130 130 131 - preferredColorSchemaChanged : { dark : Bool } -> UI.Manager 131 + preferredColorSchemaChanged : { dark : Bool } -> Manager 132 132 preferredColorSchemaChanged { dark } model = 133 133 Return.singleton { model | darkMode = dark } 134 134 135 135 136 - removeQueueSelection : UI.Manager 136 + removeQueueSelection : Manager 137 137 removeQueueSelection = 138 138 modifySingleton Queue.lens (\q -> { q | selection = Nothing }) 139 139 140 140 141 - removeTrackSelection : UI.Manager 141 + removeTrackSelection : Manager 142 142 removeTrackSelection = 143 143 modifySingleton Tracks.lens (\t -> { t | selectedTrackIndexes = [] }) 144 144 145 145 146 - resizedWindow : ( Int, Int ) -> UI.Manager 146 + resizedWindow : ( Int, Int ) -> Manager 147 147 resizedWindow ( width, height ) model = 148 148 Return.singleton 149 149 { model ··· 152 152 } 153 153 154 154 155 - setIsTouchDevice : Bool -> UI.Manager 155 + setIsTouchDevice : Bool -> Manager 156 156 setIsTouchDevice bool model = 157 157 Return.singleton { model | isTouchDevice = bool } 158 158 159 159 160 - stoppedDragging : UI.Manager 160 + stoppedDragging : Manager 161 161 stoppedDragging model = 162 162 let 163 163 notDragging = ··· 187 187 Return.singleton notDragging 188 188 189 189 190 - toggleLoadingScreen : Switch -> UI.Manager 190 + toggleLoadingScreen : Switch -> Manager 191 191 toggleLoadingScreen switch model = 192 192 case switch of 193 193 On ->
+13 -5
src/Applications/UI/Playlists/Alfred.elm
··· 3 3 import Alfred exposing (..) 4 4 import Playlists exposing (..) 5 5 import Tracks exposing (IdentifiedTrack) 6 - import UI.Reply exposing (Reply(..)) 6 + import UI.Types as UI 7 7 8 8 9 9 10 10 -- 🔱 11 11 12 12 13 - create : List IdentifiedTrack -> List Playlist -> Alfred Reply 13 + create : List IdentifiedTrack -> List Playlist -> Alfred UI.Msg 14 14 create tracks playlists = 15 15 let 16 16 playlistNames = ··· 32 32 } 33 33 34 34 35 - action : List IdentifiedTrack -> { result : Maybe String, searchTerm : Maybe String } -> List Reply 35 + action : List IdentifiedTrack -> { result : Maybe String, searchTerm : Maybe String } -> List UI.Msg 36 36 action tracks maybe = 37 37 let 38 38 playlistTracks = ··· 42 42 Just result -> 43 43 -- Add to playlist 44 44 -- 45 - [ AddTracksToPlaylist { playlistName = result, tracks = playlistTracks } ] 45 + [ UI.AddTracksToPlaylist 46 + { playlistName = result 47 + , tracks = playlistTracks 48 + } 49 + ] 46 50 47 51 Nothing -> 48 52 -- Create playlist, ··· 50 54 -- 51 55 case maybe.searchTerm of 52 56 Just searchTerm -> 53 - [ AddTracksToPlaylist { playlistName = searchTerm, tracks = playlistTracks } ] 57 + [ UI.AddTracksToPlaylist 58 + { playlistName = searchTerm 59 + , tracks = playlistTracks 60 + } 61 + ] 54 62 55 63 Nothing -> 56 64 []
+67
src/Applications/UI/Playlists/State.elm
··· 1 + module UI.Playlists.State exposing (..) 2 + 3 + import List.Extra as List 4 + import Management 5 + import Monocle.Lens as Lens 6 + import Notifications 7 + import Playlists exposing (PlaylistTrack) 8 + import Return exposing (andThen, return) 9 + import Return.Ext as Return exposing (communicate) 10 + import UI.Common.State as Common 11 + import UI.Reply exposing (Reply(..)) 12 + import UI.Reply.Translate as Reply 13 + import UI.Types as UI exposing (Manager, Msg(..)) 14 + 15 + 16 + 17 + -- 🔱 18 + 19 + 20 + addTracksToPlaylist : { playlistName : String, tracks : List PlaylistTrack } -> Manager 21 + addTracksToPlaylist { playlistName, tracks } model = 22 + let 23 + properPlaylistName = 24 + String.trim playlistName 25 + 26 + playlistIndex = 27 + List.findIndex 28 + (\p -> p.autoGenerated == False && p.name == properPlaylistName) 29 + model.playlists.collection 30 + 31 + playlistsModel = 32 + model.playlists 33 + 34 + newCollection = 35 + case playlistIndex of 36 + Just idx -> 37 + List.updateAt 38 + idx 39 + (\p -> { p | tracks = p.tracks ++ tracks }) 40 + playlistsModel.collection 41 + 42 + Nothing -> 43 + (::) 44 + { autoGenerated = False 45 + , name = properPlaylistName 46 + , tracks = tracks 47 + } 48 + playlistsModel.collection 49 + 50 + newModel = 51 + { playlistsModel 52 + | collection = newCollection 53 + , lastModifiedPlaylist = Just properPlaylistName 54 + } 55 + |> (\m -> { model | playlists = m }) 56 + in 57 + (case tracks of 58 + [ t ] -> 59 + "Added __" ++ t.title ++ "__" 60 + 61 + l -> 62 + "Added __" ++ String.fromInt (List.length l) ++ " tracks__" 63 + ) 64 + |> (\s -> s ++ " to the __" ++ properPlaylistName ++ "__ playlist") 65 + |> Notifications.success 66 + |> Common.showNotificationWithModel newModel 67 + |> andThen (Reply.translate SavePlaylists)
+5 -51
src/Applications/UI/Reply/Translate.elm
··· 1 1 module UI.Reply.Translate exposing (..) 2 2 3 - import Alfred exposing (Alfred) 4 3 import Alien 5 4 import Browser 6 5 import Browser.Dom ··· 46 45 import Time 47 46 import Tracks 48 47 import Tracks.Encoding as Tracks 49 - import UI.Alfred as Alfred 48 + import UI.Alfred.Types as Alfred 50 49 import UI.Audio.State as Audio 51 50 import UI.Audio.Types as Audio 52 51 import UI.Authentication as Authentication ··· 92 91 -- 📣 ░░ REPLIES 93 92 94 93 95 - translate : Reply -> UI.Manager 94 + translate : Reply -> Manager 96 95 translate reply model = 97 96 case reply of 98 97 Shunt -> ··· 355 354 |> TracksMsg 356 355 |> Return.performanceF model 357 356 358 - AddTracksToPlaylist { playlistName, tracks } -> 359 - let 360 - properPlaylistName = 361 - String.trim playlistName 362 - 363 - playlistIndex = 364 - List.findIndex 365 - (\p -> p.autoGenerated == False && p.name == properPlaylistName) 366 - model.playlists.collection 367 - 368 - playlistsModel = 369 - model.playlists 370 - 371 - newCollection = 372 - case playlistIndex of 373 - Just idx -> 374 - List.updateAt 375 - idx 376 - (\p -> { p | tracks = p.tracks ++ tracks }) 377 - playlistsModel.collection 378 - 379 - Nothing -> 380 - (::) 381 - { autoGenerated = False 382 - , name = properPlaylistName 383 - , tracks = tracks 384 - } 385 - playlistsModel.collection 386 - 387 - newModel = 388 - { playlistsModel 389 - | collection = newCollection 390 - , lastModifiedPlaylist = Just properPlaylistName 391 - } 392 - |> (\m -> { model | playlists = m }) 393 - in 394 - (case tracks of 395 - [ t ] -> 396 - "Added __" ++ t.title ++ "__" 397 - 398 - l -> 399 - "Added __" ++ String.fromInt (List.length l) ++ " tracks__" 400 - ) 401 - |> (\s -> s ++ " to the __" ++ properPlaylistName ++ "__ playlist") 402 - |> Notifications.success 403 - |> showNotificationWithModel newModel 404 - |> andThen (translate SavePlaylists) 357 + Reply.AddTracksToPlaylist a -> 358 + Return.performance (UI.AddTracksToPlaylist a) model 405 359 406 360 DeactivatePlaylist -> 407 361 Tracks.DeselectPlaylist ··· 480 434 |> List.filterNot .autoGenerated 481 435 |> UI.Playlists.Alfred.create tracks 482 436 |> Alfred.Assign 483 - |> AlfredMsg 437 + |> Alfred 484 438 |> Return.performanceF model 485 439 486 440 -----------------------------------------
+6 -6
src/Applications/UI/Routing/State.elm
··· 13 13 import UI.Sources.Form 14 14 import UI.Sources.Page 15 15 import UI.Sources.State as Sources 16 - import UI.Types as UI 16 + import UI.Types as UI exposing (Manager) 17 17 import Url exposing (Url) 18 18 19 19 ··· 21 21 -- 📣 22 22 23 23 24 - changeUrlUsingPage : Page -> UI.Manager 24 + changeUrlUsingPage : Page -> Manager 25 25 changeUrlUsingPage page model = 26 26 page 27 27 |> Page.toString ··· 29 29 |> return model 30 30 31 31 32 - linkClicked : UrlRequest -> UI.Manager 32 + linkClicked : UrlRequest -> Manager 33 33 linkClicked urlRequest model = 34 34 case urlRequest of 35 35 Browser.Internal urlWithFragment -> ··· 51 51 return model (Nav.load href) 52 52 53 53 54 - urlChanged : Url -> UI.Manager 54 + urlChanged : Url -> Manager 55 55 urlChanged url model = 56 56 let 57 57 rewrittenUrl = ··· 72 72 -- TRANSITIONING 73 73 74 74 75 - transition : Page -> UI.Manager 75 + transition : Page -> Manager 76 76 transition page model = 77 77 case page of 78 78 ----------------------------------------- ··· 137 137 -- ㊙️ 138 138 139 139 140 - loadSourceForForm : String -> UI.Manager 140 + loadSourceForForm : String -> Manager 141 141 loadSourceForForm sourceId model = 142 142 let 143 143 isLoading =
+2 -2
src/Applications/UI/Services/State.elm
··· 16 16 -- 📣 17 17 18 18 19 - gotLastFmSession : Result Http.Error String -> UI.Manager 19 + gotLastFmSession : Result Http.Error String -> Manager 20 20 gotLastFmSession result model = 21 21 case result of 22 22 Err _ -> ··· 32 32 (Return.performance <| Reply SaveSettings) 33 33 34 34 35 - scrobble : { duration : Int, timestamp : Int, trackId : String } -> UI.Manager 35 + scrobble : { duration : Int, timestamp : Int, trackId : String } -> Manager 36 36 scrobble { duration, timestamp, trackId } model = 37 37 case model.tracks.nowPlaying of 38 38 Just ( _, track ) ->
+3 -3
src/Applications/UI/Tracks/State.elm
··· 30 30 -- 📣 31 31 32 32 33 - downloadTracksFinished : UI.Manager 33 + downloadTracksFinished : Manager 34 34 downloadTracksFinished model = 35 35 case model.downloading of 36 36 Just { notificationId } -> ··· 43 43 Return.singleton model 44 44 45 45 46 - failedToStoreTracksInCache : List String -> UI.Manager 46 + failedToStoreTracksInCache : List String -> Manager 47 47 failedToStoreTracksInCache trackIds model = 48 48 model 49 49 |> Lens.modify lens ··· 52 52 (Notifications.error "Failed to store track in cache") 53 53 54 54 55 - finishedStoringTracksInCache : List String -> UI.Manager 55 + finishedStoringTracksInCache : List String -> Manager 56 56 finishedStoringTracksInCache trackIds model = 57 57 model 58 58 |> Lens.modify lens
+10 -6
src/Applications/UI/Types.elm
··· 34 34 import Management 35 35 import Maybe.Extra as Maybe 36 36 import Notifications exposing (Notification) 37 + import Playlists exposing (PlaylistTrack) 37 38 import Playlists.Encoding as Playlists 38 39 import Process 39 40 import Queue ··· 49 50 import Time 50 51 import Tracks 51 52 import Tracks.Encoding as Tracks 52 - import UI.Alfred as Alfred 53 + import UI.Alfred.Types as Alfred 53 54 import UI.Audio.Types as Audio 54 55 import UI.Authentication as Authentication 55 56 import UI.Authentication.ContextMenu as Authentication ··· 63 64 import UI.Notifications 64 65 import UI.Page as Page exposing (Page) 65 66 import UI.Playlists as Playlists 66 - import UI.Playlists.Alfred 67 67 import UI.Playlists.ContextMenu as Playlists 68 68 import UI.Playlists.Directory 69 69 import UI.Ports as Ports ··· 104 104 105 105 106 106 type alias Model = 107 - { contextMenu : Maybe (ContextMenu Reply) 107 + { alfred : Maybe (Alfred Msg) 108 + , contextMenu : Maybe (ContextMenu Reply) 108 109 , confirmation : Maybe String 109 110 , currentTime : Time.Posix 110 111 , darkMode : Bool ··· 128 129 ----------------------------------------- 129 130 -- Children 130 131 ----------------------------------------- 131 - , alfred : Alfred.Model 132 132 , authentication : Authentication.Model 133 133 , backdrop : Backdrop.Model 134 134 , equalizer : Equalizer.Model ··· 152 152 = Bypass 153 153 | Reply Reply 154 154 -- 155 + | Alfred (Alfred.Msg Msg) 155 156 | Audio Audio.Msg 156 157 ----------------------------------------- 157 158 -- Authentication ··· 161 162 | NotAuthenticated 162 163 | RemoteStorageWebfinger RemoteStorage.Attributes (Result Http.Error String) 163 164 ----------------------------------------- 164 - -- Children 165 + -- Children (TODO) 165 166 ----------------------------------------- 166 - | AlfredMsg Alfred.Msg 167 167 | AuthenticationMsg Authentication.Msg 168 168 | BackdropMsg Backdrop.Msg 169 169 | EqualizerMsg Equalizer.Msg ··· 187 187 | SetIsTouchDevice Bool 188 188 | StoppedDragging 189 189 | ToggleLoadingScreen Switch 190 + ----------------------------------------- 191 + -- Playlists 192 + ----------------------------------------- 193 + | AddTracksToPlaylist { playlistName : String, tracks : List PlaylistTrack } 190 194 ----------------------------------------- 191 195 -- Routing 192 196 -----------------------------------------
+4 -4
src/Applications/UI/User/State.elm
··· 35 35 -- 🔱 36 36 37 37 38 - importFile : File -> UI.Manager 38 + importFile : File -> Manager 39 39 importFile file model = 40 40 250 41 41 |> Process.sleep ··· 44 44 |> return { model | isLoading = True } 45 45 46 46 47 - importJson : String -> UI.Manager 47 + importJson : String -> Manager 48 48 importJson json model = 49 49 json 50 50 -- Load data on main thread (this app) ··· 68 68 |> Reply.saveAllHypaethralData 69 69 70 70 71 - loadEnclosedUserData : Json.Decode.Value -> UI.Manager 71 + loadEnclosedUserData : Json.Decode.Value -> Manager 72 72 loadEnclosedUserData json model = 73 73 model 74 74 |> importEnclosed json 75 75 |> Return3.wield Reply.translate 76 76 77 77 78 - loadHypaethralUserData : Json.Decode.Value -> UI.Manager 78 + loadHypaethralUserData : Json.Decode.Value -> Manager 79 79 loadHypaethralUserData json model = 80 80 model 81 81 |> importHypaethral json
+7 -8
src/Applications/UI/View.elm
··· 24 24 import Sources.Encoding as Sources 25 25 import Tracks 26 26 import Tracks.Encoding as Tracks 27 - import UI.Alfred as Alfred 27 + import UI.Alfred.Types as Alfred 28 + import UI.Alfred.View as Alfred 28 29 import UI.Authentication as Authentication 29 30 import UI.Authentication.ContextMenu as Authentication 30 31 import UI.Backdrop as Backdrop ··· 66 67 body : Model -> Html Msg 67 68 body model = 68 69 section 69 - (if Maybe.isJust model.contextMenu || Maybe.isJust model.alfred.instance then 70 + (if Maybe.isJust model.contextMenu || Maybe.isJust model.alfred then 70 71 [ on "tap" (Json.Decode.succeed HideOverlay) ] 71 72 72 73 else if Maybe.isJust model.equalizer.activeKnob then ··· 94 95 [ ----------------------------------------- 95 96 -- Alfred 96 97 ----------------------------------------- 97 - model.alfred 98 - |> Lazy.lazy Alfred.view 99 - |> Html.map AlfredMsg 98 + Lazy.lazy Alfred.view model.alfred 100 99 101 100 ----------------------------------------- 102 101 -- Backdrop ··· 123 122 -- Overlay 124 123 ----------------------------------------- 125 124 , model.contextMenu 126 - |> Lazy.lazy2 overlay model.alfred.instance 125 + |> Lazy.lazy2 overlay model.alfred 127 126 128 127 ----------------------------------------- 129 128 -- Content ··· 159 158 , ( Page.Settings UI.Settings.Page.Index, "Settings" ) 160 159 ] 161 160 ) 162 - model.alfred.instance 161 + model.alfred 163 162 model.page 164 163 165 164 ----------------------------------------- ··· 304 303 Html.map never UI.Svg.Elements.loading 305 304 306 305 307 - overlay : Maybe (Alfred Reply) -> Maybe (ContextMenu Reply) -> Html Msg 306 + overlay : Maybe (Alfred Msg) -> Maybe (ContextMenu Reply) -> Html Msg 308 307 overlay maybeAlfred maybeContextMenu = 309 308 let 310 309 isShown =
+2 -2
src/Library/Alfred.elm
··· 3 3 -- 🌳 4 4 5 5 6 - type alias Alfred reply = 7 - { action : { result : Maybe String, searchTerm : Maybe String } -> List reply 6 + type alias Alfred action = 7 + { action : { result : Maybe String, searchTerm : Maybe String } -> List action 8 8 , focus : Int 9 9 , index : List String 10 10 , message : String
+2
src/Library/Return/Ext.elm
··· 19 19 ( model, task msg ) 20 20 21 21 22 + {-| TODO: Remove when finished with refactor 23 + -} 22 24 performanceF : model -> msg -> ( model, Cmd msg ) 23 25 performanceF model msg = 24 26 performance msg model