feat: catalog read cache (api + user apps) and clearer Zarrin accounting errors
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -94,8 +94,10 @@ import ProductCarousel from '../components/home/ProductCarousel.vue'
|
||||
import { useConfigStore } from '../stores/configStore'
|
||||
import type { HomeCategory } from '../stores/configStore'
|
||||
import { useFiltersStore } from '../stores/filtersStore'
|
||||
import { api } from '../api/client'
|
||||
import type { AdminSlide, GoldPriceQuote, ProductSummary } from '../api/client'
|
||||
import { useCachedReads } from '../api/cachedReads'
|
||||
import { useCatalogStore } from '../stores/catalogStore'
|
||||
import { KEYS } from '../stores/queryCache'
|
||||
|
||||
// Module scope, so it survives this view being unmounted and remounted as the
|
||||
// user moves between tabs, and resets only on a full app reload.
|
||||
@@ -104,27 +106,29 @@ let hasRevealed = false
|
||||
const router = useRouter()
|
||||
const memory = useConfigStore()
|
||||
const filters = useFiltersStore()
|
||||
const catalog = useCatalogStore()
|
||||
const reads = useCachedReads()
|
||||
|
||||
const loading = ref(true)
|
||||
const loading = ref(!catalog.loaded)
|
||||
const refreshing = ref(false)
|
||||
const products = ref<ProductSummary[]>([])
|
||||
const goldPrice = ref<GoldPriceQuote | null>(null)
|
||||
const products = computed(() => catalog.products)
|
||||
const goldPrice = ref<GoldPriceQuote | null>(reads.peek<GoldPriceQuote>(KEYS.goldPrice) ?? null)
|
||||
// Admin-managed banner slides (manage/Const.vue "اسلایدر موبایل"). null while
|
||||
// unfetched/unavailable — distinct from "fetched, empty" — so `slides` below
|
||||
// knows whether to wait or fall back to the category-derived hero.
|
||||
// Kept in step with user-app, which has had this since the admin screen was
|
||||
// wired up; without it, slides configured in the admin panel never showed here.
|
||||
const adminSlides = ref<AdminSlide[] | null>(null)
|
||||
const adminSlides = ref<AdminSlide[] | null>(reads.peek<AdminSlide[]>(KEYS.homeSlider) ?? null)
|
||||
|
||||
|
||||
// First-paint entrance. Flipped once, after the first data load, so the
|
||||
// sections settle into place instead of appearing all at once; pull-to-refresh
|
||||
// does not replay it.
|
||||
//
|
||||
// MasterLayout renders its route without keep-alive, so this view remounts on
|
||||
// every return to the Home tab. The flag therefore lives outside the component
|
||||
// — the choreography is a first-run flourish, and replaying it on each tab
|
||||
// switch would fight the page transition rather than add to it.
|
||||
// MasterLayout keeps this view alive between tab switches, but it still
|
||||
// remounts after a visit to a full-page route. The flag therefore lives outside
|
||||
// the component — the choreography is a first-run flourish, and replaying it
|
||||
// would fight the page transition rather than add to it.
|
||||
const revealed = ref(hasRevealed)
|
||||
|
||||
// Each section starts a beat after the one above it. Kept small — the whole
|
||||
@@ -208,31 +212,25 @@ function goNewest() {
|
||||
router.push({ name: 'newestProduct' })
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
try {
|
||||
const [list] = await Promise.all([api.listProducts(), memory.init()])
|
||||
products.value = list
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
// The three reads are independent, so they run side by side. Each one is
|
||||
// cached until pull-to-refresh asks the server again.
|
||||
async function loadData(force = false) {
|
||||
const [catalogResult, priceResult, slidesResult] = await Promise.allSettled([
|
||||
// memory.init() reads the same cached catalogue, so this is one request.
|
||||
memory.init(force),
|
||||
reads.goldPrice({ force }),
|
||||
reads.homeSlider({ force }),
|
||||
])
|
||||
|
||||
try {
|
||||
goldPrice.value = await api.getLatestGoldPrice()
|
||||
} catch {
|
||||
goldPrice.value = null
|
||||
}
|
||||
|
||||
try {
|
||||
adminSlides.value = await api.getHomeSlider()
|
||||
} catch {
|
||||
// No admin slides configured, or the endpoint failed — the category-
|
||||
// derived hero covers this silently, same as before this existed.
|
||||
adminSlides.value = null
|
||||
}
|
||||
if (catalogResult.status === 'rejected') console.log(catalogResult.reason)
|
||||
goldPrice.value = priceResult.status === 'fulfilled' ? priceResult.value : null
|
||||
// No admin slides configured, or the endpoint failed — the category-derived
|
||||
// hero covers this silently, same as before this existed.
|
||||
adminSlides.value = slidesResult.status === 'fulfilled' ? slidesResult.value : null
|
||||
}
|
||||
|
||||
async function onRefresh() {
|
||||
await loadData()
|
||||
await loadData(true)
|
||||
refreshing.value = false
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user