diff options
author | Yorhel <git@yorhel.nl> | 2019-12-01 09:22:20 +0100 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2019-12-01 13:40:59 +0100 |
commit | 165b62acc991cbf30cb721af27b04a066dbc9413 (patch) | |
tree | 34cbe7fef4a020fe121ddf1026dd6be13e9498a2 /elm/Discussions/Poll.elm | |
parent | b2ba46a9a0900d2b9d62a5ff84c4d4c9d9780abc (diff) |
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/
Diffstat (limited to 'elm/Discussions/Poll.elm')
-rw-r--r-- | elm/Discussions/Poll.elm | 139 |
1 files changed, 139 insertions, 0 deletions
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 + ] + ] + ] |