Skip to content

Commit

Permalink
fix/s5-error (#380)
Browse files Browse the repository at this point in the history
fix previous file rendering when current file is erroring out
fix b tag error and validation on import.
  • Loading branch information
samueljd authored Nov 12, 2024
1 parent b1da00c commit 05c449b
Show file tree
Hide file tree
Showing 11 changed files with 337 additions and 54 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "5.0.1",
"sj-usfm-grammar": "^3.0.4",
"sj-usfm-grammar": "3.0.8",
"styled-components": "^5.3.6",
"tc-ui-toolkit": "5.3.3",
"terser-webpack-plugin": "^5.3.10",
Expand Down
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,
} = 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
48 changes: 48 additions & 0 deletions renderer/src/components/EditorPage/TextEditor/ErrorScreen.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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>
);
}
51 changes: 30 additions & 21 deletions renderer/src/components/EditorPage/TextEditor/cacheUtils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-console */
import * as path from 'path';
import * as crypto from 'crypto';
import { convertUsfmToUsj } from './conversionUtils';
Expand Down Expand Up @@ -84,32 +85,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);
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);
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.');
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');
const cachedUsj = await readCache(oldHash, projectCachePath);
return { usj: cachedUsj, error: null };
}

console.log('Cache miss or content changed');
deleteOldCacheFile(oldHash, projectCachePath);
return processAndCacheUSJ();
} catch (err) {
console.error('Error in handleCache:', err);
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
13 changes: 13 additions & 0 deletions renderer/src/components/EditorPage/TextEditor/conversionUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ export async function convertUsjToUsfm(usj) {
return usfm;
}

export async function validateUsfm(usfm) {
if (!usfmParserInstance) {
usfmParserInstance = await initializeParser();
}
try {
const { isValid, validUSFM, bookCode } = usfmParserInstance.validate(usfm);
// console.log('USFM validation, isValid:', isValid, 'validUSFM:', validUSFM, bookCode);
return { isValid, validUSFM, bookCode };
} catch (e) {
return { isValid: false, error: e };
}
}

initializeParser()
.then(() => {
// eslint-disable-next-line no-console
Expand Down
62 changes: 48 additions & 14 deletions renderer/src/components/EditorPage/TextEditor/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import React, {
} from 'react';
import { ReferenceContext } from '@/components/context/ReferenceContext';
import { debounce } from 'lodash';

import { LoadingSpinner } from '@/components/LoadingSpinner';
import { useAutoSnackbar } from '@/components/SnackBar';
import { useTranslation } from 'react-i18next';
import { useReadUsfmFile } from './hooks/useReadUsfmFile';
import EditorMenuBar from './EditorMenuBar';
import LexicalEditor from './LexicalEditor';
import { updateCacheNSaveFile } from './updateAndSave';
import EmptyScreen from './EmptyScreen';
import ErrorScreen from './ErrorScreen';

const defaultScrRef = {
bookCode: 'PSA',
Expand All @@ -24,32 +26,61 @@ 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,
bookId: defaultBookId,
selectedFont,
editorFontSize,
projectScriptureDir,
},
actions: {
handleSelectedFont, onChangeChapter, onChangeVerse, handleEditorFontSize,
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 +124,7 @@ export default function TextEditor() {
handleEditorFontSize,
bookAvailable,
booksInProject,
parseError,
};

const props = {
Expand All @@ -105,7 +137,6 @@ export default function TextEditor() {
scrRef,
setScrRef,
bookId: book,

};
return (
<div className="flex flex-col h-editor rounded-md shadow overflow-hidden">
Expand All @@ -114,8 +145,11 @@ export default function TextEditor() {
<LoadingSpinner />
) : (
<>
{!bookAvailable && <EmptyScreen />}
{bookAvailable && usjInput && <LexicalEditor {...props} />}
{parseError && <ErrorScreen />}
{!parseError && !bookAvailable && <EmptyScreen />}
{!parseError && bookAvailable && usjInput && (
<LexicalEditor {...props} />
)}
</>
)}
</div>
Expand Down
21 changes: 10 additions & 11 deletions renderer/src/components/Projects/ImportPopUp.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DocumentTextIcon, FolderOpenIcon } from '@heroicons/react/24/outline';
import { SnackBar } from '@/components/SnackBar';
import { ProjectContext } from '@/components/context/ProjectContext';
import { readUsfm } from '@/components/Projects/utils/readUsfm';
import { validateUsfm } from '@/components/EditorPage/TextEditor/conversionUtils';
import styles from './ImportPopUp.module.css';
import * as logger from '../../logger';
import CloseIcon from '@/illustrations/close-button-black.svg';
Expand Down Expand Up @@ -44,8 +45,8 @@ export default function ImportPopUp(props) {
} = useContext(ProjectContext);

const compareArrays = (a, b) => a.length === b.length
&& a.every((element) => b.indexOf(element) !== -1)
&& b.every((element) => a.indexOf(element) !== -1);
&& a.every((element) => b.indexOf(element) !== -1)
&& b.every((element) => a.indexOf(element) !== -1);

function close() {
logger.debug('ImportPopUp.js', 'Closing the Import UI');
Expand Down Expand Up @@ -114,19 +115,17 @@ export default function ImportPopUp(props) {
const fs = window.require('fs');
const files = [];
const bookCodeList = [];
folderPath.forEach((filePath) => {
folderPath.forEach(async (filePath) => {
switch (projectType) {
case 'Translation': {
const usfm = fs.readFileSync(filePath, 'utf8');
const myUsfmParser = new grammar.USFMParser(usfm, grammar.LEVEL.RELAXED);
const isJsonValid = myUsfmParser.validate();
if (isJsonValid) {
const { isValid, validUSFM, bookCode } = await validateUsfm(usfm);
if (isValid) {
// If importing a USFM file then ask user for replace of USFM with the new content or not
replaceConformation(true);
logger.debug('ImportPopUp.js', 'Valid USFM file.');
const jsonOutput = myUsfmParser.toJSON();
files.push({ id: jsonOutput.book.bookCode, content: usfm });
bookCodeList.push(jsonOutput.book.bookCode);
files.push({ id: bookCode, content: validUSFM });
bookCodeList.push(bookCode);
} else {
logger.warn('ImportPopUp.js', 'Invalid USFM file.');
setNotify('failure');
Expand Down Expand Up @@ -186,7 +185,7 @@ export default function ImportPopUp(props) {

const fileExt = filename.split('.').pop()?.toLowerCase();
if (fileExt === 'txt' || fileExt === 'usfm' || fileExt === 'text' || fileExt === 'sfm'
|| fileExt === undefined) {
|| fileExt === undefined) {
const myUsfmParser = new grammar.USFMParser(file, grammar.LEVEL.RELAXED);
const isJsonValid = myUsfmParser.validate();
// if the USFM is valid
Expand Down Expand Up @@ -238,7 +237,7 @@ export default function ImportPopUp(props) {
title: t('label-other'),
};
if (bookCodeList.length === advanceSettings.canonSpecification[2].length
&& compareArrays(advanceSettings.currentScope, bookCodeList)) {
&& compareArrays(advanceSettings.currentScope, bookCodeList)) {
newCanonSpecification.title = advanceSettings.canonSpecification[2].title;
newCanonSpecification.id = advanceSettings.canonSpecification[2].id;
} else if (bookCodeList.length === advanceSettings.canonSpecification[1].length
Expand Down
Loading

0 comments on commit 05c449b

Please sign in to comment.