Sync live quote handling, SSE product images, store hours, file URL normalization, PWA icon cache, TV monitor, about software, SuperAdmin menu gates, price alerts, and related UX from modular while preserving boursecu branding and config constraints. Co-authored-by: Cursor <cursoragent@cursor.com>
195 lines
5.5 KiB
TypeScript
195 lines
5.5 KiB
TypeScript
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
|
import type { FormInstance } from 'ant-design-vue'
|
|
import { message } from 'ant-design-vue'
|
|
import type SmsOtpField from '@/components/SmsOtpField.vue'
|
|
import type { Login } from '@/models/login'
|
|
import {
|
|
authenticateWithFingerprint,
|
|
getToken,
|
|
loading,
|
|
setToken,
|
|
} from '@/utils'
|
|
import useCountdownTimer from '@/utils/useCountdownTimer'
|
|
import { loginCustomer, verifyToken } from '@/api/login'
|
|
import { useUserStore } from '@/store/userStore'
|
|
import { useWalletStore } from '@/store/walletStore'
|
|
import { useSettingsStore } from '@/store/settingsStore'
|
|
import { setTruth } from '@/app/authBoot'
|
|
import { completeLoginNavigation } from '@/layouts/composables/useMobileScrollReset'
|
|
import { normalizeAuthFormFields, persianToEnglishDigits } from '@/utils/persianDigits'
|
|
import { beginSmsOtpCapture, clearPendingSmsOtp } from '@/utils/webOtp'
|
|
import { usePwaInstall } from '@/composables/usePwaInstall'
|
|
import {
|
|
canRequestBrowserNotification,
|
|
requestBrowserNotificationPermission,
|
|
} from '@/utils/browserNotification'
|
|
|
|
export function useLoginForm() {
|
|
const { time, onFinish, reset } = useCountdownTimer('00:02:00', 'mm:ss')
|
|
const enableSendAgainCode = ref(false)
|
|
const stepTwoVerify = ref(false)
|
|
const formRef = ref<FormInstance>()
|
|
const userStore = useUserStore()
|
|
const walletStore = useWalletStore()
|
|
const refOtpInput = ref<InstanceType<typeof SmsOtpField> | null>(null)
|
|
const isSMS = ref(true)
|
|
const isRegister = ref<boolean | undefined>()
|
|
const { tryInstallOrOpenGuide, completeGuide } = usePwaInstall()
|
|
|
|
const userFormState = reactive<Login>({
|
|
provider: 'customer',
|
|
})
|
|
|
|
onFinish(() => {
|
|
enableSendAgainCode.value = true
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
// SmsOtpField cleans its own listener
|
|
})
|
|
|
|
async function reSendCode() {
|
|
userFormState.token = undefined
|
|
clearPendingSmsOtp()
|
|
refOtpInput.value?.clearInput?.()
|
|
enableSendAgainCode.value = false
|
|
normalizeAuthFormFields(userFormState)
|
|
beginSmsOtpCapture()
|
|
await loginCustomer(userFormState)
|
|
refOtpInput.value?.startWebOtp?.()
|
|
refOtpInput.value?.focusInput?.()
|
|
reset()
|
|
}
|
|
|
|
function setIsSMS(val: boolean) {
|
|
isSMS.value = val
|
|
if (!val) stepTwoVerify.value = false
|
|
userFormState.token = ''
|
|
userFormState.password = ''
|
|
}
|
|
|
|
async function enterFingerPrint() {
|
|
const fingerPrint = await authenticateWithFingerprint()
|
|
if (fingerPrint) {
|
|
setTruth()
|
|
await completeLoginNavigation()
|
|
} else {
|
|
message.error('خطا در اعتبارسنجی')
|
|
}
|
|
}
|
|
|
|
function handleOnChange(value: string) {
|
|
userFormState.token = persianToEnglishDigits(String(value ?? ''))
|
|
normalizeAuthFormFields(userFormState)
|
|
}
|
|
|
|
async function handleOnComplete(value: string) {
|
|
userFormState.token = persianToEnglishDigits(String(value ?? ''))
|
|
normalizeAuthFormFields(userFormState)
|
|
phoneVerification()
|
|
}
|
|
|
|
async function login(accessToken: string | undefined) {
|
|
setToken(accessToken)
|
|
await userStore.getMyInfo()
|
|
await walletStore.getMyWallet()
|
|
setTruth()
|
|
await completeLoginNavigation()
|
|
}
|
|
|
|
function phoneVerification() {
|
|
return formRef.value?.validateFields().then(async () => {
|
|
try {
|
|
loading.value = true
|
|
normalizeAuthFormFields(userFormState)
|
|
if (isSMS.value) {
|
|
if (!stepTwoVerify.value) {
|
|
userFormState.token = undefined
|
|
userFormState.password = undefined
|
|
userFormState.provider = 'customer'
|
|
beginSmsOtpCapture()
|
|
const { id } = await loginCustomer(userFormState)
|
|
userFormState.id = id
|
|
stepTwoVerify.value = true
|
|
reset()
|
|
} else {
|
|
// نوتیف را در gesture شروع کن؛ await نکن تا ورود بلاک نشود
|
|
if (canRequestBrowserNotification()) {
|
|
void requestBrowserNotificationPermission()
|
|
}
|
|
const { access_token } = await verifyToken(userFormState)
|
|
await login(access_token)
|
|
}
|
|
} else {
|
|
userFormState.token = undefined
|
|
if (canRequestBrowserNotification()) {
|
|
void requestBrowserNotificationPermission()
|
|
}
|
|
const { access_token } = await loginCustomer(userFormState)
|
|
await login(access_token)
|
|
}
|
|
} catch (e) {
|
|
console.log(e)
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
})
|
|
}
|
|
|
|
function editPhoneNumber() {
|
|
stepTwoVerify.value = false
|
|
}
|
|
|
|
async function isOpenRegister() {
|
|
const settingsStore = useSettingsStore()
|
|
await settingsStore.fetchSettings()
|
|
isRegister.value = settingsStore.settings.find(
|
|
(s) => s?.key === 'open_register' && s?.is_active === true,
|
|
)
|
|
}
|
|
|
|
function installWebApp() {
|
|
void tryInstallOrOpenGuide()
|
|
}
|
|
|
|
function proceedWithInstall() {
|
|
void completeGuide()
|
|
}
|
|
|
|
const submitLabel = computed(() => {
|
|
if (!isSMS.value) return 'ورود'
|
|
return stepTwoVerify.value ? 'ورود' : 'ارسال کد'
|
|
})
|
|
|
|
const showFingerprint = computed(
|
|
() => Boolean(getToken() && userStore.userInfo?.finger_print),
|
|
)
|
|
|
|
onMounted(async () => {
|
|
await isOpenRegister()
|
|
})
|
|
|
|
return {
|
|
time,
|
|
enableSendAgainCode,
|
|
stepTwoVerify,
|
|
formRef,
|
|
refOtpInput,
|
|
isSMS,
|
|
isRegister,
|
|
userFormState,
|
|
loading,
|
|
submitLabel,
|
|
showFingerprint,
|
|
setIsSMS,
|
|
reSendCode,
|
|
enterFingerPrint,
|
|
handleOnChange,
|
|
handleOnComplete,
|
|
phoneVerification,
|
|
editPhoneNumber,
|
|
installWebApp,
|
|
proceedWithInstall,
|
|
}
|
|
}
|