mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-27 00:59:28 +01:00
first-pass context menu for albums
This commit is contained in:
parent
f3341355e1
commit
0416c0ad0d
19
app/App.tsx
19
app/App.tsx
@ -6,6 +6,7 @@ import { StatusBar, View } from 'react-native'
|
||||
import ProgressHook from './components/ProgressHook'
|
||||
import { useStore } from './state/store'
|
||||
import { selectTrackPlayer } from './state/trackplayer'
|
||||
import { MenuProvider } from 'react-native-popup-menu'
|
||||
|
||||
const Debug = () => {
|
||||
const currentTrack = useStore(selectTrackPlayer.currentTrack)
|
||||
@ -14,14 +15,16 @@ const Debug = () => {
|
||||
}
|
||||
|
||||
const App = () => (
|
||||
<View style={{ flex: 1, backgroundColor: colors.gradient.high }}>
|
||||
<StatusBar animated={true} backgroundColor={'rgba(0, 0, 0, 0.4)'} barStyle={'light-content'} translucent={true} />
|
||||
<SplashPage>
|
||||
<ProgressHook />
|
||||
<Debug />
|
||||
<RootNavigator />
|
||||
</SplashPage>
|
||||
</View>
|
||||
<MenuProvider>
|
||||
<View style={{ flex: 1, backgroundColor: colors.gradient.high }}>
|
||||
<StatusBar animated={true} backgroundColor={'rgba(0, 0, 0, 0.4)'} barStyle={'light-content'} translucent={true} />
|
||||
<SplashPage>
|
||||
<ProgressHook />
|
||||
<Debug />
|
||||
<RootNavigator />
|
||||
</SplashPage>
|
||||
</View>
|
||||
</MenuProvider>
|
||||
)
|
||||
|
||||
export default App
|
||||
|
||||
212
app/components/ContextMenu.tsx
Normal file
212
app/components/ContextMenu.tsx
Normal file
@ -0,0 +1,212 @@
|
||||
import PressableOpacity from '@app/components/PressableOpacity'
|
||||
import { AlbumListItem } from '@app/models/music'
|
||||
import colors from '@app/styles/colors'
|
||||
import font from '@app/styles/font'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { ReactComponentLike } from 'prop-types'
|
||||
import React from 'react'
|
||||
import { ScrollView, StyleProp, StyleSheet, Text, View, ViewStyle } from 'react-native'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
import { Menu, MenuOption, MenuOptions, MenuTrigger, renderers } from 'react-native-popup-menu'
|
||||
import IconFA from 'react-native-vector-icons/FontAwesome'
|
||||
import IconMat from 'react-native-vector-icons/MaterialIcons'
|
||||
import CoverArt from './CoverArt'
|
||||
|
||||
const { SlideInMenu } = renderers
|
||||
|
||||
type ContextMenuProps = {
|
||||
menuStyle?: StyleProp<ViewStyle>
|
||||
triggerWrapperStyle?: StyleProp<ViewStyle>
|
||||
triggerOuterWrapperStyle?: StyleProp<ViewStyle>
|
||||
triggerTouchableStyle?: StyleProp<ViewStyle>
|
||||
onPress?: () => void
|
||||
}
|
||||
|
||||
type InternalContextMenuProps = ContextMenuProps & {
|
||||
menuHeader: React.ReactNode
|
||||
menuOptions: React.ReactNode
|
||||
}
|
||||
|
||||
const ContextMenu: React.FC<InternalContextMenuProps> = ({
|
||||
menuStyle,
|
||||
triggerWrapperStyle,
|
||||
triggerOuterWrapperStyle,
|
||||
triggerTouchableStyle,
|
||||
onPress,
|
||||
menuHeader,
|
||||
menuOptions,
|
||||
children,
|
||||
}) => {
|
||||
menuStyle = menuStyle || { flex: 1 }
|
||||
triggerWrapperStyle = triggerWrapperStyle || { flex: 1 }
|
||||
triggerOuterWrapperStyle = triggerOuterWrapperStyle || { flex: 1 }
|
||||
triggerTouchableStyle = triggerTouchableStyle || { flex: 1 }
|
||||
return (
|
||||
<Menu renderer={SlideInMenu} style={menuStyle}>
|
||||
<MenuTrigger
|
||||
triggerOnLongPress={true}
|
||||
customStyles={{
|
||||
triggerOuterWrapper: triggerOuterWrapperStyle,
|
||||
triggerWrapper: triggerWrapperStyle,
|
||||
triggerTouchable: { style: triggerTouchableStyle },
|
||||
TriggerTouchableComponent: PressableOpacity,
|
||||
}}
|
||||
onAlternativeAction={onPress}>
|
||||
{children}
|
||||
</MenuTrigger>
|
||||
<MenuOptions
|
||||
customStyles={styles}
|
||||
renderOptionsContainer={(options: any) => (
|
||||
<ScrollView>
|
||||
{menuHeader}
|
||||
{options}
|
||||
</ScrollView>
|
||||
)}>
|
||||
{menuOptions}
|
||||
</MenuOptions>
|
||||
</Menu>
|
||||
)
|
||||
}
|
||||
|
||||
type ContextMenuOptionProps = {
|
||||
onSelect?: () => void
|
||||
}
|
||||
|
||||
const ContextMenuOption: React.FC<ContextMenuOptionProps> = ({ onSelect, children }) => (
|
||||
<MenuOption style={styles.option} onSelect={onSelect}>
|
||||
{children}
|
||||
</MenuOption>
|
||||
)
|
||||
|
||||
type ContextMenuIconTextOptionProps = ContextMenuOptionProps & {
|
||||
IconComponent: ReactComponentLike
|
||||
name: string
|
||||
size: number
|
||||
color?: string
|
||||
text: string
|
||||
}
|
||||
|
||||
const ContextMenuIconTextOption = React.memo<ContextMenuIconTextOptionProps>(
|
||||
({ onSelect, IconComponent, name, color, size, text }) => (
|
||||
<ContextMenuOption onSelect={onSelect}>
|
||||
<View style={styles.icon}>
|
||||
<IconComponent name={name} size={size} color={color || colors.text.primary} />
|
||||
</View>
|
||||
<Text style={styles.optionText}>{text}</Text>
|
||||
</ContextMenuOption>
|
||||
),
|
||||
)
|
||||
|
||||
export type AlbumContextPressableProps = ContextMenuProps & {
|
||||
album: AlbumListItem
|
||||
}
|
||||
|
||||
export const AlbumContextPressable: React.FC<AlbumContextPressableProps> = ({
|
||||
menuStyle,
|
||||
triggerWrapperStyle,
|
||||
triggerOuterWrapperStyle,
|
||||
triggerTouchableStyle,
|
||||
onPress,
|
||||
album,
|
||||
children,
|
||||
}) => {
|
||||
const navigation = useNavigation()
|
||||
|
||||
return (
|
||||
<ContextMenu
|
||||
menuStyle={menuStyle}
|
||||
triggerWrapperStyle={triggerWrapperStyle}
|
||||
triggerOuterWrapperStyle={triggerOuterWrapperStyle}
|
||||
triggerTouchableStyle={triggerTouchableStyle}
|
||||
onPress={onPress}
|
||||
menuHeader={
|
||||
<View style={styles.menuHeader}>
|
||||
<CoverArt coverArt={album.coverArt} style={styles.coverArt} resizeMode={FastImage.resizeMode.cover} />
|
||||
<View style={styles.menuHeaderText}>
|
||||
<Text numberOfLines={1} style={styles.menuTitle}>
|
||||
{album.name}
|
||||
</Text>
|
||||
{album.artist ? (
|
||||
<Text numberOfLines={1} style={styles.menuSubtitle}>
|
||||
{album.artist}
|
||||
</Text>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
}
|
||||
menuOptions={
|
||||
<>
|
||||
<ContextMenuIconTextOption IconComponent={IconFA} name="star-o" size={26} text="Star" />
|
||||
<ContextMenuIconTextOption
|
||||
IconComponent={IconFA}
|
||||
name="microphone"
|
||||
size={26}
|
||||
text="View artist"
|
||||
onSelect={() => navigation.navigate('artist', { id: album.artistId, title: album.artist })}
|
||||
/>
|
||||
<ContextMenuIconTextOption IconComponent={IconMat} name="file-download" size={26} text="Download album" />
|
||||
</>
|
||||
}>
|
||||
{children}
|
||||
</ContextMenu>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
optionsContainer: {
|
||||
backgroundColor: 'rgba(45, 45, 45, 0.95)',
|
||||
maxHeight: 365,
|
||||
},
|
||||
optionsWrapper: {
|
||||
// marginBottom: 10,
|
||||
},
|
||||
menuHeader: {
|
||||
paddingTop: 14,
|
||||
paddingBottom: 10,
|
||||
paddingHorizontal: 20,
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
coverArt: {
|
||||
width: 42,
|
||||
height: 42,
|
||||
},
|
||||
menuHeaderText: {
|
||||
flex: 1,
|
||||
marginLeft: 10,
|
||||
},
|
||||
menuTitle: {
|
||||
fontFamily: font.semiBold,
|
||||
fontSize: 16,
|
||||
color: colors.text.primary,
|
||||
},
|
||||
menuSubtitle: {
|
||||
fontFamily: font.regular,
|
||||
fontSize: 14,
|
||||
color: colors.text.secondary,
|
||||
},
|
||||
option: {
|
||||
paddingVertical: 10,
|
||||
paddingHorizontal: 20,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
icon: {
|
||||
marginRight: 10,
|
||||
width: 32,
|
||||
height: 32,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
// backgroundColor: 'red',
|
||||
},
|
||||
optionText: {
|
||||
fontFamily: font.semiBold,
|
||||
fontSize: 16,
|
||||
color: colors.text.primary,
|
||||
// backgroundColor: 'green',
|
||||
},
|
||||
})
|
||||
@ -1,15 +1,16 @@
|
||||
import { ListableItem } from '@app/models/music'
|
||||
import { AlbumListItem, ListableItem } from '@app/models/music'
|
||||
import { useStore } from '@app/state/store'
|
||||
import { selectTrackPlayer } from '@app/state/trackplayer'
|
||||
import colors from '@app/styles/colors'
|
||||
import font from '@app/styles/font'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import React, { useState } from 'react'
|
||||
import { GestureResponderEvent, StyleSheet, Text, View } from 'react-native'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { StyleSheet, Text, View } from 'react-native'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
import IconFA from 'react-native-vector-icons/FontAwesome'
|
||||
import IconFA5 from 'react-native-vector-icons/FontAwesome5'
|
||||
import IconMat from 'react-native-vector-icons/MaterialIcons'
|
||||
import { AlbumContextPressable } from './ContextMenu'
|
||||
import CoverArt from './CoverArt'
|
||||
import PressableOpacity from './PressableOpacity'
|
||||
|
||||
@ -40,7 +41,7 @@ const TitleText = React.memo<{
|
||||
|
||||
const ListItem: React.FC<{
|
||||
item: ListableItem
|
||||
onPress?: (event: GestureResponderEvent) => void
|
||||
onPress?: () => void
|
||||
showArt?: boolean
|
||||
showStar?: boolean
|
||||
listStyle?: 'big' | 'small'
|
||||
@ -81,9 +82,31 @@ const ListItem: React.FC<{
|
||||
}
|
||||
}
|
||||
|
||||
const itemPressable = useCallback(
|
||||
({ children }) => (
|
||||
<PressableOpacity onPress={onPress} style={styles.item}>
|
||||
{children}
|
||||
</PressableOpacity>
|
||||
),
|
||||
[onPress],
|
||||
)
|
||||
const albumPressable = useCallback(
|
||||
({ children }) => (
|
||||
<AlbumContextPressable album={item as AlbumListItem} onPress={onPress} triggerWrapperStyle={styles.item}>
|
||||
{children}
|
||||
</AlbumContextPressable>
|
||||
),
|
||||
[item, onPress],
|
||||
)
|
||||
|
||||
let PressableComponent = itemPressable
|
||||
if (item.itemType === 'album') {
|
||||
PressableComponent = albumPressable
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[styles.container, sizeStyle.container]}>
|
||||
<PressableOpacity onPress={onPress} style={styles.item}>
|
||||
<PressableComponent>
|
||||
{showArt ? (
|
||||
<CoverArt
|
||||
{...artSource}
|
||||
@ -117,7 +140,7 @@ const ListItem: React.FC<{
|
||||
<></>
|
||||
)}
|
||||
</View>
|
||||
</PressableOpacity>
|
||||
</PressableComponent>
|
||||
<View style={styles.controls}>
|
||||
{showStar ? (
|
||||
<PressableOpacity onPress={() => setStarred(!starred)} style={styles.controlItem}>
|
||||
@ -146,6 +169,7 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
},
|
||||
art: {
|
||||
marginRight: 10,
|
||||
|
||||
@ -43,6 +43,12 @@ const PressableOpacity: React.FC<PressableOpacityProps> = props => {
|
||||
if (!props.disabled) {
|
||||
setOpacity(1)
|
||||
}
|
||||
}}
|
||||
onLongPress={data => {
|
||||
if (!props.disabled) {
|
||||
setOpacity(1)
|
||||
props.onLongPress ? props.onLongPress(data) : null
|
||||
}
|
||||
}}>
|
||||
{props.children}
|
||||
</Pressable>
|
||||
|
||||
@ -32,6 +32,7 @@ export interface AlbumListItem {
|
||||
id: string
|
||||
name: string
|
||||
artist?: string
|
||||
artistId?: string
|
||||
starred?: Date
|
||||
coverArt?: string
|
||||
}
|
||||
@ -148,6 +149,7 @@ export function mapAlbumID3toAlbumListItem(album: AlbumID3Element): AlbumListIte
|
||||
id: album.id,
|
||||
name: album.name,
|
||||
artist: album.artist,
|
||||
artistId: album.artistId,
|
||||
starred: album.starred,
|
||||
coverArt: album.coverArt,
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { AlbumContextPressable } from '@app/components/ContextMenu'
|
||||
import CoverArt from '@app/components/CoverArt'
|
||||
import GradientBackground from '@app/components/GradientBackground'
|
||||
import GradientScrollView from '@app/components/GradientScrollView'
|
||||
import Header from '@app/components/Header'
|
||||
import ListItem from '@app/components/ListItem'
|
||||
import PressableOpacity from '@app/components/PressableOpacity'
|
||||
import { useArtistInfo } from '@app/hooks/music'
|
||||
import { useSetQueue } from '@app/hooks/trackplayer'
|
||||
import { Album, Song } from '@app/models/music'
|
||||
@ -11,7 +12,7 @@ import font from '@app/styles/font'
|
||||
import { useLayout } from '@react-native-community/hooks'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import React, { useEffect } from 'react'
|
||||
import { StyleSheet, Text, View } from 'react-native'
|
||||
import { ActivityIndicator, StyleSheet, Text, View } from 'react-native'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
|
||||
const AlbumItem = React.memo<{
|
||||
@ -21,14 +22,20 @@ const AlbumItem = React.memo<{
|
||||
}>(({ album, height, width }) => {
|
||||
const navigation = useNavigation()
|
||||
|
||||
if (height <= 0 || width <= 0) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
return (
|
||||
<PressableOpacity
|
||||
<AlbumContextPressable
|
||||
album={album}
|
||||
onPress={() => navigation.navigate('album', { id: album.id, title: album.name })}
|
||||
style={[styles.albumItem, { width }]}>
|
||||
menuStyle={[styles.albumItem, { width }]}
|
||||
triggerOuterWrapperStyle={{ width }}>
|
||||
<CoverArt coverArt={album.coverArt} style={{ height, width }} resizeMode={FastImage.resizeMode.cover} />
|
||||
<Text style={styles.albumTitle}>{album.name}</Text>
|
||||
<Text style={styles.albumYear}> {album.year ? album.year : ''}</Text>
|
||||
</PressableOpacity>
|
||||
</AlbumContextPressable>
|
||||
)
|
||||
})
|
||||
|
||||
@ -54,6 +61,12 @@ const TopSongs = React.memo<{
|
||||
)
|
||||
})
|
||||
|
||||
const ArtistDetailsFallback = React.memo(() => (
|
||||
<GradientBackground style={styles.fallback}>
|
||||
<ActivityIndicator size="large" color={colors.accent} />
|
||||
</GradientBackground>
|
||||
))
|
||||
|
||||
const ArtistDetails: React.FC<{ id: string }> = ({ id }) => {
|
||||
const artist = useArtistInfo(id)
|
||||
const albumsLayout = useLayout()
|
||||
@ -62,7 +75,7 @@ const ArtistDetails: React.FC<{ id: string }> = ({ id }) => {
|
||||
const albumSize = albumsLayout.width / 2 - styles.container.paddingHorizontal / 2
|
||||
|
||||
if (!artist) {
|
||||
return <></>
|
||||
return <ArtistDetailsFallback />
|
||||
}
|
||||
|
||||
const _albums = [...artist.albums]
|
||||
@ -117,6 +130,10 @@ const styles = StyleSheet.create({
|
||||
scroll: {
|
||||
flex: 1,
|
||||
},
|
||||
fallback: {
|
||||
alignItems: 'center',
|
||||
paddingTop: 100,
|
||||
},
|
||||
scrollContent: {
|
||||
alignItems: 'center',
|
||||
},
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { AlbumContextPressable } from '@app/components/ContextMenu'
|
||||
import CoverArt from '@app/components/CoverArt'
|
||||
import GradientScrollView from '@app/components/GradientScrollView'
|
||||
import Header from '@app/components/Header'
|
||||
import NothingHere from '@app/components/NothingHere'
|
||||
import PressableOpacity from '@app/components/PressableOpacity'
|
||||
import { useActiveListRefresh2 } from '@app/hooks/server'
|
||||
import { AlbumListItem } from '@app/models/music'
|
||||
import { selectMusic } from '@app/state/music'
|
||||
@ -14,6 +14,7 @@ import { GetAlbumListType } from '@app/subsonic/params'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import React, { useCallback } from 'react'
|
||||
import { RefreshControl, ScrollView, StatusBar, StyleSheet, Text, View } from 'react-native'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
|
||||
const titles: { [key in GetAlbumListType]?: string } = {
|
||||
recent: 'Recent Albums',
|
||||
@ -28,18 +29,22 @@ const AlbumItem = React.memo<{
|
||||
const navigation = useNavigation()
|
||||
|
||||
return (
|
||||
<PressableOpacity
|
||||
onPress={() => navigation.navigate('album', { id: album.id, title: album.name })}
|
||||
key={album.id}
|
||||
style={styles.item}>
|
||||
<CoverArt coverArt={album.coverArt} style={{ height: styles.item.width, width: styles.item.width }} />
|
||||
<AlbumContextPressable
|
||||
album={album}
|
||||
triggerWrapperStyle={styles.item}
|
||||
onPress={() => navigation.navigate('album', { id: album.id, title: album.name })}>
|
||||
<CoverArt
|
||||
coverArt={album.coverArt}
|
||||
style={{ height: styles.item.width, width: styles.item.width }}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
<Text style={styles.title} numberOfLines={1}>
|
||||
{album.name}
|
||||
</Text>
|
||||
<Text style={styles.subtitle} numberOfLines={1}>
|
||||
{album.artist}
|
||||
</Text>
|
||||
</PressableOpacity>
|
||||
</AlbumContextPressable>
|
||||
)
|
||||
})
|
||||
|
||||
@ -138,9 +143,9 @@ const styles = StyleSheet.create({
|
||||
paddingLeft: 20,
|
||||
},
|
||||
item: {
|
||||
flex: 1,
|
||||
marginRight: 10,
|
||||
width: 150,
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
title: {
|
||||
fontFamily: font.semiBold,
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { AlbumContextPressable } from '@app/components/ContextMenu'
|
||||
import CoverArt from '@app/components/CoverArt'
|
||||
import GradientFlatList from '@app/components/GradientFlatList'
|
||||
import PressableOpacity from '@app/components/PressableOpacity'
|
||||
import { useActiveListRefresh2 } from '@app/hooks/server'
|
||||
import { Album } from '@app/models/music'
|
||||
import { Album, AlbumListItem } from '@app/models/music'
|
||||
import { selectMusic } from '@app/state/music'
|
||||
import { useStore } from '@app/state/store'
|
||||
import colors from '@app/styles/colors'
|
||||
@ -13,44 +13,37 @@ import { StyleSheet, Text, useWindowDimensions, View } from 'react-native'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
|
||||
const AlbumItem = React.memo<{
|
||||
id: string
|
||||
name: string
|
||||
album: AlbumListItem
|
||||
size: number
|
||||
height: number
|
||||
artist?: string
|
||||
coverArt?: string
|
||||
}>(({ id, name, artist, size, height, coverArt }) => {
|
||||
}>(({ album, size, height }) => {
|
||||
const navigation = useNavigation()
|
||||
|
||||
return (
|
||||
<PressableOpacity
|
||||
style={[styles.item, { maxWidth: size, height }]}
|
||||
onPress={() => navigation.navigate('album', { id, title: name })}>
|
||||
<CoverArt coverArt={coverArt} style={{ height: size, width: size }} resizeMode={FastImage.resizeMode.cover} />
|
||||
<AlbumContextPressable
|
||||
album={album}
|
||||
triggerWrapperStyle={[styles.item, { maxWidth: size, height }]}
|
||||
onPress={() => navigation.navigate('album', { id: album.id, title: album.name })}>
|
||||
<CoverArt
|
||||
coverArt={album.coverArt}
|
||||
style={{ height: size, width: size }}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
/>
|
||||
<View style={styles.itemDetails}>
|
||||
<Text style={styles.title} numberOfLines={1}>
|
||||
{name}
|
||||
{album.name}
|
||||
</Text>
|
||||
<Text style={styles.subtitle} numberOfLines={1}>
|
||||
{artist}
|
||||
{album.artist}
|
||||
</Text>
|
||||
</View>
|
||||
</PressableOpacity>
|
||||
</AlbumContextPressable>
|
||||
)
|
||||
})
|
||||
|
||||
const AlbumListRenderItem: React.FC<{
|
||||
item: { album: Album; size: number; height: number }
|
||||
}> = ({ item }) => (
|
||||
<AlbumItem
|
||||
id={item.album.id}
|
||||
coverArt={item.album.coverArt}
|
||||
name={item.album.name}
|
||||
artist={item.album.artist}
|
||||
size={item.size}
|
||||
height={item.height}
|
||||
/>
|
||||
)
|
||||
}> = ({ item }) => <AlbumItem album={item.album} size={item.size} height={item.height} />
|
||||
|
||||
const AlbumsList = () => {
|
||||
const list = useStore(selectMusic.albums)
|
||||
@ -96,14 +89,14 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
},
|
||||
item: {
|
||||
alignItems: 'center',
|
||||
// alignItems: 'center',
|
||||
marginVertical: 4,
|
||||
marginHorizontal: 3,
|
||||
flex: 1 / 3,
|
||||
},
|
||||
itemDetails: {
|
||||
flex: 1,
|
||||
width: '100%',
|
||||
// width: '100%',
|
||||
},
|
||||
title: {
|
||||
fontSize: 12,
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
"react-native-get-random-values": "^1.7.0",
|
||||
"react-native-image-colors": "^1.3.0",
|
||||
"react-native-linear-gradient": "^2.5.6",
|
||||
"react-native-popup-menu": "^0.15.11",
|
||||
"react-native-reanimated": "^2.2.0",
|
||||
"react-native-safe-area-context": "^3.2.0",
|
||||
"react-native-screens": "^3.4.0",
|
||||
|
||||
@ -5549,6 +5549,11 @@ react-native-linear-gradient@^2.5.6:
|
||||
resolved "https://registry.yarnpkg.com/react-native-linear-gradient/-/react-native-linear-gradient-2.5.6.tgz#96215cbc5ec7a01247a20890888aa75b834d44a0"
|
||||
integrity sha512-HDwEaXcQIuXXCV70O+bK1rizFong3wj+5Q/jSyifKFLg0VWF95xh8XQgfzXwtq0NggL9vNjPKXa016KuFu+VFg==
|
||||
|
||||
react-native-popup-menu@^0.15.11:
|
||||
version "0.15.11"
|
||||
resolved "https://registry.yarnpkg.com/react-native-popup-menu/-/react-native-popup-menu-0.15.11.tgz#df96b1a909ecbba84487821061ce6e29e7c7bb20"
|
||||
integrity sha512-f5q2GoDN99bkA24wHiwasaErcdQEgyqYZ8IYuZPOrZNFr66E4rg6f4LElSVBA3EZJTSq5OddVeaOcU340bSTEg==
|
||||
|
||||
react-native-reanimated@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.2.0.tgz#a6412c56b4e591d1f00fac949f62d0c72c357c78"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user