aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Digitigrade/Model/StorageObject.php4
-rw-r--r--locale/en_GB.json8
-rw-r--r--routes/actor_profile_fragment.php32
-rw-r--r--static/misc.css5
-rw-r--r--static/profile.css14
-rw-r--r--templates/actor_profile.php10
-rw-r--r--templates/actor_profile_editable.php32
7 files changed, 101 insertions, 4 deletions
diff --git a/Digitigrade/Model/StorageObject.php b/Digitigrade/Model/StorageObject.php
index 333b5b1..28c77aa 100644
--- a/Digitigrade/Model/StorageObject.php
+++ b/Digitigrade/Model/StorageObject.php
@@ -40,6 +40,10 @@ class StorageObject extends Model {
return $obj;
}
+ public static function createFromUpload(string $provider, array $uploadInfo): self {
+ return self::createFromFile($provider, $uploadInfo['tmp_name'], $uploadInfo['name'], true);
+ }
+
public static function findByOid(string $oid): ?self {
return self::findWhere('oid = ?', [$oid]);
}
diff --git a/locale/en_GB.json b/locale/en_GB.json
index 06ce2a2..5706543 100644
--- a/locale/en_GB.json
+++ b/locale/en_GB.json
@@ -8,6 +8,9 @@
"user.profile.unfollow.action": "Unfollow",
"user.profile.followsYou": "Follows you",
"user.profile.pendingFollowsYou": "Wants to follow you",
+ "user.profile.displayName": "Display name",
+ "user.profile.changeAvatar": "Choose a new avatar image",
+ "user.profile.bio": "Bio",
"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.",
@@ -51,5 +54,8 @@
"followRequests.accept.action": "Accept",
"followRequests.accepted": "Accepted!",
"followRequests.reject.action": "Reject",
- "followRequests.rejected": "Rejected!"
+ "followRequests.rejected": "Rejected!",
+ "form.edit": "Edit",
+ "form.saveChanges": "Save",
+ "form.discardChanges": "Cancel"
}
diff --git a/routes/actor_profile_fragment.php b/routes/actor_profile_fragment.php
index a3be976..0a07924 100644
--- a/routes/actor_profile_fragment.php
+++ b/routes/actor_profile_fragment.php
@@ -1,8 +1,14 @@
<?php
use Digitigrade\Model\Actor;
+use Digitigrade\Model\StorageObject;
use Digitigrade\Model\UserAccount;
use Digitigrade\Router;
+use Digitigrade\StorageProvider\FilesystemStorage;
+
+Router::getInstance()->mount('/fragment/actor/:id', function (array $args) {
+ render_template('actor_profile', ['actor' => Actor::find($args['id'])]);
+});
Router::getInstance()->mount('/fragment/actor/:id/followButton', function (array $args) {
$user = UserAccount::requireByCurrentSession();
@@ -38,4 +44,30 @@ Router::getInstance()->mount('/fragment/actor/:id/rejectPending', function (arra
$user->actor->rejectPendingFollowFrom($actor);
render_template('actor_profile_pending_follow', ['response' => 'rejected']);
+});
+
+Router::getInstance()->mount('/fragment/profile', function (array $args) {
+ $user = UserAccount::requireByCurrentSession();
+ $actor = $user->actor;
+ if (isset($_POST['displayName'])) {
+ // save changes and put the normal profile back
+ $actor->displayName = trim($_POST['displayName']);
+ if (strlen($actor->displayName) <= 0) {
+ $actor->displayName = $actor->handle;
+ }
+ $actor->bio = trim($_POST['bio'] ?? '');
+
+ if (isset($_FILES['avatar'])) {
+ // TODO: allow using other backends
+ $obj = StorageObject::createFromUpload(FilesystemStorage::class, $_FILES['avatar']);
+ $actor->avatar = $obj->getUrl();
+ }
+
+ $actor->save();
+ $actor->publish();
+
+ render_template('actor_profile', ['actor' => $actor]);
+ } else {
+ render_template('actor_profile_editable', ['actor' => $actor]);
+ }
}); \ No newline at end of file
diff --git a/static/misc.css b/static/misc.css
index 4271cbe..e0d3851 100644
--- a/static/misc.css
+++ b/static/misc.css
@@ -87,6 +87,11 @@ span.unreadIndicator {
}
}
+pre {
+ font-family: inherit;
+ font-size: inherit;
+}
+
[hidden] {
display: none;
}
diff --git a/static/profile.css b/static/profile.css
index 0814cbb..3c4c7cc 100644
--- a/static/profile.css
+++ b/static/profile.css
@@ -24,7 +24,7 @@
}
}
- .followInfo {
+ .profileActions {
display: grid;
justify-content: end;
align-items: baseline;
@@ -39,3 +39,15 @@
}
}
}
+
+form.fullProfile {
+ max-width: unset;
+ input[type="text"],
+ textarea {
+ max-width: unset;
+ width: 100%;
+ }
+ textarea {
+ height: 16lh;
+ }
+}
diff --git a/templates/actor_profile.php b/templates/actor_profile.php
index 0dc3ccd..46ee273 100644
--- a/templates/actor_profile.php
+++ b/templates/actor_profile.php
@@ -25,13 +25,13 @@ $user = UserAccount::findByCurrentSession();
</div>
</div>
<?php if (isset($actor->bio) && $actor->bio != '') { ?>
- <p class="bio"><?= $actor->bio ?></p>
+ <pre class="bio"><?= $actor->bio ?></pre>
<?php } else {
call_template('placeholder_text', ['message' => __('user.profile.bio.placeholder')]);
} ?>
<?php if (isset($user) && $actor->id != $user->actor->id): ?>
- <div class="followInfo">
+ <div class="profileActions">
<?php if ($actor->followsPending($user->actor))
call_template('actor_profile_pending_follow', ['subject' => $actor, 'object' => $user->actor]); ?>
<?php if ($actor->follows($user->actor)): ?>
@@ -39,5 +39,11 @@ $user = UserAccount::findByCurrentSession();
<?php endif; ?>
<?php call_template('actor_profile_follow_button', ['actor' => $actor, 'user' => $user]); ?>
</div>
+ <?php elseif ($actor->id == $user->actor->id): ?>
+ <div class="profileActions">
+ <button class="primary" hx-get="/fragment/profile" hx-target="closest .fullProfile" hx-swap="outerHTML">
+ <?= __('form.edit') ?>
+ </button>
+ </div>
<?php endif; ?>
</div> \ No newline at end of file
diff --git a/templates/actor_profile_editable.php b/templates/actor_profile_editable.php
new file mode 100644
index 0000000..2c0ef5c
--- /dev/null
+++ b/templates/actor_profile_editable.php
@@ -0,0 +1,32 @@
+<form class="fullProfile" hx-post="/fragment/profile" hx-swap="outerHTML" enctype="multipart/form-data">
+ <div class="basicInfo">
+ <?php call_template('actor_avatar', ['actor' => $actor]); ?>
+ <div>
+ <div class="row">
+ <label for="profileDisplayName"><?= __('user.profile.displayName') ?></label>
+ <input id="profileDisplayName" name="displayName" type="text" required
+ value="<?= $actor->displayName ?>">
+ </div>
+ <div class="handle">@<?= $actor->getFullHandle() ?></div>
+ <div class="joinedDate"><?= sprintf(__('user.profile.createdAt'), $actor->created->format('Y-m-d')) ?></div>
+ </div>
+ </div>
+
+ <div class="row">
+ <label for="profileAvatar"><?= __('user.profile.changeAvatar') ?></label>
+ <input type="file" id="profileAvatar" name="avatar" accept="image/*">
+ </div>
+
+ <div class="row">
+ <label for="profileBio"><?= __('user.profile.bio') ?></label>
+ <textarea id="profileBio" name="bio"><?= htmlspecialchars($actor->bio ?? '') ?></textarea>
+ </div>
+
+ <div class="profileActions">
+ <button type="button" class="secondary" hx-get="/fragment/actor/<?= $actor->id ?>"
+ hx-target="closest .fullProfile" hx-swap="outerHTML">
+ <?= __('form.discardChanges') ?>
+ </button>
+ <button class="primary"><?= __('form.saveChanges') ?></button>
+ </div>
+</form> \ No newline at end of file