about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--app.config.js9
-rw-r--r--assets/adaptive-icon.pngbin1172966 -> 0 bytes
-rw-r--r--assets/cloud-splash.pngbin712345 -> 0 bytes
-rw-r--r--assets/default-avatar.jpgbin14528 -> 0 bytes
-rw-r--r--assets/default-avatar.pngbin0 -> 8333 bytes
-rw-r--r--assets/favicon.pngbin2604 -> 1412 bytes
-rw-r--r--assets/icon-android-background.pngbin0 -> 228644 bytes
-rw-r--r--assets/icon-android-foreground.pngbin0 -> 12108 bytes
-rw-r--r--assets/icon.pngbin1172966 -> 242260 bytes
-rw-r--r--assets/splash-with-logo.pngbin0 -> 368409 bytes
-rw-r--r--assets/splash.pngbin0 -> 362968 bytes
-rw-r--r--assets/tabs-explainer.jpgbin80104 -> 0 bytes
-rw-r--r--package.json1
-rw-r--r--src/App.native.tsx46
-rw-r--r--src/Navigation.tsx2
-rw-r--r--src/Splash.tsx154
-rw-r--r--src/lib/assets.native.ts5
-rw-r--r--src/lib/assets.ts8
-rw-r--r--src/view/com/auth/SplashScreen.tsx16
-rw-r--r--src/view/com/auth/SplashScreen.web.tsx20
-rw-r--r--src/view/com/pager/FeedsTabBarMobile.tsx13
-rw-r--r--src/view/icons/Logo.tsx48
-rw-r--r--src/view/icons/Logotype.tsx28
-rw-r--r--src/view/icons/index.tsx (renamed from src/view/icons.ts)0
-rw-r--r--src/view/shell/index.tsx18
-rw-r--r--yarn.lock5
26 files changed, 304 insertions, 69 deletions
diff --git a/app.config.js b/app.config.js
index 096756b08..d16b494d0 100644
--- a/app.config.js
+++ b/app.config.js
@@ -43,7 +43,7 @@ module.exports = function () {
       icon: './assets/icon.png',
       userInterfaceStyle: 'automatic',
       splash: {
-        image: './assets/cloud-splash.png',
+        image: './assets/splash.png',
         resizeMode: 'cover',
         backgroundColor: '#ffffff',
       },
@@ -73,9 +73,12 @@ module.exports = function () {
       },
       android: {
         versionCode: ANDROID_VERSION_CODE,
+        icon: './assets/icon.png',
         adaptiveIcon: {
-          foregroundImage: './assets/adaptive-icon.png',
-          backgroundColor: '#ffffff',
+          foregroundImage: './assets/icon-android-foreground.png',
+          monochromeImage: './assets/icon-android-foreground.png',
+          backgroundImage: './assets/icon-android-background.png',
+          backgroundColor: '#1185FE',
         },
         googleServicesFile: './google-services.json',
         package: 'xyz.blueskyweb.app',
diff --git a/assets/adaptive-icon.png b/assets/adaptive-icon.png
deleted file mode 100644
index 1dda9a342..000000000
--- a/assets/adaptive-icon.png
+++ /dev/null
Binary files differdiff --git a/assets/cloud-splash.png b/assets/cloud-splash.png
deleted file mode 100644
index 188625331..000000000
--- a/assets/cloud-splash.png
+++ /dev/null
Binary files differdiff --git a/assets/default-avatar.jpg b/assets/default-avatar.jpg
deleted file mode 100644
index ab141651e..000000000
--- a/assets/default-avatar.jpg
+++ /dev/null
Binary files differdiff --git a/assets/default-avatar.png b/assets/default-avatar.png
new file mode 100644
index 000000000..d2bd03842
--- /dev/null
+++ b/assets/default-avatar.png
Binary files differdiff --git a/assets/favicon.png b/assets/favicon.png
index 24ec61e0a..ddf55f4c8 100644
--- a/assets/favicon.png
+++ b/assets/favicon.png
Binary files differdiff --git a/assets/icon-android-background.png b/assets/icon-android-background.png
new file mode 100644
index 000000000..2c9d15382
--- /dev/null
+++ b/assets/icon-android-background.png
Binary files differdiff --git a/assets/icon-android-foreground.png b/assets/icon-android-foreground.png
new file mode 100644
index 000000000..7a6d477d6
--- /dev/null
+++ b/assets/icon-android-foreground.png
Binary files differdiff --git a/assets/icon.png b/assets/icon.png
index 1dda9a342..7dc2c31a6 100644
--- a/assets/icon.png
+++ b/assets/icon.png
Binary files differdiff --git a/assets/splash-with-logo.png b/assets/splash-with-logo.png
new file mode 100644
index 000000000..d012d724c
--- /dev/null
+++ b/assets/splash-with-logo.png
Binary files differdiff --git a/assets/splash.png b/assets/splash.png
new file mode 100644
index 000000000..603f1f379
--- /dev/null
+++ b/assets/splash.png
Binary files differdiff --git a/assets/tabs-explainer.jpg b/assets/tabs-explainer.jpg
deleted file mode 100644
index 64f0a8fe5..000000000
--- a/assets/tabs-explainer.jpg
+++ /dev/null
Binary files differdiff --git a/package.json b/package.json
index 52e4b7eff..d1d12e223 100644
--- a/package.json
+++ b/package.json
@@ -54,6 +54,7 @@
     "@react-native-clipboard/clipboard": "^1.10.0",
     "@react-native-community/blur": "^4.3.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-picker/picker": "2.4.10",
     "@react-navigation/bottom-tabs": "^6.5.7",
diff --git a/src/App.native.tsx b/src/App.native.tsx
index d11d05e70..6402b4a89 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -6,6 +6,10 @@ import {RootSiblingParent} from 'react-native-root-siblings'
 import * as SplashScreen from 'expo-splash-screen'
 import {GestureHandlerRootView} from 'react-native-gesture-handler'
 import {QueryClientProvider} from '@tanstack/react-query'
+import {
+  SafeAreaProvider,
+  initialWindowMetrics,
+} from 'react-native-safe-area-context'
 
 import 'view/icons'
 
@@ -34,6 +38,7 @@ import {
 } from 'state/session'
 import {Provider as UnreadNotifsProvider} from 'state/queries/notifications/unread'
 import * as persisted from '#/state/persisted'
+import {Splash} from '#/Splash'
 
 SplashScreen.preventAutoHideAsync()
 
@@ -53,27 +58,28 @@ function InnerApp() {
     resumeSession(account)
   }, [resumeSession])
 
-  // wait for session to resume
-  if (isInitialLoad) return null
-
   return (
-    <React.Fragment
-      // Resets the entire tree below when it changes:
-      key={currentAccount?.did}>
-      <LoggedOutViewProvider>
-        <UnreadNotifsProvider>
-          <ThemeProvider theme={colorMode}>
-            {/* All components should be within this provider */}
-            <RootSiblingParent>
-              <GestureHandlerRootView style={s.h100pct}>
-                <TestCtrls />
-                <Shell />
-              </GestureHandlerRootView>
-            </RootSiblingParent>
-          </ThemeProvider>
-        </UnreadNotifsProvider>
-      </LoggedOutViewProvider>
-    </React.Fragment>
+    <SafeAreaProvider initialMetrics={initialWindowMetrics}>
+      <Splash isReady={!isInitialLoad}>
+        <React.Fragment
+          // Resets the entire tree below when it changes:
+          key={currentAccount?.did}>
+          <LoggedOutViewProvider>
+            <UnreadNotifsProvider>
+              <ThemeProvider theme={colorMode}>
+                {/* All components should be within this provider */}
+                <RootSiblingParent>
+                  <GestureHandlerRootView style={s.h100pct}>
+                    <TestCtrls />
+                    <Shell />
+                  </GestureHandlerRootView>
+                </RootSiblingParent>
+              </ThemeProvider>
+            </UnreadNotifsProvider>
+          </LoggedOutViewProvider>
+        </React.Fragment>
+      </Splash>
+    </SafeAreaProvider>
   )
 }
 
diff --git a/src/Navigation.tsx b/src/Navigation.tsx
index 24fbb0d81..252699e53 100644
--- a/src/Navigation.tsx
+++ b/src/Navigation.tsx
@@ -1,6 +1,5 @@
 import * as React from 'react'
 import {StyleSheet} from 'react-native'
-import * as SplashScreen from 'expo-splash-screen'
 import {
   NavigationContainer,
   createNavigationContainerRef,
@@ -493,7 +492,6 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
       linking={LINKING}
       theme={theme}
       onReady={() => {
-        SplashScreen.hideAsync()
         logModuleInitTime()
         onReady()
       }}>
diff --git a/src/Splash.tsx b/src/Splash.tsx
new file mode 100644
index 000000000..92ed366f9
--- /dev/null
+++ b/src/Splash.tsx
@@ -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>
+  )
+}
diff --git a/src/lib/assets.native.ts b/src/lib/assets.native.ts
index d7ef9a05e..754bc9d2b 100644
--- a/src/lib/assets.native.ts
+++ b/src/lib/assets.native.ts
@@ -1,5 +1,4 @@
 import {ImageRequireSource} from 'react-native'
 
-export const DEF_AVATAR: ImageRequireSource = require('../../assets/default-avatar.jpg')
-export const TABS_EXPLAINER: ImageRequireSource = require('../../assets/tabs-explainer.jpg')
-export const CLOUD_SPLASH: ImageRequireSource = require('../../assets/cloud-splash.png')
+export const DEF_AVATAR: ImageRequireSource = require('../../assets/default-avatar.png')
+export const CLOUD_SPLASH: ImageRequireSource = require('../../assets/splash.png')
diff --git a/src/lib/assets.ts b/src/lib/assets.ts
index 216478762..8859607d5 100644
--- a/src/lib/assets.ts
+++ b/src/lib/assets.ts
@@ -1,10 +1,6 @@
 import {ImageRequireSource} from 'react-native'
 
 // @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
-export const TABS_EXPLAINER: ImageRequireSource = {
-  uri: '/img/tabs-explainer.jpg',
-}
-// @ts-ignore we need to pretend -prf
-export const CLOUD_SPLASH: ImageRequireSource = {uri: '/img/cloud-splash.png'}
+export const CLOUD_SPLASH: ImageRequireSource = {uri: '/img/splash.png'}
diff --git a/src/view/com/auth/SplashScreen.tsx b/src/view/com/auth/SplashScreen.tsx
index d88627f65..bb2d657ea 100644
--- a/src/view/com/auth/SplashScreen.tsx
+++ b/src/view/com/auth/SplashScreen.tsx
@@ -7,6 +7,8 @@ import {usePalette} from 'lib/hooks/usePalette'
 import {CenteredView} from '../util/Views'
 import {Trans, msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
+import {Logo} from '#/view/icons/Logo'
+import {Logotype} from '#/view/icons/Logotype'
 
 export const SplashScreen = ({
   onPressSignin,
@@ -22,11 +24,14 @@ export const SplashScreen = ({
     <CenteredView style={[styles.container, pal.view]}>
       <ErrorBoundary>
         <View style={styles.hero}>
-          <Text style={[styles.title, pal.link]}>
-            <Trans>Bluesky</Trans>
-          </Text>
-          <Text style={[styles.subtitle, pal.textLight]}>
-            <Trans>See what's next</Trans>
+          <Logo width={92} fill="sky" />
+
+          <View style={{paddingTop: 40, paddingBottom: 6}}>
+            <Logotype width={161} />
+          </View>
+
+          <Text type="lg-medium" style={[pal.textLight]}>
+            <Trans>What's next?</Trans>
           </Text>
         </View>
         <View testID="signinOrCreateAccount" style={styles.btns}>
@@ -65,6 +70,7 @@ const styles = StyleSheet.create({
   hero: {
     flex: 2,
     justifyContent: 'center',
+    alignItems: 'center',
   },
   btns: {
     paddingBottom: 40,
diff --git a/src/view/com/auth/SplashScreen.web.tsx b/src/view/com/auth/SplashScreen.web.tsx
index 08cf701da..4e942f66e 100644
--- a/src/view/com/auth/SplashScreen.web.tsx
+++ b/src/view/com/auth/SplashScreen.web.tsx
@@ -10,6 +10,8 @@ import {CenteredView} from '../util/Views'
 import {isWeb} from 'platform/detection'
 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
 import {Trans} from '@lingui/macro'
+import {Logo} from '#/view/icons/Logo'
+import {Logotype} from '#/view/icons/Logotype'
 
 export const SplashScreen = ({
   onDismiss,
@@ -55,14 +57,15 @@ export const SplashScreen = ({
             styles.containerInner,
             isMobileWeb && styles.containerInnerMobile,
             pal.border,
+            {alignItems: 'center'},
           ]}>
           <ErrorBoundary>
-            <Text style={isMobileWeb ? styles.titleMobile : styles.title}>
-              Bluesky
-            </Text>
-            <Text style={isMobileWeb ? styles.subtitleMobile : styles.subtitle}>
-              See what's next
-            </Text>
+            <Logo width={92} fill="sky" />
+
+            <View style={{paddingTop: 40, paddingBottom: 20}}>
+              <Logotype width={161} />
+            </View>
+
             <View testID="signinOrCreateAccount" style={styles.btns}>
               <TouchableOpacity
                 testID="createAccountButton"
@@ -117,8 +120,6 @@ function Footer({styles}: {styles: ReturnType<typeof useStyles>}) {
   )
 }
 const useStyles = () => {
-  const {isTabletOrMobile} = useWebMediaQueries()
-  const isMobileWeb = isWeb && isTabletOrMobile
   return StyleSheet.create({
     container: {
       height: '100%',
@@ -161,8 +162,7 @@ const useStyles = () => {
       paddingBottom: 30,
     },
     btns: {
-      flexDirection: isMobileWeb ? 'column' : 'row',
-      gap: 20,
+      gap: 10,
       justifyContent: 'center',
       paddingBottom: 40,
     },
diff --git a/src/view/com/pager/FeedsTabBarMobile.tsx b/src/view/com/pager/FeedsTabBarMobile.tsx
index 882b6cfc5..024f9bfab 100644
--- a/src/view/com/pager/FeedsTabBarMobile.tsx
+++ b/src/view/com/pager/FeedsTabBarMobile.tsx
@@ -3,12 +3,9 @@ import {StyleSheet, TouchableOpacity, View} from 'react-native'
 import {TabBar} from 'view/com/pager/TabBar'
 import {RenderTabBarFnProps} from 'view/com/pager/Pager'
 import {usePalette} from 'lib/hooks/usePalette'
-import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
 import {Link} from '../util/Link'
-import {Text} from '../util/text/Text'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
-import {s} from 'lib/styles'
 import {HITSLOP_10} from 'lib/constants'
 import Animated from 'react-native-reanimated'
 import {msg} from '@lingui/macro'
@@ -21,17 +18,17 @@ import {usePinnedFeedsInfos} from '#/state/queries/feed'
 import {isWeb} from 'platform/detection'
 import {useNavigation} from '@react-navigation/native'
 import {NavigationProp} from 'lib/routes/types'
+import {Logo} from '#/view/icons/Logo'
 
 export function FeedsTabBar(
   props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
 ) {
   const pal = usePalette('default')
-  const {isSandbox, hasSession} = useSession()
+  const {hasSession} = useSession()
   const {_} = useLingui()
   const setDrawerOpen = useSetDrawerOpen()
   const navigation = useNavigation<NavigationProp>()
   const {feeds, hasPinnedCustom} = usePinnedFeedsInfos()
-  const brandBlue = useColorSchemeStyle(s.brandBlue, s.blue3)
   const {headerHeight} = useShellLayout()
   const {headerMinimalShellTransform} = useMinimalShellMode()
   const pinnedDisplayNames = hasSession ? feeds.map(f => f.displayName) : []
@@ -86,9 +83,9 @@ export function FeedsTabBar(
             />
           </TouchableOpacity>
         </View>
-        <Text style={[brandBlue, s.bold, styles.title]}>
-          {isSandbox ? 'SANDBOX' : 'Bluesky'}
-        </Text>
+        <View>
+          <Logo width={30} />
+        </View>
         <View style={[pal.view, {width: 18}]}>
           {hasSession && (
             <Link
diff --git a/src/view/icons/Logo.tsx b/src/view/icons/Logo.tsx
new file mode 100644
index 000000000..15ab5a11c
--- /dev/null
+++ b/src/view/icons/Logo.tsx
@@ -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>
+  )
+})
diff --git a/src/view/icons/Logotype.tsx b/src/view/icons/Logotype.tsx
new file mode 100644
index 000000000..847607a3e
--- /dev/null
+++ b/src/view/icons/Logotype.tsx
@@ -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>
+  )
+}
diff --git a/src/view/icons.ts b/src/view/icons/index.tsx
index 089d3f0a8..089d3f0a8 100644
--- a/src/view/icons.ts
+++ b/src/view/icons/index.tsx
diff --git a/src/view/shell/index.tsx b/src/view/shell/index.tsx
index 18042c7ed..51c03ae3d 100644
--- a/src/view/shell/index.tsx
+++ b/src/view/shell/index.tsx
@@ -20,10 +20,6 @@ import {usePalette} from 'lib/hooks/usePalette'
 import {RoutesContainer, TabsNavigator} from '../../Navigation'
 import {isStateAtTabRoot} from 'lib/routes/helpers'
 import {
-  SafeAreaProvider,
-  initialWindowMetrics,
-} from 'react-native-safe-area-context'
-import {
   useIsDrawerOpen,
   useSetDrawerOpen,
   useIsDrawerSwipeDisabled,
@@ -107,14 +103,12 @@ export const Shell: React.FC = function ShellImpl() {
   const pal = usePalette('default')
   const theme = useTheme()
   return (
-    <SafeAreaProvider initialMetrics={initialWindowMetrics} style={pal.view}>
-      <View testID="mobileShellView" style={[styles.outerContainer, pal.view]}>
-        <StatusBar style={theme.colorScheme === 'dark' ? 'light' : 'dark'} />
-        <RoutesContainer>
-          <ShellInner />
-        </RoutesContainer>
-      </View>
-    </SafeAreaProvider>
+    <View testID="mobileShellView" style={[styles.outerContainer, pal.view]}>
+      <StatusBar style={theme.colorScheme === 'dark' ? 'light' : 'dark'} />
+      <RoutesContainer>
+        <ShellInner />
+      </RoutesContainer>
+    </View>
   )
 }
 
diff --git a/yarn.lock b/yarn.lock
index f2415d6ae..ab569ca5b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4448,6 +4448,11 @@
   resolved "https://registry.yarnpkg.com/@react-native-community/eslint-plugin/-/eslint-plugin-1.3.0.tgz#9e558170c106bbafaa1ef502bd8e6d4651012bf9"
   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":
   version "0.8.0"
   resolved "https://registry.yarnpkg.com/@react-native-menu/menu/-/menu-0.8.0.tgz#dbf227c2081e5ffd3d2073ee68ecc84cf8639727"