Browse Source

Merge branch 'master' into clear_all_notifications

merge-requests/20/head
James Southern 7 years ago
parent
commit
5b6184181e
  1. 4
      tildes/scss/_base.scss
  2. 7
      tildes/tildes/__init__.py
  3. 4
      tildes/tildes/models/comment/comment_query.py
  4. 44
      tildes/tildes/models/pagination.py
  5. 2
      tildes/tildes/templates/macros/user_menu.jinja2
  6. 4
      tildes/tildes/templates/topic_listing.jinja2
  7. 42
      tildes/tildes/templates/user.jinja2
  8. 66
      tildes/tildes/views/user.py

4
tildes/scss/_base.scss

@ -161,6 +161,10 @@ ol {
margin-top: 0.2rem; margin-top: 0.2rem;
max-width: $paragraph-max-width - 2rem; max-width: $paragraph-max-width - 2rem;
} }
&:last-child {
margin-bottom: 0.2rem;
}
} }
p { p {

7
tildes/tildes/__init__.py

@ -137,10 +137,11 @@ def current_listing_base_url(
The `query` argument allows adding query variables to the generated url. The `query` argument allows adding query variables to the generated url.
""" """
if request.matched_route.name not in ('home', 'group'):
if request.matched_route.name not in ('home', 'group', 'user'):
raise AttributeError('Current route is not supported.') raise AttributeError('Current route is not supported.')
base_view_vars = ('order', 'period', 'per_page', 'tag', 'unfiltered')
base_view_vars = (
'order', 'period', 'per_page', 'tag', 'type', 'unfiltered')
query_vars = { query_vars = {
key: val for key, val in request.GET.copy().items() key: val for key, val in request.GET.copy().items()
if key in base_view_vars if key in base_view_vars
@ -165,7 +166,7 @@ def current_listing_normal_url(
The `query` argument allows adding query variables to the generated url. The `query` argument allows adding query variables to the generated url.
""" """
if request.matched_route.name not in ('home', 'group'):
if request.matched_route.name not in ('home', 'group', 'user'):
raise AttributeError('Current route is not supported.') raise AttributeError('Current route is not supported.')
normal_view_vars = ('order', 'period', 'per_page') normal_view_vars = ('order', 'period', 'per_page')

4
tildes/tildes/models/comment/comment_query.py

@ -4,12 +4,12 @@ from typing import Any
from pyramid.request import Request from pyramid.request import Request
from tildes.models import ModelQuery
from tildes.models.pagination import PaginatedQuery
from .comment import Comment from .comment import Comment
from .comment_vote import CommentVote from .comment_vote import CommentVote
class CommentQuery(ModelQuery):
class CommentQuery(PaginatedQuery):
"""Specialized ModelQuery for Comments.""" """Specialized ModelQuery for Comments."""
def __init__(self, request: Request) -> None: def __init__(self, request: Request) -> None:

44
tildes/tildes/models/pagination.py

@ -3,9 +3,9 @@
from typing import Any, Iterator, List, Optional, TypeVar from typing import Any, Iterator, List, Optional, TypeVar
from pyramid.request import Request from pyramid.request import Request
from sqlalchemy import Column, func
from sqlalchemy import Column, func, inspect
from tildes.lib.id import id36_to_id
from tildes.lib.id import id_to_id36, id36_to_id
from .model_query import ModelQuery from .model_query import ModelQuery
@ -22,7 +22,8 @@ class PaginatedQuery(ModelQuery):
super().__init__(model_cls, request) super().__init__(model_cls, request)
self._sort_column: Optional[Column] = None
# default to sorting by created_time descending (newest first)
self._sort_column = model_cls.created_time
self.sort_desc = True self.sort_desc = True
self.after_id: Optional[int] = None self.after_id: Optional[int] = None
@ -135,17 +136,16 @@ class PaginatedQuery(ModelQuery):
"""Finalize the query before execution.""" """Finalize the query before execution."""
query = super()._finalize() query = super()._finalize()
if self._sort_column:
# if the query is reversed, we need to sort in the opposite dir
# (basically self.sort_desc XOR self.is_reversed)
desc = self.sort_desc
if self.is_reversed:
desc = not desc
# if the query is reversed, we need to sort in the opposite dir
# (basically self.sort_desc XOR self.is_reversed)
desc = self.sort_desc
if self.is_reversed:
desc = not desc
if desc:
query = query.order_by(*self.sorting_columns_desc)
else:
query = query.order_by(*self.sorting_columns)
if desc:
query = query.order_by(*self.sorting_columns_desc)
else:
query = query.order_by(*self.sorting_columns)
# pylint: disable=protected-access # pylint: disable=protected-access
query = query._apply_before_or_after() query = query._apply_before_or_after()
@ -204,3 +204,21 @@ class PaginatedResults:
def __len__(self) -> int: def __len__(self) -> int:
"""Return the number of results.""" """Return the number of results."""
return len(self.results) return len(self.results)
@property
def next_page_after_id36(self) -> str:
"""Return "after" ID36 that should be used to fetch the next page."""
if not self.has_next_page:
raise AttributeError
next_id = inspect(self.results[-1]).identity[0]
return id_to_id36(next_id)
@property
def prev_page_before_id36(self) -> str:
"""Return "before" ID36 that should be used to fetch the prev page."""
if not self.has_prev_page:
raise AttributeError
prev_id = inspect(self.results[0]).identity[0]
return id_to_id36(prev_id)

2
tildes/tildes/templates/macros/user_menu.jinja2

@ -7,7 +7,7 @@
<ul class="nav"> <ul class="nav">
<li class="nav-item {{ 'active' if route == 'user' else ''}}"> <li class="nav-item {{ 'active' if route == 'user' else ''}}">
<a href="/user/{{ request.user }}"> <a href="/user/{{ request.user }}">
Recent activity
Your posts
</a> </a>
</li> </li>
</ul> </ul>

4
tildes/tildes/templates/topic_listing.jinja2

@ -154,13 +154,13 @@
<div class="pagination"> <div class="pagination">
{% if topics.has_prev_page %} {% if topics.has_prev_page %}
<a class="page-item btn" id="prev-page" <a class="page-item btn" id="prev-page"
href="{{ request.current_listing_base_url({'before': topics[0].topic_id36}) }}"
href="{{ request.current_listing_base_url({'before': topics.prev_page_before_id36}) }}"
>Prev</a> >Prev</a>
{% endif %} {% endif %}
{% if topics.has_next_page %} {% if topics.has_next_page %}
<a class="page-item btn" id="next-page" <a class="page-item btn" id="next-page"
href="{{ request.current_listing_base_url({'after': topics[-1].topic_id36}) }}"
href="{{ request.current_listing_base_url({'after': topics.next_page_after_id36}) }}"
>Next</a> >Next</a>
{% endif %} {% endif %}
</div> </div>

42
tildes/tildes/templates/user.jinja2

@ -10,13 +10,33 @@
<a class="site-header-context" href="/user/{{ user.username }}">{{ user.username }}</a> <a class="site-header-context" href="/user/{{ user.username }}">{{ user.username }}</a>
{% endblock %} {% endblock %}
{% block main_heading %}{{ user.username }}'s recent activity{% endblock %}
{# Only show the heading if they can't see the type tabs #}
{% block main_heading %}
{% if not request.has_permission('view_types', user) %}
{{ user.username }}'s recent activity
{% endif %}
{% endblock %}
{% block content %} {% block content %}
{% if request.has_permission('view_types', user) %}
<div class="listing-options">
<menu class="tab tab-listing-order">
<li class="tab-item{{' active' if not post_type else ''}}">
<a href="{{ request.current_listing_normal_url() }}">Recent activity</a>
</li>
<li class="tab-item{{' active' if post_type == 'topic' else ''}}">
<a href="{{ request.current_listing_normal_url({'type': 'topic'}) }}">Topics</a>
</li>
<li class="tab-item{{ ' active' if post_type == 'comment' else ''}}">
<a href="{{ request.current_listing_normal_url({'type': 'comment'}) }}">Comments</a>
</li>
</menu>
</div>
{% endif %}
{% if merged_posts %}
{% if posts %}
<ol class="post-listing"> <ol class="post-listing">
{% for post in merged_posts if request.has_permission('view', post) %}
{% for post in posts if request.has_permission('view', post) %}
<li> <li>
{% if post is topic %} {% if post is topic %}
{{ render_topic_for_listing(post, show_group=True) }} {{ render_topic_for_listing(post, show_group=True) }}
@ -27,6 +47,22 @@
</li> </li>
{% endfor %} {% endfor %}
</ol> </ol>
{% if post_type and (posts.has_prev_page or posts.has_next_page) %}
<div class="pagination">
{% if posts.has_prev_page %}
<a class="page-item btn" id="prev-page"
href="{{ request.current_listing_base_url({'before': posts.prev_page_before_id36}) }}"
>Prev</a>
{% endif %}
{% if posts.has_next_page %}
<a class="page-item btn" id="next-page"
href="{{ request.current_listing_base_url({'after': posts.next_page_after_id36}) }}"
>Next</a>
{% endif %}
</div>
{% endif %}
{% else %} {% else %}
<div class="empty"> <div class="empty">
<h2 class="empty-title">This user hasn't made any posts</h2> <h2 class="empty-title">This user hasn't made any posts</h2>

66
tildes/tildes/views/user.py

@ -1,19 +1,24 @@
"""Views related to a specific user.""" """Views related to a specific user."""
from typing import List, Union
from marshmallow.fields import String
from marshmallow.validate import OneOf
from pyramid.request import Request from pyramid.request import Request
from pyramid.view import view_config from pyramid.view import view_config
from sqlalchemy.sql.expression import desc from sqlalchemy.sql.expression import desc
from webargs.pyramidparser import use_kwargs
from tildes.models.comment import Comment from tildes.models.comment import Comment
from tildes.models.topic import Topic from tildes.models.topic import Topic
from tildes.models.user import UserInviteCode
from tildes.models.user import User, UserInviteCode
from tildes.schemas.topic_listing import TopicListingSchema
@view_config(route_name='user', renderer='user.jinja2')
def get_user(request: Request) -> dict:
"""Generate the main user info page."""
user = request.context
def _get_user_recent_activity(
request: Request,
user: User,
) -> List[Union[Comment, Topic]]:
page_size = 20 page_size = 20
# Since we don't know how many comments or topics will be needed to make # Since we don't know how many comments or topics will be needed to make
@ -54,9 +59,56 @@ def get_user(request: Request) -> dict:
) )
merged_posts = merged_posts[:page_size] merged_posts = merged_posts[:page_size]
return merged_posts
@view_config(route_name='user', renderer='user.jinja2')
@use_kwargs(TopicListingSchema(only=('after', 'before', 'per_page')))
@use_kwargs({
'post_type': String(
load_from='type',
validate=OneOf(('topic', 'comment')),
),
})
def get_user(
request: Request,
after: str,
before: str,
per_page: int,
post_type: str = None,
) -> dict:
"""Generate the main user history page."""
user = request.context
if not request.has_permission('view_types', user):
post_type = None
if post_type:
if post_type == 'topic':
query = request.query(Topic).filter(Topic.user == user)
elif post_type == 'comment':
query = request.query(Comment).filter(Comment.user == user)
if before:
query = query.before_id36(before)
if after:
query = query.after_id36(after)
query = query.join_all_relationships()
# include removed posts if the user's looking at their own page
if user == request.user:
query = query.include_removed()
posts = query.get_page(per_page)
else:
posts = _get_user_recent_activity(request, user)
return { return {
'user': user, 'user': user,
'merged_posts': merged_posts,
'posts': posts,
'post_type': post_type,
} }

Loading…
Cancel
Save