Skip to content

Commit 4c67a62

Browse files
committed
[mobile] update ContextMenu
[mobile] update ContextMenu
1 parent 3afecd9 commit 4c67a62

File tree

6 files changed

+254
-52
lines changed

6 files changed

+254
-52
lines changed

mobile/src/Components/ContextMenu/ContextMenu.tsx

Lines changed: 180 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,204 @@ import React, {FC} from 'react';
22
import {StyleSheet} from 'react-native';
33
import Modal from 'react-native-modal';
44
import styled from '@emotion/native';
5+
import Feather from 'react-native-vector-icons/Feather';
6+
import MaterialCommunity from 'react-native-vector-icons/MaterialCommunityIcons';
7+
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
8+
import {Album, Artist, Track} from '../../Types';
9+
import {useCover} from '../../Hooks/useCover';
10+
import SvgMic from '../Icons/Mic';
511

612
const Container = styled.View`
713
background-color: #000;
8-
height: 300px;
14+
height: 420px;
15+
`;
16+
17+
const MetadataRow = styled.View`
18+
height: 110px;
19+
width: 100%;
20+
flex-direction: row;
21+
align-items: center;
22+
padding-left: 20px;
23+
background-color: #000;
24+
`;
25+
26+
const Separator = styled.View`
27+
height: 1px;
28+
width: 100%;
29+
background-color: #3e3e3ec4;
30+
margin-bottom: 10px;
31+
`;
32+
33+
const Cover = styled.Image`
34+
width: 80px;
35+
height: 80px;
36+
`;
37+
38+
const NoAlbumCover = styled.View`
39+
width: 80px;
40+
height: 80px;
41+
background-color: #161515;
42+
align-items: center;
43+
justify-content: center;
44+
`;
45+
46+
const Title = styled.Text`
47+
color: #fff;
48+
font-family: 'Gilroy-Bold';
49+
font-size: 16px;
50+
`;
51+
52+
const ArtistName = styled.Text`
53+
color: #a7a7a9;
54+
font-family: 'Gilroy-Bold';
55+
font-size: 14px;
56+
margin-top: 2px;
57+
`;
58+
59+
const MediaInfo = styled.View`
60+
flex-direction: column;
61+
margin-left: 15px;
62+
flex: 1;
63+
`;
64+
65+
const IconWrapper = styled.View`
66+
height: 40px;
67+
width: 40px;
68+
justify-content: center;
69+
margin-right: 10px;
70+
`;
71+
72+
const Action = styled.TouchableWithoutFeedback``;
73+
74+
const ActionWrapper = styled.View`
75+
height: 60px;
76+
width: 100%;
77+
flex-direction: row;
78+
align-items: center;
79+
padding-left: 20px;
80+
background-color: #000;
81+
`;
82+
83+
const ActionTitle = styled.Text`
84+
color: #fff;
85+
font-family: 'Gilroy-Bold';
86+
font-size: 16px;
987
`;
1088

1189
export type ContextMenuProps = {
1290
isVisible: boolean;
1391
onClose: () => void;
92+
item?: Artist | Album | Track;
93+
type: 'artist' | 'album' | 'track' | '';
94+
onPlayNext: (item: Album | Track) => void;
95+
onAddToPlaylist: (item: Album | Track) => void;
96+
onDownload: (item: Album | Track) => void;
97+
onGoToArtist: (item: Album | Track) => void;
98+
onGoToAlbum: (item: Album | Track) => void;
1499
};
15100

16-
const ContextMenu: FC<ContextMenuProps> = ({isVisible, onClose}) => {
101+
const ContextMenu: FC<ContextMenuProps> = props => {
102+
const {
103+
isVisible,
104+
onClose,
105+
onAddToPlaylist,
106+
onDownload,
107+
onGoToAlbum,
108+
onGoToArtist,
109+
onPlayNext,
110+
item,
111+
type,
112+
} = props;
113+
const itemCover = {
114+
artist: (item as Artist)?.picture,
115+
album: (item as Album)?.cover,
116+
track: (item as Track)?.cover,
117+
'': undefined,
118+
};
119+
const cover = useCover(itemCover[type]);
17120
return (
18121
<Modal
19122
isVisible={isVisible}
20123
backdropOpacity={0.03}
21124
onBackdropPress={onClose}
22125
onSwipeComplete={onClose}
23-
swipeThreshold={500}
24126
swipeDirection={['down']}
25127
style={styles.modal}>
26-
<Container></Container>
128+
<Container>
129+
<MetadataRow>
130+
{itemCover[type] && <Cover source={{uri: cover}} />}
131+
{!itemCover[type] && (
132+
<NoAlbumCover>
133+
<Feather name="disc" size={40} color="#a7a7a9" />
134+
</NoAlbumCover>
135+
)}
136+
<MediaInfo>
137+
<Title numberOfLines={1} ellipsizeMode="tail">
138+
{type === 'artist'
139+
? (item as Artist)?.name
140+
: (item as Album)?.title}
141+
</Title>
142+
<ArtistName numberOfLines={1} ellipsizeMode="tail">
143+
{type === 'artist'
144+
? (item as Artist)?.name
145+
: (item as Album)?.artist}
146+
</ArtistName>
147+
</MediaInfo>
148+
</MetadataRow>
149+
<Separator />
150+
<Action onPress={() => onPlayNext(item as Album | Track)}>
151+
<ActionWrapper>
152+
<IconWrapper>
153+
<MaterialIcons name="playlist-play" size={31} color="#ab28fc" />
154+
</IconWrapper>
155+
<ActionTitle>Play next</ActionTitle>
156+
</ActionWrapper>
157+
</Action>
158+
{(type === 'track' || type === 'album') && (
159+
<>
160+
<Action onPress={() => onDownload(item as Album | Track)}>
161+
<ActionWrapper>
162+
<IconWrapper>
163+
<MaterialCommunity
164+
name="download"
165+
size={25}
166+
color="#ab28fc"
167+
/>
168+
</IconWrapper>
169+
<ActionTitle>Download</ActionTitle>
170+
</ActionWrapper>
171+
</Action>
172+
<Action onPress={() => onAddToPlaylist(item as Album | Track)}>
173+
<ActionWrapper>
174+
<IconWrapper>
175+
<MaterialIcons
176+
name="playlist-add"
177+
size={28}
178+
color="#ab28fc"
179+
/>
180+
</IconWrapper>
181+
<ActionTitle>Add to playlist</ActionTitle>
182+
</ActionWrapper>
183+
</Action>
184+
<Action onPress={() => onGoToAlbum(item as Album | Track)}>
185+
<ActionWrapper>
186+
<IconWrapper>
187+
<Feather name="disc" size={24} color="#ab28fc" />
188+
</IconWrapper>
189+
<ActionTitle>Go to Album</ActionTitle>
190+
</ActionWrapper>
191+
</Action>
192+
<Action onPress={() => onGoToArtist(item as Album | Track)}>
193+
<ActionWrapper>
194+
<IconWrapper>
195+
<SvgMic height={26} width={26} fill="#ab28fc" />
196+
</IconWrapper>
197+
<ActionTitle>Go to Artist</ActionTitle>
198+
</ActionWrapper>
199+
</Action>
200+
</>
201+
)}
202+
</Container>
27203
</Modal>
28204
);
29205
};

mobile/src/Components/ContextMenu/ContextMenuState.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {Album, Track} from '../../Types';
33

44
export const contextMenuState = atom<{
55
visible: boolean;
6-
type: string;
6+
type: 'album' | 'track' | 'artist' | '';
77
item?: Album | Track;
88
}>({
99
key: 'contextMenuState',

mobile/src/Components/ContextMenu/ContextMenuWithData.tsx

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, {FC} from 'react';
22
import ContextMenu from './ContextMenu';
33
import {useRecoilState} from 'recoil';
44
import {contextMenuState} from './ContextMenuState';
5+
import {Album, Track} from '../../Types';
56

67
const ContextMenuWithData: FC = () => {
78
const [contextMenu, setContextMenu] = useRecoilState(contextMenuState);
@@ -11,7 +12,40 @@ const ContextMenuWithData: FC = () => {
1112
visible: false,
1213
});
1314
};
14-
return <ContextMenu isVisible={contextMenu.visible} onClose={onClose} />;
15+
const onPlayNext = (item: Album | Track) => {
16+
onClose();
17+
console.log(item);
18+
};
19+
const onDownload = (item: Album | Track) => {
20+
onClose();
21+
console.log(item);
22+
};
23+
const onAddToPlaylist = (item: Album | Track) => {
24+
onClose();
25+
console.log(item);
26+
};
27+
const onGoToArtist = (item: Album | Track) => {
28+
onClose();
29+
console.log(item);
30+
};
31+
const onGoToAlbum = (item: Album | Track) => {
32+
onClose();
33+
console.log(item);
34+
};
35+
36+
return (
37+
<ContextMenu
38+
isVisible={contextMenu.visible}
39+
onClose={onClose}
40+
item={contextMenu.item}
41+
type={contextMenu.type}
42+
onPlayNext={onPlayNext}
43+
onDownload={onDownload}
44+
onAddToPlaylist={onAddToPlaylist}
45+
onGoToArtist={onGoToArtist}
46+
onGoToAlbum={onGoToAlbum}
47+
/>
48+
);
1549
};
1650

1751
export default ContextMenuWithData;

mobile/src/Components/TrackRow/TrackRow.tsx

Lines changed: 35 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import styled, {css} from '@emotion/native';
44
import Feather from 'react-native-vector-icons/Feather';
55
import Ionicons from 'react-native-vector-icons/Ionicons';
66
import {useCover} from '../../Hooks/useCover';
7-
import ContextMenu from '../ContextMenu';
87

98
const Container = styled.View`
109
height: 80px;
@@ -92,48 +91,41 @@ const TrackRow: FC<TrackRowProps> = props => {
9291
const {track, currentTrack, onPlay, showAlbum, onPressContextMenu} = props;
9392
const cover = useCover(track.cover);
9493
return (
95-
<>
96-
<TrackWrapper>
97-
<TouchableTrack onPress={() => onPlay(track)}>
98-
<Container>
99-
{showAlbum && (
100-
<>
101-
{track.cover && <Cover source={{uri: cover}} />}
102-
{!track.cover && (
103-
<NoAlbumCover>
104-
<Feather name="disc" size={40} color="#a7a7a9" />
105-
</NoAlbumCover>
106-
)}
107-
</>
108-
)}
109-
{!showAlbum && (
110-
<TrackNumberWrapper>
111-
<TrackNumber>{track.trackNumber}</TrackNumber>
112-
</TrackNumberWrapper>
113-
)}
114-
<AlbumInfo>
115-
<Title
116-
numberOfLines={1}
117-
ellipsizeMode="tail"
118-
active={track.id === currentTrack?.id}>
119-
{track.title}
120-
</Title>
121-
<Artist numberOfLines={1} ellipsizeMode="tail">
122-
{track.artist}
123-
</Artist>
124-
</AlbumInfo>
125-
<Button onPress={() => onPressContextMenu(track)}>
126-
<Ionicons
127-
name="ellipsis-vertical"
128-
color={'#ffffff99'}
129-
size={18}
130-
/>
131-
</Button>
132-
</Container>
133-
</TouchableTrack>
134-
</TrackWrapper>
135-
<ContextMenu />
136-
</>
94+
<TrackWrapper>
95+
<TouchableTrack onPress={() => onPlay(track)}>
96+
<Container>
97+
{showAlbum && (
98+
<>
99+
{track.cover && <Cover source={{uri: cover}} />}
100+
{!track.cover && (
101+
<NoAlbumCover>
102+
<Feather name="disc" size={40} color="#a7a7a9" />
103+
</NoAlbumCover>
104+
)}
105+
</>
106+
)}
107+
{!showAlbum && (
108+
<TrackNumberWrapper>
109+
<TrackNumber>{track.trackNumber}</TrackNumber>
110+
</TrackNumberWrapper>
111+
)}
112+
<AlbumInfo>
113+
<Title
114+
numberOfLines={1}
115+
ellipsizeMode="tail"
116+
active={track.id === currentTrack?.id}>
117+
{track.title}
118+
</Title>
119+
<Artist numberOfLines={1} ellipsizeMode="tail">
120+
{track.artist}
121+
</Artist>
122+
</AlbumInfo>
123+
<Button onPress={() => onPressContextMenu(track)}>
124+
<Ionicons name="ellipsis-vertical" color={'#ffffff99'} size={18} />
125+
</Button>
126+
</Container>
127+
</TouchableTrack>
128+
</TrackWrapper>
137129
);
138130
};
139131

mobile/src/Containers/Home/Home.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Artists from '../../Components/Artists';
77
import Songs from '../../Components/Songs';
88
import MiniPlayer from '../../Components/MiniPlayer';
99
import {useNavigation} from '@react-navigation/native';
10+
import ContextMenu from '../../Components/ContextMenu';
1011

1112
const MainContainer = styled.View`
1213
flex: 1;
@@ -64,6 +65,7 @@ const Home: FC = () => {
6465
<MiniPlayerWrapper>
6566
<MiniPlayer />
6667
</MiniPlayerWrapper>
68+
<ContextMenu />
6769
</MainContainer>
6870
);
6971
};

mobile/src/Navigation/AppNavigation.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import ArtistDetails from '../Containers/ArtistDetails';
1818
import AlbumDetails from '../Containers/AlbumDetails';
1919
import Settings from '../Containers/Settings';
2020
import Filter from '../Containers/Filter';
21-
import ContextMenu from '../Components/ContextMenu';
2221

2322
const RootStack = createStackNavigator();
2423
const MainStack = createStackNavigator();
@@ -146,10 +145,9 @@ const AppNavigator: FC = () => (
146145
headerShown: false,
147146
gestureEnabled: true,
148147
animationEnabled: true,
149-
presentation: 'transparentModal',
148+
presentation: 'modal',
150149
}}>
151150
<RootStack.Screen name="Player" component={Player} />
152-
<RootStack.Screen name="ContextMenu" component={ContextMenu} />
153151
</RootStack.Group>
154152
</RootStack.Navigator>
155153
</NavigationContainer>

0 commit comments

Comments
 (0)