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