From c24d0254bcb13c49793b80b5357ec4cd2ca4688f Mon Sep 17 00:00:00 2001
From: Paul Frazee <pfrazee@gmail.com>
Date: Thu, 26 Jan 2023 19:06:46 -0600
Subject: [PATCH] Add left column of web shell

---
 src/view/com/discover/SuggestedFollows.tsx |  16 +-
 src/view/com/util/BlurView.web.tsx         |   4 -
 src/view/lib/icons.tsx                     |   5 +-
 src/view/screens/Search.tsx                |   6 +-
 src/view/shell/web/index.tsx               |   3 +-
 src/view/shell/web/left-column.tsx         | 164 ++++++++++++++-------
 6 files changed, 129 insertions(+), 69 deletions(-)

diff --git a/src/view/com/discover/SuggestedFollows.tsx b/src/view/com/discover/SuggestedFollows.tsx
index f2dc0ce4..9ed89314 100644
--- a/src/view/com/discover/SuggestedFollows.tsx
+++ b/src/view/com/discover/SuggestedFollows.tsx
@@ -1,7 +1,6 @@
 import React, {useEffect, useState} from 'react'
 import {
   ActivityIndicator,
-  FlatList,
   StyleSheet,
   TouchableOpacity,
   View,
@@ -13,6 +12,7 @@ import {
 } from '@fortawesome/react-native-fontawesome'
 import {observer} from 'mobx-react-lite'
 import _omit from 'lodash.omit'
+import {CenteredView, FlatList} from '../util/Views'
 import {ErrorScreen} from '../util/error/ErrorScreen'
 import {Link} from '../util/Link'
 import {Text} from '../util/text/Text'
@@ -120,12 +120,14 @@ export const SuggestedFollows = observer(
     return (
       <View style={styles.container}>
         {view.hasError ? (
-          <ErrorScreen
-            title="Failed to load suggestions"
-            message="There was an error while trying to load suggested follows."
-            details={view.error}
-            onPressTryAgain={onRefresh}
-          />
+          <CenteredView>
+            <ErrorScreen
+              title="Failed to load suggestions"
+              message="There was an error while trying to load suggested follows."
+              details={view.error}
+              onPressTryAgain={onRefresh}
+            />
+          </CenteredView>
         ) : view.isEmpty ? (
           <View />
         ) : (
diff --git a/src/view/com/util/BlurView.web.tsx b/src/view/com/util/BlurView.web.tsx
index 5adf0d1c..7e4300c7 100644
--- a/src/view/com/util/BlurView.web.tsx
+++ b/src/view/com/util/BlurView.web.tsx
@@ -24,10 +24,6 @@ export const BlurView = ({
 }
 
 const styles = StyleSheet.create({
-  blur: {
-    // @ts-ignore using an RNW-specific attribute here -prf
-    backdropFilter: 'blur(5px)',
-  },
   dark: {
     backgroundColor: '#0008',
   },
diff --git a/src/view/lib/icons.tsx b/src/view/lib/icons.tsx
index f4224ea2..64292ca3 100644
--- a/src/view/lib/icons.tsx
+++ b/src/view/lib/icons.tsx
@@ -47,9 +47,11 @@ export function GridIconSolid({style}: {style?: StyleProp<ViewStyle>}) {
 export function HomeIcon({
   style,
   size,
+  strokeWidth = 4,
 }: {
   style?: StyleProp<ViewStyle>
   size?: string | number
+  strokeWidth?: number
 }) {
   return (
     <Svg
@@ -57,9 +59,10 @@ export function HomeIcon({
       width={size || 24}
       height={size || 24}
       stroke="currentColor"
+      fill="none"
       style={style}>
       <Path
-        strokeWidth={4}
+        strokeWidth={strokeWidth}
         d="M 23.951 2 C 23.631 2.011 23.323 2.124 23.072 2.322 L 8.859 13.52 C 7.055 14.941 6 17.114 6 19.41 L 6 38.5 C 6 39.864 7.136 41 8.5 41 L 18.5 41 C 19.864 41 21 39.864 21 38.5 L 21 28.5 C 21 28.205 21.205 28 21.5 28 L 26.5 28 C 26.795 28 27 28.205 27 28.5 L 27 38.5 C 27 39.864 28.136 41 29.5 41 L 39.5 41 C 40.864 41 42 39.864 42 38.5 L 42 19.41 C 42 17.114 40.945 14.941 39.141 13.52 L 24.928 2.322 C 24.65 2.103 24.304 1.989 23.951 2 Z"
       />
     </Svg>
diff --git a/src/view/screens/Search.tsx b/src/view/screens/Search.tsx
index 95297222..2a1caab8 100644
--- a/src/view/screens/Search.tsx
+++ b/src/view/screens/Search.tsx
@@ -1,13 +1,13 @@
 import React, {useEffect, useState, useMemo, useRef} from 'react'
 import {
   Keyboard,
-  ScrollView,
   StyleSheet,
   TextInput,
   TouchableOpacity,
   View,
 } from 'react-native'
 import {ViewHeader} from '../com/util/ViewHeader'
+import {CenteredView, ScrollView} from '../com/util/Views'
 import {SuggestedFollows} from '../com/discover/SuggestedFollows'
 import {UserAvatar} from '../com/util/UserAvatar'
 import {Text} from '../com/util/text/Text'
@@ -54,7 +54,7 @@ export const Search = ({navIdx, visible, params}: ScreenParams) => {
   return (
     <View style={[pal.view, styles.container]}>
       <ViewHeader title="Search" />
-      <View style={[pal.view, pal.border, styles.inputContainer]}>
+      <CenteredView style={[pal.view, pal.border, styles.inputContainer]}>
         <MagnifyingGlassIcon style={[pal.text, styles.inputIcon]} />
         <TextInput
           testID="searchTextInput"
@@ -66,7 +66,7 @@ export const Search = ({navIdx, visible, params}: ScreenParams) => {
           style={[pal.text, styles.input]}
           onChangeText={onChangeQuery}
         />
-      </View>
+      </CenteredView>
       <View style={styles.outputContainer}>
         {query ? (
           <ScrollView testID="searchScrollView" onScroll={Keyboard.dismiss}>
diff --git a/src/view/shell/web/index.tsx b/src/view/shell/web/index.tsx
index c7bfeff1..93ae9282 100644
--- a/src/view/shell/web/index.tsx
+++ b/src/view/shell/web/index.tsx
@@ -3,7 +3,7 @@ import {observer} from 'mobx-react-lite'
 import {View, StyleSheet, Text} from 'react-native'
 import {useStores} from '../../../state'
 import {match, MatchResult} from '../../routes'
-// import {DesktopLeftColumn} from './left-column'
+import {DesktopLeftColumn} from './left-column'
 // import {DesktopRightColumn} from './right-column'
 import {Login} from '../../screens/Login'
 import {ErrorBoundary} from '../../com/util/ErrorBoundary'
@@ -34,6 +34,7 @@ export const WebShell: React.FC = observer(() => {
           </ErrorBoundary>
         </View>
       ))}
+      <DesktopLeftColumn />
     </View>
   )
   // TODO
diff --git a/src/view/shell/web/left-column.tsx b/src/view/shell/web/left-column.tsx
index 3ce6c2ec..b7309d9c 100644
--- a/src/view/shell/web/left-column.tsx
+++ b/src/view/shell/web/left-column.tsx
@@ -1,57 +1,115 @@
 import React from 'react'
-import {View} from 'react-native'
+import {Pressable, StyleSheet, View} from 'react-native'
+import {observer} from 'mobx-react-lite'
+import {Link} from '../../com/util/Link'
+import {Text} from '../../com/util/text/Text'
+import {colors} from '../../lib/styles'
+import {useStores} from '../../../state'
+import {usePalette} from '../../lib/hooks/usePalette'
+import {
+  HomeIcon,
+  HomeIconSolid,
+  BellIcon,
+  BellIconSolid,
+  MagnifyingGlassIcon,
+  CogIcon,
+} from '../../lib/icons'
 
-// export const NavItem: React.FC<{label: string; screen: string}> = ({
-//   label,
-//   screen,
-// }) => {
-//   const Link = <></> // TODO
-//   return (
-//     <View>
-//       <Pressable
-//         style={state => [
-//           // @ts-ignore it does exist! (react-native-web) -prf
-//           state.hovered && styles.navItemHovered,
-//         ]}>
-//         <Link
-//           style={[
-//             styles.navItemLink,
-//             false /* TODO route.name === screen*/ && styles.navItemLinkSelected,
-//           ]}
-//           to={{screen, params: {}}}>
-//           {label}
-//         </Link>
-//       </Pressable>
-//     </View>
-//   )
-// }
-
-export const DesktopLeftColumn: React.FC = () => {
-  // TODO
-  return <View />
-  // return (
-  //   <View style={styles.container}>
-  //     <NavItem screen="Home" label="Home" />
-  //     <NavItem screen="Search" label="Search" />
-  //     <NavItem screen="Notifications" label="Notifications" />
-  //   </View>
-  // )
+interface NavItemProps {
+  label: string
+  count?: number
+  href: string
+  icon: JSX.Element
+  iconFilled: JSX.Element
 }
+export const NavItem = observer(
+  ({label, count, href, icon, iconFilled}: NavItemProps) => {
+    const store = useStores()
+    const pal = usePalette('default')
+    const isCurrent = store.nav.tab.current.url === href
+    return (
+      <Pressable
+        style={state => [
+          // @ts-ignore Pressable state differs for RNW -prf
+          state.hovered && {backgroundColor: pal.colors.backgroundLight},
+        ]}>
+        <Link style={styles.navItem} href={href}>
+          <View style={styles.navItemIconWrapper}>
+            {isCurrent ? iconFilled : icon}
+            {typeof count === 'number' && count > 0 && (
+              <Text type="button" style={styles.navItemCount}>
+                {count}
+              </Text>
+            )}
+          </View>
+          <Text type={isCurrent ? 'xl-bold' : 'xl-medium'}>{label}</Text>
+        </Link>
+      </Pressable>
+    )
+  },
+)
 
-// const styles = StyleSheet.create({
-//   container: {
-//     position: 'absolute',
-//     left: 'calc(50vw - 500px)',
-//     width: '200px',
-//     height: '100%',
-//   },
-//   navItemHovered: {
-//     backgroundColor: 'gray',
-//   },
-//   navItemLink: {
-//     padding: '1rem',
-//   },
-//   navItemLinkSelected: {
-//     color: 'blue',
-//   },
-// })
+export const DesktopLeftColumn = observer(() => {
+  const store = useStores()
+  const pal = usePalette('default')
+  return (
+    <View style={[styles.container, pal.border]}>
+      <NavItem
+        href="/"
+        label="Home"
+        icon={<HomeIcon />}
+        iconFilled={<HomeIconSolid />}
+      />
+      <NavItem
+        href="/search"
+        label="Search"
+        icon={<MagnifyingGlassIcon />}
+        iconFilled={<MagnifyingGlassIcon strokeWidth={4} />}
+      />
+      <NavItem
+        href="/notifications"
+        label="Notifications"
+        count={store.me.notificationCount}
+        icon={<BellIcon />}
+        iconFilled={<BellIconSolid />}
+      />
+      <NavItem
+        href="/settings"
+        label="Settings"
+        icon={<CogIcon strokeWidth={1.5} />}
+        iconFilled={<CogIcon strokeWidth={2} />}
+      />
+    </View>
+  )
+})
+
+const styles = StyleSheet.create({
+  container: {
+    position: 'absolute',
+    left: 'calc(50vw - 500px)',
+    width: '200px',
+    height: '100%',
+    borderRightWidth: 1,
+  },
+  navItem: {
+    padding: '1rem',
+    flexDirection: 'row',
+    alignItems: 'center',
+  },
+  navItemIconWrapper: {
+    flexDirection: 'row',
+    width: 30,
+    justifyContent: 'center',
+    marginRight: 5,
+  },
+  navItemCount: {
+    position: 'absolute',
+    top: -5,
+    left: 15,
+    backgroundColor: colors.red3,
+    color: colors.white,
+    fontSize: 12,
+    paddingHorizontal: 4,
+    borderRadius: 4,
+  },
+})