@@ -13,6 +13,7 @@ import Tooltip from '@mui/material/Tooltip';
13
13
import Stack from '@mui/material/Stack' ;
14
14
import Snackbar from '@mui/material/Snackbar' ;
15
15
import IconButton from '@mui/material/IconButton' ;
16
+ import CircularProgress from '@mui/material/CircularProgress' ;
16
17
// Icons
17
18
import ThumbUpAltRoundedIcon from '@mui/icons-material/ThumbUpAltRounded' ;
18
19
import ThumbDownAltRoundedIcon from '@mui/icons-material/ThumbDownAltRounded' ;
@@ -30,7 +31,8 @@ import PageContext from 'docs/src/modules/components/PageContext';
30
31
import SvgMuiLogotype from 'docs/src/icons/SvgMuiLogotype' ;
31
32
import EditPage from 'docs/src/modules/components/EditPage' ;
32
33
import { useUserLanguage , useTranslate } from '@mui/docs/i18n' ;
33
- import { getCookie , pageToTitleI18n } from 'docs/src/modules/utils/helpers' ;
34
+ import { pageToTitleI18n } from 'docs/src/modules/utils/helpers' ;
35
+ import useLocalStorageState from '@mui/utils/useLocalStorageState' ;
34
36
35
37
const FooterLink = styled ( Link ) ( ( { theme } ) => {
36
38
return {
@@ -80,25 +82,7 @@ function orderedPages(pages, current = []) {
80
82
} ) ;
81
83
}
82
84
83
- async function postFeedback ( data ) {
84
- const env = process . env . DEPLOY_ENV === 'production' ? 'prod' : 'dev' ;
85
- try {
86
- const response = await fetch ( `${ process . env . FEEDBACK_URL } /${ env } /feedback` , {
87
- method : 'POST' ,
88
- referrerPolicy : 'origin' ,
89
- headers : { 'Content-Type' : 'application/json' } ,
90
- body : JSON . stringify ( data ) ,
91
- } ) ;
92
- return response . json ( ) ;
93
- } catch ( error ) {
94
- console . error ( error ) ;
95
- return null ;
96
- }
97
- }
98
-
99
- async function postFeedbackOnSlack ( data ) {
100
- const { rating, comment, commentedSection, productId } = data ;
101
-
85
+ async function submitFeedback ( page , rating , comment , language , commentedSection , productId ) {
102
86
const sentData = {
103
87
callback_id : 'send_feedback' ,
104
88
rating,
@@ -172,64 +156,6 @@ async function postFeedbackOnSlack(data) {
172
156
*/
173
157
}
174
158
175
- async function getUserFeedback ( id ) {
176
- const env = location . hostname === 'mui.com' ? 'prod' : 'dev' ;
177
- const URL = `${ process . env . FEEDBACK_URL } /${ env } /feedback/${ id } ` ;
178
-
179
- try {
180
- const response = await fetch ( URL , {
181
- method : 'GET' ,
182
- cache : 'no-store' ,
183
- referrerPolicy : 'origin' ,
184
- } ) ;
185
- return response . json ( ) ;
186
- } catch ( error ) {
187
- console . error ( error ) ;
188
- return null ;
189
- }
190
- }
191
-
192
- async function submitFeedback ( page , rating , comment , language , commentedSection , productId ) {
193
- const resultSlack = await postFeedbackOnSlack ( { rating, comment, commentedSection, productId } ) ;
194
-
195
- if ( rating !== undefined ) {
196
- const resultVote = await postFeedback ( {
197
- id : getCookie ( 'feedbackId' ) ,
198
- page,
199
- rating,
200
- comment,
201
- version : process . env . LIB_VERSION ,
202
- language,
203
- } ) ;
204
- if ( resultVote ) {
205
- document . cookie = `feedbackId=${ resultVote . id } ;path=/;max-age=31536000` ;
206
- setTimeout ( async ( ) => {
207
- const userFeedback = await getUserFeedback ( resultVote . id ) ;
208
- if ( userFeedback ) {
209
- document . cookie = `feedback=${ JSON . stringify ( userFeedback ) } ;path=/;max-age=31536000` ;
210
- }
211
- } ) ;
212
- }
213
- return resultSlack && resultVote ;
214
- }
215
-
216
- return resultSlack ;
217
- }
218
-
219
- function getCurrentRating ( pathname ) {
220
- let userFeedback ;
221
- if ( typeof window !== 'undefined' ) {
222
- try {
223
- userFeedback = getCookie ( 'feedback' ) ;
224
- userFeedback = userFeedback && JSON . parse ( userFeedback ) ;
225
- } catch {
226
- // For unknown reason the `userFeedback` can be uncomplet, leading the JSON.parse to crash the entire docs
227
- return undefined ;
228
- }
229
- }
230
- return userFeedback && userFeedback [ pathname ] && userFeedback [ pathname ] . rating ;
231
- }
232
-
233
159
/**
234
160
* @returns { { prevPage: OrderedMuiPage | null; nextPage: OrderedMuiPage | null } }
235
161
*/
@@ -263,14 +189,17 @@ export default function AppLayoutDocsFooter(props) {
263
189
const t = useTranslate ( ) ;
264
190
const userLanguage = useUserLanguage ( ) ;
265
191
const { activePage, productId } = React . useContext ( PageContext ) ;
266
- const [ rating , setRating ] = React . useState ( ) ;
192
+ const [ storedRating , setRating ] = useLocalStorageState ( `feedback- ${ activePage ?. pathname } ` ) ;
267
193
const [ comment , setComment ] = React . useState ( '' ) ;
194
+ const [ loading , setLoading ] = React . useState ( false ) ;
268
195
const [ snackbarOpen , setSnackbarOpen ] = React . useState ( false ) ;
269
196
const [ snackbarMessage , setSnackbarMessage ] = React . useState ( false ) ;
270
197
const inputRef = React . useRef ( ) ;
271
198
const [ commentOpen , setCommentOpen ] = React . useState ( false ) ;
272
199
const [ commentedSection , setCommentedSection ] = React . useState ( EMPTY_SECTION ) ;
273
200
201
+ const rating = storedRating ? Number ( storedRating ) : null ;
202
+
274
203
const { nextPage, prevPage } = usePageNeighbours ( ) ;
275
204
276
205
const sectionOptions = React . useMemo (
@@ -285,36 +214,32 @@ export default function AppLayoutDocsFooter(props) {
285
214
[ tableOfContents ] ,
286
215
) ;
287
216
288
- const setCurrentRatingFromCookie = React . useCallback ( ( ) => {
289
- if ( activePage !== null ) {
290
- setRating ( getCurrentRating ( activePage . pathname ) ) ;
291
- }
292
- } , [ activePage ] ) ;
217
+ async function processFeedback ( ) {
218
+ try {
219
+ setLoading ( true ) ;
293
220
294
- React . useEffect ( ( ) => {
295
- setCurrentRatingFromCookie ( ) ;
296
- } , [ setCurrentRatingFromCookie ] ) ;
221
+ if ( activePage === null ) {
222
+ setSnackbarMessage ( t ( 'feedbackFailed' ) ) ;
223
+ }
297
224
298
- async function processFeedback ( ) {
299
- if ( activePage === null ) {
300
- setSnackbarMessage ( t ( 'feedbackFailed' ) ) ;
301
- }
225
+ const result = await submitFeedback (
226
+ activePage . pathname ,
227
+ rating ,
228
+ comment ,
229
+ userLanguage ,
230
+ commentedSection ,
231
+ productId ,
232
+ ) ;
302
233
303
- const result = await submitFeedback (
304
- activePage . pathname ,
305
- rating ,
306
- comment ,
307
- userLanguage ,
308
- commentedSection ,
309
- productId ,
310
- ) ;
311
- if ( result ) {
312
- setSnackbarMessage ( t ( 'feedbackSubmitted' ) ) ;
313
- } else {
314
- setCurrentRatingFromCookie ( ) ;
315
- setSnackbarMessage ( t ( 'feedbackFailed' ) ) ;
234
+ if ( result ) {
235
+ setSnackbarMessage ( t ( 'feedbackSubmitted' ) ) ;
236
+ } else {
237
+ setSnackbarMessage ( t ( 'feedbackFailed' ) ) ;
238
+ }
239
+ setSnackbarOpen ( true ) ;
240
+ } finally {
241
+ setLoading ( false ) ;
316
242
}
317
- setSnackbarOpen ( true ) ;
318
243
}
319
244
320
245
const handleClickThumb = ( vote ) => async ( ) => {
@@ -359,7 +284,6 @@ export default function AppLayoutDocsFooter(props) {
359
284
360
285
const handleCancelComment = ( ) => {
361
286
setCommentOpen ( false ) ;
362
- setCurrentRatingFromCookie ( ) ;
363
287
setCommentedSection ( EMPTY_SECTION ) ;
364
288
} ;
365
289
@@ -411,17 +335,31 @@ export default function AppLayoutDocsFooter(props) {
411
335
{ t ( 'feedbackMessage' ) }
412
336
</ Typography >
413
337
< Tooltip title = { t ( 'feedbackYes' ) } >
414
- < IconButton onClick = { handleClickThumb ( 1 ) } aria-pressed = { rating === 1 } >
415
- < ThumbUpAltRoundedIcon
416
- sx = { { fontSize : 15 , color : rating === 1 ? 'primary' : 'text.secondary' } }
417
- />
338
+ < IconButton
339
+ onClick = { handleClickThumb ( 1 ) }
340
+ disabled = { loading }
341
+ aria-pressed = { rating === 1 }
342
+ sx = { { fontSize : 15 , color : rating === 1 ? 'primary.main' : 'text.secondary' } }
343
+ >
344
+ { rating === 1 && loading ? (
345
+ < CircularProgress size = { 15 } />
346
+ ) : (
347
+ < ThumbUpAltRoundedIcon fontSize = "inherit" />
348
+ ) }
418
349
</ IconButton >
419
350
</ Tooltip >
420
351
< Tooltip title = { t ( 'feedbackNo' ) } >
421
- < IconButton onClick = { handleClickThumb ( 0 ) } aria-pressed = { rating === 0 } >
422
- < ThumbDownAltRoundedIcon
423
- sx = { { fontSize : 15 , color : rating === 0 ? 'error' : 'text.secondary' } }
424
- />
352
+ < IconButton
353
+ onClick = { handleClickThumb ( 0 ) }
354
+ disabled = { loading }
355
+ aria-pressed = { rating === 0 }
356
+ sx = { { fontSize : 15 , color : rating === 0 ? 'error.main' : 'text.secondary' } }
357
+ >
358
+ { rating === 0 && loading ? (
359
+ < CircularProgress size = { 15 } />
360
+ ) : (
361
+ < ThumbDownAltRoundedIcon fontSize = "inherit" />
362
+ ) }
425
363
</ IconButton >
426
364
</ Tooltip >
427
365
</ Stack >
0 commit comments