Skip to content

Commit

Permalink
Merge pull request #411 from gabriel-indik/events-infinite-scroll
Browse files Browse the repository at this point in the history
Infinite scrolling
  • Loading branch information
gabriel-indik authored Nov 18, 2024
2 parents 55c0aff + fedf5d7 commit 654f3ff
Show file tree
Hide file tree
Showing 17 changed files with 453 additions and 220 deletions.
35 changes: 35 additions & 0 deletions ui/client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ui/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^15.0.2",
"react-infinite-scroll-component": "^6.1.0",
"react-json-pretty": "^2.2.0",
"react-router-dom": "^6.26.2"
},
Expand Down
47 changes: 32 additions & 15 deletions ui/client/src/components/Events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,49 +14,66 @@
// 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 { getAltModeScrollBarStyle } 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.length > 0? lastPage[lastPage.length - 1] : undefined },
});

if(isFetching) {
return <></>;
}

const theme = useTheme();

if (error) {
return <Alert sx={{ margin: '30px' }} severity="error" variant="filled">{error.message}</Alert>
}

if (events?.pages === undefined) {
return <></>;
}

return (
<>
<Typography align="center" variant="h5" sx={{ marginBottom: '20px' }}>
{t("events")}
</Typography>
<Box
id="scrollableDivEvents"
sx={{
height: "calc(100vh - 170px)",
paddingRight: "15px",
...addedStyle
...getAltModeScrollBarStyle(theme.palette.mode)
}}
>
{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>
</>
);
Expand Down
95 changes: 70 additions & 25 deletions ui/client/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -53,37 +55,80 @@ export const Header: React.FC = () => {
}
};

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' ?
'/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
}} />
</>
);

Expand Down
2 changes: 1 addition & 1 deletion ui/client/src/components/Transaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const Transaction: React.FC<Props> = ({

const [viewDetailsDialogOpen, setViewDetailsDialogOpen] = useState(false);
const receiptCount = (transactionReceipts && transactionReceipts.length) ? transactionReceipts.length : 0;
const receiptIsPrivate = (transactionReceipts && transactionReceipts.length && transactionReceipts[0].domain !== '');
const receiptIsPrivate = (transactionReceipts && transactionReceipts.length && transactionReceipts[0].domain !== undefined);
const typeKey =
receiptCount > 1 ? 'atomicNumber' :
receiptIsPrivate ? 'private' :
Expand Down
Loading

0 comments on commit 654f3ff

Please sign in to comment.