Skip to content

Commit

Permalink
[mobile] Add ContextMenu Modal
Browse files Browse the repository at this point in the history
  • Loading branch information
tsirysndr committed Apr 26, 2023
1 parent 751c186 commit 3afecd9
Show file tree
Hide file tree
Showing 18 changed files with 182 additions and 177 deletions.
Binary file modified mobile/bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"react-native-dotenv": "^3.4.8",
"react-native-gesture-handler": "^2.9.0",
"react-native-linear-gradient": "^2.6.2",
"react-native-modal": "^13.0.1",
"react-native-pager-view": "^6.2.0",
"react-native-screens": "^3.20.0",
"react-native-svg": "^13.9.0",
Expand Down
5 changes: 3 additions & 2 deletions mobile/src/Components/AlbumRow/AlbumRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,11 @@ const Button = styled.TouchableOpacity`
export type AlbumRowProps = {
album: Album;
onPressAlbum: () => void;
onPressContextMenu: (album: Album) => void;
};

const AlbumRow: FC<AlbumRowProps> = props => {
const {album, onPressAlbum} = props;
const {album, onPressAlbum, onPressContextMenu} = props;
const cover = useCover(album.cover);
return (
<TouchableWithoutFeedback onPress={onPressAlbum}>
Expand All @@ -78,7 +79,7 @@ const AlbumRow: FC<AlbumRowProps> = props => {
{album.artist}
</Artist>
</AlbumInfo>
<Button>
<Button onPress={() => onPressContextMenu(album)}>
<Ionicons name="ellipsis-vertical" color={'#ffffff99'} size={18} />
</Button>
</Container>
Expand Down
36 changes: 36 additions & 0 deletions mobile/src/Components/AlbumRow/AlbumRowWithData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, {FC} from 'react';
import AlbumRow from './AlbumRow';
import {Album} from '../../Types';
import {useNavigation} from '@react-navigation/native';
import {useRecoilState} from 'recoil';
import {contextMenuState} from '../ContextMenu/ContextMenuState';

export type AlbumRowWithDataProps = {
album: Album;
};

const AlbumRowWithData: FC<AlbumRowWithDataProps> = ({album}) => {
const navigation = useNavigation<any>();
const [contextMenu, setContextMenu] = useRecoilState(contextMenuState);
const onPressAlbum = () => {
navigation.navigate('AlbumDetails', {album});
};
const onContextMenu = (item: Album) => {
setContextMenu({
...contextMenu,
visible: true,
type: 'album',
item,
});
};

return (
<AlbumRow
album={album}
onPressAlbum={onPressAlbum}
onPressContextMenu={onContextMenu}
/>
);
};

export default AlbumRowWithData;
2 changes: 1 addition & 1 deletion mobile/src/Components/AlbumRow/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import AlbumRow from './AlbumRow';
import AlbumRow from './AlbumRowWithData';

export default AlbumRow;
Empty file.
38 changes: 38 additions & 0 deletions mobile/src/Components/ContextMenu/ContextMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React, {FC} from 'react';
import {StyleSheet} from 'react-native';
import Modal from 'react-native-modal';
import styled from '@emotion/native';

const Container = styled.View`
background-color: #000;
height: 300px;
`;

export type ContextMenuProps = {
isVisible: boolean;
onClose: () => void;
};

const ContextMenu: FC<ContextMenuProps> = ({isVisible, onClose}) => {
return (
<Modal
isVisible={isVisible}
backdropOpacity={0.03}
onBackdropPress={onClose}
onSwipeComplete={onClose}
swipeThreshold={500}
swipeDirection={['down']}
style={styles.modal}>
<Container></Container>
</Modal>
);
};

const styles = StyleSheet.create({
modal: {
justifyContent: 'flex-end',
margin: 0,
},
});

export default ContextMenu;
15 changes: 15 additions & 0 deletions mobile/src/Components/ContextMenu/ContextMenuState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {atom} from 'recoil';
import {Album, Track} from '../../Types';

export const contextMenuState = atom<{
visible: boolean;
type: string;
item?: Album | Track;
}>({
key: 'contextMenuState',
default: {
visible: false,
type: '',
item: undefined,
},
});
17 changes: 17 additions & 0 deletions mobile/src/Components/ContextMenu/ContextMenuWithData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React, {FC} from 'react';
import ContextMenu from './ContextMenu';
import {useRecoilState} from 'recoil';
import {contextMenuState} from './ContextMenuState';

const ContextMenuWithData: FC = () => {
const [contextMenu, setContextMenu] = useRecoilState(contextMenuState);
const onClose = () => {
setContextMenu({
...contextMenu,
visible: false,
});
};
return <ContextMenu isVisible={contextMenu.visible} onClose={onClose} />;
};

export default ContextMenuWithData;
3 changes: 3 additions & 0 deletions mobile/src/Components/ContextMenu/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ContextMenu from './ContextMenuWithData';

export default ContextMenu;
111 changes: 3 additions & 108 deletions mobile/src/Components/Songs/Songs.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import styled from '@emotion/native';
import React, {FC} from 'react';
import {TouchableWithoutFeedback} from 'react-native';
import Feather from 'react-native-vector-icons/Feather';
import Ionicons from 'react-native-vector-icons/Ionicons';
import {Track as TrackType} from '../../Types';
import {useCover} from '../../Hooks/useCover';
import TrackRow from '../TrackRow';

const Container = styled.View`
width: 100%;
Expand All @@ -30,118 +28,20 @@ const SeeAll = styled.Text`
font-size: 14px;
`;

const TrackRow = styled.View`
flex-direction: row;
align-items: center;
margin-left: 20px;
height: 60px;
`;

const TrackTitle = styled.Text<{active?: boolean}>`
color: #fff;
font-family: 'Gilroy-Bold';
font-size: 16px;
${props => props.active && 'color: #ab28fc;'}
`;

const TrackArtist = styled.Text`
color: #a7a7a9;
font-family: 'Gilroy-Bold';
font-size: 14px;
margin-top: 2px;
`;

const TrackInfo = styled.View`
flex-direction: column;
margin-left: 20px;
flex: 1;
`;

const Cover = styled.Image`
width: 60px;
height: 60px;
border-radius: 3px;
`;

const NoCover = styled.View`
width: 60px;
height: 60px;
background-color: #161515;
align-items: center;
justify-content: center;
border-radius: 3px;
`;

const TrackWrapper = styled.View`
background-color: #232323;
`;

const TouchableTrack = styled.TouchableOpacity`
background-color: #000;
justify-content: center;
height: 70px;
`;

const Button = styled.TouchableOpacity`
height: 40px;
width: 40px;
align-items: center;
justify-content: center;
z-index: 1;
`;

const SeeAllContainer = styled.View`
height: 30px;
width: 60px;
align-items: flex-end;
justify-content: center;
`;

export type TrackProps = {
item: any;
active: boolean;
onPress: (item: any) => void;
};

const Track: FC<TrackProps> = props => {
const {item, active, onPress} = props;
const cover = useCover(item.cover);
return (
<TrackWrapper>
<TouchableTrack onPress={() => onPress(item)}>
<TrackRow>
{item.cover && <Cover source={{uri: cover}} />}
{!item.cover && (
<NoCover>
<Feather name="music" size={30} color="#a7a7a9" />
</NoCover>
)}
<TrackInfo>
<TrackTitle active={active} ellipsizeMode="tail" numberOfLines={1}>
{item.title}
</TrackTitle>
<TrackArtist ellipsizeMode="tail" numberOfLines={1}>
{item.artist}
</TrackArtist>
</TrackInfo>
<Button>
<Ionicons name="ellipsis-vertical" color={'#ffffff99'} size={18} />
</Button>
</TrackRow>
</TouchableTrack>
</TrackWrapper>
);
};

export type SongsProps = {
tracks: TrackType[];
currentTrack?: TrackType;
onSeeAll: () => void;
onPressTrack: (item: any) => void;
};

const Songs: FC<SongsProps> = props => {
const {tracks, currentTrack, onSeeAll, onPressTrack} = props;
const {tracks, onSeeAll} = props;
return (
<Container>
<Header>
Expand All @@ -153,12 +53,7 @@ const Songs: FC<SongsProps> = props => {
</TouchableWithoutFeedback>
</Header>
{tracks.map((item: any) => (
<Track
key={item.id}
item={item}
active={item.id === currentTrack?.id}
onPress={onPressTrack}
/>
<TrackRow key={item.id} track={item} />
))}
</Container>
);
Expand Down
15 changes: 0 additions & 15 deletions mobile/src/Components/Songs/SongsWithData.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import React, {FC} from 'react';
import Songs from './Songs';
import {useRecoilState} from 'recoil';
import {currentTrackState} from '../CurrentTrack/CurrentTrackState';
import {useGetTracksQuery} from '../../Hooks/GraphQL';
import {useNavigation} from '@react-navigation/native';
import {playQueueState} from '../PlayQueue/PlayQueueState';

const SongsWithData: FC = () => {
const navigation = useNavigation<any>();
Expand All @@ -14,17 +11,7 @@ const SongsWithData: FC = () => {
},
});
const tracks = !loading && data ? data.tracks : [];
const [currentTrack, setCurrentTrack] = useRecoilState(currentTrackState);
const [playQueue, setPlayQueue] = useRecoilState(playQueueState);
const onSeeAll = () => navigation.navigate('Tracks');
const onPressTrack = (track: any) => {
setCurrentTrack(track);
setPlayQueue({
...playQueue,
previousTracks: playQueue.previousTracks.concat(track),
position: playQueue.previousTracks.length,
});
};
return (
<Songs
tracks={tracks.map(track => ({
Expand All @@ -37,8 +24,6 @@ const SongsWithData: FC = () => {
artistId: track.artists[0].id,
albumId: track.album.id,
}))}
currentTrack={currentTrack}
onPressTrack={onPressTrack}
onSeeAll={onSeeAll}
/>
);
Expand Down
Loading

0 comments on commit 3afecd9

Please sign in to comment.