mirror of
https://github.com/haveno-dex/haveno-ui.git
synced 2025-01-11 23:49:32 -05:00
feat: market offers
--- Reviewed-by: @schowdhuri
This commit is contained in:
parent
07a984bb52
commit
5ed0efd67e
@ -54,9 +54,9 @@
|
||||
"@testing-library/user-event": "^14.2.0",
|
||||
"@types/jsonwebtoken": "^8.5.8",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"@types/qrcode": "^1.4.2",
|
||||
"@types/react": "<18.0.0",
|
||||
"@types/react-dom": "<18.0.0",
|
||||
"@types/qrcode": "^1.4.2",
|
||||
"@typescript-eslint/eslint-plugin": "5.12.1",
|
||||
"@typescript-eslint/parser": "^5.19.0",
|
||||
"@vitejs/plugin-react": "^1.3.0",
|
||||
@ -95,7 +95,7 @@
|
||||
"dayjs": "^1.11.0",
|
||||
"electron-store": "^8.0.1",
|
||||
"electron-updater": "4.6.5",
|
||||
"haveno-ts": "0.0.5",
|
||||
"haveno-ts": "0.0.6",
|
||||
"joi": "^17.6.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"lodash": "^4.17.21",
|
||||
|
3
packages/renderer/assets/check-circle.svg
Normal file
3
packages/renderer/assets/check-circle.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.5 0C3.36316 0 0 3.36316 0 7.5C0 11.6368 3.36316 15 7.5 15C11.6368 15 15 11.6368 15 7.5C15 3.36316 11.6368 0 7.5 0ZM11.1316 5.81053L6.82105 10.5632C6.71053 10.6895 6.50526 10.6895 6.37895 10.5789L3.69474 7.87895C3.56842 7.75263 3.56842 7.56316 3.69474 7.45263L4.48421 6.66316C4.61053 6.53684 4.8 6.53684 4.91053 6.66316L6.55263 8.32105L9.86842 4.65789C9.97895 4.53158 10.1684 4.53158 10.2947 4.64211L11.1158 5.38421C11.2421 5.49474 11.2421 5.68421 11.1316 5.81053Z" fill="currentColor"/>
|
||||
</svg>
|
After Width: | Height: | Size: 602 B |
@ -30,6 +30,7 @@ import {
|
||||
PaymentMethods,
|
||||
} from "@pages/Account";
|
||||
import { MyWallet } from "@pages/MyWallet";
|
||||
import { MarketsOffersPage } from "@pages/Markets";
|
||||
|
||||
export function AppRoutes() {
|
||||
return (
|
||||
@ -38,6 +39,7 @@ export function AppRoutes() {
|
||||
<Route path={ROUTES.Login} element={<Login />} />
|
||||
<Route path={ROUTES.Welcome} element={<Welcome />} />
|
||||
<Route path={ROUTES.CreateAccount} element={<CreateAccount />} />
|
||||
<Route path={ROUTES.Markets} element={<MarketsOffersPage />} />
|
||||
<Route
|
||||
path={ROUTES.MyWallet}
|
||||
element={
|
||||
|
@ -38,4 +38,5 @@ export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
currencyCode: "EUR",
|
||||
value: 400000.12,
|
||||
format: "symbol",
|
||||
};
|
||||
|
@ -15,15 +15,19 @@
|
||||
// =============================================================================
|
||||
|
||||
import { useMemo } from "react";
|
||||
import type { FormatNumberOptions } from "react-intl";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
interface CurrencyProps {
|
||||
type CurrencyFormatType = "symbol" | "code" | "name" | "narrowSymbol";
|
||||
|
||||
interface CurrencyProps extends FormatNumberOptions {
|
||||
currencyCode?: string;
|
||||
format?: CurrencyFormatType;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export function Currency(props: CurrencyProps) {
|
||||
const { currencyCode, value } = props;
|
||||
const { currencyCode, format, value, ...formatNumberProps } = props;
|
||||
const intl = useIntl();
|
||||
|
||||
const formattedNumber = useMemo(
|
||||
@ -32,7 +36,7 @@ export function Currency(props: CurrencyProps) {
|
||||
...(currencyCode
|
||||
? {
|
||||
currency: currencyCode,
|
||||
currencyDisplay: "code",
|
||||
currencyDisplay: format || "code",
|
||||
style: "currency",
|
||||
}
|
||||
: {
|
||||
@ -40,6 +44,7 @@ export function Currency(props: CurrencyProps) {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 12,
|
||||
}),
|
||||
...formatNumberProps,
|
||||
}),
|
||||
[currencyCode, value]
|
||||
);
|
||||
|
@ -0,0 +1,39 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { createStyles } from "@mantine/core";
|
||||
|
||||
export const useStyles = createStyles((theme) => ({
|
||||
primary: {
|
||||
"thead tr": {
|
||||
backgroundColor: theme.colors.gray[0],
|
||||
|
||||
th: {
|
||||
borderBottomColor: theme.colors.gray[2],
|
||||
color: theme.colors.gray[5],
|
||||
fontSize: 10,
|
||||
fontWeight: 700,
|
||||
letterSpacing: "0.05em",
|
||||
textTransform: "uppercase",
|
||||
},
|
||||
},
|
||||
"tbody tr": {
|
||||
td: {
|
||||
borderBottomColor: theme.colors.gray[2],
|
||||
},
|
||||
},
|
||||
},
|
||||
}));
|
@ -20,23 +20,32 @@ import {
|
||||
getExpandedRowModel,
|
||||
} from "@tanstack/react-table";
|
||||
import { Table as MTable } from "@mantine/core";
|
||||
import type { TableProps } from "./_types";
|
||||
import { TableVariant } from "./_types";
|
||||
import { TableProvider } from "./use-table-context";
|
||||
import { TableHeader } from "./TableHeader";
|
||||
import { TableBody } from "./TableBody";
|
||||
import type { TableProps } from "./_types";
|
||||
import { useStyles } from "./Table.style";
|
||||
|
||||
export function Table(props: TableProps) {
|
||||
const { table, columns, data, tableWrap } = props;
|
||||
const { classes, cx } = useStyles();
|
||||
const { table, columns, data, tableWrap, variant, state } = props;
|
||||
|
||||
const tableInstance = useTableInstance(table, {
|
||||
data,
|
||||
columns,
|
||||
state,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getExpandedRowModel: getExpandedRowModel(),
|
||||
});
|
||||
|
||||
return (
|
||||
<MTable {...tableWrap}>
|
||||
<MTable
|
||||
{...tableWrap}
|
||||
className={cx(tableWrap?.className, {
|
||||
[classes.primary]: variant === TableVariant.Primary,
|
||||
})}
|
||||
>
|
||||
<TableProvider value={{ table: tableInstance, props }}>
|
||||
<TableHeader />
|
||||
<TableBody />
|
||||
|
@ -38,6 +38,7 @@ export function TableBody() {
|
||||
key={cell.id}
|
||||
style={{
|
||||
width: cell.column.getSize(),
|
||||
textAlign: cell.column.columnDef?.meta?.textAlign,
|
||||
}}
|
||||
>
|
||||
{cell.renderCell()}
|
||||
|
@ -36,6 +36,7 @@ export function TableHeader() {
|
||||
colSpan={header.colSpan}
|
||||
style={{
|
||||
width: header.getSize(),
|
||||
textAlign: header.column.columnDef?.meta?.textAlign,
|
||||
}}
|
||||
>
|
||||
{header.isPlaceholder ? null : header.renderHeader()}
|
||||
|
@ -15,13 +15,14 @@
|
||||
// =============================================================================
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import type { ColumnDef, Row } from "@tanstack/react-table";
|
||||
import type { ColumnDef, Row, TableState } from "@tanstack/react-table";
|
||||
import type { TableProps as MTableProps } from "@mantine/core";
|
||||
|
||||
export interface TableProps {
|
||||
columns: Array<ColumnDef<any>>;
|
||||
table: any;
|
||||
data: Array<any>;
|
||||
state?: Partial<TableState>;
|
||||
|
||||
showHeader?: boolean;
|
||||
showFooter?: boolean;
|
||||
@ -29,4 +30,10 @@ export interface TableProps {
|
||||
rowSubComponent?: ({ row }: { row: Row<any> }) => React.ReactNode;
|
||||
|
||||
tableWrap?: MTableProps;
|
||||
variant?: TableVariant;
|
||||
}
|
||||
|
||||
export enum TableVariant {
|
||||
Default = "Default",
|
||||
Primary = "Primary",
|
||||
}
|
||||
|
@ -87,4 +87,4 @@ const data = [
|
||||
height: 2482937,
|
||||
fee: 0.000005096,
|
||||
},
|
||||
] as TWalletTransaction[];
|
||||
] as Array<TWalletTransaction>;
|
||||
|
@ -0,0 +1,58 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { MarketOffersTable } from "./MarketOffersTable";
|
||||
|
||||
export default {
|
||||
title: "molecules/MarketOffersTable",
|
||||
component: MarketOffersTable,
|
||||
} as ComponentMeta<typeof MarketOffersTable>;
|
||||
|
||||
const Template: ComponentStory<typeof MarketOffersTable> = () => {
|
||||
return <MarketOffersTable data={data} />;
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
Default.args = {};
|
||||
|
||||
const data = [
|
||||
{
|
||||
price: 123,
|
||||
priceCurrency: "EUR",
|
||||
priceComparison: 0.12,
|
||||
amount: 123123,
|
||||
amountCurrency: "EUR",
|
||||
cost: 123,
|
||||
costCurrency: "USD",
|
||||
paymentMethod: "Bitcoin",
|
||||
accountAge: 12,
|
||||
accountTrades: 1212,
|
||||
},
|
||||
{
|
||||
price: 123,
|
||||
priceCurrency: "EUR",
|
||||
priceComparison: 0.12,
|
||||
amount: 123123,
|
||||
amountCurrency: "EUR",
|
||||
cost: 123,
|
||||
costCurrency: "USD",
|
||||
paymentMethod: "Altcoins",
|
||||
accountAge: 12,
|
||||
accountTrades: 1212,
|
||||
},
|
||||
];
|
@ -0,0 +1,116 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { MarketOffersTable } from "./MarketOffersTable";
|
||||
import { AppProviders } from "@atoms/AppProviders";
|
||||
|
||||
describe("molecules::MarketOffersTable", () => {
|
||||
it("renders without exploding.", () => {
|
||||
const { asFragment, unmount } = render(
|
||||
<AppProviders>
|
||||
<MarketOffersTable data={data} />
|
||||
</AppProviders>
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("renders all columns.", () => {
|
||||
const { unmount } = render(
|
||||
<AppProviders>
|
||||
<MarketOffersTable data={data} />
|
||||
</AppProviders>
|
||||
);
|
||||
expect(screen.queryByText("Price")).toBeInTheDocument();
|
||||
expect(screen.queryByText("Amount")).toBeInTheDocument();
|
||||
expect(screen.queryByText("Costs")).toBeInTheDocument();
|
||||
expect(screen.queryByText("Payment Method")).toBeInTheDocument();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("renders formatted price value with currency sign.", () => {
|
||||
const { unmount } = render(
|
||||
<AppProviders>
|
||||
<MarketOffersTable data={data} />
|
||||
</AppProviders>
|
||||
);
|
||||
expect(screen.queryByText("€5,000.96")).toBeInTheDocument();
|
||||
expect(screen.queryByText("€9,637.41")).toBeInTheDocument();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("renders formatted amount value with currency code.", () => {
|
||||
const { unmount } = render(
|
||||
<AppProviders>
|
||||
<MarketOffersTable data={data} />
|
||||
</AppProviders>
|
||||
);
|
||||
expect(screen.queryByText("XMR 7,564.94")).toBeInTheDocument();
|
||||
expect(screen.queryByText("XMR 6,483.23")).toBeInTheDocument();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("renders offer formatted payment method.", () => {
|
||||
const { unmount } = render(
|
||||
<AppProviders>
|
||||
<MarketOffersTable data={data} />
|
||||
</AppProviders>
|
||||
);
|
||||
expect(screen.queryByText("Bitcoin")).toBeInTheDocument();
|
||||
expect(screen.queryByText("Altcoins")).toBeInTheDocument();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("renders formatted price percentage.", () => {
|
||||
const { unmount } = render(
|
||||
<AppProviders>
|
||||
<MarketOffersTable data={data} />
|
||||
</AppProviders>
|
||||
);
|
||||
expect(screen.queryByText("12%")).toBeInTheDocument();
|
||||
expect(screen.queryByText("15%")).toBeInTheDocument();
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
|
||||
const data = [
|
||||
{
|
||||
price: 5000.956,
|
||||
priceComparison: 0.12,
|
||||
priceCurrency: "EUR",
|
||||
amount: 7564.94,
|
||||
amountCurrency: "XMR",
|
||||
cost: 532.34,
|
||||
costCurrency: "USD",
|
||||
paymentMethod: "Bitcoin",
|
||||
accountAge: 12,
|
||||
accountTrades: 1212,
|
||||
},
|
||||
{
|
||||
price: 9637.41,
|
||||
priceComparison: 0.15,
|
||||
priceCurrency: "EUR",
|
||||
amount: 6483.23,
|
||||
amountCurrency: "XMR",
|
||||
cost: 983.32,
|
||||
costCurrency: "USD",
|
||||
paymentMethod: "Altcoins",
|
||||
accountAge: 12,
|
||||
accountTrades: 3412,
|
||||
},
|
||||
];
|
@ -0,0 +1,150 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { createStyles } from "@mantine/core";
|
||||
import { createTable } from "@tanstack/react-table";
|
||||
import { useIntl } from "react-intl";
|
||||
import type { MarketOffer } from "./_types";
|
||||
import {
|
||||
MarketOffersPriceCell,
|
||||
MarketOffersAmountCell,
|
||||
MarketOffersAccountTradesCell,
|
||||
MarketOffersCostsCell,
|
||||
MarketOffersAccountAgeCell,
|
||||
MarketOffersPaymentCell,
|
||||
} from "./MarketOffersTableCell";
|
||||
import { Table } from "@molecules/Table";
|
||||
import { LangKeys } from "@constants/lang";
|
||||
import { TableVariant } from "@molecules/Table/_types";
|
||||
|
||||
const table = createTable().setRowType<MarketOffer>();
|
||||
|
||||
interface MarketOffersTableProps {
|
||||
data: Array<MarketOffer>;
|
||||
}
|
||||
|
||||
export function MarketOffersTable({ data }: MarketOffersTableProps) {
|
||||
const { classes } = useStyles();
|
||||
const columns = useMarketOffersColumns();
|
||||
|
||||
return (
|
||||
<Table
|
||||
table={table}
|
||||
columns={columns}
|
||||
data={data}
|
||||
variant={TableVariant.Primary}
|
||||
state={{
|
||||
columnVisibility: {
|
||||
accountAge: false,
|
||||
accountTrades: false,
|
||||
},
|
||||
}}
|
||||
tableWrap={{
|
||||
verticalSpacing: "md",
|
||||
className: classes.root,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
root: {
|
||||
"thead tr th, tbody tr td": {
|
||||
"&:first-of-type": {
|
||||
paddingLeft: 30,
|
||||
},
|
||||
"&:last-of-type": {
|
||||
paddingRight: 30,
|
||||
},
|
||||
},
|
||||
"tbody tr": {
|
||||
td: {
|
||||
background: theme.white,
|
||||
paddingTop: 22,
|
||||
paddingBottom: 22,
|
||||
},
|
||||
"&:last-of-type": {
|
||||
td: {
|
||||
borderBottom: `1px solid ${theme.colors.gray[2]}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const useMarketOffersColumns = () => {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
return [
|
||||
table.createDataColumn("price", {
|
||||
id: "price",
|
||||
header: formatMessage({
|
||||
id: LangKeys.MarketsOffersColumnPrice,
|
||||
defaultMessage: "Price",
|
||||
}),
|
||||
cell: ({ row }) => <MarketOffersPriceCell row={row.original} />,
|
||||
size: 400,
|
||||
}),
|
||||
table.createDataColumn("amount", {
|
||||
id: "amount",
|
||||
header: formatMessage({
|
||||
id: LangKeys.MarketsOffersColumnAmount,
|
||||
defaultMessage: "Amount",
|
||||
}),
|
||||
cell: ({ row }) => <MarketOffersAmountCell row={row.original} />,
|
||||
size: 400,
|
||||
}),
|
||||
table.createDataColumn("cost", {
|
||||
id: "costs",
|
||||
header: formatMessage({
|
||||
id: LangKeys.MarketsOffersColumnCost,
|
||||
defaultMessage: "Costs",
|
||||
}),
|
||||
cell: ({ row }) => <MarketOffersCostsCell row={row.original} />,
|
||||
size: 400,
|
||||
}),
|
||||
table.createDataColumn("paymentMethod", {
|
||||
id: "paymentMethod",
|
||||
header: formatMessage({
|
||||
id: LangKeys.MarketsOffersColumnPaymentMethod,
|
||||
defaultMessage: "Payment Method",
|
||||
}),
|
||||
cell: ({ row }) => <MarketOffersPaymentCell row={row.original} />,
|
||||
size: 400,
|
||||
}),
|
||||
table.createDataColumn("accountAge", {
|
||||
id: "accountAge",
|
||||
header: formatMessage({
|
||||
id: LangKeys.MarketsOffersColumnAccountAge,
|
||||
defaultMessage: "Account Age",
|
||||
}),
|
||||
cell: ({ row }) => <MarketOffersAccountAgeCell row={row.original} />,
|
||||
size: 400,
|
||||
}),
|
||||
table.createDataColumn("accountTrades", {
|
||||
id: "accountTrades",
|
||||
header: formatMessage({
|
||||
id: LangKeys.MarketsOffersColumnAccountTrades,
|
||||
defaultMessage: "Account Trades",
|
||||
}),
|
||||
cell: ({ row }) => <MarketOffersAccountTradesCell row={row.original} />,
|
||||
size: 400,
|
||||
meta: {
|
||||
textAlign: "right",
|
||||
},
|
||||
}),
|
||||
];
|
||||
};
|
@ -0,0 +1,87 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { Group, useMantineTheme } from "@mantine/core";
|
||||
import type { MarketOffer } from "./_types";
|
||||
import { Currency } from "@atoms/Currency";
|
||||
import { BodyText } from "@atoms/Typography";
|
||||
import { fractionToPercent } from "@src/utils/math";
|
||||
import { ReactComponent as CheckCircle } from "@assets/check-circle.svg";
|
||||
|
||||
export function MarketOffersAccountAgeCell({ row }: { row?: MarketOffer }) {
|
||||
const theme = useMantineTheme();
|
||||
|
||||
return (
|
||||
<Group spacing="sm">
|
||||
<CheckCircle color={theme.colors.blue[6]} width={15} height={15} />
|
||||
<BodyText heavy>{row?.accountAge} Days</BodyText>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
export function MarketOffersPriceCell({ row }: { row?: MarketOffer }) {
|
||||
return (
|
||||
<Group spacing="sm">
|
||||
<BodyText heavy>
|
||||
<Currency
|
||||
currencyCode={row?.priceCurrency}
|
||||
value={row?.price || 0}
|
||||
format="symbol"
|
||||
/>
|
||||
</BodyText>
|
||||
|
||||
<BodyText color="gray">
|
||||
<Currency
|
||||
value={fractionToPercent(row?.priceComparison || 0)}
|
||||
minimumFractionDigits={0}
|
||||
/>
|
||||
%
|
||||
</BodyText>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
|
||||
export function MarketOffersAmountCell({ row }: { row?: MarketOffer }) {
|
||||
return (
|
||||
<BodyText heavy>
|
||||
<Currency currencyCode={row?.amountCurrency} value={row?.amount || 0} />
|
||||
</BodyText>
|
||||
);
|
||||
}
|
||||
|
||||
export function MarketOffersCostsCell({ row }: { row?: MarketOffer }) {
|
||||
return (
|
||||
<BodyText heavy>
|
||||
<Currency
|
||||
currencyCode={row?.costCurrency}
|
||||
value={row?.cost || 0}
|
||||
format="symbol"
|
||||
/>
|
||||
</BodyText>
|
||||
);
|
||||
}
|
||||
|
||||
export function MarketOffersAccountTradesCell({ row }: { row?: MarketOffer }) {
|
||||
return (
|
||||
<BodyText heavy>
|
||||
<Currency value={row?.accountTrades || 0} minimumFractionDigits={0} />
|
||||
</BodyText>
|
||||
);
|
||||
}
|
||||
|
||||
export function MarketOffersPaymentCell({ row }: { row?: MarketOffer }) {
|
||||
return <BodyText heavy>{row?.paymentMethod}</BodyText>;
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`molecules::MarketOffersTable > renders without exploding. 1`] = `
|
||||
<DocumentFragment>
|
||||
<table
|
||||
class="mantine-Table-root mantine-1ei5v84"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
colspan="1"
|
||||
style="width: 400px;"
|
||||
>
|
||||
Price
|
||||
</th>
|
||||
<th
|
||||
colspan="1"
|
||||
style="width: 400px;"
|
||||
>
|
||||
Amount
|
||||
</th>
|
||||
<th
|
||||
colspan="1"
|
||||
style="width: 400px;"
|
||||
>
|
||||
Costs
|
||||
</th>
|
||||
<th
|
||||
colspan="1"
|
||||
style="width: 400px;"
|
||||
>
|
||||
Payment Method
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
style="width: 400px;"
|
||||
>
|
||||
<div
|
||||
class="mantine-Group-root mantine-1lumg83"
|
||||
>
|
||||
<div
|
||||
class="mantine-Text-root mantine-Group-child mantine-1h17kkk"
|
||||
>
|
||||
€5,000.96
|
||||
</div>
|
||||
<div
|
||||
class="mantine-Text-root mantine-Group-child mantine-1qscdi2"
|
||||
>
|
||||
12%
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
style="width: 400px;"
|
||||
>
|
||||
<div
|
||||
class="mantine-Text-root mantine-ix3vgq"
|
||||
>
|
||||
XMR 7,564.94
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
style="width: 400px;"
|
||||
>
|
||||
<div
|
||||
class="mantine-Text-root mantine-ix3vgq"
|
||||
>
|
||||
$532.34
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
style="width: 400px;"
|
||||
>
|
||||
<div
|
||||
class="mantine-Text-root mantine-ix3vgq"
|
||||
>
|
||||
Bitcoin
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
style="width: 400px;"
|
||||
>
|
||||
<div
|
||||
class="mantine-Group-root mantine-1lumg83"
|
||||
>
|
||||
<div
|
||||
class="mantine-Text-root mantine-Group-child mantine-1h17kkk"
|
||||
>
|
||||
€9,637.41
|
||||
</div>
|
||||
<div
|
||||
class="mantine-Text-root mantine-Group-child mantine-1qscdi2"
|
||||
>
|
||||
15%
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
style="width: 400px;"
|
||||
>
|
||||
<div
|
||||
class="mantine-Text-root mantine-ix3vgq"
|
||||
>
|
||||
XMR 6,483.23
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
style="width: 400px;"
|
||||
>
|
||||
<div
|
||||
class="mantine-Text-root mantine-ix3vgq"
|
||||
>
|
||||
$983.32
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
style="width: 400px;"
|
||||
>
|
||||
<div
|
||||
class="mantine-Text-root mantine-ix3vgq"
|
||||
>
|
||||
Altcoins
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</DocumentFragment>
|
||||
`;
|
@ -0,0 +1,28 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
export interface MarketOffer {
|
||||
price: number;
|
||||
priceComparison: number;
|
||||
priceCurrency: string;
|
||||
amount: number;
|
||||
amountCurrency: string;
|
||||
cost: number;
|
||||
costCurrency: string;
|
||||
paymentMethod: string;
|
||||
accountAge: number;
|
||||
accountTrades: number;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
export * from "./MarketOffersTable";
|
||||
export * from "./_types";
|
@ -0,0 +1,31 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { MarketsOffers } from "./MarketsOffers";
|
||||
|
||||
export default {
|
||||
title: "organisms/MarketsOffers",
|
||||
component: MarketsOffers,
|
||||
} as ComponentMeta<typeof MarketsOffers>;
|
||||
|
||||
const Template: ComponentStory<typeof MarketsOffers> = () => {
|
||||
return <MarketsOffers />;
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
Default.args = {};
|
@ -0,0 +1,57 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import type { FC } from "react";
|
||||
import { useMemo } from "react";
|
||||
import { Group, Loader } from "@mantine/core";
|
||||
import { transformToMarketsOffers } from "./_utils";
|
||||
import { useMarketsOffers } from "@hooks/haveno/useMarketsOffers";
|
||||
import { MarketOffersTable } from "@organisms/MarketOffersTable";
|
||||
|
||||
export function MarketsOffersLoaded() {
|
||||
const { data } = useMarketsOffers({
|
||||
assetCode: "ETH",
|
||||
direction: "sell",
|
||||
});
|
||||
const tableData = useMemo(() => transformToMarketsOffers(data || []), [data]);
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
return <MarketOffersTable data={tableData} />;
|
||||
}
|
||||
|
||||
const MarketsOffersBoot: FC = ({ children }) => {
|
||||
const { isLoading: isOffersLoading } = useMarketsOffers({
|
||||
assetCode: "ETH",
|
||||
direction: "buy",
|
||||
});
|
||||
return isOffersLoading ? (
|
||||
<Group>
|
||||
<Loader color="gray" style={{ margin: "auto" }} />
|
||||
</Group>
|
||||
) : (
|
||||
<>{children}</>
|
||||
);
|
||||
};
|
||||
|
||||
export function MarketsOffers() {
|
||||
return (
|
||||
<MarketsOffersBoot>
|
||||
<MarketsOffersLoaded />
|
||||
</MarketsOffersBoot>
|
||||
);
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import type { MarketOfferData } from "@hooks/haveno/useMarketsOffers";
|
||||
import type { MarketOffer } from "@organisms/MarketOffersTable";
|
||||
|
||||
export const transformToMarketsOffers = (
|
||||
offers: Array<MarketOfferData>
|
||||
): Array<MarketOffer> => {
|
||||
return offers.map((offer) => ({
|
||||
price: offer.price,
|
||||
priceCurrency: offer.counterCurrencyCode,
|
||||
amount: offer.amount,
|
||||
amountCurrency: offer.baseCurrencyCode,
|
||||
costCurrency: offer.baseCurrencyCode,
|
||||
paymentMethod: offer.paymentMethodShortName,
|
||||
cost: offer.txFee,
|
||||
priceComparison: 0.1,
|
||||
accountAge: 1,
|
||||
accountTrades: 1,
|
||||
}));
|
||||
};
|
@ -0,0 +1,17 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
export * from "./MarketsOffers";
|
@ -16,17 +16,20 @@
|
||||
|
||||
import { UnstyledButton, Group, Text, createStyles } from "@mantine/core";
|
||||
import type { ReactNode } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
interface NavLinkProps {
|
||||
icon: ReactNode;
|
||||
isActive?: boolean;
|
||||
label: string;
|
||||
link: string;
|
||||
}
|
||||
|
||||
export function NavLink({ icon, isActive = false, label }: NavLinkProps) {
|
||||
export function NavLink({ icon, isActive = false, label, link }: NavLinkProps) {
|
||||
const { classes } = useStyles({ isActive });
|
||||
|
||||
return (
|
||||
<UnstyledButton className={classes.navLink}>
|
||||
<UnstyledButton component={Link} className={classes.navLink} to={link}>
|
||||
<Group>
|
||||
{icon}
|
||||
<Text
|
||||
|
@ -32,9 +32,9 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
|
||||
<div
|
||||
class="mantine-khtkeg"
|
||||
>
|
||||
<button
|
||||
<a
|
||||
class="mantine-UnstyledButton-root mantine-1fq8ags"
|
||||
type="button"
|
||||
href="#/markets"
|
||||
>
|
||||
<div
|
||||
class="mantine-Group-root mantine-6y1794"
|
||||
@ -58,14 +58,14 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
|
||||
Markets
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="mantine-khtkeg"
|
||||
>
|
||||
<button
|
||||
<a
|
||||
class="mantine-UnstyledButton-root mantine-1fq8ags"
|
||||
type="button"
|
||||
href="#/"
|
||||
>
|
||||
<div
|
||||
class="mantine-Group-root mantine-6y1794"
|
||||
@ -93,14 +93,14 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
|
||||
My Offers
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="mantine-khtkeg"
|
||||
>
|
||||
<button
|
||||
<a
|
||||
class="mantine-UnstyledButton-root mantine-1fq8ags"
|
||||
type="button"
|
||||
href="#/"
|
||||
>
|
||||
<div
|
||||
class="mantine-Group-root mantine-6y1794"
|
||||
@ -132,14 +132,14 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
|
||||
My Trades
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="mantine-khtkeg"
|
||||
>
|
||||
<button
|
||||
<a
|
||||
class="mantine-UnstyledButton-root mantine-1fq8ags"
|
||||
type="button"
|
||||
href="#/"
|
||||
>
|
||||
<div
|
||||
class="mantine-Group-root mantine-6y1794"
|
||||
@ -163,14 +163,14 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
|
||||
Notifications
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="mantine-khtkeg"
|
||||
>
|
||||
<button
|
||||
<a
|
||||
class="mantine-UnstyledButton-root mantine-1fq8ags"
|
||||
type="button"
|
||||
href="#/"
|
||||
>
|
||||
<div
|
||||
class="mantine-Group-root mantine-6y1794"
|
||||
@ -193,7 +193,7 @@ exports[`molecules::Sidebar > renders without exploding 1`] = `
|
||||
Account
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="mantine-khtkeg"
|
||||
|
@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import { ReactComponent as MarketsIcon } from "@assets/markets.svg";
|
||||
import { ReactComponent as OffersIcon } from "@assets/offers.svg";
|
||||
import { ReactComponent as TradesIcon } from "@assets/trades.svg";
|
||||
@ -22,25 +23,36 @@ import { ReactComponent as AccountIcon } from "@assets/account.svg";
|
||||
|
||||
export const WIDTH = 210;
|
||||
|
||||
interface NavigationLink {
|
||||
icon: ReactNode;
|
||||
label: string;
|
||||
link: string;
|
||||
}
|
||||
|
||||
export const NAV_LINKS = [
|
||||
{
|
||||
icon: <MarketsIcon />,
|
||||
label: "Markets",
|
||||
link: "/markets",
|
||||
},
|
||||
{
|
||||
icon: <OffersIcon />,
|
||||
label: "My Offers",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
icon: <TradesIcon />,
|
||||
label: "My Trades",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
icon: <NotificationsIcon />,
|
||||
label: "Notifications",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
icon: <AccountIcon />,
|
||||
label: "Account",
|
||||
link: "/",
|
||||
},
|
||||
];
|
||||
] as Array<NavigationLink>;
|
||||
|
@ -19,7 +19,7 @@ import { NavbarLayout } from "@templates/NavbarLayout";
|
||||
import { AccountSidebar } from "@organisms/AccountSidebar";
|
||||
|
||||
interface AccountContentProps {
|
||||
children: JSX.Element | JSX.Element[];
|
||||
children: JSX.Element | Array<JSX.Element>;
|
||||
}
|
||||
|
||||
function AccountContent({ children }: AccountContentProps) {
|
||||
@ -34,7 +34,7 @@ function AccountContent({ children }: AccountContentProps) {
|
||||
}
|
||||
|
||||
interface AccountLayoutProps {
|
||||
children: JSX.Element | JSX.Element[];
|
||||
children: JSX.Element | Array<JSX.Element>;
|
||||
}
|
||||
|
||||
export function AccountLayout({ children }: AccountLayoutProps) {
|
||||
|
@ -14,15 +14,24 @@
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { Box, createStyles, Group } from "@mantine/core";
|
||||
import type { FC } from "react";
|
||||
import type { DefaultProps } from "@mantine/core";
|
||||
import { Box, createStyles, Group } from "@mantine/core";
|
||||
import { Sidebar } from "@organisms/Sidebar";
|
||||
|
||||
export const NavbarLayout: FC = (props) => {
|
||||
const { children } = props;
|
||||
const { classes } = useStyles();
|
||||
export const NavbarLayout: FC<DefaultProps> = ({
|
||||
children,
|
||||
classNames,
|
||||
className,
|
||||
...rest
|
||||
}) => {
|
||||
const { classes, cx } = useStyles(undefined, {
|
||||
name: "NavbarLayout",
|
||||
classNames,
|
||||
});
|
||||
|
||||
return (
|
||||
<Group className={classes.container} spacing={0}>
|
||||
<Group className={cx(classes.container, className)} spacing={0} {...rest}>
|
||||
<Sidebar />
|
||||
<Box className={classes.contentArea}>{children}</Box>
|
||||
</Group>
|
||||
|
@ -99,4 +99,11 @@ export enum LangKeys {
|
||||
AccountBackupRestoreBtn = "account.backup.restore.btn",
|
||||
AccountBackupDownloadSuccessNotif = "account.backup.download.successNotification",
|
||||
AccountBackupRestoreSuccessNotif = "account.backup.restore.successNotification",
|
||||
|
||||
MarketsOffersColumnPrice = "marketsOffers.columnPrice",
|
||||
MarketsOffersColumnAmount = "marketsOffers.columnAmount",
|
||||
MarketsOffersColumnCost = "marketsOffers.columnCost",
|
||||
MarketsOffersColumnPaymentMethod = "marketsOffers.columnPaymentMethod",
|
||||
MarketsOffersColumnAccountAge = "marketsOffers.columnAccountAge",
|
||||
MarketsOffersColumnAccountTrades = "marketsOffers.columnAccountTypes",
|
||||
}
|
||||
|
@ -116,6 +116,12 @@ const LangPackEN: { [key in LangKeys]: string } = {
|
||||
"The backup has been downloaded successfully.",
|
||||
[LangKeys.AccountBackupRestoreSuccessNotif]:
|
||||
"The backup has been restored successfully.",
|
||||
[LangKeys.MarketsOffersColumnPrice]: "Price",
|
||||
[LangKeys.MarketsOffersColumnAmount]: "Amount",
|
||||
[LangKeys.MarketsOffersColumnCost]: "Costs",
|
||||
[LangKeys.MarketsOffersColumnAccountAge]: "Account Age",
|
||||
[LangKeys.MarketsOffersColumnAccountTrades]: "Account Trades",
|
||||
[LangKeys.MarketsOffersColumnPaymentMethod]: "Payment Method",
|
||||
};
|
||||
|
||||
export default LangPackEN;
|
||||
|
@ -119,6 +119,12 @@ const LangPackES: { [key in LangKeys]: string } = {
|
||||
"La copia de seguridad se ha descargado correctamente.",
|
||||
[LangKeys.AccountBackupRestoreSuccessNotif]:
|
||||
"La copia de seguridad se ha restaurado correctamente.",
|
||||
[LangKeys.MarketsOffersColumnPrice]: "Precio",
|
||||
[LangKeys.MarketsOffersColumnAmount]: "Monto",
|
||||
[LangKeys.MarketsOffersColumnCost]: "Costos",
|
||||
[LangKeys.MarketsOffersColumnAccountAge]: "Edad de la cuenta",
|
||||
[LangKeys.MarketsOffersColumnAccountTrades]: "Operaciones de cuenta",
|
||||
[LangKeys.MarketsOffersColumnPaymentMethod]: "Método de pago",
|
||||
};
|
||||
|
||||
export default LangPackES;
|
||||
|
@ -29,6 +29,8 @@ export enum QueryKeys {
|
||||
XmrSeed = "Haveno.XmrSeed",
|
||||
XmrPrimaryAddress = "Haveno.XmrPrimaryAddress",
|
||||
XmrTxs = "Haveno.XmrTransactions",
|
||||
MarketsOffers = "Haveno.MarketsOffers",
|
||||
MyOffers = "Haveno.MyOffers",
|
||||
|
||||
// Storage
|
||||
StorageAccountInfo = "Storage.AccountInfo",
|
||||
|
@ -22,6 +22,7 @@ export const ROUTES = {
|
||||
CreateAccount: "/onboarding/create-account",
|
||||
RestoreBackup: "/onboarding/restore-backup",
|
||||
MyWallet: "/my-wallet",
|
||||
Markets: "/markets",
|
||||
|
||||
// Account routes
|
||||
PaymentAccounts: "/account/payment-accounts",
|
||||
|
82
packages/renderer/src/hooks/haveno/useMarketsOffers.ts
Normal file
82
packages/renderer/src/hooks/haveno/useMarketsOffers.ts
Normal file
@ -0,0 +1,82 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { useQuery } from "react-query";
|
||||
import type { OfferInfo } from "haveno-ts";
|
||||
import { useHavenoClient } from "./useHavenoClient";
|
||||
import { QueryKeys } from "@constants/query-keys";
|
||||
|
||||
interface MarketsOfferesQuery {
|
||||
assetCode: string;
|
||||
direction?: "buy" | "sell";
|
||||
}
|
||||
|
||||
export function useMarketsOffers(query: MarketsOfferesQuery) {
|
||||
const client = useHavenoClient();
|
||||
|
||||
return useQuery<Array<MarketOfferData>, Error>(
|
||||
[QueryKeys.MarketsOffers, query],
|
||||
async () => {
|
||||
const offers = await client.getMyOffers(query.assetCode);
|
||||
return transformData(offers);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const transformData = (offers: Array<OfferInfo>) => {
|
||||
return offers.map((offerObj: OfferInfo): MarketOfferData => {
|
||||
const offer = offerObj.toObject();
|
||||
|
||||
return {
|
||||
...offer,
|
||||
price: parseFloat(offer.price),
|
||||
volume: parseFloat(offer.volume),
|
||||
minVolume: parseFloat(offer.minVolume),
|
||||
triggerPrice: parseFloat(offer.triggerPrice),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export interface MarketOfferData {
|
||||
id: string;
|
||||
direction: string;
|
||||
price: number;
|
||||
useMarketBasedPrice: boolean;
|
||||
marketPriceMarginPct: number;
|
||||
amount: number;
|
||||
minAmount: number;
|
||||
volume: number;
|
||||
minVolume: number;
|
||||
buyerSecurityDeposit: number;
|
||||
triggerPrice: number;
|
||||
paymentAccountId: string;
|
||||
paymentMethodId: string;
|
||||
paymentMethodShortName: string;
|
||||
baseCurrencyCode: string;
|
||||
counterCurrencyCode: string;
|
||||
date: number;
|
||||
state: string;
|
||||
sellerSecurityDeposit: number;
|
||||
offerFeePaymentTxId: string;
|
||||
txFee: number;
|
||||
makerFee: number;
|
||||
isActivated: boolean;
|
||||
isMyOffer: boolean;
|
||||
ownerNodeAddress: string;
|
||||
pubKeyRing: string;
|
||||
versionNr: string;
|
||||
protocolVersion: number;
|
||||
}
|
33
packages/renderer/src/hooks/haveno/useMyOffers.ts
Normal file
33
packages/renderer/src/hooks/haveno/useMyOffers.ts
Normal file
@ -0,0 +1,33 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { useQuery } from "react-query";
|
||||
import type { OfferInfo } from "haveno-ts";
|
||||
import { useHavenoClient } from "./useHavenoClient";
|
||||
import { QueryKeys } from "@constants/query-keys";
|
||||
|
||||
interface MyOfferesQuery {
|
||||
assetCode: string;
|
||||
direction?: "buy" | "sell";
|
||||
}
|
||||
|
||||
export function useMarketsOffers(query: MyOfferesQuery) {
|
||||
const client = useHavenoClient();
|
||||
|
||||
return useQuery<Array<OfferInfo>, Error>([QueryKeys.MyOffers, query], () =>
|
||||
client.getMyOffers(query.assetCode, query.direction)
|
||||
);
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { useMutation, useQueryClient } from "react-query";
|
||||
import { showNotification } from "@mantine/notifications";
|
||||
import { useHavenoClient } from "./useHavenoClient";
|
||||
import { QueryKeys } from "@constants/query-keys";
|
||||
|
||||
interface SetCryptoPaymentAccount {
|
||||
accountName: string;
|
||||
assetCode: string;
|
||||
address: string;
|
||||
}
|
||||
|
||||
export function useSetCryptoPaymentAccount() {
|
||||
const client = useHavenoClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation(
|
||||
async (variables: SetCryptoPaymentAccount) => {
|
||||
return client.createCryptoPaymentAccount(
|
||||
variables.accountName,
|
||||
variables.assetCode,
|
||||
variables.address
|
||||
);
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries(QueryKeys.PaymentAccounts);
|
||||
},
|
||||
onError: (err: Error) => {
|
||||
console.dir(err);
|
||||
showNotification({
|
||||
color: "red",
|
||||
message: err.message || "",
|
||||
title: "Something went wrong",
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
67
packages/renderer/src/hooks/haveno/useSetOffer.ts
Normal file
67
packages/renderer/src/hooks/haveno/useSetOffer.ts
Normal file
@ -0,0 +1,67 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { useMutation, useQueryClient } from "react-query";
|
||||
import { showNotification } from "@mantine/notifications";
|
||||
import { useHavenoClient } from "./useHavenoClient";
|
||||
import { QueryKeys } from "@constants/query-keys";
|
||||
|
||||
interface SetOfferVariables {
|
||||
direction: string;
|
||||
amount: bigint;
|
||||
assetCode: string;
|
||||
paymentAccountId: string;
|
||||
buyerSecurityDeposit: number;
|
||||
price?: number;
|
||||
marketPriceMargin?: number;
|
||||
triggerPrice?: number;
|
||||
minAmount?: bigint;
|
||||
}
|
||||
|
||||
export function useSetOffer() {
|
||||
const client = useHavenoClient();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation(
|
||||
async (variables: SetOfferVariables) => {
|
||||
return client.postOffer(
|
||||
variables.direction,
|
||||
variables.amount,
|
||||
variables.assetCode,
|
||||
variables.paymentAccountId,
|
||||
variables.buyerSecurityDeposit,
|
||||
variables.price,
|
||||
variables.marketPriceMargin,
|
||||
variables.triggerPrice,
|
||||
variables.minAmount
|
||||
);
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries(QueryKeys.MarketsOffers);
|
||||
queryClient.invalidateQueries(QueryKeys.MyOffers);
|
||||
},
|
||||
onError: (err: Error) => {
|
||||
console.dir(err);
|
||||
showNotification({
|
||||
color: "red",
|
||||
message: err.message || "",
|
||||
title: "Something went wrong",
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
35
packages/renderer/src/pages/Markets/MarketsOffers.tsx
Normal file
35
packages/renderer/src/pages/Markets/MarketsOffers.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { createStyles } from "@mantine/core";
|
||||
import { NavbarLayout } from "@templates/NavbarLayout";
|
||||
import { MarketsOffers } from "@organisms/MarketsOffers";
|
||||
|
||||
export function MarketsOffersPage() {
|
||||
const { classes } = useStyles();
|
||||
|
||||
return (
|
||||
<NavbarLayout classNames={{ contentArea: classes.contentArea }}>
|
||||
<MarketsOffers />
|
||||
</NavbarLayout>
|
||||
);
|
||||
}
|
||||
|
||||
const useStyles = createStyles(() => ({
|
||||
contentArea: {
|
||||
padding: 0,
|
||||
},
|
||||
}));
|
17
packages/renderer/src/pages/Markets/index.ts
Normal file
17
packages/renderer/src/pages/Markets/index.ts
Normal file
@ -0,0 +1,17 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
export * from "./MarketsOffers";
|
19
packages/renderer/src/utils/math.ts
Normal file
19
packages/renderer/src/utils/math.ts
Normal file
@ -0,0 +1,19 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
export const fractionToPercent = (value: number) => {
|
||||
return value * 100;
|
||||
};
|
@ -8187,10 +8187,10 @@ hastscript@^6.0.0:
|
||||
property-information "^5.0.0"
|
||||
space-separated-tokens "^1.0.0"
|
||||
|
||||
haveno-ts@0.0.5:
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/haveno-ts/-/haveno-ts-0.0.5.tgz#996e290b4dd1659e58f86aff4e0cc0d3c7516ad3"
|
||||
integrity sha512-HzBuvMmsQLUybk99NqOe50lAcYmnwv2uv0/5IMAIuUpqFmJq2xvPW6vvZASXkRBVYPdRYjLLnUV5ygy8Rq7Yog==
|
||||
haveno-ts@0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/haveno-ts/-/haveno-ts-0.0.6.tgz#ba83986fb741744d3a4441ee323992deff838d0d"
|
||||
integrity sha512-9OYfr2tGD8nTSSBzgltVeoZdntddwt6WrYj4SLN+9BArqSEdZ9jF+6O2xtUGaVaJDNVli2Eg6pINEAxsUN5MhA==
|
||||
dependencies:
|
||||
"@types/node" "^17.0.30"
|
||||
console "^0.7.2"
|
||||
|
Loading…
Reference in New Issue
Block a user