Add voting icons
This commit is contained in:
+16
-2
@@ -37,10 +37,24 @@
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<a hx-post="/posts/{{post.id}}/upvotes" hx-target='.post-preview-details' hx-swap='outerHTML'>{{post.upvotes}}</a>
|
||||
<a hx-post="/posts/{{post.id}}/upvotes" hx-target='.post-preview-details' hx-swap='outerHTML'>
|
||||
{{post.upvotes}}
|
||||
{% if post.upvoted %}
|
||||
<img src="/static/upvote_full.svg" />
|
||||
{% else %}
|
||||
<img src="/static/upvote_empty.svg" />
|
||||
{% end %}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a hx-post="/posts/{{post.id}}/downvotes" hx-target='.post-preview-details' hx-swap='outerHTML'>{{post.downvotes}}</a>
|
||||
<a hx-post="/posts/{{post.id}}/downvotes" hx-target='.post-preview-details' hx-swap='outerHTML'>
|
||||
{{post.downvotes}}
|
||||
{% if post.downvoted %}
|
||||
<img src="/static/upvote_full.svg" class="rotate180" />
|
||||
{% else %}
|
||||
<img src="/static/upvote_empty.svg" class="rotate180" />
|
||||
{% end %}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<span>by <a href="/users/{{post.author}}">{{post.author}}</a> at {{post.created_}}</span>
|
||||
|
||||
+55
-37
@@ -107,15 +107,19 @@ void free_endpoints(void)
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
void respond_with_post_details_html(ResponseBuilder *b, int details_post_id)
|
||||
void respond_with_post_details_html(ResponseBuilder *b, int details_post_id, string login_username)
|
||||
{
|
||||
sqlite3_stmt *stmt = sqlite3_utils_prepare(db,
|
||||
"SELECT P.author, "
|
||||
" STRFTIME('%d/%m/%Y, %H.%M', P.created) AS created_, "
|
||||
" (SELECT COUNT(*) FROM Comments AS C WHERE C.parent = P.id) AS num_comments, "
|
||||
" (SELECT COUNT(*) FROM PostVotes AS PV WHERE PV.post = P.id AND PV.up=1) AS upvotes, "
|
||||
" (SELECT COUNT(*) FROM PostVotes AS PV WHERE PV.post = P.id AND PV.up=0) AS downvotes "
|
||||
"FROM Posts AS P");
|
||||
" (SELECT COUNT(*) FROM PostVotes AS PV WHERE PV.post = P.id AND PV.up=0) AS downvotes, "
|
||||
" EXISTS(SELECT * FROM PostVotes AS PV WHERE PV.post = P.id AND PV.up=1 AND PV.user=:s) AS upvoted, "
|
||||
" EXISTS(SELECT * FROM PostVotes AS PV WHERE PV.post = P.id AND PV.up=0 AND PV.user=:s) AS downvoted "
|
||||
"FROM Posts AS P",
|
||||
login_username,
|
||||
login_username);
|
||||
if (stmt == NULL) {
|
||||
status_line(b, 500);
|
||||
return;
|
||||
@@ -125,36 +129,46 @@ void respond_with_post_details_html(ResponseBuilder *b, int details_post_id)
|
||||
int num_comments;
|
||||
int upvotes;
|
||||
int downvotes;
|
||||
if (sqlite3_utils_fetch(stmt, "ssiii", &author, &created, &num_comments, &upvotes, &downvotes)) {
|
||||
int upvoted;
|
||||
int downvoted;
|
||||
if (sqlite3_utils_fetch(stmt, "ssiiiii", &author, &created, &num_comments, &upvotes, &downvotes, &upvoted, &downvoted)) {
|
||||
sqlite3_finalize(stmt);
|
||||
status_line(b, 500);
|
||||
return;
|
||||
}
|
||||
status_line(b, 200);
|
||||
append_content_f(b,
|
||||
"<div class='post-preview-details'> \
|
||||
<table> \
|
||||
<tr> \
|
||||
<td> \
|
||||
<a hx-post='/posts/%d/upvotes' hx-target='.post-preview-details' hx-swap='outerHTML'>%d</a> \
|
||||
</td> \
|
||||
<td> \
|
||||
<a hx-post='/posts/%d/downvotes' hx-target='.post-preview-details' hx-swap='outerHTML'>%d</a> \
|
||||
</td> \
|
||||
<td> \
|
||||
<span>by <a href='/users/%.*s'>%.*s</a> at %.*s</span> \
|
||||
</td> \
|
||||
<td> \
|
||||
<span>%d comments</span> \
|
||||
</td> \
|
||||
</tr> \
|
||||
</table> \
|
||||
</div>",
|
||||
details_post_id, upvotes, details_post_id, downvotes,
|
||||
(int) author.size, author.data,
|
||||
(int) author.size, author.data,
|
||||
(int) created.size, created.data,
|
||||
num_comments);
|
||||
"<div class='post-preview-details'>\
|
||||
<table>\
|
||||
<tr>\
|
||||
<td>\
|
||||
<a hx-post='/posts/%d/upvotes' hx-target='.post-preview-details' hx-swap='outerHTML'>\
|
||||
%d\
|
||||
<img src='%s' />\
|
||||
</a>\
|
||||
</td>\
|
||||
<td>\
|
||||
<a hx-post='/posts/%d/downvotes' hx-target='.post-preview-details' hx-swap='outerHTML'>\
|
||||
%d\
|
||||
<img src='%s' class='rotate180' />\
|
||||
</a>\
|
||||
</td>\
|
||||
<td>\
|
||||
<span>by <a href='/users/%.*s'>%.*s</a> at %.*s</span>\
|
||||
</td>\
|
||||
<td>\
|
||||
<span>%d comments</span>\
|
||||
</td>\
|
||||
</tr>\
|
||||
</table>\
|
||||
</div>",
|
||||
details_post_id, upvotes, upvoted ? "/static/upvote_full.svg" : "/static/upvote_empty.svg",
|
||||
details_post_id, downvotes, downvoted ? "/static/upvote_full.svg" : "/static/upvote_empty.svg",
|
||||
(int) author.size, author.data,
|
||||
(int) author.size, author.data,
|
||||
(int) created.size, created.data,
|
||||
num_comments
|
||||
);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
@@ -175,23 +189,23 @@ static bool voting_endpoints(Request request, ResponseBuilder *b, string login_u
|
||||
|
||||
// Create the UP row
|
||||
if (sqlite3_utils_exec(db, "INSERT INTO PostVotes(user, post, up) VALUES (:s, :i, :i)", login_username, vote_post_id, up)) {
|
||||
respond_with_post_details_html(b, vote_post_id);
|
||||
respond_with_post_details_html(b, vote_post_id, login_username);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we failed, maybe we need to delete the current UP row
|
||||
if (sqlite3_utils_exec(db, "DELETE FROM PostVotes WHERE up=:i AND user=:s AND post=:i", up, login_username, vote_post_id) && sqlite3_changes(db) > 0) {
|
||||
respond_with_post_details_html(b, vote_post_id);
|
||||
respond_with_post_details_html(b, vote_post_id, login_username);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we failed, maybe we need to invert the UP row
|
||||
if (sqlite3_utils_exec(db, "UPDATE PostVotes SET up=:i WHERE user=:s AND post=:i", up, login_username, vote_post_id)) {
|
||||
respond_with_post_details_html(b, vote_post_id);
|
||||
respond_with_post_details_html(b, vote_post_id, login_username);
|
||||
return true;
|
||||
}
|
||||
|
||||
respond_with_post_details_html(b, vote_post_id);
|
||||
respond_with_post_details_html(b, vote_post_id, login_username);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -206,23 +220,23 @@ static bool voting_endpoints(Request request, ResponseBuilder *b, string login_u
|
||||
|
||||
// Create the UP row
|
||||
if (sqlite3_utils_exec(db, "INSERT INTO PostVotes(user, post, up) VALUES (:s, :i, :i)", login_username, vote_post_id, up)) {
|
||||
respond_with_post_details_html(b, vote_post_id);
|
||||
respond_with_post_details_html(b, vote_post_id, login_username);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we failed, maybe we need to delete the current UP row
|
||||
if (sqlite3_utils_exec(db, "DELETE FROM PostVotes WHERE up=:i AND user=:s AND post=:i", up, login_username, vote_post_id) && sqlite3_changes(db) > 0) {
|
||||
respond_with_post_details_html(b, vote_post_id);
|
||||
respond_with_post_details_html(b, vote_post_id, login_username);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we failed, maybe we need to invert the UP row
|
||||
if (sqlite3_utils_exec(db, "UPDATE PostVotes SET up=:i WHERE user=:s AND post=:i", up, login_username, vote_post_id)) {
|
||||
respond_with_post_details_html(b, vote_post_id);
|
||||
respond_with_post_details_html(b, vote_post_id, login_username);
|
||||
return true;
|
||||
}
|
||||
|
||||
respond_with_post_details_html(b, vote_post_id);
|
||||
respond_with_post_details_html(b, vote_post_id, login_username);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -394,8 +408,12 @@ void respond(Request request, ResponseBuilder *b)
|
||||
" STRFTIME('%d/%m/%Y, %H.%M', P.created) AS created_, "
|
||||
" (SELECT COUNT(*) FROM Comments AS C WHERE C.parent = P.id) AS num_comments, "
|
||||
" (SELECT COUNT(*) FROM PostVotes AS PV WHERE PV.post = P.id AND PV.up=1) AS upvotes, "
|
||||
" (SELECT COUNT(*) FROM PostVotes AS PV WHERE PV.post = P.id AND PV.up=0) AS downvotes "
|
||||
"FROM Posts AS P");
|
||||
" (SELECT COUNT(*) FROM PostVotes AS PV WHERE PV.post = P.id AND PV.up=0) AS downvotes, "
|
||||
" EXISTS(SELECT * FROM PostVotes AS PV WHERE PV.post = P.id AND PV.up=1 AND PV.user = :s) AS upvoted, "
|
||||
" EXISTS(SELECT * FROM PostVotes AS PV WHERE PV.post = P.id AND PV.up=0 AND PV.user = :s) AS downvoted "
|
||||
"FROM Posts AS P",
|
||||
login_username,
|
||||
login_username);
|
||||
if (stmt == NULL) {
|
||||
status_line(b, 500);
|
||||
return;
|
||||
|
||||
+46
-4
@@ -96,8 +96,9 @@ form input[type=submit] {
|
||||
|
||||
.post-preview {
|
||||
margin-top: 10px;
|
||||
border: 1px solid #ccc;
|
||||
border: 1px solid #404040;
|
||||
border-radius: 3px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.post-preview-title {
|
||||
@@ -105,27 +106,50 @@ form input[type=submit] {
|
||||
}
|
||||
|
||||
.post-preview-title a {
|
||||
color: white;
|
||||
color: #333;
|
||||
font-size: 30px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.post-preview-details {
|
||||
height: 40px;
|
||||
background: #eee;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
|
||||
.post-preview-details table {
|
||||
border-collapse: collapse;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.post-preview-details table tr {
|
||||
}
|
||||
|
||||
.post-preview-details table td {
|
||||
vertical-align: middle;
|
||||
display: table-cell;
|
||||
height: 100%;
|
||||
border-right: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.post-preview-details tr td:nth-child(1) {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.post-preview-details tr td:nth-child(2) {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.post-preview-details tr td:last-child {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.post-preview-details span,
|
||||
.post-preview-details a {
|
||||
color: #ccc;
|
||||
color: black;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
@@ -142,3 +166,21 @@ form input[type=submit] {
|
||||
border-radius: 3px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* === MISC ======================================================== */
|
||||
|
||||
.rotate90 {
|
||||
-webkit-transform: rotate(90deg);
|
||||
-moz-transform: rotate(90deg);
|
||||
-o-transform: rotate(90deg);
|
||||
-ms-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.rotate180 {
|
||||
-webkit-transform: rotate(180deg);
|
||||
-moz-transform: rotate(180deg);
|
||||
-o-transform: rotate(180deg);
|
||||
-ms-transform: rotate(180deg);
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m12.781 2.375c-.381-.475-1.181-.475-1.562 0l-8 10c-.24.301-.286.712-.12 1.059.167.345.516.566.901.566h2 2v3 4c0 .553.447 1 1 1h6c.553 0 1-.447 1-1v-5-2h2 2c.385 0 .734-.221.901-.566.166-.347.12-.758-.12-1.059zm2.219 9.625h-1v1 3 4h-4v-3-4-1h-1-2.919l5.919-7.399 5.919 7.399z"/></svg>
|
||||
|
After Width: | Height: | Size: 375 B |
@@ -0,0 +1 @@
|
||||
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="m4 14h2 2v3 4c0 .553.447 1 1 1h6c.553 0 1-.447 1-1v-5-2h1 3c.385 0 .734-.221.901-.566.166-.347.12-.758-.12-1.059l-8-10c-.381-.475-1.181-.475-1.562 0l-8 10c-.24.301-.286.712-.12 1.059.167.345.516.566.901.566z"/></svg>
|
||||
|
After Width: | Height: | Size: 308 B |
Reference in New Issue
Block a user