feat(ui): modal prev/next walks the current gallery filter (#1322)
The image modal cycled GLOBAL neighbours; now the gallery hands it a snapshot of the currently-filtered, ordered id list so prev/next moves through exactly what you're viewing — the filtered-playlist behaviour lost in the ImageRepo→FC move. Generalized the modal store's post-scoped cycle into a `playlistIds` playlist reused by both GalleryView and PostCard (falls back to global neighbours when no playlist is passed, e.g. Explore's "open full viewer"). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
This commit is contained in:
@@ -200,13 +200,13 @@ async function fullImageIds () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function openModal (imageId) {
|
async function openModal (imageId) {
|
||||||
modal.open(imageId, { postImageIds: await fullImageIds() })
|
modal.open(imageId, { playlistIds: await fullImageIds() })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openModalAtMore () {
|
async function openModalAtMore () {
|
||||||
const ids = await fullImageIds()
|
const ids = await fullImageIds()
|
||||||
const first = ids[visibleCount.value] ?? ids[0]
|
const first = ids[visibleCount.value] ?? ids[0]
|
||||||
if (first != null) modal.open(first, { postImageIds: ids })
|
if (first != null) modal.open(first, { playlistIds: ids })
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- description "Show more" (text-only, in place, only when truncated) ----
|
// --- description "Show more" (text-only, in place, only when truncated) ----
|
||||||
|
|||||||
@@ -17,12 +17,13 @@ export const useModalStore = defineStore('modal', () => {
|
|||||||
// chip rail. Audit 2026-06-02.
|
// chip rail. Audit 2026-06-02.
|
||||||
const inflight = useInflightToken()
|
const inflight = useInflightToken()
|
||||||
|
|
||||||
// Post-scoped cycle. When set, prev/next cycles within this array
|
// Scoped playlist. When set, prev/next cycles within THIS ordered id array —
|
||||||
// (used by PostCard image clicks — the modal is scoped to that post's
|
// the current gallery filter (GalleryView) or a post's images (PostCard) — so
|
||||||
// images). When null, prev/next falls back to current.value.neighbors
|
// the modal walks exactly what the user was looking at, not a global order.
|
||||||
// (the gallery-store-driven /api/gallery/image/<id> neighbors).
|
// When null, prev/next falls back to current.value.neighbors (the
|
||||||
const postImageIds = ref(null)
|
// /api/gallery/image/<id> global neighbours).
|
||||||
const postImageIndex = ref(0)
|
const playlistIds = ref(null)
|
||||||
|
const playlistIndex = ref(0)
|
||||||
|
|
||||||
async function open (id, opts = {}) {
|
async function open (id, opts = {}) {
|
||||||
// Cancel any in-flight tag mutation or reloadTags from the
|
// Cancel any in-flight tag mutation or reloadTags from the
|
||||||
@@ -32,13 +33,13 @@ export const useModalStore = defineStore('modal', () => {
|
|||||||
current.value = null // cleared upfront so it stays null on error
|
current.value = null // cleared upfront so it stays null on error
|
||||||
// Update post-scoped state if caller passed it; otherwise clear so
|
// Update post-scoped state if caller passed it; otherwise clear so
|
||||||
// the next open() from gallery context uses neighbors mode.
|
// the next open() from gallery context uses neighbors mode.
|
||||||
if (opts.postImageIds != null) {
|
if (opts.playlistIds != null) {
|
||||||
postImageIds.value = opts.postImageIds
|
playlistIds.value = opts.playlistIds
|
||||||
postImageIndex.value = opts.postImageIds.indexOf(id)
|
playlistIndex.value = opts.playlistIds.indexOf(id)
|
||||||
if (postImageIndex.value < 0) postImageIndex.value = 0
|
if (playlistIndex.value < 0) playlistIndex.value = 0
|
||||||
} else if (opts.clearPostScope !== false && postImageIds.value != null) {
|
} else if (opts.clearPlaylist !== false && playlistIds.value != null) {
|
||||||
postImageIds.value = null
|
playlistIds.value = null
|
||||||
postImageIndex.value = 0
|
playlistIndex.value = 0
|
||||||
}
|
}
|
||||||
const t = inflight.claim()
|
const t = inflight.claim()
|
||||||
await run(async () => {
|
await run(async () => {
|
||||||
@@ -53,17 +54,17 @@ export const useModalStore = defineStore('modal', () => {
|
|||||||
currentImageId.value = null
|
currentImageId.value = null
|
||||||
current.value = null
|
current.value = null
|
||||||
error.value = null
|
error.value = null
|
||||||
postImageIds.value = null
|
playlistIds.value = null
|
||||||
postImageIndex.value = 0
|
playlistIndex.value = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
async function goPrev () {
|
async function goPrev () {
|
||||||
if (postImageIds.value != null) {
|
if (playlistIds.value != null) {
|
||||||
if (postImageIndex.value > 0) {
|
if (playlistIndex.value > 0) {
|
||||||
const newIdx = postImageIndex.value - 1
|
const newIdx = playlistIndex.value - 1
|
||||||
const newId = postImageIds.value[newIdx]
|
const newId = playlistIds.value[newIdx]
|
||||||
postImageIndex.value = newIdx
|
playlistIndex.value = newIdx
|
||||||
await open(newId, { postImageIds: postImageIds.value })
|
await open(newId, { playlistIds: playlistIds.value })
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -73,12 +74,12 @@ export const useModalStore = defineStore('modal', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function goNext () {
|
async function goNext () {
|
||||||
if (postImageIds.value != null) {
|
if (playlistIds.value != null) {
|
||||||
if (postImageIndex.value < postImageIds.value.length - 1) {
|
if (playlistIndex.value < playlistIds.value.length - 1) {
|
||||||
const newIdx = postImageIndex.value + 1
|
const newIdx = playlistIndex.value + 1
|
||||||
const newId = postImageIds.value[newIdx]
|
const newId = playlistIds.value[newIdx]
|
||||||
postImageIndex.value = newIdx
|
playlistIndex.value = newIdx
|
||||||
await open(newId, { postImageIds: postImageIds.value })
|
await open(newId, { playlistIds: playlistIds.value })
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -177,19 +178,19 @@ export const useModalStore = defineStore('modal', () => {
|
|||||||
|
|
||||||
const isOpen = computed(() => currentImageId.value !== null)
|
const isOpen = computed(() => currentImageId.value !== null)
|
||||||
const canPrev = computed(() => {
|
const canPrev = computed(() => {
|
||||||
if (postImageIds.value != null) return postImageIndex.value > 0
|
if (playlistIds.value != null) return playlistIndex.value > 0
|
||||||
return current.value?.neighbors?.prev_id != null
|
return current.value?.neighbors?.prev_id != null
|
||||||
})
|
})
|
||||||
const canNext = computed(() => {
|
const canNext = computed(() => {
|
||||||
if (postImageIds.value != null) {
|
if (playlistIds.value != null) {
|
||||||
return postImageIndex.value < (postImageIds.value.length - 1)
|
return playlistIndex.value < (playlistIds.value.length - 1)
|
||||||
}
|
}
|
||||||
return current.value?.neighbors?.next_id != null
|
return current.value?.neighbors?.next_id != null
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentImageId, current, loading, error,
|
currentImageId, current, loading, error,
|
||||||
postImageIds, postImageIndex,
|
playlistIds, playlistIndex,
|
||||||
isOpen, canPrev, canNext,
|
isOpen, canPrev, canNext,
|
||||||
open, close, goPrev, goNext,
|
open, close, goPrev, goNext,
|
||||||
reloadTags, removeTag, addExistingTag, createAndAdd,
|
reloadTags, removeTag, addExistingTag, createAndAdd,
|
||||||
|
|||||||
@@ -54,7 +54,10 @@ watch(() => route.query, (q) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
function openImage(id) {
|
function openImage(id) {
|
||||||
modal.open(id)
|
// Walk the current gallery filter in the modal (#1322) — prev/next moves
|
||||||
|
// through exactly the filtered set the operator is viewing, not global
|
||||||
|
// neighbours. Snapshot of the currently-loaded, filtered, ordered ids.
|
||||||
|
modal.open(id, { playlistIds: store.images.map((i) => i.id) })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user