add user started threads view
This commit is contained in:
@@ -54,6 +54,14 @@ class Users(Model):
|
|||||||
q = f'{Posts.FULL_POSTS_QUERY} WHERE posts.user_id = ? ORDER BY posts.created_at DESC LIMIT ? OFFSET ?'
|
q = f'{Posts.FULL_POSTS_QUERY} WHERE posts.user_id = ? ORDER BY posts.created_at DESC LIMIT ? OFFSET ?'
|
||||||
return db.query(q, self.id, per_page, (page - 1) * per_page)
|
return db.query(q, self.id, per_page, (page - 1) * per_page)
|
||||||
|
|
||||||
|
def get_started_threads(self, per_page: int, page: int) -> list:
|
||||||
|
q = f"""{Posts.FULL_POSTS_QUERY} WHERE threads.user_id = ? AND posts.created_at = (
|
||||||
|
SELECT MIN(p2.created_at)
|
||||||
|
FROM posts p2
|
||||||
|
WHERE p2.thread_id = posts.thread_id
|
||||||
|
) ORDER BY threads.created_at DESC LIMIT ? OFFSET ?"""
|
||||||
|
return db.query(q, self.id, per_page, (page - 1) * per_page)
|
||||||
|
|
||||||
def get_all_subscriptions(self):
|
def get_all_subscriptions(self):
|
||||||
q = """
|
q = """
|
||||||
SELECT threads.title AS thread_title, threads.slug AS thread_slug
|
SELECT threads.title AS thread_title, threads.slug AS thread_slug
|
||||||
@@ -289,6 +297,7 @@ class Posts(Model):
|
|||||||
users.id AS user_id, post_history.original_markup,
|
users.id AS user_id, post_history.original_markup,
|
||||||
users.signature_rendered, threads.slug AS thread_slug,
|
users.signature_rendered, threads.slug AS thread_slug,
|
||||||
threads.is_locked AS thread_is_locked, threads.title AS thread_title,
|
threads.is_locked AS thread_is_locked, threads.title AS thread_title,
|
||||||
|
topics.id AS topic_id, topics.name AS topic_name,
|
||||||
COALESCE(user_badges.badges_json, '[]') AS badges_json
|
COALESCE(user_badges.badges_json, '[]') AS badges_json
|
||||||
FROM
|
FROM
|
||||||
posts
|
posts
|
||||||
@@ -296,6 +305,8 @@ class Posts(Model):
|
|||||||
post_history ON posts.current_revision_id = post_history.id
|
post_history ON posts.current_revision_id = post_history.id
|
||||||
JOIN
|
JOIN
|
||||||
users ON posts.user_id = users.id
|
users ON posts.user_id = users.id
|
||||||
|
JOIN
|
||||||
|
topics ON threads.topic_id = topics.id
|
||||||
JOIN
|
JOIN
|
||||||
threads ON posts.thread_id = threads.id
|
threads ON posts.thread_id = threads.id
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from ..auth import (
|
|||||||
is_logged_in, parse_username, is_password_valid,
|
is_logged_in, parse_username, is_password_valid,
|
||||||
login_required
|
login_required
|
||||||
)
|
)
|
||||||
from ..models import Users, Posts, Reactions
|
from ..models import Users, Posts, Reactions, Threads
|
||||||
from ..constants import PermissionLevel
|
from ..constants import PermissionLevel
|
||||||
from secrets import compare_digest as compare_timesafe
|
from secrets import compare_digest as compare_timesafe
|
||||||
import math
|
import math
|
||||||
@@ -117,6 +117,8 @@ def posts(username):
|
|||||||
if username == 'deleteduser':
|
if username == 'deleteduser':
|
||||||
abort(404)
|
abort(404)
|
||||||
target_user = Users.find({'username': username})
|
target_user = Users.find({'username': username})
|
||||||
|
if not target_user:
|
||||||
|
abort(404)
|
||||||
PER_PAGE = 10
|
PER_PAGE = 10
|
||||||
posts_count = Posts.count({'user_id': target_user.id})
|
posts_count = Posts.count({'user_id': target_user.id})
|
||||||
page_count = max(1, math.ceil(posts_count / PER_PAGE))
|
page_count = max(1, math.ceil(posts_count / PER_PAGE))
|
||||||
@@ -137,7 +139,23 @@ def threads(username):
|
|||||||
username = username.lower()
|
username = username.lower()
|
||||||
if username == 'deleteduser':
|
if username == 'deleteduser':
|
||||||
abort(404)
|
abort(404)
|
||||||
return 'stub'
|
target_user = Users.find({'username': username})
|
||||||
|
if not target_user:
|
||||||
|
abort(404)
|
||||||
|
PER_PAGE = 10
|
||||||
|
threads_count = Threads.count({'user_id': target_user.id})
|
||||||
|
page_count = max(1, math.ceil(threads_count / PER_PAGE))
|
||||||
|
page = 1
|
||||||
|
try:
|
||||||
|
page = max(1, min(int(request.args.get('page', default=1)), page_count))
|
||||||
|
except ValueError:
|
||||||
|
abort(404)
|
||||||
|
threads = target_user.get_started_threads(PER_PAGE, page)
|
||||||
|
return render_template(
|
||||||
|
'users/threads.html', threads=threads,
|
||||||
|
page=page, page_count=page_count,
|
||||||
|
target_user=target_user, Reactions=Reactions
|
||||||
|
)
|
||||||
|
|
||||||
@bp.get('/<username>/comments/')
|
@bp.get('/<username>/comments/')
|
||||||
def comments(username):
|
def comments(username):
|
||||||
|
|||||||
@@ -113,7 +113,7 @@
|
|||||||
{% macro full_post(
|
{% macro full_post(
|
||||||
post, render_sig=true, is_latest=false,
|
post, render_sig=true, is_latest=false,
|
||||||
show_toolbar=true, is_editing=false, thread=none,
|
show_toolbar=true, is_editing=false, thread=none,
|
||||||
show_reactions=true, show_thread=false
|
show_reactions=true, show_thread=false, allow_reacting=true
|
||||||
) -%}
|
) -%}
|
||||||
{%- if is_logged_in() -%}
|
{%- if is_logged_in() -%}
|
||||||
{%- set can_delete = post.user_id == get_active_user().id or is_mod() -%}
|
{%- set can_delete = post.user_id == get_active_user().id or is_mod() -%}
|
||||||
@@ -190,10 +190,10 @@
|
|||||||
{% set reactors_str = reactors_str + '\n...and many others' %}
|
{% set reactors_str = reactors_str + '\n...and many others' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% set has_reacted = get_active_user() is not none and get_active_user().username in reactors %}
|
{% set has_reacted = get_active_user() is not none and get_active_user().username in reactors %}
|
||||||
<button disabled title="{{reactors_str}}" class="minimal {{'alt' if has_reacted else ''}}"><img src="/static/emoji/{{reaction.reaction_text}}.png">{{reaction.c}}</button>
|
<button type="button" disabled title="{{reactors_str}}" class="minimal {{'alt' if has_reacted else ''}}"><img src="/static/emoji/{{reaction.reaction_text}}.png">{{reaction.c}}</button>
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
</span>
|
</span>
|
||||||
{%- if is_logged_in() -%}<button disabled title="This feature requires JavaScript to be enabled.">Add reaction</button>{%- endif -%}
|
{%- if is_logged_in() and allow_reacting -%}<button disabled title="This feature requires JavaScript to be enabled.">Add reaction</button>{%- endif -%}
|
||||||
{%- elif is_editing -%}
|
{%- elif is_editing -%}
|
||||||
<input type="submit" value="Save">
|
<input type="submit" value="Save">
|
||||||
<a href="{{get_post_url(post.id, _anchor=true)}}" class="linkbutton warn">Cancel</a>
|
<a href="{{get_post_url(post.id, _anchor=true)}}" class="linkbutton warn">Cancel</a>
|
||||||
|
|||||||
@@ -74,6 +74,14 @@
|
|||||||
{{- pager(page, page_count) -}}
|
{{- pager(page, page_count) -}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="new-post-toast" class="plank even contrast-bg hidden">
|
||||||
|
<span>New post in thread!</span>
|
||||||
|
<span class="notification-buttons">
|
||||||
|
<button>Dismiss</button>
|
||||||
|
<button>View post</button>
|
||||||
|
<button>Stop updates</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
{%- if is_logged_in() and get_active_user().can_post_to_thread_or_topic(thread) -%}
|
{%- if is_logged_in() and get_active_user().can_post_to_thread_or_topic(thread) -%}
|
||||||
<form action="{{url_for('threads.reply', thread_id=thread.id)}}" method="POST" class="plank post-edit-form">
|
<form action="{{url_for('threads.reply', thread_id=thread.id)}}" method="POST" class="plank post-edit-form">
|
||||||
<h2 class="info">Reply to "{{thread.title}}"</h2>
|
<h2 class="info">Reply to "{{thread.title}}"</h2>
|
||||||
|
|||||||
@@ -4,19 +4,19 @@
|
|||||||
{%- block title -%}{{ target_user.get_readable_name() }}'s posts{%- endblock -%}
|
{%- block title -%}{{ target_user.get_readable_name() }}'s posts{%- endblock -%}
|
||||||
{%- block content -%}
|
{%- block content -%}
|
||||||
{%- set td -%}
|
{%- set td -%}
|
||||||
<a href="{{url_for('users.user_page', username=target_user.username)}}">Back to profile</a>
|
<a href="{{url_for('users.user_page', username=target_user.username)}}">← Back to profile</a>
|
||||||
|
{%- endset -%}
|
||||||
|
{%- call() subheader("%s's posts" % target_user.get_readable_name(), td) -%}
|
||||||
{%- if posts -%}
|
{%- if posts -%}
|
||||||
<fieldset class="plank even no-shadow minimal thread-actions">
|
<fieldset class="plank even no-shadow minimal thread-actions">
|
||||||
<legend>Page</legend>
|
<legend>Page</legend>
|
||||||
{{- pager(page, page_count) -}}
|
{{- pager(page, page_count) -}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
{%- endset -%}
|
|
||||||
{%- call() subheader("%s's posts" % target_user.get_readable_name(), td) -%}
|
|
||||||
{%- endcall -%}
|
{%- endcall -%}
|
||||||
{%- if posts -%}
|
{%- if posts -%}
|
||||||
{%- for post in posts -%}
|
{%- for post in posts -%}
|
||||||
<div class="post plank">{{full_post(post, show_toolbar=false, show_thread=true)}}</div>
|
<div class="post plank">{{full_post(post, show_toolbar=false, show_thread=true, allow_reacting=false)}}</div>
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
<div class="plank">
|
<div class="plank">
|
||||||
<fieldset class="plank even no-shadow minimal thread-actions">
|
<fieldset class="plank even no-shadow minimal thread-actions">
|
||||||
|
|||||||
33
app/templates/users/threads.html
Normal file
33
app/templates/users/threads.html
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{%- from 'common/macros.html' import full_post with context -%}
|
||||||
|
{%- from 'common/macros.html' import subheader, pager -%}
|
||||||
|
{%- extends 'base.html' -%}
|
||||||
|
{%- block title -%}threads by {{ target_user.get_readable_name() }}{%- endblock -%}
|
||||||
|
{%- block content -%}
|
||||||
|
{%- set td -%}
|
||||||
|
<a href="{{url_for('users.user_page', username=target_user.username)}}">← Back to profile</a>
|
||||||
|
{%- endset -%}
|
||||||
|
{%- call() subheader("%s's started threads" % target_user.get_readable_name(), td) -%}
|
||||||
|
{%- if threads -%}
|
||||||
|
<fieldset class="plank even no-shadow minimal thread-actions">
|
||||||
|
<legend>Page</legend>
|
||||||
|
{{- pager(page, page_count) -}}
|
||||||
|
</fieldset>
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endcall -%}
|
||||||
|
{%- if threads -%}
|
||||||
|
{%- for post in threads -%}
|
||||||
|
<div class="plank">
|
||||||
|
<h2 class="info"><a href="{{url_for('threads.thread_by_id', thread_id=post.thread_id)}}">"{{post.thread_title}}"</a> in topic <a href="{{url_for('topics.topic_by_id', topic_id=post.topic_id)}}">{{post.topic_name}}</a></h2>
|
||||||
|
<div class="post">{{full_post(post, show_toolbar=false, allow_reacting=false)}}</div>
|
||||||
|
</div>
|
||||||
|
{%- endfor -%}
|
||||||
|
<div class="plank">
|
||||||
|
<fieldset class="plank even no-shadow minimal thread-actions">
|
||||||
|
<legend>Page</legend>
|
||||||
|
{{- pager(page, page_count) -}}
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
{%- else -%}
|
||||||
|
<div class="plank">{{target_user.get_readable_name()}} has not started any threads.</div>
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endblock -%}
|
||||||
@@ -301,7 +301,7 @@ a.site-title {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
margin: 0;
|
margin: var(--base-padding) 0;
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user