-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Infinite scrolling #411
Infinite scrolling #411
Changes from 13 commits
a836a2d
152235d
bc5a894
b97d99d
c180230
7e20316
7ed54f3
1b1c951
098b449
80ea5db
56290c0
b973bf5
0939aab
fedf5d7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,49 +14,67 @@ | |
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
import { Alert, Box, Typography, useTheme } from "@mui/material"; | ||
import { useQuery } from "@tanstack/react-query"; | ||
import { Alert, Box, LinearProgress, Typography, useTheme } from "@mui/material"; | ||
import { useInfiniteQuery } from "@tanstack/react-query"; | ||
import { t } from "i18next"; | ||
import { fetchEvents } from "../queries/events"; | ||
import { Event } from "./Event"; | ||
import { useContext } from "react"; | ||
import { ApplicationContext } from "../contexts/ApplicationContext"; | ||
import { altDarkModeScrollbarStyle, altLightModeScrollbarStyle } from "../themes/default"; | ||
import InfiniteScroll from "react-infinite-scroll-component"; | ||
import { IEvent } from "../interfaces"; | ||
|
||
export const Events: React.FC = () => { | ||
|
||
const { lastBlockWithTransactions } = useContext(ApplicationContext); | ||
const theme = useTheme(); | ||
const addedStyle = theme.palette.mode === 'light'? altLightModeScrollbarStyle : altDarkModeScrollbarStyle; | ||
|
||
const { data: events, error, isFetching } = useQuery({ | ||
const { data: events, fetchNextPage, hasNextPage, error } = useInfiniteQuery({ | ||
queryKey: ["events", lastBlockWithTransactions], | ||
queryFn: () => fetchEvents(), | ||
queryFn: ({ pageParam }) => fetchEvents(pageParam), | ||
initialPageParam: undefined as IEvent | undefined, | ||
getNextPageParam: (lastPage) => {return lastPage[lastPage.length - 1]}, | ||
}); | ||
|
||
if(isFetching) { | ||
return <></>; | ||
} | ||
const theme = useTheme(); | ||
const addedStyle = theme.palette.mode === 'light' ? altLightModeScrollbarStyle : altDarkModeScrollbarStyle; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Think this should be moved to a utility function or a hook There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. I moved it into an utility function (just 1 line) and applied it to every place where this was being used. |
||
|
||
if (error) { | ||
return <Alert sx={{ margin: '30px' }} severity="error" variant="filled">{error.message}</Alert> | ||
} | ||
|
||
if (events?.pages === undefined) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should use skeleton loader eventually There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Absolutely agree, we would need to work together on a design that allows a loader to be shown without running into the issue of showing up and disappearing immediately which makes it appear as if the screen flickers. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good. I think the |
||
return <></>; | ||
} | ||
|
||
return ( | ||
<> | ||
<Typography align="center" variant="h5" sx={{ marginBottom: '20px' }}> | ||
{t("events")} | ||
</Typography> | ||
<Box | ||
id="scrollableDivEvents" | ||
sx={{ | ||
height: "calc(100vh - 170px)", | ||
paddingRight: "15px", | ||
...addedStyle | ||
}} | ||
> | ||
{events?.map((event) => ( | ||
<Event key={`${event.blockNumber}-${event.logIndex}`} event={event} /> | ||
))} | ||
<InfiniteScroll | ||
scrollableTarget="scrollableDivEvents" | ||
dataLength={events.pages.length} | ||
next={() => fetchNextPage()} | ||
hasMore={hasNextPage} | ||
loader={<LinearProgress />} | ||
> | ||
{ | ||
events.pages.map(eventArray => eventArray.map( | ||
(event) => ( | ||
<Event key={`${event.blockNumber}-${event.logIndex}`} event={event} /> | ||
) | ||
)) | ||
} | ||
</InfiniteScroll> | ||
</Box> | ||
</> | ||
); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,17 +14,19 @@ | |
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
import { AppBar, Box, Grid2, IconButton, Tab, Tabs, Toolbar, Tooltip, useMediaQuery, useTheme } from "@mui/material"; | ||
import { AppBar, Box, Button, Grid2, IconButton, Tab, Tabs, ToggleButton, ToggleButtonGroup, Toolbar, Tooltip, useMediaQuery, useTheme } from "@mui/material"; | ||
import { useContext, useState } from "react"; | ||
import { useTranslation } from "react-i18next"; | ||
import { useLocation, useNavigate } from "react-router-dom"; | ||
import Brightness4Icon from '@mui/icons-material/Brightness4'; | ||
import { ApplicationContext } from "../contexts/ApplicationContext"; | ||
|
||
import PlayArrowIcon from '@mui/icons-material/PlayArrow'; | ||
import PauseIcon from '@mui/icons-material/Pause'; | ||
import RefreshIcon from '@mui/icons-material/Refresh'; | ||
|
||
export const Header: React.FC = () => { | ||
|
||
const { colorMode } = useContext(ApplicationContext); | ||
const { colorMode, autoRefreshEnabled, setAutoRefreshEnabled, refreshRequired, refresh } = useContext(ApplicationContext); | ||
const { t } = useTranslation(); | ||
const navigate = useNavigate(); | ||
const pathname = useLocation().pathname.toLowerCase(); | ||
|
@@ -53,37 +55,80 @@ export const Header: React.FC = () => { | |
} | ||
}; | ||
|
||
const handleAutoRefreshChange = (value: string) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Absolutely, updated the code to const handleAutoRefreshChange = (value: 'play' | 'pause') => { |
||
switch (value) { | ||
case 'play': setAutoRefreshEnabled(true); break; | ||
case 'pause': setAutoRefreshEnabled(false); break; | ||
} | ||
}; | ||
|
||
return ( | ||
<> | ||
<AppBar> | ||
<Toolbar sx={{ backgroundColor: theme => theme.palette.background.paper }}> | ||
<Box sx={{ width: '100%', maxWidth: '1270px', marginLeft: 'auto', marginRight: 'auto' }}> | ||
<Grid2 container alignItems="center" > | ||
<Grid2 size={{ xs: 12, sm: 12, md: 4 }}> | ||
<img src={theme.palette.mode === 'dark' ? | ||
'/ui/paladin-title-dark.svg' : '/ui/paladin-title-light.svg' | ||
} style={{ marginTop: '7px' }} /> | ||
</Grid2> | ||
<Grid2 size={{ xs: 12, sm: 12, md: 4 }} alignContent="center"> | ||
<Tabs value={tab} onChange={(_event, value) => handleNavigation(value)} centered> | ||
<Tab sx={{ textTransform: 'none' }} label={t('indexer')} /> | ||
<Tab sx={{ textTransform: 'none' }} label={t('submissions')} /> | ||
<Tab sx={{ textTransform: 'none' }} label={t('registry')} /> | ||
</Tabs> | ||
</Grid2> | ||
<Grid2 size={{ xs: 12, sm: 12, md: 4 }} textAlign="right"> | ||
<Tooltip arrow title={t('switchThemeMode')}> | ||
<IconButton onClick={() => colorMode.toggleColorMode()}> | ||
<Brightness4Icon /> | ||
</IconButton> | ||
</Tooltip> | ||
<Grid2 container alignItems="center"> | ||
<Grid2 size={{ xs: 12, sm: 12, md: 4 }} textAlign={lessThanMedium ? 'center' : 'left'}> | ||
<img src={theme.palette.mode === 'dark' ? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are probably many instances of this ternary. I suggest having a util function:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can certainly add the utility. For me to better understand the snippet above, would the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can be |
||
'/ui/paladin-title-dark.svg' : '/ui/paladin-title-light.svg' | ||
} style={{ marginTop: '7px' }} /> | ||
</Grid2> | ||
<Grid2 size={{ xs: 12, sm: 12, md: 4 }} alignContent="center"> | ||
<Tabs | ||
TabIndicatorProps={{ style: { height: '4px' } }} | ||
value={tab} onChange={(_event, value) => handleNavigation(value)} centered> | ||
<Tab sx={{ textTransform: 'none' }} label={t('indexer')} /> | ||
<Tab sx={{ textTransform: 'none' }} label={t('submissions')} /> | ||
<Tab sx={{ textTransform: 'none' }} label={t('registry')} /> | ||
</Tabs> | ||
</Grid2> | ||
<Grid2 size={{ xs: 12, sm: 12, md: 4 }}> | ||
<Grid2 container justifyContent={lessThanMedium ? 'center' : 'right'} spacing={1} alignItems="center" | ||
sx={{ padding: lessThanMedium ? '20px' : undefined }}> | ||
{refreshRequired && | ||
<Grid2> | ||
<Button size="small" startIcon={<RefreshIcon />} variant="outlined" sx={{ textTransform: 'none', borderRadius: '20px'}} | ||
onClick={() => refresh()}> | ||
{t('newData')} | ||
</Button> | ||
</Grid2>} | ||
<Grid2> | ||
<ToggleButtonGroup exclusive onChange={(_event, value) => handleAutoRefreshChange(value)} value={autoRefreshEnabled ? 'play' : 'pause'}> | ||
<Tooltip arrow title={t('autoRefreshOn')} | ||
slotProps={{ popper: { modifiers: [{ name: 'offset', options: { offset: [0, -6] }, }] } }} | ||
> | ||
<ToggleButton color="primary" value="play"> | ||
<PlayArrowIcon fontSize="small" /> | ||
</ToggleButton> | ||
</Tooltip> | ||
<Tooltip arrow title={t('autoRefreshOff')} | ||
slotProps={{ popper: { modifiers: [{ name: 'offset', options: { offset: [0, -6] }, }] } }} | ||
> | ||
<ToggleButton color="primary" value="pause"> | ||
<PauseIcon fontSize="small" /> | ||
</ToggleButton> | ||
</Tooltip> | ||
</ToggleButtonGroup> | ||
</Grid2> | ||
<Grid2> | ||
<Tooltip arrow title={t('switchThemeMode')} | ||
slotProps={{ popper: { modifiers: [{ name: 'offset', options: { offset: [0, -4] }, }] } }} | ||
> | ||
<IconButton onClick={() => colorMode.toggleColorMode()}> | ||
<Brightness4Icon /> | ||
</IconButton> | ||
</Tooltip> | ||
</Grid2> | ||
</Grid2> | ||
</Grid2> | ||
</Grid2> | ||
</Grid2> | ||
</Box> | ||
</Toolbar> | ||
</AppBar> | ||
<Box sx={{ height: theme => lessThanMedium? '134px' : | ||
theme.mixins.toolbar }} /> | ||
<Box sx={{ | ||
height: theme => lessThanMedium ? '190px' : | ||
theme.mixins.toolbar | ||
}} /> | ||
</> | ||
); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check for
lastPage.length === 0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely, just like the case above, this has been updated to: