From 959c7ee48894159bbfcf24389a54caede5df13f3 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Mon, 18 Jan 2021 16:30:05 +1000 Subject: [PATCH] LinkWindow, VirtualScroller: virtualise correctly --- .../src/views/apps/links/LinkResource.tsx | 32 ++++++-------- .../src/views/apps/links/LinkWindow.tsx | 30 ++++++++++++- .../views/apps/links/components/LinkItem.tsx | 20 +++++++-- .../src/views/components/VirtualScroller.tsx | 43 ++++++++----------- 4 files changed, 77 insertions(+), 48 deletions(-) diff --git a/pkg/interface/src/views/apps/links/LinkResource.tsx b/pkg/interface/src/views/apps/links/LinkResource.tsx index e7deda0853..f8a06a2a7d 100644 --- a/pkg/interface/src/views/apps/links/LinkResource.tsx +++ b/pkg/interface/src/views/apps/links/LinkResource.tsx @@ -60,30 +60,26 @@ export function LinkResource(props: LinkResourceProps) { } return ( - + { return ( - - - - - - + ); }} /> diff --git a/pkg/interface/src/views/apps/links/LinkWindow.tsx b/pkg/interface/src/views/apps/links/LinkWindow.tsx index dd7113a69b..62a1a15d8c 100644 --- a/pkg/interface/src/views/apps/links/LinkWindow.tsx +++ b/pkg/interface/src/views/apps/links/LinkWindow.tsx @@ -1,4 +1,6 @@ -import React, { useRef, useCallback, useEffect } from "react"; +import React, { useRef, useCallback, useEffect, useMemo } from "react"; +import { Col } from "@tlon/indigo-react"; +import bigInt from 'big-integer'; import { Association, Graph, @@ -7,10 +9,12 @@ import { LocalUpdateRemoteContentPolicy, Group, Rolodex, + S3State, } from "~/types"; import GlobalApi from "~/logic/api/global"; import VirtualScroller from "~/views/components/VirtualScroller"; import { LinkItem } from "./components/LinkItem"; +import LinkSubmit from "./components/LinkSubmit"; interface LinkWindowProps { association: Association; @@ -24,6 +28,7 @@ interface LinkWindowProps { group: Group; path: string; api: GlobalApi; + s3: S3State; } export function LinkWindow(props: LinkWindowProps) { const { graph, api, association } = props; @@ -42,12 +47,24 @@ export function LinkWindow(props: LinkWindowProps) { list.calculateVisibleItems(); }, [graph.size]); + const first = graph.peekLargest()?.[0]; + + const [,,ship, name] = association['app-path'].split('/'); + + const style = useMemo(() => + ({ + height: "100%", + width: "100%", + display: 'flex', + flexDirection: 'column', + alignItems: 'center' + }), []); return ( (virtualList.current = l ?? undefined)} origin="top" - style={{ height: "100%", width: "100%" }} + style={style} onStartReached={() => {}} onScroll={() => {}} data={graph} @@ -59,8 +76,17 @@ export function LinkWindow(props: LinkWindowProps) { const linkProps = { ...props, node, + measure, key: index.toString() }; + if(index.eq(first ?? bigInt.zero)) { + return ( + + + + + ) + } return ; }} loadRows={fetchLinks} diff --git a/pkg/interface/src/views/apps/links/components/LinkItem.tsx b/pkg/interface/src/views/apps/links/components/LinkItem.tsx index a8345ef0df..e34de6cfb0 100644 --- a/pkg/interface/src/views/apps/links/components/LinkItem.tsx +++ b/pkg/interface/src/views/apps/links/components/LinkItem.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect, useRef, useCallback } from 'react'; import { Link } from 'react-router-dom'; import { Row, Col, Anchor, Box, Text, Icon, Action } from '@tlon/indigo-react'; @@ -19,6 +19,7 @@ interface LinkItemProps { path: string; contacts: Rolodex; unreads: Unreads; + measure: (el: any) => void; } export const LinkItem = (props: LinkItemProps) => { @@ -29,9 +30,12 @@ export const LinkItem = (props: LinkItemProps) => { group, path, contacts, + measure, ...rest } = props; + const ref = useRef(null); + const URLparser = new RegExp( /((?:([\w\d\.-]+)\:\/\/?){1}(?:(www)\.?){0,1}(((?:[\w\d-]+\.)*)([\w\d-]+\.[\w\d]+))){1}(?:\:(\d+)){0,1}((\/(?:(?:[^\/\s\?]+\/)*))(?:([^\?\/\s#]+?(?:.[^\?\s]+){0,1}){0,1}(?:\?([^\s#]+)){0,1})){0,1}(?:#([^#\s]+)){0,1}/ ); @@ -70,9 +74,18 @@ export const LinkItem = (props: LinkItemProps) => { const markRead = () => { api.hark.markEachAsRead(props.association, '/', `/${index}`, 'link', 'link'); } + + + const onMeasure = useCallback(() => { + ref.current && measure(ref.current); + }, [ref.current, measure]) + + useEffect(() => { + onMeasure(); + }, [onMeasure]); + return ( - - + { url={contents[1].url} text={contents[0].text} unfold={true} + onLoad={onMeasure} style={{ alignSelf: 'center' }} oembedProps={{ p: 2, diff --git a/pkg/interface/src/views/components/VirtualScroller.tsx b/pkg/interface/src/views/components/VirtualScroller.tsx index e7a57ba54d..3dd67700dc 100644 --- a/pkg/interface/src/views/components/VirtualScroller.tsx +++ b/pkg/interface/src/views/components/VirtualScroller.tsx @@ -44,6 +44,8 @@ export default class VirtualScroller extends Component; } | undefined; + overscan = 150; + OVERSCAN_SIZE = 100; // Minimum number of messages on either side before loadRows is called constructor(props: VirtualScrollerProps) { @@ -53,7 +55,7 @@ export default class VirtualScroller extends Component { - totalHeight += this.heightOf(index); + totalHeight += Math.max(this.heightOf(index), 0); }); averageHeight = Number((totalHeight / this.props.data.size).toFixed()); totalHeight += (this.props.size - this.props.data.size) * averageHeight; @@ -136,36 +140,23 @@ export default class VirtualScroller extends Component(); - let startBuffer = new BigIntOrderedMap(); - let endBuffer = new BigIntOrderedMap(); const { scrollTop, offsetHeight: windowHeight } = this.window; - const { averageHeight } = this.state; + const { averageHeight, totalHeight } = this.state; const { data, size: totalSize, onCalculateVisibleItems } = this.props; - const overscan = Math.max(windowHeight / 2, 200); - [...data].forEach(([index, datum]) => { const height = this.heightOf(index); - if (startgap < (scrollTop - overscan) && !startGapFilled) { - startBuffer.set(index, datum); + if (startgap < (scrollTop - this.overscan) && !startGapFilled) { startgap += height; - } else if (heightShown < (windowHeight + overscan)) { + } else if (heightShown < (windowHeight + this.overscan)) { startGapFilled = true; visibleItems.set(index, datum); heightShown += height; - } else if (endBuffer.size < visibleItems.size) { - endBuffer.set(index, data.get(index)); - } else { - endgap += height; - } - }); - - - startBuffer.forEach((_datum, index) => { - startgap -= this.heightOf(index); + } }); + endgap = totalHeight - heightShown - startgap; const firstVisibleKey = visibleItems.peekSmallest()?.[0] ?? this.estimateIndexFromScrollTop(scrollTop)!; const smallest = data.peekSmallest(); @@ -184,7 +175,7 @@ export default class VirtualScroller extends Component { @@ -309,7 +302,7 @@ export default class VirtualScroller extends Component - + {indexesToRender.map(render)}