Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 54-improve-discoveries-page
  • 67-improve-app-metadata
  • 7-add-lastfm-link
  • 82-reword-settings
  • master
  • Release_0.0.10_10
  • Release_0.0.11_11
  • Release_0.0.12_12
  • Release_0.0.13_13
  • Release_0.0.14_14
  • Release_0.0.15_15
  • Release_0.0.16_16
  • Release_0.0.17_17
  • Release_0.0.18_18
  • Release_0.0.19_19
  • Release_0.0.1_1
  • Release_0.0.20_20
  • Release_0.0.21_21
  • Release_0.0.22_22
  • Release_0.0.23_23
  • Release_0.0.24_24
  • Release_0.0.25_25
  • Release_0.0.26_26
  • Release_0.0.27_27
  • Release_0.0.28_28
  • Release_0.0.29_29
  • Release_0.0.2_2
  • Release_0.0.30_30
  • Release_0.0.31_31
  • Release_0.0.32_32
  • Release_0.0.33_33
  • Release_0.0.34_34
  • Release_0.0.35_35
  • Release_0.0.36_36
  • Release_0.0.37_37
  • Release_0.0.38_38
  • Release_0.0.39_39
  • Release_0.0.3_3
  • Release_0.0.40_40
  • Release_0.0.41_41
  • Release_0.0.42_42
  • Release_0.0.43_43
  • Release_0.0.44_44
  • Release_0.0.45_45
  • Release_0.0.46_46
  • Release_0.0.47_47
  • Release_0.0.48_48
  • Release_0.0.49_49
  • Release_0.0.4_4
  • Release_0.0.50_50
  • Release_0.0.51_51
  • Release_0.0.52_52
  • Release_0.0.53_53
  • Release_0.0.54_54
  • Release_0.0.55_55
  • Release_0.0.56_56
  • Release_0.0.57_57
  • Release_0.0.5_5
  • Release_0.0.6_6
  • Release_0.0.7_7
  • Release_0.0.8_8
  • Release_0.0.9_9
  • Release_0.1.0_58
  • Release_0.1.1_59
  • Release_0.1.2_60
  • Release_0.2.0_61
  • Release_0.2.1_62
  • Release_0.2.2_63
  • Release_0.3.0_64
  • Release_0.3.1_65
  • Release_0.4.0_66
  • Release_0.4.1_67
  • Release_0.4.2_68
  • Release_0.5.0_69
  • Release_0.6.0_70
  • Release_0.7.0_71
  • Release_0.8.0_72
  • Release_0.8.1_73
  • Release_0.8.2_74
  • Release_0.8.3_75
  • Release_0.8.4_76
  • Release_0.9.0_77
  • Release_0.9.1_78
83 results

Target

Select target project
  • android/org.benoitharrault.scrobbles
1 result
Select Git revision
  • 54-improve-discoveries-page
  • 67-improve-app-metadata
  • 7-add-lastfm-link
  • 82-reword-settings
  • master
  • Release_0.0.10_10
  • Release_0.0.11_11
  • Release_0.0.12_12
  • Release_0.0.13_13
  • Release_0.0.14_14
  • Release_0.0.15_15
  • Release_0.0.16_16
  • Release_0.0.17_17
  • Release_0.0.18_18
  • Release_0.0.19_19
  • Release_0.0.1_1
  • Release_0.0.20_20
  • Release_0.0.21_21
  • Release_0.0.22_22
  • Release_0.0.23_23
  • Release_0.0.24_24
  • Release_0.0.25_25
  • Release_0.0.26_26
  • Release_0.0.27_27
  • Release_0.0.28_28
  • Release_0.0.29_29
  • Release_0.0.2_2
  • Release_0.0.30_30
  • Release_0.0.31_31
  • Release_0.0.32_32
  • Release_0.0.33_33
  • Release_0.0.34_34
  • Release_0.0.35_35
  • Release_0.0.36_36
  • Release_0.0.37_37
  • Release_0.0.38_38
  • Release_0.0.39_39
  • Release_0.0.3_3
  • Release_0.0.40_40
  • Release_0.0.41_41
  • Release_0.0.42_42
  • Release_0.0.43_43
  • Release_0.0.44_44
  • Release_0.0.45_45
  • Release_0.0.46_46
  • Release_0.0.47_47
  • Release_0.0.48_48
  • Release_0.0.49_49
  • Release_0.0.4_4
  • Release_0.0.50_50
  • Release_0.0.51_51
  • Release_0.0.52_52
  • Release_0.0.53_53
  • Release_0.0.54_54
  • Release_0.0.55_55
  • Release_0.0.56_56
  • Release_0.0.57_57
  • Release_0.0.5_5
  • Release_0.0.6_6
  • Release_0.0.7_7
  • Release_0.0.8_8
  • Release_0.0.9_9
  • Release_0.1.0_58
  • Release_0.1.1_59
  • Release_0.1.2_60
  • Release_0.2.0_61
  • Release_0.2.1_62
  • Release_0.2.2_63
  • Release_0.3.0_64
  • Release_0.3.1_65
  • Release_0.4.0_66
  • Release_0.4.1_67
  • Release_0.4.2_68
  • Release_0.5.0_69
  • Release_0.6.0_70
  • Release_0.7.0_71
  • Release_0.8.0_72
  • Release_0.8.1_73
  • Release_0.8.2_74
  • Release_0.8.3_75
  • Release_0.8.4_76
  • Release_0.9.0_77
  • Release_0.9.1_78
83 results
Show changes

Commits on Source 2

org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
app.versionName=0.0.8
app.versionCode=8
app.versionName=0.0.9
app.versionCode=9
......@@ -8,5 +8,13 @@
"statistics_recent_scrobbles_count": "Scrobbles: {count}",
"statistics_discoveries": "Discoveries: {artistsCount} artists / {tracksCount} tracks",
"timeline_title": "Recent scrobbles ({daysCount} days)"
"timeline_title": "Recent scrobbles ({daysCount} days)",
"counts_by_day": "Counts by day ({daysCount} days)",
"MON": "MON",
"TUE": "TUE",
"WED": "WED",
"THU": "THU",
"FRI": "FRI",
"SAT": "SAT",
"SUN": "SUN"
}
......@@ -8,5 +8,13 @@
"statistics_recent_scrobbles_count": "Écoutes : {count}",
"statistics_discoveries": "Découvertes : {artistsCount} artistes / {tracksCount} morceaux",
"timeline_title": "Écoutes récentes ({daysCount} jours)"
"timeline_title": "Écoutes récentes ({daysCount} jours)",
"counts_by_day": "Écoutes par jour ({daysCount} jours)",
"MON": "LUN",
"TUE": "MAR",
"WED": "MER",
"THU": "JEU",
"FRI": "VEN",
"SAT": "SAM",
"SUN": "DIM"
}
Add chart "scrobbles counts per day of week".
Ajout du graphique du nombre d'écoutes par jour de la semaine.
import 'dart:convert';
class CountsByDayData {
final Map<int, double> data;
const CountsByDayData({
required this.data,
});
factory CountsByDayData.fromJson(Map<String, dynamic> json) {
Map<int, double> data = {};
if (json['counts-by-day'] != null) {
json['counts-by-day'].keys.forEach((day) {
data[int.parse(day)] = double.parse(json['counts-by-day'][day].toString());
});
}
return CountsByDayData(data: data);
}
factory CountsByDayData.createEmpty() {
return CountsByDayData.fromJson({});
}
String toString() {
Map<String, double> map = {};
this.data.keys.forEach((day) {
double? value = this.data[day];
map[day.toString()] = value != null ? value.toDouble() : 0.0;
});
return jsonEncode({'counts-by-day': map});
}
}
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../models/counts_by_day.dart';
import '../models/statistics.dart';
import '../models/timeline.dart';
......@@ -27,4 +28,15 @@ class ScrobblesApi {
throw Exception('Failed to get data from API.');
}
}
static Future<CountsByDayData> fetchCountsByDay(int daysCount) async {
final String url = baseUrl + '/data/' + daysCount.toString() + '/counts-by-day';
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
return CountsByDayData.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
} else {
throw Exception('Failed to get data from API.');
}
}
}
import 'package:flutter/material.dart';
import '../widgets/header.dart';
import '../widgets/main_screen/counts_by_day_card.dart';
import '../widgets/main_screen/statistics_card.dart';
import '../widgets/main_screen/timeline_card.dart';
......@@ -19,6 +20,8 @@ class MainScreen extends StatelessWidget {
const StatisticsCard(),
const SizedBox(height: 8),
const ChartTimelineCard(),
const SizedBox(height: 8),
const ChartCountsByDayCard(),
const SizedBox(height: 36),
],
),
......
import 'dart:convert';
import 'package:flutter/material.dart';
import '../../../models/counts_by_day.dart';
import '../../../network/scrobbles_api.dart';
import '../../../ui/widgets/error.dart';
import '../../../ui/widgets/main_screen/counts_by_day_content.dart';
class ChartCountsByDayCard extends StatelessWidget {
const ChartCountsByDayCard({super.key});
@override
Widget build(BuildContext context) {
final int daysCount = 21;
late Future<CountsByDayData> futureCountsByDay = ScrobblesApi.fetchCountsByDay(daysCount);
return FutureBuilder<CountsByDayData>(
future: futureCountsByDay,
builder: (context, snapshot) {
if (snapshot.hasError) {
return ShowErrorWidget(message: '${snapshot.error}');
}
return Card(
elevation: 2,
shadowColor: Theme.of(context).colorScheme.shadow,
color: Theme.of(context).colorScheme.primary,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(12),
),
),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: ChartCountsByDayCardContent(
daysCount: daysCount,
chartData: snapshot.hasData
? CountsByDayData.fromJson(jsonDecode(snapshot.data.toString()))
: CountsByDayData.createEmpty(),
isLoading: !snapshot.hasData,
),
),
);
},
);
}
}
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';
import '../../../config/app_colors.dart';
import '../../../models/counts_by_day.dart';
import '../../../utils/color_extensions.dart';
class CountsByDayCardContentChart extends StatelessWidget {
final CountsByDayData chartData;
const CountsByDayCardContentChart({super.key, required this.chartData});
@override
Widget build(BuildContext context) {
return Container(
height: 100.0,
child: LayoutBuilder(builder: (context, constraints) {
final double maxWidth = constraints.maxWidth;
final int barsCount = this.chartData.data.keys.length;
final double barWidth = 0.7 * (maxWidth / barsCount);
return BarChart(
BarChartData(
barGroups: getDataCounts(barWidth),
backgroundColor: Theme.of(context).colorScheme.onBackground,
borderData: getBorderData(),
gridData: getGridData(),
titlesData: getTitlesData(),
barTouchData: getBarTouchData(),
maxY: getNextRoundNumber(getMaxCountsValue(), 10),
),
);
}),
);
}
double getMaxCountsValue() {
double maxValue = 0;
this.chartData.data.keys.forEach((key) {
double? counts = this.chartData.data[key];
if (counts != null) {
if (counts > maxValue) {
maxValue = counts;
}
}
});
return maxValue;
}
double getNextRoundNumber(double number, int scale) {
return scale * ((number ~/ scale).toInt() + 1);
}
List<BarChartGroupData> getDataCounts(double barWidth) {
List<BarChartGroupData> data = [];
this.chartData.data.keys.forEach((day) {
final double? counts = this.chartData.data[day];
if (counts != null) {
Color barColor = AppColors.contentColorBlack;
switch (day) {
case 1:
barColor = Color.fromARGB(255, 255, 99, 132);
break;
case 2:
barColor = Color.fromARGB(255, 255, 159, 64);
break;
case 3:
barColor = Color.fromARGB(255, 255, 205, 86);
break;
case 4:
barColor = Color.fromARGB(255, 75, 192, 192);
break;
case 5:
barColor = Color.fromARGB(255, 54, 162, 235);
break;
case 6:
barColor = Color.fromARGB(255, 153, 102, 255);
break;
case 7:
barColor = Color.fromARGB(255, 201, 203, 207);
break;
default:
}
data.add(BarChartGroupData(
x: day,
barRods: [
BarChartRodData(
toY: counts,
color: barColor,
width: barWidth,
borderRadius: BorderRadius.all(Radius.zero),
borderSide: BorderSide(
color: barColor.darken(20),
),
),
],
));
}
});
return data;
}
FlBorderData getBorderData() {
return FlBorderData(
show: true,
border: Border.all(
color: AppColors.borderColor,
width: 2,
),
);
}
FlGridData getGridData() {
return const FlGridData(
show: true,
);
}
FlTitlesData getTitlesData() {
const AxisTitles none = const AxisTitles(
sideTitles: SideTitles(showTitles: false),
);
final AxisTitles verticalTitles = AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 30,
getTitlesWidget: getVerticalTitlesWidget,
interval: 10,
),
);
final AxisTitles horizontalTitles = AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 20,
getTitlesWidget: getHorizontalTitlesWidget,
),
);
return FlTitlesData(
show: true,
bottomTitles: horizontalTitles,
leftTitles: none,
topTitles: none,
rightTitles: verticalTitles,
);
}
Widget getVerticalTitlesWidget(double value, TitleMeta meta) {
return SideTitleWidget(
axisSide: meta.axisSide,
space: 4,
child: Text(
value.toInt().toString(),
style: const TextStyle(
color: AppColors.mainTextColor1,
fontSize: 12,
),
),
);
}
Widget getHorizontalTitlesWidget(double value, TitleMeta meta) {
final int day = value.toInt();
String dayShortName = '';
switch (day) {
case 1:
dayShortName = 'MON';
break;
case 2:
dayShortName = 'TUE';
break;
case 3:
dayShortName = 'WED';
break;
case 4:
dayShortName = 'THU';
break;
case 5:
dayShortName = 'FRI';
break;
case 6:
dayShortName = 'SAT';
break;
case 7:
dayShortName = 'SUN';
break;
default:
}
final String text = tr(dayShortName);
return SideTitleWidget(
axisSide: meta.axisSide,
space: 2,
child: Text(
text,
style: const TextStyle(
color: AppColors.mainTextColor1,
fontSize: 11,
),
),
);
}
BarTouchData getBarTouchData() {
return BarTouchData(
enabled: true,
touchTooltipData: BarTouchTooltipData(
tooltipBgColor: Colors.transparent,
tooltipPadding: EdgeInsets.zero,
tooltipMargin: 2,
getTooltipItem: (
BarChartGroupData group,
int groupIndex,
BarChartRodData rod,
int rodIndex,
) {
return BarTooltipItem(
rod.toY.round().toString(),
const TextStyle(
color: AppColors.mainTextColor2,
fontWeight: FontWeight.bold,
fontSize: 10,
),
);
},
),
);
}
}
import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import '../../../models/counts_by_day.dart';
import '../../../ui/widgets/main_screen/counts_by_day_chart.dart';
class ChartCountsByDayCardContent extends StatelessWidget {
final int daysCount;
final CountsByDayData chartData;
final bool isLoading;
const ChartCountsByDayCardContent(
{super.key, required this.daysCount, required this.chartData, required this.isLoading});
@override
Widget build(BuildContext context) {
final TextTheme textTheme = Theme.of(context).primaryTextTheme;
final String placeholder = '⏳';
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'counts_by_day',
style: textTheme.titleLarge!.apply(fontWeightDelta: 2),
).tr(
namedArgs: {
'daysCount': this.daysCount.toString(),
},
),
const SizedBox(height: 8),
this.isLoading
? Text(placeholder)
: CountsByDayCardContentChart(
chartData: CountsByDayData.fromJson(jsonDecode(this.chartData.toString())),
),
],
);
}
}
......@@ -3,7 +3,7 @@ description: Display scrobbles data and charts
publish_to: 'none'
version: 0.0.8+8
version: 0.0.9+9
environment:
sdk: '^3.0.0'
......