Skip to content

Commit

Permalink
Merge pull request #16 from arkajyotiadhikary/design-1
Browse files Browse the repository at this point in the history
[LOF-21] Open up songs from search results into audio player.
  • Loading branch information
arkajyotiadhikary authored Apr 15, 2024
2 parents a63a2b8 + d654bec commit 0566031
Show file tree
Hide file tree
Showing 8 changed files with 249 additions and 35 deletions.
38 changes: 36 additions & 2 deletions client/src/components/SearchScreen/SearchResult.component.tsx
Original file line number Diff line number Diff line change
@@ -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<SearchResultParams> = ({ data }) => {
const navigation = useNavigation<RootStackNavigationProp>();
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 (
<View style={styles.container}>
{data.length > 0 ? (
<FlatList
data={data}
renderItem={({ item }) => (
<TouchableOpacity style={styles.listItem}>
<TouchableOpacity
style={styles.listItem}
onPress={() => handleClick(item)}
>
<View style={styles.listItemContent}>
<AntDesign
name="search1"
Expand Down
67 changes: 67 additions & 0 deletions client/src/components/UserSettingsScreen/ChangePassword.modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from "react";
import {
Modal,
Text,
TextInput,
View,
TouchableOpacity,
TouchableWithoutFeedback,
} from "react-native";
import { useNavigation } from "@react-navigation/native";
import styles from "../../styles/UserSettings.style";
import { RootStackNavigationProp } from "../../../types";
import { FC } from "react";

interface ChangePasswordProps {
modalVisible: boolean;
setModalVisible: React.Dispatch<React.SetStateAction<boolean>>;
}

const ChangePassword: FC<ChangePasswordProps> = ({
modalVisible,
setModalVisible,
}) => {
const closeModal = () => {
setModalVisible(false);
};

return (
<Modal
visible={modalVisible}
transparent
animationType="slide"
onRequestClose={closeModal}
>
<TouchableWithoutFeedback onPress={closeModal}>
<View style={styles.modalContainer}>
<View style={styles.modal}>
<Text style={styles.modalTitle}>Change Password</Text>
<Text style={styles.modalInputTitle}>
Current Password
</Text>
<View style={styles.modalInputContainer}>
<TextInput
secureTextEntry
style={styles.modalInput}
/>
</View>
<Text style={styles.modalInputTitle}>New Password</Text>
<View style={styles.modalInputContainer}>
<TextInput
secureTextEntry
style={styles.modalInput}
/>
</View>
<TouchableOpacity style={styles.modalBtn}>
<Text style={styles.modalBtnText}>
Change Password
</Text>
</TouchableOpacity>
</View>
</View>
</TouchableWithoutFeedback>
</Modal>
);
};

export default ChangePassword;
60 changes: 48 additions & 12 deletions client/src/routes/Router.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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 () => {
Expand All @@ -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
}
}
}
})();
Expand Down
39 changes: 23 additions & 16 deletions client/src/screens/Auth.screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { getLikedSongs } from "../services/userService";

const Auth: FC = () => {
const dispatch = useDispatch();

const navigation = useNavigation<RootStackNavigationProp>();
// 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
Expand All @@ -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: "",
Expand All @@ -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,
Expand All @@ -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<void> => {
console.log("form data:", formData);
const validation = validateUserInput(formData, isSignIn);
if (!validation.isValid) {
setErrorState(validation.message);
Expand All @@ -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
Expand Down
17 changes: 16 additions & 1 deletion client/src/screens/UserSettings.screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<RootStackNavigationProp>();

// state to open and close modals
const [modalVisible, setModalVisible] = useState(false);

const handleLogOut = () => {
(async () => {
// delete access token from cache
Expand Down Expand Up @@ -60,8 +68,15 @@ const UserSettings = () => {
<TouchableOpacity style={styles.listBtn}>
<Text>Change Username</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.listBtn}>
<TouchableOpacity
style={styles.listBtn}
onPress={() => setModalVisible(true)}
>
<Text>Change Password</Text>
<ChangePassword
modalVisible={modalVisible}
setModalVisible={setModalVisible}
/>
</TouchableOpacity>
<TouchableOpacity style={styles.listBtn}>
<Text>Delete Account</Text>
Expand Down
49 changes: 49 additions & 0 deletions client/src/styles/UserSettings.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
});
Loading

0 comments on commit 0566031

Please sign in to comment.