mango-ui-v2/stores/useAlertsStore.tsx

193 lines
5.1 KiB
TypeScript
Raw Normal View History

import create, { State } from 'zustand'
import produce from 'immer'
import { PublicKey } from '@solana/web3.js'
import { notify } from '../utils/notifications'
export type AlertProvider = 'mail' | 'sms' | 'tg'
export interface Alert {
acc: PublicKey
alertProvider: AlertProvider
collateralRatioThresh: number
open: boolean
timestamp: number
triggeredTimestamp: number | undefined
}
interface AlertRequest {
alertProvider: AlertProvider
collateralRatioThresh: number
mangoGroupPk: string
marginAccountPk: string
phoneNumber: any
email: string | undefined
}
interface AlertsStore extends State {
activeAlerts: Array<Alert>
triggeredAlerts: Array<Alert>
loading: boolean
error: string
submitting: boolean
success: string
tgCode: string | null
set: (s: any) => void
actions: { [key: string]: (any) => void }
}
const useAlertsStore = create<AlertsStore>((set, get) => ({
activeAlerts: [],
triggeredAlerts: [],
loading: false,
error: '',
submitting: false,
success: '',
tgCode: null,
set: (fn) => set(produce(fn)),
actions: {
async createAlert(req: AlertRequest) {
const set = get().set
const alert = {
acc: new PublicKey(req.marginAccountPk),
alertProvider: req.alertProvider,
collateralRatioThresh: req.collateralRatioThresh,
open: true,
timestamp: Date.now(),
}
set((state) => {
state.submitting = true
state.error = ''
state.success = ''
})
const fetchUrl = `https://mango-margin-call.herokuapp.com/alerts`
const headers = { 'Content-Type': 'application/json' }
fetch(fetchUrl, {
method: 'POST',
headers: headers,
body: JSON.stringify(req),
})
.then((response: any) => {
if (!response.ok) {
throw response
}
return response.json()
})
.then((json: any) => {
const alerts = get().activeAlerts
set((state) => {
state.activeAlerts = [alert as Alert].concat(alerts)
state.success = json.code ? '' : 'Alert saved successfully'
state.tgCode = json.code
})
})
.catch((err) => {
if (typeof err.text === 'function') {
err.text().then((errorMessage: string) => {
set((state) => {
state.error = errorMessage
})
notify({
message: errorMessage,
type: 'error',
})
})
} else {
set((state) => {
state.error = 'Something went wrong'
})
notify({
message: 'Something went wrong',
type: 'error',
})
}
})
.finally(() => {
set((state) => {
state.submitting = false
})
})
},
async loadAlerts(marginAccounts: PublicKey[]) {
const set = get().set
set((state) => {
state.loading = true
})
const headers = { 'Content-Type': 'application/json' }
const responses = await Promise.all(
marginAccounts.map((pubkey) => {
return fetch(
`https://mango-margin-call.herokuapp.com/alerts/${pubkey}`,
{
method: 'GET',
headers: headers,
}
)
.then((response: any) => {
if (!response.ok) {
throw response
}
return response.json()
})
.catch((err) => {
if (typeof err.text === 'function') {
err.text().then((errorMessage: string) => {
notify({
message: errorMessage,
type: 'error',
})
})
} else {
notify({
message: 'Something went wrong',
type: 'error',
})
}
})
})
)
// Add margin account address to alerts
// Assign triggeredTimestamp for old alerts in the DB that don't have this property yet
responses.forEach((accounts, index) =>
accounts.alerts.forEach((alert) => {
alert.acc = marginAccounts[index]
if (!alert.open) {
alert.triggeredTimestamp ||= alert.timestamp
}
})
)
const flattenAccountAlerts = responses.map((acc) => acc.alerts).flat()
// sort active by latest creation time first
const activeAlerts = flattenAccountAlerts
.filter((alert) => alert.open)
.sort((a, b) => {
return b.timestamp - a.timestamp
})
// sort triggered by latest trigger time first
const triggeredAlerts = flattenAccountAlerts
.filter((alert) => !alert.open)
.sort((a, b) => {
return b.triggeredTimestamp - a.triggeredTimestamp
})
set((state) => {
state.activeAlerts = activeAlerts
state.triggeredAlerts = triggeredAlerts
state.loading = false
})
},
},
}))
export default useAlertsStore