Add support for per-user 24-hour clock display setting.

This commit is contained in:
Buster Neece 2022-11-02 08:44:44 -05:00
parent d5fb9edf61
commit f6c363163f
No known key found for this signature in database
GPG Key ID: F1D2E64A0005E80E
16 changed files with 166 additions and 45 deletions

View File

@ -10,6 +10,9 @@ release channel, you can take advantage of these new features and fixes.
media. Because cover art files are often named a variety of things, we currently will use _any_ image file that exists
alongside media. You can also now view cover art via the Media Manager UI.
- **24-Hour Time Display Support**: You can now choose whether to view time in 12 or 24 hour format from your user
profile, or use the default settings for your locale.
## Code Quality/Technical Changes
- Because both our Docker and Ansible installations are managed by Supervisor now, we can view the realtime status of

View File

@ -80,14 +80,26 @@ return [
$localeShort = substr($locale, 0, 2);
$localeWithDashes = str_replace('_', '-', $locale);
// User profile-specific 24-hour display setting.
$userObj = $request->getAttribute(ServerRequest::ATTR_USER);
$show24Hours = ($userObj instanceof App\Entity\User)
? $userObj->getShow24HourTime()
: null;
$timeConfig = new \stdClass();
if (null !== $show24Hours) {
$timeConfig->hour12 = !$show24Hours;
}
$app = [
'lang' => [
'confirm' => __('Are you sure?'),
'lang' => [
'confirm' => __('Are you sure?'),
'advanced' => __('Advanced'),
],
'locale' => $locale,
'locale_short' => $localeShort,
'locale_with_dashes' => $localeWithDashes,
'time_config' => $timeConfig,
'api_csrf' => null,
];

View File

@ -22,30 +22,44 @@
</template>
<b-form-row>
<b-wrapped-form-group class="col-md-6" id="edit_form_locale"
:field="form.locale">
<template #label="{lang}">
<translate :key="lang">Language</translate>
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" :options="localeOptions"
v-model="props.field.$model">
</b-form-radio-group>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_theme"
:field="form.theme">
<template #label="{lang}">
<translate :key="lang">Site Theme</translate>
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" :options="themeOptions"
v-model="props.field.$model">
</b-form-radio-group>
</template>
</b-wrapped-form-group>
<b-col md="6">
<b-wrapped-form-group class="col-md-6" id="edit_form_locale"
:field="form.locale">
<template #label="{lang}">
<translate :key="lang">Language</translate>
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" :options="localeOptions"
v-model="props.field.$model">
</b-form-radio-group>
</template>
</b-wrapped-form-group>
</b-col>
<b-col md="6">
<b-wrapped-form-group id="edit_form_theme"
:field="form.theme">
<template #label="{lang}">
<translate :key="lang">Site Theme</translate>
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" :options="themeOptions"
v-model="props.field.$model">
</b-form-radio-group>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group id="edit_form_show_24_hour_time"
:field="form.show_24_hour_time">
<template #label="{lang}">
<translate :key="lang">Time Display</translate>
</template>
<template #default="props">
<b-form-radio-group stacked :id="props.id" :options="show24hourOptions"
v-model="props.field.$model">
</b-form-radio-group>
</template>
</b-wrapped-form-group>
</b-col>
</b-form-row>
</b-form-fieldset>
</div>
@ -87,6 +101,22 @@ export default {
value: 'dark'
}
];
},
show24hourOptions() {
return [
{
text: this.$gettext('Prefer System Default'),
value: null
},
{
text: this.$gettext('12 Hour'),
value: false
},
{
text: this.$gettext('24 Hour'),
value: true
}
];
}
}
}

View File

@ -37,7 +37,8 @@ export default {
name: {},
email: {required, email},
locale: {required},
theme: {required}
theme: {required},
show_24_hour_time: {}
}
};
},
@ -52,7 +53,8 @@ export default {
name: '',
email: '',
locale: 'default',
theme: 'browser'
theme: 'browser',
show_24_hour_time: null,
};
},
open() {

View File

@ -143,7 +143,9 @@ export default {
this.$refs.datatable.relist();
},
formatTimestamp(unix_timestamp) {
return DateTime.fromSeconds(unix_timestamp).toLocaleString(DateTime.DATETIME_SHORT);
return DateTime.fromSeconds(unix_timestamp).toLocaleString(
{...DateTime.DATETIME_SHORT, ...App.time_config}
);
}
}
}

View File

@ -183,7 +183,9 @@ export default {
return DateTime.fromSeconds(timestamp).toRelative();
},
toLocaleTime(timestamp) {
return DateTime.fromSeconds(timestamp).toLocaleString(DateTime.DATETIME_SHORT);
return DateTime.fromSeconds(timestamp).toLocaleString(
{...DateTime.DATETIME_SHORT, ...App.time_config}
);
},
formatFileSize(size) {
return formatFileSize(size);

View File

@ -267,7 +267,9 @@ export default {
if (!value) {
return '';
}
return DateTime.fromSeconds(value).setZone(this.stationTimeZone).toLocaleString(DateTime.DATETIME_MED);
return DateTime.fromSeconds(value).setZone(this.stationTimeZone).toLocaleString(
{...DateTime.DATETIME_MED, ...App.time_config}
);
},
selectable: true,
visible: true

View File

@ -192,7 +192,9 @@ export default {
: this.$gettext('Enable');
},
formatTime (time) {
return DateTime.fromSeconds(time).setZone(this.stationTimeZone).toLocaleString(DateTime.DATETIME_MED);
return DateTime.fromSeconds(time).setZone(this.stationTimeZone).toLocaleString(
{...DateTime.DATETIME_MED, ...App.time_config}
);
},
formatLength (length) {
return humanizeDuration(length * 1000, {

View File

@ -60,15 +60,23 @@ export default {
row.time_until = start_moment.toRelative();
if (start_moment.hasSame(now, 'day')) {
row.start_formatted = start_moment.toLocaleString(DateTime.TIME_SIMPLE);
row.start_formatted = start_moment.toLocaleString(
{...DateTime.TIME_SIMPLE, ...App.time_config}
);
} else {
row.start_formatted = start_moment.toLocaleString(DateTime.DATETIME_MED);
row.start_formatted = start_moment.toLocaleString(
{...DateTime.DATETIME_MED, ...App.time_config}
);
}
if (end_moment.hasSame(start_moment, 'day')) {
row.end_formatted = end_moment.toLocaleString(DateTime.TIME_SIMPLE);
row.end_formatted = end_moment.toLocaleString(
{...DateTime.TIME_SIMPLE, ...App.time_config}
);
} else {
row.end_formatted = end_moment.toLocaleString(DateTime.DATETIME_MED);
row.end_formatted = end_moment.toLocaleString(
{...DateTime.DATETIME_MED, ...App.time_config}
);
}
return row;

View File

@ -81,7 +81,9 @@ export default {
},
methods: {
formatTime(time) {
return this.getDateTime(time).toLocaleString(DateTime.TIME_WITH_SECONDS);
return this.getDateTime(time).toLocaleString(
{...DateTime.TIME_WITH_SECONDS, ...App.time_config}
);
},
formatRelativeTime(time) {
return this.getDateTime(time).toRelative();

View File

@ -104,7 +104,9 @@ export default {
this.$refs.datatable.refresh();
},
formatTime(time) {
return DateTime.fromSeconds(time).setZone(this.stationTimeZone).toLocaleString(DateTime.DATETIME_MED);
return DateTime.fromSeconds(time).setZone(this.stationTimeZone).toLocaleString(
{...DateTime.DATETIME_MED, ...App.time_config}
);
},
doDelete(url) {
this.$confirmDelete({

View File

@ -165,10 +165,14 @@ export default {
return Math.abs(val);
},
formatTimestamp(unix_timestamp) {
return DateTime.fromSeconds(unix_timestamp).toLocaleString(DateTime.DATETIME_SHORT);
return DateTime.fromSeconds(unix_timestamp).toLocaleString(
{...DateTime.DATETIME_SHORT, ...App.time_config}
);
},
formatTimestampStation(unix_timestamp) {
return DateTime.fromSeconds(unix_timestamp).setZone(this.stationTimeZone).toLocaleString(DateTime.DATETIME_SHORT);
return DateTime.fromSeconds(unix_timestamp).setZone(this.stationTimeZone).toLocaleString(
{...DateTime.DATETIME_SHORT, ...App.time_config}
);
}
}
};

View File

@ -61,7 +61,9 @@ export default {
label: this.$gettext('Start Time'),
sortable: false,
formatter: (value, key, item) => {
return DateTime.fromSeconds(value).toLocaleString(DateTime.DATETIME_MED);
return DateTime.fromSeconds(value).toLocaleString(
{...DateTime.DATETIME_MED, ...App.time_config}
);
}
},
{
@ -72,7 +74,9 @@ export default {
if (value === 0) {
return this.$gettext('Live');
}
return DateTime.fromSeconds(value).toLocaleString(DateTime.DATETIME_MED);
return DateTime.fromSeconds(value).toLocaleString(
{...DateTime.DATETIME_MED, ...App.time_config}
);
}
},
{

View File

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace App\Entity\Migration;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20221102125558 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add user-level 24-hour time setting.';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE users ADD show_24_hour_time TINYINT(1) DEFAULT NULL');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE users DROP show_24_hour_time');
}
}

View File

@ -81,6 +81,14 @@ class User implements Stringable, IdentifiableEntityInterface
]
protected ?string $theme = null;
#[
OA\Property(example: true),
ORM\Column(nullable: true),
Attributes\AuditIgnore,
Groups([EntityGroupsInterface::GROUP_GENERAL, EntityGroupsInterface::GROUP_ALL])
]
protected ?bool $show_24_hour_time = null;
#[
OA\Property(example: "A1B2C3D4"),
ORM\Column(length: 255, nullable: true),
@ -234,6 +242,16 @@ class User implements Stringable, IdentifiableEntityInterface
$this->theme = $theme;
}
public function getShow24HourTime(): ?bool
{
return $this->show_24_hour_time;
}
public function setShow24HourTime(?bool $show_24_hour_time): void
{
$this->show_24_hour_time = $show_24_hour_time;
}
public function getTwoFactorSecret(): ?string
{
return $this->two_factor_secret;

View File

@ -49,10 +49,12 @@ $(document).on('click', '.api-call', function (e) {
$(function () {
function updateClock() {
let d = new Date();
let time = d.toLocaleString(App.locale_with_dashes, {
timeZone: <?=$this->escapeJs($station->getTimezone()) ?>,
timeStyle: 'long'
})
let timeConfig = App.time_config;
timeConfig.timeZone = <?=$this->escapeJs($station->getTimezone()) ?>;
timeConfig.timeStyle = 'long';
let time = d.toLocaleString(App.locale_with_dashes, timeConfig);
$('#station-time').text(time);
}