2026-06-16 19:47:28 +03:30
|
|
|
import { computed, h, inject, onUnmounted, provide, ref, type InjectionKey } from 'vue'
|
2026-06-14 10:08:46 +03:30
|
|
|
import router from '@/router'
|
2026-06-16 19:47:28 +03:30
|
|
|
import { getToken, removeToken } from '@/utils'
|
2026-06-14 10:08:46 +03:30
|
|
|
import { useUserStore } from '@/store/userStore'
|
|
|
|
|
import { useWalletStore } from '@/store/walletStore'
|
|
|
|
|
import { useWalletAssets } from '@/composables/useWalletAssets'
|
|
|
|
|
import type { WalletMetalTotal } from '@/models/walletAssets'
|
2026-06-16 10:32:10 +03:30
|
|
|
import {
|
|
|
|
|
formatBoursecuMetalModalText,
|
|
|
|
|
formatMetalAssetsInfoText,
|
|
|
|
|
formatRialAssetsInfoText,
|
|
|
|
|
resolveAssetsRialValue,
|
|
|
|
|
} from '@/utils/productAssets'
|
2026-06-14 10:08:46 +03:30
|
|
|
import { useConfigStore } from '@/store/configStore'
|
2026-06-21 14:42:27 +02:00
|
|
|
import { useSettingsStore } from '@/store/settingsStore'
|
2026-06-15 17:02:11 +03:30
|
|
|
import { getTradeBuyPrice, lastPrice, stopLastPricePolling } from '@/utils/price'
|
|
|
|
|
import { getStoreClosedMessage, isStoreClosed } from '@/utils/storeHours'
|
2026-06-14 10:08:46 +03:30
|
|
|
import { message, Modal } from 'ant-design-vue'
|
2026-06-17 17:39:41 +03:30
|
|
|
import { getReferralShareText, getReferralShareTitle, shareReferralText } from '@/utils/referralShare'
|
2026-06-14 10:08:46 +03:30
|
|
|
import { useBanners } from './useBanners'
|
|
|
|
|
import { usePriceChart, type ChartMetalType } from './usePriceChart'
|
|
|
|
|
import type { HomeActionItem } from '../layouts/types'
|
|
|
|
|
import { getHomeLayoutId } from '../layouts/registry'
|
2026-06-16 19:47:28 +03:30
|
|
|
import { initHomePageData, resetHomePageData, useHomePageLoading } from './useHomePageData'
|
2026-06-17 11:26:26 +03:30
|
|
|
import { ensureAuthenticatedRoute } from '@/app/setupRouter'
|
2026-06-14 10:08:46 +03:30
|
|
|
import { resetWalletPageData } from '../../wallet/composables/useWalletPageData'
|
|
|
|
|
import { resetAddressPageData } from '../../address/composables/useAddressPageData'
|
|
|
|
|
import { resetTransactionsPageData } from '../../transactions/composables/useTransactionsPageData'
|
|
|
|
|
import { resetProductsPageData } from '../../products/composables/useProductsPageData'
|
|
|
|
|
|
|
|
|
|
export interface HomePageContext {
|
2026-06-16 19:47:28 +03:30
|
|
|
homePageLoading: ReturnType<typeof useHomePageLoading>['homePageLoading']
|
2026-06-14 10:08:46 +03:30
|
|
|
isGaroosi: ReturnType<typeof computed<boolean>>
|
2026-06-21 14:42:27 +02:00
|
|
|
bannerEnabled: ReturnType<typeof computed<boolean>>
|
2026-06-14 10:08:46 +03:30
|
|
|
limitedPrice: ReturnType<typeof computed>
|
|
|
|
|
showClosedMessage: ReturnType<typeof computed<boolean>>
|
|
|
|
|
closedMessage: ReturnType<typeof computed>
|
|
|
|
|
mainActions: ReturnType<typeof computed<HomeActionItem[]>>
|
|
|
|
|
banners: ReturnType<typeof useBanners>['banners']
|
|
|
|
|
priceChartSeries: ReturnType<typeof usePriceChart>['priceChartSeries']
|
|
|
|
|
chartSelection: ReturnType<typeof usePriceChart>['chartSelection']
|
|
|
|
|
updateChartData: ReturnType<typeof usePriceChart>['updateChartData']
|
|
|
|
|
selectedChartMetal: ReturnType<typeof usePriceChart>['selectedChartMetal']
|
|
|
|
|
setChartMetal: (metal: ChartMetalType) => void
|
|
|
|
|
isBoursecuHomeLayout: ReturnType<typeof computed<boolean>>
|
|
|
|
|
handleBannerClick: (banner: { typeClick?: string; modalText?: string; link?: string }) => void
|
2026-06-16 10:51:55 +03:30
|
|
|
openGoldModal: (
|
|
|
|
|
metal: WalletMetalTotal,
|
|
|
|
|
modalText?: string,
|
|
|
|
|
modalTitle?: string,
|
|
|
|
|
modalContent?: string
|
|
|
|
|
) => void
|
2026-06-14 10:08:46 +03:30
|
|
|
openRialModal: () => void
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const HOME_PAGE_CONTEXT_KEY: InjectionKey<HomePageContext> = Symbol('homePageContext')
|
|
|
|
|
|
|
|
|
|
export function useHomePage() {
|
|
|
|
|
const userStore = useUserStore()
|
|
|
|
|
const walletStore = useWalletStore()
|
|
|
|
|
const configStore = useConfigStore()
|
2026-06-21 14:42:27 +02:00
|
|
|
const settingsStore = useSettingsStore()
|
2026-06-14 10:08:46 +03:30
|
|
|
const { assetsSummary, clearWalletAssets } = useWalletAssets()
|
|
|
|
|
|
|
|
|
|
const showLogoutConfirm = ref(false)
|
|
|
|
|
const wholeBalanceVisible = ref(false)
|
|
|
|
|
const showTextBanner = ref(false)
|
|
|
|
|
const showShare = ref(false)
|
|
|
|
|
const TextBanner = ref('')
|
|
|
|
|
|
|
|
|
|
const { banners } = useBanners()
|
2026-06-16 19:47:28 +03:30
|
|
|
const { homePageLoading } = useHomePageLoading()
|
2026-06-14 10:08:46 +03:30
|
|
|
const {
|
|
|
|
|
updateChartData,
|
|
|
|
|
priceChartSeries,
|
|
|
|
|
chartSelection,
|
|
|
|
|
selectedChartMetal,
|
|
|
|
|
setChartMetal,
|
|
|
|
|
} = usePriceChart()
|
|
|
|
|
const isBoursecuHomeLayout = computed(() => getHomeLayoutId() === 'boursecu')
|
|
|
|
|
|
|
|
|
|
const isGaroosi = computed(() => configStore.config?.name === 'garoosi')
|
2026-06-21 14:42:27 +02:00
|
|
|
const bannerEnabled = computed(() => settingsStore.isSettingTruthy('banner_enabled'))
|
2026-06-14 10:08:46 +03:30
|
|
|
const closedMessage = computed(() => userStore.getValueByKey('closed_message'))
|
2026-06-15 17:02:11 +03:30
|
|
|
const marketClosedText = computed(() => {
|
|
|
|
|
if (!isStoreClosed()) return ''
|
|
|
|
|
return closedMessage.value || getStoreClosedMessage()
|
|
|
|
|
})
|
|
|
|
|
const showClosedMessage = computed(() => !!marketClosedText.value)
|
2026-06-14 10:08:46 +03:30
|
|
|
const limitedPrice = computed(() => walletStore.limitedPrice?.[0])
|
|
|
|
|
const goldVal = computed(() => lastPrice.value?.price_last ?? 1)
|
|
|
|
|
|
|
|
|
|
const mainActions = computed<HomeActionItem[]>(() => [
|
|
|
|
|
{
|
|
|
|
|
name: 'buySell',
|
|
|
|
|
icon: 'vuesax-linear:money-receive',
|
|
|
|
|
label: 'خرید و فروش',
|
|
|
|
|
handler: () => router.push({ name: 'buyProduct' }),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'deposit',
|
|
|
|
|
icon: 'vuesax-linear:card-add',
|
|
|
|
|
label: isGaroosi.value ? 'واریز و برداشت' : 'واریز نقدی',
|
|
|
|
|
handler: () => router.push({ name: 'wallet', query: { state: 'deposit' } }),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'balance',
|
|
|
|
|
icon: 'vuesax-linear:receipt-item',
|
|
|
|
|
label: 'موجودی',
|
|
|
|
|
handler: () => {
|
|
|
|
|
wholeBalanceVisible.value = true
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'assets',
|
|
|
|
|
icon: 'vuesax-linear:cup',
|
|
|
|
|
label: isGaroosi.value ? 'دعوت از دوستان' : 'دارایی ها',
|
|
|
|
|
handler: () => (isGaroosi.value ? shareCode() : router.push({ name: 'fund' })),
|
|
|
|
|
},
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
async function shareCode() {
|
|
|
|
|
const text = getReferralShareText({
|
|
|
|
|
userName: userStore.userInfo?.name,
|
|
|
|
|
projectName: configStore.config.name,
|
|
|
|
|
referralId: userStore.userInfo?.referral_id,
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-17 17:39:41 +03:30
|
|
|
await shareReferralText(text, getReferralShareTitle(configStore.config.name))
|
2026-06-14 10:08:46 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function copyCode() {
|
|
|
|
|
const code = userStore.userInfo?.referral_id
|
|
|
|
|
if (code) {
|
|
|
|
|
navigator.clipboard.writeText(code)
|
|
|
|
|
message.info('کد رونوشت شد')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function logout() {
|
|
|
|
|
userStore.userInfo = undefined
|
|
|
|
|
walletStore.walletInfo = undefined
|
|
|
|
|
clearWalletAssets()
|
|
|
|
|
resetHomePageData()
|
|
|
|
|
resetWalletPageData()
|
|
|
|
|
resetAddressPageData()
|
|
|
|
|
resetTransactionsPageData()
|
|
|
|
|
resetProductsPageData()
|
|
|
|
|
removeToken()
|
|
|
|
|
await router.push({ name: 'login' })
|
|
|
|
|
window.location.reload()
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-16 10:51:55 +03:30
|
|
|
function renderModalLines(text?: string) {
|
|
|
|
|
if (!text) {
|
|
|
|
|
return undefined
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return h(
|
|
|
|
|
'div',
|
|
|
|
|
{ style: { lineHeight: '1.9' } },
|
|
|
|
|
text.split('\n').map((line, index) =>
|
|
|
|
|
h(
|
|
|
|
|
'div',
|
|
|
|
|
{ key: index, style: index > 0 ? { marginTop: '6px' } : undefined },
|
|
|
|
|
line
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function openGoldModal(
|
|
|
|
|
metal: WalletMetalTotal,
|
|
|
|
|
modalText?: string,
|
|
|
|
|
modalTitle?: string,
|
|
|
|
|
modalContent?: string
|
|
|
|
|
) {
|
2026-06-16 10:32:10 +03:30
|
|
|
const title =
|
2026-06-16 10:51:55 +03:30
|
|
|
modalTitle ??
|
2026-06-16 10:32:10 +03:30
|
|
|
modalText ??
|
|
|
|
|
(isBoursecuHomeLayout.value
|
|
|
|
|
? formatBoursecuMetalModalText(metal)
|
|
|
|
|
: formatMetalAssetsInfoText(metal))
|
|
|
|
|
|
2026-06-14 10:08:46 +03:30
|
|
|
Modal.confirm({
|
2026-06-16 10:32:10 +03:30
|
|
|
title,
|
2026-06-16 10:51:55 +03:30
|
|
|
content: renderModalLines(modalContent),
|
2026-06-14 10:08:46 +03:30
|
|
|
okText: 'متوجه شدم',
|
|
|
|
|
centered: true,
|
|
|
|
|
cancelButtonProps: { style: { display: 'none' } },
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function openRialModal() {
|
|
|
|
|
const summary = assetsSummary.value
|
2026-06-15 17:02:11 +03:30
|
|
|
const cash = summary?.cash_wallet ?? 0
|
|
|
|
|
const metalRial = resolveAssetsRialValue(summary, getTradeBuyPrice(lastPrice.value))
|
2026-06-14 10:08:46 +03:30
|
|
|
Modal.confirm({
|
2026-06-15 17:02:11 +03:30
|
|
|
title: 'دارایی ریالی',
|
2026-06-16 10:51:55 +03:30
|
|
|
content: renderModalLines(formatRialAssetsInfoText(cash, metalRial)),
|
2026-06-14 10:08:46 +03:30
|
|
|
okText: 'متوجه شدم',
|
|
|
|
|
centered: true,
|
|
|
|
|
cancelButtonProps: { style: { display: 'none' } },
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleBannerClick(banner: { typeClick?: string; modalText?: string; link?: string }) {
|
|
|
|
|
const handlers: Record<string, () => void> = {
|
|
|
|
|
share: () => {
|
|
|
|
|
showShare.value = true
|
|
|
|
|
},
|
|
|
|
|
modal: () => {
|
|
|
|
|
if (banner.modalText) {
|
|
|
|
|
TextBanner.value = banner.modalText
|
|
|
|
|
showTextBanner.value = true
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
link: () => {
|
|
|
|
|
if (banner.link) {
|
|
|
|
|
if (banner.link.startsWith('http')) {
|
|
|
|
|
window.open(banner.link, '_blank')
|
|
|
|
|
} else {
|
|
|
|
|
router.push(banner.link)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
handlers[banner.typeClick ?? '']?.()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const context: HomePageContext = {
|
2026-06-16 19:47:28 +03:30
|
|
|
homePageLoading,
|
2026-06-14 10:08:46 +03:30
|
|
|
isGaroosi,
|
2026-06-21 14:42:27 +02:00
|
|
|
bannerEnabled,
|
2026-06-14 10:08:46 +03:30
|
|
|
limitedPrice,
|
|
|
|
|
showClosedMessage,
|
2026-06-15 17:02:11 +03:30
|
|
|
closedMessage: marketClosedText,
|
2026-06-14 10:08:46 +03:30
|
|
|
mainActions,
|
|
|
|
|
banners,
|
|
|
|
|
priceChartSeries,
|
|
|
|
|
chartSelection,
|
|
|
|
|
updateChartData,
|
|
|
|
|
selectedChartMetal,
|
|
|
|
|
setChartMetal,
|
|
|
|
|
isBoursecuHomeLayout,
|
|
|
|
|
handleBannerClick,
|
|
|
|
|
openGoldModal,
|
|
|
|
|
openRialModal,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
provide(HOME_PAGE_CONTEXT_KEY, context)
|
|
|
|
|
|
2026-06-16 19:47:28 +03:30
|
|
|
void ensureHomeUserAndData()
|
|
|
|
|
|
2026-06-14 10:08:46 +03:30
|
|
|
onUnmounted(() => {
|
|
|
|
|
stopLastPricePolling()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
userStore,
|
|
|
|
|
walletStore,
|
|
|
|
|
showLogoutConfirm,
|
|
|
|
|
wholeBalanceVisible,
|
|
|
|
|
showTextBanner,
|
|
|
|
|
showShare,
|
|
|
|
|
TextBanner,
|
|
|
|
|
goldVal,
|
|
|
|
|
logout,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useHomePageContext() {
|
|
|
|
|
const context = inject(HOME_PAGE_CONTEXT_KEY)
|
|
|
|
|
if (!context) {
|
|
|
|
|
throw new Error('useHomePageContext must be used within useHomePage')
|
|
|
|
|
}
|
|
|
|
|
return context
|
|
|
|
|
}
|
2026-06-16 19:47:28 +03:30
|
|
|
|
|
|
|
|
async function ensureHomeUserAndData() {
|
2026-06-17 11:26:26 +03:30
|
|
|
const authed = await ensureAuthenticatedRoute()
|
|
|
|
|
if (!authed) {
|
|
|
|
|
return
|
2026-06-16 19:47:28 +03:30
|
|
|
}
|
|
|
|
|
|
2026-06-17 11:26:26 +03:30
|
|
|
await initHomePageData()
|
2026-06-16 19:47:28 +03:30
|
|
|
}
|