diff options
-rw-r--r-- | data/style.css | 1 | ||||
-rw-r--r-- | elm/Reviews/Vote.elm | 78 | ||||
-rw-r--r-- | lib/VNWeb/Reviews/Elm.pm | 29 | ||||
-rw-r--r-- | lib/VNWeb/Reviews/VNTab.pm | 13 | ||||
-rw-r--r-- | util/updates/wip-reviews.sql | 5 |
5 files changed, 119 insertions, 7 deletions
diff --git a/data/style.css b/data/style.css index be50889d..8f9d8ee2 100644 --- a/data/style.css +++ b/data/style.css @@ -525,6 +525,7 @@ div#vntags { margin: 0 30px 0 30px; border-top: 1px solid $bo .reviewbox > div:first-child > span:first-child { font-weight: bold } .reviewbox > div:nth-child(2) { box-sizing: border-box; padding: 5px 0 } .reviewbox > div:last-child { display: flex; justify-content: space-between; border-top: 1px solid $border$ } +.reviewbox .myvote { font-weight: bold } /***** Vote stats ****/ diff --git a/elm/Reviews/Vote.elm b/elm/Reviews/Vote.elm new file mode 100644 index 00000000..cf63fe71 --- /dev/null +++ b/elm/Reviews/Vote.elm @@ -0,0 +1,78 @@ +module Reviews.Vote exposing (main) + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Browser +import Lib.Util exposing (..) +import Lib.Html exposing (..) +import Lib.Api as Api +import Gen.Api as GApi +import Gen.ReviewsVote as GRV + + +main : Program GRV.Recv Model Msg +main = Browser.element + { init = \d -> (init d, Cmd.none) + , subscriptions = always Sub.none + , view = view + , update = update + } + +type alias Model = + { state : Api.State + , id : String + , my : Maybe Bool + , can : Bool + , up : Int + , down : Int + } + +init : GRV.Recv -> Model +init d = + { state = Api.Normal + , id = d.id + , my = d.my + , can = d.can + , up = d.up + , down = d.down + } + +type Msg + = Vote Bool + | Saved GApi.Response + + +update : Msg -> Model -> (Model, Cmd Msg) +update msg model = + case msg of + Vote b -> + let nm = case (model.my, b) of + (Nothing, True) -> { model | my = Just b, up = model.up+1 } + (Nothing, False) -> { model | my = Just b , down = model.down+1 } + (Just True, False) -> { model | my = Just b, up = model.up-1, down = model.down+1 } + (Just False, True) -> { model | my = Just b, up = model.up+1, down = model.down-1 } + (Just True, True) -> { model | my = Nothing, up = model.up-1 } + (Just False, False) -> { model | my = Nothing , down = model.down-1 } + in ({ nm | state = Api.Loading }, GRV.send { id = nm.id, my = nm.my } Saved) + + Saved GApi.Success -> ({ model | state = Api.Normal }, Cmd.none) + Saved e -> ({ model | state = Api.Error e }, Cmd.none) + + +view : Model -> Html Msg +view model = + let but opt lbl = + if not model.can + then span [] [ text lbl ] + else a [ href "#", onClickD (Vote opt), classList [("votebut", True), ("myvote", model.my == Just opt)] ] [ text lbl ] + in + span [] + [ case model.state of + Api.Loading -> span [ class "spinner" ] [] + Api.Error e -> b [ class "standout" ] [ text (Api.showResponse e) ] + Api.Normal -> if model.can && model.my == Nothing then text "Was this review helpful? " else text "" + , but True ("๐ " ++ String.fromInt model.up) + , text " " + , but False ("๐ " ++ String.fromInt model.down) + ] diff --git a/lib/VNWeb/Reviews/Elm.pm b/lib/VNWeb/Reviews/Elm.pm new file mode 100644 index 00000000..527da239 --- /dev/null +++ b/lib/VNWeb/Reviews/Elm.pm @@ -0,0 +1,29 @@ +package VNWeb::Reviews::Elm; + +use VNWeb::Prelude; + +my $VOTE = { + id => { vndbid => 'w' }, + my => { required => 0, jsonbool => 1 }, + can => { _when => 'out', anybool => 1 }, + up => { _when => 'out', uint => 1 }, + down => { _when => 'out', uint => 1 }, +}; + +my $VOTE_IN = form_compile in => $VOTE; +our $VOTE_OUT = form_compile out => $VOTE; + +elm_api ReviewsVote => $VOTE_OUT, $VOTE_IN, sub { + return elm_Unauth if !auth; + my($data) = @_; + my %id = (uid => auth->uid, id => $data->{id}); + my %val = (vote => $data->{my}?1:0, date => sql 'NOW()'); + tuwf->dbExeci( + defined $data->{my} + ? sql 'INSERT INTO reviews_votes', {%id,%val}, 'ON CONFLICT (id,uid) DO UPDATE SET', \%val + : sql 'DELETE FROM reviews_votes WHERE', \%id + ); + elm_Success +}; + +1; diff --git a/lib/VNWeb/Reviews/VNTab.pm b/lib/VNWeb/Reviews/VNTab.pm index a1871470..52fd62a2 100644 --- a/lib/VNWeb/Reviews/VNTab.pm +++ b/lib/VNWeb/Reviews/VNTab.pm @@ -10,12 +10,14 @@ sub reviews_ { # TODO: Order my $lst = tuwf->dbAlli( - 'SELECT r.id, r.rid, r.summary, r.text <> \'\' AS isfull, r.spoiler, uv.vote, s.up, s.down + 'SELECT r.id, r.rid, r.summary, r.text <> \'\' AS isfull, r.spoiler, uv.vote + , COALESCE(s.up,0) AS up, COALESCE(s.down,0) AS down, rv.vote AS my , ', sql_totime('r.date'), 'AS date, ', sql_user(), ' FROM reviews r LEFT JOIN users u ON r.uid = u.id LEFT JOIN ulist_vns uv ON uv.uid = r.uid AND uv.vid = r.vid LEFT JOIN (SELECT id, COUNT(*) FILTER(WHERE vote), COUNT(*) FILTER(WHERE NOT vote) FROM reviews_votes GROUP BY id) AS s(id,up,down) ON s.id = r.id + LEFT JOIN reviews_votes rv ON rv.uid =', \auth->uid, ' AND rv.id = r.id WhERE r.vid =', \$v->{id} ); @@ -27,7 +29,7 @@ sub reviews_ { my $r = $_; div_ sub { span_ sub { txt_ 'By '; user_ $r; txt_ ' on '.fmtdate $r->{date}, 'compact' }; - a_ href => "/r$r->{rid}", "release" if $r->{rid}; + a_ href => "/r$r->{rid}", "r$r->{rid}" if $r->{rid}; span_ "Vote: ".fmtvote($r->{vote}) if $r->{vote}; }; div_ sub { @@ -44,17 +46,18 @@ sub reviews_ { label_ class => 'review_spoil', sub { input_ type => 'checkbox', class => 'visuallyhidden'; div_ sub { lit_ bb2html $r->{summary} }; - span_ class => 'fake_link', 'This review contains spoilers, click here to view.'; + span_ class => 'fake_link', 'This review contains spoilers, click to view.'; } } else { lit_ bb2html $r->{summary}; } }; - # placeholder div_ sub { span_ '' if !$r->{isfull}; a_ href => "/$r->{id}", 'Full review ยป' if $r->{isfull}; - span_ sprintf '๐ %d ๐ %d', $r->{up}||0, $r->{down}||0; + elm_ 'Reviews.Vote' => $VNWeb::Reviews::Elm::VOTE_OUT, { %$r, can => !!auth }, sub { + span_ sprintf '๐ %d ๐ %d', $r->{up}, $r->{down}; + }; }; } for @$lst; } diff --git a/util/updates/wip-reviews.sql b/util/updates/wip-reviews.sql index fbf588d3..c9699ab2 100644 --- a/util/updates/wip-reviews.sql +++ b/util/updates/wip-reviews.sql @@ -21,8 +21,9 @@ CREATE TABLE reviews_votes ( vote boolean NOT NULL -- true = upvote, false = downvote ); -CREATE UNIQUE INDEX reviews_vid_uid ON reviews (vid,uid); -CREATE INDEX reviews_uid ON reviews (uid); +CREATE UNIQUE INDEX reviews_vid_uid ON reviews (vid,uid); +CREATE INDEX reviews_uid ON reviews (uid); +CREATE UNIQUE INDEX reviews_votes_id_uid ON reviews_votes (id,uid); ALTER TABLE reviews ADD CONSTRAINT reviews_vid_fkey FOREIGN KEY (vid) REFERENCES vn (id) ON DELETE CASCADE; ALTER TABLE reviews ADD CONSTRAINT reviews_uid_fkey FOREIGN KEY (uid) REFERENCES users (id) ON DELETE SET DEFAULT; |