aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorwinter2025-01-14 20:44:49 +0000
committerwinter2025-01-14 20:44:49 +0000
commit4d7f8450d9a842e2f69754ccaa4202710328dcb7 (patch)
tree60e90ff7ebbf069e04faf665d68861970eaee000
parent0f427b4b6f75134b2c25b7e5f8a515be2cec97f5 (diff)
add (un)follow button to profile
also other stuff i forgot what exactly
-rw-r--r--Digitigrade/Model/Actor.php18
-rw-r--r--Digitigrade/Model/Note.php4
-rw-r--r--locale/en_GB.json7
-rw-r--r--routes/actor_profile_fragment.php23
-rw-r--r--routes/webfinger.php2
-rw-r--r--static/form.css13
-rw-r--r--static/misc.css23
-rw-r--r--static/profile.css8
-rw-r--r--templates/actor_profile.php14
-rw-r--r--templates/actor_profile_follow_button.php20
-rw-r--r--templates/actor_profile_page.php13
-rw-r--r--templates/login_form.php2
-rw-r--r--templates/placeholder_text.php13
-rw-r--r--templates/reply_button.php6
-rw-r--r--templates/reply_form.php2
-rw-r--r--templates/write_note_form.php2
16 files changed, 145 insertions, 25 deletions
diff --git a/Digitigrade/Model/Actor.php b/Digitigrade/Model/Actor.php
index 46c6f9a..c8cc25e 100644
--- a/Digitigrade/Model/Actor.php
+++ b/Digitigrade/Model/Actor.php
@@ -162,6 +162,24 @@ class Actor extends PushableModel implements RpcReceiver {
}
/**
+ * @param Actor $target the other actor
+ * @return boolean whether this actor is following the target actor
+ */
+ public function follows(Actor $target): bool {
+ $relation = FollowRelation::findByActors($this, $target);
+ return $relation != null && $relation->status == FollowRelationStatus::ACTIVE;
+ }
+
+ /**
+ * @param Actor $target the other actor
+ * @return boolean whether this actor has a pending follow request to the target actor
+ */
+ public function followsPending(Actor $target): bool {
+ $relation = FollowRelation::findByActors($this, $target);
+ return $relation != null && $relation->status == FollowRelationStatus::PENDING;
+ }
+
+ /**
* @return Actor[] a list of actors that follow this actor (does not include pending follows)
*/
public function findFollowers(): array {
diff --git a/Digitigrade/Model/Note.php b/Digitigrade/Model/Note.php
index cf23e80..0aa29bd 100644
--- a/Digitigrade/Model/Note.php
+++ b/Digitigrade/Model/Note.php
@@ -111,6 +111,10 @@ class Note extends PushableModel implements TimelineIncludeable {
return self::findAllWhere($where, [$author->id], $limit, $offset, 'created DESC');
}
+ public static function countWithAuthor(Actor $author): int {
+ return self::countWhere('author = ? AND deleted = false', [$author->id]);
+ }
+
public static function findAllInThread(Note $threadApex, ?int $limit = null, int $offset = 0): array {
if (isset($limit))
$limit--; // to account for adding the note afterwards
diff --git a/locale/en_GB.json b/locale/en_GB.json
index da09f77..d7860db 100644
--- a/locale/en_GB.json
+++ b/locale/en_GB.json
@@ -2,6 +2,11 @@
"user.profile.createdAt": "Joined on %s",
"user.profile.openRemote": "Open remote profile page",
"user.profile.bio.placeholder": "This user hasn't written a bio yet.",
+ "user.profile.follow.action": "Follow",
+ "user.profile.follow.pending": "Request pending",
+ "user.profile.requestToFollow.action": "Request to follow",
+ "user.profile.unfollow.action": "Unfollow",
+ "user.profile.followsYou": "Follows you",
"user.notes.placeholder": "This user hasn't posted anything yet.",
"timeline.global": "Global timeline",
"timeline.global.description": "This timeline shows all known public indexable notes.",
@@ -13,6 +18,8 @@
"timeline.local": "Local timeline",
"timeline.local.description": "This timeline shows all public indexable notes posted by users on this server.",
"placeholder": "There's nothing here.",
+ "placeholder.moreItems": "…and %d more",
+ "placeholder.noMoreItems": "No more items.",
"timeline.global.shortName": "Global",
"timeline.local.shortName": "Local",
"digitigrade": "Digitigrade",
diff --git a/routes/actor_profile_fragment.php b/routes/actor_profile_fragment.php
new file mode 100644
index 0000000..9b864d9
--- /dev/null
+++ b/routes/actor_profile_fragment.php
@@ -0,0 +1,23 @@
+<?php
+
+use Digitigrade\Model\Actor;
+use Digitigrade\Model\UserAccount;
+use Digitigrade\Router;
+
+Router::getInstance()->mount('/fragment/actor/:id/followButton', function (array $args) {
+ $user = UserAccount::requireByCurrentSession();
+ $actor = Actor::find($args['id']);
+
+ if ($user->actor->follows($actor)) {
+ // already following, therefore unfollow
+ $user->actor->unfollow($actor);
+ } elseif ($user->actor->followsPending($actor)) {
+ // can't actually do anything at this point
+ // TODO: cancel request? is this possible?? do i need to modify the protocol
+ } else {
+ // follow
+ $user->actor->follow($actor);
+ }
+
+ render_template('actor_profile_follow_button', ['user' => $user, 'actor' => $actor]);
+}); \ No newline at end of file
diff --git a/routes/webfinger.php b/routes/webfinger.php
index 1b719cd..695f865 100644
--- a/routes/webfinger.php
+++ b/routes/webfinger.php
@@ -29,7 +29,7 @@ Router::getInstance()->mount('/.well-known/webfinger', function (array $args) {
'links' => [
[
'rel' => ActorWebfinger::REL_URI,
- 'href' => path_to_uri("/actor/$actor->handle"),
+ 'href' => path_to_uri("/actor/$actor->id"),
'type' => 'application/json'
]
]
diff --git a/static/form.css b/static/form.css
index 91f755b..cb86003 100644
--- a/static/form.css
+++ b/static/form.css
@@ -31,20 +31,7 @@ form {
resize: vertical;
}
button {
- background: #3b005e;
- color: #fff;
- font-family: inherit;
- font-size: inherit;
- border-radius: 8px;
- border: none;
width: 100%;
- padding: 8px;
- box-sizing: border-box;
- cursor: pointer;
- &:disabled {
- opacity: 80%;
- cursor: default;
- }
}
.error {
background: #f004;
diff --git a/static/misc.css b/static/misc.css
index 20d8da2..2da35b6 100644
--- a/static/misc.css
+++ b/static/misc.css
@@ -13,6 +13,29 @@
}
}
+button.primary,
+button.secondary {
+ font-family: inherit;
+ font-size: inherit;
+ border-radius: 8px;
+ border: none;
+ padding: 8px 16px;
+ box-sizing: border-box;
+ cursor: pointer;
+ &:disabled {
+ opacity: 80%;
+ cursor: default;
+ }
+ &.primary {
+ background: #3b005e;
+ color: #fff;
+ }
+ &.secondary {
+ background: #dfd4e7;
+ color: #202;
+ }
+}
+
button.icon {
border: none;
background: none;
diff --git a/static/profile.css b/static/profile.css
index 028c69c..f3f8ead 100644
--- a/static/profile.css
+++ b/static/profile.css
@@ -23,4 +23,12 @@
font-size: 10pt;
}
}
+
+ .followInfo {
+ display: grid;
+ justify-content: right;
+ align-items: baseline;
+ grid-auto-flow: column;
+ gap: 16px;
+ }
}
diff --git a/templates/actor_profile.php b/templates/actor_profile.php
index 8a97bd8..c480d21 100644
--- a/templates/actor_profile.php
+++ b/templates/actor_profile.php
@@ -1,3 +1,8 @@
+<?php
+use Digitigrade\Model\UserAccount;
+
+$user = UserAccount::findByCurrentSession();
+?>
<div class="fullProfile">
<div class="basicInfo">
<?php call_template('actor_avatar', ['actor' => $actor]); ?>
@@ -18,4 +23,13 @@
<?php } else {
call_template('placeholder_text', ['message' => __('user.profile.bio.placeholder')]);
} ?>
+
+ <?php if (isset($user) && $actor->id != $user->actor->id): ?>
+ <div class="followInfo">
+ <?php if ($actor->follows($user->actor)): ?>
+ <?= __('user.profile.followsYou') ?>
+ <?php endif; ?>
+ <?php call_template('actor_profile_follow_button', ['actor' => $actor, 'user' => $user]); ?>
+ </div>
+ <?php endif; ?>
</div> \ No newline at end of file
diff --git a/templates/actor_profile_follow_button.php b/templates/actor_profile_follow_button.php
new file mode 100644
index 0000000..e2b314d
--- /dev/null
+++ b/templates/actor_profile_follow_button.php
@@ -0,0 +1,20 @@
+<?php
+use Digitigrade\Model\FollowRelation;
+use Digitigrade\Model\FollowRelationStatus;
+
+$relation = FollowRelation::findByActors($user->actor, $actor);
+$state = match ($relation?->status) {
+ null => $actor->requestToFollow ? 'request' : 'normal',
+ FollowRelationStatus::PENDING => 'pending',
+ FollowRelationStatus::ACTIVE => 'active'
+};
+?>
+<button class="<?= $state == 'active' ? 'secondary' : 'primary' ?>"
+ hx-post="/fragment/actor/<?= $actor->id ?>/followButton" hx-swap="outerHTML" <?= $state == 'pending' ? 'disabled' : '' ?> hx-disabled-elt="this">
+ <?= __(match ($state) {
+ 'active' => 'user.profile.unfollow.action',
+ 'pending' => 'user.profile.follow.pending',
+ 'request' => 'user.profile.requestToFollow.action',
+ 'normal' => 'user.profile.follow.action'
+ }) ?>
+</button> \ No newline at end of file
diff --git a/templates/actor_profile_page.php b/templates/actor_profile_page.php
index e7d28b7..649069d 100644
--- a/templates/actor_profile_page.php
+++ b/templates/actor_profile_page.php
@@ -9,11 +9,16 @@ call_template('skeleton', [
global $actor;
call_template('actor_profile', ['actor' => $actor]);
echo '<hr>';
- $notes = Note::findAllWithAuthor($actor, 50);
+ $notes = Note::findAllWithAuthor($actor, 20);
if (count($notes) == 0) {
call_template('placeholder_text', ['message' => __('user.notes.placeholder')]);
- }
- foreach ($notes as $n) {
- call_template('note', ['note' => $n]);
+ } else {
+ foreach ($notes as $n) {
+ call_template('note', ['note' => $n]);
+ }
+ $countNotShown = Note::countWithAuthor($actor) - count($notes);
+ if ($countNotShown >= 0) {
+ call_template('placeholder_text', ['count' => $countNotShown]);
+ }
}
}); \ No newline at end of file
diff --git a/templates/login_form.php b/templates/login_form.php
index 2c32164..74bb823 100644
--- a/templates/login_form.php
+++ b/templates/login_form.php
@@ -12,7 +12,7 @@
<label for="rememberMeInput"><?= __('login.rememberMe') ?></label>
</div>
<div class="row">
- <button><?= __('login.action') ?></button>
+ <button class="primary"><?= __('login.action') ?></button>
</div>
<?php if ($failed ?? false): ?>
<p class="error"><?= __('login.failed') ?></p>
diff --git a/templates/placeholder_text.php b/templates/placeholder_text.php
index f529de9..2fc3d3d 100644
--- a/templates/placeholder_text.php
+++ b/templates/placeholder_text.php
@@ -1 +1,12 @@
-<p class="placeholder"><?= $message ?? __('placeholder') ?></p> \ No newline at end of file
+<?php
+$text = __('placeholder');
+if (isset($message)) {
+ $text = $message;
+} elseif (isset($count)) {
+ if ($count == 0)
+ $text = __('placeholder.noMoreItems');
+ else
+ $text = sprintf(__('placeholder.moreItems'), $count);
+}
+?>
+<p class="placeholder"><?= $text ?></p> \ No newline at end of file
diff --git a/templates/reply_button.php b/templates/reply_button.php
index aeaeabd..4162d23 100644
--- a/templates/reply_button.php
+++ b/templates/reply_button.php
@@ -1,9 +1,9 @@
<?php
use Digitigrade\Model\Note;
-$icon = count(Note::findAllInThread($note, 2)) > 1 ? 'reply_all' : 'reply';
-$id = "interactButton-reply-$note->id"
- ?>
+$icon = $note->countReplies() > 0 ? 'reply_all' : 'reply';
+$id = "interactButton-reply-$note->id";
+?>
<div class="interactButtonContainer reply">
<button title="<?= __('note.action.reply') ?>" class="icon material-symbols-outlined reply" id="<?= $id ?>"
_="on click send toggle to [me, next .noteReplyForm] on toggle toggle .active on closest <div/>"><?= $icon ?></button>
diff --git a/templates/reply_form.php b/templates/reply_form.php
index 84d8121..f96eb5a 100644
--- a/templates/reply_form.php
+++ b/templates/reply_form.php
@@ -6,6 +6,6 @@
<textarea id="replyContent-<?= $note->id ?>" name="content" required></textarea>
</div>
<div class="row">
- <button><?= __('writeReply.action') ?></button>
+ <button class="primary"><?= __('writeReply.action') ?></button>
</div>
</form> \ No newline at end of file
diff --git a/templates/write_note_form.php b/templates/write_note_form.php
index 38a9993..9b7c61c 100644
--- a/templates/write_note_form.php
+++ b/templates/write_note_form.php
@@ -4,6 +4,6 @@
<textarea name="content" id="noteContent" required></textarea>
</div>
<div class="row">
- <button><?= __('writeNote.action') ?></button>
+ <button class="primary"><?= __('writeNote.action') ?></button>
</div>
</form> \ No newline at end of file