mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-10-31 22:19:00 +00:00
Merge branch 'main' into production
This commit is contained in:
commit
e4001aba0b
11 changed files with 62 additions and 20 deletions
|
@ -66,6 +66,7 @@ You'll have to install the Docker and docker-compose. When you're ready, run:
|
||||||
docker-compose build
|
docker-compose build
|
||||||
docker-compose run --rm web python manage.py migrate
|
docker-compose run --rm web python manage.py migrate
|
||||||
docker-compose run --rm web python manage.py initdb
|
docker-compose run --rm web python manage.py initdb
|
||||||
|
docker-compose up
|
||||||
```
|
```
|
||||||
|
|
||||||
Once the build is complete, you can access the instance at `localhost:1333`
|
Once the build is complete, you can access the instance at `localhost:1333`
|
||||||
|
|
|
@ -31,8 +31,32 @@ window.onload = function() {
|
||||||
// hidden submit button in a form
|
// hidden submit button in a form
|
||||||
document.querySelectorAll('.hidden-form input')
|
document.querySelectorAll('.hidden-form input')
|
||||||
.forEach(t => t.onchange = revealForm);
|
.forEach(t => t.onchange = revealForm);
|
||||||
|
|
||||||
|
// polling
|
||||||
|
document.querySelectorAll('[data-poll]')
|
||||||
|
.forEach(el => polling(el));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function polling(el) {
|
||||||
|
let delay = 10000 + (Math.random() * 1000);
|
||||||
|
setTimeout(function() {
|
||||||
|
fetch('/api/updates/' + el.getAttribute('data-poll'))
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => updateCountElement(el, data));
|
||||||
|
polling(el);
|
||||||
|
}, delay, el);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCountElement(el, data) {
|
||||||
|
const currentCount = el.innerText;
|
||||||
|
const count = data[el.getAttribute('data-poll')];
|
||||||
|
if (count != currentCount) {
|
||||||
|
addRemoveClass(el, 'hidden', count < 1);
|
||||||
|
el.innerText = count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function revealForm(e) {
|
function revealForm(e) {
|
||||||
var hidden = e.currentTarget.closest('.hidden-form').getElementsByClassName('hidden')[0];
|
var hidden = e.currentTarget.closest('.hidden-form').getElementsByClassName('hidden')[0];
|
||||||
if (hidden) {
|
if (hidden) {
|
||||||
|
@ -88,7 +112,9 @@ function toggleAction(e) {
|
||||||
// set focus, if appropriate
|
// set focus, if appropriate
|
||||||
var focus = el.getAttribute('data-focus-target');
|
var focus = el.getAttribute('data-focus-target');
|
||||||
if (focus) {
|
if (focus) {
|
||||||
document.getElementById(focus).focus();
|
var focusEl = document.getElementById(focus);
|
||||||
|
focusEl.focus();
|
||||||
|
setTimeout(function(){ focusEl.selectionStart = focusEl.selectionEnd = 10000; }, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,17 +106,15 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-item">
|
<div class="navbar-item">
|
||||||
<a href="/notifications">
|
<a href="/notifications" class="tags has-addons">
|
||||||
<div class="tags has-addons">
|
<span class="tag is-medium">
|
||||||
<span class="tag is-medium">
|
<span class="icon icon-bell" title="Notifications">
|
||||||
<span class="icon icon-bell" title="Notifications">
|
<span class="is-sr-only">Notifications</span>
|
||||||
<span class="is-sr-only">Notifications</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
{% if request.user|notification_count %}
|
</span>
|
||||||
<span class="tag is-danger is-medium">{{ request.user | notification_count }}</span>
|
<span class="{% if not request.user|notification_count %}hidden {% endif %}tag is-danger is-medium" data-poll="notifications">
|
||||||
{% endif %}
|
{{ request.user | notification_count }}
|
||||||
</div>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{% load bookwyrm_tags %}
|
||||||
<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" name="{{ type }}" action="/post/{{ type }}" method="post" id="tab-{{ type }}-{{ book.id }}{{ reply_parent.id }}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="book" value="{{ book.id }}">
|
<input type="hidden" name="book" value="{{ book.id }}">
|
||||||
|
@ -34,7 +35,7 @@
|
||||||
<textarea name="quote" class="textarea" id="id_quote_{{ book.id }}_{{ type }}" placeholder="{{ placeholder }}" required></textarea>
|
<textarea name="quote" class="textarea" id="id_quote_{{ book.id }}_{{ type }}" placeholder="{{ placeholder }}" required></textarea>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% include 'snippets/content_warning_field.html' with parent_status=status %}
|
{% include 'snippets/content_warning_field.html' with parent_status=status %}
|
||||||
<textarea name="content" class="textarea" id="id_content_{{ type }}-{{ book.id }}{{reply_parent.id}}" placeholder="{{ placeholder }}" {% if type == 'reply' %} aria-label="Reply"{% endif %} required></textarea>
|
<textarea name="content" class="textarea" id="id_content_{{ type }}-{{ book.id }}{{reply_parent.id}}" placeholder="{{ placeholder }}" {% if type == 'reply' %} aria-label="Reply"{% endif %} required>{% if reply_parent %}{{ reply_parent|mentions:request.user }}{% endif %}</textarea>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if type == 'quotation' %}
|
{% if type == 'quotation' %}
|
||||||
|
|
|
@ -142,10 +142,10 @@ def get_markdown(content):
|
||||||
|
|
||||||
@register.filter(name='mentions')
|
@register.filter(name='mentions')
|
||||||
def get_mentions(status, user):
|
def get_mentions(status, user):
|
||||||
''' anyone tagged or replied to in this status '''
|
''' people to @ in a reply: the parent and all mentions '''
|
||||||
mentions = set([status.user] + list(status.mention_users.all()))
|
mentions = set([status.user] + list(status.mention_users.all()))
|
||||||
return ' '.join(
|
return ' '.join(
|
||||||
'@' + get_user_identifier(m) for m in mentions if not m == user)
|
'@' + get_user_identifier(m) for m in mentions if not m == user) + ' '
|
||||||
|
|
||||||
@register.filter(name='status_preview_name')
|
@register.filter(name='status_preview_name')
|
||||||
def get_status_preview_name(obj):
|
def get_status_preview_name(obj):
|
||||||
|
|
|
@ -228,7 +228,7 @@ class TemplateTags(TestCase):
|
||||||
status = models.Status.objects.create(
|
status = models.Status.objects.create(
|
||||||
content='hi', user=self.remote_user)
|
content='hi', user=self.remote_user)
|
||||||
result = bookwyrm_tags.get_mentions(status, self.user)
|
result = bookwyrm_tags.get_mentions(status, self.user)
|
||||||
self.assertEqual(result, '@rat@example.com')
|
self.assertEqual(result, '@rat@example.com ')
|
||||||
|
|
||||||
|
|
||||||
def test_get_status_preview_name(self):
|
def test_get_status_preview_name(self):
|
||||||
|
|
|
@ -31,14 +31,15 @@ urlpatterns = [
|
||||||
re_path(r'^inbox/?$', incoming.shared_inbox),
|
re_path(r'^inbox/?$', incoming.shared_inbox),
|
||||||
re_path(r'%s/inbox/?$' % local_user_path, incoming.inbox),
|
re_path(r'%s/inbox/?$' % local_user_path, incoming.inbox),
|
||||||
re_path(r'%s/outbox/?$' % local_user_path, views.Outbox.as_view()),
|
re_path(r'%s/outbox/?$' % local_user_path, views.Outbox.as_view()),
|
||||||
|
|
||||||
# .well-known endpoints
|
|
||||||
re_path(r'^.well-known/webfinger/?$', wellknown.webfinger),
|
re_path(r'^.well-known/webfinger/?$', wellknown.webfinger),
|
||||||
re_path(r'^.well-known/nodeinfo/?$', wellknown.nodeinfo_pointer),
|
re_path(r'^.well-known/nodeinfo/?$', wellknown.nodeinfo_pointer),
|
||||||
re_path(r'^nodeinfo/2\.0/?$', wellknown.nodeinfo),
|
re_path(r'^nodeinfo/2\.0/?$', wellknown.nodeinfo),
|
||||||
re_path(r'^api/v1/instance/?$', wellknown.instance_info),
|
re_path(r'^api/v1/instance/?$', wellknown.instance_info),
|
||||||
re_path(r'^api/v1/instance/peers/?$', wellknown.peers),
|
re_path(r'^api/v1/instance/peers/?$', wellknown.peers),
|
||||||
|
|
||||||
|
# polling updates
|
||||||
|
re_path('^api/updates/notifications/?$', views.Updates.as_view()),
|
||||||
|
|
||||||
# authentication
|
# authentication
|
||||||
re_path(r'^login/?$', views.Login.as_view()),
|
re_path(r'^login/?$', views.Login.as_view()),
|
||||||
re_path(r'^register/?$', views.Register.as_view()),
|
re_path(r'^register/?$', views.Register.as_view()),
|
||||||
|
|
|
@ -23,4 +23,5 @@ from .shelf import Shelf
|
||||||
from .shelf import user_shelves_page, create_shelf, delete_shelf
|
from .shelf import user_shelves_page, create_shelf, delete_shelf
|
||||||
from .shelf import shelve, unshelve
|
from .shelf import shelve, unshelve
|
||||||
from .status import Status, Replies, CreateStatus, DeleteStatus
|
from .status import Status, Replies, CreateStatus, DeleteStatus
|
||||||
|
from .updates import Updates
|
||||||
from .user import User, EditUser, Followers, Following
|
from .user import User, EditUser, Followers, Following
|
||||||
|
|
|
@ -13,7 +13,6 @@ from .helpers import get_activity_feed
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable= no-self-use
|
# pylint: disable= no-self-use
|
||||||
@method_decorator(login_required, name='dispatch')
|
|
||||||
class About(View):
|
class About(View):
|
||||||
''' create invites '''
|
''' create invites '''
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
|
|
|
@ -86,8 +86,6 @@ class CreateStatus(View):
|
||||||
# add reply parent to mentions and notify
|
# add reply parent to mentions and notify
|
||||||
if status.reply_parent:
|
if status.reply_parent:
|
||||||
status.mention_users.add(status.reply_parent.user)
|
status.mention_users.add(status.reply_parent.user)
|
||||||
for mention_user in status.reply_parent.mention_users.all():
|
|
||||||
status.mention_users.add(mention_user)
|
|
||||||
|
|
||||||
if status.reply_parent.user.local:
|
if status.reply_parent.user.local:
|
||||||
create_notification(
|
create_notification(
|
||||||
|
|
17
bookwyrm/views/updates.py
Normal file
17
bookwyrm/views/updates.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
''' endpoints for getting updates about activity '''
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.http import JsonResponse
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.views import View
|
||||||
|
|
||||||
|
# pylint: disable= no-self-use
|
||||||
|
@method_decorator(login_required, name='dispatch')
|
||||||
|
class Updates(View):
|
||||||
|
''' so the app can poll '''
|
||||||
|
def get(self, request):
|
||||||
|
''' any notifications waiting? '''
|
||||||
|
return JsonResponse({
|
||||||
|
'notifications': request.user.notification_set.filter(
|
||||||
|
read=False
|
||||||
|
).count(),
|
||||||
|
})
|
Loading…
Reference in a new issue