Improve main menu (animation, aesthetics)

zio/stable
Paul Frazee 2022-10-11 15:16:46 -05:00
parent d7a75a2062
commit c9388a3cc5
2 changed files with 93 additions and 84 deletions

View File

@ -1,5 +1,6 @@
import {library} from '@fortawesome/fontawesome-svg-core' import {library} from '@fortawesome/fontawesome-svg-core'
import {faAddressCard} from '@fortawesome/free-regular-svg-icons/faAddressCard'
import {faAngleDown} from '@fortawesome/free-solid-svg-icons/faAngleDown' import {faAngleDown} from '@fortawesome/free-solid-svg-icons/faAngleDown'
import {faAngleLeft} from '@fortawesome/free-solid-svg-icons/faAngleLeft' import {faAngleLeft} from '@fortawesome/free-solid-svg-icons/faAngleLeft'
import {faAngleRight} from '@fortawesome/free-solid-svg-icons/faAngleRight' import {faAngleRight} from '@fortawesome/free-solid-svg-icons/faAngleRight'
@ -15,6 +16,7 @@ import {faBell as farBell} from '@fortawesome/free-regular-svg-icons/faBell'
import {faBookmark} from '@fortawesome/free-solid-svg-icons/faBookmark' import {faBookmark} from '@fortawesome/free-solid-svg-icons/faBookmark'
import {faBookmark as farBookmark} from '@fortawesome/free-regular-svg-icons/faBookmark' import {faBookmark as farBookmark} from '@fortawesome/free-regular-svg-icons/faBookmark'
import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck' import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck'
import {faCircleUser} from '@fortawesome/free-regular-svg-icons/faCircleUser'
import {faClone} from '@fortawesome/free-regular-svg-icons/faClone' import {faClone} from '@fortawesome/free-regular-svg-icons/faClone'
import {faComment} from '@fortawesome/free-regular-svg-icons/faComment' import {faComment} from '@fortawesome/free-regular-svg-icons/faComment'
import {faEllipsis} from '@fortawesome/free-solid-svg-icons/faEllipsis' import {faEllipsis} from '@fortawesome/free-solid-svg-icons/faEllipsis'
@ -48,6 +50,7 @@ import {faX} from '@fortawesome/free-solid-svg-icons/faX'
export function setup() { export function setup() {
library.add( library.add(
faAddressCard,
faAngleDown, faAngleDown,
faAngleLeft, faAngleLeft,
faAngleRight, faAngleRight,
@ -63,6 +66,7 @@ export function setup() {
faBookmark, faBookmark,
farBookmark, farBookmark,
faCheck, faCheck,
faCircleUser,
faClone, faClone,
faComment, faComment,
faEllipsis, faEllipsis,

View File

@ -1,23 +1,45 @@
import React from 'react' import React, {useEffect} from 'react'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import { import {
Image, Image,
StyleSheet, StyleSheet,
SafeAreaView,
Text, Text,
TouchableOpacity, TouchableOpacity,
TouchableWithoutFeedback, TouchableWithoutFeedback,
View, View,
} from 'react-native' } from 'react-native'
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
interpolate,
} from 'react-native-reanimated'
import {IconProp} from '@fortawesome/fontawesome-svg-core' import {IconProp} from '@fortawesome/fontawesome-svg-core'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import LinearGradient from 'react-native-linear-gradient' import {HomeIcon} from '../../lib/icons'
import {useStores} from '../../../state' import {useStores} from '../../../state'
import {s, colors, gradients} from '../../lib/styles' import {s, colors} from '../../lib/styles'
import {DEF_AVATER} from '../../lib/assets' import {DEF_AVATER} from '../../lib/assets'
export const MainMenu = observer( export const MainMenu = observer(
({active, onClose}: {active: boolean; onClose: () => void}) => { ({active, onClose}: {active: boolean; onClose: () => void}) => {
const store = useStores() 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 // events
// = // =
@ -30,32 +52,30 @@ export const MainMenu = observer(
// rendering // rendering
// = // =
const FatMenuItem = ({ const MenuItem = ({
icon, icon,
label, label,
url, url,
gradient,
}: { }: {
icon: IconProp icon: IconProp
label: string label: string
url: string url: string
gradient: keyof typeof gradients
}) => ( }) => (
<TouchableOpacity <TouchableOpacity
style={[styles.fatMenuItem, styles.fatMenuItemMargin]} style={[styles.menuItem, styles.menuItemMargin]}
onPress={() => onNavigate(url)}> onPress={() => onNavigate(url)}>
<LinearGradient <View style={[styles.menuItemIconWrapper]}>
style={[styles.fatMenuItemIconWrapper]} {icon === 'home' ? (
colors={[gradients[gradient].start, gradients[gradient].end]} <HomeIcon style={styles.menuItemIcon} size={24} />
start={{x: 0, y: 0}} ) : (
end={{x: 1, y: 1}}> <FontAwesomeIcon
<FontAwesomeIcon icon={icon}
icon={icon} style={styles.menuItemIcon}
style={styles.fatMenuItemIcon} size={24}
size={24} />
/> )}
</LinearGradient> </View>
<Text style={styles.fatMenuItemLabel} numberOfLines={1}> <Text style={styles.menuItemLabel} numberOfLines={1}>
{label} {label}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
@ -69,50 +89,45 @@ export const MainMenu = observer(
<TouchableWithoutFeedback onPress={onClose}> <TouchableWithoutFeedback onPress={onClose}>
<View style={styles.bg} /> <View style={styles.bg} />
</TouchableWithoutFeedback> </TouchableWithoutFeedback>
<View style={styles.wrapper}> <Animated.View style={[styles.wrapper, wrapperAnimStyle]}>
<View style={[styles.topSection]}> <SafeAreaView>
<TouchableOpacity <View style={[styles.topSection]}>
style={styles.profile} <TouchableOpacity
onPress={() => onNavigate(`/profile/${store.me.name || ''}`)}> style={styles.profile}
<Image style={styles.profileImage} source={DEF_AVATER} /> onPress={() => onNavigate(`/profile/${store.me.name || ''}`)}>
<Text style={styles.profileText} numberOfLines={1}> <Image style={styles.profileImage} source={DEF_AVATER} />
{store.me.displayName || store.me.name || 'My profile'} <Text style={styles.profileText} numberOfLines={1}>
</Text> {store.me.displayName || store.me.name || 'My profile'}
</TouchableOpacity> </Text>
<View style={[s.flex1]} /> </TouchableOpacity>
<TouchableOpacity <View style={[s.flex1]} />
style={styles.settings} <TouchableOpacity
onPress={() => onNavigate(`/settings`)}> style={styles.settings}
<FontAwesomeIcon onPress={() => onNavigate(`/settings`)}>
icon="gear" <FontAwesomeIcon
style={styles.settingsIcon} icon="gear"
size={24} style={styles.settingsIcon}
/> size={24}
</TouchableOpacity> />
</View> </TouchableOpacity>
<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"
/>
</View> </View>
</View> <View style={[styles.section]}>
</View> <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, right: 0,
bottom: 0, bottom: 0,
left: 0, left: 0,
backgroundColor: '#000', // backgroundColor: '#000',
opacity: 0.2, opacity: 0,
}, },
wrapper: { wrapper: {
position: 'absolute', position: 'absolute',
top: 0,
bottom: 75, bottom: 75,
width: '100%', width: '100%',
backgroundColor: '#fff', backgroundColor: '#fff',
borderRadius: 8,
opacity: 1,
paddingVertical: 10,
}, },
topSection: { topSection: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingHorizontal: 10, paddingHorizontal: 10,
paddingBottom: 10,
}, },
section: { section: {
paddingHorizontal: 10, paddingHorizontal: 10,
@ -170,41 +184,32 @@ const styles = StyleSheet.create({
marginRight: 10, marginRight: 10,
}, },
fatMenuItems: { menuItems: {
flexDirection: 'row', flexDirection: 'row',
marginTop: 10, marginTop: 10,
marginBottom: 10, marginBottom: 10,
}, },
fatMenuItem: { menuItem: {
width: 80, width: 80,
alignItems: 'center', alignItems: 'center',
marginRight: 6, marginRight: 6,
}, },
fatMenuItemMargin: { menuItemMargin: {
marginRight: 14, marginRight: 14,
}, },
fatMenuItemIconWrapper: { menuItemIconWrapper: {
borderRadius: 6, borderRadius: 6,
width: 60, width: 60,
height: 60, height: 60,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
marginBottom: 5, marginBottom: 5,
shadowColor: '#000', backgroundColor: colors.gray1,
shadowOpacity: 0.2,
shadowOffset: {width: 0, height: 2},
shadowRadius: 2,
}, },
fatMenuItemIcon: { menuItemIcon: {
color: colors.white, color: colors.gray5,
}, },
fatMenuImage: { menuItemLabel: {
borderRadius: 30,
width: 60,
height: 60,
marginBottom: 5,
},
fatMenuItemLabel: {
fontSize: 13, fontSize: 13,
}, },
}) })