From 2dddf1b837f1ef164ce8cbbf9b7a5ae45b57c1ee Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Fri, 26 Mar 2021 16:52:50 -0500 Subject: [PATCH] group-feed: improve performance and make mobile load --- pkg/interface/src/logic/lib/withMemo.js | 20 ++ .../views/landscape/components/GroupsPane.tsx | 1 - .../landscape/components/Home/GroupFeed.js | 29 ++- .../landscape/components/Home/GroupHome.js | 4 +- .../components/Home/Post/PostContent.js | 5 +- .../components/Home/Post/PostFeed.js | 22 +- .../components/Home/Post/PostHeader.js | 5 +- .../components/Home/Post/PostInput.js | 4 +- .../components/Home/Post/PostItem.js | 7 +- .../components/Home/Post/PostReplies.js | 200 +++++++++--------- .../components/Home/Post/PostTimeline.js | 154 +++++++------- pkg/interface/src/views/landscape/index.tsx | 165 +++++++-------- 12 files changed, 322 insertions(+), 294 deletions(-) create mode 100644 pkg/interface/src/logic/lib/withMemo.js diff --git a/pkg/interface/src/logic/lib/withMemo.js b/pkg/interface/src/logic/lib/withMemo.js new file mode 100644 index 000000000..77b7d038a --- /dev/null +++ b/pkg/interface/src/logic/lib/withMemo.js @@ -0,0 +1,20 @@ +import { memo } from 'react'; + +const withMemo = (Component, checkedProps) => { + function areEqual(prevProps, nextProps) { + let isEqual = true; + for (let i = 0; i < checkedProps.length; i++) { + const checkedProp = checkedProps[i]; + console.log(checkedProp); + if (JSON.stringify(prevProps[checkedProp]) !== JSON.stringify(nextProps[checkedProp])) { + isEqual = false; + break; + } + } + return isEqual; + } + + return memo(Component, areEqual); +}; + +export default withMemo; diff --git a/pkg/interface/src/views/landscape/components/GroupsPane.tsx b/pkg/interface/src/views/landscape/components/GroupsPane.tsx index cfc2f3e7e..6545230fa 100644 --- a/pkg/interface/src/views/landscape/components/GroupsPane.tsx +++ b/pkg/interface/src/views/landscape/components/GroupsPane.tsx @@ -210,7 +210,6 @@ export function GroupsPane(props: GroupsPaneProps) { associations={associations} groups={groups} groupPath={groupPath} - contacts={contacts} workspace={workspace} /> {popovers(routeProps, baseUrl)} diff --git a/pkg/interface/src/views/landscape/components/Home/GroupFeed.js b/pkg/interface/src/views/landscape/components/Home/GroupFeed.js index 3cc67e939..b41f8a2ac 100644 --- a/pkg/interface/src/views/landscape/components/Home/GroupFeed.js +++ b/pkg/interface/src/views/landscape/components/Home/GroupFeed.js @@ -6,8 +6,8 @@ import { Col } from '@tlon/indigo-react' import { resourceFromPath } from '~/logic/lib/group'; import useGraphState from '~/logic/state/graph'; import { GroupFeedHeader } from './GroupFeedHeader'; -import { PostTimeline } from './Post/PostTimeline'; -import { PostReplies } from './Post/PostReplies'; +import PostTimeline from './Post/PostTimeline'; +import PostReplies from './Post/PostReplies'; export function GroupFeed(props) { @@ -16,7 +16,7 @@ export function GroupFeed(props) { api, history, associations, - graphPath + graphPath, } = props; const graphs = useGraphState(state => state.graphs); const graphResource = resourceFromPath(graphPath); @@ -29,6 +29,9 @@ export function GroupFeed(props) { const relativePath = (path) => baseUrl + path; const association = associations.graph[graphPath]; + const graphId = `${graphResource.ship.slice(1)}/${graphResource.name}`; + const graph = graphs[graphId]; + useEffect(() => { // TODO: VirtualScroller should support lower starting values than 100 api.graph.getNewest(graphResource.ship, graphResource.name, 100); @@ -53,11 +56,13 @@ export function GroupFeed(props) { render={(routeProps) => { return ( + graph={graph} + pendingSize={pendingSize} /> ); }} /> { return ( + graph={graph} + pendingSize={pendingSize} /> ); }} /> diff --git a/pkg/interface/src/views/landscape/components/Home/GroupHome.js b/pkg/interface/src/views/landscape/components/Home/GroupHome.js index b04116eff..abe21a5af 100644 --- a/pkg/interface/src/views/landscape/components/Home/GroupHome.js +++ b/pkg/interface/src/views/landscape/components/Home/GroupHome.js @@ -15,8 +15,7 @@ export function GroupHome(props) { groupPath, groups, graphs, - baseUrl, - contacts, + baseUrl } = props; const metadata = associations?.groups[groupPath]?.metadata; @@ -59,7 +58,6 @@ export function GroupHome(props) { state.contacts); + return ( { const { graph, - graphResource, - contacts, + graphPath, api, history, baseUrl, parentNode, association } = this.props; + const graphResource = resourceFromPath(graphPath); const node = graph.get(index); if (!node) { return null; } @@ -39,7 +39,6 @@ export class PostFeed extends React.Component { }) : []; if (parentNode && index.eq(first ?? bigInt.zero)) { - return ( state.contacts); const mb = isReply ? "2" : "3"; const permalink = !!association ? getPermalinkForGraph( diff --git a/pkg/interface/src/views/landscape/components/Home/Post/PostInput.js b/pkg/interface/src/views/landscape/components/Home/Post/PostInput.js index c53814fec..df31608b0 100644 --- a/pkg/interface/src/views/landscape/components/Home/Post/PostInput.js +++ b/pkg/interface/src/views/landscape/components/Home/Post/PostInput.js @@ -7,10 +7,12 @@ import tokenizeMessage from '~/logic/lib/tokenizeMessage'; import { useToggleState } from '~/logic/lib/useToggleState'; import { createPost } from '~/logic/api/graph'; import useStorage from '~/logic/lib/useStorage'; +import { resourceFromPath } from '~/logic/lib/group'; export function PostInput(props) { - const { api, graphResource, index, submitCallback } = props; + const { api, graphPath, index, submitCallback } = props; + const graphResource = resourceFromPath(graphPath); const [disabled, setDisabled] = useState(false); const [code, toggleCode] = useToggleState(false); diff --git a/pkg/interface/src/views/landscape/components/Home/Post/PostItem.js b/pkg/interface/src/views/landscape/components/Home/Post/PostItem.js index 46179212e..0624d0a52 100644 --- a/pkg/interface/src/views/landscape/components/Home/Post/PostItem.js +++ b/pkg/interface/src/views/landscape/components/Home/Post/PostItem.js @@ -7,6 +7,7 @@ import { PostInput } from './PostInput'; import { Mention } from "~/views/components/MentionText"; import withState from '~/logic/lib/withState'; import { useHovering } from '~/logic/lib/util'; +import { resourceFromPath } from '~/logic/lib/group'; class PostItem extends React.Component { @@ -43,9 +44,8 @@ class PostItem extends React.Component { render() { const { node, - contacts, api, - graphResource, + graphPath, association, index, innerRef, @@ -55,6 +55,7 @@ class PostItem extends React.Component { hovering, bind } = this.props; + const graphResource = resourceFromPath(graphPath); let indexString = ''; @@ -113,7 +114,7 @@ class PostItem extends React.Component { borderLeftColor="lightGray"> diff --git a/pkg/interface/src/views/landscape/components/Home/Post/PostReplies.js b/pkg/interface/src/views/landscape/components/Home/Post/PostReplies.js index 83863ff28..eedafc0b1 100644 --- a/pkg/interface/src/views/landscape/components/Home/Post/PostReplies.js +++ b/pkg/interface/src/views/landscape/components/Home/Post/PostReplies.js @@ -5,109 +5,115 @@ import { PostInput } from './PostInput'; import PostItem from './PostItem'; import { PostFeed } from './PostFeed'; import { Loading } from '~/views/components/Loading'; +import { resourceFromPath } from '~/logic/lib/group'; -export function PostReplies(props) { - const { - baseUrl, - api, - history, - association, - groups, - contacts, - graphPath, - graphs, - pendingSize, - graphResource - } = props; - const graphId = `${graphResource.ship.slice(1)}/${graphResource.name}`; - const shouldRenderFeed = graphId in graphs; +export default class PostReplies extends React.PureComponent { + constructor(props) { + super(props); + this.whyDidYouRender = true; + } + + render() { + let graph = this.props.graph; + const { + baseUrl, + api, + history, + association, + groups, + graphPath, + pendingSize + } = this.props; + + const graphResource = resourceFromPath(graphPath); + const graphId = `${graphResource.ship.slice(1)}/${graphResource.name}`; + const shouldRenderFeed = !!graph; + + if (!shouldRenderFeed) { + return ( + + + + ); + } + + const locationUrl = + history.location.pathname.replace(`${baseUrl}/feed`, ''); + let nodeIndex = locationUrl.split('/').slice(1).map((ind) => { + return bigInt(ind); + }); + + let node; + nodeIndex.forEach((i) => { + if (!graph) { + return null; + } + node = graph.get(i); + if (!node) { + return null; + } + graph = node.children; + }); + + if (!node || !graph) { + return null; + } + + const first = graph.peekLargest()?.[0]; + if (!first) { + return ( + + + + + + + + No one has posted any replies yet. + + + + + ); + } - if (!shouldRenderFeed) { return ( - - + + ); } - - const locationUrl = - history.location.pathname.replace(`${baseUrl}/feed`, ''); - let nodeIndex = locationUrl.split('/').slice(1).map((ind) => { - return bigInt(ind); - }); - - let node; - let graph = graphs[graphId]; - nodeIndex.forEach((i) => { - if (!graph) { - return null; - } - node = graph.get(i); - if (!node) { - return null; - } - graph = node.children; - }); - - if (!node || !graph) { - return null; - } - - const first = graph.peekLargest()?.[0]; - if (!first) { - return ( - - - - - - - - No one has posted any replies yet. - - - - - ); - } - - return ( - - - - ); } diff --git a/pkg/interface/src/views/landscape/components/Home/Post/PostTimeline.js b/pkg/interface/src/views/landscape/components/Home/Post/PostTimeline.js index e77dcbeaf..880c38e9c 100644 --- a/pkg/interface/src/views/landscape/components/Home/Post/PostTimeline.js +++ b/pkg/interface/src/views/landscape/components/Home/Post/PostTimeline.js @@ -3,97 +3,99 @@ import { Text, Col, Box } from '@tlon/indigo-react' import { PostInput } from './PostInput'; import { PostFeed } from './PostFeed'; import { Loading } from '~/views/components/Loading'; +import { resourceFromPath } from '~/logic/lib/group'; -export function PostTimeline(props) { - const { - baseUrl, - api, - history, - association, - groups, - contacts, - graphPath, - graphs, - pendingSize, - graphResource - } = props; - const graphId = `${graphResource.ship.slice(1)}/${graphResource.name}`; - const shouldRenderFeed = graphId in graphs; - - if (!shouldRenderFeed) { - return ( - - - - ); +export default class PostTimeline extends React.PureComponent { + constructor(props) { + super(props); + this.whyDidYouRender = true; } - const graph = graphs[graphId]; - const first = graph.peekLargest()?.[0]; - if (!first) { - return ( - + render() { + const { + baseUrl, + api, + history, + association, + graphPath, + graph, + pendingSize, + } = this.props; + const graphResource = resourceFromPath(graphPath); + const shouldRenderFeed = !!graph; + + if (!shouldRenderFeed) { + return ( + + + + ); + } + + const first = graph.peekLargest()?.[0]; + if (!first) { + return ( + + + + + + + No one has posted anything here yet. + + + + + ); + } + + return ( + <> + - + + + - - - - - No one has posted anything here yet. - - + history={history} + baseUrl={baseUrl} + /> - + ); } - - return ( - <> - - - - - - - - ); } diff --git a/pkg/interface/src/views/landscape/index.tsx b/pkg/interface/src/views/landscape/index.tsx index 00ca5a652..f545a13c1 100644 --- a/pkg/interface/src/views/landscape/index.tsx +++ b/pkg/interface/src/views/landscape/index.tsx @@ -21,6 +21,7 @@ import useGraphState from '~/logic/state/graph'; import useHarkState, { withHarkState } from '~/logic/state/hark'; import withState from '~/logic/lib/withState'; + type LandscapeProps = StoreState & { ship: PatpNoSig; api: GlobalApi; @@ -67,95 +68,85 @@ export function DMRedirect(props: LandscapeProps & RouteComponentProps & { ship: ); } -class Landscape extends Component> { - componentDidMount(): void { - this.props.subscription.startApp('groups'); - this.props.subscription.startApp('graph'); - } +export default function Landscape(props) { + const notificationsCount = useHarkState(s => s.notificationsCount); - render(): ReactElement { - const { props } = this; - - return ( - <> - - { props.notificationsCount ? `(${String(props.notificationsCount) }) `: '' }Landscape - - - { - const { - host, - name - } = routeProps.match.params as Record; - const groupPath = `/ship/${host}/${name}`; - const baseUrl = `/~landscape${groupPath}`; - const ws: Workspace = { type: 'group', group: groupPath }; - - return ( - - ); - }} - /> - { - const ws: Workspace = { type: 'home' }; - return ( - - ); - }} - /> - { - const ws: Workspace = { type: 'messages' }; - return ( - - ); - }} - /> - { - return ( - - - - - - ); - }} - /> - + + { props.notificationsCount ? `(${String(props.notificationsCount) }) `: '' }Landscape + + + { - const { ship } = routeProps.match.params; - return ; + const { + host, + name + } = routeProps.match.params as Record; + const groupPath = `/ship/${host}/${name}`; + const baseUrl = `/~landscape${groupPath}`; + const ws: Workspace = { type: 'group', group: groupPath }; + + return ( + + ); }} - /> - { - const { ship, name } = routeProps.match.params; - const autojoin = ship && name ? `${ship}/${name}` : undefined; - return ( - - - - - - ); - }} - /> - - - ); - } + /> + { + const ws: Workspace = { type: 'home' }; + return ( + + ); + }} + /> + { + const ws: Workspace = { type: 'messages' }; + return ( + + ); + }} + /> + { + return ( + + + + + + ); + }} + /> + { + const { ship } = routeProps.match.params; + return ; + }} + /> + { + const { ship, name } = routeProps.match.params; + const autojoin = ship && name ? `${ship}/${name}` : undefined; + return ( + + + + + + ); + }} + /> + + + ); } -export default withState(Landscape, [ - [useHarkState, ['notificationsCount']] -]);