summaryrefslogtreecommitdiff
path: root/elm/Reviews/Edit.elm
blob: 925de9642b6a62b5dbfe1467d21a7371ee77d948 (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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
module Reviews.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.RDate as RDate
import Gen.Api as GApi
import Gen.ReviewsEdit as GRE
import Gen.ReviewsDelete as GRD


main : Program GRE.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
  , id          : Maybe String
  , vid         : Int
  , vntitle     : String
  , rid         : Maybe Int
  , spoiler     : Bool
  , locked      : Bool
  , isfull      : Bool
  , text        : TP.Model
  , releases    : List GRE.RecvReleases
  , delete      : Bool
  , delState    : Api.State
  , mod         : Bool
  }


init : GRE.Recv -> Model
init d =
  { state       = Api.Normal
  , id          = d.id
  , vid         = d.vid
  , vntitle     = d.vntitle
  , rid         = d.rid
  , spoiler     = d.spoiler
  , locked      = d.locked
  , isfull      = d.isfull
  , text        = TP.bbcode d.text
  , releases    = d.releases
  , delete      = False
  , delState    = Api.Normal
  , mod         = d.mod
  }


encode : Model -> GRE.Send
encode m =
  { id          = m.id
  , vid         = m.vid
  , rid         = m.rid
  , spoiler     = m.spoiler
  , locked      = m.locked
  , isfull      = m.isfull
  , text        = m.text.data
  }


type Msg
  = Release (Maybe Int)
  | Full Bool
  | Spoiler Bool
  | Locked Bool
  | Text TP.Msg
  | Submit
  | Submitted GApi.Response
  | Delete Bool
  | DoDelete
  | Deleted GApi.Response


update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    Release i  -> ({ model | rid      = i }, Cmd.none)
    Full b     -> ({ model | isfull   = b }, Cmd.none)
    Spoiler b  -> ({ model | spoiler  = b }, Cmd.none)
    Locked b   -> ({ model | locked   = b }, Cmd.none)
    Text m     -> let (nm,nc) = TP.update m model.text in ({ model | text = nm }, Cmd.map Text nc)

    Submit -> ({ model | state = Api.Loading }, GRE.send (encode model) Submitted)
    Submitted (GApi.Redirect s) -> (model, load s)
    Submitted r -> ({ model | state = Api.Error r }, Cmd.none)

    Delete b   -> ({ model | delete   = b }, Cmd.none)
    DoDelete -> ({ model | delState = Api.Loading }, GRD.send ({ id = Maybe.withDefault "" model.id }) Deleted)
    Deleted GApi.Success -> (model, load <| "/v" ++ String.fromInt model.vid)
    Deleted r -> ({ model | delState = Api.Error r }, Cmd.none)


showrel r = "[" ++ (RDate.format (RDate.expand r.released)) ++ " " ++ (String.join "," r.lang) ++ "] " ++ r.title ++ " (r" ++ String.fromInt r.id ++ ")"

view : Model -> Html Msg
view model =
  let minChars = if model.isfull then   1000 else 200
      maxChars = if model.isfull then 100000 else 800
      len      = String.length model.text.data
  in
  form_ Submit (model.state == Api.Loading)
  [ div [ class "mainbox" ]
    [ h1 [] [ text <| if model.id == Nothing then "Submit a review" else "Edit review" ]
    , p [] [ b [] [ text "Rules" ] ]
    , ul []
      [ li [] [ text "Submit only reviews you have written yourself!" ]
      , li [] [ text "Reviews must be in English." ]
      , li [] [ text "Try to be as objective as possible." ]
      , li [] [ text "If you have published the review elsewhere (e.g. a personal blog), feel free to include a link at the end of the review. Formatting tip: ", em [] [ text "[Originally published at <link>]" ] ]
      , li [] [ text "Your vote (if any) will be displayed alongside the review, even if you have marked your list as private." ]
      ]
    , br [] []
    ]
  , div [ class "mainbox" ]
    [ table [ class "formtable" ]
      [ formField "Subject" [ a [ href <| "/v"++String.fromInt model.vid ] [ text model.vntitle ] ]
      , formField ""
        [ inputSelect "" model.rid Release [style "width" "500px" ] <|
          (Nothing, "No release selected")
          :: List.map (\r -> (Just r.id, showrel r)) model.releases
          ++ if model.rid == Nothing || List.any (\r -> Just r.id == model.rid) model.releases then [] else [(model.rid, "Deleted or moved release: r"++Maybe.withDefault "" (Maybe.map String.fromInt model.rid))]
        , br [] []
        , text "You do not have to select a release, but indicating which release your review is based on gives more context."
        ]
      , tr [ class "newpart" ] [ td [ colspan 2 ] [ text "" ] ]
      , formField "Review type"
        [ label [] [ inputRadio "type" (model.isfull == False) (\_ -> Full False), b [] [ text " Mini review" ]
        , text <| " - Recommendation-style, maximum 800 characters." ]
        , br [] []
        , label [] [ inputRadio "type" (model.isfull == True ) (\_ -> Full True ), b [] [ text " Full review" ]
        , text " - Longer, more detailed." ]
        , br [] []
        , b [ class "grayedout" ] [ text "You can always switch between review types later." ]
        ]
      , tr [ class "newpart" ] [ td [ colspan 2 ] [ text "" ] ]
      , formField ""
        [ label [] [ inputCheck "" model.spoiler Spoiler, text " This review contains spoilers." ]
        , br [] []
        , b [ class "grayedout" ] [ text "You do not have to check this option if all spoilers in your review are marked with [spoiler] tags." ]
        ]
      , if not model.mod then text "" else
        formField "" [ label [] [ inputCheck "" model.locked Locked, text " Locked for commenting." ] ]
      , tr [ class "newpart" ] [ td [ colspan 2 ] [ text "" ] ]
      , formField "text::Review"
        [ TP.view "sum" model.text Text 700 ([rows (if model.isfull then 30 else 10), cols 50] ++ GRE.valText)
          [ a [ href "/d9#3" ] [ text "BBCode formatting supported" ] ]
        , div [ style "width" "700px", style "text-align" "right" ] <|
          let num c s = if c then b [ class " standout" ] [ text s ] else text s
          in
          [ num (len < minChars) (String.fromInt minChars)
          , text " / "
          , b [] [ text (String.fromInt len) ]
          , text " / "
          , num (len > maxChars) (if model.isfull then "∞" else String.fromInt maxChars)
          ]
        ]
      ]
    ]
  , div [ class "mainbox" ]
    [ fieldset [ class "submit" ]
      [ submitButton "Submit" model.state (len <= maxChars && len >= minChars)
      ]
    ]
  , if model.id == Nothing then text "" else
    div [ class "mainbox" ]
    [ h1 [] [ text "Delete review" ]
    , table [ class "formtable" ] [ formField ""
      [ label [] [ inputCheck "" model.delete Delete, text " Delete this review." ]
      , if not model.delete then text "" else span []
        [ br [] []
        , b [ class "standout" ] [ text "WARNING:" ]
        , text " Deleting this review is a permanent action and can not be reverted!"
        , br [] []
        , br [] []
        , inputButton "Confirm delete" DoDelete []
        , case model.delState of
            Api.Loading -> span [ class "spinner" ] []
            Api.Error e -> b [ class "standout" ] [ text <| Api.showResponse e ]
            Api.Normal  -> text ""
        ]
      ] ]
    ]
  ]