Skip to content

Commit

Permalink
wip save via api call
Browse files Browse the repository at this point in the history
wip save via callback
  • Loading branch information
hatton committed Apr 30, 2024
1 parent f483d5b commit 85a4415
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 140 deletions.
4 changes: 2 additions & 2 deletions src/BloomBrowserUI/bookEdit/editablePage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export interface IPageFrameExports {
// For example, editTabBundle.getEditablePageBundleExports().pageSelectionChanging() can be called.
import {
pageSelectionChanging,
getBodyContentForSavePage,
callApiWithSaveData,
userStylesheetContent,
pageUnloading,
disconnectForGarbageCollection,
Expand All @@ -54,7 +54,7 @@ import {
} from "./js/bloomEditing";
export {
pageSelectionChanging,
getBodyContentForSavePage,
callApiWithSaveData,
userStylesheetContent,
pageUnloading,
disconnectForGarbageCollection,
Expand Down
10 changes: 7 additions & 3 deletions src/BloomBrowserUI/bookEdit/js/bloomEditing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import "jquery.hotkeys"; //makes the on(keydown work with keynames)
import "../../lib/jquery.resize"; // makes jquery resize work on all elements
import { getEditTabBundleExports } from "./bloomFrames";
import { showInvisibles, hideInvisibles } from "./showInvisibles";

import { postJson } from "../../utils/bloomApi";
//promise may be needed to run tests with phantomjs
//import promise = require('es6-promise');
//promise.Promise.polyfill();
Expand Down Expand Up @@ -1265,8 +1265,12 @@ export const pageSelectionChanging = () => {
}
};

// Called from C# by a RunJavaScript() in EditingView.CleanHtmlAndCopyToPageDom via
// editTabBundle.getEditablePageBundleExports().
export function callApiWithSaveData() {
postJson("editView/saveHtml", {
html: getBodyContentForSavePage(),
userStylesheetContent: userStylesheetContent()
});
}
export const getBodyContentForSavePage = () => {
const bubbleEditingOn = theOneBubbleManager.isComicEditingOn;
if (bubbleEditingOn) {
Expand Down
7 changes: 7 additions & 0 deletions src/BloomExe/Book/HtmlDom.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ public HtmlDom(XmlDocument domToClone)
_dom = (XmlDocument)domToClone.Clone();
}

public static HtmlDom FromXmlNodeNoClone(XmlNode domToOwn)
{
var h = new HtmlDom();
h.RawDom.DocumentElement.AppendChild(h.RawDom.ImportNode(domToOwn, true));
return h;
}

/// <summary>
/// Make a DOM out of the input
/// </summary>
Expand Down
216 changes: 113 additions & 103 deletions src/BloomExe/Edit/EditingModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1260,29 +1260,39 @@ private bool CannotSavePage()
int _saveCount = 0;
object _saveCountLock = new object();

public void SaveNow(bool forceFullSave = false)
public void SaveHtmlNow(string bodyHtml, string userCssContent)
{
try
{
lock (_saveCountLock)
{
Debug.Assert(
_saveCount == 0,
$"Trying to save while already saving: possible empty marginBox cause? (save count={_saveCount})"
);
_saveCount++;
}
SaveNowInternal(forceFullSave);
}
finally
var dom = XmlHtmlConverter.GetXmlDomFromHtml(bodyHtml, false);
var bodyDom = dom.SelectSingleNode("//body");
var browserDomPage = bodyDom.SelectSingleNode(
"//body//div[contains(@class,'bloom-page')]"
);
var htmlDom = HtmlDom.FromXmlNodeNoClone(browserDomPage);

var styles = HtmlDom.CreateUserModifiedStyles(userCssContent);

var head = htmlDom.RawDom.SelectSingleNode("//head");
head.InnerXml = HtmlDom.CreateUserModifiedStyles(userCssContent);

_pageSelection.CurrentSelection.Book.SavePage(
htmlDom,
true /*todo forceFullSave*/
);
_pageHasUnsavedDataDerivedChange = false;
CheckForBL2634("finished save");
while (_tasksToDoAfterSaving.Count > 0)
{
lock (_saveCountLock)
{
_saveCount--;
}
var task = _tasksToDoAfterSaving[0];
_tasksToDoAfterSaving.RemoveAt(0);
task();
}
}

public void SaveNow(bool forceFullSave = false)
{
SaveNowInternal(forceFullSave);
}

private void SaveNowInternal(bool forceFullSave = false)
{
if (_domForCurrentPage != null && !_inProcessOfSaving && !NavigatingSoSuspendSaving)
Expand All @@ -1292,92 +1302,92 @@ private void SaveNowInternal(bool forceFullSave = false)
// Check memory for the benefit of developers.
MemoryManagement.CheckMemory(false, "before EditingModel.SaveNow()", false);
#endif
try
{
_webSocketServer.SendString("pageThumbnailList", "saving", "");

// CleanHtml already requires that we are on UI thread. But it's worth asserting here too in case that changes.
// If we weren't sure of that we would need locking for access to _tasksToDoAfterSaving and _inProcessOfSaving,
// and would need to be careful about whether any delayed tasks needed to be on the UI thread.
if (_view.InvokeRequired)
{
NonFatalProblem.Report(
ModalIf.Beta,
PassiveIf.Beta,
"SaveNow called on wrong thread",
null
);
_view.Invoke((Action)(() => SaveNowInternal(forceFullSave)));
Logger.WriteMinorEvent(
"EditingModel.SaveNow() finished after Invoke to get on UI thread"
);
return;
}
CheckForBL2634("beginning SaveNow");
_inProcessOfSaving = true;
_tasksToDoAfterSaving.Clear();
_view.GetHtmlFromBrowserAndCopyToPageDom();

//BL-1064 (and several other reports) were about not being able to save a page. The problem appears to be that
//this old code:
// CurrentBook.SavePage(_domForCurrentPage);
//would some times ask book X to save a page from book Y.
//We could never reproduce it at will, so this is to help with that...
if (this._pageSelection.CurrentSelection.Book != _currentlyDisplayedBook)
{
Debug.Fail("This is the BL-1064 Situation");
Logger.WriteEvent(
"Warning: SaveNow() with a page that is not the current book. That should be ok, but it is the BL-1064 situation (though we now work around it)."
);
}
//but meanwhile, the page knows its book, so we can see if it looks like a valid book and give a helpful
//error if, for example, it was deleted:
try
{
if (!_pageSelection.CurrentSelection.Book.IsSaveable)
{
Logger.WriteEvent(
"Error: SaveNow() found that this book had IsSaveable=='false'"
);
Logger.WriteEvent(
"Book path was {0}",
_pageSelection.CurrentSelection.Book.FolderPath
);
throw new ApplicationException(
"Bloom tried to save a page to a book that was not in a position to be updated."
);
}
}
catch (ObjectDisposedException err) // in case even calling CanUpdate gave an error
{
Logger.WriteEvent("Error: SaveNow() found that this book was disposed.");
throw err;
}
catch (Exception err) // in case even calling CanUpdate gave an error
{
Logger.WriteEvent("Error: SaveNow():CanUpdate threw an exception");
throw err;
}
CheckForBL2634("save");
//OK, looks safe, time to save.
var newPageData = GetPageData(_domForCurrentPage.RawDom);
_pageSelection.CurrentSelection.Book.SavePage(
_domForCurrentPage,
forceFullSave || NeedToDoFullSave(newPageData)
);
_pageHasUnsavedDataDerivedChange = false;
CheckForBL2634("finished save");
while (_tasksToDoAfterSaving.Count > 0)
{
var task = _tasksToDoAfterSaving[0];
_tasksToDoAfterSaving.RemoveAt(0);
task();
}
}
finally
{
_inProcessOfSaving = false;
}
_webSocketServer.SendString("pageThumbnailList", "saving", "");

// CleanHtml already requires that we are on UI thread. But it's worth asserting here too in case that changes.
// If we weren't sure of that we would need locking for access to _tasksToDoAfterSaving and _inProcessOfSaving,
// and would need to be careful about whether any delayed tasks needed to be on the UI thread.
// if (_view.InvokeRequired)
// {
// NonFatalProblem.Report(
// ModalIf.Beta,
// PassiveIf.Beta,
// "SaveNow called on wrong thread",
// null
// );
// _view.Invoke((Action)(() => SaveNowInternal(forceFullSave)));
// Logger.WriteMinorEvent(
// "EditingModel.SaveNow() finished after Invoke to get on UI thread"
// );
// return;
// }
CheckForBL2634("beginning SaveNow");
_inProcessOfSaving = true;
_tasksToDoAfterSaving.Clear();
// TODO: do the toolbox removal, check that things are loaded, etc.
var script =
@"console.log('saving'); editTabBundle.getEditablePageBundleExports().callApiWithSaveData();";
_view.Browser.RunJavascriptAsync(script);

// _view.GetHtmlFromBrowserAndCopyToPageDom();

// //BL-1064 (and several other reports) were about not being able to save a page. The problem appears to be that
// //this old code:
// // CurrentBook.SavePage(_domForCurrentPage);
// //would some times ask book X to save a page from book Y.
// //We could never reproduce it at will, so this is to help with that...
// if (this._pageSelection.CurrentSelection.Book != _currentlyDisplayedBook)
// {
// Debug.Fail("This is the BL-1064 Situation");
// Logger.WriteEvent(
// "Warning: SaveNow() with a page that is not the current book. That should be ok, but it is the BL-1064 situation (though we now work around it)."
// );
// }
// //but meanwhile, the page knows its book, so we can see if it looks like a valid book and give a helpful
// //error if, for example, it was deleted:
// try
// {
// if (!_pageSelection.CurrentSelection.Book.IsSaveable)
// {
// Logger.WriteEvent(
// "Error: SaveNow() found that this book had IsSaveable=='false'"
// );
// Logger.WriteEvent(
// "Book path was {0}",
// _pageSelection.CurrentSelection.Book.FolderPath
// );
// throw new ApplicationException(
// "Bloom tried to save a page to a book that was not in a position to be updated."
// );
// }
// }
// catch (ObjectDisposedException err) // in case even calling CanUpdate gave an error
// {
// Logger.WriteEvent("Error: SaveNow() found that this book was disposed.");
// throw err;
// }
// catch (Exception err) // in case even calling CanUpdate gave an error
// {
// Logger.WriteEvent("Error: SaveNow():CanUpdate threw an exception");
// throw err;
// }
// CheckForBL2634("save");
// //OK, looks safe, time to save.
// var newPageData = GetPageData(_domForCurrentPage.RawDom);
// _pageSelection.CurrentSelection.Book.SavePage(
// _domForCurrentPage,
// forceFullSave || NeedToDoFullSave(newPageData)
// );
// _pageHasUnsavedDataDerivedChange = false;
// CheckForBL2634("finished save");
// while (_tasksToDoAfterSaving.Count > 0)
// {
// var task = _tasksToDoAfterSaving[0];
// _tasksToDoAfterSaving.RemoveAt(0);
// task();
// }

#if MEMORYCHECK
// Check memory for the benefit of developers.
MemoryManagement.CheckMemory(false, "after EditingModel.SaveNow()", false);
Expand Down
32 changes: 0 additions & 32 deletions src/BloomExe/Edit/EditingView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1481,38 +1481,6 @@ public void UpdateAllThumbnails()
_pageListView.UpdateAllThumbnails();
}

/// <summary>
/// this started as an experiment, where our textareas were not being read when we saved because of the need
/// to change the picture
/// </summary>
public async Task GetHtmlFromBrowserAndCopyToPageDom()
{
// NOTE: these calls to may lead to API calls from the JS. These are async, so the actions
// that JS might perform may not actually happen until well after this method. We ran into a problem in
// BL-9912 where the Leveled Reader Tool was prompted by some of this to call us back with a save to the
// tool state, but by then the editingModel had cleared out its knowledge of what book it had previously
// been editing, so there was an null.
var script =
@"
if (typeof(editTabBundle) !=='undefined' && typeof(editTabBundle.getToolboxBundleExports()) !=='undefined')
editTabBundle.getToolboxBundleExports().removeToolboxMarkup();
if (typeof(editTabBundle) !=='undefined' && typeof(editTabBundle.getEditablePageBundleExports()) !=='undefined')
editTabBundle.getEditablePageBundleExports().getBodyContentForSavePage() + '<SPLIT-DATA>' + editTabBundle.getEditablePageBundleExports().userStylesheetContent();";
var combinedData = RunJavascriptWithStringResult_Sync_Dangerous(script);
string bodyHtml = null;
string userCssContent = null;
if (combinedData != null)
{
var endHtml = combinedData.IndexOf("<SPLIT-DATA>", StringComparison.Ordinal);
if (endHtml > 0)
{
bodyHtml = combinedData.Substring(0, endHtml);
userCssContent = combinedData.Substring(endHtml + "<SPLIT-DATA>".Length);
}
}
_browser1.ReadEditedHtmlNow(bodyHtml, userCssContent); // TODO this makes no sense being in browser. Has nothing to do with the browser.
}

private void _copyButton_Click(object sender, EventArgs e)
{
ExecuteCommandSafely(_copyCommand);
Expand Down
18 changes: 18 additions & 0 deletions src/BloomExe/web/controllers/EditingViewApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ public void RegisterWithApiHandler(BloomApiHandler apiHandler)
HandleDuplicatePageMany,
true
);
apiHandler.RegisterEndpointHandler(
"editView/saveHtml",
HandleSaveHtml,
true /*review*/
,
true /*review*/
);
apiHandler.RegisterEndpointHandler("editView/topics", HandleTopics, false);
apiHandler.RegisterEndpointHandler("editView/changeImage", HandleChangeImage, true);
apiHandler.RegisterEndpointHandler("editView/cutImage", HandleCutImage, true);
Expand Down Expand Up @@ -231,6 +238,17 @@ dynamic messageBundle
}
}

private void HandleSaveHtml(ApiRequest request)
{
// the post is an object of the form {html: string, userStyles:string}
// after we get it we call the SaveHtmlNow method on the model
dynamic data = request.RequiredPostDynamic();
var html = (string)data.html;
var userStylesheetContent = (string)data.userStylesheetContent;
View.Model.SaveHtmlNow(html, userStylesheetContent);
request.PostSucceeded();
}

private void HandleChangeImage(ApiRequest request)
{
dynamic data = DynamicJson.Parse(request.RequiredPostJson());
Expand Down

0 comments on commit 85a4415

Please sign in to comment.