add hot key templates

This commit is contained in:
saml33 2023-10-26 14:39:36 +11:00
parent 616cc2cf66
commit a2127fcfe2
11 changed files with 468 additions and 202 deletions

View File

@ -6,7 +6,9 @@ import { HotKey } from '@components/settings/HotKeysSettings'
import Button from '@components/shared/Button'
import InlineNotification from '@components/shared/InlineNotification'
import Modal from '@components/shared/Modal'
import TabUnderline from '@components/shared/TabUnderline'
import Tooltip from '@components/shared/Tooltip'
import { CheckIcon } from '@heroicons/react/20/solid'
import useLocalStorageState from 'hooks/useLocalStorageState'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -16,6 +18,8 @@ import { HOT_KEYS_KEY } from 'utils/constants'
type FormErrors = Partial<Record<keyof HotKeyForm, string>>
type HotKeyForm = {
custom?: string
name?: string
baseKey: string
triggerKey: string
price: string
@ -29,7 +33,34 @@ type HotKeyForm = {
reduce: boolean
}
type TEMPLATE = {
price: string
side: 'buy' | 'sell'
size: string
sizeType: 'percentage' | 'notional'
orderType: 'limit' | 'market'
ioc: boolean
post: boolean
margin: boolean
reduce: boolean
}
type CUSTOM_TEMPLATE = {
custom: string
}
export enum HOTKEY_TEMPLATES {
CLOSE_LONG = 'Market close long position',
CLOSE_SHORT = 'Market close short position',
CLOSE_ALL_PERP = 'Market close all perp positions',
}
const TEMPLATE_BUTTON_CLASSES =
'flex w-full items-center justify-between border-t border-th-bkg-3 p-4 focus:outline-none md:hover:bg-th-bkg-2'
const DEFAULT_FORM_VALUES: HotKeyForm = {
custom: '',
name: '',
baseKey: 'shift',
triggerKey: '',
price: '',
@ -43,19 +74,69 @@ const DEFAULT_FORM_VALUES: HotKeyForm = {
reduce: false,
}
const CLOSE_LONG: TEMPLATE = {
price: '',
side: 'sell',
size: '100',
sizeType: 'percentage',
orderType: 'market',
ioc: false,
post: false,
margin: true,
reduce: true,
}
const CLOSE_SHORT: TEMPLATE = {
price: '',
side: 'buy',
size: '100',
sizeType: 'percentage',
orderType: 'market',
ioc: false,
post: false,
margin: true,
reduce: true,
}
const CLOSE_ALL_PERP: CUSTOM_TEMPLATE = {
custom: HOTKEY_TEMPLATES.CLOSE_ALL_PERP,
}
const TABS = ['settings:templates', 'settings:custom']
const HotKeyModal = ({ isOpen, onClose }: ModalProps) => {
const { t } = useTranslation(['common', 'settings', 'trade'])
const [activeTab, setActiveTab] = useState('settings:templates')
const [selectedTemplate, setSelectedTemplate] = useState<
HOTKEY_TEMPLATES | ''
>('')
const [hotKeys, setHotKeys] = useLocalStorageState<HotKey[]>(HOT_KEYS_KEY, [])
const [hotKeyForm, setHotKeyForm] = useState<HotKeyForm>({
...DEFAULT_FORM_VALUES,
})
const [formErrors, setFormErrors] = useState<FormErrors>({})
const handleSwitchTab = (tab: string) => {
setActiveTab(tab)
setHotKeyForm({ ...DEFAULT_FORM_VALUES })
setFormErrors({})
setSelectedTemplate('')
}
const handleSetForm = (propertyName: string, value: string | boolean) => {
setFormErrors({})
setHotKeyForm((prevState) => ({ ...prevState, [propertyName]: value }))
}
const handleSetTemplate = (
template: TEMPLATE | CUSTOM_TEMPLATE,
templateName: HOTKEY_TEMPLATES,
) => {
setFormErrors({})
setHotKeyForm((prevState) => ({ ...prevState, ...template }))
setSelectedTemplate(templateName)
}
const handlePostOnlyChange = (postOnly: boolean) => {
if (postOnly) {
handleSetForm('ioc', !postOnly)
@ -131,27 +212,40 @@ const HotKeyModal = ({ isOpen, onClose }: ModalProps) => {
const handleSave = () => {
const invalidFields = isFormValid(hotKeyForm)
if (Object.keys(invalidFields).length) {
if (Object.keys(invalidFields).length && !hotKeyForm.custom) {
return
}
const newHotKey = {
keySequence: `${hotKeyForm.baseKey}+${hotKeyForm.triggerKey}`,
orderSide: hotKeyForm.side,
orderSizeType: hotKeyForm.sizeType,
orderSize: hotKeyForm.size,
orderType: hotKeyForm.orderType,
orderPrice: hotKeyForm.price,
ioc: hotKeyForm.ioc,
margin: hotKeyForm.margin,
postOnly: hotKeyForm.post,
reduceOnly: hotKeyForm.reduce,
}
const name = hotKeyForm.name ? hotKeyForm.name : selectedTemplate || ''
const newHotKey = !hotKeyForm.custom
? {
keySequence: `${hotKeyForm.baseKey}+${hotKeyForm.triggerKey}`,
custom: hotKeyForm.custom,
name: name,
orderSide: hotKeyForm.side,
orderSizeType: hotKeyForm.sizeType,
orderSize: hotKeyForm.size,
orderType: hotKeyForm.orderType,
orderPrice: hotKeyForm.price,
ioc: hotKeyForm.ioc,
margin: hotKeyForm.margin,
postOnly: hotKeyForm.post,
reduceOnly: hotKeyForm.reduce,
}
: {
keySequence: `${hotKeyForm.baseKey}+${hotKeyForm.triggerKey}`,
custom: hotKeyForm.custom,
name: name,
}
setHotKeys([...hotKeys, newHotKey])
onClose()
}
return (
<Modal isOpen={isOpen} onClose={onClose}>
<Modal
isOpen={isOpen}
onClose={onClose}
panelClassNames="md:max-h-[calc(100vh-10%)] overflow-y-auto thin-scroll"
>
<>
<h2 className="mb-4 text-center">{t('settings:new-hot-key')}</h2>
<div className="mb-4">
@ -184,155 +278,223 @@ const HotKeyModal = ({ isOpen, onClose }: ModalProps) => {
) : null}
</div>
<div className="mb-4">
<Label text={t('settings:order-side')} />
<ButtonGroup
activeValue={hotKeyForm.side}
names={[t('buy'), t('sell')]}
onChange={(side) => handleSetForm('side', side)}
values={['buy', 'sell']}
<Label text={t('settings:nickname')} optional />
<Input
type="text"
value={hotKeyForm.name || ''}
onChange={(e) =>
handleSetForm('name', e.target.value.toLowerCase())
}
/>
</div>
<div className="mb-4">
<Label text={t('trade:order-type')} />
<ButtonGroup
activeValue={hotKeyForm.orderType}
names={[t('trade:limit'), t('market')]}
onChange={(type) => handleSetForm('orderType', type)}
values={['limit', 'market']}
<div className="pb-2">
<TabUnderline
activeValue={activeTab}
values={TABS}
onChange={(v) => handleSwitchTab(v)}
/>
</div>
<div className="mb-4">
<Label text={t('settings:order-size-type')} />
<ButtonGroup
activeValue={hotKeyForm.sizeType}
names={[t('settings:percentage'), t('settings:notional')]}
onChange={(type) => handleSetForm('sizeType', type)}
values={['percentage', 'notional']}
/>
</div>
<div className="flex items-start space-x-4">
<div className="w-full">
<Tooltip
content={
hotKeyForm.sizeType === 'notional'
? t('settings:tooltip-hot-key-notional-size')
: t('settings:tooltip-hot-key-percentage-size')
{activeTab === 'settings:templates' ? (
<div className="border-b border-th-bkg-3">
<button
className={TEMPLATE_BUTTON_CLASSES}
onClick={() =>
handleSetTemplate(CLOSE_LONG, HOTKEY_TEMPLATES.CLOSE_LONG)
}
>
<Label className="tooltip-underline" text={t('trade:size')} />
</Tooltip>
<Input
hasError={formErrors.size !== undefined}
type="text"
value={hotKeyForm.size}
onChange={(e) => handleSetForm('size', e.target.value)}
suffix={hotKeyForm.sizeType === 'percentage' ? '%' : 'USD'}
/>
{formErrors.size ? (
<div className="mt-1">
<InlineNotification
type="error"
desc={formErrors.size}
hideBorder
hidePadding
/>
</div>
) : null}
</div>
{hotKeyForm.orderType === 'limit' ? (
<div className="w-full">
<Tooltip content={t('settings:tooltip-hot-key-price')}>
<Label className="tooltip-underline" text={t('price')} />
</Tooltip>
<Input
hasError={formErrors.price !== undefined}
type="text"
value={hotKeyForm.price}
onChange={(e) => handleSetForm('price', e.target.value)}
placeholder="e.g. 1%"
suffix="%"
<span className="text-th-fgd-2">
{HOTKEY_TEMPLATES.CLOSE_LONG}
</span>
<TemplateCheckMark
isActive={selectedTemplate === HOTKEY_TEMPLATES.CLOSE_LONG}
/>
{formErrors.price ? (
<div className="mt-1">
<InlineNotification
type="error"
desc={formErrors.price}
hideBorder
hidePadding
</button>
<button
className={TEMPLATE_BUTTON_CLASSES}
onClick={() =>
handleSetTemplate(CLOSE_SHORT, HOTKEY_TEMPLATES.CLOSE_SHORT)
}
>
<span className="text-th-fgd-2">
{HOTKEY_TEMPLATES.CLOSE_SHORT}
</span>
<TemplateCheckMark
isActive={selectedTemplate === HOTKEY_TEMPLATES.CLOSE_SHORT}
/>
</button>
<button
className={TEMPLATE_BUTTON_CLASSES}
onClick={() =>
handleSetTemplate(
CLOSE_ALL_PERP,
HOTKEY_TEMPLATES.CLOSE_ALL_PERP,
)
}
>
<span className="text-th-fgd-2">
{HOTKEY_TEMPLATES.CLOSE_ALL_PERP}
</span>
<TemplateCheckMark
isActive={selectedTemplate === HOTKEY_TEMPLATES.CLOSE_ALL_PERP}
/>
</button>
</div>
) : (
<>
<div className="mb-4">
<Label text={t('settings:order-side')} />
<ButtonGroup
activeValue={hotKeyForm.side}
names={[t('buy'), t('sell')]}
onChange={(side) => handleSetForm('side', side)}
values={['buy', 'sell']}
/>
</div>
<div className="mb-4">
<Label text={t('trade:order-type')} />
<ButtonGroup
activeValue={hotKeyForm.orderType}
names={[t('trade:limit'), t('market')]}
onChange={(type) => handleSetForm('orderType', type)}
values={['limit', 'market']}
/>
</div>
<div className="mb-4">
<Label text={t('settings:order-size-type')} />
<ButtonGroup
activeValue={hotKeyForm.sizeType}
names={[t('settings:percentage'), t('settings:notional')]}
onChange={(type) => handleSetForm('sizeType', type)}
values={['percentage', 'notional']}
/>
</div>
<div className="flex items-start space-x-4">
<div className="w-full">
<Tooltip
content={
hotKeyForm.sizeType === 'notional'
? t('settings:tooltip-hot-key-notional-size')
: t('settings:tooltip-hot-key-percentage-size')
}
>
<Label className="tooltip-underline" text={t('trade:size')} />
</Tooltip>
<Input
hasError={formErrors.size !== undefined}
type="text"
value={hotKeyForm.size}
onChange={(e) => handleSetForm('size', e.target.value)}
suffix={hotKeyForm.sizeType === 'percentage' ? '%' : 'USD'}
/>
{formErrors.size ? (
<div className="mt-1">
<InlineNotification
type="error"
desc={formErrors.size}
hideBorder
hidePadding
/>
</div>
) : null}
</div>
{hotKeyForm.orderType === 'limit' ? (
<div className="w-full">
<Tooltip content={t('settings:tooltip-hot-key-price')}>
<Label className="tooltip-underline" text={t('price')} />
</Tooltip>
<Input
hasError={formErrors.price !== undefined}
type="text"
value={hotKeyForm.price}
onChange={(e) => handleSetForm('price', e.target.value)}
placeholder="e.g. 1%"
suffix="%"
/>
{formErrors.price ? (
<div className="mt-1">
<InlineNotification
type="error"
desc={formErrors.price}
hideBorder
hidePadding
/>
</div>
) : null}
</div>
) : null}
</div>
) : null}
</div>
<div className="flex flex-wrap md:flex-nowrap">
{hotKeyForm.orderType === 'limit' ? (
<div className="flex">
<div className="mr-3 mt-4" id="trade-step-six">
<div className="flex flex-wrap md:flex-nowrap">
{hotKeyForm.orderType === 'limit' ? (
<div className="flex">
<div className="mr-3 mt-4" id="trade-step-six">
<Tooltip
className="hidden md:block"
delay={100}
content={t('trade:tooltip-post')}
>
<Checkbox
checked={hotKeyForm.post}
onChange={(e) => handlePostOnlyChange(e.target.checked)}
>
{t('trade:post')}
</Checkbox>
</Tooltip>
</div>
<div className="mr-3 mt-4" id="trade-step-seven">
<Tooltip
className="hidden md:block"
delay={100}
content={t('trade:tooltip-ioc')}
>
<div className="flex items-center text-xs text-th-fgd-3">
<Checkbox
checked={hotKeyForm.ioc}
onChange={(e) => handleIocChange(e.target.checked)}
>
IOC
</Checkbox>
</div>
</Tooltip>
</div>
</div>
) : null}
<div className="mr-3 mt-4" id="trade-step-eight">
<Tooltip
className="hidden md:block"
delay={100}
content={t('trade:tooltip-post')}
content={t('trade:tooltip-enable-margin')}
>
<Checkbox
checked={hotKeyForm.post}
onChange={(e) => handlePostOnlyChange(e.target.checked)}
checked={hotKeyForm.margin}
onChange={(e) => handleSetForm('margin', e.target.checked)}
>
{t('trade:post')}
{t('trade:margin')}
</Checkbox>
</Tooltip>
</div>
<div className="mr-3 mt-4" id="trade-step-seven">
<div className="mr-3 mt-4">
<Tooltip
className="hidden md:block"
delay={100}
content={t('trade:tooltip-ioc')}
content={
'Reduce will only decrease the size of an open position. This is often used for closing a position.'
}
>
<div className="flex items-center text-xs text-th-fgd-3">
<Checkbox
checked={hotKeyForm.ioc}
onChange={(e) => handleIocChange(e.target.checked)}
checked={hotKeyForm.reduce}
onChange={(e) =>
handleSetForm('reduce', e.target.checked)
}
>
IOC
{t('trade:reduce-only')}
</Checkbox>
</div>
</Tooltip>
</div>
</div>
) : null}
<div className="mr-3 mt-4" id="trade-step-eight">
<Tooltip
className="hidden md:block"
delay={100}
content={t('trade:tooltip-enable-margin')}
>
<Checkbox
checked={hotKeyForm.margin}
onChange={(e) => handleSetForm('margin', e.target.checked)}
>
{t('trade:margin')}
</Checkbox>
</Tooltip>
</div>
<div className="mr-3 mt-4">
<Tooltip
className="hidden md:block"
delay={100}
content={
'Reduce will only decrease the size of an open position. This is often used for closing a position.'
}
>
<div className="flex items-center text-xs text-th-fgd-3">
<Checkbox
checked={hotKeyForm.reduce}
onChange={(e) => handleSetForm('reduce', e.target.checked)}
>
{t('trade:reduce-only')}
</Checkbox>
</div>
</Tooltip>
</div>
</div>
</>
)}
<Button className="mt-6 w-full" onClick={handleSave}>
{t('settings:save-hot-key')}
</Button>
@ -342,3 +504,13 @@ const HotKeyModal = ({ isOpen, onClose }: ModalProps) => {
}
export default HotKeyModal
const TemplateCheckMark = ({ isActive }: { isActive: boolean }) => {
return isActive ? (
<div className="flex h-5 w-5 items-center justify-center rounded-full bg-th-success">
<CheckIcon className="h-4 w-4 text-th-bkg-1" />
</div>
) : (
<div className="h-5 w-5 rounded-full bg-th-bkg-4" />
)
}

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react'
import { useEffect, useMemo, useState } from 'react'
import { ModalProps } from '../../types/modal'
import Modal from '../shared/Modal'
import { useTranslation } from 'next-i18next'
@ -41,6 +41,12 @@ const SettingsModal = ({ isOpen, onClose }: ModalProps) => {
isDesktop ? TABS[0] : null,
)
const tabsToShow = useMemo(() => {
if (isDesktop) {
return TABS
} else return TABS.slice(0, -1)
}, [isDesktop])
// set an active tab is screen width is desktop and no tab is set
useEffect(() => {
if (!activeTab && isDesktop) {
@ -59,8 +65,8 @@ const SettingsModal = ({ isOpen, onClose }: ModalProps) => {
<h2 className="mb-6">{t('settings')}</h2>
<div className="grid grid-cols-12 md:gap-8">
{isDesktop || !activeTab ? (
<div className="col-span-12 space-y-2 md:col-span-3 lg:col-span-4">
{TABS.map((tab) => (
<div className="col-span-12 space-y-2 md:col-span-3 2xl:col-span-4">
{tabsToShow.map((tab) => (
<TabButton
activeTab={activeTab}
key={tab}
@ -71,7 +77,7 @@ const SettingsModal = ({ isOpen, onClose }: ModalProps) => {
</div>
) : null}
{isDesktop || activeTab ? (
<div className="col-span-12 md:col-span-9 lg:col-span-8">
<div className="col-span-12 md:col-span-9 2xl:col-span-8">
<TabContent activeTab={activeTab} setActiveTab={setActiveTab} />
</div>
) : null}

View File

@ -1,5 +1,5 @@
import KeyboardIcon from '@components/icons/KeyboardIcon'
import HotKeyModal from '@components/modals/HotKeyModal'
import HotKeyModal, { HOTKEY_TEMPLATES } from '@components/modals/HotKeyModal'
import Button, { IconButton } from '@components/shared/Button'
import InlineNotification from '@components/shared/InlineNotification'
import { Table, Td, Th, TrBody, TrHead } from '@components/shared/TableElements'
@ -10,6 +10,8 @@ import { useState } from 'react'
import { HOT_KEYS_KEY } from 'utils/constants'
export type HotKey = {
custom?: HOTKEY_TEMPLATES
name?: string
ioc: boolean
keySequence: string
margin: boolean
@ -61,6 +63,7 @@ const HotKeysSettings = () => {
<Table>
<thead>
<TrHead>
<Th className="text-left">{t('settings:nickname')}</Th>
<Th className="text-left">{t('settings:key-sequence')}</Th>
<Th className="text-right">{t('trade:order-type')}</Th>
<Th className="text-right">{t('trade:side')}</Th>
@ -73,6 +76,7 @@ const HotKeysSettings = () => {
<tbody>
{hotKeys.map((hk: HotKey) => {
const {
name,
keySequence,
orderSide,
orderPrice,
@ -84,10 +88,11 @@ const HotKeysSettings = () => {
reduceOnly,
postOnly,
} = hk
const size =
orderSizeType === 'percentage'
const size = orderSize
? orderSizeType === 'percentage'
? t('settings:percentage-of-max', { size: orderSize })
: `$${orderSize}`
: ''
const price = orderPrice
? `${orderPrice}% ${
orderSide === 'buy'
@ -105,9 +110,14 @@ const HotKeysSettings = () => {
return (
<TrBody key={keySequence} className="text-right text-th-fgd-2">
<Td className="text-left">{keySequence}</Td>
<Td className="text-right">{t(`trade:${orderType}`)}</Td>
<Td className="text-right">{t(orderSide)}</Td>
<Td className="text-left">{name || ''}</Td>
<Td className="text-right">{keySequence}</Td>
<Td className="text-right">
{orderType ? t(`trade:${orderType}`) : ''}
</Td>
<Td className="text-right">
{orderSide ? t(orderSide) : ''}
</Td>
<Td className="text-right">{size}</Td>
<Td className="text-right">{price}</Td>
<Td className="text-right">

View File

@ -1,4 +1,4 @@
import { FunctionComponent, useCallback, useState } from 'react'
import { FunctionComponent, useState } from 'react'
import mangoStore from '@store/mangoStore'
import { useTranslation } from 'next-i18next'
import Modal from '@components/shared/Modal'
@ -14,6 +14,58 @@ import MarketLogos from './MarketLogos'
import PerpSideBadge from './PerpSideBadge'
import FormatNumericValue from '@components/shared/FormatNumericValue'
export const handleCloseAll = async (
setSubmitting?: (s: boolean) => void,
onClose?: () => void,
) => {
const client = mangoStore.getState().client
const mangoAccount = mangoStore.getState().mangoAccount.current
const actions = mangoStore.getState().actions
const group = mangoStore.getState().group
if (!group || !mangoAccount) {
notify({
title: 'Something went wrong. Try again later',
type: 'error',
})
return
}
if (setSubmitting) {
setSubmitting(true)
}
try {
const maxSlippage = 0.025
const { signature: tx } = await client.perpCloseAll(
group,
mangoAccount,
maxSlippage,
)
actions.fetchOpenOrders()
notify({
type: 'success',
title: 'Transaction successful',
txid: tx,
})
} catch (e) {
if (isMangoError(e)) {
notify({
title: 'There was an issue.',
description: e.message,
txid: e?.txid,
type: 'error',
})
}
console.error('Place trade error:', e)
} finally {
if (setSubmitting) {
setSubmitting(false)
}
if (onClose) {
onClose()
}
}
}
const CloseAllPositionsModal: FunctionComponent<ModalProps> = ({
onClose,
isOpen,
@ -23,47 +75,47 @@ const CloseAllPositionsModal: FunctionComponent<ModalProps> = ({
const openPerpPositions = useOpenPerpPositions()
const { group } = useMangoGroup()
const handleCloseAll = useCallback(async () => {
const client = mangoStore.getState().client
const mangoAccount = mangoStore.getState().mangoAccount.current
const actions = mangoStore.getState().actions
// const handleCloseAll = useCallback(async () => {
// const client = mangoStore.getState().client
// const mangoAccount = mangoStore.getState().mangoAccount.current
// const actions = mangoStore.getState().actions
if (!group || !mangoAccount) {
notify({
title: 'Something went wrong. Try again later',
type: 'error',
})
return
}
setSubmitting(true)
try {
const maxSlippage = 0.025
const { signature: tx } = await client.perpCloseAll(
group,
mangoAccount,
maxSlippage,
)
actions.fetchOpenOrders()
notify({
type: 'success',
title: 'Transaction successful',
txid: tx,
})
} catch (e) {
if (isMangoError(e)) {
notify({
title: 'There was an issue.',
description: e.message,
txid: e?.txid,
type: 'error',
})
}
console.error('Place trade error:', e)
} finally {
setSubmitting(false)
onClose()
}
}, [group, onClose])
// if (!group || !mangoAccount) {
// notify({
// title: 'Something went wrong. Try again later',
// type: 'error',
// })
// return
// }
// setSubmitting(true)
// try {
// const maxSlippage = 0.025
// const { signature: tx } = await client.perpCloseAll(
// group,
// mangoAccount,
// maxSlippage,
// )
// actions.fetchOpenOrders()
// notify({
// type: 'success',
// title: 'Transaction successful',
// txid: tx,
// })
// } catch (e) {
// if (isMangoError(e)) {
// notify({
// title: 'There was an issue.',
// description: e.message,
// txid: e?.txid,
// type: 'error',
// })
// }
// console.error('Place trade error:', e)
// } finally {
// setSubmitting(false)
// onClose()
// }
// }, [group, onClose])
if (!group) return null
@ -106,7 +158,7 @@ const CloseAllPositionsModal: FunctionComponent<ModalProps> = ({
</div>
<Button
className="mb-4 mt-6 flex w-full items-center justify-center"
onClick={handleCloseAll}
onClick={() => handleCloseAll(setSubmitting, onClose)}
size="large"
>
{submitting ? (

View File

@ -96,7 +96,9 @@ const HotKeysDrawer = ({
<div className="border-b border-th-bkg-3">
{hotKeys.map((hk: HotKey) => {
const {
custom,
keySequence,
name,
orderSide,
orderPrice,
orderSize,
@ -135,27 +137,32 @@ const HotKeysDrawer = ({
key={keySequence}
>
<div>
<p className="font-bold text-th-fgd-2">
<p className="text-th-fgd-1">{name}</p>
<p className="font-bold text-th-active">
{keySequence}
</p>
<p>{`${t(orderSide)} ${t(
`trade:${orderType}`,
)}, ${size} at ${price}`}</p>
<div className="flex items-center space-x-2">
{!options.margin &&
selectedMarket instanceof PerpMarket ? (
<div className={BADGE_CLASSNAMES}>
{t('trade:margin')}
</div>
) : null}
{Object.entries(options).map((e) => {
return e[1] ? (
{!custom ? (
<p>{`${t(orderSide)} ${t(
`trade:${orderType}`,
)}, ${size} at ${price}`}</p>
) : null}
{!custom ? (
<div className="flex items-center space-x-2">
{!options.margin &&
selectedMarket instanceof PerpMarket ? (
<div className={BADGE_CLASSNAMES}>
{t(`trade:${e[0]}`)}
{t('trade:margin')}
</div>
) : null
})}
</div>
) : null}
{Object.entries(options).map((e) => {
return e[1] ? (
<div className={BADGE_CLASSNAMES}>
{t(`trade:${e[0]}`)}
</div>
) : null
})}
</div>
) : null}
</div>
<div className="pl-4">
<IconButton

View File

@ -24,6 +24,8 @@ import { floorToDecimal, getDecimalCount } from 'utils/numbers'
import { Market } from '@project-serum/serum'
import { useTranslation } from 'next-i18next'
import { useCustomHotkeys } from 'hooks/useCustomHotKeys'
import { HOTKEY_TEMPLATES } from '@components/modals/HotKeyModal'
import { handleCloseAll } from './CloseAllPositionsModal'
const set = mangoStore.getState().set
@ -173,6 +175,14 @@ const TradeHotKeys = ({ children }: { children: ReactNode }) => {
INITIAL_SOUND_SETTINGS,
)
const handleHotKeyPress = (hkOrder: HotKey) => {
if (hkOrder.custom === HOTKEY_TEMPLATES.CLOSE_ALL_PERP) {
handleCloseAll()
} else {
handlePlaceOrder(hkOrder)
}
}
const handlePlaceOrder = useCallback(
async (hkOrder: HotKey) => {
const client = mangoStore.getState().client
@ -251,7 +261,6 @@ const TradeHotKeys = ({ children }: { children: ReactNode }) => {
return
}
} else {
console.log(baseSize, orderMax)
if (baseSize > orderMax) {
notify({
type: 'error',
@ -353,7 +362,7 @@ const TradeHotKeys = ({ children }: { children: ReactNode }) => {
[serumOrPerpMarket],
)
useCustomHotkeys(handlePlaceOrder)
useCustomHotkeys(handleHotKeyPress)
return <>{children}</>
}

View File

@ -63,6 +63,7 @@
"medium": "Medium",
"network": "Network",
"new-hot-key": "New Hot Key",
"nickname": "Nickname",
"no-hot-keys": "Create your first hot key",
"notification-position": "Notification Position",
"notifications": "Notifications",
@ -107,6 +108,7 @@
"swap-success": "Swap/Trade Success",
"swap-trade-size-selector": "Swap/Trade Size Selector",
"telemetry": "Telemetry",
"templates": "Templates",
"theme": "Theme",
"tooltip-close-collateral-token-instructions": "Close spot open orders slots and cancel trigger orders with {{token}} in the pair. Make sure you have enough free collateral to withdraw your total {{token}} balance.",
"tooltip-close-token-instructions": "Close spot open orders slots and cancel trigger orders with {{token}} in the pair.",

View File

@ -63,6 +63,7 @@
"medium": "Medium",
"network": "Network",
"new-hot-key": "New Hot Key",
"nickname": "Nickname",
"no-hot-keys": "Create your first hot key",
"notification-position": "Notification Position",
"notifications": "Notifications",
@ -107,6 +108,7 @@
"swap-success": "Swap/Trade Success",
"swap-trade-size-selector": "Swap/Trade Size Selector",
"telemetry": "Telemetry",
"templates": "Templates",
"theme": "Theme",
"tooltip-close-collateral-token-instructions": "Close spot open orders slots and cancel trigger orders with {{token}} in the pair. Make sure you have enough free collateral to withdraw your total {{token}} balance.",
"tooltip-close-token-instructions": "Close spot open orders slots and cancel trigger orders with {{token}} in the pair.",

View File

@ -63,6 +63,7 @@
"medium": "Medium",
"network": "Network",
"new-hot-key": "New Hot Key",
"nickname": "Nickname",
"no-hot-keys": "Create your first hot key",
"notification-position": "Notification Position",
"notifications": "Notifications",
@ -107,6 +108,7 @@
"swap-success": "Swap/Trade Success",
"swap-trade-size-selector": "Swap/Trade Size Selector",
"telemetry": "Telemetry",
"templates": "Templates",
"theme": "Theme",
"tooltip-close-collateral-token-instructions": "Close spot open orders slots and cancel trigger orders with {{token}} in the pair. Make sure you have enough free collateral to withdraw your total {{token}} balance.",
"tooltip-close-token-instructions": "Close spot open orders slots and cancel trigger orders with {{token}} in the pair.",

View File

@ -63,6 +63,7 @@
"medium": "中",
"network": "Network",
"new-hot-key": "新热键",
"nickname": "Nickname",
"no-hot-keys": "创建你的第一个热键",
"notification-position": "通知位置",
"notifications": "通知",
@ -108,6 +109,7 @@
"swap-success": "换币/交易成功",
"swap-trade-size-selector": "换币/交易大小选择器",
"telemetry": "Telemetry",
"templates": "Templates",
"theme": "模式",
"tooltip-close-collateral-token-instructions": "Close spot open orders slots and cancel trigger orders with {{token}} in the pair. Make sure you have enough free collateral to withdraw your total {{token}} balance.",
"tooltip-close-token-instructions": "Close spot open orders slots and cancel trigger orders with {{token}} in the pair.",

View File

@ -63,6 +63,7 @@
"medium": "中",
"network": "Network",
"new-hot-key": "新熱鍵",
"nickname": "Nickname",
"no-hot-keys": "創建你的第一個熱鍵",
"notification-position": "通知位置",
"notifications": "通知",
@ -108,6 +109,7 @@
"swap-success": "換幣/交易成功",
"swap-trade-size-selector": "換幣/交易大小選擇器",
"telemetry": "Telemetry",
"templates": "Templates",
"theme": "模式",
"tooltip-close-collateral-token-instructions": "Close spot open orders slots and cancel trigger orders with {{token}} in the pair. Make sure you have enough free collateral to withdraw your total {{token}} balance.",
"tooltip-close-token-instructions": "Close spot open orders slots and cancel trigger orders with {{token}} in the pair.",