diff --git a/backend/api/models.py b/backend/api/models.py index a8b5559..9c8b5ac 100644 --- a/backend/api/models.py +++ b/backend/api/models.py @@ -128,6 +128,7 @@ class Notification(models.Model): TYPE_CHOICES = [ ('reply', 'Reply'), ('mention', 'Mention'), + ('follow', 'Follow'), ('ticket_new', 'New Ticket'), ('ticket_reply', 'Ticket Reply'), ('profile_comment', 'Profile Comment'), diff --git a/backend/api/views.py b/backend/api/views.py index 78e4af5..b3598e6 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -553,7 +553,16 @@ class UserViewSet(viewsets.ModelViewSet): user_to_follow = self.get_object() if user_to_follow == request.user: return Response({'error': 'You cannot follow yourself'}, status=status.HTTP_400_BAD_REQUEST) - request.user.following.add(user_to_follow) + + if not request.user.following.filter(pk=user_to_follow.pk).exists(): + request.user.following.add(user_to_follow) + # Notify the user being followed + Notification.objects.create( + recipient=user_to_follow, + actor=request.user, + notification_type='follow' + ) + return Response({'status': 'followed'}) @action(detail=True, methods=['post'], permission_classes=[permissions.IsAuthenticated]) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 1f511a9..511c015 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -512,6 +512,9 @@ const App: React.FC = () => { } else if (n.notification_type === 'profile_comment') { handleNotificationClick(n); navigate(`/profile/${user?.username}`); + } else if (n.notification_type === 'follow') { + handleNotificationClick(n); + navigate(`/profile/${n.actor_username}`); } else { handleNotificationClick(n); } @@ -541,6 +544,7 @@ const App: React.FC = () => { {n.actor_username} { n.notification_type === 'reply' ? `replied to your review on ${n.flavor_name}` : n.notification_type === 'mention' ? `mentioned you on ${n.flavor_name}` : + n.notification_type === 'follow' ? t('community.notifFollow') : n.notification_type === 'profile_comment' ? `left a message on your guestbook` : n.notification_type === 'ticket_new' ? t('community.notifTicketNew') : user?.is_superuser ? t('community.notifTicketReplyAdmin') : t('community.notifTicketReply') diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index e98987e..3e9f089 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -141,6 +141,7 @@ const resources = { "notifications": "Recent Notifications", "notifReply": "replied", "notifMention": "mentioned you", + "notifFollow": "started following you", "notifTicketNew": "opened a new support ticket", "notifTicketReply": "replied to your support ticket", "notifTicketReplyAdmin": "replied to a support ticket", @@ -381,6 +382,7 @@ const resources = { "notifications": "Neueste Benachrichtigungen", "notifReply": "hat geantwortet", "notifMention": "hat dich erwähnt", + "notifFollow": "folgt dir jetzt", "notifTicketNew": "hat ein neues Support-Ticket eröffnet", "notifTicketReply": "hat auf dein Support-Ticket geantwortet", "notifTicketReplyAdmin": "hat auf ein Support-Ticket geantwortet", diff --git a/frontend/src/pages/CommunityFeed.tsx b/frontend/src/pages/CommunityFeed.tsx index eeee4d1..a8baec1 100644 --- a/frontend/src/pages/CommunityFeed.tsx +++ b/frontend/src/pages/CommunityFeed.tsx @@ -385,22 +385,37 @@ const CommunityFeed: React.FC = ({ adminMode }) => { {notifications.length === 0 ? ( {t('community.noNotifications')} ) : ( - notifications.map(n => ( - n.flavor_id && navigate(`/flavor/${n.flavor_id}`)} sx={{ py: 1.5 }}> - - {n.actor_username.charAt(0)} - - - {n.actor_username} {n.notification_type === 'reply' ? t('community.notifReply') : t('community.notifMention')} - - } - secondary={formatTimestamp(n.created_at)} - secondaryTypographyProps={{ sx: { fontSize: '0.7rem' } }} - /> - - )) + notifications.map(n => { + const handleClick = () => { + if (n.notification_type === 'follow') { + navigate(`/profile/${n.actor_username}`); + } else if (n.flavor_id) { + navigate(`/flavor/${n.flavor_id}`); + } + }; + + return ( + + + {n.actor_username.charAt(0)} + + + {n.actor_username} { + n.notification_type === 'reply' ? t('community.notifReply') : + n.notification_type === 'mention' ? t('community.notifMention') : + n.notification_type === 'follow' ? t('community.notifFollow') : + n.notification_type + } + + } + secondary={formatTimestamp(n.created_at)} + secondaryTypographyProps={{ sx: { fontSize: '0.7rem' } }} + /> + + ); + }) )}