diff --git a/packages/components/acter/posts/index.tsx b/packages/components/acter/posts/index.tsx index 6ef599668..51f52a330 100644 --- a/packages/components/acter/posts/index.tsx +++ b/packages/components/acter/posts/index.tsx @@ -4,7 +4,7 @@ import { Grid, makeStyles, createStyles, Theme } from '@material-ui/core' import { InfoSection } from '@acter/components/acter/landing-page/info-section' import { LandingPageLayout } from '@acter/components/acter/landing-page/layout' -import { PostList } from '@acter/components/posts' +import { PostList } from '@acter/components/posts/organisms/post-list' export const ActerPosts: FC = () => { const classes = useStyles({}) diff --git a/packages/components/acter/posts/single-post-section.tsx b/packages/components/acter/posts/single-post-section.tsx index 6b9afde61..aacbe680b 100644 --- a/packages/components/acter/posts/single-post-section.tsx +++ b/packages/components/acter/posts/single-post-section.tsx @@ -9,7 +9,7 @@ import { } from '@material-ui/core' import { ArrowBackSharp as BackIcon } from '@material-ui/icons' -import { SinglePost } from '@acter/components/posts/single-post' +import { PostContent } from '@acter/components/posts/organisms/post' import { Link } from '@acter/components/util/anchor-link' import { Post } from '@acter/schema' @@ -36,7 +36,7 @@ export const SinglePostSection: FC = ({ Back to posts - {} + {} ) } diff --git a/packages/components/activity/posts.tsx b/packages/components/activity/posts.tsx index c9c29cb09..f6863ba29 100644 --- a/packages/components/activity/posts.tsx +++ b/packages/components/activity/posts.tsx @@ -5,7 +5,7 @@ import { useRouter } from 'next/router' import slugify from 'slugify' import { SinglePostSection } from '@acter/components/acter/posts/single-post-section' -import { PostList } from '@acter/components/posts' +import { PostList } from '@acter/components/posts/organisms/post-list' import { acterAsUrl } from '@acter/lib/acter/acter-as-url' import { usePost } from '@acter/lib/post/use-post' import { Acter } from '@acter/schema' diff --git a/packages/components/group/index.tsx b/packages/components/group/index.tsx index 840946bcc..eeda7afa5 100644 --- a/packages/components/group/index.tsx +++ b/packages/components/group/index.tsx @@ -13,7 +13,7 @@ import { DescriptionSection } from '@acter/components/group/sections/description import { LinksSection } from '@acter/components/group/sections/links' import { MembersSection } from '@acter/components/group/sections/members' import { UpcomingActivities } from '@acter/components/group/sections/upcoming-activities' -import { PostList } from '@acter/components/posts' +import { PostList } from '@acter/components/posts/organisms/post-list' export const GroupLanding: FC = () => { const classes = useStyles() diff --git a/packages/components/package.json b/packages/components/package.json index 3e9f5ca70..f3612acea 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -57,7 +57,6 @@ "react-filepond": "7.1.1", "react-google-autocomplete": "2.6.1", "react-imgix": "9.5.0", - "react-infinite-scroll-component": "6.1.0", "react-is-visible": "1.1.2", "react-sanitized-html": "^2.0.0", "sanitize-html": "^2.7.0", diff --git a/packages/components/pagination/infinite-list.tsx b/packages/components/pagination/infinite-list.tsx new file mode 100644 index 000000000..fcb57878c --- /dev/null +++ b/packages/components/pagination/infinite-list.tsx @@ -0,0 +1,25 @@ +import React, { FC, ReactNode } from 'react' + +import { + LoadMoreOnVisible, + LoadMoreOnVisibleProps, +} from './load-more-on-visible' + +export interface InfiniteListProps + extends LoadMoreOnVisibleProps { + entities: T[] + render?: (entity: T) => ReactNode +} + +export const InfiniteList: FC = ({ + entities, + render, + children, + ...restProps +}) => ( + <> + {render && entities?.map(render)} + {children} + + +) diff --git a/packages/components/pagination/load-more-on-visible.tsx b/packages/components/pagination/load-more-on-visible.tsx new file mode 100644 index 000000000..16cf6b2d8 --- /dev/null +++ b/packages/components/pagination/load-more-on-visible.tsx @@ -0,0 +1,39 @@ +import React, { useEffect, useRef, FC, ReactElement } from 'react' +import { useIsVisible } from 'react-is-visible' + +import { Button } from '@acter/components/styled' + +export interface LoadMoreOnVisibleProps { + entities: T[] + fetching: boolean + hasMore: boolean + renderOnLoading?: () => ReactElement + loadMore: () => void +} + +export const LoadMoreOnVisible: FC = ({ + entities, + fetching, + hasMore, + renderOnLoading, + loadMore, +}) => { + if (fetching) { + return renderOnLoading?.() + } + + if (!entities || !hasMore) return null + + const nodeRef = useRef() + const isVisible = useIsVisible(nodeRef) + + useEffect(() => { + if (isVisible) loadMore() + }, [isVisible]) + + return ( +
+ +
+ ) +} diff --git a/packages/components/posts/molecules/content/index.ts b/packages/components/posts/molecules/content/index.ts new file mode 100644 index 000000000..3b797e48e --- /dev/null +++ b/packages/components/posts/molecules/content/index.ts @@ -0,0 +1 @@ +export * from './post-content' diff --git a/packages/components/posts/post/content.tsx b/packages/components/posts/molecules/content/post-content.tsx similarity index 87% rename from packages/components/posts/post/content.tsx rename to packages/components/posts/molecules/content/post-content.tsx index b155fa1c2..45029b233 100644 --- a/packages/components/posts/post/content.tsx +++ b/packages/components/posts/molecules/content/post-content.tsx @@ -4,8 +4,11 @@ import { Box } from '@material-ui/core' import { makeStyles, createStyles, Theme } from '@material-ui/core/styles' import { SanitizedContent } from '@acter/components/molecules/sanitized-content' -import { PostInfo, PostInfoProps } from '@acter/components/posts/post/info' -import { PostReactions } from '@acter/components/posts/reactions' +import { + PostInfo, + PostInfoProps, +} from '@acter/components/posts/molecules/info/post-info' +import { PostReactions } from '@acter/components/posts/organisms/reactions' type PostContentProps = PostInfoProps diff --git a/packages/components/posts/molecules/info/index.ts b/packages/components/posts/molecules/info/index.ts new file mode 100644 index 000000000..26069637e --- /dev/null +++ b/packages/components/posts/molecules/info/index.ts @@ -0,0 +1 @@ +export * from './post-info' diff --git a/packages/components/posts/post/info.tsx b/packages/components/posts/molecules/info/post-info.tsx similarity index 100% rename from packages/components/posts/post/info.tsx rename to packages/components/posts/molecules/info/post-info.tsx diff --git a/packages/components/posts/molecules/options/index.ts b/packages/components/posts/molecules/options/index.ts new file mode 100644 index 000000000..307c5c5c0 --- /dev/null +++ b/packages/components/posts/molecules/options/index.ts @@ -0,0 +1 @@ +export * from './post-options' diff --git a/packages/components/posts/post/options.tsx b/packages/components/posts/molecules/options/post-options.tsx similarity index 100% rename from packages/components/posts/post/options.tsx rename to packages/components/posts/molecules/options/post-options.tsx diff --git a/packages/components/posts/organisms/container/index.ts b/packages/components/posts/organisms/container/index.ts new file mode 100644 index 000000000..84e1a3359 --- /dev/null +++ b/packages/components/posts/organisms/container/index.ts @@ -0,0 +1 @@ +export * from './post-container' diff --git a/packages/components/posts/post/index.tsx b/packages/components/posts/organisms/container/post-container.tsx similarity index 88% rename from packages/components/posts/post/index.tsx rename to packages/components/posts/organisms/container/post-container.tsx index 00b766de1..0e4b1e827 100644 --- a/packages/components/posts/post/index.tsx +++ b/packages/components/posts/organisms/container/post-container.tsx @@ -6,10 +6,13 @@ import clsx from 'clsx' import { ActerAvatar } from '@acter/components/acter/avatar' import { LoadingSpinner } from '@acter/components/atoms/loading/spinner' -import { PostForm, PostFormValues } from '@acter/components/posts/form' -import { PostContent } from '@acter/components/posts/post/content' -import { PostOptions } from '@acter/components/posts/post/options' -import { AddPostReaction } from '@acter/components/posts/reactions/add-reaction' +import { PostContent } from '@acter/components/posts/molecules/content' +import { PostOptions } from '@acter/components/posts/molecules/options' +import { + PostForm, + PostFormValues, +} from '@acter/components/posts/organisms/form' +import { AddPostReaction } from '@acter/components/posts/organisms/reactions/add-reaction' import { checkMemberAccess } from '@acter/lib/acter/check-member-access' import { useActer } from '@acter/lib/acter/use-acter' import { useDeletePost } from '@acter/lib/post/use-delete-post' @@ -23,7 +26,7 @@ export interface PostsProps { parentId?: string } -export const Post: FC = ({ user, post, parentId }) => { +export const PostContainer: FC = ({ user, post, parentId }) => { const classes = useStyles() const [toggleForm, setToggleForm] = useState(false) const { acter } = useActer() diff --git a/packages/components/posts/organisms/form/index.ts b/packages/components/posts/organisms/form/index.ts new file mode 100644 index 000000000..5a1adc2e4 --- /dev/null +++ b/packages/components/posts/organisms/form/index.ts @@ -0,0 +1,2 @@ +export * from './post-form' +export * from './post-form-section' diff --git a/packages/components/posts/form/post-form-section.tsx b/packages/components/posts/organisms/form/post-form-section.tsx similarity index 98% rename from packages/components/posts/form/post-form-section.tsx rename to packages/components/posts/organisms/form/post-form-section.tsx index 9ddf7d759..1dd933914 100644 --- a/packages/components/posts/form/post-form-section.tsx +++ b/packages/components/posts/organisms/form/post-form-section.tsx @@ -10,7 +10,7 @@ import { PostForm, PostFormProps, PostFormValues, -} from '@acter/components/posts/form' +} from '@acter/components/posts/organisms/form' import { useActer } from '@acter/lib/acter/use-acter' import { useTranslation } from '@acter/lib/i18n/use-translation' import { useCreateComment } from '@acter/lib/post/use-create-comment' diff --git a/packages/components/posts/form/__stories__/post-form.stories.tsx b/packages/components/posts/organisms/form/post-form.stories.tsx similarity index 96% rename from packages/components/posts/form/__stories__/post-form.stories.tsx rename to packages/components/posts/organisms/form/post-form.stories.tsx index a1e256fed..e95e3839f 100644 --- a/packages/components/posts/form/__stories__/post-form.stories.tsx +++ b/packages/components/posts/organisms/form/post-form.stories.tsx @@ -2,7 +2,7 @@ import React from 'react' import { Meta, Story } from '@storybook/react' -import { PostForm, PostFormProps } from '@acter/components/posts/form' +import { PostForm, PostFormProps } from '@acter/components/posts/organisms/form' import { ExampleActer } from '@acter/schema/fixtures' import { ExampleUser } from '@acter/schema/fixtures' diff --git a/packages/components/posts/form/index.tsx b/packages/components/posts/organisms/form/post-form.tsx similarity index 100% rename from packages/components/posts/form/index.tsx rename to packages/components/posts/organisms/form/post-form.tsx diff --git a/packages/components/posts/organisms/post-list/index.ts b/packages/components/posts/organisms/post-list/index.ts new file mode 100644 index 000000000..36ae6a67b --- /dev/null +++ b/packages/components/posts/organisms/post-list/index.ts @@ -0,0 +1 @@ +export * from './post-list' diff --git a/packages/components/posts/__stories__/post.stories.tsx b/packages/components/posts/organisms/post-list/post-list.stories.tsx similarity index 92% rename from packages/components/posts/__stories__/post.stories.tsx rename to packages/components/posts/organisms/post-list/post-list.stories.tsx index 9bac4a22d..e69a14c03 100644 --- a/packages/components/posts/__stories__/post.stories.tsx +++ b/packages/components/posts/organisms/post-list/post-list.stories.tsx @@ -2,7 +2,7 @@ import React from 'react' import { Meta, Story } from '@storybook/react' -import { PostList } from '@acter/components/posts' +import { PostList } from '@acter/components/posts/organisms/post-list' import { withExampleActerParams, withExampleUserParams, diff --git a/packages/components/posts/index.tsx b/packages/components/posts/organisms/post-list/post-list.tsx similarity index 65% rename from packages/components/posts/index.tsx rename to packages/components/posts/organisms/post-list/post-list.tsx index 6dc81e89d..9cd0b2950 100644 --- a/packages/components/posts/index.tsx +++ b/packages/components/posts/organisms/post-list/post-list.tsx @@ -4,13 +4,14 @@ import { Box } from '@material-ui/core' import { makeStyles, createStyles, Theme } from '@material-ui/core/styles' import { LoadingSpinner } from '@acter/components/atoms/loading/spinner' -import { PostFormSection } from '@acter/components/posts/form/post-form-section' -import { SinglePost } from '@acter/components/posts/single-post' +import { InfiniteList } from '@acter/components/pagination/infinite-list' +import { PostFormSection } from '@acter/components/posts/organisms/form/post-form-section' +import { Post } from '@acter/components/posts/organisms/post' import { checkMemberAccess } from '@acter/lib/acter/check-member-access' import { useActer } from '@acter/lib/acter/use-acter' import { usePosts } from '@acter/lib/post/use-posts' import { useUser } from '@acter/lib/user/use-user' -import { ActerJoinSettings } from '@acter/schema' +import { ActerJoinSettings, Post as PostType } from '@acter/schema' interface PostListProps { acterId?: string @@ -19,11 +20,17 @@ interface PostListProps { export const PostList: FC = ({ acterId }) => { const classes = useStyles() - const { posts, fetching: postsLoading } = usePosts({ acterId }) + const { + posts, + fetching: postsLoading, + hasMore, + loadMore, + } = usePosts({ acterId }) const { user, fetching: userLoading } = useUser() const { acter, fetching: acterLoading } = useActer({ acterId }) - if (acterLoading || userLoading || postsLoading) return + if (!posts && (acterLoading || userLoading || postsLoading)) + return if (!acter) return null const isUserActerFollower = acter.Followers.find( @@ -41,11 +48,16 @@ export const PostList: FC = ({ acterId }) => { {isMember && } {(isActerPublic || isMember) && ( - <> - {posts?.map((post) => ( - - ))} - + } + render={(post: PostType) => ( + + )} + /> )} ) diff --git a/packages/components/posts/organisms/post/index.ts b/packages/components/posts/organisms/post/index.ts new file mode 100644 index 000000000..2cdb99e85 --- /dev/null +++ b/packages/components/posts/organisms/post/index.ts @@ -0,0 +1 @@ +export * from './post' diff --git a/packages/components/posts/single-post.tsx b/packages/components/posts/organisms/post/post.tsx similarity index 61% rename from packages/components/posts/single-post.tsx rename to packages/components/posts/organisms/post/post.tsx index 1659e3039..a62d3f013 100644 --- a/packages/components/posts/single-post.tsx +++ b/packages/components/posts/organisms/post/post.tsx @@ -1,10 +1,10 @@ -import React, { FC } from 'react' +import React, { useState, FC } from 'react' import { Box, Divider } from '@material-ui/core' import { makeStyles, createStyles, Theme } from '@material-ui/core/styles' -import { PostFormSection } from '@acter/components/posts/form/post-form-section' -import { Post } from '@acter/components/posts/post/index' +import { PostContainer } from '@acter/components/posts/organisms/container' +import { PostFormSection } from '@acter/components/posts/organisms/form/post-form-section' import { checkMemberAccess } from '@acter/lib/acter/check-member-access' import { useActer } from '@acter/lib/acter/use-acter' import { useUser } from '@acter/lib/user/use-user' @@ -15,29 +15,42 @@ interface SinglePostProps { acterId?: string } -export const SinglePost: FC = ({ post, acterId }) => { +export const Post: FC = ({ post, acterId }) => { const classes = useStyles() const { user } = useUser() const { acter } = useActer({ acterId }) + const [hasMoreComments, setHasMoreComments] = useState( + post.Comments.length > 5 + ) if (!acter || !user) return null const isMember = checkMemberAccess(user, acter) + const comments = post.Comments + ? post.Comments.slice(0, post.Comments.length - 1).reverse() + : [] + return ( - + - {post.Comments.length !== 0 && } - - {post.Comments?.map((comment) => ( - - ))} + {comments.length !== 0 && ( + <> + + {hasMoreComments && <>Load more comments} + {comments.map((comment) => { + return ( + + ) + })} + + )} {isMember && ( diff --git a/packages/components/posts/reactions/add-reaction.tsx b/packages/components/posts/organisms/reactions/add-reaction.tsx similarity index 100% rename from packages/components/posts/reactions/add-reaction.tsx rename to packages/components/posts/organisms/reactions/add-reaction.tsx diff --git a/packages/components/posts/reactions/index.tsx b/packages/components/posts/organisms/reactions/index.tsx similarity index 96% rename from packages/components/posts/reactions/index.tsx rename to packages/components/posts/organisms/reactions/index.tsx index 53858dfbc..6f58f8923 100644 --- a/packages/components/posts/reactions/index.tsx +++ b/packages/components/posts/organisms/reactions/index.tsx @@ -8,7 +8,7 @@ import { Typography, } from '@material-ui/core/' -import { AddPostReaction } from '@acter/components/posts/reactions/add-reaction' +import { AddPostReaction } from '@acter/components/posts/organisms/reactions/add-reaction' import { checkMemberAccess } from '@acter/lib/acter/check-member-access' import { useActer } from '@acter/lib/acter/use-acter' import { useCreatePostReaction } from '@acter/lib/post/use-create-post-reaction' diff --git a/packages/components/posts/__stories__/post-reactions.stories.tsx b/packages/components/posts/organisms/reactions/post-reactions.stories.tsx similarity index 95% rename from packages/components/posts/__stories__/post-reactions.stories.tsx rename to packages/components/posts/organisms/reactions/post-reactions.stories.tsx index 3f43d15d3..40cdae57b 100644 --- a/packages/components/posts/__stories__/post-reactions.stories.tsx +++ b/packages/components/posts/organisms/reactions/post-reactions.stories.tsx @@ -3,7 +3,7 @@ import { Meta, Story } from '@storybook/react' import { PostReactions, PostReactionsProps, -} from '@acter/components/posts/reactions' +} from '@acter/components/posts/organisms/reactions' import { withExampleActerParams } from '@acter/lib/storybook-helpers' import { ExamplePost } from '@acter/schema/fixtures' diff --git a/packages/components/search/organisms/results-infinite-list/search-results-infinite-list.tsx b/packages/components/search/organisms/results-infinite-list/search-results-infinite-list.tsx index 60b800ba3..e5f1f5d22 100644 --- a/packages/components/search/organisms/results-infinite-list/search-results-infinite-list.tsx +++ b/packages/components/search/organisms/results-infinite-list/search-results-infinite-list.tsx @@ -1,36 +1,17 @@ -import React, { FC, useEffect, useRef } from 'react' -import InfiniteScroll from 'react-infinite-scroll-component' -import { useIsVisible } from 'react-is-visible' +import React, { FC } from 'react' import { Box } from '@material-ui/core' import { createStyles, makeStyles, Theme } from '@material-ui/core/styles' -import clsx from 'clsx' - import { LoadingBar } from '@acter/components/atoms/loading/bar' +import { InfiniteList } from '@acter/components/pagination/infinite-list' import { SearchZeroResultsMessage } from '@acter/components/search/atoms/zero-results-message' -import { Button } from '@acter/components/styled' import { useActerSearch } from '@acter/lib/search/use-acter-search' -import { useSearchType } from '@acter/lib/search/use-search-type' import { SearchResultsList } from '../results-list' -interface HasMoreProps { - onVisible: () => void -} - -const HasMore: FC = ({ onVisible, children }) => { - const nodeRef = useRef() - const isVisible = useIsVisible(nodeRef) - useEffect(() => { - if (isVisible) onVisible() - }, [isVisible]) - return
{children}
-} - export const SearchResultsInfiniteList: FC = () => { const classes = useStyles() - const searchType = useSearchType() const { acters, fetching, loadMore, hasMore } = useActerSearch() @@ -45,20 +26,15 @@ export const SearchResultsInfiniteList: FC = () => { return (
- } + loadMore={loadMore} + renderOnLoading={() => } > - - {acters && hasMore && !fetching && ( - - - - )} +
) } diff --git a/packages/lib/object/get-object-array-memo-string/get-object-array-memo-string.test.ts b/packages/lib/object/get-object-array-memo-string/get-object-array-memo-string.test.ts deleted file mode 100644 index 76b5cde04..000000000 --- a/packages/lib/object/get-object-array-memo-string/get-object-array-memo-string.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { getObjectArrayMemoString } from '.' - -describe('getObjectArrayMemoString', () => { - it('should return null passed value is not an array', () => { - expect(getObjectArrayMemoString(undefined)).toBe(null) - }) - - it('should return an empty string if the array is empty', () => { - expect(getObjectArrayMemoString([])).toBe('') - }) - - it('should return a string from the ids in the array', () => { - const results = [ - { - id: 'one', - }, - { - id: 'two', - }, - { - id: 'three', - }, - ] - expect(getObjectArrayMemoString(results)).toBe('one,two,three') - }) - - it('should use empty strings if it cannot find ids in the array', () => { - const results = [ - { - foo: 'bar', - }, - { - foo: 'bar', - }, - ] - expect(getObjectArrayMemoString(results)).toBe(',') - }) -}) diff --git a/packages/lib/object/get-object-array-memo-string/index.ts b/packages/lib/object/get-object-array-memo-string/index.ts deleted file mode 100644 index a9ec48c75..000000000 --- a/packages/lib/object/get-object-array-memo-string/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -//eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/explicit-module-boundary-types -export const getObjectArrayMemoString = (results: any): string => - results && Array.isArray(results) ? results.map((r) => r.id).join(',') : null diff --git a/packages/lib/package.json b/packages/lib/package.json index 8532c6ae5..fa6133e0f 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -17,6 +17,7 @@ "deep-equal": "2.0.5", "deepmerge": "4.2.2", "graphql": "15.5.1", + "immutable": "^4.1.0", "intercom-client": "2.11.2", "ioredis": "4.27.7", "just-camel-case": "^6.0.1", diff --git a/packages/lib/post/use-posts.ts b/packages/lib/post/use-posts.ts index 7af5f89f0..613ce13aa 100644 --- a/packages/lib/post/use-posts.ts +++ b/packages/lib/post/use-posts.ts @@ -1,8 +1,12 @@ import { useEffect, useState } from 'react' -import { CombinedError, useQuery, UseQueryArgs, UseQueryState } from 'urql' +import { CombinedError, UseQueryArgs } from 'urql' import { useActer } from '@acter/lib/acter/use-acter' +import { + usePaginatedQuery, + UsePaginatedState, +} from '@acter/lib/urql/use-paginated-query' import { Post } from '@acter/schema' import GET_POSTS from '@acter/schema/queries/posts-by-acter.graphql' @@ -26,7 +30,7 @@ type UsePostsParams = { type UsePostError = CombinedError | Error export interface UsePostsResult - extends Omit, 'data'> { + extends Omit, 'data'> { posts: Post[] } @@ -35,15 +39,25 @@ export const usePosts = (params?: UsePostsParams): UsePostsResult => { const [fetching, setFetching] = useState(false) const [error, setError] = useState() const [posts, setPosts] = useState([]) - const { acter, fetching: acterFetching, error: acterError } = useActer({ + const { + acter, + fetching: acterFetching, + error: acterError, + } = useActer({ acterId, }) const [ - { data, fetching: queryFetching, error: queryError, ...restQueryResults }, - ] = useQuery({ + { + results, + fetching: queryFetching, + error: queryError, + ...restQueryResults + }, + ] = usePaginatedQuery({ ...options, query: GET_POSTS, + dataKey: 'posts', pause: !acter?.id, variables: { acterId: acter?.id, @@ -60,8 +74,10 @@ export const usePosts = (params?: UsePostsParams): UsePostsResult => { }, [acterError, queryError]) useEffect(() => { - if (data?.posts) setPosts(data.posts) - }, [data?.posts]) + debugger + console.log('post results', results.toList().toArray()) + setPosts(results.toList().toArray()) + }, [results]) return { posts, fetching, error, ...restQueryResults } as UsePostsResult } diff --git a/packages/lib/search/use-acter-search.ts b/packages/lib/search/use-acter-search.ts index de3c52be7..ff946e9d0 100644 --- a/packages/lib/search/use-acter-search.ts +++ b/packages/lib/search/use-acter-search.ts @@ -4,7 +4,6 @@ import { SearchVariables, useSearchVariables, } from '@acter/components/contexts/search-variables' -import { getObjectArrayMemoString } from '@acter/lib/object/get-object-array-memo-string' import { usePaginatedQuery, UsePaginatedState, @@ -69,7 +68,7 @@ export const useActerSearch = ( pause, }) - useEffect(() => setActers(results), [getObjectArrayMemoString(results)]) + useEffect(() => setActers(results.toList().toArray()), [results]) return { acters, ...restQueryResult } } diff --git a/packages/lib/urql/updates/post-create.ts b/packages/lib/urql/updates/post-create.ts index f2eeabda6..2dfa20b39 100644 --- a/packages/lib/urql/updates/post-create.ts +++ b/packages/lib/urql/updates/post-create.ts @@ -1,15 +1,16 @@ -import { UpdateResolver } from '@urql/exchange-graphcache' +import { NullArray, UpdateResolver } from '@urql/exchange-graphcache' -import { WithTypeName } from '../types' +import { Post } from '@acter/schema' +import POST_FRAGMENT from '@acter/schema/fragments/post-full.graphql' +import GET_POSTS from '@acter/schema/queries/posts-by-acter.graphql' + +import { WithTypeName, WithWhereActerId } from '../types' import { forEachQueryFields, matchOnActerId, prependItemFn, } from '../util/query-fields-for-each' -import { Post } from '@acter/schema' -import POST_FRAGMENT from '@acter/schema/fragments/post-full.graphql' - type PostWithType = WithTypeName interface PostData { @@ -23,15 +24,23 @@ export const createPost: UpdateResolver = ( _info ) => { if (result.createPost?.parentId) { + // forEachQueryFields({ + // cache, + // result: result.createPost, + // fieldNameMatch: 'posts', + // match: matchOnActerId, + // fn: ({ fieldKey, fieldArgs, items }) => { + // debugger + // }, + // }) const data = cache.readFragment(POST_FRAGMENT, { id: result.createPost.parentId, __typename: 'Post', }) cache.writeFragment(POST_FRAGMENT, { ...data, - Comments: [...data.Comments, result.createPost], + Comments: [result.createPost, ...data.Comments], }) - return } forEachQueryFields({ diff --git a/packages/lib/urql/use-paginated-query/pagainated-results.reducer.test.ts b/packages/lib/urql/use-paginated-query/pagainated-results.reducer.test.ts index 8ccdda34e..843989b85 100644 --- a/packages/lib/urql/use-paginated-query/pagainated-results.reducer.test.ts +++ b/packages/lib/urql/use-paginated-query/pagainated-results.reducer.test.ts @@ -1,8 +1,11 @@ +import { OrderedMap } from 'immutable' + import { getPaginatedResultsReducer } from './paginated-results-reducer' import { GetPaginatedResultsReducer, PaginatedResultsActionKind, PaginatedResultsState, + ResultsType, } from './types' describe('paginatedResultsReducer', () => { @@ -24,8 +27,8 @@ describe('paginatedResultsReducer', () => { }) describe('reducer', () => { - let reducer: GetPaginatedResultsReducer - let defaultState: PaginatedResultsState + let reducer: GetPaginatedResultsReducer + let defaultState: PaginatedResultsState beforeEach(() => { const result = getPaginatedResultsReducer() @@ -37,7 +40,7 @@ describe('paginatedResultsReducer', () => { const state = reducer( { ...defaultState, - results: [{ id: 1 }, { id: 2 }], + results: OrderedMap([{ id: 1 }, { id: 2 }]), pagination: { ...defaultState.pagination, cursor: { id: 1 }, @@ -47,7 +50,7 @@ describe('paginatedResultsReducer', () => { { type: PaginatedResultsActionKind.NEW_SEARCH } ) - expect(state.results).toStrictEqual([]) + expect(state.results.toList().toArray()).toStrictEqual([]) expect(state.pagination.cursor).toBe(undefined) expect(state.pagination.skip).toBe(0) }) @@ -61,7 +64,7 @@ describe('paginatedResultsReducer', () => { describe('new results', () => { describe('hasMore', () => { - let hasMoreState: PaginatedResultsState + let hasMoreState: PaginatedResultsState beforeEach(() => { hasMoreState = { ...defaultState, @@ -119,25 +122,54 @@ describe('paginatedResultsReducer', () => { } ) - expect(state.results).toStrictEqual([{ id: 1 }]) + expect(state.results.toList().toArray()).toStrictEqual([{ id: 1 }]) }) it('should append new results', () => { const currentResults = [{ id: 1 }, { id: 2 }] const newResults = [{ id: 3 }, { id: 4 }] const state = reducer( - { ...defaultState, results: currentResults }, + { + ...defaultState, + results: OrderedMap({ + 1: { id: 1 }, + 2: { id: 2 }, + }) as ResultsType, + }, { type: PaginatedResultsActionKind.NEW_RESULTS, payload: { results: newResults }, } ) - expect(state.results).toStrictEqual([ + expect(state.results.toList().toArray()).toStrictEqual([ ...currentResults, ...newResults, ]) }) + + it('should dedupe entries', () => { + const newResults = [{ id: 2 }, { id: 3 }] + const state = reducer( + { + ...defaultState, + results: OrderedMap({ + 1: { id: 1 }, + 2: { id: 2 }, + }) as ResultsType, + }, + { + type: PaginatedResultsActionKind.NEW_RESULTS, + payload: { results: newResults }, + } + ) + + expect(state.results.toList().toArray()).toStrictEqual([ + { id: 1 }, + { id: 2 }, + { id: 3 }, + ]) + }) }) describe('loadMore', () => { @@ -154,9 +186,9 @@ describe('paginatedResultsReducer', () => { }) it('should set cursor and skip on pagination', () => { - const currentResults = [{ id: 1 }, { id: 2 }] + const currentResults = OrderedMap({ 1: { id: 1 }, 2: { id: 2 } }) const state = reducer( - { ...defaultState, results: currentResults }, + { ...defaultState, results: OrderedMap(currentResults) }, { type: PaginatedResultsActionKind.LOAD_MORE, } diff --git a/packages/lib/urql/use-paginated-query/paginated-results-reducer.ts b/packages/lib/urql/use-paginated-query/paginated-results-reducer.ts index 7482b84e4..7e67f1cdd 100644 --- a/packages/lib/urql/use-paginated-query/paginated-results-reducer.ts +++ b/packages/lib/urql/use-paginated-query/paginated-results-reducer.ts @@ -1,3 +1,5 @@ +import { OrderedMap } from 'immutable' + import { WithId, PaginatedResultsState, @@ -26,7 +28,7 @@ export const getPaginatedResultsReducer = < } const defaultState: ReducerState = { - results: [], + results: OrderedMap(), hasMore: false, fetching: false, pagination: defaultPagination, @@ -54,11 +56,15 @@ export const getPaginatedResultsReducer = < const hasMore = results.length > state.pagination.take const sliceEnd = hasMore ? -1 : undefined const nextResultsPage = results.slice(0, sliceEnd) + const newResults = nextResultsPage.reduce( + (map, item) => map.set(item.id.toString(), item), + state.results + ) return { ...state, hasMore, fetching: false, - results: [...state.results, ...nextResultsPage], + results: newResults, } } return { @@ -68,12 +74,12 @@ export const getPaginatedResultsReducer = < } } case PaginatedResultsActionKind.LOAD_MORE: { - if (state.results.length > 0) { + if (state.results.size > 0) { return { ...state, pagination: { ...state.pagination, - cursor: { id: state.results[state.results.length - 1].id }, + cursor: { id: state.results.last().id }, skip: 1, }, } diff --git a/packages/lib/urql/use-paginated-query/types.ts b/packages/lib/urql/use-paginated-query/types.ts index 5e491ccf9..1d1303031 100644 --- a/packages/lib/urql/use-paginated-query/types.ts +++ b/packages/lib/urql/use-paginated-query/types.ts @@ -2,6 +2,7 @@ import { Reducer } from 'react' import { DocumentNode } from 'graphql/language/ast' +import { OrderedMap } from 'immutable' import { UseQueryState, TypedDocumentNode, UseQueryArgs } from 'urql' export interface Pagination { @@ -10,9 +11,10 @@ export interface Pagination { take: number } +export type ResultsType = OrderedMap export interface PaginatedResultsState extends UseQueryState { - results: TType[] + results: ResultsType fetching: boolean hasMore: boolean pagination: Pagination @@ -65,7 +67,7 @@ export type VariablesWithPagination = TVariables & Pagination export interface UsePaginatedState extends UseQueryState { - results: TType[] + results: ResultsType fetching: boolean hasMore: boolean pagination: Pagination diff --git a/packages/lib/urql/use-paginated-query/use-paginated-query.ts b/packages/lib/urql/use-paginated-query/use-paginated-query.ts index 5f61d9e53..e4cc69229 100644 --- a/packages/lib/urql/use-paginated-query/use-paginated-query.ts +++ b/packages/lib/urql/use-paginated-query/use-paginated-query.ts @@ -1,10 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { useEffect, useReducer, useRef } from 'react' +import { useEffect, useReducer } from 'react' import { useQuery } from 'urql' -import { getObjectArrayMemoString } from '@acter/lib/object/get-object-array-memo-string' - import { getPaginatedResultsReducer } from './paginated-results-reducer' import { WithId, @@ -17,8 +15,7 @@ import { export const usePaginatedQuery = ( options: UsePaginationQueryOptions ): UsePaginatedResponse => { - const { query, dataKey, variables, pageSize = 20 } = options - const dataRef = useRef('') + const { query, dataKey, variables, pageSize = 10 } = options const [paginatedResultsReducer, { defaultState }] = getPaginatedResultsReducer({ @@ -28,8 +25,7 @@ export const usePaginatedQuery = ( useEffect(() => { dispatch({ type: PaginatedResultsActionKind.NEW_SEARCH }) - dataRef.current = undefined - }, [variables]) + }, [JSON.stringify(variables)]) const [{ data, fetching: dataFetching, ...restQueryResult }, refetch] = useQuery>({ @@ -48,12 +44,7 @@ export const usePaginatedQuery = ( // Let reducer know there are new results useEffect(() => { - const newRef = getObjectArrayMemoString(data?.[dataKey]) - if ( - (!dataRef.current && data?.[dataKey]) || - (newRef && dataRef.current !== newRef) - ) { - dataRef.current = newRef + if (data?.[dataKey]) { dispatch({ type: PaginatedResultsActionKind.NEW_RESULTS, payload: { results: data[dataKey] }, @@ -61,10 +52,13 @@ export const usePaginatedQuery = ( } }, [data?.[dataKey]]) + const { results, ...restState } = state + return [ { ...restQueryResult, - ...state, + ...restState, + results, loadMore: () => { dispatch({ type: PaginatedResultsActionKind.LOAD_MORE }) }, diff --git a/packages/schema/fragments/post-full.graphql b/packages/schema/fragments/post-full.graphql index 67e737e1a..0b043ff01 100644 --- a/packages/schema/fragments/post-full.graphql +++ b/packages/schema/fragments/post-full.graphql @@ -2,7 +2,7 @@ fragment PostFull on Post { ...PostDisplay - Comments(orderBy: { createdAt: asc }) { + Comments(orderBy: { createdAt: desc }, take: 6) { ...PostDisplay } Parent { diff --git a/packages/schema/queries/posts-by-acter.graphql b/packages/schema/queries/posts-by-acter.graphql index 2d92deb4e..3bc6badd9 100644 --- a/packages/schema/queries/posts-by-acter.graphql +++ b/packages/schema/queries/posts-by-acter.graphql @@ -1,10 +1,18 @@ #import "../fragments/post-full.graphql" #import "../fragments/acter-display.fragment.graphql" -query GetPosts($acterId: String) { +query GetPosts( + $acterId: String + $take: Int + $skip: Int + $cursor: PostWhereUniqueInput +) { posts( where: { acterId: { equals: $acterId }, parentId: null } orderBy: { createdAt: desc } + take: $take + skip: $skip + cursor: $cursor ) { ...PostFull } diff --git a/yarn.lock b/yarn.lock index 3e44b1fac..96dcee027 100644 --- a/yarn.lock +++ b/yarn.lock @@ -93,7 +93,6 @@ __metadata: react-filepond: 7.1.1 react-google-autocomplete: 2.6.1 react-imgix: 9.5.0 - react-infinite-scroll-component: 6.1.0 react-is-visible: 1.1.2 react-sanitized-html: ^2.0.0 sanitize-html: ^2.7.0 @@ -163,6 +162,7 @@ __metadata: deep-equal: 2.0.5 deepmerge: 4.2.2 graphql: 15.5.1 + immutable: ^4.1.0 intercom-client: 2.11.2 ioredis: 4.27.7 just-camel-case: ^6.0.1 @@ -15903,6 +15903,13 @@ __metadata: languageName: node linkType: hard +"immutable@npm:^4.1.0": + version: 4.1.0 + resolution: "immutable@npm:4.1.0" + checksum: b9bc1f14fb18eb382d48339c064b24a1f97ae4cf43102e0906c0a6e186a27afcd18b55ca4a0b63c98eefb58143e2b5ebc7755a5fb4da4a7ad84b7a6096ac5b13 + languageName: node + linkType: hard + "immutable@npm:~3.7.4": version: 3.7.6 resolution: "immutable@npm:3.7.6" @@ -22850,17 +22857,6 @@ __metadata: languageName: node linkType: hard -"react-infinite-scroll-component@npm:6.1.0": - version: 6.1.0 - resolution: "react-infinite-scroll-component@npm:6.1.0" - dependencies: - throttle-debounce: ^2.1.0 - peerDependencies: - react: ">=16.0.0" - checksum: 3708398934366df907dbad215247ebc1033221957ce7e32289ea31750cce70aa16513e2d03743e06c8b868ac7c542d12d5dbb6c830fd408433a4762f3cb5ecfb - languageName: node - linkType: hard - "react-inspector@npm:^5.1.0": version: 5.1.1 resolution: "react-inspector@npm:5.1.1" @@ -25915,13 +25911,6 @@ resolve@^2.0.0-next.3: languageName: node linkType: hard -"throttle-debounce@npm:^2.1.0": - version: 2.3.0 - resolution: "throttle-debounce@npm:2.3.0" - checksum: 6d90aa2ddb294f8dad13d854a1cfcd88fdb757469669a096a7da10f515ee466857ac1e750649cb9da931165c6f36feb448318e7cb92570f0a3679d20e860a925 - languageName: node - linkType: hard - "throttle-debounce@npm:^3.0.1": version: 3.0.1 resolution: "throttle-debounce@npm:3.0.1"