diff --git a/android/app/build.gradle b/android/app/build.gradle
index a431a29d1d1e096dcfe8af84a3abb908afc63acf..ab94378c9b60299979ba3ac505d757c864f9e816 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 547a7f87984e869bc68c9f0a1c8e94cde2dcad43..7dd3e3281588d71aa3b798861776ac6c37f59a4e 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 51578cf240c0ab7a404cca695f530d3cdc9e5937..602be62c1bfb62b56b9bd131a8f8356cff5ee66c 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 86c15ba62ae62218c73dea90988fb32c4e70cdab..2d04be69c0f5cda9dca23a82339a8fc6a80bf3a2 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 0000000000000000000000000000000000000000..060b202b87dcd33388eb8a9d1a45cbd276fc8ba3
--- /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 0000000000000000000000000000000000000000..3cab228cbe7443782c4b187cbe805e4d0ba8e71a
--- /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 0000000000000000000000000000000000000000..b58d29592d6890feae59b01b48e977be20bdd758
--- /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 525784f01d4966e604ee67e5446e81fe5c778472..0565d15a1afbf20693e921b52dab4327e96c70ca 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 0000000000000000000000000000000000000000..2846969b1474a42a8c49892c528d2402134ee880
--- /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 be390348c7868e7c63387df13e13c46de43f8a23..138460e58f89bc93afb0899d6cfe8f8e662a466f 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 0000000000000000000000000000000000000000..55bfee6057c3d5261e493a34d1aab22cf39255e9
--- /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 0000000000000000000000000000000000000000..887b45e4255fd7de1cc7744569d82a38a66602f2
--- /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 1592a438fa2446d66db1a35519bd675d7f2dd146..0000000000000000000000000000000000000000
--- 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 ab40a21ce816fce4154bfb8b4ace9dc56cfbeff8..c2746cfc39f42e466b5fe97170362980ff271e33 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 724bd73411f6c5f22caeab39a101ab92af376713..13b78830c6f3cd59098e1cd412270945e9343a8c 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 83cd2d3b5b1e224578e295ca5ec94319364f4690..e4c477092a48cc0862e629728d7fa44dd39fac0c 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 3fc9aef713e632d27dcb8b002f314cbd62ba578c..c68949ea6261996367d489697d48b537a18b2e51 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 dabda77cf13f82954889ad4f5de186d5bd96de09..e3acd5392647f218fc5cbcc2eb1f5dcfdd671cdd 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 1d43883563ef73f81d0ce636c6977b5916d837e8..8bfa33955dfa293bb837329741f18917a49e9dcd 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 7cd9192e8d0842116c4cde4d124b3d7e87c4318c..dc7e4648d8cf5143d516caae53a96f106f8f0178 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 0814a3bd507987f5a7bd0c5a3178a9c767ed4bc2..a8d21e3c21ccb0c4567d41e2d5665f0e2c180107 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 6ef782b5dfa4b68124f4627951df2b82d6feae44..c858321c19daae38884112a9be58ae54af80b9ea 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 eb6678c624e66563b534b788f959532d545ecd00..8a9fe7ab023eb5c4412bf6ae1fcc8bfea8aa0ead 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 10923886b3f7ad976cf67a353e9855326c42bf6c..d1c1e76925fb3ba836baa9e79e9455d105dc5240 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 c23abe47f1c136b5a300957f6a2abdef76fa6b3b..d63798494e1c89ecebecffc6ae29f41e6cd50d3a 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 49e9292f1415184cef543bedb6d2dda1fa1314b1..e441c4ee26f05a23a743618033ce529e8b60a209 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 89ef0fd2a136d43ad291d44fc5899296ae1bd9e8..6d7041877c257929949bface50fe07b698969f3b 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 da24b1358002067a457e79ee7d4df48a5e0e634c..c950f64b046f57e19295f5d33925c3280c12f93d 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 1bf9d2f846d7159d1e26151420beac984d7b69e2..056cc9abe6c92fff4bba48e7f8f2ceff1f191539 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 692dfcbdec2bf4db214d78eba3c0686158d7cc1e..c82a0d686683be37292ff7c133682fcc3e6935cb 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 b0531c799f3d0ec00ba4e0f1532830ba513f98c9..c3065dc7559587325321b21cc188ef978f235eaa 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 98fe17a6bee3a4e5a32887fc84f885011dc786c2..c8850eaee028a35feded2e965af7aaa404d865b3 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 e6d14c01e33aa04f23dcb9bb0bb89342f919767a..79d95c06d13addbe23604d1c0c29e6b9843efd63 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 0000000000000000000000000000000000000000..4caca488df3fdd01091e7c7e967a5152010f2b33
--- /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 0000000000000000000000000000000000000000..43a941f44d1e53e12d533ac957a5d2eb1efe1b78
--- /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 6c86444f3d23ddd6bf3e12a466b36f496dcc26aa..f45d5dad21b87d8b6fda9fff4a90ac66653158ee 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 45ef6bb4eb11e57efbc584c77f3a311fe9090a7e..fb08d54167dba5f5a3c12f09cb0079998eafb49a 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 b9d50e8af9617478cc7565a586559022565d21a4..a69592e7e23bde7d58e1124c2d08e6e8f807f55c 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 0000000000000000000000000000000000000000..1b890ee1d9be844ac0a28749818edc072b75e7b2
--- /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 fffafc628d11ab099f053db462d74d6590adbc73..6d1e845a2d83fc2e022647f174f3492c4d6d1757 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 b1105effacdb46a2b19381bf4eda68bd6e67578a..9c39e5820f3c0db283af41f9d31ce8ff1c7511b9 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 39e3d6a8d9994df8c7a4d7f342068a2fea4e4358..2cb6f2a8f49eb69c2a83933d0d49e986be84d15e 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 3592365eaade76d0777bf7b2d6631ee582a44399..8c341ae13e0a53a9642843013ce6afea58e1b39b 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 93541243613b0f20e76485520a275d1527a6fcc9..b98107b12fabc3114ebfbec994166b588abcf1ad 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 0000000000000000000000000000000000000000..1cdade62858ef454bfc38b81f15362c677f91d26
--- /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 0000000000000000000000000000000000000000..f7cb5e5405a06f7c3f900851c70d145c3d2e4b52
--- /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 0000000000000000000000000000000000000000..ddbbe96f22ad7d434b673538864c6dfb845c1206
--- /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 0000000000000000000000000000000000000000..f7d4d8bc51ec518cd1af01d39def66ceec82f0a7
--- /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 0000000000000000000000000000000000000000..b0448b62e607d806309c1ed0fecda6a052826019
--- /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 0000000000000000000000000000000000000000..1685d05a94bab039925445ca3b1005c11ed49ea4
--- /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 0000000000000000000000000000000000000000..7ebabc1eb9cbabe088c83e8a77979cb69465be8d
--- /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 550307b8f3a37ca702a034cc49e05b628d936ab5..0000000000000000000000000000000000000000
--- 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 e943ff8ebbefe4f26626a728c1124f6b9e780f63..0000000000000000000000000000000000000000
--- 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 736f3a5fe35aaf7ec45686b01805000e21405f37..856ad882a681a2d8d765165b8f57d45a53a07528 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 7e35f1ec9905c382d0ed789cbd42217d692f7b85..0000000000000000000000000000000000000000
--- 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 6e5401de8f55105e2e99dd2eb03fb4dc4de4f376..f64158547afe53aef126a63f66f758452244fd8c 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 9a720a7b94ce6d50148e8c9af0049e5f11019eee..6d765f6dfb85f3817bc99acffe99d256cf54f020 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 ebf9fe83969c21ee4d67cf1682f64d6bae1e6abd..9b304a7f29ba6f491cb2672076b4f81bb9d1a3f3 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 6d220180bbf652b233bdc2d3c7873b97cebc2929..3d53fe1d0bccd06d0096615e19e7b0bb813a657a 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 941b44472281cd6661cc6758cf32cac039a8c939..295af4fec2807bcc0debe0a18e5eabecb31c675f 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 cef615a27e52bce077d0d02ce5a0d00ce0692252..0000000000000000000000000000000000000000
--- 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 8e00166e3dbd4711001fdae2f4c528180b095649..0000000000000000000000000000000000000000
--- 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 0d222cebd72e957a122e9d4012ccd21be8dbb52a..68587f70f622afc42aeb9908cb23c20f61b70137 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 5ece893b43f5d5bff5a8ca7f405fad109f7d2956..917183ed62d13355e989b2a871fe88975d45e3ac 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 f45c0537372b058120831f0c25653663859f8279..6931246700a8d53a8cc54af1808e0bf7c87769dd 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 415a68f11330741d0303d87ec56dcfb22fdeded1..c0ec4852a5fe77caacc764f57365be2aa4e35bf4 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 11dc86931ad7c84156c63aa5b0763de317ee8700..e89318bc6f826cd36c1a1edf698c5b62a0890a86 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 be377fe4ed84e4af9daf5c66140e177e5d2eab60..6c931373494bb84c74f63883dbd1d5305487fac2 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 4787dbfe4ca6ea8f8f46d2973da438df5c4207e4..28c89e66d96344bf0535f6d6ee13310764a52fc7 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 6477d3d9917ededd8b0e1a49178a9609b039e63e..0d920f740d2392bf955b761ef9efc25307c6b08d 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 1ef1aa20f3e327459e8fd011562d7ac823a852c9..c04bf23aab0e3daf380f4475989af03b87b9dc51 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 8e551bf0544f4edba8e8f7c0d3e424cdbe40e73c..9524a10210256006fe960ff6f2e01f47b7fb14b0 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 2830bfa8c3c4903f3d12a34c87866aa9de241c3b..20a50e9ca6425789e56022385f8106f7d7b2cb57 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 c43fc0d71fe4a0f26cd9337445e7d24cba3fa172..805046929cdd77f4ec293d31f15b3594dbbf7525 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 1a1c1539289a06e2f98f355924a2ffc80adcbc7f..a4c575cace8d78691f980751d02a3da22d779a03 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 98196656e55717b1bda9e8733aa5bce7a2398c39..75a4034b3da2fa4ce8cf9f43a81d71191c5a4616 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 2f4626dcdad2ad8c0bfcf8d7c0364595a5b38c56..eae77f8946e09982e599288d75750a83f57c29c5 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 0625dbfb332f795d310b16bf10c4e50f7bf2f11a..fcb34155ff87c3d4145ed691c938c5285ca76bba 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 4357774255a3d9f29c2242b5717e92de4cdddc14..eef0fd8189271bd10528751c689b059c9fc00fbe 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 3eeffe7b460368c5ada894a44d7e6c3ee397a4bd..bab15c0dd5dbf42fb591db199e6cd63538366376 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 79b39d317d86516367915ba2e5ba6b270403a337..f3a0181ad6e3000955f225e976fd154ffa0ec7c1 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 523c24dd3e2d33514d5dedfdd291f458f8220392..4f657e70d0763cefbc42dd7dbb0a523b57773275 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 96a678aa156b34839be6229fb79cffd458655ab4..5da9330783cfaabd2962120b19ded32391b685ce 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 678623a0f9ca98ee8eb2eb3d1c7eb6a8b06fb052..6c9ba8618333081b5530677bb5996a27a8edb694 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 aa3d5cd267b3a9ab2903518f8f789bf1b23a2cd6..0000000000000000000000000000000000000000
--- 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 ce3fe9ef11c8a15f00862a7070d897266cfe279c..08aca7896ceb6709dbb6434b464b96237e439c8f 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 8f5f50bd7f3e274c2c874c6042f2a707591e701a..63996e00d6828c22c05b4ba5c71d3496f0ab2eb4 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 27dbe2647fe4e6d562fbd99451716d1b7d448570..6d67b8f4f9eca701d1aed7331ef41dfb0bd44f20 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 0000000000000000000000000000000000000000..8d5848a985ed1068f6e6226379a9cedb69c3fa15
--- /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
+