aboutsummaryrefslogtreecommitdiffhomepage
path: root/Digitigrade/Model
diff options
context:
space:
mode:
authorwinter2025-01-19 22:17:12 +0000
committerwinter2025-01-19 22:17:12 +0000
commit8c6dad8c48d5d730b102d9de0079fee9752b0d35 (patch)
tree2cfe07a8c7958f78c767fd0d1444c3f8fef70511 /Digitigrade/Model
parent2d9bd87fb1c565ee161f93219ce2533e8dbffe06 (diff)
implement notifications
Diffstat (limited to 'Digitigrade/Model')
-rw-r--r--Digitigrade/Model/HomeTimelineItem.php53
-rw-r--r--Digitigrade/Model/Interaction.php37
-rw-r--r--Digitigrade/Model/Note.php32
-rw-r--r--Digitigrade/Model/Notification.php57
4 files changed, 175 insertions, 4 deletions
diff --git a/Digitigrade/Model/HomeTimelineItem.php b/Digitigrade/Model/HomeTimelineItem.php
new file mode 100644
index 0000000..7e2c062
--- /dev/null
+++ b/Digitigrade/Model/HomeTimelineItem.php
@@ -0,0 +1,53 @@
+<?php
+namespace Digitigrade\Model;
+
+use Digitigrade\Model;
+use Digitigrade\Model\UserAccount;
+use Digitigrade\Timeline\TimelineItem;
+
+class HomeTimelineItem extends Model {
+ public ?int $id;
+ public UserAccount $userAccount;
+ public ?string $reasonKind;
+ public ?string $reasonData;
+ public string $itemKind;
+ public string $itemData;
+ public \DateTimeImmutable $modified;
+
+ /**
+ * Creates a new home timeline entry for a particular user, but does not save it.
+ */
+ public static function fromTimelineItem(TimelineItem $item, UserAccount $user): self {
+ $hti = new self();
+ $hti->userAccount = $user;
+ $reason = $item->getReason();
+ if ($reason != null) {
+ $hti->reasonKind = $reason::class;
+ $hti->reasonData = json_encode($reason->toJsonReference());
+ }
+ $object = $item->getObject();
+ $hti->itemKind = $object::class;
+ $hti->itemData = json_encode($object->toJsonReference());
+ $hti->modified = new \DateTimeImmutable();
+ return $hti;
+ }
+
+ public function toTimelineItem(): TimelineItem {
+ $reason = null;
+ if (isset($this->reasonKind)) {
+ $reason = $this->reasonKind::fromJsonReference(json_decode($this->reasonData));
+ }
+ $object = $this->itemKind::fromJsonReference(json_decode($this->itemData));
+ return new TimelineItem($object, $reason);
+ }
+
+ public static function findAllForUser(UserAccount $user, ?int $limit = null, int $offset = 0, ?\DateTimeInterface $since = null) {
+ $where = 'user_account = ?';
+ $params = [$user->id];
+ if (isset($since)) {
+ $where .= ' AND modified > ?';
+ $params[] = $since->format('c');
+ }
+ return self::findAllWhere($where, $params, $limit, $offset, 'id DESC');
+ }
+} \ No newline at end of file
diff --git a/Digitigrade/Model/Interaction.php b/Digitigrade/Model/Interaction.php
index e5619bb..cf2be01 100644
--- a/Digitigrade/Model/Interaction.php
+++ b/Digitigrade/Model/Interaction.php
@@ -1,11 +1,12 @@
<?php
namespace Digitigrade\Model;
-use Digitigrade\Timeline\HomeTimelineItem;
+use Digitigrade\Model\HomeTimelineItem;
+use Digitigrade\Notification\Notifyable;
use Digitigrade\Timeline\ReasonReshared;
use Digitigrade\Timeline\TimelineItem;
-class Interaction extends PushableModel {
+class Interaction extends PushableModel implements Notifyable {
public ?int $id;
public string $uri;
public \DateTimeImmutable $created;
@@ -61,6 +62,30 @@ class Interaction extends PushableModel {
return array_unique($instances);
}
+ public function getNotificationTitle(): string {
+ return sprintf(__('notifications.interaction.' . $this->kind->value . '.title'), $this->author->displayName);
+ }
+ public function getNotificationTitleLink(): ?string {
+ return '/@/' . $this->target->author->getFullHandle() . '/note/' . $this->target->id;
+ }
+ public function getNotificationBody(): ?string {
+ return $this->target->plainContent;
+ }
+ public function getNotificationImageUrl(): ?string {
+ return $this->author->avatar;
+ }
+ public function getNotificationImageLink(): ?string {
+ return '/@/' . $this->author->getFullHandle();
+ }
+
+ public function toJsonReference(): mixed {
+ return $this->id;
+ }
+
+ public static function fromJsonReference(mixed $reference): self {
+ return self::find($reference);
+ }
+
public function processTimelineAdditions() {
// basically, if this is a reshare, we need to put a home timeline item
// for all followers who can see the shared note
@@ -80,6 +105,14 @@ class Interaction extends PushableModel {
}
}
+ public function processNotifications() {
+ if (!$this->target->author->isLocal || $this->author == $this->target->author) {
+ return;
+ }
+ $recipient = UserAccount::findByLinkedActor($this->target->author);
+ Notification::fromNotifyable($this, $recipient)->send();
+ }
+
public function jsonSerialize(): array {
if ($this->deleted) {
return [
diff --git a/Digitigrade/Model/Note.php b/Digitigrade/Model/Note.php
index f6f492d..cf84ee0 100644
--- a/Digitigrade/Model/Note.php
+++ b/Digitigrade/Model/Note.php
@@ -2,11 +2,12 @@
namespace Digitigrade\Model;
use Digitigrade\Db;
-use Digitigrade\Timeline\HomeTimelineItem;
+use Digitigrade\Model\HomeTimelineItem;
+use Digitigrade\Notification\Notifyable;
use Digitigrade\Timeline\TimelineIncludeable;
use Digitigrade\Timeline\TimelineItem;
-class Note extends PushableModel implements TimelineIncludeable {
+class Note extends PushableModel implements TimelineIncludeable, Notifyable {
public ?int $id;
public string $uri;
public \DateTimeImmutable $created;
@@ -195,6 +196,33 @@ class Note extends PushableModel implements TimelineIncludeable {
return self::find($reference);
}
+ public function getNotificationTitle(): string {
+ // assuming that this notification is because the note is a reply for now
+ return sprintf(__('notifications.interaction.reply.title'), $this->author->displayName);
+ }
+ public function getNotificationTitleLink(): ?string {
+ return '/@/' . $this->author->getFullHandle() . '/note/' . $this->id;
+ }
+ public function getNotificationBody(): ?string {
+ return $this->plainContent;
+ }
+ public function getNotificationImageUrl(): ?string {
+ return $this->author->avatar;
+ }
+ public function getNotificationImageLink(): ?string {
+ return '/@/' . $this->author->getFullHandle();
+ }
+
+ public function processNotifications() {
+ foreach ($this->mentions as $a) {
+ if (!$a->isLocal) {
+ continue;
+ }
+ $user = UserAccount::findByLinkedActor($a);
+ Notification::fromNotifyable($this, $user)->send();
+ }
+ }
+
public function processTimelineAdditions() {
$item = new TimelineItem($this);
foreach ($this->getRelevantActors() as $actor) {
diff --git a/Digitigrade/Model/Notification.php b/Digitigrade/Model/Notification.php
new file mode 100644
index 0000000..ee9d64a
--- /dev/null
+++ b/Digitigrade/Model/Notification.php
@@ -0,0 +1,57 @@
+<?php
+namespace Digitigrade\Model;
+
+use Digitigrade\Model;
+use Digitigrade\Notification\Notifyable;
+
+class Notification extends Model {
+ public ?int $id;
+ public UserAccount $userAccount;
+ public string $itemType;
+ public string $itemData;
+ public \DateTimeImmutable $created;
+
+ /**
+ * Creates a new Notification from a Notifyable object, but does not send it.
+ * @param Notifyable $item the item to send in the notification
+ * @param UserAccount $user the user to send the notification to
+ * @return Notification
+ */
+ public static function fromNotifyable(Notifyable $item, UserAccount $user): self {
+ $notif = new self();
+ $notif->userAccount = $user;
+ $notif->itemType = $item::class;
+ $notif->itemData = json_encode($item->toJsonReference());
+ $notif->created = new \DateTimeImmutable();
+
+ $notif->save();
+ return $notif;
+ }
+
+ public function toNotifyable(): Notifyable {
+ return $this->itemType::fromJsonReference(json_decode($this->itemData));
+ }
+
+ /**
+ * Saves this object and delivers the notification to the recipient user.
+ * @return void
+ */
+ public function send() {
+ $this->save();
+ // TODO: send it via any push mechanisms
+ }
+
+ /**
+ * @return self[]
+ */
+ public static function findAllForUser(UserAccount $user, ?int $limit = null, int $offset = 0, ?\DateTimeInterface $since = null) {
+ $where = 'user_account = ?';
+ $params = [$user->id];
+ if (isset($since)) {
+ $where .= ' AND created > ?';
+ $params[] = $since->format('c');
+ }
+ return self::findAllWhere($where, $params, $limit, $offset, 'id DESC');
+ }
+
+} \ No newline at end of file