🐛(frontend) change saving behavior firefox

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.
This commit is contained in:
Anthony LC
2024-06-25 15:49:46 +02:00
committed by Anthony LC
parent 08a39c7ebd
commit d588ae847f
2 changed files with 48 additions and 12 deletions

View File

@@ -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', {

View File

@@ -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<string>(
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<NodeJS.Timeout>();
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;