33'use client' ;
44import styled from '@emotion/styled' ;
55import { Autorenew , Send } from '@mui/icons-material' ;
6+ import { css , keyframes } from '@mui/material' ;
67import Avatar from '@mui/material/Avatar' ;
78import Button from '@mui/material/Button' ;
89import { purple } from '@mui/material/colors' ;
@@ -14,64 +15,60 @@ import { useClientContext } from '@/hooks/useClientContext';
1415
1516import SubmitButton from '@/components/shared/SubmitButton' ;
1617
17- const StyledRefreshButton = styled . div `
18- position: absolute;
19- right: 0;
20- top: 0;
21- margin: 0.5rem;
22- cursor: pointer;
23- svg {
24- width: 20px;
25- height: 20px;
26- }
27- :hover {
28- svg {
29- path {
30- fill: ${ purple [ 500 ] } ;
31- }
32- }
33- .MuiAvatar-circular {
34- background-color: ${ purple [ 50 ] } ;
35- }
36- }
37- ` ;
18+ import { getApiResponse } from '@/utils/shared/get-api-response' ;
3819
3920const DisplayRandomPicture = ( ) => {
4021 const [ imageUrl , setImageUrl ] = useState ( '' ) ;
41- const [ loading , setLoading ] = useState ( true ) ;
22+ const [ loading , setLoading ] = useState ( false ) ;
4223 const [ error , setError ] = useState ( '' ) ;
4324 const { fetchCount, updateClientCtx } = useClientContext ( ) ;
4425 const { setAlertBarProps, renderAlertBar } = useAlertBar ( ) ;
4526 const renderCountRef = React . useRef ( 0 ) ;
4627
4728 const fetchRandomPicture = async ( ) => {
29+ if ( loading ) {
30+ setAlertBarProps ( {
31+ message : 'Please wait for the current fetch to complete' ,
32+ severity : 'warning' ,
33+ } ) ;
34+ return ;
35+ }
4836 setLoading ( true ) ;
4937 setError ( '' ) ;
5038
5139 try {
52- const response = await fetch ( 'https://picsum.photos/300/150' ) ;
53- if ( ! response . ok ) {
54- throw new Error ( 'Error fetching the image' ) ;
40+ const response = await getApiResponse < Response & { url : string } > ( {
41+ apiEndpoint : 'https://picsum.photos/300/160' ,
42+ timeout : 5001 ,
43+ } ) ;
44+
45+ if ( ! response ?. url ) {
46+ throw new Error ( 'Error fetching the image, no response url' ) ;
5547 }
48+
5649 setImageUrl ( response . url ) ;
5750 updateClientCtx ( { fetchCount : fetchCount + 1 } ) ;
5851 setAlertBarProps ( {
5952 message : 'A random picture fetched successfully' ,
6053 severity : 'info' ,
6154 } ) ;
6255 } catch ( error ) {
63- setError ( 'Error fetching the image' ) ;
56+ const errorMsg =
57+ error instanceof Error ? error . message : 'Error fetching the image' ;
58+
59+ setError ( errorMsg ) ;
6460 setAlertBarProps ( {
65- message : 'Error fetching the image' ,
61+ message : errorMsg ,
6662 severity : 'error' ,
6763 } ) ;
64+ setLoading ( false ) ;
6865 } finally {
6966 setLoading ( false ) ;
7067 }
7168 } ;
7269
7370 useEffect ( ( ) => {
74- if ( renderCountRef . current === 0 ) {
71+ if ( renderCountRef . current === 0 && ! loading ) {
7572 fetchRandomPicture ( ) ;
7673 }
7774 renderCountRef . current += 1 ;
@@ -96,7 +93,7 @@ const DisplayRandomPicture = () => {
9693 ) }
9794 < div >
9895 { loading && < span > Loading...</ span > } Component Render Count:{ ' ' }
99- { renderCountRef . current }
96+ { renderCountRef . current + 1 }
10097 </ div >
10198
10299 < SubmitButton
@@ -113,14 +110,54 @@ const DisplayRandomPicture = () => {
113110 Get Another Picture
114111 </ Button >
115112 </ SubmitButton >
116- < StyledRefreshButton onClick = { fetchRandomPicture } >
117- < Avatar sx = { { width : 24 , height : 24 } } >
118- < Autorenew />
119- </ Avatar >
120- </ StyledRefreshButton >
113+ { imageUrl && (
114+ < StyledRefreshButton onClick = { fetchRandomPicture } loading = { loading } >
115+ < Avatar sx = { { width : 24 , height : 24 } } >
116+ < Autorenew />
117+ </ Avatar >
118+ </ StyledRefreshButton >
119+ ) }
121120 { renderAlertBar ( ) }
122121 </ Stack >
123122 ) ;
124123} ;
125124
125+ const spin = keyframes `
126+ from {
127+ transform : rotate (0deg );
128+ }
129+ to {
130+ transform : rotate (360deg );
131+ }
132+ ` ;
133+ const StyledRefreshButton = styled . div < { loading ?: boolean } > `
134+ position: absolute;
135+ right: 0;
136+ top: 0;
137+ margin: 0.5rem !important;
138+ pointer-events: ${ ( { loading } ) => ( loading ? 'none' : 'auto' ) } ;
139+ opacity: ${ ( { loading } ) => ( loading ? '0.6' : '1' ) } ;
140+ cursor: ${ ( { loading } ) => ( loading ? 'not-allowed' : 'pointer' ) } ;
141+ svg {
142+ width: 20px;
143+ height: 20px;
144+ animation: ${ ( { loading } ) =>
145+ loading
146+ ? css `
147+ ${ spin } 2s linear infinite
148+ `
149+ : 'none' } ;
150+ }
151+ :hover {
152+ svg {
153+ path {
154+ fill: ${ purple [ 500 ] } ;
155+ }
156+ }
157+ .MuiAvatar-circular {
158+ background-color: ${ purple [ 50 ] } ;
159+ }
160+ }
161+ ` ;
162+
126163export default DisplayRandomPicture ;
0 commit comments