feat: default-popup infinite scroll for obj-view
This commit is contained in:
parent
8b28017e39
commit
892d8c63d6
@ -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<T> implements ICommand<Promise<void>> {
|
||||
constructor(private content: SegmentData<T>, private ref = true) {}
|
||||
|
@ -15,7 +15,6 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { BrowserStorage } from '@pinmenote/browser-api';
|
||||
import { fnConsoleLog } from '../fn/fn-console';
|
||||
|
||||
export class LinkOriginStore {
|
||||
static readonly NOTE_ORIGIN = 'note:origin';
|
||||
|
@ -38,6 +38,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export const ObjListComponent: FunctionComponent<Props> = (props) => {
|
||||
LogManager.log(`RENDER !!! ${props.objList.length}`);
|
||||
const [reRender, setReRender] = useState(false);
|
||||
|
||||
const handlePinRemove = async (data: ObjDto<ObjPinDto>) => {
|
||||
|
@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<ObjPageNoteDto>) => void;
|
||||
}
|
||||
|
||||
interface FetchObjectsResult {
|
||||
objs: ObjDto<ObjPageDataDto>[];
|
||||
index: number;
|
||||
}
|
||||
|
||||
const hrefFilter = (obj: ObjDto<ObjPageDataDto>, 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<ObjPageDataDto>, href?: string) => {
|
||||
return false;
|
||||
};
|
||||
|
||||
const fetchObjects = async (idList: number[], index: number, href?: string): Promise<FetchObjectsResult> => {
|
||||
if (index >= idList.length) return { objs: [], index };
|
||||
class Store {
|
||||
static readonly limit = 10;
|
||||
static fetching = false;
|
||||
static hrefObjs: ObjDto<ObjPageDataDto>[] = [];
|
||||
static hrefIndex = 0;
|
||||
static originObjs: ObjDto<ObjPageDataDto>[] = [];
|
||||
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<boolean> => {
|
||||
let index = type === 'origin' ? Store.originIndex : Store.hrefIndex;
|
||||
if (index >= idList.length) return false;
|
||||
const objs: ObjDto<ObjPageDataDto>[] = [];
|
||||
for (index; index < idList.length; index++) {
|
||||
if (objs.length === Store.limit) break;
|
||||
const obj = await new ObjGetCommand<ObjPageDataDto>(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> = (props) => {
|
||||
const [originObjs, setOriginObjs] = useState<ObjDto<ObjPageDataDto>[]>([]);
|
||||
const [hrefObjs, setHrefObjs] = useState<ObjDto<ObjPageDataDto>[]>([]);
|
||||
const ref = useRef<HTMLDivElement>(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 (
|
||||
<div style={{ maxHeight: '420px', display: 'flex', flexDirection: 'column', overflow: 'auto' }}>
|
||||
<div
|
||||
ref={ref}
|
||||
style={{ maxHeight: '420px', display: 'flex', flexDirection: 'column', overflow: 'auto' }}
|
||||
onScroll={handleScroll}
|
||||
>
|
||||
<div style={{ fontWeight: 'bold', fontSize: '14px' }}>On this page</div>
|
||||
<ObjListComponent objList={hrefObjs} editNoteCallback={props.editNoteCallback} />
|
||||
<div style={{ fontWeight: 'bold', fontSize: '14px' }}>On {PopupActiveTabStore.url?.origin}</div>
|
||||
|
@ -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<ObjDto[]>(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;
|
||||
|
@ -118,8 +118,8 @@ export class SyncSnapshotIncomingCommand implements ICommand<Promise<boolean>> {
|
||||
try {
|
||||
src = await UrlFactory.toDataUri(segmentData);
|
||||
// @vane WORKAROUND FIX convert image/svg -> image/svg+xml to render correctly inside <img> 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);
|
||||
|
Loading…
Reference in New Issue
Block a user