🤫 (#2211)
* Add new assets * Add splashiness * Add butter icon, spread it * Cream together eggs, sugar, and vanilla * Hi, I'd like to place and order. Yeah, none pizza with left beef, plz. * test * Refine animation * tweak * tweak * tweak * Tweak * Simplify * Cleanup * Fix android logo --------- Co-authored-by: Ansh Nanda <anshnanda10@gmail.com>zio/stable
|
@ -43,7 +43,7 @@ module.exports = function () {
|
||||||
icon: './assets/icon.png',
|
icon: './assets/icon.png',
|
||||||
userInterfaceStyle: 'automatic',
|
userInterfaceStyle: 'automatic',
|
||||||
splash: {
|
splash: {
|
||||||
image: './assets/cloud-splash.png',
|
image: './assets/splash.png',
|
||||||
resizeMode: 'cover',
|
resizeMode: 'cover',
|
||||||
backgroundColor: '#ffffff',
|
backgroundColor: '#ffffff',
|
||||||
},
|
},
|
||||||
|
@ -73,9 +73,12 @@ module.exports = function () {
|
||||||
},
|
},
|
||||||
android: {
|
android: {
|
||||||
versionCode: ANDROID_VERSION_CODE,
|
versionCode: ANDROID_VERSION_CODE,
|
||||||
|
icon: './assets/icon.png',
|
||||||
adaptiveIcon: {
|
adaptiveIcon: {
|
||||||
foregroundImage: './assets/adaptive-icon.png',
|
foregroundImage: './assets/icon-android-foreground.png',
|
||||||
backgroundColor: '#ffffff',
|
monochromeImage: './assets/icon-android-foreground.png',
|
||||||
|
backgroundImage: './assets/icon-android-background.png',
|
||||||
|
backgroundColor: '#1185FE',
|
||||||
},
|
},
|
||||||
googleServicesFile: './google-services.json',
|
googleServicesFile: './google-services.json',
|
||||||
package: 'xyz.blueskyweb.app',
|
package: 'xyz.blueskyweb.app',
|
||||||
|
|
Before Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 696 KiB |
Before Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 223 KiB |
After Width: | Height: | Size: 12 KiB |
BIN
assets/icon.png
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 237 KiB |
After Width: | Height: | Size: 360 KiB |
After Width: | Height: | Size: 354 KiB |
Before Width: | Height: | Size: 78 KiB |
|
@ -54,6 +54,7 @@
|
||||||
"@react-native-clipboard/clipboard": "^1.10.0",
|
"@react-native-clipboard/clipboard": "^1.10.0",
|
||||||
"@react-native-community/blur": "^4.3.0",
|
"@react-native-community/blur": "^4.3.0",
|
||||||
"@react-native-community/datetimepicker": "7.2.0",
|
"@react-native-community/datetimepicker": "7.2.0",
|
||||||
|
"@react-native-masked-view/masked-view": "^0.3.1",
|
||||||
"@react-native-menu/menu": "^0.8.0",
|
"@react-native-menu/menu": "^0.8.0",
|
||||||
"@react-native-picker/picker": "2.4.10",
|
"@react-native-picker/picker": "2.4.10",
|
||||||
"@react-navigation/bottom-tabs": "^6.5.7",
|
"@react-navigation/bottom-tabs": "^6.5.7",
|
||||||
|
|
|
@ -6,6 +6,10 @@ import {RootSiblingParent} from 'react-native-root-siblings'
|
||||||
import * as SplashScreen from 'expo-splash-screen'
|
import * as SplashScreen from 'expo-splash-screen'
|
||||||
import {GestureHandlerRootView} from 'react-native-gesture-handler'
|
import {GestureHandlerRootView} from 'react-native-gesture-handler'
|
||||||
import {QueryClientProvider} from '@tanstack/react-query'
|
import {QueryClientProvider} from '@tanstack/react-query'
|
||||||
|
import {
|
||||||
|
SafeAreaProvider,
|
||||||
|
initialWindowMetrics,
|
||||||
|
} from 'react-native-safe-area-context'
|
||||||
|
|
||||||
import 'view/icons'
|
import 'view/icons'
|
||||||
|
|
||||||
|
@ -34,6 +38,7 @@ import {
|
||||||
} from 'state/session'
|
} from 'state/session'
|
||||||
import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unread'
|
import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unread'
|
||||||
import * as persisted from '#/state/persisted'
|
import * as persisted from '#/state/persisted'
|
||||||
|
import {Splash} from '#/Splash'
|
||||||
|
|
||||||
SplashScreen.preventAutoHideAsync()
|
SplashScreen.preventAutoHideAsync()
|
||||||
|
|
||||||
|
@ -53,10 +58,9 @@ function InnerApp() {
|
||||||
resumeSession(account)
|
resumeSession(account)
|
||||||
}, [resumeSession])
|
}, [resumeSession])
|
||||||
|
|
||||||
// wait for session to resume
|
|
||||||
if (isInitialLoad) return null
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
|
||||||
|
<Splash isReady={!isInitialLoad}>
|
||||||
<React.Fragment
|
<React.Fragment
|
||||||
// Resets the entire tree below when it changes:
|
// Resets the entire tree below when it changes:
|
||||||
key={currentAccount?.did}>
|
key={currentAccount?.did}>
|
||||||
|
@ -74,6 +78,8 @@ function InnerApp() {
|
||||||
</UnreadNotifsProvider>
|
</UnreadNotifsProvider>
|
||||||
</LoggedOutViewProvider>
|
</LoggedOutViewProvider>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
</Splash>
|
||||||
|
</SafeAreaProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import {StyleSheet} from 'react-native'
|
import {StyleSheet} from 'react-native'
|
||||||
import * as SplashScreen from 'expo-splash-screen'
|
|
||||||
import {
|
import {
|
||||||
NavigationContainer,
|
NavigationContainer,
|
||||||
createNavigationContainerRef,
|
createNavigationContainerRef,
|
||||||
|
@ -493,7 +492,6 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
|
||||||
linking={LINKING}
|
linking={LINKING}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
onReady={() => {
|
onReady={() => {
|
||||||
SplashScreen.hideAsync()
|
|
||||||
logModuleInitTime()
|
logModuleInitTime()
|
||||||
onReady()
|
onReady()
|
||||||
}}>
|
}}>
|
||||||
|
|
|
@ -0,0 +1,154 @@
|
||||||
|
import React, {useCallback, useEffect} from 'react'
|
||||||
|
import {View, StyleSheet} from 'react-native'
|
||||||
|
import * as SplashScreen from 'expo-splash-screen'
|
||||||
|
import LinearGradient from 'react-native-linear-gradient'
|
||||||
|
import Animated, {
|
||||||
|
interpolate,
|
||||||
|
runOnJS,
|
||||||
|
useAnimatedStyle,
|
||||||
|
useSharedValue,
|
||||||
|
withTiming,
|
||||||
|
Easing,
|
||||||
|
} from 'react-native-reanimated'
|
||||||
|
import MaskedView from '@react-native-masked-view/masked-view'
|
||||||
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
|
import Svg, {Path, SvgProps} from 'react-native-svg'
|
||||||
|
|
||||||
|
export const Logo = React.forwardRef(function LogoImpl(props: SvgProps, ref) {
|
||||||
|
const width = 1000
|
||||||
|
const height = width * (67 / 64)
|
||||||
|
return (
|
||||||
|
<Svg
|
||||||
|
fill="none"
|
||||||
|
// @ts-ignore it's fiiiiine
|
||||||
|
ref={ref}
|
||||||
|
viewBox="0 0 64 66"
|
||||||
|
style={{width, height}}>
|
||||||
|
<Path
|
||||||
|
fill="#fff"
|
||||||
|
d="M13.873 3.77C21.21 9.243 29.103 20.342 32 26.3v15.732c0-.335-.13.043-.41.858-1.512 4.414-7.418 21.642-20.923 7.87-7.111-7.252-3.819-14.503 9.125-16.692-7.405 1.252-15.73-.817-18.014-8.93C1.12 22.804 0 8.431 0 6.488 0-3.237 8.579-.18 13.873 3.77ZM50.127 3.77C42.79 9.243 34.897 20.342 32 26.3v15.732c0-.335.13.043.41.858 1.512 4.414 7.418 21.642 20.923 7.87 7.111-7.252 3.819-14.503-9.125-16.692 7.405 1.252 15.73-.817 18.014-8.93C62.88 22.804 64 8.431 64 6.488 64-3.237 55.422-.18 50.127 3.77Z"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
isReady: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
SplashScreen.preventAutoHideAsync().catch(() => {})
|
||||||
|
|
||||||
|
const AnimatedLogo = Animated.createAnimatedComponent(Logo)
|
||||||
|
|
||||||
|
export function Splash(props: React.PropsWithChildren<Props>) {
|
||||||
|
const insets = useSafeAreaInsets()
|
||||||
|
const intro = useSharedValue(0)
|
||||||
|
const outroLogo = useSharedValue(0)
|
||||||
|
const outroApp = useSharedValue(0)
|
||||||
|
const [isAnimationComplete, setIsAnimationComplete] = React.useState(false)
|
||||||
|
|
||||||
|
const logoAnimations = useAnimatedStyle(() => {
|
||||||
|
return {
|
||||||
|
transform: [
|
||||||
|
{
|
||||||
|
scale: interpolate(intro.value, [0, 1], [0.8, 1], 'clamp'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scale: interpolate(
|
||||||
|
outroLogo.value,
|
||||||
|
[0, 0.06, 0.08, 1],
|
||||||
|
[1, 0.8, 0.8, 800],
|
||||||
|
'clamp',
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
opacity: interpolate(intro.value, [0, 1], [0, 1], 'clamp'),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const appAnimation = useAnimatedStyle(() => {
|
||||||
|
return {
|
||||||
|
transform: [
|
||||||
|
{
|
||||||
|
scale: interpolate(
|
||||||
|
outroApp.value,
|
||||||
|
[0, 0.7, 1],
|
||||||
|
[1.1, 1.1, 1],
|
||||||
|
'clamp',
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
opacity: interpolate(outroApp.value, [0, 0.7, 1], [0, 0, 1], 'clamp'),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const onFinish = useCallback(() => setIsAnimationComplete(true), [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.isReady) {
|
||||||
|
// hide on mount
|
||||||
|
SplashScreen.hideAsync().catch(() => {})
|
||||||
|
|
||||||
|
intro.value = withTiming(
|
||||||
|
1,
|
||||||
|
{duration: 200, easing: Easing.out(Easing.cubic)},
|
||||||
|
async () => {
|
||||||
|
outroLogo.value = withTiming(
|
||||||
|
1,
|
||||||
|
{duration: 1200, easing: Easing.in(Easing.cubic)},
|
||||||
|
() => {
|
||||||
|
runOnJS(onFinish)()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
outroApp.value = withTiming(
|
||||||
|
1,
|
||||||
|
{duration: 1200, easing: Easing.inOut(Easing.cubic)},
|
||||||
|
() => {
|
||||||
|
runOnJS(onFinish)()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}, [onFinish, intro, outroLogo, outroApp, props.isReady])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={{flex: 1}}>
|
||||||
|
{!isAnimationComplete && (
|
||||||
|
<LinearGradient
|
||||||
|
colors={['#0A7AFF', '#59B9FF']}
|
||||||
|
style={[StyleSheet.absoluteFillObject]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<MaskedView
|
||||||
|
style={[StyleSheet.absoluteFillObject]}
|
||||||
|
maskElement={
|
||||||
|
<Animated.View
|
||||||
|
style={[
|
||||||
|
StyleSheet.absoluteFillObject,
|
||||||
|
{
|
||||||
|
// Transparent background because mask is based off alpha channel.
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
transform: [{translateY: -(insets.top / 2)}, {scale: 0.1}], // scale from 1000px to 100px
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<AnimatedLogo style={[logoAnimations]} />
|
||||||
|
</Animated.View>
|
||||||
|
}>
|
||||||
|
{!isAnimationComplete && (
|
||||||
|
<View
|
||||||
|
style={[StyleSheet.absoluteFillObject, {backgroundColor: 'white'}]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Animated.View style={[{flex: 1}, appAnimation]}>
|
||||||
|
{props.children}
|
||||||
|
</Animated.View>
|
||||||
|
</MaskedView>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
import {ImageRequireSource} from 'react-native'
|
import {ImageRequireSource} from 'react-native'
|
||||||
|
|
||||||
export const DEF_AVATAR: ImageRequireSource = require('../../assets/default-avatar.jpg')
|
export const DEF_AVATAR: ImageRequireSource = require('../../assets/default-avatar.png')
|
||||||
export const TABS_EXPLAINER: ImageRequireSource = require('../../assets/tabs-explainer.jpg')
|
export const CLOUD_SPLASH: ImageRequireSource = require('../../assets/splash.png')
|
||||||
export const CLOUD_SPLASH: ImageRequireSource = require('../../assets/cloud-splash.png')
|
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
import {ImageRequireSource} from 'react-native'
|
import {ImageRequireSource} from 'react-native'
|
||||||
|
|
||||||
// @ts-ignore we need to pretend -prf
|
// @ts-ignore we need to pretend -prf
|
||||||
export const DEF_AVATAR: ImageRequireSource = {uri: '/img/default-avatar.jpg'}
|
export const DEF_AVATAR: ImageRequireSource = {uri: '/img/default-avatar.png'}
|
||||||
// @ts-ignore we need to pretend -prf
|
// @ts-ignore we need to pretend -prf
|
||||||
export const TABS_EXPLAINER: ImageRequireSource = {
|
export const CLOUD_SPLASH: ImageRequireSource = {uri: '/img/splash.png'}
|
||||||
uri: '/img/tabs-explainer.jpg',
|
|
||||||
}
|
|
||||||
// @ts-ignore we need to pretend -prf
|
|
||||||
export const CLOUD_SPLASH: ImageRequireSource = {uri: '/img/cloud-splash.png'}
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {CenteredView} from '../util/Views'
|
import {CenteredView} from '../util/Views'
|
||||||
import {Trans, msg} from '@lingui/macro'
|
import {Trans, msg} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {Logo} from '#/view/icons/Logo'
|
||||||
|
import {Logotype} from '#/view/icons/Logotype'
|
||||||
|
|
||||||
export const SplashScreen = ({
|
export const SplashScreen = ({
|
||||||
onPressSignin,
|
onPressSignin,
|
||||||
|
@ -22,11 +24,14 @@ export const SplashScreen = ({
|
||||||
<CenteredView style={[styles.container, pal.view]}>
|
<CenteredView style={[styles.container, pal.view]}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<View style={styles.hero}>
|
<View style={styles.hero}>
|
||||||
<Text style={[styles.title, pal.link]}>
|
<Logo width={92} fill="sky" />
|
||||||
<Trans>Bluesky</Trans>
|
|
||||||
</Text>
|
<View style={{paddingTop: 40, paddingBottom: 6}}>
|
||||||
<Text style={[styles.subtitle, pal.textLight]}>
|
<Logotype width={161} />
|
||||||
<Trans>See what's next</Trans>
|
</View>
|
||||||
|
|
||||||
|
<Text type="lg-medium" style={[pal.textLight]}>
|
||||||
|
<Trans>What's next?</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View testID="signinOrCreateAccount" style={styles.btns}>
|
<View testID="signinOrCreateAccount" style={styles.btns}>
|
||||||
|
@ -65,6 +70,7 @@ const styles = StyleSheet.create({
|
||||||
hero: {
|
hero: {
|
||||||
flex: 2,
|
flex: 2,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
btns: {
|
btns: {
|
||||||
paddingBottom: 40,
|
paddingBottom: 40,
|
||||||
|
|
|
@ -10,6 +10,8 @@ import {CenteredView} from '../util/Views'
|
||||||
import {isWeb} from 'platform/detection'
|
import {isWeb} from 'platform/detection'
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {Trans} from '@lingui/macro'
|
import {Trans} from '@lingui/macro'
|
||||||
|
import {Logo} from '#/view/icons/Logo'
|
||||||
|
import {Logotype} from '#/view/icons/Logotype'
|
||||||
|
|
||||||
export const SplashScreen = ({
|
export const SplashScreen = ({
|
||||||
onDismiss,
|
onDismiss,
|
||||||
|
@ -55,14 +57,15 @@ export const SplashScreen = ({
|
||||||
styles.containerInner,
|
styles.containerInner,
|
||||||
isMobileWeb && styles.containerInnerMobile,
|
isMobileWeb && styles.containerInnerMobile,
|
||||||
pal.border,
|
pal.border,
|
||||||
|
{alignItems: 'center'},
|
||||||
]}>
|
]}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<Text style={isMobileWeb ? styles.titleMobile : styles.title}>
|
<Logo width={92} fill="sky" />
|
||||||
Bluesky
|
|
||||||
</Text>
|
<View style={{paddingTop: 40, paddingBottom: 20}}>
|
||||||
<Text style={isMobileWeb ? styles.subtitleMobile : styles.subtitle}>
|
<Logotype width={161} />
|
||||||
See what's next
|
</View>
|
||||||
</Text>
|
|
||||||
<View testID="signinOrCreateAccount" style={styles.btns}>
|
<View testID="signinOrCreateAccount" style={styles.btns}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
testID="createAccountButton"
|
testID="createAccountButton"
|
||||||
|
@ -117,8 +120,6 @@ function Footer({styles}: {styles: ReturnType<typeof useStyles>}) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const useStyles = () => {
|
const useStyles = () => {
|
||||||
const {isTabletOrMobile} = useWebMediaQueries()
|
|
||||||
const isMobileWeb = isWeb && isTabletOrMobile
|
|
||||||
return StyleSheet.create({
|
return StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
height: '100%',
|
height: '100%',
|
||||||
|
@ -161,8 +162,7 @@ const useStyles = () => {
|
||||||
paddingBottom: 30,
|
paddingBottom: 30,
|
||||||
},
|
},
|
||||||
btns: {
|
btns: {
|
||||||
flexDirection: isMobileWeb ? 'column' : 'row',
|
gap: 10,
|
||||||
gap: 20,
|
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
paddingBottom: 40,
|
paddingBottom: 40,
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,12 +3,9 @@ import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||||
import {TabBar} from 'view/com/pager/TabBar'
|
import {TabBar} from 'view/com/pager/TabBar'
|
||||||
import {RenderTabBarFnProps} from 'view/com/pager/Pager'
|
import {RenderTabBarFnProps} from 'view/com/pager/Pager'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
|
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {Text} from '../util/text/Text'
|
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
|
||||||
import {s} from 'lib/styles'
|
|
||||||
import {HITSLOP_10} from 'lib/constants'
|
import {HITSLOP_10} from 'lib/constants'
|
||||||
import Animated from 'react-native-reanimated'
|
import Animated from 'react-native-reanimated'
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
|
@ -21,17 +18,17 @@ import {usePinnedFeedsInfos} from '#/state/queries/feed'
|
||||||
import {isWeb} from 'platform/detection'
|
import {isWeb} from 'platform/detection'
|
||||||
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 {Logo} from '#/view/icons/Logo'
|
||||||
|
|
||||||
export function FeedsTabBar(
|
export function FeedsTabBar(
|
||||||
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
|
props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
|
||||||
) {
|
) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {isSandbox, hasSession} = useSession()
|
const {hasSession} = useSession()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const setDrawerOpen = useSetDrawerOpen()
|
const setDrawerOpen = useSetDrawerOpen()
|
||||||
const navigation = useNavigation<NavigationProp>()
|
const navigation = useNavigation<NavigationProp>()
|
||||||
const {feeds, hasPinnedCustom} = usePinnedFeedsInfos()
|
const {feeds, hasPinnedCustom} = usePinnedFeedsInfos()
|
||||||
const brandBlue = useColorSchemeStyle(s.brandBlue, s.blue3)
|
|
||||||
const {headerHeight} = useShellLayout()
|
const {headerHeight} = useShellLayout()
|
||||||
const {headerMinimalShellTransform} = useMinimalShellMode()
|
const {headerMinimalShellTransform} = useMinimalShellMode()
|
||||||
const pinnedDisplayNames = hasSession ? feeds.map(f => f.displayName) : []
|
const pinnedDisplayNames = hasSession ? feeds.map(f => f.displayName) : []
|
||||||
|
@ -86,9 +83,9 @@ export function FeedsTabBar(
|
||||||
/>
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
<Text style={[brandBlue, s.bold, styles.title]}>
|
<View>
|
||||||
{isSandbox ? 'SANDBOX' : 'Bluesky'}
|
<Logo width={30} />
|
||||||
</Text>
|
</View>
|
||||||
<View style={[pal.view, {width: 18}]}>
|
<View style={[pal.view, {width: 18}]}>
|
||||||
{hasSession && (
|
{hasSession && (
|
||||||
<Link
|
<Link
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
import React from 'react'
|
||||||
|
import Svg, {
|
||||||
|
Path,
|
||||||
|
Defs,
|
||||||
|
LinearGradient,
|
||||||
|
Stop,
|
||||||
|
SvgProps,
|
||||||
|
PathProps,
|
||||||
|
} from 'react-native-svg'
|
||||||
|
|
||||||
|
import {colors} from '#/lib/styles'
|
||||||
|
|
||||||
|
const ratio = 57 / 64
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
fill?: PathProps['fill']
|
||||||
|
} & SvgProps
|
||||||
|
|
||||||
|
export const Logo = React.forwardRef(function LogoImpl(props: Props, ref) {
|
||||||
|
const {fill, ...rest} = props
|
||||||
|
const gradient = fill === 'sky'
|
||||||
|
const _fill = gradient ? 'url(#sky)' : fill || colors.blue3
|
||||||
|
// @ts-ignore it's fiiiiine
|
||||||
|
const size = parseInt(rest.width || 32)
|
||||||
|
return (
|
||||||
|
<Svg
|
||||||
|
fill="none"
|
||||||
|
// @ts-ignore it's fiiiiine
|
||||||
|
ref={ref}
|
||||||
|
viewBox="0 0 64 57"
|
||||||
|
{...rest}
|
||||||
|
style={{width: size, height: size * ratio}}>
|
||||||
|
{gradient && (
|
||||||
|
<Defs>
|
||||||
|
<LinearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<Stop offset="0" stopColor="#0A7AFF" stopOpacity="1" />
|
||||||
|
<Stop offset="1" stopColor="#59B9FF" stopOpacity="1" />
|
||||||
|
</LinearGradient>
|
||||||
|
</Defs>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Path
|
||||||
|
fill={_fill}
|
||||||
|
d="M13.873 3.805C21.21 9.332 29.103 20.537 32 26.55v15.882c0-.338-.13.044-.41.867-1.512 4.456-7.418 21.847-20.923 7.944-7.111-7.32-3.819-14.64 9.125-16.85-7.405 1.264-15.73-.825-18.014-9.015C1.12 23.022 0 8.51 0 6.55 0-3.268 8.579-.182 13.873 3.805ZM50.127 3.805C42.79 9.332 34.897 20.537 32 26.55v15.882c0-.338.13.044.41.867 1.512 4.456 7.418 21.847 20.923 7.944 7.111-7.32 3.819-14.64-9.125-16.85 7.405 1.264 15.73-.825 18.014-9.015C62.88 23.022 64 8.51 64 6.55c0-9.818-8.578-6.732-13.873-2.745Z"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
|
)
|
||||||
|
})
|
|
@ -0,0 +1,28 @@
|
||||||
|
import React from 'react'
|
||||||
|
import Svg, {Path, SvgProps, PathProps} from 'react-native-svg'
|
||||||
|
|
||||||
|
import {colors} from '#/lib/styles'
|
||||||
|
|
||||||
|
const ratio = 17 / 64
|
||||||
|
|
||||||
|
export function Logotype({
|
||||||
|
fill,
|
||||||
|
...rest
|
||||||
|
}: {fill?: PathProps['fill']} & SvgProps) {
|
||||||
|
// @ts-ignore it's fiiiiine
|
||||||
|
const size = parseInt(rest.width || 32)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Svg
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 64 17"
|
||||||
|
{...rest}
|
||||||
|
width={size}
|
||||||
|
height={Number(size) * ratio}>
|
||||||
|
<Path
|
||||||
|
fill={fill || colors.black}
|
||||||
|
d="M8.478 6.252c1.503.538 2.3 1.78 2.3 3.172 0 2.356-1.576 3.785-4.6 3.785H0V0h5.974c2.875 0 4.267 1.466 4.267 3.413 0 1.3-.594 2.245-1.763 2.839Zm-2.69-4.193H2.504v3.45h3.284c1.28 0 1.967-.667 1.967-1.78 0-1.02-.705-1.67-1.967-1.67Zm-3.284 9.072h3.544c1.41 0 2.17-.65 2.17-1.818 0-1.224-.723-1.837-2.17-1.837H2.504v3.655ZM14.251 13.209h-2.337V0h2.337v13.209ZM22.001 8.998V3.636h2.338v9.573h-2.263v-1.392c-.724 1.076-1.726 1.614-3.006 1.614-2.022 0-3.34-1.224-3.34-3.45V3.636h2.338v5.955c0 1.206.594 1.818 1.8 1.818 1.132 0 2.133-.835 2.133-2.411ZM34.979 8.59v.556h-7.161c.167 1.651 1.076 2.467 2.486 2.467 1.076 0 1.8-.463 2.189-1.372h2.244c-.5 1.947-2.17 3.19-4.452 3.19-1.428 0-2.579-.463-3.45-1.372-.872-.91-1.318-2.115-1.318-3.637 0-1.502.427-2.708 1.299-3.636.872-.909 2.004-1.372 3.432-1.372 1.447 0 2.597.482 3.45 1.428.854.946 1.28 2.208 1.28 3.747Zm-4.75-3.358c-1.28 0-2.17.742-2.393 2.281h4.805c-.204-1.391-1.057-2.281-2.411-2.281ZM40.16 13.469c-2.783 0-4.249-1.095-4.379-3.303h2.282c.13 1.188.724 1.633 2.134 1.633 1.261 0 1.892-.39 1.892-1.15 0-.687-.445-1.02-1.874-1.262l-1.094-.185c-2.097-.353-3.136-1.318-3.136-2.894 0-1.8 1.429-2.894 3.97-2.894 2.728 0 4.138 1.075 4.23 3.246h-2.207c-.056-1.169-.742-1.577-2.023-1.577-1.113 0-1.67.371-1.67 1.113 0 .668.483.965 1.596 1.169l1.206.186c2.32.426 3.32 1.28 3.32 2.912 0 1.93-1.557 3.006-4.247 3.006ZM54.667 13.209h-2.671l-2.783-4.453-1.447 1.447v3.006h-2.3V0h2.3v7.606l3.896-3.97h2.783l-3.618 3.618 3.84 5.955ZM60.772 6.048l.78-2.412H64l-3.692 10.352c-.39 1.057-.872 1.818-1.484 2.245-.612.426-1.484.63-2.634.63-.39 0-.724-.018-1.02-.055V14.97h.89c1.057 0 1.577-.65 1.577-1.54 0-.445-.149-1.094-.446-1.929l-2.746-7.866h2.487l.779 2.393c.575 1.8 1.076 3.58 1.521 5.343.408-1.521.928-3.302 1.54-5.324Z"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
|
)
|
||||||
|
}
|
|
@ -19,10 +19,6 @@ import {useTheme} from 'lib/ThemeContext'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {RoutesContainer, TabsNavigator} from '../../Navigation'
|
import {RoutesContainer, TabsNavigator} from '../../Navigation'
|
||||||
import {isStateAtTabRoot} from 'lib/routes/helpers'
|
import {isStateAtTabRoot} from 'lib/routes/helpers'
|
||||||
import {
|
|
||||||
SafeAreaProvider,
|
|
||||||
initialWindowMetrics,
|
|
||||||
} from 'react-native-safe-area-context'
|
|
||||||
import {
|
import {
|
||||||
useIsDrawerOpen,
|
useIsDrawerOpen,
|
||||||
useSetDrawerOpen,
|
useSetDrawerOpen,
|
||||||
|
@ -107,14 +103,12 @@ export const Shell: React.FC = function ShellImpl() {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
return (
|
return (
|
||||||
<SafeAreaProvider initialMetrics={initialWindowMetrics} style={pal.view}>
|
|
||||||
<View testID="mobileShellView" style={[styles.outerContainer, pal.view]}>
|
<View testID="mobileShellView" style={[styles.outerContainer, pal.view]}>
|
||||||
<StatusBar style={theme.colorScheme === 'dark' ? 'light' : 'dark'} />
|
<StatusBar style={theme.colorScheme === 'dark' ? 'light' : 'dark'} />
|
||||||
<RoutesContainer>
|
<RoutesContainer>
|
||||||
<ShellInner />
|
<ShellInner />
|
||||||
</RoutesContainer>
|
</RoutesContainer>
|
||||||
</View>
|
</View>
|
||||||
</SafeAreaProvider>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4448,6 +4448,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@react-native-community/eslint-plugin/-/eslint-plugin-1.3.0.tgz#9e558170c106bbafaa1ef502bd8e6d4651012bf9"
|
resolved "https://registry.yarnpkg.com/@react-native-community/eslint-plugin/-/eslint-plugin-1.3.0.tgz#9e558170c106bbafaa1ef502bd8e6d4651012bf9"
|
||||||
integrity sha512-+zDZ20NUnSWghj7Ku5aFphMzuM9JulqCW+aPXT6IfIXFbb8tzYTTOSeRFOtuekJ99ibW2fUCSsjuKNlwDIbHFg==
|
integrity sha512-+zDZ20NUnSWghj7Ku5aFphMzuM9JulqCW+aPXT6IfIXFbb8tzYTTOSeRFOtuekJ99ibW2fUCSsjuKNlwDIbHFg==
|
||||||
|
|
||||||
|
"@react-native-masked-view/masked-view@^0.3.1":
|
||||||
|
version "0.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@react-native-masked-view/masked-view/-/masked-view-0.3.1.tgz#5bd76f17004a6ccbcec03856893777ee91f23d29"
|
||||||
|
integrity sha512-uVm8U6nwFIlUd1iDIB5cS+lDadApKR+l8k4k84d9hn+GN4lzAIJhUZ9syYX7c022MxNgAlbxoFLt0pqKoyaAGg==
|
||||||
|
|
||||||
"@react-native-menu/menu@^0.8.0":
|
"@react-native-menu/menu@^0.8.0":
|
||||||
version "0.8.0"
|
version "0.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/@react-native-menu/menu/-/menu-0.8.0.tgz#dbf227c2081e5ffd3d2073ee68ecc84cf8639727"
|
resolved "https://registry.yarnpkg.com/@react-native-menu/menu/-/menu-0.8.0.tgz#dbf227c2081e5ffd3d2073ee68ecc84cf8639727"
|
||||||
|
|