Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions app/components/IPRWYSIWYGEditor/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, {
forwardRef, useCallback, useEffect,
useImperativeHandle,
useMemo,
useState,
} from 'react';
Expand Down Expand Up @@ -39,11 +40,11 @@ const usePageLeaveWarning = (enabled) => {
return '';
};
useEffect(() => {
if (!enabled) return;
if (!enabled) return undefined;

window.addEventListener('beforeunload', handler);
return () => window.removeEventListener('beforeunload', handler);
}, [enabled]);
return () => window.removeEventListener('beforeunload', handler);
};

const MenuBarButton = forwardRef<HTMLButtonElement, ToggleButtonProps>(
Expand Down Expand Up @@ -194,14 +195,14 @@ type IPRWYSIWYGEditorProps = {
title?: string;
};

const IPRWYSIWYGEditor = ({
const IPRWYSIWYGEditor = forwardRef(({
alertLeave = false,
text,
isOpen,
onClose,
onSave,
title = 'IPR WYSIWYG Editor',
}: IPRWYSIWYGEditorProps): JSX.Element => {
}: IPRWYSIWYGEditorProps, editorRef): JSX.Element => {
const [isDirty, setIsDirty] = useState<boolean>(false);

const shouldWarnWhenUseLeave = alertLeave && isOpen && isDirty;
Expand All @@ -214,6 +215,8 @@ const IPRWYSIWYGEditor = ({
},
});

useImperativeHandle(editorRef, () => ({ editor, isDirty, setIsDirty }), [editor, isDirty, setIsDirty]);

useEffect(() => {
if (editor && text) {
editor.commands.setContent(text);
Expand All @@ -224,19 +227,21 @@ const IPRWYSIWYGEditor = ({
if (editor) {
onClose(editor.isEmpty ? '' : editor.getHTML());
}
setIsDirty(false);
}, [editor, onClose]);

const handleOnSave = useCallback(() => {
if (editor) {
onSave(editor.isEmpty ? '' : editor.getHTML());
setIsDirty(false);
}
setIsDirty(false);
}, [editor, onSave]);

const handleOnClose = useCallback(() => {
onClose(null);
// Reset the editor text, since we don't deal with the state in React
editor.commands.setContent(text);
onClose(null);
setIsDirty(false);
}, [onClose, editor, text]);

const saveButton = useMemo(() => {
Expand Down Expand Up @@ -266,7 +271,7 @@ const IPRWYSIWYGEditor = ({
/>
</>
);
};
});

export default IPRWYSIWYGEditor;
export { IPRWYSIWYGEditorProps, MenuBar };
55 changes: 49 additions & 6 deletions app/views/ReportView/components/AnalystComments/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, {
useEffect, useState, useContext, useCallback, useMemo,
useEffect, useState, useContext, useCallback, useMemo, useRef,
} from 'react';
import {
Typography,
Expand All @@ -24,6 +24,9 @@ import IPRWYSIWYGEditor from '@/components/IPRWYSIWYGEditor';

import './index.scss';
import { useQuery, useQueryClient } from 'react-query';
import { Editor } from '@tiptap/react';

const AUTO_SAVE_INTERVAL = 30 * 1000; // Autosaves per 30s

const useComments = (report?: ReportType) => useQuery({
queryKey: ['report-comments', report?.ident],
Expand Down Expand Up @@ -57,6 +60,7 @@ const AnalystComments = ({
const { canEdit } = useReport();
const { showConfirmDialog } = useConfirmDialog();

const editorRef = useRef<{ editor: Editor, isDirty: boolean | null }>();
const [comments, setComments] = useState('');
const [signatures, setSignatures] = useState<SignatureType>();
const [signatureTypes, setSignatureTypes] = useState<SignatureUserType[]>(DEFAULT_SIGNATURE_TYPES);
Expand All @@ -76,15 +80,45 @@ const AnalystComments = ({
commentsQuery.error || signaturesQuery.error || signatureTypesQuery.error
}`);
}
}, [commentsQuery.error, isError, signatureTypesQuery.error, signaturesQuery.error]);

useEffect(() => {
if (!isApiLoading) {
setComments(commentsQuery.data);
setSignatures(signaturesQuery.data);
setSignatureTypes(signatureTypesQuery.data);
setIsComponentLoading(false);
if (loadedDispatch) loadedDispatch({ type: 'analyst-comments' });
}
}, [setIsComponentLoading, isApiLoading, isError, commentsQuery.data, signaturesQuery.data, signatureTypesQuery.data, loadedDispatch, commentsQuery.error, signaturesQuery.error, signatureTypesQuery.error]);
}, [setIsComponentLoading, isApiLoading, isError, commentsQuery.data, signaturesQuery.data, signatureTypesQuery.data, loadedDispatch]);

// Try to load previously unsaved analyst comments
useEffect(() => {
if (isEditorOpen && !isApiLoading) {
const savedComments = localStorage.getItem(`${report.ident}-analyst_comments`);
if (savedComments) {
snackbar.info('Loaded previously unsaved analyst comments, please remember to save.');
localStorage.removeItem(`${report.ident}-analyst_comments`);
editorRef.current.editor.commands.setContent(savedComments);
}
}
}, [isApiLoading, isEditorOpen, report.ident]);

// Intervally saves in-edit analyst comments
useEffect(() => {
const interval = setInterval(() => {
const editor = editorRef.current?.editor;
const isDirty = editorRef.current?.isDirty;
if (!editor) return;

// When user is actively editing
if (isEditorOpen && isDirty) {
localStorage.setItem(`${report.ident}-analyst_comments`, editorRef.current.editor.getHTML());
}
}, AUTO_SAVE_INTERVAL);

return () => clearInterval(interval);
}, [isEditorOpen, report.ident]);

const handleSign = useCallback(async (signed: boolean) => {
setIsSigned(signed);
Expand Down Expand Up @@ -154,13 +188,21 @@ const AnalystComments = ({
);

const handleEditorSave = useCallback(
(editedComments?: string) => handleEditorAction(editedComments, false),
[handleEditorAction],
(editedComments?: string) => {
// Clear sessionStoarge because the user already saved
localStorage.removeItem(`${report.ident}-analyst_comments`);
return handleEditorAction(editedComments, false);
},
[handleEditorAction, report.ident],
);

const handleEditorClose = useCallback(
(editedComments?: string) => handleEditorAction(editedComments, true),
[handleEditorAction],
(editedComments?: string) => {
// Clear sessionStoarge because the user decided to not save
localStorage.removeItem(`${report.ident}-analyst_comments`);
return handleEditorAction(editedComments, true);
},
[handleEditorAction, report.ident],
);

const signatureSection = useMemo(() => signatureTypes.map((sigType) => {
Expand Down Expand Up @@ -206,6 +248,7 @@ const AnalystComments = ({
<EditIcon />
</Fab>
<IPRWYSIWYGEditor
ref={editorRef}
alertLeave
isOpen={isEditorOpen}
text={comments}
Expand Down