website/frontend/src/Effect.elm
2024-12-11 02:38:42 -06:00

257 lines
5.4 KiB
Elm
Executable file

module Effect exposing
( Effect
, none, batch
, sendCmd, sendMsg
, clearUser, saveUser, signIn, signOut, toggleLanguage, toggleNavbarExpansion
, pushRoute, replaceRoute, loadExternalUrl
, map, toCmd
)
{-|
@docs Effect
@docs none, batch
@docs sendCmd, sendMsg
@docs clearUser, saveUser, signIn, signOut, toggleLanguage, toggleNavbarExpansion
@docs pushRoute, replaceRoute, loadExternalUrl
@docs map, toCmd
-}
import Browser.Navigation
import Dict exposing (Dict)
import Json.Encode
import Ports
import Route exposing (Route)
import Route.Path
import Shared.Model
import Shared.Msg
import Task
import Url exposing (Url)
type Effect msg
= -- BASICS
None
| Batch (List (Effect msg))
| SendCmd (Cmd msg)
-- ROUTING
| PushUrl String
| ReplaceUrl String
| LoadExternalUrl String
-- SHARED
| SendSharedMsg Shared.Msg.Msg
| SendToLocalStorage { key : String, value : Json.Encode.Value }
-- BASICS
{-| Don't send any effect.
-}
none : Effect msg
none =
None
{-| Send multiple effects at once.
-}
batch : List (Effect msg) -> Effect msg
batch =
Batch
{-| Send a normal `Cmd msg` as an effect, something like `Http.get` or `Random.generate`.
-}
sendCmd : Cmd msg -> Effect msg
sendCmd =
SendCmd
{-| Send a message as an effect. Useful when emitting events from UI components.
-}
sendMsg : msg -> Effect msg
sendMsg msg =
Task.succeed msg
|> Task.perform identity
|> SendCmd
-- ROUTING
{-| Set the new route, and make the back button go back to the current route.
-}
pushRoute :
{ path : Route.Path.Path
, query : Dict String String
, hash : Maybe String
}
-> Effect msg
pushRoute route =
PushUrl (Route.toString route)
{-| Set given path as route (without any query params or hash), and make the back button go back to the current route.
-}
pushPath :
Route.Path.Path
-> Effect msg
pushPath path =
PushUrl (Route.toString { path = path, query = Dict.empty, hash = Nothing })
{-| Set the new route, but replace the previous one, so clicking the back
button **won't** go back to the previous route.
-}
replaceRoute :
{ path : Route.Path.Path
, query : Dict String String
, hash : Maybe String
}
-> Effect msg
replaceRoute route =
ReplaceUrl (Route.toString route)
{-| Set given path as route (without any query params or hash), but replace the previous route,
so clicking the back button **won't** go back to the previous route
-}
replacePath :
Route.Path.Path
-> Effect msg
replacePath path =
ReplaceUrl (Route.toString { path = path, query = Dict.empty, hash = Nothing })
{-| Redirect users to a new URL, somewhere external your web application.
-}
loadExternalUrl : String -> Effect msg
loadExternalUrl =
LoadExternalUrl
-- INTERNALS
{-| Elm Land depends on this function to connect pages and layouts
together into the overall app.
-}
map : (msg1 -> msg2) -> Effect msg1 -> Effect msg2
map fn effect =
case effect of
None ->
None
Batch list ->
Batch (List.map (map fn) list)
SendCmd cmd ->
SendCmd (Cmd.map fn cmd)
PushUrl url ->
PushUrl url
ReplaceUrl url ->
ReplaceUrl url
LoadExternalUrl url ->
LoadExternalUrl url
SendSharedMsg sharedMsg ->
SendSharedMsg sharedMsg
SendToLocalStorage value ->
SendToLocalStorage value
{-| Elm Land depends on this function to perform your effects.
-}
toCmd :
{ key : Browser.Navigation.Key
, url : Url
, shared : Shared.Model.Model
, fromSharedMsg : Shared.Msg.Msg -> msg
, batch : List msg -> msg
, toCmd : msg -> Cmd msg
}
-> Effect msg
-> Cmd msg
toCmd options effect =
case effect of
None ->
Cmd.none
Batch list ->
Cmd.batch (List.map (toCmd options) list)
SendCmd cmd ->
cmd
PushUrl url ->
Browser.Navigation.pushUrl options.key url
ReplaceUrl url ->
Browser.Navigation.replaceUrl options.key url
LoadExternalUrl url ->
Browser.Navigation.load url
SendSharedMsg sharedMsg ->
Task.succeed sharedMsg
|> Task.perform options.fromSharedMsg
SendToLocalStorage value ->
Ports.sendToLocalStorage value
toggleNavbarExpansion : Effect msg
toggleNavbarExpansion =
SendSharedMsg Shared.Msg.ToggleNavbarExpansion
toggleLanguage : Effect msg
toggleLanguage =
SendSharedMsg Shared.Msg.ToggleLanguage
signIn :
{ token : String
, name : String
}
-> Effect msg
signIn user =
SendSharedMsg (Shared.Msg.SignIn user)
signOut : Effect msg
signOut =
SendSharedMsg Shared.Msg.SignOut
saveUser :
{ token : String
, name : String
}
-> Effect msg
saveUser user =
SendToLocalStorage
{ key = "user"
, value =
Json.Encode.object
[ ( "token", Json.Encode.string user.token )
, ( "name", Json.Encode.string user.name )
]
}
clearUser : Effect msg
clearUser =
SendToLocalStorage
{ key = "user"
, value = Json.Encode.null
}