- {playListData.link.length !== 0 && playListData.name.length !== 0 ? (
- <>
-
- >
+ {playListData.link.length !== 0 ? (
+
) : (
{errorMessages.error !== null && errorMessages.error}
diff --git a/app/components/TopTracks/ArtistInput.tsx b/app/components/TopTracks/ArtistInput.tsx
index 427fe3f..43e738e 100644
--- a/app/components/TopTracks/ArtistInput.tsx
+++ b/app/components/TopTracks/ArtistInput.tsx
@@ -52,15 +52,15 @@ const ArtistInput = () => {
{artistArray.map((value, index) => (
- handleArtistDelete(e, index)}>
{value}
-
+
))}
)}
diff --git a/app/context/DiscoverTracks/typeContext.tsx b/app/context/DiscoverTracks/typeContext.tsx
index c22f371..1b62aad 100644
--- a/app/context/DiscoverTracks/typeContext.tsx
+++ b/app/context/DiscoverTracks/typeContext.tsx
@@ -14,11 +14,16 @@ const TypeContext = createContext(undefined);
const TypeProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [type, setType] = useState('artist');
- return (
-
- {children}
-
- );
+ const value = React.useMemo(
+ () => ({
+ type,
+ setType,
+ }),
+ [type],
+ );
+ return (
+ {children}
+ );
};
const useType = (): TypeContextProps => {
diff --git a/app/context/PlaylistViewContext.tsx b/app/context/PlaylistViewContext.tsx
new file mode 100644
index 0000000..6498881
--- /dev/null
+++ b/app/context/PlaylistViewContext.tsx
@@ -0,0 +1,152 @@
+import React, {
+ createContext,
+ useCallback,
+ useContext,
+ useEffect,
+ useMemo,
+ useState,
+} from 'react';
+import {
+ getAllTracksInAPlaylist,
+ removeTracksFromPlaylists,
+} from '../lib/spotify';
+import { loadingType, playlistSongDetails } from '../types';
+
+import { useGeneralState } from '../context/generalStateContext';
+
+type PlaylistViewContextType = {
+ showingTracks: playlistSongDetails[];
+ loading: loadingType;
+ startedEditing: boolean;
+ tracksDeleted: playlistSongDetails[];
+ deleteTrack: (id: string) => void;
+ restoreAllTracks: () => void;
+ restoreSelectedTracks: (ids: string[]) => void;
+ saveTracks: () => void;
+};
+
+const PlaylistViewContext = createContext(
+ undefined,
+);
+
+export const usePlaylistView = () => {
+ const context = useContext(PlaylistViewContext);
+ if (!context) {
+ throw new Error(
+ 'usePlaylistView must be used within a PlaylistViewProvider',
+ );
+ }
+ return context;
+};
+
+export const PlaylistViewProvider: React.FC<{ children: React.ReactNode }> = ({
+ children,
+}) => {
+ const { playListData } = useGeneralState();
+ const link = playListData.link.split('/').at(-1) as string;
+
+ const [loading, setLoading] = useState({
+ isLoading: false,
+ message: null,
+ });
+ const [startedEditing, setStartedEditing] = useState(false);
+ const [tracks, setTracks] = useState([]);
+ const [showingTracks, setShowingTracks] = useState([]);
+ const [tracksToRemove, setTracksToRemove] = useState<{ uri: string }[]>([]);
+ const [tracksDeleted, setTracksDeleted] = useState([]);
+
+ useEffect(() => {
+ (async () => {
+ await getTracks();
+ })();
+ }, []);
+
+ useEffect(() => {
+ if (showingTracks.length === 0) {
+ setShowingTracks(tracks);
+ }
+ }, [showingTracks, tracks]);
+
+ const getTracks = useCallback(async () => {
+ const data = await getAllTracksInAPlaylist(link);
+
+ console.log(data);
+ const tracks = data.map((item: any) => {
+ const track = item.track;
+ const image = track.album.images[1].url;
+ const artist = track.artists.map((subitem: any) => subitem.name);
+ return { id: track.id, name: track.name, artist, image };
+ });
+
+ setTracks(tracks);
+ }, [link]);
+
+ const deleteTrack = useCallback(
+ (id: string) => {
+ setStartedEditing(true);
+ setShowingTracks((prevTracks) =>
+ prevTracks.filter((track) => track.id !== id),
+ );
+ const deletingTrack = tracks.filter((track) => track.id === id)[0];
+
+ setTracksDeleted((prev) => [...prev, deletingTrack]);
+ setTracksToRemove((prev) => [...prev, { uri: 'spotify:track:' + id }]);
+ },
+ [tracks],
+ );
+
+ const restoreAllTracks = useCallback(() => {
+ setShowingTracks(tracks);
+ setStartedEditing(false);
+ setTracksToRemove([]);
+ setTracksDeleted([]);
+ }, [tracks]);
+
+ const restoreSelectedTracks = useCallback(
+ (ids: string[]) => {
+ const restoringTracks = tracks.filter((track) => ids.includes(track.id));
+ const remainingDeletedTracks = tracksDeleted.filter(
+ (track) => !ids.includes(track.id),
+ );
+ const TracksToRemove = tracksToRemove.filter(
+ (track) => !ids.includes(track.uri.split(':').at(-1) as string),
+ );
+
+ setTracksToRemove(TracksToRemove);
+ setTracksDeleted(remainingDeletedTracks);
+ setShowingTracks((prev) => [...prev, ...restoringTracks]);
+ },
+ [tracks, tracksDeleted, tracksToRemove],
+ );
+
+ const saveTracks = useCallback(async () => {
+ setLoading({ isLoading: true, message: 'Deleting Tracks....' });
+ await removeTracksFromPlaylists(link, tracksToRemove);
+
+ await getTracks();
+ setLoading({ isLoading: false, message: null });
+ setTracksToRemove([]);
+ setTracksDeleted([]);
+ setStartedEditing(false);
+ }, [tracksToRemove]);
+
+ const value = useMemo(
+ () => ({
+ showingTracks,
+ loading,
+ startedEditing,
+ tracksDeleted,
+ deleteTrack,
+ restoreAllTracks,
+ saveTracks,
+ restoreSelectedTracks,
+ }),
+ [showingTracks, loading, startedEditing, tracksDeleted],
+ );
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/app/context/authContext.tsx b/app/context/authContext.tsx
index a92504c..dad04fb 100644
--- a/app/context/authContext.tsx
+++ b/app/context/authContext.tsx
@@ -1,6 +1,12 @@
'use client';
-import React, { ReactNode, createContext, useContext, useState } from 'react';
+import React, {
+ ReactNode,
+ createContext,
+ useContext,
+ useMemo,
+ useState,
+} from 'react';
interface AuthContextProps {
isLoggedIn: boolean;
@@ -34,12 +40,12 @@ export const AuthProvider: React.FC = ({ children }) => {
setAuthInProgress(state);
};
- return (
-
- {children}
-
+ const value = useMemo(
+ () => ({ isLoggedIn, logOut, logIn, isAuthInProgress, authInProgress }),
+ [isLoggedIn, isAuthInProgress],
);
+
+ return {children};
};
export const useAuth = (): AuthContextProps => {
diff --git a/app/context/generalStateContext.tsx b/app/context/generalStateContext.tsx
index 3faa5e6..f4399a3 100644
--- a/app/context/generalStateContext.tsx
+++ b/app/context/generalStateContext.tsx
@@ -1,6 +1,12 @@
'use client';
-import React, { ReactNode, createContext, useContext, useState } from 'react';
+import React, {
+ ReactNode,
+ createContext,
+ useContext,
+ useMemo,
+ useState,
+} from 'react';
interface PlayListData {
link: string;
@@ -10,7 +16,7 @@ interface PlayListData {
interface ErrorMessages {
notCorrectSpotifyLink: boolean;
notCorrectFormatForArtist: boolean;
- error: null | any;
+ error: any;
}
interface GeneralStateContextProps {
@@ -40,16 +46,20 @@ const GeneralStateProvider: React.FC<{ children: ReactNode }> = ({
error: null,
});
+ const value = useMemo(
+ () => ({
+ playListData,
+ setPlayListData,
+ buttonClick,
+ setButtonClicked,
+ errorMessages,
+ setErrorMessages,
+ }),
+ [playListData, buttonClick, errorMessages],
+ );
+
return (
-
+
{children}
);
diff --git a/app/context/inputContext.tsx b/app/context/inputContext.tsx
index 57a7fc4..997f762 100644
--- a/app/context/inputContext.tsx
+++ b/app/context/inputContext.tsx
@@ -5,6 +5,7 @@ import React, {
RefObject,
createContext,
useContext,
+ useMemo,
useRef,
useState,
} from 'react';
@@ -23,11 +24,13 @@ const InputProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [artistArray, setArtistArray] = useState([]);
const spotifyPlaylist = useRef(null);
+ const value = useMemo(
+ () => ({ artistName, artistArray, setArtistArray, spotifyPlaylist }),
+ [artistArray, artistName, spotifyPlaylist],
+ );
+
return (
-
- {children}
-
+ {children}
);
};
diff --git a/app/context/loadingContext.tsx b/app/context/loadingContext.tsx
index 5ae641f..d945cd4 100644
--- a/app/context/loadingContext.tsx
+++ b/app/context/loadingContext.tsx
@@ -1,6 +1,12 @@
'use client';
-import React, { ReactNode, createContext, useContext, useState } from 'react';
+import React, {
+ ReactNode,
+ createContext,
+ useContext,
+ useMemo,
+ useState,
+} from 'react';
interface LoadingContextProps {
loading: boolean;
@@ -17,11 +23,13 @@ const LoadingProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [loading, setLoading] = useState(false);
const [loadingMessage, setLoadingMessage] = useState(null);
+ const value = useMemo(
+ () => ({ loading, setLoading, loadingMessage, setLoadingMessage }),
+ [loading, loadingMessage],
+ );
+
return (
-
- {children}
-
+ {children}
);
};
diff --git a/app/hooks/useRestoreSongs.tsx b/app/hooks/useRestoreSongs.tsx
new file mode 100644
index 0000000..edbc455
--- /dev/null
+++ b/app/hooks/useRestoreSongs.tsx
@@ -0,0 +1,39 @@
+import { usePlaylistView } from '../context/PlaylistViewContext';
+import { useState } from 'react';
+
+const useRestoreSongs = () => {
+ const { restoreSelectedTracks } = usePlaylistView();
+ const [selectedTracksToRestore, setSelectedTracksToRestore] = useState<
+ string[]
+ >([]);
+ const [restoreOption, setRestoreOption] = useState(false);
+
+ const handleCheckboxChange = (id: string) => {
+ setSelectedTracksToRestore((prevSelectedTracks) => {
+ if (prevSelectedTracks.includes(id)) {
+ return prevSelectedTracks.filter((trackId) => trackId !== id);
+ } else {
+ return [...prevSelectedTracks, id];
+ }
+ });
+ };
+
+ const handleRestoreSelected = () => {
+ restoreSelectedTracks(selectedTracksToRestore);
+ setSelectedTracksToRestore([]);
+ };
+
+ const toggleRestoreOption = () => {
+ setRestoreOption(!restoreOption);
+ };
+
+ return {
+ restoreOption,
+ selectedTracksToRestore,
+ handleCheckboxChange,
+ handleRestoreSelected,
+ toggleRestoreOption,
+ };
+};
+
+export default useRestoreSongs;
diff --git a/app/hooks/useViewPlaylist.tsx b/app/hooks/useViewPlaylist.tsx
deleted file mode 100644
index b2dd342..0000000
--- a/app/hooks/useViewPlaylist.tsx
+++ /dev/null
@@ -1,111 +0,0 @@
-import {
- getAllTracksInAPlaylist,
- removeTracksFromPlaylists,
-} from '../lib/spotify';
-import { loadingType, playlistSongDetails } from '../types';
-import { useEffect, useState } from 'react';
-
-const useViewPlaylist = (
- link: string,
-): {
- showingTracks: playlistSongDetails[];
- loading: loadingType;
- startedEditing: boolean;
- tracksDeleted: playlistSongDetails[];
- deleteTrack: (id: string) => void;
- restoreAllTracks: () => void;
- restoreSelectedTracks: (ids: string[]) => void;
- saveTracks: () => void;
-} => {
- link = link.split('/').at(-1) as string;
-
- const [loading, setLoading] = useState({
- isLoading: false,
- message: null,
- });
- const [startedEditing, setStartedEditing] = useState(false);
- const [tracks, setTracks] = useState([]);
- const [showingTracks, setShowingTracks] = useState([]);
- const [tracksToRemove, setTracksToRemove] = useState<{ uri: string }[]>([]);
- const [tracksDeleted, setTracksDeleted] = useState([]);
-
- useEffect(() => {
- (async () => {
- await getTracks();
- })();
- }, []);
-
- useEffect(() => {
- if (showingTracks.length === 0) {
- setShowingTracks(tracks);
- }
- }, [tracks]);
-
- const getTracks = async () => {
- const data = await getAllTracksInAPlaylist(link);
-
- const tracks = data.map((item: any) => {
- const track = item.track;
- const image = track.album.images[1].url;
- const artist = track.artists.map((subitem: any) => subitem.name);
- return { id: track.id, name: track.name, artist, image };
- });
-
- setTracks(tracks);
- };
-
- const deleteTrack = (id: string) => {
- setStartedEditing(true);
- setShowingTracks((prevTracks) =>
- prevTracks.filter((track) => track.id !== id),
- );
- const deletingTrack = tracks.filter((track) => track.id === id)[0];
- setTracksDeleted([...tracksDeleted, deletingTrack]);
- setTracksToRemove([...tracksToRemove, { uri: 'spotify:track:' + id }]);
- };
-
- const restoreAllTracks = () => {
- setShowingTracks(tracks);
- setStartedEditing(false);
- setTracksToRemove([]);
- setTracksDeleted([]);
- };
-
- const restoreSelectedTracks = (ids: string[]) => {
- const restoringTracks = tracks.filter((track) => ids.includes(track.id));
- const remainingDeletedTracks = tracksDeleted.filter(
- (track) => !ids.includes(track.id),
- );
- const TracksToRemove = tracksToRemove.filter((track) =>
- ids.includes(track.uri.split(':').at(-1) as string),
- );
-
- setTracksToRemove(TracksToRemove);
- setTracksDeleted(remainingDeletedTracks);
- setShowingTracks([...showingTracks, ...restoringTracks]);
- };
-
- const saveTracks = async () => {
- setLoading({ isLoading: true, message: 'Deleting Tracks....' });
- removeTracksFromPlaylists(link, tracksToRemove);
-
- await getTracks();
- setLoading({ isLoading: false, message: null });
- setTracksToRemove([]);
- setTracksDeleted([]);
- setStartedEditing(false);
- };
-
- return {
- showingTracks,
- loading,
- startedEditing,
- deleteTrack,
- restoreAllTracks,
- saveTracks,
- restoreSelectedTracks,
- tracksDeleted,
- };
-};
-
-export default useViewPlaylist;
diff --git a/app/lib/utils.ts b/app/lib/utils.ts
index 912cd1b..0450e88 100644
--- a/app/lib/utils.ts
+++ b/app/lib/utils.ts
@@ -94,6 +94,17 @@ export const copyToClipboard = async (textToCopy: string) => {
}
};
+export const addToUrl = (key: string, value: string) => {
+ const searchParams = new URLSearchParams(window.location.search);
+ searchParams.set(key, value);
+ const newUrl = `${window.location.pathname}?${searchParams.toString()}`;
+ window.history.pushState({}, '', newUrl);
+};
+
+export const getFromUrl = (key: string) => {
+ const searchParams = new URLSearchParams(window.location.search);
+ return searchParams.get(key);
+};
// export const getAllTracks = async (albums) => {
// const getAlbumTracks = albums.map(getOneAlbumTrack)
// const tracks = await Promise.all(getAlbumTracks);