Skip to content

Commit d5a14b8

Browse files
authored
Merge pull request #64 from veloek/stats-page-mobile
Stats page mobile
2 parents 4d7f8fd + 8799f81 commit d5a14b8

File tree

3 files changed

+122
-66
lines changed

3 files changed

+122
-66
lines changed

Tevling/Pages/Statistics.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
throw new ArgumentOutOfRangeException();
1717
}
1818

19-
<InputSelect @bind-Value="@NumberOfPeriodsToReview" @oninput="DrawChart" class="mb-2">
19+
<InputSelect @bind-Value="@NumberOfPeriodsToReview" @bind-Value:after="DrawChart" class="mb-2">
2020
@for (int i = 3; i <= 36; i += 3)
2121
{
2222
<option value="@i">@i</option>

Tevling/Pages/Statistics.razor.cs

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public string IncreaseVsDecrease(string increase, string decrease)
5555
private Athlete _athlete = null!;
5656
private Activity[] _activities = [];
5757
private IJSObjectReference? _module;
58+
private IJSObjectReference? _resizeHandler;
5859

5960
private TimePeriod TimePeriod { get; set; } = TimePeriod.Months;
6061
private int NumberOfPeriodsToReview { get; set; } = 3;
@@ -191,11 +192,30 @@ .. GetAggregatedMeasurementData(
191192
];
192193
}
193194

195+
private Dictionary<string, float[]> AggregateTotals(IReadOnlyList<Stats> stats)
196+
{
197+
float[] totalStats = new float[NumberOfPeriodsToReview];
198+
foreach (Stats stat in stats)
199+
{
200+
for (int i = 0; i < stat.LastTimePeriodAggregate.Length; i++)
201+
{
202+
totalStats[i] += stat.LastTimePeriodAggregate[i];
203+
}
204+
}
205+
206+
return new Dictionary<string, float[]>
207+
{
208+
{
209+
"Total", totalStats
210+
},
211+
};
212+
}
213+
194214
private async Task DrawChart()
195215
{
196-
_module = await Js.InvokeAsync<IJSObjectReference>("import", "./Pages/Statistics.razor.js");
216+
_module ??= await Js.InvokeAsync<IJSObjectReference>("import", "./Pages/Statistics.razor.js");
197217

198-
string[] months = TimePeriod switch
218+
string[] timePeriodArray = TimePeriod switch
199219
{
200220
TimePeriod.Months => CreateMonthArray(NumberOfPeriodsToReview),
201221
TimePeriod.Weeks => CreateWeekArray(NumberOfPeriodsToReview),
@@ -204,44 +224,60 @@ private async Task DrawChart()
204224

205225
await UpdateMeasurementData();
206226

227+
bool isMobile = await _module.InvokeAsync<bool>("isMobile");
207228
switch (Measurement)
208229
{
209230
case ChallengeMeasurement.Distance:
210231
await _module.InvokeVoidAsync(
211232
"drawChart",
212-
Distances.ToDictionary(stat => stat.Type, stat => stat.LastTimePeriodAggregate),
213-
months,
233+
isMobile
234+
? AggregateTotals(Distances)
235+
: Distances.ToDictionary(stat => stat.Type, stat => stat.LastTimePeriodAggregate),
236+
timePeriodArray,
214237
"TheChart",
215238
Loc["TotalDistance"] + " [km]",
216239
"km");
217240
break;
218241
case ChallengeMeasurement.Elevation:
219242
await _module.InvokeVoidAsync(
220243
"drawChart",
221-
Elevations.ToDictionary(stat => stat.Type, stat => stat.LastTimePeriodAggregate),
222-
months,
244+
isMobile
245+
? AggregateTotals(Elevations)
246+
: Elevations.ToDictionary(stat => stat.Type, stat => stat.LastTimePeriodAggregate),
247+
timePeriodArray,
223248
"TheChart",
224249
Loc["TotalElevation"] + " [m]",
225250
"m");
226251
break;
227252
case ChallengeMeasurement.Time:
228253
await _module.InvokeVoidAsync(
229254
"drawChart",
230-
Durations.ToDictionary(stat => stat.Type, stat => stat.LastTimePeriodAggregate),
231-
months,
255+
isMobile
256+
? AggregateTotals(Durations)
257+
: Durations.ToDictionary(stat => stat.Type, stat => stat.LastTimePeriodAggregate),
258+
timePeriodArray,
232259
"TheChart",
233260
Loc["TotalTime"] + " [h]",
234261
"h");
235262
break;
236263
default:
237264
throw new Exception("Unknown challenge measurement");
238265
}
266+
267+
_resizeHandler ??= await _module.InvokeAsync<IJSObjectReference>("enableCanvasResize", "TheChart");
239268
}
240269

241270
public async ValueTask DisposeAsync()
242271
{
243272
try
244273
{
274+
if (_resizeHandler != null)
275+
{
276+
await _resizeHandler.InvokeVoidAsync("dispose");
277+
await _resizeHandler.DisposeAsync();
278+
_resizeHandler = null;
279+
}
280+
245281
if (_module != null)
246282
{
247283
await _module.DisposeAsync();

Tevling/Pages/Statistics.razor.js

Lines changed: 77 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,90 @@
11
export function drawChart(activityData, labels, chartName, chartTitle, unit) {
2-
var ctx = document.getElementById(chartName).getContext("2d");
2+
var ctx = document.getElementById(chartName).getContext("2d");
33

4-
// Destroy existing chart instance if it exists
5-
if (window[chartName] instanceof Chart) {
6-
window[chartName].destroy();
7-
}
4+
// Destroy existing chart instance if it exists
5+
if (window[chartName] instanceof Chart) {
6+
window[chartName].destroy();
7+
}
88

9-
let datasets = [];
9+
let datasets = [];
1010

11-
for (const [key, value] of Object.entries(activityData)) {
12-
datasets.push({
13-
label: key,
14-
data: value,
15-
});
16-
}
11+
for (const [key, value] of Object.entries(activityData)) {
12+
datasets.push({
13+
label: key,
14+
data: value,
15+
});
16+
}
1717

18-
window[chartName] = new Chart(ctx, {
19-
type: "bar",
20-
data: {
21-
labels: labels,
22-
datasets: datasets,
23-
},
24-
options: {
25-
plugins: {
26-
title: {
27-
display: true,
28-
text: chartTitle,
29-
color: "rgba(54, 162, 235, 1)",
30-
font: {
31-
size: 20,
32-
},
18+
window[chartName] = new Chart(ctx, {
19+
type: "bar",
20+
data: {
21+
labels: labels,
22+
datasets: datasets,
3323
},
34-
tooltip: {
35-
callbacks: {
36-
label: (context) => {
37-
let label = context.dataset.label || "";
24+
options: {
25+
plugins: {
26+
title: {
27+
display: true,
28+
text: chartTitle,
29+
color: "rgba(54, 162, 235, 1)",
30+
font: {
31+
size: 20,
32+
},
33+
},
34+
tooltip: {
35+
callbacks: {
36+
label: (context) => {
37+
let label = context.dataset.label || "";
3838

39-
if (label) {
40-
label += ": ";
41-
}
39+
if (label) {
40+
label += ": ";
41+
}
4242

43-
if (context.parsed.y !== null) {
44-
label += Number.isInteger(context.parsed.y)
45-
? context.parsed.y
46-
: context.parsed.y.toFixed(1);
47-
}
43+
if (context.parsed.y !== null) {
44+
label += Number.isInteger(context.parsed.y)
45+
? context.parsed.y
46+
: context.parsed.y.toFixed(1);
47+
}
4848

49-
if (unit) {
50-
label += " " + unit;
51-
}
49+
if (unit) {
50+
label += " " + unit;
51+
}
5252

53-
return label;
53+
return label;
54+
},
55+
},
56+
},
57+
},
58+
responsive: true,
59+
scales: {
60+
x: {
61+
stacked: true,
62+
},
63+
y: {
64+
stacked: true,
65+
beginAtZero: true,
66+
},
5467
},
55-
},
56-
},
57-
},
58-
responsive: true,
59-
scales: {
60-
x: {
61-
stacked: true,
62-
},
63-
y: {
64-
stacked: true,
65-
beginAtZero: true,
6668
},
67-
},
68-
},
69-
});
69+
});
7070
}
71+
72+
export function isMobile() {
73+
return window.matchMedia('(max-width: 576px)').matches;
74+
}
75+
76+
export function enableCanvasResize(chartName) {
77+
// Add event listener for window resize
78+
const handleCanvasResize = () => {
79+
const canvas = document.getElementById(chartName);
80+
if (canvas && window[chartName] instanceof Chart) {
81+
window[chartName].resize(); // Resize Chart.js instance on canvas resize
82+
}
83+
}
84+
window.addEventListener('resize', handleCanvasResize);
85+
86+
return {
87+
dispose: () => window.removeEventListener('resize', handleCanvasResize)
88+
};
89+
}
90+

0 commit comments

Comments
 (0)