fix: photo/note reordering

This commit is contained in:
2026-04-07 15:49:49 +03:00
parent 11cb7aa233
commit ba5dd33c7b
4 changed files with 21 additions and 13 deletions

View File

@@ -43,11 +43,12 @@ export function useNotes(houseId: number) {
}
async function reorder(items: { id: number; sortOrder: number }[]): Promise<void> {
await api.reorderNotes(houseId, items)
// Apply optimistically so there's no visual jump while the API call is in flight.
const map = new Map(items.map((i) => [i.id, i.sortOrder]))
notes.value = notes.value
.map((n) => (map.has(n.id) ? { ...n, sortOrder: map.get(n.id)! } : n))
.sort((a, b) => a.sortOrder - b.sortOrder)
await api.reorderNotes(houseId, items)
}
return { notes, loading, error, load, create, update, remove, reorder }

View File

@@ -128,9 +128,9 @@ describe('usePhotos', () => {
const result = await wall.upload(file, 5)
expect(mockApi.uploadPhoto).toHaveBeenCalledWith(1, file, 5, null, expect.any(Function))
expect(result).toEqual(newPhoto)
expect(result.id).toBe(newPhoto.id)
expect(wall.photos.value).toHaveLength(1)
expect(wall.photos.value[0]).toEqual(newPhoto)
expect(wall.photos.value[0].id).toBe(newPhoto.id)
})
})

View File

@@ -33,13 +33,13 @@ export function usePhotos(houseId: number) {
}
const rootPhotos = computed(() =>
photos.value.filter((p) => p.folderId === null).sort((a, b) => b.createdAt - a.createdAt),
photos.value.filter((p) => p.folderId === null).sort((a, b) => a.sortOrder - b.sortOrder),
)
function photosInFolder(folderId: number): Photo[] {
return photos.value
.filter((p) => p.folderId === folderId)
.sort((a, b) => b.createdAt - a.createdAt)
.sort((a, b) => a.sortOrder - b.sortOrder)
}
// ----- Photos -----
@@ -56,8 +56,12 @@ export function usePhotos(houseId: number) {
const created = await api.uploadPhoto(houseId, file, folderId, null, (progress) => {
uploads.value = uploads.value.map((u) => (u.id === entry.id ? { ...u, progress } : u))
})
photos.value = [...photos.value, created]
return created
// Place new photo first by giving it a sortOrder below the current minimum.
const siblings = photos.value.filter((p) => p.folderId === (folderId ?? null))
const minSort = siblings.length > 0 ? Math.min(...siblings.map((p) => p.sortOrder)) : 0
const placed = { ...created, sortOrder: minSort - 1 }
photos.value = [...photos.value, placed]
return placed
} finally {
uploads.value = uploads.value.filter((u) => u.id !== entry.id)
}
@@ -77,12 +81,12 @@ export function usePhotos(houseId: number) {
}
async function reorderPhotos(items: { id: number; sortOrder: number }[]): Promise<void> {
await api.reorderPhotos(houseId, items)
// Apply locally
// Apply optimistically so there's no visual jump while the API call is in flight.
const map = new Map(items.map((i) => [i.id, i.sortOrder]))
photos.value = photos.value
.map((p) => (map.has(p.id) ? { ...p, sortOrder: map.get(p.id)! } : p))
.sort((a, b) => a.sortOrder - b.sortOrder)
await api.reorderPhotos(houseId, items)
}
// ----- Folders -----
@@ -109,11 +113,11 @@ export function usePhotos(houseId: number) {
}
async function reorderFolders(items: { id: number; sortOrder: number }[]): Promise<void> {
await api.reorderFolders(houseId, items)
const map = new Map(items.map((i) => [i.id, i.sortOrder]))
folders.value = folders.value
.map((f) => (map.has(f.id) ? { ...f, sortOrder: map.get(f.id)! } : f))
.sort((a, b) => a.sortOrder - b.sortOrder)
await api.reorderFolders(houseId, items)
}
return {

View File

@@ -174,18 +174,21 @@ async function commitReorder() {
await reorder(items)
}
// Capture-phase listeners
// Capture-phase listeners — commit the reorder on drop, reset on dragend.
function onDropCapture() {
commitReorder()
}
function onDragEndCapture() {
draggingNoteId.value = null
dropIndex.value = null
}
onMounted(() => {
wallRef.value?.addEventListener('drop', onDropCapture, true)
wallRef.value?.addEventListener('dragend', onDropCapture, true)
wallRef.value?.addEventListener('dragend', onDragEndCapture, true)
})
onBeforeUnmount(() => {
wallRef.value?.removeEventListener('drop', onDropCapture, true)
wallRef.value?.removeEventListener('dragend', onDropCapture, true)
wallRef.value?.removeEventListener('dragend', onDragEndCapture, true)
})
// ----- Create / Edit -----