mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-12-22 08:07:14 +00:00
Adds fav/boost class view
This commit is contained in:
parent
c8efa1ffc8
commit
cf2b9937c6
8 changed files with 289 additions and 220 deletions
|
@ -16,7 +16,6 @@ from bookwyrm.broadcast import broadcast
|
|||
from bookwyrm.sanitize_html import InputHtmlParser
|
||||
from bookwyrm.status import create_notification
|
||||
from bookwyrm.status import create_generated_note
|
||||
from bookwyrm.status import delete_status
|
||||
from bookwyrm.settings import DOMAIN
|
||||
from bookwyrm.utils import regex
|
||||
|
||||
|
@ -211,99 +210,3 @@ def handle_imported_book(user, item, include_reviews, privacy):
|
|||
# we don't need to send out pure activities because non-bookwyrm
|
||||
# instances don't need this data
|
||||
broadcast(user, review.to_create_activity(user), privacy=privacy)
|
||||
|
||||
|
||||
def handle_favorite(user, status):
|
||||
''' a user likes a status '''
|
||||
try:
|
||||
favorite = models.Favorite.objects.create(
|
||||
status=status,
|
||||
user=user
|
||||
)
|
||||
except IntegrityError:
|
||||
# you already fav'ed that
|
||||
return
|
||||
|
||||
fav_activity = favorite.to_activity()
|
||||
broadcast(
|
||||
user, fav_activity, privacy='direct', direct_recipients=[status.user])
|
||||
if status.user.local:
|
||||
create_notification(
|
||||
status.user,
|
||||
'FAVORITE',
|
||||
related_user=user,
|
||||
related_status=status
|
||||
)
|
||||
|
||||
|
||||
def handle_unfavorite(user, status):
|
||||
''' a user likes a status '''
|
||||
try:
|
||||
favorite = models.Favorite.objects.get(
|
||||
status=status,
|
||||
user=user
|
||||
)
|
||||
except models.Favorite.DoesNotExist:
|
||||
# can't find that status, idk
|
||||
return
|
||||
|
||||
fav_activity = favorite.to_undo_activity(user)
|
||||
favorite.delete()
|
||||
broadcast(user, fav_activity, direct_recipients=[status.user])
|
||||
|
||||
# check for notification
|
||||
if status.user.local:
|
||||
notification = models.Notification.objects.filter(
|
||||
user=status.user, related_user=user,
|
||||
related_status=status, notification_type='FAVORITE'
|
||||
).first()
|
||||
if notification:
|
||||
notification.delete()
|
||||
|
||||
|
||||
def handle_boost(user, status):
|
||||
''' a user wishes to boost a status '''
|
||||
# is it boostable?
|
||||
if not status.boostable:
|
||||
return
|
||||
|
||||
if models.Boost.objects.filter(
|
||||
boosted_status=status, user=user).exists():
|
||||
# you already boosted that.
|
||||
return
|
||||
boost = models.Boost.objects.create(
|
||||
boosted_status=status,
|
||||
privacy=status.privacy,
|
||||
user=user,
|
||||
)
|
||||
|
||||
boost_activity = boost.to_activity()
|
||||
broadcast(user, boost_activity)
|
||||
|
||||
if status.user.local:
|
||||
create_notification(
|
||||
status.user,
|
||||
'BOOST',
|
||||
related_user=user,
|
||||
related_status=status
|
||||
)
|
||||
|
||||
|
||||
def handle_unboost(user, status):
|
||||
''' a user regrets boosting a status '''
|
||||
boost = models.Boost.objects.filter(
|
||||
boosted_status=status, user=user
|
||||
).first()
|
||||
activity = boost.to_undo_activity(user)
|
||||
|
||||
boost.delete()
|
||||
broadcast(user, activity)
|
||||
|
||||
# delete related notification
|
||||
if status.user.local:
|
||||
notification = models.Notification.objects.filter(
|
||||
user=status.user, related_user=user,
|
||||
related_status=status, notification_type='BOOST'
|
||||
).first()
|
||||
if notification:
|
||||
notification.delete()
|
||||
|
|
|
@ -430,112 +430,3 @@ class Outgoing(TestCase):
|
|||
self.assertFalse(models.Review.objects.filter(
|
||||
book=self.book, user=self.local_user
|
||||
).exists())
|
||||
|
||||
|
||||
def test_handle_delete_status(self):
|
||||
''' marks a status as deleted '''
|
||||
status = models.Status.objects.create(
|
||||
user=self.local_user, content='hi')
|
||||
self.assertFalse(status.deleted)
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
outgoing.handle_delete_status(self.local_user, status)
|
||||
status.refresh_from_db()
|
||||
self.assertTrue(status.deleted)
|
||||
|
||||
|
||||
def test_handle_favorite(self):
|
||||
''' create and broadcast faving a status '''
|
||||
status = models.Status.objects.create(
|
||||
user=self.local_user, content='hi')
|
||||
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
outgoing.handle_favorite(self.remote_user, status)
|
||||
fav = models.Favorite.objects.get()
|
||||
self.assertEqual(fav.status, status)
|
||||
self.assertEqual(fav.user, self.remote_user)
|
||||
|
||||
notification = models.Notification.objects.get()
|
||||
self.assertEqual(notification.notification_type, 'FAVORITE')
|
||||
self.assertEqual(notification.user, self.local_user)
|
||||
self.assertEqual(notification.related_user, self.remote_user)
|
||||
|
||||
|
||||
def test_handle_unfavorite(self):
|
||||
''' unfav a status '''
|
||||
status = models.Status.objects.create(
|
||||
user=self.local_user, content='hi')
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
outgoing.handle_favorite(self.remote_user, status)
|
||||
|
||||
self.assertEqual(models.Favorite.objects.count(), 1)
|
||||
self.assertEqual(models.Notification.objects.count(), 1)
|
||||
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
outgoing.handle_unfavorite(self.remote_user, status)
|
||||
self.assertEqual(models.Favorite.objects.count(), 0)
|
||||
self.assertEqual(models.Notification.objects.count(), 0)
|
||||
|
||||
|
||||
def test_handle_boost(self):
|
||||
''' boost a status '''
|
||||
status = models.Status.objects.create(
|
||||
user=self.local_user, content='hi')
|
||||
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
outgoing.handle_boost(self.remote_user, status)
|
||||
|
||||
boost = models.Boost.objects.get()
|
||||
self.assertEqual(boost.boosted_status, status)
|
||||
self.assertEqual(boost.user, self.remote_user)
|
||||
self.assertEqual(boost.privacy, 'public')
|
||||
|
||||
notification = models.Notification.objects.get()
|
||||
self.assertEqual(notification.notification_type, 'BOOST')
|
||||
self.assertEqual(notification.user, self.local_user)
|
||||
self.assertEqual(notification.related_user, self.remote_user)
|
||||
self.assertEqual(notification.related_status, status)
|
||||
|
||||
def test_handle_boost_unlisted(self):
|
||||
''' boost a status '''
|
||||
status = models.Status.objects.create(
|
||||
user=self.local_user, content='hi', privacy='unlisted')
|
||||
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
outgoing.handle_boost(self.remote_user, status)
|
||||
|
||||
boost = models.Boost.objects.get()
|
||||
self.assertEqual(boost.privacy, 'unlisted')
|
||||
|
||||
def test_handle_boost_private(self):
|
||||
''' boost a status '''
|
||||
status = models.Status.objects.create(
|
||||
user=self.local_user, content='hi', privacy='followers')
|
||||
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
outgoing.handle_boost(self.remote_user, status)
|
||||
self.assertFalse(models.Boost.objects.exists())
|
||||
|
||||
def test_handle_boost_twice(self):
|
||||
''' boost a status '''
|
||||
status = models.Status.objects.create(
|
||||
user=self.local_user, content='hi')
|
||||
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
outgoing.handle_boost(self.remote_user, status)
|
||||
outgoing.handle_boost(self.remote_user, status)
|
||||
self.assertEqual(models.Boost.objects.count(), 1)
|
||||
|
||||
|
||||
def test_handle_unboost(self):
|
||||
''' undo a boost '''
|
||||
status = models.Status.objects.create(
|
||||
user=self.local_user, content='hi')
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
outgoing.handle_boost(self.remote_user, status)
|
||||
|
||||
self.assertEqual(models.Boost.objects.count(), 1)
|
||||
self.assertEqual(models.Notification.objects.count(), 1)
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
outgoing.handle_unboost(self.remote_user, status)
|
||||
self.assertEqual(models.Boost.objects.count(), 0)
|
||||
self.assertEqual(models.Notification.objects.count(), 0)
|
||||
|
|
152
bookwyrm/tests/views/test_interaction.py
Normal file
152
bookwyrm/tests/views/test_interaction.py
Normal file
|
@ -0,0 +1,152 @@
|
|||
''' test for app action functionality '''
|
||||
from unittest.mock import patch
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
|
||||
from bookwyrm import models, views
|
||||
|
||||
|
||||
class InteractionViews(TestCase):
|
||||
''' viewing and creating statuses '''
|
||||
def setUp(self):
|
||||
''' we need basic test data and mocks '''
|
||||
self.factory = RequestFactory()
|
||||
self.local_user = models.User.objects.create_user(
|
||||
'mouse@local.com', 'mouse@mouse.com', 'mouseword',
|
||||
local=True, localname='mouse',
|
||||
remote_id='https://example.com/users/mouse',
|
||||
)
|
||||
with patch('bookwyrm.models.user.set_remote_server'):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
'rat', 'rat@email.com', 'ratword',
|
||||
local=False,
|
||||
remote_id='https://example.com/users/rat',
|
||||
inbox='https://example.com/users/rat/inbox',
|
||||
outbox='https://example.com/users/rat/outbox',
|
||||
)
|
||||
|
||||
work = models.Work.objects.create(title='Test Work')
|
||||
self.book = models.Edition.objects.create(
|
||||
title='Example Edition',
|
||||
remote_id='https://example.com/book/1',
|
||||
parent_work=work
|
||||
)
|
||||
|
||||
|
||||
def test_handle_favorite(self):
|
||||
''' create and broadcast faving a status '''
|
||||
view = views.Favorite.as_view()
|
||||
request = self.factory.post('')
|
||||
request.user = self.local_user
|
||||
status = models.Status.objects.create(
|
||||
user=self.local_user, content='hi')
|
||||
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
view(request, status)
|
||||
fav = models.Favorite.objects.get()
|
||||
self.assertEqual(fav.status, status)
|
||||
self.assertEqual(fav.user, self.remote_user)
|
||||
|
||||
notification = models.Notification.objects.get()
|
||||
self.assertEqual(notification.notification_type, 'FAVORITE')
|
||||
self.assertEqual(notification.user, self.local_user)
|
||||
self.assertEqual(notification.related_user, self.remote_user)
|
||||
|
||||
|
||||
def test_handle_unfavorite(self):
|
||||
''' unfav a status '''
|
||||
view = views.Unfavorite.as_view()
|
||||
request = self.factory.post('')
|
||||
request.user = self.local_user
|
||||
status = models.Status.objects.create(
|
||||
user=self.local_user, content='hi')
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
view(request, status)
|
||||
|
||||
self.assertEqual(models.Favorite.objects.count(), 1)
|
||||
self.assertEqual(models.Notification.objects.count(), 1)
|
||||
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
view(request, status)
|
||||
self.assertEqual(models.Favorite.objects.count(), 0)
|
||||
self.assertEqual(models.Notification.objects.count(), 0)
|
||||
|
||||
|
||||
def test_handle_boost(self):
|
||||
''' boost a status '''
|
||||
view = views.Boost.as_view()
|
||||
request = self.factory.post('')
|
||||
request.user = self.local_user
|
||||
status = models.Status.objects.create(
|
||||
user=self.local_user, content='hi')
|
||||
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
view(request, status)
|
||||
|
||||
boost = models.Boost.objects.get()
|
||||
self.assertEqual(boost.boosted_status, status)
|
||||
self.assertEqual(boost.user, self.remote_user)
|
||||
self.assertEqual(boost.privacy, 'public')
|
||||
|
||||
notification = models.Notification.objects.get()
|
||||
self.assertEqual(notification.notification_type, 'BOOST')
|
||||
self.assertEqual(notification.user, self.local_user)
|
||||
self.assertEqual(notification.related_user, self.remote_user)
|
||||
self.assertEqual(notification.related_status, status)
|
||||
|
||||
def test_handle_boost_unlisted(self):
|
||||
''' boost a status '''
|
||||
view = views.Boost.as_view()
|
||||
request = self.factory.post('')
|
||||
request.user = self.local_user
|
||||
status = models.Status.objects.create(
|
||||
user=self.local_user, content='hi', privacy='unlisted')
|
||||
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
view(request, status)
|
||||
|
||||
boost = models.Boost.objects.get()
|
||||
self.assertEqual(boost.privacy, 'unlisted')
|
||||
|
||||
def test_handle_boost_private(self):
|
||||
''' boost a status '''
|
||||
view = views.Boost.as_view()
|
||||
request = self.factory.post('')
|
||||
request.user = self.local_user
|
||||
status = models.Status.objects.create(
|
||||
user=self.local_user, content='hi', privacy='followers')
|
||||
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
view(request, status)
|
||||
self.assertFalse(models.Boost.objects.exists())
|
||||
|
||||
def test_handle_boost_twice(self):
|
||||
''' boost a status '''
|
||||
view = views.Boost.as_view()
|
||||
request = self.factory.post('')
|
||||
request.user = self.local_user
|
||||
status = models.Status.objects.create(
|
||||
user=self.local_user, content='hi')
|
||||
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
view(request, status)
|
||||
view(request, status)
|
||||
self.assertEqual(models.Boost.objects.count(), 1)
|
||||
|
||||
|
||||
def test_handle_unboost(self):
|
||||
''' undo a boost '''
|
||||
view = views.Unboost.as_view()
|
||||
request = self.factory.post('')
|
||||
request.user = self.local_user
|
||||
status = models.Status.objects.create(
|
||||
user=self.local_user, content='hi')
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
view(request, status)
|
||||
|
||||
self.assertEqual(models.Boost.objects.count(), 1)
|
||||
self.assertEqual(models.Notification.objects.count(), 1)
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
view(request, status)
|
||||
self.assertEqual(models.Boost.objects.count(), 0)
|
||||
self.assertEqual(models.Notification.objects.count(), 0)
|
|
@ -82,10 +82,10 @@ urlpatterns = [
|
|||
views.DeleteStatus.as_view()),
|
||||
|
||||
# interact
|
||||
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'^unboost/(?P<status_id>\d+)/?$', actions.unboost),
|
||||
re_path(r'^favorite/(?P<status_id>\d+)/?$', views.Favorite.as_view()),
|
||||
re_path(r'^unfavorite/(?P<status_id>\d+)/?$', views.Unfavorite.as_view()),
|
||||
re_path(r'^boost/(?P<status_id>\d+)/?$', views.Boost.as_view()),
|
||||
re_path(r'^unboost/(?P<status_id>\d+)/?$', views.Unboost.as_view()),
|
||||
|
||||
# books
|
||||
re_path(r'%s(.json)?/?$' % book_path, vviews.book_page),
|
||||
|
|
|
@ -378,15 +378,6 @@ def untag(request):
|
|||
return redirect('/book/%s' % book_id)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
def favorite(request, status_id):
|
||||
''' like a status '''
|
||||
status = models.Status.objects.get(id=status_id)
|
||||
outgoing.handle_favorite(request.user, status)
|
||||
return redirect(request.headers.get('Referer', '/'))
|
||||
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
def unfavorite(request, status_id):
|
||||
|
|
|
@ -8,3 +8,4 @@ from .direct_message import DirectMessage
|
|||
from .import_data import Import, ImportStatus
|
||||
from .user import User, EditUser, Followers, Following
|
||||
from .status import Status, Replies, CreateStatus, DeleteStatus
|
||||
from .interaction import Favorite, Unfavorite, Boost, Unboost
|
||||
|
|
131
bookwyrm/views/interaction.py
Normal file
131
bookwyrm/views/interaction.py
Normal file
|
@ -0,0 +1,131 @@
|
|||
''' boosts and favs '''
|
||||
from django.db import IntegrityError
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import HttpResponseBadRequest, HttpResponseNotFound
|
||||
from django.shortcuts import redirect
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
|
||||
from bookwyrm import models
|
||||
from bookwyrm.broadcast import broadcast
|
||||
from bookwyrm.status import create_notification
|
||||
|
||||
|
||||
# pylint: disable= no-self-use
|
||||
@method_decorator(login_required, name='dispatch')
|
||||
class Favorite(View):
|
||||
''' like a status '''
|
||||
def post(self, request, status_id):
|
||||
''' create a like '''
|
||||
status = models.Status.objects.get(id=status_id)
|
||||
try:
|
||||
favorite = models.Favorite.objects.create(
|
||||
status=status,
|
||||
user=request.user
|
||||
)
|
||||
except IntegrityError:
|
||||
# you already fav'ed that
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
fav_activity = favorite.to_activity()
|
||||
broadcast(
|
||||
request.user, fav_activity, privacy='direct',
|
||||
direct_recipients=[status.user])
|
||||
if status.user.local:
|
||||
create_notification(
|
||||
status.user,
|
||||
'FAVORITE',
|
||||
related_user=request.user,
|
||||
related_status=status
|
||||
)
|
||||
return redirect(request.headers.get('Referer', '/'))
|
||||
|
||||
|
||||
@method_decorator(login_required, name='dispatch')
|
||||
class Unfavorite(View):
|
||||
''' take back a fav '''
|
||||
def post(self, request, status_id):
|
||||
''' unlike a status '''
|
||||
status = models.Status.objects.get(id=status_id)
|
||||
try:
|
||||
favorite = models.Favorite.objects.get(
|
||||
status=status,
|
||||
user=request.user
|
||||
)
|
||||
except models.Favorite.DoesNotExist:
|
||||
# can't find that status, idk
|
||||
return HttpResponseNotFound()
|
||||
|
||||
fav_activity = favorite.to_undo_activity(request.user)
|
||||
favorite.delete()
|
||||
broadcast(request.user, fav_activity, direct_recipients=[status.user])
|
||||
|
||||
# check for notification
|
||||
if status.user.local:
|
||||
notification = models.Notification.objects.filter(
|
||||
user=status.user, related_user=request.user,
|
||||
related_status=status, notification_type='FAVORITE'
|
||||
).first()
|
||||
if notification:
|
||||
notification.delete()
|
||||
return redirect(request.headers.get('Referer', '/'))
|
||||
|
||||
|
||||
@method_decorator(login_required, name='dispatch')
|
||||
class Boost(View):
|
||||
''' boost a status '''
|
||||
def post(self, request, status_id):
|
||||
''' boost a status '''
|
||||
status = models.Status.objects.get(id=status_id)
|
||||
# is it boostable?
|
||||
if not status.boostable:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
if models.Boost.objects.filter(
|
||||
boosted_status=status, user=request.user).exists():
|
||||
# you already boosted that.
|
||||
return redirect(request.headers.get('Referer', '/'))
|
||||
|
||||
boost = models.Boost.objects.create(
|
||||
boosted_status=status,
|
||||
privacy=status.privacy,
|
||||
user=request.user,
|
||||
)
|
||||
|
||||
boost_activity = boost.to_activity()
|
||||
broadcast(request.user, boost_activity)
|
||||
|
||||
if status.user.local:
|
||||
create_notification(
|
||||
status.user,
|
||||
'BOOST',
|
||||
related_user=request.user,
|
||||
related_status=status
|
||||
)
|
||||
return redirect(request.headers.get('Referer', '/'))
|
||||
|
||||
|
||||
|
||||
@method_decorator(login_required, name='dispatch')
|
||||
class Unboost(View):
|
||||
''' boost a status '''
|
||||
def post(self, request, status_id):
|
||||
''' boost a status '''
|
||||
status = models.Status.objects.get(id=status_id)
|
||||
boost = models.Boost.objects.filter(
|
||||
boosted_status=status, user=request.user
|
||||
).first()
|
||||
activity = boost.to_undo_activity(request.user)
|
||||
|
||||
boost.delete()
|
||||
broadcast(request.user, activity)
|
||||
|
||||
# delete related notification
|
||||
if status.user.local:
|
||||
notification = models.Notification.objects.filter(
|
||||
user=status.user, related_user=request.user,
|
||||
related_status=status, notification_type='BOOST'
|
||||
).first()
|
||||
if notification:
|
||||
notification.delete()
|
||||
return redirect(request.headers.get('Referer', '/'))
|
|
@ -1,4 +1,4 @@
|
|||
''' non-interactive pages '''
|
||||
''' what are we here for if not for posting '''
|
||||
import re
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import HttpResponseBadRequest, HttpResponseNotFound
|
||||
|
|
Loading…
Reference in a new issue