Skip to content

Commit

Permalink
fix: prevent unsaved changes loss (#1786)
Browse files Browse the repository at this point in the history
  • Loading branch information
stepan662 authored Jul 12, 2023
1 parent db03807 commit 02b6808
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 36 deletions.
4 changes: 2 additions & 2 deletions e2e/cypress/common/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ export const editCell = (oldValue: string, newValue?: string, save = true) => {
}
};

export function confirmSaveChanges() {
cy.gcy('global-confirmation-confirm').contains('Save').click();
export function confirmDiscard() {
cy.gcy('global-confirmation-confirm').contains('Discard').click();
}

export const toggleLang = (lang) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getAnyContainingText } from '../../../common/xPath';
import { ProjectDTO } from '../../../../../webapp/src/service/response.types';
import {
confirmSaveChanges,
confirmDiscard,
create4Translations,
editCell,
forEachView,
Expand Down Expand Up @@ -91,9 +91,8 @@ describe('Views with 5 Translations', () => {
it('will ask for confirmation on changed edit', () => {
editCell('Cool translated text 1', 'Cool translation edited', false);
cy.contains('Cool translated text 4').click();
cy.contains(`Unsaved changes`).should('be.visible');
confirmSaveChanges();
cy.contains('Cool translation edited');
cy.contains(`Discard changes?`).should('be.visible');
confirmDiscard();
cy.gcy('global-editor')
.contains('Cool translation edited')
.should('not.exist');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import ReactList from 'react-list';
import { useApiQuery } from 'tg.service/http/useQueryApi';

Expand Down Expand Up @@ -115,6 +115,18 @@ export const [
selectionService.clear();
};

useEffect(() => {
// prevent leaving the page when there are unsaved changes
if (editService.position?.changed) {
const handler = (e) => {
e.preventDefault();
e.returnValue = '';
};
window.addEventListener('beforeunload', handler);
return () => window.removeEventListener('beforeunload', handler);
}
}, [editService.position?.changed]);

// actions

const actions = {
Expand All @@ -126,18 +138,24 @@ export const [
translationService.setUrlSearch(search);
return handleTranslationsReset();
},
setFilters(filters: Filters) {
translationService.setFilters(filters);
return handleTranslationsReset();
async setFilters(filters: Filters) {
if (await editService.confirmUnsavedChanges()) {
translationService.setFilters(filters);
return handleTranslationsReset();
}
},
setEdit(edit: Edit | undefined) {
return editService.setEdit(edit);
async setEdit(edit: Edit | undefined) {
if (await editService.confirmUnsavedChanges(edit)) {
return editService.setPositionAndFocus(edit);
}
},
setEditForce(edit: Edit | undefined) {
return editService.setPositionAndFocus(edit);
},
updateEdit(edit: Partial<Edit>) {
return editService.updatePosition(edit);
async updateEdit(edit: Partial<Edit>) {
if (await editService.confirmUnsavedChanges(edit)) {
return editService.updatePosition(edit);
}
},
toggleSelect(index: number) {
return selectionService.toggle(index);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
import { container } from 'tsyringe';
import { T, useTranslate } from '@tolgee/react';
import { T } from '@tolgee/react';

import { components } from 'tg.service/apiSchema.generated';
import {
Expand Down Expand Up @@ -37,7 +37,6 @@ export const useEditService = ({ translations, viewRefs }: Props) => {
const putTranslation = usePutTranslation();
const putTag = usePutTag();
const deleteTag = useDeleteTag();
const { t } = useTranslate();

useEffect(() => {
// field is also focused, which breaks the scrolling
Expand Down Expand Up @@ -154,25 +153,37 @@ export const useEditService = ({ translations, viewRefs }: Props) => {
});
};

const setEdit = (newPosition: Edit | undefined) => {
if (position?.changed) {
setPositionAndFocus({ ...position, mode: 'editor' });
confirmation({
title: t('translations_unsaved_changes_confirmation_title'),
message: t('translations_unsaved_changes_confirmation'),
cancelButtonText: t('back_to_editing'),
confirmButtonText: t('translations_cell_save'),
onConfirm: () => {
changeField({
onSuccess() {
setPositionAndFocus(newPosition);
},
});
},
});
} else {
setPositionAndFocus(newPosition);
}
const confirmUnsavedChanges = (newPosition?: Partial<Edit>) => {
return new Promise<boolean>((resolve) => {
const fieldIsDifferent =
newPosition?.keyId !== undefined &&
(newPosition?.keyId !== position?.keyId ||
newPosition?.language !== position?.language);

if (
position?.changed &&
position.keyId !== undefined &&
(!newPosition || fieldIsDifferent)
) {
setPositionAndFocus({ ...position, mode: 'editor' });
confirmation({
title: <T keyName="translations_discard_unsaved_title" />,
message: <T keyName="translations_discard_unsaved_message" />,
cancelButtonText: <T keyName="back_to_editing" />,
confirmButtonText: (
<T keyName="translations_discard_button_confirm" />
),
onConfirm() {
resolve(true);
},
onCancel() {
resolve(false);
},
});
} else {
resolve(true);
}
});
};

const changeField = async (data: ChangeValue) => {
Expand Down Expand Up @@ -243,8 +254,8 @@ export const useEditService = ({ translations, viewRefs }: Props) => {
setPosition,
updatePosition,
setPositionAndFocus,
setEdit,
changeField,
confirmUnsavedChanges,
isLoading:
putKey.isLoading ||
putTranslation.isLoading ||
Expand Down

0 comments on commit 02b6808

Please sign in to comment.