Merge remote-tracking branch 'origin/revert-192-mango-notifi-integration' into main
This commit is contained in:
commit
b43f2e6232
|
@ -1,23 +1,14 @@
|
|||
import React, { FunctionComponent, useEffect, useMemo, useState } from 'react'
|
||||
import React, { FunctionComponent, useEffect, useState } from 'react'
|
||||
import { PlusCircleIcon, TrashIcon } from '@heroicons/react/outline'
|
||||
import { Source } from '@notifi-network/notifi-core'
|
||||
import Modal from './Modal'
|
||||
import Input, { Label } from './Input'
|
||||
import { ElementTitle } from './styles'
|
||||
import useMangoStore, { AlertRequest, programId } from '../stores/useMangoStore'
|
||||
import useMangoStore, { AlertRequest } from '../stores/useMangoStore'
|
||||
import Button, { LinkButton } from './Button'
|
||||
import { notify } from '../utils/notifications'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import ButtonGroup from './ButtonGroup'
|
||||
import InlineNotification from './InlineNotification'
|
||||
import { NotifiIcon } from './icons'
|
||||
import {
|
||||
BlockchainEnvironment,
|
||||
GqlError,
|
||||
useNotifiClient,
|
||||
isAlertObsolete,
|
||||
} from '@notifi-network/notifi-react-hooks'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
|
||||
interface CreateAlertModalProps {
|
||||
onClose: () => void
|
||||
|
@ -25,13 +16,6 @@ interface CreateAlertModalProps {
|
|||
repayAmount?: string
|
||||
tokenSymbol?: string
|
||||
}
|
||||
const nameForAlert = (
|
||||
health: number,
|
||||
email: string,
|
||||
phone: string,
|
||||
telegram: string
|
||||
): string =>
|
||||
`Alert for Email: ${email} Phone: ${phone} Telegram: ${telegram} When Health <= ${health}`
|
||||
|
||||
const CreateAlertModal: FunctionComponent<CreateAlertModalProps> = ({
|
||||
isOpen,
|
||||
|
@ -45,177 +29,13 @@ const CreateAlertModal: FunctionComponent<CreateAlertModalProps> = ({
|
|||
const loading = useMangoStore((s) => s.alerts.loading)
|
||||
const submitting = useMangoStore((s) => s.alerts.submitting)
|
||||
const error = useMangoStore((s) => s.alerts.error)
|
||||
const cluster = useMangoStore((s) => s.connection.cluster)
|
||||
const [email, setEmail] = useState<string>('')
|
||||
const [invalidAmountMessage, setInvalidAmountMessage] = useState('')
|
||||
const [health, setHealth] = useState('')
|
||||
const [showCustomHealthForm, setShowCustomHealthForm] = useState(false)
|
||||
const [showAlertForm, setShowAlertForm] = useState(false)
|
||||
// notifi error message
|
||||
const [errorMessage, setErrorMessage] = useState<string>('')
|
||||
|
||||
const healthPresets = ['5', '10', '15', '25', '30']
|
||||
const ALERT_LIMIT = 5
|
||||
|
||||
let env = BlockchainEnvironment.MainNetBeta
|
||||
switch (cluster) {
|
||||
case 'mainnet':
|
||||
break
|
||||
case 'devnet':
|
||||
env = BlockchainEnvironment.DevNet
|
||||
break
|
||||
}
|
||||
const { publicKey, connected, signMessage } = useWallet()
|
||||
const { data, fetchData, logIn, isAuthenticated, createAlert, deleteAlert } =
|
||||
useNotifiClient({
|
||||
dappAddress: programId.toBase58(),
|
||||
walletPublicKey: publicKey?.toString() ?? '',
|
||||
env,
|
||||
})
|
||||
const [email, setEmail] = useState<string>('')
|
||||
const [phone, setPhone] = useState<string>('+')
|
||||
const [telegramId, setTelegramId] = useState<string>('')
|
||||
|
||||
const handleError = (errors: { message: string }[]) => {
|
||||
const err = errors.length > 0 ? errors[0] : null
|
||||
if (err instanceof GqlError) {
|
||||
setErrorMessage(`${err.message}: ${err.getErrorMessages().join(', ')}`)
|
||||
} else {
|
||||
setErrorMessage(err?.message ?? 'Unknown error')
|
||||
}
|
||||
}
|
||||
|
||||
const getSourceToUse = (sources) => {
|
||||
return sources?.find((it) => {
|
||||
const filter = it.applicableFilters?.find((filter) => {
|
||||
return filter.filterType === 'VALUE_THRESHOLD'
|
||||
})
|
||||
return filter !== undefined
|
||||
})
|
||||
}
|
||||
|
||||
let { alerts, sources } = data || {}
|
||||
let sourceToUse: Source | undefined = useMemo(() => {
|
||||
return getSourceToUse(sources)
|
||||
}, [sources])
|
||||
|
||||
const handlePhone = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
let val = e.target.value
|
||||
if (val.length > 0) {
|
||||
val = val.substring(1)
|
||||
}
|
||||
|
||||
const re = /^[0-9\b]+$/
|
||||
if (val === '' || (re.test(val) && val.length <= 15)) {
|
||||
setPhone('+' + val)
|
||||
}
|
||||
}
|
||||
|
||||
const handleTelegramId = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setTelegramId(e.target.value)
|
||||
}
|
||||
|
||||
const createNotifiAlert = async function () {
|
||||
// user is not authenticated
|
||||
if (!isAuthenticated() && publicKey) {
|
||||
try {
|
||||
if (signMessage === undefined) {
|
||||
throw new Error('signMessage is not defined')
|
||||
}
|
||||
await logIn({ signMessage })
|
||||
} catch (e) {
|
||||
handleError([e])
|
||||
throw e
|
||||
}
|
||||
|
||||
// refresh data after login
|
||||
({ alerts, sources } = await fetchData())
|
||||
sourceToUse = getSourceToUse(sources)
|
||||
}
|
||||
|
||||
if (connected && isAuthenticated()) {
|
||||
if (!sourceToUse || !sourceToUse.id) return
|
||||
const filter = sourceToUse?.applicableFilters.find(
|
||||
(f) => f.filterType === 'VALUE_THRESHOLD'
|
||||
)
|
||||
if (!filter || !filter.id) return
|
||||
try {
|
||||
const healthInt = parseInt(health, 10)
|
||||
const res = await createAlert({
|
||||
filterId: filter.id,
|
||||
sourceId: sourceToUse.id,
|
||||
groupName: mangoAccount?.publicKey.toBase58(),
|
||||
name: nameForAlert(healthInt, email, phone, telegramId),
|
||||
emailAddress: email === '' ? null : email,
|
||||
phoneNumber: phone.length < 12 || phone.length > 16 ? null : phone,
|
||||
telegramId: telegramId === '' ? null : telegramId,
|
||||
filterOptions: {
|
||||
alertFrequency: 'SINGLE',
|
||||
threshold: healthInt,
|
||||
},
|
||||
})
|
||||
|
||||
if (telegramId) {
|
||||
const telegramTarget = res.targetGroup?.telegramTargets.find(
|
||||
(telegramTarget) => telegramTarget.telegramId === telegramId
|
||||
)
|
||||
if (
|
||||
telegramTarget &&
|
||||
!telegramTarget.isConfirmed &&
|
||||
telegramTarget.confirmationUrl
|
||||
) {
|
||||
window.open(telegramTarget.confirmationUrl, '_blank')
|
||||
}
|
||||
}
|
||||
|
||||
// return notifiAlertId
|
||||
return res.id
|
||||
} catch (e) {
|
||||
handleError([e])
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const deleteNotifiAlert = async function (alert) {
|
||||
// user is not authenticated
|
||||
if (!isAuthenticated() && publicKey) {
|
||||
try {
|
||||
if (signMessage === undefined) {
|
||||
throw new Error('signMessage is not defined')
|
||||
}
|
||||
await logIn({ signMessage })
|
||||
} catch (e) {
|
||||
handleError([e])
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
if (connected && isAuthenticated()) {
|
||||
try {
|
||||
await deleteAlert({ alertId: alert.notifiAlertId })
|
||||
} catch (e) {
|
||||
handleError([e])
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up alerts that don't exist in DB
|
||||
const consolidateNotifiAlerts = async function () {
|
||||
const alertsToCleanUp = alerts?.filter((alert) => {
|
||||
const isAlertExist = activeAlerts?.some(
|
||||
(a) => a.notifiAlertId === alert.id
|
||||
)
|
||||
return !isAlertExist
|
||||
})
|
||||
|
||||
if (alertsToCleanUp === undefined) return
|
||||
alertsToCleanUp.forEach((alert) => {
|
||||
if (alert.id) {
|
||||
deleteAlert({ alertId: alert.id })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const validateEmailInput = (amount) => {
|
||||
if (Number(amount) <= 0) {
|
||||
|
@ -231,10 +51,9 @@ const CreateAlertModal: FunctionComponent<CreateAlertModalProps> = ({
|
|||
async function onCreateAlert() {
|
||||
if (!mangoGroup || !mangoAccount) return
|
||||
const parsedHealth = parseFloat(health)
|
||||
|
||||
if (!email && !phone && !telegramId) {
|
||||
if (!email) {
|
||||
notify({
|
||||
title: t('alerts:notifi-type-required'),
|
||||
title: t('alerts:email-address-required'),
|
||||
type: 'error',
|
||||
})
|
||||
return
|
||||
|
@ -245,58 +64,19 @@ const CreateAlertModal: FunctionComponent<CreateAlertModalProps> = ({
|
|||
})
|
||||
return
|
||||
}
|
||||
|
||||
let notifiAlertId
|
||||
// send alert to Notifi
|
||||
try {
|
||||
notifiAlertId = await createNotifiAlert()
|
||||
} catch (e) {
|
||||
handleError([e])
|
||||
return
|
||||
const body: AlertRequest = {
|
||||
mangoGroupPk: mangoGroup.publicKey.toString(),
|
||||
mangoAccountPk: mangoAccount.publicKey.toString(),
|
||||
health: parsedHealth,
|
||||
alertProvider: 'mail',
|
||||
email,
|
||||
}
|
||||
|
||||
if (notifiAlertId) {
|
||||
const body: AlertRequest = {
|
||||
mangoGroupPk: mangoGroup.publicKey.toString(),
|
||||
mangoAccountPk: mangoAccount.publicKey.toString(),
|
||||
health: parsedHealth,
|
||||
alertProvider: 'notifi',
|
||||
email,
|
||||
notifiAlertId,
|
||||
}
|
||||
const success: any = await actions.createAlert(body)
|
||||
if (success) {
|
||||
setErrorMessage('')
|
||||
setShowAlertForm(false)
|
||||
}
|
||||
const success: any = await actions.createAlert(body)
|
||||
if (success) {
|
||||
setShowAlertForm(false)
|
||||
}
|
||||
}
|
||||
|
||||
async function onDeleteAlert(alert) {
|
||||
// delete alert from db
|
||||
actions.deleteAlert(alert._id)
|
||||
|
||||
// delete alert from Notifi
|
||||
try {
|
||||
await deleteNotifiAlert(alert)
|
||||
} catch (e) {
|
||||
handleError([e])
|
||||
}
|
||||
}
|
||||
|
||||
async function onNewAlert() {
|
||||
if (connected && isAuthenticated()) {
|
||||
try {
|
||||
await consolidateNotifiAlerts()
|
||||
} catch (e) {
|
||||
handleError([e])
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
setShowAlertForm(true)
|
||||
}
|
||||
|
||||
const handleCancelCreateAlert = () => {
|
||||
if (activeAlerts.length > 0) {
|
||||
setShowAlertForm(false)
|
||||
|
@ -311,15 +91,6 @@ const CreateAlertModal: FunctionComponent<CreateAlertModalProps> = ({
|
|||
}
|
||||
}, [])
|
||||
|
||||
// Delete notifi Alerts that have fired
|
||||
useEffect(() => {
|
||||
const firedAlert = alerts?.find(isAlertObsolete)
|
||||
|
||||
if (firedAlert !== undefined && firedAlert.id !== null) {
|
||||
deleteAlert({ alertId: firedAlert.id })
|
||||
}
|
||||
}, [alerts, deleteAlert])
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
{!loading && !submitting ? (
|
||||
|
@ -334,8 +105,8 @@ const CreateAlertModal: FunctionComponent<CreateAlertModalProps> = ({
|
|||
</ElementTitle>
|
||||
<Button
|
||||
className="min-w-20 flex h-8 items-center justify-center pt-0 pb-0 text-xs"
|
||||
disabled={activeAlerts.length >= ALERT_LIMIT}
|
||||
onClick={onNewAlert}
|
||||
disabled={activeAlerts.length >= 5}
|
||||
onClick={() => setShowAlertForm(true)}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<PlusCircleIcon className="mr-1.5 h-4 w-4" />
|
||||
|
@ -345,11 +116,6 @@ const CreateAlertModal: FunctionComponent<CreateAlertModalProps> = ({
|
|||
</div>
|
||||
</Modal.Header>
|
||||
<div className="mt-2 border-b border-th-fgd-4">
|
||||
{errorMessage.length > 0 ? (
|
||||
<div className="mt-1 text-xxs text-th-fgd-3">
|
||||
{errorMessage}
|
||||
</div>
|
||||
) : null}
|
||||
{activeAlerts.map((alert, index) => (
|
||||
<div
|
||||
className="flex items-center justify-between border-t border-th-fgd-4 p-4"
|
||||
|
@ -360,12 +126,12 @@ const CreateAlertModal: FunctionComponent<CreateAlertModalProps> = ({
|
|||
</div>
|
||||
<TrashIcon
|
||||
className="default-transition h-5 w-5 cursor-pointer text-th-fgd-3 hover:text-th-primary"
|
||||
onClick={() => onDeleteAlert(alert)}
|
||||
onClick={() => actions.deleteAlert(alert._id)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{activeAlerts.length >= ALERT_LIMIT ? (
|
||||
{activeAlerts.length >= 3 ? (
|
||||
<div className="mt-1 text-center text-xxs text-th-fgd-3">
|
||||
{t('alerts:alerts-max')}
|
||||
</div>
|
||||
|
@ -394,14 +160,6 @@ const CreateAlertModal: FunctionComponent<CreateAlertModalProps> = ({
|
|||
value={email || ''}
|
||||
onChange={(e) => onChangeEmailInput(e.target.value)}
|
||||
/>
|
||||
<Label className="mt-4">{t('phone-number')}</Label>
|
||||
<Input type="tel" value={phone} onChange={handlePhone} />
|
||||
<Label className="mt-4">{t('telegram')}</Label>
|
||||
<Input
|
||||
type="text"
|
||||
value={telegramId}
|
||||
onChange={handleTelegramId}
|
||||
/>
|
||||
<div className="mt-4 flex items-end">
|
||||
<div className="w-full">
|
||||
<div className="flex justify-between">
|
||||
|
@ -439,22 +197,7 @@ const CreateAlertModal: FunctionComponent<CreateAlertModalProps> = ({
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
{errorMessage.length > 0 ? (
|
||||
<div className="mt-1 text-xxs text-th-fgd-3">
|
||||
{errorMessage}
|
||||
</div>
|
||||
) : (
|
||||
!isAuthenticated() && (
|
||||
<div className="mt-1 text-xxs text-th-fgd-3">
|
||||
{t('alerts:prompted-to-sign-transaction')}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
<Button
|
||||
className="mt-6 w-full"
|
||||
onClick={() => onCreateAlert()}
|
||||
disabled={(!email && !phone && !telegramId) || !health}
|
||||
>
|
||||
<Button className="mt-6 w-full" onClick={() => onCreateAlert()}>
|
||||
{t('alerts:create-alert')}
|
||||
</Button>
|
||||
<LinkButton
|
||||
|
@ -488,7 +231,7 @@ const CreateAlertModal: FunctionComponent<CreateAlertModalProps> = ({
|
|||
</Modal.Header>
|
||||
<Button
|
||||
className="m-auto flex justify-center"
|
||||
onClick={onNewAlert}
|
||||
onClick={() => setShowAlertForm(true)}
|
||||
>
|
||||
{t('alerts:new-alert')}
|
||||
</Button>
|
||||
|
@ -502,25 +245,6 @@ const CreateAlertModal: FunctionComponent<CreateAlertModalProps> = ({
|
|||
<div className="h-12 w-full animate-pulse rounded-md bg-th-bkg-3" />
|
||||
</div>
|
||||
)}
|
||||
<Modal.Footer>
|
||||
<div className="item-center mt-4 flex w-full justify-between text-th-fgd-3">
|
||||
<div className="flex">
|
||||
<span>{t('alerts:powered-by')}</span>
|
||||
<span className="ml-2">
|
||||
<NotifiIcon className="h-5 w-10"></NotifiIcon>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
href="https://docs.notifi.network/NotifiIntegrationsFAQ.html"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{t('learn-more')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -70,11 +70,6 @@ const Header = ({ children }) => {
|
|||
return <div className={`flex flex-col items-center pb-2`}>{children}</div>
|
||||
}
|
||||
|
||||
const Footer = ({ children }) => {
|
||||
return <div className={`flex flex-col items-center pb-2`}>{children}</div>
|
||||
}
|
||||
|
||||
Modal.Header = Header
|
||||
Modal.Footer = Footer
|
||||
|
||||
export default Modal
|
||||
|
|
|
@ -891,66 +891,3 @@ export const AnchorIcon = ({ className }) => {
|
|||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export const NotifiIcon = ({ className }) => {
|
||||
return (
|
||||
<svg
|
||||
className={`${className}`}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="61"
|
||||
height="14"
|
||||
viewBox="0 0 61 14"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M11.4546 8.12234C11.0641 8.23352 10.6519 8.29305 10.2258 8.29305C7.75286 8.29305 5.74812 6.28831 5.74812 3.81534C5.74812 3.37405 5.81196 2.94766 5.9309 2.54492H1.29125C0.57811 2.54492 0 3.12303 0 3.83617V12.7083C0 13.4214 0.57811 13.9995 1.29125 13.9995H10.1633C10.8765 13.9995 11.4546 13.4214 11.4546 12.7083V8.12234Z"
|
||||
fill="#F5F6FB"
|
||||
/>
|
||||
<path
|
||||
d="M14.0004 3.18183C14.0004 4.93911 12.5758 6.36366 10.8186 6.36366C9.06127 6.36366 7.63672 4.93911 7.63672 3.18183C7.63672 1.42455 9.06127 0 10.8186 0C12.5758 0 14.0004 1.42455 14.0004 3.18183Z"
|
||||
fill="url(#paint0_linear_790_3397)"
|
||||
/>
|
||||
<path
|
||||
d="M27.5799 9.07334V13.9039H29.5859V9.07334C29.5859 6.10438 28.0453 4.16251 25.6059 4.16251C24.4183 4.16251 23.3591 4.86865 23.0061 5.8476V4.27485H21V13.9039H23.0061V9.08939C23.0061 7.38825 23.9529 6.16857 25.2528 6.16857C26.6651 6.16857 27.5799 7.30801 27.5799 9.07334Z"
|
||||
fill="#F5F6FB"
|
||||
/>
|
||||
<path
|
||||
d="M30.9267 9.07334C30.9267 11.8658 32.9969 14.0002 35.661 14.0002C38.325 14.0002 40.3792 11.8658 40.3792 9.07334C40.3792 6.29696 38.325 4.16251 35.661 4.16251C32.9969 4.16251 30.9267 6.29696 30.9267 9.07334ZM35.661 6.16857C37.2016 6.16857 38.3732 7.42035 38.3732 9.07334C38.3732 10.7424 37.2016 11.9942 35.661 11.9942C34.1203 11.9942 32.9327 10.7424 32.9327 9.07334C32.9327 7.42035 34.1203 6.16857 35.661 6.16857Z"
|
||||
fill="#F5F6FB"
|
||||
/>
|
||||
<path
|
||||
d="M43.2265 2.23669L42.745 4.27485H41.3167V6.16857H42.745V10.5979C42.745 12.9571 43.7079 13.9039 46.0028 13.9039H46.8374V11.9139H46.1954C45.2004 11.9139 44.7511 11.4806 44.7511 10.4856V6.16857H46.8374V4.27485H44.7511V2.23669H43.2265Z"
|
||||
fill="#F5F6FB"
|
||||
/>
|
||||
<path
|
||||
d="M50.2386 13.9039V4.27485H48.2325V13.9039H50.2386Z"
|
||||
fill="#F5F6FB"
|
||||
/>
|
||||
<path
|
||||
d="M60.5156 13.9039V4.27485H54.995V3.56872C54.995 2.57372 55.4443 2.12435 56.4393 2.12435H57.0813V0.150391H56.2467C53.9518 0.150391 52.9889 1.08121 52.9889 3.45638V4.27485H51.5766V6.16857H52.9889V13.9039H54.995V6.16857H58.5096V13.9039H60.5156Z"
|
||||
fill="#F5F6FB"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M49.0032 0C48.4363 0 48.2324 0.311122 48.2324 0.753594V2.3621H49.8052C50.3514 2.3621 50.5945 2.17089 50.5945 1.63162V0.753594C50.5945 0.2489 50.2615 0 49.7499 0H49.0032Z"
|
||||
fill="#F5F6FB"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_790_3397"
|
||||
x1="12.74"
|
||||
y1="0.49587"
|
||||
x2="9.68218"
|
||||
y2="6.36366"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#FE7970" />
|
||||
<stop offset="1" stopColor="#FEB776" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
"@headlessui/react": "^0.0.0-insiders.2dbc38c",
|
||||
"@heroicons/react": "^1.0.0",
|
||||
"@jup-ag/react-hook": "^1.0.0-beta.22",
|
||||
"@notifi-network/notifi-react-hooks": "^0.12.1",
|
||||
"@project-serum/serum": "0.13.55",
|
||||
"@project-serum/sol-wallet-adapter": "0.2.0",
|
||||
"@sentry/react": "^6.19.2",
|
||||
|
@ -65,7 +64,6 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "^12.1.0",
|
||||
"@notifi-network/notifi-core": "^0.8.0",
|
||||
"@svgr/webpack": "^6.1.2",
|
||||
"@testing-library/react": "^11.2.5",
|
||||
"@types/node": "^14.14.25",
|
||||
|
|
|
@ -6,10 +6,8 @@
|
|||
"alerts-disclaimer": "Do not rely solely on alerts to protect your account. We can't guarantee they will be delivered.",
|
||||
"alerts-max": "You've reached the maximum number of active alerts.",
|
||||
"create-alert": "Create Alert",
|
||||
"email-address-required": "An email address is required",
|
||||
"new-alert": "New Alert",
|
||||
"no-alerts": "No Active Alerts",
|
||||
"no-alerts-desc": "Create an alert to be notified when your account health is low.",
|
||||
"notifi-type-required": "An email address, a phone number or a telegram id is required",
|
||||
"powered-by": "Powered by",
|
||||
"prompted-to-sign-transaction": "When prompted, sign the transaction."
|
||||
"no-alerts-desc": "Create an alert to be notified when your account health is low."
|
||||
}
|
|
@ -298,7 +298,6 @@
|
|||
"perp-positions-tip-title": "Perp Position Details",
|
||||
"perpetual-futures": "Perpetual Futures",
|
||||
"perps": "Perps",
|
||||
"phone-number": "Phone Number",
|
||||
"pnl": "PnL",
|
||||
"pnl-error": "Error redeeming",
|
||||
"pnl-help": "Redeeming will update your USDC balance to reflect the redeemed PnL amount.",
|
||||
|
@ -379,7 +378,6 @@
|
|||
"taker": "Taker",
|
||||
"taker-fee": "Taker Fee",
|
||||
"target-period-length": "Target Period Length",
|
||||
"telegram": "Telegram",
|
||||
"theme": "Theme",
|
||||
"themes-tip-desc": "Mango, Dark or Light (if you're that way inclined).",
|
||||
"themes-tip-title": "Color Themes",
|
||||
|
|
|
@ -6,10 +6,8 @@
|
|||
"alerts-disclaimer": "Has alcanzado el número máximo de alertas activas.",
|
||||
"alerts-max": "You've reached the maximum number of active alerts.",
|
||||
"create-alert": "Crear alerta",
|
||||
"email-address-required": "An email address is required",
|
||||
"new-alert": "Alerta nueva",
|
||||
"no-alerts": "No hay alertas activas",
|
||||
"no-alerts-desc": "Cree una alerta para recibir una notificación cuando el estado de su cuenta sea bajo.",
|
||||
"notifi-type-required": "Se requiere una dirección de correo electrónico, un número de teléfono o una identificación de Telegram",
|
||||
"powered-by": "Desarrollada por",
|
||||
"prompted-to-sign-transaction": "Cuando se le solicite, firme la transacción."
|
||||
"no-alerts-desc": "Cree una alerta para recibir una notificación cuando el estado de su cuenta sea bajo."
|
||||
}
|
|
@ -298,7 +298,6 @@
|
|||
"perp-positions-tip-title": "Detalles de la posición de perp",
|
||||
"perpetual-futures": "Futuros perpetuos",
|
||||
"perps": "perpetuos",
|
||||
"phone-number": "Número de teléfono",
|
||||
"pnl": "PnL",
|
||||
"pnl-error": "Solución de errores PNL",
|
||||
"pnl-help": "La liquidación actualizará su saldo en USDC para reflejar el monto de PnL pendiente.",
|
||||
|
@ -379,7 +378,6 @@
|
|||
"taker": "Receptor",
|
||||
"taker-fee": "Tarifa del receptor",
|
||||
"target-period-length": "Duración del período objetivo",
|
||||
"telegram": "Telegram",
|
||||
"theme": "Theme",
|
||||
"themes-tip-desc": "Mango, Oscuro o Claro (si te gusta eso).",
|
||||
"themes-tip-title": "Temas de color",
|
||||
|
|
|
@ -6,10 +6,8 @@
|
|||
"alerts-disclaimer": "请别全靠警报来保护资产。我们无法保证会准时发出。",
|
||||
"alerts-max": "您已达到警报数量最多限制",
|
||||
"create-alert": "创建警报",
|
||||
"email-address-required": "您必须输入电子邮件地址",
|
||||
"new-alert": "创建警报",
|
||||
"no-alerts": "您没有活动警报",
|
||||
"no-alerts-desc": "以创建警报而收到健康度通知。",
|
||||
"notifi-type-required": "您必须输入电子邮件地址、电话号码或电报号",
|
||||
"powered-by": "技术支持由",
|
||||
"prompted-to-sign-transaction": "出現提示時,簽署交易。"
|
||||
"no-alerts-desc": "以创建警报而收到健康度通知。"
|
||||
}
|
|
@ -298,7 +298,6 @@
|
|||
"perp-positions-tip-title": "永续合约当前持仓细节",
|
||||
"perpetual-futures": "永续合约",
|
||||
"perps": "永续合约",
|
||||
"phone-number": "电话号码",
|
||||
"pnl": "盈亏",
|
||||
"pnl-error": "结清盈亏出错了",
|
||||
"pnl-help": "结清会更新USDC余额来处理尚未结清的盈亏量。",
|
||||
|
@ -379,7 +378,6 @@
|
|||
"taker": "吃单者",
|
||||
"taker-fee": "吃单费率",
|
||||
"target-period-length": "目标期间长度",
|
||||
"telegram": "电报",
|
||||
"theme": "模式",
|
||||
"themes-tip-desc": "Mango,黑暗或明亮(看您偏向)。",
|
||||
"themes-tip-title": "颜色模式",
|
||||
|
|
|
@ -6,10 +6,8 @@
|
|||
"alerts-disclaimer": "請別全靠警報來保護資產。我們無法保證會準時發出。",
|
||||
"alerts-max": "您已達到警報數量最多限制",
|
||||
"create-alert": "創建警報",
|
||||
"email-address-required": "您必須輸入電子郵件地址",
|
||||
"new-alert": "創建警報",
|
||||
"no-alerts": "您沒有活動警報",
|
||||
"no-alerts-desc": "以創建警報而收到健康度通知。",
|
||||
"notifi-type-required": "您必須輸入電子郵件地址、電話號碼或電報號",
|
||||
"powered-by": "技術支持由",
|
||||
"prompted-to-sign-transaction": "出現提示時,簽署交易。"
|
||||
"no-alerts-desc": "以創建警報而收到健康度通知。"
|
||||
}
|
|
@ -298,7 +298,6 @@
|
|||
"perp-positions-tip-title": "永續合約當前持倉細節",
|
||||
"perpetual-futures": "永續合約",
|
||||
"perps": "永續合約",
|
||||
"phone-number": "電話號碼",
|
||||
"pnl": "盈虧",
|
||||
"pnl-error": "實現盈虧出錯了",
|
||||
"pnl-help": "實現會更新USDC餘額來處理尚未實現的盈虧。",
|
||||
|
@ -379,7 +378,6 @@
|
|||
"taker": "吃單者",
|
||||
"taker-fee": "吃單費率",
|
||||
"target-period-length": "目標期間長度",
|
||||
"telegram": "電報",
|
||||
"theme": "模式",
|
||||
"themes-tip-desc": "Mango,黑暗或明亮(看您偏向)。",
|
||||
"themes-tip-title": "顏色模式",
|
||||
|
|
|
@ -112,22 +112,20 @@ export interface Orderbook {
|
|||
|
||||
export interface Alert {
|
||||
acc: PublicKey
|
||||
alertProvider: 'mail' | 'notifi'
|
||||
alertProvider: 'mail'
|
||||
health: number
|
||||
_id: string
|
||||
open: boolean
|
||||
timestamp: number
|
||||
triggeredTimestamp: number | undefined
|
||||
notifiAlertId: string | undefined
|
||||
}
|
||||
|
||||
export interface AlertRequest {
|
||||
alertProvider: 'mail' | 'notifi'
|
||||
alertProvider: 'mail'
|
||||
health: number
|
||||
mangoGroupPk: string
|
||||
mangoAccountPk: string
|
||||
email: string | undefined
|
||||
notifiAlertId: string | undefined
|
||||
}
|
||||
|
||||
export type MangoStore = {
|
||||
|
@ -837,7 +835,6 @@ const useMangoStore = create<
|
|||
health: req.health,
|
||||
open: true,
|
||||
timestamp: Date.now(),
|
||||
notifiAlertId: req.notifiAlertId,
|
||||
}
|
||||
|
||||
set((state) => {
|
||||
|
|
|
@ -67,7 +67,6 @@ export function getTokenMultiplierFromDecimals(decimals: number): BN {
|
|||
}
|
||||
|
||||
export function abbreviateAddress(address: PublicKey, size = 5) {
|
||||
if (!address) return
|
||||
const base58 = address.toBase58()
|
||||
return base58.slice(0, size) + '…' + base58.slice(-size)
|
||||
}
|
||||
|
|
109
yarn.lock
109
yarn.lock
|
@ -1394,35 +1394,6 @@
|
|||
"@nodelib/fs.scandir" "2.1.5"
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@notifi-network/notifi-axios-adapter@^0.12.1":
|
||||
version "0.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@notifi-network/notifi-axios-adapter/-/notifi-axios-adapter-0.12.1.tgz#aae412465230b7a3d7e86c93a7d43210568282c6"
|
||||
integrity sha512-vRVPN1uJHXNrxMzZ1F4OsmwbkytXy2kYBCnq+riRHKIgPykun51LGi5Y2ljW545j1YedtafY4vpPJNmMOhTuFw==
|
||||
dependencies:
|
||||
"@notifi-network/notifi-axios-utils" "^0.12.0"
|
||||
|
||||
"@notifi-network/notifi-axios-utils@^0.12.0":
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@notifi-network/notifi-axios-utils/-/notifi-axios-utils-0.12.0.tgz#4eea3088cab39c9a2c1482e94c9d020296f42339"
|
||||
integrity sha512-NVyS7x+z5wSLdNOSyxfB7XePZ1sSaSEKVPGEBnJuH5xpXkLBz1o+WlJTkA9/IcQFdSwLeLCdXGEb8jQPSjEWrA==
|
||||
|
||||
"@notifi-network/notifi-core@^0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@notifi-network/notifi-core/-/notifi-core-0.8.0.tgz#e3a3eed8a6e2995d75a736239959bf42bbf32fe1"
|
||||
integrity sha512-az+ux5QhvBS+AZnjb9+RBuzh0MWWOIUdmpVzOiNazBxX1O1XVnokfaCImeTpYpe7eEoAvwOLirTnFMsfvF0gdQ==
|
||||
|
||||
"@notifi-network/notifi-react-hooks@^0.12.1":
|
||||
version "0.12.1"
|
||||
resolved "https://registry.yarnpkg.com/@notifi-network/notifi-react-hooks/-/notifi-react-hooks-0.12.1.tgz#e84dcb751a0f0bbd9ceb2e0378b46014a9570bc7"
|
||||
integrity sha512-77bxZ/r19023x+yPjzW3LBMYxL4VyP7aR6owak51x76dEUqgi/oqzOh9+YSNXXeTvwr37jhtjYE+DHMVXD+y+g==
|
||||
dependencies:
|
||||
"@notifi-network/notifi-axios-adapter" "^0.12.1"
|
||||
"@notifi-network/notifi-axios-utils" "^0.12.0"
|
||||
axios "^0.26.0"
|
||||
localforage "^1.10.0"
|
||||
typedoc-plugin-missing-exports "^0.22.6"
|
||||
typescript "^4.5.5"
|
||||
|
||||
"@orca-so/whirlpool-client-sdk@npm:@jup-ag/whirlpool-client-sdk@0.0.7":
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@jup-ag/whirlpool-client-sdk/-/whirlpool-client-sdk-0.0.7.tgz#dabb35e9cccc4069d17c3299f7c6ff8b7d0f4bfa"
|
||||
|
@ -1571,7 +1542,18 @@
|
|||
bn.js "^5.1.2"
|
||||
buffer-layout "^1.2.0"
|
||||
|
||||
"@project-serum/serum@^0.13.61", "@project-serum/serum@~0.13.64":
|
||||
"@project-serum/serum@^0.13.61":
|
||||
version "0.13.61"
|
||||
resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.61.tgz#1f0e6dfa7786a71e4317593911e9915d8b2a06e6"
|
||||
integrity sha512-aebaRGQ0/K7a5kJ9UXO59BAQFJILVu5jbGobU8GD2CTSy6SPceprB6/pgZmZLQIabhXWUHaZRF/wXIClgWataA==
|
||||
dependencies:
|
||||
"@project-serum/anchor" "^0.11.1"
|
||||
"@solana/spl-token" "^0.1.6"
|
||||
"@solana/web3.js" "^1.21.0"
|
||||
bn.js "^5.1.2"
|
||||
buffer-layout "^1.2.0"
|
||||
|
||||
"@project-serum/serum@~0.13.64":
|
||||
version "0.13.64"
|
||||
resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.64.tgz#d5a9009ded8d2539ebfd12e1ff58b9028cfcdf45"
|
||||
integrity sha512-33d8K3qcbiFnL1Azv5k6IYMtJYjCqqmFkf95w5W8MPB0A+5zQfOwhEfXQuBw0ExZ8ft5s9Vy8sReLyx0SsINWA==
|
||||
|
@ -2750,13 +2732,6 @@ axios@^0.25.0:
|
|||
dependencies:
|
||||
follow-redirects "^1.14.7"
|
||||
|
||||
axios@^0.26.0:
|
||||
version "0.26.0"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.0.tgz#9a318f1c69ec108f8cd5f3c3d390366635e13928"
|
||||
integrity sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==
|
||||
dependencies:
|
||||
follow-redirects "^1.14.8"
|
||||
|
||||
babel-plugin-dynamic-import-node@^2.3.3:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3"
|
||||
|
@ -3675,33 +3650,7 @@ error-ex@^1.3.1:
|
|||
dependencies:
|
||||
is-arrayish "^0.2.1"
|
||||
|
||||
es-abstract@^1.18.5:
|
||||
version "1.19.2"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.2.tgz#8f7b696d8f15b167ae3640b4060670f3d054143f"
|
||||
integrity sha512-gfSBJoZdlL2xRiOCy0g8gLMryhoe1TlimjzU99L/31Z8QEGIhVQI+EWwt5lT+AuU9SnorVupXFqqOGqGfsyO6w==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
es-to-primitive "^1.2.1"
|
||||
function-bind "^1.1.1"
|
||||
get-intrinsic "^1.1.1"
|
||||
get-symbol-description "^1.0.0"
|
||||
has "^1.0.3"
|
||||
has-symbols "^1.0.3"
|
||||
internal-slot "^1.0.3"
|
||||
is-callable "^1.2.4"
|
||||
is-negative-zero "^2.0.2"
|
||||
is-regex "^1.1.4"
|
||||
is-shared-array-buffer "^1.0.1"
|
||||
is-string "^1.0.7"
|
||||
is-weakref "^1.0.2"
|
||||
object-inspect "^1.12.0"
|
||||
object-keys "^1.1.1"
|
||||
object.assign "^4.1.2"
|
||||
string.prototype.trimend "^1.0.4"
|
||||
string.prototype.trimstart "^1.0.4"
|
||||
unbox-primitive "^1.0.1"
|
||||
|
||||
es-abstract@^1.19.0, es-abstract@^1.19.1:
|
||||
es-abstract@^1.18.5, es-abstract@^1.19.0, es-abstract@^1.19.1:
|
||||
version "1.19.1"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3"
|
||||
integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==
|
||||
|
@ -4078,7 +4027,7 @@ flatted@^3.1.0:
|
|||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3"
|
||||
integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==
|
||||
|
||||
follow-redirects@^1.14.0, follow-redirects@^1.14.7, follow-redirects@^1.14.8:
|
||||
follow-redirects@^1.14.0, follow-redirects@^1.14.7:
|
||||
version "1.14.9"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7"
|
||||
integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==
|
||||
|
@ -4379,11 +4328,6 @@ ignore@^5.1.8, ignore@^5.2.0:
|
|||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
|
||||
integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
|
||||
|
||||
immediate@~3.0.5:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
|
||||
integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
|
||||
|
||||
immer@^9.0.1:
|
||||
version "9.0.12"
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.12.tgz#2d33ddf3ee1d247deab9d707ca472c8c942a0f20"
|
||||
|
@ -4552,7 +4496,7 @@ is-nan@^1.2.1:
|
|||
call-bind "^1.0.0"
|
||||
define-properties "^1.1.3"
|
||||
|
||||
is-negative-zero@^2.0.1, is-negative-zero@^2.0.2:
|
||||
is-negative-zero@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
|
||||
integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==
|
||||
|
@ -4612,7 +4556,7 @@ is-typed-array@^1.1.3, is-typed-array@^1.1.7:
|
|||
foreach "^2.0.5"
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-weakref@^1.0.1, is-weakref@^1.0.2:
|
||||
is-weakref@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
|
||||
integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==
|
||||
|
@ -4779,13 +4723,6 @@ levn@^0.4.1:
|
|||
prelude-ls "^1.2.1"
|
||||
type-check "~0.4.0"
|
||||
|
||||
lie@3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
|
||||
integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=
|
||||
dependencies:
|
||||
immediate "~3.0.5"
|
||||
|
||||
lilconfig@2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082"
|
||||
|
@ -4835,13 +4772,6 @@ listr2@^4.0.1:
|
|||
through "^2.3.8"
|
||||
wrap-ansi "^7.0.0"
|
||||
|
||||
localforage@^1.10.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4"
|
||||
integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==
|
||||
dependencies:
|
||||
lie "3.1.1"
|
||||
|
||||
lodash-es@^4.17.21:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
|
||||
|
@ -6577,12 +6507,7 @@ type-fest@^0.21.3:
|
|||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
|
||||
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
|
||||
|
||||
typedoc-plugin-missing-exports@^0.22.6:
|
||||
version "0.22.6"
|
||||
resolved "https://registry.yarnpkg.com/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-0.22.6.tgz#7467c60f1cd26507124103f0b9bca271d5aa8d71"
|
||||
integrity sha512-1uguGQqa+c5f33nWS3v1mm0uAx4Ii1lw4Kx2zQksmYFKNEWTmrmMXbMNBoBg4wu0p4dFCNC7JIWPoRzpNS6pFA==
|
||||
|
||||
typescript@^4.5.5, typescript@^4.6.3:
|
||||
typescript@^4.6.3:
|
||||
version "4.6.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c"
|
||||
integrity sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==
|
||||
|
|
Loading…
Reference in New Issue