feat: add option to change date & time format in settings (#1069)

This commit is contained in:
Stefan Dej 2022-10-09 19:59:47 +02:00 committed by GitHub
parent d1cfe7251d
commit 685665bd46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 263 additions and 73 deletions

View File

@ -84,6 +84,7 @@ export default class TempChart extends Mixins(BaseMixin) {
axisLabel: {
color: 'rgba(255, 255, 255, 0.24)',
margin: 10,
formatter: this.timeFormat,
},
},
yAxis: [
@ -225,8 +226,13 @@ export default class TempChart extends Mixins(BaseMixin) {
return this.$store.getters['printer/tempHistory/getSelectedLegends']
}
get timeFormat() {
return this.hours12Format ? '{hh}:{mm}' : '{HH}:{mm}'
}
mounted() {
this.initChart()
this.chartOptions.xAxis.axisLabel.formatter = this.timeFormat
}
beforeDestroy() {
@ -383,5 +389,10 @@ export default class TempChart extends Mixins(BaseMixin) {
boolDisplayPwmAxisChanged() {
this.updateChartPwmAxis()
}
@Watch('hours12Format')
hours12FormatChanged() {
this.chartOptions.xAxis.axisLabel.formatter = this.timeFormat
}
}
</script>

View File

@ -25,7 +25,7 @@
<template>
<v-row :class="'ma-0 ' + entryStyle">
<v-col class="col-auto pr-0 text--disabled console-time">{{ event.formatTime }}</v-col>
<v-col class="col-auto pr-0 text--disabled console-time">{{ entryFormatTime }}</v-col>
<v-col
:class="colorConsoleMessage(event) + ' ' + 'console-message'"
@click.capture="commandClick"
@ -35,12 +35,12 @@
<script lang="ts">
import Component from 'vue-class-component'
import Vue from 'vue'
import { Prop } from 'vue-property-decorator'
import { Mixins, Prop } from 'vue-property-decorator'
import { ServerStateEvent } from '@/store/server/types'
import BaseMixin from '@/components/mixins/base'
@Component
export default class ConsoleTableEntry extends Vue {
export default class ConsoleTableEntry extends Mixins(BaseMixin) {
@Prop({ required: true })
declare readonly event: ServerStateEvent
@ -48,6 +48,10 @@ export default class ConsoleTableEntry extends Vue {
return this.$store.state.gui.console.entryStyle ?? 'default'
}
get entryFormatTime() {
return this.formatTime(this.event.date.getTime(), true)
}
colorConsoleMessage(item: ServerStateEvent): string {
if (item.message.startsWith('!! ')) return 'error--text'

View File

@ -1,5 +1,6 @@
import Vue from 'vue'
import Component from 'vue-class-component'
import { DateTimeFormatOptions } from 'vue-i18n'
@Component
export default class BaseMixin extends Vue {
@ -87,4 +88,96 @@ export default class BaseMixin extends Vue {
return roots.findIndex((root: string) => root === 'gcodes') >= 0
}
get formatDateOptions(): DateTimeFormatOptions {
const format = this.$store.state.gui.general.dateFormat
switch (format) {
case '2-digits':
return { day: '2-digit', month: '2-digit', year: 'numeric' }
case 'short':
return { day: '2-digit', month: 'short', year: 'numeric' }
default:
return { dateStyle: 'medium' }
}
}
get formatTimeOptions(): DateTimeFormatOptions {
const format = this.$store.state.gui.general.timeFormat
switch (format) {
case '24hours':
return { hour: '2-digit', minute: '2-digit', hourCycle: 'h23' }
case '12hours':
return { hour: '2-digit', minute: '2-digit', hourCycle: 'h12' }
default:
return { timeStyle: 'short' }
}
}
get formatTimeWithSecondsOptions(): DateTimeFormatOptions {
const format = this.$store.state.gui.general.timeFormat
switch (format) {
case '24hours':
return { hour: '2-digit', minute: '2-digit', second: '2-digit', hourCycle: 'h23' }
case '12hours':
return { hour: '2-digit', minute: '2-digit', second: '2-digit', hourCycle: 'h12' }
default:
return { timeStyle: 'short' }
}
}
get browserLocale() {
return navigator.languages && navigator.languages.length ? navigator.languages[0] : navigator.language
}
get hours12Format() {
const setting = this.$store.state.gui.general.timeFormat
if (setting === '12hours') return true
if (setting === null && this.browserLocale === 'en_us') return true
return false
}
formatDate(value: number | Date): string {
let tmp = null
try {
// @ts-ignore
tmp = (typeof value.getMonth === 'function' ? value : new Date(value)) as Date
} catch (_) {
return 'UNKNOWN'
}
return tmp.toLocaleDateString(this.browserLocale, this.formatDateOptions)
}
formatTime(value: number | Date, boolSeconds = false): string {
let tmp = null
try {
// @ts-ignore
tmp = (typeof value.getMonth === 'function' ? value : new Date(value)) as Date
} catch (_) {
return 'UNKNOWN'
}
if (boolSeconds) return tmp.toLocaleTimeString(this.browserLocale, this.formatTimeWithSecondsOptions)
return tmp.toLocaleTimeString(this.browserLocale, this.formatTimeOptions)
}
formatDateTime(value: number, boolSeconds = false): string {
const date = this.formatDate(value)
const time = this.formatTime(value, boolSeconds)
return `${date} ${time}`
}
}

View File

@ -494,7 +494,7 @@
import { Component, Mixins, Watch } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import { validGcodeExtensions } from '@/store/variables'
import { formatDate, formatFilesize, formatPrintTime, sortFiles } from '@/plugins/helpers'
import { formatFilesize, formatPrintTime, sortFiles } from '@/plugins/helpers'
import { FileStateFile, FileStateGcodefile } from '@/store/files/types'
import Panel from '@/components/ui/Panel.vue'
import SettingsRow from '@/components/settings/SettingsRow.vue'
@ -586,7 +586,6 @@ export default class GcodefilesPanel extends Mixins(BaseMixin, ControlMixin) {
mdiDragVertical = mdiDragVertical
validGcodeExtensions = validGcodeExtensions
formatDate = formatDate
formatFilesize = formatFilesize
formatPrintTime = formatPrintTime
sortFiles = sortFiles
@ -1269,7 +1268,7 @@ export default class GcodefilesPanel extends Mixins(BaseMixin, ControlMixin) {
return formatFilesize(value)
case 'date':
return this.formatDate(value)
return this.formatDateTime(value)
case 'time':
return this.formatPrintTime(value)

View File

@ -272,7 +272,9 @@
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.LastModified') }}</v-col>
<v-col class="text-right">{{ formatDate(detailsDialog.item.metadata.modified) }}</v-col>
<v-col class="text-right">
{{ formatDateTime(detailsDialog.item.metadata.modified) }}
</v-col>
</v-row>
</template>
<v-divider class="my-3"></v-divider>
@ -289,13 +291,13 @@
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.StartTime') }}</v-col>
<v-col class="text-right">{{ formatDate(detailsDialog.item.start_time) }}</v-col>
<v-col class="text-right">{{ formatDateTime(detailsDialog.item.start_time) }}</v-col>
</v-row>
<template v-if="'end_time' in detailsDialog.item && detailsDialog.item.end_time > 0">
<v-divider class="my-3"></v-divider>
<v-row>
<v-col>{{ $t('History.EndTime') }}</v-col>
<v-col class="text-right">{{ formatDate(detailsDialog.item.end_time) }}</v-col>
<v-col class="text-right">{{ formatDateTime(detailsDialog.item.end_time) }}</v-col>
</v-row>
</template>
<template
@ -761,12 +763,6 @@ export default class HistoryListPanel extends Mixins(BaseMixin) {
this.$socket.emit('server.history.list', { start: 0, limit: 50 }, { action: 'server/history/getHistory' })
}
formatDate(date: number) {
const tmp2 = new Date(date * 1000)
return tmp2.toLocaleString().replace(',', '')
}
formatPrintTime(totalSeconds: number) {
if (totalSeconds) {
let output = ''
@ -1013,7 +1009,7 @@ export default class HistoryListPanel extends Mixins(BaseMixin) {
if (!format) {
switch (col.outputType) {
case 'date':
return this.formatDate(value)
return this.formatDateTime(value * 1000)
case 'time':
return value?.toFixed() ?? ''
@ -1038,7 +1034,7 @@ export default class HistoryListPanel extends Mixins(BaseMixin) {
return formatFilesize(value)
case 'date':
return this.formatDate(value)
return this.formatDateTime(value * 1000)
case 'time':
return this.formatPrintTime(value)

View File

@ -162,7 +162,7 @@
<td class="text-no-wrap text-right">
{{ item.isDirectory ? '--' : formatFilesize(item.size) }}
</td>
<td class="text-right">{{ formatDate(item.modified) }}</td>
<td class="text-right">{{ formatDateTime(item.modified) }}</td>
</tr>
</template>
</v-data-table>
@ -462,7 +462,7 @@
<script lang="ts">
import { Component, Mixins } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import { formatDate, formatFilesize, sortFiles } from '@/plugins/helpers'
import { formatFilesize, sortFiles } from '@/plugins/helpers'
import { FileStateFile, FileStateGcodefile } from '@/store/files/types'
import axios from 'axios'
import Panel from '@/components/ui/Panel.vue'
@ -553,7 +553,6 @@ export default class ConfigFilesPanel extends Mixins(BaseMixin) {
sortFiles = sortFiles
formatFilesize = formatFilesize
formatDate = formatDate
declare $refs: {
fileUpload: HTMLInputElement

View File

@ -81,16 +81,16 @@
<strong>{{ $t('Panels.StatusPanel.Estimate') }}</strong>
<br />
<span class="text-no-wrap">
{{ estimated_time_avg ? formatTime(estimated_time_avg) : '--' }}
{{ estimated_time_avg ? formatDuration(estimated_time_avg) : '--' }}
</span>
</div>
</template>
<div class="text-right">
{{ $t('Panels.StatusPanel.File') }}:
{{ estimated_time_file ? formatTime(estimated_time_file) : '--' }}
{{ estimated_time_file ? formatDuration(estimated_time_file) : '--' }}
<br />
{{ $t('Panels.StatusPanel.Filament') }}:
{{ estimated_time_filament ? formatTime(estimated_time_filament) : '--' }}
{{ estimated_time_filament ? formatDuration(estimated_time_filament) : '--' }}
</div>
</v-tooltip>
</v-col>
@ -98,7 +98,7 @@
<strong>{{ $t('Panels.StatusPanel.Slicer') }}</strong>
<br />
<span class="text-no-wrap">
{{ estimated_time_slicer ? formatTime(estimated_time_slicer) : '--' }}
{{ estimated_time_slicer ? formatDuration(estimated_time_slicer) : '--' }}
</span>
</v-col>
<v-col class="col-3 pa-0">
@ -108,23 +108,23 @@
<strong>{{ $t('Panels.StatusPanel.Total') }}</strong>
<br />
<span class="text-no-wrap">
{{ print_time_total ? formatTime(print_time_total) : '--' }}
{{ print_time_total ? formatDuration(print_time_total) : '--' }}
</span>
</div>
</template>
<div class="text-right">
{{ $t('Panels.StatusPanel.Print') }}:
{{ print_time ? formatTime(print_time) : '--' }}
{{ print_time ? formatDuration(print_time) : '--' }}
<br />
{{ $t('Panels.StatusPanel.Difference') }}:
{{ print_time && print_time_total ? formatTime(print_time_total - print_time) : '--' }}
{{ print_time && print_time_total ? formatDuration(print_time_total - print_time) : '--' }}
</div>
</v-tooltip>
</v-col>
<v-col class="col-3 pa-0">
<strong>{{ $t('Panels.StatusPanel.ETA') }}</strong>
<br />
<span class="text-no-wrap">{{ outputEta }}</span>
<span class="text-no-wrap">{{ eta }}</span>
</v-col>
</v-row>
</v-container>
@ -213,11 +213,7 @@ export default class StatusPanelPrintstatusPrinting extends Mixins(BaseMixin) {
}
get eta() {
return this.$store.getters['printer/getEstimatedTimeETA']
}
get outputEta() {
return this.eta ? this.formatDateTime(this.eta) : '--'
return this.$store.getters['printer/getEstimatedTimeETAFormat']
}
get filament_diameter() {
@ -242,7 +238,7 @@ export default class StatusPanelPrintstatusPrinting extends Mixins(BaseMixin) {
: this.filament_used.toFixed(2) + ' mm'
}
formatTime(seconds: number) {
formatDuration(seconds: number) {
let h = Math.floor(seconds / 3600)
seconds %= 3600
let m = ('0' + Math.floor(seconds / 60)).slice(-2)
@ -250,14 +246,5 @@ export default class StatusPanelPrintstatusPrinting extends Mixins(BaseMixin) {
return h + ':' + m + ':' + s
}
formatDateTime(msec: number) {
const date = new Date(msec)
const h = date.getHours() >= 10 ? date.getHours() : '0' + date.getHours()
const m = date.getMinutes() >= 10 ? date.getMinutes() : '0' + date.getMinutes()
const diff = msec - new Date().getTime()
return h + ':' + m + (diff > 60 * 60 * 24 * 1000 ? '+' + Math.round(diff / (60 * 60 * 24 * 1000)) : '')
}
}
</script>

View File

@ -181,7 +181,7 @@
{{ item.isDirectory ? '--' : formatFilesize(item.size) }}
</td>
<td v-if="headers.find((header) => header.value === 'modified').visible" class="text-right">
{{ formatDate(item.modified) }}
{{ formatDateTime(item.modified) }}
</td>
</tr>
</template>
@ -396,7 +396,7 @@
<script lang="ts">
import { Component, Mixins } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import { formatFilesize, formatDate, sortFiles } from '@/plugins/helpers'
import { formatFilesize, sortFiles } from '@/plugins/helpers'
import { FileStateFile, FileStateGcodefile } from '@/store/files/types'
import Panel from '@/components/ui/Panel.vue'
import {
@ -425,7 +425,6 @@ interface dialogRenameObject {
components: { Panel },
})
export default class TimelapseFilesPanel extends Mixins(BaseMixin) {
formatDate = formatDate
formatFilesize = formatFilesize
sortFiles = sortFiles

View File

@ -16,6 +16,26 @@
attach></v-select>
</settings-row>
<v-divider class="my-2"></v-divider>
<settings-row :title="$t('Settings.GeneralTab.DateFormat').toString()">
<v-select
v-model="dateFormat"
:items="dateFormatItems"
hide-details
outlined
dense
attach></v-select>
</settings-row>
<v-divider class="my-2"></v-divider>
<settings-row :title="$t('Settings.GeneralTab.TimeFormat').toString()">
<v-select
v-model="timeFormat"
:items="timeFormatItems"
hide-details
outlined
dense
attach></v-select>
</settings-row>
<v-divider class="my-2"></v-divider>
<settings-row
:title="$t('Settings.GeneralTab.CalcPrintProgress').toString()"
:sub-title="$t('Settings.GeneralTab.CalcPrintProgressDescription').toString()">
@ -364,6 +384,62 @@ export default class SettingsGeneralTab extends Mixins(BaseMixin) {
return languages
}
get dateFormat() {
return this.$store.state.gui.general.dateFormat
}
set dateFormat(newVal) {
this.$store.dispatch('gui/saveSetting', { name: 'general.dateFormat', value: newVal })
}
get dateFormatItems() {
const date = new Date()
const userLocale =
navigator.languages && navigator.languages.length ? navigator.languages[0] : navigator.language
return [
{ value: null, text: `Browser (${date.toLocaleDateString(userLocale, { dateStyle: 'medium' })})` },
{
value: '2-digits',
text: date.toLocaleDateString(userLocale, { day: '2-digit', month: '2-digit', year: 'numeric' }),
},
{
value: 'short',
text: date.toLocaleDateString(userLocale, { day: '2-digit', month: 'short', year: 'numeric' }),
},
]
}
get timeFormat() {
return this.$store.state.gui.general.timeFormat
}
set timeFormat(newVal) {
this.$store.dispatch('gui/saveSetting', { name: 'general.timeFormat', value: newVal })
}
get timeFormatItems() {
const date = new Date()
const userLocale =
navigator.languages && navigator.languages.length ? navigator.languages[0] : navigator.language
return [
{ value: null, text: `Browser (${date.toLocaleTimeString(userLocale, { timeStyle: 'short' })})` },
{
value: '24hours',
text: this.$t('Settings.GeneralTab.24hours', {
time: date.toLocaleTimeString(userLocale, { hour: '2-digit', minute: '2-digit', hourCycle: 'h23' }),
}).toString(),
},
{
value: '12hours',
text: this.$t('Settings.GeneralTab.12hours', {
time: date.toLocaleTimeString(userLocale, { hour: '2-digit', minute: '2-digit', hourCycle: 'h12' }),
}).toString(),
},
]
}
get calcPrintProgressItems() {
return [
{ value: 'file-relative', text: this.$t('Settings.GeneralTab.CalcPrintProgressItems.FileRelative') },

View File

@ -760,6 +760,8 @@
"ShowAxes": "Show Axes"
},
"GeneralTab": {
"12hours": "12-hours ({time})",
"24hours": "24-hours ({time})",
"Backup": "Backup",
"BackupDialog": "Please select all the sections you want to create a backup:",
"CalcEstimateTime": "Estimate time calculation",
@ -775,6 +777,7 @@
"Slicer": "Slicer (M73)"
},
"CannotReadJson": "Cannot read/parse backup file.",
"DateFormat": "Date Format",
"DbConsoleHistory": "Console History",
"DbHistoryJobs": "History Jobs",
"DbHistoryTotals": "History Totals",
@ -794,7 +797,8 @@
"PrinterName": "Printer Name",
"Reset": "reset",
"Restore": "Restore",
"RestoreDialog": "Please select all the sections you want to restore:"
"RestoreDialog": "Please select all the sections you want to restore:",
"TimeFormat": "Time Format"
},
"InterfaceSettings": "Interface Settings",
"MacrosTab": {

View File

@ -134,12 +134,6 @@ export const formatFilesize = (fileSizeInBytes: number): string => {
return Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i]
}
export const formatDate = (date: number): string => {
const tmp2 = new Date(date)
return tmp2.toLocaleString().replace(',', '')
}
export const formatFrequency = (frequency: number): string => {
let i = -1
const units = [' kHz', ' MHz', ' GHz']

View File

@ -20,24 +20,15 @@ export const getters: GetterTree<RootState, any> = {
if (state.server?.klippy_state !== 'ready') return i18n.t('App.Titles.Error')
else if (printer_state === 'paused') return i18n.t('App.Titles.Pause')
else if (printer_state === 'printing') {
const eta = getters['printer/getEstimatedTimeETA']
if (eta) {
const date = new Date(eta)
const h = date.getHours() >= 10 ? date.getHours() : '0' + date.getHours()
const m = date.getMinutes() >= 10 ? date.getMinutes() : '0' + date.getMinutes()
const diff = eta - new Date().getTime()
const eta = getters['printer/getEstimatedTimeETAFormat']
if (eta !== '--')
return i18n.t('App.Titles.PrintingETA', {
percent: (getters['printer/getPrintPercent'] * 100).toFixed(0),
filename: state.printer.print_stats?.filename,
eta:
h +
':' +
m +
(diff > 60 * 60 * 24 * 1000 ? '+' + (diff / (60 * 60 * 24 * 1000)).toFixed() : ''),
eta,
})
} else
else
return i18n.t('App.Titles.Printing', {
percent: (getters['printer/getPrintPercent'] * 100).toFixed(0),
filename: state.printer.print_stats?.filename,

View File

@ -69,4 +69,17 @@ export const getters: GetterTree<GuiState, any> = {
return 'm84'
},
getHours12Format: (state) => {
const setting = state.general.timeFormat
if (setting === '12hours') return true
if (setting === null) {
const browserLocale =
navigator.languages && navigator.languages.length ? navigator.languages[0] : navigator.language
if (browserLocale === 'en_us') return true
}
return false
},
}

View File

@ -19,6 +19,8 @@ export const getDefaultState = (): GuiState => {
general: {
printername: '',
language: 'en',
dateFormat: null,
timeFormat: null,
calcPrintProgress: 'file-relative',
calcEstimateTime: ['file', 'filament'],
calcEtaTime: ['file', 'filament', 'slicer'],

View File

@ -10,6 +10,8 @@ export interface GuiState {
general: {
printername: string
language: string
dateFormat: string | null
timeFormat: string | null
calcPrintProgress: 'file-relative' | 'file-absolute' | 'slicer' | 'filament'
calcEstimateTime: string[] // file, filament are possible values
calcEtaTime: string[] // file, filament, slicer are possible values

View File

@ -958,6 +958,30 @@ export const getters: GetterTree<PrinterState, RootState> = {
return 0
},
getEstimatedTimeETAFormat: (state, getters, rootState, rootGetters) => {
const hours12Format = rootGetters['gui/getHours12Format'] ?? false
const eta = getters['getEstimatedTimeETA']
if (eta === 0) return '--'
const date = new Date(eta)
let am = true
let h: string | number = date.getHours()
if (hours12Format && h > 12) {
am = false
h -= 12
}
if (h < 10) h = '0' + h
const m = date.getMinutes() >= 10 ? date.getMinutes() : '0' + date.getMinutes()
const diff = eta - new Date().getTime()
let output = h + ':' + m
if (hours12Format) output += ` ${am ? 'AM' : 'PM'}`
if (diff > 60 * 60 * 24 * 1000) output += `+${Math.trunc(diff / (60 * 60 * 24 * 1000))}`
return output
},
getToolchangeMacros: (state, getters) => {
const macros = getters['getMacros']
const tools: PrinterStateToolchangeMacro[] = []

View File

@ -1,6 +1,6 @@
import { GetterTree } from 'vuex'
import { ServerState, ServerStateNetworkInterface } from '@/store/server/types'
import { formatConsoleMessage, formatFilesize, formatTime } from '@/plugins/helpers'
import { formatConsoleMessage, formatFilesize } from '@/plugins/helpers'
// eslint-disable-next-line
export const getters: GetterTree<ServerState, any> = {
@ -22,7 +22,6 @@ export const getters: GetterTree<ServerState, any> = {
events.unshift({
date: date,
formatTime: formatTime(date),
message: message,
formatMessage: formatConsoleMessage(message),
type: 'response',

View File

@ -2,7 +2,7 @@ import Vue from 'vue'
import { getDefaultState } from './index'
import { MutationTree } from 'vuex'
import { ServerState } from '@/store/server/types'
import { formatConsoleMessage, formatTime } from '@/plugins/helpers'
import { formatConsoleMessage } from '@/plugins/helpers'
import { maxEventHistory } from '@/store/variables'
export const mutations: MutationTree<ServerState> = {
@ -105,7 +105,6 @@ export const mutations: MutationTree<ServerState> = {
state.events.push({
date: date,
formatTime: formatTime(date),
message: message.message,
formatMessage: formatMessage,
type: message.type,
@ -126,7 +125,6 @@ export const mutations: MutationTree<ServerState> = {
state.events.push({
date: payload.date,
formatTime: formatTime(payload.date),
message: payload.message,
formatMessage: payload.formatMessage,
type: payload.type,

View File

@ -66,7 +66,6 @@ export interface ServerState {
export interface ServerStateEvent {
date: Date
time?: number
formatTime: string
type: string
message: string
formatMessage: string | string[]