From 165b62acc991cbf30cb721af27b04a066dbc9413 Mon Sep 17 00:00:00 2001 From: Yorhel Date: Sun, 1 Dec 2019 09:22:20 +0100 Subject: v2rw: Convert thread display + poll voting I did not reimplement the 'poll_recast' and 'poll_preview' settings, these actions are now always permitted. Updated CSS a little bit to highlight the linked post and fix the double border at the bottom. The nice thing about the sql_visible_threads() function I wrote earlier is that is can also be used for access control on a single thread. More code re-use. \o/ --- elm/Discussions/Poll.elm | 139 +++++++++++++++++++++++++++++++++++++++++++++ elm/DocEdit.elm | 2 +- elm/Lib/Html.elm | 8 +-- elm/StaffEdit/Main.elm | 2 +- elm/UList/ManageLabels.elm | 2 +- elm/User/Edit.elm | 2 +- elm/User/Login.elm | 2 +- elm/User/PassReset.elm | 2 +- elm/User/PassSet.elm | 2 +- elm/User/Register.elm | 2 +- 10 files changed, 151 insertions(+), 12 deletions(-) create mode 100644 elm/Discussions/Poll.elm (limited to 'elm') diff --git a/elm/Discussions/Poll.elm b/elm/Discussions/Poll.elm new file mode 100644 index 00000000..40037ba8 --- /dev/null +++ b/elm/Discussions/Poll.elm @@ -0,0 +1,139 @@ +module Discussions.Poll exposing (main) + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Browser +import Lib.Html exposing (..) +import Lib.TextPreview as TP +import Lib.Api as Api +import Gen.Api as GApi +import Gen.DiscussionsPoll as GDP + + +main : Program GDP.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 + , data : GDP.Recv + , voted : Bool + } + + +init : GDP.Recv -> Model +init d = + { state = Api.Normal + -- Remove own vote from the count, so we can dynamically adjust the counter + , data = { d | options = List.map (\o -> { o | votes = if o.my then o.votes - 1 else o.votes }) d.options } + , voted = List.any (\o -> o.my) d.options + } + +type Msg + = Preview + | Vote Int Bool + | Submit + | Submitted GApi.Response + + +toomany : Model -> Bool +toomany model = List.length (List.filter (\o -> o.my) model.data.options) > model.data.max_options + +update : Msg -> Model -> (Model, Cmd Msg) +update msg model = + case msg of + Preview -> + let d = model.data + nd = { d | preview = True } + in ({ model | data = nd }, Cmd.none) + + Vote n b -> + let d = model.data + nd = { d | options = List.map (\o -> { o | my = if n == o.id then b else o.my && d.max_options > 1 }) d.options } + in ({ model | data = nd }, Cmd.none) + + Submit -> + if toomany model then (model, Cmd.none) + else + ( { model | state = Api.Loading } + , Api.post "/t/pollvote.json" (GDP.encode { tid = model.data.tid, options = List.filterMap (\o -> if o.my then Just o.id else Nothing) model.data.options }) Submitted + ) + + Submitted (GApi.Success) -> + let d = model.data + v = List.any (\o -> o.my) model.data.options + nd = { d | num_votes = model.data.num_votes + + case (model.voted, v) of + (True, False) -> -1 + (False, True) -> 1 + _ -> 0 } + in ({ model | state = Api.Normal, voted = v, data = nd }, Cmd.none) + Submitted r -> ({ model | state = Api.Error r }, Cmd.none) + + +view : Model -> Html Msg +view model = + let + cvotes = model.data.num_votes + (if not model.voted && List.any (\o -> o.my) model.data.options then 1 else 0) + nvotes o = if o.my then o.votes + 1 else o.votes + max = toFloat <| Maybe.withDefault 1 <| List.maximum <| List.map nvotes model.data.options + + opt o = + tr [ classList [("odd", o.my)] ] + [ td [ class "tc1" ] + [ label [] + [ if not model.data.can_vote + then text "" + else if model.data.max_options == 1 + then inputRadio "vote" o.my (Vote o.id) + else inputCheck "" o.my (Vote o.id) + , span [ class "option", classList [("own", o.my)] ] [ text o.option ] + ] + ] + , if model.data.preview || model.voted + then td [ class "tc2" ] + [ div [ class "graph", style "width" (String.fromFloat (toFloat (nvotes o) / max * 200) ++ "px") ] [ text " " ] + , div [ class "number" ] [ text <| String.fromInt (nvotes o) ] + ] + else td [ class "tc2", colspan 2 ] [] + , if model.data.preview || model.voted + then td [ class "tc3" ] + [ let pc = toFloat (nvotes o) / toFloat cvotes * 100 + in text <| String.fromInt (truncate pc) ++ "%" ] + else text "" + ] + in + form_ Submit (model.state == Api.Loading) + [ div [ class "mainbox" ] + [ h1 [] [ text model.data.question ] + , table [ class "votebooth" ] + [ if model.data.can_vote && model.data.max_options > 1 + then thead [] [ tr [] [ td [ colspan 3 ] [ i [] [ text <| "You may choose up to " ++ String.fromInt model.data.max_options ++ " options" ] ] ] ] + else text "" + , tfoot [] [ tr [] + [ td [ class "tc1" ] + [ if model.data.can_vote + then submitButton "Vote" model.state True + else b [ class "standout" ] [ text "You must be logged in to be able to vote." ] + , if toomany model + then b [ class "standout" ] [ text "Too many options selected." ] + else text "" + ] + , td [ class "tc2" ] + [ if model.data.num_votes == 0 + then i [] [ text "Nobody voted yet" ] + else if model.data.preview || model.voted + then text <| (String.fromInt model.data.num_votes) ++ (if model.data.num_votes == 1 then " vote total" else " votes total") + else a [ href "#", onClickD Preview ] [ text "View results" ] + ] + ] ] + , tbody [] <| List.map opt model.data.options + ] + ] + ] diff --git a/elm/DocEdit.elm b/elm/DocEdit.elm index ef4ce715..b9d70622 100644 --- a/elm/DocEdit.elm +++ b/elm/DocEdit.elm @@ -101,7 +101,7 @@ view model = , div [ class "mainbox" ] [ fieldset [ class "submit" ] [ Html.map Editsum (Editsum.view model.editsum) - , submitButton "Submit" model.state True False + , submitButton "Submit" model.state True ] ] ] diff --git a/elm/Lib/Html.elm b/elm/Lib/Html.elm index d75dced4..1e995f86 100644 --- a/elm/Lib/Html.elm +++ b/elm/Lib/Html.elm @@ -45,15 +45,15 @@ inputButton val onch attrs = -- Submit button with loading indicator and error message display -submitButton : String -> Api.State -> Bool -> Bool -> Html m -submitButton val state valid load = div [] - [ input [ type_ "submit", class "submit", tabindex 10, value val, disabled (state == Api.Loading || not valid || load) ] [] +submitButton : String -> Api.State -> Bool -> Html m +submitButton val state valid = div [] + [ input [ type_ "submit", class "submit", tabindex 10, value val, disabled (state == Api.Loading || not valid) ] [] , case state of Api.Error r -> p [] [ b [class "standout" ] [ text <| Api.showResponse r ] ] _ -> if valid then text "" else p [] [ b [class "standout" ] [ text "The form contains errors, please fix these before submitting. " ] ] - , if state == Api.Loading || load + , if state == Api.Loading then div [ class "spinner" ] [] else text "" ] diff --git a/elm/StaffEdit/Main.elm b/elm/StaffEdit/Main.elm index 9765f0c0..b7bef54a 100644 --- a/elm/StaffEdit/Main.elm +++ b/elm/StaffEdit/Main.elm @@ -222,7 +222,7 @@ view model = , div [ class "mainbox" ] [ fieldset [ class "submit" ] [ Html.map Editsum (Editsum.view model.editsum) - , submitButton "Submit" model.state (isValid model) False + , submitButton "Submit" model.state (isValid model) ] ] ] diff --git a/elm/UList/ManageLabels.elm b/elm/UList/ManageLabels.elm index 7f951389..c3e996f8 100644 --- a/elm/UList/ManageLabels.elm +++ b/elm/UList/ManageLabels.elm @@ -111,7 +111,7 @@ view model = , td [ colspan 3 ] [ a [ onClick Add ] [ text "New label" ] --, inputButton "Save changes" Noop [] - , submitButton "Save changes" model.state True False + , submitButton "Save changes" model.state True ] ] ] , tbody [] <| List.indexedMap item model.labels diff --git a/elm/User/Edit.elm b/elm/User/Edit.elm index 1a13cdb1..1a9b9a55 100644 --- a/elm/User/Edit.elm +++ b/elm/User/Edit.elm @@ -217,7 +217,7 @@ view model = ] , div [ class "mainbox" ] - [ fieldset [ class "submit" ] [ submitButton "Submit" model.state (not model.passNeq) False ] + [ fieldset [ class "submit" ] [ submitButton "Submit" model.state (not model.passNeq) ] , if not model.mailConfirm then text "" else div [ class "notice" ] [ text "A confirmation email has been sent to your new address. Your address will be updated after following the instructions in that mail." ] diff --git a/elm/User/Login.elm b/elm/User/Login.elm index bef69cb5..cc25d132 100644 --- a/elm/User/Login.elm +++ b/elm/User/Login.elm @@ -144,6 +144,6 @@ view model = in form_ Submit (model.state == Api.Loading) [ if model.insecure then changeBox else loginBox , div [ class "mainbox" ] - [ fieldset [ class "submit" ] [ submitButton "Submit" model.state True False ] + [ fieldset [ class "submit" ] [ submitButton "Submit" model.state True ] ] ] diff --git a/elm/User/PassReset.elm b/elm/User/PassReset.elm index ad265c87..c1b5b516 100644 --- a/elm/User/PassReset.elm +++ b/elm/User/PassReset.elm @@ -81,6 +81,6 @@ view model = ] ] , div [ class "mainbox" ] - [ fieldset [ class "submit" ] [ submitButton "Submit" model.state True False ] + [ fieldset [ class "submit" ] [ submitButton "Submit" model.state True ] ] ] diff --git a/elm/User/PassSet.elm b/elm/User/PassSet.elm index 3c5c50bc..bc5cc24d 100644 --- a/elm/User/PassSet.elm +++ b/elm/User/PassSet.elm @@ -84,6 +84,6 @@ view model = ] ] , div [ class "mainbox" ] - [ fieldset [ class "submit" ] [ submitButton "Submit" model.state True False ] + [ fieldset [ class "submit" ] [ submitButton "Submit" model.state True ] ] ] diff --git a/elm/User/Register.elm b/elm/User/Register.elm index d4749e47..add16418 100644 --- a/elm/User/Register.elm +++ b/elm/User/Register.elm @@ -100,6 +100,6 @@ view model = ] ] , div [ class "mainbox" ] - [ fieldset [ class "submit" ] [ submitButton "Submit" model.state True False ] + [ fieldset [ class "submit" ] [ submitButton "Submit" model.state True ] ] ] -- cgit v1.2.3