diff options
| -rw-r--r-- | Digitigrade/Model/StorageObject.php | 4 | ||||
| -rw-r--r-- | locale/en_GB.json | 8 | ||||
| -rw-r--r-- | routes/actor_profile_fragment.php | 32 | ||||
| -rw-r--r-- | static/misc.css | 5 | ||||
| -rw-r--r-- | static/profile.css | 14 | ||||
| -rw-r--r-- | templates/actor_profile.php | 10 | ||||
| -rw-r--r-- | templates/actor_profile_editable.php | 32 |
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 |
