react-hot-toast#toast TypeScript Examples
The following examples show how to use
react-hot-toast#toast.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.ts From personal-archive with MIT License | 6 votes |
useApi = (): [
boolean,
(apiFn: () => Promise<void>) => Promise<void>,
AxiosError | null,
] => {
const [fetching, setFetching] = useState(false)
const [error, setError] = useState(null as AxiosError | null)
const fetchData = (apiFn: () => Promise<void>) => {
setFetching(true)
return apiFn()
.catch(err => {
if (err) {
if (err.response != null && err.response.data != null && err.response.data.message != null) {
err.message = err.response.data.message
}
toast.error(err.toString())
setError(err)
console.error({ err })
}
})
.finally(() => setFetching(false))
}
return [ fetching, useCallback(fetchData, []), error ]
}
Example #2
Source File: dismissibleToast.tsx From mysterium-vpn-desktop with MIT License | 6 votes |
dismissibleToast = (message: JSX.Element | string | null): ((t: Toast) => JSX.Element) => {
return function dismissibleToast(t: Toast): JSX.Element {
return (
<Container>
<div>{message}</div>
<Dismiss onClick={() => toast.dismiss(t.id)}>
<FontAwesomeIcon icon={faTimes} />
</Dismiss>
</Container>
)
}
}
Example #3
Source File: EditNoteParagraphPage.tsx From personal-archive with MIT License | 6 votes |
useSubmit = (noteID: number, paragraphID: number): [
boolean,
(content: string, referencedArticles: Article[], referencedWebURLs: string[]) => void,
] => {
const [fetching, editParagraph] = useRequestEditParagraph()
const history = useHistory()
const submit = (content: string, referencedArticles: Article[], referenceWebURLs: string[]) => {
if (content.trim().length === 0) {
toast.error('content required')
return
}
const articleIDs = referencedArticles.map(a => a.id)
editParagraph(noteID, paragraphID, content, articleIDs, referenceWebURLs)
.then(() => history.push(`/notes/${noteID}`))
}
return [fetching, submit]
}
Example #4
Source File: NewNotePage.tsx From personal-archive with MIT License | 6 votes |
useSubmit = (): [
boolean,
(title: string, content: string, referenceArticles: Article[], referenceWebURLs: string[]) => void,
] => {
const [fetching, createNote, note] = useRequestCreateNote()
const history = useHistory()
const submit = (title: string, content: string, referenceArticles: Article[], referenceWebURLs: string[]) => {
if (title.trim().length === 0) {
toast.error('title required')
return
}
if (content.trim().length === 0) {
toast.error('content required')
return
}
const articleIDs = referenceArticles.map(({id}) => id)
createNote(title, content, articleIDs, referenceWebURLs)
.then(() => history.push(`/notes/${note.id}`))
}
return [ fetching, submit ]
}
Example #5
Source File: NewNoteParagraphPage.tsx From personal-archive with MIT License | 6 votes |
useSubmit = (noteID: number): [
boolean,
(content: string, referencedArticles: Article[], referenceWebURLs: string[]) => void,
] => {
const [fetching, createParagraph] = useRequestCreateParagraph()
const history = useHistory()
const submit = (content: string, referencedArticles: Article[], referenceWebURLs: string[]) => {
if (content.trim().length === 0) {
toast.error('content required')
return
}
const articleIDs = referencedArticles.map(a => a.id)
createParagraph(noteID, content, articleIDs, referenceWebURLs)
.then(note => history.push(`/notes/${noteID}`))
}
return [fetching, submit]
}
Example #6
Source File: SearchPage.tsx From personal-archive with MIT License | 6 votes |
SearchPage: FC = () => {
const keyword = useQuery().get('q') || ''
const page = usePage()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [ fetching, searchArticles, _, articles, pagination] = useRequestSearchArticles()
const history = useHistory()
useEffect(() => {
if (keyword.length <= 1) {
toast.error('keyword should be more than 2 characters')
return
}
searchArticles(keyword, page)
}, [keyword, page, searchArticles])
const onReload = () => searchArticles(keyword, page)
return (
<ArticleTagTreeLayout loading={fetching}>
<Keyword>Keyword: {keyword}</Keyword>
<ArticleList
articles={articles}
pagination={pagination}
onSelectPage={page => history.push(`/articles/search?q=${encodeURIComponent(keyword)}&page=${page}`)}
onReload={onReload}
/>
<CommandPalette
keyword={keyword}
page={page}
/>
</ArticleTagTreeLayout>
)
}
Example #7
Source File: PocketSettingUnauthenticated.tsx From personal-archive with MIT License | 6 votes |
useActivate = (): [boolean, (consumerKey: string) => void] => {
const [fetching, obtainPocketRequestToken] = useRequestObtainPocketRequestToken()
const activate = (consumerKey: string) => {
if (consumerKey.length <= 0) {
toast.error('consumer key required')
return
}
const redirectURI = `${window.location.protocol}//${window.location.host}/settings/pocket-auth`
obtainPocketRequestToken(consumerKey, redirectURI)
.then(requestToken => {
window.location.href = `https://getpocket.com/auth/authorize?request_token=${requestToken}&redirect_uri=${redirectURI}`
})
}
return [fetching, activate]
}
Example #8
Source File: RoomCode.tsx From NextLevelWeek with MIT License | 6 votes |
export function RoomCode(props: RoomCodeProps) {
// Função de copar o código da sala.
function copyRoomCodeToClipboard() {
toast.success("Código da sala copiado com sucesso!", {
id: props.code,
style: {
width: "40rem",
},
});
navigator.clipboard.writeText(props.code);
}
return (
<>
<Toaster position="top-right" reverseOrder={false} />
<button className="room-code" onClick={copyRoomCodeToClipboard}>
<div>
<img src={copyImg} alt="Copy room code" />
</div>
<span>Sala #{props.code}</span>
</button>
</>
);
}
Example #9
Source File: use-notification.tsx From admin with MIT License | 6 votes |
useNotification = () => {
return (title: string, message: string, type: NotificationTypes) => {
toast.custom(
(t) => (
<Notification toast={t} type={type} title={title} message={message} />
),
{
position: "top-right",
duration: 3000,
}
)
}
}
Example #10
Source File: use-detect-change.tsx From admin with MIT License | 6 votes |
useDetectChange = ({ isDirty, reset, options }: UseDetectChangeProps) => {
useEffect(() => {
const { fn, title, message, icon } = options
const showToaster = () => {
toast.custom(
(t) => (
<SaveNotification
toast={t}
icon={icon}
title={title}
message={message}
onSave={fn}
reset={reset}
/>
),
{
position: "bottom-right",
duration: Infinity,
id: "form-change",
}
)
}
if (isDirty) {
showToaster()
} else {
toast.dismiss("form-change")
}
}, [isDirty, options])
}
Example #11
Source File: index.tsx From admin with MIT License | 6 votes |
Toaster = ({ visible, children, ...options }: ToasterProps) => {
React.useEffect(() => {
if (visible) {
toast.custom((t) => React.cloneElement(children, { toast: t }), {
...options,
})
} else {
toast.dismiss(options.id)
}
}, [visible, children])
return null
}
Example #12
Source File: AddReferenceWebDrawer.tsx From personal-archive with MIT License | 5 votes |
AddReferenceWebDrawer: FC<Props> = ({show, onConfirm, onCancel}) => {
const [url, setUrl] = useState('')
const clear = () => {
setUrl('')
}
const onClose = () => {
clear()
onCancel()
}
const onSubmit = () => {
if (url.length === 0) {
toast.error('url required')
return
}
if (!url.startsWith('http')) {
toast.error('invalid url')
return
}
clear()
onConfirm(url)
}
return (
<>
<Drawer shown={show} onClose={onClose} title="Add Web Reference">
<InputField
size="small"
value={url}
onChange={e => setUrl((e.target as any).value)}
onKeyDown={e => {
if (e.key === 'Enter') {
onSubmit()
}
}}
suffix={
<span role="button" onClick={onSubmit} style={{ marginRight: '10px' }}>
<FaSearch />
</span>
}
/>
<ButtonWrapper>
<Button onClick={onSubmit} size="small">Add</Button>
</ButtonWrapper>
</Drawer>
</>
)
}
Example #13
Source File: TopupSuccess.tsx From mysterium-vpn-desktop with MIT License | 5 votes |
TopupSuccess: React.FC = observer(() => {
const { payment, identity, navigation } = useStores()
const isOnboarding = identity.identity?.registrationStatus != IdentityRegistrationStatus.Registered
const handleAction = () => {
if (isOnboarding) {
navigation.push(locations.registering)
} else {
navigation.goHome()
}
}
useEffect(() => {
toast.success(`${payment.appCurrency}s will be credited to your wallet within next 1-3 minutes.`)
}, [])
return (
<ViewContainer>
<ViewNavBar>
<div style={{ width: 375, textAlign: "center" }}>
<StepProgressBar step={3} />
</div>
</ViewNavBar>
<ViewSplit>
<ViewSidebar>
<SideTop>
<TitleIcon>
<IconWallet color={brandLight} />
</TitleIcon>
<Title>Payment successful!</Title>
<TitleDescription>
{payment.appCurrency}s will be credited to your wallet within next 1-3 minutes.
</TitleDescription>
</SideTop>
<SideBot>
<BrandButton style={{ marginTop: "auto" }} onClick={handleAction}>
Continue
</BrandButton>
</SideBot>
</ViewSidebar>
<Content>
<div style={{ marginTop: "auto", marginBottom: "auto" }}>
<Heading2>
<FontAwesomeIcon className="icon" icon={faCheckCircle} color="#ffffff44" size="10x" />
<div style={{ marginTop: 20, cursor: "pointer" }} onClick={() => payment.downloadInvoice()}>
<FontAwesomeIcon
style={{ marginRight: 10 }}
className="icon"
icon={faDownload}
color="#ffffff44"
size="1x"
/>
Download invoice
</div>
</Heading2>
</div>
</Content>
</ViewSplit>
</ViewContainer>
)
})
Example #14
Source File: UseReferralCodePrompt.tsx From mysterium-vpn-desktop with MIT License | 5 votes |
UseReferralCodePrompt: React.FC<UseReferralCodePromptProps> = ({ visible, onSubmit, onCancel }) => {
const {
register,
handleSubmit,
reset,
trigger,
formState: { errors },
} = useForm<ReferralCodeFormFields>({ reValidateMode: "onSubmit" })
const { referral, payment } = useStores()
useEffect(() => {
if (!visible) {
reset()
referral.resetToken()
}
}, [visible])
const handleValidate = async () => {
await trigger()
}
return (
<Prompt
title="Enter a referral code"
visible={visible}
onSubmit={referral.token ? handleSubmit(onSubmit) : handleValidate}
onCancel={onCancel}
submitText={referral.token ? "Apply" : "OK"}
>
<PromptExplanation />
<PromptInput
autoFocus
placeholder="Code"
{...register("code", {
required: "This is required",
validate: {
valid: async (code) => {
if (referral.token && code === referral.token) {
// Do not revalidate and toast on 'Apply'
return true
}
const loadingToastID = toast.loading("Validating token...")
const valid = await referral.validateToken(code)
const dismissWait = valid ? 500 : 850
_.debounce(() => toast.dismiss(loadingToastID), dismissWait, { trailing: true })()
return valid || "This token is not valid"
},
},
})}
/>
<PromptValidation>{errors.code?.message}</PromptValidation>
{!!referral.rewardAmount && (
<RewardPreview>
<RewardIcon className="icon" icon={faCheckCircle} color={brand} size="2x" />
<RewardAmount>
You will be awarded {referral.rewardAmount} {payment.appCurrency}(s)
</RewardAmount>
</RewardPreview>
)}
</Prompt>
)
}
Example #15
Source File: NewRoom.tsx From NextLevelWeek with MIT License | 5 votes |
export function NewRoom() {
// Tendo acesso ao usuário autenticado.
const { user } = useAuth();
const history = useHistory();
const [newRoom, setNewRoom] = useState("");
// Função de criação de uma sala.
async function handleCreateRoom(event: FormEvent) {
// Prevenindo o comportamento padrão do formulário.
event.preventDefault();
// Tendo acesso ao valor do input.
// console.log(newRoom);
if (newRoom.trim() === "") {
toast.error("Room name cannot be empty.", {
icon: "⚠️",
});
return;
}
const roomRef = database.ref("rooms");
// "jogando" uma informação dentro de "rooms"
const firebaseRoom = await roomRef.push({
title: newRoom,
authorId: user?.id,
});
// Após o usuário crar a sala, ele será redirecionado para a nova sala criada.
history.push(`/rooms/${firebaseRoom.key}`);
}
return (
<div id="page-auth">
<Toaster position="top-right" reverseOrder={false} />
<aside>
<img
src={illustrationImg}
alt="Ilustração simbolizando perguntas e respostas."
/>
<strong>Crie salas de Q&E ao-vivo.</strong>
<p>Tire as dúvidas da sua audência em tempo-real.</p>
</aside>
<main>
<div className="main-content">
<img src={logoImg} alt="Letmeask" />
<h2>Criar uma nova sala</h2>
<form onSubmit={handleCreateRoom}>
<input
type="text"
placeholder="Nome da sala"
onChange={(event) => setNewRoom(event.target.value)}
value={newRoom}
/>
<Button type="submit">Criar sala</Button>
</form>
<p>
Quer entrar em uma sala existente ? <Link to="/">clique aqui</Link>
</p>
</div>
</main>
</div>
);
}
Example #16
Source File: EditNoteParagraphPage.tsx From personal-archive with MIT License | 5 votes |
useNoteAndParagraph = (noteID: number, paragraphID: number): [
boolean,
Note | null,
Paragraph | null,
Article[],
string[],
] => {
const [fetching, getNote, note, articles] = useRequestGetNote()
const [paragraph, setParagraph] = useState(null as Paragraph | null)
const [referencedArticles, setReferencedArticles] = useState([] as Article[])
const [referencedWebURLs, setReferencedWebURLs] = useState([] as string[])
const history = useHistory()
useEffect(() => {
getNote(noteID)
}, [getNote, noteID])
useEffect(() => {
if (!note) {
return
}
const paragraph = note!.paragraphs.find(p => p.id === paragraphID)
if (!paragraph) {
toast.error('invalid paragraph id')
setTimeout(() => history.goBack(), 3000)
return
}
const referencedArticleIDs = paragraph.referenceArticles.map(a => a.articleID)
const referencedWebURLs = paragraph.referenceWebs.map(w => w.url)
const referencedArticles = articles.filter(a => referencedArticleIDs.includes(a.id))
setParagraph(paragraph)
setReferencedArticles(referencedArticles)
setReferencedWebURLs(referencedWebURLs)
}, [note, articles, history, paragraphID])
return [fetching, note, paragraph, referencedArticles, referencedWebURLs]
}
Example #17
Source File: Room.tsx From NextLevelWeek with MIT License | 4 votes |
export function Room() {
// Apenas usuários autenticados podem enviar novas perguntas.
const { user } = useAuth();
// "Pegando" o código da sala através dos parâmetros.
const params = useParams<RoomParams>();
const roomId = params.id;
// Importando o Hook useRoom.
const { questions, title } = useRoom(roomId);
// Informação da nova pergunta.
const [newQuestion, setNewQuestion] = useState("");
// Função para a criação de uma nova pergunta.
async function handleSendQuestion(event: FormEvent) {
event.preventDefault();
if (newQuestion.trim() === "") {
toast.error("You cannot submit an empty question.", {
icon: "⚠️",
});
return;
}
// Se não existir nem um usuário, retornar um erro.
// Dica de Toast: https://react-hot-toast.com/
if (!user) {
throw new Error("You must be logged in.");
}
// Criando a pergunta em si.
const question = {
content: newQuestion,
author: {
name: user.name,
avatar: user.avatar,
},
// Se ela está com "Highlighte" o destaque que o Admin dá na pergunta.
isHighlighted: false,
// Se já foi respondida ou não.
isAnswered: false,
};
await database.ref(`rooms/${roomId}/questions`).push(question);
// "Apagando" a pergunta no textarea após ela ser enviada.
setNewQuestion("");
}
// Função do "like".
async function handleLikeQuestion(
questionId: string,
likeId: string | undefined
) {
if (likeId) {
// Remover o "like".
await database
.ref(`rooms/${roomId}/questions/${questionId}/likes/${likeId}`)
.remove();
} else {
// Adicionando o "like".
await database.ref(`rooms/${roomId}/questions/${questionId}/likes`).push({
authorId: user?.id,
});
}
}
return (
<div id="page-room">
<Toaster position="top-right" reverseOrder={false} />
<header>
<div className="content">
<img src={logoImg} alt="Letmeask" />
<RoomCode code={roomId} />
</div>
</header>
<main>
<div className="room-title">
<h1>Sala {title}</h1>
{questions.length > 0 && <span>{questions.length} pergunta(s)</span>}
</div>
<form onSubmit={handleSendQuestion}>
<textarea
placeholder="O que deseja perguntar ?"
onChange={(event) => setNewQuestion(event.target.value)}
value={newQuestion}
/>
<div className="form-footer">
{user ? (
<div className="user-info">
<img src={user.avatar} alt={user.name} />
<span>{user.name}</span>
</div>
) : (
<span>
Para enviar uma pergunta, <button>faça seu login</button>.
</span>
)}
<Button type="submit" disabled={!user}>
Enviar pergunta
</Button>
</div>
</form>
{/* {JSON.stringify(questions)} */}
<div className="question-list">
{questions.map((question) => {
return (
<Question
key={question.id}
content={question.content}
author={question.author}
isAnswered={question.isAnswered}
isHighlighted={question.isHighlighted}
>
{!question.isAnswered && (
<button
className={`like-button ${question.likeId ? "liked" : ""}`}
type="button"
aria-label="Marcar como gostei"
onClick={() => {
handleLikeQuestion(question.id, question.likeId);
}}
>
{question.likeCount > 0 && (
<span>{question.likeCount}</span>
)}
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7 22H4C3.46957 22 2.96086 21.7893 2.58579 21.4142C2.21071 21.0391 2 20.5304 2 20V13C2 12.4696 2.21071 11.9609 2.58579 11.5858C2.96086 11.2107 3.46957 11 4 11H7M14 9V5C14 4.20435 13.6839 3.44129 13.1213 2.87868C12.5587 2.31607 11.7956 2 11 2L7 11V22H18.28C18.7623 22.0055 19.2304 21.8364 19.5979 21.524C19.9654 21.2116 20.2077 20.7769 20.28 20.3L21.66 11.3C21.7035 11.0134 21.6842 10.7207 21.6033 10.4423C21.5225 10.1638 21.3821 9.90629 21.1919 9.68751C21.0016 9.46873 20.7661 9.29393 20.5016 9.17522C20.2371 9.0565 19.9499 8.99672 19.66 9H14Z"
stroke="#737380"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</button>
)}
</Question>
);
})}
</div>
</main>
</div>
);
}
Example #18
Source File: Home.tsx From NextLevelWeek with MIT License | 4 votes |
export function Home() {
const history = useHistory();
const { user, signInWithGoogle } = useAuth();
// Estado para armazenar o código da sala.
const [roomCode, setRoomCode] = useState("");
// Navegando para a página de criação de uma sala.
async function handleCreateRoom() {
// Se o usuário NÃO estiver autenticado.
if (!user) {
await signInWithGoogle();
}
// Se já estiver autenticado, redireciona.
history.push("/rooms/new");
}
// Função para entrar em uma sala existente.
async function handleJoinRoom(event: FormEvent) {
event.preventDefault();
// Se retornar vazio, nada irá acontecer.
if (roomCode.trim() === "") {
toast.error("Enter a valid room code.", {
icon: "⚠️",
});
return;
}
// Verificando se a sala que o usuário está tentando entrar realmente existe.
const roomRef = await database.ref(`rooms/${roomCode}`).get();
// Caso retorne falso.
if (!roomRef.exists()) {
// alert("Room does not exists.");
toast.error("Room does not exists.", {
icon: "⚠️",
});
return;
}
// Verificando se a sala já não está encerrada.
if (roomRef.val().endedAt) {
// alert("Room already closed.");
toast.error("Room already closed.", {
icon: "⚠️",
});
return;
}
// Caso verdadeiro.
history.push(`/rooms/${roomCode}`);
}
return (
<div id="page-auth">
<Toaster position="top-right" reverseOrder={false} />
<aside>
<img
src={illustrationImg}
alt="Ilustração simbolizando perguntas e respostas."
/>
<strong>Crie salas de Q&E ao-vivo.</strong>
<p>Tire as dúvidas da sua audência em tempo-real.</p>
</aside>
<main>
<div className="main-content">
<img src={logoImg} alt="Letmeask" />
<button onClick={handleCreateRoom} className="create-room">
<img src={googleIconImg} alt="Logo do Google" />
Crie sua sala com o Google
</button>
<div className="separator">ou entre em uma sala</div>
<form onSubmit={handleJoinRoom}>
<input
type="text"
placeholder="Digite o código da sala"
onChange={(event) => setRoomCode(event.target.value)}
value={roomCode}
/>
<Button type="submit">Entrar na sala</Button>
</form>
</div>
</main>
</div>
);
}
Example #19
Source File: talk.tsx From website with Apache License 2.0 | 4 votes |
export default function Talk() {
const router = useRouter();
const {data: lanyard} = useLanyard(DISCORD_ID);
return (
<div className="space-y-4">
<h1 className="text-2xl font-bold sm:text-3xl">Let's talk ?</h1>
<p>
Leave a message on the form below or get in touch through Discord,
Twitter or email.
</p>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="p-5 bg-gray-100 dark:bg-white/5 rounded-lg">
<form
className="space-y-2"
action="/api/form"
method="POST"
onSubmit={async event => {
event.preventDefault();
const values = Object.fromEntries(
new FormData(event.target as HTMLFormElement).entries(),
);
const promise = fetcher('/api/talk', {
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(values),
method: 'POST',
});
await toast
.promise(promise, {
success: 'Success!',
loading: 'Sending...',
error: (error: Error) =>
error?.message ?? 'Something went wrong...',
})
.then(async () => router.push('/thanks'))
.catch(() => null);
}}
>
<label htmlFor="email" className="block">
<span className="text-sm font-bold tracking-wide dark:text-white uppercase select-none text-opacity-50">
Email Address
</span>
<input
required
type="email"
name="email"
id="email"
className="block py-1 px-4 w-full font-sans text-lg bg-gray-200 dark:bg-white/5 rounded-md focus:outline-none focus:ring"
/>
</label>
<label htmlFor="body" className="block">
<span className="text-sm font-bold tracking-wide dark:text-white uppercase select-none text-opacity-50">
Your message
</span>
<textarea
rows={5}
name="body"
minLength={10}
id="body"
className="block py-1 px-4 w-full font-sans text-lg bg-gray-200 dark:bg-white/5 rounded-md focus:outline-none focus:ring resize-none"
/>
</label>
<div className="block pt-2">
<button
type="submit"
className="inline-flex items-center py-2 px-8 space-x-2 text-lg text-blue-100 dark:text-white bg-blue-700 dark:bg-white/5 dark:hover:bg-white/10 rounded-full focus:outline-none focus:ring"
>
<span>Send</span> <RiSendPlane2Line />
</button>
</div>
</form>
</div>
<div>
<ul className="space-y-2 list-disc list-inside">
<ListItem icon={HiOutlineMail} text="[email protected]" />
<ListItem
icon={SiDiscord}
text={
lanyard ? (
<span className="flex items-center space-x-1">
<span>
{lanyard.discord_user.username}#
{lanyard.discord_user.discriminator}
</span>
<span
className={`${
statusMap[
lanyard.discord_status as keyof typeof statusMap
]
} h-2 w-2 inline-block rounded-full`}
/>
</span>
) : null
}
/>
<ListItem icon={SiTwitter} text="alistaiiiir" />
<ListItem icon={RiPhoneLine} text="+1 (424) 395-8523" />
</ul>
</div>
</div>
</div>
);
}
Example #20
Source File: CoingatePaymentOptions.tsx From mysterium-vpn-desktop with MIT License | 4 votes |
CoingatePaymentOptions: React.FC = observer(() => {
const { payment } = useStores()
const navigate = useNavigate()
const [loading, setLoading] = useState(false)
const isOptionActive = (cur: string) => {
return payment.paymentCurrency == cur
}
const selectOption = (cur: string) => () => {
payment.setPaymentCurrency(cur)
}
const setUseLightning = (): void => {
const val = !payment.lightningNetwork
payment.setLightningNetwork(val)
}
const handleNextClick = async () => {
setLoading(() => true)
try {
await payment.createOrder()
setLoading(() => false)
navigate("../" + topupSteps.coingateOrderSummary)
} catch (err) {
setLoading(() => false)
const msg = parseError(err)
logErrorMessage("Could not create a payment order", msg)
toast.error(dismissibleToast(<span>{msg.humanReadable}</span>))
}
}
const options = payment.paymentMethod?.gatewayData.currencies.filter((it) => it !== Currency.MYST) || []
return (
<ViewContainer>
<ViewNavBar onBack={() => navigate(-1)}>
<div style={{ width: 375, textAlign: "center" }}>
<StepProgressBar step={1} />
</div>
</ViewNavBar>
<ViewSplit>
<ViewSidebar>
<SideTop>
<IconWallet color={brandLight} />
<Title>Top up your account</Title>
<TitleDescription>Select the cryptocurrency in which the top-up will be done</TitleDescription>
</SideTop>
<SideBot>
<AmountSelect>
{options.map((opt) => (
<AmountToggle
key={opt}
active={isOptionActive(opt)}
onClick={selectOption(opt)}
inactiveColor={lightBlue}
height="36px"
justify="center"
>
<div style={{ textAlign: "center" }}>
<OptionValue>{opt}</OptionValue>
</div>
</AmountToggle>
))}
</AmountSelect>
{isLightningAvailable(payment.paymentCurrency) && (
<LightningCheckbox checked={payment.lightningNetwork} onChange={setUseLightning}>
Use lightning network
</LightningCheckbox>
)}
{payment.paymentCurrency == Currency.MYST && (
<Paragraph style={{ color: "red" }}>
<FontAwesomeIcon icon={faExclamationTriangle} style={{ marginRight: 5 }} />
{Currency.MYST} is currently only supported on the Ethereum network!
</Paragraph>
)}
<BrandButton
style={{ marginTop: "auto" }}
onClick={handleNextClick}
loading={loading}
disabled={loading || !payment.paymentCurrency}
>
Next
</BrandButton>
</SideBot>
</ViewSidebar>
<ViewContent>
<div style={{ paddingTop: 100 }}>
<CryptoAnimation currency={payment.paymentCurrency} />
</div>
</ViewContent>
</ViewSplit>
</ViewContainer>
)
})
Example #21
Source File: MystSelectAmount.tsx From mysterium-vpn-desktop with MIT License | 4 votes |
MystSelectAmount: React.FC = observer(() => {
const { payment } = useStores()
const navigate = useNavigate()
const [loading, setLoading] = useState(false)
const isOptionActive = (amt: number) => {
return payment.topUpAmountUSD == amt
}
const selectOption = (amt: number) => () => {
payment.setTopupAmountUSD(amt)
}
const [estimates, setEstimates] = useState<EntertainmentEstimateResponse | undefined>(undefined)
useEffect(() => {
if (payment.topUpAmountUSD) {
payment.estimateEntertainment({ USD: payment.topUpAmountUSD }).then((res) => setEstimates(res))
}
}, [payment.topUpAmountUSD])
const handleNextClick = async () => {
setLoading(() => true)
try {
await payment.createOrder()
setLoading(() => false)
navigate("../" + topupSteps.coingateOrderSummary)
} catch (err) {
setLoading(() => false)
const msg = parseError(err)
logErrorMessage("Could not create a payment order", msg)
toast.error(dismissibleToast(<span>{msg.humanReadable}</span>))
}
}
return (
<ViewContainer>
<ViewNavBar onBack={() => navigate(-1)}>
<div style={{ width: 375, textAlign: "center" }}>
<StepProgressBar step={1} />
</div>
</ViewNavBar>
<ViewSplit>
<ViewSidebar>
<SideTop>
<IconWallet color={brandLight} />
<Title>Top up your account</Title>
<TitleDescription>Select the desired amount in {payment.appFiatCurrency}</TitleDescription>
</SideTop>
<SideBot>
<AmountSelect>
{payment.orderOptions.map((opt: number) => (
<AmountToggle
key={opt}
active={isOptionActive(opt)}
onClick={selectOption(opt)}
inactiveColor={lightBlue}
height="63px"
justify="center"
>
<div style={{ textAlign: "center" }}>
<Amount>{opt}</Amount>
<Currency>{payment.appFiatCurrency}</Currency>
</div>
</AmountToggle>
))}
</AmountSelect>
<BrandButton
style={{ marginTop: "15px" }}
onClick={handleNextClick}
disabled={!payment.topUpAmountUSD || loading}
loading={loading}
>
Next
</BrandButton>
</SideBot>
</ViewSidebar>
<Content>
{!!estimates && (
<>
<EntertainmentBlocks>
<EntertainmentBlock>
<BlockIcon>
<IconPlay color={brandLight} />
</BlockIcon>
<BlockMetric>{estimates.videoMinutes}h</BlockMetric>
<EntertainmentExplanation>Online video</EntertainmentExplanation>
</EntertainmentBlock>
<EntertainmentBlock>
<BlockIcon>
<IconMusic color={brandLight} />
</BlockIcon>
<BlockMetric>{estimates.musicMinutes}h</BlockMetric>
<EntertainmentExplanation>Online music</EntertainmentExplanation>
</EntertainmentBlock>
<EntertainmentBlock>
<BlockIcon>
<IconCloudDownload color={brandLight} />
</BlockIcon>
<BlockMetric>{estimates.trafficMb}GiB</BlockMetric>
<EntertainmentExplanation>of data download</EntertainmentExplanation>
</EntertainmentBlock>
<EntertainmentBlock>
<BlockIcon>
<IconDocument color={brandLight} />
</BlockIcon>
<BlockMetric>{estimates.browsingMinutes}h</BlockMetric>
<EntertainmentExplanation>Web browsing</EntertainmentExplanation>
</EntertainmentBlock>
</EntertainmentBlocks>
</>
)}
</Content>
</ViewSplit>
</ViewContainer>
)
})
Example #22
Source File: PaypalPaymentOptions.tsx From mysterium-vpn-desktop with MIT License | 4 votes |
PaypalPaymentOptions: React.FC = observer(() => {
const { payment } = useStores()
const navigate = useNavigate()
const [loading, setLoading] = useState(false)
const isOptionActive = (cur: string) => {
return payment.paymentCurrency == cur
}
const selectOption = (cur: string) => () => {
payment.setPaymentCurrency(cur)
}
const handleNextClick = async () => {
setLoading(() => true)
try {
await payment.createOrder()
setLoading(() => false)
navigate("../" + topupSteps.paypalOrderSummary)
} catch (err) {
setLoading(() => false)
const msg = parseError(err)
logErrorMessage("Could not create a payment order", msg)
toast.error(dismissibleToast(<span>{msg.humanReadable}</span>))
}
}
const options = payment.paymentMethod?.gatewayData.currencies || []
return (
<ViewContainer>
<ViewNavBar onBack={() => navigate(-1)}>
<div style={{ width: 375, textAlign: "center" }}>
<StepProgressBar step={1} />
</div>
</ViewNavBar>
<ViewSplit>
<ViewSidebar>
<SideTop>
<IconWallet color={brandLight} />
<Title>Top up your account</Title>
<TitleDescription>Select the payment options</TitleDescription>
</SideTop>
<SideBot>
<PaymentOption>Payment currency:</PaymentOption>
<AmountSelect>
{options.map((opt) => {
let currencyIcon = faQuestionCircle
switch (opt) {
case "EUR":
currencyIcon = faEuroSign
break
case "USD":
currencyIcon = faDollarSign
break
case "GBP":
currencyIcon = faPoundSign
break
}
return (
<AmountToggle
key={opt}
active={isOptionActive(opt)}
onClick={selectOption(opt)}
inactiveColor={lightBlue}
height="36px"
justify="center"
>
<div style={{ textAlign: "center" }}>
<OptionValue>
<FontAwesomeIcon icon={currencyIcon} fixedWidth size="sm" />
{opt}
</OptionValue>
</div>
</AmountToggle>
)
})}
</AmountSelect>
<PaymentOption>Tax residence country (VAT):</PaymentOption>
<SelectTaxCountry />
<BrandButton
style={{ marginTop: "auto" }}
onClick={handleNextClick}
loading={loading}
disabled={loading || !payment.paymentCurrency || !payment.taxCountry}
>
Next
</BrandButton>
</SideBot>
</ViewSidebar>
<ViewContent />
</ViewSplit>
</ViewContainer>
)
})
Example #23
Source File: StripePaymentOptions.tsx From mysterium-vpn-desktop with MIT License | 4 votes |
StripePaymentOptions: React.FC = observer(() => {
const { payment } = useStores()
const navigate = useNavigate()
const [loading, setLoading] = useState(false)
const isOptionActive = (cur: string) => {
return payment.paymentCurrency == cur
}
const selectOption = (cur: string) => () => {
payment.setPaymentCurrency(cur)
}
const handleNextClick = async () => {
setLoading(() => true)
try {
await payment.createOrder()
setLoading(() => false)
navigate("../" + topupSteps.stripeOrderSummary)
} catch (err) {
setLoading(() => false)
const msg = parseError(err)
logErrorMessage("Could not create a payment order", msg)
toast.error(dismissibleToast(<span>{msg.humanReadable}</span>))
}
}
const options = payment.paymentMethod?.gatewayData.currencies || []
return (
<ViewContainer>
<ViewNavBar onBack={() => navigate(-1)}>
<div style={{ width: 375, textAlign: "center" }}>
<StepProgressBar step={1} />
</div>
</ViewNavBar>
<ViewSplit>
<ViewSidebar>
<SideTop>
<IconWallet color={brandLight} />
<Title>Top up your account</Title>
<TitleDescription>Select the payment options</TitleDescription>
</SideTop>
<SideBot>
<PaymentOption>Payment currency:</PaymentOption>
<AmountSelect>
{options.map((opt) => {
let currencyIcon = faQuestionCircle
switch (opt) {
case "EUR":
currencyIcon = faEuroSign
break
case "USD":
currencyIcon = faDollarSign
break
case "GBP":
currencyIcon = faPoundSign
break
}
return (
<AmountToggle
key={opt}
active={isOptionActive(opt)}
onClick={selectOption(opt)}
inactiveColor={lightBlue}
height="36px"
justify="center"
>
<div style={{ textAlign: "center" }}>
<OptionValue>
<FontAwesomeIcon icon={currencyIcon} fixedWidth size="sm" />
{opt}
</OptionValue>
</div>
</AmountToggle>
)
})}
</AmountSelect>
<PaymentOption>Tax residence country (VAT):</PaymentOption>
<SelectTaxCountry />
<BrandButton
style={{ marginTop: "auto" }}
onClick={handleNextClick}
loading={loading}
disabled={loading || !payment.paymentCurrency || !payment.taxCountry}
>
Next
</BrandButton>
</SideBot>
</ViewSidebar>
<ViewContent />
</ViewSplit>
</ViewContainer>
)
})
Example #24
Source File: WalletView.tsx From mysterium-vpn-desktop with MIT License | 4 votes |
WalletView: React.FC = observer(function WalletView() {
const { identity, payment } = useStores()
const [topupLoading, setTopupLoading] = useState(false)
const balance = Number(identity.identity?.balanceTokens.human) ?? 0
const handleTopupClick = async () => {
setTopupLoading(true)
try {
await payment.startTopupFlow(locations.walletTopup)
} catch (err) {
setTopupLoading(false)
const msg = parseError(err)
logErrorMessage("Could not contact payment gateways", msg)
toast.error(dismissibleToast(<span>{msg.humanReadable}</span>))
}
}
const [estimates, setEstimates] = useState<EntertainmentEstimateResponse | undefined>(undefined)
useEffect(() => {
payment.estimateEntertainment({ MYST: balance }).then((res) => setEstimates(res))
}, [balance])
const handleRefreshBalanceClick = () => {
if (!identity.identity?.id) {
return
}
toast.promise(identity.refreshBalance(), {
loading: "Refreshing balance from blockchain",
success: "Balance updated",
error: "Failed to refresh balance",
})
}
return (
<ViewContainer>
<ViewNavBar />
<ViewSplit>
<ViewSidebar>
<SideTop>
<IconWallet color={brandLight} />
<Balance>
{balance}{" "}
<BalanceRefreshButton
icon={faSync}
onClick={handleRefreshBalanceClick}
data-tip=""
data-for="balance-refresh-tooltip"
/>
</Balance>
<Tooltip id="balance-refresh-tooltip">
<span>Force refresh wallet's balance from the blockchain.</span>
</Tooltip>
<BalanceCurrency>{payment.appCurrency}</BalanceCurrency>
<BalanceFiatEquivalent>
{payment.appFiatCurrency} equivalent ≈ {displayUSD(payment.fiatEquivalent(balance))}
</BalanceFiatEquivalent>
</SideTop>
<SideBot>
{!!estimates && (
<>
<Paragraph style={{ textAlign: "center", marginBottom: 10 }}>
Will be enough for:
</Paragraph>
<EntertainmentBlocks>
<EntertainmentBlock>
<BlockIcon>
<IconPlay color={brandLight} />
</BlockIcon>
<Heading2>{estimates.videoMinutes}h</Heading2>
<EntertainmentExplanation>
Online <br />
video
</EntertainmentExplanation>
</EntertainmentBlock>
<EntertainmentBlock>
<BlockIcon>
<IconMusic color={brandLight} />
</BlockIcon>
<Heading2>{estimates.musicMinutes}h</Heading2>
<EntertainmentExplanation>
Online <br />
music
</EntertainmentExplanation>
</EntertainmentBlock>
<EntertainmentBlock>
<BlockIcon>
<IconCloudDownload color={brandLight} />
</BlockIcon>
<Heading2>{estimates.trafficMb}GiB</Heading2>
<EntertainmentExplanation>of data download</EntertainmentExplanation>
</EntertainmentBlock>
<EntertainmentBlock>
<BlockIcon>
<IconDocument color={brandLight} />
</BlockIcon>
<Heading2>{estimates.browsingMinutes}h</Heading2>
<EntertainmentExplanation>
Web <br />
browsing
</EntertainmentExplanation>
</EntertainmentBlock>
</EntertainmentBlocks>
</>
)}
<BrandButton style={{ marginTop: "auto" }} onClick={handleTopupClick} loading={topupLoading}>
Top up
</BrandButton>
</SideBot>
</ViewSidebar>
<Content />
</ViewSplit>
</ViewContainer>
)
})