UI changes and Android compatibility
This commit is contained in:
@@ -3,6 +3,7 @@ import { StyleSheet, Text, View, Pressable } from "react-native";
|
||||
import { NavigationContainer } from "@react-navigation/native";
|
||||
import { createNativeStackNavigator } from "@react-navigation/native-stack";
|
||||
import { createContext, useState, useContext } from "react";
|
||||
import { SafeAreaProvider } from "react-native-safe-area-context";
|
||||
|
||||
import MediaPlayer from "./src/components/MediaPlayer";
|
||||
|
||||
@@ -26,9 +27,7 @@ export function UsePlayer() {
|
||||
}
|
||||
|
||||
function PlayerProvider({ children }) {
|
||||
const [currentTrack, setCurrentTrack] = useState({
|
||||
title: "Swans - A Piece Of The Sky",
|
||||
});
|
||||
const [currentTrack, setCurrentTrack] = useState();
|
||||
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
|
||||
@@ -61,7 +60,7 @@ function RootLayout() {
|
||||
const { currentTrack } = UsePlayer();
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<SafeAreaProvider style={styles.container}>
|
||||
<StatusBar style="auto" />
|
||||
|
||||
<AppNavigator />
|
||||
@@ -69,7 +68,7 @@ function RootLayout() {
|
||||
<View style={styles.mediaPlayerWrapper} pointerEvents="box-none">
|
||||
<MediaPlayer title={currentTrack?.title ?? "No track"} />
|
||||
</View>
|
||||
</View>
|
||||
</SafeAreaProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,33 +1,38 @@
|
||||
import { useState } from "react";
|
||||
import { TouchableOpacity, Text, StyleSheet } from "react-native";
|
||||
import { View } from "react-native-web";
|
||||
import { View, Text, StyleSheet } from "react-native";
|
||||
import { Button, Slider } from "rn-inkpad";
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
|
||||
export default function MediaPlayer({ title, onPress }) {
|
||||
export default function MediaPlayer() {
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const [progress, setProgress] = useState(35);
|
||||
|
||||
return (
|
||||
<LinearGradient
|
||||
colors={[
|
||||
"rgba(25,25,25,0.95)", // top
|
||||
"rgba(15,15,15,0.98)", // middle-top
|
||||
"rgba(5,5,5,1)", // middle-bottom
|
||||
"rgba(5,5,5,1)", // bottom
|
||||
"rgba(25,25,25,0.95)",
|
||||
"rgba(15,15,15,0.98)",
|
||||
"rgba(5,5,5,1)",
|
||||
"rgba(5,5,5,1)",
|
||||
]}
|
||||
locations={[0, 0.55, 1]}
|
||||
locations={[0, 0.55, 0.85, 1]}
|
||||
start={{ x: 0.5, y: 0 }}
|
||||
end={{ x: 0.5, y: 1 }}
|
||||
style={styles.box}
|
||||
>
|
||||
<Text style={styles.name_album_and_artist}>{title}</Text>
|
||||
<Text style={styles.name_album_and_artist}>
|
||||
PlaceholderArtist - PlaceholderTrack
|
||||
</Text>
|
||||
|
||||
<View style={styles.playback_row}>
|
||||
<Text style={styles.time}>1:20</Text>
|
||||
|
||||
<View style={styles.slider_wrapper}>
|
||||
<Slider
|
||||
value={35}
|
||||
value={progress}
|
||||
min={0}
|
||||
max={100}
|
||||
onChange={setProgress}
|
||||
onValueChange={setProgress}
|
||||
trackStyles={{
|
||||
height: 5,
|
||||
borderRadius: 5,
|
||||
@@ -39,7 +44,6 @@ export default function MediaPlayer({ title, onPress }) {
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Text style={styles.time}>4:08</Text>
|
||||
</View>
|
||||
|
||||
@@ -57,12 +61,14 @@ export default function MediaPlayer({ title, onPress }) {
|
||||
const styles = StyleSheet.create({
|
||||
box: {
|
||||
borderRadius: 15,
|
||||
padding: 20,
|
||||
paddingHorizontal: 16,
|
||||
paddingTop: 12,
|
||||
paddingBottom: 10,
|
||||
alignSelf: "stretch",
|
||||
marginHorizontal: 5,
|
||||
height: "20%",
|
||||
minHeight: 120,
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
opacity: 0.98,
|
||||
},
|
||||
name_album_and_artist: {
|
||||
color: "#ffffff",
|
||||
@@ -70,11 +76,10 @@ const styles = StyleSheet.create({
|
||||
fontWeight: "bold",
|
||||
},
|
||||
playback_row: {
|
||||
flex: 1,
|
||||
flexDirection: "row",
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
marginTop: 15,
|
||||
marginTop: 10,
|
||||
},
|
||||
slider_wrapper: {
|
||||
flex: 1,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Pressable, View, Text, StyleSheet, Image } from 'react-native';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { durationFormatter } from './DurationFormatter';
|
||||
|
||||
export default function TrackRow({
|
||||
@@ -16,20 +17,22 @@ export default function TrackRow({
|
||||
{cover ? <Image source={cover} style={styles.trackCover} resizeMode="cover" /> : null}
|
||||
|
||||
<View style={styles.trackTextBlock}>
|
||||
<Text style={styles.trackTitle} numberOfLines={1}>
|
||||
{title}
|
||||
</Text>
|
||||
<Text style={styles.trackArtist} numberOfLines={1}>
|
||||
{artist}
|
||||
</Text>
|
||||
<Text style={styles.trackTitle} numberOfLines={1}>{title}</Text>
|
||||
<Text style={styles.trackArtist} numberOfLines={1}>{artist}</Text>
|
||||
</View>
|
||||
|
||||
{duration ? <Text style={styles.trackDuration}>{durationFormatter(duration)}</Text> : "0"}
|
||||
{duration ? (
|
||||
<Text style={styles.trackDuration}>{durationFormatter(duration)}</Text>
|
||||
) : (
|
||||
<Text style={styles.trackDuration}>0:00</Text>
|
||||
)}
|
||||
|
||||
<Pressable onPress={onToggleLike} hitSlop={10}>
|
||||
<Text style={[styles.heart, liked && styles.heartLiked]}>
|
||||
{liked ? '♥' : '♡'}
|
||||
</Text>
|
||||
<Pressable onPress={onToggleLike} hitSlop={10} style={styles.heartBtn}>
|
||||
<Ionicons
|
||||
name={liked ? "heart" : "heart-outline"}
|
||||
size={22}
|
||||
color={liked ? "#ff4d6d" : "#fff"}
|
||||
/>
|
||||
</Pressable>
|
||||
</Pressable>
|
||||
);
|
||||
@@ -67,14 +70,9 @@ const styles = StyleSheet.create({
|
||||
fontSize: 15,
|
||||
paddingHorizontal: 18,
|
||||
},
|
||||
heart: {
|
||||
fontSize: 22,
|
||||
color: '#fff',
|
||||
paddingHorizontal: 10,
|
||||
},
|
||||
heartLiked: {
|
||||
fontSize: 22,
|
||||
color: '#ff4d6d',
|
||||
paddingHorizontal: 10,
|
||||
heartBtn: {
|
||||
width: 32,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { View, Text, StyleSheet, FlatList, Pressable, Image } from 'react-native';
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
import TrackRow from '../components/TrackRow';
|
||||
import { useLibrary } from '../contexts/LibraryContext';
|
||||
import { durationFormatter } from '../components/DurationFormatter';
|
||||
@@ -8,14 +9,13 @@ export default function AlbumScreen({ route, navigation }) {
|
||||
const { album: routeAlbum } = route.params;
|
||||
const { albums, toggleLike } = useLibrary();
|
||||
|
||||
// Always use latest album from context (so likes stay in sync)
|
||||
const album = useMemo(
|
||||
() => albums.find((a) => a.id === routeAlbum.id) ?? routeAlbum,
|
||||
[albums, routeAlbum]
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<SafeAreaView style={styles.container}>
|
||||
<Pressable onPress={() => navigation.goBack()} style={styles.backBtn}>
|
||||
<Text style={styles.backText}>← Back</Text>
|
||||
</Pressable>
|
||||
@@ -46,7 +46,7 @@ export default function AlbumScreen({ route, navigation }) {
|
||||
)}
|
||||
contentContainerStyle={styles.trackList}
|
||||
/>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -54,48 +54,11 @@ const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#000',
|
||||
paddingTop: 28,
|
||||
paddingHorizontal: 16,
|
||||
paddingBottom: 24,
|
||||
},
|
||||
header: {
|
||||
alignItems: 'center',
|
||||
},
|
||||
cover: {
|
||||
width: 180,
|
||||
height: 180,
|
||||
borderRadius: 8,
|
||||
marginBottom: 12,
|
||||
},
|
||||
title: {
|
||||
color: '#fff',
|
||||
fontSize: 32,
|
||||
fontWeight: '700',
|
||||
textAlign: 'center',
|
||||
marginBottom: 1,
|
||||
},
|
||||
artist: {
|
||||
color: '#fff',
|
||||
fontSize: 26,
|
||||
fontWeight: '500',
|
||||
textAlign: 'center',
|
||||
marginTop: 1,
|
||||
},
|
||||
meta: {
|
||||
color: '#fff',
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
textAlign: 'center',
|
||||
marginTop: 8,
|
||||
marginBottom: 24,
|
||||
},
|
||||
trackList: {
|
||||
width: '100%',
|
||||
paddingHorizontal: 16,
|
||||
paddingBottom: 180,
|
||||
},
|
||||
backBtn: {
|
||||
marginBottom: 10,
|
||||
marginBottom: 8,
|
||||
alignSelf: 'flex-start',
|
||||
},
|
||||
backText: {
|
||||
@@ -103,4 +66,41 @@ const styles = StyleSheet.create({
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
},
|
||||
header: {
|
||||
alignItems: 'center',
|
||||
marginBottom: 10,
|
||||
},
|
||||
cover: {
|
||||
width: 140,
|
||||
height: 140,
|
||||
borderRadius: 8,
|
||||
marginBottom: 8,
|
||||
},
|
||||
title: {
|
||||
color: '#fff',
|
||||
fontSize: 24,
|
||||
fontWeight: '700',
|
||||
textAlign: 'center',
|
||||
marginBottom: 0,
|
||||
},
|
||||
artist: {
|
||||
color: '#fff',
|
||||
fontSize: 18,
|
||||
fontWeight: '500',
|
||||
textAlign: 'center',
|
||||
marginTop: 1,
|
||||
},
|
||||
meta: {
|
||||
color: '#cfcfcf',
|
||||
fontSize: 12,
|
||||
fontWeight: '500',
|
||||
textAlign: 'center',
|
||||
marginTop: 6,
|
||||
marginBottom: 10,
|
||||
},
|
||||
trackList: {
|
||||
width: '100%',
|
||||
paddingHorizontal: 4,
|
||||
paddingBottom: 180,
|
||||
},
|
||||
});
|
||||
@@ -11,6 +11,7 @@ import { Ionicons } from "@expo/vector-icons";
|
||||
import { useNavigation } from "@react-navigation/native";
|
||||
import TrackRow from "../components/TrackRow";
|
||||
import { useLibrary } from "../contexts/LibraryContext";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
|
||||
export default function HomeScreen() {
|
||||
const navigation = useNavigation();
|
||||
@@ -19,7 +20,7 @@ export default function HomeScreen() {
|
||||
const homeLikedTracks = useMemo(() => likedTracks.slice(0, 5), [likedTracks]);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<SafeAreaView style={styles.container}>
|
||||
<View style={styles.headerRow}>
|
||||
<Text style={styles.homeTitle}>Home</Text>
|
||||
<View style={styles.headerActions}>
|
||||
@@ -77,7 +78,7 @@ export default function HomeScreen() {
|
||||
)}
|
||||
showsVerticalScrollIndicator={false}
|
||||
/>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from "react-native";
|
||||
import TrackRow from "../components/TrackRow";
|
||||
import { useLibrary } from "../contexts/LibraryContext";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
|
||||
export default function LikedTracksScreen({ navigation }) {
|
||||
const [query, setQuery] = useState("");
|
||||
@@ -48,7 +49,7 @@ export default function LikedTracksScreen({ navigation }) {
|
||||
}, [likedTracks, query, sortBy, sortDir]);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<SafeAreaView style={styles.container}>
|
||||
<Pressable onPress={() => navigation.goBack()} style={styles.backBtn}>
|
||||
<Text style={styles.backText}>← Back</Text>
|
||||
</Pressable>
|
||||
@@ -123,7 +124,7 @@ export default function LikedTracksScreen({ navigation }) {
|
||||
keyboardShouldPersistTaps="handled"
|
||||
contentContainerStyle={{ paddingBottom: 180 }}
|
||||
/>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import { View, Text, StyleSheet, TextInput, Pressable } from "react-native";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
|
||||
export default function LoginScreen({ navigation }) {
|
||||
const [email, setEmail] = useState("");
|
||||
@@ -18,7 +19,7 @@ export default function LoginScreen({ navigation }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<SafeAreaView style={styles.container}>
|
||||
<Pressable onPress={() => navigation.goBack()} style={styles.backBtn}>
|
||||
<Text style={styles.backText}>← Back</Text>
|
||||
</Pressable>
|
||||
@@ -55,7 +56,7 @@ export default function LoginScreen({ navigation }) {
|
||||
<Pressable style={styles.SignupBtn} onPress={OnSignUp}>
|
||||
<Text style={styles.SignupBtnText}>Sign Up</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import { View, Text, StyleSheet, TextInput, Pressable } from "react-native";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
|
||||
export default function PasswordResetScreen({ navigation }) {
|
||||
const [email, setEmail] = useState("");
|
||||
@@ -9,7 +10,7 @@ export default function PasswordResetScreen({ navigation }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<SafeAreaView style={styles.container}>
|
||||
<Pressable onPress={() => navigation.goBack()} style={styles.backBtn}>
|
||||
<Text style={styles.backText}>← Back</Text>
|
||||
</Pressable>
|
||||
@@ -29,7 +30,7 @@ export default function PasswordResetScreen({ navigation }) {
|
||||
<Pressable style={styles.primaryBtn} onPress={onResetButtonPress}>
|
||||
<Text style={styles.primaryBtnText}>Send email</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import React, { useState } from "react";
|
||||
import { View, Text, StyleSheet, Pressable, Switch } from "react-native";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
|
||||
export default function SettingsScreen({ navigation }) {
|
||||
const [notificationsEnabled, setNotificationsEnabled] = useState(true);
|
||||
const [highQuality, setHighQuality] = useState(false);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<SafeAreaView style={styles.container}>
|
||||
<Pressable onPress={() => navigation.goBack()} style={styles.backBtn}>
|
||||
<Text style={styles.backText}>← Back</Text>
|
||||
</Pressable>
|
||||
@@ -32,7 +33,7 @@ export default function SettingsScreen({ navigation }) {
|
||||
>
|
||||
<Text style={styles.logoutBtnText}>Log out</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import { View, Text, StyleSheet, TextInput, Pressable } from "react-native";
|
||||
import { SafeAreaView } from "react-native-safe-area-context";
|
||||
|
||||
export default function SignUpScreen({ navigation }) {
|
||||
const [email, setEmail] = useState("");
|
||||
@@ -10,7 +11,7 @@ export default function SignUpScreen({ navigation }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<SafeAreaView style={styles.container}>
|
||||
<Pressable onPress={() => navigation.goBack()} style={styles.backBtn}>
|
||||
<Text style={styles.backText}>← Back</Text>
|
||||
</Pressable>
|
||||
@@ -48,7 +49,7 @@ export default function SignUpScreen({ navigation }) {
|
||||
<Pressable style={styles.primaryBtn} onPress={onSignUp}>
|
||||
<Text style={styles.primaryBtnText}>Sign Up</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user