Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(graph): resolve home graph issues #124

Merged
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
18 changes: 18 additions & 0 deletions lib/functions/graph.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/// Calculate a suitable interval for the graph
///
/// [topPoint] represents the maximum value on the graph.
///
/// The function returns an interval that divides the range
/// into approximately 5 segments, while ensuring that the interval
/// is not too small (minimum interval is 1.0).
double calcInterval(int topPoint) {
// Default interval when the top point is 0 or negative
if (topPoint <= 0) {
return 1.0;
}

final interval = (topPoint / 5.0).ceilToDouble();

// Ensure the interval is at least 1.0
return interval < 1.0 ? 1.0 : interval;
}
9 changes: 4 additions & 5 deletions lib/screens/home/widgets/clients_last_hours_line.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:pi_hole_client/config/theme.dart';
import 'package:pi_hole_client/functions/format.dart';
import 'package:pi_hole_client/functions/graph.dart';
import 'package:pi_hole_client/providers/app_config_provider.dart';
import 'package:provider/provider.dart';

Expand Down Expand Up @@ -35,14 +36,11 @@ class ClientsLastHoursLine extends StatelessWidget {
}

LineChartData mainData(Map<String, dynamic> data, ThemeMode selectedTheme) {
final double interval = (data['topPoint'] / 5).toDouble() > 0
? (data['topPoint'] / 5).toDouble()
: data['topPoint'].toDouble() > 0
? data['topPoint'].toDouble()
: 1.0;
final interval = calcInterval(data['topPoint']);
return LineChartData(
gridData: FlGridData(
drawVerticalLine: false,
horizontalInterval: interval,
getDrawingHorizontalLine: (value) => FlLine(
color: selectedTheme == ThemeMode.light
? Colors.black12
Expand Down Expand Up @@ -215,6 +213,7 @@ class ClientsLastHoursLine extends StatelessWidget {
timestamps.add(k[i]);
}

/// Dummy data for legend
final flatLine = <FlSpot>[];
var xPosition = 0;
for (var j = 0;
Expand Down
127 changes: 86 additions & 41 deletions lib/screens/home/widgets/queries_last_hours_line.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:pi_hole_client/config/theme.dart';
import 'package:pi_hole_client/functions/format.dart';
import 'package:pi_hole_client/functions/graph.dart';
import 'package:pi_hole_client/providers/app_config_provider.dart';
import 'package:provider/provider.dart';

Expand All @@ -16,16 +17,62 @@
final Map<String, dynamic> data;
final bool reducedData;

/// Create the legend for the tooltip.
List<LineTooltipItem> _createLegend(

Check warning on line 21 in lib/screens/home/widgets/queries_last_hours_line.dart

View check run for this annotation

Codecov / codecov/patch

lib/screens/home/widgets/queries_last_hours_line.dart#L21

Added line #L21 was not covered by tests
Map<String, dynamic> data,
List<LineBarSpot> items,
ThemeMode selectedTheme,
) {
final legend = List<LineTooltipItem>.filled(

Check warning on line 26 in lib/screens/home/widgets/queries_last_hours_line.dart

View check run for this annotation

Codecov / codecov/patch

lib/screens/home/widgets/queries_last_hours_line.dart#L26

Added line #L26 was not covered by tests
3,
const LineTooltipItem('', TextStyle()),
);

for (final item in items) {
switch (item.barIndex) {
case 0:
legend[0] = LineTooltipItem(
formatTimestampForChart(data['time'][item.x.toInt()]),
TextStyle(

Check warning on line 36 in lib/screens/home/widgets/queries_last_hours_line.dart

View check run for this annotation

Codecov / codecov/patch

lib/screens/home/widgets/queries_last_hours_line.dart#L31-L36

Added lines #L31 - L36 were not covered by tests
fontWeight: FontWeight.bold,
fontSize: 14,
color: selectedTheme == ThemeMode.light

Check warning on line 39 in lib/screens/home/widgets/queries_last_hours_line.dart

View check run for this annotation

Codecov / codecov/patch

lib/screens/home/widgets/queries_last_hours_line.dart#L39

Added line #L39 was not covered by tests
? Colors.black
: Colors.white,
),
);
case 1:
legend[1] = LineTooltipItem(
'Blocked: ${item.y.toInt()}',

Check warning on line 46 in lib/screens/home/widgets/queries_last_hours_line.dart

View check run for this annotation

Codecov / codecov/patch

lib/screens/home/widgets/queries_last_hours_line.dart#L44-L46

Added lines #L44 - L46 were not covered by tests
const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: Colors.blue,
),
);
case 2:
legend[2] = LineTooltipItem(
'Not blocked: ${item.y.toInt()}',

Check warning on line 55 in lib/screens/home/widgets/queries_last_hours_line.dart

View check run for this annotation

Codecov / codecov/patch

lib/screens/home/widgets/queries_last_hours_line.dart#L53-L55

Added lines #L53 - L55 were not covered by tests
const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: Colors.green,
),
);
default:
break;
}
}

return legend;
}

LineChartData mainData(
Map<String, dynamic> data,
ThemeMode selectedTheme,
BuildContext context,
) {
final double interval = (data['topPoint'] / 5).toDouble() > 0
? (data['topPoint'] / 5).toDouble()
: data['topPoint'].toDouble() > 0
? data['topPoint'].toDouble()
: 1.0;
final interval = calcInterval(data['topPoint']);
return LineChartData(
gridData: FlGridData(
drawVerticalLine: false,
Expand All @@ -44,8 +91,7 @@
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
interval:
data['topPoint'] == 0 ? 1 : (data['topPoint'] / 5).toDouble(),
interval: interval,
reservedSize: 35,
getTitlesWidget: (value, widget) => Text(
value.toInt().toString(),
Expand Down Expand Up @@ -73,11 +119,13 @@
),
lineBarsData: [
// Hidden bar to allow 3 items on tooltip
// barIndex: 0
LineChartBarData(
spots: data['data']['domains'],
spots: data['data']['flatLines'],
color: Colors.transparent,
barWidth: 0,
),
// barIndex: 1
LineChartBarData(
spots: data['data']['ads'],
color: Theme.of(context).extension<GraphColors>()!.getColor(0),
Expand All @@ -95,6 +143,7 @@
.withValues(alpha: 0.2),
),
),
// barIndex: 2
LineChartBarData(
spots: data['data']['domains'],
color: Theme.of(context).extension<GraphColors>()!.getColor(3),
Expand All @@ -119,55 +168,44 @@
? const Color.fromRGBO(220, 220, 220, 0.9)
: const Color.fromRGBO(35, 35, 35, 0.9),
fitInsideHorizontally: true,
getTooltipItems: (items) => [
LineTooltipItem(
formatTimestampForChart(data['time'][items[0].x.toInt()]),
TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: selectedTheme == ThemeMode.light
? Colors.black
: Colors.white,
),
),
LineTooltipItem(
'Not blocked: ${items[1].y.toInt()}',
const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: Colors.green,
),
),
LineTooltipItem(
'Blocked: ${items[2].y.toInt()}',
const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: Colors.blue,
),
),
],
getTooltipItems: (items) => _createLegend(data, items, selectedTheme),

Check warning on line 171 in lib/screens/home/widgets/queries_last_hours_line.dart

View check run for this annotation

Codecov / codecov/patch

lib/screens/home/widgets/queries_last_hours_line.dart#L171

Added line #L171 was not covered by tests
),
),
);
}

int calcTopPoint(
Map<String, dynamic> data,
List<String> domainsKeys,
List<String> adsKeys,
int index,
) {
final permitted = data['domains_over_time'][domainsKeys[index]] -
data['ads_over_time'][adsKeys[index]];

final blocked = data['ads_over_time'][adsKeys[index]];

return permitted > blocked ? permitted : blocked;
}

@override
Widget build(BuildContext context) {
final appConfigProvider = Provider.of<AppConfigProvider>(context);

Map<String, dynamic> formatData(Map<String, dynamic> data) {
final domains = <FlSpot>[];
final ads = <FlSpot>[];
final flatLines = <FlSpot>[];

var xPosition = 0;
var topPoint = 0;
var tmp = 0;
final List<String> domainsKeys = data['domains_over_time'].keys.toList();
final List<String> adsKeys = data['ads_over_time'].keys.toList();

if (domainsKeys.length != adsKeys.length) {
return {
'data': {'domains': [], 'ads': []},
'data': {'domains': [], 'ads': [], 'flatLines': []},
'topPoint': 0,
'time': [],
'error': 'error',
Expand All @@ -177,9 +215,9 @@
for (var i = 0;
i < data['domains_over_time'].entries.length;
reducedData == true ? i += 6 : i++) {
if (data['domains_over_time'][domainsKeys[i]] > topPoint) {
topPoint = data['domains_over_time'][domainsKeys[i]] -
data['ads_over_time'][adsKeys[i]];
tmp = calcTopPoint(data, domainsKeys, adsKeys, i);
if (tmp > topPoint) {
topPoint = tmp;
}
domains.add(
FlSpot(
Expand All @@ -194,6 +232,13 @@
data['ads_over_time'][adsKeys[i]].toDouble(),
),
);
// Dummy data for legend
flatLines.add(
FlSpot(
xPosition.toDouble(),
0.0,
),
);
xPosition++;
}

Expand All @@ -204,7 +249,7 @@
}

return {
'data': {'domains': domains, 'ads': ads},
'data': {'domains': domains, 'ads': ads, 'flatLines': flatLines},
'topPoint': topPoint,
'time': timestamps,
};
Expand Down