summaryrefslogtreecommitdiff
path: root/elm/User/Edit.elm
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2019-10-02 14:21:12 +0200
committerYorhel <git@yorhel.nl>2019-10-02 14:21:15 +0200
commit7d8274f0332b1c92825988c6393037bf7eb12af3 (patch)
treecdcc33c92f764b3199fffeef990d71e6085dff06 /elm/User/Edit.elm
parent1a9a4b4bdb8f3b6d7d0ad12032c17c44a8287a09 (diff)
v2rw: Convert user preferences form
And add a small 'formField' function to shrink the Elm form generation code a bit.
Diffstat (limited to 'elm/User/Edit.elm')
-rw-r--r--elm/User/Edit.elm192
1 files changed, 192 insertions, 0 deletions
diff --git a/elm/User/Edit.elm b/elm/User/Edit.elm
new file mode 100644
index 00000000..4bdffad9
--- /dev/null
+++ b/elm/User/Edit.elm
@@ -0,0 +1,192 @@
+module User.Edit exposing (main)
+
+import Bitwise exposing (..)
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import Browser
+import Browser.Navigation exposing (load)
+import Lib.Html exposing (..)
+import Lib.Api as Api
+import Gen.Api as GApi
+import Gen.Types as GT
+import Gen.UserEdit as GUE
+
+
+main : Program GUE.Send Model Msg
+main = Browser.element
+ { init = \e -> (init e, Cmd.none)
+ , view = view
+ , update = update
+ , subscriptions = always Sub.none
+ }
+
+
+type alias Model =
+ { state : Api.State
+ , data : GUE.Send
+ , cpass : Bool
+ , pass1 : String
+ , pass2 : String
+ , opass : String
+ , passNeq : Bool
+ }
+
+
+init : GUE.Send -> Model
+init d =
+ { state = Api.Normal
+ , data = d
+ , cpass = False
+ , pass1 = ""
+ , pass2 = ""
+ , opass = ""
+ , passNeq = False
+ }
+
+
+type Data
+ = Username String
+ | EMail String
+ | Perm Int Bool
+ | IgnVotes Bool
+ | HideList Bool
+ | ShowNsfw Bool
+ | TraitsSexual Bool
+ | Spoilers String
+ | TagsAll Bool
+ | TagsCont Bool
+ | TagsEro Bool
+ | TagsTech Bool
+ | Skin String
+ | Css String
+
+
+updateData : Data -> GUE.Send -> GUE.Send
+updateData msg model =
+ case msg of
+ Username n -> { model | username = n }
+ EMail n -> { model | email = n }
+ Perm n b -> { model | perm = if b then or model.perm n else and model.perm (complement n) }
+ IgnVotes n -> { model | ign_votes = n }
+ HideList b -> { model | hide_list = b }
+ ShowNsfw b -> { model | show_nsfw = b }
+ TraitsSexual b -> { model | traits_sexual = b }
+ Spoilers n -> { model | spoilers = Maybe.withDefault model.spoilers (String.toInt n) }
+ TagsAll b -> { model | tags_all = b }
+ TagsCont b -> { model | tags_cont = b }
+ TagsEro b -> { model | tags_ero = b }
+ TagsTech b -> { model | tags_tech = b }
+ Skin n -> { model | skin = n }
+ Css n -> { model | customcss = n }
+
+
+type Msg
+ = Set Data
+ | CPass Bool
+ | OPass String
+ | Pass1 String
+ | Pass2 String
+ | Submit
+ | Submitted GApi.Response
+
+
+-- Synchronizes model.data.password with model.stuff
+fixup : Model -> Model
+fixup model =
+ let
+ data = model.data
+ ndata = { data | password = if model.cpass && model.pass1 == model.pass2 then Just { old = model.opass, new = model.pass1 } else Nothing }
+ in { model | data = ndata }
+
+
+update : Msg -> Model -> (Model, Cmd Msg)
+update msg model =
+ case msg of
+ Set d -> ({ model | data = updateData d model.data }, Cmd.none)
+ CPass b -> (fixup { model | cpass = b, passNeq = False }, Cmd.none)
+ OPass n -> (fixup { model | opass = n, passNeq = False }, Cmd.none)
+ Pass1 n -> (fixup { model | pass1 = n, passNeq = False }, Cmd.none)
+ Pass2 n -> (fixup { model | pass2 = n, passNeq = False }, Cmd.none)
+
+ Submit ->
+ if model.cpass && model.pass1 /= model.pass2
+ then ({ model | passNeq = True }, Cmd.none )
+ else ({ model | state = Api.Loading }, Api.post "/u/edit" (GUE.encode model.data) Submitted)
+
+ -- TODO: This reload is only necessary for the skin and customcss options to apply, but it's nicer to do that directly from JS.
+ Submitted GApi.Success -> (model, load <| "/u" ++ String.fromInt model.data.id ++ "/edit")
+ Submitted r -> ({ model | state = Api.Error r }, Cmd.none)
+
+
+
+view : Model -> Html Msg
+view model =
+ let
+ modform =
+ [ tr [ class "newpart" ] [ td [ colspan 2 ] [ text "Admin options" ] ]
+ , formField "username::Username" [ inputText "username" model.data.username (Set << Username) GUE.valUsername ]
+ , formField "Permissions"
+ <| List.intersperse (br_ 1)
+ <| List.map (\(n,s) -> label [] [ inputCheck "" (and model.data.perm n > 0) (Set << Perm n), text (" " ++ s) ])
+ GT.userPerms
+ , formField "Other" [ label [] [ inputCheck "" model.data.ign_votes (Set << IgnVotes), text " Ignore votes in VN statistics" ] ]
+ ]
+
+ passform =
+ [ formField "opass::Old password" [ inputPassword "opass" model.opass OPass GUE.valPasswordOld ]
+ , formField "pass1::New password" [ inputPassword "pass1" model.pass1 Pass1 GUE.valPasswordNew ]
+ , formField "pass2::Repeat"
+ [ inputPassword "pass2" model.pass2 Pass2 GUE.valPasswordNew
+ , br_ 1
+ , if model.passNeq
+ then b [ class "standout" ] [ text "Passwords do not match" ]
+ else text ""
+ ]
+ ]
+
+ in Html.form [ onSubmit Submit ]
+ [ div [ class "mainbox" ]
+ [ h1 [] [ text <| if model.data.authmod then "Edit " ++ model.data.username else "My preferences" ]
+ , table [ class "formtable" ] <|
+ [ tr [ class "newpart" ] [ td [ colspan 2 ] [ text "General" ] ]
+ , formField "Username" [ text model.data.username ]
+ , formField "email::E-Mail" [ inputText "email" model.data.email (Set << EMail) GUE.valEmail ]
+ ]
+ ++ (if model.data.authmod then modform else []) ++
+ [ tr [ class "newpart" ] [ td [ colspan 2 ] [ text "Password" ] ]
+ , formField "" [ label [] [ inputCheck "" model.cpass CPass, text " Change password" ] ]
+ ] ++ (if model.cpass then passform else [])
+ ++
+ [ tr [ class "newpart" ] [ td [ colspan 2 ] [ text "Preferences" ] ]
+ , formField "Privacy"
+ [ label []
+ [ inputCheck "" model.data.hide_list (Set << HideList)
+ , text " Don't allow others to see my visual novel list, vote list and wishlist and exclude these lists from the database dumps and API."
+ ]
+ ]
+ , formField "NSFW" [ label [] [ inputCheck "" model.data.show_nsfw (Set << ShowNsfw), text " Show NSFW images by default" ] ]
+ , formField "" [ label [] [ inputCheck "" model.data.traits_sexual (Set << TraitsSexual), text " Show sexual traits by default on character pages" ] ]
+ , formField "Tags" [ label [] [ inputCheck "" model.data.tags_all (Set << TagsAll), text " Show all tags by default on visual novel pages (don't summarize)" ] ]
+ , formField ""
+ [ text "Default tag categories on visual novel pages:", br_ 1
+ , label [] [ inputCheck "" model.data.tags_cont (Set << TagsCont), text " Content" ], br_ 1
+ , label [] [ inputCheck "" model.data.tags_ero (Set << TagsEro ), text " Sexual content" ], br_ 1
+ , label [] [ inputCheck "" model.data.tags_tech (Set << TagsTech), text " Technical" ]
+ ]
+ , formField "spoil::Spoiler level"
+ [ inputSelect "spoil" (String.fromInt model.data.spoilers) (Set << Spoilers) []
+ [ ("0", "Hide spoilers")
+ , ("1", "Show only minor spoilers")
+ , ("2", "Show all spoilers")
+ ]
+ ]
+ , formField "skin::Skin" [ inputSelect "skin" model.data.skin (Set << Skin) [ style "width" "300px" ] GT.skins ]
+ , formField "css::Custom CSS" [ inputTextArea "css" model.data.customcss (Set << Css) ([ rows 5, cols 60 ] ++ GUE.valCustomcss) ]
+ ]
+
+ ]
+ , div [ class "mainbox" ]
+ [ fieldset [ class "submit" ] [ submitButton "Submit" model.state (not model.passNeq) False ]
+ ]
+ ]