Skip to content

Commit

Permalink
fix previous file rendering when current file is erroring out
Browse files Browse the repository at this point in the history
  • Loading branch information
samueljd committed Nov 7, 2024
1 parent 75600e3 commit 8e5c601
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import SelectChapter from './SelectChapter';

export default function BibleNavigationX(props) {
const {
chapterNumber, setChapterNumber, setBook, loading, bookAvailable, booksInProject,
chapterNumber, setChapterNumber, setBook, loading, bookAvailable, booksInProject, parseError

Check failure on line 14 in renderer/src/components/EditorPage/TextEditor/BibleNavigationX/index.js

View workflow job for this annotation

GitHub Actions / Lint Run

Missing trailing comma
} = props;

const {
Expand Down Expand Up @@ -60,6 +60,13 @@ export default function BibleNavigationX(props) {
setReference();
}, [bookId, chapter]);

useEffect(() => {
if (parseError) {
setOpenBook(false);
setOpenChapter(false);
}
}, [parseError]);

useEffect(() => {
if (openBook === false && openChapter === false) {
setCloseNavigation(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default function EditorMenuBar(props) {
loading,
bookAvailable,
booksInProject,
parseError
} = props;

const { t } = useTranslation();
Expand Down Expand Up @@ -53,6 +54,7 @@ export default function EditorMenuBar(props) {
loading={loading}
bookAvailable={bookAvailable}
booksInProject={booksInProject}
parseError={parseError}
/>
<div
aria-label="editor-pane"
Expand Down
50 changes: 50 additions & 0 deletions renderer/src/components/EditorPage/TextEditor/ErrorScreen.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* eslint-disable no-tabs */
import { t } from 'i18next';
import React from 'react';
import { ArrowSquareOut } from 'phosphor-react';

export default function ErrorScreen() {
return (
<div className="h-full w-full items-center justify-center flex">
<div className="mx-auto px-4">
<section className="py-8 px-4 text-center">
<div className="max-w-auto mx-auto">
<div className="color:red md:max-w-lg w-10 h-10 text-center mx-auto">
<svg
version="1.1"
id="Capa_1"
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
fill="#FF5500"
viewBox="0 0 486.463 486.463"
xmlSpace="preserve"
>
<g>
<g>
<path d="M243.225,333.382c-13.6,0-25,11.4-25,25s11.4,25,25,25c13.1,0,25-11.4,24.4-24.4 C268.225,344.682,256.925,333.382,243.225,333.382z" />
<path d="M474.625,421.982c15.7-27.1,15.8-59.4,0.2-86.4l-156.6-271.2c-15.5-27.3-43.5-43.5-74.9-43.5s-59.4,16.3-74.9,43.4 l-156.8,271.5c-15.6,27.3-15.5,59.8,0.3,86.9c15.6,26.8,43.5,42.9,74.7,42.9h312.8C430.725,465.582,458.825,449.282,474.625,421.982z M440.625,402.382c-8.7,15-24.1,23.9-41.3,23.9h-312.8c-17,0-32.3-8.7-40.8-23.4c-8.6-14.9-8.7-32.7-0.1-47.7l156.8-271.4c8.5-14.9,23.7-23.7,40.9-23.7c17.1,0,32.4,8.9,40.9,23.8l156.7,271.4C449.325,369.882,449.225,387.482,440.625,402.382z" />
<path d="M237.025,157.882c-11.9,3.4-19.3,14.2-19.3,27.3c0.6,7.9,1.1,15.9,1.7,23.8c1.7,30.1,3.4,59.6,5.1,89.7c0.6,10.2,8.5,17.6,18.7,17.6c10.2,0,18.2-7.9,18.7-18.2c0-6.2,0-11.9,0.6-18.2c1.1-19.3,2.3-38.6,3.4-57.9c0.6-12.5,1.7-25,2.3-37.5c0-4.5-0.6-8.5-2.3-12.5C260.825,160.782,248.925,155.082,237.025,157.882z" />
</g>
</g>
</svg>
</div>
<p className="mt-8 font-black flex items-center gap-4">
Some USFM tags appear to be in unexpected locations. Kindly check the
<a
href="https://ubsicap.github.io/usfm/"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline flex items-center gap-1"
>
USFM documentation
<ArrowSquareOut size={16} />
</a>
</p>
{/* <h2 className="mt-8 uppercase text-xl text-primary font-black">Kindly check for errors in the USFM file</h2> */}
</div>
</section>
</div>
</div>
);
}
50 changes: 29 additions & 21 deletions renderer/src/components/EditorPage/TextEditor/cacheUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,32 +84,40 @@ export async function handleCache(filePath, usfmContent, projectCachePath, fileC
const oldHash = fileCacheMap[filePath];

async function processAndCacheUSJ() {
const { usj, error } = await convertUsfmToUsj(usfmContent);
if (error) {
// eslint-disable-next-line no-console
console.error('Error parsing USFM', error);
return { error };
try {
const { usj, error } = await convertUsfmToUsj(usfmContent);
if (error) {
console.error('Error parsing USFM:', error);

Check warning on line 90 in renderer/src/components/EditorPage/TextEditor/cacheUtils.js

View workflow job for this annotation

GitHub Actions / Lint Run

Unexpected console statement
return { error, usj: null }; // Return consistent error object
}
writeCache(newHash, usj, projectCachePath);
updateCacheMapToFile(fileCacheMapPath, filePath, newHash);
return { usj, error: null }; // Always include error field
} catch (err) {
console.error('Error in processAndCacheUSJ:', err);

Check warning on line 97 in renderer/src/components/EditorPage/TextEditor/cacheUtils.js

View workflow job for this annotation

GitHub Actions / Lint Run

Unexpected console statement
return { error: err.message, usj: null };
}
writeCache(newHash, usj, projectCachePath);
updateCacheMapToFile(fileCacheMapPath, filePath, newHash);
return { usj };
}

if (!oldHash) {
// eslint-disable-next-line no-console
console.log('No existing hash found. Creating new cache entry.');
return processAndCacheUSJ();
}
try {
if (!oldHash) {
console.log('No existing hash found. Creating new cache entry.');

Check warning on line 104 in renderer/src/components/EditorPage/TextEditor/cacheUtils.js

View workflow job for this annotation

GitHub Actions / Lint Run

Unexpected console statement
return processAndCacheUSJ();
}

if (isCacheValid(oldHash, projectCachePath) && oldHash === newHash) {
// eslint-disable-next-line no-console
console.log('Cache hit');
return { usj: await readCache(oldHash, projectCachePath) };
if (isCacheValid(oldHash, projectCachePath) && oldHash === newHash) {
console.log('Cache hit');

Check warning on line 109 in renderer/src/components/EditorPage/TextEditor/cacheUtils.js

View workflow job for this annotation

GitHub Actions / Lint Run

Unexpected console statement
const cachedUsj = await readCache(oldHash, projectCachePath);
return { usj: cachedUsj, error: null };
}

console.log('Cache miss or content changed');

Check warning on line 114 in renderer/src/components/EditorPage/TextEditor/cacheUtils.js

View workflow job for this annotation

GitHub Actions / Lint Run

Unexpected console statement
deleteOldCacheFile(oldHash, projectCachePath);
return processAndCacheUSJ();
} catch (err) {
console.error('Error in handleCache:', err);

Check warning on line 118 in renderer/src/components/EditorPage/TextEditor/cacheUtils.js

View workflow job for this annotation

GitHub Actions / Lint Run

Unexpected console statement
return { error: err.message, usj: null };
}
// eslint-disable-next-line no-console
console.log('Cache miss or content changed');
deleteOldCacheFile(oldHash, projectCachePath);
return processAndCacheUSJ();
}

export async function updateCache(filePath, usj, usfm, fileCacheMapPath, projectCachePath) {
Expand Down
40 changes: 30 additions & 10 deletions renderer/src/components/EditorPage/TextEditor/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import EditorMenuBar from './EditorMenuBar';
import LexicalEditor from './LexicalEditor';
import { updateCacheNSaveFile } from './updateAndSave';
import EmptyScreen from './EmptyScreen';
import ErrorScreen from './ErrorScreen';
import { useAutoSnackbar } from '@/components/SnackBar';
import { useTranslation } from 'react-i18next';

const defaultScrRef = {
bookCode: 'PSA',
Expand All @@ -24,32 +27,46 @@ export default function TextEditor() {
const [usjInput, setUsjInput] = useState();
const [scrRef, setScrRef] = useState(defaultScrRef);
const [navRef, setNavRef] = useState();
const [parseError, setParseError] = useState(false);
const {
state: {
bookId: defaultBookId, selectedFont, editorFontSize, projectScriptureDir,
// chapter,
// verse,
},
actions: {
handleSelectedFont, onChangeChapter, onChangeVerse, handleEditorFontSize,
},
} = useContext(ReferenceContext);
const { showSnackbar } = useAutoSnackbar();
const { t } = useTranslation();
const [book, setBook] = useState(defaultBookId);

const {
cachedData, loading, bookAvailable, booksInProject,
} = useReadUsfmFile(book);

useEffect(() => {
if (cachedData.error) {
console.error('Error parsing USFM', cachedData.error);
if (loading) {
showSnackbar(`Preparing ${book.toUpperCase()} file`, 'update');
};
const { usj, error } = cachedData;
if (!loading) {
if (error) {
console.error('Error parsing USFM:', error);
setParseError(true);
showSnackbar(t('dynamic-msg-load-ref-bible-snack-fail', { refName: book.toUpperCase() }), 'failure');
setUsjInput(null);
return;
}
}
if (!usj || Object.entries(usj).length === 0) {
setParseError(false);
setUsjInput(null);
return;
}
const { usj } = cachedData;
if (!usj && usj?.entries(usj).length === 0) { return; }
// console.log(usj);
setParseError(false);
setUsjInput(usj);
}, [book, cachedData]);
!loading && showSnackbar(t('dynamic-msg-load-ref-bible-snack', { refName: book.toUpperCase() }), 'success');
}, [cachedData, loading]);

useEffect(() => {
setScrRef({
Expand Down Expand Up @@ -93,6 +110,7 @@ export default function TextEditor() {
handleEditorFontSize,
bookAvailable,
booksInProject,
parseError,
};

const props = {
Expand All @@ -114,10 +132,12 @@ export default function TextEditor() {
<LoadingSpinner />
) : (
<>
{!bookAvailable && <EmptyScreen />}
{bookAvailable && usjInput && <LexicalEditor {...props} />}
{parseError && <ErrorScreen />}
{!parseError && !bookAvailable && <EmptyScreen />}
{!parseError && bookAvailable && usjInput && <LexicalEditor {...props} />}
</>
)}

</div>
);
}
157 changes: 157 additions & 0 deletions renderer/src/components/SnackBar/AutoSnackBar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import React, { Fragment, useState, useEffect, useRef } from 'react';

Check failure on line 1 in renderer/src/components/SnackBar/AutoSnackBar.js

View workflow job for this annotation

GitHub Actions / Lint Run

Expected a line break after this opening brace

Check failure on line 1 in renderer/src/components/SnackBar/AutoSnackBar.js

View workflow job for this annotation

GitHub Actions / Lint Run

Expected a line break before this closing brace
import { XMarkIcon } from '@heroicons/react/24/outline';
import { Popover, Transition } from '@headlessui/react';
import PropTypes from 'prop-types';
import { createRoot } from 'react-dom/client';

const colors = {
success: '#82E0AA',
failure: '#F5B7B1',
warning: '#F8C471',
info: '#85C1E9',
update: '#D5D8DC'

Check failure on line 12 in renderer/src/components/SnackBar/AutoSnackBar.js

View workflow job for this annotation

GitHub Actions / Lint Run

Missing trailing comma
};

const AutoSnackBar = ({
snackText,
snackType,
isOpen,
onClose,
}) => {
const handleTransitionEnd = () => {
if (!isOpen) {
onClose();
}
};

return (
<Popover className="fixed bottom-0 left-0 w-60 z-50">
<Transition
show={isOpen}
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
afterLeave={handleTransitionEnd}
>
<Popover.Panel className="transform transition-all p-4">
<div
className="relative p-5 mt-5 rounded-lg text-sm font-semibold text-gray-600"
style={{ fontFamily: 'sans-serif', backgroundColor: colors[snackType] }}
>
<button
type="button"
className="bg-black absolute top-0 right-0 h-6 w-6 rounded-full text-center text-white p-1 -mt-2 -mr-2 focus:outline-none"
onClick={onClose}
>
<XMarkIcon />
</button>
<p aria-label="snack-text">
{snackText}
</p>
</div>
</Popover.Panel>
</Transition>
</Popover>
);
};

AutoSnackBar.propTypes = {
snackText: PropTypes.string,
snackType: PropTypes.string,
isOpen: PropTypes.bool,
onClose: PropTypes.func,
};

export const useAutoSnackbar = () => {
const [snackbar, setSnackbar] = useState({
isOpen: false,
snackText: '',
snackType: 'success',
});
const [timeLeft, setTimeLeft] = useState(null);
const portalRef = useRef(null);
const rootRef = useRef(null);

useEffect(() => {
if (!portalRef.current) {
portalRef.current = document.createElement('div');
portalRef.current.id = 'snackbar-portal';
document.body.appendChild(portalRef.current);
}

if (!rootRef.current && portalRef.current) {
rootRef.current = createRoot(portalRef.current);
}

const handleClose = () => {
if (rootRef.current) {
rootRef.current.unmount();
rootRef.current = null;
}
if (portalRef.current && document.body.contains(portalRef.current)) {
document.body.removeChild(portalRef.current);
portalRef.current = null;
}
};

if (snackbar.isOpen && rootRef.current) {
rootRef.current.render(
<AutoSnackBar
snackText={snackbar.snackText}
snackType={snackbar.snackType}
isOpen={snackbar.isOpen}
onClose={() => {
setSnackbar(prev => ({ ...prev, isOpen: false }));

Check failure on line 108 in renderer/src/components/SnackBar/AutoSnackBar.js

View workflow job for this annotation

GitHub Actions / Lint Run

Expected parentheses around arrow function argument
setTimeLeft(null);
handleClose();
}}
/>

Check failure on line 112 in renderer/src/components/SnackBar/AutoSnackBar.js

View workflow job for this annotation

GitHub Actions / Lint Run

Missing trailing comma
);
}

return () => {
if (!snackbar.isOpen) {
handleClose();
}
};
}, [snackbar]);

useEffect(() => {
if (timeLeft === 0) {
setTimeLeft(null);
setSnackbar(prev => ({ ...prev, isOpen: false }));

Check failure on line 126 in renderer/src/components/SnackBar/AutoSnackBar.js

View workflow job for this annotation

GitHub Actions / Lint Run

Expected parentheses around arrow function argument
}

if (!timeLeft) return;

Check failure on line 129 in renderer/src/components/SnackBar/AutoSnackBar.js

View workflow job for this annotation

GitHub Actions / Lint Run

Expected { after 'if' condition

const intervalId = setInterval(() => {
setTimeLeft(timeLeft - 1);
}, 1000);

return () => clearInterval(intervalId);
}, [timeLeft]);

const showSnackbar = (text, type = 'success') => {
if (rootRef.current) {
rootRef.current.unmount();
rootRef.current = null;
}
if (portalRef.current && document.body.contains(portalRef.current)) {
document.body.removeChild(portalRef.current);
portalRef.current = null;
}

setSnackbar({
snackText: text,
snackType: type,
isOpen: true,
});
setTimeLeft(type === 'failure' ? 15 : 8);
};

return { showSnackbar };
};

Check failure on line 157 in renderer/src/components/SnackBar/AutoSnackBar.js

View workflow job for this annotation

GitHub Actions / Lint Run

Newline required at end of file but not found
Loading

0 comments on commit 8e5c601

Please sign in to comment.