new UI and handle cashing
This commit is contained in:
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
}
|
||||
@@ -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()))
|
||||
}
|
||||
Reference in New Issue
Block a user