From 892d8c63d6392803698fdbe5dd719be5e4d0eacd Mon Sep 17 00:00:00 2001 From: Michal Szczepanski Date: Tue, 26 Sep 2023 21:01:00 +0200 Subject: [PATCH] feat: default-popup infinite scroll for obj-view --- .../segment/page-segment-add.command.ts | 1 - src/common/store/link-origin.store.ts | 1 - .../components/obj/obj-list.component.tsx | 1 + .../components/obj/obj-view.component.tsx | 73 +++++++++++++++---- .../components/board/board.component.tsx | 2 - .../sync-snapshot-incoming.command.ts | 4 +- 6 files changed, 60 insertions(+), 22 deletions(-) diff --git a/src/common/command/snapshot/segment/page-segment-add.command.ts b/src/common/command/snapshot/segment/page-segment-add.command.ts index db96344..ad09b46 100644 --- a/src/common/command/snapshot/segment/page-segment-add.command.ts +++ b/src/common/command/snapshot/segment/page-segment-add.command.ts @@ -18,7 +18,6 @@ import { BrowserStorage } from '@pinmenote/browser-api'; import { ICommand } from '../../../model/shared/common.dto'; import { ObjectStoreKeys } from '../../../keys/object.store.keys'; import { SegmentData } from '@pinmenote/page-compute'; -import { fnConsoleLog } from '../../../fn/fn-console'; export class PageSegmentAddCommand implements ICommand> { constructor(private content: SegmentData, private ref = true) {} diff --git a/src/common/store/link-origin.store.ts b/src/common/store/link-origin.store.ts index 02d886f..be6e153 100644 --- a/src/common/store/link-origin.store.ts +++ b/src/common/store/link-origin.store.ts @@ -15,7 +15,6 @@ * along with this program. If not, see . */ import { BrowserStorage } from '@pinmenote/browser-api'; -import { fnConsoleLog } from '../fn/fn-console'; export class LinkOriginStore { static readonly NOTE_ORIGIN = 'note:origin'; diff --git a/src/default-popup/components/obj/obj-list.component.tsx b/src/default-popup/components/obj/obj-list.component.tsx index 6db7d19..3e67e31 100644 --- a/src/default-popup/components/obj/obj-list.component.tsx +++ b/src/default-popup/components/obj/obj-list.component.tsx @@ -38,6 +38,7 @@ interface Props { } export const ObjListComponent: FunctionComponent = (props) => { + LogManager.log(`RENDER !!! ${props.objList.length}`); const [reRender, setReRender] = useState(false); const handlePinRemove = async (data: ObjDto) => { diff --git a/src/default-popup/components/obj/obj-view.component.tsx b/src/default-popup/components/obj/obj-view.component.tsx index cdf3b79..52d753c 100644 --- a/src/default-popup/components/obj/obj-view.component.tsx +++ b/src/default-popup/components/obj/obj-view.component.tsx @@ -15,7 +15,7 @@ * along with this program. If not, see . */ import { ObjDto, ObjPageDataDto, ObjTypeDto } from '../../../common/model/obj/obj.dto'; -import React, { FunctionComponent, useEffect, useState } from 'react'; +import React, { FunctionComponent, useEffect, useRef, useState } from 'react'; import { BusMessageType } from '../../../common/model/bus.model'; import { ObjGetHrefCommand } from '../../../common/command/obj/url/obj-get-href.command'; import { ObjGetOriginCommand } from '../../../common/command/obj/url/obj-get-origin.command'; @@ -33,11 +33,6 @@ interface Props { editNoteCallback: (obj: ObjDto) => void; } -interface FetchObjectsResult { - objs: ObjDto[]; - index: number; -} - const hrefFilter = (obj: ObjDto, href?: string) => { if ([ObjTypeDto.PageSnapshot, ObjTypeDto.PageElementSnapshot].includes(obj.type)) { if ((obj.data as ObjPageDto).snapshot.info.url.href === href) return true; @@ -51,10 +46,28 @@ const hrefFilter = (obj: ObjDto, href?: string) => { return false; }; -const fetchObjects = async (idList: number[], index: number, href?: string): Promise => { - if (index >= idList.length) return { objs: [], index }; +class Store { + static readonly limit = 10; + static fetching = false; + static hrefObjs: ObjDto[] = []; + static hrefIndex = 0; + static originObjs: ObjDto[] = []; + static originIndex = 0; + + static resetStore() { + this.hrefObjs = []; + this.hrefIndex = 0; + this.originObjs = []; + this.originIndex = 0; + } +} + +const fetchObjects = async (idList: number[], type: 'href' | 'origin', href?: string): Promise => { + let index = type === 'origin' ? Store.originIndex : Store.hrefIndex; + if (index >= idList.length) return false; const objs: ObjDto[] = []; for (index; index < idList.length; index++) { + if (objs.length === Store.limit) break; const obj = await new ObjGetCommand(idList[index]).execute(); // TODO check it - something is not deleting if (!obj) { @@ -64,36 +77,64 @@ const fetchObjects = async (idList: number[], index: number, href?: string): Pro if (hrefFilter(obj, href)) continue; objs.push(obj); } - return { objs, index }; + if (type === 'origin') { + Store.originObjs.push(...objs); + Store.originIndex = index; + } else { + Store.hrefObjs.push(...objs); + Store.hrefIndex = index; + } + return true; }; export const ObjViewComponent: FunctionComponent = (props) => { const [originObjs, setOriginObjs] = useState[]>([]); const [hrefObjs, setHrefObjs] = useState[]>([]); + const ref = useRef(null); useEffect(() => { // eslint-disable-next-line @typescript-eslint/no-floating-promises setTimeout(async () => await initUrl(), 100); const urlKey = TinyDispatcher.getInstance().addListener(BusMessageType.POP_UPDATE_URL, () => { - setTimeout(async () => await initUrl(), 100); + setTimeout(async () => { + Store.resetStore(); + await initUrl(); + }, 100); }); return () => { TinyDispatcher.getInstance().removeListener(BusMessageType.POP_UPDATE_URL, urlKey); }; - }, [props]); + }, []); const initUrl = async () => { LogManager.log('initUrl'); + if (Store.fetching) return; if (!PopupActiveTabStore.url) return; + Store.fetching = true; const hrefIds = await new ObjGetHrefCommand(PopupActiveTabStore.url).execute(); - const href = await fetchObjects(hrefIds, 0); - setHrefObjs(href.objs); + const hasHref = await fetchObjects(hrefIds, 'href'); + if (hasHref) setHrefObjs(Store.hrefObjs.concat()); const originIds = await new ObjGetOriginCommand(PopupActiveTabStore.url).execute(); - const origin = await fetchObjects(originIds, 0, PopupActiveTabStore.url.href); - setOriginObjs(origin.objs); + const hasOrigin = await fetchObjects(originIds, 'origin', PopupActiveTabStore.url.href); + if (hasOrigin) setOriginObjs(Store.originObjs.concat()); + Store.fetching = false; + }; + + const handleScroll = () => { + if (!ref.current) return; + if (Store.fetching) return; + const bottom = ref.current.scrollHeight - ref.current.clientHeight; + if (bottom - ref.current.scrollTop > 10) return; // too much up + setTimeout(async () => { + await initUrl(); + }, 200); }; return ( -
+
On this page
On {PopupActiveTabStore.url?.origin}
diff --git a/src/options-ui/components/board/board.component.tsx b/src/options-ui/components/board/board.component.tsx index 065e541..b71d6e5 100644 --- a/src/options-ui/components/board/board.component.tsx +++ b/src/options-ui/components/board/board.component.tsx @@ -23,7 +23,6 @@ import { ObjPdfDto } from '../../../common/model/obj/obj-pdf.dto'; import { PageNoteElement } from './page-note/page-note.element'; import { PageSnapshotElement } from './page-snapshot/page-snapshot.element'; import { PdfElement } from './pdf/pdf.element'; -import { fnConsoleLog } from '../../../common/fn/fn-console'; export const BoardComponent: FunctionComponent = () => { const [objData, setObjData] = useState(BoardStore.objList); @@ -50,7 +49,6 @@ export const BoardComponent: FunctionComponent = () => { }; const handleScroll = () => { - fnConsoleLog('handleScroll'); if (!ref.current) return; if (BoardStore.isLast) return; // last element so return const bottom = ref.current.scrollHeight - ref.current.clientHeight; diff --git a/src/service-worker/command/sync/incoming/sync-snapshot-incoming.command.ts b/src/service-worker/command/sync/incoming/sync-snapshot-incoming.command.ts index 0e19aa9..d24c585 100644 --- a/src/service-worker/command/sync/incoming/sync-snapshot-incoming.command.ts +++ b/src/service-worker/command/sync/incoming/sync-snapshot-incoming.command.ts @@ -118,8 +118,8 @@ export class SyncSnapshotIncomingCommand implements ICommand> { try { src = await UrlFactory.toDataUri(segmentData); // @vane WORKAROUND FIX convert image/svg -> image/svg+xml to render correctly inside tag - const check = 'data:image/svg'; - if (src.startsWith(check)) src = 'data:image/svg+xml' + src.substring(check.length); + if (child.mimeType == 'data:image/svg+xml' && !src.startsWith(child.mimeType)) + src = 'data:image/svg+xml' + src.substring('data:image/svg'.length); } catch (e) { const buffer = await segmentData.arrayBuffer(); const textData = new TextDecoder().decode(buffer);