Skip to content

Commit

Permalink
Redesign NftModal attribute list, simplify Row and Input components
Browse files Browse the repository at this point in the history
  • Loading branch information
mvaivre committed Nov 1, 2024
1 parent 0b44911 commit 6e867be
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 161 deletions.
7 changes: 1 addition & 6 deletions apps/mobile-wallet/src/components/AddressFlatListScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,7 @@ export interface AddressFlatListScreenProps extends Partial<FlatListScreenProps<
contentPaddingTop?: boolean | number
}

const AddressFlatListScreen = ({
onAddressPress,
selectedAddress,
contentPaddingTop,
...props
}: AddressFlatListScreenProps) => {
const AddressFlatListScreen = ({ onAddressPress, selectedAddress, ...props }: AddressFlatListScreenProps) => {
const addresses = useAppSelector(selectAllAddresses)

return (
Expand Down
1 change: 0 additions & 1 deletion apps/mobile-wallet/src/components/NFTsGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ const NFTsGrid = forwardRef(
contentContainerStyle={{ paddingHorizontal: containerHorizontalPadding, paddingBottom: 70 }}
numColumns={columns}
estimatedItemSize={props.estimatedItemSize || 64}
scrollEnabled={scrollEnabled}
ListEmptyComponent={
<NoNFTsMessage>
{isLoadingNfts ? (
Expand Down
34 changes: 7 additions & 27 deletions apps/mobile-wallet/src/components/Row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,11 @@ import { INPUTS_HEIGHT, INPUTS_PADDING } from '~/style/globalStyle'

export interface RowProps {
children?: ReactNode
isInput?: boolean
isSecondary?: boolean
title?: string
titleColor?: AppTextProps['color']
subtitle?: string
onPress?: () => void
hasRightContent?: boolean
truncate?: boolean
noMaxWidth?: boolean
transparent?: boolean
Expand Down Expand Up @@ -103,37 +101,19 @@ const Row = ({
}

export default styled(Row)`
${({ theme, isInput, isSecondary, transparent, isLast, isVertical }) =>
isInput
? css`
justify-content: center;
min-height: ${INPUTS_HEIGHT}px;
height: ${INPUTS_HEIGHT}px;
padding: 0 ${INPUTS_PADDING}px;
background-color: ${transparent ? 'transparent' : isSecondary ? theme.bg.accent : theme.bg.highlight};
`
: css`
min-height: ${INPUTS_HEIGHT}px;
padding: 16px 0;
border-bottom-width: ${isLast ? 0 : 1}px;
border-bottom-color: ${theme.border.secondary};
${!isVertical &&
css`
flex-direction: row;
align-items: center;
justify-content: space-between;
`}
`}
${({ theme, isLast, isVertical }) => css`
min-height: ${INPUTS_HEIGHT}px;
padding: 16px 0;
border-bottom-width: ${isLast ? 0 : 1}px;
border-bottom-color: ${theme.border.secondary};
${({ isInput, hasRightContent }) =>
isInput &&
hasRightContent &&
${!isVertical &&
css`
flex-direction: row;
align-items: center;
justify-content: space-between;
`}
`}
`

const Subtitle = styled(AppText)`
Expand Down
87 changes: 13 additions & 74 deletions apps/mobile-wallet/src/components/inputs/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,12 @@ import { getStringAsync } from 'expo-clipboard'
import { LinearGradient } from 'expo-linear-gradient'
import { ReactNode, RefObject, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
NativeSyntheticEvent,
StyleProp,
TextInput,
TextInputFocusEventData,
TextInputProps,
ViewProps,
ViewStyle
} from 'react-native'
import Animated, { AnimatedProps, FadeIn, FadeOut, useAnimatedStyle, withSpring } from 'react-native-reanimated'
import { StyleProp, TextInput, TextInputProps, ViewProps, ViewStyle } from 'react-native'
import Animated, { AnimatedProps, FadeIn, FadeOut } from 'react-native-reanimated'
import styled, { css, useTheme } from 'styled-components/native'

import { fastSpringConfiguration } from '~/animations/reanimated/reanimatedAnimations'
import AppText from '~/components/AppText'
import Button from '~/components/buttons/Button'
import Row from '~/components/Row'
import { BORDER_RADIUS } from '~/style/globalStyle'

export type InputValue = string | number | undefined | unknown
Expand Down Expand Up @@ -74,22 +64,13 @@ const Input = <T extends InputValue>({
}: InputProps<T>) => {
const { t } = useTranslation()
const theme = useTheme()
const [isActive, setIsActive] = useState(false)
const [copiedText, setCopiedText] = useState('')
const localInputRef = useRef<TextInput>(null)
const usedInputRef = inputRef || localInputRef

const renderedValue = renderValue ? renderValue(value) : value ? (value as object).toString() : ''
const showCustomValueRendering = typeof renderedValue !== 'string' && renderedValue !== undefined

const labelStyle = useAnimatedStyle(() => ({
bottom: withSpring(!isActive ? 0 : 30, fastSpringConfiguration)
}))

const labelTextStyle = useAnimatedStyle(() => ({
fontSize: withSpring(!isActive ? 15 : 11, fastSpringConfiguration)
}))

useEffect(() => {
const fetchCopiedText = async () => {
const text = await getStringAsync()
Expand All @@ -99,53 +80,21 @@ const Input = <T extends InputValue>({
fetchCopiedText()
})

useEffect(() => {
if (renderedValue) {
setIsActive(true)
}
}, [renderedValue])

const handleFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
setIsActive(true)
onFocus && onFocus(e)
}

const handleBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
!renderedValue && setIsActive(false)
onBlur && onBlur(e)
}

const handlePasteButtonPress = () => {
usedInputRef.current?.setNativeProps({ text: copiedText })
}

return (
<Row
onPress={onPress}
isInput
hasRightContent={!!RightContent}
style={[
style,
{
shadowColor: 'black',
shadowOpacity: theme.name === 'light' ? 0.05 : 0.2,
shadowRadius: 8,
shadowOffset: { height: 5, width: 0 }
}
]}
layout={layout}
>
<InputStyled onPress={onPress}>
<InputContainer>
<Label style={labelStyle}>
<LabelText style={labelTextStyle}>{label}</LabelText>
</Label>
{showCustomValueRendering && <CustomRenderedValue>{renderedValue}</CustomRenderedValue>}
<TextInputStyled
selectionColor={theme.gradient.yellow}
selectionColor={theme.global.accent}
value={renderedValue?.toString()}
onFocus={handleFocus}
onBlur={handleBlur}
onFocus={onFocus}
onBlur={onBlur}
ref={usedInputRef}
placeholder={label}
forwardedAs={TextInput}
style={resetDisabledColor && !props.editable ? { color: theme.font.primary } : undefined}
hide={showCustomValueRendering}
Expand Down Expand Up @@ -175,13 +124,16 @@ const Input = <T extends InputValue>({
<Error>{error}</Error>
</ErrorContainer>
)}
</Row>
</InputStyled>
)
}

export default styled(Input)`
background-color: ${({ theme }) => theme.bg.highlight};
export default Input

const InputStyled = styled.Pressable`
background-color: ${({ theme }) => theme.bg.primary};
border-radius: ${BORDER_RADIUS}px;
padding: 18px;
`

const InputContainer = styled.View`
Expand All @@ -191,7 +143,6 @@ const InputContainer = styled.View`

const TextInputStyled = styled.TextInput<{ hide?: boolean }>`
height: 100%;
padding-top: 12px;
color: ${({ theme }) => theme.font.primary};
font-size: 15px;
Expand All @@ -202,18 +153,6 @@ const TextInputStyled = styled.TextInput<{ hide?: boolean }>`
`}
`

const Label = styled(Animated.View)`
position: absolute;
top: 0;
bottom: 0;
left: 0;
justify-content: center;
`

const LabelText = styled(Animated.Text)`
color: ${({ theme }) => theme.font.secondary};
`

const CustomRenderedValue = styled.View`
position: absolute;
top: 0;
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile-wallet/src/components/layout/Screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export const ModalScreenTitle = styled(AppText)`
`

export const ScreenSectionTitle = styled(AppText)`
font-size: 14px;
font-size: 13px;
font-weight: 700;
color: ${({ theme }) => theme.font.tertiary};
margin-bottom: 16px;
Expand Down
2 changes: 1 addition & 1 deletion apps/mobile-wallet/src/components/layout/ScrollScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ const ScrollScreen = ({
contentContainerStyle={[
{
flexGrow: fill ? 1 : undefined,
paddingTop: typeof contentPaddingTop === 'boolean' ? 120 : contentPaddingTop
paddingTop: typeof contentPaddingTop === 'boolean' ? 110 : contentPaddingTop
},
contentContainerStyle
]}
Expand Down
75 changes: 25 additions & 50 deletions apps/mobile-wallet/src/features/nftsDisplay/NftModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,58 +24,52 @@ import styled from 'styled-components/native'

import AppText from '~/components/AppText'
import Button from '~/components/buttons/Button'
import { ScreenSection } from '~/components/layout/Screen'
import Surface from '~/components/layout/Surface'
import NFTImage, { NFTImageProps } from '~/components/NFTImage'
import Row from '~/components/Row'
import BottomModal from '~/features/modals/BottomModal'
import withModal from '~/features/modals/withModal'
import { useAppSelector } from '~/hooks/redux'
import { BORDER_RADIUS_SMALL, DEFAULT_MARGIN } from '~/style/globalStyle'

type NftModalProps = Pick<NFTImageProps, 'nftId'>

const attributeGap = 12
const windowWidth = Dimensions.get('window').width
const nftFullSize = windowWidth - DEFAULT_MARGIN * 4
const attributeWidth = (nftFullSize - attributeGap) / 2

const NftModal = withModal<NftModalProps>(({ id, nftId }) => {
const nft = useAppSelector((s) => selectNFTById(s, nftId))
const { t } = useTranslation()

if (!nft) return null

const attributes = nft.attributes

return (
<BottomModal modalId={id} contentVerticalGap title={nft.name}>
<ScreenSection>
<NftImageContainer>
<NFTImage nftId={nftId} size={nftFullSize} />
</NftImageContainer>

{nft.description && (
<NFTDescriptionContainer>
<AppText color="secondary" size={16}>
{nft.description}
</AppText>
</NFTDescriptionContainer>
)}
{nft.attributes && nft.attributes.length > 0 && (
<AttributesGrid>
{nft.attributes.map((attribute) => (
<Attribute key={attribute.trait_type} style={{ width: attributeWidth }}>
<AttributeType color="tertiary" semiBold>
{attribute.trait_type}
</AttributeType>
<AttributeValue semiBold>{attribute.value}</AttributeValue>
</Attribute>
))}
</AttributesGrid>
)}
</ScreenSection>
<NftImageContainer>
<NFTImage nftId={nftId} size={nftFullSize} />
</NftImageContainer>

{nft.description && (
<NFTDescriptionContainer>
<AppText color="secondary" size={16}>
{nft.description}
</AppText>
</NFTDescriptionContainer>
)}
{attributes && attributes.length > 0 && (
<Surface>
{attributes.map((attribute, i) => (
<Row key={attribute.trait_type} title={attribute.trait_type} isLast={i === attributes.length - 1}>
<AttributeValue semiBold>{attribute.value}</AttributeValue>
</Row>
))}
</Surface>
)}

{!nft.image.startsWith('data:image/') && (
<ScreenSection>
<Button title={t('View full size')} onPress={() => openBrowserAsync(nft.image)} />
</ScreenSection>
<Button title={t('View full size')} onPress={() => openBrowserAsync(nft.image)} />
)}
</BottomModal>
)
Expand All @@ -96,25 +90,6 @@ const NFTDescriptionContainer = styled.View`
border-radius: ${BORDER_RADIUS_SMALL}px;
`

const AttributesGrid = styled.View`
flex-direction: row;
flex-wrap: wrap;
gap: ${attributeGap}px;
margin-top: 20px;
`

const Attribute = styled.View`
flex: 1;
background-color: ${({ theme }) => theme.bg.primary};
padding: 10px;
border-radius: ${BORDER_RADIUS_SMALL}px;
`

const AttributeType = styled(AppText)`
font-weight: 500;
font-size: 14px;
`

const AttributeValue = styled(AppText)`
margin-top: 2px;
`
2 changes: 1 addition & 1 deletion apps/mobile-wallet/src/style/themes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const lightTheme: DefaultTheme = {
name: 'light',
bg: {
highlight: '#ffffff',
primary: 'rgba(0, 0, 0, 0.01)',
primary: '#f2f2f2',
secondary: '#f1f4f9',
tertiary: '#eff1f6',
back1: '#f2f3f7',
Expand Down

0 comments on commit 6e867be

Please sign in to comment.