fix(products): keep newly uploaded image when SSE list is stale

The live product stream was replacing the catalog with an older payload, so the new photo flashed then jumped back. Keep the newer file_id and stop resetting the img on unrelated price ticks.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-25 16:14:49 +03:30
co-authored by Cursor
parent 16ade71598
commit 29579f80d3
3 changed files with 53 additions and 9 deletions
+14 -4
View File
@@ -29,16 +29,26 @@ const props = withDefaults(
}
)
function productImageKey(product?: ProductImageSource): string {
return [
product?.file_id ?? '',
product?.file?.id ?? '',
product?.file?.full_src ?? '',
product?.file?.src ?? '',
product?.file?.url ?? '',
product?.img ?? '',
].join('|')
}
const currentSrc = ref(resolveProductImageSrc(props.product))
const usingFallback = ref(false)
watch(
() => props.product,
(product) => {
() => productImageKey(props.product),
() => {
usingFallback.value = false
currentSrc.value = resolveProductImageSrc(product)
currentSrc.value = resolveProductImageSrc(props.product)
},
{ deep: true }
)
function handleError() {
+10 -3
View File
@@ -6,7 +6,9 @@ import parsianImg from '@/assets/img/parsianImg.svg'
import venusImg from '@/assets/img/venousImg.svg'
export type ProductImageSource = {
file?: { src?: string | null } | null
file?: { id?: number | null; src?: string | null; full_src?: string | null; url?: string | null } | null
file_id?: number | string | null
img?: string | null
group_type?: string | null
} | null | undefined
@@ -43,9 +45,14 @@ export function getProductFallbackImage(product?: ProductImageSource): string {
}
export function resolveProductImageSrc(product?: ProductImageSource): string {
const remoteSrc = product?.file?.src
const remoteSrc = product?.file?.full_src || product?.file?.url || product?.file?.src
if (isUsableImageSrc(remoteSrc)) {
return remoteSrc!.trim()
const url = remoteSrc!.trim()
const version = product?.file_id ?? product?.file?.id
if (version != null && version !== '' && Number(version) > 0 && /^https?:\/\//i.test(url) && !/[?&]v=/.test(url)) {
return `${url}${url.includes('?') ? '&' : '?'}v=${encodeURIComponent(String(version))}`
}
return url
}
return getProductFallbackImage(product)
}
@@ -53,6 +53,29 @@ async function loadCategoryTabs() {
}
}
function productFileId(product: Product): number {
return Number(product.file_id ?? (product.file as { id?: number } | undefined)?.id ?? 0)
}
function mergeLiveProductList(incoming: Product[], current?: Product[]): Product[] {
if (!current?.length) return incoming
const currentById = new Map(current.map((item) => [item.id, item]))
return incoming.map((item) => {
const prev = currentById.get(item.id)
if (!prev || productFileId(prev) <= productFileId(item)) {
return item
}
return {
...item,
file: prev.file,
file_id: prev.file_id,
img: prev.img,
}
})
}
function applyProductsList(products: Product[]) {
const sorted = sortProductsByInventory(products)
productsFirstItems.value = sorted
@@ -76,8 +99,12 @@ export function applyProductsListFromSocket(products: Product[]) {
return
}
getProductsCatalogRef().value = products
applyProductsList(products)
const merged = mergeLiveProductList(
products,
getProductsCatalogRef().value.length ? getProductsCatalogRef().value : productsFirstItems.value,
)
getProductsCatalogRef().value = merged
applyProductsList(merged)
}
function startProductListPolling() {