diff --git a/jukebox/App.js b/jukebox/App.js index 864c1a9..aedafe3 100644 --- a/jukebox/App.js +++ b/jukebox/App.js @@ -6,6 +6,7 @@ import MediaPlayer from "./src/components/MediaPlayer"; import HomeScreen from "./src/screens/HomeScreen"; import { createContext, useState, useContext } from "react"; import LikedTracksScreen from "./src/screens/LikedTracksScreen"; +import AlbumScreen from "./src/screens/AlbumScreen"; const Stack = createNativeStackNavigator(); @@ -37,6 +38,7 @@ function AppNavigator() { + ); diff --git a/jukebox/assets/covers/discovery.jpg b/jukebox/assets/covers/discovery.jpg new file mode 100644 index 0000000..8fdce7d Binary files /dev/null and b/jukebox/assets/covers/discovery.jpg differ diff --git a/jukebox/assets/covers/soundtracksfortheblind.jpg b/jukebox/assets/covers/soundtracksfortheblind.jpg new file mode 100644 index 0000000..b36d517 Binary files /dev/null and b/jukebox/assets/covers/soundtracksfortheblind.jpg differ diff --git a/jukebox/package-lock.json b/jukebox/package-lock.json index ecfd89f..1f27ad9 100644 --- a/jukebox/package-lock.json +++ b/jukebox/package-lock.json @@ -64,6 +64,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1344,7 +1345,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1434,6 +1434,7 @@ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", "license": "MIT", + "peer": true, "engines": { "node": ">=6.9.0" } @@ -3000,9 +3001,9 @@ } }, "node_modules/@react-native/codegen/node_modules/minimatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", - "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -3170,6 +3171,7 @@ "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.31.tgz", "integrity": "sha512-+YCUwtfDgsux59Q0LDHc3Zid9ih93ecUCFWZOH6/+eNoUGnWx77wjS6ZfvBO/7E+EiIup11IVShDzCHR4of8hw==", "license": "MIT", + "peer": true, "dependencies": { "@react-navigation/core": "^7.15.1", "escape-string-regexp": "^4.0.0", @@ -4005,6 +4007,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -4677,6 +4680,7 @@ "resolved": "https://registry.npmjs.org/expo/-/expo-54.0.33.tgz", "integrity": "sha512-3yOEfAKqo+gqHcV8vKcnq0uA5zxlohnhA3fu4G43likN8ct5ZZ3LjAh9wDdKteEkoad3tFPvwxmXW711S5OHUw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.20.0", "@expo/cli": "54.0.23", @@ -4729,6 +4733,7 @@ "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-14.0.11.tgz", "integrity": "sha512-ga0q61ny4s/kr4k8JX9hVH69exVSIfcIc19+qZ7gt71Mqtm7xy2c6kwsPTCyhBW2Ro5yXTT8EaZOpuRi35rHbg==", "license": "MIT", + "peer": true, "dependencies": { "fontfaceobserver": "^2.1.0" }, @@ -5382,9 +5387,9 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", - "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.2" @@ -6992,12 +6997,12 @@ } }, "node_modules/minimatch": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", - "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "license": "ISC", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -7006,6 +7011,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimatch/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -7668,6 +7688,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -7687,6 +7708,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -7717,6 +7739,7 @@ "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.5.tgz", "integrity": "sha512-1w+/oSjEXZjMqsIvmkCRsOc8UBYv163bTWKTI8+1mxztvQPhCRYGTvZ/PL1w16xXHneIj/SLGfxWg2GWN2uexw==", "license": "MIT", + "peer": true, "dependencies": { "@jest/create-cache-key-function": "^29.7.0", "@react-native/assets-registry": "0.81.5", @@ -7774,6 +7797,7 @@ "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.28.0.tgz", "integrity": "sha512-0msfJ1vRxXKVgTgvL+1ZOoYw3/0z1R+Ked0+udoJhyplC2jbVKIJ8Z1bzWdpQRCV3QcQ87Op0zJVE5DhKK2A0A==", "license": "MIT", + "peer": true, "dependencies": { "@egjs/hammerjs": "^2.0.17", "hoist-non-react-statics": "^3.3.0", @@ -7827,6 +7851,7 @@ "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz", "integrity": "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==", "license": "MIT", + "peer": true, "peerDependencies": { "react": "*", "react-native": "*" @@ -7837,6 +7862,7 @@ "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.16.0.tgz", "integrity": "sha512-yIAyh7F/9uWkOzCi1/2FqvNvK6Wb9Y1+Kzn16SuGfN9YFJDTbwlzGRvePCNTOX0recpLQF3kc2FmvMUhyTCH1Q==", "license": "MIT", + "peer": true, "dependencies": { "react-freeze": "^1.0.0", "react-native-is-edge-to-edge": "^1.2.1", @@ -7939,7 +7965,6 @@ "resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.7.4.tgz", "integrity": "sha512-NYOdM1MwBb3n+AtMqy1tFy3Mn8DliQtd8sbzAVRf9Gc+uvQ0zRfxN7dS8ZzoyX7t6cyQL5THuGhlnX+iFlQTag==", "license": "MIT", - "peer": true, "dependencies": { "@babel/plugin-transform-arrow-functions": "7.27.1", "@babel/plugin-transform-class-properties": "7.27.1", @@ -7964,7 +7989,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" @@ -7981,7 +8005,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz", "integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-compilation-targets": "^7.27.2", @@ -8002,7 +8025,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -8018,7 +8040,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" @@ -8035,7 +8056,6 @@ "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", @@ -8055,7 +8075,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -8133,9 +8152,9 @@ } }, "node_modules/react-native/node_modules/minimatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", - "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -8158,6 +8177,7 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -8384,9 +8404,9 @@ } }, "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", - "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -9044,9 +9064,9 @@ } }, "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", - "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -9120,6 +9140,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, diff --git a/jukebox/src/components/MediaPlayer.js b/jukebox/src/components/MediaPlayer.js index 1aaaa07..58915d6 100644 --- a/jukebox/src/components/MediaPlayer.js +++ b/jukebox/src/components/MediaPlayer.js @@ -10,9 +10,10 @@ export default function MediaPlayer({ title, onPress }) { return ( + {cover ? : null} + + + + {title} + + + {artist} + + + + {duration ? {duration} : null} + + {showHeart ? ( + + {liked ? '♥' : '♡'} + + ) : null} + + ); +} + +const styles = StyleSheet.create({ + trackItem: { + width: '100%', + minHeight: 62, + flexDirection: 'row', + alignItems: 'center', + marginBottom: 10, + }, + trackCover: { + width: 46, + height: 46, + borderRadius: 6, + marginRight: 10, + }, + trackTextBlock: { + flex: 1, + }, + trackTitle: { + color: '#ffffff', + fontSize: 15, + fontWeight: '700', + }, + trackArtist: { + color: '#ffffff', + fontSize: 15, + marginTop: 2, + }, + trackDuration: { + color: '#ffffff', + fontSize: 15, + paddingHorizontal: 18, + }, + heart: { + fontSize: 22, + color: '#fff', + paddingHorizontal: 10, + }, + heartLiked: { + fontSize: 22, + color: '#ff4d6d', + paddingHorizontal: 10, + }, +}); \ No newline at end of file diff --git a/jukebox/src/components/track.js b/jukebox/src/components/track.js deleted file mode 100644 index e69de29..0000000 diff --git a/jukebox/src/data/library.js b/jukebox/src/data/library.js new file mode 100644 index 0000000..c76e2dc --- /dev/null +++ b/jukebox/src/data/library.js @@ -0,0 +1,66 @@ +const library = [ + { + id: 'album1', + title: 'Soundtracks For The Blind', + artist: 'Swans', + date: '1996-11-29', + label: 'Young God Records', + duration: '2:21:25', + cover: require('../../assets/covers/soundtracksfortheblind.jpg'), + tracks: [ + { id: 'a1t1', title: 'Red Velvet Corridor', duration: '3:03', liked: true }, + { id: 'a1t2', title: 'I Was a Prisoner in Your Skull', duration: '6:39', liked: false }, + { id: 'a1t3', title: 'Helpless Child', duration: '15:47', liked: true }, + { id: 'a1t4', title: 'Live Through Me', duration: '2:19', liked: false }, + { id: 'a1t5', title: 'Yum-Yab Killers', duration: '5:07', liked: false }, + { id: 'a1t6', title: 'The Beautiful Days', duration: '1:50', liked: false }, + { id: 'a1t7', title: 'Volcano', duration: '5:18', liked: false }, + { id: 'a1t8', title: 'Mellothumb', duration: '2:45', liked: false }, + { id: 'a1t9', title: 'All Lined Up', duration: '4:48', liked: false }, + { id: 'a1t10', title: 'Surrogate Drone', duration: '2:03', liked: false }, + { id: 'a1t11', title: 'How They Suffer', duration: '5:52', liked: false }, + { id: 'a1t12', title: 'Animus', duration: '10:43', liked: false }, + { id: 'a1t13', title: 'Red Velvet Wound', duration: '1:01', liked: false }, + { id: 'a1t14', title: 'The Sound', duration: '13:11', liked: true }, + { id: 'a1t15', title: 'Her Mouth Is Filled with Honey', duration: '3:18', liked: false }, + { id: 'a1t16', title: 'Blood Section', duration: '2:45', liked: false }, + { id: 'a1t17', title: 'Hypogirl', duration: '3:42', liked: false }, + { id: 'a1t18', title: 'Minus Something', duration: '4:14', liked: false }, + { id: 'a1t19', title: 'Empathy', duration: '6:45', liked: false }, + { id: 'a1t20', title: 'I Love You This Much', duration: '7:23', liked: false }, + { id: 'a1t21', title: "YRP", duration: '7:58', liked: false }, + { id: 'a1t22', title: "Fan's Lament", duration: '1:47', liked: false }, + { id: 'a1t23', title: 'Secret Friends', duration: '3:08', liked: false }, + { id: 'a1t24', title: 'The Final Sacrifice', duration: '9:51', liked: false }, + { id: 'a1t25', title: 'YRP 2', duration: '2:09', liked: false }, + { id: 'a1t26', title: 'Surrogate 2', duration: '1:55', liked: false }, + ], + }, + { + id: 'album2', + title: 'Discovery', + artist: 'Daft Punk', + date: '2001-03-12', + label: 'Virgin Records', + duration: '1:00:50', + cover: require('../../assets/covers/discovery.jpg'), + tracks: [ + { id: 'a2t1', title: 'One More Time', duration: '5:20', liked: true }, + { id: 'a2t2', title: 'Aerodynamic', duration: '3:27', liked: false }, + { id: 'a2t3', title: 'Digital Love', duration: '4:58', liked: false }, + { id: 'a2t4', title: 'Harder, Better, Faster, Stronger', duration: '3:45', liked: false }, + { id: 'a2t5', title: 'Crescendolls', duration: '3:31', liked: false }, + { id: 'a2t6', title: 'Nightvision', duration: '1:44', liked: false }, + { id: 'a2t7', title: 'Superheroes', duration: '3:57', liked: false }, + { id: 'a2t8', title: 'High Life', duration: '3:21', liked: false }, + { id: 'a2t9', title: 'Something About Us', duration: '3:51', liked: false }, + { id: 'a2t10', title: 'Voyager', duration: '3:47', liked: false }, + { id: 'a2t11', title: 'Veridis Quo', duration: '5:44', liked: false }, + { id: 'a2t12', title: 'Short Circuit', duration: '3:26', liked: false }, + { id: 'a2t13', title: 'Face to Face', duration: '4:00', liked: false }, + { id: 'a2t14', title: 'Too Long', duration: '10:00', liked: false }, + ], + }, +]; + +export default library; \ No newline at end of file diff --git a/jukebox/src/screens/AlbumScreen.js b/jukebox/src/screens/AlbumScreen.js new file mode 100644 index 0000000..5a23488 --- /dev/null +++ b/jukebox/src/screens/AlbumScreen.js @@ -0,0 +1,122 @@ +import React from 'react'; +import { View, Text, StyleSheet, FlatList, Pressable, Image } from 'react-native'; +import TrackRow from '../components/TrackRow'; + +export default function AlbumScreen({ route, navigation }) { + const { album } = route.params; + return ( + + navigation.goBack()} style={styles.backBtn}> + ← Back + + + + {album.title} + {album.artist} + {album.date} · {album.label} · {album.duration} + + + item.id ?? index.toString()} + renderItem={({ item }) => ( + { }} + showHeart={true} + liked={item.liked} + /> + )} + contentContainerStyle={styles.trackList} + /> + + ); +} + +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, + }, + trackItem: { + width: '100%', + minHeight: 62, + padding: 0, + flexDirection: 'row', + }, + trackTextBlock: { + flex: 1, + }, + trackTitle: { + color: '#ffffff', + fontSize: 15, + fontWeight: '700', + }, + trackArtist: { + color: '#ffffff', + fontSize: 15, + marginTop: 2, + }, + trackDuration: { + color: '#ffffff', + fontSize: 15, + paddingHorizontal: 18, + marginTop: 10, + }, + heart: { + fontSize: 22, + color: '#fff', + paddingHorizontal: 10, + marginTop: 5.5, + }, + backBtn: { + marginBottom: 10, + alignSelf: 'flex-start', + }, + backText: { + color: '#fff', + fontSize: 16, + fontWeight: '600', + }, +}); \ No newline at end of file diff --git a/jukebox/src/screens/HomeScreen.js b/jukebox/src/screens/HomeScreen.js index 28df417..3be6647 100644 --- a/jukebox/src/screens/HomeScreen.js +++ b/jukebox/src/screens/HomeScreen.js @@ -6,56 +6,94 @@ import { FlatList, ScrollView, Pressable, + Image, } from "react-native"; +import { Ionicons } from "@expo/vector-icons"; import { useNavigation } from "@react-navigation/native"; +import library from '../data/library'; +import TrackRow from '../components/TrackRow'; -const likedTracks = [ - "Track 1", - "Track 2", - "Track 3", - "Track 4", - "Track 5", - "Track 6", -]; +const likedAlbums = library; -const likedAlbums = [ - "Album 1", - "Album 2", - "Album 3", - "Album 4", - "Album 5", - "Album 6", - "Album 7", - "Album 8", -]; +const likedTracks = library.flatMap((album) => + album.tracks + .filter((t) => t.liked) + .map((t) => ({ + ...t, + albumId: album.id, + albumTitle: album.title, + artist: album.artist, + cover: album.cover, + })) +); export default function HomeScreen() { const navigation = useNavigation(); return ( + + Home + + { + // navigation.navigate("Login") + console.log("Login pressed"); + }} + > + + + + { + // navigation.navigate("Settings") + console.log("Settings pressed"); + }} + > + + + + - navigation.navigate("LikedTracks", { tracks: likedTracks }) - } + onPress={() => navigation.navigate("LikedTracks", { tracks: likedTracks })} > Liked tracks ➚ - {likedTracks.map((item, index) => ( - - ))} + {likedTracks.map((track) => { + const album = likedAlbums.find((a) => a.id === track.albumId); - - Liked albums ➚ - + return ( + album && navigation.navigate("Album", { album })} + /> + ); + })} + + Liked albums index.toString()} + keyExtractor={(item) => item.id} numColumns={2} columnWrapperStyle={styles.albumRow} contentContainerStyle={styles.albumList} - renderItem={() => } + renderItem={({ item }) => ( + navigation.navigate("Album", { album: item })} + > + + + )} showsVerticalScrollIndicator={false} /> @@ -69,34 +107,83 @@ const styles = StyleSheet.create({ paddingTop: 24, paddingHorizontal: 16, }, + headerRow: { + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + marginBottom: 12, + }, + headerActions: { + flexDirection: "row", + alignItems: "center", + gap: 8, // if gap not supported on your RN version, use marginLeft in iconBtn + }, + settingsBtn: { + padding: 2, + }, + loginBtn: { + padding: 2, + }, + homeTitle: { + color: "#ffffff", + fontSize: 32, + fontWeight: "700", + marginBottom: 12, + }, sectionTitle: { - color: "#fff", - fontSize: 34, + color: "#ffffff", + fontSize: 24, fontWeight: "700", marginBottom: 12, }, trackItem: { - height: 42, - backgroundColor: "#ffd9d9", - borderWidth: 2, - borderColor: "#ff4d4d", - borderRadius: 8, - marginBottom: 10, + minHeight: 62, + flexDirection: 'row', + alignItems: 'center', + }, + + trackCover: { + width: 46, + height: 46, + borderRadius: 6, + marginRight: 10, + }, + + trackTextBlock: { + flex: 1, + justifyContent: 'center', + }, + + trackTitle: { + color: '#ffffff', + fontSize: 15, + fontWeight: '700', + }, + + trackArtist: { + color: '#ffffff', + fontSize: 13, + marginTop: 2, }, tracksBox: { - maxHeight: 260, // about 5 track rows + maxHeight: 260, }, albumList: { - paddingBottom: 120, // keep room for your media player at bottom + paddingBottom: 120, }, albumRow: { justifyContent: "space-between", marginBottom: 12, }, albumItem: { - width: "48%", - aspectRatio: 1, // square - backgroundColor: "#f7d7a6", - borderRadius: 18, + width: '48%', + aspectRatio: 1, + borderRadius: 8, + overflow: 'hidden', + backgroundColor: '#222', + }, + cover: { + width: '100%', + height: '100%', }, }); diff --git a/jukebox/src/screens/LikedTracksScreen.js b/jukebox/src/screens/LikedTracksScreen.js index 9c0f166..0e83da1 100644 --- a/jukebox/src/screens/LikedTracksScreen.js +++ b/jukebox/src/screens/LikedTracksScreen.js @@ -1,8 +1,23 @@ import React from "react"; import { View, Text, StyleSheet, FlatList, Pressable } from "react-native"; +import library from "../data/library"; +import TrackRow from "../components/TrackRow"; -export default function LikedTracksScreen({ route, navigation }) { - const tracks = route.params?.tracks ?? []; +export default function LikedTracksScreen({ navigation }) { + const likedTracks = library.flatMap((album) => + album.tracks + .filter((track) => track.liked) + .map((track) => ({ + ...track, + albumId: album.id, + albumTitle: album.title, + artist: album.artist, + cover: album.cover, + })) + ); + + // testing only + const tracks = likedTracks.slice(0, 2); return ( @@ -14,13 +29,22 @@ export default function LikedTracksScreen({ route, navigation }) { index.toString()} + keyExtractor={(item) => item.id} renderItem={({ item }) => ( - - {item} - + { + const album = library.find((a) => a.id === item.albumId); + if (album) navigation.navigate("Album", { album }); + }} + /> )} - contentContainerStyle={{ paddingBottom: 24 }} + contentContainerStyle={{ paddingBottom: 180 }} /> ); @@ -33,18 +57,19 @@ const styles = StyleSheet.create({ paddingTop: 24, paddingHorizontal: 16, }, - backBtn: { marginBottom: 10 }, - backText: { color: "#fff", fontSize: 16 }, - title: { color: "#fff", fontSize: 30, fontWeight: "700", marginBottom: 12 }, - trackItem: { - height: 50, - justifyContent: "center", - paddingHorizontal: 12, - backgroundColor: "#ffd9d9", - borderWidth: 2, - borderColor: "#ff4d4d", - borderRadius: 8, + backBtn: { marginBottom: 10, + alignSelf: "flex-start", }, - trackText: { color: "#111", fontWeight: "600" }, -}); + backText: { + color: "#fff", + fontSize: 16, + fontWeight: "600", + }, + title: { + color: "#fff", + fontSize: 30, + fontWeight: "700", + marginBottom: 12, + }, +}); \ No newline at end of file diff --git a/jukebox/src/screens/album.js b/jukebox/src/screens/album.js deleted file mode 100644 index e69de29..0000000