added assets into stream
This commit is contained in:
@@ -113,7 +113,9 @@ export async function bootstrapApp() {
|
|||||||
|
|
||||||
await withTimeout(settingsStore.fetchSettings(), 45000, 'Settings loading timeout')
|
await withTimeout(settingsStore.fetchSettings(), 45000, 'Settings loading timeout')
|
||||||
|
|
||||||
initPriceRealtimeChannel((key) => settingsStore.getSettingByKey(key))
|
if (getToken()) {
|
||||||
|
initPriceRealtimeChannel((key) => settingsStore.getSettingByKey(key))
|
||||||
|
}
|
||||||
|
|
||||||
if (getToken()) {
|
if (getToken()) {
|
||||||
await withTimeout(userStore.getMyInfo(), 45000, 'User session loading timeout')
|
await withTimeout(userStore.getMyInfo(), 45000, 'User session loading timeout')
|
||||||
|
|||||||
@@ -1,12 +1,57 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { getWalletAssets } from '@/api/wallet'
|
import { getWalletAssets } from '@/api/wallet'
|
||||||
import { getProductsCatalog } from '@/composables/useProductsCatalog'
|
import { getProductsCatalog, getProductsCatalogRef } from '@/composables/useProductsCatalog'
|
||||||
|
import { useWalletStore } from '@/store/walletStore'
|
||||||
import type { WalletAssetsSummary } from '@/models/walletAssets'
|
import type { WalletAssetsSummary } from '@/models/walletAssets'
|
||||||
import { enrichInventoryCardsWithCatalog } from '@/utils/productAssets'
|
import { enrichInventoryCardsWithCatalog } from '@/utils/productAssets'
|
||||||
|
|
||||||
const assetsSummary = ref<WalletAssetsSummary | null>(null)
|
const assetsSummary = ref<WalletAssetsSummary | null>(null)
|
||||||
let assetsPromise: Promise<WalletAssetsSummary> | null = null
|
let assetsPromise: Promise<WalletAssetsSummary> | null = null
|
||||||
|
|
||||||
|
function enrichAssetsSummary(data: WalletAssetsSummary): WalletAssetsSummary {
|
||||||
|
const catalog = getProductsCatalogRef().value
|
||||||
|
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
inventory_cards: enrichInventoryCardsWithCatalog(data.inventory_cards ?? [], catalog ?? []),
|
||||||
|
count_products: enrichInventoryCardsWithCatalog(data.count_products ?? [], catalog ?? []),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncWalletStoreFromAssets(data: WalletAssetsSummary) {
|
||||||
|
const walletStore = useWalletStore()
|
||||||
|
if (!walletStore.walletInfo) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const goldMetal = data.metal_totals?.find((metal) => metal.metal_id === 1) ?? data.metal_totals?.[0]
|
||||||
|
|
||||||
|
walletStore.walletInfo.cash = data.cash_wallet ?? walletStore.walletInfo.cash
|
||||||
|
|
||||||
|
if (goldMetal) {
|
||||||
|
walletStore.walletInfo.gold = goldMetal.melted_weight ?? walletStore.walletInfo.gold
|
||||||
|
walletStore.walletInfo.gold_sum = goldMetal.total_weight ?? walletStore.walletInfo.gold_sum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function applyWalletAssetsFromStream(payload: unknown) {
|
||||||
|
if (!payload || typeof payload !== 'object') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = payload as WalletAssetsSummary
|
||||||
|
if (data.meta == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!getProductsCatalogRef().value.length) {
|
||||||
|
await getProductsCatalog()
|
||||||
|
}
|
||||||
|
|
||||||
|
assetsSummary.value = enrichAssetsSummary(data)
|
||||||
|
syncWalletStoreFromAssets(assetsSummary.value)
|
||||||
|
}
|
||||||
|
|
||||||
export function useWalletAssets() {
|
export function useWalletAssets() {
|
||||||
async function loadWalletAssets(force = false) {
|
async function loadWalletAssets(force = false) {
|
||||||
if (!force && assetsSummary.value) {
|
if (!force && assetsSummary.value) {
|
||||||
@@ -17,14 +62,10 @@ export function useWalletAssets() {
|
|||||||
return assetsPromise
|
return assetsPromise
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reuses cached getProductsCatalog() — same catalog as chart offset logic
|
|
||||||
assetsPromise = Promise.all([getWalletAssets(), getProductsCatalog(force)])
|
assetsPromise = Promise.all([getWalletAssets(), getProductsCatalog(force)])
|
||||||
.then(([data, catalog]) => {
|
.then(([data]) => {
|
||||||
assetsSummary.value = {
|
assetsSummary.value = enrichAssetsSummary(data)
|
||||||
...data,
|
syncWalletStoreFromAssets(assetsSummary.value)
|
||||||
inventory_cards: enrichInventoryCardsWithCatalog(data.inventory_cards ?? [], catalog ?? []),
|
|
||||||
count_products: enrichInventoryCardsWithCatalog(data.count_products ?? [], catalog ?? []),
|
|
||||||
}
|
|
||||||
return assetsSummary.value
|
return assetsSummary.value
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
findStoredAuthToken,
|
findStoredAuthToken,
|
||||||
persistAuthToken,
|
persistAuthToken,
|
||||||
} from '@/utils/authToken'
|
} from '@/utils/authToken'
|
||||||
|
import { stopPriceRealtimeAuth } from '@/utils/priceSocket'
|
||||||
|
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import {useConfigStore} from "@/store/configStore";
|
import {useConfigStore} from "@/store/configStore";
|
||||||
@@ -85,6 +86,7 @@ export async function authenticateWithFingerprint() {
|
|||||||
|
|
||||||
|
|
||||||
function removeToken() {
|
function removeToken() {
|
||||||
|
stopPriceRealtimeAuth()
|
||||||
return clearStoredAuthTokens()
|
return clearStoredAuthTokens()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+74
-12
@@ -2,6 +2,7 @@ import type { Price } from '@/models/home'
|
|||||||
import type { Product } from '@/models/products'
|
import type { Product } from '@/models/products'
|
||||||
import { Modal } from 'ant-design-vue'
|
import { Modal } from 'ant-design-vue'
|
||||||
import { getConfigApp } from '@/utils/config'
|
import { getConfigApp } from '@/utils/config'
|
||||||
|
import { findStoredAuthToken } from '@/utils/authToken'
|
||||||
import { priceSocketConnected, priceStreamLifecycle, priceWssEnabled } from '@/utils/priceSocketState'
|
import { priceSocketConnected, priceStreamLifecycle, priceWssEnabled } from '@/utils/priceSocketState'
|
||||||
|
|
||||||
export { priceSocketConnected, priceStreamLifecycle, priceWssEnabled, shouldUsePricePolling } from '@/utils/priceSocketState'
|
export { priceSocketConnected, priceStreamLifecycle, priceWssEnabled, shouldUsePricePolling } from '@/utils/priceSocketState'
|
||||||
@@ -56,8 +57,16 @@ function resolveStreamUrl(): string | null {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const token = findStoredAuthToken()
|
||||||
|
if (!token) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const base = String(getConfigApp('node_server') || window.location.origin).replace(/\/$/, '')
|
const base = String(getConfigApp('node_server') || window.location.origin).replace(/\/$/, '')
|
||||||
return `${base}${resolveStreamPath()}`
|
const url = new URL(`${base}${resolveStreamPath()}`)
|
||||||
|
url.searchParams.set('token', token)
|
||||||
|
|
||||||
|
return url.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearReconnectTimer() {
|
function clearReconnectTimer() {
|
||||||
@@ -205,6 +214,11 @@ async function applyListFromStream(products: Product[]) {
|
|||||||
applyProductsListFromSocket(products)
|
applyProductsListFromSocket(products)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function applyAssetsFromStream(payload: unknown) {
|
||||||
|
const { applyWalletAssetsFromStream } = await import('@/composables/useWalletAssets')
|
||||||
|
applyWalletAssetsFromStream(payload)
|
||||||
|
}
|
||||||
|
|
||||||
function handleListPayload(payload: unknown) {
|
function handleListPayload(payload: unknown) {
|
||||||
if (!Array.isArray(payload) || !payload.length) {
|
if (!Array.isArray(payload) || !payload.length) {
|
||||||
return
|
return
|
||||||
@@ -214,6 +228,15 @@ function handleListPayload(payload: unknown) {
|
|||||||
void applyListFromStream(payload as Product[])
|
void applyListFromStream(payload as Product[])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleAssetsPayload(payload: unknown) {
|
||||||
|
if (!payload || typeof payload !== 'object') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
touchStreamActivity()
|
||||||
|
void applyAssetsFromStream(payload)
|
||||||
|
}
|
||||||
|
|
||||||
function bindStreamEvents(source: EventSource) {
|
function bindStreamEvents(source: EventSource) {
|
||||||
source.addEventListener('last', (event) => {
|
source.addEventListener('last', (event) => {
|
||||||
try {
|
try {
|
||||||
@@ -231,6 +254,14 @@ function bindStreamEvents(source: EventSource) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
source.addEventListener('assets', (event) => {
|
||||||
|
try {
|
||||||
|
handleAssetsPayload(JSON.parse(event.data))
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Price SSE assets parse failed', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
source.addEventListener('ping', () => {
|
source.addEventListener('ping', () => {
|
||||||
touchStreamActivity()
|
touchStreamActivity()
|
||||||
})
|
})
|
||||||
@@ -414,27 +445,58 @@ function onVisibilityChange() {
|
|||||||
|
|
||||||
export function isPriceWebSocketEnabled(settingGetter: (key: string) => unknown): boolean {
|
export function isPriceWebSocketEnabled(settingGetter: (key: string) => unknown): boolean {
|
||||||
getSetting = settingGetter
|
getSetting = settingGetter
|
||||||
|
if (!isTruthySetting(settingGetter('wss_enabled'), true)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!findStoredAuthToken()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return resolveStreamUrl() !== null
|
return resolveStreamUrl() !== null
|
||||||
}
|
}
|
||||||
|
|
||||||
let channelStarted = false
|
let channelStarted = false
|
||||||
|
|
||||||
export function initPriceRealtimeChannel(settingGetter: (key: string) => unknown) {
|
function shutdownPriceRealtimeChannel() {
|
||||||
|
if (!channelStarted && subscribers <= 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
channelStarted = false
|
||||||
|
stopped = true
|
||||||
|
subscribers = 0
|
||||||
|
disconnect()
|
||||||
|
unbindBrowserNetworkListeners()
|
||||||
|
|
||||||
|
if (typeof document !== 'undefined') {
|
||||||
|
document.removeEventListener('visibilitychange', onVisibilityChange)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function syncPriceRealtimeChannel(settingGetter: (key: string) => unknown) {
|
||||||
|
getSetting = settingGetter
|
||||||
priceWssEnabled.value = isTruthySetting(settingGetter('wss_enabled'), true)
|
priceWssEnabled.value = isTruthySetting(settingGetter('wss_enabled'), true)
|
||||||
|
|
||||||
if (channelStarted) {
|
if (!priceWssEnabled.value || !isPriceWebSocketEnabled(settingGetter)) {
|
||||||
|
shutdownPriceRealtimeChannel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channelStarted && subscribers > 0) {
|
||||||
|
forceReconnect()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
channelStarted = true
|
channelStarted = true
|
||||||
|
stopped = false
|
||||||
if (!priceWssEnabled.value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isPriceWebSocketEnabled(settingGetter)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
startPriceSocket(settingGetter)
|
startPriceSocket(settingGetter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function initPriceRealtimeChannel(settingGetter: (key: string) => unknown) {
|
||||||
|
syncPriceRealtimeChannel(settingGetter)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stopPriceRealtimeAuth() {
|
||||||
|
shutdownPriceRealtimeChannel()
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
'z-100 self-center w-36',
|
'z-100 self-center w-36',
|
||||||
config.config?.name === 'garoosi'
|
config.config?.name === 'garoosi'
|
||||||
? 'h-auto object-contain'
|
? 'h-auto object-contain'
|
||||||
: 'rounded-full aspect-square',
|
: 'rounded aspect-square',
|
||||||
]"
|
]"
|
||||||
style="margin-bottom: 60px"
|
style="margin-bottom: 60px"
|
||||||
>
|
>
|
||||||
@@ -375,6 +375,7 @@ import router from '@/router'
|
|||||||
import { useUserStore } from '@/store/userStore'
|
import { useUserStore } from '@/store/userStore'
|
||||||
import { useWalletStore } from '@/store/walletStore'
|
import { useWalletStore } from '@/store/walletStore'
|
||||||
import { useSettingsStore } from '@/store/settingsStore'
|
import { useSettingsStore } from '@/store/settingsStore'
|
||||||
|
import { syncPriceRealtimeChannel } from '@/utils/priceSocket'
|
||||||
import { setTruth } from '@/app/authBoot'
|
import { setTruth } from '@/app/authBoot'
|
||||||
import {useConfigStore} from "@/store/configStore";
|
import {useConfigStore} from "@/store/configStore";
|
||||||
import {
|
import {
|
||||||
@@ -480,6 +481,7 @@ async function handleOnComplete(value: string) {
|
|||||||
|
|
||||||
async function login(accessToken: string | undefined) {
|
async function login(accessToken: string | undefined) {
|
||||||
setToken(accessToken)
|
setToken(accessToken)
|
||||||
|
syncPriceRealtimeChannel((key) => setting.getSettingByKey(key))
|
||||||
await userStore.getMyInfo()
|
await userStore.getMyInfo()
|
||||||
// console.log(URLUserType())
|
// console.log(URLUserType())
|
||||||
// console.log(userStore.userInfo?.type)
|
// console.log(userStore.userInfo?.type)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
'z-100 self-center w-36',
|
'z-100 self-center w-36',
|
||||||
config.config?.name === 'garoosi'
|
config.config?.name === 'garoosi'
|
||||||
? 'h-auto object-contain mb-[60px]'
|
? 'h-auto object-contain mb-[60px]'
|
||||||
: 'rounded-full aspect-square',
|
: 'rounded aspect-square',
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<div v-if="variant !== 'desktop'" class="font-bold text-center text-xl">ثبت نام</div>
|
<div v-if="variant !== 'desktop'" class="font-bold text-center text-xl">ثبت نام</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user