diff --git a/server/app/templates/index.html b/server/app/templates/index.html
index e31d6af..ef6a415 100644
--- a/server/app/templates/index.html
+++ b/server/app/templates/index.html
@@ -27,7 +27,14 @@
.thumb { width: 160px; max-width: 100%; border-radius: 4px; display: block; }
.photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); gap: 10px; margin-top: 8px; }
- .photo-card { position: relative; cursor: grab; border-radius: 6px; overflow: hidden; aspect-ratio: 1; background: #f3f4f6; border: 1px solid #e5e7eb; }
+ .photo-card {
+ position: relative; cursor: grab; border-radius: 6px; overflow: hidden; aspect-ratio: 1;
+ background: #f3f4f6; border: 1px solid #e5e7eb;
+ /* Touching a card is what starts a drag -- without this the browser
+ treats that touch as the start of a page scroll instead, and
+ pointermove events for the drag never arrive on mobile. */
+ touch-action: none;
+ }
.photo-card:active { cursor: grabbing; }
.photo-card.dragging { opacity: 0.35; }
.photo-card.drag-over { outline: 3px solid #2563eb; outline-offset: -3px; }
@@ -158,7 +165,25 @@
});
let upcomingItems = [];
- let dragFromIndex = null;
+
+ // Pointer Events (not the native HTML5 Drag-and-Drop API) so the same
+ // code drives mouse, touch, and pen -- native drag-and-drop is
+ // mouse-only by spec and never fires at all on phones/tablets.
+ let dragState = null; // { pointerId, fromIndex, toIndex, moved }
+ const DRAG_START_THRESHOLD_PX = 6; // ignore tiny jitter from an imprecise tap
+
+ function clearDragOverStyling() {
+ document.querySelectorAll('.photo-card.drag-over').forEach((el) => el.classList.remove('drag-over'));
+ }
+
+ function endDrag(card) {
+ card.classList.remove('dragging');
+ clearDragOverStyling();
+ if (dragState && dragState.moved && dragState.toIndex !== undefined && dragState.toIndex !== dragState.fromIndex) {
+ moveItem(dragState.fromIndex, dragState.toIndex);
+ }
+ dragState = null;
+ }
function renderUpcoming(items) {
upcomingItems = items;
@@ -168,7 +193,7 @@
items.forEach((item, i) => {
const card = document.createElement('div');
card.className = 'photo-card';
- card.draggable = true;
+ card.dataset.index = String(i);
const img = document.createElement('img');
img.src = item.thumbnail_url;
@@ -190,25 +215,48 @@
});
card.appendChild(nextBtn);
- card.addEventListener('dragstart', (e) => {
- dragFromIndex = i;
- card.classList.add('dragging');
- e.dataTransfer.effectAllowed = 'move';
+ card.addEventListener('pointerdown', (e) => {
+ if (e.target.closest('.show-next')) {
+ return; // let the button's own click handler run, don't start a drag
+ }
+ if (e.pointerType === 'mouse' && e.button !== 0) {
+ return; // left button only
+ }
+ dragState = { pointerId: e.pointerId, fromIndex: i, toIndex: undefined, moved: false, startX: e.clientX, startY: e.clientY };
+ card.setPointerCapture(e.pointerId);
});
- card.addEventListener('dragend', () => {
- card.classList.remove('dragging');
- dragFromIndex = null;
+
+ card.addEventListener('pointermove', (e) => {
+ if (!dragState || dragState.pointerId !== e.pointerId) {
+ return;
+ }
+ if (!dragState.moved) {
+ const dx = e.clientX - dragState.startX;
+ const dy = e.clientY - dragState.startY;
+ if (Math.hypot(dx, dy) < DRAG_START_THRESHOLD_PX) {
+ return;
+ }
+ dragState.moved = true;
+ card.classList.add('dragging');
+ }
+ const overCard = document.elementFromPoint(e.clientX, e.clientY)?.closest('.photo-card');
+ clearDragOverStyling();
+ if (overCard && overCard !== card) {
+ overCard.classList.add('drag-over');
+ dragState.toIndex = Number(overCard.dataset.index);
+ } else {
+ dragState.toIndex = undefined;
+ }
});
- card.addEventListener('dragover', (e) => {
- e.preventDefault();
- card.classList.add('drag-over');
+
+ card.addEventListener('pointerup', (e) => {
+ if (dragState && dragState.pointerId === e.pointerId) {
+ endDrag(card);
+ }
});
- card.addEventListener('dragleave', () => card.classList.remove('drag-over'));
- card.addEventListener('drop', (e) => {
- e.preventDefault();
- card.classList.remove('drag-over');
- if (dragFromIndex !== null && dragFromIndex !== i) {
- moveItem(dragFromIndex, i);
+ card.addEventListener('pointercancel', (e) => {
+ if (dragState && dragState.pointerId === e.pointerId) {
+ endDrag(card);
}
});