diff options
| author | winter | 2025-03-15 18:53:29 +0000 |
|---|---|---|
| committer | winter | 2025-03-15 18:53:29 +0000 |
| commit | e0588a6113ff6b3c1000fa6c82629f81fb5e05b5 (patch) | |
| tree | ef6beb046a7dcc7542b88f0962d4e5e264895b7a /templates/note | |
| parent | 61b122e88cc6783b4d0cf5142a22002f8e558c60 (diff) | |
[refactor] move some templates to subdirs
Diffstat (limited to 'templates/note')
| -rw-r--r-- | templates/note/attachment.php | 17 | ||||
| -rw-r--r-- | templates/note/attachment_pill.php | 44 | ||||
| -rw-r--r-- | templates/note/link_preview.php | 23 | ||||
| -rw-r--r-- | templates/note/note.php | 176 | ||||
| -rw-r--r-- | templates/note/reply_button.php | 13 | ||||
| -rw-r--r-- | templates/note/reply_form.php | 60 | ||||
| -rw-r--r-- | templates/note/write_form.php | 49 |
7 files changed, 382 insertions, 0 deletions
diff --git a/templates/note/attachment.php b/templates/note/attachment.php new file mode 100644 index 0000000..c139a00 --- /dev/null +++ b/templates/note/attachment.php @@ -0,0 +1,17 @@ +<?php +$filename = basename($attachment->href); +if (!$attachment->isImage() && $attachment->description) { + $filename = $attachment->description; +} +?> +<div> + <?php if ($attachment->isImage()): ?> + <img src="<?= $attachment->href ?>" alt="<?= htmlspecialchars($attachment->description ?? '') ?>" + title="<?= htmlspecialchars($attachment->description ?? '') ?>"> + <?php else: ?> + <a class="file" href="<?= $attachment->href ?>" target="_blank" download="<?= htmlspecialchars($filename) ?>"> + <span class="material-symbols attach-file"></span> + <span><?= htmlspecialchars($filename) ?></span> + </a> + <?php endif; ?> +</div>
\ No newline at end of file diff --git a/templates/note/attachment_pill.php b/templates/note/attachment_pill.php new file mode 100644 index 0000000..6fff74d --- /dev/null +++ b/templates/note/attachment_pill.php @@ -0,0 +1,44 @@ +<?php +$type ??= 'add'; +?> + +<?php if ($type == 'add'): ?> + + <button id="writeNoteAttachmentPillAdder" type="button" class="inlinePill" type="button" + _="on click set f to #noteAttachmentFile then f.click()"> + <span class="pillIcon material-symbols add"></span> + <span class="pillText"><?= __('writeNote.attachments.add.action') ?></span> + </button> + +<?php elseif ($type == 'file'): ?> + + <button class="inlinePill" type="button" _="on click remove me"> + <span class="pillIcon material-symbols attach-file"></span> + <span class="pillText"><?= $name ?></span> + <input type="hidden" name="attachments[]" value="<?= $oid ?>"> + <input type="hidden" name="altTexts[]" value="<?= $name ?>"> + <span class="pillIcon material-symbols close-small"></span> + </button> + +<?php elseif ($type == 'image'): ?> + + <div class="inlinePill imageAttachmentPill"> + <div class="row pillInfo"> + <span class="pillIcon material-symbols attach-file"></span> + <span class="pillText"><?= $name ?></span> + </div> + <img src="<?= $href ?>"> + <input type="hidden" name="attachments[]" value="<?= $oid ?>"> + <div class="row"> + <label for="altText-<?= $oid ?>"><?= __('writeNote.attachments.altText.label') ?></label> + <textarea id="altText-<?= $oid ?>" name="altTexts[]"></textarea> + </div> + <div class="row"> + <button type="button" class="secondary" _="on click remove closest .inlinePill"> + <span class="material-symbols close-small"></span> + <?= __('writeNote.attachments.remove.action') ?> + </button> + </div> + </div> + +<?php endif; ?>
\ No newline at end of file diff --git a/templates/note/link_preview.php b/templates/note/link_preview.php new file mode 100644 index 0000000..1525642 --- /dev/null +++ b/templates/note/link_preview.php @@ -0,0 +1,23 @@ +<?php +$siteName = hostname_from_uri($preview->url); +if ($preview->siteName != $siteName) { + $siteName = htmlspecialchars($preview->siteName) . ' — ' . $siteName; +} +?> + +<a class="linkPreview style-<?= htmlspecialchars($preview->style) ?>" href="<?= htmlspecialchars($preview->url) ?>" + target="_blank" rel="noreferrer nofollow"> + + <?php if ($preview->style == 'text' || !isset($preview->imageUrl)): ?> + <div></div> + <?php else: ?> + <img src="<?= htmlspecialchars($preview->imageUrl) ?>"> + <?php endif; ?> + + <div class="info"> + <span class="siteName"><?= $siteName ?></span> + <span class="title"><?= htmlspecialchars($preview->title) ?></span> + <span class="description"><?= htmlspecialchars($preview->description) ?></span> + </div> + +</a>
\ No newline at end of file diff --git a/templates/note/note.php b/templates/note/note.php new file mode 100644 index 0000000..7cef342 --- /dev/null +++ b/templates/note/note.php @@ -0,0 +1,176 @@ +<?php +/** @var \Digitigrade\Model\Note $note */ + +use Digitigrade\Model\Actor; +use Digitigrade\Model\InteractionKind; +use Digitigrade\Model\NotePrivacyScope; +use Digitigrade\Model\UserAccount; +use Digitigrade\UserSettings; + +$MAX_PREVIEW_CARDS = 1; + +$user = UserAccount::findByCurrentSession(); +$prefs = $user ? new UserSettings($user) : null; +$interKinds = []; +$actionsDisabled = $user == null; +if ($user != null) { + $interactions = $note->getInteractionsWithAuthor($user->actor); + $interKinds = array_map(fn($inter) => $inter->kind, $interactions); +} +$pathToSelf = $note->getLocalUiHref(); +?> +<article class="note <?= ($selected ?? false) ? 'selected' : '' ?>"> + + <a href="/@/<?= $note->author->getFullHandle() ?>"> + <?php call_template('actor/avatar', ['actor' => $note->author]); ?> + </a> + + <div> + + <div class="infoLine"> + <a class="authorName" + href="/@/<?= htmlspecialchars($note->author->getFullHandle()) ?>"><?= htmlspecialchars($note->author->displayName) ?></a> + <span class="authorPronouns"> + <?php if (isset($note->author->pronouns) && trim($note->author->pronouns) != '') { + echo htmlspecialchars($note->author->pronouns); + } ?> + </span> + <a class="timestamp" href="<?= htmlspecialchars($pathToSelf) ?>"> + <?= format_datetime($note->created) + . (isset($note->modified) && $note->modified != $note->created + ? ' (' . format_datetime($note->modified) . ')' + : '' + ) ?> + </a> + <span class="privacyScope inlineIcon material-symbols <?= match ($note->privacy->scope) { + NotePrivacyScope::NONE => 'lock', + NotePrivacyScope::MUTUALS => 'group', + NotePrivacyScope::FOLLOWERS => 'groups', + NotePrivacyScope::PUBLIC => 'public' + } ?>" title="<?= match ($note->privacy->scope) { + NotePrivacyScope::NONE => __('note.privacy.scope.none'), + NotePrivacyScope::MUTUALS => __('note.privacy.scope.mutuals'), + NotePrivacyScope::FOLLOWERS => __('note.privacy.scope.followers'), + NotePrivacyScope::PUBLIC => __('note.privacy.scope.public') + } ?>"></span> + </div> + + <?php if (isset($note->inReplyTo)): ?> + <div class="replyInfo"> + <span class="material-symbols reply inlineIcon"></span> + <a + href="/@/<?= htmlspecialchars($note->inReplyTo->author->getFullHandle()) ?>/note/<?= $note->inReplyTo->id ?>"> + <?= sprintf(__('note.info.replyTo'), + '<b>' . htmlspecialchars($note->inReplyTo->author->displayName)) . '</b>' ?> + </a> + </div> + <?php elseif (count($note->mentions) > 0): ?> + <div class="replyInfo"> + <span class="material-symbols alternate-email inlineIcon"></span> + <span> + <?= sprintf(__('note.info.mentions'), implode(', ', array_map( + fn(Actor $m) => '<b><a href="/@/' . htmlspecialchars($m->getFullHandle()) . '">' . htmlspecialchars($m->displayName) . '</a></b>', + $note->mentions + ))) ?> + </span> + </div> + <?php endif; ?> + + <?php if (isset($note->summary)): ?> + <details> + <summary><?= htmlspecialchars($note->summary) ?></summary> + <?php endif; ?> + <div class="content" lang="<?= $note->language ?>"> + <?php if ($note->hasPage()): ?> + <?= __f('note.page.summaryMessage', htmlspecialchars($note->getPage()->title)) ?> + <?php else: ?> + <?= $note->getBestContentAsHtml() ?> + <?php endif; ?> + </div> + + <?php if (count($note->attachments) > 0): ?> + <div class="attachments"> + <?php foreach ($note->attachments as $attachment) { + call_template('note/attachment', ['attachment' => $attachment]); + } ?> + </div> + <?php endif; ?> + + <?php + $i = 0; + foreach ($note->getLinkPreviews() as $preview) { + call_template('note/link_preview', ['preview' => $preview]); + if (++$i > $MAX_PREVIEW_CARDS) + break; + } + ?> + + <?php if (isset($note->summary)): ?> + </details> + <?php endif; ?> + + <?php if ($note->hasPage()): ?> + <a class="button secondary minWidth" href="<?= $note->getLocalUiHref(true) ?>"> + <span class="material-symbols article-outline"></span> + <span><?= __('note.openPage') ?></span> + </a> + <?php endif; ?> + + <div class="buttons"> + <?php foreach (['like', 'dislike', 'reshare'] as $action) { + $kind = InteractionKind::from($action); + call_template('interaction_button', [ + 'action' => $action, + 'note' => $note, + 'active' => in_array($kind, $interKinds), + 'count' => $note->countInteractionsOfKind($kind), + 'disabled' => $actionsDisabled + ]); + } + call_template('note/reply_button', [ + 'note' => $note, + 'count' => $note->countReplies(), + 'disabled' => $actionsDisabled + ]); + ?> + <div class="right"> + <?php call_template('menu_button', [], function () { + global $note; + $user = UserAccount::findByCurrentSession(); + ?> + <li> + <button data-share-url="<?= htmlspecialchars($note->uri) ?>" _=" + on click + set url to my @data-share-url + js(url) + navigator.share({ url: url }) + end" type="button"> + <span class="material-symbols share"></span> + <span><?= __('action.share') ?></span> + </button> + </li> + <?php if ($note->author->isLocal && ($note->author == $user?->actor || $user?->isAdmin)): ?> + <li> + <button hx-delete="/fragment/note/<?= $note->id ?>" + hx-confirm="<?= __('note.action.delete.confirmation') ?>" hx-target="closest .note" + hx-swap="delete"> + <span class="material-symbols delete-outline"></span> + <span><?= __('note.action.delete') ?></span> + </button> + </li> + <?php endif; ?> + <?php if (!$note->author->isLocal): ?> + <li> + <a target="_blank" href="<?= htmlspecialchars($note->uri) ?>" rel="nofollow noreferrer"> + <span class="material-symbols open-in-new"></span> + <span><?= __('note.openRemote') ?></span> + </a> + </li> + <?php endif; ?> + <?php }); ?> + </div> + </div> + + <?php call_template('note/reply_form', ['note' => $note]); ?> + </div> +</article>
\ No newline at end of file diff --git a/templates/note/reply_button.php b/templates/note/reply_button.php new file mode 100644 index 0000000..dadc121 --- /dev/null +++ b/templates/note/reply_button.php @@ -0,0 +1,13 @@ +<?php +use Digitigrade\Model\Note; + +/** @var Note $note */ + +$icon = $note->inReplyTo == null ? 'reply' : 'reply-all'; +$id = "interactButton-reply-$note->id"; +?> +<div class="interactButtonContainer reply"> + <button title="<?= __('note.action.reply') ?>" class="material-symbols <?= $icon ?>" id="<?= $id ?>" <?= ($disabled ?? false) ? 'disabled' : '' ?> + _="on click send toggle to [me, next .noteReplyForm] on toggle toggle .active on closest <div/>"></button> + <label for="<?= $id ?>"><?= $count ?? '' ?></label> +</div>
\ No newline at end of file diff --git a/templates/note/reply_form.php b/templates/note/reply_form.php new file mode 100644 index 0000000..2d8cfc3 --- /dev/null +++ b/templates/note/reply_form.php @@ -0,0 +1,60 @@ +<?php +use Digitigrade\Model\NotePrivacyScope; +use Digitigrade\Model\UserAccount; + +$user = UserAccount::findByCurrentSession(); + +$summaryText = ''; +if ($note->summary) { + if (strtolower(substr($note->summary, 0, 3)) == 're:') { + $summaryText = $note->summary; + } else { + $summaryText = "re: $note->summary"; + } +} + +$scopeOptions = match ($note->privacy->scope) { + NotePrivacyScope::NONE => ['none'], + NotePrivacyScope::MUTUALS => ['none', 'mutuals'], + NotePrivacyScope::FOLLOWERS => ['none', 'mutuals', 'followers'], + default => ['none', 'mutuals', 'followers', 'public'] +}; +$selectedScope = $note->privacy->scope->value; +?> +<form class="noteReplyForm" id="replyForm-<?= $note->id ?>" + _="on submit send toggle to previous <button.reply/> then wait 1s then trigger update on #liveTimelineUpdater on toggle or submit toggle @hidden" + hidden hx-post="/fragment/note/<?= $note->id ?>/reply" hx-disabled-elt="find button" hx-swap="outerHTML"> + <div class="row summaryAndContent"> + <label for="replyContent-<?= $note->id ?>"><?= __('writeReply.label') ?></label> + <input type="text" id="replySummary-<?= $note->id ?>" name="summary" autocomplete="off" + value="<?= $summaryText ?>" placeholder="<?= __('writeNote.summary.label') ?>"> + <textarea id="replyContent-<?= $note->id ?>" name="content" required autocomplete="off" + placeholder="<?= __('writeReply.placeholder') ?>"></textarea> + <?php call_template('formatting_hint'); ?> + </div> + <details> + <summary><?= __('writeNote.moreOptions') ?></summary> + <div class="row"> + <label class="standalone"><?= __('writeNote.mentions.label') ?></label> + <?php foreach (array_merge($note->mentions, [$note->author]) as $m) { + if ($m != $user?->actor) { + call_template('user_mention_pill', ['type' => 'user', 'actor' => $m]); + } + } ?> + <?php call_template('user_mention_pill'); ?> + </div> + <div class="row"> + <label for="replyScope-<?= $note->id ?>"><?= __('writeNote.scope.label') ?></label> + <select name="scope" id="replyScope-<?= $note->id ?>" required autocomplete="off"> + <?php foreach ($scopeOptions as $s): ?> + <option value="<?= $s ?>" <?= $selectedScope == $s ? 'selected' : '' ?>> + <?= __("note.privacy.scope.$s") ?> + </option> + <?php endforeach; ?> + </select> + </div> + </details> + <div class="row"> + <button class="primary"><?= __('writeReply.action') ?></button> + </div> +</form>
\ No newline at end of file diff --git a/templates/note/write_form.php b/templates/note/write_form.php new file mode 100644 index 0000000..add00ec --- /dev/null +++ b/templates/note/write_form.php @@ -0,0 +1,49 @@ +<form hidden id="writeNoteAttachmentForm" action="/todo" hx-post="/fragment/note/attachmentPill" hx-swap="beforebegin" + hx-target="#writeNoteAttachmentPillAdder" enctype="multipart/form-data"> + <input hidden type="file" name="file" id="noteAttachmentFile" _="on change send submit to closest <form/>"> +</form> + +<form id="writeNoteForm" action="/todo" method="post" hx-post="/fragment/note" hx-swap="outerHTML" + hx-disabled-elt="find button" _="on submit wait 1s then trigger update on #liveTimelineUpdater" + <?= ($_SERVER['REQUEST_METHOD'] == 'GET') ? 'hx-preserve' : '' ?>> + <div class="row miniProfile"> + <a href="/@/<?= $actor->handle ?>"> + <?php call_template('actor/avatar', ['actor' => $actor]); ?> + </a> + <div class="basicInfo"> + <a class="displayName" href="/@/<?= $actor->handle ?>"><?= htmlspecialchars($actor->displayName) ?></a> + <div class="handle">@<?= $actor->handle ?></div> + </div> + </div> + <div class="row summaryAndContent"> + <label for="noteContent"><?= __('writeNote.label') ?></label> + <input type="text" id="noteSummary" name="summary" autocomplete="off" + placeholder="<?= __('writeNote.summary.label') ?>"> + <textarea name="content" id="noteContent" required autocomplete="off" + placeholder="<?= __('writeNote.placeholder') ?>"></textarea> + <?php call_template('formatting_hint'); ?> + </div> + <details> + <summary><?= __('writeNote.moreOptions') ?></summary> + <div class="row"> + <label class="standalone"><?= __('writeNote.mentions.label') ?></label> + <?php call_template('user_mention_pill'); ?> + </div> + <div class="row"> + <label class="standalone"><?= __('writeNote.attachments.label') ?></label> + <?php call_template('note/attachment_pill'); ?> + </div> + <div class="row"> + <label for="noteScope"><?= __('writeNote.scope.label') ?></label> + <select name="scope" id="noteScope" required autocomplete="off"> + <option value="none"><?= __('note.privacy.scope.none') ?></option> + <option value="mutuals"><?= __('note.privacy.scope.mutuals') ?></option> + <option value="followers"><?= __('note.privacy.scope.followers') ?></option> + <option value="public" selected><?= __('note.privacy.scope.public') ?></option> + </select> + </div> + </details> + <div class="row"> + <button class="primary"><?= __('writeNote.action') ?></button> + </div> +</form>
\ No newline at end of file |
