Add post hiding

This allows users to hide posts that they may find unsavory. On threads
themselves in the board view, this adds a minus/plus button that will
toggle back and forth if hidden. This also adds a post menu item that
will toggle a post being hidden.

This also changes the post snippet layout a little bit. This caused
minor issues with the other menu items, but it should be fixed in this
set of changes too.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2022-07-13 12:03:09 -07:00
parent 089913c8fe
commit f131c58d1b
5 changed files with 167 additions and 56 deletions

View File

@@ -65,7 +65,8 @@ function openMenu(e) {
.append($("<lh><strong>Actions</strong></lh>").addClass("post_menu_item"))
.addClass("post_menu_items");
window.menuItemFactories.forEach(factory => {
item = factory(sender.parentElement);
let postElement = $(sender).closest(".post");
item = factory(postElement[0]);
menuList.append(
$("<li></li>")
.addClass("post_menu_item")
@@ -204,6 +205,52 @@ function openReportWindow(reportUrl) {
reportWindow.show();
}
////////////////////////////////////////////////////////////////////////////////
// Post hiding
////////////////////////////////////////////////////////////////////////////////
function getHiddenPosts() {
let hiddenPosts = JSON.parse(localStorage.getItem("hidden_posts"));
if (hiddenPosts === null) {
return [];
} else {
return hiddenPosts;
}
}
function addHiddenPost(postId) {
let hiddenPosts = getHiddenPosts();
if (!hiddenPosts.includes(postId)) {
hiddenPosts.push(postId);
}
localStorage.setItem("hidden_posts", JSON.stringify(hiddenPosts));
}
function removeHiddenPost(postId) {
let hiddenPosts = getHiddenPosts().filter((item) => item !== postId);
localStorage.setItem("hidden_posts", JSON.stringify(hiddenPosts));
}
function hidePost(postId) {
$(postId + " > .reply").addClass("post_hidden");
$(postId + " > .post_content").addClass("post_hidden");
$(postId + " >>> .thread_hide_button")
.addClass("fa-plus-square")
.addClass("thread_show_button")
.removeClass("fa-minus-square")
.removeClass("thread_hide_button");
}
function unhidePost(postId) {
$(postId + " > .reply").removeClass("post_hidden");
$(postId + " > .post_content").removeClass("post_hidden");
$(postId + " >>> .thread_show_button")
.removeClass("fa-plus-square")
.removeClass("thread_show_button")
.addClass("fa-minus-square")
.addClass("thread_hide_button");
}
////////////////////////////////////////////////////////////////////////////////
// Events
////////////////////////////////////////////////////////////////////////////////
@@ -215,7 +262,8 @@ $(window.top).on("load", () => {
window.top.jsFrame = new JSFrame();
}
});
$(window).on("load", (e) => {
// This on("load") sets up more events
$(window).on("load", () => {
$(".post_image").on("mouseover", (e) => {
// Load the original image and display it
let thumbnail = e.target;
@@ -267,6 +315,29 @@ $(window).on("load", (e) => {
});
});
// Load event that actually does stuff
$(window).on("load", () => {
getHiddenPosts().forEach(item => hidePost(item));
});
// $(document).on("click", ...) is used for dynamic event dispatch.
// Since these elements are going to have their classes changed, doing a typical
// $(".class").on(...) method won't fire on elements that don't have the
// expected class name when the hook is created.
// Don't change this.
$(document).on("click", ".thread_hide_button", (e) => {
e.preventDefault();
let id = "#" + $(e.target).closest(".post").attr("id");
hidePost(id);
addHiddenPost(id);
});
$(document).on("click", ".thread_show_button", (e) => {
e.preventDefault();
let id = "#" + $(e.target).closest(".post").attr("id");
unhidePost(id);
removeHiddenPost(id);
});
window.menuItemFactories = [];
window.menuItemFactories.push(
(postElement) =>
@@ -278,3 +349,21 @@ window.menuItemFactories.push(
openReportWindow($(postElement).attr("data-report-url"));
})
);
window.menuItemFactories.push(
(postElement) =>
$("<a>")
.text("Toggle hide")
.attr("href", "#")
.on("click", (e) => {
e.preventDefault();
let id = "#" + $(postElement).attr("id");
let hiddenPosts = getHiddenPosts();
if (hiddenPosts.includes(id)) {
unhidePost(id);
removeHiddenPost(id);
} else {
hidePost(id);
addHiddenPost(id);
}
})
);

View File

@@ -112,7 +112,7 @@ th {
float: right;
}
.post {
.thread {
background-color: var(--post-background);
padding: 10px;
}
@@ -140,6 +140,18 @@ th {
text-decoration: line-through;
}
.thread_hide_button {
cursor: pointer;
}
.thread_show_button {
cursor: pointer;
}
.post_hidden {
display: none;
}
#image_hover {
position: fixed;
top: 0px;

View File

@@ -31,9 +31,9 @@
<hr />
{# posts #}
{% for post in threads %}
<div class="row post" id="p{{post.id}}">
<div class="row thread" id="p{{post.id}}">
{# TODO we need some way to parameterize the last N threads #}
{% with reply_link=True replies_elided=post.replies.all|length|add:"-3" %}
{% with reply_link=True thread_hide_button=True replies_elided=post.replies.all|length|add:"-3" %}
{% include "board/post_snippet.html" %}
{% endwith %}

View File

@@ -40,7 +40,7 @@
</span>
<hr />
{# posts #}
<div class="row post">
<div class="row thread">
{% include "board/post_snippet.html" %}
{% for post in post.replies.all %}
<div class="row reply">

View File

@@ -2,6 +2,7 @@
{% load i18n %}
<div
id="p{{post.id}}"
class="post"
data-report-url="{% url 'board:report_form' board.url post.id %}"
{% if perms.board.add_ban %}
data-ban-url="{% url 'board:ban_create' board.url post.id %}"
@@ -11,60 +12,69 @@
{% endif %}
>
{# Post ID, status icons, username, time #}
{% if post.sticky and not post.op %}
<span title="Stickied" class="fa fa-thumb-tack"></span>
{% endif %}
{% if post.lock and not post.op %}
<span title="Locked" class="fa fa-lock"></span>
{% endif %}
<a href="#p{{post.id}}">#.</a>
<span class="post_id">{{post.id}}</span>
{% if post.subject %}
<span class="post_subject">{{post.subject}}</span>
{% endif %}
{% blocktranslate with post_name=post.name|default:"Anonymous" %}
by
<span class="post_name">{{post_name}}</span>
{% endblocktranslate %}
{% if post.capcode %}
<span class="post_capcode" style="color: {{post.capcode.color}};">{{post.capcode}}</span>
{% endif %}
{% blocktranslate with post_created=post.created %}
at {{post_created}}
{% endblocktranslate %}
{% if reply_link %}
[<a href="{{post.get_absolute_url}}">{% translate "Reply" %}</a>]
{% endif %}
<a href="#" class="post_menu_button" data-id="post_menu_button"></a>
<div class="post_title">
{# Hide post button #}
{% if thread_hide_button %}
<span class="fa fa-minus-square thread_hide_button"></span>
{% endif %}
{# Image #}
{% if post.thumbnail %}
{# Image info #}
<div class="post_image_info">
{% translate "File" %}
<a href="{{post.image.url}}" target="_blank">{{post.original_image_name}}</a>
({{post.image.size|measure_bytes}}, {{post.image_width}}x{{post.image_height}})
{# Post ID, status icons, username, time #}
<a href="#p{{post.id}}">#.</a>
<span class="post_id">{{post.id}}</span>
{% if post.subject %}
<span class="post_subject">{{post.subject}}</span>
{% endif %}
{% blocktranslate with post_name=post.name|default:"Anonymous" %}
by
<span class="post_name">{{post_name}}</span>
{% endblocktranslate %}
{% if post.capcode %}
<span class="post_capcode" style="color: {{post.capcode.color}};">{{post.capcode}}</span>
{% endif %}
{% blocktranslate with post_created=post.created %}
at {{post_created}}
{% endblocktranslate %}
{% if post.sticky and not post.op %}
<span title="Stickied" class="fa fa-thumb-tack"></span>
{% endif %}
{% if post.lock and not post.op %}
<span title="Locked" class="fa fa-lock"></span>
{% endif %}
{% if reply_link %}
[<a href="{{post.get_absolute_url}}">{% translate "Reply" %}</a>]
{% endif %}
<a href="#" class="post_menu_button" data-id="post_menu_button"></a>
</div>
{# Image thumbnail #}
<div class="post_image_thumbnail">
<a href="{{post.image.url}}" target="_blank">
<img class="post_image" src="{{post.thumbnail.url}}" data-url="{{post.image.url}}" data-thumb="{{post.thumbnail.url}}">
</a>
</div>
{% endif %}
{# "X replies elided" dialog for OPs on the board #}
<div class="post_content">
{% if replies_elided > 0 %}
<br/>
<span class="replies_elided">
({% blocktranslate %}{{replies_elided}} replies elided, click reply to view{% endblocktranslate %})
</span>
{% endif %}
{# Image #}
{% if post.thumbnail %}
{# Image info #}
<div class="post_image_info">
{% translate "File" %}
<a href="{{post.image.url}}" target="_blank">{{post.original_image_name}}</a>
({{post.image.size|measure_bytes}}, {{post.image_width}}x{{post.image_height}})
</div>
{# Post body #}
<p class="post_body">{{post|post_body|safe}}</p>
{# Image thumbnail #}
<div class="post_image_thumbnail">
<a href="{{post.image.url}}" target="_blank">
<img class="post_image" src="{{post.thumbnail.url}}" data-url="{{post.image.url}}" data-thumb="{{post.thumbnail.url}}">
</a>
</div>
{% endif %}
{# "X replies elided" dialog for OPs on the board #}
{% if replies_elided > 0 %}
<br/>
<span class="replies_elided">
({% blocktranslate %}{{replies_elided}} replies elided, click reply to view{% endblocktranslate %})
</span>
{% endif %}
{# Post body #}
<p class="post_body">{{post|post_body|safe}}</p>
</div>
</div>