From e9c65d7e63341fc560125528f63d0b117c28bd4c Mon Sep 17 00:00:00 2001 From: Martin Cech Date: Thu, 9 Feb 2023 17:16:53 -0800 Subject: [PATCH 1/4] continue blocking dragging through dragleave event --- client/src/composables/fileDrop.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client/src/composables/fileDrop.ts b/client/src/composables/fileDrop.ts index 0d71b92425b..3f5d8370cb9 100644 --- a/client/src/composables/fileDrop.ts +++ b/client/src/composables/fileDrop.ts @@ -68,10 +68,12 @@ export function useFileDrop( ); /** Reset all variables */ - const reset = () => { + const reset = (continueBlock = false) => { isFileOverDocument.value = false; isFileOverDropZone.value = false; - dragBlocked = false; + if (!continueBlock) { + dragBlocked = false; + } hasExited = true; }; @@ -86,7 +88,7 @@ export function useFileDrop( await wait(100); if (hasExited) { - reset(); + reset(dragBlocked); } }); From 65dd5fd4fa8b967f737f3c7fb76f9b1231d2acd6 Mon Sep 17 00:00:00 2001 From: Laila Los <44241786+ElectronicBlueberry@users.noreply.github.com> Date: Wed, 15 Feb 2023 10:17:53 +0100 Subject: [PATCH 2/4] refactor to state machine use timer based approach clear timeout on state change --- client/src/composables/fileDrop.ts | 186 ++++++++++++++--------------- 1 file changed, 88 insertions(+), 98 deletions(-) diff --git a/client/src/composables/fileDrop.ts b/client/src/composables/fileDrop.ts index 3f5d8370cb9..5c281137695 100644 --- a/client/src/composables/fileDrop.ts +++ b/client/src/composables/fileDrop.ts @@ -1,6 +1,5 @@ -import { ref, unref, type Ref } from "vue"; +import { computed, ref, unref, type Ref } from "vue"; import { useEventListener, type MaybeComputedRef } from "@vueuse/core"; -import { wait } from "@/utils/wait"; export type FileDropHandler = (event: DragEvent) => void; @@ -9,116 +8,108 @@ export type FileDropHandler = (event: DragEvent) => void; * @param dropZone Element which files should be dropped on * @param onDrop callback function called when drop occurs * @param solo when true, only reacts if no modal is open + * @param idleTime how long to wait until state resets */ export function useFileDrop( dropZone: MaybeComputedRef, onDrop: Ref | FileDropHandler, - solo: MaybeComputedRef + solo: MaybeComputedRef, + idleTime = 800 ) { - const isFileOverDocument = ref(false); - const isFileOverDropZone = ref(false); - - // blocks drag events in this composable, to avoid drag events in unwanted situations - let dragBlocked = false; - - // keeps track if the drag has exited, to avoid premature drag canceling - let hasExited = true; - - // Don't react to page-internal drag events - useEventListener( - document.body, - "dragstart", - () => { - dragBlocked = true; - }, - true - ); - - useEventListener( - document.body, - "dragover", - (event) => { - if (!dragBlocked) { - // prevent the browser from opening the file - event.preventDefault(); - hasExited = false; - } - }, - true - ); - - useEventListener( - document.body, - "drop", - (event) => { - if (!dragBlocked) { - // prevent the browser from opening the file - event.preventDefault(); - - if (isFileOverDropZone.value && isFileOverDocument.value) { - const dropHandler = unref(onDrop); - dropHandler(event as DragEvent); - } - } - isFileOverDocument.value = false; - dragBlocked = false; - hasExited = true; - }, - true - ); - - /** Reset all variables */ - const reset = (continueBlock = false) => { - isFileOverDocument.value = false; - isFileOverDropZone.value = false; - if (!continueBlock) { - dragBlocked = false; - } - hasExited = true; - }; - - useEventListener(document.body, "dragend", reset, true); - - useEventListener(document.body, "dragleave", async () => { - hasExited = true; - - // This event may have been triggered by components - // which have not been properly childed to the body yet. - // Wait a bit, and check if hasExited is still true. - await wait(100); - - if (hasExited) { - reset(dragBlocked); - } - }); - - useEventListener( - document.body, - "dragenter", - (event) => { - // init values if drag is possible - if (!dragBlocked && !(unref(solo) && isAnyModalOpen())) { - isFileOverDocument.value = true; - isFileOverDropZone.value = false; - hasExited = false; - - event.preventDefault(); - } - }, - true - ); - /** returns if any bootstrap modal is open */ function isAnyModalOpen() { return document.querySelectorAll(".modal.show").length > 0; } + type State = "idle" | "blocked" | "fileDragging"; + type StateMachine = { + [state in State]: (event: MouseEvent) => State; + }; + + const currentState: Ref = ref("idle"); + + let idleTimer: ReturnType | null = null; + const resetTimer = () => { + if (idleTimer) { + clearTimeout(idleTimer); + } + }; + + const stateMachine = { + idle(event: MouseEvent): State { + switch (event.type) { + case "dragstart": + return "blocked"; + case "dragenter": + if (!(unref(solo) && isAnyModalOpen())) { + return "fileDragging"; + } + break; + } + + return "idle"; + }, + blocked(event: MouseEvent): State { + switch (event.type) { + case "drop": + return "idle"; + case "dragend": + return "idle"; + } + + return "blocked"; + }, + fileDragging(event: MouseEvent): State { + resetTimer(); + + switch (event.type) { + case "dragover": + event.preventDefault(); + idleTimer = setTimeout(() => (currentState.value = "idle"), idleTime); + break; + case "drop": + event.preventDefault(); + if (isFileOverDropZone.value) { + const dropHandler = unref(onDrop); + dropHandler(event as DragEvent); + } + return "idle"; + case "dragend": + return "idle"; + } + + return "fileDragging"; + }, + } as const satisfies StateMachine; + + const eventHandler = (event: MouseEvent) => (currentState.value = stateMachine[currentState.value](event)); + + useEventListener(document.body, "dragstart", eventHandler, true); + useEventListener(document.body, "dragover", eventHandler, true); + useEventListener(document.body, "drop", eventHandler, true); + useEventListener(document.body, "dragend", eventHandler, true); + useEventListener(document.body, "dragenter", eventHandler, true); + + const isFileOverDocument = computed({ + get() { + return currentState.value === "fileDragging"; + }, + set(value) { + if (value !== true) { + currentState.value = "idle"; + } else { + currentState.value = "fileDragging"; + } + }, + }); + + const isFileOverDropZone = ref(false); + useEventListener( dropZone, "dragenter", () => { isFileOverDropZone.value = true; - hasExited = false; }, true ); @@ -128,7 +119,6 @@ export function useFileDrop( "dragleave", () => { isFileOverDropZone.value = false; - hasExited = false; }, true ); From 2e2ea7b2efeb04d4148a9126ffd838faee576137 Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Wed, 15 Feb 2023 20:12:01 +0100 Subject: [PATCH 3/4] Enable conversion of bz2 files to gz in converter We'd previously just produce invalid, double-compressed data. Fixes https://github.com/galaxyproject/galaxy/issues/14361 and https://sentry.galaxyproject.org/share/issue/fab8c5f18d1149598c98899f07107f0f/: ``` NotFound: cannot find 'fq1' File "galaxy/jobs/runners/pulsar.py", line 480, in __prepare_job job_wrapper.prepare(**prepare_kwds) File "galaxy/jobs/__init__.py", line 1253, in prepare ) = tool_evaluator.build() File "galaxy/tools/evaluation.py", line 558, in build global_tool_logs(self._build_command_line, config_file, "Building Command Line") File "galaxy/tools/evaluation.py", line 90, in global_tool_logs raise e File "galaxy/tools/evaluation.py", line 86, in global_tool_logs return func() File "galaxy/tools/evaluation.py", line 575, in _build_command_line command_line = fill_template( File "galaxy/util/template.py", line 110, in fill_template raise first_exception or e File "galaxy/util/template.py", line 82, in fill_template return unicodify(t, log_exception=False) File "galaxy/util/__init__.py", line 1184, in unicodify value = str(value) File "Cheetah/Template.py", line 1053, in __unicode__ return getattr(self, mainMethName)() File "cheetah_DynamicallyCompiledCheetahTemplate_1675947468_485589_65108.py", line 102, in respond ``` which happens when people force fastqsanger.bz2.gz files into unicycler, because unicycler does: ``` #if $paired_unpaired.fastq_input1.file_ext in $uncompressed #set fq1 = "fq1.fastq" #elif $paired_unpaired.fastq_input1.file_ext in $compressed #set fq1 = "fq1.fastq.gz" #end if ``` --- .../converters/uncompressed_to_gz.xml | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/galaxy/datatypes/converters/uncompressed_to_gz.xml b/lib/galaxy/datatypes/converters/uncompressed_to_gz.xml index e3b2db365d1..7cbdb7abb6c 100644 --- a/lib/galaxy/datatypes/converters/uncompressed_to_gz.xml +++ b/lib/galaxy/datatypes/converters/uncompressed_to_gz.xml @@ -1,19 +1,27 @@