UI: add 12h/24h setting (#21140)

This commit is contained in:
Michael Geers 2025-05-09 15:55:45 +02:00 • committed by GitHub
parent 376537363f
commit 9da6236e03
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 80 additions and 3 deletions

View file

@ -42,6 +42,21 @@
equal-width
/>
</FormRow>
<FormRow id="settingsTimeFormat" :label="$t('settings.time.label')">
<SelectGroup
id="settingsTimeFormat"
v-model="timeFormat"
class="w-75"
transparent
:options="
TIME_FORMATS.map((value) => ({
value,
name: $t(`settings.time.${value}h`),
}))
"
equal-width
/>
</FormRow>
<FormRow id="telemetryEnabled" :label="$t('settings.telemetry.label')">
<TelemetrySettings :sponsorActive="sponsor && !!sponsor.name" class="mt-1 mb-0" />
</FormRow>
@ -82,12 +97,15 @@ import FormRow from "../Helper/FormRow.vue";
import SelectGroup from "../Helper/SelectGroup.vue";
import { getLocalePreference, setLocalePreference, LOCALES, removeLocalePreference } from "@/i18n";
import { getThemePreference, setThemePreference, THEMES } from "@/theme";
import { getUnits, setUnits, UNITS } from "@/units";
import { getUnits, setUnits, UNITS, is12hFormat, set12hFormat } from "@/units";
import { getHiddenFeatures, setHiddenFeatures } from "@/featureflags";
import { isApp } from "@/utils/native";
import { defineComponent, type PropType } from "vue";
import type { Sponsor } from "@/types/evcc";
const TIME_12H = "12";
const TIME_24H = "24";
export default defineComponent({
name: "UserInterfaceSettings",
components: { TelemetrySettings, FormRow, SelectGroup },
@ -99,10 +117,12 @@ export default defineComponent({
theme: getThemePreference(),
language: getLocalePreference() || "",
unit: getUnits(),
timeFormat: is12hFormat() ? TIME_12H : TIME_24H,
hiddenFeatures: getHiddenFeatures(),
fullscreenActive: false,
THEMES,
UNITS,
TIME_FORMATS: [TIME_24H, TIME_12H],
};
},
computed: {
@ -126,6 +146,9 @@ export default defineComponent({
unit(value) {
setUnits(value);
},
timeFormat(value) {
set12hFormat(value === TIME_12H);
},
theme(value) {
setThemePreference(value);
},

View file

@ -27,7 +27,7 @@
</div>
<div class="slot-label">
<span v-if="!slot.isTarget || targetNearlyOutOfRange">{{
slot.startHour
formatHour(slot.startHour)
}}</span>
<br />
<span v-if="showWeekday(index)">{{ slot.day }}</span>
@ -55,6 +55,7 @@
<script lang="ts">
import type { PropType } from "vue";
import "@h2d2/shopicons/es/regular/arrowright";
import { is12hFormat } from "@/units";
import PlanEndIcon from "../MaterialIcon/PlanEnd.vue";
import formatter from "@/mixins/formatter";
import type { Slot } from "@/types/evcc";
@ -164,6 +165,9 @@ export default {
clearTimeout(this.longPressTimer);
this.hoverSlot(null);
},
formatHour(hour: number) {
return is12hFormat() ? hour % 12 : hour;
},
},
};
</script>

View file

@ -1,6 +1,9 @@
import { mount, config } from "@vue/test-utils";
import { describe, expect, test } from "vitest";
import { describe, expect, test, vi } from "vitest";
import formatter, { POWER_UNIT } from "./formatter";
import * as units from "../units";
const is12hSpy = vi.spyOn(units, "is12hFormat").mockReturnValue(false);
config.global.mocks["$i18n"] = { locale: "de-DE" };
config.global.mocks["$t"] = (a) => a;
@ -183,3 +186,23 @@ describe("fmtPercentage", () => {
expect(fmt.fmtPercentage(-100, 0, false)).eq("-100 %");
});
});
const testDate = new Date(2023, 0, 15, 15, 30); // Jan 15, 2023, 15:30 (3:30 PM)
describe("12h/24h time format", () => {
test("12h format", () => {
is12hSpy.mockReturnValue(true);
expect(fmt.fmtHourMinute(testDate)).toBe("3:30 PM");
expect(fmt.fmtFullDateTime(testDate)).toBe("So., 15. Jan., 3:30 PM");
expect(fmt.fmtWeekdayTime(testDate)).toBe("So. 3:30 PM");
expect(fmt.fmtAbsoluteDate(testDate)).toBe("So 3:30 PM");
});
test("24h format", () => {
is12hSpy.mockReturnValue(false);
expect(fmt.fmtHourMinute(testDate)).toBe("15:30");
expect(fmt.fmtFullDateTime(testDate)).toBe("So., 15. Jan., 15:30");
expect(fmt.fmtWeekdayTime(testDate)).toBe("So., 15:30");
expect(fmt.fmtAbsoluteDate(testDate)).toBe("So 15:30");
});
});

View file

@ -1,4 +1,5 @@
import { defineComponent } from "vue";
import { is12hFormat } from "@/units";
import { CURRENCY } from "../types/evcc";
// list of currencies where energy price should be displayed in subunits (factor 100)
@ -198,6 +199,7 @@ export default defineComponent({
if (locale === "de") return date.getHours();
return new Intl.DateTimeFormat(locale, {
hour: "numeric",
hour12: is12hFormat(),
}).format(date);
},
weekdayShort(date: Date) {
@ -210,6 +212,7 @@ export default defineComponent({
const hour = new Intl.DateTimeFormat(this.$i18n?.locale, {
hour: "numeric",
minute: "numeric",
hour12: is12hFormat(),
}).format(date);
return `${weekday} ${hour}`.trim();
@ -218,6 +221,7 @@ export default defineComponent({
return new Intl.DateTimeFormat(this.$i18n?.locale, {
hour: "numeric",
minute: "numeric",
hour12: is12hFormat(),
}).format(date);
},
fmtFullDateTime(date: Date, short: boolean) {
@ -227,6 +231,7 @@ export default defineComponent({
day: "numeric",
hour: "numeric",
minute: "numeric",
hour12: is12hFormat(),
}).format(date);
},
fmtWeekdayTime(date: Date) {
@ -234,6 +239,7 @@ export default defineComponent({
weekday: "short",
hour: "numeric",
minute: "numeric",
hour12: is12hFormat(),
}).format(date);
},
fmtMonthYear(date: Date) {

View file

@ -3,6 +3,7 @@ import { reactive, watch } from "vue";
const SETTINGS_LOCALE = "settings_locale";
const SETTINGS_THEME = "settings_theme";
const SETTINGS_UNIT = "settings_unit";
const SETTINGS_12H_FORMAT = "settings_12h_format";
const SETTINGS_HIDDEN_FEATURES = "settings_hidden_features";
const SETTINGS_ENERGYFLOW_DETAILS = "settings_energyflow_details";
const SETTINGS_ENERGYFLOW_PV = "settings_energyflow_pv";
@ -59,6 +60,7 @@ const settings = reactive({
locale: read(SETTINGS_LOCALE),
theme: read(SETTINGS_THEME),
unit: read(SETTINGS_UNIT),
is12hFormat: readBool(SETTINGS_12H_FORMAT),
hiddenFeatures: readBool(SETTINGS_HIDDEN_FEATURES),
energyflowDetails: readBool(SETTINGS_ENERGYFLOW_DETAILS),
energyflowPv: readBool(SETTINGS_ENERGYFLOW_PV),
@ -76,6 +78,7 @@ const settings = reactive({
watch(() => settings.locale, save(SETTINGS_LOCALE));
watch(() => settings.theme, save(SETTINGS_THEME));
watch(() => settings.unit, save(SETTINGS_UNIT));
watch(() => settings.is12hFormat, saveBool(SETTINGS_12H_FORMAT));
watch(() => settings.hiddenFeatures, saveBool(SETTINGS_HIDDEN_FEATURES));
watch(() => settings.energyflowDetails, saveBool(SETTINGS_ENERGYFLOW_DETAILS));
watch(() => settings.energyflowPv, saveBool(SETTINGS_ENERGYFLOW_PV));

View file

@ -29,3 +29,11 @@ export function getUnits() {
export function setUnits(value) {
settings.unit = value;
}
export function is12hFormat() {
return settings.is12hFormat;
}
export function set12hFormat(value) {
settings.is12hFormat = value;
}

View file

@ -931,6 +931,11 @@
"label": "Design",
"light": "Hell"
},
"time": {
"12h": "12h",
"24h": "24h",
"label": "Zeitformat"
},
"title": "Darstellung",
"unit": {
"km": "Kilometer",

View file

@ -931,6 +931,11 @@
"label": "Design",
"light": "light"
},
"time": {
"12h": "12h",
"24h": "24h",
"label": "Time format"
},
"title": "User Interface",
"unit": {
"km": "km",