aboutsummaryrefslogtreecommitdiffhomepage
path: root/templates/note
diff options
context:
space:
mode:
Diffstat (limited to 'templates/note')
-rw-r--r--templates/note/attachment.php17
-rw-r--r--templates/note/attachment_pill.php44
-rw-r--r--templates/note/link_preview.php23
-rw-r--r--templates/note/note.php176
-rw-r--r--templates/note/reply_button.php13
-rw-r--r--templates/note/reply_form.php60
-rw-r--r--templates/note/write_form.php49
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) . ' &mdash; ' . $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