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 }
, 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
]
]
]
|