summaryrefslogtreecommitdiff
path: root/elm/Discussions/Poll.elm
blob: 04761530fd849c70dbb5e9b8c7344f3a7941d9db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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 }
      , GDP.send { 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
      ]
    ]
  ]