diff --git a/.gitignore b/.gitignore
index 6a5ffc8bc79b00a722d91bc74d1b9700b3b69a89..773bc05d992c9ee9ccab760ae11eee8d64263e64 100644
--- a/.gitignore
+++ b/.gitignore
@@ -88,3 +88,5 @@ lint/tmp/
 .idea/codeStyles/
 
 .idea/
+
+app/circuli_key.jks
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e6a7b51b8de744c591136f20da01e2e4ccca3023
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,46 @@
+image: jangrewe/gitlab-ci-android
+
+cache:
+  key: ${CI_PROJECT_ID}
+  paths:
+    - .gradle/
+
+before_script:
+  - chmod +x ./gradlew
+  - echo "$SIGNING_CONFIG" > local.properties
+  - echo "$SIGNING_KEY_BASE64" | base64 -d > android/app/circuli_key.jks
+
+stages:
+  - release
+  - deploy
+
+createGitlabRelease:
+  stage: release
+  rules:
+    - if: $CI_COMMIT_TAG
+  script:
+    - echo "Running the release job"
+    - bundle exec fastlane buildFdroid
+  artifacts:
+    name: "${CI_BUILD_NAME}_${CI_BUILD_REF_NAME}"
+    paths:
+      - app/build/outputs/apk/*.apk
+  release:
+    tag_name: $CI_COMMIT_TAG
+    name: 'Version $CI_COMMIT_TAG'
+    description: 'Release created using the release-cli.'
+    assets:
+      links:
+        - name: 'fdroid release apk $CI_COMMIT_TAG'
+          url: 'https://gitlab.com/api/v4/projects/13/jobs/artifacts/main/download?job=createGitlabRelease'
+
+
+
+uploadPlayStoreBeta:
+  stage: deploy
+  script:
+    - echo "Running the uploadPlayStoreBeta job"
+    - echo "$SERVICE_JSON" > google_play_api_key.json
+    - bundle exec fastlane deployGoogle
+  only:
+    - main
\ No newline at end of file
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000000000000000000000000000000000000..7a118b49be750543e59f7b9c55123e11322b00c6
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,3 @@
+source "https://rubygems.org"
+
+gem "fastlane"
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000000000000000000000000000000000000..3e31ec5e9daa1fc9a35d4972a14211cc4ff9fae5
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,217 @@
+GEM
+  remote: https://rubygems.org/
+  specs:
+    CFPropertyList (3.0.6)
+      rexml
+    addressable (2.8.5)
+      public_suffix (>= 2.0.2, < 6.0)
+    artifactory (3.0.15)
+    atomos (0.1.3)
+    aws-eventstream (1.2.0)
+    aws-partitions (1.834.0)
+    aws-sdk-core (3.185.1)
+      aws-eventstream (~> 1, >= 1.0.2)
+      aws-partitions (~> 1, >= 1.651.0)
+      aws-sigv4 (~> 1.5)
+      jmespath (~> 1, >= 1.6.1)
+    aws-sdk-kms (1.72.0)
+      aws-sdk-core (~> 3, >= 3.184.0)
+      aws-sigv4 (~> 1.1)
+    aws-sdk-s3 (1.136.0)
+      aws-sdk-core (~> 3, >= 3.181.0)
+      aws-sdk-kms (~> 1)
+      aws-sigv4 (~> 1.6)
+    aws-sigv4 (1.6.0)
+      aws-eventstream (~> 1, >= 1.0.2)
+    babosa (1.0.4)
+    claide (1.1.0)
+    colored (1.2)
+    colored2 (3.1.2)
+    commander (4.6.0)
+      highline (~> 2.0.0)
+    declarative (0.0.20)
+    digest-crc (0.6.5)
+      rake (>= 12.0.0, < 14.0.0)
+    domain_name (0.5.20190701)
+      unf (>= 0.0.5, < 1.0.0)
+    dotenv (2.8.1)
+    emoji_regex (3.2.3)
+    excon (0.104.0)
+    faraday (1.10.3)
+      faraday-em_http (~> 1.0)
+      faraday-em_synchrony (~> 1.0)
+      faraday-excon (~> 1.1)
+      faraday-httpclient (~> 1.0)
+      faraday-multipart (~> 1.0)
+      faraday-net_http (~> 1.0)
+      faraday-net_http_persistent (~> 1.0)
+      faraday-patron (~> 1.0)
+      faraday-rack (~> 1.0)
+      faraday-retry (~> 1.0)
+      ruby2_keywords (>= 0.0.4)
+    faraday-cookie_jar (0.0.7)
+      faraday (>= 0.8.0)
+      http-cookie (~> 1.0.0)
+    faraday-em_http (1.0.0)
+    faraday-em_synchrony (1.0.0)
+    faraday-excon (1.1.0)
+    faraday-httpclient (1.0.1)
+    faraday-multipart (1.0.4)
+      multipart-post (~> 2)
+    faraday-net_http (1.0.1)
+    faraday-net_http_persistent (1.2.0)
+    faraday-patron (1.0.0)
+    faraday-rack (1.0.0)
+    faraday-retry (1.0.3)
+    faraday_middleware (1.2.0)
+      faraday (~> 1.0)
+    fastimage (2.2.7)
+    fastlane (2.216.0)
+      CFPropertyList (>= 2.3, < 4.0.0)
+      addressable (>= 2.8, < 3.0.0)
+      artifactory (~> 3.0)
+      aws-sdk-s3 (~> 1.0)
+      babosa (>= 1.0.3, < 2.0.0)
+      bundler (>= 1.12.0, < 3.0.0)
+      colored
+      commander (~> 4.6)
+      dotenv (>= 2.1.1, < 3.0.0)
+      emoji_regex (>= 0.1, < 4.0)
+      excon (>= 0.71.0, < 1.0.0)
+      faraday (~> 1.0)
+      faraday-cookie_jar (~> 0.0.6)
+      faraday_middleware (~> 1.0)
+      fastimage (>= 2.1.0, < 3.0.0)
+      gh_inspector (>= 1.1.2, < 2.0.0)
+      google-apis-androidpublisher_v3 (~> 0.3)
+      google-apis-playcustomapp_v1 (~> 0.1)
+      google-cloud-storage (~> 1.31)
+      highline (~> 2.0)
+      http-cookie (~> 1.0.5)
+      json (< 3.0.0)
+      jwt (>= 2.1.0, < 3)
+      mini_magick (>= 4.9.4, < 5.0.0)
+      multipart-post (>= 2.0.0, < 3.0.0)
+      naturally (~> 2.2)
+      optparse (~> 0.1.1)
+      plist (>= 3.1.0, < 4.0.0)
+      rubyzip (>= 2.0.0, < 3.0.0)
+      security (= 0.1.3)
+      simctl (~> 1.6.3)
+      terminal-notifier (>= 2.0.0, < 3.0.0)
+      terminal-table (~> 3)
+      tty-screen (>= 0.6.3, < 1.0.0)
+      tty-spinner (>= 0.8.0, < 1.0.0)
+      word_wrap (~> 1.0.0)
+      xcodeproj (>= 1.13.0, < 2.0.0)
+      xcpretty (~> 0.3.0)
+      xcpretty-travis-formatter (>= 0.0.3)
+    gh_inspector (1.1.3)
+    google-apis-androidpublisher_v3 (0.50.0)
+      google-apis-core (>= 0.11.0, < 2.a)
+    google-apis-core (0.11.1)
+      addressable (~> 2.5, >= 2.5.1)
+      googleauth (>= 0.16.2, < 2.a)
+      httpclient (>= 2.8.1, < 3.a)
+      mini_mime (~> 1.0)
+      representable (~> 3.0)
+      retriable (>= 2.0, < 4.a)
+      rexml
+      webrick
+    google-apis-iamcredentials_v1 (0.17.0)
+      google-apis-core (>= 0.11.0, < 2.a)
+    google-apis-playcustomapp_v1 (0.13.0)
+      google-apis-core (>= 0.11.0, < 2.a)
+    google-apis-storage_v1 (0.19.0)
+      google-apis-core (>= 0.9.0, < 2.a)
+    google-cloud-core (1.6.0)
+      google-cloud-env (~> 1.0)
+      google-cloud-errors (~> 1.0)
+    google-cloud-env (1.6.0)
+      faraday (>= 0.17.3, < 3.0)
+    google-cloud-errors (1.3.1)
+    google-cloud-storage (1.44.0)
+      addressable (~> 2.8)
+      digest-crc (~> 0.4)
+      google-apis-iamcredentials_v1 (~> 0.1)
+      google-apis-storage_v1 (~> 0.19.0)
+      google-cloud-core (~> 1.6)
+      googleauth (>= 0.16.2, < 2.a)
+      mini_mime (~> 1.0)
+    googleauth (1.8.1)
+      faraday (>= 0.17.3, < 3.a)
+      jwt (>= 1.4, < 3.0)
+      multi_json (~> 1.11)
+      os (>= 0.9, < 2.0)
+      signet (>= 0.16, < 2.a)
+    highline (2.0.3)
+    http-cookie (1.0.5)
+      domain_name (~> 0.5)
+    httpclient (2.8.3)
+    jmespath (1.6.2)
+    json (2.6.3)
+    jwt (2.7.1)
+    mini_magick (4.12.0)
+    mini_mime (1.1.5)
+    multi_json (1.15.0)
+    multipart-post (2.3.0)
+    nanaimo (0.3.0)
+    naturally (2.2.1)
+    optparse (0.1.1)
+    os (1.1.4)
+    plist (3.7.0)
+    public_suffix (5.0.3)
+    rake (13.0.6)
+    representable (3.2.0)
+      declarative (< 0.1.0)
+      trailblazer-option (>= 0.1.1, < 0.2.0)
+      uber (< 0.2.0)
+    retriable (3.1.2)
+    rexml (3.2.6)
+    rouge (2.0.7)
+    ruby2_keywords (0.0.5)
+    rubyzip (2.3.2)
+    security (0.1.3)
+    signet (0.18.0)
+      addressable (~> 2.8)
+      faraday (>= 0.17.5, < 3.a)
+      jwt (>= 1.5, < 3.0)
+      multi_json (~> 1.10)
+    simctl (1.6.10)
+      CFPropertyList
+      naturally
+    terminal-notifier (2.0.0)
+    terminal-table (3.0.2)
+      unicode-display_width (>= 1.1.1, < 3)
+    trailblazer-option (0.1.2)
+    tty-cursor (0.7.1)
+    tty-screen (0.8.1)
+    tty-spinner (0.9.3)
+      tty-cursor (~> 0.7)
+    uber (0.1.0)
+    unf (0.1.4)
+      unf_ext
+    unf_ext (0.0.8.2)
+    unicode-display_width (2.5.0)
+    webrick (1.8.1)
+    word_wrap (1.0.0)
+    xcodeproj (1.23.0)
+      CFPropertyList (>= 2.3.3, < 4.0)
+      atomos (~> 0.1.3)
+      claide (>= 1.0.2, < 2.0)
+      colored2 (~> 3.1)
+      nanaimo (~> 0.3.0)
+      rexml (~> 3.2.4)
+    xcpretty (0.3.0)
+      rouge (~> 2.0.7)
+    xcpretty-travis-formatter (1.0.1)
+      xcpretty (~> 0.2, >= 0.0.7)
+
+PLATFORMS
+  arm64-darwin-23
+
+DEPENDENCIES
+  fastlane
+
+BUNDLED WITH
+   2.4.10
diff --git a/app/build.gradle b/app/build.gradle
index 3b7175336c323bff15f5ac8f3d530bd609bdc4e8..6c07772d7fab5e4d88122fcc4b4e304ce3ceb5bc 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -13,6 +13,7 @@ android {
         targetSdk rootProject.ext.sdk_version
         versionCode 25
         versionName "1.0.16"
+        archivesBaseName = "circles-v${versionName}"
 
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
     }
@@ -21,11 +22,25 @@ android {
         viewBinding true
     }
 
+    signingConfigs{
+        release {
+            Properties properties = new Properties()
+            if (rootProject.file("local.properties").exists()) {
+                properties.load(rootProject.file("local.properties").newDataInputStream())
+            }
+            storeFile file("circuli_key.jks")
+            storePassword properties.getProperty("KEY_PASSWORD")
+            keyAlias properties.getProperty("ALIAS_NAME")
+            keyPassword properties.getProperty("KEY_PASSWORD")
+        }
+    }
+
     buildTypes {
         debug {
             minifyEnabled false
         }
         release {
+            signingConfig signingConfigs.release
             minifyEnabled true
             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
         }
@@ -62,7 +77,7 @@ dependencies {
     implementation project(path: ':gallery')
 
     //Firebase
-    gplayImplementation platform('com.google.firebase:firebase-bom:32.3.0')
+    gplayImplementation platform('com.google.firebase:firebase-bom:32.3.1')
     gplayImplementation 'com.google.firebase:firebase-crashlytics-ktx'
     gplayImplementation 'com.google.firebase:firebase-analytics-ktx'
     gplayImplementation 'com.google.firebase:firebase-messaging-ktx'
diff --git a/app/src/main/java/org/futo/circles/App.kt b/app/src/main/java/org/futo/circles/App.kt
index 44308ca77602c404847b83df098d551885c1f74f..d99ecb03dd6e4226e46f5ad34eb7c45b330c0699 100644
--- a/app/src/main/java/org/futo/circles/App.kt
+++ b/app/src/main/java/org/futo/circles/App.kt
@@ -8,6 +8,7 @@ import com.vanniktech.emoji.EmojiManager
 import com.vanniktech.emoji.google.GoogleEmojiProvider
 import dagger.hilt.android.HiltAndroidApp
 import org.futo.circles.core.CirclesAppConfig
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.provider.MatrixNotificationSetupListener
 import org.futo.circles.core.provider.MatrixSessionProvider
 import org.futo.circles.feature.notifications.FcmHelper
@@ -36,6 +37,7 @@ class App : Application() {
 
     override fun onCreate() {
         super.onCreate()
+        NetworkObserver.register(applicationContext)
         CirclesAppConfig.Initializer()
             .buildConfigInfo(
                 BuildConfig.APPLICATION_ID,
diff --git a/app/src/main/java/org/futo/circles/feature/circles/CirclesFragment.kt b/app/src/main/java/org/futo/circles/feature/circles/CirclesFragment.kt
index be2f601628b96150f5028d2bcfcd63879ce181b5..9f54e751d74e680c81020b131d05face1d1309be 100644
--- a/app/src/main/java/org/futo/circles/feature/circles/CirclesFragment.kt
+++ b/app/src/main/java/org/futo/circles/feature/circles/CirclesFragment.kt
@@ -10,10 +10,13 @@ import by.kirich1409.viewbindingdelegate.viewBinding
 import dagger.hilt.android.AndroidEntryPoint
 import org.futo.circles.R
 import org.futo.circles.auth.explanation.CirclesExplanationDialog
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.databinding.FragmentRoomsBinding
 import org.futo.circles.core.extensions.navigateSafe
 import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.observeResponse
+import org.futo.circles.core.extensions.setEnabledViews
+import org.futo.circles.core.extensions.showNoInternetConnection
 import org.futo.circles.core.model.CircleRoomTypeArg
 import org.futo.circles.core.provider.PreferencesProvider
 import org.futo.circles.core.view.EmptyTabPlaceholderView
@@ -69,6 +72,7 @@ class CirclesFragment : Fragment(org.futo.circles.core.R.layout.fragment_rooms)
     }
 
     private fun setupObservers() {
+        NetworkObserver.observe(this) { setEnabledViews(it, listOf(binding.rvRooms)) }
         viewModel.roomsLiveData.observeData(this) {
             listAdapter?.submitList(it)
             binding.rvRooms.notifyItemsChanged()
@@ -84,11 +88,13 @@ class CirclesFragment : Fragment(org.futo.circles.core.R.layout.fragment_rooms)
     }
 
     private fun onInviteClicked(room: CircleListItem, isAccepted: Boolean) {
+        if (showNoInternetConnection()) return
         if (isAccepted) onAcceptInviteClicked(room)
         else viewModel.rejectInvite(room.id)
     }
 
     private fun onRequestClicked(room: RequestCircleListItem, isAccepted: Boolean) {
+        if (showNoInternetConnection()) return
         if (isAccepted) viewModel.inviteUser(room)
         else viewModel.kickUser(room)
     }
diff --git a/app/src/main/java/org/futo/circles/feature/groups/GroupsFragment.kt b/app/src/main/java/org/futo/circles/feature/groups/GroupsFragment.kt
index 3931403772c625f3ef6c25cdac6d2af791d922ea..c53edc3ddf087af1df3ed5de11707fe4e95cc252 100644
--- a/app/src/main/java/org/futo/circles/feature/groups/GroupsFragment.kt
+++ b/app/src/main/java/org/futo/circles/feature/groups/GroupsFragment.kt
@@ -10,10 +10,13 @@ import by.kirich1409.viewbindingdelegate.viewBinding
 import dagger.hilt.android.AndroidEntryPoint
 import org.futo.circles.R
 import org.futo.circles.auth.explanation.CirclesExplanationDialog
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.databinding.FragmentRoomsBinding
 import org.futo.circles.core.extensions.navigateSafe
 import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.observeResponse
+import org.futo.circles.core.extensions.setEnabledViews
+import org.futo.circles.core.extensions.showNoInternetConnection
 import org.futo.circles.core.model.CircleRoomTypeArg
 import org.futo.circles.core.provider.PreferencesProvider
 import org.futo.circles.core.view.EmptyTabPlaceholderView
@@ -64,16 +67,19 @@ class GroupsFragment : Fragment(org.futo.circles.core.R.layout.fragment_rooms) {
     }
 
     private fun setupObservers() {
+        NetworkObserver.observe(this) { setEnabledViews(it, listOf(binding.rvRooms)) }
         viewModel.roomsLiveData.observeData(this) { listAdapter.submitList(it) }
         viewModel.inviteResultLiveData.observeResponse(this)
     }
 
     private fun onInviteClicked(room: GroupListItem, isAccepted: Boolean) {
+        if (showNoInternetConnection()) return
         if (isAccepted) viewModel.acceptGroupInvite(room.id)
         else viewModel.rejectInvite(room.id)
     }
 
     private fun onRequestClicked(room: RequestGroupListItem, isAccepted: Boolean) {
+        if (showNoInternetConnection()) return
         if (isAccepted) viewModel.inviteUser(room)
         else viewModel.kickUser(room)
     }
diff --git a/app/src/main/java/org/futo/circles/feature/people/PeopleFragment.kt b/app/src/main/java/org/futo/circles/feature/people/PeopleFragment.kt
index 5bbc837d953970cf8092f0d0a9fc89bb3a920a0c..295136d758cddd0df165d19d4d4e742ebb641b8e 100644
--- a/app/src/main/java/org/futo/circles/feature/people/PeopleFragment.kt
+++ b/app/src/main/java/org/futo/circles/feature/people/PeopleFragment.kt
@@ -14,10 +14,13 @@ import androidx.recyclerview.widget.DividerItemDecoration
 import by.kirich1409.viewbindingdelegate.viewBinding
 import dagger.hilt.android.AndroidEntryPoint
 import org.futo.circles.R
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.extensions.getQueryTextChangeStateFlow
 import org.futo.circles.core.extensions.navigateSafe
 import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.observeResponse
+import org.futo.circles.core.extensions.setEnabledViews
+import org.futo.circles.core.extensions.showNoInternetConnection
 import org.futo.circles.core.extensions.showSuccess
 import org.futo.circles.core.view.EmptyTabPlaceholderView
 import org.futo.circles.databinding.FragmentPeopleBinding
@@ -33,6 +36,7 @@ class PeopleFragment : Fragment(R.layout.fragment_people), MenuProvider {
         PeopleAdapter(
             onUserClicked = { userId -> navigateToUserPage(userId) },
             onRequestClicked = { userId, isAccepted ->
+                if (showNoInternetConnection()) return@PeopleAdapter
                 viewModel.onFollowRequestAnswered(userId, isAccepted)
             },
             onUnIgnore = { userId -> viewModel.unIgnoreUser(userId) }
@@ -68,6 +72,7 @@ class PeopleFragment : Fragment(R.layout.fragment_people), MenuProvider {
     }
 
     private fun setupObservers() {
+        NetworkObserver.observe(this) { setEnabledViews(it, listOf(binding.rvUsers)) }
         viewModel.peopleLiveData.observeData(this) { items ->
             peopleAdapter.submitList(items)
         }
diff --git a/app/src/main/java/org/futo/circles/feature/people/user/UserDialogFragment.kt b/app/src/main/java/org/futo/circles/feature/people/user/UserDialogFragment.kt
index cc2ea9145960862128f49606169636413e7403e5..51791a9cf6ec116efc14e3ee843cf70ba374105c 100644
--- a/app/src/main/java/org/futo/circles/feature/people/user/UserDialogFragment.kt
+++ b/app/src/main/java/org/futo/circles/feature/people/user/UserDialogFragment.kt
@@ -8,12 +8,15 @@ import androidx.fragment.app.viewModels
 import androidx.recyclerview.widget.DividerItemDecoration
 import dagger.hilt.android.AndroidEntryPoint
 import org.futo.circles.R
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.extensions.loadProfileIcon
 import org.futo.circles.core.extensions.notEmptyDisplayName
 import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.observeResponse
 import org.futo.circles.core.extensions.onBackPressed
+import org.futo.circles.core.extensions.setEnabledChildren
 import org.futo.circles.core.extensions.setIsVisible
+import org.futo.circles.core.extensions.showNoInternetConnection
 import org.futo.circles.core.extensions.showSuccess
 import org.futo.circles.core.extensions.withConfirmation
 import org.futo.circles.core.fragment.BaseFullscreenDialogFragment
@@ -35,8 +38,12 @@ class UserDialogFragment : BaseFullscreenDialogFragment(DialogFragmentUserBindin
 
     private val usersCirclesAdapter by lazy {
         UsersCirclesAdapter(
-            onRequestFollow = { timelineId -> viewModel.requestFollowTimeline(timelineId) },
+            onRequestFollow = { timelineId ->
+                if (showNoInternetConnection()) return@UsersCirclesAdapter
+                viewModel.requestFollowTimeline(timelineId)
+            },
             onUnFollow = { timelineId ->
+                if (showNoInternetConnection()) return@UsersCirclesAdapter
                 withConfirmation(UnfollowTimeline()) {
                     viewModel.unFollowTimeline(timelineId)
                 }
@@ -91,6 +98,12 @@ class UserDialogFragment : BaseFullscreenDialogFragment(DialogFragmentUserBindin
     }
 
     private fun setupObservers() {
+        NetworkObserver.observe(this) {
+            binding.toolbar.apply {
+                isEnabled = it
+                setEnabledChildren(it)
+            }
+        }
         viewModel.userLiveData.observeData(this) { setupUserInfo(it) }
         viewModel.timelineLiveDataLiveData.observeData(this) {
             usersCirclesAdapter.submitList(it)
diff --git a/app/src/main/java/org/futo/circles/feature/settings/SettingsFragment.kt b/app/src/main/java/org/futo/circles/feature/settings/SettingsFragment.kt
index e8a3f3d145de0f1bd25965c8df153d710e9875ae..0e405b7cd32463000c6997877ee2da1be24cc65f 100644
--- a/app/src/main/java/org/futo/circles/feature/settings/SettingsFragment.kt
+++ b/app/src/main/java/org/futo/circles/feature/settings/SettingsFragment.kt
@@ -4,7 +4,6 @@ import android.os.Bundle
 import android.view.View
 import android.widget.Toast
 import androidx.fragment.app.Fragment
-import androidx.fragment.app.activityViewModels
 import androidx.fragment.app.viewModels
 import by.kirich1409.viewbindingdelegate.viewBinding
 import dagger.hilt.android.AndroidEntryPoint
@@ -13,10 +12,12 @@ import org.futo.circles.R
 import org.futo.circles.auth.model.LogOut
 import org.futo.circles.auth.model.SwitchUser
 import org.futo.circles.core.CirclesAppConfig
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.extensions.loadProfileIcon
 import org.futo.circles.core.extensions.notEmptyDisplayName
 import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.observeResponse
+import org.futo.circles.core.extensions.setEnabledViews
 import org.futo.circles.core.extensions.showError
 import org.futo.circles.core.extensions.showSuccess
 import org.futo.circles.core.extensions.withConfirmation
@@ -69,6 +70,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
     }
 
     private fun setupObservers() {
+        NetworkObserver.observe(this) { setEnabledViews(it) }
         viewModel.logOutLiveData.observeResponse(this,
             success = { clearSessionAndRestart() },
             onRequestInvoked = { loadingDialog.dismiss() }
@@ -113,8 +115,12 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
     }
 
     private fun setVersion() {
-        binding.tvVersion.text =
-            getString(org.futo.circles.core.R.string.version_format, CirclesAppConfig.appVersion)
+        binding.tvVersion.setText(
+            getString(
+                org.futo.circles.core.R.string.version_format,
+                CirclesAppConfig.appVersion
+            )
+        )
     }
 
     private fun toggleDeveloperMode() {
diff --git a/app/src/main/java/org/futo/circles/feature/timeline/TimelineDialogFragment.kt b/app/src/main/java/org/futo/circles/feature/timeline/TimelineDialogFragment.kt
index 6ca7e373577652be41c8092f175cbb26b6aba6da..86356f523de34763d6acb0e02135d702dad75c7e 100644
--- a/app/src/main/java/org/futo/circles/feature/timeline/TimelineDialogFragment.kt
+++ b/app/src/main/java/org/futo/circles/feature/timeline/TimelineDialogFragment.kt
@@ -8,11 +8,14 @@ import androidx.navigation.fragment.navArgs
 import androidx.recyclerview.widget.DividerItemDecoration
 import dagger.hilt.android.AndroidEntryPoint
 import org.futo.circles.R
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.extensions.getCurrentUserPowerLevel
 import org.futo.circles.core.extensions.isCurrentUserAbleToPost
 import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.observeResponse
+import org.futo.circles.core.extensions.setEnabledViews
 import org.futo.circles.core.extensions.showError
+import org.futo.circles.core.extensions.showNoInternetConnection
 import org.futo.circles.core.extensions.showSuccess
 import org.futo.circles.core.extensions.withConfirmation
 import org.futo.circles.core.fragment.BaseFullscreenDialogFragment
@@ -23,7 +26,6 @@ import org.futo.circles.core.model.PostContent
 import org.futo.circles.core.model.PostContentType
 import org.futo.circles.core.share.ShareProvider
 import org.futo.circles.core.utils.debounce
-import org.futo.circles.core.utils.getTimelineRoomFor
 import org.futo.circles.core.utils.getTimelineRoomIdOrThrow
 import org.futo.circles.databinding.DialogFragmentTimelineBinding
 import org.futo.circles.feature.timeline.list.TimelineAdapter
@@ -144,6 +146,7 @@ class TimelineDialogFragment : BaseFullscreenDialogFragment(DialogFragmentTimeli
 
 
     private fun setupObservers() {
+        NetworkObserver.observe(this) { setEnabledViews(it) }
         viewModel.titleLiveData.observeData(this) { roomName ->
             val title = if (isThread) getString(R.string.thread_format, roomName) else roomName
             binding.toolbar.title = title
@@ -176,6 +179,7 @@ class TimelineDialogFragment : BaseFullscreenDialogFragment(DialogFragmentTimeli
     }
 
     override fun onShowMenuClicked(roomId: String, eventId: String) {
+        if (!showNoInternetConnection()) return
         navigator.navigatePostMenu(roomId, eventId)
     }
 
@@ -188,14 +192,17 @@ class TimelineDialogFragment : BaseFullscreenDialogFragment(DialogFragmentTimeli
     }
 
     override fun onShowEmoji(roomId: String, eventId: String) {
+        if (showNoInternetConnection()) return
         navigator.navigateToShowEmoji(roomId, eventId)
     }
 
     override fun onReply(roomId: String, eventId: String) {
+        if (showNoInternetConnection()) return
         navigator.navigateToThread(roomId, eventId)
     }
 
     override fun onShare(content: PostContent) {
+        if (showNoInternetConnection()) return
         viewModel.sharePostContent(content)
     }
 
@@ -214,6 +221,7 @@ class TimelineDialogFragment : BaseFullscreenDialogFragment(DialogFragmentTimeli
     override fun onEmojiChipClicked(
         roomId: String, eventId: String, emoji: String, isUnSend: Boolean
     ) {
+        if (showNoInternetConnection()) return
         if (viewModel.accessLevelLiveData.value?.isCurrentUserAbleToPost() != true) {
             showError(getString(R.string.you_can_not_post_to_this_room))
             return
@@ -223,6 +231,7 @@ class TimelineDialogFragment : BaseFullscreenDialogFragment(DialogFragmentTimeli
     }
 
     override fun onPollOptionSelected(roomId: String, eventId: String, optionId: String) {
+        if (showNoInternetConnection()) return
         viewModel.pollVote(roomId, eventId, optionId)
     }
 
diff --git a/app/src/main/java/org/futo/circles/feature/timeline/post/create/CreatePostDialogFragment.kt b/app/src/main/java/org/futo/circles/feature/timeline/post/create/CreatePostDialogFragment.kt
index c9327b9b271b158c9a52acc464debd7b3f22af50..c09bb3cc36f822cf9f5396eb2d0f674882e73e4d 100644
--- a/app/src/main/java/org/futo/circles/feature/timeline/post/create/CreatePostDialogFragment.kt
+++ b/app/src/main/java/org/futo/circles/feature/timeline/post/create/CreatePostDialogFragment.kt
@@ -10,9 +10,11 @@ import androidx.navigation.fragment.findNavController
 import androidx.navigation.fragment.navArgs
 import dagger.hilt.android.AndroidEntryPoint
 import org.futo.circles.R
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.extensions.navigateSafe
 import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.onBackPressed
+import org.futo.circles.core.extensions.showError
 import org.futo.circles.core.fragment.BaseFullscreenDialogFragment
 import org.futo.circles.core.model.MediaContent
 import org.futo.circles.core.model.MediaType
@@ -58,6 +60,10 @@ class CreatePostDialogFragment :
         with(binding) {
             btnSend.apply {
                 setOnClickListener {
+                    if (!NetworkObserver.isConnected()) {
+                        showError(getString(org.futo.circles.core.R.string.no_internet_connection))
+                        return@setOnClickListener
+                    }
                     sendPost()
                     onBackPressed()
                 }
diff --git a/app/src/main/java/org/futo/circles/feature/timeline/preview/TimelineMediaPreviewDialogFragment.kt b/app/src/main/java/org/futo/circles/feature/timeline/preview/TimelineMediaPreviewDialogFragment.kt
index 03b77c3b5ff2e316e537f61f9f06fd9cd955a316..4fa3cb686d5817612084c18d1378f7e62f7d0f8c 100644
--- a/app/src/main/java/org/futo/circles/feature/timeline/preview/TimelineMediaPreviewDialogFragment.kt
+++ b/app/src/main/java/org/futo/circles/feature/timeline/preview/TimelineMediaPreviewDialogFragment.kt
@@ -3,8 +3,6 @@ package org.futo.circles.feature.timeline.preview
 import android.annotation.SuppressLint
 import android.graphics.Color
 import android.os.Bundle
-import android.text.method.MovementMethod
-import android.view.MotionEvent
 import android.view.View
 import android.view.WindowManager
 import androidx.appcompat.view.menu.MenuBuilder
@@ -13,8 +11,10 @@ import androidx.fragment.app.Fragment
 import androidx.fragment.app.viewModels
 import androidx.navigation.fragment.navArgs
 import dagger.hilt.android.AndroidEntryPoint
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.onBackPressed
+import org.futo.circles.core.extensions.setEnabledChildren
 import org.futo.circles.core.extensions.setIsVisible
 import org.futo.circles.core.extensions.showSuccess
 import org.futo.circles.core.extensions.withConfirmation
@@ -51,6 +51,7 @@ class TimelineMediaPreviewDialogFragment :
         replaceFragment(mediaFragment)
         binding.lContainer.setOnClickListener { binding.toolbar.setIsVisible(binding.toolbar.isVisible.not()) }
     }
+
     private fun replaceFragment(fragment: Fragment) {
         childFragmentManager.beginTransaction()
             .replace(R.id.lContainer, fragment)
@@ -89,6 +90,12 @@ class TimelineMediaPreviewDialogFragment :
     }
 
     private fun setupObservers() {
+        NetworkObserver.observe(this) {
+            binding.toolbar.apply {
+                isEnabled = it
+                setEnabledChildren(it)
+            }
+        }
         viewModel.shareLiveData.observeData(this) { content ->
             context?.let { ShareProvider.share(it, content) }
         }
diff --git a/app/src/main/java/org/futo/circles/view/PostFooterView.kt b/app/src/main/java/org/futo/circles/view/PostFooterView.kt
index ae68cd4ff4ac7e2bfb728772e26ca8775bb30ff2..6a9a3aa971c0fee2931ff5e4e8b2f341121c7a36 100644
--- a/app/src/main/java/org/futo/circles/view/PostFooterView.kt
+++ b/app/src/main/java/org/futo/circles/view/PostFooterView.kt
@@ -5,6 +5,7 @@ import android.util.AttributeSet
 import android.view.LayoutInflater
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.core.view.isVisible
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.extensions.setIsVisible
 import org.futo.circles.core.model.Post
 import org.futo.circles.core.model.ReactionsData
@@ -102,6 +103,7 @@ class PostFooterView(
     }
 
     private fun locallyUpdateEmojisList(reaction: ReactionsData) {
+        if (!NetworkObserver.isConnected()) return
         if (areUserAbleToPost().not()) return
         val emojisList = post?.reactionsData?.toMutableList() ?: return
         val newItem = if (reaction.addedByMe) {
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 77806044080c943f7d37214d3f1bdcd447e57f54..43352b04c0b85d461203857f3c8b9b3882e717bf 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,10 +1,15 @@
 <?xml version="1.0" encoding="utf-8"?>
-<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/nav_host_fragment"
-    android:name="androidx.navigation.fragment.NavHostFragment"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    app:defaultNavHost="true"
-    app:navGraph="@navigation/nav_graph_start_host" />
+    android:layout_height="match_parent">
+
+    <androidx.fragment.app.FragmentContainerView
+        android:id="@+id/nav_host_fragment"
+        android:name="androidx.navigation.fragment.NavHostFragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:defaultNavHost="true"
+        app:navGraph="@navigation/nav_graph_start_host" />
+</FrameLayout>
 
diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml
index f36e2bf229a3e2dc93f76dac7b77c58df8f7c24c..b874a1c18b51a1cc8c637c8ff9fb8bd18dfd2116 100644
--- a/app/src/main/res/layout/fragment_settings.xml
+++ b/app/src/main/res/layout/fragment_settings.xml
@@ -121,13 +121,13 @@
                 android:layout_height="wrap_content"
                 android:text="@string/notifications" />
 
-            <TextView
+            <org.futo.circles.core.view.SettingsMenuItemView
                 android:id="@+id/tvPushNotifications"
-                style="@style/settingMenuItem"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:text="@string/push_notifications"
-                app:drawableStartCompat="@drawable/ic_notifications" />
+                app:hasDivider="false"
+                app:optionIcon="@drawable/ic_notifications"
+                app:optionName="@string/push_notifications" />
 
             <TextView
                 style="@style/settingMenuHeader"
@@ -143,13 +143,13 @@
                 app:optionName="@string/login_sessions" />
 
 
-            <TextView
+            <org.futo.circles.core.view.SettingsMenuItemView
                 android:id="@+id/tvClearCache"
-                style="@style/settingMenuItem"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:text="@string/clear_cache_and_reload"
-                app:drawableStartCompat="@drawable/ic_reload" />
+                app:hasDivider="false"
+                app:optionIcon="@drawable/ic_reload"
+                app:optionName="@string/clear_cache_and_reload" />
 
             <TextView
                 style="@style/settingMenuHeader"
@@ -195,13 +195,12 @@
                 android:layout_height="wrap_content"
                 android:text="@string/other" />
 
-            <TextView
+            <org.futo.circles.core.view.SettingsMenuItemView
                 android:id="@+id/tvVersion"
-                style="@style/settingMenuItem"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                app:drawableStartCompat="@drawable/ic_app_version"
-                tools:text="Version" />
+                app:optionIcon="@drawable/ic_app_version"
+                tools:optionName="Version" />
 
         </LinearLayout>
 
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/log_in/LogInFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/log_in/LogInFragment.kt
index 4562d7105d9ec7a21ccf93d178facefe68470339..93bec05e6699d5a9a921799b0c568927905648d1 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/log_in/LogInFragment.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/log_in/LogInFragment.kt
@@ -6,6 +6,8 @@ import android.view.View.OnFocusChangeListener
 import android.widget.ArrayAdapter
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
 import androidx.navigation.fragment.findNavController
 import by.kirich1409.viewbindingdelegate.viewBinding
 import dagger.hilt.android.AndroidEntryPoint
@@ -15,10 +17,12 @@ import org.futo.circles.auth.feature.log_in.switch_user.list.SwitchUsersAdapter
 import org.futo.circles.auth.feature.log_in.switch_user.list.SwitchUsersViewHolder
 import org.futo.circles.auth.model.RemoveUser
 import org.futo.circles.core.CirclesAppConfig
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.extensions.getText
 import org.futo.circles.core.extensions.navigateSafe
 import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.observeResponse
+import org.futo.circles.core.extensions.setEnabledViews
 import org.futo.circles.core.extensions.setIsVisible
 import org.futo.circles.core.extensions.showError
 import org.futo.circles.core.extensions.withConfirmation
@@ -84,6 +88,7 @@ class LogInFragment : Fragment(R.layout.fragment_log_in), HasLoadingState {
     }
 
     private fun setupObservers() {
+        NetworkObserver.observe(this){ setEnabledViews(it) }
         viewModel.loginResultLiveData.observeResponse(this,
             success = {
                 findNavController().navigateSafe(LogInFragmentDirections.toLoginStagesFragment())
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/LogInStagesFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/LogInStagesFragment.kt
index 4b0999d38f2aa46a7270270245ec1e08fdf97797..77310891aa57ac01c696c3a72512f3f46211f08c 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/LogInStagesFragment.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/log_in/stages/LogInStagesFragment.kt
@@ -17,10 +17,12 @@ import org.futo.circles.auth.base.LoginStageNavigationEvent
 import org.futo.circles.auth.databinding.FragmentLoginStagesBinding
 import org.futo.circles.auth.feature.log_in.recovery.EnterPassPhraseDialog
 import org.futo.circles.auth.feature.log_in.recovery.EnterPassPhraseDialogListener
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.extensions.navigateSafe
 import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.observeResponse
 import org.futo.circles.core.extensions.onBackPressed
+import org.futo.circles.core.extensions.setEnabledViews
 import org.futo.circles.core.extensions.showDialog
 import org.futo.circles.core.extensions.showError
 import org.futo.circles.core.fragment.BackPressOwner
@@ -55,6 +57,7 @@ class LogInStagesFragment : Fragment(R.layout.fragment_login_stages),
     }
 
     private fun setupObservers() {
+        NetworkObserver.observe(this) { setEnabledViews(it) }
         viewModel.loginStageNavigationLiveData.observeData(this) { event ->
             val id = when (event) {
                 LoginStageNavigationEvent.DirectPassword -> R.id.to_direct_login
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/profile/setup/SetupProfileFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/profile/setup/SetupProfileFragment.kt
index 761dc679a9db9fb908c6981ecf8a1cb818640c07..c1e24da4593d9dc4aa7a6bb9a7ddc95572744792 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/profile/setup/SetupProfileFragment.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/profile/setup/SetupProfileFragment.kt
@@ -10,10 +10,12 @@ import by.kirich1409.viewbindingdelegate.viewBinding
 import dagger.hilt.android.AndroidEntryPoint
 import org.futo.circles.auth.R
 import org.futo.circles.auth.databinding.FragmentSetupProfileBinding
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.extensions.getText
 import org.futo.circles.core.extensions.navigateSafe
 import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.observeResponse
+import org.futo.circles.core.extensions.setEnabledViews
 import org.futo.circles.core.extensions.showDialog
 import org.futo.circles.core.fragment.HasLoadingState
 import org.futo.circles.core.picker.helper.MediaPickerHelper
@@ -55,6 +57,9 @@ class SetupProfileFragment : Fragment(R.layout.fragment_setup_profile), HasLoadi
     }
 
     private fun setupObservers() {
+        NetworkObserver.observe(this) {
+            setEnabledViews(it, listOf(binding.btnSkip))
+        }
         viewModel.profileImageLiveData.observeData(this) {
             setSaveButtonEnabled()
             binding.ivProfile.setImageURI(it)
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/SignUpFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/sign_up/SignUpFragment.kt
index e0a6a229aa011fce81cffac7cc2147255acb89ba..de8e6c58b0a215b1165f53d0fa5323925feaa700 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/sign_up/SignUpFragment.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/sign_up/SignUpFragment.kt
@@ -12,10 +12,12 @@ import by.kirich1409.viewbindingdelegate.viewBinding
 import dagger.hilt.android.AndroidEntryPoint
 import org.futo.circles.auth.R
 import org.futo.circles.auth.databinding.FragmentSignUpBinding
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.extensions.navigateSafe
 import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.observeResponse
 import org.futo.circles.core.extensions.onBackPressed
+import org.futo.circles.core.extensions.setEnabledViews
 import org.futo.circles.core.extensions.showDialog
 import org.futo.circles.core.extensions.showError
 import org.futo.circles.core.fragment.BackPressOwner
@@ -40,6 +42,7 @@ class SignUpFragment : Fragment(R.layout.fragment_sign_up),
     }
 
     private fun setupObservers() {
+        NetworkObserver.observe(this){ setEnabledViews(it) }
         viewModel.subtitleLiveData.observeData(this) {
             binding.toolbar.subtitle = it
         }
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/workspace/ConfigureWorkspaceFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/workspace/ConfigureWorkspaceFragment.kt
index 071bc2ba0aea567ce2da51476a781778943ea9fa..b2bd5b25f90cdf81c6abfcac6ee39fe2508f769a 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/workspace/ConfigureWorkspaceFragment.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/workspace/ConfigureWorkspaceFragment.kt
@@ -13,9 +13,11 @@ import dagger.hilt.android.AndroidEntryPoint
 import org.futo.circles.auth.R
 import org.futo.circles.auth.databinding.FragmentConfigureWorkspaceBinding
 import org.futo.circles.auth.feature.workspace.list.WorkspaceTasksListAdapter
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.extensions.navigateSafe
 import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.observeResponse
+import org.futo.circles.core.extensions.setEnabledViews
 import org.futo.circles.core.extensions.showError
 import org.futo.circles.core.fragment.HasLoadingState
 
@@ -65,6 +67,7 @@ class ConfigureWorkspaceFragment : Fragment(R.layout.fragment_configure_workspac
     }
 
     private fun setupObservers() {
+        NetworkObserver.observe(this){ setEnabledViews(it) }
         viewModel.tasksLiveData.observeData(this) {
             tasksAdapter.submitList(it)
         }
diff --git a/build.gradle b/build.gradle
index f46d04848b3fde07d02e4087ad7a05fc992ccf4b..17677ac2f1c87383f8a85877a9fb27acc77d70d6 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,7 @@ buildscript {
     ext {
         sdk_version = 34
         min_sdk_version = 24
-        androidx_nav_version = "2.7.2"
+        androidx_nav_version = '2.7.3'
         hilt_version = '2.48'
         modules_version = "1.0.9"
         modules_gitlab_projectId = "13"
@@ -12,7 +12,7 @@ buildscript {
         mavenCentral()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:8.1.1'
+        classpath 'com.android.tools.build:gradle:8.1.2'
         classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.21'
         classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$androidx_nav_version"
         classpath 'com.google.gms:google-services:4.4.0'
diff --git a/core/src/main/AndroidManifest.xml b/core/src/main/AndroidManifest.xml
index 2d915ee4dfac8bdfb4246438e1171bf6c27ad962..3de53f08d1a3637f60e5f0f887e94cc3d855ba8a 100644
--- a/core/src/main/AndroidManifest.xml
+++ b/core/src/main/AndroidManifest.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
 
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.VIBRATE" />
 </manifest>
\ No newline at end of file
diff --git a/core/src/main/java/org/futo/circles/core/BaseActivity.kt b/core/src/main/java/org/futo/circles/core/BaseActivity.kt
index 01e9f875cc656519795c3ea3bb8e7c1edd37256d..878652c33b2aa8f4bf93939a1d7bc9b5e3d1a0ee 100644
--- a/core/src/main/java/org/futo/circles/core/BaseActivity.kt
+++ b/core/src/main/java/org/futo/circles/core/BaseActivity.kt
@@ -1,16 +1,26 @@
 package org.futo.circles.core
 
+import android.os.Bundle
+import android.view.ViewGroup
 import androidx.appcompat.app.AppCompatActivity
+import by.kirich1409.viewbindingdelegate.internal.findRootView
 import org.futo.circles.core.rageshake.BugReportDataCollector
 import org.futo.circles.core.rageshake.RageShake
 import javax.inject.Inject
 
+
 abstract class BaseActivity(contentLayoutId: Int) : AppCompatActivity(contentLayoutId) {
 
     @Inject
     lateinit var bugReportDataCollector: BugReportDataCollector
 
     private val rageShake by lazy { RageShake(this, bugReportDataCollector) }
+    private val noInternetConnectionPresenter = NoInternetConnectionViewPresenter()
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        noInternetConnectionPresenter.register(this, findRootView(this) as? ViewGroup)
+    }
 
     override fun onResume() {
         super.onResume()
@@ -21,4 +31,9 @@ abstract class BaseActivity(contentLayoutId: Int) : AppCompatActivity(contentLay
         super.onPause()
         if (CirclesAppConfig.isRageshakeEnabled) rageShake.stop()
     }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        noInternetConnectionPresenter.unregister()
+    }
 }
\ No newline at end of file
diff --git a/core/src/main/java/org/futo/circles/core/ErrorParser.kt b/core/src/main/java/org/futo/circles/core/ErrorParser.kt
index d055b1ec31557adcc4c63ffdb525b4bf9a06a353..a89365b98e25c1913bbe519f1d3f647e8eecd495 100644
--- a/core/src/main/java/org/futo/circles/core/ErrorParser.kt
+++ b/core/src/main/java/org/futo/circles/core/ErrorParser.kt
@@ -1,6 +1,7 @@
 package org.futo.circles.core
 
 import org.json.JSONObject
+import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.failure.Failure
 import org.matrix.android.sdk.api.session.crypto.MXCryptoError
 import retrofit2.HttpException
@@ -10,22 +11,19 @@ object ErrorParser {
     private const val AUTH_EXCEPTION_REASON_KEY = "reason"
 
     fun getErrorMessage(t: Throwable): String =
-        handleErrorBodyException(t) ?: handleOtherException(t) ?: "Unexpected error"
+        handleErrorBodyException(t) ?: handleOtherException(t)
 
 
     private fun handleErrorBodyException(t: Throwable): String? =
         (t as? HttpException)?.response()?.errorBody()?.string()?.let {
-            try {
-                JSONObject(it).getString(AUTH_EXCEPTION_REASON_KEY)
-            } catch (e: Exception) {
-                null
-            }
+            tryOrNull { JSONObject(it).getString(AUTH_EXCEPTION_REASON_KEY) }
         }
 
-    private fun handleOtherException(t: Throwable): String? = when (t) {
+    private fun handleOtherException(t: Throwable): String = when (t) {
+        is Failure.NetworkConnection -> "No network. Please check your Internet connection"
         is Failure.ServerError -> t.error.message
         is MXCryptoError.Base -> t.technicalMessage
-        else -> t.message
+        else -> t.message ?: "Unexpected error"
     }
 
 }
\ No newline at end of file
diff --git a/core/src/main/java/org/futo/circles/core/NetworkObserver.kt b/core/src/main/java/org/futo/circles/core/NetworkObserver.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2b3d396b440643d746a0d868cfa3e5caf3fb47ed
--- /dev/null
+++ b/core/src/main/java/org/futo/circles/core/NetworkObserver.kt
@@ -0,0 +1,71 @@
+package org.futo.circles.core
+
+import android.content.Context
+import android.net.ConnectivityManager
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.net.NetworkRequest
+import androidx.core.content.getSystemService
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
+import org.matrix.android.sdk.api.extensions.orFalse
+
+object NetworkObserver {
+
+    private val internetConnectionFlow = MutableStateFlow(false)
+
+    fun isConnected() = internetConnectionFlow.value
+
+    fun register(context: Context) {
+        internetConnectionFlow.value = isConnectedToInternet(context)
+        setInternetConnectionObserver(context) { internetConnectionFlow.value = it }
+    }
+
+    fun observe(lifecycleOwner: LifecycleOwner, onConnectionChanged: (Boolean) -> Unit) {
+        lifecycleOwner.lifecycleScope.launch {
+            lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+                internetConnectionFlow.collectLatest {
+                    onConnectionChanged(it)
+                }
+            }
+        }
+    }
+
+    private fun setInternetConnectionObserver(
+        context: Context,
+        onConnectionChanged: (Boolean) -> Unit
+    ): ConnectivityManager.NetworkCallback {
+        val networkCallback = object : ConnectivityManager.NetworkCallback() {
+            override fun onAvailable(network: Network) {
+                super.onAvailable(network)
+                onConnectionChanged(true)
+            }
+
+            override fun onLost(network: Network) {
+                super.onLost(network)
+                onConnectionChanged(false)
+            }
+        }
+        val request = NetworkRequest.Builder()
+            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).build()
+        context.getSystemService<ConnectivityManager>()
+            ?.registerNetworkCallback(request, networkCallback)
+        return networkCallback
+    }
+
+    private fun isConnectedToInternet(context: Context): Boolean {
+        val connectivityManager = context.getSystemService<ConnectivityManager>() ?: return false
+        val capabilities =
+            connectivityManager.activeNetwork?.let { connectivityManager.getNetworkCapabilities(it) }
+        val hasWifi = capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI).orFalse()
+        val hasMobileData =
+            capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR).orFalse()
+        return hasWifi || hasMobileData
+    }
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/futo/circles/core/NoInternetConnectionViewPresenter.kt b/core/src/main/java/org/futo/circles/core/NoInternetConnectionViewPresenter.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a1d5105a1f570af0d46b81254d21920eb2a3405d
--- /dev/null
+++ b/core/src/main/java/org/futo/circles/core/NoInternetConnectionViewPresenter.kt
@@ -0,0 +1,32 @@
+package org.futo.circles.core
+
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.lifecycle.LifecycleOwner
+import org.futo.circles.core.extensions.addNoInternetConnectionView
+
+class NoInternetConnectionViewPresenter {
+
+    private var noInternetConnectionView: TextView? = null
+
+    fun register(viewLifecycleOwner: LifecycleOwner, viewGroup: ViewGroup?) {
+        NetworkObserver.observe(viewLifecycleOwner) { isConnected ->
+            val rootViewGroup = viewGroup ?: return@observe
+            if (isConnected) {
+                noInternetConnectionView?.let {
+                    rootViewGroup.removeView(it)
+                    noInternetConnectionView = null
+                }
+            } else {
+                if (noInternetConnectionView != null) return@observe
+                rootViewGroup.addNoInternetConnectionView()
+                    .also { noInternetConnectionView = it }
+            }
+        }
+    }
+
+    fun unregister() {
+        noInternetConnectionView = null
+    }
+
+}
\ No newline at end of file
diff --git a/gallery/src/main/java/org/futo/circles/gallery/extensions/ContextExtensions.kt b/core/src/main/java/org/futo/circles/core/extensions/ContextExtensions.kt
similarity index 92%
rename from gallery/src/main/java/org/futo/circles/gallery/extensions/ContextExtensions.kt
rename to core/src/main/java/org/futo/circles/core/extensions/ContextExtensions.kt
index b1038a0be224793760f9e5b4be4a89467fc5b3d0..a0a7b329b8675c30b5959cf1436046d31888e8fc 100644
--- a/gallery/src/main/java/org/futo/circles/gallery/extensions/ContextExtensions.kt
+++ b/core/src/main/java/org/futo/circles/core/extensions/ContextExtensions.kt
@@ -1,4 +1,4 @@
-package org.futo.circles.gallery.extensions
+package org.futo.circles.core.extensions
 
 import android.content.Context
 import android.net.ConnectivityManager
@@ -11,4 +11,4 @@ fun Context.isConnectedToWifi(): Boolean {
     return connectivityManager.activeNetwork?.let { connectivityManager.getNetworkCapabilities(it) }
         ?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
         .orFalse()
-}
\ No newline at end of file
+}
diff --git a/core/src/main/java/org/futo/circles/core/extensions/FragmentExtensions.kt b/core/src/main/java/org/futo/circles/core/extensions/FragmentExtensions.kt
index 51afe62b84dd84d3b44df2d325084094e1c494d8..c2867368949ad7605d209bf82b72f4051eb907ba 100644
--- a/core/src/main/java/org/futo/circles/core/extensions/FragmentExtensions.kt
+++ b/core/src/main/java/org/futo/circles/core/extensions/FragmentExtensions.kt
@@ -22,6 +22,7 @@ import androidx.appcompat.app.AppCompatActivity
 import androidx.appcompat.widget.Toolbar
 import androidx.fragment.app.Fragment
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.R
 import org.futo.circles.core.fragment.BaseFullscreenDialogFragment
 import org.futo.circles.core.model.ConfirmationType
@@ -66,8 +67,14 @@ fun Fragment.showSuccess(message: String) {
     showDialogBar(message, false)
 }
 
+fun Fragment.showNoInternetConnection(): Boolean {
+    val isConnected = NetworkObserver.isConnected()
+    if (!isConnected) showError(getString(org.futo.circles.core.R.string.no_internet_connection))
+    return !isConnected
+}
+
 fun Fragment.setEnabledViews(enabled: Boolean, viewsToExclude: List<View> = emptyList()) {
-    (view?.rootView as? ViewGroup)?.setEnabledChildren(enabled, viewsToExclude)
+    (view as? ViewGroup)?.setEnabledChildren(enabled, viewsToExclude)
 }
 
 fun Fragment.setSupportActionBar(toolbar: Toolbar) {
diff --git a/core/src/main/java/org/futo/circles/core/extensions/ViewExtensions.kt b/core/src/main/java/org/futo/circles/core/extensions/ViewExtensions.kt
index d862ad8b0e1e255c7c2bdd2424635ccbbfd4bf55..e66b2f962304b3c34ed197bc10ba11c50b9e6eee 100644
--- a/core/src/main/java/org/futo/circles/core/extensions/ViewExtensions.kt
+++ b/core/src/main/java/org/futo/circles/core/extensions/ViewExtensions.kt
@@ -5,8 +5,12 @@ import android.util.AttributeSet
 import android.util.TypedValue
 import android.view.View
 import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.TextView
 import androidx.annotation.StyleRes
+import androidx.core.content.ContextCompat
 import androidx.core.view.children
+import org.futo.circles.core.R
 
 
 fun View.visible() {
@@ -51,4 +55,20 @@ fun ViewGroup.setEnabledChildren(enabled: Boolean, viewsToExclude: List<View> =
             (view as? ViewGroup)?.setEnabledChildren(enabled, viewsToExclude)
         }
     }
+}
+
+fun ViewGroup.addNoInternetConnectionView(): TextView {
+    val noInternetView = TextView(context).apply {
+        text = context.getString(R.string.no_internet_connection)
+        setTextColor(ContextCompat.getColor(context, R.color.white))
+        textAlignment = View.TEXT_ALIGNMENT_CENTER
+        textSize = 13f
+        setBackgroundColor(ContextCompat.getColor(context, R.color.red))
+        layoutParams = FrameLayout.LayoutParams(
+            FrameLayout.LayoutParams.MATCH_PARENT,
+            FrameLayout.LayoutParams.WRAP_CONTENT
+        )
+    }
+    addView(noInternetView)
+    return noInternetView
 }
\ No newline at end of file
diff --git a/core/src/main/java/org/futo/circles/core/fragment/BaseFullscreenDialogFragment.kt b/core/src/main/java/org/futo/circles/core/fragment/BaseFullscreenDialogFragment.kt
index d84e4ffd99040d17812324d46052c9e0c5cd520b..fb8c430182ba3758f0f6d91c0cfb5c7788cda67d 100644
--- a/core/src/main/java/org/futo/circles/core/fragment/BaseFullscreenDialogFragment.kt
+++ b/core/src/main/java/org/futo/circles/core/fragment/BaseFullscreenDialogFragment.kt
@@ -9,6 +9,7 @@ import android.view.WindowManager
 import androidx.appcompat.app.AppCompatDialogFragment
 import androidx.viewbinding.ViewBinding
 import com.google.android.material.appbar.MaterialToolbar
+import org.futo.circles.core.NoInternetConnectionViewPresenter
 import org.futo.circles.core.R
 import org.futo.circles.core.extensions.onBackPressed
 
@@ -19,6 +20,7 @@ abstract class BaseFullscreenDialogFragment(
 
     private var _binding: ViewBinding? = null
     protected open val toolbarId = R.id.toolbar
+    private val noInternetConnectionPresenter = NoInternetConnectionViewPresenter()
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
@@ -36,6 +38,7 @@ abstract class BaseFullscreenDialogFragment(
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         setupToolbar()
+        noInternetConnectionPresenter.register(requireActivity(), _binding?.root as? ViewGroup)
     }
 
     protected fun getBinding() = _binding
@@ -43,6 +46,7 @@ abstract class BaseFullscreenDialogFragment(
     override fun onDestroyView() {
         super.onDestroyView()
         _binding = null
+        noInternetConnectionPresenter.unregister()
     }
 
     private fun setupToolbar() {
diff --git a/core/src/main/java/org/futo/circles/core/timeline/options/TimelineOptionsDialogFragment.kt b/core/src/main/java/org/futo/circles/core/timeline/options/TimelineOptionsDialogFragment.kt
index e3ba891d46f123db23d624f4379bc5bb41d5fc9b..0435eeee283af5abcbf4f6633fe0a47402e88382 100644
--- a/core/src/main/java/org/futo/circles/core/timeline/options/TimelineOptionsDialogFragment.kt
+++ b/core/src/main/java/org/futo/circles/core/timeline/options/TimelineOptionsDialogFragment.kt
@@ -6,6 +6,7 @@ import androidx.fragment.app.viewModels
 import androidx.navigation.fragment.findNavController
 import androidx.navigation.fragment.navArgs
 import dagger.hilt.android.AndroidEntryPoint
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.R
 import org.futo.circles.core.databinding.DialogFragmentTimelineOptionsBinding
 import org.futo.circles.core.extensions.isCurrentUserAbleToChangeSettings
@@ -14,6 +15,7 @@ import org.futo.circles.core.extensions.isCurrentUserOnlyAdmin
 import org.futo.circles.core.extensions.loadProfileIcon
 import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.observeResponse
+import org.futo.circles.core.extensions.setEnabledViews
 import org.futo.circles.core.extensions.setIsVisible
 import org.futo.circles.core.extensions.showDialog
 import org.futo.circles.core.extensions.withConfirmation
@@ -131,6 +133,7 @@ class TimelineOptionsDialogFragment :
     }
 
     private fun setupObservers() {
+        NetworkObserver.observe(this) { setEnabledViews(it) }
         viewModel.leaveDeleteEventLiveData.observeResponse(this,
             success = {
                 val controller = findNavController()
diff --git a/core/src/main/java/org/futo/circles/core/view/SettingsMenuItemView.kt b/core/src/main/java/org/futo/circles/core/view/SettingsMenuItemView.kt
index 2da8bd8af091b52b8cd3d25f3db46f374c807797..bae0a2e3bdda7b32710d65074ea28be98a9b2855 100644
--- a/core/src/main/java/org/futo/circles/core/view/SettingsMenuItemView.kt
+++ b/core/src/main/java/org/futo/circles/core/view/SettingsMenuItemView.kt
@@ -7,6 +7,7 @@ import android.widget.LinearLayout
 import org.futo.circles.core.R
 import org.futo.circles.core.databinding.ViewSettingsMenuItemBinding
 import org.futo.circles.core.extensions.getAttributes
+import org.futo.circles.core.extensions.setIsVisible
 
 
 class SettingsMenuItemView(
@@ -24,6 +25,9 @@ class SettingsMenuItemView(
 
     init {
         getAttributes(attrs, R.styleable.SettingsMenuItemView) {
+            val isDividerVisible = getBoolean(R.styleable.SettingsMenuItemView_hasDivider, true)
+            binding.vBottomDivider.setIsVisible(isDividerVisible)
+
             binding.tvOptionName.apply {
                 text = getString(R.styleable.SettingsMenuItemView_optionName)
                     ?.replace(' ', Typography.nbsp)
diff --git a/core/src/main/res/values/attrs.xml b/core/src/main/res/values/attrs.xml
index cb8f1c16cd789ac439e85fe6f96209e9283a48ad..8e9019d23f0bb30045e958406937b950c822d90b 100644
--- a/core/src/main/res/values/attrs.xml
+++ b/core/src/main/res/values/attrs.xml
@@ -15,6 +15,7 @@
     <declare-styleable name="SettingsMenuItemView">
         <attr name="optionName" format="string" />
         <attr name="optionIcon" format="reference" />
+        <attr name="hasDivider" format="boolean" />
     </declare-styleable>
 
 </resources>
\ No newline at end of file
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 9d39d6f217cc0cfb5076c7ecb97080cee3eb19f2..3ab592d50890a414f32420b8c9fb4670c6d1ec79 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -141,4 +141,5 @@
     <string name="group_topic">Group topic</string>
     <string name="remove_from_this_circle_but_do_not_unfollow">Remove from this circle, but do not unfollow</string>
     <string name="unfollow_completely_remove_from_all_circles">Unfollow completely (remove from all circles)</string>
+    <string name="no_internet_connection">No Internet connection</string>
 </resources>
\ No newline at end of file
diff --git a/fastlane/Appfile b/fastlane/Appfile
new file mode 100644
index 0000000000000000000000000000000000000000..8485f0a1c1d21406d8aab08ce1d417b03ac34783
--- /dev/null
+++ b/fastlane/Appfile
@@ -0,0 +1,2 @@
+json_key_file("google_play_api_key.json") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
+package_name("org.futo.circles") # e.g. com.krausefx.app
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
new file mode 100644
index 0000000000000000000000000000000000000000..30ec4430e09c6e795cff70004d4a78006f95f68d
--- /dev/null
+++ b/fastlane/Fastfile
@@ -0,0 +1,47 @@
+# This file contains the fastlane.tools configuration
+# You can find the documentation at https://docs.fastlane.tools
+#
+# For a list of all available actions, check out
+#
+#     https://docs.fastlane.tools/actions
+#
+# For a list of all available plugins, check out
+#
+#     https://docs.fastlane.tools/plugins/available-plugins
+#
+
+# Uncomment the line if you want fastlane to automatically update itself
+# update_fastlane
+
+default_platform(:android)
+
+platform :android do
+
+  desc "Deploy to the Google Play Beta"
+  lane :buildFdroid do
+    gradle(task: "clean")
+    gradle(
+        task: "assemble",
+        flavor: "Gplay",
+        build_type: "Release"
+          )
+  end
+
+  desc "Deploy to the Google Play Beta"
+  lane :deployGoogle do
+    gradle(task: "clean")
+    gradle(
+        task: "bundle",
+        flavor: "Gplay",
+        build_type: "Release"
+          )
+    upload_to_play_store(
+        track: "beta",
+        skip_upload_metadata: true,
+        skip_upload_images: true,
+        skip_upload_screenshots: true
+    )
+  end
+
+
+end
diff --git a/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosFragment.kt b/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosFragment.kt
index 0f1004782c64e6efdbf8a9b2a68f12bfd4904136..ca48d24df852d053e910f5efbc5edd14aa3d93b9 100644
--- a/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosFragment.kt
+++ b/gallery/src/main/java/org/futo/circles/gallery/feature/PhotosFragment.kt
@@ -17,10 +17,12 @@ import androidx.navigation.fragment.findNavController
 import by.kirich1409.viewbindingdelegate.viewBinding
 import dagger.hilt.android.AndroidEntryPoint
 import org.futo.circles.core.CirclesAppConfig
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.databinding.FragmentRoomsBinding
 import org.futo.circles.core.extensions.navigateSafe
 import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.observeResponse
+import org.futo.circles.core.extensions.setEnabledViews
 import org.futo.circles.core.model.GalleryListItem
 import org.futo.circles.core.model.RequestGalleryListItem
 import org.futo.circles.core.picker.helper.RuntimePermissionHelper
@@ -83,6 +85,7 @@ class PhotosFragment : Fragment(org.futo.circles.core.R.layout.fragment_rooms),
     }
 
     private fun setupObservers() {
+        NetworkObserver.observe(this) { setEnabledViews(it, listOf(binding.rvRooms)) }
         viewModel.roomsLiveData.observeData(this) { listAdapter.submitList(it) }
         viewModel.inviteResultLiveData.observeResponse(this)
     }
diff --git a/gallery/src/main/java/org/futo/circles/gallery/feature/gallery/GalleryDialogFragment.kt b/gallery/src/main/java/org/futo/circles/gallery/feature/gallery/GalleryDialogFragment.kt
index 597a3fa387485f3fa15e3e074946edfd094bf784..026325ff6ad932ebf2de1b1d8649ccd103cab27f 100644
--- a/gallery/src/main/java/org/futo/circles/gallery/feature/gallery/GalleryDialogFragment.kt
+++ b/gallery/src/main/java/org/futo/circles/gallery/feature/gallery/GalleryDialogFragment.kt
@@ -10,10 +10,12 @@ import androidx.fragment.app.viewModels
 import androidx.navigation.fragment.findNavController
 import androidx.navigation.fragment.navArgs
 import dagger.hilt.android.AndroidEntryPoint
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.extensions.navigateSafe
 import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.observeResponse
 import org.futo.circles.core.extensions.onBackPressed
+import org.futo.circles.core.extensions.setEnabledChildren
 import org.futo.circles.core.fragment.BackPressOwner
 import org.futo.circles.core.fragment.BaseFullscreenDialogFragment
 import org.futo.circles.core.model.CircleRoomTypeArg
@@ -81,6 +83,12 @@ class GalleryDialogFragment : BaseFullscreenDialogFragment(DialogFragmentGallery
     }
 
     private fun setupObservers() {
+        NetworkObserver.observe(this) {
+            binding.toolbar.apply {
+                isEnabled = it
+                setEnabledChildren(it)
+            }
+        }
         viewModel.titleLiveData?.observeData(this) { title ->
             binding.toolbar.title = title
         }
diff --git a/gallery/src/main/java/org/futo/circles/gallery/feature/gallery/full_screen/FullScreenPagerFragment.kt b/gallery/src/main/java/org/futo/circles/gallery/feature/gallery/full_screen/FullScreenPagerFragment.kt
index ea4805b1cb58e57a35bfe511630ffbb9d04961d4..3ee05928798de2fd6655886ab074cee9d4ba43fb 100644
--- a/gallery/src/main/java/org/futo/circles/gallery/feature/gallery/full_screen/FullScreenPagerFragment.kt
+++ b/gallery/src/main/java/org/futo/circles/gallery/feature/gallery/full_screen/FullScreenPagerFragment.kt
@@ -13,8 +13,10 @@ import androidx.transition.TransitionInflater
 import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
 import by.kirich1409.viewbindingdelegate.viewBinding
 import dagger.hilt.android.AndroidEntryPoint
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.onBackPressed
+import org.futo.circles.core.extensions.setEnabledChildren
 import org.futo.circles.core.extensions.setIsVisible
 import org.futo.circles.core.extensions.showSuccess
 import org.futo.circles.core.extensions.withConfirmation
@@ -86,6 +88,12 @@ class FullScreenPagerFragment : ParentBackPressOwnerFragment(R.layout.fragment_f
     }
 
     private fun setupObservers() {
+        NetworkObserver.observe(this) {
+            binding.toolbar.apply {
+                isEnabled = it
+                setEnabledChildren(it)
+            }
+        }
         viewModel.galleryItemsLiveData.observeData(this) {
             pagerAdapter.submitList(it)
             setToolbarTitle()
diff --git a/gallery/src/main/java/org/futo/circles/gallery/feature/gallery/grid/GalleryGridFragment.kt b/gallery/src/main/java/org/futo/circles/gallery/feature/gallery/grid/GalleryGridFragment.kt
index fedc66884fe5a02abe7e1469888f3095e6e0aff8..a360d54504668e5f276c9e80a73ff78020f4d57a 100644
--- a/gallery/src/main/java/org/futo/circles/gallery/feature/gallery/grid/GalleryGridFragment.kt
+++ b/gallery/src/main/java/org/futo/circles/gallery/feature/gallery/grid/GalleryGridFragment.kt
@@ -13,6 +13,7 @@ import androidx.recyclerview.widget.StaggeredGridLayoutManager
 import androidx.transition.TransitionInflater
 import by.kirich1409.viewbindingdelegate.viewBinding
 import dagger.hilt.android.AndroidEntryPoint
+import org.futo.circles.core.NetworkObserver
 import org.futo.circles.core.extensions.isCurrentUserAbleToPost
 import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.setIsVisible
@@ -92,6 +93,9 @@ class GalleryGridFragment : Fragment(R.layout.fragment_gallery_grid) {
     }
 
     private fun setupObservers() {
+        NetworkObserver.observe(this) {
+            binding.fbUploadImage.isEnabled = it
+        }
         viewModel.galleryItemsLiveData.observeData(this) {
             listAdapter.submitList(it)
         }
diff --git a/gallery/src/main/java/org/futo/circles/gallery/model/MediaBackupSettingsData.kt b/gallery/src/main/java/org/futo/circles/gallery/model/MediaBackupSettingsData.kt
index fa28e0793d8f0041fbc42262ff034c292b7828af..c3f072bdb26adedec4ef0c31b2fa8f75b49989e3 100644
--- a/gallery/src/main/java/org/futo/circles/gallery/model/MediaBackupSettingsData.kt
+++ b/gallery/src/main/java/org/futo/circles/gallery/model/MediaBackupSettingsData.kt
@@ -1,7 +1,7 @@
 package org.futo.circles.gallery.model
 
 import android.content.Context
-import org.futo.circles.gallery.extensions.isConnectedToWifi
+import org.futo.circles.core.extensions.isConnectedToWifi
 import org.matrix.android.sdk.api.session.events.model.Content
 
 data class MediaBackupSettingsData(
diff --git a/gallery/src/main/res/transition/grid_exit_transition.xml b/gallery/src/main/res/transition/grid_exit_transition.xml
index 8b2c9c3bc039958d61d1399d59b57bc63877d11a..7cd386fd36432e370ea0766ac7d63a03f86f7e5a 100644
--- a/gallery/src/main/res/transition/grid_exit_transition.xml
+++ b/gallery/src/main/res/transition/grid_exit_transition.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
-    android:duration="375"
+    android:duration="150"
     android:interpolator="@android:interpolator/fast_out_slow_in"
     android:startDelay="25">
     <fade>
diff --git a/gallery/src/main/res/transition/image_shared_element_transition.xml b/gallery/src/main/res/transition/image_shared_element_transition.xml
index 3d86880d7191e3549b79a31b6b810290613c86d2..3b89b5ca2c5d208199155da19d8be40595e6659c 100644
--- a/gallery/src/main/res/transition/image_shared_element_transition.xml
+++ b/gallery/src/main/res/transition/image_shared_element_transition.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
-    android:duration="375"
+    android:duration="150"
     android:interpolator="@android:interpolator/fast_out_slow_in"
     android:transitionOrdering="together">
     <changeClipBounds />