From e7244ad8e3e0eef793857cc4d1faec5e965620be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Harrault?= <benoit@harrault.fr>
Date: Sat, 11 Nov 2023 23:30:17 +0100
Subject: [PATCH] Clean/move/rename/refactor code

---
 android/gradle.properties                     |  4 +-
 assets/translations/en.json                   |  8 +--
 assets/translations/fr.json                   |  8 +--
 .../metadata/android/en-US/changelogs/28.txt  |  1 +
 .../metadata/android/fr-FR/changelogs/28.txt  |  1 +
 lib/main.dart                                 |  5 +-
 lib/models/counts_by_day.dart                 |  8 +--
 lib/models/counts_by_hour.dart                |  6 +-
 lib/models/discoveries.dart                   | 10 +--
 lib/models/statistics_global.dart             |  8 +--
 lib/models/statistics_recent.dart             | 16 +++--
 lib/models/timeline.dart                      | 10 +--
 lib/models/topartists.dart                    | 10 +--
 .../{scrobbles_api.dart => scrobbles.dart}    | 14 ++--
 ...scoveries_screen.dart => discoveries.dart} | 12 ++--
 lib/ui/screens/home.dart                      | 37 +++++++++++
 lib/ui/screens/home_screen.dart               | 37 -----------
 ...statistics_screen.dart => statistics.dart} | 16 ++---
 .../skeleton_screen.dart => skeleton.dart}    | 19 +++---
 .../custom_bar_chart.dart                     |  5 +-
 .../custom_line_chart.dart                    |  3 +-
 lib/ui/widgets/app_bar.dart                   |  4 +-
 lib/ui/widgets/bottom_nav_bar.dart            |  2 +-
 lib/ui/widgets/card_content.dart              | 43 ++++++++++++
 lib/ui/widgets/cards/counts_by_day.dart       | 44 +++++++++++++
 lib/ui/widgets/cards/counts_by_hour.dart      | 44 +++++++++++++
 lib/ui/widgets/cards/discoveries.dart         | 66 +++++++++++++++++++
 lib/ui/widgets/cards/statistics_global.dart   | 39 +++++++++++
 lib/ui/widgets/cards/statistics_recent.dart   | 45 +++++++++++++
 lib/ui/widgets/cards/timeline.dart            | 51 ++++++++++++++
 lib/ui/widgets/cards/top_artists.dart         | 44 +++++++++++++
 .../counts_by_day.dart}                       | 30 +++++----
 .../counts_by_hour.dart}                      | 32 +++++----
 .../discoveries_artists.dart}                 | 30 +++++----
 .../discoveries_tracks.dart}                  | 30 +++++----
 .../timeline_counts.dart}                     | 28 +++++---
 .../timeline_eclecticism.dart}                | 41 ++++++------
 .../top_artists.dart}                         | 49 +++++++-------
 .../statistics_global.dart}                   | 13 ++--
 .../statistics_recent.dart}                   | 22 ++-----
 .../widgets/{header.dart => header_app.dart}  |  4 +-
 .../main_screen/counts_by_day_card.dart       | 48 --------------
 .../main_screen/counts_by_day_content.dart    | 43 ------------
 .../main_screen/counts_by_hour_card.dart      | 49 --------------
 .../main_screen/counts_by_hour_content.dart   | 43 ------------
 .../widgets/main_screen/discoveries_card.dart | 61 -----------------
 .../discoveries_content_artists.dart          | 43 ------------
 .../discoveries_content_tracks.dart           | 43 ------------
 .../main_screen/statistics_global_card.dart   | 46 -------------
 .../main_screen/statistics_recent_card.dart   | 48 --------------
 lib/ui/widgets/main_screen/timeline_card.dart | 48 --------------
 .../widgets/main_screen/timeline_content.dart | 51 --------------
 .../widgets/main_screen/topartists_card.dart  | 48 --------------
 .../main_screen/topartists_content.dart       | 43 ------------
 pubspec.yaml                                  |  2 +-
 55 files changed, 655 insertions(+), 860 deletions(-)
 create mode 100644 fastlane/metadata/android/en-US/changelogs/28.txt
 create mode 100644 fastlane/metadata/android/fr-FR/changelogs/28.txt
 rename lib/network/{scrobbles_api.dart => scrobbles.dart} (89%)
 rename lib/ui/screens/{discoveries_screen.dart => discoveries.dart} (59%)
 create mode 100644 lib/ui/screens/home.dart
 delete mode 100644 lib/ui/screens/home_screen.dart
 rename lib/ui/screens/{statistics_screen.dart => statistics.dart} (54%)
 rename lib/ui/{screens/skeleton_screen.dart => skeleton.dart} (80%)
 rename lib/ui/widgets/{charts => abstracts}/custom_bar_chart.dart (97%)
 rename lib/ui/widgets/{charts => abstracts}/custom_line_chart.dart (95%)
 create mode 100644 lib/ui/widgets/card_content.dart
 create mode 100644 lib/ui/widgets/cards/counts_by_day.dart
 create mode 100644 lib/ui/widgets/cards/counts_by_hour.dart
 create mode 100644 lib/ui/widgets/cards/discoveries.dart
 create mode 100644 lib/ui/widgets/cards/statistics_global.dart
 create mode 100644 lib/ui/widgets/cards/statistics_recent.dart
 create mode 100644 lib/ui/widgets/cards/timeline.dart
 create mode 100644 lib/ui/widgets/cards/top_artists.dart
 rename lib/ui/widgets/{main_screen/counts_by_day_chart.dart => charts/counts_by_day.dart} (76%)
 rename lib/ui/widgets/{main_screen/counts_by_hour_chart.dart => charts/counts_by_hour.dart} (63%)
 rename lib/ui/widgets/{main_screen/discoveries_chart_artists.dart => charts/discoveries_artists.dart} (61%)
 rename lib/ui/widgets/{main_screen/discoveries_chart_tracks.dart => charts/discoveries_tracks.dart} (61%)
 rename lib/ui/widgets/{main_screen/timeline_chart_counts.dart => charts/timeline_counts.dart} (62%)
 rename lib/ui/widgets/{main_screen/timeline_chart_eclecticism.dart => charts/timeline_eclecticism.dart} (66%)
 rename lib/ui/widgets/{main_screen/topartists_chart.dart => charts/top_artists.dart} (71%)
 rename lib/ui/widgets/{main_screen/statistics_global_content.dart => content/statistics_global.dart} (77%)
 rename lib/ui/widgets/{main_screen/statistics_recent_content.dart => content/statistics_recent.dart} (64%)
 rename lib/ui/widgets/{header.dart => header_app.dart} (78%)
 delete mode 100644 lib/ui/widgets/main_screen/counts_by_day_card.dart
 delete mode 100644 lib/ui/widgets/main_screen/counts_by_day_content.dart
 delete mode 100644 lib/ui/widgets/main_screen/counts_by_hour_card.dart
 delete mode 100644 lib/ui/widgets/main_screen/counts_by_hour_content.dart
 delete mode 100644 lib/ui/widgets/main_screen/discoveries_card.dart
 delete mode 100644 lib/ui/widgets/main_screen/discoveries_content_artists.dart
 delete mode 100644 lib/ui/widgets/main_screen/discoveries_content_tracks.dart
 delete mode 100644 lib/ui/widgets/main_screen/statistics_global_card.dart
 delete mode 100644 lib/ui/widgets/main_screen/statistics_recent_card.dart
 delete mode 100644 lib/ui/widgets/main_screen/timeline_card.dart
 delete mode 100644 lib/ui/widgets/main_screen/timeline_content.dart
 delete mode 100644 lib/ui/widgets/main_screen/topartists_card.dart
 delete mode 100644 lib/ui/widgets/main_screen/topartists_content.dart

diff --git a/android/gradle.properties b/android/gradle.properties
index 408253c..e644338 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,5 +1,5 @@
 org.gradle.jvmargs=-Xmx1536M
 android.useAndroidX=true
 android.enableJetifier=true
-app.versionName=0.0.27
-app.versionCode=27
+app.versionName=0.0.28
+app.versionCode=28
diff --git a/assets/translations/en.json b/assets/translations/en.json
index a2823eb..3f4f688 100644
--- a/assets/translations/en.json
+++ b/assets/translations/en.json
@@ -9,16 +9,16 @@
   "statistics_total_scrobbles_count": "Total scrobbles count: {count}",
   "statistics_last_scrobble": "Last scrobble: {datetime}",
 
-  "recent_statistics": "Recent statistics",
-  "statistics_selected_period": "On last {daysCount} days:",
+  "recent_statistics": "Recent statistics ({daysCount} days)",
   "statistics_recent_scrobbles_count_and_discoveries": "{count} scrobbles and {artistsCount} artists / {tracksCount} tracks discovered.",
 
   "timeline_title": "Recent scrobbles ({daysCount} days)",
   "counts_by_day": "Counts by day ({daysCount} days)",
   "counts_by_hour": "Counts by hour ({daysCount} days)",
 
-  "discoveries_artists_title": "Artists discoveries ({daysCount} days)",
-  "discoveries_tracks_title": "Tracks discoveries ({daysCount} days)",
+  "discoveries_title": "Discoveries ({daysCount} days)",
+  "discoveries_artists_title": "Artists",
+  "discoveries_tracks_title": "Tracks",
 
   "top_artists_title": "Top artists ({daysCount} days)",
 
diff --git a/assets/translations/fr.json b/assets/translations/fr.json
index 67d019d..2f2fb63 100644
--- a/assets/translations/fr.json
+++ b/assets/translations/fr.json
@@ -9,16 +9,16 @@
   "statistics_total_scrobbles_count": "Nombre total d'écoutes : {count}",
   "statistics_last_scrobble": "Dernière écoute : {datetime}",
 
-  "recent_statistics": "Statistiques récentes",
-  "statistics_selected_period": "Sur les {daysCount} derniers jours :",
+  "recent_statistics": "Statistiques récentes ({daysCount} jours)",
   "statistics_recent_scrobbles_count_and_discoveries": "{count} écoutes et {artistsCount} artistes / {tracksCount} morceaux découverts.",
 
   "timeline_title": "Écoutes récentes ({daysCount} jours)",
   "counts_by_day": "Écoutes par jour ({daysCount} jours)",
   "counts_by_hour": "Écoutes par heure ({daysCount} jours)",
 
-  "discoveries_artists_title": "Artistes découverts ({daysCount} jours)",
-  "discoveries_tracks_title": "Morceaux découverts ({daysCount} jours)",
+  "discoveries_title": "Découvertes ({daysCount} jours)",
+  "discoveries_artists_title": "Artistes",
+  "discoveries_tracks_title": "Morceaux",
 
   "top_artists_title": "Top artistes ({daysCount} jours)",
 
diff --git a/fastlane/metadata/android/en-US/changelogs/28.txt b/fastlane/metadata/android/en-US/changelogs/28.txt
new file mode 100644
index 0000000..b2da89f
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/28.txt
@@ -0,0 +1 @@
+Clean/improve/refactor code.
diff --git a/fastlane/metadata/android/fr-FR/changelogs/28.txt b/fastlane/metadata/android/fr-FR/changelogs/28.txt
new file mode 100644
index 0000000..92d90dd
--- /dev/null
+++ b/fastlane/metadata/android/fr-FR/changelogs/28.txt
@@ -0,0 +1 @@
+Nettoyage/améliorations/refactorisations de code.
diff --git a/lib/main.dart b/lib/main.dart
index c6ec2e7..da465fa 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -6,9 +6,8 @@ import 'package:hive/hive.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
 import 'package:path_provider/path_provider.dart';
 
-import 'config/theme.dart';
-
-import 'ui/screens/skeleton_screen.dart';
+import 'package:scrobbles/config/theme.dart';
+import 'package:scrobbles/ui/skeleton.dart';
 
 void main() async {
   /// Initialize packages
diff --git a/lib/models/counts_by_day.dart b/lib/models/counts_by_day.dart
index cff1d53..fcb4551 100644
--- a/lib/models/counts_by_day.dart
+++ b/lib/models/counts_by_day.dart
@@ -7,11 +7,11 @@ class CountsByDayData {
     required this.data,
   });
 
-  factory CountsByDayData.fromJson(Map<String, dynamic> json) {
+  factory CountsByDayData.fromJson(Map<String, dynamic>? json) {
     Map<int, double> data = {};
 
-    if (json['counts-by-day'] != null) {
-      json['counts-by-day'].keys.forEach((day) {
+    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());
       });
     }
@@ -20,7 +20,7 @@ class CountsByDayData {
   }
 
   factory CountsByDayData.createEmpty() {
-    return CountsByDayData.fromJson({});
+    return CountsByDayData.fromJson({'counts-by-day': {}});
   }
 
   String toString() {
diff --git a/lib/models/counts_by_hour.dart b/lib/models/counts_by_hour.dart
index 1285752..f0c6d50 100644
--- a/lib/models/counts_by_hour.dart
+++ b/lib/models/counts_by_hour.dart
@@ -7,11 +7,11 @@ class CountsByHourData {
     required this.data,
   });
 
-  factory CountsByHourData.fromJson(Map<String, dynamic> json) {
+  factory CountsByHourData.fromJson(Map<String, dynamic>? json) {
     Map<int, double> data = {};
 
-    if (json['counts-by-hour'] != null) {
-      json['counts-by-hour'].keys.forEach((day) {
+    if (json?['counts-by-hour'] != null) {
+      json?['counts-by-hour'].keys.forEach((day) {
         if (int.parse(day) != 24) {
           data[int.parse(day)] = double.parse(json['counts-by-hour'][day].toString());
         }
diff --git a/lib/models/discoveries.dart b/lib/models/discoveries.dart
index 3e67649..14f5819 100644
--- a/lib/models/discoveries.dart
+++ b/lib/models/discoveries.dart
@@ -6,10 +6,10 @@ class DiscoveriesDataValue {
 
   const DiscoveriesDataValue({required this.newArtistsCount, required this.newTracksCount});
 
-  factory DiscoveriesDataValue.fromJson(Map<String, dynamic> json) {
+  factory DiscoveriesDataValue.fromJson(Map<String, dynamic>? json) {
     return DiscoveriesDataValue(
-      newArtistsCount: json['new-artists'] as int,
-      newTracksCount: json['new-tracks'] as int,
+      newArtistsCount: json?['new-artists'] as int,
+      newTracksCount: json?['new-tracks'] as int,
     );
   }
 }
@@ -21,10 +21,10 @@ class DiscoveriesData {
     required this.data,
   });
 
-  factory DiscoveriesData.fromJson(Map<String, dynamic> json) {
+  factory DiscoveriesData.fromJson(Map<String, dynamic>? json) {
     Map<String, DiscoveriesDataValue> data = {};
 
-    json.keys.forEach((date) {
+    json?.keys.forEach((date) {
       DiscoveriesDataValue value = DiscoveriesDataValue(
         newArtistsCount: json[date]['new-artists'] as int,
         newTracksCount: json[date]['new-tracks'] as int,
diff --git a/lib/models/statistics_global.dart b/lib/models/statistics_global.dart
index 0610e27..4eb90fc 100644
--- a/lib/models/statistics_global.dart
+++ b/lib/models/statistics_global.dart
@@ -9,12 +9,12 @@ class StatisticsGlobalData {
     required this.lastScrobble,
   });
 
-  factory StatisticsGlobalData.fromJson(Map<String, dynamic> json) {
+  factory StatisticsGlobalData.fromJson(Map<String, dynamic>? json) {
     return StatisticsGlobalData(
-      totalCount: json['totalCount'] != null ? json['totalCount'] as int : 0,
-      lastScrobble: (json['lastScrobble'] != null && json['lastScrobble']['date'] != null)
+      totalCount: (json?['totalCount'] != null) ? (json?['totalCount'] as int) : 0,
+      lastScrobble: (json?['lastScrobble'] != null && json?['lastScrobble']['date'] != null)
           ? DateTime.parse(
-              json['lastScrobble']['date'],
+              json?['lastScrobble']['date'],
             )
           : DateTime.now(),
     );
diff --git a/lib/models/statistics_recent.dart b/lib/models/statistics_recent.dart
index 218dd1f..d72173f 100644
--- a/lib/models/statistics_recent.dart
+++ b/lib/models/statistics_recent.dart
@@ -13,14 +13,16 @@ class StatisticsRecentData {
     required this.selectedPeriod,
   });
 
-  factory StatisticsRecentData.fromJson(Map<String, dynamic> json) {
+  factory StatisticsRecentData.fromJson(Map<String, dynamic>? json) {
     return StatisticsRecentData(
-      recentCount: json['recentCount'] != null ? json['recentCount'] as int : 0,
-      firstPlayedArtistsCount:
-          json['firstPlayedArtistsCount'] != null ? json['firstPlayedArtistsCount'] as int : 0,
-      firstPlayedTracksCount:
-          json['firstPlayedTracksCount'] != null ? json['firstPlayedTracksCount'] as int : 0,
-      selectedPeriod: json['selectedPeriod'] != null ? json['selectedPeriod'] as int : 0,
+      recentCount: (json?['recentCount'] != null) ? (json?['recentCount'] as int) : 0,
+      firstPlayedArtistsCount: (json?['firstPlayedArtistsCount'] != null)
+          ? (json?['firstPlayedArtistsCount'] as int)
+          : 0,
+      firstPlayedTracksCount: (json?['firstPlayedTracksCount'] != null)
+          ? (json?['firstPlayedTracksCount'] as int)
+          : 0,
+      selectedPeriod: (json?['selectedPeriod'] != null) ? (json?['selectedPeriod'] as int) : 0,
     );
   }
 
diff --git a/lib/models/timeline.dart b/lib/models/timeline.dart
index 1504233..b52a546 100644
--- a/lib/models/timeline.dart
+++ b/lib/models/timeline.dart
@@ -6,10 +6,10 @@ class TimelineDataValue {
 
   const TimelineDataValue({required this.counts, required this.eclecticism});
 
-  factory TimelineDataValue.fromJson(Map<String, dynamic> json) {
+  factory TimelineDataValue.fromJson(Map<String, dynamic>? json) {
     return TimelineDataValue(
-      counts: json['counts'] as int,
-      eclecticism: json['eclecticism'] as int,
+      counts: json?['counts'] as int,
+      eclecticism: json?['eclecticism'] as int,
     );
   }
 }
@@ -21,10 +21,10 @@ class TimelineData {
     required this.data,
   });
 
-  factory TimelineData.fromJson(Map<String, dynamic> json) {
+  factory TimelineData.fromJson(Map<String, dynamic>? json) {
     Map<String, TimelineDataValue> data = {};
 
-    json.keys.forEach((date) {
+    json?.keys.forEach((date) {
       TimelineDataValue value = TimelineDataValue(
         counts: json[date]['counts'] as int,
         eclecticism: json[date]['eclecticism'] as int,
diff --git a/lib/models/topartists.dart b/lib/models/topartists.dart
index 52c06f2..9a7b418 100644
--- a/lib/models/topartists.dart
+++ b/lib/models/topartists.dart
@@ -6,10 +6,10 @@ class TopArtistsDataValue {
 
   const TopArtistsDataValue({required this.artistName, required this.count});
 
-  factory TopArtistsDataValue.fromJson(Map<String, dynamic> json) {
+  factory TopArtistsDataValue.fromJson(Map<String, dynamic>? json) {
     return TopArtistsDataValue(
-      artistName: json['artistName'] as String,
-      count: json['count'] as int,
+      artistName: json?['artistName'] as String,
+      count: json?['count'] as int,
     );
   }
 }
@@ -21,10 +21,10 @@ class TopArtistsData {
     required this.topArtists,
   });
 
-  factory TopArtistsData.fromJson(Map<String, dynamic> json) {
+  factory TopArtistsData.fromJson(Map<String, dynamic>? json) {
     List<TopArtistsDataValue> topArtists = [];
 
-    json['top-artists'].forEach((element) {
+    json?['top-artists'].forEach((element) {
       TopArtistsDataValue value = TopArtistsDataValue(
         artistName: element['artistName'] as String,
         count: element['count'] as int,
diff --git a/lib/network/scrobbles_api.dart b/lib/network/scrobbles.dart
similarity index 89%
rename from lib/network/scrobbles_api.dart
rename to lib/network/scrobbles.dart
index 3efb3f6..ebfca8a 100644
--- a/lib/network/scrobbles_api.dart
+++ b/lib/network/scrobbles.dart
@@ -1,13 +1,13 @@
 import 'dart:convert';
 import 'package:http/http.dart' as http;
 
-import '../models/counts_by_day.dart';
-import '../models/counts_by_hour.dart';
-import '../models/discoveries.dart';
-import '../models/statistics_global.dart';
-import '../models/statistics_recent.dart';
-import '../models/timeline.dart';
-import '../models/topartists.dart';
+import 'package:scrobbles/models/counts_by_day.dart';
+import 'package:scrobbles/models/counts_by_hour.dart';
+import 'package:scrobbles/models/discoveries.dart';
+import 'package:scrobbles/models/statistics_global.dart';
+import 'package:scrobbles/models/statistics_recent.dart';
+import 'package:scrobbles/models/timeline.dart';
+import 'package:scrobbles/models/topartists.dart';
 
 class ScrobblesApi {
   static String baseUrl = 'https://scrobble.harrault.fr';
diff --git a/lib/ui/screens/discoveries_screen.dart b/lib/ui/screens/discoveries.dart
similarity index 59%
rename from lib/ui/screens/discoveries_screen.dart
rename to lib/ui/screens/discoveries.dart
index ca590c7..fd5f836 100644
--- a/lib/ui/screens/discoveries_screen.dart
+++ b/lib/ui/screens/discoveries.dart
@@ -1,15 +1,15 @@
 import 'package:flutter/material.dart';
 
-import '../widgets/main_screen/discoveries_card.dart';
+import 'package:scrobbles/ui/widgets/cards/discoveries.dart';
 
-class DiscoveriesScreen extends StatefulWidget {
-  const DiscoveriesScreen({super.key});
+class ScreenDiscoveries extends StatefulWidget {
+  const ScreenDiscoveries({super.key});
 
   @override
-  State<DiscoveriesScreen> createState() => _DiscoveriesScreenState();
+  State<ScreenDiscoveries> createState() => _ScreenDiscoveriesState();
 }
 
-class _DiscoveriesScreenState extends State<DiscoveriesScreen> {
+class _ScreenDiscoveriesState extends State<ScreenDiscoveries> {
   @override
   Widget build(BuildContext context) {
     return Material(
@@ -19,7 +19,7 @@ class _DiscoveriesScreenState extends State<DiscoveriesScreen> {
         physics: const BouncingScrollPhysics(),
         children: <Widget>[
           const SizedBox(height: 8),
-          const ChartDiscoveriesCard(),
+          const CardDiscoveries(),
           const SizedBox(height: 36),
         ],
       ),
diff --git a/lib/ui/screens/home.dart b/lib/ui/screens/home.dart
new file mode 100644
index 0000000..c2acc00
--- /dev/null
+++ b/lib/ui/screens/home.dart
@@ -0,0 +1,37 @@
+import 'package:flutter/material.dart';
+
+import 'package:scrobbles/ui/widgets/cards/statistics_global.dart';
+import 'package:scrobbles/ui/widgets/cards/statistics_recent.dart';
+import 'package:scrobbles/ui/widgets/cards/timeline.dart';
+import 'package:scrobbles/ui/widgets/cards/top_artists.dart';
+
+class ScreenHome extends StatefulWidget {
+  const ScreenHome({super.key});
+
+  @override
+  State<ScreenHome> createState() => _ScreenHomeState();
+}
+
+class _ScreenHomeState extends State<ScreenHome> {
+  @override
+  Widget build(BuildContext context) {
+    return Material(
+      color: Theme.of(context).colorScheme.background,
+      child: ListView(
+        padding: const EdgeInsets.symmetric(horizontal: 4),
+        physics: const BouncingScrollPhysics(),
+        children: <Widget>[
+          const SizedBox(height: 8),
+          const CardStatisticsGlobal(),
+          const SizedBox(height: 6),
+          const CardStatisticsRecent(),
+          const SizedBox(height: 6),
+          const CardTimeline(),
+          const SizedBox(height: 6),
+          const CardTopArtists(),
+          const SizedBox(height: 36),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/ui/screens/home_screen.dart b/lib/ui/screens/home_screen.dart
deleted file mode 100644
index 5af4a2d..0000000
--- a/lib/ui/screens/home_screen.dart
+++ /dev/null
@@ -1,37 +0,0 @@
-import 'package:flutter/material.dart';
-
-import '../widgets/main_screen/statistics_global_card.dart';
-import '../widgets/main_screen/statistics_recent_card.dart';
-import '../widgets/main_screen/timeline_card.dart';
-import '../widgets/main_screen/topartists_card.dart';
-
-class HomeScreen extends StatefulWidget {
-  const HomeScreen({super.key});
-
-  @override
-  State<HomeScreen> createState() => _HomeScreenState();
-}
-
-class _HomeScreenState extends State<HomeScreen> {
-  @override
-  Widget build(BuildContext context) {
-    return Material(
-      color: Theme.of(context).colorScheme.background,
-      child: ListView(
-        padding: const EdgeInsets.symmetric(horizontal: 4),
-        physics: const BouncingScrollPhysics(),
-        children: <Widget>[
-          const SizedBox(height: 8),
-          const StatisticsGlobalCard(),
-          const SizedBox(height: 6),
-          const StatisticsRecentCard(),
-          const SizedBox(height: 6),
-          const ChartTimelineCard(),
-          const SizedBox(height: 6),
-          const ChartTopArtistsCard(),
-          const SizedBox(height: 36),
-        ],
-      ),
-    );
-  }
-}
diff --git a/lib/ui/screens/statistics_screen.dart b/lib/ui/screens/statistics.dart
similarity index 54%
rename from lib/ui/screens/statistics_screen.dart
rename to lib/ui/screens/statistics.dart
index c3f9e46..b719361 100644
--- a/lib/ui/screens/statistics_screen.dart
+++ b/lib/ui/screens/statistics.dart
@@ -1,16 +1,16 @@
 import 'package:flutter/material.dart';
 
-import '../widgets/main_screen/counts_by_day_card.dart';
-import '../widgets/main_screen/counts_by_hour_card.dart';
+import 'package:scrobbles/ui/widgets/cards/counts_by_day.dart';
+import 'package:scrobbles/ui/widgets/cards/counts_by_hour.dart';
 
-class StatisticsScreen extends StatefulWidget {
-  const StatisticsScreen({super.key});
+class ScreenStatistics extends StatefulWidget {
+  const ScreenStatistics({super.key});
 
   @override
-  State<StatisticsScreen> createState() => _StatisticsScreenState();
+  State<ScreenStatistics> createState() => _ScreenStatisticsState();
 }
 
-class _StatisticsScreenState extends State<StatisticsScreen> {
+class _ScreenStatisticsState extends State<ScreenStatistics> {
   @override
   Widget build(BuildContext context) {
     return Material(
@@ -20,9 +20,9 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
         physics: const BouncingScrollPhysics(),
         children: <Widget>[
           const SizedBox(height: 8),
-          const ChartCountsByDayCard(),
+          const CardCountsByDay(),
           const SizedBox(height: 6),
-          const ChartCountsByHourCard(),
+          const CardCountsByHour(),
           const SizedBox(height: 36),
         ],
       ),
diff --git a/lib/ui/screens/skeleton_screen.dart b/lib/ui/skeleton.dart
similarity index 80%
rename from lib/ui/screens/skeleton_screen.dart
rename to lib/ui/skeleton.dart
index 9a10a78..ad00a7b 100644
--- a/lib/ui/screens/skeleton_screen.dart
+++ b/lib/ui/skeleton.dart
@@ -1,13 +1,12 @@
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-import '../../cubit/bottom_nav_cubit.dart';
-import '../widgets/app_bar.dart';
-import '../widgets/bottom_nav_bar.dart';
-
-import 'discoveries_screen.dart';
-import 'home_screen.dart';
-import 'statistics_screen.dart';
+import 'package:scrobbles/cubit/bottom_nav_cubit.dart';
+import 'package:scrobbles/ui/screens/discoveries.dart';
+import 'package:scrobbles/ui/screens/home.dart';
+import 'package:scrobbles/ui/screens/statistics.dart';
+import 'package:scrobbles/ui/widgets/app_bar.dart';
+import 'package:scrobbles/ui/widgets/bottom_nav_bar.dart';
 
 class SkeletonScreen extends StatefulWidget {
   const SkeletonScreen({super.key});
@@ -20,9 +19,9 @@ class _SkeletonScreenState extends State<SkeletonScreen> {
   @override
   Widget build(BuildContext context) {
     const List<Widget> pageNavigation = <Widget>[
-      const HomeScreen(),
-      const DiscoveriesScreen(),
-      const StatisticsScreen(),
+      const ScreenHome(),
+      const ScreenDiscoveries(),
+      const ScreenStatistics(),
     ];
 
     return BlocProvider<BottomNavCubit>(
diff --git a/lib/ui/widgets/charts/custom_bar_chart.dart b/lib/ui/widgets/abstracts/custom_bar_chart.dart
similarity index 97%
rename from lib/ui/widgets/charts/custom_bar_chart.dart
rename to lib/ui/widgets/abstracts/custom_bar_chart.dart
index 8cdd32d..1ccfafb 100644
--- a/lib/ui/widgets/charts/custom_bar_chart.dart
+++ b/lib/ui/widgets/abstracts/custom_bar_chart.dart
@@ -2,12 +2,13 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:fl_chart/fl_chart.dart';
 import 'package:flutter/material.dart';
 
-import '../../../config/app_colors.dart';
-import '../../../utils/color_extensions.dart';
+import 'package:scrobbles/config/app_colors.dart';
+import 'package:scrobbles/utils/color_extensions.dart';
 
 class CustomBarChart extends StatelessWidget {
   CustomBarChart({super.key});
 
+  final Widget placeholder = Text('⏳');
   final double chartHeight = 150.0;
   final double verticalTicksInterval = 10;
   final String verticalAxisTitleSuffix = '';
diff --git a/lib/ui/widgets/charts/custom_line_chart.dart b/lib/ui/widgets/abstracts/custom_line_chart.dart
similarity index 95%
rename from lib/ui/widgets/charts/custom_line_chart.dart
rename to lib/ui/widgets/abstracts/custom_line_chart.dart
index fadf283..5e1a90c 100644
--- a/lib/ui/widgets/charts/custom_line_chart.dart
+++ b/lib/ui/widgets/abstracts/custom_line_chart.dart
@@ -1,11 +1,12 @@
 import 'package:fl_chart/fl_chart.dart';
 import 'package:flutter/material.dart';
 
-import '../../../config/app_colors.dart';
+import 'package:scrobbles/config/app_colors.dart';
 
 class CustomLineChart extends StatelessWidget {
   CustomLineChart({super.key});
 
+  final Widget placeholder = Text('⏳');
   final double chartHeight = 150.0;
   final double titleFontSize = 10;
 
diff --git a/lib/ui/widgets/app_bar.dart b/lib/ui/widgets/app_bar.dart
index 3313e59..6db981e 100644
--- a/lib/ui/widgets/app_bar.dart
+++ b/lib/ui/widgets/app_bar.dart
@@ -1,7 +1,7 @@
 import 'package:flutter/material.dart';
 import 'package:unicons/unicons.dart';
 
-import '../widgets/header.dart';
+import 'package:scrobbles/ui/widgets/header_app.dart';
 
 class StandardAppBar extends StatelessWidget implements PreferredSizeWidget {
   final Function() notifyParent;
@@ -11,7 +11,7 @@ class StandardAppBar extends StatelessWidget implements PreferredSizeWidget {
   @override
   Widget build(BuildContext context) {
     return AppBar(
-      title: const Header(text: 'app_name'),
+      title: const AppHeader(text: 'app_name'),
       actions: [
         IconButton(
           onPressed: () {
diff --git a/lib/ui/widgets/bottom_nav_bar.dart b/lib/ui/widgets/bottom_nav_bar.dart
index 02aa39a..3d2fb30 100644
--- a/lib/ui/widgets/bottom_nav_bar.dart
+++ b/lib/ui/widgets/bottom_nav_bar.dart
@@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:ionicons/ionicons.dart';
 
-import '../../cubit/bottom_nav_cubit.dart';
+import 'package:scrobbles/cubit/bottom_nav_cubit.dart';
 
 class BottomNavBar extends StatelessWidget {
   const BottomNavBar({super.key});
diff --git a/lib/ui/widgets/card_content.dart b/lib/ui/widgets/card_content.dart
new file mode 100644
index 0000000..2694475
--- /dev/null
+++ b/lib/ui/widgets/card_content.dart
@@ -0,0 +1,43 @@
+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,
+          ],
+        ),
+      ),
+    );
+  }
+}
diff --git a/lib/ui/widgets/cards/counts_by_day.dart b/lib/ui/widgets/cards/counts_by_day.dart
new file mode 100644
index 0000000..59d80cb
--- /dev/null
+++ b/lib/ui/widgets/cards/counts_by_day.dart
@@ -0,0 +1,44 @@
+import 'dart:convert';
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+
+import 'package:scrobbles/models/counts_by_day.dart';
+import 'package:scrobbles/network/scrobbles.dart';
+import 'package:scrobbles/ui/widgets/card_content.dart';
+import 'package:scrobbles/ui/widgets/charts/counts_by_day.dart';
+import 'package:scrobbles/ui/widgets/error.dart';
+
+class CardCountsByDay extends StatelessWidget {
+  const CardCountsByDay({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    final int daysCount = 21;
+    late Future<CountsByDayData> future = ScrobblesApi.fetchCountsByDay(daysCount);
+
+    return FutureBuilder<CountsByDayData>(
+      future: future,
+      builder: (context, snapshot) {
+        if (snapshot.hasError) {
+          return ShowErrorWidget(message: '${snapshot.error}');
+        }
+
+        return CardContent(
+          color: Theme.of(context).colorScheme.surface,
+          title: 'counts_by_day'.tr(
+            namedArgs: {
+              'daysCount': daysCount.toString(),
+            },
+          ),
+          content: ChartCountsByDay(
+            chartData: snapshot.hasData
+                ? CountsByDayData.fromJson(jsonDecode(snapshot.data.toString()))
+                : CountsByDayData.createEmpty(),
+            isLoading: !snapshot.hasData,
+          ),
+        );
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/cards/counts_by_hour.dart b/lib/ui/widgets/cards/counts_by_hour.dart
new file mode 100644
index 0000000..6c0d91f
--- /dev/null
+++ b/lib/ui/widgets/cards/counts_by_hour.dart
@@ -0,0 +1,44 @@
+import 'dart:convert';
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+
+import 'package:scrobbles/models/counts_by_hour.dart';
+import 'package:scrobbles/network/scrobbles.dart';
+import 'package:scrobbles/ui/widgets/card_content.dart';
+import 'package:scrobbles/ui/widgets/charts/counts_by_hour.dart';
+import 'package:scrobbles/ui/widgets/error.dart';
+
+class CardCountsByHour extends StatelessWidget {
+  const CardCountsByHour({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    final int daysCount = 21;
+    late Future<CountsByHourData> future = ScrobblesApi.fetchCountsByHour(daysCount);
+
+    return FutureBuilder<CountsByHourData>(
+      future: future,
+      builder: (context, snapshot) {
+        if (snapshot.hasError) {
+          return ShowErrorWidget(message: '${snapshot.error}');
+        }
+
+        return CardContent(
+          color: Theme.of(context).colorScheme.surface,
+          title: 'counts_by_hour'.tr(
+            namedArgs: {
+              'daysCount': daysCount.toString(),
+            },
+          ),
+          content: ChartCountsByHour(
+            chartData: snapshot.hasData
+                ? CountsByHourData.fromJson(jsonDecode(snapshot.data.toString()))
+                : CountsByHourData.createEmpty(),
+            isLoading: !snapshot.hasData,
+          ),
+        );
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/cards/discoveries.dart b/lib/ui/widgets/cards/discoveries.dart
new file mode 100644
index 0000000..f0cd96d
--- /dev/null
+++ b/lib/ui/widgets/cards/discoveries.dart
@@ -0,0 +1,66 @@
+import 'dart:convert';
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+
+import 'package:scrobbles/models/discoveries.dart';
+import 'package:scrobbles/network/scrobbles.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';
+import 'package:scrobbles/ui/widgets/error.dart';
+
+class CardDiscoveries extends StatelessWidget {
+  const CardDiscoveries({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    final int daysCount = 14;
+    late Future<DiscoveriesData> future = ScrobblesApi.fetchDiscoveries(daysCount);
+
+    return FutureBuilder<DiscoveriesData>(
+      future: future,
+      builder: (context, snapshot) {
+        if (snapshot.hasError) {
+          return ShowErrorWidget(message: '${snapshot.error}');
+        }
+
+        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(snapshot.data.toString())),
+                isLoading: !snapshot.hasData,
+              ),
+              const SizedBox(height: 8),
+              Text(
+                'discoveries_tracks_title',
+                style: textTheme.titleMedium!.apply(fontWeightDelta: 2),
+              ).tr(),
+              const SizedBox(height: 8),
+              ChartDiscoveriesTracks(
+                chartData: DiscoveriesData.fromJson(jsonDecode(snapshot.data.toString())),
+                isLoading: !snapshot.hasData,
+              ),
+            ],
+          ),
+        );
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/cards/statistics_global.dart b/lib/ui/widgets/cards/statistics_global.dart
new file mode 100644
index 0000000..0d8feb8
--- /dev/null
+++ b/lib/ui/widgets/cards/statistics_global.dart
@@ -0,0 +1,39 @@
+import 'dart:convert';
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+
+import 'package:scrobbles/models/statistics_global.dart';
+import 'package:scrobbles/network/scrobbles.dart';
+import 'package:scrobbles/ui/widgets/card_content.dart';
+import 'package:scrobbles/ui/widgets/content/statistics_global.dart';
+import 'package:scrobbles/ui/widgets/error.dart';
+
+class CardStatisticsGlobal extends StatelessWidget {
+  const CardStatisticsGlobal({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    late Future<StatisticsGlobalData> future = ScrobblesApi.fetchGlobalStatistics();
+
+    return FutureBuilder<StatisticsGlobalData>(
+      future: future,
+      builder: (context, snapshot) {
+        if (snapshot.hasError) {
+          return ShowErrorWidget(message: '${snapshot.error}');
+        }
+
+        return CardContent(
+          color: Theme.of(context).colorScheme.primary,
+          title: 'global_statistics'.tr(),
+          content: ContentStatisticsGlobal(
+            statistics: snapshot.hasData
+                ? StatisticsGlobalData.fromJson(jsonDecode(snapshot.data.toString()))
+                : StatisticsGlobalData.createEmpty(),
+            isLoading: !snapshot.hasData,
+          ),
+        );
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/cards/statistics_recent.dart b/lib/ui/widgets/cards/statistics_recent.dart
new file mode 100644
index 0000000..0925c42
--- /dev/null
+++ b/lib/ui/widgets/cards/statistics_recent.dart
@@ -0,0 +1,45 @@
+import 'dart:convert';
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+
+import 'package:scrobbles/models/statistics_recent.dart';
+import 'package:scrobbles/network/scrobbles.dart';
+import 'package:scrobbles/ui/widgets/card_content.dart';
+import 'package:scrobbles/ui/widgets/content/statistics_recent.dart';
+import 'package:scrobbles/ui/widgets/error.dart';
+
+class CardStatisticsRecent extends StatelessWidget {
+  const CardStatisticsRecent({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    final int daysCount = 21;
+
+    late Future<StatisticsRecentData> future = ScrobblesApi.fetchRecentStatistics(daysCount);
+
+    return FutureBuilder<StatisticsRecentData>(
+      future: future,
+      builder: (context, snapshot) {
+        if (snapshot.hasError) {
+          return ShowErrorWidget(message: '${snapshot.error}');
+        }
+
+        return CardContent(
+          color: Theme.of(context).colorScheme.primary,
+          title: 'recent_statistics'.tr(
+            namedArgs: {
+              'daysCount': daysCount.toString(),
+            },
+          ),
+          content: ContentStatisticsRecent(
+            statistics: snapshot.hasData
+                ? StatisticsRecentData.fromJson(jsonDecode(snapshot.data.toString()))
+                : StatisticsRecentData.createEmpty(),
+            isLoading: !snapshot.hasData,
+          ),
+        );
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/cards/timeline.dart b/lib/ui/widgets/cards/timeline.dart
new file mode 100644
index 0000000..a7ea95d
--- /dev/null
+++ b/lib/ui/widgets/cards/timeline.dart
@@ -0,0 +1,51 @@
+import 'dart:convert';
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+
+import 'package:scrobbles/models/timeline.dart';
+import 'package:scrobbles/network/scrobbles.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';
+import 'package:scrobbles/ui/widgets/error.dart';
+
+class CardTimeline extends StatelessWidget {
+  const CardTimeline({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    final int daysCount = 14;
+    late Future<TimelineData> future = ScrobblesApi.fetchTimeline(daysCount);
+
+    return FutureBuilder<TimelineData>(
+      future: future,
+      builder: (context, snapshot) {
+        if (snapshot.hasError) {
+          return ShowErrorWidget(message: '${snapshot.error}');
+        }
+
+        return CardContent(
+          color: Theme.of(context).colorScheme.surface,
+          title: 'timeline_title'.tr(
+            namedArgs: {
+              'daysCount': daysCount.toString(),
+            },
+          ),
+          content: Stack(
+            children: [
+              ChartTimelineCounts(
+                chartData: TimelineData.fromJson(jsonDecode(snapshot.data.toString())),
+                isLoading: !snapshot.hasData,
+              ),
+              ChartTimelineEclecticism(
+                chartData: TimelineData.fromJson(jsonDecode(snapshot.data.toString())),
+                isLoading: !snapshot.hasData,
+              ),
+            ],
+          ),
+        );
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/cards/top_artists.dart b/lib/ui/widgets/cards/top_artists.dart
new file mode 100644
index 0000000..d589518
--- /dev/null
+++ b/lib/ui/widgets/cards/top_artists.dart
@@ -0,0 +1,44 @@
+import 'dart:convert';
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+
+import 'package:scrobbles/models/topartists.dart';
+import 'package:scrobbles/network/scrobbles.dart';
+import 'package:scrobbles/ui/widgets/card_content.dart';
+import 'package:scrobbles/ui/widgets/charts/top_artists.dart';
+import 'package:scrobbles/ui/widgets/error.dart';
+
+class CardTopArtists extends StatelessWidget {
+  const CardTopArtists({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    final int daysCount = 14;
+    late Future<TopArtistsData> future = ScrobblesApi.fetchTopArtists(daysCount);
+
+    return FutureBuilder<TopArtistsData>(
+      future: future,
+      builder: (context, snapshot) {
+        if (snapshot.hasError) {
+          return ShowErrorWidget(message: '${snapshot.error}');
+        }
+
+        return CardContent(
+          color: Theme.of(context).colorScheme.surface,
+          title: 'top_artists_title'.tr(
+            namedArgs: {
+              'daysCount': daysCount.toString(),
+            },
+          ),
+          content: ChartTopArtists(
+            chartData: snapshot.hasData
+                ? TopArtistsData.fromJson(jsonDecode(snapshot.data.toString()))
+                : TopArtistsData.createEmpty(),
+            isLoading: !snapshot.hasData,
+          ),
+        );
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/main_screen/counts_by_day_chart.dart b/lib/ui/widgets/charts/counts_by_day.dart
similarity index 76%
rename from lib/ui/widgets/main_screen/counts_by_day_chart.dart
rename to lib/ui/widgets/charts/counts_by_day.dart
index 360dcc1..19492b4 100644
--- a/lib/ui/widgets/main_screen/counts_by_day_chart.dart
+++ b/lib/ui/widgets/charts/counts_by_day.dart
@@ -2,14 +2,15 @@ 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 '../../../ui/widgets/charts/custom_bar_chart.dart';
+import 'package:scrobbles/config/app_colors.dart';
+import 'package:scrobbles/models/counts_by_day.dart';
+import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart';
 
-class CountsByDayCardContentChart extends CustomBarChart {
+class ChartCountsByDay extends CustomBarChart {
   final CountsByDayData chartData;
+  final bool isLoading;
 
-  CountsByDayCardContentChart({super.key, required this.chartData});
+  ChartCountsByDay({super.key, required this.chartData, required this.isLoading});
 
   final double verticalTicksInterval = 5;
   final String verticalAxisTitleSuffix = '%';
@@ -18,12 +19,19 @@ class CountsByDayCardContentChart extends CustomBarChart {
   Widget build(BuildContext context) {
     return Container(
       height: this.chartHeight,
-      child: LayoutBuilder(builder: (context, constraints) {
-        return getBarChart(
-          barWidth: this.getBarWidth(constraints.maxWidth, this.chartData.data.keys.length),
-          backgroundColor: Theme.of(context).colorScheme.onSurface,
-        );
-      }),
+      child: this.isLoading
+          ? this.placeholder
+          : LayoutBuilder(
+              builder: (context, constraints) {
+                final double maxWidth = constraints.maxWidth;
+                final int barsCount = this.chartData.data.keys.length;
+
+                return getBarChart(
+                  barWidth: this.getBarWidth(maxWidth, barsCount),
+                  backgroundColor: Theme.of(context).colorScheme.onSurface,
+                );
+              },
+            ),
     );
   }
 
diff --git a/lib/ui/widgets/main_screen/counts_by_hour_chart.dart b/lib/ui/widgets/charts/counts_by_hour.dart
similarity index 63%
rename from lib/ui/widgets/main_screen/counts_by_hour_chart.dart
rename to lib/ui/widgets/charts/counts_by_hour.dart
index ec5766a..79c8167 100644
--- a/lib/ui/widgets/main_screen/counts_by_hour_chart.dart
+++ b/lib/ui/widgets/charts/counts_by_hour.dart
@@ -1,15 +1,16 @@
 import 'package:flutter/material.dart';
 import 'package:fl_chart/fl_chart.dart';
 
-import '../../../config/app_colors.dart';
-import '../../../models/counts_by_hour.dart';
-import '../../../ui/widgets/charts/custom_bar_chart.dart';
-import '../../../utils/color_extensions.dart';
+import 'package:scrobbles/config/app_colors.dart';
+import 'package:scrobbles/models/counts_by_hour.dart';
+import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart';
+import 'package:scrobbles/utils/color_extensions.dart';
 
-class CountsByHourCardContentChart extends CustomBarChart {
+class ChartCountsByHour extends CustomBarChart {
   final CountsByHourData chartData;
+  final bool isLoading;
 
-  CountsByHourCardContentChart({super.key, required this.chartData});
+  ChartCountsByHour({super.key, required this.chartData, required this.isLoading});
 
   final double verticalTicksInterval = 5;
   final String verticalAxisTitleSuffix = '%';
@@ -18,12 +19,19 @@ class CountsByHourCardContentChart extends CustomBarChart {
   Widget build(BuildContext context) {
     return Container(
       height: this.chartHeight,
-      child: LayoutBuilder(builder: (context, constraints) {
-        return getBarChart(
-          barWidth: this.getBarWidth(constraints.maxWidth, this.chartData.data.keys.length),
-          backgroundColor: Theme.of(context).colorScheme.onSurface,
-        );
-      }),
+      child: this.isLoading
+          ? this.placeholder
+          : LayoutBuilder(
+              builder: (context, constraints) {
+                final double maxWidth = constraints.maxWidth;
+                final int barsCount = this.chartData.data.keys.length;
+
+                return getBarChart(
+                  barWidth: this.getBarWidth(maxWidth, barsCount),
+                  backgroundColor: Theme.of(context).colorScheme.onSurface,
+                );
+              },
+            ),
     );
   }
 
diff --git a/lib/ui/widgets/main_screen/discoveries_chart_artists.dart b/lib/ui/widgets/charts/discoveries_artists.dart
similarity index 61%
rename from lib/ui/widgets/main_screen/discoveries_chart_artists.dart
rename to lib/ui/widgets/charts/discoveries_artists.dart
index eb76c31..d441c27 100644
--- a/lib/ui/widgets/main_screen/discoveries_chart_artists.dart
+++ b/lib/ui/widgets/charts/discoveries_artists.dart
@@ -1,26 +1,34 @@
 import 'package:flutter/material.dart';
 import 'package:fl_chart/fl_chart.dart';
 
-import '../../../config/app_colors.dart';
-import '../../../models/discoveries.dart';
-import '../../../utils/color_extensions.dart';
-import '../../../ui/widgets/charts/custom_bar_chart.dart';
+import 'package:scrobbles/config/app_colors.dart';
+import 'package:scrobbles/models/discoveries.dart';
+import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart';
+import 'package:scrobbles/utils/color_extensions.dart';
 
 class ChartDiscoveriesArtists extends CustomBarChart {
   final DiscoveriesData chartData;
+  final bool isLoading;
 
-  ChartDiscoveriesArtists({super.key, required this.chartData});
+  ChartDiscoveriesArtists({super.key, required this.chartData, required this.isLoading});
 
   @override
   Widget build(BuildContext context) {
     return Container(
       height: this.chartHeight,
-      child: LayoutBuilder(builder: (context, constraints) {
-        return getBarChart(
-          barWidth: this.getBarWidth(constraints.maxWidth, this.chartData.data.keys.length),
-          backgroundColor: Theme.of(context).colorScheme.onSurface,
-        );
-      }),
+      child: this.isLoading
+          ? this.placeholder
+          : LayoutBuilder(
+              builder: (context, constraints) {
+                final double maxWidth = constraints.maxWidth;
+                final int barsCount = this.chartData.data.keys.length;
+
+                return getBarChart(
+                  barWidth: this.getBarWidth(maxWidth, barsCount),
+                  backgroundColor: Theme.of(context).colorScheme.onSurface,
+                );
+              },
+            ),
     );
   }
 
diff --git a/lib/ui/widgets/main_screen/discoveries_chart_tracks.dart b/lib/ui/widgets/charts/discoveries_tracks.dart
similarity index 61%
rename from lib/ui/widgets/main_screen/discoveries_chart_tracks.dart
rename to lib/ui/widgets/charts/discoveries_tracks.dart
index 622911d..343c02d 100644
--- a/lib/ui/widgets/main_screen/discoveries_chart_tracks.dart
+++ b/lib/ui/widgets/charts/discoveries_tracks.dart
@@ -1,26 +1,34 @@
 import 'package:flutter/material.dart';
 import 'package:fl_chart/fl_chart.dart';
 
-import '../../../config/app_colors.dart';
-import '../../../models/discoveries.dart';
-import '../../../utils/color_extensions.dart';
-import '../../../ui/widgets/charts/custom_bar_chart.dart';
+import 'package:scrobbles/config/app_colors.dart';
+import 'package:scrobbles/models/discoveries.dart';
+import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart';
+import 'package:scrobbles/utils/color_extensions.dart';
 
 class ChartDiscoveriesTracks extends CustomBarChart {
   final DiscoveriesData chartData;
+  final bool isLoading;
 
-  ChartDiscoveriesTracks({super.key, required this.chartData});
+  ChartDiscoveriesTracks({super.key, required this.chartData, required this.isLoading});
 
   @override
   Widget build(BuildContext context) {
     return Container(
       height: this.chartHeight,
-      child: LayoutBuilder(builder: (context, constraints) {
-        return getBarChart(
-          barWidth: this.getBarWidth(constraints.maxWidth, this.chartData.data.keys.length),
-          backgroundColor: Theme.of(context).colorScheme.onSurface,
-        );
-      }),
+      child: this.isLoading
+          ? this.placeholder
+          : LayoutBuilder(
+              builder: (context, constraints) {
+                final double maxWidth = constraints.maxWidth;
+                final int barsCount = this.chartData.data.keys.length;
+
+                return getBarChart(
+                  barWidth: this.getBarWidth(maxWidth, barsCount),
+                  backgroundColor: Theme.of(context).colorScheme.onSurface,
+                );
+              },
+            ),
     );
   }
 
diff --git a/lib/ui/widgets/main_screen/timeline_chart_counts.dart b/lib/ui/widgets/charts/timeline_counts.dart
similarity index 62%
rename from lib/ui/widgets/main_screen/timeline_chart_counts.dart
rename to lib/ui/widgets/charts/timeline_counts.dart
index bdfa0c5..8e5fec0 100644
--- a/lib/ui/widgets/main_screen/timeline_chart_counts.dart
+++ b/lib/ui/widgets/charts/timeline_counts.dart
@@ -1,14 +1,15 @@
 import 'package:flutter/material.dart';
 import 'package:fl_chart/fl_chart.dart';
 
-import '../../../config/app_colors.dart';
-import '../../../models/timeline.dart';
-import '../../../ui/widgets/charts/custom_bar_chart.dart';
+import 'package:scrobbles/config/app_colors.dart';
+import 'package:scrobbles/models/timeline.dart';
+import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart';
 
 class ChartTimelineCounts extends CustomBarChart {
   final TimelineData chartData;
+  final bool isLoading;
 
-  ChartTimelineCounts({super.key, required this.chartData});
+  ChartTimelineCounts({super.key, required this.chartData, required this.isLoading});
 
   final double verticalTicksInterval = 50;
 
@@ -16,12 +17,19 @@ class ChartTimelineCounts extends CustomBarChart {
   Widget build(BuildContext context) {
     return Container(
       height: this.chartHeight,
-      child: LayoutBuilder(builder: (context, constraints) {
-        return getBarChart(
-          barWidth: this.getBarWidth(constraints.maxWidth, this.chartData.data.keys.length),
-          backgroundColor: Theme.of(context).colorScheme.onSurface,
-        );
-      }),
+      child: this.isLoading
+          ? this.placeholder
+          : LayoutBuilder(
+              builder: (context, constraints) {
+                final double maxWidth = constraints.maxWidth;
+                final int barsCount = this.chartData.data.keys.length;
+
+                return getBarChart(
+                  barWidth: this.getBarWidth(maxWidth, barsCount),
+                  backgroundColor: Theme.of(context).colorScheme.onSurface,
+                );
+              },
+            ),
     );
   }
 
diff --git a/lib/ui/widgets/main_screen/timeline_chart_eclecticism.dart b/lib/ui/widgets/charts/timeline_eclecticism.dart
similarity index 66%
rename from lib/ui/widgets/main_screen/timeline_chart_eclecticism.dart
rename to lib/ui/widgets/charts/timeline_eclecticism.dart
index 71e358e..f5ddd96 100644
--- a/lib/ui/widgets/main_screen/timeline_chart_eclecticism.dart
+++ b/lib/ui/widgets/charts/timeline_eclecticism.dart
@@ -1,15 +1,16 @@
 import 'package:flutter/material.dart';
 import 'package:fl_chart/fl_chart.dart';
 
-import '../../../config/app_colors.dart';
-import '../../../models/timeline.dart';
-import '../../../ui/widgets/charts/custom_line_chart.dart';
-import '../../../utils/color_extensions.dart';
+import 'package:scrobbles/config/app_colors.dart';
+import 'package:scrobbles/models/timeline.dart';
+import 'package:scrobbles/ui/widgets/abstracts/custom_line_chart.dart';
+import 'package:scrobbles/utils/color_extensions.dart';
 
 class ChartTimelineEclecticism extends CustomLineChart {
   final TimelineData chartData;
+  final bool isLoading;
 
-  ChartTimelineEclecticism({super.key, required this.chartData});
+  ChartTimelineEclecticism({super.key, required this.chartData, required this.isLoading});
 
   @override
   Widget build(BuildContext context) {
@@ -17,20 +18,22 @@ class ChartTimelineEclecticism extends CustomLineChart {
 
     return Container(
       height: this.chartHeight,
-      child: LineChart(
-        LineChartData(
-          lineBarsData: getDataEclecticism(),
-          borderData: getBorderData(),
-          gridData: getGridData(),
-          titlesData: getTitlesData(),
-          lineTouchData: const LineTouchData(enabled: false),
-          minX: horizontalScale['min'],
-          maxX: horizontalScale['max'],
-          maxY: 100,
-          minY: 0,
-        ),
-        duration: const Duration(milliseconds: 250),
-      ),
+      child: this.isLoading
+          ? this.placeholder
+          : LineChart(
+              LineChartData(
+                lineBarsData: getDataEclecticism(),
+                borderData: getBorderData(),
+                gridData: getGridData(),
+                titlesData: getTitlesData(),
+                lineTouchData: const LineTouchData(enabled: false),
+                minX: horizontalScale['min'],
+                maxX: horizontalScale['max'],
+                maxY: 100,
+                minY: 0,
+              ),
+              duration: const Duration(milliseconds: 250),
+            ),
     );
   }
 
diff --git a/lib/ui/widgets/main_screen/topartists_chart.dart b/lib/ui/widgets/charts/top_artists.dart
similarity index 71%
rename from lib/ui/widgets/main_screen/topartists_chart.dart
rename to lib/ui/widgets/charts/top_artists.dart
index 0a6bcd8..5b0de9c 100644
--- a/lib/ui/widgets/main_screen/topartists_chart.dart
+++ b/lib/ui/widgets/charts/top_artists.dart
@@ -1,39 +1,44 @@
 import 'package:flutter/material.dart';
 import 'package:fl_chart/fl_chart.dart';
 
-import '../../../config/app_colors.dart';
-import '../../../models/topartists.dart';
-import '../../../utils/color_extensions.dart';
+import 'package:scrobbles/config/app_colors.dart';
+import 'package:scrobbles/models/topartists.dart';
+import 'package:scrobbles/utils/color_extensions.dart';
 
 class ChartTopArtists extends StatelessWidget {
   final TopArtistsData chartData;
+  final bool isLoading;
 
-  ChartTopArtists({super.key, required this.chartData});
+  ChartTopArtists({super.key, required this.chartData, required this.isLoading});
+
+  final Widget placeholder = Text('⏳');
 
   @override
   Widget build(BuildContext context) {
     return AspectRatio(
       aspectRatio: 2.1,
-      child: Row(
-        children: <Widget>[
-          Expanded(
-            child: AspectRatio(
-              aspectRatio: 1,
-              child: PieChart(
-                PieChartData(
-                  sections: getPieChartData(),
-                  sectionsSpace: 2,
-                  centerSpaceRadius: 40,
-                  startDegreeOffset: -45,
-                  pieTouchData: PieTouchData(enabled: false),
-                  borderData: FlBorderData(show: false),
+      child: this.isLoading
+          ? this.placeholder
+          : Row(
+              children: <Widget>[
+                Expanded(
+                  child: AspectRatio(
+                    aspectRatio: 1,
+                    child: PieChart(
+                      PieChartData(
+                        sections: getPieChartData(),
+                        sectionsSpace: 2,
+                        centerSpaceRadius: 40,
+                        startDegreeOffset: -45,
+                        pieTouchData: PieTouchData(enabled: false),
+                        borderData: FlBorderData(show: false),
+                      ),
+                    ),
+                  ),
                 ),
-              ),
+                buildLegendWidget(),
+              ],
             ),
-          ),
-          buildLegendWidget(),
-        ],
-      ),
     );
   }
 
diff --git a/lib/ui/widgets/main_screen/statistics_global_content.dart b/lib/ui/widgets/content/statistics_global.dart
similarity index 77%
rename from lib/ui/widgets/main_screen/statistics_global_content.dart
rename to lib/ui/widgets/content/statistics_global.dart
index 4459347..d171441 100644
--- a/lib/ui/widgets/main_screen/statistics_global_content.dart
+++ b/lib/ui/widgets/content/statistics_global.dart
@@ -1,29 +1,24 @@
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 
-import '../../../models/statistics_global.dart';
+import 'package:scrobbles/models/statistics_global.dart';
 
-class StatisticsGlobalContent extends StatelessWidget {
+class ContentStatisticsGlobal extends StatelessWidget {
   final StatisticsGlobalData statistics;
   final bool isLoading;
 
-  const StatisticsGlobalContent(
+  const ContentStatisticsGlobal(
       {super.key, required this.statistics, required this.isLoading});
 
   @override
   Widget build(BuildContext context) {
     final TextTheme textTheme = Theme.of(context).primaryTextTheme;
-
-    final String placeholder = '⏳';
+    const String placeholder = '⏳';
 
     return Column(
       mainAxisSize: MainAxisSize.min,
       crossAxisAlignment: CrossAxisAlignment.start,
       children: <Widget>[
-        Text(
-          'global_statistics',
-          style: textTheme.titleLarge!.apply(fontWeightDelta: 2),
-        ).tr(),
         Text(
           'statistics_total_scrobbles_count',
           style: textTheme.bodyMedium,
diff --git a/lib/ui/widgets/main_screen/statistics_recent_content.dart b/lib/ui/widgets/content/statistics_recent.dart
similarity index 64%
rename from lib/ui/widgets/main_screen/statistics_recent_content.dart
rename to lib/ui/widgets/content/statistics_recent.dart
index cd95b00..01f04ac 100644
--- a/lib/ui/widgets/main_screen/statistics_recent_content.dart
+++ b/lib/ui/widgets/content/statistics_recent.dart
@@ -1,38 +1,24 @@
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 
-import '../../../models/statistics_recent.dart';
+import 'package:scrobbles/models/statistics_recent.dart';
 
-class StatisticsRecentContent extends StatelessWidget {
+class ContentStatisticsRecent extends StatelessWidget {
   final StatisticsRecentData statistics;
   final bool isLoading;
 
-  const StatisticsRecentContent(
+  const ContentStatisticsRecent(
       {super.key, required this.statistics, required this.isLoading});
 
   @override
   Widget build(BuildContext context) {
     final TextTheme textTheme = Theme.of(context).primaryTextTheme;
-
-    final String placeholder = '⏳';
+    const String placeholder = '⏳';
 
     return Column(
       mainAxisSize: MainAxisSize.min,
       crossAxisAlignment: CrossAxisAlignment.start,
       children: <Widget>[
-        Text(
-          'recent_statistics',
-          style: textTheme.titleLarge!.apply(fontWeightDelta: 2),
-        ).tr(),
-        Text(
-          'statistics_selected_period',
-          style: textTheme.bodyMedium!.apply(fontWeightDelta: 2),
-        ).tr(
-          namedArgs: {
-            'daysCount':
-                this.isLoading ? placeholder : this.statistics.selectedPeriod.toString(),
-          },
-        ),
         Text(
           'statistics_recent_scrobbles_count_and_discoveries',
           style: textTheme.bodyMedium,
diff --git a/lib/ui/widgets/header.dart b/lib/ui/widgets/header_app.dart
similarity index 78%
rename from lib/ui/widgets/header.dart
rename to lib/ui/widgets/header_app.dart
index 2187ef8..77b015b 100644
--- a/lib/ui/widgets/header.dart
+++ b/lib/ui/widgets/header_app.dart
@@ -1,8 +1,8 @@
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 
-class Header extends StatelessWidget {
-  const Header({super.key, required this.text});
+class AppHeader extends StatelessWidget {
+  const AppHeader({super.key, required this.text});
 
   final String text;
 
diff --git a/lib/ui/widgets/main_screen/counts_by_day_card.dart b/lib/ui/widgets/main_screen/counts_by_day_card.dart
deleted file mode 100644
index 7f46132..0000000
--- a/lib/ui/widgets/main_screen/counts_by_day_card.dart
+++ /dev/null
@@ -1,48 +0,0 @@
-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.surface,
-          shape: const RoundedRectangleBorder(
-            borderRadius: BorderRadius.all(
-              Radius.circular(8),
-            ),
-          ),
-          child: Padding(
-            padding: const EdgeInsets.all(8.0),
-            child: ChartCountsByDayCardContent(
-              daysCount: daysCount,
-              chartData: snapshot.hasData
-                  ? CountsByDayData.fromJson(jsonDecode(snapshot.data.toString()))
-                  : CountsByDayData.createEmpty(),
-              isLoading: !snapshot.hasData,
-            ),
-          ),
-        );
-      },
-    );
-  }
-}
diff --git a/lib/ui/widgets/main_screen/counts_by_day_content.dart b/lib/ui/widgets/main_screen/counts_by_day_content.dart
deleted file mode 100644
index 3e31962..0000000
--- a/lib/ui/widgets/main_screen/counts_by_day_content.dart
+++ /dev/null
@@ -1,43 +0,0 @@
-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())),
-              ),
-      ],
-    );
-  }
-}
diff --git a/lib/ui/widgets/main_screen/counts_by_hour_card.dart b/lib/ui/widgets/main_screen/counts_by_hour_card.dart
deleted file mode 100644
index 8060aed..0000000
--- a/lib/ui/widgets/main_screen/counts_by_hour_card.dart
+++ /dev/null
@@ -1,49 +0,0 @@
-import 'dart:convert';
-
-import 'package:flutter/material.dart';
-
-import '../../../models/counts_by_hour.dart';
-import '../../../network/scrobbles_api.dart';
-import '../../../ui/widgets/error.dart';
-import '../../../ui/widgets/main_screen/counts_by_hour_content.dart';
-
-class ChartCountsByHourCard extends StatelessWidget {
-  const ChartCountsByHourCard({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    final int daysCount = 21;
-    late Future<CountsByHourData> futureCountsByHour =
-        ScrobblesApi.fetchCountsByHour(daysCount);
-
-    return FutureBuilder<CountsByHourData>(
-      future: futureCountsByHour,
-      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.surface,
-          shape: const RoundedRectangleBorder(
-            borderRadius: BorderRadius.all(
-              Radius.circular(8),
-            ),
-          ),
-          child: Padding(
-            padding: const EdgeInsets.all(8.0),
-            child: ChartCountsByHourCardContent(
-              daysCount: daysCount,
-              chartData: snapshot.hasData
-                  ? CountsByHourData.fromJson(jsonDecode(snapshot.data.toString()))
-                  : CountsByHourData.createEmpty(),
-              isLoading: !snapshot.hasData,
-            ),
-          ),
-        );
-      },
-    );
-  }
-}
diff --git a/lib/ui/widgets/main_screen/counts_by_hour_content.dart b/lib/ui/widgets/main_screen/counts_by_hour_content.dart
deleted file mode 100644
index d53c34f..0000000
--- a/lib/ui/widgets/main_screen/counts_by_hour_content.dart
+++ /dev/null
@@ -1,43 +0,0 @@
-import 'dart:convert';
-
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-
-import '../../../models/counts_by_hour.dart';
-import '../../../ui/widgets/main_screen/counts_by_hour_chart.dart';
-
-class ChartCountsByHourCardContent extends StatelessWidget {
-  final int daysCount;
-  final CountsByHourData chartData;
-  final bool isLoading;
-
-  const ChartCountsByHourCardContent(
-      {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_hour',
-          style: textTheme.titleLarge!.apply(fontWeightDelta: 2),
-        ).tr(
-          namedArgs: {
-            'daysCount': this.daysCount.toString(),
-          },
-        ),
-        const SizedBox(height: 8),
-        this.isLoading
-            ? Text(placeholder)
-            : CountsByHourCardContentChart(
-                chartData: CountsByHourData.fromJson(jsonDecode(this.chartData.toString())),
-              ),
-      ],
-    );
-  }
-}
diff --git a/lib/ui/widgets/main_screen/discoveries_card.dart b/lib/ui/widgets/main_screen/discoveries_card.dart
deleted file mode 100644
index 57f0d04..0000000
--- a/lib/ui/widgets/main_screen/discoveries_card.dart
+++ /dev/null
@@ -1,61 +0,0 @@
-import 'dart:convert';
-
-import 'package:flutter/material.dart';
-
-import '../../../models/discoveries.dart';
-import '../../../network/scrobbles_api.dart';
-import '../../../ui/widgets/error.dart';
-import 'discoveries_content_artists.dart';
-import 'discoveries_content_tracks.dart';
-
-class ChartDiscoveriesCard extends StatelessWidget {
-  const ChartDiscoveriesCard({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    final int daysCount = 14;
-    late Future<DiscoveriesData> futureTimeline = ScrobblesApi.fetchDiscoveries(daysCount);
-
-    return FutureBuilder<DiscoveriesData>(
-      future: futureTimeline,
-      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.surface,
-          shape: const RoundedRectangleBorder(
-            borderRadius: BorderRadius.all(
-              Radius.circular(8),
-            ),
-          ),
-          child: Padding(
-            padding: const EdgeInsets.all(8.0),
-            child: Column(
-              children: [
-                ChartDiscoveriesArtistsCardContent(
-                  daysCount: daysCount,
-                  chartData: snapshot.hasData
-                      ? DiscoveriesData.fromJson(jsonDecode(snapshot.data.toString()))
-                      : DiscoveriesData.createEmpty(),
-                  isLoading: !snapshot.hasData,
-                ),
-                const SizedBox(height: 8),
-                ChartDiscoveriesTracksCardContent(
-                  daysCount: daysCount,
-                  chartData: snapshot.hasData
-                      ? DiscoveriesData.fromJson(jsonDecode(snapshot.data.toString()))
-                      : DiscoveriesData.createEmpty(),
-                  isLoading: !snapshot.hasData,
-                ),
-              ],
-            ),
-          ),
-        );
-      },
-    );
-  }
-}
diff --git a/lib/ui/widgets/main_screen/discoveries_content_artists.dart b/lib/ui/widgets/main_screen/discoveries_content_artists.dart
deleted file mode 100644
index b608e9d..0000000
--- a/lib/ui/widgets/main_screen/discoveries_content_artists.dart
+++ /dev/null
@@ -1,43 +0,0 @@
-import 'dart:convert';
-
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-
-import '../../../models/discoveries.dart';
-import 'discoveries_chart_artists.dart';
-
-class ChartDiscoveriesArtistsCardContent extends StatelessWidget {
-  final int daysCount;
-  final DiscoveriesData chartData;
-  final bool isLoading;
-
-  const ChartDiscoveriesArtistsCardContent(
-      {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(
-          'discoveries_artists_title',
-          style: textTheme.titleLarge!.apply(fontWeightDelta: 2),
-        ).tr(
-          namedArgs: {
-            'daysCount': this.daysCount.toString(),
-          },
-        ),
-        const SizedBox(height: 8),
-        this.isLoading
-            ? Text(placeholder)
-            : ChartDiscoveriesArtists(
-                chartData: DiscoveriesData.fromJson(jsonDecode(this.chartData.toString())),
-              ),
-      ],
-    );
-  }
-}
diff --git a/lib/ui/widgets/main_screen/discoveries_content_tracks.dart b/lib/ui/widgets/main_screen/discoveries_content_tracks.dart
deleted file mode 100644
index 9ca8ff1..0000000
--- a/lib/ui/widgets/main_screen/discoveries_content_tracks.dart
+++ /dev/null
@@ -1,43 +0,0 @@
-import 'dart:convert';
-
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-
-import '../../../models/discoveries.dart';
-import 'discoveries_chart_tracks.dart';
-
-class ChartDiscoveriesTracksCardContent extends StatelessWidget {
-  final int daysCount;
-  final DiscoveriesData chartData;
-  final bool isLoading;
-
-  const ChartDiscoveriesTracksCardContent(
-      {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(
-          'discoveries_tracks_title',
-          style: textTheme.titleLarge!.apply(fontWeightDelta: 2),
-        ).tr(
-          namedArgs: {
-            'daysCount': this.daysCount.toString(),
-          },
-        ),
-        const SizedBox(height: 8),
-        this.isLoading
-            ? Text(placeholder)
-            : ChartDiscoveriesTracks(
-                chartData: DiscoveriesData.fromJson(jsonDecode(this.chartData.toString())),
-              ),
-      ],
-    );
-  }
-}
diff --git a/lib/ui/widgets/main_screen/statistics_global_card.dart b/lib/ui/widgets/main_screen/statistics_global_card.dart
deleted file mode 100644
index 1a7474c..0000000
--- a/lib/ui/widgets/main_screen/statistics_global_card.dart
+++ /dev/null
@@ -1,46 +0,0 @@
-import 'dart:convert';
-
-import 'package:flutter/material.dart';
-
-import '../../../models/statistics_global.dart';
-import '../../../network/scrobbles_api.dart';
-import '../../../ui/widgets/error.dart';
-import 'statistics_global_content.dart';
-
-class StatisticsGlobalCard extends StatelessWidget {
-  const StatisticsGlobalCard({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    late Future<StatisticsGlobalData> future = ScrobblesApi.fetchGlobalStatistics();
-
-    return FutureBuilder<StatisticsGlobalData>(
-      future: future,
-      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(8),
-            ),
-          ),
-          child: Padding(
-            padding: const EdgeInsets.all(8.0),
-            child: StatisticsGlobalContent(
-              statistics: snapshot.hasData
-                  ? StatisticsGlobalData.fromJson(jsonDecode(snapshot.data.toString()))
-                  : StatisticsGlobalData.createEmpty(),
-              isLoading: !snapshot.hasData,
-            ),
-          ),
-        );
-      },
-    );
-  }
-}
diff --git a/lib/ui/widgets/main_screen/statistics_recent_card.dart b/lib/ui/widgets/main_screen/statistics_recent_card.dart
deleted file mode 100644
index 5f12d27..0000000
--- a/lib/ui/widgets/main_screen/statistics_recent_card.dart
+++ /dev/null
@@ -1,48 +0,0 @@
-import 'dart:convert';
-
-import 'package:flutter/material.dart';
-
-import '../../../models/statistics_recent.dart';
-import '../../../network/scrobbles_api.dart';
-import '../../../ui/widgets/error.dart';
-import 'statistics_recent_content.dart';
-
-class StatisticsRecentCard extends StatelessWidget {
-  const StatisticsRecentCard({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    final int daysCount = 21;
-
-    late Future<StatisticsRecentData> future = ScrobblesApi.fetchRecentStatistics(daysCount);
-
-    return FutureBuilder<StatisticsRecentData>(
-      future: future,
-      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(8),
-            ),
-          ),
-          child: Padding(
-            padding: const EdgeInsets.all(8.0),
-            child: StatisticsRecentContent(
-              statistics: snapshot.hasData
-                  ? StatisticsRecentData.fromJson(jsonDecode(snapshot.data.toString()))
-                  : StatisticsRecentData.createEmpty(),
-              isLoading: !snapshot.hasData,
-            ),
-          ),
-        );
-      },
-    );
-  }
-}
diff --git a/lib/ui/widgets/main_screen/timeline_card.dart b/lib/ui/widgets/main_screen/timeline_card.dart
deleted file mode 100644
index 931ea22..0000000
--- a/lib/ui/widgets/main_screen/timeline_card.dart
+++ /dev/null
@@ -1,48 +0,0 @@
-import 'dart:convert';
-
-import 'package:flutter/material.dart';
-
-import '../../../models/timeline.dart';
-import '../../../network/scrobbles_api.dart';
-import '../../../ui/widgets/error.dart';
-import '../../../ui/widgets/main_screen/timeline_content.dart';
-
-class ChartTimelineCard extends StatelessWidget {
-  const ChartTimelineCard({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    final int daysCount = 14;
-    late Future<TimelineData> futureTimeline = ScrobblesApi.fetchTimeline(daysCount);
-
-    return FutureBuilder<TimelineData>(
-      future: futureTimeline,
-      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.surface,
-          shape: const RoundedRectangleBorder(
-            borderRadius: BorderRadius.all(
-              Radius.circular(8),
-            ),
-          ),
-          child: Padding(
-            padding: const EdgeInsets.all(8.0),
-            child: ChartTimelineCardContent(
-              daysCount: daysCount,
-              chartData: snapshot.hasData
-                  ? TimelineData.fromJson(jsonDecode(snapshot.data.toString()))
-                  : TimelineData.createEmpty(),
-              isLoading: !snapshot.hasData,
-            ),
-          ),
-        );
-      },
-    );
-  }
-}
diff --git a/lib/ui/widgets/main_screen/timeline_content.dart b/lib/ui/widgets/main_screen/timeline_content.dart
deleted file mode 100644
index 1533ee1..0000000
--- a/lib/ui/widgets/main_screen/timeline_content.dart
+++ /dev/null
@@ -1,51 +0,0 @@
-import 'dart:convert';
-
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-
-import '../../../models/timeline.dart';
-import 'timeline_chart_counts.dart';
-import 'timeline_chart_eclecticism.dart';
-
-class ChartTimelineCardContent extends StatelessWidget {
-  final int daysCount;
-  final TimelineData chartData;
-  final bool isLoading;
-
-  const ChartTimelineCardContent(
-      {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(
-          'timeline_title',
-          style: textTheme.titleLarge!.apply(fontWeightDelta: 2),
-        ).tr(
-          namedArgs: {
-            'daysCount': this.daysCount.toString(),
-          },
-        ),
-        const SizedBox(height: 8),
-        this.isLoading
-            ? Text(placeholder)
-            : Stack(
-                children: [
-                  ChartTimelineCounts(
-                    chartData: TimelineData.fromJson(jsonDecode(this.chartData.toString())),
-                  ),
-                  ChartTimelineEclecticism(
-                    chartData: TimelineData.fromJson(jsonDecode(this.chartData.toString())),
-                  ),
-                ],
-              ),
-      ],
-    );
-  }
-}
diff --git a/lib/ui/widgets/main_screen/topartists_card.dart b/lib/ui/widgets/main_screen/topartists_card.dart
deleted file mode 100644
index e26de07..0000000
--- a/lib/ui/widgets/main_screen/topartists_card.dart
+++ /dev/null
@@ -1,48 +0,0 @@
-import 'dart:convert';
-
-import 'package:flutter/material.dart';
-
-import '../../../models/topartists.dart';
-import '../../../network/scrobbles_api.dart';
-import '../../../ui/widgets/error.dart';
-import '../../../ui/widgets/main_screen/topartists_content.dart';
-
-class ChartTopArtistsCard extends StatelessWidget {
-  const ChartTopArtistsCard({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    final int daysCount = 14;
-    late Future<TopArtistsData> futureTimeline = ScrobblesApi.fetchTopArtists(daysCount);
-
-    return FutureBuilder<TopArtistsData>(
-      future: futureTimeline,
-      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.surface,
-          shape: const RoundedRectangleBorder(
-            borderRadius: BorderRadius.all(
-              Radius.circular(8),
-            ),
-          ),
-          child: Padding(
-            padding: const EdgeInsets.all(8.0),
-            child: ChartTopArtistsCardContent(
-              daysCount: daysCount,
-              chartData: snapshot.hasData
-                  ? TopArtistsData.fromJson(jsonDecode(snapshot.data.toString()))
-                  : TopArtistsData.createEmpty(),
-              isLoading: !snapshot.hasData,
-            ),
-          ),
-        );
-      },
-    );
-  }
-}
diff --git a/lib/ui/widgets/main_screen/topartists_content.dart b/lib/ui/widgets/main_screen/topartists_content.dart
deleted file mode 100644
index 00d41e8..0000000
--- a/lib/ui/widgets/main_screen/topartists_content.dart
+++ /dev/null
@@ -1,43 +0,0 @@
-import 'dart:convert';
-
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-
-import '../../../models/topartists.dart';
-import '../../../ui/widgets/main_screen/topartists_chart.dart';
-
-class ChartTopArtistsCardContent extends StatelessWidget {
-  final int daysCount;
-  final TopArtistsData chartData;
-  final bool isLoading;
-
-  const ChartTopArtistsCardContent(
-      {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(
-          'top_artists_title',
-          style: textTheme.titleLarge!.apply(fontWeightDelta: 2),
-        ).tr(
-          namedArgs: {
-            'daysCount': this.daysCount.toString(),
-          },
-        ),
-        const SizedBox(height: 8),
-        this.isLoading
-            ? Text(placeholder)
-            : ChartTopArtists(
-                chartData: TopArtistsData.fromJson(jsonDecode(this.chartData.toString())),
-              ),
-      ],
-    );
-  }
-}
diff --git a/pubspec.yaml b/pubspec.yaml
index ad3eda2..33d49a5 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -3,7 +3,7 @@ description: Display scrobbles data and charts
 
 publish_to: 'none'
 
-version: 0.0.27+27
+version: 0.0.28+28
 
 environment:
   sdk: '^3.0.0'
-- 
GitLab