aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Digitigrade/Model/Note.php7
-rw-r--r--Digitigrade/Timeline.php25
-rw-r--r--Digitigrade/Timeline/GlobalTimeline.php7
-rw-r--r--Digitigrade/Timeline/LocalTimeline.php7
-rw-r--r--Digitigrade/TimelineIncludeable.php6
-rw-r--r--Digitigrade/TimelineInclusionReason.php6
-rw-r--r--Digitigrade/TimelineInclusionReason/Reshared.php17
-rw-r--r--Digitigrade/TimelineItem.php20
-rw-r--r--locale/en_GB.json3
-rw-r--r--locale/fr_FR.json3
-rw-r--r--routes/public_timelines.php8
-rw-r--r--static/style.css27
-rw-r--r--templates/global_timeline.php10
-rw-r--r--templates/local_timeline.php10
-rw-r--r--templates/reason_reshared.php10
-rw-r--r--templates/timeline.php11
16 files changed, 132 insertions, 45 deletions
diff --git a/Digitigrade/Model/Note.php b/Digitigrade/Model/Note.php
index bc8b39b..7389617 100644
--- a/Digitigrade/Model/Note.php
+++ b/Digitigrade/Model/Note.php
@@ -3,8 +3,9 @@ namespace Digitigrade\Model;
use Digitigrade\Db;
use Digitigrade\Job\PushObject;
+use Digitigrade\TimelineIncludeable;
-class Note extends PushableModel {
+class Note extends PushableModel implements TimelineIncludeable {
public ?int $id;
public string $uri;
public \DateTimeImmutable $created;
@@ -137,6 +138,10 @@ class Note extends PushableModel {
return array_filter($this->formattedContent, fn($item) => $item['mimetype'] == $mimetype)[0]['body'] ?? null;
}
+ public function renderAsHtml() {
+ render_template('note', ['note' => $this]);
+ }
+
public function jsonSerialize(): array {
if ($this->deleted) {
return [
diff --git a/Digitigrade/Timeline.php b/Digitigrade/Timeline.php
index 67708ab..64aabe1 100644
--- a/Digitigrade/Timeline.php
+++ b/Digitigrade/Timeline.php
@@ -8,22 +8,19 @@ abstract class Timeline {
private const RESULTS_PER_PAGE = 50;
/**
- * Gets a pageful of note IDs to be included in this timeline
- * @param int $limit maximum number of results to return
- * @param int $offset number of results to skip (i.e. results from previous pages)
- * @return int[]
+ * Gets a pageful of items to be included in the timeline
+ * @param int $page page number to return (0-indexed)
+ * @return TimelineItem[]
*/
- protected abstract function getNoteIds(int $limit, int $offset): array;
+ public function getPage(int $page = 0): array {
+ return $this->getItems(self::RESULTS_PER_PAGE, self::RESULTS_PER_PAGE * $page);
+ }
/**
- * Gets a pageful of notes that are included in this timeline
- * @param int $page page number to return (0-indexed)
- * @return Note[]
+ * Gets a selection of items to be included in the timeline
+ * @param int $limit max number of items to return
+ * @param int $offset number of items to skip at first
+ * @return TimelineItem[]
*/
- public function getNotes(int $page = 0): array {
- $ids = $this->getNoteIds(self::RESULTS_PER_PAGE, self::RESULTS_PER_PAGE * $page);
- return array_map(function (int $id) {
- return Note::find($id);
- }, $ids);
- }
+ abstract protected function getItems(int $limit, int $offset): array;
} \ No newline at end of file
diff --git a/Digitigrade/Timeline/GlobalTimeline.php b/Digitigrade/Timeline/GlobalTimeline.php
index 2236ee4..278b557 100644
--- a/Digitigrade/Timeline/GlobalTimeline.php
+++ b/Digitigrade/Timeline/GlobalTimeline.php
@@ -2,10 +2,12 @@
namespace Digitigrade\Timeline;
use Digitigrade\Db;
+use Digitigrade\Model\Note;
use Digitigrade\Timeline;
+use Digitigrade\TimelineItem;
class GlobalTimeline extends Timeline {
- protected function getNoteIds(int $limit, int $offset): array {
+ protected function getItems(int $limit, int $offset): array {
$db = Db::getInstance()->getPdo();
$stmt = $db->prepare(<<<END
SELECT note.id FROM note LEFT JOIN note_privacy ON note_privacy.note_id = note.id
@@ -13,6 +15,7 @@ class GlobalTimeline extends Timeline {
ORDER BY note.created DESC LIMIT ? OFFSET ?
END);
$stmt->execute([$limit, $offset]);
- return $stmt->fetchAll(\PDO::FETCH_COLUMN, 0);
+ $ids = $stmt->fetchAll(\PDO::FETCH_COLUMN, 0);
+ return array_map(fn($id) => new TimelineItem(Note::find($id)), $ids);
}
} \ No newline at end of file
diff --git a/Digitigrade/Timeline/LocalTimeline.php b/Digitigrade/Timeline/LocalTimeline.php
index 69b8634..c20ada6 100644
--- a/Digitigrade/Timeline/LocalTimeline.php
+++ b/Digitigrade/Timeline/LocalTimeline.php
@@ -2,10 +2,12 @@
namespace Digitigrade\Timeline;
use Digitigrade\Db;
+use Digitigrade\Model\Note;
use Digitigrade\Timeline;
+use Digitigrade\TimelineItem;
class LocalTimeline extends Timeline {
- protected function getNoteIds(int $limit, int $offset): array {
+ protected function getItems(int $limit, int $offset): array {
$db = Db::getInstance()->getPdo();
$stmt = $db->prepare(<<<END
SELECT note.id FROM note
@@ -15,6 +17,7 @@ class LocalTimeline extends Timeline {
ORDER BY note.created DESC LIMIT ? OFFSET ?
END);
$stmt->execute([$limit, $offset]);
- return $stmt->fetchAll(\PDO::FETCH_COLUMN, 0);
+ $ids = $stmt->fetchAll(\PDO::FETCH_COLUMN, 0);
+ return array_map(fn($id) => new TimelineItem(Note::find($id)), $ids);
}
} \ No newline at end of file
diff --git a/Digitigrade/TimelineIncludeable.php b/Digitigrade/TimelineIncludeable.php
new file mode 100644
index 0000000..44433b4
--- /dev/null
+++ b/Digitigrade/TimelineIncludeable.php
@@ -0,0 +1,6 @@
+<?php
+namespace Digitigrade;
+
+interface TimelineIncludeable {
+ public function renderAsHtml();
+} \ No newline at end of file
diff --git a/Digitigrade/TimelineInclusionReason.php b/Digitigrade/TimelineInclusionReason.php
new file mode 100644
index 0000000..e9840d1
--- /dev/null
+++ b/Digitigrade/TimelineInclusionReason.php
@@ -0,0 +1,6 @@
+<?php
+namespace Digitigrade;
+
+abstract class TimelineInclusionReason {
+ abstract public function renderAsHtml();
+} \ No newline at end of file
diff --git a/Digitigrade/TimelineInclusionReason/Reshared.php b/Digitigrade/TimelineInclusionReason/Reshared.php
new file mode 100644
index 0000000..f185149
--- /dev/null
+++ b/Digitigrade/TimelineInclusionReason/Reshared.php
@@ -0,0 +1,17 @@
+<?php
+namespace Digitigrade\TimelineInclusionReason;
+
+use Digitigrade\Model\Actor;
+use Digitigrade\TimelineInclusionReason;
+
+class Reshared extends TimelineInclusionReason {
+ private Actor $resharer;
+
+ public function __construct(Actor $resharer) {
+ $this->resharer = $resharer;
+ }
+
+ public function renderAsHtml() {
+ render_template('reason_reshared', ['actor' => $this->resharer]);
+ }
+} \ No newline at end of file
diff --git a/Digitigrade/TimelineItem.php b/Digitigrade/TimelineItem.php
new file mode 100644
index 0000000..e59fbec
--- /dev/null
+++ b/Digitigrade/TimelineItem.php
@@ -0,0 +1,20 @@
+<?php
+namespace Digitigrade;
+
+class TimelineItem {
+ private ?TimelineInclusionReason $reason;
+ private TimelineIncludeable $object;
+
+ public function __construct(TimelineIncludeable $object, ?TimelineInclusionReason $reason = null) {
+ $this->reason = $reason;
+ $this->object = $object;
+ }
+
+ public function getReason(): ?TimelineInclusionReason {
+ return $this->reason;
+ }
+
+ public function getObject(): TimelineIncludeable {
+ return $this->object;
+ }
+} \ No newline at end of file
diff --git a/locale/en_GB.json b/locale/en_GB.json
index 67a4ec5..b1ed2b2 100644
--- a/locale/en_GB.json
+++ b/locale/en_GB.json
@@ -13,5 +13,6 @@
"placeholder": "There's nothing here.",
"timeline.global.shortName": "Global",
"timeline.local.shortName": "Local",
- "digitigrade": "Digitigrade"
+ "digitigrade": "Digitigrade",
+ "note.resharedBy": "Reshared by %s"
}
diff --git a/locale/fr_FR.json b/locale/fr_FR.json
index bc09a4c..b17b03a 100644
--- a/locale/fr_FR.json
+++ b/locale/fr_FR.json
@@ -8,5 +8,6 @@
"placeholder": "Il n'y a rien ici.",
"timeline.global.shortName": "Mondiale",
"timeline.local.shortName": "Locale",
- "digitigrade": "La Digitigrade πŸ‡«πŸ‡·πŸ‡«πŸ‡·πŸ‡«πŸ‡·πŸ‡«πŸ‡·"
+ "digitigrade": "La Digitigrade πŸ‡«πŸ‡·πŸ‡«πŸ‡·πŸ‡«πŸ‡·πŸ‡«πŸ‡·",
+ "note.resharedBy": "RepartegΓ© par %s"
}
diff --git a/routes/public_timelines.php b/routes/public_timelines.php
index 2d40328..b60f141 100644
--- a/routes/public_timelines.php
+++ b/routes/public_timelines.php
@@ -5,11 +5,11 @@ use Digitigrade\Timeline\GlobalTimeline;
use Digitigrade\Timeline\LocalTimeline;
Router::getInstance()->mount('/feed/global', function (array $args) {
- $notes = (new GlobalTimeline())->getNotes();
- render_template('global_timeline', ['notes' => $notes]);
+ $items = (new GlobalTimeline())->getPage();
+ render_template('timeline', ['kind' => 'global', 'items' => $items]);
});
Router::getInstance()->mount('/feed/local', function (array $args) {
- $notes = (new LocalTimeline())->getNotes();
- render_template('local_timeline', ['notes' => $notes]);
+ $items = (new LocalTimeline())->getPage();
+ render_template('timeline', ['kind' => 'local', 'items' => $items]);
}); \ No newline at end of file
diff --git a/static/style.css b/static/style.css
index fcf6e81..0809f63 100644
--- a/static/style.css
+++ b/static/style.css
@@ -74,6 +74,33 @@ hr {
text-align: center;
}
+.timelineReason {
+ background: #fee4;
+ border: 1px solid #3b005e44;
+ border-bottom: none;
+ border-radius: 8px 8px 0 0;
+ margin: 16px 16px -16px 16px;
+ padding: 8px;
+ display: grid;
+ grid-template-columns: max-content 1fr;
+ align-items: center;
+ gap: 8px;
+
+ .icon {
+ font-size: 24px;
+ }
+ a {
+ text-decoration: none;
+ font-weight: bold;
+ color: inherit;
+ }
+
+ + article.note {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ }
+}
+
article.note {
background: #fee4;
border: 1px solid #3b005e44;
diff --git a/templates/global_timeline.php b/templates/global_timeline.php
deleted file mode 100644
index f475656..0000000
--- a/templates/global_timeline.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php call_template('skeleton', ['pageTitle' => __('timeline.global')], function () {
- global $notes;
- call_template('alertbox', [
- 'variety' => 'info',
- 'message' => __('timeline.global.description')
- ]);
- foreach ($notes as $n) {
- call_template('note', ['note' => $n]);
- }
-}); \ No newline at end of file
diff --git a/templates/local_timeline.php b/templates/local_timeline.php
deleted file mode 100644
index 9d9027c..0000000
--- a/templates/local_timeline.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php call_template('skeleton', ['pageTitle' => __('timeline.local')], function () {
- global $notes;
- call_template('alertbox', [
- 'variety' => 'info',
- 'message' => __('timeline.local.description')
- ]);
- foreach ($notes as $n) {
- call_template('note', ['note' => $n]);
- }
-}); \ No newline at end of file
diff --git a/templates/reason_reshared.php b/templates/reason_reshared.php
new file mode 100644
index 0000000..2c1e4c5
--- /dev/null
+++ b/templates/reason_reshared.php
@@ -0,0 +1,10 @@
+<div class="timelineReason">
+ <span class="icon material-symbols-outlined">repeat</span>
+ <span>
+ <?php
+ $handle = $actor->getFullHandle();
+ $name = htmlspecialchars($actor->displayName);
+ printf(__('note.resharedBy'), "<a href=\"/@/$handle\">$name</a>");
+ ?>
+ </span>
+</div> \ No newline at end of file
diff --git a/templates/timeline.php b/templates/timeline.php
new file mode 100644
index 0000000..7ba2fbf
--- /dev/null
+++ b/templates/timeline.php
@@ -0,0 +1,11 @@
+<?php call_template('skeleton', ['pageTitle' => __("timeline.$kind")], function () {
+ global $items, $kind;
+ call_template('alertbox', [
+ 'variety' => 'info',
+ 'message' => __("timeline.$kind.description")
+ ]);
+ foreach ($items as $item) {
+ $item->getReason()?->renderAsHtml();
+ $item->getObject()->renderAsHtml();
+ }
+}); \ No newline at end of file