aboutsummaryrefslogtreecommitdiffhomepage
path: root/Digitigrade
diff options
context:
space:
mode:
authorwinter2025-01-23 18:55:52 +0000
committerwinter2025-01-23 18:55:58 +0000
commitbc9c83b6c33d460a574fbeb764c23bfbe941b4c0 (patch)
tree5d9e816b56feea462d15e391544b0b13eeb47c8d /Digitigrade
parentddccaf16f39a4bf710266edc389fcde124e7ef56 (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.php38
-rw-r--r--Digitigrade/Model/BlockRelation.php82
-rw-r--r--Digitigrade/Model/FollowRelation.php2
-rw-r--r--Digitigrade/Model/Notification.php2
-rw-r--r--Digitigrade/Notification/Notifyable.php2
-rw-r--r--Digitigrade/Notification/UnblockNotif.php49
-rw-r--r--Digitigrade/Timeline/HomeTimeline.php11
-rw-r--r--Digitigrade/Timeline/Timeline.php23
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;
+ });
}
/**