Jump to content

Community

When recovering editor contents, don't insert immediately


Recommended Posts

The editor being prefilled with the contents of the last post you wrote is the feature that is reported to me as a bug most often, and it is also the source of one of the most common actual bugs that is reported - not being able to delete contents from the editor (quotes, images etc) on mobile.

It would make the user experience much better if, rather than automatically filling it into the editor, it just showed a banner (like the rich content paste banner), offering to recover the unsaved data. That way, the user can easily start again from scratch if they want, but if they didn't mean to leave it, they can just click the button on the banner, and their post will be recovered.

It would also be more user friendly if the backups were removed after 24 hours or something like that (aside from anything else, it would prevent you from bloating local storage for no reason).

Link to post
Share on other sites

I had made an editor plugin to do this, but unfortunately replacing the default editor plugins is almost impossible. Instead, here's the code that I used. I am placing the following code in the public domain, so please make use of it.

(function($, _, undefined) {
	CKEDITOR.plugins.add('lmgautosave', {
		init: function (editor) {
			if (editor.config.ipsAutoSaveKey && ips.utils.db.enabled) {
				var autoSaveInterval;
				var $ipsEditor = null;
				var $mainEditorArea = null;

				editor.on('instanceReady', function () {
					//Set up variables
					$ipsEditor = $('.' + editor.id).closest('[data-ipsEditor]');
					$mainEditorArea = $ipsEditor.find('[data-role="mainEditorArea"]');

					var autoSaveValue = ips.utils.db.get('editorSave', editor.config.ipsAutoSaveKey);
					if (!autoSaveValue) {
						autoSaveValue = {date: 0, text: ''}
					}

					//If we can recover unsaved data, prompt the user about it
					dataRecovery(autoSaveValue);

					/* Save content every 2 seconds */
					onEditorLoad(function () {
						autoSaveInterval = setInterval(function () {
							var text = editor.getData();
							if (!text) return;
							var data = {
								date: Date.now(),
								text: text
							};
							ips.utils.db.set('editorSave', editor.config.ipsAutoSaveKey, data);
						}, 2000);
					});

					/* Clear content on submission */
					$('.' + editor.id).closest('form').on('submit', function (e) {
						if (autoSaveInterval) window.clearInterval(autoSaveInterval);
					});
				});

				editor.on('destroy', function () {
					if (autoSaveInterval) {
						window.clearInterval(autoSaveInterval);
					}
				});

				/**
				 * Set up and show the unsaved post recovery message
				 *
				 * @param autoSaveValue object {date: int Last modified timestamp, text: string Editor contents}
				 */
				function dataRecovery(autoSaveValue) {
					//Make sure there's actually something there to recover
					if (!autoSaveValue.text || cleanHtml(autoSaveValue.text) === cleanHtml(editor.getData())) {
						return;
					}
					//Check whether it's recent (currently cuts off after a day)
					if (autoSaveValue.date < Date.now() - 86400000) {
						ips.utils.db.remove('editorSave', editor.config.ipsAutoSaveKey);
						return;
					}

					var $message = $ipsEditor.find('[data-role="autoSavedContentMessage"]');

					function showAutoSaveMessage() {
						onEditorLoad(function () {
							$message.show();
						});
					}

					function hideAutoSaveMessage() {
						$message.slideUp();
						$message.find('[data-action="dismissAutoSavedContentMessage"]').off("click.lmgAutoSave");
						$message.find('[data-action="recoverAutoSavedContent"]').off("click.lmgAutoSave");
					}

					$message.find('[data-action="dismissAutoSavedContentMessage"]').on("click.lmgAutoSave", function (e) {
						hideAutoSaveMessage();
					});
					$message.find('[data-action="recoverAutoSavedContent"]').on('click.lmgAutoSave', function (e) {
						editor.setData(autoSaveValue.text);

						hideAutoSaveMessage();
					});

					showAutoSaveMessage();
				}

				/**
				 * Strips the unnecessary whitespace from HTML so it can be compared properly (CKE does some prettifying)
				 *
				 * @param html string
				 * @return string
				 */
				function cleanHtml(html) {
					html = html.replace(/\s+<(\/?(?:p|blockquote|div|iframe|hr|pre|ul|ol|li))/, "<$1");
					html = html.replace(/(<(?:p|blockquote|div|iframe|hr|pre|ul|ol|li)[^>]*>)\s+/, "$1");

					return html;
				}

				var observer = null;
				var editorLoadCallbacks = [];

				function onEditorLoad(callback) {
					//If it's already visible, just run the callback
					if ($mainEditorArea.is(":visible")) {
						callback();
						return;
					}
					if (observer === null) {
						//We need to wait until it shows
						observer = new MutationObserver(function (mutations) {
							if ($mainEditorArea.is(":visible")) {
								for (var i = 0; i < editorLoadCallbacks.length; i++) {
									editorLoadCallbacks[i]();
								}
								observer.disconnect();
							}
						});
						observer.observe($mainEditorArea[0], {
							attributes: true,
							attributeFilter: ["style"]
						});
					}
					editorLoadCallbacks.push(callback);
				}
			}

			//Delete old editor saves, but set a timeout so even if something weird happens it doesn't affect page performance
			setTimeout(function () {
				$.each(ips.utils.db.getByType('editorSave'), function (key, value) {
					if (_.isObject(value) && value.date) {
						if (value.date < Date.now() - 86400000) {
							//Old
							ips.utils.db.remove('editorSave', key);
						}
					} else {
						// Invalid data - remove
						ips.utils.db.remove('editorSave', key);
					}
				});
			}, 1000);
		}
	});
})(jQuery, _);

It has been a while since I wrote it, and I haven't used it since, so I can't promise that it works properly, though I don't recall it having any major issues.

Link to post
Share on other sites

If it is replaced with a banner, then I really hope it will be prominent enough for users to notice, because personally I think the auto-save feature in is the best improvement in IPS 4 compared to earlier versions by far. 

The issues it presents, such as not being able to remove some content from the editor, is really separate problems that needs to be tackled on their own. Having a banner feels like you'll just be circumventing the real problems. 

Personally I feel an "empty content" button would be more of use, as that would circumvent other circumstances where the issues pops up. 

When you rewrite this, I hope you'll make it fairly easy to hook into to change the cutoff-time and whether it's a banner or autofilled ourself.

Link to post
Share on other sites
6 hours ago, TSP said:

If it is replaced with a banner, then I really hope it will be prominent enough for users to notice, because personally I think the auto-save feature in is the best improvement in IPS 4 compared to earlier versions by far. 

The issues it presents, such as not being able to remove some content from the editor, is really separate problems that needs to be tackled on their own. Having a banner feels like you'll just be circumventing the real problems. 

Personally I feel an "empty content" button would be more of use, as that would circumvent other circumstances where the issues pops up. 

When you rewrite this, I hope you'll make it fairly easy to hook into to change the cutoff-time and whether it's a banner or autofilled ourself.

I really like the idea of a cut off time as well.  

It's one thing to need to refresh the page within a minute (or 5, or 10) but it's another to auto save a couple when hours have transpired.

Link to post
Share on other sites

Well personally I can see cases where a long cut-off time is "required". I would much rather have it be 3 days than 3 hours. 

Which is why I hope it would not be "impossible" to override whatever IPS may decide on. 

Maybe it could autofill if it's been less than 30 minutes or something and if it's been more than that it could show a banner. And if it's been more than 3 days it could be removed. But if it's moved to a banner, I feel it would be necessary to have a way to preview and discard the contents. (So you wouldn't have to click on it to have it be filled and then you would have to select all to remove if it wasn't what you expected or something)

Link to post
Share on other sites
  • 2 months later...
18 hours ago, Charles said:

We have made some changes to how auto-save works. These changes will be in 4.1.17.

  • Auto-saved content in the editor expires after 48 hours.
  • When you load an editor that has auto-saved content it will tell you and allow you to clear the editor.

Welcome! tests - A Test Forum - IPS Community Suite 2016-10-06 zhkmu.png

 

It would be nice if this was configurable for admins and even users possibly to be reversed, aka a bar to restore the contents rather than clear.

Link to post
Share on other sites
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...

Important Information

We use technologies, such as cookies, to customise content and advertising, to provide social media features and to analyse traffic to the site. We also share information about your use of our site with our trusted social media, advertising and analytics partners. See more about cookies and our Privacy Policy