3
3
'use client' ;
4
4
import styled from '@emotion/styled' ;
5
5
import { Autorenew , Send } from '@mui/icons-material' ;
6
+ import { css , keyframes } from '@mui/material' ;
6
7
import Avatar from '@mui/material/Avatar' ;
7
8
import Button from '@mui/material/Button' ;
8
9
import { purple } from '@mui/material/colors' ;
@@ -14,64 +15,60 @@ import { useClientContext } from '@/hooks/useClientContext';
14
15
15
16
import SubmitButton from '@/components/shared/SubmitButton' ;
16
17
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' ;
38
19
39
20
const DisplayRandomPicture = ( ) => {
40
21
const [ imageUrl , setImageUrl ] = useState ( '' ) ;
41
- const [ loading , setLoading ] = useState ( true ) ;
22
+ const [ loading , setLoading ] = useState ( false ) ;
42
23
const [ error , setError ] = useState ( '' ) ;
43
24
const { fetchCount, updateClientCtx } = useClientContext ( ) ;
44
25
const { setAlertBarProps, renderAlertBar } = useAlertBar ( ) ;
45
26
const renderCountRef = React . useRef ( 0 ) ;
46
27
47
28
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
+ }
48
36
setLoading ( true ) ;
49
37
setError ( '' ) ;
50
38
51
39
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' ) ;
55
47
}
48
+
56
49
setImageUrl ( response . url ) ;
57
50
updateClientCtx ( { fetchCount : fetchCount + 1 } ) ;
58
51
setAlertBarProps ( {
59
52
message : 'A random picture fetched successfully' ,
60
53
severity : 'info' ,
61
54
} ) ;
62
55
} 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 ) ;
64
60
setAlertBarProps ( {
65
- message : 'Error fetching the image' ,
61
+ message : errorMsg ,
66
62
severity : 'error' ,
67
63
} ) ;
64
+ setLoading ( false ) ;
68
65
} finally {
69
66
setLoading ( false ) ;
70
67
}
71
68
} ;
72
69
73
70
useEffect ( ( ) => {
74
- if ( renderCountRef . current === 0 ) {
71
+ if ( renderCountRef . current === 0 && ! loading ) {
75
72
fetchRandomPicture ( ) ;
76
73
}
77
74
renderCountRef . current += 1 ;
@@ -96,7 +93,7 @@ const DisplayRandomPicture = () => {
96
93
) }
97
94
< div >
98
95
{ loading && < span > Loading...</ span > } Component Render Count:{ ' ' }
99
- { renderCountRef . current }
96
+ { renderCountRef . current + 1 }
100
97
</ div >
101
98
102
99
< SubmitButton
@@ -113,14 +110,54 @@ const DisplayRandomPicture = () => {
113
110
Get Another Picture
114
111
</ Button >
115
112
</ 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
+ ) }
121
120
{ renderAlertBar ( ) }
122
121
</ Stack >
123
122
) ;
124
123
} ;
125
124
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
+
126
163
export default DisplayRandomPicture ;
0 commit comments