From 4b567d90861e2435138549c820a50318ca955835 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Harrault?= <benoit@harrault.fr>
Date: Wed, 22 May 2024 18:31:19 +0200
Subject: [PATCH] Normalize app architecture

---
 android/app/build.gradle                      |   2 +-
 android/gradle.properties                     |   4 +-
 assets/translations/en.json                   |  15 ++-
 assets/translations/fr.json                   |  17 ++-
 .../metadata/android/en-US/changelogs/58.txt  |   1 +
 .../metadata/android/fr-FR/changelogs/58.txt  |   1 +
 lib/config/activity_page.dart                 |  61 ++++++++++
 ...ings.dart => default_global_settings.dart} |   2 +-
 lib/config/screen.dart                        |  61 ++++++++++
 lib/config/theme.dart                         |   8 +-
 lib/cubit/activity_cubit.dart                 |  60 ++++++++++
 lib/cubit/activity_state.dart                 |  15 +++
 lib/cubit/bottom_nav_cubit.dart               |  31 -----
 lib/cubit/data_counts_by_day_cubit.dart       |  59 ++++++++--
 lib/cubit/data_counts_by_day_state.dart       |   5 +-
 lib/cubit/data_counts_by_hour_cubit.dart      |  59 ++++++++--
 lib/cubit/data_counts_by_hour_state.dart      |   5 +-
 lib/cubit/data_discoveries_cubit.dart         |  59 ++++++++--
 lib/cubit/data_discoveries_state.dart         |   5 +-
 lib/cubit/data_heatmap_cubit.dart             |  59 ++++++++--
 lib/cubit/data_heatmap_state.dart             |   5 +-
 lib/cubit/data_new_artists_cubit.dart         |  59 ++++++++--
 lib/cubit/data_new_artists_state.dart         |   5 +-
 lib/cubit/data_new_tracks_cubit.dart          |  59 ++++++++--
 lib/cubit/data_new_tracks_state.dart          |   5 +-
 lib/cubit/data_statistics_global_cubit.dart   |  54 +++++++--
 lib/cubit/data_statistics_global_state.dart   |   5 +-
 lib/cubit/data_statistics_recent_cubit.dart   |  59 ++++++++--
 lib/cubit/data_statistics_recent_state.dart   |   5 +-
 lib/cubit/data_timeline_cubit.dart            |  59 ++++++++--
 lib/cubit/data_timeline_state.dart            |   5 +-
 lib/cubit/data_top_artists_cubit.dart         |  59 ++++++++--
 lib/cubit/data_top_artists_state.dart         |   5 +-
 lib/cubit/nav_cubit_pages.dart                |  29 +++++
 lib/cubit/nav_cubit_screens.dart              |  37 ++++++
 ..._cubit.dart => settings_global_cubit.dart} |  94 +++++++--------
 ..._state.dart => settings_global_state.dart} |   6 +-
 lib/main.dart                                 |  71 +++++------
 lib/models/activity/activity.dart             |  26 +++++
 lib/models/{ => data}/artists.dart            |   0
 lib/models/{ => data}/counts_by_day.dart      |   0
 lib/models/{ => data}/counts_by_hour.dart     |   0
 lib/models/{ => data}/discoveries.dart        |   0
 lib/models/{ => data}/heatmap.dart            |   0
 lib/models/{ => data}/new_artists.dart        |   2 +-
 lib/models/{ => data}/new_tracks.dart         |   2 +-
 lib/models/{ => data}/statistics_global.dart  |   0
 lib/models/{ => data}/statistics_recent.dart  |   0
 lib/models/{ => data}/timeline.dart           |   0
 lib/models/{ => data}/topartists.dart         |   0
 lib/models/{ => data}/track.dart              |   2 +-
 lib/network/scrobbles.dart                    | 110 ++++++++++--------
 lib/ui/{widgets => helpers}/app_titles.dart   |  27 +----
 lib/ui/nav/bottom_nav_bar.dart                |  53 +++++++++
 lib/ui/nav/global_app_bar.dart                |  65 +++++++++++
 lib/ui/pages/discoveries.dart                 |  33 ++++++
 lib/ui/pages/home.dart                        |  36 ++++++
 lib/ui/pages/statistics.dart                  |  33 ++++++
 lib/ui/screens/about.dart                     |  41 +++++++
 lib/ui/screens/activity.dart                  |  39 +++++++
 lib/ui/screens/discoveries.dart               |  36 ------
 lib/ui/screens/home.dart                      |  39 -------
 lib/ui/screens/settings.dart                  |  23 ++--
 lib/ui/screens/statistics.dart                |  36 ------
 .../{widgets => settings}/settings_form.dart  |  88 +++++++-------
 lib/ui/{widgets => settings}/theme_card.dart  |   0
 lib/ui/skeleton.dart                          |  74 ++++--------
 .../widgets/abstracts/custom_bar_chart.dart   |   4 +-
 lib/ui/widgets/abstracts/custom_chart.dart    |  13 ++-
 .../widgets/abstracts/custom_line_chart.dart  |   4 +-
 lib/ui/widgets/app_bar.dart                   |  28 -----
 lib/ui/widgets/bottom_nav_bar.dart            |  58 ---------
 lib/ui/widgets/card_content.dart              |  13 ++-
 lib/ui/widgets/cards/counts_by_day.dart       |  35 +-----
 lib/ui/widgets/cards/counts_by_hour.dart      |  35 +-----
 lib/ui/widgets/cards/discoveries.dart         |  35 +-----
 lib/ui/widgets/cards/heatmap.dart             |  35 +-----
 lib/ui/widgets/cards/new_artists.dart         |  34 +-----
 lib/ui/widgets/cards/new_tracks.dart          |  37 +-----
 lib/ui/widgets/cards/statistics_global.dart   |  30 +----
 lib/ui/widgets/cards/statistics_recent.dart   |  35 +-----
 lib/ui/widgets/cards/timeline.dart            |  35 +-----
 lib/ui/widgets/cards/top_artists.dart         |  35 +-----
 lib/ui/widgets/charts/counts_by_day.dart      |   8 +-
 lib/ui/widgets/charts/counts_by_hour.dart     |  13 ++-
 .../widgets/charts/discoveries_artists.dart   |   4 +-
 lib/ui/widgets/charts/discoveries_tracks.dart |   4 +-
 lib/ui/widgets/charts/heatmap.dart            |   7 +-
 lib/ui/widgets/charts/timeline_counts.dart    |   6 +-
 .../widgets/charts/timeline_eclecticism.dart  |   4 +-
 lib/ui/widgets/charts/top_artists.dart        |   2 +-
 lib/ui/widgets/charts/top_artists_stream.dart |   6 +-
 lib/ui/widgets/content/statistics_global.dart |   2 +-
 lib/ui/widgets/content/statistics_recent.dart |   2 +-
 lib/ui/widgets/error.dart                     |  21 ----
 pubspec.lock                                  | 100 +++++++++-------
 pubspec.yaml                                  |  23 ++--
 .../app/build_application_resources.sh        |   2 +-
 {icons => resources/app}/featureGraphic.svg   |   0
 {icons => resources/app}/icon.svg             |   0
 resources/build_resources.sh                  |   6 +
 101 files changed, 1549 insertions(+), 1042 deletions(-)
 create mode 100644 fastlane/metadata/android/en-US/changelogs/58.txt
 create mode 100644 fastlane/metadata/android/fr-FR/changelogs/58.txt
 create mode 100644 lib/config/activity_page.dart
 rename lib/config/{default_settings.dart => default_global_settings.dart} (94%)
 create mode 100644 lib/config/screen.dart
 create mode 100644 lib/cubit/activity_cubit.dart
 create mode 100644 lib/cubit/activity_state.dart
 delete mode 100644 lib/cubit/bottom_nav_cubit.dart
 create mode 100644 lib/cubit/nav_cubit_pages.dart
 create mode 100644 lib/cubit/nav_cubit_screens.dart
 rename lib/cubit/{settings_cubit.dart => settings_global_cubit.dart} (60%)
 rename lib/cubit/{settings_state.dart => settings_global_state.dart} (92%)
 create mode 100644 lib/models/activity/activity.dart
 rename lib/models/{ => data}/artists.dart (100%)
 rename lib/models/{ => data}/counts_by_day.dart (100%)
 rename lib/models/{ => data}/counts_by_hour.dart (100%)
 rename lib/models/{ => data}/discoveries.dart (100%)
 rename lib/models/{ => data}/heatmap.dart (100%)
 rename lib/models/{ => data}/new_artists.dart (95%)
 rename lib/models/{ => data}/new_tracks.dart (95%)
 rename lib/models/{ => data}/statistics_global.dart (100%)
 rename lib/models/{ => data}/statistics_recent.dart (100%)
 rename lib/models/{ => data}/timeline.dart (100%)
 rename lib/models/{ => data}/topartists.dart (100%)
 rename lib/models/{ => data}/track.dart (93%)
 rename lib/ui/{widgets => helpers}/app_titles.dart (52%)
 create mode 100644 lib/ui/nav/bottom_nav_bar.dart
 create mode 100644 lib/ui/nav/global_app_bar.dart
 create mode 100644 lib/ui/pages/discoveries.dart
 create mode 100644 lib/ui/pages/home.dart
 create mode 100644 lib/ui/pages/statistics.dart
 create mode 100644 lib/ui/screens/about.dart
 create mode 100644 lib/ui/screens/activity.dart
 delete mode 100644 lib/ui/screens/discoveries.dart
 delete mode 100644 lib/ui/screens/home.dart
 delete mode 100644 lib/ui/screens/statistics.dart
 rename lib/ui/{widgets => settings}/settings_form.dart (78%)
 rename lib/ui/{widgets => settings}/theme_card.dart (100%)
 delete mode 100644 lib/ui/widgets/app_bar.dart
 delete mode 100644 lib/ui/widgets/bottom_nav_bar.dart
 delete mode 100644 lib/ui/widgets/error.dart
 rename icons/build_repository_icons.sh => resources/app/build_application_resources.sh (98%)
 rename {icons => resources/app}/featureGraphic.svg (100%)
 rename {icons => resources/app}/icon.svg (100%)
 create mode 100755 resources/build_resources.sh

diff --git a/android/app/build.gradle b/android/app/build.gradle
index a431a29..ab94378 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -37,7 +37,7 @@ if (keystorePropertiesFile.exists()) {
 }
 
 android {
-    compileSdkVersion 33
+    compileSdkVersion 34
     namespace "org.benoitharrault.scrobbles"
 
     defaultConfig {
diff --git a/android/gradle.properties b/android/gradle.properties
index 547a7f8..7dd3e32 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.57
-app.versionCode=57
+app.versionName=0.1.0
+app.versionCode=58
diff --git a/assets/translations/en.json b/assets/translations/en.json
index 51578cf..602be62 100644
--- a/assets/translations/en.json
+++ b/assets/translations/en.json
@@ -1,12 +1,15 @@
 {
   "app_name": "Scrobbles",
-  "": "",
 
-  "bottom_nav_home": "Home",
-  "bottom_nav_discoveries": "Discoveries",
-  "bottom_nav_repartition": "Statistics",
+  "page_home": "Home",
+  "page_discoveries": "Discoveries",
+  "page_statistics": "Statistics",
   "bottom_nav_settings": "Settings",
 
+  "about_title": "Informations",
+  "about_content": "Scrobbles",
+  "about_version": "Version: {version}",
+
   "global_statistics": "Global statistics",
   "statistics_total_scrobbles_count": "Total scrobbles count: {count}",
   "statistics_last_scrobble": "Last scrobble: {datetime}",
@@ -50,5 +53,7 @@
   "THU": "THU",
   "FRI": "FRI",
   "SAT": "SAT",
-  "SUN": "SUN"
+  "SUN": "SUN",
+
+  "": ""
 }
diff --git a/assets/translations/fr.json b/assets/translations/fr.json
index 86c15ba..2d04be6 100644
--- a/assets/translations/fr.json
+++ b/assets/translations/fr.json
@@ -1,12 +1,15 @@
 {
   "app_name": "Scrobbles",
-  "": "",
 
-  "bottom_nav_home": "Accueil",
-  "bottom_nav_discoveries": "Découvertes",
-  "bottom_nav_repartition": "Statistiques",
+  "page_home": "Général",
+  "page_discoveries": "Découvertes",
+  "page_statistics": "Statistiques",
   "bottom_nav_settings": "Paramètres",
 
+  "about_title": "Informations",
+  "about_content": "Scrobbles.",
+  "about_version": "Version : {version}",
+
   "global_statistics": "Statistiques globales d'écoutes",
   "statistics_total_scrobbles_count": "Nombre total d'écoutes : {count}",
   "statistics_last_scrobble": "Dernière écoute : {datetime}",
@@ -34,7 +37,7 @@
   "settings_label_security_token": "Jeton de sécurité : ",
   "settings_title_days_count": "Nombre de jours : ",
   "settings_label_discoveries_days_count": "Découvertes : ",
-  "settings_label_distribution_days_count": "Répartitions (par jour/heure) : ",
+  "settings_label_distribution_days_count": "Répartitions par jour/heure : ",
   "settings_label_statistics_recent_days_count": "Statistiques : ",
   "settings_label_timeline_days_count": "Timeline globale : ",
   "settings_label_top_artists_days_count": "Top Artistes : ",
@@ -50,5 +53,7 @@
   "THU": "JEU",
   "FRI": "VEN",
   "SAT": "SAM",
-  "SUN": "DIM"
+  "SUN": "DIM",
+
+  "": ""
 }
diff --git a/fastlane/metadata/android/en-US/changelogs/58.txt b/fastlane/metadata/android/en-US/changelogs/58.txt
new file mode 100644
index 0000000..060b202
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/58.txt
@@ -0,0 +1 @@
+Improve/normalize app architecture.
diff --git a/fastlane/metadata/android/fr-FR/changelogs/58.txt b/fastlane/metadata/android/fr-FR/changelogs/58.txt
new file mode 100644
index 0000000..3cab228
--- /dev/null
+++ b/fastlane/metadata/android/fr-FR/changelogs/58.txt
@@ -0,0 +1 @@
+Amélioration/normalisation de l'architecture de l'application.
diff --git a/lib/config/activity_page.dart b/lib/config/activity_page.dart
new file mode 100644
index 0000000..b58d295
--- /dev/null
+++ b/lib/config/activity_page.dart
@@ -0,0 +1,61 @@
+import 'package:flutter/material.dart';
+import 'package:ionicons/ionicons.dart';
+
+import 'package:scrobbles/ui/pages/discoveries.dart';
+import 'package:scrobbles/ui/pages/home.dart';
+import 'package:scrobbles/ui/pages/statistics.dart';
+
+class ActivityPageItem {
+  final Icon icon;
+  final Widget page;
+  final String code;
+
+  const ActivityPageItem({
+    required this.icon,
+    required this.page,
+    required this.code,
+  });
+}
+
+class ActivityPage {
+  static const indexHome = 0;
+  static const pageHome = ActivityPageItem(
+    icon: Icon(Ionicons.home_outline),
+    page: PageHome(),
+    code: 'page_home',
+  );
+
+  static const indexDiscoveries = 1;
+  static const pageDiscoveries = ActivityPageItem(
+    icon: Icon(Ionicons.star_outline),
+    page: PageDiscoveries(),
+    code: 'page_discoveries',
+  );
+
+  static const indexStatistics = 2;
+  static const pageStatistics = ActivityPageItem(
+    icon: Icon(Ionicons.bar_chart_outline),
+    page: PageStatistics(),
+    code: 'page_statistics',
+  );
+
+  static Map<int, ActivityPageItem> items = {
+    indexHome: pageHome,
+    indexDiscoveries: pageDiscoveries,
+    indexStatistics: pageStatistics,
+  };
+
+  static bool isIndexAllowed(int pageIndex) {
+    return items.keys.contains(pageIndex);
+  }
+
+  static ActivityPageItem getPageItem(int pageIndex) {
+    return items[pageIndex] ?? pageHome;
+  }
+
+  static Widget getPageWidget(int pageIndex) {
+    return items[pageIndex]?.page ?? pageHome.page;
+  }
+
+  static int itemsCount = ActivityPage.items.length;
+}
diff --git a/lib/config/default_settings.dart b/lib/config/default_global_settings.dart
similarity index 94%
rename from lib/config/default_settings.dart
rename to lib/config/default_global_settings.dart
index 525784f..0565d15 100644
--- a/lib/config/default_settings.dart
+++ b/lib/config/default_global_settings.dart
@@ -1,4 +1,4 @@
-class DefaultSettings {
+class DefaultGlobalSettings {
   static const List<int> allowedDaysCountValues = [
     7,
     14,
diff --git a/lib/config/screen.dart b/lib/config/screen.dart
new file mode 100644
index 0000000..2846969
--- /dev/null
+++ b/lib/config/screen.dart
@@ -0,0 +1,61 @@
+import 'package:flutter/material.dart';
+import 'package:unicons/unicons.dart';
+
+import 'package:scrobbles/ui/screens/about.dart';
+import 'package:scrobbles/ui/screens/activity.dart';
+import 'package:scrobbles/ui/screens/settings.dart';
+
+class ScreenItem {
+  final Icon icon;
+  final Widget screen;
+  final bool displayBottomNavBar;
+
+  const ScreenItem({
+    required this.icon,
+    required this.screen,
+    required this.displayBottomNavBar,
+  });
+}
+
+class Screen {
+  static const indexActivity = 0;
+  static const screenActivity = ScreenItem(
+    icon: Icon(UniconsLine.home),
+    screen: ScreenActivity(),
+    displayBottomNavBar: true,
+  );
+
+  static const indexSettings = 1;
+  static const screenSettings = ScreenItem(
+    icon: Icon(UniconsLine.setting),
+    screen: ScreenSettings(),
+    displayBottomNavBar: false,
+  );
+
+  static const indexAbout = 2;
+  static const screenAbout = ScreenItem(
+    icon: Icon(UniconsLine.info_circle),
+    screen: ScreenAbout(),
+    displayBottomNavBar: false,
+  );
+
+  static Map<int, ScreenItem> items = {
+    indexActivity: screenActivity,
+    indexSettings: screenSettings,
+    indexAbout: screenAbout,
+  };
+
+  static bool isIndexAllowed(int screenIndex) {
+    return items.keys.contains(screenIndex);
+  }
+
+  static Widget getWidget(int screenIndex) {
+    return items[screenIndex]?.screen ?? screenActivity.screen;
+  }
+
+  static bool displayBottomNavBar(int screenIndex) {
+    return items[screenIndex]?.displayBottomNavBar ?? screenActivity.displayBottomNavBar;
+  }
+
+  static int itemsCount = Screen.items.length;
+}
diff --git a/lib/config/theme.dart b/lib/config/theme.dart
index be39034..138460e 100644
--- a/lib/config/theme.dart
+++ b/lib/config/theme.dart
@@ -39,11 +39,9 @@ final ColorScheme lightColorScheme = ColorScheme.light(
   secondary: primarySwatch.shade500,
   onSecondary: Colors.white,
   error: errorColor,
-  background: textSwatch.shade200,
-  onBackground: textSwatch.shade500,
   onSurface: textSwatch.shade500,
   surface: textSwatch.shade50,
-  surfaceVariant: Colors.white,
+  surfaceContainerHighest: Colors.white,
   shadow: textSwatch.shade900.withOpacity(.1),
 );
 
@@ -52,11 +50,9 @@ final ColorScheme darkColorScheme = ColorScheme.dark(
   secondary: primarySwatch.shade500,
   onSecondary: Colors.white,
   error: errorColor,
-  background: const Color(0xFF171724),
-  onBackground: textSwatch.shade400,
   onSurface: textSwatch.shade300,
   surface: const Color(0xFF262630),
-  surfaceVariant: const Color(0xFF282832),
+  surfaceContainerHighest: const Color(0xFF282832),
   shadow: textSwatch.shade900.withOpacity(.2),
 );
 
diff --git a/lib/cubit/activity_cubit.dart b/lib/cubit/activity_cubit.dart
new file mode 100644
index 0000000..55bfee6
--- /dev/null
+++ b/lib/cubit/activity_cubit.dart
@@ -0,0 +1,60 @@
+import 'package:equatable/equatable.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:hydrated_bloc/hydrated_bloc.dart';
+
+import 'package:scrobbles/cubit/data_counts_by_day_cubit.dart';
+import 'package:scrobbles/cubit/data_counts_by_hour_cubit.dart';
+import 'package:scrobbles/cubit/data_discoveries_cubit.dart';
+import 'package:scrobbles/cubit/data_heatmap_cubit.dart';
+import 'package:scrobbles/cubit/data_new_artists_cubit.dart';
+import 'package:scrobbles/cubit/data_new_tracks_cubit.dart';
+import 'package:scrobbles/cubit/data_statistics_global_cubit.dart';
+import 'package:scrobbles/cubit/data_statistics_recent_cubit.dart';
+import 'package:scrobbles/cubit/data_timeline_cubit.dart';
+import 'package:scrobbles/cubit/data_top_artists_cubit.dart';
+import 'package:scrobbles/models/activity/activity.dart';
+
+part 'activity_state.dart';
+
+class ActivityCubit extends HydratedCubit<ActivityState> {
+  ActivityCubit()
+      : super(ActivityState(
+          currentActivity: Activity.createNull(),
+        ));
+
+  void updateState(Activity activity) {
+    emit(ActivityState(
+      currentActivity: activity,
+    ));
+  }
+
+  void refresh(BuildContext context) {
+    BlocProvider.of<DataCountsByDayCubit>(context).refresh(context);
+    BlocProvider.of<DataCountsByHourCubit>(context).refresh(context);
+    BlocProvider.of<DataDiscoveriesCubit>(context).refresh(context);
+    BlocProvider.of<DataHeatmapCubit>(context).refresh(context);
+    BlocProvider.of<DataNewArtistsCubit>(context).refresh(context);
+    BlocProvider.of<DataNewTracksCubit>(context).refresh(context);
+    BlocProvider.of<DataStatisticsGlobalCubit>(context).refresh(context);
+    BlocProvider.of<DataStatisticsRecentCubit>(context).refresh(context);
+    BlocProvider.of<DataTimelineCubit>(context).refresh(context);
+    BlocProvider.of<DataTopArtistsCubit>(context).refresh(context);
+  }
+
+  @override
+  ActivityState? fromJson(Map<String, dynamic> json) {
+    final Activity currentActivity = json['currentActivity'] as Activity;
+
+    return ActivityState(
+      currentActivity: currentActivity,
+    );
+  }
+
+  @override
+  Map<String, dynamic>? toJson(ActivityState state) {
+    return <String, dynamic>{
+      'currentActivity': state.currentActivity.toJson(),
+    };
+  }
+}
diff --git a/lib/cubit/activity_state.dart b/lib/cubit/activity_state.dart
new file mode 100644
index 0000000..887b45e
--- /dev/null
+++ b/lib/cubit/activity_state.dart
@@ -0,0 +1,15 @@
+part of 'activity_cubit.dart';
+
+@immutable
+class ActivityState extends Equatable {
+  const ActivityState({
+    required this.currentActivity,
+  });
+
+  final Activity currentActivity;
+
+  @override
+  List<dynamic> get props => <dynamic>[
+        currentActivity,
+      ];
+}
diff --git a/lib/cubit/bottom_nav_cubit.dart b/lib/cubit/bottom_nav_cubit.dart
deleted file mode 100644
index 1592a43..0000000
--- a/lib/cubit/bottom_nav_cubit.dart
+++ /dev/null
@@ -1,31 +0,0 @@
-import 'package:hydrated_bloc/hydrated_bloc.dart';
-
-class BottomNavCubit extends HydratedCubit<int> {
-  BottomNavCubit() : super(0);
-
-  int pagesCount = 4;
-
-  void updateIndex(int index) {
-    if (isIndexAllowed(index)) {
-      emit(index);
-    } else {
-      goToHomePage();
-    }
-  }
-
-  bool isIndexAllowed(int index) {
-    return (index >= 0) && (index < pagesCount);
-  }
-
-  void goToHomePage() => emit(0);
-
-  @override
-  int? fromJson(Map<String, dynamic> json) {
-    return 0;
-  }
-
-  @override
-  Map<String, dynamic>? toJson(int state) {
-    return <String, int>{'pageIndex': state};
-  }
-}
diff --git a/lib/cubit/data_counts_by_day_cubit.dart b/lib/cubit/data_counts_by_day_cubit.dart
index ab40a21..c2746cf 100644
--- a/lib/cubit/data_counts_by_day_cubit.dart
+++ b/lib/cubit/data_counts_by_day_cubit.dart
@@ -1,38 +1,76 @@
 import 'package:equatable/equatable.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
 
-import 'package:scrobbles/models/counts_by_day.dart';
+import 'package:scrobbles/cubit/settings_global_cubit.dart';
+import 'package:scrobbles/models/data/counts_by_day.dart';
+import 'package:scrobbles/network/scrobbles.dart';
 
 part 'data_counts_by_day_state.dart';
 
 class DataCountsByDayCubit extends HydratedCubit<DataCountsByDayState> {
-  DataCountsByDayCubit() : super(const DataCountsByDayState());
+  DataCountsByDayCubit()
+      : super(const DataCountsByDayState(
+          countsByDay: null,
+          status: '',
+        ));
 
-  void getData(DataCountsByDayState state) {
-    emit(state);
+  void setLoading() {
+    emit(DataCountsByDayState(
+      countsByDay: state.countsByDay,
+      status: 'loading',
+    ));
   }
 
-  CountsByDayData? getValue() {
-    return state.countsByDay;
+  void setValue(CountsByDayData? countsByDay) {
+    emit(DataCountsByDayState(
+      countsByDay: countsByDay,
+      status: '',
+    ));
+  }
+
+  void setFailed() {
+    emit(DataCountsByDayState(
+      countsByDay: state.countsByDay,
+      status: 'failed',
+    ));
+  }
+
+  void setLoaded() {
+    emit(DataCountsByDayState(
+      countsByDay: state.countsByDay,
+      status: '',
+    ));
   }
 
   void update(CountsByDayData? countsByDay) {
     if ((countsByDay != null) && (state.countsByDay.toString() != countsByDay.toString())) {
       setValue(countsByDay);
+    } else {
+      setLoaded();
     }
   }
 
-  void setValue(CountsByDayData? countsByDay) {
-    emit(DataCountsByDayState(
-      countsByDay: countsByDay,
-    ));
+  void refresh(BuildContext context) async {
+    setLoading();
+
+    GlobalSettingsCubit settings = BlocProvider.of<GlobalSettingsCubit>(context);
+    final int daysCount = settings.getDistributionDaysCount();
+
+    final CountsByDayData? data = await ScrobblesApi.fetchCountsByDay(daysCount);
+    if (data != null) {
+      update(data);
+    } else {
+      setFailed();
+    }
   }
 
   @override
   DataCountsByDayState? fromJson(Map<String, dynamic> json) {
     return DataCountsByDayState(
       countsByDay: CountsByDayData.fromJson(json['countsByDay']),
+      status: '',
     );
   }
 
@@ -40,6 +78,7 @@ class DataCountsByDayCubit extends HydratedCubit<DataCountsByDayState> {
   Map<String, Object?>? toJson(DataCountsByDayState state) {
     return <String, Object?>{
       'countsByDay': state.countsByDay?.toJson(),
+      'status': state.status,
     };
   }
 }
diff --git a/lib/cubit/data_counts_by_day_state.dart b/lib/cubit/data_counts_by_day_state.dart
index 724bd73..13b7883 100644
--- a/lib/cubit/data_counts_by_day_state.dart
+++ b/lib/cubit/data_counts_by_day_state.dart
@@ -3,13 +3,16 @@ part of 'data_counts_by_day_cubit.dart';
 @immutable
 class DataCountsByDayState extends Equatable {
   const DataCountsByDayState({
-    this.countsByDay,
+    required this.countsByDay,
+    required this.status,
   });
 
   final CountsByDayData? countsByDay;
+  final String status;
 
   @override
   List<Object?> get props => <Object?>[
         countsByDay,
+        status,
       ];
 }
diff --git a/lib/cubit/data_counts_by_hour_cubit.dart b/lib/cubit/data_counts_by_hour_cubit.dart
index 83cd2d3..e4c4770 100644
--- a/lib/cubit/data_counts_by_hour_cubit.dart
+++ b/lib/cubit/data_counts_by_hour_cubit.dart
@@ -1,38 +1,76 @@
 import 'package:equatable/equatable.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
 
-import 'package:scrobbles/models/counts_by_hour.dart';
+import 'package:scrobbles/cubit/settings_global_cubit.dart';
+import 'package:scrobbles/models/data/counts_by_hour.dart';
+import 'package:scrobbles/network/scrobbles.dart';
 
 part 'data_counts_by_hour_state.dart';
 
 class DataCountsByHourCubit extends HydratedCubit<DataCountsByHourState> {
-  DataCountsByHourCubit() : super(const DataCountsByHourState());
+  DataCountsByHourCubit()
+      : super(const DataCountsByHourState(
+          countsByHour: null,
+          status: '',
+        ));
 
-  void getData(DataCountsByHourState state) {
-    emit(state);
+  void setLoading() {
+    emit(DataCountsByHourState(
+      countsByHour: state.countsByHour,
+      status: 'loading',
+    ));
   }
 
-  CountsByHourData? getValue() {
-    return state.countsByHour;
+  void setValue(CountsByHourData? countsByHour) {
+    emit(DataCountsByHourState(
+      countsByHour: countsByHour,
+      status: '',
+    ));
+  }
+
+  void setFailed() {
+    emit(DataCountsByHourState(
+      countsByHour: state.countsByHour,
+      status: 'failed',
+    ));
+  }
+
+  void setLoaded() {
+    emit(DataCountsByHourState(
+      countsByHour: state.countsByHour,
+      status: '',
+    ));
   }
 
   void update(CountsByHourData? countsByHour) {
     if ((countsByHour != null) && (state.countsByHour.toString() != countsByHour.toString())) {
       setValue(countsByHour);
+    } else {
+      setLoaded();
     }
   }
 
-  void setValue(CountsByHourData? countsByHour) {
-    emit(DataCountsByHourState(
-      countsByHour: countsByHour,
-    ));
+  void refresh(BuildContext context) async {
+    setLoading();
+
+    GlobalSettingsCubit settings = BlocProvider.of<GlobalSettingsCubit>(context);
+    final int daysCount = settings.getDistributionDaysCount();
+
+    final CountsByHourData? data = await ScrobblesApi.fetchCountsByHour(daysCount);
+    if (data != null) {
+      update(data);
+    } else {
+      setFailed();
+    }
   }
 
   @override
   DataCountsByHourState? fromJson(Map<String, dynamic> json) {
     return DataCountsByHourState(
       countsByHour: CountsByHourData.fromJson(json['countsByHour']),
+      status: '',
     );
   }
 
@@ -40,6 +78,7 @@ class DataCountsByHourCubit extends HydratedCubit<DataCountsByHourState> {
   Map<String, Object?>? toJson(DataCountsByHourState state) {
     return <String, Object?>{
       'countsByHour': state.countsByHour?.toJson(),
+      'status': state.status,
     };
   }
 }
diff --git a/lib/cubit/data_counts_by_hour_state.dart b/lib/cubit/data_counts_by_hour_state.dart
index 3fc9aef..c68949e 100644
--- a/lib/cubit/data_counts_by_hour_state.dart
+++ b/lib/cubit/data_counts_by_hour_state.dart
@@ -3,13 +3,16 @@ part of 'data_counts_by_hour_cubit.dart';
 @immutable
 class DataCountsByHourState extends Equatable {
   const DataCountsByHourState({
-    this.countsByHour,
+    required this.countsByHour,
+    required this.status,
   });
 
   final CountsByHourData? countsByHour;
+  final String status;
 
   @override
   List<Object?> get props => <Object?>[
         countsByHour,
+        status,
       ];
 }
diff --git a/lib/cubit/data_discoveries_cubit.dart b/lib/cubit/data_discoveries_cubit.dart
index dabda77..e3acd53 100644
--- a/lib/cubit/data_discoveries_cubit.dart
+++ b/lib/cubit/data_discoveries_cubit.dart
@@ -1,38 +1,76 @@
 import 'package:equatable/equatable.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
 
-import 'package:scrobbles/models/discoveries.dart';
+import 'package:scrobbles/cubit/settings_global_cubit.dart';
+import 'package:scrobbles/models/data/discoveries.dart';
+import 'package:scrobbles/network/scrobbles.dart';
 
 part 'data_discoveries_state.dart';
 
 class DataDiscoveriesCubit extends HydratedCubit<DataDiscoveriesState> {
-  DataDiscoveriesCubit() : super(const DataDiscoveriesState());
+  DataDiscoveriesCubit()
+      : super(const DataDiscoveriesState(
+          discoveries: null,
+          status: '',
+        ));
 
-  void getData(DataDiscoveriesState state) {
-    emit(state);
+  void setLoading() {
+    emit(DataDiscoveriesState(
+      discoveries: state.discoveries,
+      status: 'loading',
+    ));
   }
 
-  DiscoveriesData? getValue() {
-    return state.discoveries;
+  void setValue(DiscoveriesData? discoveries) {
+    emit(DataDiscoveriesState(
+      discoveries: discoveries,
+      status: '',
+    ));
+  }
+
+  void setFailed() {
+    emit(DataDiscoveriesState(
+      discoveries: state.discoveries,
+      status: 'failed',
+    ));
+  }
+
+  void setLoaded() {
+    emit(DataDiscoveriesState(
+      discoveries: state.discoveries,
+      status: '',
+    ));
   }
 
   void update(DiscoveriesData? discoveries) {
     if ((discoveries != null) && (state.discoveries.toString() != discoveries.toString())) {
       setValue(discoveries);
+    } else {
+      setLoaded();
     }
   }
 
-  void setValue(DiscoveriesData? discoveries) {
-    emit(DataDiscoveriesState(
-      discoveries: discoveries,
-    ));
+  void refresh(BuildContext context) async {
+    setLoading();
+
+    GlobalSettingsCubit settings = BlocProvider.of<GlobalSettingsCubit>(context);
+    final int daysCount = settings.getDiscoveriesDaysCount();
+
+    final DiscoveriesData? data = await ScrobblesApi.fetchDiscoveries(daysCount);
+    if (data != null) {
+      update(data);
+    } else {
+      setFailed();
+    }
   }
 
   @override
   DataDiscoveriesState? fromJson(Map<String, dynamic> json) {
     return DataDiscoveriesState(
       discoveries: DiscoveriesData.fromJson(json['discoveries']),
+      status: '',
     );
   }
 
@@ -40,6 +78,7 @@ class DataDiscoveriesCubit extends HydratedCubit<DataDiscoveriesState> {
   Map<String, Object?>? toJson(DataDiscoveriesState state) {
     return <String, Object?>{
       'discoveries': state.discoveries?.toJson(),
+      'status': state.status,
     };
   }
 }
diff --git a/lib/cubit/data_discoveries_state.dart b/lib/cubit/data_discoveries_state.dart
index 1d43883..8bfa339 100644
--- a/lib/cubit/data_discoveries_state.dart
+++ b/lib/cubit/data_discoveries_state.dart
@@ -3,13 +3,16 @@ part of 'data_discoveries_cubit.dart';
 @immutable
 class DataDiscoveriesState extends Equatable {
   const DataDiscoveriesState({
-    this.discoveries,
+    required this.discoveries,
+    required this.status,
   });
 
   final DiscoveriesData? discoveries;
+  final String status;
 
   @override
   List<Object?> get props => <Object?>[
         discoveries,
+        status,
       ];
 }
diff --git a/lib/cubit/data_heatmap_cubit.dart b/lib/cubit/data_heatmap_cubit.dart
index 7cd9192..dc7e464 100644
--- a/lib/cubit/data_heatmap_cubit.dart
+++ b/lib/cubit/data_heatmap_cubit.dart
@@ -1,34 +1,76 @@
 import 'package:equatable/equatable.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
 
-import 'package:scrobbles/models/heatmap.dart';
+import 'package:scrobbles/cubit/settings_global_cubit.dart';
+import 'package:scrobbles/models/data/heatmap.dart';
+import 'package:scrobbles/network/scrobbles.dart';
 
 part 'data_heatmap_state.dart';
 
 class DataHeatmapCubit extends HydratedCubit<DataHeatmapState> {
-  DataHeatmapCubit() : super(const DataHeatmapState());
+  DataHeatmapCubit()
+      : super(const DataHeatmapState(
+          heatmap: null,
+          status: '',
+        ));
 
-  void getData(DataHeatmapState state) {
-    emit(state);
+  void setLoading() {
+    emit(DataHeatmapState(
+      heatmap: state.heatmap,
+      status: 'loading',
+    ));
+  }
+
+  void setValue(HeatmapData? heatmapData) {
+    emit(DataHeatmapState(
+      heatmap: heatmapData,
+      status: '',
+    ));
+  }
+
+  void setFailed() {
+    emit(DataHeatmapState(
+      heatmap: state.heatmap,
+      status: 'failed',
+    ));
+  }
+
+  void setLoaded() {
+    emit(DataHeatmapState(
+      heatmap: state.heatmap,
+      status: '',
+    ));
   }
 
   void update(HeatmapData? heatmapData) {
     if ((heatmapData != null) && (state.heatmap.toString() != heatmapData.toString())) {
       setValue(heatmapData);
+    } else {
+      setLoaded();
     }
   }
 
-  void setValue(HeatmapData? heatmapData) {
-    emit(DataHeatmapState(
-      heatmap: heatmapData,
-    ));
+  void refresh(BuildContext context) async {
+    setLoading();
+
+    GlobalSettingsCubit settings = BlocProvider.of<GlobalSettingsCubit>(context);
+    final int daysCount = settings.getDistributionDaysCount();
+
+    final HeatmapData? data = await ScrobblesApi.fetchHeatmap(daysCount);
+    if (data != null) {
+      update(data);
+    } else {
+      setFailed();
+    }
   }
 
   @override
   DataHeatmapState? fromJson(Map<String, dynamic> json) {
     return DataHeatmapState(
       heatmap: HeatmapData.fromJson(json['heatmap']),
+      status: '',
     );
   }
 
@@ -36,6 +78,7 @@ class DataHeatmapCubit extends HydratedCubit<DataHeatmapState> {
   Map<String, Object?>? toJson(DataHeatmapState state) {
     return <String, Object?>{
       'heatmap': state.heatmap?.toJson(),
+      'status': state.status,
     };
   }
 }
diff --git a/lib/cubit/data_heatmap_state.dart b/lib/cubit/data_heatmap_state.dart
index 0814a3b..a8d21e3 100644
--- a/lib/cubit/data_heatmap_state.dart
+++ b/lib/cubit/data_heatmap_state.dart
@@ -3,13 +3,16 @@ part of 'data_heatmap_cubit.dart';
 @immutable
 class DataHeatmapState extends Equatable {
   const DataHeatmapState({
-    this.heatmap,
+    required this.heatmap,
+    required this.status,
   });
 
   final HeatmapData? heatmap;
+  final String status;
 
   @override
   List<Object?> get props => <Object?>[
         heatmap,
+        status,
       ];
 }
diff --git a/lib/cubit/data_new_artists_cubit.dart b/lib/cubit/data_new_artists_cubit.dart
index 6ef782b..c858321 100644
--- a/lib/cubit/data_new_artists_cubit.dart
+++ b/lib/cubit/data_new_artists_cubit.dart
@@ -1,38 +1,76 @@
 import 'package:equatable/equatable.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
 
-import 'package:scrobbles/models/new_artists.dart';
+import 'package:scrobbles/cubit/settings_global_cubit.dart';
+import 'package:scrobbles/models/data/new_artists.dart';
+import 'package:scrobbles/network/scrobbles.dart';
 
 part 'data_new_artists_state.dart';
 
 class DataNewArtistsCubit extends HydratedCubit<DataNewArtistsState> {
-  DataNewArtistsCubit() : super(const DataNewArtistsState());
+  DataNewArtistsCubit()
+      : super(const DataNewArtistsState(
+          newArtists: null,
+          status: '',
+        ));
 
-  void getData(DataNewArtistsState state) {
-    emit(state);
+  void setLoading() {
+    emit(DataNewArtistsState(
+      newArtists: state.newArtists,
+      status: 'loading',
+    ));
   }
 
-  NewArtistsData? getValue() {
-    return state.newArtists;
+  void setValue(NewArtistsData? newArtists) {
+    emit(DataNewArtistsState(
+      newArtists: newArtists,
+      status: '',
+    ));
+  }
+
+  void setFailed() {
+    emit(DataNewArtistsState(
+      newArtists: state.newArtists,
+      status: 'failed',
+    ));
+  }
+
+  void setLoaded() {
+    emit(DataNewArtistsState(
+      newArtists: state.newArtists,
+      status: '',
+    ));
   }
 
   void update(NewArtistsData? newArtists) {
     if ((newArtists != null) && (state.newArtists.toString() != newArtists.toString())) {
       setValue(newArtists);
+    } else {
+      setLoaded();
     }
   }
 
-  void setValue(NewArtistsData? newArtists) {
-    emit(DataNewArtistsState(
-      newArtists: newArtists,
-    ));
+  void refresh(BuildContext context) async {
+    setLoading();
+
+    GlobalSettingsCubit settings = BlocProvider.of<GlobalSettingsCubit>(context);
+    final int count = settings.getNewArtistsCount();
+
+    final NewArtistsData? data = await ScrobblesApi.fetchNewArtists(count);
+    if (data != null) {
+      update(data);
+    } else {
+      setFailed();
+    }
   }
 
   @override
   DataNewArtistsState? fromJson(Map<String, dynamic> json) {
     return DataNewArtistsState(
       newArtists: NewArtistsData.fromJson(json['newArtists']),
+      status: '',
     );
   }
 
@@ -40,6 +78,7 @@ class DataNewArtistsCubit extends HydratedCubit<DataNewArtistsState> {
   Map<String, Object?>? toJson(DataNewArtistsState state) {
     return <String, Object?>{
       'newArtists': state.newArtists?.toJson(),
+      'status': state.status,
     };
   }
 }
diff --git a/lib/cubit/data_new_artists_state.dart b/lib/cubit/data_new_artists_state.dart
index eb6678c..8a9fe7a 100644
--- a/lib/cubit/data_new_artists_state.dart
+++ b/lib/cubit/data_new_artists_state.dart
@@ -3,13 +3,16 @@ part of 'data_new_artists_cubit.dart';
 @immutable
 class DataNewArtistsState extends Equatable {
   const DataNewArtistsState({
-    this.newArtists,
+    required this.newArtists,
+    required this.status,
   });
 
   final NewArtistsData? newArtists;
+  final String status;
 
   @override
   List<Object?> get props => <Object?>[
         newArtists,
+        status,
       ];
 }
diff --git a/lib/cubit/data_new_tracks_cubit.dart b/lib/cubit/data_new_tracks_cubit.dart
index 1092388..d1c1e76 100644
--- a/lib/cubit/data_new_tracks_cubit.dart
+++ b/lib/cubit/data_new_tracks_cubit.dart
@@ -1,38 +1,76 @@
 import 'package:equatable/equatable.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
 
-import 'package:scrobbles/models/new_tracks.dart';
+import 'package:scrobbles/cubit/settings_global_cubit.dart';
+import 'package:scrobbles/models/data/new_tracks.dart';
+import 'package:scrobbles/network/scrobbles.dart';
 
 part 'data_new_tracks_state.dart';
 
 class DataNewTracksCubit extends HydratedCubit<DataNewTracksState> {
-  DataNewTracksCubit() : super(const DataNewTracksState());
+  DataNewTracksCubit()
+      : super(const DataNewTracksState(
+          newTracks: null,
+          status: '',
+        ));
 
-  void getData(DataNewTracksState state) {
-    emit(state);
+  void setLoading() {
+    emit(DataNewTracksState(
+      newTracks: state.newTracks,
+      status: 'loading',
+    ));
   }
 
-  NewTracksData? getValue() {
-    return state.newTracks;
+  void setValue(NewTracksData? newTracks) {
+    emit(DataNewTracksState(
+      newTracks: newTracks,
+      status: '',
+    ));
+  }
+
+  void setFailed() {
+    emit(DataNewTracksState(
+      newTracks: state.newTracks,
+      status: 'failed',
+    ));
+  }
+
+  void setLoaded() {
+    emit(DataNewTracksState(
+      newTracks: state.newTracks,
+      status: '',
+    ));
   }
 
   void update(NewTracksData? newTracks) {
     if ((newTracks != null) && (state.newTracks.toString() != newTracks.toString())) {
       setValue(newTracks);
+    } else {
+      setLoaded();
     }
   }
 
-  void setValue(NewTracksData? newTracks) {
-    emit(DataNewTracksState(
-      newTracks: newTracks,
-    ));
+  void refresh(BuildContext context) async {
+    setLoading();
+
+    GlobalSettingsCubit settings = BlocProvider.of<GlobalSettingsCubit>(context);
+    final int count = settings.getNewTracksCount();
+
+    final NewTracksData? data = await ScrobblesApi.fetchNewTracks(count);
+    if (data != null) {
+      update(data);
+    } else {
+      setFailed();
+    }
   }
 
   @override
   DataNewTracksState? fromJson(Map<String, dynamic> json) {
     return DataNewTracksState(
       newTracks: NewTracksData.fromJson(json['newTracks']),
+      status: '',
     );
   }
 
@@ -40,6 +78,7 @@ class DataNewTracksCubit extends HydratedCubit<DataNewTracksState> {
   Map<String, Object?>? toJson(DataNewTracksState state) {
     return <String, Object?>{
       'newTracks': state.newTracks?.toJson(),
+      'status': state.status,
     };
   }
 }
diff --git a/lib/cubit/data_new_tracks_state.dart b/lib/cubit/data_new_tracks_state.dart
index c23abe4..d637984 100644
--- a/lib/cubit/data_new_tracks_state.dart
+++ b/lib/cubit/data_new_tracks_state.dart
@@ -3,13 +3,16 @@ part of 'data_new_tracks_cubit.dart';
 @immutable
 class DataNewTracksState extends Equatable {
   const DataNewTracksState({
-    this.newTracks,
+    required this.newTracks,
+    required this.status,
   });
 
   final NewTracksData? newTracks;
+  final String status;
 
   @override
   List<Object?> get props => <Object?>[
         newTracks,
+        status,
       ];
 }
diff --git a/lib/cubit/data_statistics_global_cubit.dart b/lib/cubit/data_statistics_global_cubit.dart
index 49e9292..e441c4e 100644
--- a/lib/cubit/data_statistics_global_cubit.dart
+++ b/lib/cubit/data_statistics_global_cubit.dart
@@ -2,38 +2,71 @@ import 'package:equatable/equatable.dart';
 import 'package:flutter/material.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
 
-import 'package:scrobbles/models/statistics_global.dart';
+import 'package:scrobbles/models/data/statistics_global.dart';
+import 'package:scrobbles/network/scrobbles.dart';
 
 part 'data_statistics_global_state.dart';
 
 class DataStatisticsGlobalCubit extends HydratedCubit<DataStatisticsGlobalState> {
-  DataStatisticsGlobalCubit() : super(const DataStatisticsGlobalState());
+  DataStatisticsGlobalCubit()
+      : super(const DataStatisticsGlobalState(
+          statisticsGlobal: null,
+          status: '',
+        ));
 
-  void getData(DataStatisticsGlobalState state) {
-    emit(state);
+  void setLoading() {
+    emit(DataStatisticsGlobalState(
+      statisticsGlobal: state.statisticsGlobal,
+      status: 'loading',
+    ));
+  }
+
+  void setValue(StatisticsGlobalData? statisticsGlobal) {
+    emit(DataStatisticsGlobalState(
+      statisticsGlobal: statisticsGlobal,
+      status: '',
+    ));
+  }
+
+  void setFailed() {
+    emit(DataStatisticsGlobalState(
+      statisticsGlobal: state.statisticsGlobal,
+      status: 'failed',
+    ));
   }
 
-  StatisticsGlobalData? getValue() {
-    return state.statisticsGlobal;
+  void setLoaded() {
+    emit(DataStatisticsGlobalState(
+      statisticsGlobal: state.statisticsGlobal,
+      status: '',
+    ));
   }
 
   void update(StatisticsGlobalData? statisticsGlobal) {
     if ((statisticsGlobal != null) &&
         (state.statisticsGlobal.toString() != statisticsGlobal.toString())) {
       setValue(statisticsGlobal);
+    } else {
+      setLoaded();
     }
   }
 
-  void setValue(StatisticsGlobalData? statisticsGlobal) {
-    emit(DataStatisticsGlobalState(
-      statisticsGlobal: statisticsGlobal,
-    ));
+  void refresh(BuildContext context) async {
+    setLoading();
+
+    final StatisticsGlobalData? data = await ScrobblesApi.fetchGlobalStatistics();
+    if (data != null) {
+      update(data);
+    } else {
+      setFailed();
+    }
   }
 
   @override
   DataStatisticsGlobalState? fromJson(Map<String, dynamic> json) {
     return DataStatisticsGlobalState(
       statisticsGlobal: StatisticsGlobalData.fromJson(json['statisticsGlobal']),
+      status: '',
     );
   }
 
@@ -41,6 +74,7 @@ class DataStatisticsGlobalCubit extends HydratedCubit<DataStatisticsGlobalState>
   Map<String, Object?>? toJson(DataStatisticsGlobalState state) {
     return <String, Object?>{
       'statisticsGlobal': state.statisticsGlobal?.toJson(),
+      'status': state.status,
     };
   }
 }
diff --git a/lib/cubit/data_statistics_global_state.dart b/lib/cubit/data_statistics_global_state.dart
index 89ef0fd..6d70418 100644
--- a/lib/cubit/data_statistics_global_state.dart
+++ b/lib/cubit/data_statistics_global_state.dart
@@ -3,13 +3,16 @@ part of 'data_statistics_global_cubit.dart';
 @immutable
 class DataStatisticsGlobalState extends Equatable {
   const DataStatisticsGlobalState({
-    this.statisticsGlobal,
+    required this.statisticsGlobal,
+    required this.status,
   });
 
   final StatisticsGlobalData? statisticsGlobal;
+  final String status;
 
   @override
   List<Object?> get props => <Object?>[
         statisticsGlobal,
+        status,
       ];
 }
diff --git a/lib/cubit/data_statistics_recent_cubit.dart b/lib/cubit/data_statistics_recent_cubit.dart
index da24b13..c950f64 100644
--- a/lib/cubit/data_statistics_recent_cubit.dart
+++ b/lib/cubit/data_statistics_recent_cubit.dart
@@ -1,39 +1,77 @@
 import 'package:equatable/equatable.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
 
-import 'package:scrobbles/models/statistics_recent.dart';
+import 'package:scrobbles/cubit/settings_global_cubit.dart';
+import 'package:scrobbles/models/data/statistics_recent.dart';
+import 'package:scrobbles/network/scrobbles.dart';
 
 part 'data_statistics_recent_state.dart';
 
 class DataStatisticsRecentCubit extends HydratedCubit<DataStatisticsRecentState> {
-  DataStatisticsRecentCubit() : super(const DataStatisticsRecentState());
+  DataStatisticsRecentCubit()
+      : super(const DataStatisticsRecentState(
+          statisticsRecent: null,
+          status: '',
+        ));
 
-  void getData(DataStatisticsRecentState state) {
-    emit(state);
+  void setLoading() {
+    emit(DataStatisticsRecentState(
+      statisticsRecent: state.statisticsRecent,
+      status: 'loading',
+    ));
   }
 
-  StatisticsRecentData? getValue() {
-    return state.statisticsRecent;
+  void setValue(StatisticsRecentData? statisticsRecent) {
+    emit(DataStatisticsRecentState(
+      statisticsRecent: statisticsRecent,
+      status: '',
+    ));
+  }
+
+  void setFailed() {
+    emit(DataStatisticsRecentState(
+      statisticsRecent: state.statisticsRecent,
+      status: 'failed',
+    ));
+  }
+
+  void setLoaded() {
+    emit(DataStatisticsRecentState(
+      statisticsRecent: state.statisticsRecent,
+      status: '',
+    ));
   }
 
   void update(StatisticsRecentData? statisticsRecent) {
     if ((statisticsRecent != null) &&
         (state.statisticsRecent.toString() != statisticsRecent.toString())) {
       setValue(statisticsRecent);
+    } else {
+      setLoaded();
     }
   }
 
-  void setValue(StatisticsRecentData? statisticsRecent) {
-    emit(DataStatisticsRecentState(
-      statisticsRecent: statisticsRecent,
-    ));
+  void refresh(BuildContext context) async {
+    setLoading();
+
+    GlobalSettingsCubit settings = BlocProvider.of<GlobalSettingsCubit>(context);
+    final int daysCount = settings.getStatisticsRecentDaysCount();
+
+    final StatisticsRecentData? data = await ScrobblesApi.fetchRecentStatistics(daysCount);
+    if (data != null) {
+      update(data);
+    } else {
+      setFailed();
+    }
   }
 
   @override
   DataStatisticsRecentState? fromJson(Map<String, dynamic> json) {
     return DataStatisticsRecentState(
       statisticsRecent: StatisticsRecentData.fromJson(json['statisticsRecent']),
+      status: '',
     );
   }
 
@@ -41,6 +79,7 @@ class DataStatisticsRecentCubit extends HydratedCubit<DataStatisticsRecentState>
   Map<String, Object?>? toJson(DataStatisticsRecentState state) {
     return <String, Object?>{
       'statisticsRecent': state.statisticsRecent?.toJson(),
+      'status': state.status,
     };
   }
 }
diff --git a/lib/cubit/data_statistics_recent_state.dart b/lib/cubit/data_statistics_recent_state.dart
index 1bf9d2f..056cc9a 100644
--- a/lib/cubit/data_statistics_recent_state.dart
+++ b/lib/cubit/data_statistics_recent_state.dart
@@ -3,13 +3,16 @@ part of 'data_statistics_recent_cubit.dart';
 @immutable
 class DataStatisticsRecentState extends Equatable {
   const DataStatisticsRecentState({
-    this.statisticsRecent,
+    required this.statisticsRecent,
+    required this.status,
   });
 
   final StatisticsRecentData? statisticsRecent;
+  final String status;
 
   @override
   List<Object?> get props => <Object?>[
         statisticsRecent,
+        status,
       ];
 }
diff --git a/lib/cubit/data_timeline_cubit.dart b/lib/cubit/data_timeline_cubit.dart
index 692dfcb..c82a0d6 100644
--- a/lib/cubit/data_timeline_cubit.dart
+++ b/lib/cubit/data_timeline_cubit.dart
@@ -1,38 +1,76 @@
 import 'package:equatable/equatable.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
 
-import 'package:scrobbles/models/timeline.dart';
+import 'package:scrobbles/cubit/settings_global_cubit.dart';
+import 'package:scrobbles/models/data/timeline.dart';
+import 'package:scrobbles/network/scrobbles.dart';
 
 part 'data_timeline_state.dart';
 
 class DataTimelineCubit extends HydratedCubit<DataTimelineState> {
-  DataTimelineCubit() : super(const DataTimelineState());
+  DataTimelineCubit()
+      : super(const DataTimelineState(
+          timeline: null,
+          status: '',
+        ));
 
-  void getData(DataTimelineState state) {
-    emit(state);
+  void setLoading() {
+    emit(DataTimelineState(
+      timeline: state.timeline,
+      status: 'loading',
+    ));
   }
 
-  TimelineData? getValue() {
-    return state.timeline;
+  void setValue(TimelineData? timeline) {
+    emit(DataTimelineState(
+      timeline: timeline,
+      status: '',
+    ));
+  }
+
+  void setFailed() {
+    emit(DataTimelineState(
+      timeline: state.timeline,
+      status: 'failed',
+    ));
+  }
+
+  void setLoaded() {
+    emit(DataTimelineState(
+      timeline: state.timeline,
+      status: '',
+    ));
   }
 
   void update(TimelineData? timeline) {
     if ((timeline != null) && (state.timeline.toString() != timeline.toString())) {
       setValue(timeline);
+    } else {
+      setLoaded();
     }
   }
 
-  void setValue(TimelineData? timeline) {
-    emit(DataTimelineState(
-      timeline: timeline,
-    ));
+  void refresh(BuildContext context) async {
+    setLoading();
+
+    GlobalSettingsCubit settings = BlocProvider.of<GlobalSettingsCubit>(context);
+    final int daysCount = settings.getTimelineDaysCount();
+
+    final TimelineData? data = await ScrobblesApi.fetchTimeline(daysCount);
+    if (data != null) {
+      update(data);
+    } else {
+      setFailed();
+    }
   }
 
   @override
   DataTimelineState? fromJson(Map<String, dynamic> json) {
     return DataTimelineState(
       timeline: TimelineData.fromJson(json['timeline']),
+      status: '',
     );
   }
 
@@ -40,6 +78,7 @@ class DataTimelineCubit extends HydratedCubit<DataTimelineState> {
   Map<String, Object?>? toJson(DataTimelineState state) {
     return <String, Object?>{
       'timeline': state.timeline?.toJson(),
+      'status': state.status,
     };
   }
 }
diff --git a/lib/cubit/data_timeline_state.dart b/lib/cubit/data_timeline_state.dart
index b0531c7..c3065dc 100644
--- a/lib/cubit/data_timeline_state.dart
+++ b/lib/cubit/data_timeline_state.dart
@@ -3,13 +3,16 @@ part of 'data_timeline_cubit.dart';
 @immutable
 class DataTimelineState extends Equatable {
   const DataTimelineState({
-    this.timeline,
+    required this.timeline,
+    required this.status,
   });
 
   final TimelineData? timeline;
+  final String status;
 
   @override
   List<Object?> get props => <Object?>[
         timeline,
+        status,
       ];
 }
diff --git a/lib/cubit/data_top_artists_cubit.dart b/lib/cubit/data_top_artists_cubit.dart
index 98fe17a..c8850ea 100644
--- a/lib/cubit/data_top_artists_cubit.dart
+++ b/lib/cubit/data_top_artists_cubit.dart
@@ -1,38 +1,76 @@
 import 'package:equatable/equatable.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
 
-import 'package:scrobbles/models/topartists.dart';
+import 'package:scrobbles/cubit/settings_global_cubit.dart';
+import 'package:scrobbles/models/data/topartists.dart';
+import 'package:scrobbles/network/scrobbles.dart';
 
 part 'data_top_artists_state.dart';
 
 class DataTopArtistsCubit extends HydratedCubit<DataTopArtistsState> {
-  DataTopArtistsCubit() : super(const DataTopArtistsState());
+  DataTopArtistsCubit()
+      : super(const DataTopArtistsState(
+          topArtists: null,
+          status: '',
+        ));
 
-  void getData(DataTopArtistsState state) {
-    emit(state);
+  void setLoading() {
+    emit(DataTopArtistsState(
+      topArtists: state.topArtists,
+      status: 'loading',
+    ));
   }
 
-  TopArtistsData? getValue(key) {
-    return state.topArtists;
+  void setValue(TopArtistsData? topArtists) {
+    emit(DataTopArtistsState(
+      topArtists: topArtists,
+      status: '',
+    ));
+  }
+
+  void setFailed() {
+    emit(DataTopArtistsState(
+      topArtists: state.topArtists,
+      status: 'failed',
+    ));
+  }
+
+  void setLoaded() {
+    emit(DataTopArtistsState(
+      topArtists: state.topArtists,
+      status: '',
+    ));
   }
 
   void update(TopArtistsData? topArtists) {
     if ((topArtists != null) && (state.topArtists.toString() != topArtists.toString())) {
       setValue(topArtists);
+    } else {
+      setLoaded();
     }
   }
 
-  void setValue(TopArtistsData? topArtists) {
-    emit(DataTopArtistsState(
-      topArtists: topArtists,
-    ));
+  void refresh(BuildContext context) async {
+    setLoading();
+
+    GlobalSettingsCubit settings = BlocProvider.of<GlobalSettingsCubit>(context);
+    final int daysCount = settings.getTopArtistsDaysCount();
+
+    final TopArtistsData? data = await ScrobblesApi.fetchTopArtists(daysCount);
+    if (data != null) {
+      if (context.mounted) update(data);
+    } else {
+      if (context.mounted) setFailed();
+    }
   }
 
   @override
   DataTopArtistsState? fromJson(Map<String, dynamic> json) {
     return DataTopArtistsState(
       topArtists: TopArtistsData.fromJson(json['topArtists']),
+      status: '',
     );
   }
 
@@ -40,6 +78,7 @@ class DataTopArtistsCubit extends HydratedCubit<DataTopArtistsState> {
   Map<String, Object?>? toJson(DataTopArtistsState state) {
     return <String, Object?>{
       'topArtists': state.topArtists?.toJson(),
+      'status': state.status,
     };
   }
 }
diff --git a/lib/cubit/data_top_artists_state.dart b/lib/cubit/data_top_artists_state.dart
index e6d14c0..79d95c0 100644
--- a/lib/cubit/data_top_artists_state.dart
+++ b/lib/cubit/data_top_artists_state.dart
@@ -3,13 +3,16 @@ part of 'data_top_artists_cubit.dart';
 @immutable
 class DataTopArtistsState extends Equatable {
   const DataTopArtistsState({
-    this.topArtists,
+    required this.topArtists,
+    required this.status,
   });
 
   final TopArtistsData? topArtists;
+  final String status;
 
   @override
   List<Object?> get props => <Object?>[
         topArtists,
+        status,
       ];
 }
diff --git a/lib/cubit/nav_cubit_pages.dart b/lib/cubit/nav_cubit_pages.dart
new file mode 100644
index 0000000..4caca48
--- /dev/null
+++ b/lib/cubit/nav_cubit_pages.dart
@@ -0,0 +1,29 @@
+import 'package:hydrated_bloc/hydrated_bloc.dart';
+
+import 'package:scrobbles/config/activity_page.dart';
+
+class NavCubitPage extends HydratedCubit<int> {
+  NavCubitPage() : super(0);
+
+  void updateIndex(int index) {
+    if (ActivityPage.isIndexAllowed(index)) {
+      emit(index);
+    } else {
+      emit(ActivityPage.indexHome);
+    }
+  }
+
+  void goToHomePage() {
+    emit(ActivityPage.indexHome);
+  }
+
+  @override
+  int fromJson(Map<String, dynamic> json) {
+    return ActivityPage.indexHome;
+  }
+
+  @override
+  Map<String, dynamic>? toJson(int state) {
+    return <String, int>{'pageIndex': state};
+  }
+}
diff --git a/lib/cubit/nav_cubit_screens.dart b/lib/cubit/nav_cubit_screens.dart
new file mode 100644
index 0000000..43a941f
--- /dev/null
+++ b/lib/cubit/nav_cubit_screens.dart
@@ -0,0 +1,37 @@
+import 'package:hydrated_bloc/hydrated_bloc.dart';
+
+import 'package:scrobbles/config/screen.dart';
+
+class NavCubitScreen extends HydratedCubit<int> {
+  NavCubitScreen() : super(0);
+
+  void updateIndex(int index) {
+    if (Screen.isIndexAllowed(index)) {
+      emit(index);
+    } else {
+      goToActivityPage();
+    }
+  }
+
+  void goToActivityPage() {
+    emit(Screen.indexActivity);
+  }
+
+  void goToSettingsPage() {
+    emit(Screen.indexSettings);
+  }
+
+  void goToAboutPage() {
+    emit(Screen.indexAbout);
+  }
+
+  @override
+  int fromJson(Map<String, dynamic> json) {
+    return Screen.indexActivity;
+  }
+
+  @override
+  Map<String, dynamic>? toJson(int state) {
+    return <String, int>{'screenIndex': state};
+  }
+}
diff --git a/lib/cubit/settings_cubit.dart b/lib/cubit/settings_global_cubit.dart
similarity index 60%
rename from lib/cubit/settings_cubit.dart
rename to lib/cubit/settings_global_cubit.dart
index 6c86444..f45d5da 100644
--- a/lib/cubit/settings_cubit.dart
+++ b/lib/cubit/settings_global_cubit.dart
@@ -2,12 +2,36 @@ import 'package:equatable/equatable.dart';
 import 'package:flutter/material.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
 
-import 'package:scrobbles/config/default_settings.dart';
+import 'package:scrobbles/config/default_global_settings.dart';
 
-part 'settings_state.dart';
+part 'settings_global_state.dart';
 
-class SettingsCubit extends HydratedCubit<SettingsState> {
-  SettingsCubit() : super(const SettingsState());
+class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> {
+  GlobalSettingsCubit() : super(const GlobalSettingsState());
+
+  void setValues({
+    String? username,
+    String? securityToken,
+    int? discoveriesDaysCount,
+    int? distributionDaysCount,
+    int? statisticsRecentDaysCount,
+    int? timelineDaysCount,
+    int? topArtistsDaysCount,
+    int? newArtistsCount,
+    int? newTracksCount,
+  }) {
+    emit(GlobalSettingsState(
+      username: username ?? state.username,
+      securityToken: securityToken ?? state.securityToken,
+      discoveriesDaysCount: discoveriesDaysCount ?? state.discoveriesDaysCount,
+      distributionDaysCount: distributionDaysCount ?? state.distributionDaysCount,
+      statisticsRecentDaysCount: statisticsRecentDaysCount ?? state.statisticsRecentDaysCount,
+      timelineDaysCount: timelineDaysCount ?? state.timelineDaysCount,
+      topArtistsDaysCount: topArtistsDaysCount ?? state.topArtistsDaysCount,
+      newArtistsCount: newArtistsCount ?? state.newArtistsCount,
+      newTracksCount: newTracksCount ?? state.newTracksCount,
+    ));
+  }
 
   String getUsername() {
     return state.username ?? '';
@@ -18,59 +42,36 @@ class SettingsCubit extends HydratedCubit<SettingsState> {
   }
 
   int getDiscoveriesDaysCount() {
-    return state.discoveriesDaysCount ?? DefaultSettings.defaultDiscoveriesDaysCount;
+    return state.discoveriesDaysCount ?? DefaultGlobalSettings.defaultDiscoveriesDaysCount;
   }
 
   int getDistributionDaysCount() {
-    return state.distributionDaysCount ?? DefaultSettings.defaultDistributionDaysCount;
+    return state.distributionDaysCount ?? DefaultGlobalSettings.defaultDistributionDaysCount;
   }
 
   int getStatisticsRecentDaysCount() {
-    return state.statisticsRecentDaysCount ?? DefaultSettings.defaultStatisticsRecentDaysCount;
+    return state.statisticsRecentDaysCount ??
+        DefaultGlobalSettings.defaultStatisticsRecentDaysCount;
   }
 
   int getTimelineDaysCount() {
-    return state.timelineDaysCount ?? DefaultSettings.defaultTimelineDaysCount;
+    return state.timelineDaysCount ?? DefaultGlobalSettings.defaultTimelineDaysCount;
   }
 
   int getTopArtistsDaysCount() {
-    return state.topArtistsDaysCount ?? DefaultSettings.defaultTopArtistsDaysCount;
+    return state.topArtistsDaysCount ?? DefaultGlobalSettings.defaultTopArtistsDaysCount;
   }
 
   int getNewArtistsCount() {
-    return state.newArtistsCount ?? DefaultSettings.defaultNewArtistsCount;
+    return state.newArtistsCount ?? DefaultGlobalSettings.defaultNewArtistsCount;
   }
 
   int getNewTracksCount() {
-    return state.newTracksCount ?? DefaultSettings.defaultNewTracksCount;
-  }
-
-  void setValues({
-    String? username,
-    String? securityToken,
-    int? discoveriesDaysCount,
-    int? distributionDaysCount,
-    int? statisticsRecentDaysCount,
-    int? timelineDaysCount,
-    int? topArtistsDaysCount,
-    int? newArtistsCount,
-    int? newTracksCount,
-  }) {
-    emit(SettingsState(
-      username: username ?? state.username,
-      securityToken: securityToken ?? state.securityToken,
-      discoveriesDaysCount: discoveriesDaysCount ?? state.discoveriesDaysCount,
-      distributionDaysCount: distributionDaysCount ?? state.distributionDaysCount,
-      statisticsRecentDaysCount: statisticsRecentDaysCount ?? state.statisticsRecentDaysCount,
-      timelineDaysCount: timelineDaysCount ?? state.timelineDaysCount,
-      topArtistsDaysCount: topArtistsDaysCount ?? state.topArtistsDaysCount,
-      newArtistsCount: newArtistsCount ?? state.newArtistsCount,
-      newTracksCount: newTracksCount ?? state.newTracksCount,
-    ));
+    return state.newTracksCount ?? DefaultGlobalSettings.defaultNewTracksCount;
   }
 
   @override
-  SettingsState? fromJson(Map<String, dynamic> json) {
+  GlobalSettingsState? fromJson(Map<String, dynamic> json) {
     String username = json['username'] as String;
     String securityToken = json['securityToken'] as String;
     int discoveriesDaysCount = json['discoveriesDaysCount'] as int;
@@ -81,7 +82,7 @@ class SettingsCubit extends HydratedCubit<SettingsState> {
     int newArtistsCount = json['newArtistsCount'] as int;
     int newTracksCount = json['newTracksCount'] as int;
 
-    return SettingsState(
+    return GlobalSettingsState(
       username: username,
       securityToken: securityToken,
       discoveriesDaysCount: discoveriesDaysCount,
@@ -95,21 +96,22 @@ class SettingsCubit extends HydratedCubit<SettingsState> {
   }
 
   @override
-  Map<String, dynamic>? toJson(SettingsState state) {
+  Map<String, dynamic>? toJson(GlobalSettingsState state) {
     return <String, dynamic>{
       'username': state.username ?? '',
       'securityToken': state.securityToken ?? '',
       'discoveriesDaysCount':
-          state.discoveriesDaysCount ?? DefaultSettings.defaultDiscoveriesDaysCount,
+          state.discoveriesDaysCount ?? DefaultGlobalSettings.defaultDiscoveriesDaysCount,
       'distributionDaysCount':
-          state.distributionDaysCount ?? DefaultSettings.defaultDistributionDaysCount,
-      'statisticsRecentDaysCount':
-          state.statisticsRecentDaysCount ?? DefaultSettings.defaultStatisticsRecentDaysCount,
-      'timelineDaysCount': state.timelineDaysCount ?? DefaultSettings.defaultTimelineDaysCount,
+          state.distributionDaysCount ?? DefaultGlobalSettings.defaultDistributionDaysCount,
+      'statisticsRecentDaysCount': state.statisticsRecentDaysCount ??
+          DefaultGlobalSettings.defaultStatisticsRecentDaysCount,
+      'timelineDaysCount':
+          state.timelineDaysCount ?? DefaultGlobalSettings.defaultTimelineDaysCount,
       'topArtistsDaysCount':
-          state.topArtistsDaysCount ?? DefaultSettings.defaultTopArtistsDaysCount,
-      'newArtistsCount': state.newArtistsCount ?? DefaultSettings.defaultNewArtistsCount,
-      'newTracksCount': state.newTracksCount ?? DefaultSettings.defaultNewTracksCount,
+          state.topArtistsDaysCount ?? DefaultGlobalSettings.defaultTopArtistsDaysCount,
+      'newArtistsCount': state.newArtistsCount ?? DefaultGlobalSettings.defaultNewArtistsCount,
+      'newTracksCount': state.newTracksCount ?? DefaultGlobalSettings.defaultNewTracksCount,
     };
   }
 }
diff --git a/lib/cubit/settings_state.dart b/lib/cubit/settings_global_state.dart
similarity index 92%
rename from lib/cubit/settings_state.dart
rename to lib/cubit/settings_global_state.dart
index 45ef6bb..fb08d54 100644
--- a/lib/cubit/settings_state.dart
+++ b/lib/cubit/settings_global_state.dart
@@ -1,8 +1,8 @@
-part of 'settings_cubit.dart';
+part of 'settings_global_cubit.dart';
 
 @immutable
-class SettingsState extends Equatable {
-  const SettingsState({
+class GlobalSettingsState extends Equatable {
+  const GlobalSettingsState({
     this.username,
     this.securityToken,
     this.discoveriesDaysCount,
diff --git a/lib/main.dart b/lib/main.dart
index b9d50e8..a69592e 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -2,13 +2,16 @@ import 'dart:io';
 
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:hive/hive.dart';
 import 'package:hydrated_bloc/hydrated_bloc.dart';
 import 'package:path_provider/path_provider.dart';
 
 import 'package:scrobbles/config/theme.dart';
-import 'package:scrobbles/cubit/bottom_nav_cubit.dart';
+import 'package:scrobbles/cubit/activity_cubit.dart';
+import 'package:scrobbles/cubit/nav_cubit_pages.dart';
+import 'package:scrobbles/cubit/nav_cubit_screens.dart';
 import 'package:scrobbles/cubit/data_counts_by_day_cubit.dart';
 import 'package:scrobbles/cubit/data_counts_by_hour_cubit.dart';
 import 'package:scrobbles/cubit/data_discoveries_cubit.dart';
@@ -19,12 +22,12 @@ import 'package:scrobbles/cubit/data_statistics_global_cubit.dart';
 import 'package:scrobbles/cubit/data_statistics_recent_cubit.dart';
 import 'package:scrobbles/cubit/data_timeline_cubit.dart';
 import 'package:scrobbles/cubit/data_top_artists_cubit.dart';
-import 'package:scrobbles/cubit/settings_cubit.dart';
+import 'package:scrobbles/cubit/settings_global_cubit.dart';
 import 'package:scrobbles/cubit/theme_cubit.dart';
 import 'package:scrobbles/ui/skeleton.dart';
 
 void main() async {
-  /// Initialize packages
+  // Initialize packages
   WidgetsFlutterBinding.ensureInitialized();
   await EasyLocalization.ensureInitialized();
   final Directory tmpDir = await getTemporaryDirectory();
@@ -33,18 +36,17 @@ void main() async {
     storageDirectory: tmpDir,
   );
 
-  runApp(
-    EasyLocalization(
-      path: 'assets/translations',
-      supportedLocales: const <Locale>[
-        Locale('en'),
-        Locale('fr'),
-      ],
-      fallbackLocale: const Locale('en'),
-      useFallbackTranslations: true,
-      child: const MyApp(),
-    ),
-  );
+  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
+      .then((value) => runApp(EasyLocalization(
+            path: 'assets/translations',
+            supportedLocales: const <Locale>[
+              Locale('en'),
+              Locale('fr'),
+            ],
+            fallbackLocale: const Locale('en'),
+            useFallbackTranslations: true,
+            child: const MyApp(),
+          )));
 }
 
 class MyApp extends StatelessWidget {
@@ -54,8 +56,11 @@ class MyApp extends StatelessWidget {
   Widget build(BuildContext context) {
     return MultiBlocProvider(
       providers: [
-        BlocProvider<SettingsCubit>(create: (context) => SettingsCubit()),
-        BlocProvider<BottomNavCubit>(create: (context) => BottomNavCubit()),
+        BlocProvider<NavCubitPage>(create: (context) => NavCubitPage()),
+        BlocProvider<NavCubitScreen>(create: (context) => NavCubitScreen()),
+        BlocProvider<ThemeCubit>(create: (context) => ThemeCubit()),
+        BlocProvider<ActivityCubit>(create: (context) => ActivityCubit()),
+        BlocProvider<GlobalSettingsCubit>(create: (context) => GlobalSettingsCubit()),
         BlocProvider<DataCountsByDayCubit>(create: (context) => DataCountsByDayCubit()),
         BlocProvider<DataCountsByHourCubit>(create: (context) => DataCountsByHourCubit()),
         BlocProvider<DataDiscoveriesCubit>(create: (context) => DataDiscoveriesCubit()),
@@ -68,26 +73,26 @@ class MyApp extends StatelessWidget {
             create: (context) => DataStatisticsRecentCubit()),
         BlocProvider<DataTimelineCubit>(create: (context) => DataTimelineCubit()),
         BlocProvider<DataTopArtistsCubit>(create: (context) => DataTopArtistsCubit()),
-        BlocProvider<ThemeCubit>(create: (context) => ThemeCubit()),
       ],
       child: BlocBuilder<ThemeCubit, ThemeModeState>(
-          builder: (BuildContext context, ThemeModeState state) {
-        return MaterialApp(
-          title: 'Scrobbles',
-          home: const SkeletonScreen(),
+        builder: (BuildContext context, ThemeModeState state) {
+          return MaterialApp(
+            title: 'Scrobbles',
+            home: const SkeletonScreen(),
 
-          // Theme stuff
-          theme: lightTheme,
-          darkTheme: darkTheme,
-          themeMode: state.themeMode,
+            // Theme stuff
+            theme: lightTheme,
+            darkTheme: darkTheme,
+            themeMode: state.themeMode,
 
-          // Localization stuff
-          localizationsDelegates: context.localizationDelegates,
-          supportedLocales: context.supportedLocales,
-          locale: context.locale,
-          debugShowCheckedModeBanner: false,
-        );
-      }),
+            // Localization stuff
+            localizationsDelegates: context.localizationDelegates,
+            supportedLocales: context.supportedLocales,
+            locale: context.locale,
+            debugShowCheckedModeBanner: false,
+          );
+        },
+      ),
     );
   }
 }
diff --git a/lib/models/activity/activity.dart b/lib/models/activity/activity.dart
new file mode 100644
index 0000000..1b890ee
--- /dev/null
+++ b/lib/models/activity/activity.dart
@@ -0,0 +1,26 @@
+import 'package:scrobbles/utils/tools.dart';
+
+class Activity {
+  const Activity();
+
+  factory Activity.createNull() {
+    return const Activity();
+  }
+
+  void dump() {
+    printlog('');
+    printlog('## Current activity dump:');
+    printlog('');
+    printlog('$Activity:');
+    printlog('');
+  }
+
+  @override
+  String toString() {
+    return '$Activity(${toJson()})';
+  }
+
+  Map<String, dynamic>? toJson() {
+    return <String, dynamic>{};
+  }
+}
diff --git a/lib/models/artists.dart b/lib/models/data/artists.dart
similarity index 100%
rename from lib/models/artists.dart
rename to lib/models/data/artists.dart
diff --git a/lib/models/counts_by_day.dart b/lib/models/data/counts_by_day.dart
similarity index 100%
rename from lib/models/counts_by_day.dart
rename to lib/models/data/counts_by_day.dart
diff --git a/lib/models/counts_by_hour.dart b/lib/models/data/counts_by_hour.dart
similarity index 100%
rename from lib/models/counts_by_hour.dart
rename to lib/models/data/counts_by_hour.dart
diff --git a/lib/models/discoveries.dart b/lib/models/data/discoveries.dart
similarity index 100%
rename from lib/models/discoveries.dart
rename to lib/models/data/discoveries.dart
diff --git a/lib/models/heatmap.dart b/lib/models/data/heatmap.dart
similarity index 100%
rename from lib/models/heatmap.dart
rename to lib/models/data/heatmap.dart
diff --git a/lib/models/new_artists.dart b/lib/models/data/new_artists.dart
similarity index 95%
rename from lib/models/new_artists.dart
rename to lib/models/data/new_artists.dart
index fffafc6..6d1e845 100644
--- a/lib/models/new_artists.dart
+++ b/lib/models/data/new_artists.dart
@@ -1,6 +1,6 @@
 import 'dart:convert';
 
-import 'package:scrobbles/models/artists.dart';
+import 'package:scrobbles/models/data/artists.dart';
 
 class NewArtistData {
   final DateTime? firstPlayed;
diff --git a/lib/models/new_tracks.dart b/lib/models/data/new_tracks.dart
similarity index 95%
rename from lib/models/new_tracks.dart
rename to lib/models/data/new_tracks.dart
index b1105ef..9c39e58 100644
--- a/lib/models/new_tracks.dart
+++ b/lib/models/data/new_tracks.dart
@@ -1,6 +1,6 @@
 import 'dart:convert';
 
-import 'package:scrobbles/models/track.dart';
+import 'package:scrobbles/models/data/track.dart';
 
 class NewTrackData {
   final DateTime? firstPlayed;
diff --git a/lib/models/statistics_global.dart b/lib/models/data/statistics_global.dart
similarity index 100%
rename from lib/models/statistics_global.dart
rename to lib/models/data/statistics_global.dart
diff --git a/lib/models/statistics_recent.dart b/lib/models/data/statistics_recent.dart
similarity index 100%
rename from lib/models/statistics_recent.dart
rename to lib/models/data/statistics_recent.dart
diff --git a/lib/models/timeline.dart b/lib/models/data/timeline.dart
similarity index 100%
rename from lib/models/timeline.dart
rename to lib/models/data/timeline.dart
diff --git a/lib/models/topartists.dart b/lib/models/data/topartists.dart
similarity index 100%
rename from lib/models/topartists.dart
rename to lib/models/data/topartists.dart
diff --git a/lib/models/track.dart b/lib/models/data/track.dart
similarity index 93%
rename from lib/models/track.dart
rename to lib/models/data/track.dart
index 39e3d6a..2cb6f2a 100644
--- a/lib/models/track.dart
+++ b/lib/models/data/track.dart
@@ -1,6 +1,6 @@
 import 'dart:convert';
 
-import 'package:scrobbles/models/artists.dart';
+import 'package:scrobbles/models/data/artists.dart';
 
 class Track {
   final int id;
diff --git a/lib/network/scrobbles.dart b/lib/network/scrobbles.dart
index 3592365..8c341ae 100644
--- a/lib/network/scrobbles.dart
+++ b/lib/network/scrobbles.dart
@@ -1,127 +1,137 @@
 import 'dart:convert';
 import 'package:http/http.dart' as http;
 
-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/heatmap.dart';
-import 'package:scrobbles/models/new_artists.dart';
-import 'package:scrobbles/models/new_tracks.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';
+import 'package:scrobbles/models/data/counts_by_day.dart';
+import 'package:scrobbles/models/data/counts_by_hour.dart';
+import 'package:scrobbles/models/data/discoveries.dart';
+import 'package:scrobbles/models/data/heatmap.dart';
+import 'package:scrobbles/models/data/new_artists.dart';
+import 'package:scrobbles/models/data/new_tracks.dart';
+import 'package:scrobbles/models/data/statistics_global.dart';
+import 'package:scrobbles/models/data/statistics_recent.dart';
+import 'package:scrobbles/models/data/timeline.dart';
+import 'package:scrobbles/models/data/topartists.dart';
 
 class ScrobblesApi {
   static String baseUrl = 'https://scrobble.harrault.fr';
 
-  static Future<StatisticsGlobalData> fetchGlobalStatistics() async {
+  static Future<StatisticsGlobalData?> fetchGlobalStatistics() async {
     final String url = '$baseUrl/stats';
     final response = await http.get(Uri.parse(url));
 
     if (response.statusCode == 200) {
-      return StatisticsGlobalData.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
-    } else {
-      throw Exception('Failed to get data from API.');
+      final Map<String, dynamic> rawData = jsonDecode(response.body) as Map<String, dynamic>;
+      return StatisticsGlobalData.fromJson(rawData);
     }
+
+    return null;
   }
 
-  static Future<StatisticsRecentData> fetchRecentStatistics(int daysCount) async {
+  static Future<StatisticsRecentData?> fetchRecentStatistics(int daysCount) async {
     final String url = '$baseUrl/$daysCount/stats';
     final response = await http.get(Uri.parse(url));
 
     if (response.statusCode == 200) {
-      return StatisticsRecentData.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
-    } else {
-      throw Exception('Failed to get data from API.');
+      final Map<String, dynamic> rawData = jsonDecode(response.body) as Map<String, dynamic>;
+      return StatisticsRecentData.fromJson(rawData);
     }
+
+    return null;
   }
 
-  static Future<TimelineData> fetchTimeline(int daysCount) async {
+  static Future<TimelineData?> fetchTimeline(int daysCount) async {
     final String url = '$baseUrl/data/$daysCount/timeline';
     final response = await http.get(Uri.parse(url));
 
     if (response.statusCode == 200) {
-      return TimelineData.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
-    } else {
-      throw Exception('Failed to get data from API.');
+      final Map<String, dynamic> rawData = jsonDecode(response.body) as Map<String, dynamic>;
+      return TimelineData.fromJson(rawData);
     }
+
+    return null;
   }
 
-  static Future<CountsByDayData> fetchCountsByDay(int daysCount) async {
+  static Future<CountsByDayData?> fetchCountsByDay(int daysCount) async {
     final String url = '$baseUrl/data/$daysCount/counts-by-day';
     final response = await http.get(Uri.parse(url));
 
     if (response.statusCode == 200) {
-      return CountsByDayData.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
-    } else {
-      throw Exception('Failed to get data from API.');
+      final Map<String, dynamic> rawData = jsonDecode(response.body) as Map<String, dynamic>;
+      return CountsByDayData.fromJson(rawData);
     }
+
+    return null;
   }
 
-  static Future<CountsByHourData> fetchCountsByHour(int daysCount) async {
+  static Future<CountsByHourData?> fetchCountsByHour(int daysCount) async {
     final String url = '$baseUrl/data/$daysCount/counts-by-hour';
     final response = await http.get(Uri.parse(url));
 
     if (response.statusCode == 200) {
-      return CountsByHourData.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
-    } else {
-      throw Exception('Failed to get data from API.');
+      final Map<String, dynamic> rawData = jsonDecode(response.body) as Map<String, dynamic>;
+      return CountsByHourData.fromJson(rawData);
     }
+
+    return null;
   }
 
-  static Future<DiscoveriesData> fetchDiscoveries(int daysCount) async {
+  static Future<DiscoveriesData?> fetchDiscoveries(int daysCount) async {
     final String url = '$baseUrl/data/$daysCount/news';
     final response = await http.get(Uri.parse(url));
 
     if (response.statusCode == 200) {
-      return DiscoveriesData.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
-    } else {
-      throw Exception('Failed to get data from API.');
+      final Map<String, dynamic> rawData = jsonDecode(response.body) as Map<String, dynamic>;
+      return DiscoveriesData.fromJson(rawData);
     }
+
+    return null;
   }
 
-  static Future<TopArtistsData> fetchTopArtists(int daysCount) async {
+  static Future<TopArtistsData?> fetchTopArtists(int daysCount) async {
     final String url = '$baseUrl/data/$daysCount/top-artists';
     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.');
+      final Map<String, dynamic> rawData = jsonDecode(response.body) as Map<String, dynamic>;
+      return TopArtistsData.fromJson(rawData);
     }
+
+    return null;
   }
 
-  static Future<HeatmapData> fetchHeatmap(int daysCount) async {
+  static Future<HeatmapData?> fetchHeatmap(int daysCount) async {
     final String url = '$baseUrl/data/$daysCount/heatmap';
     final response = await http.get(Uri.parse(url));
 
     if (response.statusCode == 200) {
-      return HeatmapData.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
-    } else {
-      throw Exception('Failed to get data from API.');
+      final Map<String, dynamic> rawData = jsonDecode(response.body) as Map<String, dynamic>;
+      return HeatmapData.fromJson(rawData);
     }
+
+    return null;
   }
 
-  static Future<NewArtistsData> fetchNewArtists(int count) async {
+  static Future<NewArtistsData?> fetchNewArtists(int count) async {
     final String url = '$baseUrl/data/discoveries/artists/$count';
     final response = await http.get(Uri.parse(url));
 
     if (response.statusCode == 200) {
-      return NewArtistsData.fromJson(jsonDecode(response.body) as List<dynamic>);
-    } else {
-      throw Exception('Failed to get data from API.');
+      final List<dynamic> rawData = jsonDecode(response.body) as List<dynamic>;
+      return NewArtistsData.fromJson(rawData);
     }
+
+    return null;
   }
 
-  static Future<NewTracksData> fetchNewTracks(int count) async {
+  static Future<NewTracksData?> fetchNewTracks(int count) async {
     final String url = '$baseUrl/data/discoveries/tracks/$count';
     final response = await http.get(Uri.parse(url));
 
     if (response.statusCode == 200) {
-      return NewTracksData.fromJson(jsonDecode(response.body) as List<dynamic>);
-    } else {
-      throw Exception('Failed to get data from API.');
+      final List<dynamic> rawData = jsonDecode(response.body) as List<dynamic>;
+      return NewTracksData.fromJson(rawData);
     }
+
+    return null;
   }
 }
diff --git a/lib/ui/widgets/app_titles.dart b/lib/ui/helpers/app_titles.dart
similarity index 52%
rename from lib/ui/widgets/app_titles.dart
rename to lib/ui/helpers/app_titles.dart
index 9354124..b98107b 100644
--- a/lib/ui/widgets/app_titles.dart
+++ b/lib/ui/helpers/app_titles.dart
@@ -1,8 +1,8 @@
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 
-class AppTitle extends StatelessWidget {
-  const AppTitle({super.key, required this.text});
+class AppHeader extends StatelessWidget {
+  const AppHeader({super.key, required this.text});
 
   final String text;
 
@@ -11,37 +11,22 @@ class AppTitle extends StatelessWidget {
     return Text(
       tr(text),
       textAlign: TextAlign.start,
-      style: Theme.of(context).textTheme.headlineLarge!.apply(fontWeightDelta: 2),
+      style: Theme.of(context).textTheme.headlineMedium!.apply(fontWeightDelta: 2),
     );
   }
 }
 
-class AppTitle1 extends StatelessWidget {
-  const AppTitle1({super.key, required this.text});
+class AppTitle extends StatelessWidget {
+  const AppTitle({super.key, required this.text});
 
   final String text;
 
   @override
   Widget build(BuildContext context) {
     return Text(
-      text,
+      tr(text),
       textAlign: TextAlign.start,
       style: Theme.of(context).textTheme.titleLarge!.apply(fontWeightDelta: 2),
     );
   }
 }
-
-class AppTitle2 extends StatelessWidget {
-  const AppTitle2({super.key, required this.text});
-
-  final String text;
-
-  @override
-  Widget build(BuildContext context) {
-    return Text(
-      text,
-      textAlign: TextAlign.start,
-      style: Theme.of(context).textTheme.titleMedium!.apply(fontWeightDelta: 2),
-    );
-  }
-}
diff --git a/lib/ui/nav/bottom_nav_bar.dart b/lib/ui/nav/bottom_nav_bar.dart
new file mode 100644
index 0000000..1cdade6
--- /dev/null
+++ b/lib/ui/nav/bottom_nav_bar.dart
@@ -0,0 +1,53 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flutter_swipe/flutter_swipe.dart';
+
+import 'package:scrobbles/config/activity_page.dart';
+import 'package:scrobbles/cubit/nav_cubit_pages.dart';
+
+class BottomNavBar extends StatelessWidget {
+  const BottomNavBar({super.key, required this.swipeController});
+
+  final SwiperController swipeController;
+
+  @override
+  Widget build(BuildContext context) {
+    return Card(
+      margin: const EdgeInsets.all(0),
+      elevation: 4,
+      shadowColor: Theme.of(context).colorScheme.shadow,
+      color: Theme.of(context).colorScheme.surfaceContainerHighest,
+      shape: const ContinuousRectangleBorder(),
+      child: BlocBuilder<NavCubitPage, int>(
+        builder: (BuildContext context, int pageIndex) {
+          final List<ActivityPageItem> pageItems = [
+            ActivityPage.pageHome,
+            ActivityPage.pageDiscoveries,
+            ActivityPage.pageStatistics,
+          ];
+          final List<BottomNavigationBarItem> items = pageItems.map((ActivityPageItem item) {
+            return BottomNavigationBarItem(
+              icon: item.icon,
+              label: tr(item.code),
+            );
+          }).toList();
+
+          return BottomNavigationBar(
+            currentIndex: pageIndex,
+            onTap: (int index) {
+              context.read<NavCubitPage>().updateIndex(index);
+              swipeController.move(index);
+            },
+            type: BottomNavigationBarType.fixed,
+            elevation: 0,
+            backgroundColor: Colors.transparent,
+            selectedItemColor: Theme.of(context).colorScheme.primary,
+            unselectedItemColor: Theme.of(context).textTheme.bodySmall!.color,
+            items: items,
+          );
+        },
+      ),
+    );
+  }
+}
diff --git a/lib/ui/nav/global_app_bar.dart b/lib/ui/nav/global_app_bar.dart
new file mode 100644
index 0000000..f7cb5e5
--- /dev/null
+++ b/lib/ui/nav/global_app_bar.dart
@@ -0,0 +1,65 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:scrobbles/cubit/activity_cubit.dart';
+import 'package:unicons/unicons.dart';
+
+import 'package:scrobbles/cubit/nav_cubit_pages.dart';
+import 'package:scrobbles/config/screen.dart';
+import 'package:scrobbles/cubit/nav_cubit_screens.dart';
+import 'package:scrobbles/ui/helpers/app_titles.dart';
+
+class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget {
+  const GlobalAppBar({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<NavCubitScreen, int>(
+      builder: (BuildContext context, int screenIndex) {
+        final List<Widget> menuActions = [];
+
+        if (screenIndex == Screen.indexActivity) {
+          // go to Settings page
+          menuActions.add(IconButton(
+            onPressed: () {
+              context.read<NavCubitScreen>().goToSettingsPage();
+            },
+            icon: Screen.screenSettings.icon,
+          ));
+
+          // go to About page
+          menuActions.add(IconButton(
+            onPressed: () {
+              context.read<NavCubitScreen>().goToAboutPage();
+            },
+            icon: Screen.screenAbout.icon,
+          ));
+
+          // refresh data
+          menuActions.add(IconButton(
+            onPressed: () {
+              BlocProvider.of<ActivityCubit>(context).refresh(context);
+            },
+            icon: const Icon(UniconsSolid.refresh),
+          ));
+        } else {
+          // back to Home page
+          menuActions.add(IconButton(
+            onPressed: () {
+              context.read<NavCubitScreen>().goToActivityPage();
+              context.read<NavCubitPage>().goToHomePage();
+            },
+            icon: Screen.screenActivity.icon,
+          ));
+        }
+
+        return AppBar(
+          title: const AppHeader(text: 'app_name'),
+          actions: menuActions,
+        );
+      },
+    );
+  }
+
+  @override
+  Size get preferredSize => const Size.fromHeight(50);
+}
diff --git a/lib/ui/pages/discoveries.dart b/lib/ui/pages/discoveries.dart
new file mode 100644
index 0000000..ddbbe96
--- /dev/null
+++ b/lib/ui/pages/discoveries.dart
@@ -0,0 +1,33 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:scrobbles/cubit/activity_cubit.dart';
+import 'package:scrobbles/ui/widgets/cards/discoveries.dart';
+import 'package:scrobbles/ui/widgets/cards/new_artists.dart';
+import 'package:scrobbles/ui/widgets/cards/new_tracks.dart';
+
+class PageDiscoveries extends StatelessWidget {
+  const PageDiscoveries({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return RefreshIndicator(
+      onRefresh: () async {
+        BlocProvider.of<ActivityCubit>(context).refresh(context);
+      },
+      child: ListView(
+        padding: const EdgeInsets.symmetric(horizontal: 4),
+        physics: const BouncingScrollPhysics(),
+        children: const <Widget>[
+          SizedBox(height: 8),
+          CardDiscoveries(),
+          SizedBox(height: 6),
+          CardNewArtists(),
+          SizedBox(height: 6),
+          CardNewTracks(),
+          SizedBox(height: 36),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/ui/pages/home.dart b/lib/ui/pages/home.dart
new file mode 100644
index 0000000..f7d4d8b
--- /dev/null
+++ b/lib/ui/pages/home.dart
@@ -0,0 +1,36 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:scrobbles/cubit/activity_cubit.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 PageHome extends StatelessWidget {
+  const PageHome({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return RefreshIndicator(
+      onRefresh: () async {
+        BlocProvider.of<ActivityCubit>(context).refresh(context);
+      },
+      child: ListView(
+        padding: const EdgeInsets.symmetric(horizontal: 4),
+        physics: const BouncingScrollPhysics(),
+        children: const <Widget>[
+          SizedBox(height: 8),
+          CardStatisticsGlobal(),
+          SizedBox(height: 6),
+          CardStatisticsRecent(),
+          SizedBox(height: 6),
+          CardTimeline(),
+          SizedBox(height: 6),
+          CardTopArtists(),
+          SizedBox(height: 36),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/ui/pages/statistics.dart b/lib/ui/pages/statistics.dart
new file mode 100644
index 0000000..b0448b6
--- /dev/null
+++ b/lib/ui/pages/statistics.dart
@@ -0,0 +1,33 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:scrobbles/cubit/activity_cubit.dart';
+import 'package:scrobbles/ui/widgets/cards/counts_by_day.dart';
+import 'package:scrobbles/ui/widgets/cards/counts_by_hour.dart';
+import 'package:scrobbles/ui/widgets/cards/heatmap.dart';
+
+class PageStatistics extends StatelessWidget {
+  const PageStatistics({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return RefreshIndicator(
+      onRefresh: () async {
+        BlocProvider.of<ActivityCubit>(context).refresh(context);
+      },
+      child: ListView(
+        padding: const EdgeInsets.symmetric(horizontal: 4),
+        physics: const BouncingScrollPhysics(),
+        children: const <Widget>[
+          SizedBox(height: 8),
+          CardHeatmap(),
+          SizedBox(height: 6),
+          CardCountsByDay(),
+          SizedBox(height: 6),
+          CardCountsByHour(),
+          SizedBox(height: 36),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/ui/screens/about.dart b/lib/ui/screens/about.dart
new file mode 100644
index 0000000..1685d05
--- /dev/null
+++ b/lib/ui/screens/about.dart
@@ -0,0 +1,41 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:package_info_plus/package_info_plus.dart';
+
+import 'package:scrobbles/ui/helpers/app_titles.dart';
+
+class ScreenAbout extends StatelessWidget {
+  const ScreenAbout({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Padding(
+      padding: const EdgeInsets.symmetric(horizontal: 8),
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.start,
+        crossAxisAlignment: CrossAxisAlignment.start,
+        mainAxisSize: MainAxisSize.max,
+        children: <Widget>[
+          const SizedBox(height: 8),
+          const AppTitle(text: 'about_title'),
+          const Text('about_content').tr(),
+          FutureBuilder<PackageInfo>(
+            future: PackageInfo.fromPlatform(),
+            builder: (context, snapshot) {
+              switch (snapshot.connectionState) {
+                case ConnectionState.done:
+                  return const Text('about_version').tr(
+                    namedArgs: {
+                      'version': snapshot.data!.version,
+                    },
+                  );
+                default:
+                  return const SizedBox();
+              }
+            },
+          ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/lib/ui/screens/activity.dart b/lib/ui/screens/activity.dart
new file mode 100644
index 0000000..7ebabc1
--- /dev/null
+++ b/lib/ui/screens/activity.dart
@@ -0,0 +1,39 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flutter_swipe/flutter_swipe.dart';
+
+import 'package:scrobbles/config/activity_page.dart';
+import 'package:scrobbles/config/screen.dart';
+import 'package:scrobbles/cubit/nav_cubit_pages.dart';
+import 'package:scrobbles/ui/nav/bottom_nav_bar.dart';
+
+class ScreenActivity extends StatelessWidget {
+  const ScreenActivity({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocBuilder<NavCubitPage, int>(
+      builder: (BuildContext context, int pageIndex) {
+        return Swiper(
+          itemCount: Screen.itemsCount,
+          itemBuilder: (BuildContext context, int pageIndex) {
+            return ActivityPage.getPageWidget(pageIndex);
+          },
+          pagination: SwiperPagination(
+            margin: const EdgeInsets.all(0),
+            builder: SwiperCustomPagination(
+              builder: (BuildContext context, SwiperPluginConfig config) {
+                return BottomNavBar(swipeController: config.controller);
+              },
+            ),
+          ),
+          onIndexChanged: (newPageIndex) {
+            BlocProvider.of<NavCubitPage>(context).updateIndex(newPageIndex);
+          },
+          outer: true,
+          loop: false,
+        );
+      },
+    );
+  }
+}
diff --git a/lib/ui/screens/discoveries.dart b/lib/ui/screens/discoveries.dart
deleted file mode 100644
index 550307b..0000000
--- a/lib/ui/screens/discoveries.dart
+++ /dev/null
@@ -1,36 +0,0 @@
-import 'package:flutter/material.dart';
-
-import 'package:scrobbles/ui/widgets/cards/discoveries.dart';
-import 'package:scrobbles/ui/widgets/cards/new_artists.dart';
-import 'package:scrobbles/ui/widgets/cards/new_tracks.dart';
-
-class ScreenDiscoveries extends StatelessWidget {
-  final Function() notifyParent;
-
-  const ScreenDiscoveries({super.key, required this.notifyParent});
-
-  @override
-  Widget build(BuildContext context) {
-    return Material(
-      color: Theme.of(context).colorScheme.background,
-      child: RefreshIndicator(
-        onRefresh: () async {
-          notifyParent();
-        },
-        child: ListView(
-          padding: const EdgeInsets.symmetric(horizontal: 4),
-          physics: const BouncingScrollPhysics(),
-          children: const <Widget>[
-            SizedBox(height: 8),
-            CardDiscoveries(),
-            SizedBox(height: 6),
-            CardNewArtists(),
-            SizedBox(height: 6),
-            CardNewTracks(),
-            SizedBox(height: 36),
-          ],
-        ),
-      ),
-    );
-  }
-}
diff --git a/lib/ui/screens/home.dart b/lib/ui/screens/home.dart
deleted file mode 100644
index e943ff8..0000000
--- a/lib/ui/screens/home.dart
+++ /dev/null
@@ -1,39 +0,0 @@
-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 StatelessWidget {
-  final Function() notifyParent;
-
-  const ScreenHome({super.key, required this.notifyParent});
-
-  @override
-  Widget build(BuildContext context) {
-    return Material(
-      color: Theme.of(context).colorScheme.background,
-      child: RefreshIndicator(
-        onRefresh: () async {
-          notifyParent();
-        },
-        child: ListView(
-          padding: const EdgeInsets.symmetric(horizontal: 4),
-          physics: const BouncingScrollPhysics(),
-          children: const <Widget>[
-            SizedBox(height: 8),
-            CardStatisticsGlobal(),
-            SizedBox(height: 6),
-            CardStatisticsRecent(),
-            SizedBox(height: 6),
-            CardTimeline(),
-            SizedBox(height: 6),
-            CardTopArtists(),
-            SizedBox(height: 36),
-          ],
-        ),
-      ),
-    );
-  }
-}
diff --git a/lib/ui/screens/settings.dart b/lib/ui/screens/settings.dart
index 736f3a5..856ad88 100644
--- a/lib/ui/screens/settings.dart
+++ b/lib/ui/screens/settings.dart
@@ -1,25 +1,22 @@
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 
-import 'package:scrobbles/ui/widgets/app_titles.dart';
-import 'package:scrobbles/ui/widgets/settings_form.dart';
+import 'package:scrobbles/ui/helpers/app_titles.dart';
+import 'package:scrobbles/ui/settings/settings_form.dart';
 
 class ScreenSettings extends StatelessWidget {
   const ScreenSettings({super.key});
 
   @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),
-          AppTitle1(text: tr('settings_title')),
-          const SettingsForm(),
-        ],
-      ),
+    return ListView(
+      padding: const EdgeInsets.symmetric(horizontal: 4),
+      physics: const BouncingScrollPhysics(),
+      children: <Widget>[
+        const SizedBox(height: 8),
+        AppTitle(text: tr('settings_title')),
+        const SettingsForm(),
+      ],
     );
   }
 }
diff --git a/lib/ui/screens/statistics.dart b/lib/ui/screens/statistics.dart
deleted file mode 100644
index 7e35f1e..0000000
--- a/lib/ui/screens/statistics.dart
+++ /dev/null
@@ -1,36 +0,0 @@
-import 'package:flutter/material.dart';
-
-import 'package:scrobbles/ui/widgets/cards/counts_by_day.dart';
-import 'package:scrobbles/ui/widgets/cards/counts_by_hour.dart';
-import 'package:scrobbles/ui/widgets/cards/heatmap.dart';
-
-class ScreenStatistics extends StatelessWidget {
-  final Function() notifyParent;
-
-  const ScreenStatistics({super.key, required this.notifyParent});
-
-  @override
-  Widget build(BuildContext context) {
-    return Material(
-      color: Theme.of(context).colorScheme.background,
-      child: RefreshIndicator(
-        onRefresh: () async {
-          notifyParent();
-        },
-        child: ListView(
-          padding: const EdgeInsets.symmetric(horizontal: 4),
-          physics: const BouncingScrollPhysics(),
-          children: const <Widget>[
-            SizedBox(height: 8),
-            CardHeatmap(),
-            SizedBox(height: 6),
-            CardCountsByDay(),
-            SizedBox(height: 6),
-            CardCountsByHour(),
-            SizedBox(height: 36),
-          ],
-        ),
-      ),
-    );
-  }
-}
diff --git a/lib/ui/widgets/settings_form.dart b/lib/ui/settings/settings_form.dart
similarity index 78%
rename from lib/ui/widgets/settings_form.dart
rename to lib/ui/settings/settings_form.dart
index 6e5401d..f641585 100644
--- a/lib/ui/widgets/settings_form.dart
+++ b/lib/ui/settings/settings_form.dart
@@ -1,12 +1,12 @@
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:scrobbles/ui/widgets/theme_card.dart';
+import 'package:scrobbles/ui/settings/theme_card.dart';
 import 'package:unicons/unicons.dart';
 
-import 'package:scrobbles/config/default_settings.dart';
-import 'package:scrobbles/cubit/settings_cubit.dart';
-import 'package:scrobbles/ui/widgets/app_titles.dart';
+import 'package:scrobbles/config/default_global_settings.dart';
+import 'package:scrobbles/cubit/settings_global_cubit.dart';
+import 'package:scrobbles/ui/helpers/app_titles.dart';
 
 class SettingsForm extends StatefulWidget {
   const SettingsForm({super.key});
@@ -19,13 +19,13 @@ class _SettingsFormState extends State<SettingsForm> {
   final usernameController = TextEditingController();
   final securityTokenController = TextEditingController();
 
-  int discoveriesDaysCount = DefaultSettings.defaultDiscoveriesDaysCount;
-  int distributionDaysCount = DefaultSettings.defaultDistributionDaysCount;
-  int statisticsRecentDaysCount = DefaultSettings.defaultStatisticsRecentDaysCount;
-  int timelineDaysCount = DefaultSettings.defaultTimelineDaysCount;
-  int topArtistsDaysCount = DefaultSettings.defaultTopArtistsDaysCount;
-  int newArtistsCount = DefaultSettings.defaultNewArtistsCount;
-  int newTracksCount = DefaultSettings.defaultNewTracksCount;
+  int discoveriesDaysCount = DefaultGlobalSettings.defaultDiscoveriesDaysCount;
+  int distributionDaysCount = DefaultGlobalSettings.defaultDistributionDaysCount;
+  int statisticsRecentDaysCount = DefaultGlobalSettings.defaultStatisticsRecentDaysCount;
+  int timelineDaysCount = DefaultGlobalSettings.defaultTimelineDaysCount;
+  int topArtistsDaysCount = DefaultGlobalSettings.defaultTopArtistsDaysCount;
+  int newArtistsCount = DefaultGlobalSettings.defaultNewArtistsCount;
+  int newTracksCount = DefaultGlobalSettings.defaultNewTracksCount;
 
   List<bool> _selectedDiscoveriesDaysCount = [];
   List<bool> _selectedDistributionDaysCount = [];
@@ -37,7 +37,7 @@ class _SettingsFormState extends State<SettingsForm> {
 
   @override
   void didChangeDependencies() {
-    SettingsCubit settings = BlocProvider.of<SettingsCubit>(context);
+    GlobalSettingsCubit settings = BlocProvider.of<GlobalSettingsCubit>(context);
 
     usernameController.text = settings.getUsername();
     securityTokenController.text = settings.getSecurityToken();
@@ -50,23 +50,25 @@ class _SettingsFormState extends State<SettingsForm> {
     newArtistsCount = settings.getNewArtistsCount();
     newTracksCount = settings.getNewTracksCount();
 
-    _selectedDiscoveriesDaysCount = DefaultSettings.allowedDaysCountValues
+    _selectedDiscoveriesDaysCount = DefaultGlobalSettings.allowedDaysCountValues
         .map((e) => (e == discoveriesDaysCount))
         .toList();
-    _selectedDistributionDaysCount = DefaultSettings.allowedDaysCountValues
+    _selectedDistributionDaysCount = DefaultGlobalSettings.allowedDaysCountValues
         .map((e) => (e == distributionDaysCount))
         .toList();
-    _selectedStatisticsRecentDaysCount = DefaultSettings.allowedDaysCountValues
+    _selectedStatisticsRecentDaysCount = DefaultGlobalSettings.allowedDaysCountValues
         .map((e) => (e == statisticsRecentDaysCount))
         .toList();
-    _selectedTimelineDaysCount =
-        DefaultSettings.allowedDaysCountValues.map((e) => (e == timelineDaysCount)).toList();
-    _selectedTopArtistsDaysCount =
-        DefaultSettings.allowedDaysCountValues.map((e) => (e == topArtistsDaysCount)).toList();
+    _selectedTimelineDaysCount = DefaultGlobalSettings.allowedDaysCountValues
+        .map((e) => (e == timelineDaysCount))
+        .toList();
+    _selectedTopArtistsDaysCount = DefaultGlobalSettings.allowedDaysCountValues
+        .map((e) => (e == topArtistsDaysCount))
+        .toList();
     _selectedNewArtistsCount =
-        DefaultSettings.allowedCountValues.map((e) => (e == newArtistsCount)).toList();
+        DefaultGlobalSettings.allowedCountValues.map((e) => (e == newArtistsCount)).toList();
     _selectedNewTracksCount =
-        DefaultSettings.allowedCountValues.map((e) => (e == newTracksCount)).toList();
+        DefaultGlobalSettings.allowedCountValues.map((e) => (e == newTracksCount)).toList();
 
     super.didChangeDependencies();
   }
@@ -81,7 +83,7 @@ class _SettingsFormState extends State<SettingsForm> {
   @override
   Widget build(BuildContext context) {
     void saveSettings() {
-      BlocProvider.of<SettingsCubit>(context).setValues(
+      BlocProvider.of<GlobalSettingsCubit>(context).setValues(
         username: usernameController.text,
         securityToken: securityTokenController.text,
         discoveriesDaysCount: discoveriesDaysCount,
@@ -130,7 +132,7 @@ class _SettingsFormState extends State<SettingsForm> {
 
         const SizedBox(height: 16),
 
-        AppTitle2(text: tr('settings_title_global')),
+        AppTitle(text: tr('settings_title_global')),
 
         // Username
         const Text('settings_label_username').tr(),
@@ -153,7 +155,8 @@ class _SettingsFormState extends State<SettingsForm> {
         ),
 
         const SizedBox(height: 8),
-        AppTitle2(text: tr('settings_title_days_count')),
+
+        AppTitle(text: tr('settings_title_days_count')),
 
         // Statistics (recent)
         Row(
@@ -164,7 +167,8 @@ class _SettingsFormState extends State<SettingsForm> {
             ToggleButtons(
               onPressed: (int index) {
                 setState(() {
-                  statisticsRecentDaysCount = DefaultSettings.allowedDaysCountValues[index];
+                  statisticsRecentDaysCount =
+                      DefaultGlobalSettings.allowedDaysCountValues[index];
                   for (int i = 0; i < _selectedStatisticsRecentDaysCount.length; i++) {
                     _selectedStatisticsRecentDaysCount[i] = i == index;
                   }
@@ -174,7 +178,7 @@ class _SettingsFormState extends State<SettingsForm> {
               borderRadius: const BorderRadius.all(Radius.circular(8)),
               constraints: const BoxConstraints(minHeight: 30.0, minWidth: 30.0),
               isSelected: _selectedStatisticsRecentDaysCount,
-              children: DefaultSettings.allowedDaysCountValues
+              children: DefaultGlobalSettings.allowedDaysCountValues
                   .map((e) => Text(e.toString()))
                   .toList(),
             ),
@@ -190,7 +194,7 @@ class _SettingsFormState extends State<SettingsForm> {
             ToggleButtons(
               onPressed: (int index) {
                 setState(() {
-                  timelineDaysCount = DefaultSettings.allowedDaysCountValues[index];
+                  timelineDaysCount = DefaultGlobalSettings.allowedDaysCountValues[index];
                   for (int i = 0; i < _selectedTimelineDaysCount.length; i++) {
                     _selectedTimelineDaysCount[i] = i == index;
                   }
@@ -200,7 +204,7 @@ class _SettingsFormState extends State<SettingsForm> {
               borderRadius: const BorderRadius.all(Radius.circular(8)),
               constraints: const BoxConstraints(minHeight: 30.0, minWidth: 30.0),
               isSelected: _selectedTimelineDaysCount,
-              children: DefaultSettings.allowedDaysCountValues
+              children: DefaultGlobalSettings.allowedDaysCountValues
                   .map((e) => Text(e.toString()))
                   .toList(),
             ),
@@ -216,7 +220,7 @@ class _SettingsFormState extends State<SettingsForm> {
             ToggleButtons(
               onPressed: (int index) {
                 setState(() {
-                  topArtistsDaysCount = DefaultSettings.allowedDaysCountValues[index];
+                  topArtistsDaysCount = DefaultGlobalSettings.allowedDaysCountValues[index];
                   for (int i = 0; i < _selectedTopArtistsDaysCount.length; i++) {
                     _selectedTopArtistsDaysCount[i] = i == index;
                   }
@@ -226,7 +230,7 @@ class _SettingsFormState extends State<SettingsForm> {
               borderRadius: const BorderRadius.all(Radius.circular(8)),
               constraints: const BoxConstraints(minHeight: 30.0, minWidth: 30.0),
               isSelected: _selectedTopArtistsDaysCount,
-              children: DefaultSettings.allowedDaysCountValues
+              children: DefaultGlobalSettings.allowedDaysCountValues
                   .map((e) => Text(e.toString()))
                   .toList(),
             ),
@@ -242,7 +246,7 @@ class _SettingsFormState extends State<SettingsForm> {
             ToggleButtons(
               onPressed: (int index) {
                 setState(() {
-                  discoveriesDaysCount = DefaultSettings.allowedDaysCountValues[index];
+                  discoveriesDaysCount = DefaultGlobalSettings.allowedDaysCountValues[index];
                   for (int i = 0; i < _selectedDiscoveriesDaysCount.length; i++) {
                     _selectedDiscoveriesDaysCount[i] = i == index;
                   }
@@ -252,7 +256,7 @@ class _SettingsFormState extends State<SettingsForm> {
               borderRadius: const BorderRadius.all(Radius.circular(8)),
               constraints: const BoxConstraints(minHeight: 30.0, minWidth: 30.0),
               isSelected: _selectedDiscoveriesDaysCount,
-              children: DefaultSettings.allowedDaysCountValues
+              children: DefaultGlobalSettings.allowedDaysCountValues
                   .map((e) => Text(e.toString()))
                   .toList(),
             ),
@@ -268,7 +272,7 @@ class _SettingsFormState extends State<SettingsForm> {
             ToggleButtons(
               onPressed: (int index) {
                 setState(() {
-                  distributionDaysCount = DefaultSettings.allowedDaysCountValues[index];
+                  distributionDaysCount = DefaultGlobalSettings.allowedDaysCountValues[index];
                   for (int i = 0; i < _selectedDistributionDaysCount.length; i++) {
                     _selectedDistributionDaysCount[i] = i == index;
                   }
@@ -278,7 +282,7 @@ class _SettingsFormState extends State<SettingsForm> {
               borderRadius: const BorderRadius.all(Radius.circular(8)),
               constraints: const BoxConstraints(minHeight: 30.0, minWidth: 30.0),
               isSelected: _selectedDistributionDaysCount,
-              children: DefaultSettings.allowedDaysCountValues
+              children: DefaultGlobalSettings.allowedDaysCountValues
                   .map((e) => Text(e.toString()))
                   .toList(),
             ),
@@ -286,7 +290,7 @@ class _SettingsFormState extends State<SettingsForm> {
         ),
 
         const SizedBox(height: 8),
-        AppTitle2(text: tr('settings_title_counts')),
+        AppTitle(text: tr('settings_title_counts')),
 
         // New artists count
         Row(
@@ -297,7 +301,7 @@ class _SettingsFormState extends State<SettingsForm> {
             ToggleButtons(
               onPressed: (int index) {
                 setState(() {
-                  newArtistsCount = DefaultSettings.allowedCountValues[index];
+                  newArtistsCount = DefaultGlobalSettings.allowedCountValues[index];
                   for (int i = 0; i < _selectedNewArtistsCount.length; i++) {
                     _selectedNewArtistsCount[i] = i == index;
                   }
@@ -307,8 +311,9 @@ class _SettingsFormState extends State<SettingsForm> {
               borderRadius: const BorderRadius.all(Radius.circular(8)),
               constraints: const BoxConstraints(minHeight: 30.0, minWidth: 30.0),
               isSelected: _selectedNewArtistsCount,
-              children:
-                  DefaultSettings.allowedCountValues.map((e) => Text(e.toString())).toList(),
+              children: DefaultGlobalSettings.allowedCountValues
+                  .map((e) => Text(e.toString()))
+                  .toList(),
             ),
           ],
         ),
@@ -322,7 +327,7 @@ class _SettingsFormState extends State<SettingsForm> {
             ToggleButtons(
               onPressed: (int index) {
                 setState(() {
-                  newTracksCount = DefaultSettings.allowedCountValues[index];
+                  newTracksCount = DefaultGlobalSettings.allowedCountValues[index];
                   for (int i = 0; i < _selectedNewTracksCount.length; i++) {
                     _selectedNewTracksCount[i] = i == index;
                   }
@@ -332,8 +337,9 @@ class _SettingsFormState extends State<SettingsForm> {
               borderRadius: const BorderRadius.all(Radius.circular(8)),
               constraints: const BoxConstraints(minHeight: 30.0, minWidth: 30.0),
               isSelected: _selectedNewTracksCount,
-              children:
-                  DefaultSettings.allowedCountValues.map((e) => Text(e.toString())).toList(),
+              children: DefaultGlobalSettings.allowedCountValues
+                  .map((e) => Text(e.toString()))
+                  .toList(),
             ),
           ],
         ),
diff --git a/lib/ui/widgets/theme_card.dart b/lib/ui/settings/theme_card.dart
similarity index 100%
rename from lib/ui/widgets/theme_card.dart
rename to lib/ui/settings/theme_card.dart
diff --git a/lib/ui/skeleton.dart b/lib/ui/skeleton.dart
index 9a720a7..6d765f6 100644
--- a/lib/ui/skeleton.dart
+++ b/lib/ui/skeleton.dart
@@ -1,64 +1,34 @@
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:flutter_swipe/flutter_swipe.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/settings.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';
+import 'package:scrobbles/config/screen.dart';
+import 'package:scrobbles/cubit/nav_cubit_screens.dart';
+import 'package:scrobbles/ui/nav/global_app_bar.dart';
 
-class SkeletonScreen extends StatefulWidget {
+class SkeletonScreen extends StatelessWidget {
   const SkeletonScreen({super.key});
 
-  @override
-  State<SkeletonScreen> createState() => _SkeletonScreenState();
-}
-
-class _SkeletonScreenState extends State<SkeletonScreen> {
   @override
   Widget build(BuildContext context) {
-    List<Widget> pageNavigation = <Widget>[
-      ScreenHome(notifyParent: refresh),
-      ScreenDiscoveries(notifyParent: refresh),
-      ScreenStatistics(notifyParent: refresh),
-      const ScreenSettings(),
-    ];
-
-    return Scaffold(
-      appBar: StandardAppBar(notifyParent: refresh),
-      extendBodyBehindAppBar: false,
-      body: Swiper(
-        itemCount: BlocProvider.of<BottomNavCubit>(context).pagesCount,
-        itemBuilder: (BuildContext context, int index) {
-          return pageNavigation.elementAt(index);
-        },
-        pagination: SwiperPagination(
-          margin: const EdgeInsets.all(0),
-          builder: SwiperCustomPagination(
-            builder: (BuildContext context, SwiperPluginConfig config) {
-              return BottomNavBar(swipeController: config.controller);
-            },
+    return BlocBuilder<NavCubitScreen, int>(
+      builder: (BuildContext context, int screenIndex) {
+        return Scaffold(
+          appBar: const GlobalAppBar(),
+          extendBodyBehindAppBar: false,
+          body: Material(
+            color: Theme.of(context).colorScheme.surface,
+            child: Padding(
+              padding: const EdgeInsets.only(
+                top: 8,
+                left: 2,
+                right: 2,
+              ),
+              child: Screen.getWidget(screenIndex),
+            ),
           ),
-        ),
-        onIndexChanged: (newPageIndex) {
-          BlocProvider.of<BottomNavCubit>(context).updateIndex(newPageIndex);
-        },
-        outer: true,
-        loop: false,
-      ),
-      backgroundColor: Theme.of(context).colorScheme.background,
+          backgroundColor: Theme.of(context).colorScheme.surface,
+        );
+      },
     );
   }
-
-  refresh() {
-    void rebuild(Element el) {
-      el.markNeedsBuild();
-      el.visitChildren(rebuild);
-    }
-
-    (context as Element).visitChildren(rebuild);
-  }
 }
diff --git a/lib/ui/widgets/abstracts/custom_bar_chart.dart b/lib/ui/widgets/abstracts/custom_bar_chart.dart
index ebf9fe8..9b304a7 100644
--- a/lib/ui/widgets/abstracts/custom_bar_chart.dart
+++ b/lib/ui/widgets/abstracts/custom_bar_chart.dart
@@ -5,7 +5,9 @@ import 'package:scrobbles/ui/widgets/abstracts/custom_chart.dart';
 import 'package:scrobbles/utils/color_extensions.dart';
 
 class CustomBarChart extends CustomChart {
-  const CustomBarChart({super.key});
+  const CustomBarChart({
+    super.key,
+  });
 
   @override
   Widget build(BuildContext context) {
diff --git a/lib/ui/widgets/abstracts/custom_chart.dart b/lib/ui/widgets/abstracts/custom_chart.dart
index 6d22018..3d53fe1 100644
--- a/lib/ui/widgets/abstracts/custom_chart.dart
+++ b/lib/ui/widgets/abstracts/custom_chart.dart
@@ -7,10 +7,15 @@ import 'package:scrobbles/config/app_colors.dart';
 class CustomChart extends StatelessWidget {
   const CustomChart({super.key});
 
-  final double chartHeight = 150.0;
-  final double verticalTicksInterval = 10;
-  final String verticalAxisTitleSuffix = '';
-  final double titleFontSize = 9;
+  static const double defaultChartHeight = 150.0;
+  static const double defaultVerticalTicksInterval = 10;
+  static const String defaultVerticalAxisTitleSuffix = '';
+  static const double defaultTitleFontSize = 9;
+
+  double get chartHeight => defaultChartHeight;
+  double get verticalTicksInterval => defaultVerticalTicksInterval;
+  String get verticalAxisTitleSuffix => defaultVerticalAxisTitleSuffix;
+  double get titleFontSize => defaultTitleFontSize;
 
   @override
   Widget build(BuildContext context) {
diff --git a/lib/ui/widgets/abstracts/custom_line_chart.dart b/lib/ui/widgets/abstracts/custom_line_chart.dart
index 941b444..295af4f 100644
--- a/lib/ui/widgets/abstracts/custom_line_chart.dart
+++ b/lib/ui/widgets/abstracts/custom_line_chart.dart
@@ -4,7 +4,9 @@ import 'package:flutter/material.dart';
 import 'package:scrobbles/ui/widgets/abstracts/custom_chart.dart';
 
 class CustomLineChart extends CustomChart {
-  const CustomLineChart({super.key});
+  const CustomLineChart({
+    super.key,
+  });
 
   @override
   Widget build(BuildContext context) {
diff --git a/lib/ui/widgets/app_bar.dart b/lib/ui/widgets/app_bar.dart
deleted file mode 100644
index cef615a..0000000
--- a/lib/ui/widgets/app_bar.dart
+++ /dev/null
@@ -1,28 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:unicons/unicons.dart';
-
-import 'package:scrobbles/ui/widgets/app_titles.dart';
-
-class StandardAppBar extends StatelessWidget implements PreferredSizeWidget {
-  final Function() notifyParent;
-
-  const StandardAppBar({super.key, required this.notifyParent});
-
-  @override
-  Widget build(BuildContext context) {
-    return AppBar(
-      title: const AppTitle(text: 'app_name'),
-      actions: [
-        IconButton(
-          onPressed: () {
-            notifyParent();
-          },
-          icon: const Icon(UniconsSolid.refresh),
-        ),
-      ],
-    );
-  }
-
-  @override
-  Size get preferredSize => const Size.fromHeight(50);
-}
diff --git a/lib/ui/widgets/bottom_nav_bar.dart b/lib/ui/widgets/bottom_nav_bar.dart
deleted file mode 100644
index 8e00166..0000000
--- a/lib/ui/widgets/bottom_nav_bar.dart
+++ /dev/null
@@ -1,58 +0,0 @@
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:flutter_swipe/flutter_swipe.dart';
-import 'package:ionicons/ionicons.dart';
-
-import 'package:scrobbles/cubit/bottom_nav_cubit.dart';
-
-class BottomNavBar extends StatelessWidget {
-  const BottomNavBar({super.key, required this.swipeController});
-
-  final SwiperController swipeController;
-
-  @override
-  Widget build(BuildContext context) {
-    return Card(
-      margin: const EdgeInsets.all(0),
-      elevation: 4,
-      shadowColor: Theme.of(context).colorScheme.shadow,
-      color: Theme.of(context).colorScheme.surfaceVariant,
-      shape: const ContinuousRectangleBorder(),
-      child: BlocBuilder<BottomNavCubit, int>(
-        builder: (BuildContext context, int state) {
-          return BottomNavigationBar(
-            currentIndex: state,
-            onTap: (int index) {
-              context.read<BottomNavCubit>().updateIndex(index);
-              swipeController.move(index);
-            },
-            type: BottomNavigationBarType.fixed,
-            elevation: 0,
-            backgroundColor: Colors.transparent,
-            selectedItemColor: Theme.of(context).colorScheme.primary,
-            unselectedItemColor: Theme.of(context).textTheme.bodySmall!.color,
-            items: <BottomNavigationBarItem>[
-              BottomNavigationBarItem(
-                icon: const Icon(Ionicons.home_outline),
-                label: tr('bottom_nav_home'),
-              ),
-              BottomNavigationBarItem(
-                icon: const Icon(Ionicons.star_outline),
-                label: tr('bottom_nav_discoveries'),
-              ),
-              BottomNavigationBarItem(
-                icon: const Icon(Ionicons.bar_chart_outline),
-                label: tr('bottom_nav_repartition'),
-              ),
-              BottomNavigationBarItem(
-                icon: const Icon(Ionicons.settings_outline),
-                label: tr('bottom_nav_settings'),
-              ),
-            ],
-          );
-        },
-      ),
-    );
-  }
-}
diff --git a/lib/ui/widgets/card_content.dart b/lib/ui/widgets/card_content.dart
index 0d222ce..68587f7 100644
--- a/lib/ui/widgets/card_content.dart
+++ b/lib/ui/widgets/card_content.dart
@@ -1,18 +1,19 @@
 import 'package:flutter/material.dart';
-import 'package:scrobbles/ui/widgets/app_titles.dart';
+
+import 'package:scrobbles/ui/helpers/app_titles.dart';
 
 class CardContent extends StatelessWidget {
   const CardContent({
     super.key,
     required this.title,
     required this.color,
-    required this.loader,
+    required this.status,
     required this.content,
   });
 
   final String title;
   final Color color;
-  final Widget loader;
+  final String status;
   final Widget content;
 
   @override
@@ -36,8 +37,10 @@ class CardContent extends StatelessWidget {
               crossAxisAlignment: CrossAxisAlignment.start,
               mainAxisAlignment: MainAxisAlignment.start,
               children: [
-                AppTitle1(text: title),
-                loader,
+                AppTitle(text: title),
+                (status == 'loading')
+                    ? const Text('⏳')
+                    : ((status == 'failed') ? const Text('⚠️') : const Text('')),
               ],
             ),
             const SizedBox(height: 8),
diff --git a/lib/ui/widgets/cards/counts_by_day.dart b/lib/ui/widgets/cards/counts_by_day.dart
index 5ece893..917183e 100644
--- a/lib/ui/widgets/cards/counts_by_day.dart
+++ b/lib/ui/widgets/cards/counts_by_day.dart
@@ -3,20 +3,17 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 import 'package:scrobbles/cubit/data_counts_by_day_cubit.dart';
-import 'package:scrobbles/cubit/settings_cubit.dart';
-import 'package:scrobbles/models/counts_by_day.dart';
-import 'package:scrobbles/network/scrobbles.dart';
+import 'package:scrobbles/cubit/settings_global_cubit.dart';
+import 'package:scrobbles/models/data/counts_by_day.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) {
-    SettingsCubit settings = BlocProvider.of<SettingsCubit>(context);
-
+    GlobalSettingsCubit settings = BlocProvider.of<GlobalSettingsCubit>(context);
     final int daysCount = settings.getDistributionDaysCount();
 
     return BlocBuilder<DataCountsByDayCubit, DataCountsByDayState>(
@@ -30,34 +27,10 @@ class CardCountsByDay extends StatelessWidget {
               'daysCount': daysCount.toString(),
             },
           ),
-          loader: update(daysCount),
+          status: data.status,
           content: ChartCountsByDay(chartData: counts),
         );
       },
     );
   }
-
-  Widget update(int daysCount) {
-    const Widget loading = Text('⏳');
-    const Widget done = Text('');
-
-    late Future<CountsByDayData> future = ScrobblesApi.fetchCountsByDay(daysCount);
-
-    return BlocBuilder<DataCountsByDayCubit, DataCountsByDayState>(
-      builder: (BuildContext context, DataCountsByDayState data) {
-        return FutureBuilder<CountsByDayData>(
-          future: future,
-          builder: (context, snapshot) {
-            if (snapshot.hasError) {
-              return ShowErrorWidget(message: '${snapshot.error}');
-            }
-
-            BlocProvider.of<DataCountsByDayCubit>(context).update(snapshot.data);
-
-            return !snapshot.hasData ? loading : done;
-          },
-        );
-      },
-    );
-  }
 }
diff --git a/lib/ui/widgets/cards/counts_by_hour.dart b/lib/ui/widgets/cards/counts_by_hour.dart
index f45c053..6931246 100644
--- a/lib/ui/widgets/cards/counts_by_hour.dart
+++ b/lib/ui/widgets/cards/counts_by_hour.dart
@@ -3,20 +3,17 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 import 'package:scrobbles/cubit/data_counts_by_hour_cubit.dart';
-import 'package:scrobbles/cubit/settings_cubit.dart';
-import 'package:scrobbles/models/counts_by_hour.dart';
-import 'package:scrobbles/network/scrobbles.dart';
+import 'package:scrobbles/cubit/settings_global_cubit.dart';
+import 'package:scrobbles/models/data/counts_by_hour.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) {
-    SettingsCubit settings = BlocProvider.of<SettingsCubit>(context);
-
+    GlobalSettingsCubit settings = BlocProvider.of<GlobalSettingsCubit>(context);
     final int daysCount = settings.getDistributionDaysCount();
 
     return BlocBuilder<DataCountsByHourCubit, DataCountsByHourState>(
@@ -30,34 +27,10 @@ class CardCountsByHour extends StatelessWidget {
               'daysCount': daysCount.toString(),
             },
           ),
-          loader: update(daysCount),
+          status: data.status,
           content: ChartCountsByHour(chartData: counts),
         );
       },
     );
   }
-
-  Widget update(int daysCount) {
-    const Widget loading = Text('⏳');
-    const Widget done = Text('');
-
-    late Future<CountsByHourData> future = ScrobblesApi.fetchCountsByHour(daysCount);
-
-    return BlocBuilder<DataCountsByHourCubit, DataCountsByHourState>(
-      builder: (BuildContext context, DataCountsByHourState data) {
-        return FutureBuilder<CountsByHourData>(
-          future: future,
-          builder: (context, snapshot) {
-            if (snapshot.hasError) {
-              return ShowErrorWidget(message: '${snapshot.error}');
-            }
-
-            BlocProvider.of<DataCountsByHourCubit>(context).update(snapshot.data);
-
-            return !snapshot.hasData ? loading : done;
-          },
-        );
-      },
-    );
-  }
 }
diff --git a/lib/ui/widgets/cards/discoveries.dart b/lib/ui/widgets/cards/discoveries.dart
index 415a68f..c0ec485 100644
--- a/lib/ui/widgets/cards/discoveries.dart
+++ b/lib/ui/widgets/cards/discoveries.dart
@@ -3,21 +3,18 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 import 'package:scrobbles/cubit/data_discoveries_cubit.dart';
-import 'package:scrobbles/cubit/settings_cubit.dart';
-import 'package:scrobbles/models/discoveries.dart';
-import 'package:scrobbles/network/scrobbles.dart';
+import 'package:scrobbles/cubit/settings_global_cubit.dart';
+import 'package:scrobbles/models/data/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';
-import 'package:scrobbles/ui/widgets/error.dart';
 
 class CardDiscoveries extends StatelessWidget {
   const CardDiscoveries({super.key});
 
   @override
   Widget build(BuildContext context) {
-    SettingsCubit settings = BlocProvider.of<SettingsCubit>(context);
-
+    GlobalSettingsCubit settings = BlocProvider.of<GlobalSettingsCubit>(context);
     final int daysCount = settings.getDiscoveriesDaysCount();
 
     return BlocBuilder<DataDiscoveriesCubit, DataDiscoveriesState>(
@@ -33,7 +30,7 @@ class CardDiscoveries extends StatelessWidget {
               'daysCount': daysCount.toString(),
             },
           ),
-          loader: update(daysCount),
+          status: data.status,
           content: Column(
             mainAxisSize: MainAxisSize.min,
             crossAxisAlignment: CrossAxisAlignment.start,
@@ -57,28 +54,4 @@ class CardDiscoveries extends StatelessWidget {
       },
     );
   }
-
-  Widget update(int daysCount) {
-    const Widget loading = Text('⏳');
-    const Widget done = Text('');
-
-    late Future<DiscoveriesData> future = ScrobblesApi.fetchDiscoveries(daysCount);
-
-    return BlocBuilder<DataDiscoveriesCubit, DataDiscoveriesState>(
-      builder: (BuildContext context, DataDiscoveriesState data) {
-        return FutureBuilder<DiscoveriesData>(
-          future: future,
-          builder: (context, snapshot) {
-            if (snapshot.hasError) {
-              return ShowErrorWidget(message: '${snapshot.error}');
-            }
-
-            BlocProvider.of<DataDiscoveriesCubit>(context).update(snapshot.data);
-
-            return !snapshot.hasData ? loading : done;
-          },
-        );
-      },
-    );
-  }
 }
diff --git a/lib/ui/widgets/cards/heatmap.dart b/lib/ui/widgets/cards/heatmap.dart
index 11dc869..e89318b 100644
--- a/lib/ui/widgets/cards/heatmap.dart
+++ b/lib/ui/widgets/cards/heatmap.dart
@@ -3,20 +3,17 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 import 'package:scrobbles/cubit/data_heatmap_cubit.dart';
-import 'package:scrobbles/cubit/settings_cubit.dart';
-import 'package:scrobbles/models/heatmap.dart';
-import 'package:scrobbles/network/scrobbles.dart';
+import 'package:scrobbles/cubit/settings_global_cubit.dart';
+import 'package:scrobbles/models/data/heatmap.dart';
 import 'package:scrobbles/ui/widgets/card_content.dart';
 import 'package:scrobbles/ui/widgets/charts/heatmap.dart';
-import 'package:scrobbles/ui/widgets/error.dart';
 
 class CardHeatmap extends StatelessWidget {
   const CardHeatmap({super.key});
 
   @override
   Widget build(BuildContext context) {
-    SettingsCubit settings = BlocProvider.of<SettingsCubit>(context);
-
+    GlobalSettingsCubit settings = BlocProvider.of<GlobalSettingsCubit>(context);
     final int daysCount = settings.getDistributionDaysCount();
 
     return BlocBuilder<DataHeatmapCubit, DataHeatmapState>(
@@ -30,34 +27,10 @@ class CardHeatmap extends StatelessWidget {
               'daysCount': daysCount.toString(),
             },
           ),
-          loader: update(daysCount),
+          status: data.status,
           content: ChartHeatmap(chartData: heatmap),
         );
       },
     );
   }
-
-  Widget update(int daysCount) {
-    const Widget loading = Text('⏳');
-    const Widget done = Text('');
-
-    late Future<HeatmapData> future = ScrobblesApi.fetchHeatmap(daysCount);
-
-    return BlocBuilder<DataHeatmapCubit, DataHeatmapState>(
-      builder: (BuildContext context, DataHeatmapState data) {
-        return FutureBuilder<HeatmapData>(
-          future: future,
-          builder: (context, snapshot) {
-            if (snapshot.hasError) {
-              return ShowErrorWidget(message: '${snapshot.error}');
-            }
-
-            BlocProvider.of<DataHeatmapCubit>(context).update(snapshot.data);
-
-            return !snapshot.hasData ? loading : done;
-          },
-        );
-      },
-    );
-  }
 }
diff --git a/lib/ui/widgets/cards/new_artists.dart b/lib/ui/widgets/cards/new_artists.dart
index be377fe..6c93137 100644
--- a/lib/ui/widgets/cards/new_artists.dart
+++ b/lib/ui/widgets/cards/new_artists.dart
@@ -3,27 +3,19 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 import 'package:scrobbles/cubit/data_new_artists_cubit.dart';
-import 'package:scrobbles/cubit/settings_cubit.dart';
-import 'package:scrobbles/models/new_artists.dart';
-import 'package:scrobbles/network/scrobbles.dart';
 import 'package:scrobbles/ui/widgets/card_content.dart';
-import 'package:scrobbles/ui/widgets/error.dart';
 
 class CardNewArtists extends StatelessWidget {
   const CardNewArtists({super.key});
 
   @override
   Widget build(BuildContext context) {
-    SettingsCubit settings = BlocProvider.of<SettingsCubit>(context);
-
-    final int count = settings.getNewArtistsCount();
-
     return BlocBuilder<DataNewArtistsCubit, DataNewArtistsState>(
       builder: (BuildContext context, DataNewArtistsState data) {
         return CardContent(
           color: Theme.of(context).colorScheme.surface,
           title: 'new_artists_title'.tr(),
-          loader: update(count),
+          status: data.status,
           content: Column(
             mainAxisAlignment: MainAxisAlignment.start,
             crossAxisAlignment: CrossAxisAlignment.start,
@@ -36,28 +28,4 @@ class CardNewArtists extends StatelessWidget {
       },
     );
   }
-
-  Widget update(int count) {
-    const Widget loading = Text('⏳');
-    const Widget done = Text('');
-
-    late Future<NewArtistsData> future = ScrobblesApi.fetchNewArtists(count);
-
-    return BlocBuilder<DataNewArtistsCubit, DataNewArtistsState>(
-      builder: (BuildContext context, DataNewArtistsState data) {
-        return FutureBuilder<NewArtistsData>(
-          future: future,
-          builder: (context, snapshot) {
-            if (snapshot.hasError) {
-              return ShowErrorWidget(message: '${snapshot.error}');
-            }
-
-            BlocProvider.of<DataNewArtistsCubit>(context).update(snapshot.data);
-
-            return !snapshot.hasData ? loading : done;
-          },
-        );
-      },
-    );
-  }
 }
diff --git a/lib/ui/widgets/cards/new_tracks.dart b/lib/ui/widgets/cards/new_tracks.dart
index 4787dbf..28c89e6 100644
--- a/lib/ui/widgets/cards/new_tracks.dart
+++ b/lib/ui/widgets/cards/new_tracks.dart
@@ -3,32 +3,25 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 import 'package:scrobbles/cubit/data_new_tracks_cubit.dart';
-import 'package:scrobbles/cubit/settings_cubit.dart';
-import 'package:scrobbles/models/new_tracks.dart';
-import 'package:scrobbles/network/scrobbles.dart';
 import 'package:scrobbles/ui/widgets/card_content.dart';
-import 'package:scrobbles/ui/widgets/error.dart';
 
 class CardNewTracks extends StatelessWidget {
   const CardNewTracks({super.key});
 
   @override
   Widget build(BuildContext context) {
-    SettingsCubit settings = BlocProvider.of<SettingsCubit>(context);
-
-    final int count = settings.getNewTracksCount();
-
     return BlocBuilder<DataNewTracksCubit, DataNewTracksState>(
       builder: (BuildContext context, DataNewTracksState data) {
         return CardContent(
           color: Theme.of(context).colorScheme.surface,
           title: 'new_tracks_title'.tr(),
-          loader: update(count),
+          status: data.status,
           content: Column(
             mainAxisAlignment: MainAxisAlignment.start,
             crossAxisAlignment: CrossAxisAlignment.start,
             children: data.newTracks?.data
-                    .map((newTrack) => Text('${newTrack.track?.artist.name ?? ''} - ${newTrack.track?.name ?? ''}'))
+                    .map((newTrack) => Text(
+                        '${newTrack.track?.artist.name ?? ''} - ${newTrack.track?.name ?? ''}'))
                     .toList() ??
                 [],
           ),
@@ -36,28 +29,4 @@ class CardNewTracks extends StatelessWidget {
       },
     );
   }
-
-  Widget update(int count) {
-    const Widget loading = Text('⏳');
-    const Widget done = Text('');
-
-    late Future<NewTracksData> future = ScrobblesApi.fetchNewTracks(count);
-
-    return BlocBuilder<DataNewTracksCubit, DataNewTracksState>(
-      builder: (BuildContext context, DataNewTracksState data) {
-        return FutureBuilder<NewTracksData>(
-          future: future,
-          builder: (context, snapshot) {
-            if (snapshot.hasError) {
-              return ShowErrorWidget(message: '${snapshot.error}');
-            }
-
-            BlocProvider.of<DataNewTracksCubit>(context).update(snapshot.data);
-
-            return !snapshot.hasData ? loading : done;
-          },
-        );
-      },
-    );
-  }
 }
diff --git a/lib/ui/widgets/cards/statistics_global.dart b/lib/ui/widgets/cards/statistics_global.dart
index 6477d3d..0d920f7 100644
--- a/lib/ui/widgets/cards/statistics_global.dart
+++ b/lib/ui/widgets/cards/statistics_global.dart
@@ -3,11 +3,9 @@ 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/network/scrobbles.dart';
+import 'package:scrobbles/models/data/statistics_global.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});
@@ -22,34 +20,10 @@ class CardStatisticsGlobal extends StatelessWidget {
         return CardContent(
           color: Theme.of(context).colorScheme.primary,
           title: 'global_statistics'.tr(),
-          loader: update(),
+          status: data.status,
           content: ContentStatisticsGlobal(statistics: statistics),
         );
       },
     );
   }
-
-  Widget update() {
-    const Widget loading = Text('⏳');
-    const Widget done = Text('');
-
-    late Future<StatisticsGlobalData> future = ScrobblesApi.fetchGlobalStatistics();
-
-    return BlocBuilder<DataStatisticsGlobalCubit, DataStatisticsGlobalState>(
-      builder: (BuildContext context, DataStatisticsGlobalState data) {
-        return FutureBuilder<StatisticsGlobalData>(
-          future: future,
-          builder: (context, snapshot) {
-            if (snapshot.hasError) {
-              return ShowErrorWidget(message: '${snapshot.error}');
-            }
-
-            BlocProvider.of<DataStatisticsGlobalCubit>(context).update(snapshot.data);
-
-            return !snapshot.hasData ? loading : done;
-          },
-        );
-      },
-    );
-  }
 }
diff --git a/lib/ui/widgets/cards/statistics_recent.dart b/lib/ui/widgets/cards/statistics_recent.dart
index 1ef1aa2..c04bf23 100644
--- a/lib/ui/widgets/cards/statistics_recent.dart
+++ b/lib/ui/widgets/cards/statistics_recent.dart
@@ -3,20 +3,17 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 import 'package:scrobbles/cubit/data_statistics_recent_cubit.dart';
-import 'package:scrobbles/cubit/settings_cubit.dart';
-import 'package:scrobbles/models/statistics_recent.dart';
-import 'package:scrobbles/network/scrobbles.dart';
+import 'package:scrobbles/cubit/settings_global_cubit.dart';
+import 'package:scrobbles/models/data/statistics_recent.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) {
-    SettingsCubit settings = BlocProvider.of<SettingsCubit>(context);
-
+    GlobalSettingsCubit settings = BlocProvider.of<GlobalSettingsCubit>(context);
     final int daysCount = settings.getStatisticsRecentDaysCount();
 
     return BlocBuilder<DataStatisticsRecentCubit, DataStatisticsRecentState>(
@@ -31,34 +28,10 @@ class CardStatisticsRecent extends StatelessWidget {
               'daysCount': daysCount.toString(),
             },
           ),
-          loader: update(daysCount),
+          status: data.status,
           content: ContentStatisticsRecent(statistics: statistics),
         );
       },
     );
   }
-
-  Widget update(int daysCount) {
-    const Widget loading = Text('⏳');
-    const Widget done = Text('');
-
-    late Future<StatisticsRecentData> future = ScrobblesApi.fetchRecentStatistics(daysCount);
-
-    return BlocBuilder<DataStatisticsRecentCubit, DataStatisticsRecentState>(
-      builder: (BuildContext context, DataStatisticsRecentState data) {
-        return FutureBuilder<StatisticsRecentData>(
-          future: future,
-          builder: (context, snapshot) {
-            if (snapshot.hasError) {
-              return ShowErrorWidget(message: '${snapshot.error}');
-            }
-
-            BlocProvider.of<DataStatisticsRecentCubit>(context).update(snapshot.data);
-
-            return !snapshot.hasData ? loading : done;
-          },
-        );
-      },
-    );
-  }
 }
diff --git a/lib/ui/widgets/cards/timeline.dart b/lib/ui/widgets/cards/timeline.dart
index 8e551bf..9524a10 100644
--- a/lib/ui/widgets/cards/timeline.dart
+++ b/lib/ui/widgets/cards/timeline.dart
@@ -3,21 +3,18 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 import 'package:scrobbles/cubit/data_timeline_cubit.dart';
-import 'package:scrobbles/cubit/settings_cubit.dart';
-import 'package:scrobbles/models/timeline.dart';
-import 'package:scrobbles/network/scrobbles.dart';
+import 'package:scrobbles/cubit/settings_global_cubit.dart';
+import 'package:scrobbles/models/data/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';
-import 'package:scrobbles/ui/widgets/error.dart';
 
 class CardTimeline extends StatelessWidget {
   const CardTimeline({super.key});
 
   @override
   Widget build(BuildContext context) {
-    SettingsCubit settings = BlocProvider.of<SettingsCubit>(context);
-
+    GlobalSettingsCubit settings = BlocProvider.of<GlobalSettingsCubit>(context);
     final int daysCount = settings.getTimelineDaysCount();
 
     return BlocBuilder<DataTimelineCubit, DataTimelineState>(
@@ -31,7 +28,7 @@ class CardTimeline extends StatelessWidget {
               'daysCount': daysCount.toString(),
             },
           ),
-          loader: update(daysCount),
+          status: data.status,
           content: Stack(
             children: [
               ChartTimelineCounts(chartData: timeline),
@@ -42,28 +39,4 @@ class CardTimeline extends StatelessWidget {
       },
     );
   }
-
-  Widget update(int daysCount) {
-    const Widget loading = Text('⏳');
-    const Widget done = Text('');
-
-    late Future<TimelineData> future = ScrobblesApi.fetchTimeline(daysCount);
-
-    return BlocBuilder<DataTimelineCubit, DataTimelineState>(
-      builder: (BuildContext context, DataTimelineState data) {
-        return FutureBuilder<TimelineData>(
-          future: future,
-          builder: (context, snapshot) {
-            if (snapshot.hasError) {
-              return ShowErrorWidget(message: '${snapshot.error}');
-            }
-
-            BlocProvider.of<DataTimelineCubit>(context).update(snapshot.data);
-
-            return !snapshot.hasData ? loading : done;
-          },
-        );
-      },
-    );
-  }
 }
diff --git a/lib/ui/widgets/cards/top_artists.dart b/lib/ui/widgets/cards/top_artists.dart
index 2830bfa..20a50e9 100644
--- a/lib/ui/widgets/cards/top_artists.dart
+++ b/lib/ui/widgets/cards/top_artists.dart
@@ -3,21 +3,18 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 import 'package:scrobbles/cubit/data_top_artists_cubit.dart';
-import 'package:scrobbles/cubit/settings_cubit.dart';
-import 'package:scrobbles/models/topartists.dart';
-import 'package:scrobbles/network/scrobbles.dart';
+import 'package:scrobbles/cubit/settings_global_cubit.dart';
+import 'package:scrobbles/models/data/topartists.dart';
 import 'package:scrobbles/ui/widgets/card_content.dart';
 import 'package:scrobbles/ui/widgets/charts/top_artists.dart';
 import 'package:scrobbles/ui/widgets/charts/top_artists_stream.dart';
-import 'package:scrobbles/ui/widgets/error.dart';
 
 class CardTopArtists extends StatelessWidget {
   const CardTopArtists({super.key});
 
   @override
   Widget build(BuildContext context) {
-    SettingsCubit settings = BlocProvider.of<SettingsCubit>(context);
-
+    GlobalSettingsCubit settings = BlocProvider.of<GlobalSettingsCubit>(context);
     final int daysCount = settings.getTopArtistsDaysCount();
 
     return BlocBuilder<DataTopArtistsCubit, DataTopArtistsState>(
@@ -31,7 +28,7 @@ class CardTopArtists extends StatelessWidget {
               'daysCount': daysCount.toString(),
             },
           ),
-          loader: update(daysCount),
+          status: data.status,
           content: Column(
             mainAxisSize: MainAxisSize.min,
             crossAxisAlignment: CrossAxisAlignment.start,
@@ -45,28 +42,4 @@ class CardTopArtists extends StatelessWidget {
       },
     );
   }
-
-  Widget update(int daysCount) {
-    const Widget loading = Text('⏳');
-    const Widget done = Text('');
-
-    late Future<TopArtistsData> future = ScrobblesApi.fetchTopArtists(daysCount);
-
-    return BlocBuilder<DataTopArtistsCubit, DataTopArtistsState>(
-      builder: (BuildContext context, DataTopArtistsState update) {
-        return FutureBuilder<TopArtistsData>(
-          future: future,
-          builder: (context, snapshot) {
-            if (snapshot.hasError) {
-              return ShowErrorWidget(message: '${snapshot.error}');
-            }
-
-            BlocProvider.of<DataTopArtistsCubit>(context).update(snapshot.data);
-
-            return !snapshot.hasData ? loading : done;
-          },
-        );
-      },
-    );
-  }
 }
diff --git a/lib/ui/widgets/charts/counts_by_day.dart b/lib/ui/widgets/charts/counts_by_day.dart
index c43fc0d..8050469 100644
--- a/lib/ui/widgets/charts/counts_by_day.dart
+++ b/lib/ui/widgets/charts/counts_by_day.dart
@@ -1,7 +1,7 @@
 import 'package:flutter/material.dart';
 import 'package:fl_chart/fl_chart.dart';
 
-import 'package:scrobbles/models/counts_by_day.dart';
+import 'package:scrobbles/models/data/counts_by_day.dart';
 import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart';
 
 class ChartCountsByDay extends CustomBarChart {
@@ -10,9 +10,9 @@ class ChartCountsByDay extends CustomBarChart {
   final CountsByDayData chartData;
 
   @override
-  final double verticalTicksInterval = 5;
+  double get verticalTicksInterval => 5;
   @override
-  final String verticalAxisTitleSuffix = '%';
+  String get verticalAxisTitleSuffix => '%';
 
   @override
   Widget build(BuildContext context) {
@@ -31,7 +31,7 @@ class ChartCountsByDay extends CustomBarChart {
 
           return getBarChart(
             barWidth: getBarWidth(maxWidth, barsCount),
-            backgroundColor: Theme.of(context).colorScheme.background,
+            backgroundColor: Theme.of(context).colorScheme.surface,
           );
         },
       ),
diff --git a/lib/ui/widgets/charts/counts_by_hour.dart b/lib/ui/widgets/charts/counts_by_hour.dart
index 1a1c153..a4c575c 100644
--- a/lib/ui/widgets/charts/counts_by_hour.dart
+++ b/lib/ui/widgets/charts/counts_by_hour.dart
@@ -2,19 +2,22 @@ import 'package:flutter/material.dart';
 import 'package:fl_chart/fl_chart.dart';
 
 import 'package:scrobbles/config/app_colors.dart';
-import 'package:scrobbles/models/counts_by_hour.dart';
+import 'package:scrobbles/models/data/counts_by_hour.dart';
 import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart';
 import 'package:scrobbles/utils/color_extensions.dart';
 
 class ChartCountsByHour extends CustomBarChart {
-  const ChartCountsByHour({super.key, required this.chartData});
+  const ChartCountsByHour({
+    super.key,
+    required this.chartData,
+  });
 
   final CountsByHourData chartData;
 
   @override
-  final double verticalTicksInterval = 5;
+  double get verticalTicksInterval => 5;
   @override
-  final String verticalAxisTitleSuffix = '%';
+  String get verticalAxisTitleSuffix => '%';
 
   @override
   Widget build(BuildContext context) {
@@ -33,7 +36,7 @@ class ChartCountsByHour extends CustomBarChart {
 
           return getBarChart(
             barWidth: getBarWidth(maxWidth, barsCount),
-            backgroundColor: Theme.of(context).colorScheme.background,
+            backgroundColor: Theme.of(context).colorScheme.surface,
           );
         },
       ),
diff --git a/lib/ui/widgets/charts/discoveries_artists.dart b/lib/ui/widgets/charts/discoveries_artists.dart
index 9819665..75a4034 100644
--- a/lib/ui/widgets/charts/discoveries_artists.dart
+++ b/lib/ui/widgets/charts/discoveries_artists.dart
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
 import 'package:fl_chart/fl_chart.dart';
 
 import 'package:scrobbles/config/app_colors.dart';
-import 'package:scrobbles/models/discoveries.dart';
+import 'package:scrobbles/models/data/discoveries.dart';
 import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart';
 import 'package:scrobbles/utils/color_extensions.dart';
 
@@ -28,7 +28,7 @@ class ChartDiscoveriesArtists extends CustomBarChart {
 
           return getBarChart(
             barWidth: getBarWidth(maxWidth, barsCount),
-            backgroundColor: Theme.of(context).colorScheme.background,
+            backgroundColor: Theme.of(context).colorScheme.surface,
           );
         },
       ),
diff --git a/lib/ui/widgets/charts/discoveries_tracks.dart b/lib/ui/widgets/charts/discoveries_tracks.dart
index 2f4626d..eae77f8 100644
--- a/lib/ui/widgets/charts/discoveries_tracks.dart
+++ b/lib/ui/widgets/charts/discoveries_tracks.dart
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
 import 'package:fl_chart/fl_chart.dart';
 
 import 'package:scrobbles/config/app_colors.dart';
-import 'package:scrobbles/models/discoveries.dart';
+import 'package:scrobbles/models/data/discoveries.dart';
 import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart';
 import 'package:scrobbles/utils/color_extensions.dart';
 
@@ -28,7 +28,7 @@ class ChartDiscoveriesTracks extends CustomBarChart {
 
           return getBarChart(
             barWidth: getBarWidth(maxWidth, barsCount),
-            backgroundColor: Theme.of(context).colorScheme.background,
+            backgroundColor: Theme.of(context).colorScheme.surface,
           );
         },
       ),
diff --git a/lib/ui/widgets/charts/heatmap.dart b/lib/ui/widgets/charts/heatmap.dart
index 0625dbf..fcb3415 100644
--- a/lib/ui/widgets/charts/heatmap.dart
+++ b/lib/ui/widgets/charts/heatmap.dart
@@ -3,7 +3,7 @@ import 'package:fl_chart/fl_chart.dart';
 import 'package:flutter/material.dart';
 
 import 'package:scrobbles/config/app_colors.dart';
-import 'package:scrobbles/models/heatmap.dart';
+import 'package:scrobbles/models/data/heatmap.dart';
 import 'package:scrobbles/ui/widgets/abstracts/custom_chart.dart';
 
 class ChartHeatmap extends CustomChart {
@@ -12,9 +12,10 @@ class ChartHeatmap extends CustomChart {
   final HeatmapData chartData;
 
   @override
-  final double chartHeight = 150.0;
+  double get chartHeight => 150.0;
   @override
-  final double titleFontSize = 9;
+  double get titleFontSize => 9;
+
   final Color baseColor = AppColors.contentColorPink;
   final double scale = 8.0;
   final double darkenAmount = 50;
diff --git a/lib/ui/widgets/charts/timeline_counts.dart b/lib/ui/widgets/charts/timeline_counts.dart
index 4357774..eef0fd8 100644
--- a/lib/ui/widgets/charts/timeline_counts.dart
+++ b/lib/ui/widgets/charts/timeline_counts.dart
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
 import 'package:fl_chart/fl_chart.dart';
 
 import 'package:scrobbles/config/app_colors.dart';
-import 'package:scrobbles/models/timeline.dart';
+import 'package:scrobbles/models/data/timeline.dart';
 import 'package:scrobbles/ui/widgets/abstracts/custom_bar_chart.dart';
 
 class ChartTimelineCounts extends CustomBarChart {
@@ -11,7 +11,7 @@ class ChartTimelineCounts extends CustomBarChart {
   final TimelineData chartData;
 
   @override
-  final double verticalTicksInterval = 50;
+  double get verticalTicksInterval => 50;
 
   @override
   Widget build(BuildContext context) {
@@ -30,7 +30,7 @@ class ChartTimelineCounts extends CustomBarChart {
 
           return getBarChart(
             barWidth: getBarWidth(maxWidth, barsCount),
-            backgroundColor: Theme.of(context).colorScheme.background,
+            backgroundColor: Theme.of(context).colorScheme.surface,
           );
         },
       ),
diff --git a/lib/ui/widgets/charts/timeline_eclecticism.dart b/lib/ui/widgets/charts/timeline_eclecticism.dart
index 3eeffe7..bab15c0 100644
--- a/lib/ui/widgets/charts/timeline_eclecticism.dart
+++ b/lib/ui/widgets/charts/timeline_eclecticism.dart
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
 import 'package:fl_chart/fl_chart.dart';
 
 import 'package:scrobbles/config/app_colors.dart';
-import 'package:scrobbles/models/timeline.dart';
+import 'package:scrobbles/models/data/timeline.dart';
 import 'package:scrobbles/ui/widgets/abstracts/custom_line_chart.dart';
 import 'package:scrobbles/utils/color_extensions.dart';
 
@@ -12,7 +12,7 @@ class ChartTimelineEclecticism extends CustomLineChart {
   final TimelineData chartData;
 
   @override
-  final String verticalAxisTitleSuffix = '%';
+  String get verticalAxisTitleSuffix => '%';
 
   @override
   Widget build(BuildContext context) {
diff --git a/lib/ui/widgets/charts/top_artists.dart b/lib/ui/widgets/charts/top_artists.dart
index 79b39d3..f3a0181 100644
--- a/lib/ui/widgets/charts/top_artists.dart
+++ b/lib/ui/widgets/charts/top_artists.dart
@@ -1,7 +1,7 @@
 import 'package:flutter/material.dart';
 import 'package:fl_chart/fl_chart.dart';
 
-import 'package:scrobbles/models/topartists.dart';
+import 'package:scrobbles/models/data/topartists.dart';
 import 'package:scrobbles/ui/widgets/abstracts/custom_chart.dart';
 import 'package:scrobbles/utils/color_extensions.dart';
 
diff --git a/lib/ui/widgets/charts/top_artists_stream.dart b/lib/ui/widgets/charts/top_artists_stream.dart
index 523c24d..4f657e7 100644
--- a/lib/ui/widgets/charts/top_artists_stream.dart
+++ b/lib/ui/widgets/charts/top_artists_stream.dart
@@ -1,7 +1,7 @@
 import 'package:flutter/material.dart';
 import 'package:fl_chart/fl_chart.dart';
 
-import 'package:scrobbles/models/topartists.dart';
+import 'package:scrobbles/models/data/topartists.dart';
 import 'package:scrobbles/ui/widgets/abstracts/custom_line_chart.dart';
 import 'package:scrobbles/utils/color_extensions.dart';
 
@@ -11,9 +11,9 @@ class ChartTopArtistsStream extends CustomLineChart {
   final TopArtistsData chartData;
 
   @override
-  final double verticalTicksInterval = 10;
+  double get verticalTicksInterval => 10;
   @override
-  final String verticalAxisTitleSuffix = '%';
+  String get verticalAxisTitleSuffix => '%';
 
   @override
   Widget build(BuildContext context) {
diff --git a/lib/ui/widgets/content/statistics_global.dart b/lib/ui/widgets/content/statistics_global.dart
index 96a678a..5da9330 100644
--- a/lib/ui/widgets/content/statistics_global.dart
+++ b/lib/ui/widgets/content/statistics_global.dart
@@ -1,7 +1,7 @@
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 
-import 'package:scrobbles/models/statistics_global.dart';
+import 'package:scrobbles/models/data/statistics_global.dart';
 
 class ContentStatisticsGlobal extends StatelessWidget {
   const ContentStatisticsGlobal({super.key, required this.statistics});
diff --git a/lib/ui/widgets/content/statistics_recent.dart b/lib/ui/widgets/content/statistics_recent.dart
index 678623a..6c9ba86 100644
--- a/lib/ui/widgets/content/statistics_recent.dart
+++ b/lib/ui/widgets/content/statistics_recent.dart
@@ -1,7 +1,7 @@
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 
-import 'package:scrobbles/models/statistics_recent.dart';
+import 'package:scrobbles/models/data/statistics_recent.dart';
 
 class ContentStatisticsRecent extends StatelessWidget {
   const ContentStatisticsRecent({super.key, required this.statistics});
diff --git a/lib/ui/widgets/error.dart b/lib/ui/widgets/error.dart
deleted file mode 100644
index aa3d5cd..0000000
--- a/lib/ui/widgets/error.dart
+++ /dev/null
@@ -1,21 +0,0 @@
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-
-import 'package:scrobbles/utils/tools.dart';
-
-class ShowErrorWidget extends StatelessWidget {
-  const ShowErrorWidget({super.key, required this.message});
-
-  final String message;
-
-  @override
-  Widget build(BuildContext context) {
-    printlog(message);
-
-    return Text(
-      '⚠️ ${tr(message)}',
-      textAlign: TextAlign.start,
-      style: const TextStyle(color: Colors.red),
-    );
-  }
-}
diff --git a/pubspec.lock b/pubspec.lock
index ce3fe9e..08aca78 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -5,10 +5,10 @@ packages:
     dependency: transitive
     description:
       name: args
-      sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
+      sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
       url: "https://pub.dev"
     source: hosted
-    version: "2.4.2"
+    version: "2.5.0"
   async:
     dependency: transitive
     description:
@@ -21,10 +21,10 @@ packages:
     dependency: transitive
     description:
       name: bloc
-      sha256: f53a110e3b48dcd78136c10daa5d51512443cea5e1348c9d80a320095fa2db9e
+      sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"
       url: "https://pub.dev"
     source: hosted
-    version: "8.1.3"
+    version: "8.1.4"
   characters:
     dependency: transitive
     description:
@@ -61,10 +61,10 @@ packages:
     dependency: "direct main"
     description:
       name: easy_localization
-      sha256: c145aeb6584aedc7c862ab8c737c3277788f47488bfdf9bae0fe112bd0a4789c
+      sha256: fa59bcdbbb911a764aa6acf96bbb6fa7a5cf8234354fc45ec1a43a0349ef0201
       url: "https://pub.dev"
     source: hosted
-    version: "3.0.5"
+    version: "3.0.7"
   easy_logger:
     dependency: transitive
     description:
@@ -101,10 +101,10 @@ packages:
     dependency: "direct main"
     description:
       name: fl_chart
-      sha256: "00b74ae680df6b1135bdbea00a7d1fc072a9180b7c3f3702e4b19a9943f5ed7d"
+      sha256: d0f0d49112f2f4b192481c16d05b6418bd7820e021e265a3c22db98acf7ed7fb
       url: "https://pub.dev"
     source: hosted
-    version: "0.66.2"
+    version: "0.68.0"
   flutter:
     dependency: "direct main"
     description: flutter
@@ -114,18 +114,18 @@ packages:
     dependency: "direct main"
     description:
       name: flutter_bloc
-      sha256: "87325da1ac757fcc4813e6b34ed5dd61169973871fdf181d6c2109dd6935ece1"
+      sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a
       url: "https://pub.dev"
     source: hosted
-    version: "8.1.4"
+    version: "8.1.6"
   flutter_lints:
     dependency: "direct dev"
     description:
       name: flutter_lints
-      sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
+      sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c"
       url: "https://pub.dev"
     source: hosted
-    version: "3.0.1"
+    version: "4.0.0"
   flutter_localizations:
     dependency: transitive
     description: flutter
@@ -172,18 +172,18 @@ packages:
     dependency: "direct main"
     description:
       name: hydrated_bloc
-      sha256: "00a2099680162e74b5a836b8a7f446e478520a9cae9f6032e028ad8129f4432d"
+      sha256: af35b357739fe41728df10bec03aad422cdc725a1e702e03af9d2a41ea05160c
       url: "https://pub.dev"
     source: hosted
-    version: "9.1.4"
+    version: "9.1.5"
   intl:
     dependency: transitive
     description:
       name: intl
-      sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
+      sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
       url: "https://pub.dev"
     source: hosted
-    version: "0.18.1"
+    version: "0.19.0"
   ionicons:
     dependency: "direct main"
     description:
@@ -196,10 +196,10 @@ packages:
     dependency: transitive
     description:
       name: lints
-      sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
+      sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
       url: "https://pub.dev"
     source: hosted
-    version: "3.0.0"
+    version: "4.0.0"
   material_color_utilities:
     dependency: transitive
     description:
@@ -212,10 +212,10 @@ packages:
     dependency: transitive
     description:
       name: meta
-      sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
+      sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
       url: "https://pub.dev"
     source: hosted
-    version: "1.11.0"
+    version: "1.12.0"
   nested:
     dependency: transitive
     description:
@@ -224,6 +224,22 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.0.0"
+  package_info_plus:
+    dependency: "direct main"
+    description:
+      name: package_info_plus
+      sha256: b93d8b4d624b4ea19b0a5a208b2d6eff06004bc3ce74c06040b120eeadd00ce0
+      url: "https://pub.dev"
+    source: hosted
+    version: "8.0.0"
+  package_info_plus_platform_interface:
+    dependency: transitive
+    description:
+      name: package_info_plus_platform_interface
+      sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.0.0"
   path:
     dependency: transitive
     description:
@@ -236,26 +252,26 @@ packages:
     dependency: "direct main"
     description:
       name: path_provider
-      sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
+      sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
       url: "https://pub.dev"
     source: hosted
-    version: "2.1.2"
+    version: "2.1.3"
   path_provider_android:
     dependency: transitive
     description:
       name: path_provider_android
-      sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
+      sha256: bca87b0165ffd7cdb9cad8edd22d18d2201e886d9a9f19b4fb3452ea7df3a72a
       url: "https://pub.dev"
     source: hosted
-    version: "2.2.2"
+    version: "2.2.6"
   path_provider_foundation:
     dependency: transitive
     description:
       name: path_provider_foundation
-      sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
+      sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
       url: "https://pub.dev"
     source: hosted
-    version: "2.3.2"
+    version: "2.4.0"
   path_provider_linux:
     dependency: transitive
     description:
@@ -284,10 +300,10 @@ packages:
     dependency: transitive
     description:
       name: platform
-      sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
+      sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
       url: "https://pub.dev"
     source: hosted
-    version: "3.1.4"
+    version: "3.1.5"
   plugin_platform_interface:
     dependency: transitive
     description:
@@ -308,26 +324,26 @@ packages:
     dependency: transitive
     description:
       name: shared_preferences
-      sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
+      sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
       url: "https://pub.dev"
     source: hosted
-    version: "2.2.2"
+    version: "2.2.3"
   shared_preferences_android:
     dependency: transitive
     description:
       name: shared_preferences_android
-      sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
+      sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577"
       url: "https://pub.dev"
     source: hosted
-    version: "2.2.1"
+    version: "2.2.3"
   shared_preferences_foundation:
     dependency: transitive
     description:
       name: shared_preferences_foundation
-      sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
+      sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7"
       url: "https://pub.dev"
     source: hosted
-    version: "2.3.5"
+    version: "2.4.0"
   shared_preferences_linux:
     dependency: transitive
     description:
@@ -409,10 +425,10 @@ packages:
     dependency: "direct main"
     description:
       name: unicons
-      sha256: dbfcf93ff4d4ea19b324113857e358e4882115ab85db04417a4ba1c72b17a670
+      sha256: "1cca7462df18ff191b7e41b52f747d08854916531d1d7ab7cec0552095995206"
       url: "https://pub.dev"
     source: hosted
-    version: "2.1.1"
+    version: "2.1.2"
   vector_math:
     dependency: transitive
     description:
@@ -425,18 +441,18 @@ packages:
     dependency: transitive
     description:
       name: web
-      sha256: "1d9158c616048c38f712a6646e317a3426da10e884447626167240d45209cbad"
+      sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
       url: "https://pub.dev"
     source: hosted
-    version: "0.5.0"
+    version: "0.5.1"
   win32:
     dependency: transitive
     description:
       name: win32
-      sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8"
+      sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
       url: "https://pub.dev"
     source: hosted
-    version: "5.2.0"
+    version: "5.5.1"
   xdg_directories:
     dependency: transitive
     description:
@@ -446,5 +462,5 @@ packages:
     source: hosted
     version: "1.0.4"
 sdks:
-  dart: ">=3.3.0 <4.0.0"
-  flutter: ">=3.19.0"
+  dart: ">=3.4.0 <4.0.0"
+  flutter: ">=3.22.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index 8f5f50b..63996e0 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,34 +1,38 @@
 name: scrobbles
 description: Display scrobbles data and charts
 
-publish_to: 'none'
+publish_to: "none"
 
-version: 0.0.57+57
+version: 0.1.0+58
 
 environment:
-  sdk: '^3.0.0'
+  sdk: "^3.0.0"
 
 dependencies:
   flutter:
     sdk: flutter
 
+  # base
   easy_localization: ^3.0.1
   equatable: ^2.0.5
-  fl_chart: ^0.66.0
   flutter_bloc: ^8.1.1
   hive: ^2.2.3
-  http: ^1.1.0
-  path_provider: ^2.0.11
   hydrated_bloc: ^9.0.0
-  ionicons: ^0.2.2
+  package_info_plus: ^8.0.0
+  path_provider: ^2.0.11
   unicons: ^2.1.1
+
+  # specific
+  fl_chart: ^0.68.0
   flutter_swipe: ^1.0.1
+  http: ^1.1.0
+  ionicons: ^0.2.2
 
 dev_dependencies:
-  flutter_lints: ^3.0.1
+  flutter_lints: ^4.0.0
 
 flutter:
-  uses-material-design: false
+  uses-material-design: true
   assets:
     - assets/translations/
 
@@ -43,3 +47,4 @@ flutter:
           weight: 400
         - asset: assets/fonts/Nunito-Light.ttf
           weight: 300
+
diff --git a/icons/build_repository_icons.sh b/resources/app/build_application_resources.sh
similarity index 98%
rename from icons/build_repository_icons.sh
rename to resources/app/build_application_resources.sh
index 27dbe26..6d67b8f 100755
--- a/icons/build_repository_icons.sh
+++ b/resources/app/build_application_resources.sh
@@ -6,7 +6,7 @@ command -v scour >/dev/null 2>&1 || { echo >&2 "I require scour but it's not ins
 command -v optipng >/dev/null 2>&1 || { echo >&2 "I require optipng but it's not installed. Aborting."; exit 1; }
 
 CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
-BASE_DIR="$(dirname "${CURRENT_DIR}")"
+BASE_DIR="$(dirname "$(dirname "${CURRENT_DIR}")")"
 
 SOURCE_ICON="${CURRENT_DIR}/icon.svg"
 SOURCE_FASTLANE="${CURRENT_DIR}/featureGraphic.svg"
diff --git a/icons/featureGraphic.svg b/resources/app/featureGraphic.svg
similarity index 100%
rename from icons/featureGraphic.svg
rename to resources/app/featureGraphic.svg
diff --git a/icons/icon.svg b/resources/app/icon.svg
similarity index 100%
rename from icons/icon.svg
rename to resources/app/icon.svg
diff --git a/resources/build_resources.sh b/resources/build_resources.sh
new file mode 100755
index 0000000..8d5848a
--- /dev/null
+++ b/resources/build_resources.sh
@@ -0,0 +1,6 @@
+#! /bin/bash
+
+CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
+
+${CURRENT_DIR}/app/build_application_resources.sh
+
-- 
GitLab