11'use client' ;
22
3- import {
4- createContext ,
5- useCallback ,
6- useContext ,
7- useEffect ,
8- useState ,
9- } from 'react' ;
3+ import { useEffect } from 'react' ;
4+ import { create } from 'zustand' ;
105import { toast } from 'react-hot-toast' ;
116
127import { MY_SONGS } from '@nbw/config' ;
@@ -18,69 +13,73 @@ import type {
1813import axiosInstance from '@web/lib/axios' ;
1914import { getTokenLocal } from '@web/lib/axios/token.utils' ;
2015
21- type MySongsContextType = {
16+ interface MySongsState {
17+ loadedSongs : SongsFolder ;
2218 page : SongPageDtoType | null ;
23- nextpage : ( ) => void ;
24- prevpage : ( ) => void ;
25- gotoPage : ( page : number ) => void ;
2619 totalSongs : number ;
2720 totalPages : number ;
2821 currentPage : number ;
2922 pageSize : number ;
3023 isLoading : boolean ;
3124 error : string | null ;
3225 isDeleteDialogOpen : boolean ;
33- setIsDeleteDialogOpen : ( isOpen : boolean ) => void ;
3426 songToDelete : SongPreviewDtoType | null ;
27+ }
28+
29+ interface MySongsActions {
30+ initialize : (
31+ initialSongsFolder : SongsFolder ,
32+ totalPagesInit : number ,
33+ currentPageInit : number ,
34+ pageSizeInit : number ,
35+ ) => void ;
36+ fetchSongsPage : ( ) => Promise < void > ;
37+ loadPage : ( ) => Promise < void > ;
38+ gotoPage : ( page : number ) => void ;
39+ nextpage : ( ) => void ;
40+ prevpage : ( ) => void ;
41+ setIsDeleteDialogOpen : ( isOpen : boolean ) => void ;
3542 setSongToDelete : ( song : SongPreviewDtoType ) => void ;
36- deleteSong : ( ) => void ;
37- } ;
38-
39- const MySongsContext = createContext < MySongsContextType > (
40- { } as MySongsContextType ,
41- ) ;
42-
43- type MySongProviderProps = {
44- InitialsongsFolder ?: SongsFolder ;
45- children ?: React . ReactNode ;
46- totalPagesInit ?: number ;
47- currentPageInit ?: number ;
48- pageSizeInit ?: number ;
49- } ;
50-
51- export const MySongProvider = ( {
52- InitialsongsFolder = { } ,
53- children,
54- totalPagesInit = 0 ,
55- currentPageInit = 0 ,
56- pageSizeInit = MY_SONGS . PAGE_SIZE ,
57- } : MySongProviderProps ) => {
58- const [ loadedSongs , setLoadedSongs ] =
59- useState < SongsFolder > ( InitialsongsFolder ) ;
60-
61- const [ totalSongs , setTotalSongs ] = useState < number > ( 0 ) ;
62- const [ totalPages , setTotalPages ] = useState < number > ( totalPagesInit ) ;
63- const [ currentPage , setCurrentPage ] = useState < number > ( currentPageInit ) ;
64-
65- const [ pageSize , _setPageSize ] = useState < number > ( pageSizeInit ) ;
66- const [ page , setPage ] = useState < SongPageDtoType | null > ( null ) ;
67- const [ isLoading , setIsLoading ] = useState < boolean > ( true ) ;
68- const [ error , setError ] = useState < string | null > ( null ) ;
69- const [ isDeleteDialogOpen , setIsDeleteDialogOpen ] = useState < boolean > ( false ) ;
70-
71- const [ songToDelete , setSongToDelete ] = useState < SongPreviewDtoType | null > (
72- null ,
73- ) ;
74-
75- const putPage = useCallback (
76- async ( { key, page } : { key : number ; page : SongPageDtoType } ) => {
77- setLoadedSongs ( { ...loadedSongs , [ key ] : page } ) ;
78- } ,
79- [ loadedSongs ] ,
80- ) ;
81-
82- const fetchSongsPage = useCallback ( async ( ) : Promise < void > => {
83- setIsLoading ( true ) ;
43+ deleteSong : ( ) => Promise < void > ;
44+ }
45+
46+ type MySongsStore = MySongsState & MySongsActions ;
47+
48+ export const useMySongsStore = create < MySongsStore > ( ( set , get ) => ( {
49+ // Initial state
50+ loadedSongs : { } ,
51+ page : null ,
52+ totalSongs : 0 ,
53+ totalPages : 0 ,
54+ currentPage : 0 ,
55+ pageSize : MY_SONGS . PAGE_SIZE ,
56+ isLoading : true ,
57+ error : null ,
58+ isDeleteDialogOpen : false ,
59+ songToDelete : null ,
60+
61+ // Actions
62+ initialize : (
63+ initialSongsFolder ,
64+ totalPagesInit ,
65+ currentPageInit ,
66+ pageSizeInit ,
67+ ) => {
68+ const initialPage = initialSongsFolder [ currentPageInit ] || null ;
69+ set ( {
70+ loadedSongs : initialSongsFolder ,
71+ totalPages : totalPagesInit ,
72+ currentPage : currentPageInit ,
73+ pageSize : pageSizeInit ,
74+ page : initialPage ,
75+ totalSongs : initialPage ?. total || 0 ,
76+ isLoading : false ,
77+ } ) ;
78+ } ,
79+
80+ fetchSongsPage : async ( ) => {
81+ const { currentPage, pageSize } = get ( ) ;
82+ set ( { isLoading : true } ) ;
8483 const token = getTokenLocal ( ) ;
8584
8685 try {
@@ -99,67 +98,73 @@ export const MySongProvider = ({
9998 const data = response . data as SongPageDtoType ;
10099
101100 // TODO: total, page and pageSize are stored in every page, when it should be stored in the folder (what matters is 'content')
102- putPage ( {
103- key : currentPage ,
101+ set ( ( state ) => ( {
102+ loadedSongs : { ...state . loadedSongs , [ currentPage ] : data } ,
103+ totalSongs : data . total ,
104+ totalPages : Math . ceil ( data . total / pageSize ) ,
104105 page : data ,
105- } ) ;
106-
107- setTotalSongs ( data . total ) ;
108- setTotalPages ( Math . ceil ( data . total / pageSize ) ) ;
109- setPage ( data ) ;
106+ error : null ,
107+ } ) ) ;
110108 } catch ( error : unknown ) {
109+ let errorMessage : string | null = null ;
111110 if ( error instanceof Error ) {
112- setError ( error . message ) ;
113- }
114-
115- if ( error instanceof Response ) {
116- setError ( error . statusText ) ;
111+ errorMessage = error . message ;
112+ } else if ( error instanceof Response ) {
113+ errorMessage = error . statusText ;
117114 }
115+ set ( { error : errorMessage } ) ;
118116 } finally {
119- setIsLoading ( false ) ;
117+ set ( { isLoading : false } ) ;
120118 }
121- } , [ currentPage , pageSize , putPage ] ) ;
119+ } ,
122120
123- const loadPage = useCallback ( async ( ) => {
124- if ( currentPage in loadedSongs ) {
125- setPage ( loadedSongs [ currentPage ] ) ;
126- setIsLoading ( false ) ;
121+ loadPage : async ( ) => {
122+ const { currentPage, loadedSongs, fetchSongsPage } = get ( ) ;
127123
124+ set ( { isLoading : true } ) ;
125+
126+ if ( currentPage in loadedSongs ) {
127+ set ( {
128+ page : loadedSongs [ currentPage ] ,
129+ isLoading : false ,
130+ } ) ;
128131 return ;
129132 }
130133
131134 await fetchSongsPage ( ) ;
132- } , [ currentPage , fetchSongsPage , loadedSongs ] ) ;
133-
134- useEffect ( ( ) => {
135- ( async ( ) => {
136- setIsLoading ( true ) ;
137- loadPage ( ) ;
138- } ) ( ) ;
139- } , [ currentPage , loadPage ] ) ;
135+ } ,
140136
141- const gotoPage = useCallback (
142- ( page : number ) => {
143- if ( page > 0 && page <= totalPages ) {
144- setCurrentPage ( page ) ;
145- }
146- } ,
147- [ totalPages ] ,
148- ) ;
137+ gotoPage : ( page ) => {
138+ const { totalPages } = get ( ) ;
139+ if ( page > 0 && page <= totalPages ) {
140+ set ( { currentPage : page } ) ;
141+ }
142+ } ,
149143
150- const nextpage = useCallback ( ( ) => {
144+ nextpage : ( ) => {
145+ const { currentPage, totalPages, gotoPage } = get ( ) ;
151146 if ( currentPage < totalPages ) {
152147 gotoPage ( currentPage + 1 ) ;
153148 }
154- } , [ currentPage , totalPages , gotoPage ] ) ;
149+ } ,
155150
156- const prevpage = useCallback ( ( ) => {
151+ prevpage : ( ) => {
152+ const { currentPage, gotoPage } = get ( ) ;
157153 if ( currentPage > 1 ) {
158154 gotoPage ( currentPage - 1 ) ;
159155 }
160- } , [ currentPage , gotoPage ] ) ;
156+ } ,
161157
162- const deleteSong = useCallback ( async ( ) => {
158+ setIsDeleteDialogOpen : ( isOpen ) => {
159+ set ( { isDeleteDialogOpen : isOpen } ) ;
160+ } ,
161+
162+ setSongToDelete : ( song ) => {
163+ set ( { songToDelete : song } ) ;
164+ } ,
165+
166+ deleteSong : async ( ) => {
167+ const { songToDelete, fetchSongsPage } = get ( ) ;
163168 if ( ! songToDelete ) {
164169 return ;
165170 }
@@ -173,8 +178,10 @@ export const MySongProvider = ({
173178 } ,
174179 } ) ;
175180
176- setIsDeleteDialogOpen ( false ) ;
177- setSongToDelete ( null ) ;
181+ set ( {
182+ isDeleteDialogOpen : false ,
183+ songToDelete : null ,
184+ } ) ;
178185 } catch ( error : unknown ) {
179186 toast . error ( 'An error occurred while deleting the song!' ) ;
180187
@@ -189,39 +196,56 @@ export const MySongProvider = ({
189196
190197 await fetchSongsPage ( ) ;
191198 toast . success ( 'Song deleted successfully!' ) ;
192- } , [ songToDelete , fetchSongsPage ] ) ;
193-
194- return (
195- < MySongsContext . Provider
196- value = { {
197- page,
198- nextpage,
199- prevpage,
200- gotoPage,
201- totalSongs,
202- totalPages,
203- currentPage,
204- pageSize,
205- isLoading,
206- error,
207- isDeleteDialogOpen,
208- setIsDeleteDialogOpen,
209- songToDelete,
210- setSongToDelete,
211- deleteSong,
212- } }
213- >
214- { children }
215- </ MySongsContext . Provider >
216- ) ;
199+ } ,
200+ } ) ) ;
201+
202+ // Hook to sync currentPage changes with loadPage
203+ export const useMySongsPageLoader = ( ) => {
204+ const currentPage = useMySongsStore ( ( state ) => state . currentPage ) ;
205+ const loadPage = useMySongsStore ( ( state ) => state . loadPage ) ;
206+
207+ useEffect ( ( ) => {
208+ loadPage ( ) ;
209+ } , [ currentPage , loadPage ] ) ;
217210} ;
218211
212+ // Legacy hook name for backward compatibility
219213export const useMySongsProvider = ( ) => {
220- const context = useContext ( MySongsContext ) ;
214+ return useMySongsStore ( ) ;
215+ } ;
221216
222- if ( context === undefined || context === null ) {
223- throw new Error ( 'useMySongsProvider must be used within a MySongsProvider' ) ;
224- }
217+ // Provider component for initialization (now just a wrapper)
218+ type MySongProviderProps = {
219+ InitialsongsFolder ?: SongsFolder ;
220+ children ?: React . ReactNode ;
221+ totalPagesInit ?: number ;
222+ currentPageInit ?: number ;
223+ pageSizeInit ?: number ;
224+ } ;
225+
226+ export const MySongProvider = ( {
227+ InitialsongsFolder = { } ,
228+ children,
229+ totalPagesInit = 0 ,
230+ currentPageInit = 0 ,
231+ pageSizeInit = MY_SONGS . PAGE_SIZE ,
232+ } : MySongProviderProps ) => {
233+ const initialize = useMySongsStore ( ( state ) => state . initialize ) ;
225234
226- return context ;
235+ useEffect ( ( ) => {
236+ initialize (
237+ InitialsongsFolder ,
238+ totalPagesInit ,
239+ currentPageInit ,
240+ pageSizeInit ,
241+ ) ;
242+ } , [
243+ InitialsongsFolder ,
244+ totalPagesInit ,
245+ currentPageInit ,
246+ pageSizeInit ,
247+ initialize ,
248+ ] ) ;
249+
250+ return < > { children } </ > ;
227251} ;
0 commit comments