Merge branch 'main' into list-not-loading
This commit is contained in:
@ -135,6 +135,18 @@ button::-moz-focus-inner {
|
||||
border-bottom: 1px solid #ededed;
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.is-flex-direction-row-mobile {
|
||||
flex-direction: row !important;
|
||||
}
|
||||
|
||||
.is-flex-direction-column-mobile {
|
||||
flex-direction: column !important;
|
||||
}
|
||||
}
|
||||
|
||||
.tag.is-small {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.button.is-transparent {
|
||||
@ -215,7 +227,7 @@ input[type=file]::file-selector-button:hover {
|
||||
/** General `details` element styles
|
||||
******************************************************************************/
|
||||
|
||||
summary {
|
||||
details summary {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@ -223,22 +235,22 @@ summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
summary::marker {
|
||||
details summary::marker {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.detail-pinned-button summary {
|
||||
details.detail-pinned-button summary {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.detail-pinned-button form {
|
||||
details.detail-pinned-button form {
|
||||
float: left;
|
||||
width: -webkit-fill-available;
|
||||
width: 100%;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
/** Details dropdown
|
||||
/** Dropdown w/ Details element
|
||||
******************************************************************************/
|
||||
|
||||
details.dropdown[open] summary.dropdown-trigger::before {
|
||||
@ -250,11 +262,11 @@ details.dropdown[open] summary.dropdown-trigger::before {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
details .dropdown-menu {
|
||||
details.dropdown .dropdown-menu {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
details .dropdown-menu button {
|
||||
details.dropdown .dropdown-menu button {
|
||||
/* Fix weird Safari defaults */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@ -289,6 +301,46 @@ details.dropdown .dropdown-menu a:focus-visible {
|
||||
}
|
||||
}
|
||||
|
||||
/** Details panel
|
||||
******************************************************************************/
|
||||
|
||||
details.details-panel {
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
|
||||
transition: box-shadow 0.2s ease;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
details[open].details-panel,
|
||||
details.details-panel:hover {
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
details.details-panel summary {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
details.details-panel summary .details-close {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
transform: rotate(45deg);
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
details[open].details-panel summary .details-close {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 769px) {
|
||||
.details-panel .filters-field:not(:last-child) {
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.1);
|
||||
margin-top: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
padding-top: 0.25rem;
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
/** Shelving
|
||||
******************************************************************************/
|
||||
|
||||
@ -1149,3 +1201,93 @@ ol.ordered-list li::before {
|
||||
margin-bottom: 0.75rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Gaps (for Flexbox and Grid)
|
||||
*
|
||||
* Those are supplementary rules to Bulma’s. They follow the same conventions.
|
||||
* Add those you’ll need.
|
||||
******************************************************************************/
|
||||
|
||||
.is-gap-0 {
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.is-gap-1 {
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.is-gap-2 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.is-gap-3 {
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.is-gap-4 {
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.is-gap-5 {
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.is-gap-6 {
|
||||
gap: 3rem;
|
||||
}
|
||||
|
||||
.is-row-gap-0 {
|
||||
row-gap: 0;
|
||||
}
|
||||
|
||||
.is-row-gap-1 {
|
||||
row-gap: 0.25rem;
|
||||
}
|
||||
|
||||
.is-row-gap-2 {
|
||||
row-gap: 0.5rem;
|
||||
}
|
||||
|
||||
.is-row-gap-3 {
|
||||
row-gap: 0.75rem;
|
||||
}
|
||||
|
||||
.is-row-gap-4 {
|
||||
row-gap: 1rem;
|
||||
}
|
||||
|
||||
.is-row-gap-5 {
|
||||
row-gap: 1.5rem;
|
||||
}
|
||||
|
||||
.is-row-gap-6 {
|
||||
row-gap: 3rem;
|
||||
}
|
||||
|
||||
.is-column-gap-0 {
|
||||
column-gap: 0;
|
||||
}
|
||||
|
||||
.is-column-gap-1 {
|
||||
column-gap: 0.25rem;
|
||||
}
|
||||
|
||||
.is-column-gap-2 {
|
||||
column-gap: 0.5rem;
|
||||
}
|
||||
|
||||
.is-column-gap-3 {
|
||||
column-gap: 0.75rem;
|
||||
}
|
||||
|
||||
.is-column-gap-4 {
|
||||
column-gap: 1rem;
|
||||
}
|
||||
|
||||
.is-column-gap-5 {
|
||||
column-gap: 1.5rem;
|
||||
}
|
||||
|
||||
.is-column-gap-6 {
|
||||
column-gap: 3rem;
|
||||
}
|
||||
|
4
bookwyrm/static/css/vendor/icons.css
vendored
4
bookwyrm/static/css/vendor/icons.css
vendored
@ -25,6 +25,10 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon.is-small {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.icon-book:before {
|
||||
content: "\e901";
|
||||
}
|
||||
|
@ -1,21 +0,0 @@
|
||||
/* exported BlockHref */
|
||||
|
||||
let BlockHref = new (class {
|
||||
constructor() {
|
||||
document
|
||||
.querySelectorAll("[data-href]")
|
||||
.forEach((t) => t.addEventListener("click", this.followLink.bind(this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Follow a fake link
|
||||
*
|
||||
* @param {Event} event
|
||||
* @return {undefined}
|
||||
*/
|
||||
followLink(event) {
|
||||
const url = event.currentTarget.dataset.href;
|
||||
|
||||
window.location.href = url;
|
||||
}
|
||||
})();
|
@ -192,6 +192,31 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if goal_status and goal_status.percent >= 100 %}
|
||||
<div class="columns">
|
||||
<div class="column has-text-centered">
|
||||
<h2 class="title is-3 is-serif">
|
||||
{% with goal=goal_status.goal goal_percent=goal_status.percent %}
|
||||
{% blocktrans trimmed count counter=goal %}
|
||||
{{ display_name }} set a goal of reading {{ goal }} book in {{ year }},<br />
|
||||
and achieved {{ goal_percent }}% of that goal
|
||||
{% plural %}
|
||||
{{ display_name }} set a goal of reading {{ goal }} books in {{ year }},<br />
|
||||
and achieved {{ goal_percent }}% of that goal
|
||||
{% endblocktrans %}
|
||||
{% endwith %}
|
||||
</h2>
|
||||
<p class="subtitle is-5">{% trans "Way to go!" %}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns">
|
||||
<div class="column is-one-fifth is-offset-two-fifths">
|
||||
<hr />
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if ratings_total > 0 %}
|
||||
<div class="columns">
|
||||
<div class="column has-text-centered">
|
||||
|
@ -6,57 +6,22 @@
|
||||
<h1 class="title">
|
||||
{{ tab.name }}
|
||||
</h1>
|
||||
<div class="block is-clipped">
|
||||
<div class="is-pulled-left">
|
||||
<div class="tabs">
|
||||
<ul>
|
||||
{% for stream in streams %}
|
||||
<li class="{% if tab.key == stream.key %}is-active{% endif %}"{% if tab.key == stream.key %} aria-current="page"{% endif %}>
|
||||
<a href="/{{ stream.key }}#feed">{{ stream.shortname }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# feed settings #}
|
||||
<details class="detail-pinned-button" {% if settings_saved %}open{% endif %}>
|
||||
<summary class="control">
|
||||
<span class="button">
|
||||
<span class="icon icon-dots-three m-0-mobile" aria-hidden="true"></span>
|
||||
<span class="is-sr-only-mobile">{{ _("Feed settings") }}</span>
|
||||
</span>
|
||||
</summary>
|
||||
<form class="notification level is-align-items-flex-end" method="post" action="/{{ tab.key }}#feed">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="level-left">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<span class="is-flex is-align-items-baseline">
|
||||
<label class="label mt-2 mb-1">Status types</label>
|
||||
{% if settings_saved %}
|
||||
<span class="tag is-success is-light ml-2">{{ _("Saved!") }}</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% for name, value in feed_status_types_options %}
|
||||
<label class="mr-2">
|
||||
<input type="checkbox" name="feed_status_types" value="{{ name }}" {% if name in user.feed_status_types %}checked=""{% endif %}/>
|
||||
{{ value }}
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right control">
|
||||
<button class="button is-small is-primary is-outlined" type="submit">
|
||||
{{ _("Save settings") }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</details>
|
||||
<div class="tabs">
|
||||
<ul>
|
||||
{% for stream in streams %}
|
||||
<li class="{% if tab.key == stream.key %}is-active{% endif %}"{% if tab.key == stream.key %} aria-current="page"{% endif %}>
|
||||
<a href="/{{ stream.key }}#feed">{{ stream.shortname }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{# feed settings #}
|
||||
{% with "/"|add:tab.key|add:"#feed" as action %}
|
||||
{% include 'feed/feed_filters.html' with size="small" method="post" action=action %}
|
||||
{% endwith %}
|
||||
|
||||
{# announcements and system messages #}
|
||||
{% if not activities.number > 1 %}
|
||||
<a href="{{ request.path }}" class="transition-y is-hidden notification is-primary is-block" data-poll-wrapper>
|
||||
@ -73,7 +38,7 @@
|
||||
{% endif %}
|
||||
|
||||
{% if annual_summary_year and tab.key == 'home' %}
|
||||
<section class="block" data-hide="hide_annual_summary_{{ annual_summary_year }}">
|
||||
<section class="block is-hidden" data-hide="hide_annual_summary_{{ annual_summary_year }}">
|
||||
{% include 'feed/summary_card.html' with year=annual_summary_year %}
|
||||
<hr>
|
||||
</section>
|
||||
|
5
bookwyrm/templates/feed/feed_filters.html
Normal file
5
bookwyrm/templates/feed/feed_filters.html
Normal file
@ -0,0 +1,5 @@
|
||||
{% extends 'snippets/filters_panel/filters_panel.html' %}
|
||||
|
||||
{% block filter_fields %}
|
||||
{% include 'feed/status_types_filter.html' %}
|
||||
{% endblock %}
|
16
bookwyrm/templates/feed/status_types_filter.html
Normal file
16
bookwyrm/templates/feed/status_types_filter.html
Normal file
@ -0,0 +1,16 @@
|
||||
{% extends 'snippets/filters_panel/filter_field.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block filter %}
|
||||
<label class="label mt-2 mb-1">Status types</label>
|
||||
|
||||
<div class="is-flex is-flex-direction-row is-flex-direction-column-mobile">
|
||||
{% for name, value in feed_status_types_options %}
|
||||
<label class="mr-2">
|
||||
<input type="checkbox" name="feed_status_types" value="{{ name }}" {% if name in user.feed_status_types %}checked=""{% endif %}/>
|
||||
{{ value }}
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -1,12 +1,15 @@
|
||||
{% load bookwyrm_tags %}
|
||||
{% related_status notification as related_status %}
|
||||
<div class="notification is-clickable {% if notification.id in unread %} is-primary{% endif %}" data-href="{% block primary_link %}{% endblock %}">
|
||||
<div class="box is-shadowless has-background-white-ter {% if notification.id in unread %} is-primary{% endif %}">
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-narrow is-size-3 {% if notification.id in unread%}has-text-white{% else %}has-text-grey{% endif %}">
|
||||
{% block icon %}{% endblock %}
|
||||
<a class="has-text-dark" href="{% block primary_link %}{% endblock %}">
|
||||
{% block icon %}{% endblock %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="column is-clipped">
|
||||
<div class="block">
|
||||
<div class="block content">
|
||||
<p>
|
||||
{% if notification.related_user %}
|
||||
<a href="{{ notification.related_user.local_path }}">{% include 'snippets/avatar.html' with user=notification.related_user %}
|
||||
@ -15,6 +18,7 @@
|
||||
{% block description %}{% endblock %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% if related_status %}
|
||||
<div class="block">
|
||||
{% block preview %}{% endblock %}
|
||||
|
@ -46,7 +46,3 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{% static "js/block_href.js" %}?v={{ js_cache }}"></script>
|
||||
{% endblock %}
|
||||
|
@ -8,7 +8,7 @@
|
||||
<ul class="block">
|
||||
{% for result in local_results.results %}
|
||||
<li class="pd-4 mb-5">
|
||||
<div class="columns is-mobile is-gapless">
|
||||
<div class="columns is-mobile is-gapless mb-0">
|
||||
<div class="column is-cover">
|
||||
{% include 'snippets/book_cover.html' with book=result cover_class='is-w-xs is-h-xs' %}
|
||||
</div>
|
||||
@ -34,34 +34,28 @@
|
||||
<div class="block">
|
||||
{% for result_set in results|slice:"1:" %}
|
||||
{% if result_set.results %}
|
||||
<section class="box has-background-white-bis">
|
||||
<section class="mb-5">
|
||||
{% if not result_set.connector.local %}
|
||||
<header class="columns is-mobile">
|
||||
<div class="column">
|
||||
<h3 class="title is-5">
|
||||
<details class="details-panel box" {% if forloop.first %}open{% endif %}>
|
||||
{% endif %}
|
||||
{% if not result_set.connector.local %}
|
||||
<summary class="is-flex is-align-items-center is-flex-wrap-wrap is-gap-2">
|
||||
<span class="mb-0 title is-5">
|
||||
{% trans 'Results from' %}
|
||||
<a href="{{ result_set.connector.base_url }}" target="_blank">{{ result_set.connector.name|default:result_set.connector.identifier }}</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="column is-narrow">
|
||||
{% trans "Open" as button_text %}
|
||||
{% include 'snippets/toggle/open_button.html' with text=button_text controls_text="more_results_panel" controls_uid=result_set.connector.identifier class="is-small" icon_with_text="arrow-down" pressed=forloop.first %}
|
||||
{% trans "Close" as button_text %}
|
||||
{% include 'snippets/toggle/close_button.html' with text=button_text controls_text="more_results_panel" controls_uid=result_set.connector.identifier class="is-small" icon_with_text="arrow-up" pressed=forloop.first %}
|
||||
</div>
|
||||
</header>
|
||||
{% endif %}
|
||||
</span>
|
||||
|
||||
<div class="box has-background-white is-shadowless{% if not forloop.first %} is-hidden{% endif %}" id="more_results_panel_{{ result_set.connector.identifier }}">
|
||||
<span class="details-close icon icon-x" aria-hidden></span>
|
||||
</summary>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-5">
|
||||
<div class="is-flex is-flex-direction-row-reverse">
|
||||
<div>
|
||||
</div>
|
||||
|
||||
<ul class="is-flex-grow-1">
|
||||
{% for result in result_set.results %}
|
||||
<li class="mb-5">
|
||||
<li class="{% if not forloop.last %}mb-5{% endif %}">
|
||||
<div class="columns is-mobile is-gapless">
|
||||
<div class="columns is-mobile is-gapless">
|
||||
<div class="column is-1 is-cover">
|
||||
{% include 'snippets/book_cover.html' with book=result cover_class='is-w-xs is-h-xs' external_path=True %}
|
||||
</div>
|
||||
<div class="column is-10 ml-3">
|
||||
@ -92,6 +86,9 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% if not result_set.connector.local %}
|
||||
</details>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
@ -1,6 +1,4 @@
|
||||
<div class="column is-flex">
|
||||
<div class="box is-flex-grow-1">
|
||||
{% block filter %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
<div class="filters-field column">
|
||||
{% block filter %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
@ -1,28 +1,47 @@
|
||||
{% load i18n %}
|
||||
<div class="notification content">
|
||||
<h2 class="columns is-mobile mb-0">
|
||||
<span class="column pb-0">Filters</span>
|
||||
<details class="details-panel box is-size-{{ size|default:'normal' }}" {% if filters_applied %}open{% endif %}>
|
||||
<summary class="is-flex is-align-items-center is-flex-wrap-wrap is-gap-2">
|
||||
<span class="mb-0 title {% if size == 'small' %}is-6{% else %}is-5{% endif %} is-flex-shrink-0">
|
||||
{% trans "Filters" %}
|
||||
|
||||
<span class="column is-narrow pb-0">
|
||||
{% trans "Show filters" as text %}
|
||||
{% include 'snippets/toggle/open_button.html' with text=text controls_text="filters" icon_with_text="arrow-down" class="is-small" focus="filters" %}
|
||||
{% trans "Hide filters" as text %}
|
||||
{% include 'snippets/toggle/close_button.html' with text=text controls_text="filters" icon_with_text="arrow-up" class="is-small" %}
|
||||
</span>
|
||||
</h2>
|
||||
|
||||
<form class="is-hidden mt-3" id="filters" method="get" action="{{ request.path }}" tabindex="0">
|
||||
{% if sort %}
|
||||
<input type="hidden" name="sort" value="{{ sort }}">
|
||||
{% if filters_applied %}
|
||||
<span class="tag is-success is-light ml-2 mb-0 is-{{ size|default:'normal' }}">
|
||||
{{ _("Filters are applied") }}
|
||||
</span>
|
||||
{% endif %}
|
||||
<div class="columns">
|
||||
{% block filter_fields %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
<button class="button is-primary">{% trans "Apply filters" %}</button>
|
||||
</form>
|
||||
|
||||
{% if request.GET %}
|
||||
<a class="help" href="{{ request.path }}">{% trans "Clear filters" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if request.GET %}
|
||||
<span class="mb-0 tags has-addons">
|
||||
<span class="mb-0 tag is-success is-light is-{{ size|default:'normal' }}">
|
||||
{% trans "Filters are applied" %}
|
||||
</span>
|
||||
<a class="mb-0 tag is-success is-{{ size|default:'normal' }}" href="{{ request.path }}">
|
||||
{% trans "Clear filters" %}
|
||||
</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
<span class="details-close icon icon-x is-{{ size|default:'normal' }}" aria-hidden></span>
|
||||
</summary>
|
||||
|
||||
<div class="mt-3">
|
||||
<form id="filters" method="{{ method|default:'get' }}" action="{{ action|default:request.path }}">
|
||||
{% if method == 'post' %}
|
||||
{% csrf_token %}
|
||||
{% endif %}
|
||||
|
||||
{% if sort %}
|
||||
<input type="hidden" name="sort" value="{{ sort }}">
|
||||
{% endif %}
|
||||
<div class="mt-3 columns filters-fields is-align-items-stretch">
|
||||
{% block filter_fields %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
<button type="submit" class="button is-primary is-small">
|
||||
{% trans "Apply filters" %}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</details>
|
||||
|
@ -63,16 +63,18 @@
|
||||
{% endfor %}
|
||||
|
||||
{% if shelf.identifier == 'all' %}
|
||||
{% for shelved_in in book.shelves.all %}
|
||||
{% for user_shelf in user_shelves %}
|
||||
{% if user_shelf in book.shelves.all %}
|
||||
<li class="navbar-divider m-0" role="separator" ></li>
|
||||
<li role="menuitem" class="dropdown-item p-0">
|
||||
<form name="shelve" action="/unshelve/" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="book" value="{{ book.id }}">
|
||||
<input type="hidden" name="shelf" value="{{ shelved_in.id }}">
|
||||
<button class="button is-fullwidth is-small is-radiusless is-danger is-light" type="submit">{% trans "Remove from" %} {{ shelved_in.name }}</button>
|
||||
<input type="hidden" name="shelf" value="{{ user_shelf.id }}">
|
||||
<button class="button is-fullwidth is-small is-radiusless is-danger is-light" type="submit">{% trans "Remove from" %} {{ user_shelf.name }}</button>
|
||||
</form>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<li class="navbar-divider" role="separator" ></li>
|
||||
|
@ -1,11 +1,9 @@
|
||||
{% if status.content == 'wants to read' %}
|
||||
{% include 'snippets/status/headers/to_read.html' with book=status.mention_books.first %}
|
||||
{% endif %}
|
||||
|
||||
{% if status.content == 'finished reading' %}
|
||||
{% elif status.content == 'finished reading' %}
|
||||
{% include 'snippets/status/headers/read.html' with book=status.mention_books.first %}
|
||||
{% endif %}
|
||||
|
||||
{% if status.content == 'started reading' %}
|
||||
{% elif status.content == 'started reading' %}
|
||||
{% include 'snippets/status/headers/reading.html' with book=status.mention_books.first %}
|
||||
{% else %}
|
||||
{{ status.content }}
|
||||
{% endif %}
|
||||
|
@ -1,5 +0,0 @@
|
||||
{% spaceless %}
|
||||
{% load i18n %}{% load humanize %}
|
||||
|
||||
{{ status.content }}
|
||||
{% endspaceless %}
|
@ -484,7 +484,6 @@ class ModelFields(TestCase):
|
||||
|
||||
instance.set_field_from_activity(book, mock_activity)
|
||||
self.assertIsNotNone(book.cover.name)
|
||||
self.assertEqual(book.cover.size, 43200)
|
||||
|
||||
@responses.activate
|
||||
def test_image_field_set_field_from_activity_no_overwrite_no_cover(self, *_):
|
||||
@ -511,7 +510,6 @@ class ModelFields(TestCase):
|
||||
|
||||
instance.set_field_from_activity(book, mock_activity, overwrite=False)
|
||||
self.assertIsNotNone(book.cover.name)
|
||||
self.assertEqual(book.cover.size, 43200)
|
||||
|
||||
@responses.activate
|
||||
def test_image_field_set_field_from_activity_no_overwrite_with_cover(self, *_):
|
||||
@ -540,14 +538,15 @@ class ModelFields(TestCase):
|
||||
)
|
||||
book = Edition.objects.create(title="hello")
|
||||
book.cover.save("test.jpg", ContentFile(output.getvalue()))
|
||||
self.assertEqual(book.cover.size, 2136)
|
||||
cover_size = book.cover.size
|
||||
self.assertIsNotNone(cover_size)
|
||||
|
||||
MockActivity = namedtuple("MockActivity", ("cover"))
|
||||
mock_activity = MockActivity("http://www.example.com/image.jpg")
|
||||
|
||||
instance.set_field_from_activity(book, mock_activity, overwrite=False)
|
||||
# same cover as before
|
||||
self.assertEqual(book.cover.size, 2136)
|
||||
self.assertEqual(book.cover.size, cover_size)
|
||||
|
||||
@responses.activate
|
||||
def test_image_field_set_field_from_activity_with_overwrite_with_cover(self, *_):
|
||||
@ -560,7 +559,8 @@ class ModelFields(TestCase):
|
||||
image.save(output, format=image.format)
|
||||
book = Edition.objects.create(title="hello")
|
||||
book.cover.save("test.jpg", ContentFile(output.getvalue()))
|
||||
self.assertEqual(book.cover.size, 2136)
|
||||
cover_size = book.cover.size
|
||||
self.assertIsNotNone(cover_size)
|
||||
|
||||
another_image_file = pathlib.Path(__file__).parent.joinpath(
|
||||
"../../static/images/logo.png"
|
||||
@ -584,7 +584,7 @@ class ModelFields(TestCase):
|
||||
instance.set_field_from_activity(book, mock_activity, overwrite=True)
|
||||
# new cover
|
||||
self.assertIsNotNone(book.cover.name)
|
||||
self.assertEqual(book.cover.size, 376800)
|
||||
self.assertNotEqual(book.cover.size, cover_size)
|
||||
|
||||
def test_datetime_field(self, *_):
|
||||
"""this one is pretty simple, it just has to use isoformat"""
|
||||
|
@ -24,7 +24,7 @@ LAST_DAY = 15
|
||||
class AnnualSummary(View):
|
||||
"""display a summary of the year for the current user"""
|
||||
|
||||
def get(self, request, username, year):
|
||||
def get(self, request, username, year): # pylint: disable=too-many-locals
|
||||
"""get response"""
|
||||
|
||||
user = get_user_from_username(request.user, username)
|
||||
@ -79,6 +79,9 @@ class AnnualSummary(View):
|
||||
)
|
||||
ratings_stats = ratings.aggregate(Avg("rating"))
|
||||
|
||||
# annual goal status
|
||||
goal_status = get_goal_status(user, year)
|
||||
|
||||
data = {
|
||||
"summary_user": user,
|
||||
"year": year,
|
||||
@ -101,6 +104,7 @@ class AnnualSummary(View):
|
||||
review.book.id for review in ratings.filter(rating=5)
|
||||
],
|
||||
"paginated_years": paginated_years,
|
||||
"goal_status": goal_status,
|
||||
}
|
||||
|
||||
return TemplateResponse(request, "annual_summary/layout.html", data)
|
||||
@ -208,3 +212,17 @@ def get_books_from_shelfbooks(books_ids):
|
||||
books = models.Edition.objects.filter(id__in=books_ids).order_by(ordered)
|
||||
|
||||
return books
|
||||
|
||||
|
||||
def get_goal_status(user, year):
|
||||
"""return a dict with the year's goal status"""
|
||||
|
||||
try:
|
||||
goal = models.AnnualGoal.objects.get(user=user, year=year)
|
||||
except models.AnnualGoal.DoesNotExist:
|
||||
return None
|
||||
|
||||
if goal.privacy != "public":
|
||||
return None
|
||||
|
||||
return dict(**goal.progress, **{"goal": goal.goal})
|
||||
|
@ -26,17 +26,17 @@ class Feed(View):
|
||||
|
||||
def post(self, request, tab):
|
||||
"""save feed settings form, with a silent validation fail"""
|
||||
settings_saved = False
|
||||
filters_applied = False
|
||||
form = forms.FeedStatusTypesForm(request.POST, instance=request.user)
|
||||
if form.is_valid():
|
||||
# workaround to avoid broadcasting this change
|
||||
user = form.save(commit=False)
|
||||
user.save(broadcast=False, update_fields=["feed_status_types"])
|
||||
settings_saved = True
|
||||
filters_applied = True
|
||||
|
||||
return self.get(request, tab, settings_saved)
|
||||
return self.get(request, tab, filters_applied)
|
||||
|
||||
def get(self, request, tab, settings_saved=False):
|
||||
def get(self, request, tab, filters_applied=False):
|
||||
"""user's homepage with activity feed"""
|
||||
tab = [s for s in STREAMS if s["key"] == tab]
|
||||
tab = tab[0] if tab else STREAMS[0]
|
||||
@ -63,7 +63,7 @@ class Feed(View):
|
||||
"goal_form": forms.GoalForm(),
|
||||
"feed_status_types_options": FeedFilterChoices,
|
||||
"allowed_status_types": request.user.feed_status_types,
|
||||
"settings_saved": settings_saved,
|
||||
"filters_applied": filters_applied,
|
||||
"path": f"/{tab['key']}",
|
||||
"annual_summary_year": get_annual_summary_year(),
|
||||
},
|
||||
|
@ -72,9 +72,13 @@ class Shelf(View):
|
||||
"start_date"
|
||||
)
|
||||
|
||||
if shelf_identifier:
|
||||
books = books.annotate(shelved_date=F("shelfbook__shelved_date"))
|
||||
else:
|
||||
# sorting by shelved date will cause duplicates in the "all books" view
|
||||
books = books.annotate(shelved_date=F("updated_date"))
|
||||
books = books.annotate(
|
||||
rating=Subquery(reviews.values("rating")[:1]),
|
||||
shelved_date=F("shelfbook__shelved_date"),
|
||||
start_date=Subquery(reading.values("start_date")[:1]),
|
||||
finish_date=Subquery(reading.values("finish_date")[:1]),
|
||||
author=Subquery(
|
||||
|
Reference in New Issue
Block a user