From c9388a3cc536d1c276d5b83c0378371356bd594c Mon Sep 17 00:00:00 2001
From: Paul Frazee <pfrazee@gmail.com>
Date: Tue, 11 Oct 2022 15:16:46 -0500
Subject: Improve main menu (animation, aesthetics)

---
 src/view/shell/mobile/MainMenu.tsx | 173 +++++++++++++++++++------------------
 1 file changed, 89 insertions(+), 84 deletions(-)

(limited to 'src/view/shell/mobile/MainMenu.tsx')

diff --git a/src/view/shell/mobile/MainMenu.tsx b/src/view/shell/mobile/MainMenu.tsx
index 3efef3c28..60f7d3ce3 100644
--- a/src/view/shell/mobile/MainMenu.tsx
+++ b/src/view/shell/mobile/MainMenu.tsx
@@ -1,23 +1,45 @@
-import React from 'react'
+import React, {useEffect} from 'react'
 import {observer} from 'mobx-react-lite'
 import {
   Image,
   StyleSheet,
+  SafeAreaView,
   Text,
   TouchableOpacity,
   TouchableWithoutFeedback,
   View,
 } from 'react-native'
+import Animated, {
+  useSharedValue,
+  useAnimatedStyle,
+  withTiming,
+  interpolate,
+} from 'react-native-reanimated'
 import {IconProp} from '@fortawesome/fontawesome-svg-core'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import LinearGradient from 'react-native-linear-gradient'
+import {HomeIcon} from '../../lib/icons'
 import {useStores} from '../../../state'
-import {s, colors, gradients} from '../../lib/styles'
+import {s, colors} from '../../lib/styles'
 import {DEF_AVATER} from '../../lib/assets'
 
 export const MainMenu = observer(
   ({active, onClose}: {active: boolean; onClose: () => void}) => {
     const store = useStores()
+    const initInterp = useSharedValue<number>(0)
+
+    useEffect(() => {
+      if (active) {
+        initInterp.value = withTiming(1, {duration: 150})
+      } else {
+        initInterp.value = 0
+      }
+    }, [initInterp, active])
+    const wrapperAnimStyle = useAnimatedStyle(() => ({
+      opacity: interpolate(initInterp.value, [0, 1.0], [0, 1.0]),
+    }))
+    const menuItemsAnimStyle = useAnimatedStyle(() => ({
+      marginTop: interpolate(initInterp.value, [0, 1.0], [15, 0]),
+    }))
 
     // events
     // =
@@ -30,32 +52,30 @@ export const MainMenu = observer(
     // rendering
     // =
 
-    const FatMenuItem = ({
+    const MenuItem = ({
       icon,
       label,
       url,
-      gradient,
     }: {
       icon: IconProp
       label: string
       url: string
-      gradient: keyof typeof gradients
     }) => (
       <TouchableOpacity
-        style={[styles.fatMenuItem, styles.fatMenuItemMargin]}
+        style={[styles.menuItem, styles.menuItemMargin]}
         onPress={() => onNavigate(url)}>
-        <LinearGradient
-          style={[styles.fatMenuItemIconWrapper]}
-          colors={[gradients[gradient].start, gradients[gradient].end]}
-          start={{x: 0, y: 0}}
-          end={{x: 1, y: 1}}>
-          <FontAwesomeIcon
-            icon={icon}
-            style={styles.fatMenuItemIcon}
-            size={24}
-          />
-        </LinearGradient>
-        <Text style={styles.fatMenuItemLabel} numberOfLines={1}>
+        <View style={[styles.menuItemIconWrapper]}>
+          {icon === 'home' ? (
+            <HomeIcon style={styles.menuItemIcon} size={24} />
+          ) : (
+            <FontAwesomeIcon
+              icon={icon}
+              style={styles.menuItemIcon}
+              size={24}
+            />
+          )}
+        </View>
+        <Text style={styles.menuItemLabel} numberOfLines={1}>
           {label}
         </Text>
       </TouchableOpacity>
@@ -69,50 +89,45 @@ export const MainMenu = observer(
         <TouchableWithoutFeedback onPress={onClose}>
           <View style={styles.bg} />
         </TouchableWithoutFeedback>
-        <View style={styles.wrapper}>
-          <View style={[styles.topSection]}>
-            <TouchableOpacity
-              style={styles.profile}
-              onPress={() => onNavigate(`/profile/${store.me.name || ''}`)}>
-              <Image style={styles.profileImage} source={DEF_AVATER} />
-              <Text style={styles.profileText} numberOfLines={1}>
-                {store.me.displayName || store.me.name || 'My profile'}
-              </Text>
-            </TouchableOpacity>
-            <View style={[s.flex1]} />
-            <TouchableOpacity
-              style={styles.settings}
-              onPress={() => onNavigate(`/settings`)}>
-              <FontAwesomeIcon
-                icon="gear"
-                style={styles.settingsIcon}
-                size={24}
-              />
-            </TouchableOpacity>
-          </View>
-          <View style={[styles.section]}>
-            <View style={styles.fatMenuItems}>
-              <FatMenuItem
-                icon="house"
-                label="Feed"
-                url="/"
-                gradient="primary"
-              />
-              <FatMenuItem
-                icon="bell"
-                label="Notifications"
-                url="/notifications"
-                gradient="purple"
-              />
-              <FatMenuItem
-                icon="gear"
-                label="Settings"
-                url="/settings"
-                gradient="blue"
-              />
+        <Animated.View style={[styles.wrapper, wrapperAnimStyle]}>
+          <SafeAreaView>
+            <View style={[styles.topSection]}>
+              <TouchableOpacity
+                style={styles.profile}
+                onPress={() => onNavigate(`/profile/${store.me.name || ''}`)}>
+                <Image style={styles.profileImage} source={DEF_AVATER} />
+                <Text style={styles.profileText} numberOfLines={1}>
+                  {store.me.displayName || store.me.name || 'My profile'}
+                </Text>
+              </TouchableOpacity>
+              <View style={[s.flex1]} />
+              <TouchableOpacity
+                style={styles.settings}
+                onPress={() => onNavigate(`/settings`)}>
+                <FontAwesomeIcon
+                  icon="gear"
+                  style={styles.settingsIcon}
+                  size={24}
+                />
+              </TouchableOpacity>
             </View>
-          </View>
-        </View>
+            <View style={[styles.section]}>
+              <Animated.View style={[styles.menuItems, menuItemsAnimStyle]}>
+                <MenuItem icon="home" label="Home" url="/" />
+                <MenuItem
+                  icon={['far', 'circle-user']}
+                  label="Contacts"
+                  url="/contacts"
+                />
+                <MenuItem
+                  icon={['far', 'bell']}
+                  label="Notifications"
+                  url="/notifications"
+                />
+              </Animated.View>
+            </View>
+          </SafeAreaView>
+        </Animated.View>
       </>
     )
   },
@@ -125,23 +140,22 @@ const styles = StyleSheet.create({
     right: 0,
     bottom: 0,
     left: 0,
-    backgroundColor: '#000',
-    opacity: 0.2,
+    // backgroundColor: '#000',
+    opacity: 0,
   },
   wrapper: {
     position: 'absolute',
+    top: 0,
     bottom: 75,
     width: '100%',
     backgroundColor: '#fff',
-    borderRadius: 8,
-    opacity: 1,
-    paddingVertical: 10,
   },
 
   topSection: {
     flexDirection: 'row',
     alignItems: 'center',
     paddingHorizontal: 10,
+    paddingBottom: 10,
   },
   section: {
     paddingHorizontal: 10,
@@ -170,41 +184,32 @@ const styles = StyleSheet.create({
     marginRight: 10,
   },
 
-  fatMenuItems: {
+  menuItems: {
     flexDirection: 'row',
     marginTop: 10,
     marginBottom: 10,
   },
-  fatMenuItem: {
+  menuItem: {
     width: 80,
     alignItems: 'center',
     marginRight: 6,
   },
-  fatMenuItemMargin: {
+  menuItemMargin: {
     marginRight: 14,
   },
-  fatMenuItemIconWrapper: {
+  menuItemIconWrapper: {
     borderRadius: 6,
     width: 60,
     height: 60,
     justifyContent: 'center',
     alignItems: 'center',
     marginBottom: 5,
-    shadowColor: '#000',
-    shadowOpacity: 0.2,
-    shadowOffset: {width: 0, height: 2},
-    shadowRadius: 2,
+    backgroundColor: colors.gray1,
   },
-  fatMenuItemIcon: {
-    color: colors.white,
-  },
-  fatMenuImage: {
-    borderRadius: 30,
-    width: 60,
-    height: 60,
-    marginBottom: 5,
+  menuItemIcon: {
+    color: colors.gray5,
   },
-  fatMenuItemLabel: {
+  menuItemLabel: {
     fontSize: 13,
   },
 })
-- 
cgit 1.4.1

