Skip to content

Commit

Permalink
Toastify added (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
moiskillnadne authored Nov 3, 2024
1 parent 29fea52 commit 58ee0af
Show file tree
Hide file tree
Showing 12 changed files with 173 additions and 12 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"react-dom": "^18.3.1",
"react-i18next": "^15.1.0",
"react-router-dom": "^6.27.0",
"react-toastify": "^10.0.6",
"tailwindcss": "^3.4.14",
"zod": "^3.23.8"
},
Expand Down
2 changes: 2 additions & 0 deletions src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'

import { AppToastContainer } from './system/AppToastContainer'
import i18nManager from './system/i18n.manager'

import ApplicationRouter from '~/pages'
Expand All @@ -15,6 +16,7 @@ function App() {
return (
<QueryClientProvider client={queryClient}>
<ApplicationRouter />
<AppToastContainer />
</QueryClientProvider>
)
}
Expand Down
1 change: 1 addition & 0 deletions src/app/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { createRoot } from 'react-dom/client'

import App from './App'
import './index.css'
import 'react-toastify/dist/ReactToastify.css'

if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
Expand Down
19 changes: 19 additions & 0 deletions src/app/system/AppToastContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Bounce, ToastContainer } from 'react-toastify'

export const AppToastContainer = () => {
return (
<ToastContainer
position="top-right"
autoClose={3500}
hideProgressBar={false}
newestOnTop={false}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover
theme="light"
transition={Bounce}
/>
)
}
27 changes: 27 additions & 0 deletions src/feature/LoginOTP/lib/useOTPLogin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { authService } from '~/shared/api/auth.service'
type Props = {
onLoginSuccess?: () => void
onCodeSuccess?: () => void

onLoginError?: (error: unknown) => void
onCodeError?: (error: unknown) => void
}

export const useOTPLogin = (props?: Props) => {
Expand All @@ -21,6 +24,10 @@ export const useOTPLogin = (props?: Props) => {
},
onError: (err) => {
console.error(`[LoginMutation:onError] ${JSON.stringify(err)}`)

if (props?.onLoginError) {
props.onLoginError(err)
}
},
})

Expand All @@ -35,6 +42,10 @@ export const useOTPLogin = (props?: Props) => {
},
onError: (err) => {
console.info(`[CodeMutation:onError]: ${JSON.stringify(err)}`)

if (props?.onCodeError) {
props.onCodeError(err)
}
},
})

Expand All @@ -45,16 +56,32 @@ export const useOTPLogin = (props?: Props) => {
[loginMutation],
)

const tryLoginPromise = useCallback(
async (email: string) => {
return loginMutation.mutateAsync({ email })
},
[loginMutation],
)

const confirmLogin = useCallback(
(email: string, code: string) => {
codeMutation.mutate({ email, code })
},
[codeMutation],
)

const confirmLoginPromise = useCallback(
async (email: string, code: string) => {
return codeMutation.mutateAsync({ email, code })
},
[codeMutation],
)

return {
tryLogin,
tryLoginPromise,
confirmLogin,
confirmLoginPromise,
loadingState: {
isLoading: loginMutation.isPending || codeMutation.isPending,
isTryLoginLoading: loginMutation.isPending,
Expand Down
9 changes: 8 additions & 1 deletion src/i18n/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@
"yes": "Yes",
"no": "No",
"welcomeToChallengeLogger": ["Welcome to", "Challenge Logger"],
"yourAccount": "Your account"
"yourAccount": "Your account",
"sendingEmail": "Sending email",
"sendingCode": "Sending code",
"emailSent": "Email sent",
"codeSent": "Code sent",
"failedToSendEmail": "Failed to send email",
"failedToSendCode": "Failed to send code",
"oopsSomethingWentWrong": "Oops, something went wrong"
}
}
9 changes: 8 additions & 1 deletion src/i18n/ru/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@
"yes": "Да",
"no": "Нет",
"welcomeToChallengeLogger": ["Добро пожаловать в", "Challenge Logger"],
"yourAccount": "Ваш аккаунт"
"yourAccount": "Ваш аккаунт",
"sendingEmail": "Отправка письма",
"sendingCode": "Отправка кода",
"emailSent": "Письмо отправлено",
"codeSent": "Код отправлен",
"failedToSendEmail": "Не удалось отправить письмо",
"failedToSendCode": "Не удалось отправить код",
"oopsSomethingWentWrong": "Упс, что-то пошло не так"
}
}
1 change: 1 addition & 0 deletions src/shared/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './useVisitorId'
export * from './useToast'
53 changes: 53 additions & 0 deletions src/shared/hooks/useToast.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useCallback } from 'react'

import { toast } from 'react-toastify'

type PromiseToastProps = {
pending: string
success: string
error: string
}

export const useToast = () => {
const showInfoToast = useCallback((message: string) => {
return toast.info(message)
}, [])

const showSuccessToast = useCallback((message: string) => {
return toast.success(message)
}, [])

const showErrorToast = useCallback((message: string) => {
return toast.error(message)
}, [])

const showWarningToast = useCallback((message: string) => {
return toast.warning(message)
}, [])

const showPromiseToast = useCallback(
(promise: Promise<unknown>, promiseParams: PromiseToastProps) => {
return toast.promise(promise, promiseParams)
},
[],
)

const dismissAllToasts = useCallback(() => {
return toast.dismiss()
}, [])

const dismissToast = useCallback((toastId: string) => {
return toast.dismiss(toastId)
}, [])

return {
showToast: toast,
showInfoToast,
showSuccessToast,
showErrorToast,
showWarningToast,
showPromiseToast,
dismissAllToasts,
dismissToast,
}
}
2 changes: 1 addition & 1 deletion src/widget/Login/ui/LoginButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useCustomTranslation } from '~/feature/translation'
type Props = {
labelKey: string
onClick: () => void
isLoading: boolean
isLoading?: boolean
isDisabled?: boolean
}

Expand Down
41 changes: 32 additions & 9 deletions src/widget/Login/ui/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import z from 'zod'

import { LoginButton } from './LoginButton'
import { LoginHeader } from './LoginHeader'
import { useToast } from '../../../shared/hooks'

import { useAuthenticateViaPasskeys } from '~/feature/AuthorizePasskeys/'
import { useOTPLogin } from '~/feature/LoginOTP'
Expand All @@ -23,6 +24,8 @@ export const LoginWidget = () => {

const navigate = useNavigate()

const { showPromiseToast, dismissAllToasts, showErrorToast } = useToast()

const codeInputVisibility = new Map([
[true, '40px'],
[false, '0px'],
Expand All @@ -31,9 +34,24 @@ export const LoginWidget = () => {
const [email, setEmail] = useState<string>('')
const [code, setCode] = useState<string>('')

const { tryLogin, confirmLogin, loadingState, mutationState } = useOTPLogin({
const { tryLoginPromise, confirmLoginPromise, loadingState, mutationState } = useOTPLogin({
onCodeSuccess: () => {
return navigate(Routes.HOME)
setTimeout(() => {
dismissAllToasts()
return navigate(Routes.HOME)
}, 700)
},
onCodeError(error) {
if (error instanceof Error) {
return showErrorToast(error.message)
}
return showErrorToast(t('oopsSomethingWentWrong'))
},
onLoginError(error) {
if (error instanceof Error) {
return showErrorToast(error.message)
}
return showErrorToast(t('oopsSomethingWentWrong'))
},
})

Expand Down Expand Up @@ -65,8 +83,12 @@ export const LoginWidget = () => {
const loginOTP = useCallback(async () => {
const emailValue = processEmailValue()

tryLogin(emailValue)
}, [tryLogin, processEmailValue])
showPromiseToast(tryLoginPromise(emailValue), {
pending: t('sendingEmail'),
success: t('emailSent'),
error: t('failedToSendEmail'),
})
}, [processEmailValue, showPromiseToast, t, tryLoginPromise])

const confirmLoginOTP = useCallback(() => {
if (!isEmailSent) {
Expand All @@ -81,8 +103,12 @@ export const LoginWidget = () => {
throw new Error(JSON.stringify(codeSafeParse.error))
}

confirmLogin(emailValue, codeSafeParse.data)
}, [code, confirmLogin, isEmailSent, processEmailValue])
showPromiseToast(confirmLoginPromise(emailValue, codeSafeParse.data), {
pending: t('sendingCode'),
success: t('codeSent'),
error: t('failedToSendCode'),
})
}, [code, confirmLoginPromise, isEmailSent, processEmailValue, showPromiseToast, t])

const loginPasskeys = useCallback(async () => {
const emailValue = processEmailValue()
Expand Down Expand Up @@ -114,7 +140,6 @@ export const LoginWidget = () => {
<div className="flex flex-1 flex-col items-center">
<div className="flex flex-col items-center gap-[8px] mb-[64px]">
<LoginHeader />

<input
type="email"
name="email"
Expand All @@ -124,7 +149,6 @@ export const LoginWidget = () => {
className="bg-transparent focus:outline-none duration-300 h-[40px] placeholder-black/50 border-b-2 border-black hover:border-black/20 focus:border-black/50 w-[300px]"
onChange={(e) => setEmail(e.target.value)}
/>

<div
className="overflow-hidden duration-300"
style={{ height: `${codeInputVisibility.get(mutationState.loginMutation.isSuccess)}` }}
Expand All @@ -138,7 +162,6 @@ export const LoginWidget = () => {
onChange={(e) => setCode(e.target.value)}
/>
</div>

<LoginButton
labelKey={'login'}
onClick={isEmailSent ? confirmLoginOTP : loginOTP}
Expand Down
20 changes: 20 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2984,6 +2984,7 @@ __metadata:
react-dom: "npm:^18.3.1"
react-i18next: "npm:^15.1.0"
react-router-dom: "npm:^6.27.0"
react-toastify: "npm:^10.0.6"
tailwindcss: "npm:^3.4.14"
typescript: "npm:^5.6.3"
vite: "npm:^5.4.10"
Expand Down Expand Up @@ -3026,6 +3027,13 @@ __metadata:
languageName: node
linkType: hard

"clsx@npm:^2.1.0":
version: 2.1.1
resolution: "clsx@npm:2.1.1"
checksum: 10c0/c4c8eb865f8c82baab07e71bfa8897c73454881c4f99d6bc81585aecd7c441746c1399d08363dc096c550cceaf97bd4ce1e8854e1771e9998d9f94c4fe075839
languageName: node
linkType: hard

"color-convert@npm:^1.9.0":
version: 1.9.3
resolution: "color-convert@npm:1.9.3"
Expand Down Expand Up @@ -5862,6 +5870,18 @@ __metadata:
languageName: node
linkType: hard

"react-toastify@npm:^10.0.6":
version: 10.0.6
resolution: "react-toastify@npm:10.0.6"
dependencies:
clsx: "npm:^2.1.0"
peerDependencies:
react: ">=18"
react-dom: ">=18"
checksum: 10c0/4042b716d008295d0feab32488d1e88ec655a1b7a9176fa7d253c70387578a8a0c04aca0ff86d20e1722f3b4baadae8970f50f462940d67a90453c307dd350a9
languageName: node
linkType: hard

"react@npm:^18.3.1":
version: 18.3.1
resolution: "react@npm:18.3.1"
Expand Down

0 comments on commit 58ee0af

Please sign in to comment.