Files
boursecu_user/src/views/login/composables/useLoginForm.ts
T
safavimohadeseh78andCursor 9356a2ab5e Port modular price, product, and PWA fixes into boursecu
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>
2026-09-02 08:26:52 -04:00

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,
}
}