Browse Source

Prep for infinite scroll

Philipp Heckel 4 years ago
parent
commit
9757983046

+ 11 - 3
web/src/app/SubscriptionManager.js

@@ -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() {

+ 10 - 5
web/src/components/Navigation.js

@@ -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"/>

+ 3 - 4
web/src/components/Notifications.js

@@ -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}>