@@ -13,6 +13,7 @@ import Tooltip from '@mui/material/Tooltip';
1313import Stack from '@mui/material/Stack' ;
1414import Snackbar from '@mui/material/Snackbar' ;
1515import IconButton from '@mui/material/IconButton' ;
16+ import CircularProgress from '@mui/material/CircularProgress' ;
1617// Icons
1718import ThumbUpAltRoundedIcon from '@mui/icons-material/ThumbUpAltRounded' ;
1819import ThumbDownAltRoundedIcon from '@mui/icons-material/ThumbDownAltRounded' ;
@@ -30,7 +31,8 @@ import PageContext from 'docs/src/modules/components/PageContext';
3031import SvgMuiLogotype from 'docs/src/icons/SvgMuiLogotype' ;
3132import EditPage from 'docs/src/modules/components/EditPage' ;
3233import { 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' ;
3436
3537const FooterLink = styled ( Link ) ( ( { theme } ) => {
3638 return {
@@ -80,25 +82,7 @@ function orderedPages(pages, current = []) {
8082 } ) ;
8183}
8284
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 ) {
10286 const sentData = {
10387 callback_id : 'send_feedback' ,
10488 rating,
@@ -172,64 +156,6 @@ async function postFeedbackOnSlack(data) {
172156 */
173157}
174158
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-
233159/**
234160 * @returns { { prevPage: OrderedMuiPage | null; nextPage: OrderedMuiPage | null } }
235161 */
@@ -263,14 +189,17 @@ export default function AppLayoutDocsFooter(props) {
263189 const t = useTranslate ( ) ;
264190 const userLanguage = useUserLanguage ( ) ;
265191 const { activePage, productId } = React . useContext ( PageContext ) ;
266- const [ rating , setRating ] = React . useState ( ) ;
192+ const [ storedRating , setRating ] = useLocalStorageState ( `feedback- ${ activePage ?. pathname } ` ) ;
267193 const [ comment , setComment ] = React . useState ( '' ) ;
194+ const [ loading , setLoading ] = React . useState ( false ) ;
268195 const [ snackbarOpen , setSnackbarOpen ] = React . useState ( false ) ;
269196 const [ snackbarMessage , setSnackbarMessage ] = React . useState ( false ) ;
270197 const inputRef = React . useRef ( ) ;
271198 const [ commentOpen , setCommentOpen ] = React . useState ( false ) ;
272199 const [ commentedSection , setCommentedSection ] = React . useState ( EMPTY_SECTION ) ;
273200
201+ const rating = storedRating ? Number ( storedRating ) : null ;
202+
274203 const { nextPage, prevPage } = usePageNeighbours ( ) ;
275204
276205 const sectionOptions = React . useMemo (
@@ -285,36 +214,32 @@ export default function AppLayoutDocsFooter(props) {
285214 [ tableOfContents ] ,
286215 ) ;
287216
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 ) ;
293220
294- React . useEffect ( ( ) => {
295- setCurrentRatingFromCookie ( ) ;
296- } , [ setCurrentRatingFromCookie ] ) ;
221+ if ( activePage === null ) {
222+ setSnackbarMessage ( t ( 'feedbackFailed' ) ) ;
223+ }
297224
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+ ) ;
302233
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 ) ;
316242 }
317- setSnackbarOpen ( true ) ;
318243 }
319244
320245 const handleClickThumb = ( vote ) => async ( ) => {
@@ -359,7 +284,6 @@ export default function AppLayoutDocsFooter(props) {
359284
360285 const handleCancelComment = ( ) => {
361286 setCommentOpen ( false ) ;
362- setCurrentRatingFromCookie ( ) ;
363287 setCommentedSection ( EMPTY_SECTION ) ;
364288 } ;
365289
@@ -411,17 +335,31 @@ export default function AppLayoutDocsFooter(props) {
411335 { t ( 'feedbackMessage' ) }
412336 </ Typography >
413337 < 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+ ) }
418349 </ IconButton >
419350 </ Tooltip >
420351 < 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+ ) }
425363 </ IconButton >
426364 </ Tooltip >
427365 </ Stack >
0 commit comments