Merge pull request #1388 from bookwyrm-social/draft-caching

Save status drafts in localstorage
This commit is contained in:
Mouse Reeve 2021-09-10 10:36:41 -07:00 committed by GitHub
commit a15ba14226
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 435 additions and 75 deletions

View file

@ -302,7 +302,42 @@ body {
right: 0;
}
/* States
/** Animations and transitions
******************************************************************************/
@keyframes turning {
from { transform: rotateZ(0deg); }
to { transform: rotateZ(360deg); }
}
.is-processing .icon-spinner::before {
animation: turning 1.5s infinite linear;
}
.icon-spinner {
display: none;
}
.is-processing .icon-spinner {
display: flex;
}
@media (prefers-reduced-motion: reduce) {
.is-processing .icon::before {
transition-duration: 0.001ms !important;
}
}
/** Transient notification
******************************************************************************/
#live-messages {
position: fixed;
bottom: 1em;
right: 1em;
}
/** States
******************************************************************************/
/* "disabled" for non-buttons */

View file

@ -39,6 +39,7 @@
<glyph unicode="&#xe91e;" glyph-name="graphic-heart" d="M934.176 791.52c-116.128 115.072-301.824 117.472-422.112 9.216-120.32 108.256-305.952 105.856-422.144-9.216-119.712-118.528-119.712-310.688 0-429.28 34.208-33.888 353.696-350.112 353.696-350.112 37.856-37.504 99.072-37.504 136.896 0 0 0 349.824 346.304 353.696 350.112 119.744 118.592 119.744 310.752-0.032 429.28zM888.576 407.424l-353.696-350.112c-12.576-12.512-33.088-12.512-45.6 0l-353.696 350.112c-94.4 93.44-94.4 245.472 0 338.912 91.008 90.080 237.312 93.248 333.088 7.104l43.392-39.040 43.36 39.040c95.808 86.144 242.112 83.008 333.12-7.104 94.4-93.408 94.4-245.44 0.032-338.912zM296.096 719.968c8.864 0 16-7.168 16-16s-7.168-16-16-16h-0.032c-57.408 0-103.968-46.56-103.968-103.968v-0.032c0-8.832-7.168-16-16-16s-16 7.168-16 16v0c0 75.072 60.832 135.904 135.872 135.968 0.064 0 0.064 0.032 0.128 0.032z" />
<glyph unicode="&#xe91f;" glyph-name="graphic-paperplane" d="M1009.376 954.88c-5.312 3.424-11.36 5.12-17.376 5.12-6.176 0-12.384-1.76-17.76-5.376l-960-640c-9.888-6.56-15.328-18.112-14.048-29.952 1.216-11.808 8.896-22.016 19.936-26.368l250.368-100.192 117.728-206.016c5.632-9.888 16.096-16 27.424-16.128 0.128 0 0.224 0 0.352 0 11.232 0 21.664 5.952 27.424 15.552l66.464 110.816 310.24-124.064c3.808-1.536 7.808-2.272 11.872-2.272 5.44 0 10.816 1.376 15.68 4.128 8.448 4.736 14.24 13.056 15.872 22.624l160 960c2.080 12.576-3.488 25.184-14.176 32.128zM100.352 295.136l741.6 494.432-539.2-577.184c-2.848 1.696-5.376 3.936-8.512 5.184l-193.888 77.568zM326.048 189.888c-0.064 0.128-0.16 0.192-0.224 0.32l606.176 648.8-516.768-805.184-89.184 156.064zM806.944 12.512l-273.312 109.312c-6.496 2.56-13.248 3.424-19.936 3.808l420.864 652.416-127.616-765.536z" />
<glyph unicode="&#xe920;" glyph-name="graphic-banknote" d="M1005.28 621.248l-320 320c-15.872 15.872-38.88 22.24-60.672 16.864-11.488-2.816-21.76-8.736-29.888-16.864-7.264-7.264-12.736-16.256-15.872-26.304-14.496-47.008-39.552-87.872-76.64-124.928-49.536-49.504-114.048-87.008-182.304-126.656-72.448-41.984-147.296-85.504-208.64-146.816-52.128-52.192-87.616-110.24-108.416-177.632-7.008-22.752-0.896-47.36 15.872-64.192l320-320c15.872-15.872 38.88-22.24 60.672-16.864 11.488 2.88 21.76 8.736 29.888 16.864 7.264 7.264 12.736 16.256 15.872 26.368 14.528 47.008 39.584 87.872 76.704 124.928 49.504 49.504 113.984 86.944 182.304 126.56 72.384 42.048 147.264 85.568 208.576 146.88 52.128 52.128 87.616 110.24 108.448 177.632 6.976 22.72 0.832 47.424-15.904 64.16zM384 0c-105.984 105.984-214.016 214.048-320 320 90.944 294.432 485.12 281.568 576 576 105.984-105.952 214.048-214.016 320.064-320-90.976-294.368-485.152-281.568-576.064-576zM625.984 483.2c-10.432 8.736-20.928 14.688-31.488 17.632-10.496 2.944-20.992 4.128-31.616 3.36-10.496-0.8-21.248-3.2-32-7.328-10.752-4.192-21.568-8.736-32.448-14.016-17.184 19.744-34.368 39.264-51.552 57.376 7.744 7.008 15.264 10.56 22.496 10.816 7.264 0.32 14.24-0.448 20.864-2.112 6.752-1.696 12.928-3.136 18.624-4.256 5.76-1.12 10.752 0.128 15.136 3.808 4.64 4 7.2 9.184 7.552 15.424 0.32 6.304-2.048 12.448-7.328 18.432-6.752 7.744-14.88 12.448-24.64 14.176-9.632 1.696-19.488 1.568-29.76-0.672-10.112-2.304-19.744-6.112-28.864-11.488s-16.448-10.88-21.888-16.256c-2.080 1.984-4.16 3.936-6.24 5.888-2.304 2.112-5.184 3.264-8.64 3.2-3.488 0-6.368-1.504-8.736-4.256-2.304-2.688-3.36-5.824-2.944-9.12 0.32-3.424 1.696-6.048 4.064-8.064 2.080-1.76 4.16-3.488 6.24-5.312-8.192-9.888-14.944-20.8-20.256-32.32-5.376-11.552-8.576-23.008-9.76-34.112-1.248-11.2-0.064-21.44 3.36-30.944 3.424-9.568 9.76-17.696 19.008-25.376 15.072-12.512 32.8-17.824 53.376-16.64 20.512 1.248 42.624 7.36 66.4 20.128 18.88-21.824 37.824-43.488 56.736-63.616-8-6.752-15.008-10.624-21.184-11.872-6.176-1.312-11.68-1.184-16.672 0.32-4.992 1.568-9.632 3.808-13.888 6.688-4.256 2.944-8.448 5.44-12.64 7.488-4.128 2.048-8.384 3.2-12.736 3.264s-8.992-2.048-14.112-6.432c-5.248-4.576-7.872-9.888-7.872-15.872 0-5.952 2.752-12 8.128-18.112 5.44-6.112 12.512-11.264 21.056-15.328s18.208-6.624 28.832-7.328c10.624-0.736 21.824 0.864 33.632 5.248 11.872 4.32 23.616 12.128 35.2 23.744 5.568-5.44 11.2-10.624 16.8-15.616 2.368-2.048 5.248-3.072 8.736-2.816 3.36 0.128 6.304 1.696 8.64 4.512 2.368 2.88 3.36 6.048 3.008 9.376-0.32 3.36-1.696 5.952-4 7.808-5.632 4.512-11.264 9.248-16.864 14.24 9.568 11.744 17.248 24.128 22.944 36.384 5.696 12.32 9.056 24.192 10.176 35.2 1.12 11.072-0.192 21.056-3.808 30.112-3.584 9.184-9.952 17.056-19.072 24.64zM447.072 461.504c-9.056-0.384-16.96 2.624-23.872 9.312-2.944 2.816-4.992 6.24-6.24 10.304-1.312 4.064-1.76 8.512-1.248 13.376 0.448 4.8 1.888 9.824 4.384 14.88 2.368 5.056 5.888 10.112 10.368 15.008 16.224-16.128 32.416-33.824 48.64-52.128-12.288-6.752-22.976-10.368-32.032-10.752zM598.016 397.44c-2.88-5.312-6.176-10.048-10.048-14.176-17.952 18.112-35.872 38.016-53.76 58.432 4.576 2.048 9.376 4.192 14.56 6.368s10.368 3.616 15.552 4.512c5.312 0.8 10.56 0.576 15.808-0.672 5.184-1.312 10.112-4.128 14.688-8.576 4.512-4.512 7.36-9.184 8.512-14.24 1.248-5.12 1.312-10.304 0.448-15.616-0.928-5.344-2.816-10.656-5.76-16.032zM470.944 250.24c6.304 5.088 15.584 4.832 21.376-1.056 6.272-6.24 6.272-16.448 0-22.688-0.512-0.512-1.056-0.864-1.632-1.312l0.064-0.064c-20.256-15.392-36.896-29.248-54.848-47.2-16.224-16.192-30.88-33.248-43.552-50.56l-20.448-28c-0.64-1.152-1.408-2.208-2.368-3.2-6.272-6.24-16.48-6.24-22.72 0-5.44 5.44-6.112 13.824-2.112 20.064l-0.064 0.064 21.888 29.888c13.664 18.688 29.376 36.992 46.752 54.368 18.080 18.144 37.6 34.336 57.6 49.696h0.064zM588.096 713.12c16.192 16.192 30.816 33.184 43.52 50.592l21.248 29.12c0.768 1.376 1.632 2.752 2.816 3.936 6.304 6.304 16.512 6.304 22.816 0 5.984-6.016 6.24-15.52 0.8-21.888l0.064-0.064-21.888-30.016c-13.696-18.688-29.376-36.928-46.752-54.304-18.080-18.080-37.568-34.336-57.568-49.696l-0.128 0.064c-6.368-5.856-16.256-5.728-22.368 0.448-6.304 6.304-6.304 16.576 0 22.88 1.12 1.184 2.432 2.016 3.744 2.752 18.816 14.368 36.96 29.44 53.696 46.176z" />
<glyph unicode="&#xe97a;" glyph-name="spinner" d="M384 832c0 70.692 57.308 128 128 128s128-57.308 128-128c0-70.692-57.308-128-128-128s-128 57.308-128 128zM655.53 719.53c0 70.692 57.308 128 128 128s128-57.308 128-128c0-70.692-57.308-128-128-128s-128 57.308-128 128zM832 448c0 35.346 28.654 64 64 64s64-28.654 64-64c0-35.346-28.654-64-64-64s-64 28.654-64 64zM719.53 176.47c0 35.346 28.654 64 64 64s64-28.654 64-64c0-35.346-28.654-64-64-64s-64 28.654-64 64zM448.002 64c0 0 0 0 0 0 0 35.346 28.654 64 64 64s64-28.654 64-64c0 0 0 0 0 0 0-35.346-28.654-64-64-64s-64 28.654-64 64zM176.472 176.47c0 0 0 0 0 0 0 35.346 28.654 64 64 64s64-28.654 64-64c0 0 0 0 0 0 0-35.346-28.654-64-64-64s-64 28.654-64 64zM144.472 719.53c0 0 0 0 0 0 0 53.019 42.981 96 96 96s96-42.981 96-96c0 0 0 0 0 0 0-53.019-42.981-96-96-96s-96 42.981-96 96zM56 448c0 39.765 32.235 72 72 72s72-32.235 72-72c0-39.765-32.235-72-72-72s-72 32.235-72 72z" />
<glyph unicode="&#xe986;" glyph-name="search" d="M992.262 88.604l-242.552 206.294c-25.074 22.566-51.89 32.926-73.552 31.926 57.256 67.068 91.842 154.078 91.842 249.176 0 212.078-171.922 384-384 384-212.076 0-384-171.922-384-384s171.922-384 384-384c95.098 0 182.108 34.586 249.176 91.844-1-21.662 9.36-48.478 31.926-73.552l206.294-242.552c35.322-39.246 93.022-42.554 128.22-7.356s31.892 92.898-7.354 128.22zM384 320c-141.384 0-256 114.616-256 256s114.616 256 256 256 256-114.616 256-256-114.614-256-256-256z" />
<glyph unicode="&#xe9d7;" glyph-name="star-empty" d="M1024 562.95l-353.78 51.408-158.22 320.582-158.216-320.582-353.784-51.408 256-249.538-60.432-352.352 316.432 166.358 316.432-166.358-60.434 352.352 256.002 249.538zM512 206.502l-223.462-117.48 42.676 248.83-180.786 176.222 249.84 36.304 111.732 226.396 111.736-226.396 249.836-36.304-180.788-176.222 42.678-248.83-223.462 117.48z" />
<glyph unicode="&#xe9d8;" glyph-name="star-half" d="M1024 562.95l-353.78 51.408-158.22 320.582-158.216-320.582-353.784-51.408 256-249.538-60.432-352.352 316.432 166.358 316.432-166.358-60.434 352.352 256.002 249.538zM512 206.502l-0.942-0.496 0.942 570.768 111.736-226.396 249.836-36.304-180.788-176.222 42.678-248.83-223.462 117.48z" />

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View file

@ -1,10 +1,10 @@
@font-face {
font-family: 'icomoon';
src: url('../fonts/icomoon.eot?wjd7rd');
src: url('../fonts/icomoon.eot?wjd7rd#iefix') format('embedded-opentype'),
url('../fonts/icomoon.ttf?wjd7rd') format('truetype'),
url('../fonts/icomoon.woff?wjd7rd') format('woff'),
url('../fonts/icomoon.svg?wjd7rd#icomoon') format('svg');
src: url('../fonts/icomoon.eot?36x4a3');
src: url('../fonts/icomoon.eot?36x4a3#iefix') format('embedded-opentype'),
url('../fonts/icomoon.ttf?36x4a3') format('truetype'),
url('../fonts/icomoon.woff?36x4a3') format('woff'),
url('../fonts/icomoon.svg?36x4a3#icomoon') format('svg');
font-weight: normal;
font-style: normal;
font-display: block;
@ -139,3 +139,6 @@
.icon-question-circle:before {
content: "\e900";
}
.icon-spinner:before {
content: "\e97a";
}

View file

@ -301,7 +301,10 @@ let BookWyrm = new class {
ajaxPost(form) {
return fetch(form.action, {
method : "POST",
body: new FormData(form)
body: new FormData(form),
headers: {
'Accept': 'application/json',
}
});
}

View file

@ -0,0 +1,202 @@
/* exported StatusCache */
/* globals BookWyrm */
let StatusCache = new class {
constructor() {
document.querySelectorAll('[data-cache-draft]')
.forEach(t => t.addEventListener('change', this.updateDraft.bind(this)));
document.querySelectorAll('[data-cache-draft]')
.forEach(t => this.populateDraft(t));
document.querySelectorAll('.submit-status')
.forEach(button => button.addEventListener(
'submit',
this.submitStatus.bind(this))
);
}
/**
* Update localStorage copy of drafted status
*
* @param {Event} event
* @return {undefined}
*/
updateDraft(event) {
// Used in set reading goal
let key = event.target.dataset.cacheDraft;
let value = event.target.value;
if (!value) {
window.localStorage.removeItem(key);
return;
}
window.localStorage.setItem(key, value);
}
/**
* Toggle display of a DOM node based on its value in the localStorage.
*
* @param {object} node - DOM node to toggle.
* @return {undefined}
*/
populateDraft(node) {
// Used in set reading goal
let key = node.dataset.cacheDraft;
let value = window.localStorage.getItem(key);
if (!value) {
return;
}
node.value = value;
}
/**
* Post a status with ajax
*
* @param {} event
* @return {undefined}
*/
submitStatus(event) {
event.preventDefault();
const form = event.currentTarget;
const trigger = event.submitter;
BookWyrm.addRemoveClass(form, 'is-processing', true);
trigger.setAttribute('disabled', null);
BookWyrm.ajaxPost(form).finally(() => {
// Change icon to remove ongoing activity on the current UI.
// Enable back the element used to submit the form.
BookWyrm.addRemoveClass(form, 'is-processing', false);
trigger.removeAttribute('disabled');
})
.then(response => {
if (!response.ok) {
throw new Error();
}
this.submitStatusSuccess(form);
})
.catch(error => {
console.warn(error);
this.announceMessage('status-error-message');
});
}
/**
* Show a message in the live region
*
* @param {String} the id of the message dom element
* @return {undefined}
*/
announceMessage(message_id) {
const element = document.getElementById(message_id);
let copy = element.cloneNode(true);
copy.id = null;
element.insertAdjacentElement('beforebegin', copy);
BookWyrm.addRemoveClass(copy, 'is-hidden', false);
setTimeout(function() {
copy.remove();
}, 10000, copy);
}
/**
* Success state for a posted status
*
* @param {Object} the html form that was submitted
* @return {undefined}
*/
submitStatusSuccess(form) {
// Clear form data
form.reset();
// Clear localstorage
form.querySelectorAll('[data-cache-draft]')
.forEach(node => window.localStorage.removeItem(node.dataset.cacheDraft));
// Close modals
let modal = form.closest(".modal.is-active");
if (modal) {
modal.getElementsByClassName("modal-close")[0].click();
// Update shelve buttons
document.querySelectorAll("[data-shelve-button-book='" + form.book.value +"']")
.forEach(button => this.cycleShelveButtons(button, form.reading_status.value));
return;
}
// Close reply panel
let reply = form.closest(".reply-panel");
if (reply) {
document.querySelector("[data-controls=" + reply.id + "]").click();
}
this.announceMessage('status-success-message');
}
/**
* Change which buttons are available for a shelf
*
* @param {Object} html button dom element
* @param {String} the identifier of the selected shelf
* @return {undefined}
*/
cycleShelveButtons(button, identifier) {
// Pressed button
let shelf = button.querySelector("[data-shelf-identifier='" + identifier + "']");
let next_identifier = shelf.dataset.shelfNext;
// Set all buttons to hidden
button.querySelectorAll("[data-shelf-identifier]")
.forEach(item => BookWyrm.addRemoveClass(item, "is-hidden", true));
// Button that should be visible now
let next = button.querySelector("[data-shelf-identifier=" + next_identifier + "]");
// Show the desired button
BookWyrm.addRemoveClass(next, "is-hidden", false);
// ------ update the dropdown buttons
// Remove existing hidden class
button.querySelectorAll("[data-shelf-dropdown-identifier]")
.forEach(item => BookWyrm.addRemoveClass(item, "is-hidden", false));
// Remove existing disabled states
button.querySelectorAll("[data-shelf-dropdown-identifier] button")
.forEach(item => item.disabled = false);
next_identifier = next_identifier == 'complete' ? 'read' : next_identifier;
// Disable the current state
button.querySelector(
"[data-shelf-dropdown-identifier=" + identifier + "] button"
).disabled = true;
let main_button = button.querySelector(
"[data-shelf-dropdown-identifier=" + next_identifier + "]"
);
// Hide the option that's shown as the main button
BookWyrm.addRemoveClass(main_button, "is-hidden", true);
// Just hide the other two menu options, idk what to do with them
button.querySelectorAll("[data-extra-options]")
.forEach(item => BookWyrm.addRemoveClass(item, "is-hidden", true));
// Close menu
let menu = button.querySelector(".dropdown-trigger[aria-expanded=true]");
if (menu) {
menu.click();
}
}
}();

View file

@ -210,6 +210,11 @@
</div>
</div>
<div role="region" aria-live="polite" id="live-messages">
<p id="status-success-message" class="live-message is-sr-only is-hidden">{% trans "Successfully posted status" %}</p>
<p id="status-error-message" class="live-message notification is-danger p-3 pr-5 pl-5 is-hidden">{% trans "Error posting status" %}</p>
</div>
<footer class="footer">
<div class="container">
<div class="columns">
@ -249,8 +254,11 @@
<script>
var csrf_token = '{{ csrf_token }}';
</script>
<script src="{% static "js/bookwyrm.js" %}"></script>
<script src="{% static "js/localstorage.js" %}"></script>
<script src="{% static "js/status_cache.js" %}"></script>
{% block scripts %}{% endblock %}
</body>
</html>

View file

@ -35,11 +35,16 @@ uuid: a unique identifier used to make html "id" attributes unique and clarify j
size="3"
value="{% firstof draft.progress readthrough.progress '' %}"
id="progress_{{ uuid }}"
data-cache-draft="id_progress_comment_{{ book.id }}"
>
</div>
<div class="control">
<div class="select">
<select name="progress_mode" aria-label="Progress mode">
<select
name="progress_mode"
aria-label="Progress mode"
data-cache-draft="id_progress_mode_comment_{{ book.id }}"
>
<option
value="PG"
{% if draft.progress_mode == 'PG' or readthrough.progress_mode == 'PG' %}selected{% endif %}

View file

@ -10,7 +10,8 @@ draft: an existing Status object that is providing default values for input fiel
{% endcomment %}
<textarea
name="content"
class="textarea"
class="textarea save-draft"
data-cache-draft="id_content_{{ type }}_{{ book.id }}{{ reply_parent.id }}"
id="id_content_{{ type }}_{{ book.id }}{{ reply_parent.id }}"
placeholder="{{ placeholder }}"
aria-label="{% if reply_parent %}{% trans 'Reply' %}{% else %}{% trans 'Content' %}{% endif %}"

View file

@ -8,5 +8,7 @@
class="input"
id="id_content_warning_{{ uuid }}"
placeholder="{% trans 'Spoilers ahead!' %}"
value="{% firstof draft.content_warning parent_status.content_warning '' %}">
value="{% firstof draft.content_warning parent_status.content_warning '' %}"
data-cache-draft="id_content_warning_{{ book.id }}_{{ type }}"
>
</div>

View file

@ -1,7 +1,15 @@
{% load i18n %}
<div class="control">
<input type="checkbox" class="is-hidden" name="sensitive" id="id_show_spoilers_{{ uuid }}" {% if draft.content_warning or status.content_warning %}checked{% endif %} aria-hidden="true">
<input
type="checkbox"
class="is-hidden"
name="sensitive"
id="id_show_spoilers_{{ uuid }}"
{% if draft.content_warning or status.content_warning %}checked{% endif %}
aria-hidden="true"
data-cache-draft="id_sensitive_{{ book.id }}_{{ type }}{{ reply_parent.id }}"
>
{% trans "Include spoiler alert" as button_text %}
{% firstof draft.content_warning status.content_warning as pressed %}
{% include 'snippets/toggle/toggle_button.html' with text=button_text icon="warning is-size-4" controls_text="spoilers" controls_uid=uuid focus="id_content_warning" checkbox="id_show_spoilers" class="toggle-button" pressed=pressed %}

View file

@ -14,7 +14,7 @@ reply_parent: the Status object this post will be in reply to, if applicable
{% block form_open %}
{# default form tag syntax, can be overriddden #}
<form class="is-flex-grow-1" name="{{ type }}" action="/post/{{ type }}" method="post" id="tab_{{ type }}_{{ book.id }}{{ reply_parent.id }}">
<form class="is-flex-grow-1 submit-status" name="{{ type }}" action="/post/{{ type }}" method="post" id="tab_{{ type }}_{{ book.id }}{{ reply_parent.id }}">
{% endblock %}
{% csrf_token %}

View file

@ -16,7 +16,10 @@
</div>
</div>
<div class="column is-narrow">
<button class="button is-link" type="submit">{% trans "Post" %}</button>
<button class="button is-link" type="submit">
<span class="icon icon-spinner" aria-hidden="true"></span>
<span>{% trans "Post" %}</span>
</button>
</div>
</div>

View file

@ -24,6 +24,7 @@ uuid: a unique identifier used to make html "id" attributes unique and clarify j
id="id_quote_{{ book.id }}_{{ type }}"
placeholder="{% blocktrans with book_title=book.title %}An excerpt from '{{ book_title }}'{% endblocktrans %}"
required
data-cache-draft="id_quote_{{ book.id }}_{{ type }}"
>{{ draft.quote|default:'' }}</textarea>
</div>
</div>
@ -32,7 +33,11 @@ uuid: a unique identifier used to make html "id" attributes unique and clarify j
<div class="field has-addons mb-0">
<div class="control">
<div class="select">
<select name="position_mode" aria-label="Position mode">
<select
name="position_mode"
aria-label="Position mode"
data-cache-draft="id_position_mode_{{ book.id }}_{{ type }}"
>
<option
value="PG"
{% if draft.position_mode == 'PG' or not draft %}selected{% endif %}
@ -58,6 +63,7 @@ uuid: a unique identifier used to make html "id" attributes unique and clarify j
size="3"
value="{% firstof draft.position '' %}"
id="position_{{ uuid }}"
data-cache-draft="id_position_{{ book.id }}_{{ type }}"
>
</div>
</div>

View file

@ -15,7 +15,17 @@ uuid: a unique identifier used to make html "id" attributes unique and clarify j
<div class="field">
<label class="label" for="id_name_{{ book.id }}">{% trans "Title:" %}</label>
<div class="control">
<input type="text" name="name" maxlength="255" class="input" required="" id="id_name_{{ book.id }}" placeholder="{% blocktrans with book_title=book.title %}Your review of '{{ book_title }}'{% endblocktrans %}" value="{% firstof draft.name ''%}">
<input
type="text"
name="name"
maxlength="255"
class="input"
required=""
id="id_name_{{ book.id }}"
placeholder="{% blocktrans with book_title=book.title %}Your review of '{{ book_title }}'{% endblocktrans %}"
value="{% firstof draft.name ''%}"
data-cache-draft="id_name_{{ book.id }}_{{ type }}"
>
</div>
</div>

View file

@ -9,7 +9,7 @@ Finish "<em>{{ book_title }}</em>"
{% endblock %}
{% block modal-form-open %}
<form name="finish-reading" action="{% url 'reading-status' 'finish' book.id %}" method="post">
<form name="finish-reading" action="{% url 'reading-status' 'finish' book.id %}" method="post" class="submit-status">
{% csrf_token %}
<input type="hidden" name="reading_status" value="read">
{% endblock %}

View file

@ -6,7 +6,7 @@
{% endblock %}
{% block modal-form-open %}
<form action="{% url 'edit-readthrough' %}" method="POST">
<form action="{% url 'edit-readthrough' %}" method="POST" class="submit-status">
{% endblock %}
{% block modal-body %}

View file

@ -9,7 +9,7 @@ Start "<em>{{ book_title }}</em>"
{% endblock %}
{% block modal-form-open %}
<form name="start-reading" action="{% url 'reading-status' 'start' book.id %}" method="post">
<form name="start-reading" action="{% url 'reading-status' 'start' book.id %}" method="post" class="submit-status">
<input type="hidden" name="reading_status" value="reading">
{% csrf_token %}
{% endblock %}

View file

@ -9,7 +9,7 @@ Want to Read "<em>{{ book_title }}</em>"
{% endblock %}
{% block modal-form-open %}
<form name="shelve" action="{% url 'reading-status' 'want' book.id %}" method="post">
<form name="shelve" action="{% url 'reading-status' 'want' book.id %}" method="post" class="submit-status">
<input type="hidden" name="reading_status" value="to-read">
{% csrf_token %}
{% endblock %}

View file

@ -6,7 +6,7 @@
{% with book.id|uuid as uuid %}
{% active_shelf book as active_shelf %}
{% latest_read_through book request.user as readthrough %}
<div class="field has-addons mb-0">
<div class="field has-addons mb-0" data-shelve-button-book="{{ book.id }}">
{% if switch_mode and active_shelf.book != book %}
<div class="control">
{% include 'snippets/switch_edition_button.html' with edition=book size='is-small' %}

View file

@ -7,5 +7,5 @@
{% endblock %}
{% block dropdown-list %}
{% include 'snippets/shelve_button/shelve_button_options.html' with active_shelf=active_shelf shelves=user_shelves dropdown=True class="shelf-option is-fullwidth is-small is-radiusless is-white" %}
{% include 'snippets/shelve_button/shelve_button_dropdown_options.html' with active_shelf=active_shelf shelves=user_shelves dropdown=True class="shelf-option is-fullwidth is-small is-radiusless is-white" %}
{% endblock %}

View file

@ -0,0 +1,69 @@
{% load bookwyrm_tags %}
{% load utilities %}
{% load i18n %}
{% with next_shelf_identifier=active_shelf.shelf.identifier|next_shelf %}
{% for shelf in shelves %}
{% comparison_bool shelf.identifier active_shelf.shelf.identifier as is_current %}
<li role="menuitem" class="dropdown-item p-0">
<div
class="{% if next_shelf_identifier == shelf.identifier %}is-hidden{% endif %}"
data-shelf-dropdown-identifier="{{ shelf.identifier }}"
data-shelf-next="{{ shelf.identifier|next_shelf }}"
>
{% if shelf.identifier == 'reading' %}
{% trans "Start reading" as button_text %}
{% url 'reading-status' 'start' book.id as fallback_url %}
{% include 'snippets/toggle/toggle_button.html' with class=class text=button_text controls_text="start_reading" controls_uid=button_uuid focus="modal_title_start_reading" disabled=is_current fallback_url=fallback_url %}
{% elif shelf.identifier == 'read' %}
{% trans "Read" as button_text %}
{% url 'reading-status' 'finish' book.id as fallback_url %}
{% include 'snippets/toggle/toggle_button.html' with class=class text=button_text controls_text="finish_reading" controls_uid=button_uuid focus="modal_title_finish_reading" disabled=is_current fallback_url=fallback_url %}
{% elif shelf.identifier == 'to-read' %}
{% trans "Want to read" as button_text %}
{% url 'reading-status' 'want' book.id as fallback_url %}
{% include 'snippets/toggle/toggle_button.html' with class=class text=button_text controls_text="want_to_read" controls_uid=button_uuid focus="modal_title_want_to_read" disabled=is_current fallback_url=fallback_url %}
{% elif shelf.editable %}
<form name="shelve" action="/shelve/" method="post">
{% csrf_token %}
<input type="hidden" name="book" value="{{ active_shelf.book.id }}">
<button class="button {{ class }}" name="shelf" type="submit" value="{{ shelf.identifier }}" {% if shelf in book.shelf_set.all %} disabled {% endif %}>
<span>{{ shelf.name }}</span>
</button>
</form>
{% endif %}
</div>
</li>
{% endfor %}
{% if readthrough and active_shelf.shelf.identifier != 'read' %}
<li role="menuitem" class="dropdown-item p-0" data-extra-options>
{% trans "Update progress" as button_text %}
{% include 'snippets/toggle/toggle_button.html' with class=class text=button_text controls_text="progress_update" controls_uid=button_uuid focus="modal_title_progress_update" %}
</li>
{% endif %}
{% if active_shelf.shelf %}
<li role="menuitem" class="dropdown-item p-0" data-extra-options>
<form name="shelve" action="/unshelve/" method="post">
{% csrf_token %}
<input type="hidden" name="book" value="{{ active_shelf.book.id }}">
<input type="hidden" name="shelf" value="{{ active_shelf.shelf.id }}">
<button class="button is-fullwidth is-small{% if dropdown %} is-radiusless{% endif %} is-danger is-light" type="submit">
{% blocktrans with name=active_shelf.shelf.name %}Remove from {{ name }}{% endblocktrans %}
</button>
</form>
</li>
{% endif %}
{% endwith %}

View file

@ -2,33 +2,43 @@
{% load utilities %}
{% load i18n %}
{% for shelf in shelves %}
{% comparison_bool shelf.identifier active_shelf.shelf.identifier as is_current %}
{% if dropdown %}<li role="menuitem" class="dropdown-item p-0">{% endif %}
<div class="{% if not dropdown and active_shelf.shelf.identifier|next_shelf != shelf.identifier %}is-hidden{% endif %}">
{% if shelf.identifier == 'reading' %}{% if not dropdown or active_shelf.shelf.identifier|next_shelf != shelf.identifier %}
{% with next_shelf_identifier=active_shelf.shelf.identifier|next_shelf %}
{% trans "Start reading" as button_text %}
{% url 'reading-status' 'start' book.id as fallback_url %}
{% include 'snippets/toggle/toggle_button.html' with class=class text=button_text controls_text="start_reading" controls_uid=button_uuid focus="modal_title_start_reading" disabled=is_current fallback_url=fallback_url %}
{% endif %}{% elif shelf.identifier == 'read' and active_shelf.shelf.identifier == 'read' %}{% if not dropdown or active_shelf.shelf.identifier|next_shelf != shelf.identifier %}
<div
class="{% if next_shelf_identifier != 'complete' %}is-hidden{% endif %}"
data-shelf-identifier="complete"
>
<button type="button" class="button {{ class }}" disabled>
<span>{% trans "Read" %}</span>
</button>
{% endif %}{% elif shelf.identifier == 'read' %}{% if not dropdown or active_shelf.shelf.identifier|next_shelf != shelf.identifier %}
</div>
{% trans "Finish reading" as button_text %}
{% url 'reading-status' 'finish' book.id as fallback_url %}
{% include 'snippets/toggle/toggle_button.html' with class=class text=button_text controls_text="finish_reading" controls_uid=button_uuid focus="modal_title_finish_reading" disabled=is_current fallback_url=fallback_url %}
{% for shelf in shelves %}
<div
class="{% if next_shelf_identifier != shelf.identifier %}is-hidden{% endif %}"
data-shelf-identifier="{{ shelf.identifier }}"
data-shelf-next="{{ shelf.identifier|next_shelf }}"
>
{% if shelf.identifier == 'reading' %}
{% endif %}{% elif shelf.identifier == 'to-read' %}{% if not dropdown or active_shelf.shelf.identifier|next_shelf != shelf.identifier %}
{% trans "Start reading" as button_text %}
{% url 'reading-status' 'start' book.id as fallback_url %}
{% include 'snippets/toggle/toggle_button.html' with class=class text=button_text controls_text="start_reading" controls_uid=button_uuid focus="modal_title_start_reading" fallback_url=fallback_url %}
{% trans "Want to read" as button_text %}
{% url 'reading-status' 'want' book.id as fallback_url %}
{% include 'snippets/toggle/toggle_button.html' with class=class text=button_text controls_text="want_to_read" controls_uid=button_uuid focus="modal_title_want_to_read" disabled=is_current fallback_url=fallback_url %}
{% elif shelf.identifier == 'read' %}
{% trans "Finish reading" as button_text %}
{% url 'reading-status' 'finish' book.id as fallback_url %}
{% include 'snippets/toggle/toggle_button.html' with class=class text=button_text controls_text="finish_reading" controls_uid=button_uuid focus="modal_title_finish_reading" fallback_url=fallback_url %}
{% elif shelf.identifier == 'to-read' %}
{% trans "Want to read" as button_text %}
{% url 'reading-status' 'want' book.id as fallback_url %}
{% include 'snippets/toggle/toggle_button.html' with class=class text=button_text controls_text="want_to_read" controls_uid=button_uuid focus="modal_title_want_to_read" fallback_url=fallback_url %}
{% elif shelf.editable %}
{% endif %}{% elif shelf.editable %}
<form name="shelve" action="/shelve/" method="post">
{% csrf_token %}
<input type="hidden" name="book" value="{{ active_shelf.book.id }}">
@ -36,30 +46,9 @@
<span>{{ shelf.name }}</span>
</button>
</form>
{% endif %}
</div>
{% if dropdown %}</li>{% endif %}
{% endfor %}
{% if dropdown %}
{% if readthrough and active_shelf.shelf.identifier != 'read' %}
<li role="menuitem" class="dropdown-item p-0">
{% trans "Update progress" as button_text %}
{% include 'snippets/toggle/toggle_button.html' with class=class text=button_text controls_text="progress_update" controls_uid=button_uuid focus="modal_title_progress_update" %}
</li>
{% endif %}
{% if active_shelf.shelf %}
<li role="menuitem" class="dropdown-item p-0">
<form name="shelve" action="/unshelve/" method="post">
{% csrf_token %}
<input type="hidden" name="book" value="{{ active_shelf.book.id }}">
<input type="hidden" name="shelf" value="{{ active_shelf.shelf.id }}">
<button class="button is-fullwidth is-small{% if dropdown %} is-radiusless{% endif %} is-danger is-light" type="submit">
{% blocktrans with name=active_shelf.shelf.name %}Remove from {{ name }}{% endblocktrans %}
</button>
</form>
</li>
{% endif %}
{% endif %}
{% endwith %}

View file

@ -66,7 +66,7 @@
{% block card-bonus %}
{% if request.user.is_authenticated and not moderation_mode %}
{% with status.id|uuid as uuid %}
<section class="is-hidden" id="show_comment_{{ status.id }}">
<section class="reply-panel is-hidden" id="show_comment_{{ status.id }}">
<div class="card-footer">
<div class="card-footer-item">
{% include 'snippets/create_status/status.html' with type="reply" reply_parent=status book=None %}

View file

@ -49,7 +49,7 @@ def get_next_shelf(current_shelf):
if current_shelf == "reading":
return "read"
if current_shelf == "read":
return "read"
return "complete"
return "to-read"

View file

@ -37,7 +37,7 @@ def get_title(book, too_short=5):
@register.simple_tag(takes_context=False)
def comparison_bool(str1, str2):
"""idk why I need to write a tag for this, it reutrns a bool"""
"""idk why I need to write a tag for this, it returns a bool"""
return str1 == str2

View file

@ -179,5 +179,5 @@ class TemplateTags(TestCase):
"""self progress helper"""
self.assertEqual(bookwyrm_tags.get_next_shelf("to-read"), "reading")
self.assertEqual(bookwyrm_tags.get_next_shelf("reading"), "read")
self.assertEqual(bookwyrm_tags.get_next_shelf("read"), "read")
self.assertEqual(bookwyrm_tags.get_next_shelf("read"), "complete")
self.assertEqual(bookwyrm_tags.get_next_shelf("blooooga"), "to-read")

View file

@ -1,12 +1,13 @@
""" boosts and favs """
from django.db import IntegrityError
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseBadRequest, HttpResponseNotFound
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseNotFound
from django.shortcuts import redirect
from django.utils.decorators import method_decorator
from django.views import View
from bookwyrm import models
from .helpers import is_api_request
# pylint: disable= no-self-use
@ -23,6 +24,8 @@ class Favorite(View):
# you already fav'ed that
return HttpResponseBadRequest()
if is_api_request(request):
return HttpResponse()
return redirect(request.headers.get("Referer", "/"))
@ -40,6 +43,8 @@ class Unfavorite(View):
return HttpResponseNotFound()
favorite.delete()
if is_api_request(request):
return HttpResponse()
return redirect(request.headers.get("Referer", "/"))
@ -65,6 +70,8 @@ class Boost(View):
privacy=status.privacy,
user=request.user,
)
if is_api_request(request):
return HttpResponse()
return redirect(request.headers.get("Referer", "/"))
@ -80,4 +87,6 @@ class Unboost(View):
).first()
boost.delete()
if is_api_request(request):
return HttpResponse()
return redirect(request.headers.get("Referer", "/"))

View file

@ -5,7 +5,7 @@ import dateutil.tz
from dateutil.parser import ParserError
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseBadRequest, HttpResponseNotFound
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseNotFound
from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator
@ -13,7 +13,7 @@ from django.views import View
from django.views.decorators.http import require_POST
from bookwyrm import forms, models
from .helpers import get_edition, handle_reading_status
from .helpers import get_edition, handle_reading_status, is_api_request
@method_decorator(login_required, name="dispatch")
@ -61,8 +61,7 @@ class ReadingStatus(View):
)
referer = request.headers.get("Referer", "/")
if "reading-status" in referer:
referer = "/"
referer = "/" if "reading-status" in referer else referer
if current_status_shelfbook is not None:
if current_status_shelfbook.shelf.identifier != desired_shelf.identifier:
current_status_shelfbook.delete()
@ -92,6 +91,9 @@ class ReadingStatus(View):
else:
privacy = request.POST.get("privacy")
handle_reading_status(request.user, desired_shelf, book, privacy)
if is_api_request(request):
return HttpResponse()
return redirect(referer)

View file

@ -5,7 +5,7 @@ from urllib.parse import urlparse
from django.contrib.auth.decorators import login_required
from django.core.validators import URLValidator
from django.core.exceptions import ValidationError
from django.http import HttpResponseBadRequest
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator
@ -16,7 +16,7 @@ from bookwyrm import forms, models
from bookwyrm.sanitize_html import InputHtmlParser
from bookwyrm.settings import DOMAIN
from bookwyrm.utils import regex
from .helpers import handle_remote_webfinger
from .helpers import handle_remote_webfinger, is_api_request
from .reading import edit_readthrough
@ -26,7 +26,7 @@ class CreateStatus(View):
"""the view for *posting*"""
def get(self, request, status_type): # pylint: disable=unused-argument
"""compose view (used for delete-and-redraft"""
"""compose view (used for delete-and-redraft)"""
book = get_object_or_404(models.Edition, id=request.GET.get("book"))
data = {"book": book}
return TemplateResponse(request, "compose.html", data)
@ -40,6 +40,8 @@ class CreateStatus(View):
except AttributeError:
return HttpResponseBadRequest()
if not form.is_valid():
if is_api_request(request):
return HttpResponse(status=500)
return redirect(request.headers.get("Referer", "/"))
status = form.save(commit=False)
@ -79,6 +81,8 @@ class CreateStatus(View):
# update a readthorugh, if needed
edit_readthrough(request)
if is_api_request(request):
return HttpResponse()
return redirect("/")