134 lines
3.4 KiB
TypeScript
134 lines
3.4 KiB
TypeScript
/*
|
|
* Vencord, a Discord client mod
|
|
* Copyright (c) 2026 Vendicated and contributors
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*/
|
|
|
|
import type { DropEntity } from "./utils";
|
|
|
|
type DragSessionState = {
|
|
activeEntity: DropEntity | null;
|
|
activeUserId: string | null;
|
|
activeGuildId: string | null;
|
|
dragSourceIsInput: boolean;
|
|
active: boolean;
|
|
lastDropAt: number;
|
|
lastHandledDrop: { at: number; key: string; };
|
|
lastDragEventAt: number;
|
|
watchdogTimer: number | null;
|
|
guildCleanupTimer: number | null;
|
|
};
|
|
|
|
const dropDedupeWindowMs = 150;
|
|
|
|
const state: DragSessionState = {
|
|
activeEntity: null,
|
|
activeUserId: null,
|
|
activeGuildId: null,
|
|
dragSourceIsInput: false,
|
|
active: false,
|
|
lastDropAt: 0,
|
|
lastHandledDrop: { at: 0, key: "" },
|
|
lastDragEventAt: 0,
|
|
watchdogTimer: null,
|
|
guildCleanupTimer: null,
|
|
};
|
|
|
|
export function beginDrag(entity: DropEntity) {
|
|
cancelGuildCleanup();
|
|
state.activeEntity = entity;
|
|
state.activeUserId = entity.kind === "user" ? entity.id : null;
|
|
state.activeGuildId = entity.kind === "guild" ? entity.id : null;
|
|
state.active = true;
|
|
touchDrag();
|
|
}
|
|
|
|
export function clearDragState() {
|
|
state.activeEntity = null;
|
|
state.activeUserId = null;
|
|
state.activeGuildId = null;
|
|
state.dragSourceIsInput = false;
|
|
state.active = false;
|
|
}
|
|
|
|
export function markInputDragSource() {
|
|
clearDragState();
|
|
state.dragSourceIsInput = true;
|
|
}
|
|
|
|
export function isInputDragSource() {
|
|
return state.dragSourceIsInput;
|
|
}
|
|
|
|
export function touchDrag() {
|
|
state.lastDragEventAt = Date.now();
|
|
}
|
|
|
|
export function getLastDropAt() {
|
|
return state.lastDropAt;
|
|
}
|
|
|
|
export function markDrop() {
|
|
state.lastDropAt = Date.now();
|
|
}
|
|
|
|
export function shouldIgnoreDrop(key: string) {
|
|
const now = Date.now();
|
|
if (state.lastHandledDrop.key === key && now - state.lastHandledDrop.at < dropDedupeWindowMs) return true;
|
|
state.lastHandledDrop = { key, at: now };
|
|
return false;
|
|
}
|
|
|
|
export function hasActiveDrag() {
|
|
return state.active || state.activeEntity !== null || state.activeUserId !== null || state.activeGuildId !== null;
|
|
}
|
|
|
|
export function isGuildDragActive() {
|
|
return state.activeGuildId !== null || state.activeEntity?.kind === "guild";
|
|
}
|
|
|
|
export function isUserDragActive() {
|
|
return state.activeUserId !== null || state.activeEntity?.kind === "user";
|
|
}
|
|
|
|
export function getActiveEntity() {
|
|
return state.activeEntity;
|
|
}
|
|
|
|
export function startDragWatchdog(onExpire: () => void) {
|
|
if (state.watchdogTimer !== null) return;
|
|
state.watchdogTimer = window.setInterval(() => {
|
|
if (!state.active) return;
|
|
if (Date.now() - state.lastDragEventAt < 1200) return;
|
|
clearDragState();
|
|
onExpire();
|
|
}, 500);
|
|
}
|
|
|
|
export function stopDragWatchdog() {
|
|
if (state.watchdogTimer !== null) {
|
|
clearInterval(state.watchdogTimer);
|
|
state.watchdogTimer = null;
|
|
}
|
|
}
|
|
|
|
export function scheduleGuildCleanup(onExpire: () => void) {
|
|
cancelGuildCleanup();
|
|
state.guildCleanupTimer = window.setTimeout(() => {
|
|
if (Date.now() - state.lastDragEventAt < 200) return;
|
|
clearDragState();
|
|
onExpire();
|
|
}, 300);
|
|
}
|
|
|
|
export function cancelGuildCleanup() {
|
|
if (state.guildCleanupTimer === null) return;
|
|
clearTimeout(state.guildCleanupTimer);
|
|
state.guildCleanupTimer = null;
|
|
}
|
|
|
|
export function stopDragState() {
|
|
cancelGuildCleanup();
|
|
stopDragWatchdog();
|
|
clearDragState();
|
|
}
|