diff --git a/app.json b/app.json
index de3ff2467f..670ccd0422 100644
--- a/app.json
+++ b/app.json
@@ -36,7 +36,10 @@
"NSPhotoLibraryAddUsageDescription": "Used to save images to your library.",
"NSPhotoLibraryUsageDescription": "Used for profile pictures, posts, and other kinds of content"
},
- "associatedDomains": ["applinks:bsky.app", "applinks:staging.bsky.app"]
+ "associatedDomains": ["applinks:bsky.app", "applinks:staging.bsky.app"],
+ "entitlements": {
+ "com.apple.developer.networking.wifi-info": true
+ }
},
"androidStatusBar": {
"barStyle": "dark-content",
diff --git a/package.json b/package.json
index 02492acebf..68fb482a88 100644
--- a/package.json
+++ b/package.json
@@ -42,6 +42,7 @@
"@react-native-clipboard/clipboard": "^1.10.0",
"@react-native-community/blur": "^4.3.0",
"@react-native-community/datetimepicker": "6.7.3",
+ "@react-native-community/netinfo": "^9.4.1",
"@react-native-menu/menu": "^0.8.0",
"@react-navigation/bottom-tabs": "^6.5.7",
"@react-navigation/drawer": "^6.6.2",
diff --git a/src/view/com/util/OfflineIndicator.tsx b/src/view/com/util/OfflineIndicator.tsx
new file mode 100644
index 0000000000..5fbdb77654
--- /dev/null
+++ b/src/view/com/util/OfflineIndicator.tsx
@@ -0,0 +1,65 @@
+import React, {useEffect, useState} from 'react'
+import {StyleSheet, View, ViewStyle} from 'react-native'
+import NetInfo from '@react-native-community/netinfo'
+import {
+ FontAwesomeIcon,
+ FontAwesomeIconStyle,
+} from '@fortawesome/react-native-fontawesome'
+
+import {usePalette} from 'lib/hooks/usePalette'
+import {Text} from 'view/com/util/text/Text'
+import {s} from 'lib/styles'
+import {isNative} from 'platform/detection'
+import {CenteredView} from './Views.web'
+
+const Indicator = () => {
+ const pal = usePalette('error')
+
+ const containerStyles: ViewStyle[] = [
+ styles.container,
+ s.flexRow,
+ s.alignCenter,
+ ]
+
+ if (!isNative) {
+ containerStyles.push(pal.view)
+ }
+
+ return (
+
+
+ You're offline
+
+ )
+}
+
+export const OfflineIndicator = () => {
+ const [isConnected, setIsConnected] = useState(true)
+ useEffect(() => {
+ const unsubscribe = NetInfo.addEventListener(state => {
+ setIsConnected(!!state.isConnected)
+ })
+
+ return () => unsubscribe()
+ }, [])
+
+ if (isConnected) return null
+
+ if (isNative) {
+ return
+ }
+
+ return (
+
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ container: {
+ width: isNative ? s.window.width : undefined,
+ justifyContent: 'center',
+ height: 20,
+ },
+})
diff --git a/src/view/index.ts b/src/view/index.ts
index 4294508de6..11342c24f7 100644
--- a/src/view/index.ts
+++ b/src/view/index.ts
@@ -90,6 +90,7 @@ import {faPlay} from '@fortawesome/free-solid-svg-icons/faPlay'
import {faPause} from '@fortawesome/free-solid-svg-icons/faPause'
import {faThumbtack} from '@fortawesome/free-solid-svg-icons/faThumbtack'
import {faList} from '@fortawesome/free-solid-svg-icons/faList'
+import {faWifi} from '@fortawesome/free-solid-svg-icons/faWifi'
export function setup() {
library.add(
@@ -183,5 +184,6 @@ export function setup() {
faPlay,
faPause,
faList,
+ faWifi,
)
}
diff --git a/src/view/shell/index.tsx b/src/view/shell/index.tsx
index 7ab371d593..5d4911f314 100644
--- a/src/view/shell/index.tsx
+++ b/src/view/shell/index.tsx
@@ -18,6 +18,7 @@ import {RoutesContainer, TabsNavigator} from '../../Navigation'
import {isStateAtTabRoot} from 'lib/routes/helpers'
import {SafeAreaProvider} from 'react-native-safe-area-context'
import {useOTAUpdate} from 'lib/hooks/useOTAUpdate'
+import {OfflineIndicator} from 'view/com/util/OfflineIndicator'
const ShellInner = observer(() => {
const store = useStores()
@@ -46,6 +47,7 @@ const ShellInner = observer(() => {
<>
+
{
const store = useStores()
@@ -32,6 +33,7 @@ const ShellInner = observer(() => {
return (
<>
+
diff --git a/yarn.lock b/yarn.lock
index 8295df2244..de13e6f14e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4505,6 +4505,11 @@
resolved "https://registry.yarnpkg.com/@react-native-community/eslint-plugin/-/eslint-plugin-1.3.0.tgz#9e558170c106bbafaa1ef502bd8e6d4651012bf9"
integrity sha512-+zDZ20NUnSWghj7Ku5aFphMzuM9JulqCW+aPXT6IfIXFbb8tzYTTOSeRFOtuekJ99ibW2fUCSsjuKNlwDIbHFg==
+"@react-native-community/netinfo@^9.4.1":
+ version "9.4.1"
+ resolved "https://registry.yarnpkg.com/@react-native-community/netinfo/-/netinfo-9.4.1.tgz#7b880758adca65fe47ee866cf7b00416b9dcc192"
+ integrity sha512-dAbY5mfw+6Kas/GJ6QX9AZyY+K+eq9ad4Su6utoph/nxyH3whp5cMSgRNgE2VhGQVRZ/OG0qq3IaD3+wzoqJXw==
+
"@react-native-menu/menu@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@react-native-menu/menu/-/menu-0.8.0.tgz#dbf227c2081e5ffd3d2073ee68ecc84cf8639727"