-
Notifications
You must be signed in to change notification settings - Fork 12
refactor: refactor the infinite scroll wrapper and queries #267
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { MutableRefObject } from "react"; | ||
|
||
export default (onLoadMore?: () => void) => { | ||
const handleAutoFillPage = (ref: MutableRefObject<HTMLDivElement | undefined | null>) => { | ||
if (ref.current && ref.current?.scrollHeight <= document.documentElement.clientHeight) { | ||
onLoadMore?.(); | ||
} | ||
}; | ||
|
||
const handleScrollToBottom = ( | ||
{ currentTarget }: React.UIEvent<HTMLDivElement, UIEvent>, | ||
threshold = 5, | ||
) => { | ||
if ( | ||
currentTarget.scrollTop + currentTarget.clientHeight >= | ||
currentTarget.scrollHeight - threshold | ||
) { | ||
onLoadMore?.(); | ||
} | ||
}; | ||
|
||
return { handleAutoFillPage, handleScrollToBottom }; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import React, { ReactNode, useEffect, useRef } from "react"; | ||
|
||
import { styled } from "@reearth/theme"; | ||
|
||
import useHooks from "./hooks"; | ||
|
||
type Props = { | ||
className?: string; | ||
loading?: boolean; | ||
hasMoreItems?: boolean; | ||
children?: ReactNode; | ||
onGetMoreItems?: () => void; | ||
}; | ||
|
||
const InfiniteScrollWrapper: React.FC<Props> = ({ | ||
className, | ||
loading, | ||
hasMoreItems, | ||
onGetMoreItems, | ||
children, | ||
}) => { | ||
const wrapperRef = useRef<HTMLDivElement>(null); | ||
const { handleAutoFillPage, handleScrollToBottom } = useHooks(onGetMoreItems); | ||
|
||
useEffect(() => { | ||
if (wrapperRef.current && !loading && hasMoreItems) handleAutoFillPage(wrapperRef); | ||
}, [handleAutoFillPage, hasMoreItems, loading, onGetMoreItems]); | ||
Comment on lines
+22
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This component doesn't have too many hooks, so you don't really need the hooks.ts file. But if you want to keep the file, please bring the ref and useEffect into the hooks file as well to keep things consistent (unless there is a reason you separated them, in which case please let me know). |
||
|
||
return ( | ||
<Wrapper | ||
ref={wrapperRef} | ||
className={className} | ||
onScroll={e => !loading && hasMoreItems && handleScrollToBottom(e)}> | ||
{children} | ||
</Wrapper> | ||
); | ||
}; | ||
const Wrapper = styled.div` | ||
width: 100%; | ||
height: 100%; | ||
overflow-y: scroll; | ||
-webkit-overflow-scrolling: touch; | ||
`; | ||
export default React.memo(InfiniteScrollWrapper); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,6 @@ import { useApolloClient } from "@apollo/client"; | |
import { useCallback, useState, useEffect } from "react"; | ||
|
||
import { | ||
GetAssetsQuery, | ||
useCreateAssetMutation, | ||
useRemoveAssetMutation, | ||
useGetAssetsQuery, | ||
|
@@ -12,8 +11,6 @@ import { | |
import { useT } from "@reearth/i18n"; | ||
import { useNotification } from "@reearth/state"; | ||
|
||
export type AssetNodes = NonNullable<GetAssetsQuery["assets"]["nodes"][number]>[]; | ||
|
||
export type AssetSortType = "date" | "name" | "size"; | ||
|
||
export type Asset = { | ||
|
@@ -74,7 +71,21 @@ export default (teamId?: string, initialAssetUrl?: string | null, allowDeletion? | |
data?.assets.pageInfo?.hasNextPage || data?.assets.pageInfo?.hasPreviousPage; | ||
|
||
const isRefetching = networkStatus === 3; | ||
const assets = data?.assets.edges?.map(e => e.node) as AssetNodes; | ||
|
||
const assets = data?.assets.edges | ||
.map<Asset | undefined>(asset => | ||
asset.node | ||
? { | ||
id: asset.node.id, | ||
name: asset.node.name, | ||
size: asset.node.size, | ||
teamId: asset.node.teamId, | ||
url: asset.node.url, | ||
contentType: asset.node.contentType, | ||
} | ||
: undefined, | ||
) | ||
.filter((asset): asset is Asset => !!asset); | ||
Comment on lines
+75
to
+88
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can keep it more concise if you do something like this:
Please test to make sure it works though. |
||
|
||
const initialAsset = assets?.find(a => a.url === initialAssetUrl); | ||
const [selectedAssets, selectAsset] = useState<Asset[]>(initialAsset ? [initialAsset] : []); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,13 +12,10 @@ import { | |
useCreateProjectMutation, | ||
useCreateSceneMutation, | ||
Visualizer, | ||
GetProjectsQuery, | ||
} from "@reearth/gql"; | ||
import { useT } from "@reearth/i18n"; | ||
import { useTeam, useProject, useUnselectProject, useNotification } from "@reearth/state"; | ||
|
||
export type ProjectNodes = NonNullable<GetProjectsQuery["projects"]["nodes"][number]>[]; | ||
|
||
const projectsPerPage = 9; | ||
|
||
export default (teamId?: string) => { | ||
|
@@ -117,25 +114,23 @@ export default (teamId?: string) => { | |
notifyOnNetworkStatusChange: true, | ||
}); | ||
|
||
const projectNodes = projectData?.projects.edges.map(e => e.node) as ProjectNodes; | ||
|
||
const projects = useMemo(() => { | ||
return (projectNodes ?? []) | ||
return (projectData?.projects.edges ?? []) | ||
.map<Project | undefined>(project => | ||
project | ||
project.node | ||
? { | ||
id: project.id, | ||
description: project.description, | ||
name: project.name, | ||
image: project.imageUrl, | ||
status: toPublishmentStatus(project.publishmentStatus), | ||
isArchived: project.isArchived, | ||
sceneId: project.scene?.id, | ||
id: project.node.id, | ||
description: project.node.description, | ||
name: project.node.name, | ||
image: project.node.imageUrl, | ||
status: toPublishmentStatus(project.node.publishmentStatus), | ||
isArchived: project.node.isArchived, | ||
sceneId: project.node.scene?.id, | ||
} | ||
: undefined, | ||
) | ||
.filter((project): project is Project => !!project); | ||
}, [projectNodes]); | ||
}, [projectData?.projects.edges]); | ||
Comment on lines
117
to
+133
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to the comment above, you can destructure the node so you can clean up the assignment below, something like this:
You can also destructure further like this:
The second one might be too much? Also, you can go even further to ONLLY destructure specifically the keys that are different between the data and the frontend's Project (aka image VS imageUrl, etc) and leave the rest as |
||
|
||
const hasMoreProjects = | ||
projectData?.projects.pageInfo?.hasNextPage || projectData?.projects.pageInfo?.hasPreviousPage; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,8 @@ | ||
import { useApolloClient } from "@apollo/client"; | ||
import { useCallback } from "react"; | ||
|
||
import { Item } from "@reearth/components/molecules/Settings/Project/Dataset/DatasetList"; | ||
import { | ||
DatasetsListQuery, | ||
useGetProjectSceneQuery, | ||
useImportDatasetMutation, | ||
useRemoveDatasetMutation, | ||
|
@@ -11,10 +11,6 @@ import { | |
import { useT } from "@reearth/i18n"; | ||
import { useTeam, useProject, useNotification } from "@reearth/state"; | ||
|
||
type Nodes = NonNullable<DatasetsListQuery["datasetSchemas"]["nodes"]>; | ||
|
||
type DatasetSchemas = NonNullable<Nodes[number]>[]; | ||
|
||
const datasetPerPage = 20; | ||
|
||
export default (projectId: string) => { | ||
|
@@ -38,9 +34,11 @@ export default (projectId: string) => { | |
notifyOnNetworkStatusChange: true, | ||
}); | ||
|
||
const nodes = data?.datasetSchemas.edges.map(e => e.node) ?? []; | ||
|
||
const datasetSchemas = nodes.filter(Boolean) as DatasetSchemas; | ||
const datasetSchemas = data?.datasetSchemas.edges | ||
.map<Item | undefined>(item => | ||
item.node ? { id: item.node.id, name: item.node.name } : undefined, | ||
) | ||
.filter((item): item is Item => !!item); | ||
Comment on lines
+37
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above: destructure the node and then you don't need to assign the values to keys. And again, not sure if the filter is necessary? Let me know |
||
|
||
const hasMoreDataSets = | ||
data?.datasetSchemas?.pageInfo.hasNextPage || data?.datasetSchemas?.pageInfo.hasPreviousPage; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think something about this isn't quite working correctly. In the Asset settings page you get this called when the page loads, before any scrolling. It isn't a BIG deal, since it only seems to call once, but it really shouldn't call UNLESS the page is too long for the initial fetch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just happen to see this. I think this part comes from my suggestion some weeks ago. This is only for solving large screen issue (first fetch cannot fill the area) and yes it should be called only under this situation but the point is it seems to be some difficult to check if it is too long (from my memory it was due to the DOM structure is different with different usecases and the function is a common util at that time). So my idea was always want the content fill the area and if not and if there's more data then load more. At that time i only look into this with load projects in large screen issue and i'm not sure about other situations (like Asset page). And also i think the code changes a lot now maybe can optimize this with adding an area's ref?