Add voting icons
This commit is contained in:
+16
-2
@@ -37,10 +37,24 @@
|
|||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<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>
|
||||||
<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>
|
||||||
<td>
|
<td>
|
||||||
<span>by <a href="/users/{{post.author}}">{{post.author}}</a> at {{post.created_}}</span>
|
<span>by <a href="/users/{{post.author}}">{{post.author}}</a> at {{post.created_}}</span>
|
||||||
|
|||||||
+36
-18
@@ -107,15 +107,19 @@ void free_endpoints(void)
|
|||||||
sqlite3_close(db);
|
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,
|
sqlite3_stmt *stmt = sqlite3_utils_prepare(db,
|
||||||
"SELECT P.author, "
|
"SELECT P.author, "
|
||||||
" STRFTIME('%d/%m/%Y, %H.%M', P.created) AS created_, "
|
" 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 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=1) AS upvotes, "
|
||||||
" (SELECT COUNT(*) FROM PostVotes AS PV WHERE PV.post = P.id AND PV.up=0) AS downvotes "
|
" (SELECT COUNT(*) FROM PostVotes AS PV WHERE PV.post = P.id AND PV.up=0) AS downvotes, "
|
||||||
"FROM Posts AS P");
|
" 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) {
|
if (stmt == NULL) {
|
||||||
status_line(b, 500);
|
status_line(b, 500);
|
||||||
return;
|
return;
|
||||||
@@ -125,7 +129,9 @@ void respond_with_post_details_html(ResponseBuilder *b, int details_post_id)
|
|||||||
int num_comments;
|
int num_comments;
|
||||||
int upvotes;
|
int upvotes;
|
||||||
int downvotes;
|
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);
|
sqlite3_finalize(stmt);
|
||||||
status_line(b, 500);
|
status_line(b, 500);
|
||||||
return;
|
return;
|
||||||
@@ -136,10 +142,16 @@ void respond_with_post_details_html(ResponseBuilder *b, int details_post_id)
|
|||||||
<table>\
|
<table>\
|
||||||
<tr>\
|
<tr>\
|
||||||
<td>\
|
<td>\
|
||||||
<a hx-post='/posts/%d/upvotes' hx-target='.post-preview-details' hx-swap='outerHTML'>%d</a> \
|
<a hx-post='/posts/%d/upvotes' hx-target='.post-preview-details' hx-swap='outerHTML'>\
|
||||||
|
%d\
|
||||||
|
<img src='%s' />\
|
||||||
|
</a>\
|
||||||
</td>\
|
</td>\
|
||||||
<td>\
|
<td>\
|
||||||
<a hx-post='/posts/%d/downvotes' hx-target='.post-preview-details' hx-swap='outerHTML'>%d</a> \
|
<a hx-post='/posts/%d/downvotes' hx-target='.post-preview-details' hx-swap='outerHTML'>\
|
||||||
|
%d\
|
||||||
|
<img src='%s' class='rotate180' />\
|
||||||
|
</a>\
|
||||||
</td>\
|
</td>\
|
||||||
<td>\
|
<td>\
|
||||||
<span>by <a href='/users/%.*s'>%.*s</a> at %.*s</span>\
|
<span>by <a href='/users/%.*s'>%.*s</a> at %.*s</span>\
|
||||||
@@ -150,11 +162,13 @@ void respond_with_post_details_html(ResponseBuilder *b, int details_post_id)
|
|||||||
</tr>\
|
</tr>\
|
||||||
</table>\
|
</table>\
|
||||||
</div>",
|
</div>",
|
||||||
details_post_id, upvotes, details_post_id, downvotes,
|
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) author.size, author.data,
|
(int) author.size, author.data,
|
||||||
(int) created.size, created.data,
|
(int) created.size, created.data,
|
||||||
num_comments);
|
num_comments
|
||||||
|
);
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
}
|
}
|
||||||
@@ -175,23 +189,23 @@ static bool voting_endpoints(Request request, ResponseBuilder *b, string login_u
|
|||||||
|
|
||||||
// Create the UP row
|
// 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)) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we failed, maybe we need to delete the current UP row
|
// 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) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we failed, maybe we need to invert the UP row
|
// 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)) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
respond_with_post_details_html(b, vote_post_id);
|
respond_with_post_details_html(b, vote_post_id, login_username);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,23 +220,23 @@ static bool voting_endpoints(Request request, ResponseBuilder *b, string login_u
|
|||||||
|
|
||||||
// Create the UP row
|
// 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)) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we failed, maybe we need to delete the current UP row
|
// 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) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we failed, maybe we need to invert the UP row
|
// 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)) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
respond_with_post_details_html(b, vote_post_id);
|
respond_with_post_details_html(b, vote_post_id, login_username);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,8 +408,12 @@ void respond(Request request, ResponseBuilder *b)
|
|||||||
" STRFTIME('%d/%m/%Y, %H.%M', P.created) AS created_, "
|
" 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 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=1) AS upvotes, "
|
||||||
" (SELECT COUNT(*) FROM PostVotes AS PV WHERE PV.post = P.id AND PV.up=0) AS downvotes "
|
" (SELECT COUNT(*) FROM PostVotes AS PV WHERE PV.post = P.id AND PV.up=0) AS downvotes, "
|
||||||
"FROM Posts AS P");
|
" 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) {
|
if (stmt == NULL) {
|
||||||
status_line(b, 500);
|
status_line(b, 500);
|
||||||
return;
|
return;
|
||||||
|
|||||||
+46
-4
@@ -96,8 +96,9 @@ form input[type=submit] {
|
|||||||
|
|
||||||
.post-preview {
|
.post-preview {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #404040;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
background: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-preview-title {
|
.post-preview-title {
|
||||||
@@ -105,27 +106,50 @@ form input[type=submit] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.post-preview-title a {
|
.post-preview-title a {
|
||||||
color: white;
|
color: #333;
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-preview-details {
|
.post-preview-details {
|
||||||
|
height: 40px;
|
||||||
|
background: #eee;
|
||||||
border-top: 1px solid #ccc;
|
border-top: 1px solid #ccc;
|
||||||
|
border-bottom-left-radius: 3px;
|
||||||
|
border-bottom-right-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-preview-details table {
|
.post-preview-details table {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-preview-details table tr {
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-preview-details table td {
|
.post-preview-details table td {
|
||||||
|
vertical-align: middle;
|
||||||
|
display: table-cell;
|
||||||
|
height: 100%;
|
||||||
border-right: 1px solid #ccc;
|
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 span,
|
||||||
.post-preview-details a {
|
.post-preview-details a {
|
||||||
color: #ccc;
|
color: black;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
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;
|
border-radius: 3px;
|
||||||
margin-top: 10px;
|
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