Compare commits

...

5 Commits

Author SHA1 Message Date
Chowdhury Foysal Ahamed d6b7ccb0a3 Fix connection check 2023-08-18 19:48:39 +02:00
Chowdhury Foysal Ahamed 38b525d759 💄 Change text in offline details modal 2023-08-18 19:46:57 +02:00
Chowdhury Foysal Ahamed 2247d99d66 Add modal describing offline behavior 2023-08-18 18:44:05 +02:00
Chowdhury Foysal Ahamed ecc4b13c8f 💄 Move offline indicator on desktop and mobile web 2023-08-18 11:37:41 +02:00
Chowdhury Foysal Ahamed 60088038ab Show Offline indicator status across platforms 2023-08-18 01:07:58 +02:00
11 changed files with 171 additions and 2 deletions
+4 -1
View File
@@ -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",
+1
View File
@@ -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",
+5
View File
@@ -36,6 +36,10 @@ export interface ProfilePreviewModal {
did: string
}
export interface OfflineDetailsModal {
name: 'offline-details'
}
export interface ServerInputModal {
name: 'server-input'
initialService: string
@@ -180,6 +184,7 @@ export type Modal =
// Generic
| ConfirmModal
| OfflineDetailsModal
interface LightboxModel {}
+4 -1
View File
@@ -31,6 +31,7 @@ import * as PostLanguagesSettingsModal from './lang-settings/PostLanguagesSettin
import * as PreferencesHomeFeed from './PreferencesHomeFeed'
import * as OnboardingModal from './OnboardingModal'
import * as ModerationDetailsModal from './ModerationDetails'
import * as OfflineDetailsModal from '../offline/DetailsModal'
const DEFAULT_SNAPPOINTS = ['90%']
@@ -140,10 +141,12 @@ export const ModalsContainer = observer(function ModalsContainer() {
} else if (activeModal?.name === 'moderation-details') {
snapPoints = ModerationDetailsModal.snapPoints
element = <ModerationDetailsModal.Component {...activeModal} />
} else if (activeModal?.name === 'offline-details') {
snapPoints = OfflineDetailsModal.snapPoints
element = <OfflineDetailsModal.Component />
} else {
return null
}
if (snapPoints[0] === 'fullscreen') {
return (
<SafeAreaView style={[styles.fullscreenContainer, pal.view]}>
+3
View File
@@ -28,6 +28,7 @@ import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguages
import * as PostLanguagesSettingsModal from './lang-settings/PostLanguagesSettings'
import * as OnboardingModal from './OnboardingModal'
import * as ModerationDetailsModal from './ModerationDetails'
import * as OfflineDetailsModal from '../offline/DetailsModal'
import * as PreferencesHomeFeed from './PreferencesHomeFeed'
@@ -113,6 +114,8 @@ function Modal({modal}: {modal: ModalIface}) {
element = <OnboardingModal.Component />
} else if (modal.name === 'moderation-details') {
element = <ModerationDetailsModal.Component {...modal} />
} else if (modal.name === 'offline-details') {
element = <OfflineDetailsModal.Component />
} else {
return null
}
+54
View File
@@ -0,0 +1,54 @@
import React from 'react'
import {StyleSheet, View} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {s} from 'lib/styles'
import {usePalette} from 'lib/hooks/usePalette'
import {isDesktopWeb} from 'platform/detection'
import {useStores} from 'state/index'
import {Text} from 'view/com/util/text/Text'
import {Button} from 'view/com/util/forms/Button'
export const snapPoints = [300]
export const Component = () => {
const pal = usePalette('default')
const store = useStores()
return (
<View style={[pal.view, styles.container]}>
<View style={[s.alignCenter]}>
<FontAwesomeIcon icon="wifi" style={s.white} size={50} />
</View>
<Text type="title-lg" style={[s.textCenter, pal.text, s.pb20]}>
Offline Mode
</Text>
<Text style={[pal.text, s.pl20, s.pr20]}>
BlueSky works best when you're connected to the internet, you can still
browse around while offline.
</Text>
<Text style={[pal.text, s.pl20, s.pr20, s.pt10]}>
However, you won't be able to post or interact with posts/users until
you're back online.
</Text>
<Button
type="primary"
style={styles.btn}
onPress={() => store.shell.closeModal()}>
<Text type="button-lg" style={[pal.textLight, s.textCenter, s.white]}>
Close
</Text>
</Button>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingBottom: isDesktopWeb ? 0 : 10,
},
btn: {
marginHorizontal: 15,
marginTop: 20,
},
})
+87
View File
@@ -0,0 +1,87 @@
import React, {useEffect, useState} from 'react'
import {StyleSheet, TouchableOpacity, 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 {isDesktopWeb, isNative} from 'platform/detection'
import {useStores} from 'state/index'
const Indicator = () => {
const store = useStores()
const pal = usePalette('error')
const showOfflineDetails = () => {
store.shell.openModal({
name: 'offline-details',
})
}
const containerStyles: ViewStyle[] = [
styles.container,
s.flexRow,
s.alignCenter,
pal.view,
]
return (
<TouchableOpacity
style={containerStyles}
onPress={showOfflineDetails}
accessibilityRole="button"
accessibilityLabel="Show offline details"
accessibilityHint="Opens modal content describing offline behavior of the app">
<FontAwesomeIcon icon="wifi" style={s.white as FontAwesomeIconStyle} />
<Text style={[pal.text, s.f13, s.ml5]}>You're offline</Text>
</TouchableOpacity>
)
}
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 <Indicator />
}
return (
<View style={isDesktopWeb ? styles.desktopWrapper : styles.mobileWrapper}>
<Indicator />
</View>
)
}
const styles = StyleSheet.create({
container: {
width: isNative ? s.window.width : undefined,
justifyContent: 'center',
height: 18,
},
// This follows the positioning of the left nav bar on desktop
desktopWrapper: {
position: 'absolute',
top: 0,
zIndex: 1,
right: 'calc(50vw + 312px)',
width: 220,
},
mobileWrapper: {
position: 'absolute',
left: 0,
bottom: 0,
width: '100%',
},
})
+2
View File
@@ -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,
)
}
+2
View File
@@ -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/offline/Indicator'
const ShellInner = observer(() => {
const store = useStores()
@@ -46,6 +47,7 @@ const ShellInner = observer(() => {
<>
<View style={containerPadding}>
<ErrorBoundary>
<OfflineIndicator />
<Drawer
renderDrawerContent={renderDrawerContent}
open={store.shell.isDrawerOpen}
+4
View File
@@ -16,6 +16,8 @@ import {useWebMediaQueries} from '../../lib/hooks/useWebMediaQueries'
import {BottomBarWeb} from './bottom-bar/BottomBarWeb'
import {useNavigation} from '@react-navigation/native'
import {NavigationProp} from 'lib/routes/types'
import {OfflineIndicator} from 'view/com/offline/Indicator'
import {isMobileWeb} from 'platform/detection'
const ShellInner = observer(() => {
const store = useStores()
@@ -36,6 +38,7 @@ const ShellInner = observer(() => {
<FlatNavigator />
</ErrorBoundary>
</View>
{isDesktop && <OfflineIndicator />}
{isDesktop && store.session.hasSession && (
<>
<DesktopLeftNav />
@@ -51,6 +54,7 @@ const ShellInner = observer(() => {
onPost={store.shell.composerOpts?.onPost}
/>
{!isDesktop && <BottomBarWeb />}
{isMobileWeb && <OfflineIndicator />}
<ModalsContainer />
<Lightbox />
{!isDesktop && store.shell.isDrawerOpen && (
+5
View File
@@ -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"