Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 2 additions & 11 deletions src/Options.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,7 @@
<section>
<h2>{{ t('options.headings.stats') }}</h2>
<!-- option: startOfWeek -->
<!-- <div class="entry">
<label for="start">
{{ t("options.startOfWeek.label") }}
<span class="description">{{ t("options.startOfWeek.description") }}</span>
</label>
<div class="action">
<select v-model="options.startOfWeek" id="start">
<option v-for="(name, pos) in weekdayNames(locale)" :key="pos" :value="pos">{{ name }}</option>
</select>
</div>
</div> -->
<start-of-week-option />
<!-- option: addresses -->
<addresses-option />
<!-- option: account selection -->
Expand Down Expand Up @@ -103,6 +93,7 @@ import OrdinateOption from '@/options/OrdinateOption.vue';
import TagColorsOption from '@/options/TagColorsOption.vue';
import LiveCountUpOption from '@/options/LiveCountUpOption.vue';
import AutoRefreshOption from '@/options/AutoRefreshOption.vue';
import StartOfWeekOption from '@/options/StartOfWeekOption.vue';
import AddressesOption from '@/options/AddressesOption.vue';
import AccountsOption from '@/options/AccountsOption.vue';
import SelfMessagesOption from '@/options/SelfMessagesOption.vue';
Expand Down
36 changes: 18 additions & 18 deletions src/charts/MatrixChart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<script setup>
import { computed, onMounted, watch } from 'vue';
import { Chart, color } from '@/chart.config.js';
import { isoDayOfWeek } from '@/utils.js';
import { weekdayNames } from '@/utils.js';
import { getDateFnsLocale } from '@/translations.js';
import { useI18n } from 'vue-i18n';

Expand All @@ -28,15 +28,20 @@ const props = defineProps({
dimension: Object, // {cols, rows}
parseTime: Boolean, // if true, parse values as Date objects
datasets: Array, // [{data: [[date, value], [date, value], ...], label: ''}, ...]
weekdayLabels: Array, // 7 short weekday names, in top-to-bottom row order
});

// Chart.js's category scale renders labels bottom-to-top on a vertical axis,
// so the array needs reversing to make weekdayLabels[0] the top row
const yAxisLabels = computed(() => [...(props.weekdayLabels || [])].reverse());

const processedDatasets = computed(() => {
const data = props.datasets;
data.map((d) => {
d.data = d.data.map((e) => {
return {
x: props.parseTime ? e[0] : new Date(e[0]).getHours(),
y: isoDayOfWeek(new Date(e[0])),
y: weekdayNames(locale.value)[new Date(e[0]).getDay()],
d: props.parseTime
? new Date(e[0]).toLocaleDateString(locale.value, { year: 'numeric', month: 'long', day: 'numeric' })
: new Date(e[0]).toLocaleDateString(locale.value, { weekday: 'long', hour: 'numeric' }),
Expand Down Expand Up @@ -97,23 +102,9 @@ const draw = (localeObject) => {
border: {
display: false,
},
type: 'time',
adapters: {
date: {
locale: localeObject,
},
},
type: 'category',
labels: yAxisLabels.value,
offset: true,
time: {
unit: 'day',
round: 'day',
isoWeekday: true,
parser: 'i',
displayFormats: {
day: 'iiiiii',
},
},
reverse: true,
position: 'left',
ticks: {
maxRotation: 0,
Expand Down Expand Up @@ -184,6 +175,15 @@ watch(
}
}
);

// update weekday row order if the start-of-week option changes
watch(
() => yAxisLabels.value,
(newValue) => {
chart.options.scales.y.labels = newValue;
chart.update();
}
);
</script>

<style>
Expand Down
1 change: 0 additions & 1 deletion src/composables/useActivityChartData.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export function useActivityChartData({ display, activityPrefs, t }) {
s.push([key, sentMap[key] || 0]);
}

// TODO: handle options.startOfWeek
return {
received: { label: t('stats.mailsReceived'), data: r },
sent: { label: t('stats.mailsSent'), data: s },
Expand Down
26 changes: 6 additions & 20 deletions src/composables/useOnedimChartData.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// chart-data shaping for the "onedim" section (daytime/weekday/month bar charts)
import { computed } from 'vue';
import { accentColors } from '@/definitions.js';
import { monthNames, weekdayNames } from '@/utils.js';
import { monthNames, rotateArray, weekdayNames } from '@/utils.js';

export function useOnedimChartData({ display, comparison, accounts, options, t, locale }) {
// prepare data for daytime bar chart
Expand Down Expand Up @@ -45,15 +45,9 @@ export function useOnedimChartData({ display, comparison, accounts, options, t,

// prepare data for weekday bar chart
const weekdayChartData = computed(() => {
const r = Object.values(display.value.weekdayData.received);
const s = Object.values(display.value.weekdayData.sent);
let labels = [...weekdayNames(locale.value)];
// TODO: start week with user defined day of week
for (let d = 0; d < 1 /*options.startOfWeek*/; d++) {
r.push(r.shift());
s.push(s.shift());
labels.push(labels.shift());
}
const r = rotateArray(Object.values(display.value.weekdayData.received), options.startOfWeek);
const s = rotateArray(Object.values(display.value.weekdayData.sent), options.startOfWeek);
const labels = rotateArray(weekdayNames(locale.value), options.startOfWeek);
return {
datasets: [
{
Expand All @@ -73,18 +67,10 @@ export function useOnedimChartData({ display, comparison, accounts, options, t,
// prepare comparison data for weekday bar chart
const weekdayComparedChartData = computed(() => {
let datasets = [];
let labels = [...weekdayNames(locale.value)];
// TODO: labels: start week with user defined day of week
for (let d = 0; d < 1 /*options.startOfWeek*/; d++) {
labels.push(labels.shift());
}
const labels = rotateArray(weekdayNames(locale.value), options.startOfWeek);
// compute dataset for each account
accounts.value.forEach((a) => {
const data = Object.values(comparison.value.weekdayData[a.id]);
// TODO: data: start week with user defined day of week
for (let d = 0; d < 1 /*options.startOfWeek*/; d++) {
data.push(data.shift());
}
const data = rotateArray(Object.values(comparison.value.weekdayData[a.id]), options.startOfWeek);
// add dataset for this account
datasets.push({
label: `${t('popup.nMessages', 2)} - ${a.name}`,
Expand Down
2 changes: 1 addition & 1 deletion src/composables/useStatsData.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export function useStatsData() {
options.liveCountUp = result.options.liveCountUp ?? defaultOptions.liveCountUp;
options.autoRefresh = result.options.autoRefresh ?? defaultOptions.autoRefresh;
options.autoRefreshInterval = result.options.autoRefreshInterval ?? defaultOptions.autoRefreshInterval;
// options.startOfWeek = result.options.startOfWeek ?? defaultOptions.startOfWeek;
options.startOfWeek = result.options.startOfWeek ?? defaultOptions.startOfWeek;
options.addresses = result.options.addresses
? result.options.addresses
.toLowerCase()
Expand Down
1 change: 0 additions & 1 deletion src/composables/useTwodimChartData.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export function useTwodimChartData({ display, t }) {
],
[]
);
// TODO: handle options.startOfWeek
return {
received: { label: t('stats.mailsReceived'), data: r },
sent: { label: t('stats.mailsSent'), data: s },
Expand Down
1 change: 0 additions & 1 deletion src/definitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ const defaultOptions = {
maxListCount: 20,
cache: true,
debug: false,
// startofWeek:
};

// tab navigation for stats page
Expand Down
33 changes: 33 additions & 0 deletions src/options/StartOfWeekOption.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<template>
<div class="entry">
<label for="start">
{{ t('options.startOfWeek.label') }}
<span class="description">{{ t('options.startOfWeek.description') }}</span>
</label>
<div class="action action-row">
<ts-select class="action-select" v-model="options.startOfWeek" id="start">
<option v-for="(name, pos) in weekdayNames(locale, 'long')" :key="pos" :value="pos">{{ name }}</option>
</ts-select>
</div>
</div>
</template>

<script setup>
import { inject } from 'vue';
import { useI18n } from 'vue-i18n';
import { weekdayNames } from '@/utils.js';

const { options } = inject('engine');

const { t, locale } = useI18n();
</script>

<style scoped>
.action-row {
display: flex;
}

.action-select {
flex-grow: 1;
}
</style>
8 changes: 4 additions & 4 deletions src/options/ThemeOption.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
{{ t('options.theme.label') }}
<span class="description">{{ t('options.theme.description') }}</span>
</label>
<div class="action theme-row">
<ts-select class="theme-select" v-model="options.theme" id="theme">
<div class="action action-row">
<ts-select class="action-select" v-model="options.theme" id="theme">
<option v-for="theme in ['system', 'light', 'dark']" :key="theme" :value="theme">
{{ t(`options.theme.${theme}`) }}
</option>
Expand All @@ -24,11 +24,11 @@ const { t } = useI18n();
</script>

<style scoped>
.theme-row {
.action-row {
display: flex;
}

.theme-select {
.action-select {
flex-grow: 1;
}
</style>
12 changes: 9 additions & 3 deletions src/sections/ActivitySection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
:dimension="{ cols: 53, rows: 7 }"
:parseTime="true"
:datasets="[dateChartData.received]"
:weekday-labels="weekdayLabels"
/>
<!-- activity per day sent -->
<matrix-chart
Expand All @@ -64,27 +65,32 @@
:parseTime="true"
:dimension="{ cols: 53, rows: 7 }"
:datasets="[dateChartData.sent]"
:weekday-labels="weekdayLabels"
/>
</div>
</div>
</template>

<script setup>
import { ref, inject } from 'vue';
import { ref, computed, inject } from 'vue';
import { useI18n } from 'vue-i18n';
import { tabsActivity } from '@/definitions.js';
import { rotateArray, weekdayNames } from '@/utils.js';
import { useActivityChartData } from '@/composables/useActivityChartData.js';
import MatrixChart from '@/charts/MatrixChart.vue';
import SectionTabHeader from '@/parts/SectionTabHeader.vue';

const { display, preferences, minYear, maxYear, yearsList, nextYear, previousYear } = inject('engine');
const { display, preferences, options, minYear, maxYear, yearsList, nextYear, previousYear } = inject('engine');

const { t } = useI18n();
const { t, locale } = useI18n();

// tab navigation local to this section
const tabActivity = ref(tabsActivity.days);

const { dateChartData } = useActivityChartData({ display, activityPrefs: preferences.sections.activity, t });

// weekday row order for the matrix charts, respecting the configured start of week
const weekdayLabels = computed(() => rotateArray(weekdayNames(locale.value), options.startOfWeek));
</script>

<style scoped>
Expand Down
12 changes: 9 additions & 3 deletions src/sections/TwodimSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
:dimension="{ cols: 24, rows: 7 }"
:parseTime="false"
:datasets="[weekdayPerHourChartData.received]"
:weekday-labels="weekdayLabels"
/>
<!-- emails per weekday per hour sent -->
<matrix-chart
Expand All @@ -21,25 +22,30 @@
:dimension="{ cols: 24, rows: 7 }"
:parseTime="false"
:datasets="[weekdayPerHourChartData.sent]"
:weekday-labels="weekdayLabels"
/>
</div>
</div>
</template>

<script setup>
import { ref, inject } from 'vue';
import { ref, computed, inject } from 'vue';
import { useI18n } from 'vue-i18n';
import { tabsTwodim } from '@/definitions.js';
import { rotateArray, weekdayNames } from '@/utils.js';
import { useTwodimChartData } from '@/composables/useTwodimChartData.js';
import MatrixChart from '@/charts/MatrixChart.vue';
import SectionTabHeader from '@/parts/SectionTabHeader.vue';

const { display } = inject('engine');
const { display, options } = inject('engine');

const { t } = useI18n();
const { t, locale } = useI18n();

// tab navigation local to this section (single-entry tab enum, kept for template consistency)
const tabTwodim = ref(tabsTwodim.temporalDistribution);

const { weekdayPerHourChartData } = useTwodimChartData({ display, t });

// weekday row order for the matrix charts, respecting the configured start of week
const weekdayLabels = computed(() => rotateArray(weekdayNames(locale.value), options.startOfWeek));
</script>
22 changes: 12 additions & 10 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,13 +245,6 @@ const localDateKey = (d) => {
return `${y}-${m}-${day}`;
};

// return day of week in iso format
const isoDayOfWeek = (d) => {
let wd = d.getDay(); // 0..6, from sunday
wd = ((wd + 6) % 7) + 1; // 1..7 from monday
return String(wd);
};

const startOfToday = () => {
const d = new Date();
return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);
Expand Down Expand Up @@ -288,15 +281,24 @@ const monthNames = (locale = 'en') => {
};

// array of localized, short day of week names
const weekdayNames = (locale = 'en') => {
const weekdayNames = (locale = 'en', weekdayFormat = 'short') => {
let names = [];
for (let wd = 1; wd <= 7; wd++) {
const d = new Date(1970, 1, wd); // choose a date to retrieve weekdays from, starting on a Sunday
names.push(d.toLocaleDateString(locale, { weekday: 'short' }));
names.push(d.toLocaleDateString(locale, { weekday: weekdayFormat }));
}
return names;
};

// rotate array to the left by <n> positions, moving the first <n> elements to the end
const rotateArray = (arr, n) => {
const rotated = [...arr];
for (let i = 0; i < n; i++) {
rotated.push(rotated.shift());
}
return rotated;
};

// format bytes and append unit
const formatBytes = (bytes, decimals = 2) => {
if (bytes === 0) return '0 Bytes';
Expand Down Expand Up @@ -359,7 +361,6 @@ export {
formatBytes,
formatDate,
formatFolder,
isoDayOfWeek,
isSelfMessage,
localDateKey,
localStartOfWeek,
Expand All @@ -371,6 +372,7 @@ export {
pluralUkrainian,
quarterNumber,
queryMessages,
rotateArray,
setTheme,
sortAndLimitObject,
sortAndLimitObjectToArray,
Expand Down
Loading