From 003738f95a2f4bee41b5e7ef6ecddac9f60416e7 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Mon, 8 Feb 2021 14:04:55 +1000 Subject: [PATCH] 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. --- pkg/interface/src/logic/lib/useLazyScroll.ts | 47 ++++++++++++------- .../src/logic/reducers/hark-update.ts | 7 ++- .../src/views/apps/notifications/inbox.tsx | 18 +++++-- 3 files changed, 52 insertions(+), 20 deletions(-) diff --git a/pkg/interface/src/logic/lib/useLazyScroll.ts b/pkg/interface/src/logic/lib/useLazyScroll.ts index f9e8c10eb..bd1c31a32 100644 --- a/pkg/interface/src/logic/lib/useLazyScroll.ts +++ b/pkg/interface/src/logic/lib/useLazyScroll.ts @@ -1,5 +1,6 @@ import { useEffect, RefObject, useRef, useState } from "react"; import _ from "lodash"; +import usePreviousValue from "./usePreviousValue"; export function distanceToBottom(el: HTMLElement) { const { scrollTop, scrollHeight, clientHeight } = el; @@ -11,28 +12,41 @@ export function distanceToBottom(el: HTMLElement) { export function useLazyScroll( ref: RefObject, margin: number, + count: number, loadMore: () => Promise ) { 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(() => { if (!ref.current) { return; } setIsDone(false); 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); const onScroll = (e: Event) => { @@ -40,12 +54,13 @@ export function useLazyScroll( loadUntil(el); }; - ref.current.addEventListener("scroll", onScroll); + ref.current.addEventListener("scroll", onScroll, { passive: true }); return () => { ref.current?.removeEventListener("scroll", onScroll); }; - }, [ref?.current]); + }, [ref?.current, count]); + - return isDone; + return { isDone, isLoading }; } diff --git a/pkg/interface/src/logic/reducers/hark-update.ts b/pkg/interface/src/logic/reducers/hark-update.ts index 8db766f53..418f2d0aa 100644 --- a/pkg/interface/src/logic/reducers/hark-update.ts +++ b/pkg/interface/src/logic/reducers/hark-update.ts @@ -385,7 +385,12 @@ function archive(json: any, state: HarkState) { const [archived, unarchived] = _.partition(timebox, (idxNotif) => 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; updateNotificationStats(state, index, 'notifications', (x) => x - newlyRead); } diff --git a/pkg/interface/src/views/apps/notifications/inbox.tsx b/pkg/interface/src/views/apps/notifications/inbox.tsx index 3bc95e7ec..12b7301cc 100644 --- a/pkg/interface/src/views/apps/notifications/inbox.tsx +++ b/pkg/interface/src/views/apps/notifications/inbox.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useCallback, useRef, useState } from "react"; import f from "lodash/fp"; 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 { Notifications, Rolodex, Timebox, IndexedNotification, Groups, GroupNotificationsConfig, NotificationGraphConfig } from "~/types"; 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: { notifications: Notifications; + notificationsSize: number; archive: Notifications; groups: Groups; showArchive?: boolean; @@ -99,7 +100,12 @@ export default function Inbox(props: { return api.hark.getMore(); }, [api]); - const loadedAll = useLazyScroll(scrollRef, 0.2, loadMore); + const { isDone, isLoading } = useLazyScroll( + scrollRef, + 0.2, + _.flatten(notifications).length, + loadMore + ); return ( @@ -122,11 +128,17 @@ export default function Inbox(props: { /> ); })} - {loadedAll && ( + {isDone && (
No more notifications
+ )} + {isLoading && ( +
+ +
)} + ); }