chore: 🔧 remove hodlhodl (#113)

* chore: 🔧 remove hodlhodl

* chore: 🔧 cleanup package json
This commit is contained in:
Anthony Potdevin 2020-08-03 17:20:31 +02:00 • committed by Anthony Potdevin
parent 581185e6b0
commit c8a9869062
No known key found for this signature in database
GPG key ID: 4403F1DFBE779457
28 changed files with 9 additions and 1858 deletions

2
.env
View file

@ -10,7 +10,6 @@
# Server Configs
# -----------
# LOG_LEVEL='info'
# HODL_KEY='HODL_HODL_API_KEY'
# BASE_PATH='/basePath'
# -----------
@ -24,7 +23,6 @@
# -----------
# FETCH_PRICES=false
# FETCH_FEES=false
# HODL_HODL=false
# DISABLE_LINKS=true
# NO_CLIENT_ACCOUNTS=true
# NO_VERSION_CHECK=true

View file

@ -14,9 +14,6 @@ COPY package.json .
COPY package-lock.json .
RUN npm install --production --silent
# Install dependencies necessary for build and start
RUN npm install -D cross-env typescript @types/react @next/bundle-analyzer
# ---------------
# Build App
# ---------------

View file

@ -102,22 +102,7 @@ This repository consists of a **NextJS** server that handles both the backend **
## **Requirements**
- Yarn/npm installed
- Node installed (Version 12.16.0 or higher)
**Older Versions of Node**
Earlier versions of Node can be used if you replace the following commands:
```js
//Yarn
yarn start -> yarn start:compatible
yarn dev -> yarn dev:compatible
//NPM
npm start -> npm start:compatible
npm run dev -> npm run dev:compatible
```
**HodlHodl integration will not work with older versions of Node!**
- Node installed (Version 10 or higher)
---
@ -132,7 +117,6 @@ You can define some environment variables that ThunderHub can start with. To do
# Server Configs
# -----------
LOG_LEVEL = 'error' | 'warn' | 'info' | 'http' | 'verbose' | 'debug' | 'silly' # Default: 'info'
HODL_KEY = '[Key provided by HodlHodl]' # Default: ''
BASE_PATH = '[Base path where you want to have thunderhub running i.e. '/btcpay']' # Default: ''
# -----------
@ -146,7 +130,6 @@ CURRENCY = 'sat' | 'btc' | 'fiat' # Default: 'sat'
# -----------
FETCH_PRICES = true | false # Default: true
FETCH_FEES = true | false # Default: true
HODL_HODL = true | false # Default: true
DISABLE_LINKS = true | false # Default: false
NO_CLIENT_ACCOUNTS = true | false # Default: false
NO_VERSION_CHECK = true | false # Default: false
@ -252,11 +235,6 @@ ThunderHub shows you links for quick viewing of nodes by public key on [1ml.com]
If you don't want to show these links, you can set `DISABLE_LINKS=true` in your `.env` file.
**HodlHodl**
ThunderHub has a HodlHodl integration to view offers from this platform.
If you want to disable this integration, you can set `HODL_HODL=false` in your `.env` file.
**Client Accounts**
ThunderHub allows you to create accounts on the browser which are also encrypted and stored in the same browser.

View file

@ -14,9 +14,6 @@ COPY package.json .
COPY package-lock.json .
RUN npm install --production --silent
# Install dependencies necessary for build and start
RUN npm install -D cross-env typescript @types/react @next/bundle-analyzer
# ---------------
# Build App
# ---------------

View file

@ -14,9 +14,6 @@ COPY package.json .
COPY package-lock.json .
RUN npm install --production --silent
# Install dependencies necessary for build and start
RUN npm install -D cross-env typescript @types/react @next/bundle-analyzer
# ---------------
# Build App
# ---------------

View file

@ -9,7 +9,6 @@ module.exports = withBundleAnalyzer({
serverRuntimeConfig: {
nodeEnv: process.env.NODE_ENV || 'development',
logLevel: process.env.LOG_LEVEL || 'info',
hodlKey: process.env.HODL_KEY || '',
cookiePath: process.env.COOKIE_PATH || '',
lnServerUrl: process.env.SSO_SERVER_URL || '',
lnCertPath: process.env.SSO_CERT_PATH || '',
@ -26,7 +25,6 @@ module.exports = withBundleAnalyzer({
defaultCurrency: process.env.CURRENCY || 'sat',
fetchPrices: process.env.FETCH_PRICES === 'false' ? false : true,
fetchFees: process.env.FETCH_FEES === 'false' ? false : true,
hodlhodl: process.env.HODL_HODL === 'false' ? false : true,
disableLinks: process.env.DISABLE_LINKS === 'true' ? true : false,
noVersionCheck: process.env.NO_VERSION_CHECK === 'true' ? true : false,
},

52
package-lock.json generated
View file

@ -9919,58 +9919,6 @@
"sha.js": "^2.4.8"
}
},
"cross-env": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.2.tgz",
"integrity": "sha512-KZP/bMEOJEDCkDQAyRhu3RL2ZO/SUVrxQVI0G3YEQ+OLbRA3c6zgixe8Mq8a/z7+HKlNEjo8oiLUs8iRijY2Rw==",
"dev": true,
"requires": {
"cross-spawn": "^7.0.1"
},
"dependencies": {
"cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"dev": true,
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
}
},
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true
},
"shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"requires": {
"shebang-regex": "^3.0.0"
}
},
"shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"requires": {
"isexe": "^2.0.0"
}
}
}
},
"cross-fetch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.4.tgz",

View file

@ -6,12 +6,9 @@
"scripts": {
"bs": "yarn build && yarn start",
"dev": "next",
"dev:compatible": "next",
"build": "next build",
"start": "next start",
"start:two": "next start -p 3001",
"start:compatible": "next start",
"start:compatible:two": "next start -p 3001",
"start:cookie": "sh ./scripts/initCookie.sh",
"lint": "eslint . --ext ts --ext tsx --ext js",
"format": "prettier --write \"**/*.{js,ts,tsx}\"",
@ -19,7 +16,7 @@
"release:push": "standard-version --sign && git push --follow-tags origin master",
"release:test": "standard-version --sign --dry-run",
"release:minor": "standard-version --sign --release-as minor && git push --follow-tags origin master",
"analyze": "cross-env ANALYZE=true next build",
"analyze": "npx cross-env ANALYZE=true next build",
"generate": "graphql-codegen --config codegen.yml && yarn format",
"test": "jest",
"test:watch": "jest --watch",
@ -38,6 +35,8 @@
"license": "MIT",
"dependencies": {
"@apollo/client": "^3.1.1",
"@next/bundle-analyzer": "^9.4.4",
"@types/react": "^16.9.43",
"apollo-server-micro": "^2.15.1",
"balanceofsatoshis": "^5.40.2",
"bcryptjs": "^2.4.3",
@ -71,6 +70,7 @@
"styled-components": "^5.1.1",
"styled-react-modal": "^2.0.1",
"styled-theming": "^2.2.0",
"typescript": "^3.9.6",
"underscore": "^1.10.2",
"uuid": "^8.2.0",
"victory": "^35.0.3",
@ -88,7 +88,6 @@
"@graphql-codegen/typescript-operations": "^1.17.0",
"@graphql-codegen/typescript-react-apollo": "^1.17.0",
"@graphql-codegen/typescript-resolvers": "^1.17.0",
"@next/bundle-analyzer": "^9.4.4",
"@testing-library/jest-dom": "^5.11.0",
"@testing-library/react": "^10.4.5",
"@types/bcryptjs": "^2.4.2",
@ -104,7 +103,6 @@
"@types/node": "^14.0.22",
"@types/numeral": "0.0.28",
"@types/qrcode.react": "^1.0.1",
"@types/react": "^16.9.43",
"@types/react-copy-to-clipboard": "^4.3.0",
"@types/styled-components": "^5.1.1",
"@types/styled-react-modal": "^1.2.0",
@ -120,7 +118,6 @@
"babel-plugin-inline-react-svg": "^1.1.1",
"babel-plugin-styled-components": "^1.10.7",
"babel-preset-react-app": "^9.1.2",
"cross-env": "^7.0.2",
"devmoji": "^2.1.9",
"eslint": "^7.4.0",
"eslint-config-prettier": "^6.11.0",
@ -135,8 +132,7 @@
"jest-fetch-mock": "^3.0.3",
"lint-staged": "^10.2.11",
"prettier": "^2.0.5",
"standard-version": "^8.0.2",
"typescript": "^3.9.6"
"standard-version": "^8.0.2"
},
"husky": {
"hooks": {

View file

@ -1,177 +0,0 @@
import React, { useState } from 'react';
import { toast } from 'react-toastify';
import { useRouter } from 'next/router';
import { useGetOffersQuery } from 'src/graphql/hodlhodl/__generated__/query.generated';
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
import getConfig from 'next/config';
import { HodlOfferType } from 'src/graphql/types';
import { NextPageContext } from 'next';
import { getProps } from 'src/utils/ssr';
import {
CardWithTitle,
SubTitle,
Card,
DarkSubTitle,
ResponsiveLine,
} from '../src/components/generic/Styled';
import { LoadingCard } from '../src/components/loading/LoadingCard';
import { OfferCard } from '../src/views/trading/OfferCard';
import { OfferFilters } from '../src/views/trading/OfferFilters';
import { Link } from '../src/components/link/Link';
import { ColorButton } from '../src/components/buttons/colorButton/ColorButton';
import { decode } from '../src/utils/helpers';
const { publicRuntimeConfig } = getConfig();
const { hodlhodl } = publicRuntimeConfig;
export interface QueryProps {
pagination: {
limit: number;
offset: number;
};
filters: { [key: string]: string };
sort: {
by: string;
direction: string;
};
}
const defaultQuery: QueryProps = {
pagination: {
limit: 25,
offset: 0,
},
filters: {},
sort: {
by: '',
direction: '',
},
};
const TradingView = () => {
const { query } = useRouter();
let decoded: QueryProps = defaultQuery;
if (query?.filter) {
const { filter } = query;
try {
if (typeof filter === 'string') {
decoded = JSON.parse(decode(filter));
} else {
decoded = JSON.parse(decode(filter[0]));
}
} catch (error) {
toast.error('Incorrect url.');
}
}
const queryObject = {
...defaultQuery,
...decoded,
};
const [indexOpen, setIndexOpen] = useState(0);
const [page, setPage] = useState(1);
const [fetching, setFetching] = useState(false);
const { data, loading, fetchMore, error } = useGetOffersQuery({
skip: !hodlhodl,
variables: { filter: JSON.stringify(queryObject) },
});
if (!hodlhodl) {
return (
<CardWithTitle>
<SubTitle>P2P Trading</SubTitle>
<Card bottom={'16px'}>
HodlHodl integration is disabled from the server.
</Card>
</CardWithTitle>
);
}
if (error) {
return (
<CardWithTitle>
<SubTitle>P2P Trading</SubTitle>
<Card bottom={'16px'}>Failed to connect with HodlHodl.</Card>
</CardWithTitle>
);
}
if (loading || !data || !data.getOffers) {
return <LoadingCard title={'P2P Trading'} />;
}
const amountOfOffers = data.getOffers.length;
const {
pagination: { limit },
} = queryObject;
return (
<CardWithTitle>
<ResponsiveLine>
<SubTitle>P2P Trading</SubTitle>
<DarkSubTitle>
Powered by <Link href={'https://hodlhodl.com/'}>HodlHodl</Link>
</DarkSubTitle>
</ResponsiveLine>
<Card bottom={'16px'}>
<OfferFilters offerFilters={queryObject} />
</Card>
<Card bottom={'8px'} mobileCardPadding={'0'} mobileNoBackground={true}>
{amountOfOffers <= 0 && <DarkSubTitle>No Offers Found</DarkSubTitle>}
{data.getOffers.map((offer, index) => (
<OfferCard
offer={offer as HodlOfferType}
index={index + 1}
setIndexOpen={setIndexOpen}
indexOpen={indexOpen}
key={`${index}-${offer?.id}`}
/>
))}
</Card>
{amountOfOffers > 0 && amountOfOffers === limit * page && (
<ColorButton
loading={fetching}
disabled={fetching}
onClick={() => {
setFetching(true);
fetchMore({
variables: {
filter: JSON.stringify({
...queryObject,
pagination: { limit, offset: limit * page },
}),
},
updateQuery: (prev, { fetchMoreResult: result }) => {
if (!result) return prev;
setFetching(false);
setPage(prev => prev + 1);
return {
getOffers: [...prev.getOffers, ...result.getOffers],
};
},
});
}}
>
Show More
</ColorButton>
)}
</CardWithTitle>
);
};
const Wrapped = () => (
<GridWrapper>
<TradingView />
</GridWrapper>
);
export default Wrapped;
export async function getServerSideProps(context: NextPageContext) {
return await getProps(context);
}

View file

@ -1,18 +0,0 @@
export const getHodlParams = (params: any): string => {
let paramString = '?';
for (const key in params) {
if (Object.prototype.hasOwnProperty.call(params, key)) {
const element = params[key];
for (const subKey in element) {
if (Object.prototype.hasOwnProperty.call(element, subKey)) {
const subElement = element[subKey];
paramString = `${paramString}&${key}[${subKey}]=${subElement}`;
}
}
}
}
return paramString;
};

View file

@ -1,103 +0,0 @@
import getConfig from 'next/config';
import { ContextType } from 'server/types/apiTypes';
import { requestLimiter } from 'server/helpers/rateLimiter';
import { logger } from 'server/helpers/logger';
import { appUrls } from 'server/utils/appUrls';
import { getHodlParams } from 'server/helpers/hodlHelpers';
const { serverRuntimeConfig } = getConfig() || {};
const { hodlKey } = serverRuntimeConfig || {};
const defaultQuery = {
filters: {},
sort: {
by: '',
direction: '',
},
};
export const hodlResolvers = {
Query: {
getCountries: async (_: undefined, params: any, context: ContextType) => {
await requestLimiter(context.ip, 'getCountries');
const headers = {
Authorization: `Bearer ${hodlKey}`,
};
try {
const response = await fetch(`${appUrls.hodlhodl}/v1/countries`, {
headers,
});
const json = await response.json();
if (json) {
const { countries } = json;
return countries;
}
throw new Error('Problem getting HodlHodl countries.');
} catch (error) {
logger.error('Error getting HodlHodl countries: %o', error);
throw new Error('Problem getting HodlHodl countries.');
}
},
getCurrencies: async (_: undefined, params: any, context: ContextType) => {
await requestLimiter(context.ip, 'getCurrencies');
const headers = {
Authorization: `Bearer ${hodlKey}`,
};
try {
const response = await fetch(`${appUrls.hodlhodl}/v1/currencies`, {
headers,
});
const json = await response.json();
if (json) {
const { currencies } = json;
return currencies;
}
throw new Error('Problem getting HodlHodl currencies.');
} catch (error) {
logger.error('Error getting HodlHodl currencies: %o', error);
throw new Error('Problem getting HodlHodl currencies.');
}
},
getOffers: async (_: undefined, params: any, context: ContextType) => {
await requestLimiter(context.ip, 'getOffers');
let queryParams = defaultQuery;
if (params.filter) {
try {
queryParams = JSON.parse(params.filter);
} catch (error) {
queryParams = defaultQuery;
}
}
try {
const fullParams = {
...queryParams,
};
const paramString = getHodlParams(fullParams);
const response = await fetch(
`${appUrls.hodlhodl}/v1/offers${paramString}`
);
const json = await response.json();
if (json) {
const { offers } = json;
return offers;
}
throw new Error('Problem getting HodlHodl offers.');
} catch (error) {
logger.error('Error getting HodlHodl offers: %o', error);
throw new Error('Problem getting HodlHodl offers.');
}
},
},
};

View file

@ -1,69 +0,0 @@
import { gql } from 'apollo-server-micro';
export const hodlTypes = gql`
type hodlCountryType {
code: String
name: String
native_name: String
currency_code: String
currency_name: String
}
type hodlCurrencyType {
code: String
name: String
type: String
}
type hodlOfferFeeType {
author_fee_rate: String
}
type hodlOfferPaymentType {
id: String
version: String
payment_method_id: String
payment_method_type: String
payment_method_name: String
}
type hodlOfferTraderType {
login: String
online_status: String
rating: String
trades_count: Int
url: String
verified: Boolean
verified_by: String
strong_hodler: Boolean
country: String
country_code: String
average_payment_time_minutes: Int
average_release_time_minutes: Int
days_since_last_trade: Int
}
type hodlOfferType {
id: String
version: String
asset_code: String
searchable: Boolean
country: String
country_code: String
working_now: Boolean
side: String
title: String
description: String
currency_code: String
price: String
min_amount: String
max_amount: String
first_trade_limit: String
fee: hodlOfferFeeType
balance: String
payment_window_minutes: Int
confirmations: Int
payment_method_instructions: [hodlOfferPaymentType]
trader: hodlOfferTraderType
}
`;

View file

@ -6,8 +6,6 @@ import { authResolvers } from './auth/resolvers';
import { generalTypes, queryTypes, mutationTypes } from './types';
import { accountResolvers } from './account/resolvers';
import { accountTypes } from './account/types';
import { hodlTypes } from './hodlhodl/types';
import { hodlResolvers } from './hodlhodl/resolvers';
import { lnpayResolvers } from './lnpay/resolvers';
import { lnpayTypes } from './lnpay/types';
import { bitcoinResolvers } from './bitcoin/resolvers';
@ -49,7 +47,6 @@ const typeDefs = [
mutationTypes,
nodeTypes,
accountTypes,
hodlTypes,
lnpayTypes,
bitcoinTypes,
peerTypes,
@ -72,7 +69,6 @@ const resolvers = merge(
nodeResolvers,
authResolvers,
accountResolvers,
hodlResolvers,
lnpayResolvers,
bitcoinResolvers,
peerResolvers,

View file

@ -74,9 +74,6 @@ export const queryTypes = gql`
getPendingChainBalance: Int
getChainTransactions: [getTransactionsType]
getUtxos: [getUtxosType]
getOffers(filter: String): [hodlOfferType]
getCountries: [hodlCountryType]
getCurrencies: [hodlCurrencyType]
getMessages(
token: String
initialize: Boolean

View file

@ -15,7 +15,6 @@ export const appUrls = {
blockchain: 'https://www.blockchain.com/btc/tx/',
fees: 'https://mempool.space/api/v1/fees/recommended',
ticker: 'https://blockchain.info/ticker',
hodlhodl: 'https://hodlhodl.com/api',
github: 'https://api.github.com/repos/apotdevin/thunderhub/releases/latest',
update: 'https://github.com/apotdevin/thunderhub#updating',
};

View file

@ -1,312 +0,0 @@
import {
gql,
QueryHookOptions,
useQuery,
useLazyQuery,
QueryResult,
LazyQueryHookOptions,
} from '@apollo/client';
import * as Types from '../../types';
export type GetCountriesQueryVariables = Types.Exact<{ [key: string]: never }>;
export type GetCountriesQuery = { __typename?: 'Query' } & {
getCountries?: Types.Maybe<
Array<
Types.Maybe<
{ __typename?: 'hodlCountryType' } & Pick<
Types.HodlCountryType,
'code' | 'name' | 'native_name' | 'currency_code' | 'currency_name'
>
>
>
>;
};
export type GetCurrenciesQueryVariables = Types.Exact<{ [key: string]: never }>;
export type GetCurrenciesQuery = { __typename?: 'Query' } & {
getCurrencies?: Types.Maybe<
Array<
Types.Maybe<
{ __typename?: 'hodlCurrencyType' } & Pick<
Types.HodlCurrencyType,
'code' | 'name' | 'type'
>
>
>
>;
};
export type GetOffersQueryVariables = Types.Exact<{
filter?: Types.Maybe<Types.Scalars['String']>;
}>;
export type GetOffersQuery = { __typename?: 'Query' } & {
getOffers?: Types.Maybe<
Array<
Types.Maybe<
{ __typename?: 'hodlOfferType' } & Pick<
Types.HodlOfferType,
| 'id'
| 'asset_code'
| 'country'
| 'country_code'
| 'working_now'
| 'side'
| 'title'
| 'description'
| 'currency_code'
| 'price'
| 'min_amount'
| 'max_amount'
| 'first_trade_limit'
| 'balance'
| 'payment_window_minutes'
| 'confirmations'
> & {
fee?: Types.Maybe<
{ __typename?: 'hodlOfferFeeType' } & Pick<
Types.HodlOfferFeeType,
'author_fee_rate'
>
>;
payment_method_instructions?: Types.Maybe<
Array<
Types.Maybe<
{ __typename?: 'hodlOfferPaymentType' } & Pick<
Types.HodlOfferPaymentType,
| 'id'
| 'version'
| 'payment_method_id'
| 'payment_method_type'
| 'payment_method_name'
>
>
>
>;
trader?: Types.Maybe<
{ __typename?: 'hodlOfferTraderType' } & Pick<
Types.HodlOfferTraderType,
| 'login'
| 'online_status'
| 'rating'
| 'trades_count'
| 'url'
| 'verified'
| 'verified_by'
| 'strong_hodler'
| 'country'
| 'country_code'
| 'average_payment_time_minutes'
| 'average_release_time_minutes'
| 'days_since_last_trade'
>
>;
}
>
>
>;
};
export const GetCountriesDocument = gql`
query GetCountries {
getCountries {
code
name
native_name
currency_code
currency_name
}
}
`;
/**
* __useGetCountriesQuery__
*
* To run a query within a React component, call `useGetCountriesQuery` and pass it any options that fit your needs.
* When your component renders, `useGetCountriesQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useGetCountriesQuery({
* variables: {
* },
* });
*/
export function useGetCountriesQuery(
baseOptions?: QueryHookOptions<GetCountriesQuery, GetCountriesQueryVariables>
) {
return useQuery<GetCountriesQuery, GetCountriesQueryVariables>(
GetCountriesDocument,
baseOptions
);
}
export function useGetCountriesLazyQuery(
baseOptions?: LazyQueryHookOptions<
GetCountriesQuery,
GetCountriesQueryVariables
>
) {
return useLazyQuery<GetCountriesQuery, GetCountriesQueryVariables>(
GetCountriesDocument,
baseOptions
);
}
export type GetCountriesQueryHookResult = ReturnType<
typeof useGetCountriesQuery
>;
export type GetCountriesLazyQueryHookResult = ReturnType<
typeof useGetCountriesLazyQuery
>;
export type GetCountriesQueryResult = QueryResult<
GetCountriesQuery,
GetCountriesQueryVariables
>;
export const GetCurrenciesDocument = gql`
query GetCurrencies {
getCurrencies {
code
name
type
}
}
`;
/**
* __useGetCurrenciesQuery__
*
* To run a query within a React component, call `useGetCurrenciesQuery` and pass it any options that fit your needs.
* When your component renders, `useGetCurrenciesQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useGetCurrenciesQuery({
* variables: {
* },
* });
*/
export function useGetCurrenciesQuery(
baseOptions?: QueryHookOptions<
GetCurrenciesQuery,
GetCurrenciesQueryVariables
>
) {
return useQuery<GetCurrenciesQuery, GetCurrenciesQueryVariables>(
GetCurrenciesDocument,
baseOptions
);
}
export function useGetCurrenciesLazyQuery(
baseOptions?: LazyQueryHookOptions<
GetCurrenciesQuery,
GetCurrenciesQueryVariables
>
) {
return useLazyQuery<GetCurrenciesQuery, GetCurrenciesQueryVariables>(
GetCurrenciesDocument,
baseOptions
);
}
export type GetCurrenciesQueryHookResult = ReturnType<
typeof useGetCurrenciesQuery
>;
export type GetCurrenciesLazyQueryHookResult = ReturnType<
typeof useGetCurrenciesLazyQuery
>;
export type GetCurrenciesQueryResult = QueryResult<
GetCurrenciesQuery,
GetCurrenciesQueryVariables
>;
export const GetOffersDocument = gql`
query GetOffers($filter: String) {
getOffers(filter: $filter) {
id
asset_code
country
country_code
working_now
side
title
description
currency_code
price
min_amount
max_amount
first_trade_limit
fee {
author_fee_rate
}
balance
payment_window_minutes
confirmations
payment_method_instructions {
id
version
payment_method_id
payment_method_type
payment_method_name
}
trader {
login
online_status
rating
trades_count
url
verified
verified_by
strong_hodler
country
country_code
average_payment_time_minutes
average_release_time_minutes
days_since_last_trade
}
}
}
`;
/**
* __useGetOffersQuery__
*
* To run a query within a React component, call `useGetOffersQuery` and pass it any options that fit your needs.
* When your component renders, `useGetOffersQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useGetOffersQuery({
* variables: {
* filter: // value for 'filter'
* },
* });
*/
export function useGetOffersQuery(
baseOptions?: QueryHookOptions<GetOffersQuery, GetOffersQueryVariables>
) {
return useQuery<GetOffersQuery, GetOffersQueryVariables>(
GetOffersDocument,
baseOptions
);
}
export function useGetOffersLazyQuery(
baseOptions?: LazyQueryHookOptions<GetOffersQuery, GetOffersQueryVariables>
) {
return useLazyQuery<GetOffersQuery, GetOffersQueryVariables>(
GetOffersDocument,
baseOptions
);
}
export type GetOffersQueryHookResult = ReturnType<typeof useGetOffersQuery>;
export type GetOffersLazyQueryHookResult = ReturnType<
typeof useGetOffersLazyQuery
>;
export type GetOffersQueryResult = QueryResult<
GetOffersQuery,
GetOffersQueryVariables
>;

View file

@ -1,71 +0,0 @@
import { gql } from '@apollo/client';
export const GET_HODL_COUNTRIES = gql`
query GetCountries {
getCountries {
code
name
native_name
currency_code
currency_name
}
}
`;
export const GET_HODL_CURRENCIES = gql`
query GetCurrencies {
getCurrencies {
code
name
type
}
}
`;
export const GET_HODL_OFFERS = gql`
query GetOffers($filter: String) {
getOffers(filter: $filter) {
id
asset_code
country
country_code
working_now
side
title
description
currency_code
price
min_amount
max_amount
first_trade_limit
fee {
author_fee_rate
}
balance
payment_window_minutes
confirmations
payment_method_instructions {
id
version
payment_method_id
payment_method_type
payment_method_name
}
trader {
login
online_status
rating
trades_count
url
verified
verified_by
strong_hodler
country
country_code
average_payment_time_minutes
average_release_time_minutes
days_since_last_trade
}
}
}
`;

View file

@ -72,9 +72,6 @@ export type Query = {
getPendingChainBalance?: Maybe<Scalars['Int']>;
getChainTransactions?: Maybe<Array<Maybe<GetTransactionsType>>>;
getUtxos?: Maybe<Array<Maybe<GetUtxosType>>>;
getOffers?: Maybe<Array<Maybe<HodlOfferType>>>;
getCountries?: Maybe<Array<Maybe<HodlCountryType>>>;
getCurrencies?: Maybe<Array<Maybe<HodlCurrencyType>>>;
getMessages?: Maybe<GetMessagesType>;
getAuthToken?: Maybe<Scalars['Boolean']>;
getSessionToken?: Maybe<Scalars['Boolean']>;
@ -343,78 +340,6 @@ export type ServerAccountType = {
loggedIn: Scalars['Boolean'];
};
export type HodlCountryType = {
__typename?: 'hodlCountryType';
code?: Maybe<Scalars['String']>;
name?: Maybe<Scalars['String']>;
native_name?: Maybe<Scalars['String']>;
currency_code?: Maybe<Scalars['String']>;
currency_name?: Maybe<Scalars['String']>;
};
export type HodlCurrencyType = {
__typename?: 'hodlCurrencyType';
code?: Maybe<Scalars['String']>;
name?: Maybe<Scalars['String']>;
type?: Maybe<Scalars['String']>;
};
export type HodlOfferFeeType = {
__typename?: 'hodlOfferFeeType';
author_fee_rate?: Maybe<Scalars['String']>;
};
export type HodlOfferPaymentType = {
__typename?: 'hodlOfferPaymentType';
id?: Maybe<Scalars['String']>;
version?: Maybe<Scalars['String']>;
payment_method_id?: Maybe<Scalars['String']>;
payment_method_type?: Maybe<Scalars['String']>;
payment_method_name?: Maybe<Scalars['String']>;
};
export type HodlOfferTraderType = {
__typename?: 'hodlOfferTraderType';
login?: Maybe<Scalars['String']>;
online_status?: Maybe<Scalars['String']>;
rating?: Maybe<Scalars['String']>;
trades_count?: Maybe<Scalars['Int']>;
url?: Maybe<Scalars['String']>;
verified?: Maybe<Scalars['Boolean']>;
verified_by?: Maybe<Scalars['String']>;
strong_hodler?: Maybe<Scalars['Boolean']>;
country?: Maybe<Scalars['String']>;
country_code?: Maybe<Scalars['String']>;
average_payment_time_minutes?: Maybe<Scalars['Int']>;
average_release_time_minutes?: Maybe<Scalars['Int']>;
days_since_last_trade?: Maybe<Scalars['Int']>;
};
export type HodlOfferType = {
__typename?: 'hodlOfferType';
id?: Maybe<Scalars['String']>;
version?: Maybe<Scalars['String']>;
asset_code?: Maybe<Scalars['String']>;
searchable?: Maybe<Scalars['Boolean']>;
country?: Maybe<Scalars['String']>;
country_code?: Maybe<Scalars['String']>;
working_now?: Maybe<Scalars['Boolean']>;
side?: Maybe<Scalars['String']>;
title?: Maybe<Scalars['String']>;
description?: Maybe<Scalars['String']>;
currency_code?: Maybe<Scalars['String']>;
price?: Maybe<Scalars['String']>;
min_amount?: Maybe<Scalars['String']>;
max_amount?: Maybe<Scalars['String']>;
first_trade_limit?: Maybe<Scalars['String']>;
fee?: Maybe<HodlOfferFeeType>;
balance?: Maybe<Scalars['String']>;
payment_window_minutes?: Maybe<Scalars['Int']>;
confirmations?: Maybe<Scalars['Int']>;
payment_method_instructions?: Maybe<Array<Maybe<HodlOfferPaymentType>>>;
trader?: Maybe<HodlOfferTraderType>;
};
export type LnPayInfoType = {
__typename?: 'lnPayInfoType';
max?: Maybe<Scalars['Int']>;

View file

@ -3,7 +3,6 @@ import {
Cpu,
Menu,
X,
CreditCard,
MessageCircle,
Settings,
Home,
@ -11,7 +10,6 @@ import {
} from 'react-feather';
import { useTransition, animated } from 'react-spring';
import { useRouter } from 'next/router';
import getConfig from 'next/config';
import { headerColor, headerTextColor } from '../../styles/Themes';
import { SingleLine } from '../../components/generic/Styled';
import { BurgerMenu } from '../../components/burgerMenu/BurgerMenu';
@ -30,13 +28,9 @@ import {
const MAIN = '/';
const HOME = '/home';
const TRADER = '/trading';
const CHAT = '/chat';
const SETTINGS = '/settings';
const { publicRuntimeConfig } = getConfig();
const { hodlhodl } = publicRuntimeConfig;
export const Header = () => {
const { pathname } = useRouter();
const [open, setOpen] = useState(false);
@ -44,7 +38,7 @@ export const Header = () => {
const isRoot = pathname === '/';
const showHomeButton = (): boolean =>
pathname === TRADER || pathname === CHAT || pathname === SETTINGS;
pathname === CHAT || pathname === SETTINGS;
const transitions = useTransition(open, null, {
from: { position: 'absolute', opacity: 0 },
@ -80,7 +74,6 @@ export const Header = () => {
<ViewSwitch hideMobile={true}>
<HeaderButtons>
{showHomeButton() && renderNavButton(HOME, Home)}
{hodlhodl && renderNavButton(TRADER, CreditCard)}
{renderNavButton(CHAT, MessageCircle)}
{renderNavButton(SETTINGS, Settings)}
</HeaderButtons>

View file

@ -11,7 +11,6 @@ import {
Link as LinkIcon,
Repeat,
Users,
CreditCard,
MessageCircle,
BarChart2,
Icon,
@ -122,7 +121,6 @@ const CHAIN_TRANS = '/chain';
const TOOLS = '/tools';
const DETAILS = '/details';
const STATS = '/stats';
const TRADER = '/trading';
const CHAT = '/chat';
const SETTINGS = '/settings';
@ -190,7 +188,6 @@ export const Navigation = ({ isBurger, setOpen }: NavigationProps) => {
{renderBurgerNav('Chain', CHAIN_TRANS, LinkIcon)}
{renderBurgerNav('Tools', TOOLS, Shield)}
{renderBurgerNav('Stats', STATS, BarChart2)}
{renderBurgerNav('Trading', TRADER, CreditCard)}
{renderBurgerNav('Chat', CHAT, MessageCircle)}
{renderBurgerNav('Settings', SETTINGS, Settings)}
</BurgerRow>

View file

@ -1,6 +1,5 @@
import React, { useState } from 'react';
import { toast } from 'react-toastify';
import { ModalType } from 'src/views/trading/Modal/FilterModal';
import {
Sub4Title,
ResponsiveLine,
@ -14,6 +13,8 @@ import { isLightningInvoice } from '../../../../utils/helpers';
import { RequestModal } from './RequestModal';
import { KeysendModal } from './KeysendModal';
type ModalType = 'keysend' | 'request' | 'none';
export const PayCard = ({ setOpen }: { setOpen: () => void }) => {
const [request, setRequest] = useState<string>('');
const [modalType, setModalType] = useState<ModalType>('none');

View file

@ -1,51 +0,0 @@
import React from 'react';
import styled from 'styled-components';
import { HodlOfferPaymentType } from 'src/graphql/types';
import { themeColors, subCardColor } from '../../styles/Themes';
interface MethodProps {
id: string;
payment_method_type: string;
payment_method_name: string;
}
interface MethodBoxesProps {
methods?: HodlOfferPaymentType[] | null;
}
const StyledMethodBoxes = styled.div`
width: 100%;
position: relative;
right: -16px;
top: -26px;
display: flex;
justify-content: flex-end;
margin: 0 0 -24px 0;
flex-wrap: wrap;
overflow: hidden;
height: 23px;
`;
const StyledMethod = styled.div`
font-size: 12px;
margin: 0 0 0 8px;
border: 1px solid ${themeColors.blue2};
border-radius: 4px;
padding: 2px 4px;
background: ${subCardColor};
white-space: nowrap;
`;
export const MethodBoxes = ({ methods }: MethodBoxesProps) => {
if (!methods?.length) return null;
return (
<StyledMethodBoxes>
{methods.map((method, index) => (
<StyledMethod key={`${method.payment_method_name}/${index}`}>
{method.payment_method_name}
</StyledMethod>
))}
</StyledMethodBoxes>
);
};

View file

@ -1,163 +0,0 @@
import React, { useState, useEffect, Dispatch } from 'react';
import ScaleLoader from 'react-spinners/ScaleLoader';
import { toast } from 'react-toastify';
import {
useGetCountriesQuery,
useGetCurrenciesQuery,
GetCountriesQuery,
GetCurrenciesQuery,
} from 'src/graphql/hodlhodl/__generated__/query.generated';
import { HodlCountryType, HodlCurrencyType } from 'src/graphql/types';
import { SubTitle } from '../../../components/generic/Styled';
import { SortOptions, NewOptions } from '../OfferConfigs';
import { FilterType, FilterActionType } from '../OfferFilters';
import { themeColors } from '../../../styles/Themes';
import { OptionsLoading } from '../OfferCard.styled';
import { FilteredList, FilteredOptionsProps } from './FilteredList';
export type ModalType = 'keysend' | 'request' | 'none' | 'new' | 'sort';
interface FilterProps {
type: string;
dispatch: Dispatch<FilterActionType>;
final?: { title: string; name: string; searchable?: boolean };
newOptions?: FilterType[];
setModalType: (type: ModalType) => void;
}
interface CountryType {
code: string;
name: string;
native_name: string;
currency_code: string;
currency_name: string;
}
interface CurrencyType {
code: string;
name: string;
type: string;
}
export const FilterModal = ({
type,
dispatch,
final,
newOptions,
setModalType,
}: FilterProps) => {
const searchable: boolean = final?.searchable ?? false;
const skipable: boolean = type !== 'Country' && type !== 'Currency';
const [selected, setSelected] = useState<FilteredOptionsProps>();
const [options, setOptions] = useState(newOptions ?? []);
const [title, setTitle] = useState(final?.title ?? '');
const useQuery =
type === 'Country' ? useGetCountriesQuery : useGetCurrenciesQuery;
const { loading, data, error } = useQuery({
skip: skipable,
onError: () => toast.error('Error Loading Options. Please try again.'),
});
useEffect(() => {
switch (type) {
case 'sort':
setTitle('Sort Offers by:');
setOptions(SortOptions);
break;
case 'new':
setTitle('Add New Filter:');
setOptions(NewOptions);
break;
default:
break;
}
}, [type]);
useEffect(() => {
if (!loading && data && (data as GetCountriesQuery).getCountries) {
const countryOptions =
(data as GetCountriesQuery).getCountries?.map(country => {
const { code, name, native_name } = country as HodlCountryType;
return { name: code || '', title: `${name} (${native_name})` };
}) || [];
setOptions(countryOptions);
}
if (!loading && data && (data as GetCurrenciesQuery).getCurrencies) {
const filtered =
(data as GetCurrenciesQuery).getCurrencies?.filter(
currency => currency?.type === 'fiat'
) || [];
const currencyOptions = filtered.map(currency => {
const { code, name } = currency as HodlCurrencyType;
return { name: code || '', title: name || '' };
});
setOptions(currencyOptions);
}
}, [data, loading]);
const handleClick = (name: string, option: FilteredOptionsProps) => () => {
if (final) {
dispatch({
type: 'addFilter',
newItem: { [final.name]: name },
});
setModalType('none');
}
switch (type) {
case 'sort':
if (name === 'none') {
dispatch({
type: 'removeSort',
});
} else {
dispatch({
type: 'addSort',
newItem: { by: name },
});
}
setModalType('none');
break;
case 'new':
setSelected(option);
break;
default:
break;
}
};
if (error) {
return null;
}
if (selected) {
return (
<FilterModal
type={selected.title}
dispatch={dispatch}
final={selected}
newOptions={selected.options}
setModalType={setModalType}
/>
);
}
return (
<>
<SubTitle>{title}</SubTitle>
<FilteredList
searchable={searchable}
options={options}
handleClick={handleClick}
/>
<OptionsLoading>
{loading && <ScaleLoader height={20} color={themeColors.blue3} />}
</OptionsLoading>
</>
);
};

View file

@ -1,78 +0,0 @@
import React, { useState, useEffect } from 'react';
import { ColorButton } from '../../../components/buttons/colorButton/ColorButton';
import { OfferModalBox } from '../OfferCard.styled';
import { Input } from '../../../components/input/Input';
import { Sub4Title } from '../../../components/generic/Styled';
import { FilterType } from '../OfferFilters';
interface FilteredProps {
searchable: boolean;
options: FilterType[];
handleClick: (name: string, option: FilteredOptionsProps) => () => void;
}
export interface FilteredOptionsProps {
name: string;
title: string;
options?: FilterType[];
}
export const FilteredList = ({
searchable,
options,
handleClick,
}: FilteredProps) => {
const [searchTerm, setSearchTerm] = useState<string>('');
const [filteredOptions, setOptions] = useState<FilteredOptionsProps[]>(
options
);
useEffect(() => {
const filtered = options.filter(
(option: { name: string; title: string }) => {
const inName = option.name
.toLowerCase()
.includes(searchTerm.toLowerCase());
const inTitle = option.title
.toLowerCase()
.includes(searchTerm.toLowerCase());
return inName || inTitle;
}
);
setOptions(filtered);
}, [searchTerm, options]);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSearchTerm(event.target.value);
};
return (
<>
{searchable && (
<Input
placeholder={'Search'}
fullWidth={true}
onChange={handleChange}
withMargin={'0 0 8px 0'}
/>
)}
<OfferModalBox>
{filteredOptions.length > 0 ? (
filteredOptions.map((option, index) => (
<ColorButton
key={`${index}-${option.name}`}
fullWidth={true}
withMargin={'0 0 2px 0'}
onClick={handleClick(option.name, option)}
>
{option.title}
</ColorButton>
))
) : (
<Sub4Title>No results</Sub4Title>
)}
</OfferModalBox>
</>
);
};

View file

@ -1,54 +0,0 @@
import styled from 'styled-components';
import { ChevronRight } from 'react-feather';
import { unSelectedNavButton, mediaWidths } from '../../styles/Themes';
export const TradesAmount = styled.div`
font-size: 14px;
color: ${unSelectedNavButton};
margin: 0 4px;
`;
export const StyleArrow = styled(ChevronRight)`
margin-bottom: -3px;
`;
export const OfferModalBox = styled.div`
overflow-y: auto;
max-height: 640px;
min-height: 240px;
@media (${mediaWidths.mobile}) {
max-height: 240px;
min-height: 120px;
}
`;
export const StyledTitle = styled.div`
font-size: 14px;
margin: 16px 0;
@media (${mediaWidths.mobile}) {
text-align: center;
margin-top: 8px;
}
`;
export const StyledLogin = styled.div`
max-width: 160px;
overflow: hidden;
text-overflow: ellipsis;
`;
export const StyledDescription = styled(StyledLogin)`
max-width: unset;
overflow-y: auto;
font-size: 14px;
max-height: 160px;
`;
export const OptionsLoading = styled.div`
display: flex;
height: 100%;
width: 100%;
justify-content: center;
align-items: center;
`;

View file

@ -1,192 +0,0 @@
import React from 'react';
import numeral from 'numeral';
import { HodlOfferType, HodlOfferPaymentType } from 'src/graphql/types';
import {
SubCard,
Sub4Title,
SubTitle,
SingleLine,
ResponsiveLine,
Separation,
} from '../../components/generic/Styled';
import { Rating } from '../../components/rating/Rating';
import { MainInfo } from '../../components/generic/CardGeneric';
import { themeColors } from '../../styles/Themes';
import { renderLine } from '../../components/generic/helpers';
import { Link } from '../../components/link/Link';
import { ColorButton } from '../../components/buttons/colorButton/ColorButton';
import { MethodBoxes } from './MethodBoxes';
import {
TradesAmount,
StyleArrow,
StyledTitle,
StyledLogin,
StyledDescription,
} from './OfferCard.styled';
const format = (value: number | string | null | undefined, format = '0,0.00') =>
value ? numeral(value).format(format) : null;
interface OfferCardProps {
offer: HodlOfferType;
index: number;
setIndexOpen: (index: number) => void;
indexOpen: number;
}
export const OfferCard = ({
offer,
index,
setIndexOpen,
indexOpen,
}: OfferCardProps) => {
const {
id,
asset_code,
country,
country_code,
working_now,
side,
title,
description,
currency_code,
price,
min_amount,
max_amount,
first_trade_limit,
fee,
balance,
payment_window_minutes,
confirmations,
payment_method_instructions,
trader,
} = offer;
const { author_fee_rate } = fee || {};
const {
login,
online_status,
rating,
trades_count = 0,
url,
verified,
verified_by,
strong_hodler,
country: traderCountry,
country_code: traderCode,
average_payment_time_minutes,
average_release_time_minutes,
days_since_last_trade,
} = trader || {};
const handleClick = () => {
if (indexOpen === index) {
setIndexOpen(0);
} else {
setIndexOpen(index);
}
};
const renderPayments = (): string => {
if (payment_method_instructions) {
const methods = payment_method_instructions.map(method =>
method
? `${method.payment_method_name} (${method.payment_method_type})`
: ''
);
return methods.join(', ');
}
return '';
};
const renderDetails = () => (
<>
<Separation />
<StyledDescription>{description}</StyledDescription>
<Separation />
{renderLine('Price', format(price))}
{renderLine('Min Amount:', format(min_amount))}
{renderLine('Max Amount:', format(max_amount))}
{renderLine('First Trade Limit:', format(first_trade_limit))}
{renderLine('Payment Options:', renderPayments())}
{renderLine('Country:', `${country} (${country_code})`)}
{renderLine('Available Now:', working_now ? 'Yes' : 'No')}
{renderLine('Balance:', format(balance))}
{renderLine('Payment Window (min):', payment_window_minutes)}
{renderLine('Confirmations:', confirmations)}
{renderLine('Fee Rate:', `${format(Number(author_fee_rate) * 100)}%`)}
<Separation />
<Sub4Title>Trader</Sub4Title>
{renderLine('User:', login)}
{renderLine('Online:', online_status)}
{renderLine('Rating:', rating)}
{renderLine('Amount of Trades:', trades_count)}
{renderLine('Verified:', verified ? 'True' : 'False')}
{renderLine('Verified By:', verified_by)}
{renderLine('Strong Hodler:', strong_hodler ? 'True' : 'False')}
{renderLine('Country:', `${traderCountry} (${traderCode})`)}
{renderLine('Average Payment Time (min):', average_payment_time_minutes)}
{renderLine('Average Release Time (min):', average_release_time_minutes)}
{renderLine('Days since last trade:', days_since_last_trade)}
<SingleLine>
<Link
href={`https://hodlhodl.com/offers/${id}`}
underline={'transparent'}
fullWidth={true}
>
<ColorButton
withBorder={true}
withMargin={'16px 8px 0 0'}
fullWidth={true}
>
View Offer
</ColorButton>
</Link>
{url && (
<Link href={url} underline={'transparent'} fullWidth={true}>
<ColorButton
withBorder={true}
withMargin={'16px 0 0 8px'}
fullWidth={true}
>
View trader
</ColorButton>
</Link>
)}
</SingleLine>
</>
);
return (
<SubCard withMargin={'16px 0 24px'} key={`${index}-${id}`}>
<MainInfo onClick={() => handleClick()}>
<MethodBoxes
methods={payment_method_instructions as HodlOfferPaymentType[]}
/>
<ResponsiveLine>
<SubTitle>
{side !== 'buy' ? asset_code : currency_code}
<StyleArrow size={18} color={themeColors.blue3} />
{side !== 'buy' ? currency_code : asset_code}
</SubTitle>
<SingleLine>
<StyledLogin>{login}</StyledLogin>
{(trades_count || 0) > 0 && (
<TradesAmount>{`(${trades_count}) `}</TradesAmount>
)}
<Rating rating={Number(rating)} />
</SingleLine>
</ResponsiveLine>
<StyledTitle>{title}</StyledTitle>
{renderLine('Price:', format(price))}
{renderLine(
'Min/Max amount:',
`${format(min_amount, '0a')}/${format(max_amount, '0a')}`
)}
</MainInfo>
{index === indexOpen && renderDetails()}
</SubCard>
);
};

View file

@ -1,141 +0,0 @@
export const SortOptions = [
{
name: 'none',
title: 'None',
},
{
name: 'price',
title: 'Price',
},
{
name: 'payment_window_minutes',
title: 'Payment Window',
},
{
name: 'user_average_payment_time_minutes',
title: 'Average Payment Time',
},
{
name: 'user_average_release_time_minutes',
title: 'Average Release Time',
},
{
name: 'rating',
title: 'Rating',
},
];
export const NewOptions = [
{
name: 'asset_code',
title: 'Asset Code',
options: [
{
name: 'BTC',
title: 'Bitcoin',
},
{
name: 'BTCLN',
title: 'Lightning Bitcoin',
},
],
},
{
name: 'side',
title: 'Side',
options: [
{
name: 'buy',
title: 'I want to sell Bitcoin',
},
{
name: 'sell',
title: 'I want to buy Bitcoin',
},
],
},
{
name: 'include_global',
title: 'Include Global Offers',
options: [
{
name: 'true',
title: 'Yes',
},
{
name: 'false',
title: 'No',
},
],
},
{
name: 'only_working_now',
title: 'Only Working Now Offers',
options: [
{
name: 'true',
title: 'Yes',
},
{
name: 'false',
title: 'No',
},
],
},
{
name: 'country',
title: 'Country',
searchable: true,
},
{
name: 'currency_code',
title: 'Currency',
searchable: true,
},
{
name: 'payment_method_type',
title: 'Payment Type',
options: [
{
name: 'Bank wire',
title: 'Bank wire',
},
{
name: 'Cash',
title: 'Cash',
},
{
name: 'Cryptocurrency',
title: 'Cryptocurrency',
},
{
name: 'Online payment system',
title: 'Online payment system',
},
],
},
// {
// name: 'payment_method_id',
// title: 'Payment Id',
// },
// {
// name: 'payment_method_name',
// title: 'Payment Name',
// },
// {
// name: 'volume',
// title: 'Volume',
// },
// {
// name: 'payment_window_minutes_max',
// title: 'Max Payment Window',
// },
// {
// name: 'user_average_payment_time_minutes_max',
// title: 'Max User Payment Time',
// },
// {
// name: 'user_average_release_time_minutes_max',
// title: 'Max User Release Time',
// },
];

View file

@ -1,237 +0,0 @@
import React, { useState, useReducer } from 'react';
import { X } from 'react-feather';
import { useRouter } from 'next/router';
import {
SingleLine,
Separation,
ResponsiveLine,
NoWrapTitle,
} from '../../components/generic/Styled';
import { ColorButton } from '../../components/buttons/colorButton/ColorButton';
import {
MultiButton,
SingleButton,
} from '../../components/buttons/multiButton/MultiButton';
import Modal from '../../components/modal/ReactModal';
import { QueryProps } from '../../../pages/trading';
import { renderLine } from '../../components/generic/helpers';
import { chartColors } from '../../styles/Themes';
import { encode } from '../../utils/helpers';
import { appendBasePath } from '../../utils/basePath';
import { SortOptions } from './OfferConfigs';
import { FilterModal, ModalType } from './Modal/FilterModal';
export type FilterActionType = {
type: 'addFilter' | 'addSort' | 'removeSort' | 'removeFilter' | 'changeLimit';
state?: QueryProps;
newItem?: {};
removeKey?: string;
changeLimit?: number;
};
const reducer = (state: QueryProps, action: FilterActionType): QueryProps => {
const { sort, filters } = state;
switch (action.type) {
case 'addSort': {
let direction = {};
if (sort && !sort.direction) {
direction = { direction: 'desc' };
}
const newSort = { ...sort, ...direction, ...action.newItem };
return { ...state, sort: newSort };
}
case 'removeSort':
return { ...state, sort: { by: '', direction: '' } };
case 'addFilter': {
const newFilters = { ...filters, ...action.newItem };
return { ...state, filters: newFilters };
}
case 'removeFilter':
if (action.removeKey) {
const remaining = { ...filters };
delete remaining[action.removeKey];
return { ...state, filters: remaining };
}
return state;
case 'changeLimit':
if (action.changeLimit) {
return {
...state,
pagination: { limit: action.changeLimit, offset: 0 },
};
}
return state;
default:
throw new Error();
}
};
interface FilterProps {
offerFilters: QueryProps;
}
export interface FilterType {
name: string;
title: string;
optionOne?: string;
optionTwo?: string;
}
export const OfferFilters = ({ offerFilters }: FilterProps) => {
const { push } = useRouter();
const [filterState, dispatch] = useReducer(reducer, offerFilters);
const [modalType, setModalType] = useState<ModalType>('none');
const [willApply, setWillApply] = useState<boolean>(false);
const renderButton = (
onClick: () => void,
text: string,
selected: boolean
) => (
<SingleButton selected={selected} onClick={onClick}>
{text}
</SingleButton>
);
const handleSave = () => {
const stringFormat = JSON.stringify(filterState);
const encoded = encode(stringFormat);
push(appendBasePath(`/trading?filter=${encoded}`));
};
const handleRemoveAll = () => push(appendBasePath('/trading'));
const handleRemove = (removeKey: string) => {
dispatch({ type: 'removeFilter', removeKey });
};
const renderAppliedFilters = () => {
const currentFilters = filterState.filters;
const activeFilters = Object.entries(currentFilters).map(([key, element]) =>
renderLine(key, element, `${key}-${element}`, () => handleRemove(key))
);
return <>{activeFilters}</>;
};
const renderLimitOptions = () => {
const currentLimit = filterState.pagination.limit;
const renderButton = (value: number, margin?: string) => (
<ColorButton
onClick={() => dispatch({ type: 'changeLimit', changeLimit: value })}
selected={currentLimit === value}
withMargin={margin}
>
{value}
</ColorButton>
);
return (
<SingleLine>
{renderButton(25)}
{renderButton(50, '0 4px 0')}
{renderButton(100)}
</SingleLine>
);
};
const renderFilters = () => {
return (
<>
<Separation />
<ResponsiveLine>
<NoWrapTitle>Results:</NoWrapTitle>
{renderLimitOptions()}
</ResponsiveLine>
<Separation />
<ResponsiveLine>
<NoWrapTitle>Filters</NoWrapTitle>
<ColorButton arrow={true} onClick={() => setModalType('new')}>
New Filter
</ColorButton>
</ResponsiveLine>
{Object.keys(filterState.filters).length > 0 && renderAppliedFilters()}
<Separation />
<ResponsiveLine>
<NoWrapTitle>Sort By:</NoWrapTitle>
<ColorButton arrow={true} onClick={() => setModalType('sort')}>
{SortOptions.find(option => option.name === filterState.sort.by)
?.title ?? 'None'}
</ColorButton>
</ResponsiveLine>
{!!filterState.sort.by && (
<ResponsiveLine>
<NoWrapTitle>Sort Direction:</NoWrapTitle>
<MultiButton>
{renderButton(
() => {
dispatch({
type: 'addSort',
newItem: { direction: 'asc' },
});
},
'Asc',
filterState.sort.direction === 'asc'
)}
{renderButton(
() => {
dispatch({
type: 'addSort',
newItem: { direction: 'desc' },
});
},
'Desc',
filterState.sort.direction !== 'asc'
)}
</MultiButton>
</ResponsiveLine>
)}
<Separation />
<SingleLine>
<ColorButton
fullWidth={true}
withMargin={'16px 4px 0 0'}
onClick={handleSave}
>
Filter
</ColorButton>
<ColorButton
color={chartColors.orange2}
fullWidth={true}
withMargin={'16px 0 0 4px'}
onClick={handleRemoveAll}
>
Remove All
</ColorButton>
</SingleLine>
</>
);
};
return (
<>
<SingleLine>
Filters
<ColorButton
arrow={!willApply}
onClick={() => setWillApply(prev => !prev)}
>
{willApply ? <X size={18} /> : 'Apply'}
</ColorButton>
</SingleLine>
{willApply && renderFilters()}
<Modal
isOpen={modalType !== 'none'}
closeCallback={() => setModalType('none')}
>
<FilterModal
type={modalType}
dispatch={dispatch}
setModalType={setModalType}
/>
</Modal>
</>
);
};