diff --git a/CHANGELOG.md b/CHANGELOG.md index df9e4bbbd..15a9e0fb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [UNRELEASED] * [#524] Added `noreferrer` to Banner links +* [#447] Fixed txs scrolling issue that was causing page to reload and jump to the top when user scrolled to the bottom of the page. ## v0.41.x-14 * [#488] Updated missing proposer address in proposals. diff --git a/client/styles.scss b/client/styles.scss index 61106cde3..834cfb4ac 100644 --- a/client/styles.scss +++ b/client/styles.scss @@ -568,6 +568,10 @@ body { padding-bottom: 0.4rem; border-bottom: 1px solid $gray-300; } + &.tx-info-row{ + padding-top: 0.4rem; + padding-bottom: 0.4rem; + } } .tx-info.invalid { diff --git a/imports/ui/transactions/List.jsx b/imports/ui/transactions/List.jsx index 18b2ecbf4..935e0dcb5 100644 --- a/imports/ui/transactions/List.jsx +++ b/imports/ui/transactions/List.jsx @@ -2,51 +2,90 @@ import React, { Component } from 'react'; import { Row, Col, Spinner } from 'reactstrap'; import { TransactionRow } from './TransactionRow.jsx'; import i18n from 'meteor/universe:i18n'; +import { VariableSizeList } from 'react-window'; + const T = i18n.createComponent(); -export default class Transactions extends Component{ - constructor(props){ +export default class Transactions extends Component { + constructor(props) { super(props); + this.childRef = React.createRef(); + this.state = { txs: "", - homepage: window?.location?.pathname === '/' ? true : false - + homepage: window?.location?.pathname === '/' ? true : false, } } - componentDidUpdate(prevProps){ - if (this.props != prevProps){ - if (this.props.transactions.length > 0){ + componentDidUpdate(prevProps, prevState) { + if (this.props != prevProps) { + if (this.props.transactions.length > 0) { this.setState({ txs: this.props.transactions.map((tx, i) => { - return - }) - }) + }), + height: this.childRef.current?.myRef?.current?.clientHeight + }) + } + } + } + + componentDidMount() { + this.childRef?.current?.myRef?.current?.clientHeight + } + + + getItemSize = index => { + if (this.state.txs[index]?.props?.tx?.tx?.body?.messages.length > 1) { + if (this.state.txs[index]?.props?.tx?.tx?.body?.messages[0]['@type'] === '/cosmos.bank.v1beta1.MsgSend') { + return 100 * this.state.txs[index]?.props?.tx?.tx?.body?.messages.length } + return 80 * this.state.txs[index]?.props?.tx?.tx?.body?.messages.length + } + else { + return 130 } } - render(){ - if (this.props.loading){ + TxRow = ({ index, style }) => ( +
{this.state.txs[index]}
+ ); + + render() { + if (this.props.loading) { return } - else if (!this.props.transactionsExist){ + else if (!this.props.transactionsExist) { return
transactions.notFound
} - else{ + else { return
message transactions.activities transactions.txHash common.height check_circle transactions.valid - {!this.state.homepage ? monetization_on transactions.fee : null } + {!this.state.homepage ? monetization_on transactions.fee : null} - {this.state.txs} + {this.state.txs ? + + {this.TxRow} + + : ''}
} } diff --git a/imports/ui/transactions/ListContainer.js b/imports/ui/transactions/ListContainer.js index 898714ed4..e2b48e17e 100644 --- a/imports/ui/transactions/ListContainer.js +++ b/imports/ui/transactions/ListContainer.js @@ -7,24 +7,25 @@ export default ValidatorDetailsContainer = withTracker((props) => { let transactionsHandle, transactions, transactionsExist; let loading = true; - if (Meteor.isClient){ + if (Meteor.isClient) { transactionsHandle = Meteor.subscribe('transactions.list', props.limit); - loading = !transactionsHandle.ready(); + loading = !transactionsHandle.ready() && props.limit == Meteor.settings.public.initialPageSize; if (!loading) { - transactions = Transactions.find({}, {sort:{height:-1}}).fetch(); + transactions = Transactions.find({}, { sort: { height: -1 } }).fetch(); transactionsExist = !loading && !!transactions; } } - if (Meteor.isServer){ - transactions = Transactions.find({}, {sort:{height:-1}}).fetch(); + if (Meteor.isServer) { + transactions = Transactions.find({}, { sort: { height: -1 } }).fetch(); transactionsExist = !!transactions; } - + return { loading, transactionsExist, transactions: transactionsExist ? transactions : {}, + transactionsCount: transactionsExist ? transactions.length : 0 }; })(List); \ No newline at end of file diff --git a/imports/ui/transactions/Transaction.jsx b/imports/ui/transactions/Transaction.jsx index d066ff286..9ed6ac7e3 100644 --- a/imports/ui/transactions/Transaction.jsx +++ b/imports/ui/transactions/Transaction.jsx @@ -12,32 +12,32 @@ import Coin from '/both/utils/coins.js'; import TimeStamp from '../components/TimeStamp.jsx'; const T = i18n.createComponent(); -export default class Transaction extends Component{ - constructor(props){ +export default class Transaction extends Component { + constructor(props) { super(props); - let showdown = require('showdown'); + let showdown = require('showdown'); showdown.setFlavor('github'); let denom = this.props.denom; } - render(){ - - - if (this.props.loading){ + render() { + + + if (this.props.loading) { return } - else{ - if (this.props.transactionExist){ + else { + if (this.props.transactionExist) { let tx = this.props.transaction; return Transaction {tx.txhash} on {Meteor.settings.public.chainName} | Big Dipper - + -

transactions.transaction {(!tx.code)?:}

- {(tx.code)? +

transactions.transaction {(!tx.code) ? : }

+ {(tx.code) ? - :''} +
: ''}
common.information
@@ -54,30 +54,29 @@ export default class Transaction extends Component{ {tx.txhash} common.height - {numbro(tx.height).format("0,0")} - {tx.block()? :null} + {numbro(tx.height).format("0,0")} + {tx.block() ? : null} transactions.fee - {(tx.tx.auth_info.fee.amount.length > 0)?tx.tx.auth_info.fee.amount.map((fee,i) => { + {(tx.tx.auth_info.fee.amount.length > 0) ? tx.tx.auth_info.fee.amount.map((fee, i) => { return {(new Coin(parseFloat(fee.amount), fee.denom)).toString(6)} - }):transactions.noFee} + }) : transactions.noFee} transactions.gasUsedWanted {numbro(tx.tx_response.gas_used).format("0,0")} / {numbro(tx.tx_response.gas_wanted).format("0,0")} transactions.memo - - +
transactions.activities
- {(tx.tx.body.messages && tx.tx.body.messages.length >0)?tx.tx.body.messages.map((msg,i) => { - return - }):''} + {(tx.tx.body.messages && tx.tx.body.messages.length > 0) ? tx.tx.body.messages.map((msg, i) => { + return + }) : ''}
} - else{ + else { return
transactions.noTxFound
} } diff --git a/imports/ui/transactions/TransactionRow.jsx b/imports/ui/transactions/TransactionRow.jsx index e60ffa90f..9ea186643 100644 --- a/imports/ui/transactions/TransactionRow.jsx +++ b/imports/ui/transactions/TransactionRow.jsx @@ -11,41 +11,51 @@ import numbro from 'numbro'; import Coin from '/both/utils/coins.js' import SentryBoundary from '../components/SentryBoundary.jsx'; import { Markdown } from 'react-showdown'; +import { + useRef, + useEffect, + createRef, +} from 'react'; -let showdown = require('showdown'); +let showdown = require('showdown'); showdown.setFlavor('github'); -export const TransactionRow = (props) => { - let tx = props.tx; - let homepage = window?.location?.pathname === '/' ? true : false; - - return - {(tx?.tx?.body?.messages && tx?.tx?.body?.messages.length >0)?tx?.tx?.body?.messages.map((msg,i) => { - return - }):''} - {!homepage ? {tx.txhash} : - {tx.txhash}} - schedule {tx.block()?:''}{(tx?.tx?.body?.memo && tx?.tx?.body?.memo != "")? - message - - - - :""} - {(!props.blockList) ? {numbro(tx.height).format("0,0")}:''} - {(!tx.code)?:} - {!homepage ? monetization_on {(tx?.tx?.auth_info?.fee?.amount.length > 0)?tx?.tx?.auth_info?.fee?.amount.map((fee,i) => { - return {(new Coin(parseFloat(fee.amount), (fee)?fee.denom:null)).toString(6)} - }) : No fee} : monetization_on {(tx?.tx?.auth_info?.fee?.amount.length > 0) ? tx?.tx?.auth_info?.fee?.amount.map((fee, i) => { - return {(new Coin(parseFloat(fee.amount), (fee) ? fee.denom : null)).toString(6)} - }) : No fee} } - {(tx.code)? - - - - :''} - -} +export class TransactionRow extends React.Component { + constructor(props) { + super(props); + this.myRef = React.createRef(); + } + render() { + let tx = this.props.tx; + let homepage = window?.location?.pathname === '/' ? true : false; + return
+ {(tx?.tx?.body?.messages && tx?.tx?.body?.messages.length > 0) ? tx?.tx?.body?.messages.map((msg, i) => { + return + }) : ''} + {!homepage ? {tx.txhash} : + {tx.txhash}} + schedule {tx.block() ? : ''}{(tx?.tx?.body?.memo && tx?.tx?.body?.memo != "") ? + message + + + + : ""} + {(!this.props.blockList) ? {numbro(tx.height).format("0,0")} : ''} + {(!tx.code) ? : } + {!homepage ? monetization_on {(tx?.tx?.auth_info?.fee?.amount.length > 0) ? tx?.tx?.auth_info?.fee?.amount.map((fee, i) => { + return {(new Coin(parseFloat(fee.amount), (fee) ? fee.denom : null)).toString(6)} + }) : No fee} : monetization_on {(tx?.tx?.auth_info?.fee?.amount.length > 0) ? tx?.tx?.auth_info?.fee?.amount.map((fee, i) => { + return {(new Coin(parseFloat(fee.amount), (fee) ? fee.denom : null)).toString(6)} + }) : No fee}} + {(tx.code) ? + + + + : ''} +
+ } +} \ No newline at end of file diff --git a/imports/ui/transactions/TransactionsList.jsx b/imports/ui/transactions/TransactionsList.jsx index 4a9544796..54bd62af4 100644 --- a/imports/ui/transactions/TransactionsList.jsx +++ b/imports/ui/transactions/TransactionsList.jsx @@ -12,19 +12,19 @@ import i18n from 'meteor/universe:i18n'; const T = i18n.createComponent(); -export default class Transactions extends Component{ - constructor(props){ +export default class TransactionsList extends Component { + constructor(props) { super(props); this.state = { - limit: props.homepage ? 16: Meteor.settings.public.initialPageSize, + limit: props.homepage ? 16 : Meteor.settings.public.initialPageSize, monikerDir: 1, votingPowerDir: -1, uptimeDir: -1, proposerDir: -1, priority: 2, loadmore: false, - sidebarOpen: (props?.location?.pathname.split("/transactions/").length == 2) + sidebarOpen: (props?.location?.pathname.split("/transactions/").length == 2), } this.onSetSidebarOpen = this.onSetSidebarOpen.bind(this); @@ -33,17 +33,17 @@ export default class Transactions extends Component{ isBottom(el) { return el.getBoundingClientRect().bottom <= window.innerHeight; } - + componentDidMount() { document.addEventListener('scroll', this.trackScrolling); } - + componentWillUnmount() { document.removeEventListener('scroll', this.trackScrolling); } - - componentDidUpdate(prevProps){ - if (this.props?.location?.pathname != prevProps?.location?.pathname){ + + componentDidUpdate(prevProps) { + if (this.props?.location?.pathname != prevProps?.location?.pathname) { this.setState({ sidebarOpen: (this.props?.location?.pathname.split("/transactions/").length == 2) }) @@ -53,37 +53,35 @@ export default class Transactions extends Component{ trackScrolling = () => { const wrappedElement = document.getElementById('transactions'); if (this.isBottom(wrappedElement)) { - // console.log('header bottom reached'); document.removeEventListener('scroll', this.trackScrolling); - this.setState({loadmore:true}); + this.setState({ loadmore: true }); this.setState({ - limit: this.state.limit+10 + limit: this.state.limit + 3000 }, (err, result) => { - if (!err){ + if (!err) { document.addEventListener('scroll', this.trackScrolling); } - if (result){ - this.setState({loadmore:false}); + if (result) { + this.setState({ loadmore: false }); } }) } }; onSetSidebarOpen(open) { - // console.log(open); - this.setState({ sidebarOpen: open }, (error, result) =>{ + this.setState({ sidebarOpen: open }, (error, result) => { let timer = Meteor.setTimeout(() => { - if (!open){ + if (!open) { this.props.history.push('/transactions'); } Meteor.clearTimeout(timer); - },500) + }, 500) }); - + } - render(){ - return !this.props.homepage ?
+ render() { + return !this.props.homepage ?
Latest Transactions on {Meteor.settings.public.chainName} | Big Dipper @@ -93,23 +91,24 @@ export default class Transactions extends Component{ - } open={this.state.sidebarOpen} onSetOpen={this.onSetSidebarOpen} - styles={{ sidebar: { - background: "white", - position: "fixed", - width: '85%', - zIndex: 4 - },overlay: { - zIndex: 3 - } }} + styles={{ + sidebar: { + background: "white", + position: "fixed", + width: '85%', + zIndex: 4 + }, overlay: { + zIndex: 3 + } + }} > } /> - - +
:
transactions.transactions
@@ -139,8 +138,6 @@ export default class Transactions extends Component{ - -
; diff --git a/package-lock.json b/package-lock.json index b71de92da..cfa3e4245 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4698,8 +4698,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "mute-stream": { "version": "0.0.7", @@ -5920,6 +5919,15 @@ "react-lifecycles-compat": "^3.0.4" } }, + "react-window": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.6.tgz", + "integrity": "sha512-8VwEEYyjz6DCnGBsd+MgkD0KJ2/OXFULyDtorIiTz+QzwoP94tBoA7CnbtyXMm+cCeAUER5KJcPtWl9cpKbOBg==", + "requires": { + "@babel/runtime": "^7.0.0", + "memoize-one": ">=3.1.1 <6" + } + }, "reactstrap": { "version": "8.9.0", "resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-8.9.0.tgz", @@ -6837,6 +6845,34 @@ "supercop.js": "^2.0.1", "varstruct": "^6.1.1", "websocket-stream": "^5.1.1" + }, + "dependencies": { + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "requires": { + "follow-redirects": "1.5.10" + } + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + } } }, "text-table": { diff --git a/package.json b/package.json index 0077af2f0..6f9f235e6 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "react-showdown": "^1.6.0", "react-sidebar": "^3.0.2", "react-toastify": "^4.5.2", + "react-window": "^1.8.6", "reactstrap": "^8.9.0", "rollup": "^2.49.0", "secp256k1": "^3.8.0",