Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a123976646 | ||
|
|
788fed9c26 | ||
|
|
b8526a5568 | ||
|
|
b0cd8e71ac |
+1
-1
@@ -37,7 +37,7 @@
|
|||||||
"@cosmjs/proto-signing": "^0.31.0",
|
"@cosmjs/proto-signing": "^0.31.0",
|
||||||
"@cosmjs/stargate": "^0.31.0",
|
"@cosmjs/stargate": "^0.31.0",
|
||||||
"@cosmjs/tendermint-rpc": "^0.31.0",
|
"@cosmjs/tendermint-rpc": "^0.31.0",
|
||||||
"@dydxprotocol/abacus": "^0.4.7",
|
"@dydxprotocol/abacus": "^0.4.8",
|
||||||
"@dydxprotocol/v4-client-js": "^0.32.0",
|
"@dydxprotocol/v4-client-js": "^0.32.0",
|
||||||
"@dydxprotocol/v4-localization": "^0.0.25",
|
"@dydxprotocol/v4-localization": "^0.0.25",
|
||||||
"@ethersproject/providers": "^5.7.2",
|
"@ethersproject/providers": "^5.7.2",
|
||||||
|
|||||||
Generated
+4
-4
@@ -27,8 +27,8 @@ dependencies:
|
|||||||
specifier: ^0.31.0
|
specifier: ^0.31.0
|
||||||
version: 0.31.0
|
version: 0.31.0
|
||||||
'@dydxprotocol/abacus':
|
'@dydxprotocol/abacus':
|
||||||
specifier: ^0.4.7
|
specifier: ^0.4.8
|
||||||
version: 0.4.7
|
version: 0.4.8
|
||||||
'@dydxprotocol/v4-client-js':
|
'@dydxprotocol/v4-client-js':
|
||||||
specifier: ^0.32.0
|
specifier: ^0.32.0
|
||||||
version: 0.32.0
|
version: 0.32.0
|
||||||
@@ -1145,8 +1145,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==}
|
resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@dydxprotocol/abacus@0.4.7:
|
/@dydxprotocol/abacus@0.4.8:
|
||||||
resolution: {integrity: sha512-3c7Dx5H+KeXZYDpAOJs6fqxSNerKqNSD4tuMNn5F7RXAPWUOU8ADQHAFPnrP1PgTxPjfZBZQzLIziKpyXww4NQ==}
|
resolution: {integrity: sha512-hX31m+a5Bwbs4dr5raPDtnflwUzEu/9BIt+LOlQgt1YOViuIse8dozgpgCHvqvA8MttqY3QGPalx5iRB8j8vtw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@dydxprotocol/dydxjs@0.3.0:
|
/@dydxprotocol/dydxjs@0.3.0:
|
||||||
|
|||||||
@@ -80,8 +80,6 @@ export const ComboboxMenu = <MenuItemValue extends string, MenuGroupValue extend
|
|||||||
{group.items.map((item) => (
|
{group.items.map((item) => (
|
||||||
<Fragment key={item.value}>
|
<Fragment key={item.value}>
|
||||||
<Styled.Item
|
<Styled.Item
|
||||||
// value={item.value} // search by both value and textContent
|
|
||||||
// value={[group.groupLabel, item.label, item.tag].filter(Boolean).join('|')} // exclude item.value from searchable terms (not guaranteed to be unique)
|
|
||||||
value={[group.groupLabel, item.value, item.description, item.label, item.tag]
|
value={[group.groupLabel, item.value, item.description, item.label, item.tag]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join('|')}
|
.join('|')}
|
||||||
@@ -116,8 +114,6 @@ export const ComboboxMenu = <MenuItemValue extends string, MenuGroupValue extend
|
|||||||
item.subitems?.map((subitem) => (
|
item.subitems?.map((subitem) => (
|
||||||
<Fragment key={subitem.value}>
|
<Fragment key={subitem.value}>
|
||||||
<Styled.Item
|
<Styled.Item
|
||||||
// value={subitem.value} // search by both value and textContent
|
|
||||||
// value={[group.groupLabel, item.label, subitem.label, subitem.tag].filter(Boolean).join('|')}
|
|
||||||
value={[
|
value={[
|
||||||
group.groupLabel,
|
group.groupLabel,
|
||||||
item.value,
|
item.value,
|
||||||
|
|||||||
@@ -40,7 +40,10 @@ export type AbacusFileSystemProtocol = Omit<
|
|||||||
Abacus.exchange.dydx.abacus.protocols.FileSystemProtocol,
|
Abacus.exchange.dydx.abacus.protocols.FileSystemProtocol,
|
||||||
'__doNotUseOrImplementIt'
|
'__doNotUseOrImplementIt'
|
||||||
>;
|
>;
|
||||||
|
export type AbacusTrackingProtocol = Omit<
|
||||||
|
Abacus.exchange.dydx.abacus.protocols.TrackingProtocol,
|
||||||
|
'__doNotUseOrImplementIt'
|
||||||
|
>;
|
||||||
export type FileLocation = Abacus.exchange.dydx.abacus.protocols.FileLocation;
|
export type FileLocation = Abacus.exchange.dydx.abacus.protocols.FileLocation;
|
||||||
export type ThreadingType = Abacus.exchange.dydx.abacus.protocols.ThreadingType;
|
export type ThreadingType = Abacus.exchange.dydx.abacus.protocols.ThreadingType;
|
||||||
export const CoroutineTimer = Abacus.exchange.dydx.abacus.utils.CoroutineTimer;
|
export const CoroutineTimer = Abacus.exchange.dydx.abacus.utils.CoroutineTimer;
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ export const STRING_KEYS = {
|
|||||||
...WARNINGS_STRING_KEYS,
|
...WARNINGS_STRING_KEYS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type StringKey = keyof typeof STRING_KEYS;
|
||||||
|
|
||||||
export type LocaleData = typeof EN_LOCALE_DATA;
|
export type LocaleData = typeof EN_LOCALE_DATA;
|
||||||
|
|
||||||
export type StringGetterFunction = (a: {
|
export type StringGetterFunction = (a: {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { StatusResponse } from "@0xsquid/sdk";
|
import type { ReactNode } from 'react';
|
||||||
|
import type { StatusResponse } from '@0xsquid/sdk';
|
||||||
|
|
||||||
/** implemented in useNotificationTypes */
|
/** implemented in useNotificationTypes */
|
||||||
export enum NotificationType {
|
export enum NotificationType {
|
||||||
@@ -18,26 +19,31 @@ export type NotificationTypeConfig<
|
|||||||
|
|
||||||
/** React hook to trigger notifications based on app state */
|
/** React hook to trigger notifications based on app state */
|
||||||
useTrigger: (_: {
|
useTrigger: (_: {
|
||||||
trigger: (
|
trigger: ({
|
||||||
|
id,
|
||||||
|
displayData,
|
||||||
|
updateKey,
|
||||||
|
isNew,
|
||||||
|
}: {
|
||||||
/** Unique ID for the triggered notification */
|
/** Unique ID for the triggered notification */
|
||||||
id: _NotificationId,
|
id: _NotificationId;
|
||||||
|
|
||||||
/** Display data for the triggered notification */
|
/** Display data for the triggered notification */
|
||||||
displayData: NotificationDisplayData,
|
displayData: NotificationDisplayData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSON-serializable key.
|
* JSON-serializable key.
|
||||||
* Re-triggers the notification if passed a different value from the last trigger() call (even from a previous browser session).
|
* Re-triggers the notification if passed a different value from the last trigger() call (even from a previous browser session).
|
||||||
* Suggested usage: data dependency array
|
* Suggested usage: data dependency array
|
||||||
*/
|
*/
|
||||||
updateKey?: NotificationUpdateKey,
|
updateKey?: NotificationUpdateKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param true (default): Notification initialized with status NotificationStatus.Triggered
|
* @param true (default): Notification initialized with status NotificationStatus.Triggered
|
||||||
* @param false: Notification initialized with status NotificationStatus.Cleared
|
* @param false: Notification initialized with status NotificationStatus.Cleared
|
||||||
*/
|
*/
|
||||||
isNew?: boolean
|
isNew?: boolean;
|
||||||
) => void;
|
}) => void;
|
||||||
|
|
||||||
lastUpdated: number;
|
lastUpdated: number;
|
||||||
}) => void;
|
}) => void;
|
||||||
@@ -79,15 +85,15 @@ export type Notifications = Record<NotificationId, Notification<any>>;
|
|||||||
|
|
||||||
/** Notification display data derived from app state at runtime. */
|
/** Notification display data derived from app state at runtime. */
|
||||||
export type NotificationDisplayData = {
|
export type NotificationDisplayData = {
|
||||||
icon?: React.ReactNode;
|
icon?: React.ReactElement<any, 'svg'>;
|
||||||
|
|
||||||
title?: string;
|
title?: string;
|
||||||
|
|
||||||
description?: React.ReactNode;
|
description?: ReactNode;
|
||||||
|
|
||||||
customContent?: React.ReactNode;
|
customContent?: ReactNode;
|
||||||
|
|
||||||
customMenuContent?: React.ReactNode;
|
customMenuContent?: ReactNode;
|
||||||
|
|
||||||
actionDescription?: string;
|
actionDescription?: string;
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
import { useCallback, useEffect, useMemo } from 'react';
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
import { useSelector, shallowEqual, useDispatch } from 'react-redux';
|
import { useSelector, shallowEqual, useDispatch } from 'react-redux';
|
||||||
import { groupBy } from 'lodash';
|
import { groupBy } from 'lodash';
|
||||||
|
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client-js';
|
||||||
|
|
||||||
import { AbacusOrderStatus, ORDER_SIDES, ORDER_STATUS_STRINGS } from '@/constants/abacus';
|
import { AbacusOrderStatus, ORDER_SIDES, ORDER_STATUS_STRINGS } from '@/constants/abacus';
|
||||||
import { DialogTypes } from '@/constants/dialogs';
|
import { DialogTypes } from '@/constants/dialogs';
|
||||||
import { STRING_KEYS } from '@/constants/localization';
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
import { type NotificationTypeConfig, NotificationType } from '@/constants/notifications';
|
import { type NotificationTypeConfig, NotificationType } from '@/constants/notifications';
|
||||||
import { ORDER_SIDE_STRINGS, TRADE_TYPE_STRINGS, TradeTypes } from '@/constants/trade';
|
import { ORDER_SIDE_STRINGS } from '@/constants/trade';
|
||||||
|
|
||||||
import { useLocalNotifications } from '@/hooks/useLocalNotifications';
|
import { useLocalNotifications } from '@/hooks/useLocalNotifications';
|
||||||
|
|
||||||
|
import { AssetIcon } from '@/components/AssetIcon';
|
||||||
import { Icon, IconName } from '@/components/Icon';
|
import { Icon, IconName } from '@/components/Icon';
|
||||||
import { Output, OutputType } from '@/components/Output';
|
import { Output, OutputType } from '@/components/Output';
|
||||||
|
import { OrderStatusIcon } from '@/views/OrderStatusIcon';
|
||||||
import { TransferStatusToast } from '@/views/TransferStatus';
|
import { TransferStatusToast } from '@/views/TransferStatus';
|
||||||
|
import { TransferStatusSteps } from '@/views/TransferStatusSteps';
|
||||||
|
|
||||||
import { getSubaccountFills, getSubaccountOrders } from '@/state/accountSelectors';
|
import { getSubaccountFills, getSubaccountOrders } from '@/state/accountSelectors';
|
||||||
import { openDialog } from '@/state/dialogs';
|
import { openDialog } from '@/state/dialogs';
|
||||||
|
|
||||||
import { OrderStatusIcon } from '@/views/OrderStatusIcon';
|
|
||||||
|
|
||||||
import { useStringGetter } from './useStringGetter';
|
import { useStringGetter } from './useStringGetter';
|
||||||
import { TransferStatusSteps } from '@/views/TransferStatusSteps';
|
|
||||||
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client-js';
|
|
||||||
|
|
||||||
export const notificationTypes = [
|
export const notificationTypes = [
|
||||||
{
|
{
|
||||||
@@ -58,34 +58,33 @@ export const notificationTypes = [
|
|||||||
}
|
}
|
||||||
: undefined);
|
: undefined);
|
||||||
|
|
||||||
if (order)
|
if (order) console.log(order);
|
||||||
trigger(
|
trigger({
|
||||||
order.id,
|
id: order.id,
|
||||||
{
|
displayData: {
|
||||||
icon: (
|
icon: <AssetIcon symbol={order.marketId.split('-')[0]} />, //<OrderStatusIcon status={order.status} totalFilled={order.totalFilled ?? 0} />,
|
||||||
<OrderStatusIcon status={order.status} totalFilled={order.totalFilled ?? 0} />
|
title: `${stringGetter({
|
||||||
),
|
key: order.resources.typeStringKey ?? '',
|
||||||
title: `${stringGetter({
|
})} ${
|
||||||
key: TRADE_TYPE_STRINGS[order.type.rawValue as TradeTypes].tradeTypeKey,
|
order.status === AbacusOrderStatus.open && (order?.totalFilled ?? 0) > 0
|
||||||
})} ${
|
? stringGetter({ key: STRING_KEYS.PARTIALLY_FILLED })
|
||||||
order.status === AbacusOrderStatus.open && (order?.totalFilled ?? 0) > 0
|
: stringGetter({ key: ORDER_STATUS_STRINGS[order.status.name] })
|
||||||
? stringGetter({ key: STRING_KEYS.PARTIALLY_FILLED })
|
}`,
|
||||||
: stringGetter({ key: ORDER_STATUS_STRINGS[order.status.name] })
|
|
||||||
}`,
|
description: `${stringGetter({
|
||||||
description: `${stringGetter({
|
key: ORDER_SIDE_STRINGS[ORDER_SIDES[order.side.name]],
|
||||||
key: ORDER_SIDE_STRINGS[ORDER_SIDES[order.side.name]],
|
})} ${order.size} ${order.marketId} @ $${order.price}`,
|
||||||
})} ${order.size} ${order.marketId} @ $${order.price}`,
|
actionDescription: 'View Order',
|
||||||
actionDescription: 'View Order',
|
actionAltText: 'View this order in the Orders tab or the Notifications menu.',
|
||||||
actionAltText: 'View this order in the Orders tab or the Notifications menu.',
|
toastSensitivity:
|
||||||
toastSensitivity:
|
order.status === AbacusOrderStatus.pending ? 'foreground' : 'background',
|
||||||
order.status === AbacusOrderStatus.pending ? 'foreground' : 'background',
|
toastDuration: Infinity,
|
||||||
toastDuration: 5000,
|
},
|
||||||
},
|
updateKey: [order.status.name, order.size],
|
||||||
[order.status.name, order.size],
|
isNew: !order.createdAtMilliseconds || order.createdAtMilliseconds > lastUpdated,
|
||||||
!order.createdAtMilliseconds || order.createdAtMilliseconds > lastUpdated
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}, [orderIds]);
|
}, [orderIds, stringGetter]);
|
||||||
},
|
},
|
||||||
|
|
||||||
useNotificationAction: () => {
|
useNotificationAction: () => {
|
||||||
@@ -120,9 +119,9 @@ export const notificationTypes = [
|
|||||||
const finished = Boolean(status) && status?.squidTransactionStatus !== 'ongoing';
|
const finished = Boolean(status) && status?.squidTransactionStatus !== 'ongoing';
|
||||||
const type = toChainId === TESTNET_CHAIN_ID ? 'deposit' : 'withdraw';
|
const type = toChainId === TESTNET_CHAIN_ID ? 'deposit' : 'withdraw';
|
||||||
|
|
||||||
trigger(
|
trigger({
|
||||||
txHash,
|
id: txHash,
|
||||||
{
|
displayData: {
|
||||||
icon: <Icon iconName={finished ? IconName.Transfer : IconName.Clock} />,
|
icon: <Icon iconName={finished ? IconName.Transfer : IconName.Clock} />,
|
||||||
title: stringGetter({ key: getTitleStringKey(type, finished) }),
|
title: stringGetter({ key: getTitleStringKey(type, finished) }),
|
||||||
// TODO: confirm with design what the description should be
|
// TODO: confirm with design what the description should be
|
||||||
@@ -142,10 +141,10 @@ export const notificationTypes = [
|
|||||||
customMenuContent: !finished && <TransferStatusSteps status={transfer.status} />,
|
customMenuContent: !finished && <TransferStatusSteps status={transfer.status} />,
|
||||||
toastSensitivity: 'foreground',
|
toastSensitivity: 'foreground',
|
||||||
},
|
},
|
||||||
[]
|
updateKey: [],
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}, [transferNotifications]);
|
}, [transferNotifications, stringGetter]);
|
||||||
},
|
},
|
||||||
},
|
} as NotificationTypeConfig<string, []>,
|
||||||
] satisfies NotificationTypeConfig[];
|
] satisfies NotificationTypeConfig[];
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ const useNotificationsContext = () => {
|
|||||||
for (const { type, useTrigger } of notificationTypes)
|
for (const { type, useTrigger } of notificationTypes)
|
||||||
useTrigger({
|
useTrigger({
|
||||||
trigger: useCallback(
|
trigger: useCallback(
|
||||||
(id, displayData, updateKey, isNew = true) => {
|
({ id, displayData, updateKey, isNew = true }) => {
|
||||||
const key = getKey({ type, id });
|
const key = getKey({ type, id });
|
||||||
|
|
||||||
const notification = notifications[key];
|
const notification = notifications[key];
|
||||||
@@ -182,28 +182,23 @@ const useNotificationsContext = () => {
|
|||||||
const iconUrl =
|
const iconUrl =
|
||||||
displayData.icon && (await renderSvgToDataUrl(displayData.icon).catch(() => undefined));
|
displayData.icon && (await renderSvgToDataUrl(displayData.icon).catch(() => undefined));
|
||||||
|
|
||||||
const pushNotification = new globalThis.Notification(displayData.title, {
|
if (displayData.title) {
|
||||||
renotify: true,
|
const pushNotification = new globalThis.Notification(displayData.title, {
|
||||||
tag: getKey(notification),
|
renotify: true,
|
||||||
data: notification,
|
tag: getKey(notification),
|
||||||
body: displayData.description,
|
data: notification,
|
||||||
icon: iconUrl ?? '/favicon.svg',
|
body: displayData.description ?? '',
|
||||||
badge: iconUrl ?? '/favicon.svg',
|
icon: (iconUrl as string) ?? '/favicon.svg',
|
||||||
image: iconUrl ?? '/favicon.svg',
|
badge: (iconUrl as string) ?? '/favicon.svg',
|
||||||
vibrate: displayData.toastSensitivity === 'foreground',
|
image: (iconUrl as string) ?? '/favicon.svg',
|
||||||
requireInteraction: displayData.toastDuration === Infinity,
|
requireInteraction: displayData.toastDuration === Infinity,
|
||||||
// actions: [
|
});
|
||||||
// {
|
|
||||||
// action: displayData.actionDescription,
|
|
||||||
// title: displayData.actionDescription,
|
|
||||||
// }
|
|
||||||
// ].slice(0, globalThis.Notification.maxActions),
|
|
||||||
});
|
|
||||||
|
|
||||||
pushNotification.addEventListener('click', () => {
|
pushNotification.addEventListener('click', () => {
|
||||||
onNotificationAction(notification);
|
onNotificationAction(notification);
|
||||||
markSeen(notification);
|
markSeen(notification);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setPushNotificationsLastUpdated(Date.now());
|
setPushNotificationsLastUpdated(Date.now());
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import AbacusStateNotifier from './stateNotification';
|
|||||||
import AbacusLocalizer from './localizer';
|
import AbacusLocalizer from './localizer';
|
||||||
import AbacusFormatter from './formatter';
|
import AbacusFormatter from './formatter';
|
||||||
import AbacusThreading from './threading';
|
import AbacusThreading from './threading';
|
||||||
|
import AbacusTracking from './tracking';
|
||||||
import AbacusFileSystem, { ENDPOINTS_PATH } from './filesystem';
|
import AbacusFileSystem, { ENDPOINTS_PATH } from './filesystem';
|
||||||
import { LocaleSeparators } from '../numbers';
|
import { LocaleSeparators } from '../numbers';
|
||||||
class AbacusStateManager {
|
class AbacusStateManager {
|
||||||
@@ -59,7 +60,7 @@ class AbacusStateManager {
|
|||||||
new AbacusRest(),
|
new AbacusRest(),
|
||||||
this.websocket,
|
this.websocket,
|
||||||
new AbacusChainTransaction(),
|
new AbacusChainTransaction(),
|
||||||
null,
|
new AbacusTracking(),
|
||||||
new AbacusThreading(),
|
new AbacusThreading(),
|
||||||
new CoroutineTimer(),
|
new CoroutineTimer(),
|
||||||
new AbacusFileSystem()
|
new AbacusFileSystem()
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import type { AbacusTrackingProtocol, Nullable } from '@/constants/abacus';
|
||||||
|
|
||||||
|
class AbacusTracking implements AbacusTrackingProtocol {
|
||||||
|
log(event: string, data: Nullable<string>) {
|
||||||
|
console.log({ event, data });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AbacusTracking;
|
||||||
@@ -21,8 +21,10 @@ const applyComputedStyles = (html: string) => {
|
|||||||
const toDataUrl = (bytes: string, type = 'image/svg+xml') =>
|
const toDataUrl = (bytes: string, type = 'image/svg+xml') =>
|
||||||
new Promise<string | ArrayBuffer | null>((resolve, reject) => {
|
new Promise<string | ArrayBuffer | null>((resolve, reject) => {
|
||||||
Object.assign(new FileReader(), {
|
Object.assign(new FileReader(), {
|
||||||
onload: (e) => resolve(e.target.result),
|
onload: (e: {
|
||||||
onerror: (e) => reject(e.target.error),
|
target: { result: string | ArrayBuffer | PromiseLike<string | ArrayBuffer | null> | null };
|
||||||
|
}) => resolve(e.target.result),
|
||||||
|
onerror: (e: { target: { error: Error } }) => reject(e.target.error),
|
||||||
}).readAsDataURL(new File([bytes], '', { type }));
|
}).readAsDataURL(new File([bytes], '', { type }));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { layoutMixins } from '@/styles/layoutMixins';
|
|||||||
|
|
||||||
import { AbacusOrderStatus, AbacusOrderTypes, type Nullable } from '@/constants/abacus';
|
import { AbacusOrderStatus, AbacusOrderTypes, type Nullable } from '@/constants/abacus';
|
||||||
import { ButtonAction } from '@/constants/buttons';
|
import { ButtonAction } from '@/constants/buttons';
|
||||||
import { STRING_KEYS } from '@/constants/localization';
|
import { STRING_KEYS, StringKey } from '@/constants/localization';
|
||||||
|
|
||||||
import { AssetIcon } from '@/components/AssetIcon';
|
import { AssetIcon } from '@/components/AssetIcon';
|
||||||
import { Button } from '@/components/Button';
|
import { Button } from '@/components/Button';
|
||||||
@@ -38,6 +38,9 @@ type ElementProps = {
|
|||||||
setIsOpen: (open: boolean) => void;
|
setIsOpen: (open: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isStringKey = (key: Nullable<string>): key is StringKey =>
|
||||||
|
key != null && STRING_KEYS.hasOwnProperty(key);
|
||||||
|
|
||||||
export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
|
export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
|
||||||
const stringGetter = useStringGetter();
|
const stringGetter = useStringGetter();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@@ -47,7 +50,7 @@ export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
|
|||||||
const { cancelOrder } = useSubaccount();
|
const { cancelOrder } = useSubaccount();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
asset = {},
|
asset,
|
||||||
cancelReason,
|
cancelReason,
|
||||||
createdAtMilliseconds,
|
createdAtMilliseconds,
|
||||||
expiresAtMilliseconds,
|
expiresAtMilliseconds,
|
||||||
@@ -57,7 +60,7 @@ export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
|
|||||||
price,
|
price,
|
||||||
reduceOnly,
|
reduceOnly,
|
||||||
totalFilled,
|
totalFilled,
|
||||||
resources = {},
|
resources,
|
||||||
size,
|
size,
|
||||||
status,
|
status,
|
||||||
stepSizeDecimals,
|
stepSizeDecimals,
|
||||||
@@ -109,9 +112,9 @@ export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
|
|||||||
<Styled.Row>
|
<Styled.Row>
|
||||||
<Styled.StatusIcon iconName={statusIcon} color={statusIconColor} />
|
<Styled.StatusIcon iconName={statusIcon} color={statusIconColor} />
|
||||||
<Styled.Status>
|
<Styled.Status>
|
||||||
{statusStringKey
|
{isStringKey(statusStringKey)
|
||||||
? stringGetter({ key: statusStringKey })
|
? stringGetter({ key: statusStringKey })
|
||||||
: resources.statusStringKey
|
: isStringKey(resources.statusStringKey)
|
||||||
? stringGetter({ key: resources.statusStringKey })
|
? stringGetter({ key: resources.statusStringKey })
|
||||||
: undefined}
|
: undefined}
|
||||||
</Styled.Status>
|
</Styled.Status>
|
||||||
@@ -121,7 +124,9 @@ export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
|
|||||||
{
|
{
|
||||||
key: 'cancel-reason',
|
key: 'cancel-reason',
|
||||||
label: stringGetter({ key: STRING_KEYS.CANCEL_REASON }),
|
label: stringGetter({ key: STRING_KEYS.CANCEL_REASON }),
|
||||||
value: cancelReason ? stringGetter({ key: STRING_KEYS[cancelReason] }) : undefined,
|
value: isStringKey(cancelReason)
|
||||||
|
? stringGetter({ key: STRING_KEYS[cancelReason] })
|
||||||
|
: undefined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'amount',
|
key: 'amount',
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
|
|
||||||
import { groupBy } from 'lodash';
|
import { groupBy } from 'lodash';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
import { type Notification, NotificationStatus } from '@/constants/notifications';
|
import { type Notification, NotificationStatus } from '@/constants/notifications';
|
||||||
|
import { useStringGetter } from '@/hooks';
|
||||||
import { useNotifications } from '@/hooks/useNotifications';
|
import { useNotifications } from '@/hooks/useNotifications';
|
||||||
|
import { CloseIcon } from '@/icons';
|
||||||
|
|
||||||
import { Button } from '@/components/Button';
|
import { Button } from '@/components/Button';
|
||||||
import { ButtonAction, ButtonSize } from '@/constants/buttons';
|
import { ButtonAction, ButtonSize } from '@/constants/buttons';
|
||||||
@@ -13,9 +15,7 @@ import { DialogPlacement } from '@/components/Dialog';
|
|||||||
import { Output, OutputType } from '@/components/Output';
|
import { Output, OutputType } from '@/components/Output';
|
||||||
import { IconButton } from '@/components/IconButton';
|
import { IconButton } from '@/components/IconButton';
|
||||||
import { Toolbar } from '@/components/Toolbar';
|
import { Toolbar } from '@/components/Toolbar';
|
||||||
import { CloseIcon } from '@/icons';
|
|
||||||
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { layoutMixins } from '@/styles/layoutMixins';
|
import { layoutMixins } from '@/styles/layoutMixins';
|
||||||
|
|
||||||
type ElementProps = {
|
type ElementProps = {
|
||||||
@@ -27,6 +27,8 @@ export const NotificationsMenu = ({
|
|||||||
slotTrigger,
|
slotTrigger,
|
||||||
placement = DialogPlacement.Sidebar,
|
placement = DialogPlacement.Sidebar,
|
||||||
}: ElementProps) => {
|
}: ElementProps) => {
|
||||||
|
const stringGetter = useStringGetter();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
notifications,
|
notifications,
|
||||||
getDisplayData,
|
getDisplayData,
|
||||||
@@ -69,7 +71,7 @@ export const NotificationsMenu = ({
|
|||||||
.map(([status, notifications]) => ({
|
.map(([status, notifications]) => ({
|
||||||
group: status,
|
group: status,
|
||||||
groupLabel: {
|
groupLabel: {
|
||||||
[NotificationStatus.Triggered]: 'New',
|
[NotificationStatus.Triggered]: stringGetter({ key: STRING_KEYS.NEW }),
|
||||||
// [NotificationStatus.Updated]: 'Updates',
|
// [NotificationStatus.Updated]: 'Updates',
|
||||||
[NotificationStatus.Seen]: 'Seen',
|
[NotificationStatus.Seen]: 'Seen',
|
||||||
[NotificationStatus.Cleared]: 'Archived',
|
[NotificationStatus.Cleared]: 'Archived',
|
||||||
@@ -135,7 +137,7 @@ export const NotificationsMenu = ({
|
|||||||
isOpen={isMenuOpen || placement === DialogPlacement.Inline}
|
isOpen={isMenuOpen || placement === DialogPlacement.Inline}
|
||||||
setIsOpen={setIsMenuOpen}
|
setIsOpen={setIsMenuOpen}
|
||||||
items={items}
|
items={items}
|
||||||
title="Notifications"
|
title={stringGetter({ key: STRING_KEYS.NOTIFICATIONS })}
|
||||||
slotTrigger={
|
slotTrigger={
|
||||||
<$TriggerContainer>
|
<$TriggerContainer>
|
||||||
{slotTrigger}
|
{slotTrigger}
|
||||||
@@ -168,7 +170,7 @@ export const NotificationsMenu = ({
|
|||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Clear All
|
{stringGetter({ key: STRING_KEYS.CLEAR })}
|
||||||
</Button>
|
</Button>
|
||||||
</$FooterToolbar>
|
</$FooterToolbar>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user