Skip to content

Commit

Permalink
create a reusable historyDragDrop composable
Browse files Browse the repository at this point in the history
This unifies the drag and drop operations in `HistoryPanel` as well as the drop area in the `MultipleViewList`.
  • Loading branch information
ahmedhamidawan committed Aug 21, 2024
1 parent 989b769 commit f65df80
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 193 deletions.
131 changes: 5 additions & 126 deletions client/src/components/History/CurrentHistory/HistoryPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,21 @@ import { BAlert } from "bootstrap-vue";
import { storeToRefs } from "pinia";
import { computed, onMounted, type Ref, ref, set as VueSet, unref, watch } from "vue";
import {
type DCEDataset,
type HistoryItemSummary,
type HistorySummaryExtended,
isDatasetElement,
isHistoryItem,
userOwnsHistory,
} from "@/api";
import { copyDataset, type HistoryContentSource, type HistoryContentType } from "@/api/datasets";
import { type HistoryItemSummary, type HistorySummaryExtended, userOwnsHistory } from "@/api";
import ExpandedItems from "@/components/History/Content/ExpandedItems";
import SelectedItems from "@/components/History/Content/SelectedItems";
import { HistoryFilters } from "@/components/History/HistoryFilters";
import { deleteContent, updateContentFields } from "@/components/History/model/queries";
import { Toast } from "@/composables/toast";
import { useActiveElement } from "@/composables/useActiveElement";
import { startWatchingHistory } from "@/store/historyStore/model/watchHistory";
import { useEventStore } from "@/stores/eventStore";
import { useHistoryItemsStore } from "@/stores/historyItemsStore";
import { useHistoryStore } from "@/stores/historyStore";
import { useUserStore } from "@/stores/userStore";
import { type Alias, getOperatorForAlias } from "@/utils/filtering";
import { setItemDragstart } from "@/utils/setDrag";
import { useHistoryDragDrop } from "../../../composables/historyDragDrop";
import HistoryCounter from "./HistoryCounter.vue";
import HistoryDetails from "./HistoryDetails.vue";
import HistoryDropZone from "./HistoryDropZone.vue";
Expand Down Expand Up @@ -61,8 +53,6 @@ interface Props {
isMultiViewItem?: boolean;
}
type DraggableHistoryItem = HistoryItemSummary | DCEDataset; // TODO: DCESummary instead of DCEDataset
type ContentItemRef = Record<string, Ref<InstanceType<typeof ContentItem> | null>>;
const props = withDefaults(defineProps<Props>(), {
Expand All @@ -79,11 +69,9 @@ const isLoading = ref(false);
const offsetQueryParam = ref(0);
const searchError = ref<BackendFilterError | undefined>(undefined);
const showAdvanced = ref(false);
const showDropZone = ref(false);
const operationRunning = ref<string | null>(null);
const operationError = ref(null);
const querySelectionBreak = ref(false);
const dragTarget = ref<EventTarget | null>(null);
const contentItemRefs = computed(() => {
return historyItems.value.reduce((acc: ContentItemRef, item) => {
acc[itemUniqueKey(item)] = ref(null);
Expand All @@ -100,6 +88,8 @@ const historyStore = useHistoryStore();
const historyItemsStore = useHistoryItemsStore();
const { currentUser } = storeToRefs(useUserStore());
const { showDropZone, onDragEnter, onDragLeave, onDragOver, onDrop } = useHistoryDragDrop(props.history.id);
const currentUserOwnsHistory = computed(() => {
return userOwnsHistory(currentUser.value, props.history);
});
Expand Down Expand Up @@ -228,28 +218,6 @@ watch(
}
);
function dragSameHistory() {
return getDragData().sameHistory;
}
function getDragData() {
const eventStore = useEventStore();
const dragItems = eventStore.getDragItems();
// Filter out any non-history items
// TODO: `isDCE` instead of `isDatasetElement`
const historyItems = dragItems?.filter(
(item: any) => isHistoryItem(item) || isDatasetElement(item)
) as DraggableHistoryItem[];
// TODO: handle historyId === null || historyItems.length === 0
const historyId = historyItems[0]
? isHistoryItem(historyItems[0])
? historyItems[0].history_id
: historyItems[0].object?.history_id
: null;
return { data: historyItems, sameHistory: historyId === props.history.id, multiple: historyItems?.length > 1 };
}
function getHighlight(item: HistoryItemSummary) {
if (unref(isLoading)) {
return undefined;
Expand Down Expand Up @@ -363,95 +331,6 @@ function onOperationError(error: any) {
operationError.value = error;
}
function onDragEnter(e: DragEvent) {
if (dragSameHistory()) {
return;
}
dragTarget.value = e.target;
showDropZone.value = true;
}
function onDragOver(e: DragEvent) {
if (dragSameHistory()) {
return;
}
e.preventDefault();
}
function onDragLeave(e: DragEvent) {
if (dragSameHistory()) {
return;
}
if (dragTarget.value === e.target) {
showDropZone.value = false;
}
}
async function onDrop() {
showDropZone.value = false;
const { data, sameHistory, multiple } = getDragData();
if (!data || sameHistory) {
return;
}
let datasetCount = 0;
let collectionCount = 0;
try {
// iterate over the data array and copy each item to the current history
for (const item of data) {
let dataSource: HistoryContentSource;
let type: HistoryContentType;
let id: string;
if (isHistoryItem(item)) {
dataSource = item.history_content_type === "dataset" ? "hda" : "hdca";
type = item.history_content_type;
id = item.id;
}
// TEMPORARY: fix this when DCEs are handled correctly, unify like commented out code below
else if (isDatasetElement(item) && item.object) {
dataSource = "hda";
type = "dataset";
id = item.object.id;
}
/** TODO: Handle DCE, `DCEDataset`s work fine as they are HDAs,
* `DCECollection`s are `dataset_collection`s and need to be HDCAs...
*/
// else if (isDCE(item) && (item as DCESummary).object) {
// const collectionElement = item as DCESummary;
// dataSource = collectionElement.element_type === "dataset_collection" ? "hdca" : "hda"; // incorrect...
// type = collectionElement.element_type === "dataset_collection" ? "dataset_collection" : "dataset";
// id = collectionElement.object.id as string;
// }
else {
throw new Error(`Invalid item type${item.element_type ? `: ${item.element_type}` : ""}`);
}
await copyDataset(id, props.history.id, type, dataSource);
if (dataSource === "hda") {
datasetCount++;
if (!multiple) {
Toast.info("Dataset copied to history");
}
} else {
collectionCount++;
if (!multiple) {
Toast.info("Collection copied to history");
}
}
}
if (multiple && datasetCount > 0) {
Toast.info(`${datasetCount} dataset${datasetCount > 1 ? "s" : ""} copied to new history`);
}
if (multiple && collectionCount > 0) {
Toast.info(`${collectionCount} collection${collectionCount > 1 ? "s" : ""} copied to new history`);
}
historyStore.loadHistoryById(props.history.id);
} catch (error) {
Toast.error(`${error}`);
}
}
function updateFilterValue(filterKey: string, newValue: any) {
const currentFilterText = filterText.value;
filterText.value = filterClass.setFilterValue(currentFilterText, filterKey, newValue);
Expand Down
73 changes: 6 additions & 67 deletions client/src/components/History/Multiple/MultipleViewList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@ import { computed, type Ref, ref } from "vue";
//@ts-ignore missing typedefs
import VirtualList from "vue-virtual-scroll-list";
import { HistoryItemSummary, isHistoryItem } from "@/api";
import { copyDataset } from "@/api/datasets";
import { useAnimationFrameResizeObserver } from "@/composables/sensors/animationFrameResizeObserver";
import { useAnimationFrameScroll } from "@/composables/sensors/animationFrameScroll";
import { Toast } from "@/composables/toast";
import { useEventStore } from "@/stores/eventStore";
import { useHistoryStore } from "@/stores/historyStore";
import localize from "@/utils/localization";
import { errorMessageAsString } from "@/utils/simple-error";
import { useHistoryDragDrop } from "../../../composables/historyDragDrop";
import HistoryDropZone from "../CurrentHistory/HistoryDropZone.vue";
import MultipleViewItem from "./MultipleViewItem.vue";
Expand Down Expand Up @@ -65,67 +64,7 @@ async function createAndPin() {
}
}
const showDropZone = ref(false);
const processingDrop = ref(false);
async function onDrop(evt: any) {
const eventStore = useEventStore();
if (processingDrop.value) {
showDropZone.value = false;
return;
}
processingDrop.value = true;
showDropZone.value = false;
const dragItems = eventStore.getDragItems();
// Filter out any non-history items
const historyItems = dragItems?.filter((item: any) => isHistoryItem(item)) as HistoryItemSummary[];
const multiple = historyItems.length > 1;
const originalHistoryId = historyItems?.[0]?.history_id;
if (historyItems && originalHistoryId) {
await historyStore.createNewHistory();
const currentHistoryId = historyStore.currentHistoryId;
let datasetCount = 0;
let collectionCount = 0;
if (currentHistoryId) {
// iterate over the data array and copy each item to the new history
for (const item of historyItems) {
const dataSource = item.history_content_type === "dataset" ? "hda" : "hdca";
await copyDataset(item.id, currentHistoryId, item.history_content_type, dataSource)
.then(() => {
if (item.history_content_type === "dataset") {
datasetCount++;
if (!multiple) {
Toast.info(localize("Dataset copied to new history"));
}
} else {
collectionCount++;
if (!multiple) {
Toast.info(localize("Collection copied to new history"));
}
}
})
.catch((error) => {
Toast.error(errorMessageAsString(error));
});
}
if (multiple && datasetCount > 0) {
Toast.info(`${datasetCount} dataset${datasetCount > 1 ? "s" : ""} copied to new history`);
}
if (multiple && collectionCount > 0) {
Toast.info(`${collectionCount} collection${collectionCount > 1 ? "s" : ""} copied to new history`);
}
if (historyStore.pinnedHistories.length > 0) {
// pin the newly created history via the drop
historyStore.pinHistory(currentHistoryId);
// also pin the original history where the item came from
historyStore.pinHistory(originalHistoryId);
}
}
processingDrop.value = false;
}
}
const { showDropZone, onDragEnter, onDragLeave, onDragOver, onDrop } = useHistoryDragDrop(undefined, true, true);
async function onKeyDown(evt: KeyboardEvent) {
if (evt.key === "Enter" || evt.key === " ") {
Expand Down Expand Up @@ -159,9 +98,9 @@ async function onKeyDown(evt: KeyboardEvent) {
<div
class="history-picker"
@drop.prevent="onDrop"
@dragenter.prevent="showDropZone = true"
@dragover.prevent
@dragleave.prevent="showDropZone = false">
@dragenter.prevent="onDragEnter"
@dragover="onDragOver"
@dragleave.prevent="onDragLeave">
<span v-if="!showDropZone" class="d-flex flex-column h-100">
<div
class="history-picker-box top-picker text-primary"
Expand Down
Loading

0 comments on commit f65df80

Please sign in to comment.