diff --git a/client/src/components/SearchScreen/SearchResult.component.tsx b/client/src/components/SearchScreen/SearchResult.component.tsx index be6a5c0..71e5493 100644 --- a/client/src/components/SearchScreen/SearchResult.component.tsx +++ b/client/src/components/SearchScreen/SearchResult.component.tsx @@ -1,22 +1,56 @@ import React, { FC, useEffect, useState } from "react"; -import { View, Text, FlatList, TouchableOpacity } from "react-native"; +import { + View, + Text, + FlatList, + TouchableOpacity, + ImageSourcePropType, +} from "react-native"; import { type AddTrack } from "react-native-track-player"; +import { useNavigation } from "@react-navigation/native"; import styles from "../../styles/SearchScreen/SearchResult.style"; import { Feather } from "@expo/vector-icons"; import { AntDesign } from "@expo/vector-icons"; +import { RootStackNavigationProp } from "../../../types"; + +import { setCurrentPlayingSong } from "../../features/song/songSlice"; +import { useDispatch } from "react-redux"; + interface SearchResultParams { data: AddTrack[]; } const SearchResult: FC = ({ data }) => { + const navigation = useNavigation(); + const dispatch = useDispatch(); + + // Open up the song in the audio player + const handleClick = (item: AddTrack) => { + console.log("Open up song in the audio player"); + // set current playing songs + dispatch( + setCurrentPlayingSong({ + songID: item.id, + title: item.title!, + artist: item.artist!, + artwork: { uri: item.artwork! }, + audioIndex: 0, + }) + ); + navigation.navigate("Player"); + }; + return ( {data.length > 0 ? ( ( - + handleClick(item)} + > >; +} + +const ChangePassword: FC = ({ + modalVisible, + setModalVisible, +}) => { + const closeModal = () => { + setModalVisible(false); + }; + + return ( + + + + + Change Password + + Current Password + + + + + New Password + + + + + + Change Password + + + + + + + ); +}; + +export default ChangePassword; diff --git a/client/src/routes/Router.tsx b/client/src/routes/Router.tsx index fde12f6..9c0aea2 100644 --- a/client/src/routes/Router.tsx +++ b/client/src/routes/Router.tsx @@ -1,3 +1,14 @@ +/** + * The root component of the app, responsible for setting up the user's + * authentication state in Redux store and rendering the appropriate stack + * based on that state. The app will start by validating the token stored in + * AsyncStorage and setting up the Redux store accordingly. If the token is + * valid, the user will be redirected to the `AppStack`, which is the main + * stack of the app. If the token is invalid or unavailable, the user will + * be redirected to the `AuthStack`, which is the stack for logging in and + * signing up. + */ + import { useEffect } from "react"; import { NavigationContainer } from "@react-navigation/native"; import { AppStack, AuthStack } from "../stacks/mAuth.stack"; @@ -16,6 +27,11 @@ import { getLikedSongs } from "../services/userService"; const Router = () => { const userState = useSelector((state: RootState) => state.userAuthReducer); + const userData = useSelector((state: RootState) => state.userDataReducer); + const userLikedSongs = useSelector( + (state: RootState) => state.likedSongsReducer + ); + const dispatch = useDispatch(); useEffect(() => { (async () => { @@ -32,18 +48,38 @@ const Router = () => { if (isAuthenticated) { dispatch(setCurrentUserAuth({ isAuthenticated })); - // Check if the user data is available in cache; if not, set auth to false - const userData = await loadCachedResult("userData"); - if (!userData) { - dispatch(setCurrentUserAuth({ isAuthenticated: false })); - } else { - dispatch(setCurrentUserData(userData)); - - // If authenticated, get user's liked songs and set them up in the Redux store - const songs = await getLikedSongs(userData?.userID!); - const songIDArray = songs?.map((song) => song.SongID); - console.log("songs id array", songIDArray); - dispatch(setLikedSongs(songIDArray || [])); + // if user data in redux store is empty thn only look for it in cache + if (!userData.userID) { + console.log( + "No user data in redux store. Looking for it in cache" + ); + // look it in cache + const userData = await loadCachedResult("userData"); + // if user data is not there also set auth to false + if (!userData) { + console.log( + "No user data in cache setting auth to false" + ); + dispatch( + setCurrentUserAuth({ isAuthenticated: false }) + ); + } + // or else set the user the from the cache + else { + console.log( + "User data in cache, setting it in redux store" + ); + dispatch(setCurrentUserData(userData)); + + // TODO find out a better way to set up this + // With user data we are setting up liked song in the redux store + const songs = await getLikedSongs(userData?.userID!); + const songIDArray = songs?.map((song) => song.SongID); + console.log("songs id array", songIDArray); + dispatch(setLikedSongs(songIDArray || [])); + + // If authenticated, get user's liked songs and set them up in the Redux store + } } } })(); diff --git a/client/src/screens/Auth.screen.tsx b/client/src/screens/Auth.screen.tsx index 2d7a839..156db6c 100644 --- a/client/src/screens/Auth.screen.tsx +++ b/client/src/screens/Auth.screen.tsx @@ -21,7 +21,7 @@ import { getLikedSongs } from "../services/userService"; const Auth: FC = () => { const dispatch = useDispatch(); - + const navigation = useNavigation(); // a variable to check is it sign in or sign up form const [isSignIn, setIsSignIn] = useState(false); // a object to store user data from the input field @@ -34,7 +34,7 @@ const Auth: FC = () => { // error message const [errorState, setErrorState] = useState(""); - // empty the input when isSignin changes + // * empty the input when isSignin changes useEffect(() => { setFormData({ username: "", @@ -44,7 +44,7 @@ const Auth: FC = () => { setErrorState(""); }, [isSignIn]); - // method to handle change in the input field . Assing updated values in the variable + // * Method to handle change in the input field . Assing updated values in the variable const handleChange = (name: keyof User, value: string): void => { setFormData((prevState) => ({ ...prevState, @@ -54,7 +54,6 @@ const Auth: FC = () => { // method to handle submit . Check wheather you are submittin sign in form of sign up form const handleSubmit = async (): Promise => { - console.log("form data:", formData); const validation = validateUserInput(formData, isSignIn); if (!validation.isValid) { setErrorState(validation.message); @@ -63,37 +62,45 @@ const Auth: FC = () => { try { let response; - // check if sign in. if sign in then request sign in + // Check if sign in. if sign in then request sign in if (isSignIn) { + console.log("Sign in"); response = await signIn(formData.email, formData.password); - // check if you have response - // if you have thn store token in cache - // get liked songs and store in redux store + // Check if you have response // store user data in redux store if (!response?.hasError && "data" in response!) { - await AsyncStorage.setItem("token", response?.data?.token!); - - const songs = await getLikedSongs(response?.data?.userID!); - const songIDArray = songs?.map((song) => song.SongID); dispatch( setCurrentUserAuth({ isAuthenticated: true, - }), + }) + ); + console.log("Set up user auth in redux store"); + console.log("Sign in response:", response); + // if you have thn store token in cache + await AsyncStorage.setItem("token", response?.data?.token!); + console.log("Set token in cache"); + // Set up current user data in redux + dispatch( setCurrentUserData({ userID: response?.data?.userID!, username: response?.data?.username!, email: response?.data?.email!, role: response?.data?.role!, - }), - setLikedSongs(songIDArray || []) + profilePic: "", + }) ); - + console.log("Set user data in redux store"); + // Get liked songs and store in redux store + const songs = await getLikedSongs(response?.data?.userID!); + const songIDArray = songs?.map((song) => song.SongID); + dispatch(setLikedSongs(songIDArray || [])); await saveCachedResult("userData", { userID: response?.data?.userID!, username: response?.data?.username!, email: response?.data?.email!, role: response?.data?.role!, }); + console.log("Set liked songs in redux store"); } } // else request sign up diff --git a/client/src/screens/UserSettings.screen.tsx b/client/src/screens/UserSettings.screen.tsx index 0350974..5d2e7c6 100644 --- a/client/src/screens/UserSettings.screen.tsx +++ b/client/src/screens/UserSettings.screen.tsx @@ -12,12 +12,20 @@ import { RootState } from "../store"; import styles from "../styles/UserSettings.style"; import { Ionicons } from "@expo/vector-icons"; +// modals +import ChangePassword from "../components/UserSettingsScreen/ChangePassword.modal"; +import { useState } from "react"; + const UserSettings = () => { const dispatch = useDispatch(); const { username, profilePic } = useSelector( (state: RootState) => state.userDataReducer ); const navigation = useNavigation(); + + // state to open and close modals + const [modalVisible, setModalVisible] = useState(false); + const handleLogOut = () => { (async () => { // delete access token from cache @@ -60,8 +68,15 @@ const UserSettings = () => { Change Username - + setModalVisible(true)} + > Change Password + Delete Account diff --git a/client/src/styles/UserSettings.style.ts b/client/src/styles/UserSettings.style.ts index 94660ef..74d5f94 100644 --- a/client/src/styles/UserSettings.style.ts +++ b/client/src/styles/UserSettings.style.ts @@ -55,4 +55,53 @@ export default StyleSheet.create({ logoutBtnText: { color: "white", }, + // change password + modalContainer: { + flex: 1, + justifyContent: "center", + alignItems: "center", + }, + modal: { + borderRadius: 5, + backgroundColor: "white", + alignSelf: "center", + padding: 20, + height: 320, + width: 350, + elevation: 5, + }, + modalTitle: { + fontSize: 18, + fontWeight: "bold", + alignSelf: "center", + marginBottom: 10, + }, + + modalInputTitle: { + fontSize: 14, + marginBottom: 5, + marginTop: 10, + }, + modalInputContainer: { + flexDirection: "row", + alignItems: "center", + borderRadius: 5, + borderWidth: 1, + borderColor: "#ccc", + padding: 5, + marginBottom: 10, + }, + modalInput: {}, + + modalBtn: { + alignSelf: "center", + marginTop: 10, + marginBottom: 10, + padding: 10, + borderRadius: 5, + backgroundColor: "red", + }, + modalBtnText: { + color: "white", + }, }); diff --git a/server/dist/controllers/songs.controller.js b/server/dist/controllers/songs.controller.js index 71ee390..c685bdc 100644 --- a/server/dist/controllers/songs.controller.js +++ b/server/dist/controllers/songs.controller.js @@ -20,9 +20,12 @@ const UserSongPlays_Model_1 = require("../models/UserSongPlays.Model"); // get all songs const getAllSongs = (req, res) => __awaiter(void 0, void 0, void 0, function* () { const { limit } = req.query; - console.log("Limit: ", limit); try { - const songs = yield Songs_Model_1.Song.findAll({ limit: Number(limit) }); + let songs; + if (limit) { + songs = yield Songs_Model_1.Song.findAll({ limit: Number(limit) }); + } + songs = yield Songs_Model_1.Song.findAll(); if (!songs) { console.error(chalk_1.default.red("Error fetching songs from database 😓")); res.status(404).json({ message: "Songs not found!" }); diff --git a/server/src/controllers/songs.controller.ts b/server/src/controllers/songs.controller.ts index 926776a..ac6b6db 100644 --- a/server/src/controllers/songs.controller.ts +++ b/server/src/controllers/songs.controller.ts @@ -7,9 +7,12 @@ import { UserSongPlays } from "../models/UserSongPlays.Model"; // get all songs export const getAllSongs = async (req: Request, res: Response): Promise => { const { limit } = req.query; - console.log("Limit: ", limit); try { - const songs = await Song.findAll({ limit: Number(limit) }); + let songs; + if (limit) { + songs = await Song.findAll({ limit: Number(limit) }); + } + songs = await Song.findAll(); if (!songs) { console.error(chalk.red("Error fetching songs from database 😓")); res.status(404).json({ message: "Songs not found!" });