forked from mirrors/bookwyrm
Improves templates
This commit is contained in:
parent
b554280481
commit
e0c174d990
11 changed files with 166 additions and 132 deletions
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 3.0.2 on 2020-01-28 23:19
|
# Generated by Django 3.0.2 on 2020-01-29 01:16
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
import django.contrib.auth.models
|
import django.contrib.auth.models
|
||||||
|
@ -86,7 +86,7 @@ class Migration(migrations.Migration):
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('activitypub_id', models.CharField(max_length=255)),
|
('activitypub_id', models.CharField(max_length=255)),
|
||||||
('openlibrary_key', models.CharField(max_length=255)),
|
('openlibrary_key', models.CharField(max_length=255, unique=True)),
|
||||||
('data', django.contrib.postgres.fields.jsonb.JSONField()),
|
('data', django.contrib.postgres.fields.jsonb.JSONField()),
|
||||||
('added_date', models.DateTimeField(auto_now_add=True)),
|
('added_date', models.DateTimeField(auto_now_add=True)),
|
||||||
('updated_date', models.DateTimeField(auto_now=True)),
|
('updated_date', models.DateTimeField(auto_now=True)),
|
||||||
|
|
|
@ -199,7 +199,7 @@ class ShelfBook(models.Model):
|
||||||
class Book(models.Model):
|
class Book(models.Model):
|
||||||
''' a non-canonical copy from open library '''
|
''' a non-canonical copy from open library '''
|
||||||
activitypub_id = models.CharField(max_length=255)
|
activitypub_id = models.CharField(max_length=255)
|
||||||
openlibrary_key = models.CharField(max_length=255)
|
openlibrary_key = models.CharField(max_length=255, unique=True)
|
||||||
data = JSONField()
|
data = JSONField()
|
||||||
works = models.ManyToManyField('Work')
|
works = models.ManyToManyField('Work')
|
||||||
authors = models.ManyToManyField('Author')
|
authors = models.ManyToManyField('Author')
|
||||||
|
|
|
@ -1,23 +1,41 @@
|
||||||
|
/* some colors that are okay: #247BA0 #70C1B2 #B2DBBF #F3FFBD #FF1654 */
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
line-height: 1.3em;
|
line-height: 1.3em;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
body > * > * {
|
h1 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1rem;
|
||||||
|
background-color: #F3FFBD;
|
||||||
|
padding: 0.5rem 0.2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 1rem;
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
max-width: 75rem;
|
max-width: 75rem;
|
||||||
min-width: 30rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#top-bar {
|
#top-bar {
|
||||||
height: 4rem;
|
height: 4rem;
|
||||||
border-bottom: 1px solid #aaa;
|
background-color: #B2DBBF;
|
||||||
box-shadow: 0 0.5em 0.5em -0.6em #666;
|
overflow: hidden;
|
||||||
margin-bottom: 1em;
|
padding: 0.5rem;
|
||||||
overflow: auto;
|
}
|
||||||
|
|
||||||
|
#top-bar a {
|
||||||
|
color: black;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#branding {
|
#branding {
|
||||||
|
@ -33,17 +51,9 @@ header > div:last-child {
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar {
|
#sidebar {
|
||||||
width: 30%;
|
display: flex;
|
||||||
float: left;
|
flex-direction: column;
|
||||||
}
|
flex-grow: 1;
|
||||||
|
|
||||||
.carosel {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
.carosel > div {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 1rem;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-pic {
|
.user-pic {
|
||||||
|
@ -63,16 +73,24 @@ header > div:last-child {
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.update {
|
#feed, #content {
|
||||||
border: 1px solid #333;
|
display: flex;
|
||||||
border-radius: 0.2rem;
|
flex-direction: column;
|
||||||
margin-bottom: 1em;
|
flex-grow: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.update > * {
|
#content > div, #feed > div, #sidebar > div {
|
||||||
padding: 1em;
|
background-color: #EFEFEF;
|
||||||
|
margin: 1rem auto;
|
||||||
|
padding: 1rem;
|
||||||
|
width: 90%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.interact {
|
form label {
|
||||||
background-color: #eee;
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-form textarea {
|
||||||
|
width: 30rem;
|
||||||
|
height: 10rem;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,30 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="main">
|
<div id="content">
|
||||||
<div class="book-profile">
|
<div>
|
||||||
<img class="book-cover" src="/static/images/med.jpg">
|
<div class="book-preview">
|
||||||
<h1>{{ book.data.title }}</h1>
|
<img class="book-cover" src="/static/images/med.jpg">
|
||||||
by {{ book.authors.first.data.name }}
|
<h1>{{ book.data.title }}</h1>
|
||||||
{{ rating }} stars
|
by {{ book.authors.first.data.name }}
|
||||||
|
{{ rating }} stars
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Leave a review</h3>
|
||||||
|
|
||||||
|
<form class="review-form" name="review" action="/review/" method="post">
|
||||||
|
<input type="hidden" name="book" value="{{ book.openlibrary_key }}"></input>
|
||||||
|
<label for="name">Title:
|
||||||
|
<input type="text" name="name"></input>
|
||||||
|
</label>
|
||||||
|
<label for="content">
|
||||||
|
<textarea name="content"></textarea>
|
||||||
|
</label>
|
||||||
|
<label for="rating">Your rating (1-5)
|
||||||
|
<input type="number" name="rating"></input>
|
||||||
|
</label>
|
||||||
|
<button type="submit">Post review</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<form name="review" action="/review/" method="post">
|
|
||||||
<input type="hidden" name="book" value="{{ book.openlibrary_key }}"></input>
|
|
||||||
<input type="text" name="name"></input>
|
|
||||||
<textarea name="content">Your review</textarea>
|
|
||||||
<input type="number" name="rating"></input>
|
|
||||||
<input type="submit" value="Post review"></input>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div class="reviews">
|
<div class="reviews">
|
||||||
<h2>Reviews</h2>
|
<h2>Reviews</h2>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="main">
|
<div id="content">
|
||||||
<div class="user-profile">
|
<div class="user-profile">
|
||||||
<img class="user-pic" src="/static/images/profile.jpg">
|
<img class="user-pic" src="/static/images/profile.jpg">
|
||||||
<form name="avatar" action="/upload-avatar/" method="post" enctype="multipart/form-data">
|
<form name="avatar" action="/upload-avatar/" method="post" enctype="multipart/form-data">
|
||||||
|
|
|
@ -1,30 +1,27 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="sidebar">
|
<div id="sidebar">
|
||||||
{# listing books currently on user's shelves #}
|
<div>
|
||||||
{# TODO: this should only show currently reading probably #}
|
<h2>Currently Reading</h2>
|
||||||
{% for shelf in shelves %}
|
{# listing books currently on user's shelves #}
|
||||||
{% if shelf.books.all %}
|
{% if not shelves.first.books.all %}
|
||||||
<h2>{{ shelf.name }}</h2>
|
<p>Start a book!</p>
|
||||||
{% for book in shelf.books.all %}
|
|
||||||
<div class="book-preview">
|
|
||||||
<img class="cover" src="static/images/small.jpg">
|
|
||||||
<p class="title"><a href="{{ book.openlibrary_key }}">{{ book.data.title }}</a></p>
|
|
||||||
<p>by <a href="" class="author">{{ book.authors.first.data.name }}</a></p>
|
|
||||||
{% if shelf.type == 'reading' %}
|
|
||||||
{# TODO: re-shelve a book #}
|
|
||||||
<button>done reading</button>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% for book in shelves.first.books.all %}
|
||||||
|
<div class="book-preview">
|
||||||
|
<img class="cover" src="static/images/small.jpg">
|
||||||
|
<p class="title"><a href="{{ book.openlibrary_key }}">{{ book.data.title }}</a></p>
|
||||||
|
<p>by <a href="" class="author">{{ book.authors.first.data.name }}</a></p>
|
||||||
|
{% if shelf.type == 'reading' %}
|
||||||
|
{# TODO: re-shelve a book #}
|
||||||
|
<button>done reading</button>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="main">
|
<div>
|
||||||
|
<h2>Recently Added Books</h2>
|
||||||
<div class="carosel">
|
|
||||||
{# a display of books in your local db, so you have somewhere to start #}
|
|
||||||
{% for book in recent_books %}
|
{% for book in recent_books %}
|
||||||
<div class="book-preview">
|
<div class="book-preview">
|
||||||
<img class="cover" src="static/images/small.jpg">
|
<img class="cover" src="static/images/small.jpg">
|
||||||
|
@ -34,7 +31,7 @@
|
||||||
{# TODO: there should be a helper function for listing authors #}
|
{# TODO: there should be a helper function for listing authors #}
|
||||||
<a href="" class="author">{{ book.authors.first.data.name }}</a>
|
<a href="" class="author">{{ book.authors.first.data.name }}</a>
|
||||||
</p>
|
</p>
|
||||||
{% if not book.user_shelves %}
|
{% if not book in user_books.all %}
|
||||||
<form name="shelve" action="/shelve/{{ request.user.localname }}_to-read/{{ book.id }}" method="post">
|
<form name="shelve" action="/shelve/{{ request.user.localname }}_to-read/{{ book.id }}" method="post">
|
||||||
<input type="hidden" name="book" value="book.id"></input>
|
<input type="hidden" name="book" value="book.id"></input>
|
||||||
<button type="submit">Want to read</button>
|
<button type="submit">Want to read</button>
|
||||||
|
@ -43,55 +40,62 @@
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="feed">
|
||||||
|
|
||||||
{% for activity in activities %}
|
{% for activity in activities %}
|
||||||
<div class="update">
|
<div class="update">
|
||||||
<div class="user-preview">
|
<h2>
|
||||||
<img class="user-pic" src="static/images/profile.jpg">
|
<img class="user-pic" src="static/images/profile.jpg">
|
||||||
{# TODO: a helper function for displaying a username #}
|
{# TODO: a helper function for displaying a username #}
|
||||||
<span><a href="/user/{% if activity.user.localname %}{{ activity.user.localname }}{% else %}{{ activity.user.username }}{% endif %}" class="user">
|
<a href="/user/{% if activity.user.localname %}{{ activity.user.localname }}{% else %}{{ activity.user.username }}{% endif %}" class="user">
|
||||||
{% if activity.user.localname %}{{ activity.user.localname }}{% else %}{{ activity.user.username }}{% endif %}</a>
|
{% if activity.user.localname %}{{ activity.user.localname }}{% else %}{{ activity.user.username }}{% endif %}</a>
|
||||||
{% if activity.fedireads_type == 'Shelve' %}
|
{% if activity.fedireads_type == 'Shelve' %}
|
||||||
{# display a reading/shelving activity #}
|
{# display a reading/shelving activity #}
|
||||||
{% if activity.shelf.shelf_type == 'to-read' %}
|
{% if activity.shelf.shelf_type == 'to-read' %}
|
||||||
wants to read
|
wants to read
|
||||||
{% elif activity.shelf.shelf_type == 'read' %}
|
{% elif activity.shelf.shelf_type == 'read' %}
|
||||||
finished reading
|
finished reading
|
||||||
{% elif activity.shelf.shelf_type == 'reading' %}
|
{% elif activity.shelf.shelf_type == 'reading' %}
|
||||||
started reading
|
started reading
|
||||||
{% else %}
|
|
||||||
shelved in "{{ activity.shelf.name }}"
|
|
||||||
{% endif %}
|
|
||||||
{# TODO: wouldn't it rule if this was a reusable piece of markup? #}
|
|
||||||
<div class="book-preview">
|
|
||||||
<img class="cover" src="static/images/med.jpg">
|
|
||||||
<p class="title">
|
|
||||||
<a href="{{ activity.book.openlibrary_key }}">{{ activity.book.data.title }}</a>
|
|
||||||
by
|
|
||||||
<a href="" class="author">{{ activity.book.authors.first.data.name }}</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{% elif activity.fedireads_type == 'Review' %}
|
|
||||||
{# display a review #}
|
|
||||||
reviewed {{ activity.book.data.title }}
|
|
||||||
<div class="book-preview review">
|
|
||||||
<img class="cover" src="static/images/med.jpg">
|
|
||||||
<p class="title">
|
|
||||||
<a href="{{ activity.book.openlibrary_key }}">{{ activity.book.data.title }}</a>
|
|
||||||
by
|
|
||||||
<a href="" class="author">{{ activity.book.authors.first.data.name }}</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>{{ activity.name }}</h3>
|
|
||||||
<p>{{ activity.rating }} stars</p>
|
|
||||||
<p>{{ activity.review_content }}</p>
|
|
||||||
</div>
|
|
||||||
{% elif activity.activity_type == 'Follow' %}
|
|
||||||
started following someone
|
|
||||||
{% else %}
|
{% else %}
|
||||||
{# generic handling for a misc activity, which perhaps should not be displayed at all #}
|
shelved in "{{ activity.shelf.name }}"
|
||||||
did {{ activity.activity_type }}</span>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</h2>
|
||||||
|
{# TODO: wouldn't it rule if this was a reusable piece of markup? #}
|
||||||
|
<div class="book-preview">
|
||||||
|
<img class="cover" src="static/images/med.jpg">
|
||||||
|
<p class="title">
|
||||||
|
<a href="{{ activity.book.openlibrary_key }}">{{ activity.book.data.title }}</a>
|
||||||
|
by
|
||||||
|
<a href="" class="author">{{ activity.book.authors.first.data.name }}</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% elif activity.fedireads_type == 'Review' %}
|
||||||
|
{# display a review #}
|
||||||
|
reviewed {{ activity.book.data.title }}
|
||||||
|
</h2>
|
||||||
|
<div class="book-preview review">
|
||||||
|
<img class="cover" src="static/images/med.jpg">
|
||||||
|
<p class="title">
|
||||||
|
<a href="{{ activity.book.openlibrary_key }}">{{ activity.book.data.title }}</a>
|
||||||
|
by
|
||||||
|
<a href="" class="author">{{ activity.book.authors.first.data.name }}</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>{{ activity.name }}</h3>
|
||||||
|
<p>{{ activity.rating }} stars</p>
|
||||||
|
<p>{{ activity.review_content }}</p>
|
||||||
|
</div>
|
||||||
|
{% elif activity.activity_type == 'Follow' %}
|
||||||
|
started following someone
|
||||||
|
</h2>
|
||||||
|
{% else %}
|
||||||
|
{# generic handling for a misc activity, which perhaps should not be displayed at all #}
|
||||||
|
did {{ activity.activity_type }}
|
||||||
|
</h2>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -47,12 +47,9 @@
|
||||||
</header>
|
</header>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="content">
|
<div id="main">
|
||||||
<div>
|
{% block content %}
|
||||||
{% block content %}
|
{% endblock %}
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<form name="login" method="post">
|
<div id="content">
|
||||||
<input type="text" name="username"></input>
|
<div>
|
||||||
<input type="password" name="password"></input>
|
<form name="login" method="post">
|
||||||
<input type="submit"></input>
|
<label for="username">Username:
|
||||||
</form>
|
<input type="text" name="username"></input>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="password">Password:
|
||||||
|
<input type="password" name="password"></input>
|
||||||
|
</label>
|
||||||
|
<button type="submit">Log in</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="main">
|
<div id="content">
|
||||||
{% for result in results %}
|
{% for result in results %}
|
||||||
{{ result.username }}
|
{{ result.username }}
|
||||||
<form action="/follow/" method="post">
|
<form action="/follow/" method="post">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="main">
|
<div id="content">
|
||||||
<div class="user-profile">
|
<div class="user-profile">
|
||||||
<img class="user-pic" src="/static/images/profile.jpg">
|
<img class="user-pic" src="/static/images/profile.jpg">
|
||||||
<h1>{% if user.localname %}{{ user.localname }}{% else %}{{ user.username }}{% endif %}</h1>
|
<h1>{% if user.localname %}{{ user.localname }}{% else %}{{ user.username }}{% endif %}</h1>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
''' application views/pages '''
|
''' application views/pages '''
|
||||||
from django.contrib.auth import authenticate, login, logout
|
from django.contrib.auth import authenticate, login, logout
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.db.models import Avg, FilteredRelation, Q
|
from django.db.models import Avg, Q
|
||||||
from django.http import HttpResponseNotFound
|
from django.http import HttpResponseNotFound
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
|
@ -15,16 +15,10 @@ from fedireads import models, openlibrary, outgoing as api
|
||||||
def home(request):
|
def home(request):
|
||||||
''' user's homepage with activity feed '''
|
''' user's homepage with activity feed '''
|
||||||
shelves = models.Shelf.objects.filter(user=request.user.id)
|
shelves = models.Shelf.objects.filter(user=request.user.id)
|
||||||
|
user_books = models.Book.objects.filter(shelves__user=request.user).all()
|
||||||
recent_books = models.Book.objects.order_by(
|
recent_books = models.Book.objects.order_by(
|
||||||
'added_date'
|
'added_date'
|
||||||
).annotate(
|
)[:10]
|
||||||
user_shelves=FilteredRelation(
|
|
||||||
'shelves',
|
|
||||||
condition=Q(shelves__user_id=request.user.id)
|
|
||||||
)
|
|
||||||
).values(
|
|
||||||
'id', 'authors', 'data', 'user_shelves', 'openlibrary_key'
|
|
||||||
).distinct()
|
|
||||||
|
|
||||||
following = models.User.objects.filter(
|
following = models.User.objects.filter(
|
||||||
Q(followers=request.user) | Q(id=request.user.id)
|
Q(followers=request.user) | Q(id=request.user.id)
|
||||||
|
@ -41,6 +35,7 @@ def home(request):
|
||||||
'user': request.user,
|
'user': request.user,
|
||||||
'shelves': shelves,
|
'shelves': shelves,
|
||||||
'recent_books': recent_books,
|
'recent_books': recent_books,
|
||||||
|
'user_books': user_books,
|
||||||
'activities': activities,
|
'activities': activities,
|
||||||
}
|
}
|
||||||
return TemplateResponse(request, 'feed.html', data)
|
return TemplateResponse(request, 'feed.html', data)
|
||||||
|
|
Loading…
Reference in a new issue