new UI and handle cashing

This commit is contained in:
2026-06-16 19:47:28 +03:30
parent e174ae28c5
commit 6e5ea0319f
41 changed files with 2936 additions and 641 deletions
+17
View File
@@ -0,0 +1,17 @@
let fingerPosition = false
export function setTruth() {
fingerPosition = true
}
export function setEnableAuthenticate() {
fingerPosition = false
}
export function getFingerPosition() {
return fingerPosition
}
export function setFingerPosition(value: boolean) {
fingerPosition = value
}
+114
View File
@@ -0,0 +1,114 @@
import { loadAppConfig } from '@/config/load-config'
import { setAppConfig } from '@/config/app-config'
import { applyAppConfig } from '@/config/apply-config'
import { startPwaManifest } from '@/modules/pwa/manifest'
import { createVueApp } from '@/app/createApp'
import { setupRouterGuards } from '@/app/setupRouter'
import { applyProjectTheme } from '@/utils/config'
import { useSettingsStore } from '@/store/settingsStore'
import { useUserStore } from '@/store/userStore'
import { getToken } from '@/utils'
type BootWindow = Window & {
__BOOT_TIMEOUT__?: number
__APP_BOOTSTRAPPED__?: boolean
}
function clearBootTimeout() {
const bootWindow = window as BootWindow
if (bootWindow.__BOOT_TIMEOUT__) {
window.clearTimeout(bootWindow.__BOOT_TIMEOUT__)
bootWindow.__BOOT_TIMEOUT__ = undefined
}
}
function withTimeout<T>(promise: Promise<T>, ms: number, message: string): Promise<T> {
return Promise.race([
promise,
new Promise<T>((_, reject) => {
window.setTimeout(() => {
reject(new Error(message))
}, ms)
})
])
}
function showBootstrapError(error: unknown) {
clearBootTimeout()
console.error('Bootstrap failed:', error)
const appRoot = document.querySelector('#app')
if (!appRoot) return
appRoot.innerHTML = `
<div style="min-height: 100vh; display: flex; align-items: center; justify-content: center; direction: rtl; font-family: sans-serif; padding: 24px; background: var(--boot-bg, #ffffff); color: var(--boot-text, #111827);">
<div style="width: min(420px, 100%); text-align: center; background: var(--boot-card, #fff); border: 1px solid var(--boot-border, rgba(15,23,42,0.08)); border-radius: 24px; padding: 28px 22px;">
<div style="font-size: 42px; margin-bottom: 12px;">⚠️</div>
<h2 style="margin: 0 0 12px; font-size: 20px;">خطا در راه‌اندازی برنامه</h2>
<p style="margin: 0 0 20px; color: var(--boot-muted, rgba(15,23,42,0.62)); line-height: 2; font-size: 14px;">
لطفاً اتصال اینترنت خود را بررسی کنید و دوباره تلاش کنید.
</p>
<button
onclick="location.reload()"
style="padding: 11px 22px; border: 0; border-radius: 12px; background: var(--boot-accent, #2563eb); color: white; cursor: pointer; font-weight: 700;"
>
تلاش مجدد
</button>
</div>
</div>
`
}
function applyTenantThemeVars() {
applyProjectTheme()
const color = window.__APP_CONFIG__?.user?.color
const secondaryColor = window.__APP_CONFIG__?.user?.secondary_color
const backgroundColor = window.__APP_CONFIG__?.user?.background_color
if (color) {
document.documentElement.style.setProperty('--App-primary-color', color)
}
if (secondaryColor) {
document.documentElement.style.setProperty('--App-secondary-color', secondaryColor)
}
if (backgroundColor) {
document.documentElement.style.setProperty('--App-background-color', backgroundColor)
}
}
export async function bootstrapApp() {
try {
const config = await withTimeout(loadAppConfig(), 20000, 'Config loading timeout')
setAppConfig(config)
applyTenantThemeVars()
startPwaManifest(config)
applyAppConfig()
const { app } = createVueApp()
const settingsStore = useSettingsStore()
await withTimeout(settingsStore.fetchSettings(), 45000, 'Settings loading timeout')
setupRouterGuards()
if (getToken()) {
await useUserStore().getMyInfo()
}
app.mount('#app')
;(window as BootWindow).__APP_BOOTSTRAPPED__ = true
clearBootTimeout()
sessionStorage.removeItem('__APP_BOOT_RECOVERY_DONE__')
sessionStorage.removeItem('__APP_BOOT_RETRY_CLICKED__')
} catch (error) {
showBootstrapError(error)
}
}
+18
View File
@@ -0,0 +1,18 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import VueLazyLoad from 'vue3-lazyload'
import VueApexCharts from 'vue3-apexcharts'
import App from '@/App.vue'
import router from '@/router'
export function createVueApp() {
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.use(VueLazyLoad, {})
app.use(router)
app.use(VueApexCharts)
return { app, pinia }
}
+157
View File
@@ -0,0 +1,157 @@
import { computed, watch } from 'vue'
import router from '@/router'
import { authenticateWithFingerprint, getToken, isIphone, removeToken } from '@/utils'
import { useUserStore } from '@/store/userStore'
import { useWalletStore } from '@/store/walletStore'
import { useFundUserStore } from '@/store/fundStore'
import type { NavigationGuardNext, RouteLocationNormalized } from 'vue-router'
import { useAuthStore } from '@/store/useAuthStore'
import { getConfigApp } from '@/utils/config'
import { getFingerPosition, setFingerPosition } from '@/app/authBoot'
const PUBLIC_ROUTE_NAMES = ['login', '404', 'educate', 'aboutUs'] as const
export function setupRouterGuards() {
const userStore = useUserStore()
const walletStore = useWalletStore()
const fundStore = useFundUserStore()
const authStore = useAuthStore()
const isApple: boolean = isIphone()
let isFirst = true
let timeoutId: ReturnType<typeof setTimeout> | null = null
let hasFingerPrint = false
const currentRoute = computed(() => router.currentRoute.value.fullPath)
async function start() {
if (getToken()) {
await walletStore.getLimitedPrice()
await walletStore.getLimitedBuyGold()
await fundStore.getMyFund()
}
}
function goToLogin(to: RouteLocationNormalized, next: NavigationGuardNext) {
if (to.name !== 'login') {
return next({ name: 'login' })
}
return next()
}
function goToHome(to: RouteLocationNormalized, next: NavigationGuardNext) {
if (to.name !== 'home') {
return next({ name: 'home' })
}
return next()
}
function isPublicRoute(name: string | symbol | null | undefined) {
return PUBLIC_ROUTE_NAMES.includes(name as (typeof PUBLIC_ROUTE_NAMES)[number])
}
router.beforeEach(async (to, from, next) => {
authStore.setLoading(true)
try {
if (getToken() && !userStore.userInfo) {
await userStore.getMyInfo()
}
if (timeoutId) {
clearTimeout(timeoutId)
}
if (to.name == 'giftCard' && getConfigApp('name') != 'garoosi') {
return next({ name: from.name })
}
if (from.name !== 'login' || from.name !== undefined) {
timeoutId = setTimeout(() => {
if (hasFingerPrint) {
setFingerPosition(false)
console.log('Fingerprint authentication reset after 5 seconds of inactivity')
}
}, 180000)
}
if (isFirst) {
isFirst = false
if (getToken()) {
await userStore.getMyInfo()
if (!userStore.userInfo) {
return goToLogin(to, next)
}
const finger_print = userStore.userInfo.finger_print
hasFingerPrint = !!finger_print
if (isApple && finger_print) {
removeToken()
return goToLogin(to, next)
}
if (!isApple && finger_print) {
setFingerPosition(await authenticateWithFingerprint())
if (!getFingerPosition()) {
return goToLogin(to, next)
}
if (to.name === 'login') {
return goToHome(to, next)
}
return next()
}
if (to.name == 'login') {
return goToHome(to, next)
}
return next()
}
if (isPublicRoute(to.name)) {
return next()
}
return goToLogin(to, next)
}
if (getFingerPosition()) {
if (getToken()) {
if (to.name === 'login') {
return next({ name: from.name || 'home' })
}
return next()
}
if (isPublicRoute(to.name)) {
return next()
}
return next({ name: 'login' })
}
if (getToken() && !hasFingerPrint) {
return next()
}
if (isPublicRoute(to.name)) {
if (to.name === 'login') {
return goToLogin(to, next)
}
return next()
}
return goToLogin(to, next)
} finally {
authStore.setLoading(false)
}
})
watch(currentRoute, async () => (userStore.userInfo ? '' : await start()))
}