summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/style.css1
-rw-r--r--elm/Reviews/Vote.elm78
-rw-r--r--lib/VNWeb/Reviews/Elm.pm29
-rw-r--r--lib/VNWeb/Reviews/VNTab.pm13
-rw-r--r--util/updates/wip-reviews.sql5
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;