mirror of
https://github.com/haveno-dex/haveno-ui.git
synced 2025-12-13 23:48:59 -05:00
feat: market offers
--- Reviewed-by: @schowdhuri
This commit is contained in:
parent
07a984bb52
commit
5ed0efd67e
40 changed files with 1191 additions and 39 deletions
|
|
@ -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;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue