From d86f4e230cee73aae6640c05d7d2a6ec4409c304 Mon Sep 17 00:00:00 2001 From: FoolRunning Date: Mon, 16 Oct 2023 11:10:42 -0400 Subject: [PATCH] #460 Make the Resource Viewer editable as a proof-of-concept Scripture editor --- .gitignore | 1 + c-sharp/NetworkObjects/UsfmDataProvider.cs | 37 +++++++++++++++++++ c-sharp/ParatextUtils/ParatextGlobals.cs | 1 + .../resource-viewer.web-view.tsx | 20 +++++++--- extensions/src/usfm-data-provider/index.d.ts | 2 +- 5 files changed, 55 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index d5e87be21a..f76c5c2282 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,4 @@ launchSettings.json # Test development user appdata files dev-appdata/ +c-sharp/**/*.BAK diff --git a/c-sharp/NetworkObjects/UsfmDataProvider.cs b/c-sharp/NetworkObjects/UsfmDataProvider.cs index 865ec0f612..d1072a64c3 100644 --- a/c-sharp/NetworkObjects/UsfmDataProvider.cs +++ b/c-sharp/NetworkObjects/UsfmDataProvider.cs @@ -1,6 +1,7 @@ using System.Text.Json; using System.Text.Json.Nodes; using System.Xml; +using System.Xml.XPath; using Paranext.DataProvider.JsonUtils; using Paranext.DataProvider.MessageHandlers; using Paranext.DataProvider.MessageTransports; @@ -43,6 +44,7 @@ protected override ResponseToRequest HandleRequest(string functionName, JsonArra "getBookNames" => GetBookNames(), "getChapter" => GetChapter(args[0]!.ToJsonString()), "getChapterUsx" => GetChapterUsx(args[0]!.ToJsonString()), + "setChapterUsx" => SetChapterUsx(args[0]!.ToJsonString(), args[1]!.ToString()), "getVerse" => GetVerse(args[0]!.ToJsonString()), _ => ResponseToRequest.Failed($"Unexpected function: {functionName}") }; @@ -73,6 +75,13 @@ private ResponseToRequest GetChapterUsx(string args) : ResponseToRequest.Failed(errorMsg); } + private ResponseToRequest SetChapterUsx(string argVref, string argNewUsx) + { + return VerseRefConverter.TryCreateVerseRef(argVref, out var verseRef, out string errorMsg) + ? SetUsx(verseRef, argNewUsx) + : ResponseToRequest.Failed(errorMsg); + } + private ResponseToRequest GetVerse(string args) { return VerseRefConverter.TryCreateVerseRef(args, out var verseRef, out string errorMsg) @@ -87,6 +96,34 @@ public string GetUsx(VerseRef vref) return contents; } + public ResponseToRequest SetUsx(VerseRef vref, string newUsx) + { + try + { + XmlDocument doc = new() { PreserveWhitespace = true }; + doc.LoadXml(newUsx); + if (doc.FirstChild?.Name != "usx") + return ResponseToRequest.Failed("Invalid USX"); + + UsxFragmenter.FindFragments( + _scrText!.ScrStylesheet(vref.BookNum), + doc.CreateNavigator(), + XPathExpression.Compile("*[false()]"), + out string usfm + ); + + usfm = UsfmToken.NormalizeUsfm(_scrText, vref.BookNum, usfm); + _scrText.PutText(vref.BookNum, vref.ChapterNum, false, usfm, null); + SendDataUpdateEvent("*"); + } + catch (Exception e) + { + return ResponseToRequest.Failed(e.Message); + } + + return ResponseToRequest.Succeeded(); + } + private XmlDocument GetUsxForChapter(int bookNum, int chapterNum) { return ConvertUsfmToUsx(GetUsfmForChapter(bookNum, chapterNum), bookNum); diff --git a/c-sharp/ParatextUtils/ParatextGlobals.cs b/c-sharp/ParatextUtils/ParatextGlobals.cs index 1a93744631..240cba4342 100644 --- a/c-sharp/ParatextUtils/ParatextGlobals.cs +++ b/c-sharp/ParatextUtils/ParatextGlobals.cs @@ -34,6 +34,7 @@ public static void Initialize(string dataFolderPath) ICUDllLocator.Initialize(false, false); // Now tell Paratext.Data to use the specified folder + dataFolderPath = Path.GetFullPath(dataFolderPath); // Make sure path is rooted ParatextData.Initialize(dataFolderPath, false); s_initialized = true; } diff --git a/extensions/src/resource-viewer/resource-viewer.web-view.tsx b/extensions/src/resource-viewer/resource-viewer.web-view.tsx index 38a2b93d1e..897cdd7cba 100644 --- a/extensions/src/resource-viewer/resource-viewer.web-view.tsx +++ b/extensions/src/resource-viewer/resource-viewer.web-view.tsx @@ -123,6 +123,7 @@ const usxEditorCharMap = Object.fromEntries( interface ScriptureTextPanelUsxProps { usx: string; + onChanged?: (newUsx: string) => void; } const defaultScrRef: ScriptureReference = { @@ -135,7 +136,7 @@ const defaultScrRef: ScriptureReference = { * Scripture text panel that displays a read only version of a usx editor that displays the current * chapter */ -function ScriptureTextPanelUsxEditor({ usx }: ScriptureTextPanelUsxProps) { +function ScriptureTextPanelUsxEditor({ usx, onChanged }: ScriptureTextPanelUsxProps) { return ( // eslint-disable-next-line jsx-a11y/no-static-element-interactions
@@ -143,8 +144,9 @@ function ScriptureTextPanelUsxEditor({ usx }: ScriptureTextPanelUsxProps) { usx={usx} paraMap={usxEditorParaMap} charMap={usxEditorCharMap} - onUsxChanged={() => { - /* Read only */ + onUsxChanged={(newUsx) => { + // TODO: Check if the project is editable + if (onChanged) onChanged(newUsx); }} />
@@ -155,11 +157,19 @@ globalThis.webViewComponent = function ResourceViewer(): JSX.Element { logger.info('Preparing to display the Resource Viewer'); const [scrRef] = useSetting('platform.verseRef', defaultScrRef); - const [usx, , isLoading] = useData.ChapterUsx( + const [usx, setUsx, isLoading] = useData.ChapterUsx( 'usfm', useMemo(() => new VerseRef(scrRef.bookNum, scrRef.chapterNum, scrRef.verseNum), [scrRef]), 'Loading Scripture...', ); - return
{isLoading ? 'Loading' : '} />}
; + return ( +
+ {isLoading ? ( + 'Loading' + ) : ( + '} onChanged={setUsx} /> + )} +
+ ); }; diff --git a/extensions/src/usfm-data-provider/index.d.ts b/extensions/src/usfm-data-provider/index.d.ts index bcac0d81a0..6c8f0a85b0 100644 --- a/extensions/src/usfm-data-provider/index.d.ts +++ b/extensions/src/usfm-data-provider/index.d.ts @@ -15,7 +15,7 @@ declare module 'usfm-data-provider' { export type UsfmProviderDataTypes = { BookNames: DataProviderDataType; Chapter: DataProviderDataType; - ChapterUsx: DataProviderDataType; + ChapterUsx: DataProviderDataType; Verse: DataProviderDataType; };