Prep for infinite scroll

This commit is contained in:
Philipp Heckel 2022-03-07 20:11:58 -05:00
parent 5bed926323
commit 9757983046
3 changed files with 24 additions and 12 deletions

View File

@ -35,11 +35,19 @@ class SubscriptionManager {
return db.subscriptions.toCollection().first(); // May be undefined return db.subscriptions.toCollection().first(); // May be undefined
} }
async getNotifications(subscriptionId) { async getNotifications(subscriptionId, offset) {
// This is quite awkward, but it is the recommended approach as per the Dexie docs.
// It's actually fine, because the reading and filtering is quite fast. The rendering is what's
// killing performance. See https://dexie.org/docs/Collection/Collection.offset()#a-better-paging-approach
const pageSize = 20;
return db.notifications return db.notifications
.where({ subscriptionId: subscriptionId }) .orderBy("time") // Sort by time first
.filter(n => n.subscriptionId === subscriptionId)
.offset(offset)
.limit(pageSize)
.reverse() .reverse()
.sortBy("time"); // Inefficient, but there is no other way (see docs) .toArray();
} }
async getAllNotifications() { async getAllNotifications() {

View File

@ -9,7 +9,6 @@ import Toolbar from "@mui/material/Toolbar";
import Divider from "@mui/material/Divider"; import Divider from "@mui/material/Divider";
import List from "@mui/material/List"; import List from "@mui/material/List";
import SettingsIcon from "@mui/icons-material/Settings"; import SettingsIcon from "@mui/icons-material/Settings";
import HomeIcon from '@mui/icons-material/Home';
import AddIcon from "@mui/icons-material/Add"; import AddIcon from "@mui/icons-material/Add";
import SubscribeDialog from "./SubscribeDialog"; import SubscribeDialog from "./SubscribeDialog";
import {Alert, AlertTitle, Badge, CircularProgress, ListSubheader} from "@mui/material"; import {Alert, AlertTitle, Badge, CircularProgress, ListSubheader} from "@mui/material";
@ -19,6 +18,7 @@ import {subscriptionRoute, topicShortUrl, topicUrl} from "../app/utils";
import {ConnectionState} from "../app/Connection"; import {ConnectionState} from "../app/Connection";
import {useLocation, useNavigate} from "react-router-dom"; import {useLocation, useNavigate} from "react-router-dom";
import subscriptionManager from "../app/SubscriptionManager"; import subscriptionManager from "../app/SubscriptionManager";
import {ChatBubble} from "@mui/icons-material";
const navWidth = 240; const navWidth = 240;
@ -81,19 +81,24 @@ const NavList = (props) => {
<Toolbar sx={{ display: { xs: 'none', sm: 'block' } }}/> <Toolbar sx={{ display: { xs: 'none', sm: 'block' } }}/>
<List component="nav" sx={{ paddingTop: (showGrantPermissionsBox) ? '0' : '' }}> <List component="nav" sx={{ paddingTop: (showGrantPermissionsBox) ? '0' : '' }}>
{showGrantPermissionsBox && <PermissionAlert onRequestPermissionClick={props.requestNotificationPermission}/>} {showGrantPermissionsBox && <PermissionAlert onRequestPermissionClick={props.requestNotificationPermission}/>}
{!showSubscriptionsList &&
<ListItemButton onClick={() => navigate("/")} selected={location.pathname === "/"}>
<ListItemIcon><ChatBubble/></ListItemIcon>
<ListItemText primary="All notifications"/>
</ListItemButton>}
{showSubscriptionsList && {showSubscriptionsList &&
<> <>
<ListSubheader>Subscribed topics</ListSubheader> <ListSubheader>Subscribed topics</ListSubheader>
<ListItemButton onClick={() => navigate("/")} selected={location.pathname === "/"}>
<ListItemIcon><ChatBubble/></ListItemIcon>
<ListItemText primary="All notifications"/>
</ListItemButton>
<SubscriptionList <SubscriptionList
subscriptions={props.subscriptions} subscriptions={props.subscriptions}
selectedSubscription={props.selectedSubscription} selectedSubscription={props.selectedSubscription}
/> />
<Divider sx={{my: 1}}/> <Divider sx={{my: 1}}/>
</>} </>}
<ListItemButton onClick={() => navigate("/")} selected={location.pathname === "/"}>
<ListItemIcon><HomeIcon/></ListItemIcon>
<ListItemText primary="Home"/>
</ListItemButton>
<ListItemButton onClick={() => navigate("/settings")} selected={location.pathname === "/settings"}> <ListItemButton onClick={() => navigate("/settings")} selected={location.pathname === "/settings"}>
<ListItemIcon><SettingsIcon/></ListItemIcon> <ListItemIcon><SettingsIcon/></ListItemIcon>
<ListItemText primary="Settings"/> <ListItemText primary="Settings"/>

View File

@ -40,19 +40,18 @@ const AllSubscriptions = () => {
const SingleSubscription = (props) => { const SingleSubscription = (props) => {
const subscription = props.subscription; const subscription = props.subscription;
const notifications = useLiveQuery(() => subscriptionManager.getNotifications(subscription.id), [subscription]); const [offset, setOffset] = useState(0);
const notifications = useLiveQuery(() => subscriptionManager.getNotifications(subscription.id, offset), [subscription, offset]);
if (notifications === null || notifications === undefined) { if (notifications === null || notifications === undefined) {
return <Loading/>; return <Loading/>;
} else if (notifications.length === 0) { } else if (notifications.length === 0) {
return <NoNotifications subscription={subscription}/>; return <NoNotifications subscription={subscription}/>;
} }
return <NotificationList notifications={notifications}/>; return <NotificationList notifications={notifications} onScroll={() => setOffset(prev => prev + 20)}/>;
} }
const NotificationList = (props) => { const NotificationList = (props) => {
const sortedNotifications = props.notifications; const sortedNotifications = props.notifications;
/*const sortedNotifications = Array.from(props.notifications)
.sort((a, b) => a.time < b.time ? 1 : -1);*/
return ( return (
<Container maxWidth="md" sx={{marginTop: 3, marginBottom: 3}}> <Container maxWidth="md" sx={{marginTop: 3, marginBottom: 3}}>
<Stack spacing={3}> <Stack spacing={3}>