Add layout shells, desktop views, module-level page caches, and UI fixes for auth, home, wallet, transactions, address, and related pages. Co-authored-by: Cursor <cursoragent@cursor.com>
226 lines
6.2 KiB
TypeScript
226 lines
6.2 KiB
TypeScript
import { ref, watch } from 'vue'
|
|
import { getAllPrices } from '@/api/home'
|
|
import type { Product } from '@/models/products'
|
|
import { getProductsCatalog } from '@/composables/useProductsCatalog'
|
|
import { getHomeLayoutId } from '../layouts/registry'
|
|
import { getProductBuyPrice, resolvePriceOffsetRatio } from '@/utils/productPrice'
|
|
import { isKatodMesGroup, isMaftolMesGroup } from '@/utils/productDealMethod'
|
|
import { getTradeBuyPrice, lastPrice } from '@/utils/price'
|
|
|
|
export type ChartMetalType = 'katod' | 'maftol'
|
|
|
|
type ChartPoint = [number, number]
|
|
|
|
/** Module-level chart state — one dataset shared by mobile and desktop PriceChart instances. */
|
|
const priceChartSeries = ref<{ name: string; data: ChartPoint[] }[]>([
|
|
{ name: 'قیمت', data: [] },
|
|
])
|
|
const rawChartPoints = ref<ChartPoint[]>([])
|
|
const chartSelection = ref('DAILY')
|
|
const chartLoading = ref(false)
|
|
const selectedChartMetal = ref<ChartMetalType>('katod')
|
|
const maftolChartRatio = ref(0)
|
|
|
|
let chartFetchPromise: Promise<void> | null = null
|
|
let chartInitialized = false
|
|
let maftolOffsetPromise: Promise<void> | null = null
|
|
let maftolOffsetLoaded = false
|
|
|
|
function findKatodProduct(products: Product[]) {
|
|
return (
|
|
products.find((product) => isKatodMesGroup(product.group_type))
|
|
?? products.find((product) => (product.title ?? '').includes('کاتد'))
|
|
)
|
|
}
|
|
|
|
function findMaftolProduct(products: Product[]) {
|
|
return (
|
|
products.find((product) => isMaftolMesGroup(product.group_type))
|
|
?? products.find((product) => (product.title ?? '').includes('مفتول'))
|
|
)
|
|
}
|
|
|
|
function applyChartRatio(basePrice: number, ratio: number) {
|
|
if (!ratio) {
|
|
return basePrice
|
|
}
|
|
|
|
return Math.round(basePrice + basePrice * ratio)
|
|
}
|
|
|
|
function deriveMaftolChartRatio(maftolProduct?: Product, katodProduct?: Product) {
|
|
const offsetRatio = resolvePriceOffsetRatio(maftolProduct?.price_offset_percent)
|
|
if (offsetRatio) {
|
|
return offsetRatio
|
|
}
|
|
|
|
if (!maftolProduct) {
|
|
return 0
|
|
}
|
|
|
|
const basePrice = getTradeBuyPrice(lastPrice.value)
|
|
const katodPrice = katodProduct ? getProductBuyPrice(katodProduct) : basePrice
|
|
const maftolPrice = getProductBuyPrice(maftolProduct)
|
|
const referencePrice = katodPrice || basePrice
|
|
|
|
if (!referencePrice || !maftolPrice || referencePrice === maftolPrice) {
|
|
return 0
|
|
}
|
|
|
|
return (maftolPrice - referencePrice) / referencePrice
|
|
}
|
|
|
|
function normalizeChartTimestamp(timestamp: number) {
|
|
return timestamp < 1_000_000_000_000 ? timestamp * 1000 : timestamp
|
|
}
|
|
|
|
function resolvePricesMap(response: unknown): Record<string, Array<{ timestamp: number; value: number }>> | null {
|
|
if (!response || typeof response !== 'object') {
|
|
return null
|
|
}
|
|
|
|
const body = response as Record<string, unknown>
|
|
const nested = body.data
|
|
|
|
if (nested && typeof nested === 'object' && 'prices' in nested) {
|
|
return (nested as { prices: Record<string, Array<{ timestamp: number; value: number }>> }).prices
|
|
}
|
|
|
|
if ('prices' in body) {
|
|
return body.prices as Record<string, Array<{ timestamp: number; value: number }>>
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
function buildChartPoints(items: Array<{ timestamp: number; value: number }>): ChartPoint[] {
|
|
return items.map((item) => [
|
|
normalizeChartTimestamp(item.timestamp),
|
|
item.value - 1,
|
|
])
|
|
}
|
|
|
|
function syncChartSeries() {
|
|
const data =
|
|
getHomeLayoutId() === 'boursecu' && selectedChartMetal.value === 'maftol'
|
|
? rawChartPoints.value.map(
|
|
([timestamp, value]) =>
|
|
[timestamp, applyChartRatio(value, maftolChartRatio.value)] as ChartPoint
|
|
)
|
|
: rawChartPoints.value
|
|
|
|
priceChartSeries.value[0].data = data
|
|
}
|
|
|
|
watch([selectedChartMetal, maftolChartRatio], () => {
|
|
syncChartSeries()
|
|
})
|
|
|
|
async function fetchPrices(force = false) {
|
|
if (!force && chartInitialized && rawChartPoints.value.length > 0) {
|
|
syncChartSeries()
|
|
return
|
|
}
|
|
|
|
if (!force && chartFetchPromise) {
|
|
return chartFetchPromise
|
|
}
|
|
|
|
chartFetchPromise = (async () => {
|
|
try {
|
|
chartLoading.value = true
|
|
const response = await getAllPrices()
|
|
const data = resolvePricesMap(response)
|
|
const selectedData = data?.[chartSelection.value] ?? []
|
|
rawChartPoints.value = buildChartPoints(selectedData)
|
|
syncChartSeries()
|
|
chartInitialized = true
|
|
} catch (error) {
|
|
console.error('Error fetching prices:', error)
|
|
} finally {
|
|
chartLoading.value = false
|
|
chartFetchPromise = null
|
|
}
|
|
})()
|
|
|
|
return chartFetchPromise
|
|
}
|
|
|
|
async function loadMaftolOffset(force = false) {
|
|
if (getHomeLayoutId() !== 'boursecu') {
|
|
maftolChartRatio.value = 0
|
|
syncChartSeries()
|
|
return
|
|
}
|
|
|
|
if (!force && maftolOffsetLoaded) {
|
|
return
|
|
}
|
|
|
|
if (!force && maftolOffsetPromise) {
|
|
return maftolOffsetPromise
|
|
}
|
|
|
|
maftolOffsetPromise = (async () => {
|
|
try {
|
|
const products = await getProductsCatalog(force)
|
|
const katodProduct = findKatodProduct(products)
|
|
const maftolProduct = findMaftolProduct(products)
|
|
maftolChartRatio.value = deriveMaftolChartRatio(maftolProduct, katodProduct)
|
|
syncChartSeries()
|
|
maftolOffsetLoaded = true
|
|
} catch (error) {
|
|
console.error('Error loading maftol chart ratio:', error)
|
|
maftolChartRatio.value = 0
|
|
syncChartSeries()
|
|
} finally {
|
|
maftolOffsetPromise = null
|
|
}
|
|
})()
|
|
|
|
return maftolOffsetPromise
|
|
}
|
|
|
|
function updateChartData(timeline: string) {
|
|
chartSelection.value = timeline
|
|
void fetchPrices(true)
|
|
}
|
|
|
|
function setChartMetal(metal: ChartMetalType) {
|
|
selectedChartMetal.value = metal
|
|
}
|
|
|
|
export function usePriceChart() {
|
|
return {
|
|
priceChartSeries,
|
|
chartSelection,
|
|
chartLoading,
|
|
selectedChartMetal,
|
|
maftolChartRatio,
|
|
updateChartData,
|
|
fetchPrices,
|
|
loadMaftolOffset,
|
|
setChartMetal,
|
|
}
|
|
}
|
|
|
|
export function fetchHomeChartPrices(force = false) {
|
|
return fetchPrices(force)
|
|
}
|
|
|
|
export function loadHomeMaftolOffset(force = false) {
|
|
return loadMaftolOffset(force)
|
|
}
|
|
|
|
export function resetHomePriceChart() {
|
|
priceChartSeries.value = [{ name: 'قیمت', data: [] }]
|
|
rawChartPoints.value = []
|
|
chartSelection.value = 'DAILY'
|
|
selectedChartMetal.value = 'katod'
|
|
maftolChartRatio.value = 0
|
|
chartInitialized = false
|
|
chartFetchPromise = null
|
|
maftolOffsetLoaded = false
|
|
maftolOffsetPromise = null
|
|
}
|