forked from mirrors/bookwyrm
User page
This commit is contained in:
parent
d28efe54dd
commit
c41b53bdbe
7 changed files with 180 additions and 108 deletions
|
@ -39,6 +39,7 @@ h2 {
|
|||
font-size: 1rem;
|
||||
padding: 0.5rem 0.2rem;
|
||||
margin-bottom: 1rem;
|
||||
border-bottom: 3px solid #B2DBBF;
|
||||
}
|
||||
|
||||
#top-bar {
|
||||
|
@ -69,6 +70,9 @@ h2 {
|
|||
#actions > * {
|
||||
display: inline-block;
|
||||
}
|
||||
#actions > *:last-child {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
#notifications .icon {
|
||||
font-size: 1.1rem;
|
||||
|
@ -165,6 +169,21 @@ ul.menu a {
|
|||
.row > * {
|
||||
flex-grow: 1;
|
||||
width: min-content;
|
||||
margin-right: 1em;
|
||||
}
|
||||
.row > *:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
.row.shrink > * {
|
||||
flex-grow: 0;
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.follow-requests .row {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.follow-requests .row > *:first-child {
|
||||
width: 15em;
|
||||
}
|
||||
|
||||
.login form {
|
||||
|
@ -196,9 +215,8 @@ button.secondary {
|
|||
border: 2px solid #247BA0;
|
||||
color: #247BA0;
|
||||
}
|
||||
|
||||
.login h2 {
|
||||
border-bottom: 3px solid #B2DBBF;
|
||||
button.warning {
|
||||
background-color: #FF1654;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
|
@ -235,6 +253,25 @@ button.secondary {
|
|||
position: relative;
|
||||
bottom: 0.35em;
|
||||
}
|
||||
.user-pic.large {
|
||||
width: 5em;
|
||||
height: 5em;
|
||||
}
|
||||
|
||||
.user-profile h2 a {
|
||||
text-decoration: none;
|
||||
font-size: 0.9em;
|
||||
float: right;
|
||||
}
|
||||
.user-profile h2 .icon {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.user-profile .row > * {
|
||||
flex-grow: 0;
|
||||
}
|
||||
.user-profile .row > *:last-child {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.review-form label {
|
||||
display: block;
|
||||
|
@ -285,8 +322,19 @@ button.secondary {
|
|||
.all-shelves > div:first-child > * {
|
||||
padding-left: 1em;
|
||||
}
|
||||
.all-shelves h2 {
|
||||
border-bottom: 3px solid #B2DBBF;
|
||||
|
||||
.user-shelves .covers-shelf {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.user-shelves > div {
|
||||
margin: 1em 0;
|
||||
padding: 0;
|
||||
}
|
||||
.user-shelves > div > * {
|
||||
padding-left: 1em;
|
||||
}
|
||||
.user-shelves .covers-shelf .book-cover {
|
||||
height: 9em;
|
||||
}
|
||||
|
||||
.covers-shelf {
|
||||
|
@ -378,7 +426,14 @@ button.secondary {
|
|||
}
|
||||
|
||||
blockquote {
|
||||
white-space: pre-wrap;
|
||||
white-space: pre-line;
|
||||
margin-left: 2em;
|
||||
}
|
||||
blockquote .icon-quote-open {
|
||||
float: left;
|
||||
font-size: 2rem;
|
||||
margin-right: 0.5rem;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.interaction {
|
||||
|
@ -456,6 +511,7 @@ th, td {
|
|||
.post h2, .compose-suggestion h2 {
|
||||
position: relative;
|
||||
right: 2em;
|
||||
border: none;
|
||||
}
|
||||
.post .time-ago {
|
||||
position: relative;
|
||||
|
@ -487,6 +543,11 @@ th, td {
|
|||
background-color: #DDD;
|
||||
}
|
||||
|
||||
a .icon {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.hidden-text {
|
||||
height: 0;
|
||||
width: 0;
|
||||
|
|
|
@ -3,25 +3,7 @@
|
|||
{% block content %}
|
||||
|
||||
<div class="all-shelves content-container">
|
||||
{% for shelf in shelves %}
|
||||
{% if shelf.books %}
|
||||
<div>
|
||||
<h2>{{ shelf.name }}
|
||||
{% if shelf.size > shelf.books|length %}
|
||||
<small>(<a href="/shelf/{{ user | username }}/{{ shelf.identifier }}">See all {{ shelf.size }}</a>)</small>
|
||||
{% endif %}
|
||||
</h2>
|
||||
<div class="covers-shelf {{ shelf.identifier }}">
|
||||
{% for book in shelf.books %}
|
||||
<div class="book-preview" onclick="show_compose(this)" id="book-{{ book.id }}">
|
||||
{% include 'snippets/book_cover.html' with book=book %}
|
||||
{% include 'snippets/shelve_button.html' with book=book %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% include 'snippets/covers_shelf.html' with shelves=shelves user=request.user %}
|
||||
</div>
|
||||
|
||||
{% for shelf in shelves %}
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
<img class="user-pic" src="{% if user.avatar %}/images/{{ user.avatar }}{% else %}/static/images/default_avi.jpg{% endif %}">
|
||||
<img class="user-pic{% if large %} large{% endif %}" src="{% if user.avatar %}/images/{{ user.avatar }}{% else %}/static/images/default_avi.jpg{% endif %}">
|
||||
|
||||
|
|
|
@ -1,6 +1,20 @@
|
|||
{% load fr_display %}
|
||||
{% for book in books %}
|
||||
<div class="book-preview">
|
||||
{% include 'snippets/book.html' with rating=rating %}
|
||||
{% for shelf in shelves %}
|
||||
{% if shelf.books %}
|
||||
<div>
|
||||
<h2>{{ shelf.name }}
|
||||
{% if shelf.size > shelf.books|length %}
|
||||
<small>(<a href="/shelf/{{ user | username }}/{{ shelf.identifier }}">See all {{ shelf.size }}</a>)</small>
|
||||
{% endif %}
|
||||
</h2>
|
||||
<div class="covers-shelf {{ shelf.identifier }}">
|
||||
{% for book in shelf.books %}
|
||||
<div class="book-preview" onclick="show_compose(this)" id="book-{{ book.id }}">
|
||||
{% include 'snippets/book_cover.html' with book=book %}
|
||||
{% include 'snippets/shelve_button.html' with book=book %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<form action="/accept_follow_request/" method="POST">
|
||||
{% csrf_token %}
|
||||
<input type=hidden name="user" value="{{ user.username }}">
|
||||
<input type=submit value="Accept">
|
||||
<input type="hidden" name="user" value="{{ user.username }}">
|
||||
<button type="submit">Accept</button>
|
||||
</form>
|
||||
<form action="/delete_follow_request/" method="POST">
|
||||
{% csrf_token %}
|
||||
<input type=hidden name="user" value="{{ user.username }}">
|
||||
<input type=submit value="Delete">
|
||||
<input type="hidden" name="user" value="{{ user.username }}">
|
||||
<button type="submit" class="warning">Delete</button>
|
||||
</form>
|
||||
|
|
|
@ -1,64 +1,61 @@
|
|||
{% extends 'layout.html' %}
|
||||
{% load humanize %}
|
||||
{% block content %}
|
||||
<div id="sidebar">
|
||||
<div class="user-profile">
|
||||
<h2>
|
||||
{% include 'snippets/avatar.html' with user=user %}
|
||||
{% if user.name %}{{ user.name }}{% endif %}
|
||||
<small>{{ user.username }}</small>
|
||||
</h2>
|
||||
{% if user.summary %}
|
||||
<blockquote>{{ user.summary | safe }}</blockquote>
|
||||
{% endif %}
|
||||
{% if not is_self %}
|
||||
{% include 'snippets/follow_button.html' with user=user %}
|
||||
{% endif %}
|
||||
|
||||
<div class="content-container user-profile">
|
||||
<h2>User Profile
|
||||
{% if is_self %}
|
||||
<div class="interaction">
|
||||
<a href="/user-edit/">Edit profile</a>
|
||||
<a href="/user-edit/">edit
|
||||
<span class="icon icon-pencil">
|
||||
<span class="hidden-text">Edit profile</span>
|
||||
</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</h2>
|
||||
|
||||
<div class="row">
|
||||
<div class="pic-container">
|
||||
{% include 'snippets/avatar.html' with user=user large=True %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p>{% if user.name %}{{ user.name }}{% else %}{{ user.localname }}{% endif %}</p>
|
||||
<p>{{ user.username }}</p>
|
||||
<p>Joined {{ user.created_date | naturaltime }}</p>
|
||||
</div>
|
||||
|
||||
{% if user.summary %}
|
||||
<blockquote><span class="icon icon-quote-open"></span>{{ user.summary | safe }}</blockquote>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if not is_self %}
|
||||
{% include 'snippets/follow_button.html' with user=user %}
|
||||
{% endif %}
|
||||
|
||||
{% if is_self and user.follower_requests.all %}
|
||||
<div>
|
||||
<div class="follow-requests">
|
||||
<h2>Follow Requests</h2>
|
||||
{% for requester in user.follower_requests.all %}
|
||||
<div>
|
||||
{% include 'snippets/username.html' with user=requester show_full=True %}
|
||||
<div class="row shrink">
|
||||
<p>
|
||||
{% include 'snippets/username.html' with user=requester show_full=True %}
|
||||
</p>
|
||||
{% include 'snippets/follow_request_buttons.html' with user=requester %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div>
|
||||
<h2>Followers</h2>
|
||||
{% for follower in user.followers.all %}
|
||||
<div>
|
||||
{% include 'snippets/username.html' with user=follower show_full=True %}
|
||||
{% include 'snippets/follow_button.html' with user=follower %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2>Following</h2>
|
||||
{% for following in user.following.all %}
|
||||
<div>
|
||||
{% include 'snippets/username.html' with user=following show_full=True %}
|
||||
{% include 'snippets/follow_button.html' with user=following %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
{% for shelf in shelves %}
|
||||
<div>
|
||||
<h2>{{ shelf.name }}</h2>
|
||||
{% include 'snippets/shelf.html' with shelf=shelf ratings=ratings %}
|
||||
<div class="all-shelves content-container">
|
||||
{% include 'snippets/covers_shelf.html' with shelves=shelves user=user %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="content-container"><h2>User Activity</h2></div>
|
||||
{% for activity in activities %}
|
||||
<div class="content-container">
|
||||
{% include 'snippets/status.html' with status=activity %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
|
@ -37,39 +37,20 @@ def home(request):
|
|||
def home_tab(request, tab):
|
||||
''' user's homepage with activity feed '''
|
||||
shelves = []
|
||||
book_count = 6
|
||||
for (identifier, count) in [('reading', 3), ('read', 1), ('to-read', 3)]:
|
||||
if book_count <= 0:
|
||||
break
|
||||
shelf = models.Shelf.objects.get(
|
||||
user=request.user,
|
||||
identifier=identifier,
|
||||
)
|
||||
if not shelf.books.count():
|
||||
continue
|
||||
books = models.ShelfBook.objects.filter(
|
||||
shelf=shelf,
|
||||
).order_by(
|
||||
'-updated_date'
|
||||
)[:count]
|
||||
|
||||
book_count -= len(books)
|
||||
|
||||
shelves.append({
|
||||
'name': shelf.name,
|
||||
'identifier': shelf.identifier,
|
||||
'books': [b.book for b in books],
|
||||
'size': shelf.books.count(),
|
||||
})
|
||||
shelves = get_user_shelf_preview(
|
||||
request.user,
|
||||
[('reading', 3), ('read', 1), ('to-read', 3)]
|
||||
)
|
||||
size = sum(len(s['books']) for s in shelves)
|
||||
# books new to the instance, for discovery
|
||||
if book_count > 0:
|
||||
if size < 6:
|
||||
shelves.append({
|
||||
'name': 'Recently added',
|
||||
'identifier': None,
|
||||
'books': models.Book.objects.order_by(
|
||||
'-created_date'
|
||||
)[:book_count],
|
||||
'count': book_count,
|
||||
)[:6 - size],
|
||||
'count': 6 - size,
|
||||
})
|
||||
|
||||
# allows us to check if a user has shelved a book
|
||||
|
@ -138,7 +119,6 @@ def notifications_page(request):
|
|||
notifications.update(read=True)
|
||||
return TemplateResponse(request, 'notifications.html', data)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def user_page(request, username):
|
||||
''' profile page for a user '''
|
||||
|
@ -153,15 +133,19 @@ def user_page(request, username):
|
|||
# otherwise we're at a UI view
|
||||
|
||||
# TODO: change display with privacy and authentication considerations
|
||||
shelves = models.Shelf.objects.filter(user=user)
|
||||
ratings = {r.book.id: r.rating for r in \
|
||||
models.Review.objects.filter(user=user, book__shelves__user=user)}
|
||||
shelves = get_user_shelf_preview(user)
|
||||
|
||||
activities = models.Status.objects.filter(
|
||||
user=user,
|
||||
).order_by(
|
||||
'-created_date',
|
||||
).select_subclasses()[:10]
|
||||
|
||||
data = {
|
||||
'user': user,
|
||||
'shelves': shelves,
|
||||
'ratings': ratings,
|
||||
'is_self': request.user.id == user.id,
|
||||
'activities': activities,
|
||||
}
|
||||
return TemplateResponse(request, 'user.html', data)
|
||||
|
||||
|
@ -390,3 +374,37 @@ def shelf_page(request, username, shelf_identifier):
|
|||
}
|
||||
return TemplateResponse(request, 'shelf.html', data)
|
||||
|
||||
|
||||
def get_user_shelf_preview(user, shelf_proportions=None):
|
||||
''' data for the covers shelf (user page and feed page) '''
|
||||
shelves = []
|
||||
shelf_max = 6
|
||||
if not shelf_proportions:
|
||||
shelf_proportions = [('reading', 3), ('read', 2), ('to-read', -1)]
|
||||
for (identifier, count) in shelf_proportions:
|
||||
if shelf_max <= 0:
|
||||
break
|
||||
if count > shelf_max or count < 0:
|
||||
count = shelf_max
|
||||
shelf = models.Shelf.objects.get(
|
||||
user=user,
|
||||
identifier=identifier,
|
||||
)
|
||||
if not shelf.books.count():
|
||||
continue
|
||||
books = models.ShelfBook.objects.filter(
|
||||
shelf=shelf,
|
||||
).order_by(
|
||||
'-updated_date'
|
||||
)[:count]
|
||||
|
||||
shelf_max -= len(books)
|
||||
|
||||
shelves.append({
|
||||
'name': shelf.name,
|
||||
'identifier': shelf.identifier,
|
||||
'books': [b.book for b in books],
|
||||
'size': shelf.books.count(),
|
||||
})
|
||||
return shelves
|
||||
|
||||
|
|
Loading…
Reference in a new issue