mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-27 00:59:28 +01:00
song context menu
This commit is contained in:
parent
0416c0ad0d
commit
26749d0458
@ -1,14 +1,15 @@
|
||||
import PressableOpacity from '@app/components/PressableOpacity'
|
||||
import { AlbumListItem } from '@app/models/music'
|
||||
import { AlbumListItem, Song } from '@app/models/music'
|
||||
import colors from '@app/styles/colors'
|
||||
import font from '@app/styles/font'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { NavigationProp, 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 IconFA5 from 'react-native-vector-icons/FontAwesome5'
|
||||
import IconMat from 'react-native-vector-icons/MaterialIcons'
|
||||
import CoverArt from './CoverArt'
|
||||
|
||||
@ -97,56 +98,124 @@ const ContextMenuIconTextOption = React.memo<ContextMenuIconTextOptionProps>(
|
||||
),
|
||||
)
|
||||
|
||||
const MenuHeader = React.memo<{
|
||||
coverArt?: string
|
||||
title: string
|
||||
subtitle?: string
|
||||
}>(({ coverArt, title, subtitle }) => (
|
||||
<View style={styles.menuHeader}>
|
||||
{coverArt ? (
|
||||
<CoverArt coverArt={coverArt} style={styles.coverArt} resizeMode={FastImage.resizeMode.cover} />
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<View style={styles.menuHeaderText}>
|
||||
<Text numberOfLines={1} style={styles.menuTitle}>
|
||||
{title}
|
||||
</Text>
|
||||
{subtitle ? (
|
||||
<Text numberOfLines={1} style={styles.menuSubtitle}>
|
||||
{subtitle}
|
||||
</Text>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
))
|
||||
|
||||
const OptionStar = React.memo(() => (
|
||||
<ContextMenuIconTextOption IconComponent={IconFA} name="star-o" size={26} text="Star" />
|
||||
))
|
||||
|
||||
const OptionViewArtist = React.memo<{
|
||||
navigation: NavigationProp<any>
|
||||
artist?: string
|
||||
artistId?: string
|
||||
}>(({ navigation, artist, artistId }) => {
|
||||
if (!artist || !artistId) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
return (
|
||||
<ContextMenuIconTextOption
|
||||
IconComponent={IconFA}
|
||||
name="microphone"
|
||||
size={26}
|
||||
text="View artist"
|
||||
onSelect={() => navigation.navigate('artist', { id: artistId, title: artist })}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
const OptionViewAlbum = React.memo<{
|
||||
navigation: NavigationProp<any>
|
||||
album?: string
|
||||
albumId?: string
|
||||
}>(({ navigation, album, albumId }) => {
|
||||
if (!album || !albumId) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
return (
|
||||
<ContextMenuIconTextOption
|
||||
IconComponent={IconFA5}
|
||||
name="compact-disc"
|
||||
size={26}
|
||||
text="View album"
|
||||
onSelect={() => navigation.navigate('album', { id: albumId, title: album })}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
const OptionDownload = React.memo<{
|
||||
itemType: string
|
||||
}>(({ itemType }) => (
|
||||
<ContextMenuIconTextOption IconComponent={IconMat} name="file-download" size={26} text={`Download ${itemType}`} />
|
||||
))
|
||||
|
||||
export type AlbumContextPressableProps = ContextMenuProps & {
|
||||
album: AlbumListItem
|
||||
}
|
||||
|
||||
export const AlbumContextPressable: React.FC<AlbumContextPressableProps> = ({
|
||||
menuStyle,
|
||||
triggerWrapperStyle,
|
||||
triggerOuterWrapperStyle,
|
||||
triggerTouchableStyle,
|
||||
onPress,
|
||||
album,
|
||||
children,
|
||||
}) => {
|
||||
export const AlbumContextPressable: React.FC<AlbumContextPressableProps> = props => {
|
||||
const navigation = useNavigation()
|
||||
const { album, children } = props
|
||||
|
||||
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>
|
||||
}
|
||||
{...props}
|
||||
menuHeader={<MenuHeader title={album.name} subtitle={album.artist} coverArt={album.coverArt} />}
|
||||
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" />
|
||||
<OptionStar />
|
||||
<OptionViewArtist artist={album.artist} artistId={album.artistId} navigation={navigation} />
|
||||
<OptionDownload itemType={album.itemType} />
|
||||
</>
|
||||
}>
|
||||
{children}
|
||||
</ContextMenu>
|
||||
)
|
||||
}
|
||||
|
||||
export type SongContextPressableProps = ContextMenuProps & {
|
||||
song: Song
|
||||
}
|
||||
|
||||
export const SongContextPressable: React.FC<SongContextPressableProps> = props => {
|
||||
const navigation = useNavigation()
|
||||
const { song, children } = props
|
||||
|
||||
return (
|
||||
<ContextMenu
|
||||
{...props}
|
||||
menuHeader={<MenuHeader title={song.title} subtitle={song.artist} coverArt={song.coverArt} />}
|
||||
menuOptions={
|
||||
<>
|
||||
<OptionStar />
|
||||
<OptionViewArtist artist={song.artist} artistId={song.artistId} navigation={navigation} />
|
||||
<OptionViewAlbum album={song.album} albumId={song.albumId} navigation={navigation} />
|
||||
<OptionDownload itemType={song.itemType} />
|
||||
</>
|
||||
}>
|
||||
{children}
|
||||
@ -190,7 +259,7 @@ const styles = StyleSheet.create({
|
||||
color: colors.text.secondary,
|
||||
},
|
||||
option: {
|
||||
paddingVertical: 10,
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 20,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { AlbumListItem, ListableItem } from '@app/models/music'
|
||||
import { AlbumListItem, ListableItem, Song } from '@app/models/music'
|
||||
import { useStore } from '@app/state/store'
|
||||
import { selectTrackPlayer } from '@app/state/trackplayer'
|
||||
import colors from '@app/styles/colors'
|
||||
@ -10,7 +10,7 @@ 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 { AlbumContextPressable, SongContextPressable } from './ContextMenu'
|
||||
import CoverArt from './CoverArt'
|
||||
import PressableOpacity from './PressableOpacity'
|
||||
|
||||
@ -98,10 +98,20 @@ const ListItem: React.FC<{
|
||||
),
|
||||
[item, onPress],
|
||||
)
|
||||
const songPressable = useCallback(
|
||||
({ children }) => (
|
||||
<SongContextPressable song={item as Song} onPress={onPress} triggerWrapperStyle={styles.item}>
|
||||
{children}
|
||||
</SongContextPressable>
|
||||
),
|
||||
[item, onPress],
|
||||
)
|
||||
|
||||
let PressableComponent = itemPressable
|
||||
if (item.itemType === 'album') {
|
||||
PressableComponent = albumPressable
|
||||
} else if (item.itemType === 'song') {
|
||||
PressableComponent = songPressable
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@ -69,7 +69,9 @@ export interface Song {
|
||||
itemType: 'song'
|
||||
id: string
|
||||
album?: string
|
||||
albumId?: string
|
||||
artist?: string
|
||||
artistId?: string
|
||||
title: string
|
||||
track?: number
|
||||
duration?: number
|
||||
@ -168,7 +170,9 @@ export function mapChildToSong(child: ChildElement, client: SubsonicApiClient):
|
||||
itemType: 'song',
|
||||
id: child.id,
|
||||
album: child.album,
|
||||
albumId: child.albumId,
|
||||
artist: child.artist,
|
||||
artistId: child.artistId,
|
||||
title: child.title,
|
||||
track: child.track,
|
||||
duration: child.duration,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user