Inbox: make useLazyScroll account for archived notifications

useLazyScroll was not accounting for the case where a notification would
be archived. This would mean that archiving a lot of notifications
without scrolling would erroneously not load more. Accounts for this
case by passing a count argument to useLazyScroll, which is then
compared against its previous value. If the previous value is greater
than the new value, (i.e. a notification was archived) then check if we
should load more.
This commit is contained in:
Liam Fitzgerald 2021-02-08 14:04:55 +10:00
parent 71ae452132
commit 003738f95a
No known key found for this signature in database
GPG Key ID: D390E12C61D1CFFB
3 changed files with 52 additions and 20 deletions

View File

@ -1,5 +1,6 @@
import { useEffect, RefObject, useRef, useState } from "react"; import { useEffect, RefObject, useRef, useState } from "react";
import _ from "lodash"; import _ from "lodash";
import usePreviousValue from "./usePreviousValue";
export function distanceToBottom(el: HTMLElement) { export function distanceToBottom(el: HTMLElement) {
const { scrollTop, scrollHeight, clientHeight } = el; const { scrollTop, scrollHeight, clientHeight } = el;
@ -11,28 +12,41 @@ export function distanceToBottom(el: HTMLElement) {
export function useLazyScroll( export function useLazyScroll(
ref: RefObject<HTMLElement>, ref: RefObject<HTMLElement>,
margin: number, margin: number,
count: number,
loadMore: () => Promise<boolean> loadMore: () => Promise<boolean>
) { ) {
const [isDone, setIsDone] = useState(false); const [isDone, setIsDone] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const oldCount = usePreviousValue(count);
const loadUntil = (el: HTMLElement) => {
if (!isDone && distanceToBottom(el) < margin) {
setIsLoading(true);
return loadMore().then((done) => {
setIsLoading(false);
if (done) {
setIsDone(true);
return Promise.resolve();
}
return loadUntil(el);
});
}
setIsLoading(false);
return Promise.resolve();
};
useEffect(() => {
if((oldCount > count) && ref.current) {
loadUntil(ref.current);
}
}, [count]);
useEffect(() => { useEffect(() => {
if (!ref.current) { if (!ref.current) {
return; return;
} }
setIsDone(false); setIsDone(false);
const scroll = ref.current; const scroll = ref.current;
const loadUntil = (el: HTMLElement) => {
if (!isDone && distanceToBottom(el) < margin) {
return loadMore().then((done) => {
if (done) {
setIsDone(true);
return Promise.resolve();
}
return loadUntil(el);
});
}
return Promise.resolve();
};
loadUntil(scroll); loadUntil(scroll);
const onScroll = (e: Event) => { const onScroll = (e: Event) => {
@ -40,12 +54,13 @@ export function useLazyScroll(
loadUntil(el); loadUntil(el);
}; };
ref.current.addEventListener("scroll", onScroll); ref.current.addEventListener("scroll", onScroll, { passive: true });
return () => { return () => {
ref.current?.removeEventListener("scroll", onScroll); ref.current?.removeEventListener("scroll", onScroll);
}; };
}, [ref?.current]); }, [ref?.current, count]);
return isDone; return { isDone, isLoading };
} }

View File

@ -385,7 +385,12 @@ function archive(json: any, state: HarkState) {
const [archived, unarchived] = _.partition(timebox, (idxNotif) => const [archived, unarchived] = _.partition(timebox, (idxNotif) =>
notifIdxEqual(index, idxNotif.index) notifIdxEqual(index, idxNotif.index)
); );
state.notifications.set(time, unarchived); if(unarchived.length === 0) {
console.log('deleting entire timebox');
state.notifications.delete(time);
} else {
state.notifications.set(time, unarchived);
}
const newlyRead = archived.filter(x => !x.notification.read).length; const newlyRead = archived.filter(x => !x.notification.read).length;
updateNotificationStats(state, index, 'notifications', (x) => x - newlyRead); updateNotificationStats(state, index, 'notifications', (x) => x - newlyRead);
} }

View File

@ -1,7 +1,7 @@
import React, { useEffect, useCallback, useRef, useState } from "react"; import React, { useEffect, useCallback, useRef, useState } from "react";
import f from "lodash/fp"; import f from "lodash/fp";
import _ from "lodash"; import _ from "lodash";
import { Icon, Col, Row, Box, Text, Anchor, Rule, Center } from "@tlon/indigo-react"; import { Icon, Col, Row, Box, Text, Anchor, Rule, Center, LoadingSpinner } from "@tlon/indigo-react";
import moment from "moment"; import moment from "moment";
import { Notifications, Rolodex, Timebox, IndexedNotification, Groups, GroupNotificationsConfig, NotificationGraphConfig } from "~/types"; import { Notifications, Rolodex, Timebox, IndexedNotification, Groups, GroupNotificationsConfig, NotificationGraphConfig } from "~/types";
import { MOMENT_CALENDAR_DATE, daToUnix, resourceAsPath } from "~/logic/lib/util"; import { MOMENT_CALENDAR_DATE, daToUnix, resourceAsPath } from "~/logic/lib/util";
@ -35,6 +35,7 @@ function filterNotification(associations: Associations, groups: string[]) {
export default function Inbox(props: { export default function Inbox(props: {
notifications: Notifications; notifications: Notifications;
notificationsSize: number;
archive: Notifications; archive: Notifications;
groups: Groups; groups: Groups;
showArchive?: boolean; showArchive?: boolean;
@ -99,7 +100,12 @@ export default function Inbox(props: {
return api.hark.getMore(); return api.hark.getMore();
}, [api]); }, [api]);
const loadedAll = useLazyScroll(scrollRef, 0.2, loadMore); const { isDone, isLoading } = useLazyScroll(
scrollRef,
0.2,
_.flatten(notifications).length,
loadMore
);
return ( return (
@ -122,11 +128,17 @@ export default function Inbox(props: {
/> />
); );
})} })}
{loadedAll && ( {isDone && (
<Center mt="2" borderTop={notifications.length !== 0 ? 1 : 0} borderTopColor="washedGray" width="100%" height="96px"> <Center mt="2" borderTop={notifications.length !== 0 ? 1 : 0} borderTopColor="washedGray" width="100%" height="96px">
<Text gray fontSize="1">No more notifications</Text> <Text gray fontSize="1">No more notifications</Text>
</Center> </Center>
)}
{isLoading && (
<Center mt="2" borderTop={notifications.length !== 0 ? 1 : 0} borderTopColor="washedGray" width="100%" height="96px">
<LoadingSpinner />
</Center>
)} )}
</Col> </Col>
); );
} }