mirror of
https://github.com/apotdevin/thunderhub.git
synced 2025-02-21 22:11:37 +01:00
feat: ✨ privacy settings (#37)
* feat: ✨ privacy configs * chore: 🔧 chat polling speed * refactor: ♻️ chat state * chore: 🔧 move config to cookie
This commit is contained in:
parent
ef5c2d16f4
commit
3d29616300
53 changed files with 664 additions and 434 deletions
12
.eslintrc.js
12
.eslintrc.js
|
@ -4,6 +4,11 @@ module.exports = {
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaFeatures: { jsx: true },
|
ecmaFeatures: { jsx: true },
|
||||||
},
|
},
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
amd: true,
|
||||||
|
node: true,
|
||||||
|
},
|
||||||
plugins: ['react', 'jest', 'import', 'prettier'],
|
plugins: ['react', 'jest', 'import', 'prettier'],
|
||||||
settings: {
|
settings: {
|
||||||
react: {
|
react: {
|
||||||
|
@ -37,7 +42,12 @@ module.exports = {
|
||||||
'import/no-unresolved': 'off',
|
'import/no-unresolved': 'off',
|
||||||
camelcase: 'off',
|
camelcase: 'off',
|
||||||
'@typescript-eslint/camelcase': 'off',
|
'@typescript-eslint/camelcase': 'off',
|
||||||
'prettier/prettier': 'error',
|
|
||||||
'react/prop-types': 'off',
|
'react/prop-types': 'off',
|
||||||
|
'prettier/prettier': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
endOfLine: 'auto',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
|
@ -1,6 +1,3 @@
|
||||||
{
|
{
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true
|
||||||
"editor.codeActionsOnSave": {
|
}
|
||||||
"source.fixAll.eslint": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
127
README.md
127
README.md
|
@ -1,22 +1,32 @@
|
||||||
# **ThunderHub - Lightning Node Manager**
|
# **ThunderHub - Lightning Node Manager**
|
||||||
|
|
||||||

|

|
||||||
[](https://github.com/DAVFoundation/captain-n3m0/blob/master/LICENSE)
|
[](https://github.com/DAVFoundation/captain-n3m0/blob/master/LICENSE)
|
||||||
|
|
||||||
## Table Of Contents
|
## Table Of Contents
|
||||||
|
|
||||||
- [Introduction](#introduction)
|
- [Introduction](#introduction)
|
||||||
|
- [Integrations](#integrations)
|
||||||
- [Features](#features)
|
- [Features](#features)
|
||||||
- [Installation](#installation)
|
- [Installation](#installation)
|
||||||
- [Development](#development)
|
- [Development](#development)
|
||||||
|
- [Docker deployment](#docker)
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
ThunderHub is an **open-source** LND node manager where you can manage and monitor your node on any device or browser. It allows you to take control of the lightning network with a simple and intuitive UX and the most up-to-date tech stack.
|
ThunderHub is an **open-source** LND node manager where you can manage and monitor your node on any device or browser. It allows you to take control of the lightning network with a simple and intuitive UX and the most up-to-date tech stack.
|
||||||
|
|
||||||
|
### Integrations
|
||||||
|
|
||||||
|
**BTCPay Server**
|
||||||
|
ThunderHub is currently integrated into BTCPay for easier deployment. If you already have a BTCPay server and want to add ThunderHub or even want to start a BTCPay server from zero, be sure to check out this [tutorial](https://apotdevin.com/blog/thunderhub-btcpay)
|
||||||
|
|
||||||
|
**Raspiblitz**
|
||||||
|
For Raspiblitz users you can also get ThunderHub running by following this [gist](https://gist.github.com/openoms/8ba963915c786ce01892f2c9fa2707bc)
|
||||||
|
|
||||||
### Tech Stack
|
### Tech Stack
|
||||||
|
|
||||||
This repository consists of a **NextJS** server that handles both the backend **Graphql Server** and the frontend **React App**.
|
This repository consists of a **NextJS** server that handles both the backend **Graphql Server** and the frontend **React App**. ThunderHub connects to your Lightning Network node by using the gRPC ports.
|
||||||
|
|
||||||
- NextJS
|
- NextJS
|
||||||
- ReactJS
|
- ReactJS
|
||||||
|
@ -50,7 +60,7 @@ This repository consists of a **NextJS** server that handles both the backend **
|
||||||
- Send and Receive Bitcoin payments.
|
- Send and Receive Bitcoin payments.
|
||||||
- Decode lightning payment requests.
|
- Decode lightning payment requests.
|
||||||
- Open and close channels.
|
- Open and close channels.
|
||||||
- Balance your channels through circular payments. ([Check out the Tutorial](https://medium.com/coinmonks/lightning-network-channel-balancing-with-thunderhub-972b41bf9243))
|
- Balance your channels through circular payments. ([Check out the Tutorial](https://apotdevin.com/blog/thunderhub-balancing))
|
||||||
- Update your all your channels fees or individual ones.
|
- Update your all your channels fees or individual ones.
|
||||||
- Backup, verify and recover all your channels.
|
- Backup, verify and recover all your channels.
|
||||||
- Sign and verify messages.
|
- Sign and verify messages.
|
||||||
|
@ -70,54 +80,135 @@ This repository consists of a **NextJS** server that handles both the backend **
|
||||||
|
|
||||||
### Deployment
|
### Deployment
|
||||||
|
|
||||||
- Docker images for easier deployment (WIP)
|
- Docker images for easier deployment
|
||||||
|
|
||||||
### Future Features
|
### Future Features
|
||||||
|
|
||||||
|
- Channel health/recommendations view
|
||||||
- Loop In and Out to provide liquidity or remove it from your channels.
|
- Loop In and Out to provide liquidity or remove it from your channels.
|
||||||
- Integration with HodlHodl
|
|
||||||
- Storefront interface
|
- Storefront interface
|
||||||
|
|
||||||
|
## **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!**
|
||||||
|
|
||||||
|
## Config
|
||||||
|
|
||||||
|
You can define some environment variables that ThunderHub can start with. To do this create a `.env` file in the root directory with the following parameters:
|
||||||
|
|
||||||
|
```js
|
||||||
|
THEME = 'dark' | 'light'; // Default: 'dark'
|
||||||
|
CURRENCY = 'sat' | 'btc' | 'eur' | 'usd'; // Default: 'sat'
|
||||||
|
FETCH_PRICES = true | false // Default: true
|
||||||
|
FETCH_FEES = true | false // Default: true
|
||||||
|
HODL_KEY='[Key provided by HodlHodl]' //Default: ''
|
||||||
|
BASE_PATH='[Base path where you want to have thunderhub running i.e. '/btcpay']' //Default: '/'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fetching prices and fees
|
||||||
|
|
||||||
|
ThunderHub fetches fiat prices from [Blockchain.com](https://blockchain.info/ticker)'s api and bitcoin on chain fees from [Earn.com](https://bitcoinfees.earn.com/api/v1/fees/recommended)'s api.
|
||||||
|
|
||||||
|
If you want to deactivate these requests you can set `FETCH_PRICES=false` and `FETCH_FEES=false` in your `.env` file or manually change them inside the settings view of ThunderHub.
|
||||||
|
|
||||||
|
### Running on different base path
|
||||||
|
|
||||||
|
Adding a BASE_PATH will run the ThunderHub server on a different base path.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
- default base path of `/` runs ThunderHub on `http://localhost:3000`
|
||||||
|
- base path of `/thub` runs ThunderHub on `http://localhost:3000/thub`
|
||||||
|
|
||||||
|
To run on a base path, ThunderHub needs to be behind a proxy with the following configuration (NGINX example):
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location /thub/ {
|
||||||
|
rewrite ^/thub(.*)$ $1 break;
|
||||||
|
proxy_pass http://localhost:3000/;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
To run ThunderHub you first need to clone this repository.
|
To run ThunderHub you first need to clone this repository.
|
||||||
|
|
||||||
```javascript
|
```js
|
||||||
git clone https://github.com/apotdevin/thunderhub.git
|
git clone https://github.com/apotdevin/thunderhub.git
|
||||||
```
|
```
|
||||||
|
|
||||||
### **Requirements**
|
After cloning the repository run `yarn` or `npm install` to get all the necessary modules installed.
|
||||||
|
|
||||||
- Node installed
|
After all the dependencies have finished installing, you can proceed to build and run the app with the following commands.
|
||||||
- Yarn installed
|
|
||||||
|
|
||||||
After cloning the repository run `yarn` to get all the necessary modules installed.
|
|
||||||
|
|
||||||
After `yarn` has finished installing all the dependencies you can proceed to build and run the app with the following commands.
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
//Yarn
|
||||||
yarn build
|
yarn build
|
||||||
yarn start
|
yarn start
|
||||||
|
|
||||||
|
//NPM
|
||||||
|
npm run build
|
||||||
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
This will start the server on port 3000, so just head to `localhost:3000` to see the app running.
|
This will start the server on port 3000, so just go to `localhost:3000` to see the app running.
|
||||||
|
|
||||||
#### HodlHodl Integration
|
If you want to specify a different port (for example port `4000`) run with:
|
||||||
|
|
||||||
To be able to use the HodlHodl integration create a `.env` file in the root folder with `HODL_KEY='[YOUR API KEY]'` and replace `[YOUR API KEY]` with the one that HodlHodl provides you.
|
```js
|
||||||
|
// Yarn
|
||||||
|
yarn start -p 4000
|
||||||
|
|
||||||
|
// NPM
|
||||||
|
npm start -p 4000
|
||||||
|
```
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
If you want to develop on ThunderHub and want hot reloading when you do changes, use the following commands:
|
If you want to develop on ThunderHub and want hot reloading when you do changes, use the following commands:
|
||||||
|
|
||||||
```javascript
|
```js
|
||||||
|
//Yarn
|
||||||
yarn dev
|
yarn dev
|
||||||
|
|
||||||
|
//NPM
|
||||||
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Storybook
|
#### Storybook
|
||||||
|
|
||||||
You can also get storybook running for quicker component development.
|
You can also get storybook running for quicker component development.
|
||||||
|
|
||||||
```javascript
|
```js
|
||||||
|
//Yarn
|
||||||
yarn storybook
|
yarn storybook
|
||||||
|
|
||||||
|
//NPM
|
||||||
|
npm run storybook
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
ThunderHub also provides docker images for easier deployment. [Docker Hub](https://hub.docker.com/repository/docker/apotdevin/thunderhub)
|
||||||
|
|
||||||
|
To get ThunderHub running with docker follow these steps:
|
||||||
|
|
||||||
|
1. `docker pull apotdevin/thunderhub:v0.5.5` (Or the latest version you find)
|
||||||
|
2. `docker run --rm -it -p 3000:3000/tcp apotdevin/thunderhub:v0.5.5`
|
||||||
|
|
||||||
|
You can now go to `localhost:3000` to see your running instance of ThunderHub
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint @typescript-eslint/no-var-requires: 0 */
|
||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
const withBundleAnalyzer = require('@next/bundle-analyzer')({
|
const withBundleAnalyzer = require('@next/bundle-analyzer')({
|
||||||
enabled: process.env.ANALYZE === 'true',
|
enabled: process.env.ANALYZE === 'true',
|
||||||
|
@ -17,6 +18,9 @@ module.exports = withBundleAnalyzer({
|
||||||
apiBaseUrl: `${process.env.API_BASE_URL || ''}/api/v1`,
|
apiBaseUrl: `${process.env.API_BASE_URL || ''}/api/v1`,
|
||||||
basePath: process.env.BASE_PATH || '',
|
basePath: process.env.BASE_PATH || '',
|
||||||
npmVersion: process.env.npm_package_version || '0.0.0',
|
npmVersion: process.env.npm_package_version || '0.0.0',
|
||||||
trustNeeded: process.env.TRUST || false,
|
defaultTheme: process.env.THEME || 'dark',
|
||||||
|
defaultCurrency: process.env.CURRENCY || 'sat',
|
||||||
|
fetchPrices: process.env.FETCH_PRICES === 'true' ? true : false,
|
||||||
|
fetchFees: process.env.FETCH_FEES === 'true' ? true : false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,8 +5,10 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cross-env NODE_OPTIONS='--insecure-http-parser' next",
|
"dev": "cross-env NODE_OPTIONS='--insecure-http-parser' next",
|
||||||
|
"dev:compatible": "next",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "cross-env NODE_OPTIONS='--insecure-http-parser' next start",
|
"start": "cross-env NODE_OPTIONS='--insecure-http-parser' next start",
|
||||||
|
"start:compatible": "next start",
|
||||||
"lint": "eslint */**/*.{js,ts,tsx} --quiet --fix",
|
"lint": "eslint */**/*.{js,ts,tsx} --quiet --fix",
|
||||||
"prettier": "prettier --write **/*.{ts,tsx,js,css,html}",
|
"prettier": "prettier --write **/*.{ts,tsx,js,css,html}",
|
||||||
"release": "standard-version",
|
"release": "standard-version",
|
||||||
|
@ -33,6 +35,7 @@
|
||||||
"apollo-boost": "^0.4.7",
|
"apollo-boost": "^0.4.7",
|
||||||
"apollo-server-micro": "^2.12.0",
|
"apollo-server-micro": "^2.12.0",
|
||||||
"base64url": "^3.0.1",
|
"base64url": "^3.0.1",
|
||||||
|
"cookie": "^0.4.1",
|
||||||
"crypto-js": "^4.0.0",
|
"crypto-js": "^4.0.0",
|
||||||
"date-fns": "^2.12.0",
|
"date-fns": "^2.12.0",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
|
@ -42,6 +45,7 @@
|
||||||
"graphql-tag": "^2.10.3",
|
"graphql-tag": "^2.10.3",
|
||||||
"intersection-observer": "^0.10.0",
|
"intersection-observer": "^0.10.0",
|
||||||
"isomorphic-unfetch": "^3.0.0",
|
"isomorphic-unfetch": "^3.0.0",
|
||||||
|
"js-cookie": "^2.2.1",
|
||||||
"ln-service": "^48.0.5",
|
"ln-service": "^48.0.5",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"lodash.groupby": "^4.6.0",
|
"lodash.groupby": "^4.6.0",
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import App from 'next/app';
|
import * as React from 'react';
|
||||||
import React from 'react';
|
|
||||||
import { ContextProvider } from '../src/context/ContextProvider';
|
import { ContextProvider } from '../src/context/ContextProvider';
|
||||||
import { ThemeProvider } from 'styled-components';
|
import { ThemeProvider } from 'styled-components';
|
||||||
import { useSettings } from '../src/context/SettingsContext';
|
import { useConfigState, ConfigProvider } from '../src/context/ConfigContext';
|
||||||
import { ModalProvider, BaseModalBackground } from 'styled-react-modal';
|
import { ModalProvider, BaseModalBackground } from 'styled-react-modal';
|
||||||
import { GlobalStyles } from '../src/styles/GlobalStyle';
|
import { GlobalStyles } from '../src/styles/GlobalStyle';
|
||||||
import { Header } from '../src/layouts/header/Header';
|
import { Header } from '../src/layouts/header/Header';
|
||||||
|
@ -20,11 +19,12 @@ import { PageWrapper, HeaderBodyWrapper } from '../src/layouts/Layout.styled';
|
||||||
import { useStatusState } from '../src/context/StatusContext';
|
import { useStatusState } from '../src/context/StatusContext';
|
||||||
import { ChatFetcher } from '../src/components/chat/ChatFetcher';
|
import { ChatFetcher } from '../src/components/chat/ChatFetcher';
|
||||||
import { ChatInit } from '../src/components/chat/ChatInit';
|
import { ChatInit } from '../src/components/chat/ChatInit';
|
||||||
|
import { parseCookies } from '../src/utils/cookies';
|
||||||
|
|
||||||
toast.configure({ draggable: false, pauseOnFocusLoss: false });
|
toast.configure({ draggable: false, pauseOnFocusLoss: false });
|
||||||
|
|
||||||
const Wrapper: React.FC = ({ children }) => {
|
const Wrapper: React.FC = ({ children }) => {
|
||||||
const { theme } = useSettings();
|
const { theme } = useConfigState();
|
||||||
const { pathname } = useRouter();
|
const { pathname } = useRouter();
|
||||||
const { connected } = useStatusState();
|
const { connected } = useStatusState();
|
||||||
|
|
||||||
|
@ -63,24 +63,36 @@ const Wrapper: React.FC = ({ children }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
class MyApp extends App<any> {
|
const App = ({ Component, pageProps, apollo, initialConfig }: any) => (
|
||||||
render() {
|
<>
|
||||||
const { Component, pageProps, apollo } = this.props;
|
<Head>
|
||||||
return (
|
<title>ThunderHub - Lightning Node Manager</title>
|
||||||
<>
|
</Head>
|
||||||
<Head>
|
<ApolloProvider client={apollo}>
|
||||||
<title>ThunderHub - Lightning Node Manager</title>
|
<ConfigProvider initialConfig={initialConfig}>
|
||||||
</Head>
|
<ContextProvider>
|
||||||
<ApolloProvider client={apollo}>
|
<Wrapper>
|
||||||
<ContextProvider>
|
<Component {...pageProps} />
|
||||||
<Wrapper>
|
</Wrapper>
|
||||||
<Component {...pageProps} />
|
</ContextProvider>
|
||||||
</Wrapper>
|
</ConfigProvider>
|
||||||
</ContextProvider>
|
</ApolloProvider>
|
||||||
</ApolloProvider>
|
</>
|
||||||
</>
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withApollo(MyApp);
|
App.getInitialProps = async props => {
|
||||||
|
const cookies = parseCookies(props.ctx.req);
|
||||||
|
if (!cookies?.config) {
|
||||||
|
return { initialConfig: {} };
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const config = JSON.parse(cookies.config);
|
||||||
|
return {
|
||||||
|
initialConfig: config,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return { initialConfig: {} };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withApollo(App);
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
ColorButton,
|
ColorButton,
|
||||||
} from '../src/components/generic/Styled';
|
} from '../src/components/generic/Styled';
|
||||||
import { useAccount } from '../src/context/AccountContext';
|
import { useAccount } from '../src/context/AccountContext';
|
||||||
import { useSettings } from '../src/context/SettingsContext';
|
import { useConfigState } from '../src/context/ConfigContext';
|
||||||
import { textColorMap } from '../src/styles/Themes';
|
import { textColorMap } from '../src/styles/Themes';
|
||||||
import { useGetChannelAmountInfoQuery } from '../src/generated/graphql';
|
import { useGetChannelAmountInfoQuery } from '../src/generated/graphql';
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ const ChannelView = () => {
|
||||||
closed: 0,
|
closed: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { theme } = useSettings();
|
const { theme } = useConfigState();
|
||||||
const { auth } = useAccount();
|
const { auth } = useAccount();
|
||||||
|
|
||||||
const { data } = useGetChannelAmountInfoQuery({
|
const { data } = useGetChannelAmountInfoQuery({
|
||||||
|
|
|
@ -48,7 +48,7 @@ const FeesView = () => {
|
||||||
? toast.success('Fees Updated')
|
? toast.success('Fees Updated')
|
||||||
: toast.error('Error updating fees');
|
: toast.error('Error updating fees');
|
||||||
},
|
},
|
||||||
refetchQueries: ['GetChannelFees'],
|
refetchQueries: ['ChannelFees'],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (loading || !data || !data.getChannelFees) {
|
if (loading || !data || !data.getChannelFees) {
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { getErrorContent } from '../src/utils/error';
|
||||||
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
||||||
import { ForwardCard } from '../src/views/forwards/ForwardsCard';
|
import { ForwardCard } from '../src/views/forwards/ForwardsCard';
|
||||||
import { textColorMap } from '../src/styles/Themes';
|
import { textColorMap } from '../src/styles/Themes';
|
||||||
import { useSettings } from '../src/context/SettingsContext';
|
import { useConfigState } from '../src/context/ConfigContext';
|
||||||
import { ForwardBox } from '../src/views/home/reports/forwardReport';
|
import { ForwardBox } from '../src/views/home/reports/forwardReport';
|
||||||
import { useGetForwardsQuery } from '../src/generated/graphql';
|
import { useGetForwardsQuery } from '../src/generated/graphql';
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ const ForwardsView = () => {
|
||||||
const [time, setTime] = useState('week');
|
const [time, setTime] = useState('week');
|
||||||
const [indexOpen, setIndexOpen] = useState(0);
|
const [indexOpen, setIndexOpen] = useState(0);
|
||||||
|
|
||||||
const { theme } = useSettings();
|
const { theme } = useConfigState();
|
||||||
const { auth } = useAccount();
|
const { auth } = useAccount();
|
||||||
|
|
||||||
const { loading, data } = useGetForwardsQuery({
|
const { loading, data } = useGetForwardsQuery({
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { DangerView } from '../src/views/settings/Danger';
|
||||||
import { CurrentSettings } from '../src/views/settings/Current';
|
import { CurrentSettings } from '../src/views/settings/Current';
|
||||||
import { SyncSettings } from '../src/views/settings/Sync';
|
import { SyncSettings } from '../src/views/settings/Sync';
|
||||||
import { ChatSettings } from '../src/views/settings/Chat';
|
import { ChatSettings } from '../src/views/settings/Chat';
|
||||||
|
import { PrivacySettings } from '../src/views/settings/Privacy';
|
||||||
|
|
||||||
export const ButtonRow = styled.div`
|
export const ButtonRow = styled.div`
|
||||||
width: auto;
|
width: auto;
|
||||||
|
@ -30,6 +31,7 @@ const SettingsView = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<InterfaceSettings />
|
<InterfaceSettings />
|
||||||
|
<PrivacySettings />
|
||||||
<ChatSettings />
|
<ChatSettings />
|
||||||
<SyncSettings />
|
<SyncSettings />
|
||||||
<CurrentSettings />
|
<CurrentSettings />
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSpring, animated } from 'react-spring';
|
import { useSpring, animated } from 'react-spring';
|
||||||
import { getValue } from '../../utils/helpers';
|
import { getValue } from '../../utils/helpers';
|
||||||
import { useSettings } from '../../context/SettingsContext';
|
import { useConfigState } from '../../context/ConfigContext';
|
||||||
import { usePriceState } from '../../context/PriceContext';
|
import { usePriceState } from '../../context/PriceContext';
|
||||||
|
|
||||||
type PriceProps = {
|
type PriceProps = {
|
||||||
|
@ -19,8 +19,12 @@ export const AnimatedNumber = ({ amount = 0 }: AnimatedProps) => {
|
||||||
from: { value: 0 },
|
from: { value: 0 },
|
||||||
value: amount,
|
value: amount,
|
||||||
});
|
});
|
||||||
const { currency } = useSettings();
|
const { currency, displayValues } = useConfigState();
|
||||||
const { prices } = usePriceState();
|
const { prices, dontShow } = usePriceState();
|
||||||
|
|
||||||
|
if (!displayValues) {
|
||||||
|
return <>-</>;
|
||||||
|
}
|
||||||
|
|
||||||
let priceProps: PriceProps = {
|
let priceProps: PriceProps = {
|
||||||
price: 0,
|
price: 0,
|
||||||
|
@ -28,7 +32,7 @@ export const AnimatedNumber = ({ amount = 0 }: AnimatedProps) => {
|
||||||
currency: currency !== 'btc' && currency !== 'sat' ? 'sat' : currency,
|
currency: currency !== 'btc' && currency !== 'sat' ? 'sat' : currency,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (prices) {
|
if (prices && !dontShow) {
|
||||||
const current: { last: number; symbol: string } = prices[currency] ?? {
|
const current: { last: number; symbol: string } = prices[currency] ?? {
|
||||||
last: 0,
|
last: 0,
|
||||||
symbol: '',
|
symbol: '',
|
||||||
|
|
|
@ -1,13 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Checkbox } from '../../checkbox/Checkbox';
|
import { Checkbox } from '../../checkbox/Checkbox';
|
||||||
import { CheckboxText, StyledContainer, FixedWidth } from '../Auth.styled';
|
import { CheckboxText } from '../Auth.styled';
|
||||||
import { AlertCircle } from 'react-feather';
|
|
||||||
import { fontColors } from '../../../styles/Themes';
|
|
||||||
import { ColorButton } from '../../buttons/colorButton/ColorButton';
|
import { ColorButton } from '../../buttons/colorButton/ColorButton';
|
||||||
import getConfig from 'next/config';
|
|
||||||
|
|
||||||
const { publicRuntimeConfig } = getConfig();
|
|
||||||
const { trustNeeded } = publicRuntimeConfig;
|
|
||||||
|
|
||||||
type CheckboxProps = {
|
type CheckboxProps = {
|
||||||
handleClick: () => void;
|
handleClick: () => void;
|
||||||
|
@ -37,24 +31,5 @@ export const RiskCheckboxAndConfirm = ({
|
||||||
>
|
>
|
||||||
Connect
|
Connect
|
||||||
</ColorButton>
|
</ColorButton>
|
||||||
<WarningBox />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const WarningBox = () => {
|
|
||||||
if (!trustNeeded) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<StyledContainer>
|
|
||||||
<FixedWidth>
|
|
||||||
<AlertCircle size={18} color={fontColors.grey7} />
|
|
||||||
</FixedWidth>
|
|
||||||
<CheckboxText>
|
|
||||||
Macaroons are handled by the ThunderHub server to connect to your LND
|
|
||||||
node but are never stored. Still, this involves a certain degree of
|
|
||||||
trust you must be aware of.
|
|
||||||
</CheckboxText>
|
|
||||||
</StyledContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,27 +1,34 @@
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useBitcoinDispatch } from '../../context/BitcoinContext';
|
import { useBitcoinDispatch } from '../../context/BitcoinContext';
|
||||||
import { useGetBitcoinFeesQuery } from '../../generated/graphql';
|
import { useGetBitcoinFeesQuery } from '../../generated/graphql';
|
||||||
|
import { useConfigState } from '../../context/ConfigContext';
|
||||||
|
|
||||||
export const BitcoinFees = () => {
|
export const BitcoinFees = () => {
|
||||||
|
const { fetchFees } = useConfigState();
|
||||||
const setInfo = useBitcoinDispatch();
|
const setInfo = useBitcoinDispatch();
|
||||||
|
|
||||||
const { loading, data, stopPolling } = useGetBitcoinFeesQuery({
|
const { loading, data, stopPolling } = useGetBitcoinFeesQuery({
|
||||||
|
skip: !fetchFees,
|
||||||
fetchPolicy: 'network-only',
|
fetchPolicy: 'network-only',
|
||||||
onError: () => {
|
onError: () => {
|
||||||
setInfo({ type: 'error' });
|
setInfo({ type: 'dontShow' });
|
||||||
stopPolling();
|
stopPolling();
|
||||||
},
|
},
|
||||||
pollInterval: 60000,
|
pollInterval: 60000,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && data && data.getBitcoinFees) {
|
if (!fetchFees) {
|
||||||
const { fast, halfHour, hour } = data.getBitcoinFees;
|
setInfo({ type: 'dontShow' });
|
||||||
setInfo({
|
|
||||||
type: 'fetched',
|
|
||||||
state: { loading: false, error: false, fast, halfHour, hour },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, [data, loading, setInfo]);
|
}, [fetchFees, setInfo]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!loading && data && data.getBitcoinFees && fetchFees) {
|
||||||
|
const { fast, halfHour, hour } = data.getBitcoinFees;
|
||||||
|
setInfo({ type: 'fetched', state: { fast, halfHour, hour } });
|
||||||
|
}
|
||||||
|
}, [data, loading, setInfo, fetchFees]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,29 +1,38 @@
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { usePriceDispatch } from '../../context/PriceContext';
|
import { usePriceDispatch } from '../../context/PriceContext';
|
||||||
import { useGetBitcoinPriceQuery } from '../../generated/graphql';
|
import { useGetBitcoinPriceQuery } from '../../generated/graphql';
|
||||||
|
import { useConfigState } from '../../context/ConfigContext';
|
||||||
|
|
||||||
export const BitcoinPrice = () => {
|
export const BitcoinPrice = () => {
|
||||||
|
const { fetchPrices } = useConfigState();
|
||||||
const setPrices = usePriceDispatch();
|
const setPrices = usePriceDispatch();
|
||||||
const { loading, data, stopPolling } = useGetBitcoinPriceQuery({
|
const { loading, data, stopPolling } = useGetBitcoinPriceQuery({
|
||||||
|
skip: !fetchPrices,
|
||||||
fetchPolicy: 'network-only',
|
fetchPolicy: 'network-only',
|
||||||
onError: () => setPrices({ type: 'error' }),
|
onError: () => {
|
||||||
|
setPrices({ type: 'dontShow' });
|
||||||
|
stopPolling();
|
||||||
|
},
|
||||||
pollInterval: 60000,
|
pollInterval: 60000,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && data && data.getBitcoinPrice) {
|
if (!fetchPrices) {
|
||||||
|
setPrices({ type: 'dontShow' });
|
||||||
|
}
|
||||||
|
}, [fetchPrices, setPrices]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!loading && data && data.getBitcoinPrice && fetchPrices) {
|
||||||
try {
|
try {
|
||||||
const prices = JSON.parse(data.getBitcoinPrice);
|
const prices = JSON.parse(data.getBitcoinPrice);
|
||||||
setPrices({
|
setPrices({ type: 'fetched', state: { prices } });
|
||||||
type: 'fetched',
|
|
||||||
state: { loading: false, error: false, prices },
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
setPrices({ type: 'dontShow' });
|
||||||
stopPolling();
|
stopPolling();
|
||||||
setPrices({ type: 'error' });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [data, loading, setPrices, stopPolling]);
|
}, [data, loading, setPrices, stopPolling, fetchPrices]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,10 +5,13 @@ import { useAccount } from '../../context/AccountContext';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { getErrorContent } from '../../utils/error';
|
import { getErrorContent } from '../../utils/error';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
import { useConfigState } from '../../context/ConfigContext';
|
||||||
|
|
||||||
export const ChatFetcher = () => {
|
export const ChatFetcher = () => {
|
||||||
const newChatToastId = 'newChatToastId';
|
const newChatToastId = 'newChatToastId';
|
||||||
|
|
||||||
|
const { chatPollingSpeed } = useConfigState();
|
||||||
|
|
||||||
const { auth } = useAccount();
|
const { auth } = useAccount();
|
||||||
const { pathname } = useRouter();
|
const { pathname } = useRouter();
|
||||||
const { lastChat, chats, sentChats, initialized } = useChatState();
|
const { lastChat, chats, sentChats, initialized } = useChatState();
|
||||||
|
@ -18,7 +21,7 @@ export const ChatFetcher = () => {
|
||||||
|
|
||||||
const { data, loading, error } = useGetMessagesQuery({
|
const { data, loading, error } = useGetMessagesQuery({
|
||||||
skip: !auth || initialized || noChatsAvailable,
|
skip: !auth || initialized || noChatsAvailable,
|
||||||
pollInterval: 1000,
|
pollInterval: chatPollingSpeed,
|
||||||
fetchPolicy: 'network-only',
|
fetchPolicy: 'network-only',
|
||||||
variables: { auth, initialize: !noChatsAvailable },
|
variables: { auth, initialize: !noChatsAvailable },
|
||||||
onError: error => toast.error(getErrorContent(error)),
|
onError: error => toast.error(getErrorContent(error)),
|
||||||
|
@ -58,7 +61,7 @@ export const ChatFetcher = () => {
|
||||||
const last = newMessages[0]?.id;
|
const last = newMessages[0]?.id;
|
||||||
dispatch({ type: 'additional', chats: newMessages, lastChat: last });
|
dispatch({ type: 'additional', chats: newMessages, lastChat: last });
|
||||||
}
|
}
|
||||||
}, [data, loading, error]);
|
}, [data, loading, error, dispatch, lastChat, pathname]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,10 +19,6 @@ export const ChatInit = () => {
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const storageChats = localStorage.getItem(`${id}-sentChats`) || '';
|
const storageChats = localStorage.getItem(`${id}-sentChats`) || '';
|
||||||
const hideFee = localStorage.getItem('hideFee') === 'true' ? true : false;
|
|
||||||
const hideNonVerified =
|
|
||||||
localStorage.getItem('hideNonVerified') === 'true' ? true : false;
|
|
||||||
const maxFee = Number(localStorage.getItem('maxChatFee')) || 20;
|
|
||||||
|
|
||||||
if (storageChats !== '') {
|
if (storageChats !== '') {
|
||||||
try {
|
try {
|
||||||
|
@ -33,9 +29,6 @@ export const ChatInit = () => {
|
||||||
type: 'initialized',
|
type: 'initialized',
|
||||||
sentChats: savedChats,
|
sentChats: savedChats,
|
||||||
sender,
|
sender,
|
||||||
hideFee,
|
|
||||||
hideNonVerified,
|
|
||||||
maxFee,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -43,7 +36,7 @@ export const ChatInit = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getMessages();
|
getMessages();
|
||||||
}, []);
|
}, [dispatch, getMessages, id]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!initLoading && !initError && initData?.getMessages) {
|
if (!initLoading && !initError && initData?.getMessages) {
|
||||||
|
@ -64,7 +57,7 @@ export const ChatInit = () => {
|
||||||
sender,
|
sender,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [initLoading, initError, initData]);
|
}, [initLoading, initError, initData, dispatch]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
|
@ -41,13 +41,13 @@ export const CloseChannel = ({
|
||||||
channelId,
|
channelId,
|
||||||
channelName,
|
channelName,
|
||||||
}: CloseChannelProps) => {
|
}: CloseChannelProps) => {
|
||||||
|
const { fast, halfHour, hour, dontShow } = useBitcoinState();
|
||||||
|
|
||||||
const [isForce, setIsForce] = useState<boolean>(false);
|
const [isForce, setIsForce] = useState<boolean>(false);
|
||||||
const [isType, setIsType] = useState<string>('none');
|
const [isType, setIsType] = useState<string>(dontShow ? 'fee' : 'none');
|
||||||
const [amount, setAmount] = useState<number>(0);
|
const [amount, setAmount] = useState<number>(0);
|
||||||
const [isConfirmed, setIsConfirmed] = useState<boolean>(false);
|
const [isConfirmed, setIsConfirmed] = useState<boolean>(false);
|
||||||
|
|
||||||
const { fast, halfHour, hour } = useBitcoinState();
|
|
||||||
|
|
||||||
const [closeChannel] = useCloseChannelMutation({
|
const [closeChannel] = useCloseChannelMutation({
|
||||||
onCompleted: data => {
|
onCompleted: data => {
|
||||||
if (data.closeChannel) {
|
if (data.closeChannel) {
|
||||||
|
@ -113,7 +113,8 @@ export const CloseChannel = ({
|
||||||
<Sub4Title>Fee:</Sub4Title>
|
<Sub4Title>Fee:</Sub4Title>
|
||||||
</SingleLine>
|
</SingleLine>
|
||||||
<MultiButton>
|
<MultiButton>
|
||||||
{renderButton(() => setIsType('none'), 'Auto', isType === 'none')}
|
{!dontShow &&
|
||||||
|
renderButton(() => setIsType('none'), 'Auto', isType === 'none')}
|
||||||
{renderButton(() => setIsType('fee'), 'Fee', isType === 'fee')}
|
{renderButton(() => setIsType('fee'), 'Fee', isType === 'fee')}
|
||||||
{renderButton(() => setIsType('target'), 'Target', isType === 'target')}
|
{renderButton(() => setIsType('target'), 'Target', isType === 'target')}
|
||||||
</MultiButton>
|
</MultiButton>
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
import { HelpCircle } from 'react-feather';
|
import { HelpCircle } from 'react-feather';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import ReactTooltip from 'react-tooltip';
|
import ReactTooltip from 'react-tooltip';
|
||||||
import { useSettings } from '../../context/SettingsContext';
|
import { useConfigState } from '../../context/ConfigContext';
|
||||||
import { getTooltipType } from '../generic/helpers';
|
import { getTooltipType } from '../generic/helpers';
|
||||||
|
|
||||||
const StyledQuestion = styled(HelpCircle)`
|
const StyledQuestion = styled(HelpCircle)`
|
||||||
|
@ -20,10 +20,9 @@ const StyledQuestion = styled(HelpCircle)`
|
||||||
|
|
||||||
export const NodeBar = () => {
|
export const NodeBar = () => {
|
||||||
const { accounts } = useAccount();
|
const { accounts } = useAccount();
|
||||||
const { nodeInfo } = useSettings();
|
const { multiNodeInfo, theme } = useConfigState();
|
||||||
const slider = React.useRef<HTMLDivElement>(null);
|
const slider = React.useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const { theme } = useSettings();
|
|
||||||
const tooltipType: any = getTooltipType(theme);
|
const tooltipType: any = getTooltipType(theme);
|
||||||
|
|
||||||
const viewOnlyAccounts = accounts.filter(account => account.viewOnly !== '');
|
const viewOnlyAccounts = accounts.filter(account => account.viewOnly !== '');
|
||||||
|
@ -38,7 +37,7 @@ export const NodeBar = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (viewOnlyAccounts.length <= 1 || !nodeInfo) {
|
if (viewOnlyAccounts.length <= 1 || !multiNodeInfo) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSettings } from '../../context/SettingsContext';
|
import { useConfigState } from '../../context/ConfigContext';
|
||||||
import { getValue } from '../../utils/helpers';
|
import { getValue } from '../../utils/helpers';
|
||||||
import { usePriceState } from '../../context/PriceContext';
|
import { usePriceState } from '../../context/PriceContext';
|
||||||
|
|
||||||
|
@ -16,8 +16,12 @@ export const Price = ({
|
||||||
amount: number | string;
|
amount: number | string;
|
||||||
breakNumber?: boolean;
|
breakNumber?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const { currency } = useSettings();
|
const { currency, displayValues } = useConfigState();
|
||||||
const { prices, loading, error } = usePriceState();
|
const { prices, dontShow } = usePriceState();
|
||||||
|
|
||||||
|
if (!displayValues) {
|
||||||
|
return <>-</>;
|
||||||
|
}
|
||||||
|
|
||||||
let priceProps: PriceProps = {
|
let priceProps: PriceProps = {
|
||||||
price: 0,
|
price: 0,
|
||||||
|
@ -25,7 +29,7 @@ export const Price = ({
|
||||||
currency: currency !== 'btc' && currency !== 'sat' ? 'sat' : currency,
|
currency: currency !== 'btc' && currency !== 'sat' ? 'sat' : currency,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (prices && !loading && !error) {
|
if (prices && !dontShow) {
|
||||||
const current: { last: number; symbol: string } = prices[currency] ?? {
|
const current: { last: number; symbol: string } = prices[currency] ?? {
|
||||||
last: 0,
|
last: 0,
|
||||||
symbol: '',
|
symbol: '',
|
||||||
|
@ -48,13 +52,17 @@ interface GetPriceProps {
|
||||||
|
|
||||||
export const getPrice = (
|
export const getPrice = (
|
||||||
currency: string,
|
currency: string,
|
||||||
|
displayValues: boolean,
|
||||||
priceContext: {
|
priceContext: {
|
||||||
error: boolean;
|
dontShow: boolean;
|
||||||
loading: boolean;
|
|
||||||
prices?: { [key: string]: { last: number; symbol: string } };
|
prices?: { [key: string]: { last: number; symbol: string } };
|
||||||
}
|
}
|
||||||
) => ({ amount, breakNumber = false }: GetPriceProps): string => {
|
) => ({ amount, breakNumber = false }: GetPriceProps): string => {
|
||||||
const { prices, loading, error } = priceContext;
|
const { prices, dontShow } = priceContext;
|
||||||
|
|
||||||
|
if (!displayValues) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
|
||||||
let priceProps: PriceProps = {
|
let priceProps: PriceProps = {
|
||||||
price: 0,
|
price: 0,
|
||||||
|
@ -62,7 +70,7 @@ export const getPrice = (
|
||||||
currency: currency !== 'btc' && currency !== 'sat' ? 'sat' : currency,
|
currency: currency !== 'btc' && currency !== 'sat' ? 'sat' : currency,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (prices && !loading && !error) {
|
if (prices && !dontShow) {
|
||||||
const current: { last: number; symbol: string } = prices[currency] ?? {
|
const current: { last: number; symbol: string } = prices[currency] ?? {
|
||||||
last: 0,
|
last: 0,
|
||||||
symbol: '',
|
symbol: '',
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
import React, { createContext, useContext, useReducer } from 'react';
|
import React, { createContext, useContext, useReducer } from 'react';
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
loading: boolean;
|
dontShow: boolean;
|
||||||
error: boolean;
|
fast: number;
|
||||||
|
halfHour: number;
|
||||||
|
hour: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ChangeState = {
|
||||||
fast: number;
|
fast: number;
|
||||||
halfHour: number;
|
halfHour: number;
|
||||||
hour: number;
|
hour: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ActionType = {
|
type ActionType = {
|
||||||
type: 'fetched' | 'error';
|
type: 'fetched' | 'dontShow';
|
||||||
state?: State;
|
state?: ChangeState;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Dispatch = (action: ActionType) => void;
|
type Dispatch = (action: ActionType) => void;
|
||||||
|
@ -19,8 +24,7 @@ export const StateContext = createContext<State | undefined>(undefined);
|
||||||
export const DispatchContext = createContext<Dispatch | undefined>(undefined);
|
export const DispatchContext = createContext<Dispatch | undefined>(undefined);
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
loading: true,
|
dontShow: true,
|
||||||
error: false,
|
|
||||||
fast: 0,
|
fast: 0,
|
||||||
halfHour: 0,
|
halfHour: 0,
|
||||||
hour: 0,
|
hour: 0,
|
||||||
|
@ -28,16 +32,12 @@ const initialState = {
|
||||||
|
|
||||||
const stateReducer = (state: State, action: ActionType): State => {
|
const stateReducer = (state: State, action: ActionType): State => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
case 'dontShow':
|
||||||
|
return { ...initialState, dontShow: true };
|
||||||
case 'fetched':
|
case 'fetched':
|
||||||
return action.state || initialState;
|
return { ...initialState, ...action.state, dontShow: false };
|
||||||
case 'error':
|
|
||||||
return {
|
|
||||||
...initialState,
|
|
||||||
loading: false,
|
|
||||||
error: true,
|
|
||||||
};
|
|
||||||
default:
|
default:
|
||||||
return initialState;
|
return state;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -28,9 +28,6 @@ type State = {
|
||||||
sentChats: SentChatProps[];
|
sentChats: SentChatProps[];
|
||||||
lastChat: string;
|
lastChat: string;
|
||||||
sender: string;
|
sender: string;
|
||||||
hideFee: boolean;
|
|
||||||
hideNonVerified: boolean;
|
|
||||||
maxFee: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type ActionType = {
|
type ActionType = {
|
||||||
|
@ -39,9 +36,6 @@ type ActionType = {
|
||||||
| 'additional'
|
| 'additional'
|
||||||
| 'changeActive'
|
| 'changeActive'
|
||||||
| 'newChat'
|
| 'newChat'
|
||||||
| 'hideNonVerified'
|
|
||||||
| 'hideFee'
|
|
||||||
| 'changeFee'
|
|
||||||
| 'disconnected';
|
| 'disconnected';
|
||||||
chats?: ChatProps[];
|
chats?: ChatProps[];
|
||||||
sentChats?: SentChatProps[];
|
sentChats?: SentChatProps[];
|
||||||
|
@ -49,9 +43,6 @@ type ActionType = {
|
||||||
lastChat?: string;
|
lastChat?: string;
|
||||||
sender?: string;
|
sender?: string;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
hideFee?: boolean;
|
|
||||||
hideNonVerified?: boolean;
|
|
||||||
maxFee?: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type Dispatch = (action: ActionType) => void;
|
type Dispatch = (action: ActionType) => void;
|
||||||
|
@ -59,15 +50,12 @@ type Dispatch = (action: ActionType) => void;
|
||||||
const StateContext = createContext<State | undefined>(undefined);
|
const StateContext = createContext<State | undefined>(undefined);
|
||||||
const DispatchContext = createContext<Dispatch | undefined>(undefined);
|
const DispatchContext = createContext<Dispatch | undefined>(undefined);
|
||||||
|
|
||||||
const initialState = {
|
const initialState: State = {
|
||||||
initialized: false,
|
initialized: false,
|
||||||
chats: [],
|
chats: [],
|
||||||
lastChat: '',
|
lastChat: '',
|
||||||
sender: '',
|
sender: '',
|
||||||
sentChats: [],
|
sentChats: [],
|
||||||
hideFee: false,
|
|
||||||
hideNonVerified: false,
|
|
||||||
maxFee: 20,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const stateReducer = (state: State, action: ActionType): State => {
|
const stateReducer = (state: State, action: ActionType): State => {
|
||||||
|
@ -100,29 +88,8 @@ const stateReducer = (state: State, action: ActionType): State => {
|
||||||
sentChats: [...state.sentChats, action.newChat],
|
sentChats: [...state.sentChats, action.newChat],
|
||||||
...(action.sender && { sender: action.sender }),
|
...(action.sender && { sender: action.sender }),
|
||||||
};
|
};
|
||||||
case 'hideFee':
|
|
||||||
localStorage.setItem('hideFee', JSON.stringify(action.hideFee));
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
hideFee: action.hideFee,
|
|
||||||
};
|
|
||||||
case 'hideNonVerified':
|
|
||||||
localStorage.setItem(
|
|
||||||
'hideNonVerified',
|
|
||||||
JSON.stringify(action.hideNonVerified)
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
hideNonVerified: action.hideNonVerified,
|
|
||||||
};
|
|
||||||
case 'changeFee':
|
|
||||||
localStorage.setItem('maxChatFee', JSON.stringify(action.maxFee));
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
maxFee: action.maxFee,
|
|
||||||
};
|
|
||||||
default:
|
default:
|
||||||
return initialState;
|
return state;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
117
src/context/ConfigContext.tsx
Normal file
117
src/context/ConfigContext.tsx
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
import React, { createContext, useContext, useReducer, useEffect } from 'react';
|
||||||
|
import getConfig from 'next/config';
|
||||||
|
import Cookies from 'js-cookie';
|
||||||
|
|
||||||
|
const themeTypes = ['dark', 'light'];
|
||||||
|
const currencyTypes = ['sat', 'btc', 'EUR', 'USD'];
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
currency: string;
|
||||||
|
theme: string;
|
||||||
|
sidebar: boolean;
|
||||||
|
multiNodeInfo: boolean;
|
||||||
|
fetchFees: boolean;
|
||||||
|
fetchPrices: boolean;
|
||||||
|
displayValues: boolean;
|
||||||
|
hideFee: boolean;
|
||||||
|
hideNonVerified: boolean;
|
||||||
|
maxFee: number;
|
||||||
|
chatPollingSpeed: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ConfigInitProps = {
|
||||||
|
initialConfig: State;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ActionType = {
|
||||||
|
type: 'change';
|
||||||
|
currency?: string;
|
||||||
|
theme?: string;
|
||||||
|
sidebar?: boolean;
|
||||||
|
multiNodeInfo?: boolean;
|
||||||
|
fetchFees?: boolean;
|
||||||
|
fetchPrices?: boolean;
|
||||||
|
displayValues?: boolean;
|
||||||
|
hideFee?: boolean;
|
||||||
|
hideNonVerified?: boolean;
|
||||||
|
maxFee?: number;
|
||||||
|
chatPollingSpeed?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Dispatch = (action: ActionType) => void;
|
||||||
|
|
||||||
|
const StateContext = createContext<State | undefined>(undefined);
|
||||||
|
const DispatchContext = createContext<Dispatch | undefined>(undefined);
|
||||||
|
|
||||||
|
const { publicRuntimeConfig } = getConfig();
|
||||||
|
const {
|
||||||
|
defaultTheme: defT,
|
||||||
|
defaultCurrency: defC,
|
||||||
|
fetchPrices,
|
||||||
|
fetchFees,
|
||||||
|
} = publicRuntimeConfig;
|
||||||
|
|
||||||
|
const initialState: State = {
|
||||||
|
currency: currencyTypes.indexOf(defC) > -1 ? defC : 'sat',
|
||||||
|
theme: themeTypes.indexOf(defT) > -1 ? defT : 'dark',
|
||||||
|
sidebar: true,
|
||||||
|
multiNodeInfo: false,
|
||||||
|
fetchFees,
|
||||||
|
fetchPrices,
|
||||||
|
displayValues: true,
|
||||||
|
hideFee: false,
|
||||||
|
hideNonVerified: false,
|
||||||
|
maxFee: 20,
|
||||||
|
chatPollingSpeed: 1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
const stateReducer = (state: State, action: ActionType): State => {
|
||||||
|
const { type, ...settings } = action;
|
||||||
|
switch (type) {
|
||||||
|
case 'change':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...settings,
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ConfigProvider: React.FC<ConfigInitProps> = ({
|
||||||
|
children,
|
||||||
|
initialConfig,
|
||||||
|
}) => {
|
||||||
|
const [state, dispatch] = useReducer(stateReducer, {
|
||||||
|
...initialState,
|
||||||
|
...initialConfig,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Cookies.set('config', state, { expires: 365, sameSite: 'strict' });
|
||||||
|
}, [state]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DispatchContext.Provider value={dispatch}>
|
||||||
|
<StateContext.Provider value={state}>{children}</StateContext.Provider>
|
||||||
|
</DispatchContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const useConfigState = () => {
|
||||||
|
const context = useContext(StateContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error('useConfigState must be used within a ConfigProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useConfigDispatch = () => {
|
||||||
|
const context = useContext(DispatchContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error('useConfigDispatch must be used within a ConfigProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { ConfigProvider, useConfigState, useConfigDispatch };
|
|
@ -1,6 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { AccountProvider } from './AccountContext';
|
import { AccountProvider } from './AccountContext';
|
||||||
import { SettingsProvider } from './SettingsContext';
|
|
||||||
import { BitcoinInfoProvider } from './BitcoinContext';
|
import { BitcoinInfoProvider } from './BitcoinContext';
|
||||||
import { StatusProvider } from './StatusContext';
|
import { StatusProvider } from './StatusContext';
|
||||||
import { PriceProvider } from './PriceContext';
|
import { PriceProvider } from './PriceContext';
|
||||||
|
@ -8,14 +7,12 @@ import { ChatProvider } from './ChatContext';
|
||||||
|
|
||||||
export const ContextProvider: React.FC = ({ children }) => (
|
export const ContextProvider: React.FC = ({ children }) => (
|
||||||
<AccountProvider>
|
<AccountProvider>
|
||||||
<SettingsProvider>
|
<BitcoinInfoProvider>
|
||||||
<BitcoinInfoProvider>
|
<PriceProvider>
|
||||||
<PriceProvider>
|
<ChatProvider>
|
||||||
<ChatProvider>
|
<StatusProvider>{children}</StatusProvider>
|
||||||
<StatusProvider>{children}</StatusProvider>
|
</ChatProvider>
|
||||||
</ChatProvider>
|
</PriceProvider>
|
||||||
</PriceProvider>
|
</BitcoinInfoProvider>
|
||||||
</BitcoinInfoProvider>
|
|
||||||
</SettingsProvider>
|
|
||||||
</AccountProvider>
|
</AccountProvider>
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,14 +6,17 @@ type PriceProps = {
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
loading: boolean;
|
dontShow: boolean;
|
||||||
error: boolean;
|
prices?: { [key: string]: PriceProps };
|
||||||
|
};
|
||||||
|
|
||||||
|
type ChangeState = {
|
||||||
prices?: { [key: string]: PriceProps };
|
prices?: { [key: string]: PriceProps };
|
||||||
};
|
};
|
||||||
|
|
||||||
type ActionType = {
|
type ActionType = {
|
||||||
type: 'fetched' | 'error';
|
type: 'fetched' | 'dontShow';
|
||||||
state?: State;
|
state?: ChangeState;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Dispatch = (action: ActionType) => void;
|
type Dispatch = (action: ActionType) => void;
|
||||||
|
@ -22,23 +25,18 @@ export const StateContext = createContext<State | undefined>(undefined);
|
||||||
export const DispatchContext = createContext<Dispatch | undefined>(undefined);
|
export const DispatchContext = createContext<Dispatch | undefined>(undefined);
|
||||||
|
|
||||||
const initialState: State = {
|
const initialState: State = {
|
||||||
loading: true,
|
dontShow: true,
|
||||||
error: false,
|
|
||||||
prices: { EUR: { last: 0, symbol: '€' } },
|
prices: { EUR: { last: 0, symbol: '€' } },
|
||||||
};
|
};
|
||||||
|
|
||||||
const stateReducer = (state: State, action: ActionType): State => {
|
const stateReducer = (state: State, action: ActionType): State => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
case 'dontShow':
|
||||||
|
return { ...initialState, dontShow: true };
|
||||||
case 'fetched':
|
case 'fetched':
|
||||||
return action.state || initialState;
|
return { ...initialState, ...action.state, dontShow: false };
|
||||||
case 'error':
|
|
||||||
return {
|
|
||||||
...initialState,
|
|
||||||
loading: false,
|
|
||||||
error: true,
|
|
||||||
};
|
|
||||||
default:
|
default:
|
||||||
return initialState;
|
return state;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,93 +0,0 @@
|
||||||
/* eslint-disable @typescript-eslint/no-use-before-define */
|
|
||||||
import React, { createContext, useState, useContext, useEffect } from 'react';
|
|
||||||
import merge from 'lodash.merge';
|
|
||||||
|
|
||||||
interface ChangeProps {
|
|
||||||
theme?: string;
|
|
||||||
sidebar?: boolean;
|
|
||||||
currency?: string;
|
|
||||||
nodeInfo?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SettingsProps {
|
|
||||||
currency: string;
|
|
||||||
theme: string;
|
|
||||||
sidebar: boolean;
|
|
||||||
nodeInfo: boolean;
|
|
||||||
setSettings: (newProps: ChangeProps) => void;
|
|
||||||
refreshSettings: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SettingsContext = createContext<SettingsProps>({
|
|
||||||
currency: '',
|
|
||||||
theme: '',
|
|
||||||
sidebar: true,
|
|
||||||
nodeInfo: false,
|
|
||||||
setSettings: () => ({}),
|
|
||||||
refreshSettings: () => ({}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const SettingsProvider = ({ children }: any) => {
|
|
||||||
// const savedTheme = localStorage.getItem('theme') || 'light';
|
|
||||||
// const savedSidebar = localStorage.getItem('sidebar') === 'false' ? false : true;
|
|
||||||
// const savedCurrency = localStorage.getItem('currency') || 'sat';
|
|
||||||
// const savedNodeInfo = localStorage.getItem('nodeInfo') === 'true' ? true : false;
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
refreshSettings();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const refreshSettings = (account?: string) => {
|
|
||||||
const savedTheme = localStorage.getItem('theme') || 'light';
|
|
||||||
const savedSidebar =
|
|
||||||
localStorage.getItem('sidebar') === 'false' ? false : true;
|
|
||||||
const savedCurrency = localStorage.getItem('currency') || 'sat';
|
|
||||||
const savedNodeInfo =
|
|
||||||
localStorage.getItem('nodeInfo') === 'true' ? true : false;
|
|
||||||
|
|
||||||
updateSettings((prevState: any) => {
|
|
||||||
const newState = { ...prevState };
|
|
||||||
return merge(newState, {
|
|
||||||
currency: savedCurrency,
|
|
||||||
theme: savedTheme,
|
|
||||||
sidebar: savedSidebar,
|
|
||||||
nodeInfo: savedNodeInfo,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const setSettings = ({ currency, theme, sidebar }: ChangeProps) => {
|
|
||||||
updateSettings((prevState: any) => {
|
|
||||||
const newState = { ...prevState };
|
|
||||||
return merge(newState, {
|
|
||||||
currency,
|
|
||||||
theme,
|
|
||||||
sidebar,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const settingsState = {
|
|
||||||
prices: { EUR: { last: 0, symbol: '€' } },
|
|
||||||
price: 0,
|
|
||||||
symbol: '',
|
|
||||||
currency: 'sat',
|
|
||||||
theme: 'dark',
|
|
||||||
sidebar: true,
|
|
||||||
nodeInfo: false,
|
|
||||||
setSettings,
|
|
||||||
refreshSettings,
|
|
||||||
};
|
|
||||||
|
|
||||||
const [settings, updateSettings] = useState(settingsState);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SettingsContext.Provider value={settings}>
|
|
||||||
{children}
|
|
||||||
</SettingsContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const useSettings = () => useContext(SettingsContext);
|
|
||||||
|
|
||||||
export { SettingsProvider, useSettings };
|
|
|
@ -24,7 +24,7 @@ import {
|
||||||
CreditCard,
|
CreditCard,
|
||||||
MessageCircle,
|
MessageCircle,
|
||||||
} from 'react-feather';
|
} from 'react-feather';
|
||||||
import { useSettings } from '../../context/SettingsContext';
|
import { useConfigState } from '../../context/ConfigContext';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { Link } from '../../components/link/Link';
|
import { Link } from '../../components/link/Link';
|
||||||
import { useStatusState } from '../../context/StatusContext';
|
import { useStatusState } from '../../context/StatusContext';
|
||||||
|
@ -131,7 +131,7 @@ interface NavigationProps {
|
||||||
|
|
||||||
export const Navigation = ({ isBurger, setOpen }: NavigationProps) => {
|
export const Navigation = ({ isBurger, setOpen }: NavigationProps) => {
|
||||||
const { pathname } = useRouter();
|
const { pathname } = useRouter();
|
||||||
const { sidebar, setSettings } = useSettings();
|
const { sidebar } = useConfigState();
|
||||||
const { connected } = useStatusState();
|
const { connected } = useStatusState();
|
||||||
|
|
||||||
const renderNavButton = (
|
const renderNavButton = (
|
||||||
|
@ -204,7 +204,7 @@ export const Navigation = ({ isBurger, setOpen }: NavigationProps) => {
|
||||||
<LinkView>
|
<LinkView>
|
||||||
{connected && <NodeInfo isOpen={sidebar} />}
|
{connected && <NodeInfo isOpen={sidebar} />}
|
||||||
{renderLinks()}
|
{renderLinks()}
|
||||||
<SideSettings isOpen={sidebar} setIsOpen={setSettings} />
|
<SideSettings />
|
||||||
</LinkView>
|
</LinkView>
|
||||||
</StickyCard>
|
</StickyCard>
|
||||||
</NavigationStyle>
|
</NavigationStyle>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSettings } from '../../../context/SettingsContext';
|
import { useConfigState } from '../../../context/ConfigContext';
|
||||||
import {
|
import {
|
||||||
Separation,
|
Separation,
|
||||||
SingleLine,
|
SingleLine,
|
||||||
|
@ -84,9 +84,9 @@ export const NodeInfo = ({ isOpen, isBurger }: NodeInfoProps) => {
|
||||||
onError: error => toast.error(getErrorContent(error)),
|
onError: error => toast.error(getErrorContent(error)),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { theme, currency } = useSettings();
|
const { theme, currency, displayValues } = useConfigState();
|
||||||
const priceContext = usePriceState();
|
const priceContext = usePriceState();
|
||||||
const format = getPrice(currency, priceContext);
|
const format = getPrice(currency, displayValues, priceContext);
|
||||||
|
|
||||||
const tooltipType: any = getTooltipType(theme);
|
const tooltipType: any = getTooltipType(theme);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Separation, SingleLine } from '../../../components/generic/Styled';
|
import { Separation, SingleLine } from '../../../components/generic/Styled';
|
||||||
import { useSettings } from '../../../context/SettingsContext';
|
import {
|
||||||
|
useConfigState,
|
||||||
|
useConfigDispatch,
|
||||||
|
} from '../../../context/ConfigContext';
|
||||||
import { Sun, Moon, ChevronLeft, ChevronRight } from 'react-feather';
|
import { Sun, Moon, ChevronLeft, ChevronRight } from 'react-feather';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import {
|
import {
|
||||||
|
@ -9,6 +12,7 @@ import {
|
||||||
inverseTextColor,
|
inverseTextColor,
|
||||||
unSelectedNavButton,
|
unSelectedNavButton,
|
||||||
} from '../../../styles/Themes';
|
} from '../../../styles/Themes';
|
||||||
|
import { usePriceState } from '../../../context/PriceContext';
|
||||||
|
|
||||||
const SelectedIcon = styled.div<{ selected: boolean }>`
|
const SelectedIcon = styled.div<{ selected: boolean }>`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -48,13 +52,20 @@ const BurgerPadding = styled(SingleLine)`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const currencyArray = ['sat', 'btc', 'EUR', 'USD'];
|
const currencyArray = ['sat', 'btc', 'EUR', 'USD'];
|
||||||
|
const currencyNoFiatArray = ['sat', 'btc'];
|
||||||
|
|
||||||
const themeArray = ['light', 'dark'];
|
const themeArray = ['light', 'dark'];
|
||||||
|
|
||||||
const currencyMap: { [key: string]: string } = {
|
const currencyMap: { [key: string]: string } = {
|
||||||
sat: 'S',
|
sat: 'S',
|
||||||
btc: '₿',
|
btc: '₿',
|
||||||
EUR: '€',
|
EUR: '€',
|
||||||
USD: '$',
|
USD: '$',
|
||||||
};
|
};
|
||||||
|
const currencyNoFiatMap: { [key: string]: string } = {
|
||||||
|
sat: 'S',
|
||||||
|
btc: '₿',
|
||||||
|
};
|
||||||
|
|
||||||
const getNextValue = (array: string[], current: string): string => {
|
const getNextValue = (array: string[], current: string): string => {
|
||||||
const length = array.length;
|
const length = array.length;
|
||||||
|
@ -71,17 +82,16 @@ const getNextValue = (array: string[], current: string): string => {
|
||||||
};
|
};
|
||||||
|
|
||||||
interface SideSettingsProps {
|
interface SideSettingsProps {
|
||||||
isOpen?: boolean;
|
|
||||||
isBurger?: boolean;
|
isBurger?: boolean;
|
||||||
setIsOpen?: (state: any) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SideSettings = ({
|
export const SideSettings = ({ isBurger }: SideSettingsProps) => {
|
||||||
isOpen,
|
const { dontShow } = usePriceState();
|
||||||
isBurger,
|
const { theme, currency, sidebar } = useConfigState();
|
||||||
setIsOpen,
|
const dispatch = useConfigDispatch();
|
||||||
}: SideSettingsProps) => {
|
|
||||||
const { theme, currency, setSettings } = useSettings();
|
const correctMap = dontShow ? currencyNoFiatMap : currencyMap;
|
||||||
|
const correctArray = dontShow ? currencyNoFiatArray : currencyArray;
|
||||||
|
|
||||||
const renderIcon = (
|
const renderIcon = (
|
||||||
type: string,
|
type: string,
|
||||||
|
@ -97,11 +107,12 @@ export const SideSettings = ({
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
localStorage.setItem(type, value);
|
localStorage.setItem(type, value);
|
||||||
type === 'currency' &&
|
type === 'currency' &&
|
||||||
setSettings({
|
dispatch({
|
||||||
|
type: 'change',
|
||||||
currency:
|
currency:
|
||||||
isOpen || isBurger ? value : getNextValue(currencyArray, value),
|
sidebar || isBurger ? value : getNextValue(correctArray, value),
|
||||||
});
|
});
|
||||||
type === 'theme' && setSettings({ theme: value });
|
type === 'theme' && dispatch({ type: 'change', theme: value });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{type === 'currency' && <Symbol>{text}</Symbol>}
|
{type === 'currency' && <Symbol>{text}</Symbol>}
|
||||||
|
@ -110,12 +121,12 @@ export const SideSettings = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderContent = () => {
|
const renderContent = () => {
|
||||||
if (!isOpen) {
|
if (!sidebar) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Separation lineColor={unSelectedNavButton} />
|
<Separation lineColor={unSelectedNavButton} />
|
||||||
<IconRow center={true}>
|
<IconRow center={true}>
|
||||||
{renderIcon('currency', currency, currencyMap[currency], true)}
|
{renderIcon('currency', currency, correctMap[currency], true)}
|
||||||
</IconRow>
|
</IconRow>
|
||||||
<IconRow center={true}>
|
<IconRow center={true}>
|
||||||
{renderIcon(
|
{renderIcon(
|
||||||
|
@ -135,8 +146,8 @@ export const SideSettings = ({
|
||||||
<IconRow>
|
<IconRow>
|
||||||
{renderIcon('currency', 'sat', 'S')}
|
{renderIcon('currency', 'sat', 'S')}
|
||||||
{renderIcon('currency', 'btc', '₿')}
|
{renderIcon('currency', 'btc', '₿')}
|
||||||
{renderIcon('currency', 'EUR', '€')}
|
{!dontShow && renderIcon('currency', 'EUR', '€')}
|
||||||
{renderIcon('currency', 'USD', '$')}
|
{!dontShow && renderIcon('currency', 'USD', '$')}
|
||||||
</IconRow>
|
</IconRow>
|
||||||
<IconRow>
|
<IconRow>
|
||||||
{renderIcon('theme', 'light', '', false, Sun)}
|
{renderIcon('theme', 'light', '', false, Sun)}
|
||||||
|
@ -152,8 +163,8 @@ export const SideSettings = ({
|
||||||
<IconRow>
|
<IconRow>
|
||||||
{renderIcon('currency', 'sat', 'S')}
|
{renderIcon('currency', 'sat', 'S')}
|
||||||
{renderIcon('currency', 'btc', '₿')}
|
{renderIcon('currency', 'btc', '₿')}
|
||||||
{renderIcon('currency', 'EUR', '€')}
|
{!dontShow && renderIcon('currency', 'EUR', '€')}
|
||||||
{renderIcon('currency', 'USD', '$')}
|
{!dontShow && renderIcon('currency', 'USD', '$')}
|
||||||
</IconRow>
|
</IconRow>
|
||||||
<IconRow>
|
<IconRow>
|
||||||
{renderIcon('theme', 'light', '', false, Sun)}
|
{renderIcon('theme', 'light', '', false, Sun)}
|
||||||
|
@ -166,19 +177,17 @@ export const SideSettings = ({
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{renderContent()}
|
{renderContent()}
|
||||||
{setIsOpen && (
|
<IconRow center={!sidebar}>
|
||||||
<IconRow center={!isOpen}>
|
<SelectedIcon
|
||||||
<SelectedIcon
|
selected={true}
|
||||||
selected={true}
|
onClick={() => {
|
||||||
onClick={() => {
|
localStorage.setItem('sidebar', (!sidebar).toString());
|
||||||
localStorage.setItem('sidebar', (!isOpen).toString());
|
dispatch({ type: 'change', sidebar: !sidebar });
|
||||||
setIsOpen({ sidebar: !isOpen });
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{sidebar ? <ChevronLeft size={18} /> : <ChevronRight size={18} />}
|
||||||
{isOpen ? <ChevronLeft size={18} /> : <ChevronRight size={18} />}
|
</SelectedIcon>
|
||||||
</SelectedIcon>
|
</IconRow>
|
||||||
</IconRow>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
5
src/utils/cookies.ts
Normal file
5
src/utils/cookies.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import cookie from 'cookie';
|
||||||
|
|
||||||
|
export const parseCookies = req => {
|
||||||
|
return cookie.parse(req ? req.headers.cookie || '' : document.cookie);
|
||||||
|
};
|
|
@ -14,7 +14,7 @@ import {
|
||||||
} from '../../../components/generic/helpers';
|
} from '../../../components/generic/helpers';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { getPrice } from '../../../components/price/Price';
|
import { getPrice } from '../../../components/price/Price';
|
||||||
import { useSettings } from '../../../context/SettingsContext';
|
import { useConfigState } from '../../../context/ConfigContext';
|
||||||
import { usePriceState } from '../../../context/PriceContext';
|
import { usePriceState } from '../../../context/PriceContext';
|
||||||
|
|
||||||
const AddMargin = styled.div`
|
const AddMargin = styled.div`
|
||||||
|
@ -34,9 +34,9 @@ export const TransactionsCard = ({
|
||||||
setIndexOpen,
|
setIndexOpen,
|
||||||
indexOpen,
|
indexOpen,
|
||||||
}: TransactionsCardProps) => {
|
}: TransactionsCardProps) => {
|
||||||
const { currency } = useSettings();
|
const { currency, displayValues } = useConfigState();
|
||||||
const priceContext = usePriceState();
|
const priceContext = usePriceState();
|
||||||
const format = getPrice(currency, priceContext);
|
const format = getPrice(currency, displayValues, priceContext);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
block_id,
|
block_id,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Separation, SubCard } from '../../../components/generic/Styled';
|
||||||
import { MainInfo } from '../../../components/generic/CardGeneric';
|
import { MainInfo } from '../../../components/generic/CardGeneric';
|
||||||
import { renderLine } from '../../../components/generic/helpers';
|
import { renderLine } from '../../../components/generic/helpers';
|
||||||
import { getPrice } from '../../../components/price/Price';
|
import { getPrice } from '../../../components/price/Price';
|
||||||
import { useSettings } from '../../../context/SettingsContext';
|
import { useConfigState } from '../../../context/ConfigContext';
|
||||||
import { usePriceState } from '../../../context/PriceContext';
|
import { usePriceState } from '../../../context/PriceContext';
|
||||||
|
|
||||||
interface TransactionsCardProps {
|
interface TransactionsCardProps {
|
||||||
|
@ -19,9 +19,9 @@ export const UtxoCard = ({
|
||||||
setIndexOpen,
|
setIndexOpen,
|
||||||
indexOpen,
|
indexOpen,
|
||||||
}: TransactionsCardProps) => {
|
}: TransactionsCardProps) => {
|
||||||
const { currency } = useSettings();
|
const { currency, displayValues } = useConfigState();
|
||||||
const priceContext = usePriceState();
|
const priceContext = usePriceState();
|
||||||
const format = getPrice(currency, priceContext);
|
const format = getPrice(currency, displayValues, priceContext);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
address,
|
address,
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
ResponsiveSingle,
|
ResponsiveSingle,
|
||||||
ResponsiveCol,
|
ResponsiveCol,
|
||||||
} from '../../../components/generic/Styled';
|
} from '../../../components/generic/Styled';
|
||||||
import { useSettings } from '../../../context/SettingsContext';
|
import { useConfigState } from '../../../context/ConfigContext';
|
||||||
import {
|
import {
|
||||||
getStatusDot,
|
getStatusDot,
|
||||||
getTooltipType,
|
getTooltipType,
|
||||||
|
@ -64,9 +64,9 @@ export const ChannelCard = ({
|
||||||
}: ChannelCardProps) => {
|
}: ChannelCardProps) => {
|
||||||
const [modalOpen, setModalOpen] = useState(false);
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
|
|
||||||
const { theme, currency } = useSettings();
|
const { theme, currency, displayValues } = useConfigState();
|
||||||
const priceContext = usePriceState();
|
const priceContext = usePriceState();
|
||||||
const format = getPrice(currency, priceContext);
|
const format = getPrice(currency, displayValues, priceContext);
|
||||||
|
|
||||||
const tooltipType: any = getTooltipType(theme);
|
const tooltipType: any = getTooltipType(theme);
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
ResponsiveSingle,
|
ResponsiveSingle,
|
||||||
ResponsiveCol,
|
ResponsiveCol,
|
||||||
} from '../../../components/generic/Styled';
|
} from '../../../components/generic/Styled';
|
||||||
import { useSettings } from '../../../context/SettingsContext';
|
import { useConfigState } from '../../../context/ConfigContext';
|
||||||
import {
|
import {
|
||||||
getStatusDot,
|
getStatusDot,
|
||||||
getTooltipType,
|
getTooltipType,
|
||||||
|
@ -66,9 +66,9 @@ export const PendingCard = ({
|
||||||
setIndexOpen,
|
setIndexOpen,
|
||||||
indexOpen,
|
indexOpen,
|
||||||
}: PendingCardProps) => {
|
}: PendingCardProps) => {
|
||||||
const { theme, currency } = useSettings();
|
const { theme, currency, displayValues } = useConfigState();
|
||||||
const priceContext = usePriceState();
|
const priceContext = usePriceState();
|
||||||
const format = getPrice(currency, priceContext);
|
const format = getPrice(currency, displayValues, priceContext);
|
||||||
|
|
||||||
const tooltipType: any = getTooltipType(theme);
|
const tooltipType: any = getTooltipType(theme);
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ import {
|
||||||
ChatBoxTopAlias,
|
ChatBoxTopAlias,
|
||||||
} from './Chat.styled';
|
} from './Chat.styled';
|
||||||
import { ChatBubble } from './ChatBubble';
|
import { ChatBubble } from './ChatBubble';
|
||||||
import { useChatState } from '../../context/ChatContext';
|
import { useConfigState } from '../../context/ConfigContext';
|
||||||
|
|
||||||
export const MessageCard = ({
|
export const MessageCard = ({
|
||||||
message,
|
message,
|
||||||
|
@ -28,7 +28,7 @@ export const MessageCard = ({
|
||||||
message: MessageType;
|
message: MessageType;
|
||||||
key?: string;
|
key?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const { hideFee, hideNonVerified } = useChatState();
|
const { hideFee, hideNonVerified } = useConfigState();
|
||||||
if (!message.message && message.contentType === 'text') {
|
if (!message.message && message.contentType === 'text') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { useChatState, useChatDispatch } from '../../context/ChatContext';
|
||||||
import { useAccount } from '../../context/AccountContext';
|
import { useAccount } from '../../context/AccountContext';
|
||||||
import { Circle } from 'react-feather';
|
import { Circle } from 'react-feather';
|
||||||
import ScaleLoader from 'react-spinners/ScaleLoader';
|
import ScaleLoader from 'react-spinners/ScaleLoader';
|
||||||
import { useSettings } from '../../context/SettingsContext';
|
import { useConfigState } from '../../context/ConfigContext';
|
||||||
import { usePriceState } from '../../context/PriceContext';
|
import { usePriceState } from '../../context/PriceContext';
|
||||||
import { getPrice } from '../../components/price/Price';
|
import { getPrice } from '../../components/price/Price';
|
||||||
|
|
||||||
|
@ -29,7 +29,8 @@ interface SendButtonProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const SendButton = ({ amount }: SendButtonProps) => {
|
const SendButton = ({ amount }: SendButtonProps) => {
|
||||||
const { sender, maxFee } = useChatState();
|
const { maxFee } = useConfigState();
|
||||||
|
const { sender } = useChatState();
|
||||||
const dispatch = useChatDispatch();
|
const dispatch = useChatDispatch();
|
||||||
const { id } = useAccount();
|
const { id } = useAccount();
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@ const SendButton = ({ amount }: SendButtonProps) => {
|
||||||
sender,
|
sender,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [loading, data]);
|
}, [loading, data, amount, dispatch, sender, id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SecureWrapper
|
<SecureWrapper
|
||||||
|
@ -80,9 +81,9 @@ interface ChatBubbleProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ChatBubble = ({ message }: ChatBubbleProps) => {
|
export const ChatBubble = ({ message }: ChatBubbleProps) => {
|
||||||
const { currency } = useSettings();
|
const { currency, displayValues } = useConfigState();
|
||||||
const priceContext = usePriceState();
|
const priceContext = usePriceState();
|
||||||
const format = getPrice(currency, priceContext);
|
const format = getPrice(currency, displayValues, priceContext);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
contentType,
|
contentType,
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { toast } from 'react-toastify';
|
||||||
import { getErrorContent } from '../../utils/error';
|
import { getErrorContent } from '../../utils/error';
|
||||||
import { useAccount } from '../../context/AccountContext';
|
import { useAccount } from '../../context/AccountContext';
|
||||||
import { handleMessage } from './helpers/chatHelpers';
|
import { handleMessage } from './helpers/chatHelpers';
|
||||||
|
import { useConfigState } from '../../context/ConfigContext';
|
||||||
|
|
||||||
export const ChatInput = ({
|
export const ChatInput = ({
|
||||||
alias,
|
alias,
|
||||||
|
@ -21,7 +22,8 @@ export const ChatInput = ({
|
||||||
const [message, setMessage] = React.useState('');
|
const [message, setMessage] = React.useState('');
|
||||||
const { id } = useAccount();
|
const { id } = useAccount();
|
||||||
|
|
||||||
const { sender, maxFee } = useChatState();
|
const { maxFee } = useConfigState();
|
||||||
|
const { sender } = useChatState();
|
||||||
const dispatch = useChatDispatch();
|
const dispatch = useChatDispatch();
|
||||||
|
|
||||||
const [sendMessage, { loading, data }] = useSendMessageMutation({
|
const [sendMessage, { loading, data }] = useSendMessageMutation({
|
||||||
|
@ -50,7 +52,17 @@ export const ChatInput = ({
|
||||||
sender: customSender || sender,
|
sender: customSender || sender,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [loading, data]);
|
}, [
|
||||||
|
loading,
|
||||||
|
data,
|
||||||
|
formattedMessage,
|
||||||
|
customSender,
|
||||||
|
sender,
|
||||||
|
contentType,
|
||||||
|
tokens,
|
||||||
|
id,
|
||||||
|
dispatch,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SingleLine>
|
<SingleLine>
|
||||||
|
|
|
@ -17,7 +17,7 @@ import { toast } from 'react-toastify';
|
||||||
import { getErrorContent } from '../../utils/error';
|
import { getErrorContent } from '../../utils/error';
|
||||||
import { ChevronRight } from 'react-feather';
|
import { ChevronRight } from 'react-feather';
|
||||||
import { SecureButton } from '../../components/buttons/secureButton/SecureButton';
|
import { SecureButton } from '../../components/buttons/secureButton/SecureButton';
|
||||||
import { useSettings } from '../../context/SettingsContext';
|
import { useConfigState } from '../../context/ConfigContext';
|
||||||
import { textColorMap } from '../../styles/Themes';
|
import { textColorMap } from '../../styles/Themes';
|
||||||
import { Input } from '../../components/input/Input';
|
import { Input } from '../../components/input/Input';
|
||||||
import { AdminSwitch } from '../../components/adminSwitch/AdminSwitch';
|
import { AdminSwitch } from '../../components/adminSwitch/AdminSwitch';
|
||||||
|
@ -39,7 +39,7 @@ export const FeeCard = ({
|
||||||
const [newBaseFee, setBaseFee] = useState(0);
|
const [newBaseFee, setBaseFee] = useState(0);
|
||||||
const [newFeeRate, setFeeRate] = useState(0);
|
const [newFeeRate, setFeeRate] = useState(0);
|
||||||
|
|
||||||
const { theme } = useSettings();
|
const { theme } = useConfigState();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
alias,
|
alias,
|
||||||
|
@ -59,7 +59,7 @@ export const FeeCard = ({
|
||||||
? toast.success('Channel fees updated')
|
? toast.success('Channel fees updated')
|
||||||
: toast.error('Error updating channel fees');
|
: toast.error('Error updating channel fees');
|
||||||
},
|
},
|
||||||
refetchQueries: ['GetChannelFees'],
|
refetchQueries: ['ChannelFees'],
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
|
|
|
@ -38,6 +38,7 @@ export const PayCard = ({ setOpen }: { setOpen: () => void }) => {
|
||||||
setModalType('none');
|
setModalType('none');
|
||||||
setOpen();
|
setOpen();
|
||||||
},
|
},
|
||||||
|
refetchQueries: ['GetInOut', 'GetNodeInfo', 'GetBalances'],
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
|
|
|
@ -19,7 +19,7 @@ import {
|
||||||
} from '../../../../components/buttons/multiButton/MultiButton';
|
} from '../../../../components/buttons/multiButton/MultiButton';
|
||||||
import { Price, getPrice } from '../../../../components/price/Price';
|
import { Price, getPrice } from '../../../../components/price/Price';
|
||||||
import { mediaWidths } from '../../../../styles/Themes';
|
import { mediaWidths } from '../../../../styles/Themes';
|
||||||
import { useSettings } from '../../../../context/SettingsContext';
|
import { useConfigState } from '../../../../context/ConfigContext';
|
||||||
import Modal from '../../../../components/modal/ReactModal';
|
import Modal from '../../../../components/modal/ReactModal';
|
||||||
import { ColorButton } from '../../../../components/buttons/colorButton/ColorButton';
|
import { ColorButton } from '../../../../components/buttons/colorButton/ColorButton';
|
||||||
import { renderLine } from '../../../../components/generic/helpers';
|
import { renderLine } from '../../../../components/generic/helpers';
|
||||||
|
@ -43,22 +43,21 @@ const Margin = styled.div`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const SendOnChainCard = ({ setOpen }: { setOpen: () => void }) => {
|
export const SendOnChainCard = ({ setOpen }: { setOpen: () => void }) => {
|
||||||
const { currency } = useSettings();
|
const { fast, halfHour, hour, dontShow } = useBitcoinState();
|
||||||
|
const { currency, displayValues } = useConfigState();
|
||||||
const priceContext = usePriceState();
|
const priceContext = usePriceState();
|
||||||
const format = getPrice(currency, priceContext);
|
const format = getPrice(currency, displayValues, priceContext);
|
||||||
|
|
||||||
const [modalOpen, setModalOpen] = useState(false);
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
|
|
||||||
const [address, setAddress] = useState('');
|
const [address, setAddress] = useState('');
|
||||||
const [tokens, setTokens] = useState(0);
|
const [tokens, setTokens] = useState(0);
|
||||||
const [type, setType] = useState('none');
|
const [type, setType] = useState(dontShow ? 'fee' : 'none');
|
||||||
const [amount, setAmount] = useState(0);
|
const [amount, setAmount] = useState(0);
|
||||||
const [sendAll, setSendAll] = useState(false);
|
const [sendAll, setSendAll] = useState(false);
|
||||||
|
|
||||||
const canSend = address !== '' && (sendAll || tokens > 0) && amount > 0;
|
const canSend = address !== '' && (sendAll || tokens > 0) && amount > 0;
|
||||||
|
|
||||||
const { fast, halfHour, hour } = useBitcoinState();
|
|
||||||
|
|
||||||
const [payAddress, { loading }] = usePayAddressMutation({
|
const [payAddress, { loading }] = usePayAddressMutation({
|
||||||
onError: error => toast.error(getErrorContent(error)),
|
onError: error => toast.error(getErrorContent(error)),
|
||||||
onCompleted: () => {
|
onCompleted: () => {
|
||||||
|
@ -143,14 +142,15 @@ export const SendOnChainCard = ({ setOpen }: { setOpen: () => void }) => {
|
||||||
<SingleLine>
|
<SingleLine>
|
||||||
<NoWrapTitle>Fee:</NoWrapTitle>
|
<NoWrapTitle>Fee:</NoWrapTitle>
|
||||||
<MultiButton margin={'8px 0 8px 16px'}>
|
<MultiButton margin={'8px 0 8px 16px'}>
|
||||||
{renderButton(
|
{!dontShow &&
|
||||||
() => {
|
renderButton(
|
||||||
setType('none');
|
() => {
|
||||||
setAmount(fast);
|
setType('none');
|
||||||
},
|
setAmount(fast);
|
||||||
'Auto',
|
},
|
||||||
type === 'none'
|
'Auto',
|
||||||
)}
|
type === 'none'
|
||||||
|
)}
|
||||||
{renderButton(
|
{renderButton(
|
||||||
() => {
|
() => {
|
||||||
setType('fee');
|
setType('fee');
|
||||||
|
|
|
@ -34,13 +34,12 @@ interface OpenChannelProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OpenChannelCard = ({ color, setOpenCard }: OpenChannelProps) => {
|
export const OpenChannelCard = ({ color, setOpenCard }: OpenChannelProps) => {
|
||||||
|
const { fast, halfHour, hour, dontShow } = useBitcoinState();
|
||||||
const [size, setSize] = useState(0);
|
const [size, setSize] = useState(0);
|
||||||
const [fee, setFee] = useState(0);
|
const [fee, setFee] = useState(0);
|
||||||
const [publicKey, setPublicKey] = useState('');
|
const [publicKey, setPublicKey] = useState('');
|
||||||
const [privateChannel, setPrivateChannel] = useState(false);
|
const [privateChannel, setPrivateChannel] = useState(false);
|
||||||
const [type, setType] = useState('none');
|
const [type, setType] = useState(dontShow ? 'fee' : 'none');
|
||||||
|
|
||||||
const { fast, halfHour, hour } = useBitcoinState();
|
|
||||||
|
|
||||||
const [openChannel] = useOpenChannelMutation({
|
const [openChannel] = useOpenChannelMutation({
|
||||||
onError: error => toast.error(getErrorContent(error)),
|
onError: error => toast.error(getErrorContent(error)),
|
||||||
|
@ -108,24 +107,26 @@ export const OpenChannelCard = ({ color, setOpenCard }: OpenChannelProps) => {
|
||||||
</MultiButton>
|
</MultiButton>
|
||||||
</SingleLine>
|
</SingleLine>
|
||||||
<Separation />
|
<Separation />
|
||||||
<SingleLine>
|
{!dontShow && (
|
||||||
<NoWrapTitle>Fee:</NoWrapTitle>
|
<SingleLine>
|
||||||
<MultiButton margin={'8px 0 8px 16px'}>
|
<NoWrapTitle>Fee:</NoWrapTitle>
|
||||||
{renderButton(
|
<MultiButton margin={'8px 0 8px 16px'}>
|
||||||
() => {
|
{renderButton(
|
||||||
setType('none');
|
() => {
|
||||||
setFee(fast);
|
setType('none');
|
||||||
},
|
setFee(fast);
|
||||||
'Auto',
|
},
|
||||||
type === 'none'
|
'Auto',
|
||||||
)}
|
type === 'none'
|
||||||
{renderButton(
|
)}
|
||||||
() => setType('fee'),
|
{renderButton(
|
||||||
'Fee (Sats/Byte)',
|
() => setType('fee'),
|
||||||
type === 'fee'
|
'Fee (Sats/Byte)',
|
||||||
)}
|
type === 'fee'
|
||||||
</MultiButton>
|
)}
|
||||||
</SingleLine>
|
</MultiButton>
|
||||||
|
</SingleLine>
|
||||||
|
)}
|
||||||
<SingleLine>
|
<SingleLine>
|
||||||
<ResponsiveWrap>
|
<ResponsiveWrap>
|
||||||
<NoWrapTitle>Fee Amount:</NoWrapTitle>
|
<NoWrapTitle>Fee Amount:</NoWrapTitle>
|
||||||
|
@ -141,7 +142,6 @@ export const OpenChannelCard = ({ color, setOpenCard }: OpenChannelProps) => {
|
||||||
type={'number'}
|
type={'number'}
|
||||||
onChange={e => setFee(Number(e.target.value))}
|
onChange={e => setFee(Number(e.target.value))}
|
||||||
/>
|
/>
|
||||||
// </MultiButton>
|
|
||||||
)}
|
)}
|
||||||
{type === 'none' && (
|
{type === 'none' && (
|
||||||
<MultiButton margin={'8px 0 8px 16px'}>
|
<MultiButton margin={'8px 0 8px 16px'}>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { DarkSubTitle } from '../../../../components/generic/Styled';
|
import { DarkSubTitle } from '../../../../components/generic/Styled';
|
||||||
import { useSettings } from '../../../../context/SettingsContext';
|
import { useConfigState } from '../../../../context/ConfigContext';
|
||||||
import { VictoryPie } from 'victory';
|
import { VictoryPie } from 'victory';
|
||||||
import { chartAxisColor } from '../../../../styles/Themes';
|
import { chartAxisColor } from '../../../../styles/Themes';
|
||||||
import { Row, Col, PieRow } from '.';
|
import { Row, Col, PieRow } from '.';
|
||||||
|
@ -13,9 +13,9 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FlowPie = ({ flowPie, isType }: Props) => {
|
export const FlowPie = ({ flowPie, isType }: Props) => {
|
||||||
const { theme, currency } = useSettings();
|
const { theme, currency, displayValues } = useConfigState();
|
||||||
const priceContext = usePriceState();
|
const priceContext = usePriceState();
|
||||||
const format = getPrice(currency, priceContext);
|
const format = getPrice(currency, displayValues, priceContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row>
|
<Row>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import numeral from 'numeral';
|
import numeral from 'numeral';
|
||||||
import { useSettings } from '../../../../context/SettingsContext';
|
import { useConfigState } from '../../../../context/ConfigContext';
|
||||||
import {
|
import {
|
||||||
VictoryBar,
|
VictoryBar,
|
||||||
VictoryChart,
|
VictoryChart,
|
||||||
|
@ -41,9 +41,9 @@ export const FlowReport = ({
|
||||||
parsedData2,
|
parsedData2,
|
||||||
}: // waterfall,
|
}: // waterfall,
|
||||||
Props) => {
|
Props) => {
|
||||||
const { theme, currency } = useSettings();
|
const { theme, currency, displayValues } = useConfigState();
|
||||||
const priceContext = usePriceState();
|
const priceContext = usePriceState();
|
||||||
const format = getPrice(currency, priceContext);
|
const format = getPrice(currency, displayValues, priceContext);
|
||||||
|
|
||||||
let domain = 24;
|
let domain = 24;
|
||||||
let barWidth = 3;
|
let barWidth = 3;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { DarkSubTitle } from '../../../../components/generic/Styled';
|
import { DarkSubTitle } from '../../../../components/generic/Styled';
|
||||||
import { useSettings } from '../../../../context/SettingsContext';
|
import { useConfigState } from '../../../../context/ConfigContext';
|
||||||
import { VictoryPie } from 'victory';
|
import { VictoryPie } from 'victory';
|
||||||
import { chartAxisColor } from '../../../../styles/Themes';
|
import { chartAxisColor } from '../../../../styles/Themes';
|
||||||
import { Row, Col, PieRow } from '.';
|
import { Row, Col, PieRow } from '.';
|
||||||
|
@ -10,7 +10,7 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const InvoicePie = ({ invoicePie }: Props) => {
|
export const InvoicePie = ({ invoicePie }: Props) => {
|
||||||
const { theme } = useSettings();
|
const { theme } = useConfigState();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row>
|
<Row>
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { GitCommit, ArrowDown, ArrowUp } from 'react-feather';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { LoadingCard } from '../../../../components/loading/LoadingCard';
|
import { LoadingCard } from '../../../../components/loading/LoadingCard';
|
||||||
import { getPrice } from '../../../../components/price/Price';
|
import { getPrice } from '../../../../components/price/Price';
|
||||||
import { useSettings } from '../../../../context/SettingsContext';
|
import { useConfigState } from '../../../../context/ConfigContext';
|
||||||
import { usePriceState } from '../../../../context/PriceContext';
|
import { usePriceState } from '../../../../context/PriceContext';
|
||||||
import { useGetForwardChannelsReportQuery } from '../../../../generated/graphql';
|
import { useGetForwardChannelsReportQuery } from '../../../../generated/graphql';
|
||||||
|
|
||||||
|
@ -64,9 +64,9 @@ interface Props {
|
||||||
export const ForwardChannelsReport = ({ isTime, isType, color }: Props) => {
|
export const ForwardChannelsReport = ({ isTime, isType, color }: Props) => {
|
||||||
const [type, setType] = useState('route');
|
const [type, setType] = useState('route');
|
||||||
|
|
||||||
const { currency } = useSettings();
|
const { currency, displayValues } = useConfigState();
|
||||||
const priceContext = usePriceState();
|
const priceContext = usePriceState();
|
||||||
const format = getPrice(currency, priceContext);
|
const format = getPrice(currency, displayValues, priceContext);
|
||||||
|
|
||||||
const { auth } = useAccount();
|
const { auth } = useAccount();
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Sub4Title } from '../../../../components/generic/Styled';
|
import { Sub4Title } from '../../../../components/generic/Styled';
|
||||||
import numeral from 'numeral';
|
import numeral from 'numeral';
|
||||||
import { useSettings } from '../../../../context/SettingsContext';
|
import { useConfigState } from '../../../../context/ConfigContext';
|
||||||
import { useAccount } from '../../../../context/AccountContext';
|
import { useAccount } from '../../../../context/AccountContext';
|
||||||
import {
|
import {
|
||||||
VictoryBar,
|
VictoryBar,
|
||||||
|
@ -34,9 +34,9 @@ const timeMap: { [key: string]: string } = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ForwardReport = ({ isTime, isType }: Props) => {
|
export const ForwardReport = ({ isTime, isType }: Props) => {
|
||||||
const { theme, currency } = useSettings();
|
const { theme, currency, displayValues } = useConfigState();
|
||||||
const priceContext = usePriceState();
|
const priceContext = usePriceState();
|
||||||
const format = getPrice(currency, priceContext);
|
const format = getPrice(currency, displayValues, priceContext);
|
||||||
|
|
||||||
const { auth } = useAccount();
|
const { auth } = useAccount();
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {
|
||||||
VictoryVoronoiContainer,
|
VictoryVoronoiContainer,
|
||||||
VictoryTooltip,
|
VictoryTooltip,
|
||||||
} from 'victory';
|
} from 'victory';
|
||||||
import { useSettings } from '../../../../context/SettingsContext';
|
import { useConfigState } from '../../../../context/ConfigContext';
|
||||||
import {
|
import {
|
||||||
chartGridColor,
|
chartGridColor,
|
||||||
chartAxisColor,
|
chartAxisColor,
|
||||||
|
@ -26,9 +26,9 @@ import { useGetLiquidReportQuery } from '../../../../generated/graphql';
|
||||||
export const LiquidReport = () => {
|
export const LiquidReport = () => {
|
||||||
const { auth } = useAccount();
|
const { auth } = useAccount();
|
||||||
|
|
||||||
const { theme, currency } = useSettings();
|
const { theme, currency, displayValues } = useConfigState();
|
||||||
const priceContext = usePriceState();
|
const priceContext = usePriceState();
|
||||||
const format = getPrice(currency, priceContext);
|
const format = getPrice(currency, displayValues, priceContext);
|
||||||
|
|
||||||
const { data, loading } = useGetLiquidReportQuery({
|
const { data, loading } = useGetLiquidReportQuery({
|
||||||
skip: !auth,
|
skip: !auth,
|
||||||
|
|
|
@ -24,7 +24,7 @@ import {
|
||||||
MainInfo,
|
MainInfo,
|
||||||
} from '../../components/generic/CardGeneric';
|
} from '../../components/generic/CardGeneric';
|
||||||
import { getPercent } from '../../utils/helpers';
|
import { getPercent } from '../../utils/helpers';
|
||||||
import { useSettings } from '../../context/SettingsContext';
|
import { useConfigState } from '../../context/ConfigContext';
|
||||||
import ReactTooltip from 'react-tooltip';
|
import ReactTooltip from 'react-tooltip';
|
||||||
import { usePriceState } from '../../context/PriceContext';
|
import { usePriceState } from '../../context/PriceContext';
|
||||||
import { getPrice } from '../../components/price/Price';
|
import { getPrice } from '../../components/price/Price';
|
||||||
|
@ -57,10 +57,10 @@ export const PeersCard = ({
|
||||||
}: PeerProps) => {
|
}: PeerProps) => {
|
||||||
const [modalOpen, setModalOpen] = useState(false);
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
|
|
||||||
const { theme, currency } = useSettings();
|
const { theme, currency, displayValues } = useConfigState();
|
||||||
const priceContext = usePriceState();
|
const priceContext = usePriceState();
|
||||||
|
|
||||||
const format = getPrice(currency, priceContext);
|
const format = getPrice(currency, displayValues, priceContext);
|
||||||
const tooltipType: any = getTooltipType(theme);
|
const tooltipType: any = getTooltipType(theme);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
|
@ -11,11 +11,16 @@ import {
|
||||||
MultiButton,
|
MultiButton,
|
||||||
SingleButton,
|
SingleButton,
|
||||||
} from '../../components/buttons/multiButton/MultiButton';
|
} from '../../components/buttons/multiButton/MultiButton';
|
||||||
import { useChatState, useChatDispatch } from '../../context/ChatContext';
|
import { useConfigState, useConfigDispatch } from '../../context/ConfigContext';
|
||||||
|
|
||||||
export const ChatSettings = () => {
|
export const ChatSettings = () => {
|
||||||
const { hideFee, hideNonVerified, maxFee } = useChatState();
|
const {
|
||||||
const dispatch = useChatDispatch();
|
hideFee,
|
||||||
|
hideNonVerified,
|
||||||
|
maxFee,
|
||||||
|
chatPollingSpeed: cps,
|
||||||
|
} = useConfigState();
|
||||||
|
const dispatch = useConfigDispatch();
|
||||||
|
|
||||||
const renderButton = (
|
const renderButton = (
|
||||||
title: string,
|
title: string,
|
||||||
|
@ -29,15 +34,19 @@ export const ChatSettings = () => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'fee':
|
case 'fee':
|
||||||
typeof value === 'boolean' &&
|
typeof value === 'boolean' &&
|
||||||
dispatch({ type: 'hideFee', hideFee: value });
|
dispatch({ type: 'change', hideFee: value });
|
||||||
break;
|
break;
|
||||||
case 'nonverified':
|
case 'nonverified':
|
||||||
typeof value === 'boolean' &&
|
typeof value === 'boolean' &&
|
||||||
dispatch({ type: 'hideNonVerified', hideNonVerified: value });
|
dispatch({ type: 'change', hideNonVerified: value });
|
||||||
|
break;
|
||||||
|
case 'pollingSpeed':
|
||||||
|
typeof value === 'number' &&
|
||||||
|
dispatch({ type: 'change', chatPollingSpeed: value });
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
typeof value === 'number' &&
|
typeof value === 'number' &&
|
||||||
dispatch({ type: 'changeFee', maxFee: value });
|
dispatch({ type: 'change', maxFee: value });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -74,6 +83,18 @@ export const ChatSettings = () => {
|
||||||
{renderButton('100', 'maxFee', maxFee === 100, 100)}
|
{renderButton('100', 'maxFee', maxFee === 100, 100)}
|
||||||
</MultiButton>
|
</MultiButton>
|
||||||
</SettingsLine>
|
</SettingsLine>
|
||||||
|
<SettingsLine>
|
||||||
|
<Sub4Title>{'Polling Speed:'}</Sub4Title>
|
||||||
|
<MultiButton>
|
||||||
|
{renderButton('1s', 'pollingSpeed', cps === 1000, 1000)}
|
||||||
|
{renderButton('5s', 'pollingSpeed', cps === 5000, 5000)}
|
||||||
|
{renderButton('10s', 'pollingSpeed', cps === 10000, 10000)}
|
||||||
|
{renderButton('1m', 'pollingSpeed', cps === 60000, 60000)}
|
||||||
|
{renderButton('10m', 'pollingSpeed', cps === 600000, 600000)}
|
||||||
|
{renderButton('30m', 'pollingSpeed', cps === 1800000, 1800000)}
|
||||||
|
{renderButton('None', 'pollingSpeed', cps === 0, 0)}
|
||||||
|
</MultiButton>
|
||||||
|
</SettingsLine>
|
||||||
</Card>
|
</Card>
|
||||||
</CardWithTitle>
|
</CardWithTitle>
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { useStatusDispatch } from '../../context/StatusContext';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { appendBasePath } from '../../utils/basePath';
|
import { appendBasePath } from '../../utils/basePath';
|
||||||
import { useChatDispatch } from '../../context/ChatContext';
|
import { useChatDispatch } from '../../context/ChatContext';
|
||||||
|
import Cookies from 'js-cookie';
|
||||||
|
|
||||||
export const ButtonRow = styled.div`
|
export const ButtonRow = styled.div`
|
||||||
width: auto;
|
width: auto;
|
||||||
|
@ -90,6 +91,7 @@ export const DangerView = () => {
|
||||||
chatDispatch({ type: 'disconnected' });
|
chatDispatch({ type: 'disconnected' });
|
||||||
deleteStorage();
|
deleteStorage();
|
||||||
refreshAccount();
|
refreshAccount();
|
||||||
|
Cookies.remove('config');
|
||||||
push(appendBasePath('/'));
|
push(appendBasePath('/'));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,22 +6,19 @@ import {
|
||||||
Sub4Title,
|
Sub4Title,
|
||||||
} from '../../components/generic/Styled';
|
} from '../../components/generic/Styled';
|
||||||
import { SettingsLine } from '../../../pages/settings';
|
import { SettingsLine } from '../../../pages/settings';
|
||||||
import { useSettings } from '../../context/SettingsContext';
|
import { useConfigState, useConfigDispatch } from '../../context/ConfigContext';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
MultiButton,
|
MultiButton,
|
||||||
SingleButton,
|
SingleButton,
|
||||||
} from '../../components/buttons/multiButton/MultiButton';
|
} from '../../components/buttons/multiButton/MultiButton';
|
||||||
import { useAccount } from '../../context/AccountContext';
|
import { useAccount } from '../../context/AccountContext';
|
||||||
|
import { usePriceState } from '../../context/PriceContext';
|
||||||
|
|
||||||
export const InterfaceSettings = () => {
|
export const InterfaceSettings = () => {
|
||||||
const {
|
const { dontShow } = usePriceState();
|
||||||
theme,
|
const { theme, currency, multiNodeInfo } = useConfigState();
|
||||||
currency,
|
const dispatch = useConfigDispatch();
|
||||||
nodeInfo,
|
|
||||||
setSettings,
|
|
||||||
refreshSettings,
|
|
||||||
} = useSettings();
|
|
||||||
|
|
||||||
const { accounts } = useAccount();
|
const { accounts } = useAccount();
|
||||||
|
|
||||||
|
@ -37,11 +34,13 @@ export const InterfaceSettings = () => {
|
||||||
selected={current === value}
|
selected={current === value}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
localStorage.setItem(type, value);
|
localStorage.setItem(type, value);
|
||||||
type === 'theme' && setSettings({ theme: value });
|
type === 'theme' && dispatch({ type: 'change', theme: value });
|
||||||
type === 'currency' && setSettings({ currency: value });
|
type === 'currency' && dispatch({ type: 'change', currency: value });
|
||||||
type === 'nodeInfo' &&
|
type === 'nodeInfo' &&
|
||||||
setSettings({ nodeInfo: value === 'true' ? true : false });
|
dispatch({
|
||||||
refreshSettings();
|
type: 'change',
|
||||||
|
multiNodeInfo: value === 'true' ? true : false,
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
|
@ -63,18 +62,18 @@ export const InterfaceSettings = () => {
|
||||||
<SettingsLine>
|
<SettingsLine>
|
||||||
<Sub4Title>Show all accounts on homepage:</Sub4Title>
|
<Sub4Title>Show all accounts on homepage:</Sub4Title>
|
||||||
<MultiButton>
|
<MultiButton>
|
||||||
{renderButton('Yes', 'true', 'nodeInfo', `${nodeInfo}`)}
|
{renderButton('Yes', 'true', 'nodeInfo', `${multiNodeInfo}`)}
|
||||||
{renderButton('No', 'false', 'nodeInfo', `${nodeInfo}`)}
|
{renderButton('No', 'false', 'nodeInfo', `${multiNodeInfo}`)}
|
||||||
</MultiButton>
|
</MultiButton>
|
||||||
</SettingsLine>
|
</SettingsLine>
|
||||||
)}
|
)}
|
||||||
<SettingsLine>
|
<SettingsLine>
|
||||||
<Sub4Title>Currency:</Sub4Title>
|
<Sub4Title>Currency:</Sub4Title>
|
||||||
<MultiButton margin={'0 0 0 16px'}>
|
<MultiButton margin={'0 0 0 16px'}>
|
||||||
{renderButton('Bitcoin', 'btc', 'currency', currency)}
|
|
||||||
{renderButton('Satoshis', 'sat', 'currency', currency)}
|
{renderButton('Satoshis', 'sat', 'currency', currency)}
|
||||||
{renderButton('Euro', 'EUR', 'currency', currency)}
|
{renderButton('Bitcoin', 'btc', 'currency', currency)}
|
||||||
{renderButton('US Dollar', 'USD', 'currency', currency)}
|
{!dontShow && renderButton('Euro', 'EUR', 'currency', currency)}
|
||||||
|
{!dontShow && renderButton('USD', 'USD', 'currency', currency)}
|
||||||
</MultiButton>
|
</MultiButton>
|
||||||
</SettingsLine>
|
</SettingsLine>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
65
src/views/settings/Privacy.tsx
Normal file
65
src/views/settings/Privacy.tsx
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
CardWithTitle,
|
||||||
|
SubTitle,
|
||||||
|
Card,
|
||||||
|
Sub4Title,
|
||||||
|
} from '../../components/generic/Styled';
|
||||||
|
import { SettingsLine } from '../../../pages/settings';
|
||||||
|
import { useConfigState, useConfigDispatch } from '../../context/ConfigContext';
|
||||||
|
|
||||||
|
import {
|
||||||
|
MultiButton,
|
||||||
|
SingleButton,
|
||||||
|
} from '../../components/buttons/multiButton/MultiButton';
|
||||||
|
|
||||||
|
export const PrivacySettings = () => {
|
||||||
|
const { fetchFees, fetchPrices, displayValues } = useConfigState();
|
||||||
|
const dispatch = useConfigDispatch();
|
||||||
|
|
||||||
|
const renderButton = (
|
||||||
|
title: string,
|
||||||
|
value: boolean,
|
||||||
|
type: string,
|
||||||
|
current: boolean
|
||||||
|
) => (
|
||||||
|
<SingleButton
|
||||||
|
selected={current === value}
|
||||||
|
onClick={() => {
|
||||||
|
localStorage.setItem(type, JSON.stringify(value));
|
||||||
|
dispatch({ type: 'change', [type]: value });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</SingleButton>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CardWithTitle>
|
||||||
|
<SubTitle>Privacy</SubTitle>
|
||||||
|
<Card>
|
||||||
|
<SettingsLine>
|
||||||
|
<Sub4Title>Fetch Bitcoin Fees:</Sub4Title>
|
||||||
|
<MultiButton>
|
||||||
|
{renderButton('On', true, 'fetchFees', fetchFees)}
|
||||||
|
{renderButton('Off', false, 'fetchFees', fetchFees)}
|
||||||
|
</MultiButton>
|
||||||
|
</SettingsLine>
|
||||||
|
<SettingsLine>
|
||||||
|
<Sub4Title>Fetch Fiat Prices:</Sub4Title>
|
||||||
|
<MultiButton margin={'0 0 0 16px'}>
|
||||||
|
{renderButton('On', true, 'fetchPrices', fetchPrices)}
|
||||||
|
{renderButton('Off', false, 'fetchPrices', fetchPrices)}
|
||||||
|
</MultiButton>
|
||||||
|
</SettingsLine>
|
||||||
|
<SettingsLine>
|
||||||
|
<Sub4Title>Values:</Sub4Title>
|
||||||
|
<MultiButton margin={'0 0 0 16px'}>
|
||||||
|
{renderButton('Show', true, 'displayValues', displayValues)}
|
||||||
|
{renderButton('Hide', false, 'displayValues', displayValues)}
|
||||||
|
</MultiButton>
|
||||||
|
</SettingsLine>
|
||||||
|
</Card>
|
||||||
|
</CardWithTitle>
|
||||||
|
);
|
||||||
|
};
|
|
@ -16,5 +16,5 @@
|
||||||
"downlevelIteration": true
|
"downlevelIteration": true
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", ".next"],
|
"exclude": ["node_modules", ".next"],
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "next.config.js"]
|
||||||
}
|
}
|
||||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -6106,6 +6106,11 @@ cookie@0.4.0:
|
||||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
|
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
|
||||||
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
|
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
|
||||||
|
|
||||||
|
cookie@^0.4.1:
|
||||||
|
version "0.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1"
|
||||||
|
integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==
|
||||||
|
|
||||||
copy-concurrently@^1.0.0:
|
copy-concurrently@^1.0.0:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0"
|
resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0"
|
||||||
|
@ -10327,6 +10332,11 @@ jest@^25.5.4:
|
||||||
import-local "^3.0.2"
|
import-local "^3.0.2"
|
||||||
jest-cli "^25.5.4"
|
jest-cli "^25.5.4"
|
||||||
|
|
||||||
|
js-cookie@^2.2.1:
|
||||||
|
version "2.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8"
|
||||||
|
integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==
|
||||||
|
|
||||||
js-levenshtein@^1.1.3:
|
js-levenshtein@^1.1.3:
|
||||||
version "1.1.6"
|
version "1.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
|
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
|
||||||
|
|
Loading…
Add table
Reference in a new issue