From d588ae847f89ef2d9e8653066f6f345e244b42cb Mon Sep 17 00:00:00 2001 From: Anthony LC Date: Tue, 25 Jun 2024 15:49:46 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B(frontend)=20change=20saving=20beha?= =?UTF-8?q?vior=20firefox?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Firefox does not trigger the request everytime the user leaves the page. Plus the request is not intercepted by the service worker. So we prevent the default behavior to have the popup asking the user if he wants to leave the page, by adding the popup, we let the time to the request to be sent, and intercepted by the service worker (for the offline part). We also add a toast to inform the user that the document has been saved, it will make it more obvious that the document has been saved if a firefox user come back from the popup to the page. --- .../__tests__/app-impress/doc-editor.spec.ts | 4 ++ .../docs/doc-editor/hook/useSaveDoc.tsx | 56 +++++++++++++++---- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts index 27dee6f0..3d502fad 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts @@ -132,6 +132,10 @@ test.describe('Doc Editor', () => { }) .click(); + await expect( + page.getByText(`Your document "${doc}" has been saved.`), + ).toBeVisible(); + const card = page.getByLabel('Create new document card').first(); await expect( card.getByRole('heading', { diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useSaveDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useSaveDoc.tsx index f02fe2ea..319133b6 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useSaveDoc.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useSaveDoc.tsx @@ -1,5 +1,7 @@ +import { VariantType, useToastProvider } from '@openfun/cunningham-react'; import { useRouter } from 'next/router'; import { useCallback, useEffect, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import * as Y from 'yjs'; import { useUpdateDoc } from '@/features/docs/doc-management/'; @@ -7,7 +9,19 @@ import { useUpdateDoc } from '@/features/docs/doc-management/'; import { toBase64 } from '../utils'; const useSaveDoc = (docId: string, doc: Y.Doc, canSave: boolean) => { - const { mutate: updateDoc } = useUpdateDoc(); + const { toast } = useToastProvider(); + const { t } = useTranslation(); + + const { mutate: updateDoc } = useUpdateDoc({ + onSuccess: (data) => { + toast( + t('Your document "{{docTitle}}" has been saved.', { + docTitle: data.title, + }), + VariantType.SUCCESS, + ); + }, + }); const [initialDoc, setInitialDoc] = useState( toBase64(Y.encodeStateAsUpdate(doc)), ); @@ -40,23 +54,23 @@ const useSaveDoc = (docId: string, doc: Y.Doc, canSave: boolean) => { }; }, [doc]); + /** + * Check if the doc has been updated and can be saved. + */ + const shouldSave = useCallback(() => { + const newDoc = toBase64(Y.encodeStateAsUpdate(doc)); + return initialDoc !== newDoc && canSave; + }, [canSave, doc, initialDoc]); + const saveDoc = useCallback(() => { const newDoc = toBase64(Y.encodeStateAsUpdate(doc)); - - /** - * Save only if the doc has changed. - */ - if (initialDoc === newDoc || !canSave) { - return; - } - setInitialDoc(newDoc); updateDoc({ id: docId, content: newDoc, }); - }, [initialDoc, docId, doc, updateDoc, canSave]); + }, [doc, docId, updateDoc]); const timeout = useRef(); const router = useRouter(); @@ -66,8 +80,26 @@ const useSaveDoc = (docId: string, doc: Y.Doc, canSave: boolean) => { clearTimeout(timeout.current); } - const onSave = () => { + const onSave = (e?: Event) => { + if (!shouldSave()) { + return; + } + saveDoc(); + + /** + * Firefox does not trigger the request everytime the user leaves the page. + * Plus the request is not intercepted by the service worker. + * So we prevent the default behavior to have the popup asking the user + * if he wants to leave the page, by adding the popup, we let the time to the + * request to be sent, and intercepted by the service worker (for the offline part). + */ + const isFirefox = + navigator.userAgent.toLowerCase().indexOf('firefox') > -1; + + if (typeof e !== 'undefined' && e.preventDefault && isFirefox) { + e.preventDefault(); + } }; // Save every minute @@ -82,7 +114,7 @@ const useSaveDoc = (docId: string, doc: Y.Doc, canSave: boolean) => { removeEventListener('beforeunload', onSave); router.events.off('routeChangeStart', onSave); }; - }, [router.events, saveDoc]); + }, [router.events, saveDoc, shouldSave]); }; export default useSaveDoc;