From 3bdf16aa494aede5479ed63ec53e93f5984096e2 Mon Sep 17 00:00:00 2001 From: bytedream Date: Thu, 11 Dec 2025 14:26:23 +0100 Subject: [PATCH 1/4] Show edit page confirmation dialog on tree view file change --- web_src/js/components/ViewFileTreeItem.vue | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/web_src/js/components/ViewFileTreeItem.vue b/web_src/js/components/ViewFileTreeItem.vue index 9a50adedaac7f..093cf5730320e 100644 --- a/web_src/js/components/ViewFileTreeItem.vue +++ b/web_src/js/components/ViewFileTreeItem.vue @@ -37,9 +37,10 @@ const doLoadChildren = async () => { }; const onItemClick = (e: MouseEvent) => { - // only handle the click event with page partial reloading if the user didn't press any special key - // let browsers handle special keys like "Ctrl+Click" - if (!isPlainClick(e)) return; + // only handle the click event with partial page reloading if both + // - the user didn't press any special key like "Ctrl+Click" (which may have custom browser behavior) + // - the editor/commit form isn't dirty (a full page reload shows a confirmation dialog if the form contains unsaved changes) + if (!isPlainClick(e) || document.querySelector('.repo-view-content .form.dirty')) return; e.preventDefault(); if (props.item.entryMode === 'tree') doLoadChildren(); store.navigateTreeView(props.item.fullPath); From f3a0ade0419a6e2ea5785893ba9e3dfcd2e3b13d Mon Sep 17 00:00:00 2001 From: bytedream Date: Thu, 18 Dec 2025 13:15:24 +0100 Subject: [PATCH 2/4] move dirty check to own function --- web_src/js/components/ViewFileTreeItem.vue | 3 +- web_src/js/vendor/jquery.are-you-sure.ts | 32 +++++++++++++--------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/web_src/js/components/ViewFileTreeItem.vue b/web_src/js/components/ViewFileTreeItem.vue index 093cf5730320e..abf2692f5c03f 100644 --- a/web_src/js/components/ViewFileTreeItem.vue +++ b/web_src/js/components/ViewFileTreeItem.vue @@ -3,6 +3,7 @@ import {SvgIcon} from '../svg.ts'; import {isPlainClick} from '../utils/dom.ts'; import {shallowRef} from 'vue'; import {type createViewFileTreeStore} from './ViewFileTreeStore.ts'; +import {triggersAreYouSure} from '../vendor/jquery.are-you-sure.ts'; export type Item = { entryName: string; @@ -40,7 +41,7 @@ const onItemClick = (e: MouseEvent) => { // only handle the click event with partial page reloading if both // - the user didn't press any special key like "Ctrl+Click" (which may have custom browser behavior) // - the editor/commit form isn't dirty (a full page reload shows a confirmation dialog if the form contains unsaved changes) - if (!isPlainClick(e) || document.querySelector('.repo-view-content .form.dirty')) return; + if (!isPlainClick(e) || triggersAreYouSure()) return; e.preventDefault(); if (props.item.entryMode === 'tree') doLoadChildren(); store.navigateTreeView(props.item.fullPath); diff --git a/web_src/js/vendor/jquery.are-you-sure.ts b/web_src/js/vendor/jquery.are-you-sure.ts index 51359c87a70cf..fba091023db95 100644 --- a/web_src/js/vendor/jquery.are-you-sure.ts +++ b/web_src/js/vendor/jquery.are-you-sure.ts @@ -3,6 +3,8 @@ // * use export to make it work with ES6 modules. // * the addition of `const` to make it strict mode compatible. // * ignore forms with "ignore-dirty" class, ignore hidden forms (closest('.tw-hidden')) +// * extract the default options to make it available to other functions +// * check if any form is dirty /*! * jQuery Plugin: Are-You-Sure (Dirty Form Detection) @@ -16,20 +18,21 @@ * Version: 1.9.0 * Date: 13th August 2014 */ +const defaultOptions = { + 'message': 'You have unsaved changes!', + 'dirtyClass': 'dirty', + 'change': null, + 'silent': false, + 'addRemoveFieldsMarksDirty': false, + 'fieldEvents': 'change keyup propertychange input', + 'fieldSelector': ":input:not(input[type=submit]):not(input[type=button])" +}; + export function initAreYouSure($) { $.fn.areYouSure = function(options) { - var settings = $.extend( - { - 'message' : 'You have unsaved changes!', - 'dirtyClass' : 'dirty', - 'change' : null, - 'silent' : false, - 'addRemoveFieldsMarksDirty' : false, - 'fieldEvents' : 'change keyup propertychange input', - 'fieldSelector': ":input:not(input[type=submit]):not(input[type=button])" - }, options); + var settings = $.extend({}, defaultOptions, options); var getValue = function($field) { if ($field.hasClass('ays-ignore') @@ -162,9 +165,7 @@ export function initAreYouSure($) { if (!settings.silent && !window.aysUnloadSet) { window.aysUnloadSet = true; $(window).bind('beforeunload', function() { - const $forms = $("form:not(.ignore-dirty)").filter('.' + settings.dirtyClass); - const dirtyFormCount = Array.from($forms).reduce((res, form) => form.closest('.tw-hidden') ? res : res + 1, 0); - if (dirtyFormCount === 0) return; + if (!triggersAreYouSure(settings)) return; // Prevent multiple prompts - seen on Chrome and IE if (navigator.userAgent.toLowerCase().match(/msie|chrome/)) { @@ -210,3 +211,8 @@ export function ignoreAreYouSure(selectorOrEl: string|Element|$) { // because when using "enter" to submit a form, the "dirty" class will appear again before reloading. $(selectorOrEl).addClass('ignore-dirty'); } + +export function triggersAreYouSure(opts = {}) { + const $forms = $('form:not(.ignore-dirty)').filter('.' + (opts.dirtyClass ?? defaultOptions.dirtyClass)); + return Array.from($forms).some((form) => form.closest('.tw-hidden') === null); +} From 6b7856c6a7602fbded0176cd30382060fee90ad4 Mon Sep 17 00:00:00 2001 From: bytedream Date: Thu, 18 Dec 2025 13:23:12 +0100 Subject: [PATCH 3/4] move import --- web_src/js/components/ViewFileTreeItem.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/components/ViewFileTreeItem.vue b/web_src/js/components/ViewFileTreeItem.vue index 962dbd856f14c..ba804fbe27243 100644 --- a/web_src/js/components/ViewFileTreeItem.vue +++ b/web_src/js/components/ViewFileTreeItem.vue @@ -1,9 +1,9 @@