diff --git a/paybutton/dev/demo/index.html b/paybutton/dev/demo/index.html index f92140e2..0cad42fe 100644 --- a/paybutton/dev/demo/index.html +++ b/paybutton/dev/demo/index.html @@ -1,5 +1,5 @@ - + @@ -7,19 +7,73 @@ - - -
@@ -77,7 +131,7 @@

-
+
- - - - -
-
-
-
- - -
-
-
- - -
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
-
- - -
-
- - -
-
- - -
-
-
-
-
- - -
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
- - -
-
-
-
- -
- -
-
-
-
-
-
- - - - - \ No newline at end of file diff --git a/paybutton/dev/demo/style.css b/paybutton/dev/demo/style.css deleted file mode 100644 index 8a3d022a..00000000 --- a/paybutton/dev/demo/style.css +++ /dev/null @@ -1,130 +0,0 @@ -.navbar { - color: #333333; - display: flex; - justify-content: center; - padding: 10px 20px; -} -.navbar a { - color: #333333; - text-decoration: none; - padding: 10px; -} -.buttons-section { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); - gap: 20px; - padding: 50px; - align-items: center; -} -.widgets-section { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); - gap: 40px; - padding: 50px; - align-items: center; -} -.btn { - padding: 20px; - display: flex; -} -.btn button { - text-transform: none; - color: #fff; - border: 1px solid rgb(58, 48, 166); - margin: auto !important; - padding: 0.618em 1.618em !important; - min-width: 14em !important; - background: #518efe; - transition: background-position 0.8s ease 0s, color 0.15s ease 0s, - border-color 0.4s ease 0s; - border-radius: 10px !important; -} -.btn button:hover { - cursor: pointer !important; - background: #518efeb7; - color: #101563; -} -.btn.purple button { - border: 2px solid #7a33ff; - background: #9a55ff; -} -.btn.purple button:hover { - background: #ebe0fb; - color: #5805f3; -} - -.toggle { - margin-bottom: 0 !important; -} -.toggle input[type="checkbox"] { - visibility: hidden; -} - -.toggle label { - display: block; - cursor: pointer; - position: relative; - padding-left: 40px; - font-size: 12px; -} - -.toggle label::before, .toggle label::after { - content: ""; - position: absolute; - transition: all 0.3s ease; -} - -.toggle label::before { - width: 34px; - height: 20px; - background-color: #e7e5e5; - border-radius: 10px; - top: 50%; - transform: translateY(-50%); - left: 0; -} - -.toggle label::after { - width: 20px; - height: 20px; - background-color: #c3c2c2; - border-radius: 50%; - top: 50%; - transform: translateY(-50%); - left: 2px; - box-shadow: 0 0 2px rgba(0,0,0,0.4); -} - -.toggle input[type="checkbox"]:checked + label::before { - background-color: #ccc; -} - -.toggle input[type="checkbox"]:checked + label::after { - left: 12px; - background-color: #2186fa ; -} - -form { - width: 300px; -} -.card { - border: 1px solid #ccc; - padding: 20px; - box-shadow: 0 0 5px rgba(0, 0, 0, 0.169); - border-radius: 10px; - min-height: 650px; - min-width: 310px; - height: 100%; - align-self: center; - margin: 10px; -} -.form-input { - margin-bottom: 10px; - display: flex; - flex-direction: column; - min-width: 25px; - font-size: 12px; -} -label { - padding-bottom: 6px; -} \ No newline at end of file diff --git a/react/lib/components/Widget/Widget.tsx b/react/lib/components/Widget/Widget.tsx index 9cdbea1e..2f936c66 100644 --- a/react/lib/components/Widget/Widget.tsx +++ b/react/lib/components/Widget/Widget.tsx @@ -1,5 +1,3 @@ -import React, { useEffect, useMemo, useState } from 'react'; - import { Box, CircularProgress, @@ -9,10 +7,10 @@ import { TextField, Grid, } from '@material-ui/core'; - +import React, { useEffect, useMemo, useState } from 'react'; import copy from 'copy-to-clipboard'; import QRCode, { BaseQRCodeProps } from 'qrcode.react'; -import io from 'socket.io-client'; +import io, { Socket } from 'socket.io-client'; import PencilIcon from '../../assets/edit-pencil'; import config from '../../config.json'; import { Theme, ThemeName, ThemeProvider, useTheme } from '../../themes'; @@ -162,10 +160,10 @@ export const Widget: React.FunctionComponent = props => { const [errorMsg, setErrorMsg] = useState(''); const [goalText, setGoalText] = useState(''); const [goalPercent, setGoalPercent] = useState(0); + const [socket, setSocket] = useState(undefined); const [addressType, setAddressType] = useState( getCurrencyTypeFromAddress(to), ); - const [convertedCurrencyObj, setConvertedCurrencyObj] = useState(); const price = props.price ?? 0; diff --git a/react/lib/components/Widget/WidgetContainer.tsx b/react/lib/components/Widget/WidgetContainer.tsx index 9ee95d13..da590ca5 100644 --- a/react/lib/components/Widget/WidgetContainer.tsx +++ b/react/lib/components/Widget/WidgetContainer.tsx @@ -3,10 +3,24 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import successSound from '../../assets/success.mp3.json'; +import { + getFiatPrice, + Currency, + CurrencyObject, + Transaction, + generatePaymentId, + getCurrencyTypeFromAddress, + isCrypto, + isFiat, + isGreaterThanZero, + isValidCurrency, + resolveNumber, + compareAddresses, + isAddressSupported, + shouldTriggerOnSuccess +} from '../../util'; import Widget, { WidgetProps } from './Widget'; -import { Currency } from 'currency-formatter'; -import { CurrencyObject, Transaction, getCurrencyTypeFromAddress, isValidCurrency, isCrypto, isAddressSupported, compareAddresses, getFiatPrice, isFiat, resolveNumber } from '../../util'; export interface WidgetContainerProps extends Omit { @@ -177,13 +191,21 @@ export const WidgetContainer: React.FunctionComponent = } }, [currency, price]); - useEffect(() => { - newTxs?.map(tx => { - if (tx.confirmed === false && - isLessThanZero(tx.amount)){ - handlePayment(tx); - setNewTxs([]) + if (isFiat(currency) && price === undefined) { + (async () => { + getPrice(); + })() + } + }, [currency, price]); + + const handleNewTransaction = useCallback( + (tx: Transaction) => { + if ( + tx.confirmed === false && + isGreaterThanZero(resolveNumber(tx.amount)) + ) { + handlePayment(tx); } }, [handlePayment], diff --git a/react/lib/tests/util/api-client.test.ts b/react/lib/tests/util/api-client.test.ts index e22e9381..4f5ab008 100644 --- a/react/lib/tests/util/api-client.test.ts +++ b/react/lib/tests/util/api-client.test.ts @@ -7,7 +7,7 @@ global.fetch = jest.fn(); describe('API Client Util Tests', () => { describe('shouldTriggerOnSuccess', () => { - it('should return true when all conditions match', () => { + it('returns true when all conditions match', () => { const transaction:Transaction = { amount: '100.00', paymentId: '123', @@ -20,7 +20,31 @@ describe('API Client Util Tests', () => { }); - it('should return false when the payment ID does not match', () => { + it('returns true when tx paymentId is empty and component paymentId is undefined', () => { + const transaction:Transaction = { + amount: '101.00', + paymentId: '', + message: 'test opReturn message', + hash: '', + timestamp: 0, + address: '' + }; + expect(shouldTriggerOnSuccess(transaction, undefined, '101.00', 'test opReturn message')).toBe(true); + }); + + it('returns true when tx paymentId is empty and component paymentId is empty', () => { + const transaction:Transaction = { + amount: '101.00', + paymentId: '', + message: 'test opReturn message', + hash: '', + timestamp: 0, + address: '' + }; + expect(shouldTriggerOnSuccess(transaction, '', '101.00', 'test opReturn message')).toBe(true); + }); + + it('returns false when the payment ID does not match', () => { const transaction:Transaction = { amount: '100.00', paymentId: '123', @@ -32,7 +56,7 @@ describe('API Client Util Tests', () => { expect(shouldTriggerOnSuccess(transaction, '999', '100.00', 'test opReturn message')).toBe(false); }); - it('should return false when crypto amounts do not match', () => { + it('returns false when crypto amounts do not match', () => { const transaction:Transaction = { amount: '101.00', paymentId: '123', @@ -42,9 +66,9 @@ describe('API Client Util Tests', () => { address: '' }; expect(shouldTriggerOnSuccess(transaction, '123', '100.00', 'test opReturn message')).toBe(false); - }); // todo - check if amount higher than the one requested is also valid + }); - it('should return false when OpReturn data does not match', () => { + it('returns false when OpReturn data does not match', () => { const transaction:Transaction = { amount: '101.00', paymentId: '123', @@ -80,7 +104,7 @@ describe('API Client Util Tests', () => { expect(shouldTriggerOnSuccess(transaction, undefined, '101.00', 'test opReturn')).toBe(true); }); - it('should ignore opReturn validation when opReturn does not exists', () => { + it('should fail when there is a message but opReturn is not enabled', () => { const transaction:Transaction = { amount: '101.00', paymentId: '123', @@ -89,7 +113,7 @@ describe('API Client Util Tests', () => { timestamp: 0, address: '' }; - expect(shouldTriggerOnSuccess(transaction, '123', '101.00', undefined)).toBe(true); + expect(shouldTriggerOnSuccess(transaction, '123', '101.00', undefined)).toBe(false); }); }); }); diff --git a/react/lib/util/api-client.ts b/react/lib/util/api-client.ts index 5b104c2e..f176c7cc 100644 --- a/react/lib/util/api-client.ts +++ b/react/lib/util/api-client.ts @@ -10,6 +10,7 @@ import { Currency, } from './types'; import { isFiat } from './currency'; +import { resolveNumber } from './number'; export const shouldTriggerOnSuccess = ( transaction: Transaction, @@ -30,7 +31,7 @@ export const shouldTriggerOnSuccess = ( const isPaymentIdValid = thisPaymentId ? thisPaymentId === paymentId : true; const isOpReturnValid = thisOpReturn ? formattedOpReturn === formattedTxOpReturn - : true; + : (message === ''); return isAmountValid && isPaymentIdValid && isOpReturnValid; }; diff --git a/react/lib/util/satoshis.ts b/react/lib/util/satoshis.ts index 981941de..dad57dc6 100644 --- a/react/lib/util/satoshis.ts +++ b/react/lib/util/satoshis.ts @@ -1,4 +1,9 @@ -import { formatPrice, formatBCH, formatXEC, DECIMALS, Currency, isCrypto, randomizeSatoshis, CurrencyObject, resolveNumber } from './index'; +import { formatPrice, formatBCH, formatXEC } from './format'; +import { DECIMALS, } from './constants'; +import { isCrypto, } from './currency'; +import { randomizeSatoshis } from './randomizeSats' +import { Currency, CurrencyObject } from './types' +import { resolveNumber } from './number'; export const getCurrencyObject = (