mirror of
https://github.com/apotdevin/thunderhub.git
synced 2025-02-20 13:34:30 +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: {
|
||||
ecmaFeatures: { jsx: true },
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
amd: true,
|
||||
node: true,
|
||||
},
|
||||
plugins: ['react', 'jest', 'import', 'prettier'],
|
||||
settings: {
|
||||
react: {
|
||||
|
@ -37,7 +42,12 @@ module.exports = {
|
|||
'import/no-unresolved': 'off',
|
||||
camelcase: 'off',
|
||||
'@typescript-eslint/camelcase': 'off',
|
||||
'prettier/prettier': 'error',
|
||||
'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.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
}
|
||||
}
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
|
|
127
README.md
127
README.md
|
@ -1,22 +1,32 @@
|
|||
# **ThunderHub - Lightning Node Manager**
|
||||
|
||||

|
||||

|
||||
[](https://github.com/DAVFoundation/captain-n3m0/blob/master/LICENSE)
|
||||
|
||||
## Table Of Contents
|
||||
|
||||
- [Introduction](#introduction)
|
||||
- [Integrations](#integrations)
|
||||
- [Features](#features)
|
||||
- [Installation](#installation)
|
||||
- [Development](#development)
|
||||
- [Docker deployment](#docker)
|
||||
|
||||
## 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.
|
||||
|
||||
### 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
|
||||
|
||||
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
|
||||
- ReactJS
|
||||
|
@ -50,7 +60,7 @@ This repository consists of a **NextJS** server that handles both the backend **
|
|||
- Send and Receive Bitcoin payments.
|
||||
- Decode lightning payment requests.
|
||||
- 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.
|
||||
- Backup, verify and recover all your channels.
|
||||
- Sign and verify messages.
|
||||
|
@ -70,54 +80,135 @@ This repository consists of a **NextJS** server that handles both the backend **
|
|||
|
||||
### Deployment
|
||||
|
||||
- Docker images for easier deployment (WIP)
|
||||
- Docker images for easier deployment
|
||||
|
||||
### Future Features
|
||||
|
||||
- Channel health/recommendations view
|
||||
- Loop In and Out to provide liquidity or remove it from your channels.
|
||||
- Integration with HodlHodl
|
||||
- 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
|
||||
|
||||
To run ThunderHub you first need to clone this repository.
|
||||
|
||||
```javascript
|
||||
```js
|
||||
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
|
||||
- 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.
|
||||
After all the dependencies have finished installing, you can proceed to build and run the app with the following commands.
|
||||
|
||||
```javascript
|
||||
//Yarn
|
||||
yarn build
|
||||
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
|
||||
|
||||
If you want to develop on ThunderHub and want hot reloading when you do changes, use the following commands:
|
||||
|
||||
```javascript
|
||||
```js
|
||||
//Yarn
|
||||
yarn dev
|
||||
|
||||
//NPM
|
||||
npm run dev
|
||||
```
|
||||
|
||||
#### Storybook
|
||||
|
||||
You can also get storybook running for quicker component development.
|
||||
|
||||
```javascript
|
||||
```js
|
||||
//Yarn
|
||||
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();
|
||||
const withBundleAnalyzer = require('@next/bundle-analyzer')({
|
||||
enabled: process.env.ANALYZE === 'true',
|
||||
|
@ -17,6 +18,9 @@ module.exports = withBundleAnalyzer({
|
|||
apiBaseUrl: `${process.env.API_BASE_URL || ''}/api/v1`,
|
||||
basePath: process.env.BASE_PATH || '',
|
||||
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",
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_OPTIONS='--insecure-http-parser' next",
|
||||
"dev:compatible": "next",
|
||||
"build": "next build",
|
||||
"start": "cross-env NODE_OPTIONS='--insecure-http-parser' next start",
|
||||
"start:compatible": "next start",
|
||||
"lint": "eslint */**/*.{js,ts,tsx} --quiet --fix",
|
||||
"prettier": "prettier --write **/*.{ts,tsx,js,css,html}",
|
||||
"release": "standard-version",
|
||||
|
@ -33,6 +35,7 @@
|
|||
"apollo-boost": "^0.4.7",
|
||||
"apollo-server-micro": "^2.12.0",
|
||||
"base64url": "^3.0.1",
|
||||
"cookie": "^0.4.1",
|
||||
"crypto-js": "^4.0.0",
|
||||
"date-fns": "^2.12.0",
|
||||
"dotenv": "^8.2.0",
|
||||
|
@ -42,6 +45,7 @@
|
|||
"graphql-tag": "^2.10.3",
|
||||
"intersection-observer": "^0.10.0",
|
||||
"isomorphic-unfetch": "^3.0.0",
|
||||
"js-cookie": "^2.2.1",
|
||||
"ln-service": "^48.0.5",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.groupby": "^4.6.0",
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import App from 'next/app';
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
import { ContextProvider } from '../src/context/ContextProvider';
|
||||
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 { GlobalStyles } from '../src/styles/GlobalStyle';
|
||||
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 { ChatFetcher } from '../src/components/chat/ChatFetcher';
|
||||
import { ChatInit } from '../src/components/chat/ChatInit';
|
||||
import { parseCookies } from '../src/utils/cookies';
|
||||
|
||||
toast.configure({ draggable: false, pauseOnFocusLoss: false });
|
||||
|
||||
const Wrapper: React.FC = ({ children }) => {
|
||||
const { theme } = useSettings();
|
||||
const { theme } = useConfigState();
|
||||
const { pathname } = useRouter();
|
||||
const { connected } = useStatusState();
|
||||
|
||||
|
@ -63,24 +63,36 @@ const Wrapper: React.FC = ({ children }) => {
|
|||
);
|
||||
};
|
||||
|
||||
class MyApp extends App<any> {
|
||||
render() {
|
||||
const { Component, pageProps, apollo } = this.props;
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>ThunderHub - Lightning Node Manager</title>
|
||||
</Head>
|
||||
<ApolloProvider client={apollo}>
|
||||
<ContextProvider>
|
||||
<Wrapper>
|
||||
<Component {...pageProps} />
|
||||
</Wrapper>
|
||||
</ContextProvider>
|
||||
</ApolloProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
const App = ({ Component, pageProps, apollo, initialConfig }: any) => (
|
||||
<>
|
||||
<Head>
|
||||
<title>ThunderHub - Lightning Node Manager</title>
|
||||
</Head>
|
||||
<ApolloProvider client={apollo}>
|
||||
<ConfigProvider initialConfig={initialConfig}>
|
||||
<ContextProvider>
|
||||
<Wrapper>
|
||||
<Component {...pageProps} />
|
||||
</Wrapper>
|
||||
</ContextProvider>
|
||||
</ConfigProvider>
|
||||
</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,
|
||||
} from '../src/components/generic/Styled';
|
||||
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 { useGetChannelAmountInfoQuery } from '../src/generated/graphql';
|
||||
|
||||
|
@ -22,7 +22,7 @@ const ChannelView = () => {
|
|||
closed: 0,
|
||||
});
|
||||
|
||||
const { theme } = useSettings();
|
||||
const { theme } = useConfigState();
|
||||
const { auth } = useAccount();
|
||||
|
||||
const { data } = useGetChannelAmountInfoQuery({
|
||||
|
|
|
@ -48,7 +48,7 @@ const FeesView = () => {
|
|||
? toast.success('Fees Updated')
|
||||
: toast.error('Error updating fees');
|
||||
},
|
||||
refetchQueries: ['GetChannelFees'],
|
||||
refetchQueries: ['ChannelFees'],
|
||||
});
|
||||
|
||||
if (loading || !data || !data.getChannelFees) {
|
||||
|
|
|
@ -13,7 +13,7 @@ import { getErrorContent } from '../src/utils/error';
|
|||
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
||||
import { ForwardCard } from '../src/views/forwards/ForwardsCard';
|
||||
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 { useGetForwardsQuery } from '../src/generated/graphql';
|
||||
|
||||
|
@ -28,7 +28,7 @@ const ForwardsView = () => {
|
|||
const [time, setTime] = useState('week');
|
||||
const [indexOpen, setIndexOpen] = useState(0);
|
||||
|
||||
const { theme } = useSettings();
|
||||
const { theme } = useConfigState();
|
||||
const { auth } = useAccount();
|
||||
|
||||
const { loading, data } = useGetForwardsQuery({
|
||||
|
|
|
@ -8,6 +8,7 @@ import { DangerView } from '../src/views/settings/Danger';
|
|||
import { CurrentSettings } from '../src/views/settings/Current';
|
||||
import { SyncSettings } from '../src/views/settings/Sync';
|
||||
import { ChatSettings } from '../src/views/settings/Chat';
|
||||
import { PrivacySettings } from '../src/views/settings/Privacy';
|
||||
|
||||
export const ButtonRow = styled.div`
|
||||
width: auto;
|
||||
|
@ -30,6 +31,7 @@ const SettingsView = () => {
|
|||
return (
|
||||
<>
|
||||
<InterfaceSettings />
|
||||
<PrivacySettings />
|
||||
<ChatSettings />
|
||||
<SyncSettings />
|
||||
<CurrentSettings />
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { useSpring, animated } from 'react-spring';
|
||||
import { getValue } from '../../utils/helpers';
|
||||
import { useSettings } from '../../context/SettingsContext';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
import { usePriceState } from '../../context/PriceContext';
|
||||
|
||||
type PriceProps = {
|
||||
|
@ -19,8 +19,12 @@ export const AnimatedNumber = ({ amount = 0 }: AnimatedProps) => {
|
|||
from: { value: 0 },
|
||||
value: amount,
|
||||
});
|
||||
const { currency } = useSettings();
|
||||
const { prices } = usePriceState();
|
||||
const { currency, displayValues } = useConfigState();
|
||||
const { prices, dontShow } = usePriceState();
|
||||
|
||||
if (!displayValues) {
|
||||
return <>-</>;
|
||||
}
|
||||
|
||||
let priceProps: PriceProps = {
|
||||
price: 0,
|
||||
|
@ -28,7 +32,7 @@ export const AnimatedNumber = ({ amount = 0 }: AnimatedProps) => {
|
|||
currency: currency !== 'btc' && currency !== 'sat' ? 'sat' : currency,
|
||||
};
|
||||
|
||||
if (prices) {
|
||||
if (prices && !dontShow) {
|
||||
const current: { last: number; symbol: string } = prices[currency] ?? {
|
||||
last: 0,
|
||||
symbol: '',
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
import React from 'react';
|
||||
import { Checkbox } from '../../checkbox/Checkbox';
|
||||
import { CheckboxText, StyledContainer, FixedWidth } from '../Auth.styled';
|
||||
import { AlertCircle } from 'react-feather';
|
||||
import { fontColors } from '../../../styles/Themes';
|
||||
import { CheckboxText } from '../Auth.styled';
|
||||
import { ColorButton } from '../../buttons/colorButton/ColorButton';
|
||||
import getConfig from 'next/config';
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
const { trustNeeded } = publicRuntimeConfig;
|
||||
|
||||
type CheckboxProps = {
|
||||
handleClick: () => void;
|
||||
|
@ -37,24 +31,5 @@ export const RiskCheckboxAndConfirm = ({
|
|||
>
|
||||
Connect
|
||||
</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 { useBitcoinDispatch } from '../../context/BitcoinContext';
|
||||
import { useGetBitcoinFeesQuery } from '../../generated/graphql';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
|
||||
export const BitcoinFees = () => {
|
||||
const { fetchFees } = useConfigState();
|
||||
const setInfo = useBitcoinDispatch();
|
||||
|
||||
const { loading, data, stopPolling } = useGetBitcoinFeesQuery({
|
||||
skip: !fetchFees,
|
||||
fetchPolicy: 'network-only',
|
||||
onError: () => {
|
||||
setInfo({ type: 'error' });
|
||||
setInfo({ type: 'dontShow' });
|
||||
stopPolling();
|
||||
},
|
||||
pollInterval: 60000,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && data && data.getBitcoinFees) {
|
||||
const { fast, halfHour, hour } = data.getBitcoinFees;
|
||||
setInfo({
|
||||
type: 'fetched',
|
||||
state: { loading: false, error: false, fast, halfHour, hour },
|
||||
});
|
||||
if (!fetchFees) {
|
||||
setInfo({ type: 'dontShow' });
|
||||
}
|
||||
}, [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;
|
||||
};
|
||||
|
|
|
@ -1,29 +1,38 @@
|
|||
import { useEffect } from 'react';
|
||||
import { usePriceDispatch } from '../../context/PriceContext';
|
||||
import { useGetBitcoinPriceQuery } from '../../generated/graphql';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
|
||||
export const BitcoinPrice = () => {
|
||||
const { fetchPrices } = useConfigState();
|
||||
const setPrices = usePriceDispatch();
|
||||
const { loading, data, stopPolling } = useGetBitcoinPriceQuery({
|
||||
skip: !fetchPrices,
|
||||
fetchPolicy: 'network-only',
|
||||
onError: () => setPrices({ type: 'error' }),
|
||||
onError: () => {
|
||||
setPrices({ type: 'dontShow' });
|
||||
stopPolling();
|
||||
},
|
||||
pollInterval: 60000,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && data && data.getBitcoinPrice) {
|
||||
if (!fetchPrices) {
|
||||
setPrices({ type: 'dontShow' });
|
||||
}
|
||||
}, [fetchPrices, setPrices]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && data && data.getBitcoinPrice && fetchPrices) {
|
||||
try {
|
||||
const prices = JSON.parse(data.getBitcoinPrice);
|
||||
setPrices({
|
||||
type: 'fetched',
|
||||
state: { loading: false, error: false, prices },
|
||||
});
|
||||
setPrices({ type: 'fetched', state: { prices } });
|
||||
} catch (error) {
|
||||
setPrices({ type: 'dontShow' });
|
||||
stopPolling();
|
||||
setPrices({ type: 'error' });
|
||||
}
|
||||
}
|
||||
}, [data, loading, setPrices, stopPolling]);
|
||||
}, [data, loading, setPrices, stopPolling, fetchPrices]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -5,10 +5,13 @@ import { useAccount } from '../../context/AccountContext';
|
|||
import { toast } from 'react-toastify';
|
||||
import { getErrorContent } from '../../utils/error';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
|
||||
export const ChatFetcher = () => {
|
||||
const newChatToastId = 'newChatToastId';
|
||||
|
||||
const { chatPollingSpeed } = useConfigState();
|
||||
|
||||
const { auth } = useAccount();
|
||||
const { pathname } = useRouter();
|
||||
const { lastChat, chats, sentChats, initialized } = useChatState();
|
||||
|
@ -18,7 +21,7 @@ export const ChatFetcher = () => {
|
|||
|
||||
const { data, loading, error } = useGetMessagesQuery({
|
||||
skip: !auth || initialized || noChatsAvailable,
|
||||
pollInterval: 1000,
|
||||
pollInterval: chatPollingSpeed,
|
||||
fetchPolicy: 'network-only',
|
||||
variables: { auth, initialize: !noChatsAvailable },
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
|
@ -58,7 +61,7 @@ export const ChatFetcher = () => {
|
|||
const last = newMessages[0]?.id;
|
||||
dispatch({ type: 'additional', chats: newMessages, lastChat: last });
|
||||
}
|
||||
}, [data, loading, error]);
|
||||
}, [data, loading, error, dispatch, lastChat, pathname]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -19,10 +19,6 @@ export const ChatInit = () => {
|
|||
|
||||
React.useEffect(() => {
|
||||
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 !== '') {
|
||||
try {
|
||||
|
@ -33,9 +29,6 @@ export const ChatInit = () => {
|
|||
type: 'initialized',
|
||||
sentChats: savedChats,
|
||||
sender,
|
||||
hideFee,
|
||||
hideNonVerified,
|
||||
maxFee,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
|
@ -43,7 +36,7 @@ export const ChatInit = () => {
|
|||
}
|
||||
}
|
||||
getMessages();
|
||||
}, []);
|
||||
}, [dispatch, getMessages, id]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!initLoading && !initError && initData?.getMessages) {
|
||||
|
@ -64,7 +57,7 @@ export const ChatInit = () => {
|
|||
sender,
|
||||
});
|
||||
}
|
||||
}, [initLoading, initError, initData]);
|
||||
}, [initLoading, initError, initData, dispatch]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -41,13 +41,13 @@ export const CloseChannel = ({
|
|||
channelId,
|
||||
channelName,
|
||||
}: CloseChannelProps) => {
|
||||
const { fast, halfHour, hour, dontShow } = useBitcoinState();
|
||||
|
||||
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 [isConfirmed, setIsConfirmed] = useState<boolean>(false);
|
||||
|
||||
const { fast, halfHour, hour } = useBitcoinState();
|
||||
|
||||
const [closeChannel] = useCloseChannelMutation({
|
||||
onCompleted: data => {
|
||||
if (data.closeChannel) {
|
||||
|
@ -113,7 +113,8 @@ export const CloseChannel = ({
|
|||
<Sub4Title>Fee:</Sub4Title>
|
||||
</SingleLine>
|
||||
<MultiButton>
|
||||
{renderButton(() => setIsType('none'), 'Auto', isType === 'none')}
|
||||
{!dontShow &&
|
||||
renderButton(() => setIsType('none'), 'Auto', isType === 'none')}
|
||||
{renderButton(() => setIsType('fee'), 'Fee', isType === 'fee')}
|
||||
{renderButton(() => setIsType('target'), 'Target', isType === 'target')}
|
||||
</MultiButton>
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
import { HelpCircle } from 'react-feather';
|
||||
import styled from 'styled-components';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
import { useSettings } from '../../context/SettingsContext';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
import { getTooltipType } from '../generic/helpers';
|
||||
|
||||
const StyledQuestion = styled(HelpCircle)`
|
||||
|
@ -20,10 +20,9 @@ const StyledQuestion = styled(HelpCircle)`
|
|||
|
||||
export const NodeBar = () => {
|
||||
const { accounts } = useAccount();
|
||||
const { nodeInfo } = useSettings();
|
||||
const { multiNodeInfo, theme } = useConfigState();
|
||||
const slider = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
const { theme } = useSettings();
|
||||
const tooltipType: any = getTooltipType(theme);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { useSettings } from '../../context/SettingsContext';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
import { getValue } from '../../utils/helpers';
|
||||
import { usePriceState } from '../../context/PriceContext';
|
||||
|
||||
|
@ -16,8 +16,12 @@ export const Price = ({
|
|||
amount: number | string;
|
||||
breakNumber?: boolean;
|
||||
}) => {
|
||||
const { currency } = useSettings();
|
||||
const { prices, loading, error } = usePriceState();
|
||||
const { currency, displayValues } = useConfigState();
|
||||
const { prices, dontShow } = usePriceState();
|
||||
|
||||
if (!displayValues) {
|
||||
return <>-</>;
|
||||
}
|
||||
|
||||
let priceProps: PriceProps = {
|
||||
price: 0,
|
||||
|
@ -25,7 +29,7 @@ export const Price = ({
|
|||
currency: currency !== 'btc' && currency !== 'sat' ? 'sat' : currency,
|
||||
};
|
||||
|
||||
if (prices && !loading && !error) {
|
||||
if (prices && !dontShow) {
|
||||
const current: { last: number; symbol: string } = prices[currency] ?? {
|
||||
last: 0,
|
||||
symbol: '',
|
||||
|
@ -48,13 +52,17 @@ interface GetPriceProps {
|
|||
|
||||
export const getPrice = (
|
||||
currency: string,
|
||||
displayValues: boolean,
|
||||
priceContext: {
|
||||
error: boolean;
|
||||
loading: boolean;
|
||||
dontShow: boolean;
|
||||
prices?: { [key: string]: { last: number; symbol: string } };
|
||||
}
|
||||
) => ({ amount, breakNumber = false }: GetPriceProps): string => {
|
||||
const { prices, loading, error } = priceContext;
|
||||
const { prices, dontShow } = priceContext;
|
||||
|
||||
if (!displayValues) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
let priceProps: PriceProps = {
|
||||
price: 0,
|
||||
|
@ -62,7 +70,7 @@ export const getPrice = (
|
|||
currency: currency !== 'btc' && currency !== 'sat' ? 'sat' : currency,
|
||||
};
|
||||
|
||||
if (prices && !loading && !error) {
|
||||
if (prices && !dontShow) {
|
||||
const current: { last: number; symbol: string } = prices[currency] ?? {
|
||||
last: 0,
|
||||
symbol: '',
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
import React, { createContext, useContext, useReducer } from 'react';
|
||||
|
||||
type State = {
|
||||
loading: boolean;
|
||||
error: boolean;
|
||||
dontShow: boolean;
|
||||
fast: number;
|
||||
halfHour: number;
|
||||
hour: number;
|
||||
};
|
||||
|
||||
type ChangeState = {
|
||||
fast: number;
|
||||
halfHour: number;
|
||||
hour: number;
|
||||
};
|
||||
|
||||
type ActionType = {
|
||||
type: 'fetched' | 'error';
|
||||
state?: State;
|
||||
type: 'fetched' | 'dontShow';
|
||||
state?: ChangeState;
|
||||
};
|
||||
|
||||
type Dispatch = (action: ActionType) => void;
|
||||
|
@ -19,8 +24,7 @@ export const StateContext = createContext<State | undefined>(undefined);
|
|||
export const DispatchContext = createContext<Dispatch | undefined>(undefined);
|
||||
|
||||
const initialState = {
|
||||
loading: true,
|
||||
error: false,
|
||||
dontShow: true,
|
||||
fast: 0,
|
||||
halfHour: 0,
|
||||
hour: 0,
|
||||
|
@ -28,16 +32,12 @@ const initialState = {
|
|||
|
||||
const stateReducer = (state: State, action: ActionType): State => {
|
||||
switch (action.type) {
|
||||
case 'dontShow':
|
||||
return { ...initialState, dontShow: true };
|
||||
case 'fetched':
|
||||
return action.state || initialState;
|
||||
case 'error':
|
||||
return {
|
||||
...initialState,
|
||||
loading: false,
|
||||
error: true,
|
||||
};
|
||||
return { ...initialState, ...action.state, dontShow: false };
|
||||
default:
|
||||
return initialState;
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -28,9 +28,6 @@ type State = {
|
|||
sentChats: SentChatProps[];
|
||||
lastChat: string;
|
||||
sender: string;
|
||||
hideFee: boolean;
|
||||
hideNonVerified: boolean;
|
||||
maxFee: number;
|
||||
};
|
||||
|
||||
type ActionType = {
|
||||
|
@ -39,9 +36,6 @@ type ActionType = {
|
|||
| 'additional'
|
||||
| 'changeActive'
|
||||
| 'newChat'
|
||||
| 'hideNonVerified'
|
||||
| 'hideFee'
|
||||
| 'changeFee'
|
||||
| 'disconnected';
|
||||
chats?: ChatProps[];
|
||||
sentChats?: SentChatProps[];
|
||||
|
@ -49,9 +43,6 @@ type ActionType = {
|
|||
lastChat?: string;
|
||||
sender?: string;
|
||||
userId?: string;
|
||||
hideFee?: boolean;
|
||||
hideNonVerified?: boolean;
|
||||
maxFee?: number;
|
||||
};
|
||||
|
||||
type Dispatch = (action: ActionType) => void;
|
||||
|
@ -59,15 +50,12 @@ type Dispatch = (action: ActionType) => void;
|
|||
const StateContext = createContext<State | undefined>(undefined);
|
||||
const DispatchContext = createContext<Dispatch | undefined>(undefined);
|
||||
|
||||
const initialState = {
|
||||
const initialState: State = {
|
||||
initialized: false,
|
||||
chats: [],
|
||||
lastChat: '',
|
||||
sender: '',
|
||||
sentChats: [],
|
||||
hideFee: false,
|
||||
hideNonVerified: false,
|
||||
maxFee: 20,
|
||||
};
|
||||
|
||||
const stateReducer = (state: State, action: ActionType): State => {
|
||||
|
@ -100,29 +88,8 @@ const stateReducer = (state: State, action: ActionType): State => {
|
|||
sentChats: [...state.sentChats, action.newChat],
|
||||
...(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:
|
||||
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 { AccountProvider } from './AccountContext';
|
||||
import { SettingsProvider } from './SettingsContext';
|
||||
import { BitcoinInfoProvider } from './BitcoinContext';
|
||||
import { StatusProvider } from './StatusContext';
|
||||
import { PriceProvider } from './PriceContext';
|
||||
|
@ -8,14 +7,12 @@ import { ChatProvider } from './ChatContext';
|
|||
|
||||
export const ContextProvider: React.FC = ({ children }) => (
|
||||
<AccountProvider>
|
||||
<SettingsProvider>
|
||||
<BitcoinInfoProvider>
|
||||
<PriceProvider>
|
||||
<ChatProvider>
|
||||
<StatusProvider>{children}</StatusProvider>
|
||||
</ChatProvider>
|
||||
</PriceProvider>
|
||||
</BitcoinInfoProvider>
|
||||
</SettingsProvider>
|
||||
<BitcoinInfoProvider>
|
||||
<PriceProvider>
|
||||
<ChatProvider>
|
||||
<StatusProvider>{children}</StatusProvider>
|
||||
</ChatProvider>
|
||||
</PriceProvider>
|
||||
</BitcoinInfoProvider>
|
||||
</AccountProvider>
|
||||
);
|
||||
|
|
|
@ -6,14 +6,17 @@ type PriceProps = {
|
|||
};
|
||||
|
||||
type State = {
|
||||
loading: boolean;
|
||||
error: boolean;
|
||||
dontShow: boolean;
|
||||
prices?: { [key: string]: PriceProps };
|
||||
};
|
||||
|
||||
type ChangeState = {
|
||||
prices?: { [key: string]: PriceProps };
|
||||
};
|
||||
|
||||
type ActionType = {
|
||||
type: 'fetched' | 'error';
|
||||
state?: State;
|
||||
type: 'fetched' | 'dontShow';
|
||||
state?: ChangeState;
|
||||
};
|
||||
|
||||
type Dispatch = (action: ActionType) => void;
|
||||
|
@ -22,23 +25,18 @@ export const StateContext = createContext<State | undefined>(undefined);
|
|||
export const DispatchContext = createContext<Dispatch | undefined>(undefined);
|
||||
|
||||
const initialState: State = {
|
||||
loading: true,
|
||||
error: false,
|
||||
dontShow: true,
|
||||
prices: { EUR: { last: 0, symbol: '€' } },
|
||||
};
|
||||
|
||||
const stateReducer = (state: State, action: ActionType): State => {
|
||||
switch (action.type) {
|
||||
case 'dontShow':
|
||||
return { ...initialState, dontShow: true };
|
||||
case 'fetched':
|
||||
return action.state || initialState;
|
||||
case 'error':
|
||||
return {
|
||||
...initialState,
|
||||
loading: false,
|
||||
error: true,
|
||||
};
|
||||
return { ...initialState, ...action.state, dontShow: false };
|
||||
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,
|
||||
MessageCircle,
|
||||
} from 'react-feather';
|
||||
import { useSettings } from '../../context/SettingsContext';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Link } from '../../components/link/Link';
|
||||
import { useStatusState } from '../../context/StatusContext';
|
||||
|
@ -131,7 +131,7 @@ interface NavigationProps {
|
|||
|
||||
export const Navigation = ({ isBurger, setOpen }: NavigationProps) => {
|
||||
const { pathname } = useRouter();
|
||||
const { sidebar, setSettings } = useSettings();
|
||||
const { sidebar } = useConfigState();
|
||||
const { connected } = useStatusState();
|
||||
|
||||
const renderNavButton = (
|
||||
|
@ -204,7 +204,7 @@ export const Navigation = ({ isBurger, setOpen }: NavigationProps) => {
|
|||
<LinkView>
|
||||
{connected && <NodeInfo isOpen={sidebar} />}
|
||||
{renderLinks()}
|
||||
<SideSettings isOpen={sidebar} setIsOpen={setSettings} />
|
||||
<SideSettings />
|
||||
</LinkView>
|
||||
</StickyCard>
|
||||
</NavigationStyle>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { useSettings } from '../../../context/SettingsContext';
|
||||
import { useConfigState } from '../../../context/ConfigContext';
|
||||
import {
|
||||
Separation,
|
||||
SingleLine,
|
||||
|
@ -84,9 +84,9 @@ export const NodeInfo = ({ isOpen, isBurger }: NodeInfoProps) => {
|
|||
onError: error => toast.error(getErrorContent(error)),
|
||||
});
|
||||
|
||||
const { theme, currency } = useSettings();
|
||||
const { theme, currency, displayValues } = useConfigState();
|
||||
const priceContext = usePriceState();
|
||||
const format = getPrice(currency, priceContext);
|
||||
const format = getPrice(currency, displayValues, priceContext);
|
||||
|
||||
const tooltipType: any = getTooltipType(theme);
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import React from 'react';
|
||||
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 styled from 'styled-components';
|
||||
import {
|
||||
|
@ -9,6 +12,7 @@ import {
|
|||
inverseTextColor,
|
||||
unSelectedNavButton,
|
||||
} from '../../../styles/Themes';
|
||||
import { usePriceState } from '../../../context/PriceContext';
|
||||
|
||||
const SelectedIcon = styled.div<{ selected: boolean }>`
|
||||
display: flex;
|
||||
|
@ -48,13 +52,20 @@ const BurgerPadding = styled(SingleLine)`
|
|||
`;
|
||||
|
||||
const currencyArray = ['sat', 'btc', 'EUR', 'USD'];
|
||||
const currencyNoFiatArray = ['sat', 'btc'];
|
||||
|
||||
const themeArray = ['light', 'dark'];
|
||||
|
||||
const currencyMap: { [key: string]: string } = {
|
||||
sat: 'S',
|
||||
btc: '₿',
|
||||
EUR: '€',
|
||||
USD: '$',
|
||||
};
|
||||
const currencyNoFiatMap: { [key: string]: string } = {
|
||||
sat: 'S',
|
||||
btc: '₿',
|
||||
};
|
||||
|
||||
const getNextValue = (array: string[], current: string): string => {
|
||||
const length = array.length;
|
||||
|
@ -71,17 +82,16 @@ const getNextValue = (array: string[], current: string): string => {
|
|||
};
|
||||
|
||||
interface SideSettingsProps {
|
||||
isOpen?: boolean;
|
||||
isBurger?: boolean;
|
||||
setIsOpen?: (state: any) => void;
|
||||
}
|
||||
|
||||
export const SideSettings = ({
|
||||
isOpen,
|
||||
isBurger,
|
||||
setIsOpen,
|
||||
}: SideSettingsProps) => {
|
||||
const { theme, currency, setSettings } = useSettings();
|
||||
export const SideSettings = ({ isBurger }: SideSettingsProps) => {
|
||||
const { dontShow } = usePriceState();
|
||||
const { theme, currency, sidebar } = useConfigState();
|
||||
const dispatch = useConfigDispatch();
|
||||
|
||||
const correctMap = dontShow ? currencyNoFiatMap : currencyMap;
|
||||
const correctArray = dontShow ? currencyNoFiatArray : currencyArray;
|
||||
|
||||
const renderIcon = (
|
||||
type: string,
|
||||
|
@ -97,11 +107,12 @@ export const SideSettings = ({
|
|||
onClick={() => {
|
||||
localStorage.setItem(type, value);
|
||||
type === 'currency' &&
|
||||
setSettings({
|
||||
dispatch({
|
||||
type: 'change',
|
||||
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>}
|
||||
|
@ -110,12 +121,12 @@ export const SideSettings = ({
|
|||
);
|
||||
|
||||
const renderContent = () => {
|
||||
if (!isOpen) {
|
||||
if (!sidebar) {
|
||||
return (
|
||||
<>
|
||||
<Separation lineColor={unSelectedNavButton} />
|
||||
<IconRow center={true}>
|
||||
{renderIcon('currency', currency, currencyMap[currency], true)}
|
||||
{renderIcon('currency', currency, correctMap[currency], true)}
|
||||
</IconRow>
|
||||
<IconRow center={true}>
|
||||
{renderIcon(
|
||||
|
@ -135,8 +146,8 @@ export const SideSettings = ({
|
|||
<IconRow>
|
||||
{renderIcon('currency', 'sat', 'S')}
|
||||
{renderIcon('currency', 'btc', '₿')}
|
||||
{renderIcon('currency', 'EUR', '€')}
|
||||
{renderIcon('currency', 'USD', '$')}
|
||||
{!dontShow && renderIcon('currency', 'EUR', '€')}
|
||||
{!dontShow && renderIcon('currency', 'USD', '$')}
|
||||
</IconRow>
|
||||
<IconRow>
|
||||
{renderIcon('theme', 'light', '', false, Sun)}
|
||||
|
@ -152,8 +163,8 @@ export const SideSettings = ({
|
|||
<IconRow>
|
||||
{renderIcon('currency', 'sat', 'S')}
|
||||
{renderIcon('currency', 'btc', '₿')}
|
||||
{renderIcon('currency', 'EUR', '€')}
|
||||
{renderIcon('currency', 'USD', '$')}
|
||||
{!dontShow && renderIcon('currency', 'EUR', '€')}
|
||||
{!dontShow && renderIcon('currency', 'USD', '$')}
|
||||
</IconRow>
|
||||
<IconRow>
|
||||
{renderIcon('theme', 'light', '', false, Sun)}
|
||||
|
@ -166,19 +177,17 @@ export const SideSettings = ({
|
|||
return (
|
||||
<>
|
||||
{renderContent()}
|
||||
{setIsOpen && (
|
||||
<IconRow center={!isOpen}>
|
||||
<SelectedIcon
|
||||
selected={true}
|
||||
onClick={() => {
|
||||
localStorage.setItem('sidebar', (!isOpen).toString());
|
||||
setIsOpen({ sidebar: !isOpen });
|
||||
}}
|
||||
>
|
||||
{isOpen ? <ChevronLeft size={18} /> : <ChevronRight size={18} />}
|
||||
</SelectedIcon>
|
||||
</IconRow>
|
||||
)}
|
||||
<IconRow center={!sidebar}>
|
||||
<SelectedIcon
|
||||
selected={true}
|
||||
onClick={() => {
|
||||
localStorage.setItem('sidebar', (!sidebar).toString());
|
||||
dispatch({ type: 'change', sidebar: !sidebar });
|
||||
}}
|
||||
>
|
||||
{sidebar ? <ChevronLeft size={18} /> : <ChevronRight size={18} />}
|
||||
</SelectedIcon>
|
||||
</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';
|
||||
import styled from 'styled-components';
|
||||
import { getPrice } from '../../../components/price/Price';
|
||||
import { useSettings } from '../../../context/SettingsContext';
|
||||
import { useConfigState } from '../../../context/ConfigContext';
|
||||
import { usePriceState } from '../../../context/PriceContext';
|
||||
|
||||
const AddMargin = styled.div`
|
||||
|
@ -34,9 +34,9 @@ export const TransactionsCard = ({
|
|||
setIndexOpen,
|
||||
indexOpen,
|
||||
}: TransactionsCardProps) => {
|
||||
const { currency } = useSettings();
|
||||
const { currency, displayValues } = useConfigState();
|
||||
const priceContext = usePriceState();
|
||||
const format = getPrice(currency, priceContext);
|
||||
const format = getPrice(currency, displayValues, priceContext);
|
||||
|
||||
const {
|
||||
block_id,
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Separation, SubCard } from '../../../components/generic/Styled';
|
|||
import { MainInfo } from '../../../components/generic/CardGeneric';
|
||||
import { renderLine } from '../../../components/generic/helpers';
|
||||
import { getPrice } from '../../../components/price/Price';
|
||||
import { useSettings } from '../../../context/SettingsContext';
|
||||
import { useConfigState } from '../../../context/ConfigContext';
|
||||
import { usePriceState } from '../../../context/PriceContext';
|
||||
|
||||
interface TransactionsCardProps {
|
||||
|
@ -19,9 +19,9 @@ export const UtxoCard = ({
|
|||
setIndexOpen,
|
||||
indexOpen,
|
||||
}: TransactionsCardProps) => {
|
||||
const { currency } = useSettings();
|
||||
const { currency, displayValues } = useConfigState();
|
||||
const priceContext = usePriceState();
|
||||
const format = getPrice(currency, priceContext);
|
||||
const format = getPrice(currency, displayValues, priceContext);
|
||||
|
||||
const {
|
||||
address,
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
ResponsiveSingle,
|
||||
ResponsiveCol,
|
||||
} from '../../../components/generic/Styled';
|
||||
import { useSettings } from '../../../context/SettingsContext';
|
||||
import { useConfigState } from '../../../context/ConfigContext';
|
||||
import {
|
||||
getStatusDot,
|
||||
getTooltipType,
|
||||
|
@ -64,9 +64,9 @@ export const ChannelCard = ({
|
|||
}: ChannelCardProps) => {
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
|
||||
const { theme, currency } = useSettings();
|
||||
const { theme, currency, displayValues } = useConfigState();
|
||||
const priceContext = usePriceState();
|
||||
const format = getPrice(currency, priceContext);
|
||||
const format = getPrice(currency, displayValues, priceContext);
|
||||
|
||||
const tooltipType: any = getTooltipType(theme);
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
ResponsiveSingle,
|
||||
ResponsiveCol,
|
||||
} from '../../../components/generic/Styled';
|
||||
import { useSettings } from '../../../context/SettingsContext';
|
||||
import { useConfigState } from '../../../context/ConfigContext';
|
||||
import {
|
||||
getStatusDot,
|
||||
getTooltipType,
|
||||
|
@ -66,9 +66,9 @@ export const PendingCard = ({
|
|||
setIndexOpen,
|
||||
indexOpen,
|
||||
}: PendingCardProps) => {
|
||||
const { theme, currency } = useSettings();
|
||||
const { theme, currency, displayValues } = useConfigState();
|
||||
const priceContext = usePriceState();
|
||||
const format = getPrice(currency, priceContext);
|
||||
const format = getPrice(currency, displayValues, priceContext);
|
||||
|
||||
const tooltipType: any = getTooltipType(theme);
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ import {
|
|||
ChatBoxTopAlias,
|
||||
} from './Chat.styled';
|
||||
import { ChatBubble } from './ChatBubble';
|
||||
import { useChatState } from '../../context/ChatContext';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
|
||||
export const MessageCard = ({
|
||||
message,
|
||||
|
@ -28,7 +28,7 @@ export const MessageCard = ({
|
|||
message: MessageType;
|
||||
key?: string;
|
||||
}) => {
|
||||
const { hideFee, hideNonVerified } = useChatState();
|
||||
const { hideFee, hideNonVerified } = useConfigState();
|
||||
if (!message.message && message.contentType === 'text') {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import { useChatState, useChatDispatch } from '../../context/ChatContext';
|
|||
import { useAccount } from '../../context/AccountContext';
|
||||
import { Circle } from 'react-feather';
|
||||
import ScaleLoader from 'react-spinners/ScaleLoader';
|
||||
import { useSettings } from '../../context/SettingsContext';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
import { usePriceState } from '../../context/PriceContext';
|
||||
import { getPrice } from '../../components/price/Price';
|
||||
|
||||
|
@ -29,7 +29,8 @@ interface SendButtonProps {
|
|||
}
|
||||
|
||||
const SendButton = ({ amount }: SendButtonProps) => {
|
||||
const { sender, maxFee } = useChatState();
|
||||
const { maxFee } = useConfigState();
|
||||
const { sender } = useChatState();
|
||||
const dispatch = useChatDispatch();
|
||||
const { id } = useAccount();
|
||||
|
||||
|
@ -54,7 +55,7 @@ const SendButton = ({ amount }: SendButtonProps) => {
|
|||
sender,
|
||||
});
|
||||
}
|
||||
}, [loading, data]);
|
||||
}, [loading, data, amount, dispatch, sender, id]);
|
||||
|
||||
return (
|
||||
<SecureWrapper
|
||||
|
@ -80,9 +81,9 @@ interface ChatBubbleProps {
|
|||
}
|
||||
|
||||
export const ChatBubble = ({ message }: ChatBubbleProps) => {
|
||||
const { currency } = useSettings();
|
||||
const { currency, displayValues } = useConfigState();
|
||||
const priceContext = usePriceState();
|
||||
const format = getPrice(currency, priceContext);
|
||||
const format = getPrice(currency, displayValues, priceContext);
|
||||
|
||||
const {
|
||||
contentType,
|
||||
|
|
|
@ -8,6 +8,7 @@ import { toast } from 'react-toastify';
|
|||
import { getErrorContent } from '../../utils/error';
|
||||
import { useAccount } from '../../context/AccountContext';
|
||||
import { handleMessage } from './helpers/chatHelpers';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
|
||||
export const ChatInput = ({
|
||||
alias,
|
||||
|
@ -21,7 +22,8 @@ export const ChatInput = ({
|
|||
const [message, setMessage] = React.useState('');
|
||||
const { id } = useAccount();
|
||||
|
||||
const { sender, maxFee } = useChatState();
|
||||
const { maxFee } = useConfigState();
|
||||
const { sender } = useChatState();
|
||||
const dispatch = useChatDispatch();
|
||||
|
||||
const [sendMessage, { loading, data }] = useSendMessageMutation({
|
||||
|
@ -50,7 +52,17 @@ export const ChatInput = ({
|
|||
sender: customSender || sender,
|
||||
});
|
||||
}
|
||||
}, [loading, data]);
|
||||
}, [
|
||||
loading,
|
||||
data,
|
||||
formattedMessage,
|
||||
customSender,
|
||||
sender,
|
||||
contentType,
|
||||
tokens,
|
||||
id,
|
||||
dispatch,
|
||||
]);
|
||||
|
||||
return (
|
||||
<SingleLine>
|
||||
|
|
|
@ -17,7 +17,7 @@ import { toast } from 'react-toastify';
|
|||
import { getErrorContent } from '../../utils/error';
|
||||
import { ChevronRight } from 'react-feather';
|
||||
import { SecureButton } from '../../components/buttons/secureButton/SecureButton';
|
||||
import { useSettings } from '../../context/SettingsContext';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
import { textColorMap } from '../../styles/Themes';
|
||||
import { Input } from '../../components/input/Input';
|
||||
import { AdminSwitch } from '../../components/adminSwitch/AdminSwitch';
|
||||
|
@ -39,7 +39,7 @@ export const FeeCard = ({
|
|||
const [newBaseFee, setBaseFee] = useState(0);
|
||||
const [newFeeRate, setFeeRate] = useState(0);
|
||||
|
||||
const { theme } = useSettings();
|
||||
const { theme } = useConfigState();
|
||||
|
||||
const {
|
||||
alias,
|
||||
|
@ -59,7 +59,7 @@ export const FeeCard = ({
|
|||
? toast.success('Channel fees updated')
|
||||
: toast.error('Error updating channel fees');
|
||||
},
|
||||
refetchQueries: ['GetChannelFees'],
|
||||
refetchQueries: ['ChannelFees'],
|
||||
});
|
||||
|
||||
const handleClick = () => {
|
||||
|
|
|
@ -38,6 +38,7 @@ export const PayCard = ({ setOpen }: { setOpen: () => void }) => {
|
|||
setModalType('none');
|
||||
setOpen();
|
||||
},
|
||||
refetchQueries: ['GetInOut', 'GetNodeInfo', 'GetBalances'],
|
||||
});
|
||||
|
||||
const handleClick = () => {
|
||||
|
|
|
@ -19,7 +19,7 @@ import {
|
|||
} from '../../../../components/buttons/multiButton/MultiButton';
|
||||
import { Price, getPrice } from '../../../../components/price/Price';
|
||||
import { mediaWidths } from '../../../../styles/Themes';
|
||||
import { useSettings } from '../../../../context/SettingsContext';
|
||||
import { useConfigState } from '../../../../context/ConfigContext';
|
||||
import Modal from '../../../../components/modal/ReactModal';
|
||||
import { ColorButton } from '../../../../components/buttons/colorButton/ColorButton';
|
||||
import { renderLine } from '../../../../components/generic/helpers';
|
||||
|
@ -43,22 +43,21 @@ const Margin = styled.div`
|
|||
`;
|
||||
|
||||
export const SendOnChainCard = ({ setOpen }: { setOpen: () => void }) => {
|
||||
const { currency } = useSettings();
|
||||
const { fast, halfHour, hour, dontShow } = useBitcoinState();
|
||||
const { currency, displayValues } = useConfigState();
|
||||
const priceContext = usePriceState();
|
||||
const format = getPrice(currency, priceContext);
|
||||
const format = getPrice(currency, displayValues, priceContext);
|
||||
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
|
||||
const [address, setAddress] = useState('');
|
||||
const [tokens, setTokens] = useState(0);
|
||||
const [type, setType] = useState('none');
|
||||
const [type, setType] = useState(dontShow ? 'fee' : 'none');
|
||||
const [amount, setAmount] = useState(0);
|
||||
const [sendAll, setSendAll] = useState(false);
|
||||
|
||||
const canSend = address !== '' && (sendAll || tokens > 0) && amount > 0;
|
||||
|
||||
const { fast, halfHour, hour } = useBitcoinState();
|
||||
|
||||
const [payAddress, { loading }] = usePayAddressMutation({
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
onCompleted: () => {
|
||||
|
@ -143,14 +142,15 @@ export const SendOnChainCard = ({ setOpen }: { setOpen: () => void }) => {
|
|||
<SingleLine>
|
||||
<NoWrapTitle>Fee:</NoWrapTitle>
|
||||
<MultiButton margin={'8px 0 8px 16px'}>
|
||||
{renderButton(
|
||||
() => {
|
||||
setType('none');
|
||||
setAmount(fast);
|
||||
},
|
||||
'Auto',
|
||||
type === 'none'
|
||||
)}
|
||||
{!dontShow &&
|
||||
renderButton(
|
||||
() => {
|
||||
setType('none');
|
||||
setAmount(fast);
|
||||
},
|
||||
'Auto',
|
||||
type === 'none'
|
||||
)}
|
||||
{renderButton(
|
||||
() => {
|
||||
setType('fee');
|
||||
|
|
|
@ -34,13 +34,12 @@ interface OpenChannelProps {
|
|||
}
|
||||
|
||||
export const OpenChannelCard = ({ color, setOpenCard }: OpenChannelProps) => {
|
||||
const { fast, halfHour, hour, dontShow } = useBitcoinState();
|
||||
const [size, setSize] = useState(0);
|
||||
const [fee, setFee] = useState(0);
|
||||
const [publicKey, setPublicKey] = useState('');
|
||||
const [privateChannel, setPrivateChannel] = useState(false);
|
||||
const [type, setType] = useState('none');
|
||||
|
||||
const { fast, halfHour, hour } = useBitcoinState();
|
||||
const [type, setType] = useState(dontShow ? 'fee' : 'none');
|
||||
|
||||
const [openChannel] = useOpenChannelMutation({
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
|
@ -108,24 +107,26 @@ export const OpenChannelCard = ({ color, setOpenCard }: OpenChannelProps) => {
|
|||
</MultiButton>
|
||||
</SingleLine>
|
||||
<Separation />
|
||||
<SingleLine>
|
||||
<NoWrapTitle>Fee:</NoWrapTitle>
|
||||
<MultiButton margin={'8px 0 8px 16px'}>
|
||||
{renderButton(
|
||||
() => {
|
||||
setType('none');
|
||||
setFee(fast);
|
||||
},
|
||||
'Auto',
|
||||
type === 'none'
|
||||
)}
|
||||
{renderButton(
|
||||
() => setType('fee'),
|
||||
'Fee (Sats/Byte)',
|
||||
type === 'fee'
|
||||
)}
|
||||
</MultiButton>
|
||||
</SingleLine>
|
||||
{!dontShow && (
|
||||
<SingleLine>
|
||||
<NoWrapTitle>Fee:</NoWrapTitle>
|
||||
<MultiButton margin={'8px 0 8px 16px'}>
|
||||
{renderButton(
|
||||
() => {
|
||||
setType('none');
|
||||
setFee(fast);
|
||||
},
|
||||
'Auto',
|
||||
type === 'none'
|
||||
)}
|
||||
{renderButton(
|
||||
() => setType('fee'),
|
||||
'Fee (Sats/Byte)',
|
||||
type === 'fee'
|
||||
)}
|
||||
</MultiButton>
|
||||
</SingleLine>
|
||||
)}
|
||||
<SingleLine>
|
||||
<ResponsiveWrap>
|
||||
<NoWrapTitle>Fee Amount:</NoWrapTitle>
|
||||
|
@ -141,7 +142,6 @@ export const OpenChannelCard = ({ color, setOpenCard }: OpenChannelProps) => {
|
|||
type={'number'}
|
||||
onChange={e => setFee(Number(e.target.value))}
|
||||
/>
|
||||
// </MultiButton>
|
||||
)}
|
||||
{type === 'none' && (
|
||||
<MultiButton margin={'8px 0 8px 16px'}>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import { DarkSubTitle } from '../../../../components/generic/Styled';
|
||||
import { useSettings } from '../../../../context/SettingsContext';
|
||||
import { useConfigState } from '../../../../context/ConfigContext';
|
||||
import { VictoryPie } from 'victory';
|
||||
import { chartAxisColor } from '../../../../styles/Themes';
|
||||
import { Row, Col, PieRow } from '.';
|
||||
|
@ -13,9 +13,9 @@ interface Props {
|
|||
}
|
||||
|
||||
export const FlowPie = ({ flowPie, isType }: Props) => {
|
||||
const { theme, currency } = useSettings();
|
||||
const { theme, currency, displayValues } = useConfigState();
|
||||
const priceContext = usePriceState();
|
||||
const format = getPrice(currency, priceContext);
|
||||
const format = getPrice(currency, displayValues, priceContext);
|
||||
|
||||
return (
|
||||
<Row>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import numeral from 'numeral';
|
||||
import { useSettings } from '../../../../context/SettingsContext';
|
||||
import { useConfigState } from '../../../../context/ConfigContext';
|
||||
import {
|
||||
VictoryBar,
|
||||
VictoryChart,
|
||||
|
@ -41,9 +41,9 @@ export const FlowReport = ({
|
|||
parsedData2,
|
||||
}: // waterfall,
|
||||
Props) => {
|
||||
const { theme, currency } = useSettings();
|
||||
const { theme, currency, displayValues } = useConfigState();
|
||||
const priceContext = usePriceState();
|
||||
const format = getPrice(currency, priceContext);
|
||||
const format = getPrice(currency, displayValues, priceContext);
|
||||
|
||||
let domain = 24;
|
||||
let barWidth = 3;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import { DarkSubTitle } from '../../../../components/generic/Styled';
|
||||
import { useSettings } from '../../../../context/SettingsContext';
|
||||
import { useConfigState } from '../../../../context/ConfigContext';
|
||||
import { VictoryPie } from 'victory';
|
||||
import { chartAxisColor } from '../../../../styles/Themes';
|
||||
import { Row, Col, PieRow } from '.';
|
||||
|
@ -10,7 +10,7 @@ interface Props {
|
|||
}
|
||||
|
||||
export const InvoicePie = ({ invoicePie }: Props) => {
|
||||
const { theme } = useSettings();
|
||||
const { theme } = useConfigState();
|
||||
|
||||
return (
|
||||
<Row>
|
||||
|
|
|
@ -12,7 +12,7 @@ import { GitCommit, ArrowDown, ArrowUp } from 'react-feather';
|
|||
import styled from 'styled-components';
|
||||
import { LoadingCard } from '../../../../components/loading/LoadingCard';
|
||||
import { getPrice } from '../../../../components/price/Price';
|
||||
import { useSettings } from '../../../../context/SettingsContext';
|
||||
import { useConfigState } from '../../../../context/ConfigContext';
|
||||
import { usePriceState } from '../../../../context/PriceContext';
|
||||
import { useGetForwardChannelsReportQuery } from '../../../../generated/graphql';
|
||||
|
||||
|
@ -64,9 +64,9 @@ interface Props {
|
|||
export const ForwardChannelsReport = ({ isTime, isType, color }: Props) => {
|
||||
const [type, setType] = useState('route');
|
||||
|
||||
const { currency } = useSettings();
|
||||
const { currency, displayValues } = useConfigState();
|
||||
const priceContext = usePriceState();
|
||||
const format = getPrice(currency, priceContext);
|
||||
const format = getPrice(currency, displayValues, priceContext);
|
||||
|
||||
const { auth } = useAccount();
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { Sub4Title } from '../../../../components/generic/Styled';
|
||||
import numeral from 'numeral';
|
||||
import { useSettings } from '../../../../context/SettingsContext';
|
||||
import { useConfigState } from '../../../../context/ConfigContext';
|
||||
import { useAccount } from '../../../../context/AccountContext';
|
||||
import {
|
||||
VictoryBar,
|
||||
|
@ -34,9 +34,9 @@ const timeMap: { [key: string]: string } = {
|
|||
};
|
||||
|
||||
export const ForwardReport = ({ isTime, isType }: Props) => {
|
||||
const { theme, currency } = useSettings();
|
||||
const { theme, currency, displayValues } = useConfigState();
|
||||
const priceContext = usePriceState();
|
||||
const format = getPrice(currency, priceContext);
|
||||
const format = getPrice(currency, displayValues, priceContext);
|
||||
|
||||
const { auth } = useAccount();
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
VictoryVoronoiContainer,
|
||||
VictoryTooltip,
|
||||
} from 'victory';
|
||||
import { useSettings } from '../../../../context/SettingsContext';
|
||||
import { useConfigState } from '../../../../context/ConfigContext';
|
||||
import {
|
||||
chartGridColor,
|
||||
chartAxisColor,
|
||||
|
@ -26,9 +26,9 @@ import { useGetLiquidReportQuery } from '../../../../generated/graphql';
|
|||
export const LiquidReport = () => {
|
||||
const { auth } = useAccount();
|
||||
|
||||
const { theme, currency } = useSettings();
|
||||
const { theme, currency, displayValues } = useConfigState();
|
||||
const priceContext = usePriceState();
|
||||
const format = getPrice(currency, priceContext);
|
||||
const format = getPrice(currency, displayValues, priceContext);
|
||||
|
||||
const { data, loading } = useGetLiquidReportQuery({
|
||||
skip: !auth,
|
||||
|
|
|
@ -24,7 +24,7 @@ import {
|
|||
MainInfo,
|
||||
} from '../../components/generic/CardGeneric';
|
||||
import { getPercent } from '../../utils/helpers';
|
||||
import { useSettings } from '../../context/SettingsContext';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
import { usePriceState } from '../../context/PriceContext';
|
||||
import { getPrice } from '../../components/price/Price';
|
||||
|
@ -57,10 +57,10 @@ export const PeersCard = ({
|
|||
}: PeerProps) => {
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
|
||||
const { theme, currency } = useSettings();
|
||||
const { theme, currency, displayValues } = useConfigState();
|
||||
const priceContext = usePriceState();
|
||||
|
||||
const format = getPrice(currency, priceContext);
|
||||
const format = getPrice(currency, displayValues, priceContext);
|
||||
const tooltipType: any = getTooltipType(theme);
|
||||
|
||||
const {
|
||||
|
|
|
@ -11,11 +11,16 @@ import {
|
|||
MultiButton,
|
||||
SingleButton,
|
||||
} from '../../components/buttons/multiButton/MultiButton';
|
||||
import { useChatState, useChatDispatch } from '../../context/ChatContext';
|
||||
import { useConfigState, useConfigDispatch } from '../../context/ConfigContext';
|
||||
|
||||
export const ChatSettings = () => {
|
||||
const { hideFee, hideNonVerified, maxFee } = useChatState();
|
||||
const dispatch = useChatDispatch();
|
||||
const {
|
||||
hideFee,
|
||||
hideNonVerified,
|
||||
maxFee,
|
||||
chatPollingSpeed: cps,
|
||||
} = useConfigState();
|
||||
const dispatch = useConfigDispatch();
|
||||
|
||||
const renderButton = (
|
||||
title: string,
|
||||
|
@ -29,15 +34,19 @@ export const ChatSettings = () => {
|
|||
switch (type) {
|
||||
case 'fee':
|
||||
typeof value === 'boolean' &&
|
||||
dispatch({ type: 'hideFee', hideFee: value });
|
||||
dispatch({ type: 'change', hideFee: value });
|
||||
break;
|
||||
case 'nonverified':
|
||||
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;
|
||||
default:
|
||||
typeof value === 'number' &&
|
||||
dispatch({ type: 'changeFee', maxFee: value });
|
||||
dispatch({ type: 'change', maxFee: value });
|
||||
break;
|
||||
}
|
||||
}}
|
||||
|
@ -74,6 +83,18 @@ export const ChatSettings = () => {
|
|||
{renderButton('100', 'maxFee', maxFee === 100, 100)}
|
||||
</MultiButton>
|
||||
</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>
|
||||
</CardWithTitle>
|
||||
);
|
||||
|
|
|
@ -21,6 +21,7 @@ import { useStatusDispatch } from '../../context/StatusContext';
|
|||
import { useRouter } from 'next/router';
|
||||
import { appendBasePath } from '../../utils/basePath';
|
||||
import { useChatDispatch } from '../../context/ChatContext';
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
export const ButtonRow = styled.div`
|
||||
width: auto;
|
||||
|
@ -90,6 +91,7 @@ export const DangerView = () => {
|
|||
chatDispatch({ type: 'disconnected' });
|
||||
deleteStorage();
|
||||
refreshAccount();
|
||||
Cookies.remove('config');
|
||||
push(appendBasePath('/'));
|
||||
};
|
||||
|
||||
|
|
|
@ -6,22 +6,19 @@ import {
|
|||
Sub4Title,
|
||||
} from '../../components/generic/Styled';
|
||||
import { SettingsLine } from '../../../pages/settings';
|
||||
import { useSettings } from '../../context/SettingsContext';
|
||||
import { useConfigState, useConfigDispatch } from '../../context/ConfigContext';
|
||||
|
||||
import {
|
||||
MultiButton,
|
||||
SingleButton,
|
||||
} from '../../components/buttons/multiButton/MultiButton';
|
||||
import { useAccount } from '../../context/AccountContext';
|
||||
import { usePriceState } from '../../context/PriceContext';
|
||||
|
||||
export const InterfaceSettings = () => {
|
||||
const {
|
||||
theme,
|
||||
currency,
|
||||
nodeInfo,
|
||||
setSettings,
|
||||
refreshSettings,
|
||||
} = useSettings();
|
||||
const { dontShow } = usePriceState();
|
||||
const { theme, currency, multiNodeInfo } = useConfigState();
|
||||
const dispatch = useConfigDispatch();
|
||||
|
||||
const { accounts } = useAccount();
|
||||
|
||||
|
@ -37,11 +34,13 @@ export const InterfaceSettings = () => {
|
|||
selected={current === value}
|
||||
onClick={() => {
|
||||
localStorage.setItem(type, value);
|
||||
type === 'theme' && setSettings({ theme: value });
|
||||
type === 'currency' && setSettings({ currency: value });
|
||||
type === 'theme' && dispatch({ type: 'change', theme: value });
|
||||
type === 'currency' && dispatch({ type: 'change', currency: value });
|
||||
type === 'nodeInfo' &&
|
||||
setSettings({ nodeInfo: value === 'true' ? true : false });
|
||||
refreshSettings();
|
||||
dispatch({
|
||||
type: 'change',
|
||||
multiNodeInfo: value === 'true' ? true : false,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
|
@ -63,18 +62,18 @@ export const InterfaceSettings = () => {
|
|||
<SettingsLine>
|
||||
<Sub4Title>Show all accounts on homepage:</Sub4Title>
|
||||
<MultiButton>
|
||||
{renderButton('Yes', 'true', 'nodeInfo', `${nodeInfo}`)}
|
||||
{renderButton('No', 'false', 'nodeInfo', `${nodeInfo}`)}
|
||||
{renderButton('Yes', 'true', 'nodeInfo', `${multiNodeInfo}`)}
|
||||
{renderButton('No', 'false', 'nodeInfo', `${multiNodeInfo}`)}
|
||||
</MultiButton>
|
||||
</SettingsLine>
|
||||
)}
|
||||
<SettingsLine>
|
||||
<Sub4Title>Currency:</Sub4Title>
|
||||
<MultiButton margin={'0 0 0 16px'}>
|
||||
{renderButton('Bitcoin', 'btc', 'currency', currency)}
|
||||
{renderButton('Satoshis', 'sat', 'currency', currency)}
|
||||
{renderButton('Euro', 'EUR', 'currency', currency)}
|
||||
{renderButton('US Dollar', 'USD', 'currency', currency)}
|
||||
{renderButton('Bitcoin', 'btc', 'currency', currency)}
|
||||
{!dontShow && renderButton('Euro', 'EUR', 'currency', currency)}
|
||||
{!dontShow && renderButton('USD', 'USD', 'currency', currency)}
|
||||
</MultiButton>
|
||||
</SettingsLine>
|
||||
</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
|
||||
},
|
||||
"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"
|
||||
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:
|
||||
version "1.0.5"
|
||||
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"
|
||||
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:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
|
||||
|
|
Loading…
Add table
Reference in a new issue