4
0
mirror of https://github.com/AzuraCast/AzuraCast.git synced 2024-06-18 06:57:05 +00:00

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 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. 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 ## Code Quality/Technical Changes
- Because both our Docker and Ansible installations are managed by Supervisor now, we can view the realtime status of - 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); $localeShort = substr($locale, 0, 2);
$localeWithDashes = str_replace('_', '-', $locale); $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 = [ $app = [
'lang' => [ 'lang' => [
'confirm' => __('Are you sure?'), 'confirm' => __('Are you sure?'),
'advanced' => __('Advanced'), 'advanced' => __('Advanced'),
], ],
'locale' => $locale, 'locale' => $locale,
'locale_short' => $localeShort, 'locale_short' => $localeShort,
'locale_with_dashes' => $localeWithDashes, 'locale_with_dashes' => $localeWithDashes,
'time_config' => $timeConfig,
'api_csrf' => null, 'api_csrf' => null,
]; ];

View File

@ -22,30 +22,44 @@
</template> </template>
<b-form-row> <b-form-row>
<b-wrapped-form-group class="col-md-6" id="edit_form_locale" <b-col md="6">
:field="form.locale"> <b-wrapped-form-group class="col-md-6" id="edit_form_locale"
<template #label="{lang}"> :field="form.locale">
<translate :key="lang">Language</translate> <template #label="{lang}">
</template> <translate :key="lang">Language</translate>
<template #default="props"> </template>
<b-form-radio-group stacked :id="props.id" :options="localeOptions" <template #default="props">
v-model="props.field.$model"> <b-form-radio-group stacked :id="props.id" :options="localeOptions"
</b-form-radio-group> v-model="props.field.$model">
</template> </b-form-radio-group>
</b-wrapped-form-group> </template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_theme" </b-col>
:field="form.theme"> <b-col md="6">
<template #label="{lang}"> <b-wrapped-form-group id="edit_form_theme"
<translate :key="lang">Site Theme</translate> :field="form.theme">
</template> <template #label="{lang}">
<template #default="props"> <translate :key="lang">Site Theme</translate>
<b-form-radio-group stacked :id="props.id" :options="themeOptions" </template>
v-model="props.field.$model"> <template #default="props">
</b-form-radio-group> <b-form-radio-group stacked :id="props.id" :options="themeOptions"
</template> v-model="props.field.$model">
</b-wrapped-form-group> </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-row>
</b-form-fieldset> </b-form-fieldset>
</div> </div>
@ -87,6 +101,22 @@ export default {
value: 'dark' 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: {}, name: {},
email: {required, email}, email: {required, email},
locale: {required}, locale: {required},
theme: {required} theme: {required},
show_24_hour_time: {}
} }
}; };
}, },
@ -52,7 +53,8 @@ export default {
name: '', name: '',
email: '', email: '',
locale: 'default', locale: 'default',
theme: 'browser' theme: 'browser',
show_24_hour_time: null,
}; };
}, },
open() { open() {

View File

@ -143,7 +143,9 @@ export default {
this.$refs.datatable.relist(); this.$refs.datatable.relist();
}, },
formatTimestamp(unix_timestamp) { 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(); return DateTime.fromSeconds(timestamp).toRelative();
}, },
toLocaleTime(timestamp) { toLocaleTime(timestamp) {
return DateTime.fromSeconds(timestamp).toLocaleString(DateTime.DATETIME_SHORT); return DateTime.fromSeconds(timestamp).toLocaleString(
{...DateTime.DATETIME_SHORT, ...App.time_config}
);
}, },
formatFileSize(size) { formatFileSize(size) {
return formatFileSize(size); return formatFileSize(size);

View File

@ -267,7 +267,9 @@ export default {
if (!value) { if (!value) {
return ''; 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, selectable: true,
visible: true visible: true

View File

@ -192,7 +192,9 @@ export default {
: this.$gettext('Enable'); : this.$gettext('Enable');
}, },
formatTime (time) { 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) { formatLength (length) {
return humanizeDuration(length * 1000, { return humanizeDuration(length * 1000, {

View File

@ -60,15 +60,23 @@ export default {
row.time_until = start_moment.toRelative(); row.time_until = start_moment.toRelative();
if (start_moment.hasSame(now, 'day')) { 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 { } 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')) { 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 { } else {
row.end_formatted = end_moment.toLocaleString(DateTime.DATETIME_MED); row.end_formatted = end_moment.toLocaleString(
{...DateTime.DATETIME_MED, ...App.time_config}
);
} }
return row; return row;

View File

@ -81,7 +81,9 @@ export default {
}, },
methods: { methods: {
formatTime(time) { 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) { formatRelativeTime(time) {
return this.getDateTime(time).toRelative(); return this.getDateTime(time).toRelative();

View File

@ -104,7 +104,9 @@ export default {
this.$refs.datatable.refresh(); this.$refs.datatable.refresh();
}, },
formatTime(time) { 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) { doDelete(url) {
this.$confirmDelete({ this.$confirmDelete({

View File

@ -165,10 +165,14 @@ export default {
return Math.abs(val); return Math.abs(val);
}, },
formatTimestamp(unix_timestamp) { 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) { 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'), label: this.$gettext('Start Time'),
sortable: false, sortable: false,
formatter: (value, key, item) => { 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) { if (value === 0) {
return this.$gettext('Live'); 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; 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"), OA\Property(example: "A1B2C3D4"),
ORM\Column(length: 255, nullable: true), ORM\Column(length: 255, nullable: true),
@ -234,6 +242,16 @@ class User implements Stringable, IdentifiableEntityInterface
$this->theme = $theme; $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 public function getTwoFactorSecret(): ?string
{ {
return $this->two_factor_secret; return $this->two_factor_secret;

View File

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