✨ Add modal describing offline behavior
This commit is contained in:
@@ -36,6 +36,10 @@ export interface ProfilePreviewModal {
|
|||||||
did: string
|
did: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OfflineDetailsModal {
|
||||||
|
name: 'offline-details'
|
||||||
|
}
|
||||||
|
|
||||||
export interface ServerInputModal {
|
export interface ServerInputModal {
|
||||||
name: 'server-input'
|
name: 'server-input'
|
||||||
initialService: string
|
initialService: string
|
||||||
@@ -180,6 +184,7 @@ export type Modal =
|
|||||||
|
|
||||||
// Generic
|
// Generic
|
||||||
| ConfirmModal
|
| ConfirmModal
|
||||||
|
| OfflineDetailsModal
|
||||||
|
|
||||||
interface LightboxModel {}
|
interface LightboxModel {}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import * as PostLanguagesSettingsModal from './lang-settings/PostLanguagesSettin
|
|||||||
import * as PreferencesHomeFeed from './PreferencesHomeFeed'
|
import * as PreferencesHomeFeed from './PreferencesHomeFeed'
|
||||||
import * as OnboardingModal from './OnboardingModal'
|
import * as OnboardingModal from './OnboardingModal'
|
||||||
import * as ModerationDetailsModal from './ModerationDetails'
|
import * as ModerationDetailsModal from './ModerationDetails'
|
||||||
|
import * as OfflineDetailsModal from '../offline/DetailsModal'
|
||||||
|
|
||||||
const DEFAULT_SNAPPOINTS = ['90%']
|
const DEFAULT_SNAPPOINTS = ['90%']
|
||||||
|
|
||||||
@@ -140,10 +141,12 @@ export const ModalsContainer = observer(function ModalsContainer() {
|
|||||||
} else if (activeModal?.name === 'moderation-details') {
|
} else if (activeModal?.name === 'moderation-details') {
|
||||||
snapPoints = ModerationDetailsModal.snapPoints
|
snapPoints = ModerationDetailsModal.snapPoints
|
||||||
element = <ModerationDetailsModal.Component {...activeModal} />
|
element = <ModerationDetailsModal.Component {...activeModal} />
|
||||||
|
} else if (activeModal?.name === 'offline-details') {
|
||||||
|
snapPoints = OfflineDetailsModal.snapPoints
|
||||||
|
element = <OfflineDetailsModal.Component />
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (snapPoints[0] === 'fullscreen') {
|
if (snapPoints[0] === 'fullscreen') {
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={[styles.fullscreenContainer, pal.view]}>
|
<SafeAreaView style={[styles.fullscreenContainer, pal.view]}>
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguages
|
|||||||
import * as PostLanguagesSettingsModal from './lang-settings/PostLanguagesSettings'
|
import * as PostLanguagesSettingsModal from './lang-settings/PostLanguagesSettings'
|
||||||
import * as OnboardingModal from './OnboardingModal'
|
import * as OnboardingModal from './OnboardingModal'
|
||||||
import * as ModerationDetailsModal from './ModerationDetails'
|
import * as ModerationDetailsModal from './ModerationDetails'
|
||||||
|
import * as OfflineDetailsModal from '../offline/DetailsModal'
|
||||||
|
|
||||||
import * as PreferencesHomeFeed from './PreferencesHomeFeed'
|
import * as PreferencesHomeFeed from './PreferencesHomeFeed'
|
||||||
|
|
||||||
@@ -113,6 +114,8 @@ function Modal({modal}: {modal: ModalIface}) {
|
|||||||
element = <OnboardingModal.Component />
|
element = <OnboardingModal.Component />
|
||||||
} else if (modal.name === 'moderation-details') {
|
} else if (modal.name === 'moderation-details') {
|
||||||
element = <ModerationDetailsModal.Component {...modal} />
|
element = <ModerationDetailsModal.Component {...modal} />
|
||||||
|
} else if (modal.name === 'offline-details') {
|
||||||
|
element = <OfflineDetailsModal.Component />
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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]}>
|
||||||
|
While 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]}>
|
||||||
|
We will try out best to sync your basic interactions such as likes and
|
||||||
|
follows once 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,
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, {useEffect, useState} from 'react'
|
import React, {useEffect, useState} from 'react'
|
||||||
import {StyleSheet, View, ViewStyle} from 'react-native'
|
import {StyleSheet, TouchableOpacity, View, ViewStyle} from 'react-native'
|
||||||
import NetInfo from '@react-native-community/netinfo'
|
import NetInfo from '@react-native-community/netinfo'
|
||||||
import {
|
import {
|
||||||
FontAwesomeIcon,
|
FontAwesomeIcon,
|
||||||
@@ -10,9 +10,16 @@ import {usePalette} from 'lib/hooks/usePalette'
|
|||||||
import {Text} from 'view/com/util/text/Text'
|
import {Text} from 'view/com/util/text/Text'
|
||||||
import {s} from 'lib/styles'
|
import {s} from 'lib/styles'
|
||||||
import {isDesktopWeb, isNative} from 'platform/detection'
|
import {isDesktopWeb, isNative} from 'platform/detection'
|
||||||
|
import {useStores} from 'state/index'
|
||||||
|
|
||||||
const Indicator = () => {
|
const Indicator = () => {
|
||||||
|
const store = useStores()
|
||||||
const pal = usePalette('error')
|
const pal = usePalette('error')
|
||||||
|
const showOfflineDetails = () => {
|
||||||
|
store.shell.openModal({
|
||||||
|
name: 'offline-details',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const containerStyles: ViewStyle[] = [
|
const containerStyles: ViewStyle[] = [
|
||||||
styles.container,
|
styles.container,
|
||||||
@@ -22,10 +29,15 @@ const Indicator = () => {
|
|||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={containerStyles}>
|
<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} />
|
<FontAwesomeIcon icon="wifi" style={s.white as FontAwesomeIconStyle} />
|
||||||
<Text style={[pal.text, s.f13, s.ml5]}>You're offline</Text>
|
<Text style={[pal.text, s.f13, s.ml5]}>You're offline</Text>
|
||||||
</View>
|
</TouchableOpacity>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,14 +51,14 @@ export const OfflineIndicator = () => {
|
|||||||
return () => unsubscribe()
|
return () => unsubscribe()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
if (isConnected) return null
|
if (!isConnected) return null
|
||||||
|
|
||||||
if (isNative) {
|
if (isNative) {
|
||||||
return <Indicator />
|
return <Indicator />
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={isDesktopWeb ? undefined : styles.mobileWrapper}>
|
<View style={isDesktopWeb ? styles.desktopWrapper : styles.mobileWrapper}>
|
||||||
<Indicator />
|
<Indicator />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
@@ -56,7 +68,15 @@ const styles = StyleSheet.create({
|
|||||||
container: {
|
container: {
|
||||||
width: isNative ? s.window.width : undefined,
|
width: isNative ? s.window.width : undefined,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
height: 20,
|
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: {
|
mobileWrapper: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@@ -37,7 +37,6 @@ import {getCurrentRoute, isTab, isStateAtTabRoot} from 'lib/routes/helpers'
|
|||||||
import {NavigationProp, CommonNavigatorParams} from 'lib/routes/types'
|
import {NavigationProp, CommonNavigatorParams} from 'lib/routes/types'
|
||||||
import {router} from '../../../routes'
|
import {router} from '../../../routes'
|
||||||
import {makeProfileLink} from 'lib/routes/links'
|
import {makeProfileLink} from 'lib/routes/links'
|
||||||
import {OfflineIndicator} from 'view/com/util/OfflineIndicator'
|
|
||||||
|
|
||||||
const ProfileCard = observer(() => {
|
const ProfileCard = observer(() => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
@@ -180,7 +179,6 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.leftNav, pal.view]}>
|
<View style={[styles.leftNav, pal.view]}>
|
||||||
<OfflineIndicator />
|
|
||||||
{store.session.hasSession && <ProfileCard />}
|
{store.session.hasSession && <ProfileCard />}
|
||||||
<BackBtn />
|
<BackBtn />
|
||||||
<NavItem
|
<NavItem
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {RoutesContainer, TabsNavigator} from '../../Navigation'
|
|||||||
import {isStateAtTabRoot} from 'lib/routes/helpers'
|
import {isStateAtTabRoot} from 'lib/routes/helpers'
|
||||||
import {SafeAreaProvider} from 'react-native-safe-area-context'
|
import {SafeAreaProvider} from 'react-native-safe-area-context'
|
||||||
import {useOTAUpdate} from 'lib/hooks/useOTAUpdate'
|
import {useOTAUpdate} from 'lib/hooks/useOTAUpdate'
|
||||||
import {OfflineIndicator} from 'view/com/util/OfflineIndicator'
|
import {OfflineIndicator} from 'view/com/offline/Indicator'
|
||||||
|
|
||||||
const ShellInner = observer(() => {
|
const ShellInner = observer(() => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ import {useWebMediaQueries} from '../../lib/hooks/useWebMediaQueries'
|
|||||||
import {BottomBarWeb} from './bottom-bar/BottomBarWeb'
|
import {BottomBarWeb} from './bottom-bar/BottomBarWeb'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
import {NavigationProp} from 'lib/routes/types'
|
import {NavigationProp} from 'lib/routes/types'
|
||||||
import {OfflineIndicator} from 'view/com/util/OfflineIndicator'
|
import {OfflineIndicator} from 'view/com/offline/Indicator'
|
||||||
|
import {isMobileWeb} from 'platform/detection'
|
||||||
|
|
||||||
const ShellInner = observer(() => {
|
const ShellInner = observer(() => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
@@ -37,9 +38,9 @@ const ShellInner = observer(() => {
|
|||||||
<FlatNavigator />
|
<FlatNavigator />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</View>
|
</View>
|
||||||
|
{isDesktop && <OfflineIndicator />}
|
||||||
{isDesktop && store.session.hasSession && (
|
{isDesktop && store.session.hasSession && (
|
||||||
<>
|
<>
|
||||||
<OfflineIndicator />
|
|
||||||
<DesktopLeftNav />
|
<DesktopLeftNav />
|
||||||
<DesktopRightNav />
|
<DesktopRightNav />
|
||||||
</>
|
</>
|
||||||
@@ -52,12 +53,8 @@ const ShellInner = observer(() => {
|
|||||||
quote={store.shell.composerOpts?.quote}
|
quote={store.shell.composerOpts?.quote}
|
||||||
onPost={store.shell.composerOpts?.onPost}
|
onPost={store.shell.composerOpts?.onPost}
|
||||||
/>
|
/>
|
||||||
{!isDesktop && (
|
{!isDesktop && <BottomBarWeb />}
|
||||||
<>
|
{isMobileWeb && <OfflineIndicator />}
|
||||||
<BottomBarWeb />
|
|
||||||
<OfflineIndicator />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<ModalsContainer />
|
<ModalsContainer />
|
||||||
<Lightbox />
|
<Lightbox />
|
||||||
{!isDesktop && store.shell.isDrawerOpen && (
|
{!isDesktop && store.shell.isDrawerOpen && (
|
||||||
|
|||||||
Reference in New Issue
Block a user