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

Target

Select target project
  • android/org.benoitharrault.scrobbles
1 result
Show changes
Showing
with 580 additions and 73 deletions
import 'dart:convert'; import 'dart:convert';
class StatisticsGlobalData { class StatisticsGlobalData {
final int totalCount; final int? totalCount;
final DateTime lastScrobble; final DateTime? lastScrobble;
const StatisticsGlobalData({ const StatisticsGlobalData({
required this.totalCount, required this.totalCount,
required this.lastScrobble, required this.lastScrobble,
}); });
factory StatisticsGlobalData.fromJson(Map<String, dynamic> json) { factory StatisticsGlobalData.fromJson(Map<String, dynamic>? json) {
return StatisticsGlobalData( return StatisticsGlobalData(
totalCount: json['totalCount'] != null ? json['totalCount'] as int : 0, totalCount: (json?['totalCount'] != null) ? (json?['totalCount'] as int) : null,
lastScrobble: (json['lastScrobble'] != null && json['lastScrobble']['date'] != null) lastScrobble: (json?['lastScrobble'] != null && json?['lastScrobble']['date'] != null)
? DateTime.parse( ? DateTime.parse(
json['lastScrobble']['date'], json?['lastScrobble']['date'],
) )
: DateTime.now(), : null,
); );
} }
factory StatisticsGlobalData.createEmpty() { Map<String, Object?>? toJson() {
return StatisticsGlobalData.fromJson({}); return <String, Object?>{
}
String toString() {
Map<String, dynamic> map = {
'totalCount': this.totalCount, 'totalCount': this.totalCount,
'lastScrobble': { 'lastScrobble': {
'date': this.lastScrobble.toString(), 'date': this.lastScrobble != null ? this.lastScrobble.toString() : null,
}, },
}; };
}
return jsonEncode(map); String toString() {
return jsonEncode(this.toJson());
} }
} }
import 'dart:convert'; import 'dart:convert';
class StatisticsRecentData { class StatisticsRecentData {
final int recentCount; final int? recentCount;
final int firstPlayedArtistsCount; final int? firstPlayedArtistsCount;
final int firstPlayedTracksCount; final int? firstPlayedTracksCount;
final int selectedPeriod; final int? selectedPeriod;
const StatisticsRecentData({ const StatisticsRecentData({
required this.recentCount, required this.recentCount,
...@@ -13,29 +13,30 @@ class StatisticsRecentData { ...@@ -13,29 +13,30 @@ class StatisticsRecentData {
required this.selectedPeriod, required this.selectedPeriod,
}); });
factory StatisticsRecentData.fromJson(Map<String, dynamic> json) { factory StatisticsRecentData.fromJson(Map<String, dynamic>? json) {
return StatisticsRecentData( return StatisticsRecentData(
recentCount: json['recentCount'] != null ? json['recentCount'] as int : 0, recentCount: (json?['recentCount'] != null) ? (json?['recentCount'] as int) : null,
firstPlayedArtistsCount: firstPlayedArtistsCount: (json?['firstPlayedArtistsCount'] != null)
json['firstPlayedArtistsCount'] != null ? json['firstPlayedArtistsCount'] as int : 0, ? (json?['firstPlayedArtistsCount'] as int)
firstPlayedTracksCount: : null,
json['firstPlayedTracksCount'] != null ? json['firstPlayedTracksCount'] as int : 0, firstPlayedTracksCount: (json?['firstPlayedTracksCount'] != null)
selectedPeriod: json['selectedPeriod'] != null ? json['selectedPeriod'] as int : 0, ? (json?['firstPlayedTracksCount'] as int)
: null,
selectedPeriod:
(json?['selectedPeriod'] != null) ? (json?['selectedPeriod'] as int) : null,
); );
} }
factory StatisticsRecentData.createEmpty() { Map<String, Object?>? toJson() {
return StatisticsRecentData.fromJson({}); return <String, Object?>{
}
String toString() {
Map<String, dynamic> map = {
'recentCount': this.recentCount, 'recentCount': this.recentCount,
'firstPlayedArtistsCount': this.firstPlayedArtistsCount, 'firstPlayedArtistsCount': this.firstPlayedArtistsCount,
'firstPlayedTracksCount': this.firstPlayedTracksCount, 'firstPlayedTracksCount': this.firstPlayedTracksCount,
'selectedPeriod': this.selectedPeriod, 'selectedPeriod': this.selectedPeriod,
}; };
}
return jsonEncode(map); String toString() {
return jsonEncode(this.toJson());
} }
} }
...@@ -6,10 +6,10 @@ class TimelineDataValue { ...@@ -6,10 +6,10 @@ class TimelineDataValue {
const TimelineDataValue({required this.counts, required this.eclecticism}); const TimelineDataValue({required this.counts, required this.eclecticism});
factory TimelineDataValue.fromJson(Map<String, dynamic> json) { factory TimelineDataValue.fromJson(Map<String, dynamic>? json) {
return TimelineDataValue( return TimelineDataValue(
counts: json['counts'] as int, counts: json?['counts'] as int,
eclecticism: json['eclecticism'] as int, eclecticism: json?['eclecticism'] as int,
); );
} }
} }
...@@ -21,10 +21,10 @@ class TimelineData { ...@@ -21,10 +21,10 @@ class TimelineData {
required this.data, required this.data,
}); });
factory TimelineData.fromJson(Map<String, dynamic> json) { factory TimelineData.fromJson(Map<String, dynamic>? json) {
Map<String, TimelineDataValue> data = {}; Map<String, TimelineDataValue> data = {};
json.keys.forEach((date) { json?.keys.forEach((date) {
TimelineDataValue value = TimelineDataValue( TimelineDataValue value = TimelineDataValue(
counts: json[date]['counts'] as int, counts: json[date]['counts'] as int,
eclecticism: json[date]['eclecticism'] as int, eclecticism: json[date]['eclecticism'] as int,
...@@ -36,11 +36,7 @@ class TimelineData { ...@@ -36,11 +36,7 @@ class TimelineData {
return TimelineData(data: data); return TimelineData(data: data);
} }
factory TimelineData.createEmpty() { Map<String, Object?>? toJson() {
return TimelineData.fromJson({});
}
String toString() {
Map<String, Map<String, int>> map = {}; Map<String, Map<String, int>> map = {};
this.data.keys.forEach((element) { this.data.keys.forEach((element) {
...@@ -51,6 +47,10 @@ class TimelineData { ...@@ -51,6 +47,10 @@ class TimelineData {
}; };
}); });
return jsonEncode(map); return map;
}
String toString() {
return jsonEncode(this.toJson());
} }
} }
import 'dart:convert';
class TopArtistsDataValue {
final String artistName;
final int count;
const TopArtistsDataValue({required this.artistName, required this.count});
factory TopArtistsDataValue.fromJson(Map<String, dynamic>? json) {
return TopArtistsDataValue(
artistName: json?['artistName'] as String,
count: json?['count'] as int,
);
}
}
class TopArtistsData {
final List<TopArtistsDataValue> topArtists;
const TopArtistsData({
required this.topArtists,
});
factory TopArtistsData.fromJson(Map<String, dynamic>? json) {
List<TopArtistsDataValue> topArtists = [];
json?['top-artists'].forEach((element) {
TopArtistsDataValue value = TopArtistsDataValue(
artistName: element['artistName'] as String,
count: element['count'] as int,
);
topArtists.add(value);
});
return TopArtistsData(
topArtists: topArtists,
);
}
Map<String, Object?>? toJson() {
List<Map<String, Object>> listArtists = [];
this.topArtists.forEach((TopArtistsDataValue? item) {
listArtists.add({
'artistName': item != null ? item.artistName : '',
'count': item != null ? item.count : 0,
});
});
return {
'top-artists': listArtists,
};
}
String toString() {
return jsonEncode(this.toJson());
}
}
import 'dart:convert'; import 'dart:convert';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import '../models/counts_by_day.dart'; import 'package:scrobbles/models/counts_by_day.dart';
import '../models/counts_by_hour.dart'; import 'package:scrobbles/models/counts_by_hour.dart';
import '../models/discoveries.dart'; import 'package:scrobbles/models/discoveries.dart';
import '../models/statistics_global.dart'; import 'package:scrobbles/models/statistics_global.dart';
import '../models/statistics_recent.dart'; import 'package:scrobbles/models/statistics_recent.dart';
import '../models/timeline.dart'; import 'package:scrobbles/models/timeline.dart';
import 'package:scrobbles/models/topartists.dart';
class ScrobblesApi { class ScrobblesApi {
static String baseUrl = 'https://scrobble.harrault.fr'; static String baseUrl = 'https://scrobble.harrault.fr';
...@@ -82,4 +83,16 @@ class ScrobblesApi { ...@@ -82,4 +83,16 @@ class ScrobblesApi {
throw Exception('Failed to get data from API.'); throw Exception('Failed to get data from API.');
} }
} }
static Future<TopArtistsData> fetchTopArtists(int daysCount) async {
final String url = baseUrl + '/data/' + daysCount.toString() + '/top-artists';
print('fetching ' + url);
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
return TopArtistsData.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
} else {
throw Exception('Failed to get data from API.');
}
}
} }
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../widgets/main_screen/discoveries_card.dart'; import 'package:scrobbles/ui/widgets/cards/discoveries.dart';
class DiscoveriesScreen extends StatefulWidget { class ScreenDiscoveries extends StatefulWidget {
const DiscoveriesScreen({super.key}); const ScreenDiscoveries({super.key});
@override @override
State<DiscoveriesScreen> createState() => _DiscoveriesScreenState(); State<ScreenDiscoveries> createState() => _ScreenDiscoveriesState();
} }
class _DiscoveriesScreenState extends State<DiscoveriesScreen> { class _ScreenDiscoveriesState extends State<ScreenDiscoveries> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Material( return Material(
...@@ -19,7 +19,7 @@ class _DiscoveriesScreenState extends State<DiscoveriesScreen> { ...@@ -19,7 +19,7 @@ class _DiscoveriesScreenState extends State<DiscoveriesScreen> {
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
children: <Widget>[ children: <Widget>[
const SizedBox(height: 8), const SizedBox(height: 8),
const ChartDiscoveriesCard(), const CardDiscoveries(),
const SizedBox(height: 36), const SizedBox(height: 36),
], ],
), ),
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../widgets/main_screen/statistics_global_card.dart'; import 'package:scrobbles/ui/widgets/cards/statistics_global.dart';
import '../widgets/main_screen/statistics_recent_card.dart'; import 'package:scrobbles/ui/widgets/cards/statistics_recent.dart';
import '../widgets/main_screen/timeline_card.dart'; import 'package:scrobbles/ui/widgets/cards/timeline.dart';
import 'package:scrobbles/ui/widgets/cards/top_artists.dart';
class HomeScreen extends StatefulWidget { class ScreenHome extends StatefulWidget {
const HomeScreen({super.key}); const ScreenHome({super.key});
@override @override
State<HomeScreen> createState() => _HomeScreenState(); State<ScreenHome> createState() => _ScreenHomeState();
} }
class _HomeScreenState extends State<HomeScreen> { class _ScreenHomeState extends State<ScreenHome> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Material( return Material(
...@@ -21,11 +22,13 @@ class _HomeScreenState extends State<HomeScreen> { ...@@ -21,11 +22,13 @@ class _HomeScreenState extends State<HomeScreen> {
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
children: <Widget>[ children: <Widget>[
const SizedBox(height: 8), const SizedBox(height: 8),
const StatisticsGlobalCard(), const CardStatisticsGlobal(),
const SizedBox(height: 6), const SizedBox(height: 6),
const StatisticsRecentCard(), const CardStatisticsRecent(),
const SizedBox(height: 6), const SizedBox(height: 6),
const ChartTimelineCard(), const CardTimeline(),
const SizedBox(height: 6),
const CardTopArtists(),
const SizedBox(height: 36), const SizedBox(height: 36),
], ],
), ),
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../widgets/main_screen/counts_by_day_card.dart'; import 'package:scrobbles/ui/widgets/cards/counts_by_day.dart';
import '../widgets/main_screen/counts_by_hour_card.dart'; import 'package:scrobbles/ui/widgets/cards/counts_by_hour.dart';
class StatisticsScreen extends StatefulWidget { class ScreenStatistics extends StatefulWidget {
const StatisticsScreen({super.key}); const ScreenStatistics({super.key});
@override @override
State<StatisticsScreen> createState() => _StatisticsScreenState(); State<ScreenStatistics> createState() => _ScreenStatisticsState();
} }
class _StatisticsScreenState extends State<StatisticsScreen> { class _ScreenStatisticsState extends State<ScreenStatistics> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Material( return Material(
...@@ -20,9 +20,9 @@ class _StatisticsScreenState extends State<StatisticsScreen> { ...@@ -20,9 +20,9 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
children: <Widget>[ children: <Widget>[
const SizedBox(height: 8), const SizedBox(height: 8),
const ChartCountsByDayCard(), const CardCountsByDay(),
const SizedBox(height: 6), const SizedBox(height: 6),
const ChartCountsByHourCard(), const CardCountsByHour(),
const SizedBox(height: 36), const SizedBox(height: 36),
], ],
), ),
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_swipe/flutter_swipe.dart';
import '../../cubit/bottom_nav_cubit.dart'; import 'package:scrobbles/cubit/bottom_nav_cubit.dart';
import '../widgets/app_bar.dart'; import 'package:scrobbles/ui/screens/discoveries.dart';
import '../widgets/bottom_nav_bar.dart'; import 'package:scrobbles/ui/screens/home.dart';
import 'package:scrobbles/ui/screens/statistics.dart';
import 'discoveries_screen.dart'; import 'package:scrobbles/ui/widgets/app_bar.dart';
import 'home_screen.dart'; import 'package:scrobbles/ui/widgets/bottom_nav_bar.dart';
import 'statistics_screen.dart';
class SkeletonScreen extends StatefulWidget { class SkeletonScreen extends StatefulWidget {
const SkeletonScreen({super.key}); const SkeletonScreen({super.key});
...@@ -20,37 +20,40 @@ class _SkeletonScreenState extends State<SkeletonScreen> { ...@@ -20,37 +20,40 @@ class _SkeletonScreenState extends State<SkeletonScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
const List<Widget> pageNavigation = <Widget>[ const List<Widget> pageNavigation = <Widget>[
const HomeScreen(), const ScreenHome(),
const DiscoveriesScreen(), const ScreenDiscoveries(),
const StatisticsScreen(), const ScreenStatistics(),
]; ];
return BlocProvider<BottomNavCubit>( return BlocProvider<BottomNavCubit>(
create: (BuildContext context) => BottomNavCubit(), create: (BuildContext context) => BottomNavCubit(),
child: BlocBuilder<BottomNavCubit, int>( child: BlocBuilder<BottomNavCubit, int>(
builder: (BuildContext context, int state) { builder: (BuildContext context, int state) {
return GestureDetector( return Scaffold(
onHorizontalDragEnd: (dragDetail) { appBar: StandardAppBar(notifyParent: refresh),
if (dragDetail.velocity.pixelsPerSecond.dx < 1) { extendBodyBehindAppBar: false,
context.read<BottomNavCubit>().moveNext(); body: Swiper(
} else { itemCount: BlocProvider.of<BottomNavCubit>(context).pagesCount,
context.read<BottomNavCubit>().movePrevious(); itemBuilder: (BuildContext context, int index) {
} return AnimatedSwitcher(
}, duration: const Duration(milliseconds: 300),
child: Scaffold( child: pageNavigation.elementAt(index),
appBar: StandardAppBar(notifyParent: refresh), );
extendBodyBehindAppBar: false, },
body: BlocBuilder<BottomNavCubit, int>( pagination: SwiperPagination(
builder: (BuildContext context, int state) { builder: SwiperCustomPagination(
return AnimatedSwitcher( builder: (BuildContext context, SwiperPluginConfig config) {
duration: const Duration(milliseconds: 300), return BottomNavBar(swipeController: config.controller);
child: pageNavigation.elementAt(state), },
); ),
},
), ),
bottomNavigationBar: const BottomNavBar(), onIndexChanged: (newPageIndex) {
backgroundColor: Theme.of(context).colorScheme.background, BlocProvider.of<BottomNavCubit>(context).updateIndex(newPageIndex);
},
outer: true,
loop: false,
), ),
backgroundColor: Theme.of(context).colorScheme.background,
); );
}, },
), ),
......
...@@ -2,12 +2,13 @@ import 'package:easy_localization/easy_localization.dart'; ...@@ -2,12 +2,13 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../config/app_colors.dart'; import 'package:scrobbles/config/app_colors.dart';
import '../../../utils/color_extensions.dart'; import 'package:scrobbles/utils/color_extensions.dart';
class CustomBarChart extends StatelessWidget { class CustomBarChart extends StatelessWidget {
CustomBarChart({super.key}); CustomBarChart({super.key});
final Widget placeholder = Text('⏳');
final double chartHeight = 150.0; final double chartHeight = 150.0;
final double verticalTicksInterval = 10; final double verticalTicksInterval = 10;
final String verticalAxisTitleSuffix = ''; final String verticalAxisTitleSuffix = '';
......
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../config/app_colors.dart'; import 'package:scrobbles/config/app_colors.dart';
class CustomLineChart extends StatelessWidget { class CustomLineChart extends StatelessWidget {
CustomLineChart({super.key}); CustomLineChart({super.key});
final Widget placeholder = Text('⏳');
final double chartHeight = 150.0; final double chartHeight = 150.0;
final double titleFontSize = 10; final double titleFontSize = 10;
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:unicons/unicons.dart'; import 'package:unicons/unicons.dart';
import '../widgets/header.dart'; import 'package:scrobbles/ui/widgets/update_data.dart';
import 'package:scrobbles/ui/widgets/header_app.dart';
class StandardAppBar extends StatelessWidget implements PreferredSizeWidget { class StandardAppBar extends StatelessWidget implements PreferredSizeWidget {
final Function() notifyParent; final Function() notifyParent;
...@@ -11,8 +12,9 @@ class StandardAppBar extends StatelessWidget implements PreferredSizeWidget { ...@@ -11,8 +12,9 @@ class StandardAppBar extends StatelessWidget implements PreferredSizeWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppBar( return AppBar(
title: const Header(text: 'app_name'), title: const AppHeader(text: 'app_name'),
actions: [ actions: [
UpdateData(),
IconButton( IconButton(
onPressed: () { onPressed: () {
this.notifyParent(); this.notifyParent();
......
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_swipe/flutter_swipe.dart';
import 'package:ionicons/ionicons.dart'; import 'package:ionicons/ionicons.dart';
import '../../cubit/bottom_nav_cubit.dart'; import 'package:scrobbles/cubit/bottom_nav_cubit.dart';
class BottomNavBar extends StatelessWidget { class BottomNavBar extends StatelessWidget {
const BottomNavBar({super.key}); const BottomNavBar({super.key, required this.swipeController});
final SwiperController swipeController;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Card( return Card(
margin: const EdgeInsets.only(top: 1, right: 4, left: 4), margin: const EdgeInsets.only(
top: 1,
right: 0,
left: 0,
),
elevation: 4, elevation: 4,
shadowColor: Theme.of(context).colorScheme.shadow, shadowColor: Theme.of(context).colorScheme.shadow,
color: Theme.of(context).colorScheme.surfaceVariant, color: Theme.of(context).colorScheme.surfaceVariant,
...@@ -25,7 +32,10 @@ class BottomNavBar extends StatelessWidget { ...@@ -25,7 +32,10 @@ class BottomNavBar extends StatelessWidget {
builder: (BuildContext context, int state) { builder: (BuildContext context, int state) {
return BottomNavigationBar( return BottomNavigationBar(
currentIndex: state, currentIndex: state,
onTap: (int index) => context.read<BottomNavCubit>().updateIndex(index), onTap: (int index) {
context.read<BottomNavCubit>().updateIndex(index);
swipeController.move(index);
},
type: BottomNavigationBarType.fixed, type: BottomNavigationBarType.fixed,
elevation: 0, elevation: 0,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
......
import 'package:flutter/material.dart';
class CardContent extends StatelessWidget {
const CardContent({
super.key,
required this.title,
required this.color,
required this.content,
});
final String title;
final Color color;
final Widget content;
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
shadowColor: Theme.of(context).colorScheme.shadow,
color: this.color,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(8),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
this.title,
style: Theme.of(context).primaryTextTheme.titleLarge!.apply(fontWeightDelta: 2),
),
const SizedBox(height: 8),
this.content,
],
),
),
);
}
}
import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:scrobbles/config/settings.dart';
import 'package:scrobbles/cubit/data_counts_by_day_cubit.dart';
import 'package:scrobbles/models/counts_by_day.dart';
import 'package:scrobbles/ui/widgets/card_content.dart';
import 'package:scrobbles/ui/widgets/charts/counts_by_day.dart';
class CardCountsByDay extends StatelessWidget {
const CardCountsByDay({super.key});
@override
Widget build(BuildContext context) {
final int daysCount = Settings.countsByDayDaysCount;
return BlocProvider<DataCountsByDayCubit>(
create: (BuildContext context) => DataCountsByDayCubit(),
child: BlocBuilder<DataCountsByDayCubit, DataCountsByDayState>(
builder: (BuildContext context, DataCountsByDayState state) {
return CardContent(
color: Theme.of(context).colorScheme.surface,
title: 'counts_by_day'.tr(
namedArgs: {
'daysCount': daysCount.toString(),
},
),
content: ChartCountsByDay(
chartData: CountsByDayData.fromJson(jsonDecode(state.countsByDay.toString())),
isLoading: false,
),
);
},
),
);
}
}
import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:scrobbles/config/settings.dart';
import 'package:scrobbles/cubit/data_counts_by_hour_cubit.dart';
import 'package:scrobbles/models/counts_by_hour.dart';
import 'package:scrobbles/ui/widgets/card_content.dart';
import 'package:scrobbles/ui/widgets/charts/counts_by_hour.dart';
class CardCountsByHour extends StatelessWidget {
const CardCountsByHour({super.key});
@override
Widget build(BuildContext context) {
final int daysCount = Settings.countsByHourDaysCount;
return BlocProvider<DataCountsByHourCubit>(
create: (BuildContext context) => DataCountsByHourCubit(),
child: BlocBuilder<DataCountsByHourCubit, DataCountsByHourState>(
builder: (BuildContext context, DataCountsByHourState state) {
return CardContent(
color: Theme.of(context).colorScheme.surface,
title: 'counts_by_hour'.tr(
namedArgs: {
'daysCount': daysCount.toString(),
},
),
content: ChartCountsByHour(
chartData: CountsByHourData.fromJson(jsonDecode(state.countsByHour.toString())),
isLoading: false,
),
);
},
),
);
}
}
import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:scrobbles/config/settings.dart';
import 'package:scrobbles/cubit/data_discoveries_cubit.dart';
import 'package:scrobbles/models/discoveries.dart';
import 'package:scrobbles/ui/widgets/card_content.dart';
import 'package:scrobbles/ui/widgets/charts/discoveries_artists.dart';
import 'package:scrobbles/ui/widgets/charts/discoveries_tracks.dart';
class CardDiscoveries extends StatelessWidget {
const CardDiscoveries({super.key});
@override
Widget build(BuildContext context) {
final int daysCount = Settings.discoveriesDaysCount;
return BlocProvider<DataDiscoveriesCubit>(
create: (BuildContext context) => DataDiscoveriesCubit(),
child: BlocBuilder<DataDiscoveriesCubit, DataDiscoveriesState>(
builder: (BuildContext context, DataDiscoveriesState state) {
final TextTheme textTheme = Theme.of(context).primaryTextTheme;
return CardContent(
color: Theme.of(context).colorScheme.surface,
title: 'discoveries_title'.tr(
namedArgs: {
'daysCount': daysCount.toString(),
},
),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'discoveries_artists_title',
style: textTheme.titleMedium!.apply(fontWeightDelta: 2),
).tr(),
const SizedBox(height: 8),
ChartDiscoveriesArtists(
chartData:
DiscoveriesData.fromJson(jsonDecode(state.discoveries.toString())),
isLoading: false,
),
const SizedBox(height: 8),
Text(
'discoveries_tracks_title',
style: textTheme.titleMedium!.apply(fontWeightDelta: 2),
).tr(),
const SizedBox(height: 8),
ChartDiscoveriesTracks(
chartData:
DiscoveriesData.fromJson(jsonDecode(state.discoveries.toString())),
isLoading: false,
),
],
),
);
},
),
);
}
}
import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:scrobbles/cubit/data_statistics_global_cubit.dart';
import 'package:scrobbles/models/statistics_global.dart';
import 'package:scrobbles/ui/widgets/card_content.dart';
import 'package:scrobbles/ui/widgets/content/statistics_global.dart';
class CardStatisticsGlobal extends StatelessWidget {
const CardStatisticsGlobal({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider<DataStatisticsGlobalCubit>(
create: (BuildContext context) => DataStatisticsGlobalCubit(),
child: BlocBuilder<DataStatisticsGlobalCubit, DataStatisticsGlobalState>(
builder: (BuildContext context, DataStatisticsGlobalState state) {
return CardContent(
color: Theme.of(context).colorScheme.primary,
title: 'global_statistics'.tr(),
content: ContentStatisticsGlobal(
statistics:
StatisticsGlobalData.fromJson(jsonDecode(state.statisticsGlobal.toString())),
isLoading: false,
),
);
},
),
);
}
}
import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:scrobbles/config/settings.dart';
import 'package:scrobbles/cubit/data_statistics_recent_cubit.dart';
import 'package:scrobbles/models/statistics_recent.dart';
import 'package:scrobbles/ui/widgets/card_content.dart';
import 'package:scrobbles/ui/widgets/content/statistics_recent.dart';
class CardStatisticsRecent extends StatelessWidget {
const CardStatisticsRecent({super.key});
@override
Widget build(BuildContext context) {
final int daysCount = Settings.statisticsRecentDaysCount;
// data context
return BlocProvider<DataStatisticsRecentCubit>(
create: (BuildContext context) => DataStatisticsRecentCubit(),
child: BlocBuilder<DataStatisticsRecentCubit, DataStatisticsRecentState>(
builder: (BuildContext context, DataStatisticsRecentState dataState) {
return CardContent(
color: Theme.of(context).colorScheme.primary,
title: 'recent_statistics'.tr(
namedArgs: {
'daysCount': daysCount.toString(),
},
),
content: ContentStatisticsRecent(
statistics: StatisticsRecentData.fromJson(
jsonDecode(dataState.statisticsRecent.toString())),
isLoading: false,
),
);
},
),
);
}
}
import 'dart:convert';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:scrobbles/config/settings.dart';
import 'package:scrobbles/cubit/data_timeline_cubit.dart';
import 'package:scrobbles/models/timeline.dart';
import 'package:scrobbles/ui/widgets/card_content.dart';
import 'package:scrobbles/ui/widgets/charts/timeline_counts.dart';
import 'package:scrobbles/ui/widgets/charts/timeline_eclecticism.dart';
class CardTimeline extends StatelessWidget {
const CardTimeline({super.key});
@override
Widget build(BuildContext context) {
final int daysCount = Settings.timelineDaysCount;
return BlocProvider<DataTimelineCubit>(
create: (BuildContext context) => DataTimelineCubit(),
child: BlocBuilder<DataTimelineCubit, DataTimelineState>(
builder: (BuildContext context, DataTimelineState state) {
return CardContent(
color: Theme.of(context).colorScheme.surface,
title: 'timeline_title'.tr(
namedArgs: {
'daysCount': daysCount.toString(),
},
),
content: Stack(
children: [
ChartTimelineCounts(
chartData: TimelineData.fromJson(jsonDecode(state.timeline.toString())),
isLoading: false,
),
ChartTimelineEclecticism(
chartData: TimelineData.fromJson(jsonDecode(state.timeline.toString())),
isLoading: false,
),
],
),
);
},
),
);
}
}