diff --git a/assets/ui/main.blp b/assets/ui/main.blp
index 42944aa..81c087d 100644
--- a/assets/ui/main.blp
+++ b/assets/ui/main.blp
@@ -7,9 +7,7 @@ Adw.ApplicationWindow window {
 
     styles ["devel"]
 
-    content: Gtk.Box {
-        orientation: vertical;
-
+    content: Adw.ToastOverlay toast_overlay {
         Adw.Leaflet leaflet {
             can-navigate-back: true;
             can-unfold: false;
@@ -40,7 +38,7 @@ Adw.ApplicationWindow window {
                         }
                     }
 
-                    Adw.PreferencesGroup {
+                    Adw.PreferencesGroup launch_game_group {
                         vexpand: true;
                         valign: center;
 
@@ -69,9 +67,32 @@ Adw.ApplicationWindow window {
                             }
                         }
                     }
-                }
 
-                Adw.ToastOverlay toast_overlay {}
+                    Adw.PreferencesGroup progress_bar_group {
+                        vexpand: true;
+                        valign: center;
+                        visible: false;
+
+                        Gtk.Box {
+                            halign: center;
+                            margin-top: 64;
+                            spacing: 20;
+
+                            Gtk.ProgressBar progress_bar {
+                                text: "Downloading: 37% (3.7 of 10 GB)\n";
+                                show-text: true;
+
+                                width-request: 260;
+                                fraction: 0.37;
+                                valign: center;
+                            }
+
+                            Gtk.Button {
+                                label: "Pause";
+                            }
+                        }
+                    }
+                }
             }
         }
     };
diff --git a/assets/ui/preferences.blp b/assets/ui/preferences.blp
index a19cec8..133bda8 100644
--- a/assets/ui/preferences.blp
+++ b/assets/ui/preferences.blp
@@ -15,8 +15,16 @@ Gtk.Box preferences {
         }
     }
 
-    Adw.Flap {
+    Adw.StatusPage status_page {
+        icon-name: "image-loading-symbolic";
+        title: "Loading data";
+
         vexpand: true;
+    }
+
+    Adw.Flap flap {
+        vexpand: true;
+        visible: false;
         
         flap: Gtk.StackSidebar {
             width-request: 200;
diff --git a/src/main.rs b/src/main.rs
index b37d182..28857c2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,7 +4,7 @@ use libadwaita::{self as adw, prelude::*};
 pub mod ui;
 pub mod lib;
 
-use ui::MainApp;
+use ui::*;
 
 fn main() {
     gtk::init().expect("GTK initialization failed");
diff --git a/src/ui/main.rs b/src/ui/main.rs
index 600e9d8..874d22c 100644
--- a/src/ui/main.rs
+++ b/src/ui/main.rs
@@ -3,11 +3,20 @@ use libadwaita::{self as adw, prelude::*};
 
 use std::io::Error;
 
-use super::{get_object, add_action};
+use super::get_object;
 use super::preferences::PreferencesStack;
+use super::ToastError;
 
 use crate::lib::game;
 
+pub enum AppState {
+    Launch,
+    Progress {
+        title: String,
+        progress: f64
+    }
+}
+
 #[derive(Clone)]
 pub struct App {
     pub window: adw::ApplicationWindow,
@@ -17,6 +26,10 @@ pub struct App {
     pub open_preferences: gtk::Button,
     pub toast_overlay: adw::ToastOverlay,
 
+    pub launch_game_group: adw::PreferencesGroup,
+    pub progress_bar_group: adw::PreferencesGroup,
+    pub progress_bar: gtk::ProgressBar,
+
     pub preferences_stack: PreferencesStack
 }
 
@@ -25,16 +38,23 @@ impl App {
         // Create builder from UI file
         let builder = gtk::Builder::from_string(include_str!("../../assets/ui/.dist/main.ui"));
 
+        let window = get_object::<adw::ApplicationWindow>(&builder, "window")?;
+        let toast_overlay = get_object::<adw::ToastOverlay>(&builder, "toast_overlay")?;
+
         // Parse objects from builder
         let result = Self {
-            window: get_object(&builder, "window")?,
+            window: window.clone(),
             leaflet: get_object(&builder, "leaflet")?,
             launch_game: get_object(&builder, "launch_game")?,
             launch_game_debug: get_object(&builder, "launch_game_debug")?,
             open_preferences: get_object(&builder, "open_preferences")?,
-            toast_overlay: get_object(&builder, "toast_overlay")?,
+            toast_overlay: toast_overlay.clone(),
 
-            preferences_stack: PreferencesStack::new()?
+            launch_game_group: get_object(&builder, "launch_game_group")?,
+            progress_bar_group: get_object(&builder, "progress_bar_group")?,
+            progress_bar: get_object(&builder, "progress_bar")?,
+
+            preferences_stack: PreferencesStack::new(window, toast_overlay)?
         };
 
         // Add preferences page to the leaflet
@@ -73,37 +93,6 @@ impl App {
         Ok(result)
     }
 
-    /// Show toast with `toast` title and `See message` button
-    /// 
-    /// This button will show message dialog with error message
-    pub fn toast_error(&self, toast: &str, err: std::io::Error) {
-        let toast = adw::Toast::new(toast);
-
-        toast.set_button_label(Some("See message"));
-        toast.set_action_name(Some("see-message.see-message"));
-
-        let window_copy = self.window.clone();
-
-        // Show error message in a dialog window
-        add_action(&self.toast_overlay, "see-message", move || {
-            let dialog = gtk::MessageDialog::new(
-                Some(&window_copy),
-                gtk::DialogFlags::all(),
-                gtk::MessageType::Info,
-                gtk::ButtonsType::Close,
-                &err.to_string()
-            );
-
-            dialog.connect_response(move |dialog, _| {
-                dialog.close();
-            });
-
-            dialog.show();
-        });
-
-        self.toast_overlay.add_toast(&toast);
-    }
-
     pub fn open_preferences_page(&self) -> Result<(), Error> {
         self.preferences_stack.update()?;
         
@@ -111,4 +100,26 @@ impl App {
 
         Ok(())
     }
+
+    pub fn update_state(&self, state: AppState) {
+        match state {
+            AppState::Launch => {
+                self.launch_game_group.set_visible(true);
+                self.progress_bar_group.set_visible(false);
+            },
+            AppState::Progress { title, progress } => {
+                self.launch_game_group.set_visible(false);
+                self.progress_bar_group.set_visible(true);
+
+                self.progress_bar.set_text(Some(&title));
+                self.progress_bar.set_fraction(progress);
+            }
+        }
+    }
+}
+
+impl ToastError for App {
+    fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay) {
+        (self.window.clone(), self.toast_overlay.clone())
+    }
 }
diff --git a/src/ui/mod.rs b/src/ui/mod.rs
index a67d792..284e3d1 100644
--- a/src/ui/mod.rs
+++ b/src/ui/mod.rs
@@ -3,8 +3,14 @@ use libadwaita::{self as adw, prelude::*};
 
 mod main;
 mod preferences;
+mod toast_error;
 
-pub use main::App as MainApp;
+pub use main::{
+    App as MainApp,
+    AppState as MainAppState,
+};
+
+pub use toast_error::ToastError;
 
 /// This function loads object from builder or panics if it doesn't exist
 pub fn get_object<T: IsA<gtk::glib::Object>>(builder: &gtk::Builder, name: &str) -> Result<T, String> {
diff --git a/src/ui/preferences/enhanced_page.rs b/src/ui/preferences/enhanced_page.rs
index f83f71a..0a8cab7 100644
--- a/src/ui/preferences/enhanced_page.rs
+++ b/src/ui/preferences/enhanced_page.rs
@@ -103,9 +103,11 @@ impl Page {
     }
 
     /// This method is being called by the `PreferencesStack::update`
-    pub fn update(&self) -> Result<(), Error> {
+    pub fn update(&self, status_page: &adw::StatusPage) -> Result<(), Error> {
         let config = config::get()?;
 
+        status_page.set_description(Some("Loading preferences..."));
+
         // Update Wine sync
         self.sync_combo.set_selected(config.game.wine.sync.into());
 
diff --git a/src/ui/preferences/general_page.rs b/src/ui/preferences/general_page.rs
index b97862e..2e41cc3 100644
--- a/src/ui/preferences/general_page.rs
+++ b/src/ui/preferences/general_page.rs
@@ -32,13 +32,15 @@ impl Page {
     }
 
     /// This method is being called by the `PreferencesStack::update`
-    pub fn update(&self) -> Result<(), Error> {
+    pub fn update(&self, status_page: &adw::StatusPage) -> Result<(), Error> {
         let config = config::get()?;
         let game = Game::new(config.game.path);
 
         self.game_version.set_tooltip_text(None);
         self.patch_version.set_tooltip_text(None);
 
+        status_page.set_description(Some("Updating game info..."));
+
         match game.try_get_diff()? {
             VersionDiff::Latest(version) => {
                 self.game_version.set_label(&version.to_string());
@@ -61,6 +63,8 @@ impl Page {
             }
         }
 
+        status_page.set_description(Some("Updating patch info..."));
+
         match Patch::try_fetch(config.patch.servers)? {
             Patch::NotAvailable => {
                 self.patch_version.set_label("not available");
diff --git a/src/ui/preferences/mod.rs b/src/ui/preferences/mod.rs
index 6a82605..f662500 100644
--- a/src/ui/preferences/mod.rs
+++ b/src/ui/preferences/mod.rs
@@ -4,6 +4,7 @@ use libadwaita::{self as adw, prelude::*};
 use std::io::Error;
 
 use crate::ui::get_object;
+use crate::ui::ToastError;
 
 mod general_page;
 mod enhanced_page;
@@ -15,9 +16,15 @@ pub mod pages {
 
 #[derive(Clone)]
 pub struct PreferencesStack {
+    pub window: adw::ApplicationWindow,
+    pub toast_overlay: adw::ToastOverlay,
+
     pub preferences: gtk::Box,
     pub preferences_go_back: gtk::Button,
 
+    pub status_page: adw::StatusPage,
+    pub flap: adw::Flap,
+
     pub stack: gtk::Stack,
 
     pub general_page: pages::GeneralPage,
@@ -25,13 +32,21 @@ pub struct PreferencesStack {
 }
 
 impl PreferencesStack {
-    pub fn new() -> Result<Self, String> {
+    pub fn new(window: adw::ApplicationWindow, toast_overlay: adw::ToastOverlay) -> Result<Self, String> {
         let builder = gtk::Builder::from_string(include_str!("../../../assets/ui/.dist/preferences.ui"));
 
         let result = Self {
+            window,
+            toast_overlay,
+
             preferences: get_object(&builder, "preferences")?,
             preferences_go_back: get_object(&builder, "preferences_go_back")?,
+
+            status_page: get_object(&builder, "status_page")?,
+            flap: get_object(&builder, "flap")?,
+
             stack: get_object(&builder, "stack")?,
+            
             general_page: pages::GeneralPage::new()?,
             enhanced_page: pages::EnhancedPage::new()?
         };
@@ -48,9 +63,24 @@ impl PreferencesStack {
     /// 
     /// TODO: do it asynchronously. The problem is that I somehow need to handle this function's error to display it as a toast
     pub fn update(&self) -> Result<(), Error> {
-        self.general_page.update()?;
-        self.enhanced_page.update()?;
+        self.status_page.set_visible(true);
+        self.status_page.set_description(None);
+        self.flap.set_visible(false);
+
+        self.general_page.update(&self.status_page)?;
+        self.enhanced_page.update(&self.status_page)?;
+
+        self.status_page.set_visible(false);
+        self.flap.set_visible(true);
+
+        self.toast_error("Aboba amogus", Error::last_os_error());
 
         Ok(())
     }
 }
+
+impl ToastError for PreferencesStack {
+    fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay) {
+        (self.window.clone(), self.toast_overlay.clone())
+    }
+}
diff --git a/src/ui/toast_error.rs b/src/ui/toast_error.rs
new file mode 100644
index 0000000..9618c23
--- /dev/null
+++ b/src/ui/toast_error.rs
@@ -0,0 +1,42 @@
+use gtk4::{self as gtk, prelude::*};
+use libadwaita::{self as adw, prelude::*};
+
+use std::io::Error;
+
+use super::add_action;
+
+pub trait ToastError {
+    fn get_toast_widgets(&self) -> (adw::ApplicationWindow, adw::ToastOverlay);
+
+    /// Show toast with `toast` title and `See message` button
+    /// 
+    /// This button will show message dialog with error message
+    fn toast_error(&self, toast: &str, err: Error) {
+        let toast = adw::Toast::new(toast);
+
+        toast.set_button_label(Some("See message"));
+        toast.set_action_name(Some("see-message.see-message"));
+        toast.set_timeout(0);
+
+        let (window, toast_overlay) = self.get_toast_widgets();
+
+        // Show error message in a dialog window
+        add_action(&toast_overlay, "see-message", move || {
+            let dialog = gtk::MessageDialog::new(
+                Some(&window),
+                gtk::DialogFlags::all(),
+                gtk::MessageType::Info,
+                gtk::ButtonsType::Close,
+                &err.to_string()
+            );
+
+            dialog.connect_response(move |dialog, _| {
+                dialog.close();
+            });
+
+            dialog.show();
+        });
+
+        toast_overlay.add_toast(&toast);
+    }
+}