diff options
| author | winter | 2025-01-23 18:55:52 +0000 |
|---|---|---|
| committer | winter | 2025-01-23 18:55:58 +0000 |
| commit | bc9c83b6c33d460a574fbeb764c23bfbe941b4c0 (patch) | |
| tree | 5d9e816b56feea462d15e391544b0b13eeb47c8d /Digitigrade | |
| parent | ddccaf16f39a4bf710266edc389fcde124e7ef56 (diff) | |
implement blocking users
it doesn't work yet for notifications! but i think i got it in most other places
Diffstat (limited to 'Digitigrade')
| -rw-r--r-- | Digitigrade/Model/Actor.php | 38 | ||||
| -rw-r--r-- | Digitigrade/Model/BlockRelation.php | 82 | ||||
| -rw-r--r-- | Digitigrade/Model/FollowRelation.php | 2 | ||||
| -rw-r--r-- | Digitigrade/Model/Notification.php | 2 | ||||
| -rw-r--r-- | Digitigrade/Notification/Notifyable.php | 2 | ||||
| -rw-r--r-- | Digitigrade/Notification/UnblockNotif.php | 49 | ||||
| -rw-r--r-- | Digitigrade/Timeline/HomeTimeline.php | 11 | ||||
| -rw-r--r-- | Digitigrade/Timeline/Timeline.php | 23 |
8 files changed, 202 insertions, 7 deletions
diff --git a/Digitigrade/Model/Actor.php b/Digitigrade/Model/Actor.php index b4e9392..f2eecae 100644 --- a/Digitigrade/Model/Actor.php +++ b/Digitigrade/Model/Actor.php @@ -2,6 +2,7 @@ namespace Digitigrade\Model; use Digitigrade\Notification\PendingFollowActionedNotif; +use Digitigrade\Notification\UnblockNotif; use Digitigrade\Notification\UnfollowNotif; use Digitigrade\RpcException; use Digitigrade\RpcReceiver; @@ -142,6 +143,16 @@ class Actor extends PushableModel implements RpcReceiver { (new PendingFollowActionedNotif($args[0], $this, 'reject'))->processNotifications(); return; + case 'block': + $rel = BlockRelation::create($args[0], $this); + $rel->processNotifications(); + return; + + case 'unblock': + BlockRelation::findByActors($args[0], $this)->remove(); + (new UnblockNotif($args[0], $this))->processNotifications(); + return; + default: throw new \RuntimeException("behaviour for actor rpc method $method not (yet?) implemented"); } @@ -233,6 +244,31 @@ class Actor extends PushableModel implements RpcReceiver { return array_intersect($followers, $followees); } + /** + * Blocks another actor. + * @param Actor $target the actor to block + */ + public function block(self $target) { + $target->rpcCall('block', [$this]); + } + + /** + * Unblocks another actor. + * @param Actor $target the actor to unblock + */ + public function unblock(self $target) { + $target->rpcCall('unblock', [$this]); + } + + /** + * @param Actor $target the other actor + * @return boolean whether this actor blocks the target actor + */ + public function blocks(Actor $target): bool { + $relation = BlockRelation::findByActors($this, $target); + return $relation != null; + } + public function findHomeInstance(): Instance { if ($this->isLocal) { // can't do this because we don't keep track of ourself in the instance table @@ -282,6 +318,8 @@ class Actor extends PushableModel implements RpcReceiver { 'unfollow' => path_to_uri("/actor/$this->id/unfollow"), 'acceptedFollow' => path_to_uri("/actor/$this->id/acceptedFollow"), 'rejectedFollow' => path_to_uri("/actor/$this->id/rejectedFollow"), + 'block' => path_to_uri("/actor/$this->id/block"), + 'unblock' => path_to_uri("/actor/$this->id/unblock"), ] ]; } diff --git a/Digitigrade/Model/BlockRelation.php b/Digitigrade/Model/BlockRelation.php new file mode 100644 index 0000000..ca7166c --- /dev/null +++ b/Digitigrade/Model/BlockRelation.php @@ -0,0 +1,82 @@ +<?php +namespace Digitigrade\Model; + +use Digitigrade\Model; +use Digitigrade\Notification\Notifyable; + +class BlockRelation extends Model implements Notifyable { + public Actor $subject; + public Actor $object; + + /** + * Creates and saves a new blocking relationship + * @param Actor $subject the actor who doesn't want to see the other one + * @param Actor $object the actor being blocked + * @return self + */ + public static function create(Actor $subject, Actor $object): self { + $rel = new self(); + $rel->subject = $subject; + $rel->object = $object; + $rel->save(); + return $rel; + } + + protected function getUpdateWhereClause(\PDO $db): ?string { + if (self::findWhere('subject = ? and object = ?', [$this->subject->id, $this->object->id]) != null) { + return 'subject = ' . $this->subject->id . ' and object = ' . $this->object->id; + } + return null; + } + + public static function findByActors(Actor $subject, Actor $object): ?self { + return self::findWhere('subject = ? and object = ?', [$subject->id, $object->id]); + } + + /** + * @return self[] + */ + public static function findAllWithSubject(Actor $subject): array { + return self::findAllWhere('subject = ?', [$subject->id]); + } + + /** + * @return self[] + */ + public static function findAllWithObject(Actor $object): array { + return self::findAllWhere('object = ?', [$object->id]); + } + + public function toJsonReference(): mixed { + return [$this->subject->id, $this->object->id]; + } + + public static function fromJsonReference(mixed $reference): ?self { + return self::findByActors( + Actor::find($reference[0]), + Actor::find($reference[1]) + ); + } + + public function getNotificationTitle(): string { + return sprintf(__("notifications.block"), $this->subject->displayName); + } + public function getNotificationTitleLink(): ?string { + return '/@/' . $this->subject->getFullHandle(); + } + public function getNotificationImageUrl(): ?string { + return $this->subject->avatar ?? '/static/default-avatar.png'; + } + public function getNotificationImageLink(): ?string { + return $this->getNotificationTitleLink(); + } + public function getNotificationBody(): ?string { + return null; + } + + public function processNotifications() { + if (!$this->object->isLocal) + return; + Notification::fromNotifyable($this, UserAccount::findByLinkedActor($this->object))->send(); + } +}
\ No newline at end of file diff --git a/Digitigrade/Model/FollowRelation.php b/Digitigrade/Model/FollowRelation.php index 7cab761..ecb625a 100644 --- a/Digitigrade/Model/FollowRelation.php +++ b/Digitigrade/Model/FollowRelation.php @@ -54,7 +54,7 @@ class FollowRelation extends Model implements Notifyable { return [$this->subject->id, $this->object->id]; } - public static function fromJsonReference(mixed $reference): self { + public static function fromJsonReference(mixed $reference): ?self { return self::findByActors( Actor::find($reference[0]), Actor::find($reference[1]) diff --git a/Digitigrade/Model/Notification.php b/Digitigrade/Model/Notification.php index ee9d64a..50e9991 100644 --- a/Digitigrade/Model/Notification.php +++ b/Digitigrade/Model/Notification.php @@ -28,7 +28,7 @@ class Notification extends Model { return $notif; } - public function toNotifyable(): Notifyable { + public function toNotifyable(): ?Notifyable { return $this->itemType::fromJsonReference(json_decode($this->itemData)); } diff --git a/Digitigrade/Notification/Notifyable.php b/Digitigrade/Notification/Notifyable.php index 510437e..7a7826f 100644 --- a/Digitigrade/Notification/Notifyable.php +++ b/Digitigrade/Notification/Notifyable.php @@ -3,7 +3,7 @@ namespace Digitigrade\Notification; interface Notifyable { public function toJsonReference(): mixed; - public static function fromJsonReference(mixed $reference): self; + public static function fromJsonReference(mixed $reference): ?self; public function getNotificationTitle(): string; public function getNotificationTitleLink(): ?string; public function getNotificationBody(): ?string; diff --git a/Digitigrade/Notification/UnblockNotif.php b/Digitigrade/Notification/UnblockNotif.php new file mode 100644 index 0000000..7f7ac64 --- /dev/null +++ b/Digitigrade/Notification/UnblockNotif.php @@ -0,0 +1,49 @@ +<?php +namespace Digitigrade\Notification; + +use Digitigrade\Model\Actor; +use Digitigrade\Model\Notification; +use Digitigrade\Model\UserAccount; + +class UnblockNotif implements Notifyable { + private Actor $subject; + private Actor $object; + + public function __construct(Actor $subject, Actor $object) { + $this->subject = $subject; + $this->object = $object; + } + + public function toJsonReference(): mixed { + return [$this->subject->id, $this->object->id]; + } + + public static function fromJsonReference(mixed $reference): self { + return new self( + Actor::find($reference[0]), + Actor::find($reference[1]) + ); + } + + public function getNotificationTitle(): string { + return sprintf(__('notifications.unblock'), $this->subject->displayName); + } + public function getNotificationTitleLink(): ?string { + return '/@/' . $this->subject->getFullHandle(); + } + public function getNotificationImageUrl(): ?string { + return $this->subject->avatar ?? '/static/default-avatar.png'; + } + public function getNotificationImageLink(): ?string { + return $this->getNotificationTitleLink(); + } + public function getNotificationBody(): ?string { + return null; + } + + public function processNotifications() { + if (!$this->object->isLocal) + return; + Notification::fromNotifyable($this, UserAccount::findByLinkedActor($this->object))->send(); + } +}
\ No newline at end of file diff --git a/Digitigrade/Timeline/HomeTimeline.php b/Digitigrade/Timeline/HomeTimeline.php index 0867541..9f420e5 100644 --- a/Digitigrade/Timeline/HomeTimeline.php +++ b/Digitigrade/Timeline/HomeTimeline.php @@ -1,6 +1,7 @@ <?php namespace Digitigrade\Timeline; +use Digitigrade\Model\Note; use Digitigrade\Model\UserAccount; use Digitigrade\Model\HomeTimelineItem; @@ -19,9 +20,13 @@ class HomeTimeline extends Timeline { } public function getItemsSince(\DateTimeInterface $when, ?int $limit = null, int $offset = 0): array { - return array_map( - fn(HomeTimelineItem $hti) => $hti->toTimelineItem(), - HomeTimelineItem::findAllForUser($this->user, $limit, $offset, $when) + return array_filter( + array_map( + fn(HomeTimelineItem $hti) => $hti->toTimelineItem(), + HomeTimelineItem::findAllForUser($this->user, $limit, $offset, $when) + ), + // filter out notes from blocked users + fn(TimelineItem $item) => !($item instanceof Note && $this->user->actor->block($item->author)) ); } }
\ No newline at end of file diff --git a/Digitigrade/Timeline/Timeline.php b/Digitigrade/Timeline/Timeline.php index b3def28..f53eaa4 100644 --- a/Digitigrade/Timeline/Timeline.php +++ b/Digitigrade/Timeline/Timeline.php @@ -1,6 +1,9 @@ <?php namespace Digitigrade\Timeline; +use Digitigrade\Model\Note; +use Digitigrade\Model\UserAccount; + abstract class Timeline { // TODO: currently making this number up. probably should be modifiable somewhere private const RESULTS_PER_PAGE = 50; @@ -11,7 +14,25 @@ abstract class Timeline { * @return TimelineItem[] */ public function getPage(int $page = 0): array { - return $this->getItems(self::RESULTS_PER_PAGE, self::RESULTS_PER_PAGE * $page); + return $this->getItemsFiltered(self::RESULTS_PER_PAGE, self::RESULTS_PER_PAGE * $page); + } + + /** + * @return TimelineItem[] + */ + private function getItemsFiltered(int $limit, int $offset): array { + $user = UserAccount::findByCurrentSession(); + $items = $this->getItems($limit, $offset); + if ($user == null) + return $items; + // filter out notes from blocked users + return array_filter($items, function (TimelineItem $item) use ($user) { + $obj = $item->getObject(); + if ($obj instanceof Note && $user->actor->blocks($obj->author)) { + return false; + } + return true; + }); } /** |
