diff options
Diffstat (limited to 'elm/Discussions/Edit.elm')
-rw-r--r-- | elm/Discussions/Edit.elm | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/elm/Discussions/Edit.elm b/elm/Discussions/Edit.elm new file mode 100644 index 00000000..495a95e4 --- /dev/null +++ b/elm/Discussions/Edit.elm @@ -0,0 +1,240 @@ +module Discussions.Edit exposing (main) + +import Html exposing (..) +import Html.Attributes exposing (..) +import Browser +import Browser.Navigation exposing (load) +import Lib.Html exposing (..) +import Lib.TextPreview as TP +import Lib.Api as Api +import Lib.Util exposing (..) +import Lib.Autocomplete as A +import Gen.Api as GApi +import Gen.Types exposing (boardTypes) +import Gen.DiscussionsEdit as GDE + + +main : Program GDE.Recv 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 + , tid : Maybe Int + , num : Maybe Int + , can_mod : Bool + , can_private : Bool + , locked : Bool + , hidden : Bool + , private : Bool + , nolastmod : Bool + , title : Maybe String + , boards : Maybe (List GDE.SendBoards) + , boardAdd : A.Model GApi.ApiBoardResult + , msg : TP.Model + , poll : GDE.SendPoll + , pollEnabled : Bool + , pollEdit : Bool + } + + +init : GDE.Recv -> Model +init d = + { state = Api.Normal + , can_mod = d.can_mod + , can_private = d.can_private + , tid = d.tid + , num = d.num + , locked = d.locked + , hidden = d.hidden + , private = d.private + , nolastmod = False + , title = d.title + , boards = d.boards + , boardAdd = A.init + , msg = TP.bbcode d.msg + , poll = d.poll + , pollEnabled = isJust d.poll + , pollEdit = isJust d.poll + } + + +searchConfig : A.Config Msg GApi.ApiBoardResult +searchConfig = { wrap = BoardSearch, id = "boardadd", source = A.boardSource } + + +encode : Model -> GDE.Send +encode m = + { tid = m.tid + , num = m.num + , locked = m.locked + , hidden = m.hidden + , private = m.private + , nolastmod = m.nolastmod + , boards = m.boards + , poll = if m.pollEnabled then m.poll else Nothing + , title = m.title + , msg = m.msg.data + } + + +numPollOptions : Model -> Int +numPollOptions model = Maybe.withDefault 0 (Maybe.map (\o -> List.length o.options) model.poll) + +dupBoards : Model -> Bool +dupBoards model = hasDuplicates (List.map (\b -> (b.btype, b.iid)) (Maybe.withDefault [] model.boards)) + +isValid : Model -> Bool +isValid model = not (model.boards == Just [] || dupBoards model || Maybe.map (\p -> p.max_options < 1 || p.max_options > numPollOptions model) model.poll == Just True) + + +type Msg + = Locked Bool + | Hidden Bool + | Private Bool + | Nolastmod Bool + | Content TP.Msg + | Title String + | BoardDel Int + | BoardSearch (A.Msg GApi.ApiBoardResult) + | PollEnabled Bool + | PollQ String + | PollMax Int + | PollOpt Int String + | PollRem Int + | PollAdd + | Submit + | Submitted GApi.Response + + +update : Msg -> Model -> (Model, Cmd Msg) +update msg model = + case msg of + Locked b -> ({ model | locked = b }, Cmd.none) + Hidden b -> ({ model | hidden = b }, Cmd.none) + Private b -> ({ model | private = b }, Cmd.none) + Nolastmod b -> ({ model | nolastmod=b }, Cmd.none) + Content m -> let (nm,nc) = TP.update m model.msg in ({ model | msg = nm }, Cmd.map Content nc) + Title s -> ({ model | title = Just s }, Cmd.none) + PollEnabled b -> ({ model | pollEnabled = b, poll = if model.poll == Nothing then Just { question = "", max_options = 1, options = ["",""] } else model.poll }, Cmd.none) + PollQ s -> ({ model | poll = Maybe.map (\p -> { p | question = s}) model.poll }, Cmd.none) + PollMax n -> ({ model | poll = Maybe.map (\p -> { p | max_options = n}) model.poll }, Cmd.none) + PollOpt n s -> ({ model | poll = Maybe.map (\p -> { p | options = modidx n (always s) p.options }) model.poll }, Cmd.none) + PollRem n -> ({ model | poll = Maybe.map (\p -> { p | options = delidx n p.options }) model.poll }, Cmd.none) + PollAdd -> ({ model | poll = Maybe.map (\p -> { p | options = p.options ++ [""] }) model.poll }, Cmd.none) + + BoardDel i -> ({ model | boards = Maybe.map (\b -> delidx i b) model.boards }, Cmd.none) + BoardSearch m -> + let (nm, c, res) = A.update searchConfig m model.boardAdd + in case res of + Nothing -> ({ model | boardAdd = nm }, c) + Just r -> ({ model | boardAdd = A.clear nm, boards = Maybe.map (\b -> b ++ [r]) model.boards }, c) + + Submit -> ({ model | state = Api.Loading }, Api.post "/t/edit.json" (GDE.encode (encode model)) Submitted) + Submitted (GApi.Redirect s) -> (model, load s) + Submitted r -> ({ model | state = Api.Error r }, Cmd.none) + + +view : Model -> Html Msg +view model = + let + thread = model.tid == Nothing || model.num == Just 1 + + board n bd = + li [] <| + [ text "[" + , a [ href "#", onClickD (BoardDel n), tabindex 10 ] [ text "remove" ] + , text "] " + , text (Maybe.withDefault "" (lookup bd.btype boardTypes)) + ] ++ case (bd.btype, bd.title) of + (_, Just title) -> + [ b [ class "grayedout" ] [ text " > " ] + , a [ href <| bd.btype ++ String.fromInt bd.iid ] [ text title ] + ] + ("u", _) -> [ b [ class "grayedout" ] [ text " > " ], text <| bd.btype ++ String.fromInt bd.iid ++ " (deleted)" ] + (_, _) -> [] + + boards () = + [ text "You can link this thread to multiple boards. Every visual novel, producer and user in the database has its own board," + , text " but you can also use the \"General Discussions\" and \"VNDB Discussions\" boards for threads that do not fit at a particular database entry." + , ul [ style "list-style-type" "none", style "margin" "10px" ] <| List.indexedMap board (Maybe.withDefault [] model.boards) + , A.view searchConfig model.boardAdd [placeholder "Add boards..."] + ] ++ + if model.boards == Just [] + then [ b [ class "standout" ] [ text "Please add at least one board." ] ] + else if dupBoards model + then [ b [ class "standout" ] [ text "List contains duplicates." ] ] + else [] + + pollOpt n p = + li [] + [ inputText "" p (PollOpt n) (style "width" "400px" :: placeholder ("Option #" ++ String.fromInt (n+1)) :: GDE.valPollOptions) + , if numPollOptions model > 2 + then a [ href "#", onClickD (PollRem n), tabindex 10 ] [ text "remove" ] + else text "" + ] + + poll () = + [ tr [ class "newpart" ] [ td [ colspan 2 ] [ text "" ] ] + , formField "" [ label [] [ inputCheck "" model.pollEnabled PollEnabled, text " Add poll" ] ] + ] ++ + case (model.pollEnabled, model.poll) of + (True, Just p) -> + [ if model.pollEdit + then formField "" [ b [ class "standout" ] [ text "Votes will be reset if any changes are made to these options!" ] ] + else text "" + , formField "pollq::Poll question" [ inputText "pollq" p.question PollQ (style "width" "400px" :: GDE.valPollQuestion) ] + , formField "Options" + [ ul [ style "list-style-type" "none", style "margin" "0px" ] <| List.indexedMap pollOpt p.options + , if numPollOptions model < 20 + then a [ href "#", onClickD PollAdd, tabindex 10 ] [ text "Add option" ] + else text "" + ] + , formField "" + [ inputNumber "" p.max_options PollMax <| GDE.valPollMax_Options ++ [ Html.Attributes.max <| String.fromInt <| List.length p.options ] + , text " Number of options people are allowed to choose." + ] + ] + (_, _) -> [] + + + in + form_ Submit (model.state == Api.Loading) + [ div [ class "mainbox" ] + [ h1 [] [ text <| if model.tid == Nothing then "Create new thread" else "Edit post" ] + , table [ class "formtable" ] <| + [ if thread + then formField "title::Thread title" [ inputText "title" (Maybe.withDefault "" model.title) Title (style "width" "400px" :: GDE.valTitle) ] + else formField "Topic" [ a [ href <| "/t" ++ String.fromInt (Maybe.withDefault 0 model.tid) ] [ text (Maybe.withDefault "" model.title) ] ] + , if thread && model.can_mod + then formField "" [ label [] [ inputCheck "" model.locked Locked, text " Locked" ] ] + else text "" + , if model.can_mod + then formField "" [ label [] [ inputCheck "" model.hidden Hidden, text " Hidden" ] ] + else text "" + , if thread && model.can_private + then formField "" [ label [] [ inputCheck "" model.private Private, text " Private" ] ] + else text "" + , if model.tid /= Nothing && model.can_mod + then formField "" [ label [] [ inputCheck "" model.nolastmod Nolastmod, text " Don't update last modification timestamp" ] ] + else text "" + , if thread + then formField "boardadd::Boards" (boards ()) + else text "" + , tr [ class "newpart" ] [ td [ colspan 2 ] [ text "" ] ] + , formField "msg::Message" + [ TP.view "msg" model.msg Content 700 ([rows 12, cols 50] ++ GDE.valMsg) + [ b [ class "standout" ] [ text " (English please!) " ] + , a [ href "/d9#3" ] [ text "Formatting" ] + ] + ] + ] ++ if thread then poll () else [] + ] + , div [ class "mainbox" ] + [ fieldset [ class "submit" ] [ submitButton "Submit" model.state (isValid model) ] ] + ] |