aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorwinter2025-02-07 18:26:05 +0000
committerwinter2025-02-07 18:26:05 +0000
commitd18ec187847893f94418b29bc6a087d6b2dd9a2d (patch)
tree8ea4451cb1f62f8041eae71ca60ec2b64fafe8b2
parent43f0b9f41a582925838b2ba686110b2f07576be3 (diff)
localise note timestamps and stuff
-rw-r--r--Digitigrade/Model/NoteAttachment.php31
-rw-r--r--locale/en_GB.json3
-rw-r--r--locale/en_US.json3
-rw-r--r--misc/format_datetime.php14
-rw-r--r--routes/note_fragment.php2
-rw-r--r--routes/preferences.php1
-rw-r--r--settings.user.ini6
-rw-r--r--static/form.css3
-rw-r--r--static/icons.css3
-rw-r--r--templates/note.php14
-rw-r--r--templates/preferences_form.php31
-rw-r--r--templates/settings_field.php8
12 files changed, 94 insertions, 25 deletions
diff --git a/Digitigrade/Model/NoteAttachment.php b/Digitigrade/Model/NoteAttachment.php
index 1ca0064..8c6fb1f 100644
--- a/Digitigrade/Model/NoteAttachment.php
+++ b/Digitigrade/Model/NoteAttachment.php
@@ -29,4 +29,35 @@ class NoteAttachment extends Model {
}
return null;
}
+
+ /**
+ * @return bool if this attachment is a supported image file
+ */
+ public function isImage(): bool {
+ return in_array($this->type, [
+ 'image/apng',
+ 'image/avif',
+ 'image/gif',
+ 'image/jpeg',
+ 'image/png',
+ 'image/svg+xml',
+ 'image/webp',
+ ]);
+ }
+
+ /**
+ * @return bool if this attachment is a supported audio file
+ */
+ public function isAudio(): bool {
+ // TODO: be a bit more careful about what counts as supported
+ return str_starts_with($this->type, 'audio/');
+ }
+
+ /**
+ * @return bool if this attachment is a supported video file
+ */
+ public function isVideo(): bool {
+ // TODO: be a bit more careful about what counts as supported
+ return str_starts_with($this->type, 'video/');
+ }
} \ No newline at end of file
diff --git a/locale/en_GB.json b/locale/en_GB.json
index 57c0fa1..b0c6598 100644
--- a/locale/en_GB.json
+++ b/locale/en_GB.json
@@ -95,6 +95,9 @@
"preferences.requestToFollow.description": "Lets you approve other users before they are able to follow you",
"preferences.smallIndicators.name": "Reduced unread indicators",
"preferences.smallIndicators.description": "Hides the number of unread items and just shows a small dot instead",
+ "preferences.timezone.name": "Time zone",
+ "preferences.timezone.description": "Preferred time zone, which all dates and times are shown in",
+ "preferences.timezone.auto": "Set automatically",
"changePassword.heading": "Change password",
"changePassword.currentPassword": "Current password",
"changePassword.newPassword": "New password",
diff --git a/locale/en_US.json b/locale/en_US.json
new file mode 100644
index 0000000..0c1c01b
--- /dev/null
+++ b/locale/en_US.json
@@ -0,0 +1,3 @@
+{
+ "FALLBACK": "en_GB"
+}
diff --git a/misc/format_datetime.php b/misc/format_datetime.php
new file mode 100644
index 0000000..1a4db52
--- /dev/null
+++ b/misc/format_datetime.php
@@ -0,0 +1,14 @@
+<?php
+
+function format_datetime(
+ DateTimeInterface $dt,
+ DateTimeZone|IntlTimeZone|string|null $tz = null
+): string {
+ static $formatter = new IntlDateFormatter(
+ get_ui_language(),
+ IntlDateFormatter::RELATIVE_MEDIUM,
+ IntlDateFormatter::SHORT
+ );
+ $formatter->setTimeZone($tz ?? $dt->getTimezone());
+ return $formatter->format($dt);
+} \ No newline at end of file
diff --git a/routes/note_fragment.php b/routes/note_fragment.php
index be1c33f..916fc8d 100644
--- a/routes/note_fragment.php
+++ b/routes/note_fragment.php
@@ -101,7 +101,7 @@ Router::getInstance()->mount('/fragment/note', function (array $args) {
$note = Note::create(
$author->actor,
trim($_POST['content']),
- 'unk',
+ get_ui_language(), // TODO: actual language selector/detection
$summary,
NotePrivacyScope::from($_POST['scope'])
);
diff --git a/routes/preferences.php b/routes/preferences.php
index cb07bd1..243f90d 100644
--- a/routes/preferences.php
+++ b/routes/preferences.php
@@ -18,6 +18,7 @@ Router::getInstance()->mount('/fragment/preferences', function (array $args) {
$user->actor->requestToFollow = $_POST['requestToFollow'] == 'true';
$user->actor->save();
$settings->set('interface.smallUnreadIndicators', $_POST['smallIndicators']);
+ $settings->set('locale.timezone', $_POST['timezone']);
$saved = true;
}
diff --git a/settings.user.ini b/settings.user.ini
index b82e425..7baa633 100644
--- a/settings.user.ini
+++ b/settings.user.ini
@@ -3,4 +3,8 @@
[interface.smallUnreadIndicators]
default = "false"
-type = "boolean" \ No newline at end of file
+type = "boolean"
+
+[locale.timezone]
+default = "Europe/London"
+type = "timezone" \ No newline at end of file
diff --git a/static/form.css b/static/form.css
index 859ba25..b82240e 100644
--- a/static/form.css
+++ b/static/form.css
@@ -119,6 +119,9 @@ form:not(.nopanel) {
&:not(:last-child) {
border-bottom: var(--border);
}
+ &:has(button.material-symbols) {
+ grid-template-columns: 1fr calc(16em - 24px - var(--spacing-double)) 24px;
+ }
@media (width < 600px) {
grid-template-columns: 1fr;
grid-auto-flow: row;
diff --git a/static/icons.css b/static/icons.css
index 8d6b7b5..a528e6e 100644
--- a/static/icons.css
+++ b/static/icons.css
@@ -102,4 +102,7 @@
&.attach-file {
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M18 15.75q0 2.6-1.825 4.425T11.75 22t-4.425-1.825T5.5 15.75V6.5q0-1.875 1.313-3.187T10 2t3.188 1.313T14.5 6.5v8.75q0 1.15-.8 1.95t-1.95.8t-1.95-.8t-.8-1.95V6h2v9.25q0 .325.213.538t.537.212t.538-.213t.212-.537V6.5q-.025-1.05-.737-1.775T10 4t-1.775.725T7.5 6.5v9.25q-.025 1.775 1.225 3.013T11.75 20q1.75 0 2.975-1.237T16 15.75V6h2z'/%3E%3C/svg%3E");
}
+ &.update {
+ --svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M12 21q-1.875 0-3.512-.712t-2.85-1.925t-1.925-2.85T3 12t.713-3.512t1.924-2.85t2.85-1.925T12 3q2.05 0 3.888.875T19 6.35V4h2v6h-6V8h2.75q-1.025-1.4-2.525-2.2T12 5Q9.075 5 7.038 7.038T5 12t2.038 4.963T12 19q2.625 0 4.588-1.7T18.9 13h2.05q-.375 3.425-2.937 5.713T12 21m2.8-4.8L11 12.4V7h2v4.6l3.2 3.2z'/%3E%3C/svg%3E");
+ }
}
diff --git a/templates/note.php b/templates/note.php
index b43e63e..db4fbb9 100644
--- a/templates/note.php
+++ b/templates/note.php
@@ -3,8 +3,10 @@ use Digitigrade\Model\Actor;
use Digitigrade\Model\InteractionKind;
use Digitigrade\Model\NotePrivacyScope;
use Digitigrade\Model\UserAccount;
+use Digitigrade\UserSettings;
$user = UserAccount::findByCurrentSession();
+$prefs = $user ? new UserSettings($user) : null;
$interKinds = [];
$actionsDisabled = $user == null;
if ($user != null) {
@@ -25,13 +27,11 @@ if ($user != null) {
href="/@/<?= $note->author->getFullHandle() ?>"><?= htmlspecialchars($note->author->displayName) ?></a>
<span class="authorHandle">@<?= $note->author->getFullHandle() ?></span>
<a class="timestamp" href="/@/<?= $note->author->getFullHandle() ?>/note/<?= $note->id ?>">
- <?= // TODO: localise
- $note->created->format('Y-m-d \a\t H:i')
+ <?= format_datetime($note->created, $prefs?->get('locale.timezone'))
. (isset($note->modified) && $note->modified != $note->created
- ? ' (edited ' . $note->modified->format('Y-m-d \a\t H:i') . ')'
+ ? ' (' . format_datetime($note->modified, $prefs?->get('locale.timezone')) . ')'
: ''
- )
- ?>
+ ) ?>
</a>
<span class="privacyScope inlineIcon material-symbols <?= match ($note->privacy->scope) {
NotePrivacyScope::NONE => 'lock',
@@ -78,11 +78,11 @@ if ($user != null) {
<div class="attachments">
<?php foreach ($note->attachments as $attachment): ?>
<div>
- <?php if (str_starts_with($attachment->type, 'image/')): ?>
+ <?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">
+ <a class="file" href="<?= $attachment->href ?>" target="_blank" download>
<span class="material-symbols attach-file"></span>
<span><?= htmlspecialchars($attachment->description ?? basename($attachment->href)) ?></span>
</a>
diff --git a/templates/preferences_form.php b/templates/preferences_form.php
index 8c8f203..33418f6 100644
--- a/templates/preferences_form.php
+++ b/templates/preferences_form.php
@@ -2,27 +2,26 @@
use Digitigrade\UserSettings;
$settings = new UserSettings($user);
+
+function _user_pref(string $id, mixed $value, string $type) {
+ call_template('settings_field', [
+ 'id' => "pref-$id",
+ 'fieldName' => $id,
+ 'name' => __("preferences.$id.name"),
+ 'description' => __("preferences.$id.description"),
+ 'value' => $value,
+ 'type' => $type
+ ]);
+}
+
?>
<form class="settings" method="post" action="/todo" hx-post="/fragment/preferences" hx-disabled-elt="find button"
hx-swap="outerHTML">
<?php
- call_template('settings_field', [
- 'id' => 'pref-requestToFollow',
- 'fieldName' => 'requestToFollow',
- 'name' => __('preferences.requestToFollow.name'),
- 'description' => __('preferences.requestToFollow.description'),
- 'value' => $user->actor->requestToFollow,
- 'type' => 'boolean'
- ]);
- call_template('settings_field', [
- 'id' => 'pref-smallIndicators',
- 'fieldName' => 'smallIndicators',
- 'name' => __('preferences.smallIndicators.name'),
- 'description' => __('preferences.smallIndicators.description'),
- 'value' => $settings->getBool('interface.smallUnreadIndicators'),
- 'type' => 'boolean'
- ]);
+ _user_pref('requestToFollow', $user->actor->requestToFollow, 'boolean');
+ _user_pref('smallIndicators', $settings->getBool('interface.smallUnreadIndicators'), 'boolean');
+ _user_pref('timezone', $settings->get('locale.timezone'), 'timezone');
?>
<div class="row">
diff --git a/templates/settings_field.php b/templates/settings_field.php
index fe68a52..f9de886 100644
--- a/templates/settings_field.php
+++ b/templates/settings_field.php
@@ -38,5 +38,13 @@ $fieldName ??= str_replace('.', '<DOT>', $key);
<input id="<?= $id ?>" name="<?= $fieldName ?>" value="<?= $value ?>" type="<?= $inputType ?>" autocomplete="off">
+ <?php if ($inputType == 'timezone'): ?>
+ <button type="button" class="primary material-symbols update" title="<?= __('preferences.timezone.auto') ?>" _="
+ on click
+ js return Intl.DateTimeFormat().resolvedOptions().timeZone end
+ set the value of #<?= $id ?> to it
+ "></button>
+ <?php endif; ?>
+
<?php endif; ?>
</div> \ No newline at end of file