forked from mirrors/bookwyrm
commit
eb04600dc5
12 changed files with 140 additions and 3 deletions
|
@ -10,4 +10,5 @@ from .status import get_review, get_review_article
|
|||
from .status import get_comment, get_comment_article
|
||||
from .status import get_status, get_replies, get_replies_page
|
||||
from .status import get_favorite, get_unfavorite
|
||||
from .status import get_boost
|
||||
from .status import get_add_tag, get_remove_tag
|
||||
|
|
|
@ -158,6 +158,17 @@ def get_unfavorite(favorite):
|
|||
}
|
||||
|
||||
|
||||
def get_boost(boost):
|
||||
''' boost/announce a post '''
|
||||
return {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'id': boost.absolute_id,
|
||||
'type': 'Announce',
|
||||
'actor': boost.user.actor,
|
||||
'object': boost.boosted_status.absolute_id,
|
||||
}
|
||||
|
||||
|
||||
def get_add_tag(tag):
|
||||
''' add activity for tagging a book '''
|
||||
uuid = uuid4()
|
||||
|
|
|
@ -38,6 +38,7 @@ def shared_inbox(request):
|
|||
'Reject': handle_follow_reject,
|
||||
'Create': handle_create,
|
||||
'Like': handle_favorite,
|
||||
'Announce': handle_boost,
|
||||
'Add': {
|
||||
'Tag': handle_add,
|
||||
},
|
||||
|
@ -286,6 +287,27 @@ def handle_unfavorite(activity):
|
|||
return HttpResponse()
|
||||
|
||||
|
||||
def handle_boost(activity):
|
||||
''' someone gave us a boost! '''
|
||||
try:
|
||||
status_id = activity['object'].split('/')[-1]
|
||||
status = models.Status.objects.get(id=status_id)
|
||||
booster = get_or_create_remote_user(activity['actor'])
|
||||
except (models.Status.DoesNotExist, models.User.DoesNotExist):
|
||||
return HttpResponseNotFound()
|
||||
|
||||
if not booster.local:
|
||||
status_builder.create_boost_from_activity(booster, activity)
|
||||
|
||||
status_builder.create_notification(
|
||||
status.user,
|
||||
'BOOST',
|
||||
related_user=booster,
|
||||
related_status=status,
|
||||
)
|
||||
|
||||
return HttpResponse()
|
||||
|
||||
def handle_add(activity):
|
||||
''' someone is tagging or shelving a book '''
|
||||
if activity['object']['type'] == 'Tag':
|
||||
|
|
42
fedireads/migrations/0026_auto_20200330_1456.py
Normal file
42
fedireads/migrations/0026_auto_20200330_1456.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
# Generated by Django 3.0.3 on 2020-03-30 14:56
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('fedireads', '0025_auto_20200330_0037'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Boost',
|
||||
fields=[
|
||||
('status_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='fedireads.Status')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=('fedireads.status',),
|
||||
),
|
||||
migrations.RemoveConstraint(
|
||||
model_name='notification',
|
||||
name='notification_type_valid',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='notification',
|
||||
name='notification_type',
|
||||
field=models.CharField(choices=[('FAVORITE', 'Favorite'), ('REPLY', 'Reply'), ('TAG', 'Tag'), ('FOLLOW', 'Follow'), ('FOLLOW_REQUEST', 'Follow Request'), ('BOOST', 'Boost')], max_length=255),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='notification',
|
||||
constraint=models.CheckConstraint(check=models.Q(notification_type__in=['FAVORITE', 'REPLY', 'TAG', 'FOLLOW', 'FOLLOW_REQUEST', 'BOOST']), name='notification_type_valid'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='boost',
|
||||
name='boosted_status',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='boosters', to='fedireads.Status'),
|
||||
),
|
||||
]
|
|
@ -1,6 +1,6 @@
|
|||
''' bring all the models into the app namespace '''
|
||||
from .book import Connector, Book, Work, Edition, Author
|
||||
from .shelf import Shelf, ShelfBook
|
||||
from .status import Status, Review, Comment, Favorite, Tag, Notification
|
||||
from .status import Status, Review, Comment, Favorite, Boost, Tag, Notification
|
||||
from .user import User, UserFollows, UserFollowRequest, UserBlocks
|
||||
from .user import FederatedServer
|
||||
|
|
|
@ -89,6 +89,21 @@ class Favorite(FedireadsModel):
|
|||
unique_together = ('user', 'status')
|
||||
|
||||
|
||||
class Boost(Status):
|
||||
''' boost'ing a post '''
|
||||
boosted_status = models.ForeignKey(
|
||||
'Status',
|
||||
on_delete=models.PROTECT,
|
||||
related_name="boosters")
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.status_type = 'Boost'
|
||||
self.activity_type = 'Announce'
|
||||
super().save(*args, **kwargs)
|
||||
# This constraint can't work as it would cross tables.
|
||||
# class Meta:
|
||||
# unique_together = ('user', 'boosted_status')
|
||||
|
||||
class Tag(FedireadsModel):
|
||||
''' freeform tags for books '''
|
||||
user = models.ForeignKey('User', on_delete=models.PROTECT)
|
||||
|
@ -107,7 +122,7 @@ class Tag(FedireadsModel):
|
|||
|
||||
|
||||
NotificationType = models.TextChoices(
|
||||
'NotificationType', 'FAVORITE REPLY TAG FOLLOW FOLLOW_REQUEST')
|
||||
'NotificationType', 'FAVORITE REPLY TAG FOLLOW FOLLOW_REQUEST BOOST')
|
||||
|
||||
class Notification(FedireadsModel):
|
||||
''' you've been tagged, liked, followed, etc '''
|
||||
|
|
|
@ -291,6 +291,21 @@ def handle_unfavorite(user, status):
|
|||
recipients = get_recipients(user, 'direct', [status.user])
|
||||
broadcast(user, fav_activity, recipients)
|
||||
|
||||
def handle_boost(user, status):
|
||||
''' a user wishes to boost a status '''
|
||||
if models.Boost.objects.filter(
|
||||
boosted_status=status, user=user).exists():
|
||||
# you already boosted that.
|
||||
return
|
||||
boost = models.Boost.objects.create(
|
||||
boosted_status=status,
|
||||
user=user,
|
||||
)
|
||||
boost.save()
|
||||
|
||||
boost_activity = activitypub.get_boost(boost)
|
||||
recipients = get_recipients(user, 'public')
|
||||
broadcast(user, boost_activity, recipients)
|
||||
|
||||
def handle_update_book(user, book):
|
||||
''' broadcast the news about our book '''
|
||||
|
|
|
@ -102,6 +102,20 @@ def create_favorite_from_activity(user, activity):
|
|||
return models.Favorite.objects.get(status=status, user=user)
|
||||
|
||||
|
||||
def create_boost_from_activity(user, activity):
|
||||
''' create a new boost activity '''
|
||||
status = get_status(activity['object'])
|
||||
remote_id = activity['id']
|
||||
try:
|
||||
return models.Boost.objects.create(
|
||||
status=status,
|
||||
user=user,
|
||||
remote_id=remote_id,
|
||||
)
|
||||
except IntegrityError:
|
||||
return models.Boost.objects.get(status=status, user=user)
|
||||
|
||||
|
||||
def get_status(absolute_id):
|
||||
''' find a status in the database '''
|
||||
return get_by_absolute_id(absolute_id, models.Status)
|
||||
|
|
|
@ -32,6 +32,9 @@
|
|||
<div class="row shrink">
|
||||
{% include 'snippets/follow_request_buttons.html' with user=notification.related_user %}
|
||||
</div>
|
||||
|
||||
{% elif notification.notification_type == 'BOOST' %}
|
||||
boosted your <a href="{{ notification.related_status.absolute_id}}">status</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
reviewed {{ status.book.title }}
|
||||
{% elif status.status_type == 'Comment' %}
|
||||
commented on {{ status.book.title }}
|
||||
{% elif status.status_type == 'Boost' %}
|
||||
boosted
|
||||
{% elif status.reply_parent %}
|
||||
{% with parent_status=status|parent %}
|
||||
replied to {% include 'snippets/username.html' with user=parent_status.user possessive=True %} <a href="{{parent_status.absolute_id }}">{{ parent_status.status_type|lower }}</a>
|
||||
|
@ -43,10 +45,15 @@
|
|||
{% if status.status_type == 'Review' %}<h4>{{ status.name }}
|
||||
<small>{{ status.rating | stars }} stars, by {% include 'snippets/username.html' with user=status.user %}</small>
|
||||
</h4>{% endif %}
|
||||
{% if status.status_type != 'Update' %}
|
||||
{% if status.status_type != 'Update' and status.status_type != 'Boost' %}
|
||||
<blockquote>{{ status.content | safe }}</blockquote>
|
||||
{% endif %}
|
||||
{% if status.status_type == 'Boost' %}
|
||||
{% include 'snippets/status.html' with status=status.boosted_status depth=depth|add:1 %}
|
||||
{% endif %}
|
||||
{% if not max_depth and status.reply_parent or status|replies %}<p><a href="{{ status.absolute_id }}">Thread</a>{% endif %}
|
||||
</div>
|
||||
{% if status.status_type != 'Boost' %}
|
||||
{% include 'snippets/interaction.html' with activity=status %}
|
||||
{% endif %}
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ urlpatterns = [
|
|||
|
||||
re_path(r'^favorite/(?P<status_id>\d+)/?$', actions.favorite),
|
||||
re_path(r'^unfavorite/(?P<status_id>\d+)/?$', actions.unfavorite),
|
||||
re_path(r'^boost/(?P<status_id>\d+)/?$', actions.boost),
|
||||
|
||||
re_path(r'^shelve/?$', actions.shelve),
|
||||
|
||||
|
|
|
@ -240,6 +240,12 @@ def unfavorite(request, status_id):
|
|||
outgoing.handle_unfavorite(request.user, status)
|
||||
return redirect(request.headers.get('Referer', '/'))
|
||||
|
||||
@login_required
|
||||
def boost(request, status_id):
|
||||
''' boost a status '''
|
||||
status = models.Status.objects.get(id=status_id)
|
||||
outgoing.handle_boost(request.user, status)
|
||||
return redirect(request.headers.get('Referer', '/'))
|
||||
|
||||
@login_required
|
||||
def follow(request):
|
||||
|
|
Loading…
Reference in a new issue