Skip to content

Commit

Permalink
WIP Post/comment pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
orbiteleven committed Jun 9, 2022
1 parent 4e6ee86 commit 9bb5291
Show file tree
Hide file tree
Showing 13 changed files with 134 additions and 45 deletions.
2 changes: 2 additions & 0 deletions packages/components/posts/organisms/form/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './post-form'
export * from './post-form-section'
37 changes: 24 additions & 13 deletions packages/components/posts/organisms/post/post.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
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'
Expand All @@ -19,27 +19,38 @@ export const Post: FC<SinglePostProps> = ({ 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 (
<Box className={classes.contentContainer}>
<PostContainer post={post} user={user} />
<Box className={classes.commentSection}>
{post.Comments.length !== 0 && <Divider className={classes.divider} />}

{post.Comments?.map((comment) => {
return (
<PostContainer
key={`post-${post.id}-comment-${comment.id}`}
post={comment}
parentId={post.id}
user={user}
/>
)
})}
{comments.length !== 0 && (
<>
<Divider className={classes.divider} />
{hasMoreComments && <>Load more comments</>}
{comments.map((comment) => {
return (
<PostContainer
key={`post-${post.id}-comment-${comment.id}`}
post={comment}
parentId={post.id}
user={user}
/>
)
})}
</>
)}

{isMember && (
<PostFormSection parentId={post.id} user={user} acterId={acterId} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { getObjectArrayMemoString } from './get-object-array-memo-string'

describe('getObjectArrayMemoString', () => {
it('should return null passed value is not an array', () => {
expect(getObjectArrayMemoString(undefined)).toBe(null)
it('should return an empty string if given an undefined value', () => {
expect(getObjectArrayMemoString(undefined)).toBe('')
})

it('should return an empty string if the array is empty', () => {
Expand Down Expand Up @@ -35,4 +35,27 @@ describe('getObjectArrayMemoString', () => {
]
expect(getObjectArrayMemoString(results)).toBe(',')
})

it('should return a string of nested ids when fields have their own lists', () => {
const results = [
{
id: 'one',
list: [
{
id: 'a',
},
{
id: 'b',
},
],
},
{
id: 'two',
},
{
id: 'three',
},
]
expect(getObjectArrayMemoString(results)).toBe('one,one.a,one.b,two,three')
})
})
Original file line number Diff line number Diff line change
@@ -1,2 +1,31 @@
export const getObjectArrayMemoString = (results: unknown): string =>
results && Array.isArray(results) ? results.map((r) => r.id).join(',') : null
type WithId = {
id: unknown
}

export const getObjectArrayMemoString = (
results: unknown,
prefix = ''
): string => {
if (!results) return ''
if (Array.isArray(results)) {
return results
.map((result) => getObjectArrayMemoString(result, prefix))
.join(',')
}
if (typeof results === 'object') {
const objectId = (results as WithId).id || ''
return Object.values(results)
.reduce(
(state, value) => {
if (!Array.isArray(value)) return [`${prefix}${state}`]
return [
...state,
getObjectArrayMemoString(value, `${prefix}${objectId}.`),
]
},
[objectId]
)
.join(',')
}
return `${prefix}${results}`
}
1 change: 1 addition & 0 deletions packages/lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
7 changes: 5 additions & 2 deletions packages/lib/post/use-posts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { useEffect, useState } from 'react'
import { CombinedError, UseQueryArgs } from 'urql'

import { useActer } from '@acter/lib/acter/use-acter'
import { getObjectArrayMemoString } from '@acter/lib/get-object-array-memo-string'
import {
usePaginatedQuery,
UsePaginatedState,
Expand Down Expand Up @@ -74,7 +73,11 @@ export const usePosts = (params?: UsePostsParams): UsePostsResult => {
if (queryError) return setError(queryError)
}, [acterError, queryError])

useEffect(() => setPosts(results), [getObjectArrayMemoString(results)])
useEffect(() => {
debugger
console.log('post results', results.toList().toArray())
setPosts(results.toList().toArray())
}, [results])

return { posts, fetching, error, ...restQueryResults } as UsePostsResult
}
23 changes: 16 additions & 7 deletions packages/lib/urql/updates/post-create.ts
Original file line number Diff line number Diff line change
@@ -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<Post>

interface PostData {
Expand All @@ -23,15 +24,23 @@ export const createPost: UpdateResolver<PostData> = (
_info
) => {
if (result.createPost?.parentId) {
// forEachQueryFields({
// cache,
// result: result.createPost,
// fieldNameMatch: 'posts',
// match: matchOnActerId,
// fn: ({ fieldKey, fieldArgs, items }) => {
// debugger
// },
// })
const data = cache.readFragment<PostWithType>(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({
Expand Down
15 changes: 11 additions & 4 deletions packages/lib/urql/use-paginated-query/paginated-results-reducer.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { OrderedMap } from 'immutable'

import {
WithId,
PaginatedResultsState,
Expand Down Expand Up @@ -26,7 +28,7 @@ export const getPaginatedResultsReducer = <
}

const defaultState: ReducerState = {
results: [],
results: OrderedMap(),
hasMore: false,
fetching: false,
pagination: defaultPagination,
Expand Down Expand Up @@ -54,11 +56,16 @@ 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, item),
state.results
)
debugger
return {
...state,
hasMore,
fetching: false,
results: [...state.results, ...nextResultsPage],
results: newResults,
}
}
return {
Expand All @@ -68,12 +75,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,
},
}
Expand Down
6 changes: 4 additions & 2 deletions packages/lib/urql/use-paginated-query/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -10,9 +11,10 @@ export interface Pagination {
take: number
}

export type ResultsType<TType = unknown> = OrderedMap<string | number, TType>
export interface PaginatedResultsState<TType, TData, TVariables>
extends UseQueryState<TData, TVariables> {
results: TType[]
results: ResultsType<TType>
fetching: boolean
hasMore: boolean
pagination: Pagination
Expand Down Expand Up @@ -65,7 +67,7 @@ export type VariablesWithPagination<TVariables> = TVariables & Pagination

export interface UsePaginatedState<TType = any, TData = any, TVariables = any>
extends UseQueryState<TData, TVariables> {
results: TType[]
results: ResultsType<TType>
fetching: boolean
hasMore: boolean
pagination: Pagination
Expand Down
18 changes: 6 additions & 12 deletions packages/lib/urql/use-paginated-query/use-paginated-query.ts
Original file line number Diff line number Diff line change
@@ -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/get-object-array-memo-string'

import { getPaginatedResultsReducer } from './paginated-results-reducer'
import {
WithId,
Expand All @@ -18,7 +16,6 @@ export const usePaginatedQuery = <TType extends WithId, TData, TVariables>(
options: UsePaginationQueryOptions<TData, TVariables>
): UsePaginatedResponse<TType, TData, TVariables> => {
const { query, dataKey, variables, pageSize = 10 } = options
const dataRef = useRef('')

const [paginatedResultsReducer, { defaultState }] =
getPaginatedResultsReducer<TType, TData, TVariables>({
Expand All @@ -28,7 +25,6 @@ export const usePaginatedQuery = <TType extends WithId, TData, TVariables>(

useEffect(() => {
dispatch({ type: PaginatedResultsActionKind.NEW_SEARCH })
dataRef.current = undefined
}, [JSON.stringify(variables)])

const [{ data, fetching: dataFetching, ...restQueryResult }, refetch] =
Expand All @@ -48,23 +44,21 @@ export const usePaginatedQuery = <TType extends WithId, TData, TVariables>(

// 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] },
})
}
}, [data?.[dataKey]])

const { results, ...restState } = state

return [
{
...restQueryResult,
...state,
...restState,
results,
loadMore: () => {
dispatch({ type: PaginatedResultsActionKind.LOAD_MORE })
},
Expand Down
2 changes: 1 addition & 1 deletion packages/schema/fragments/post-full.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

fragment PostFull on Post {
...PostDisplay
Comments(orderBy: { createdAt: asc }, take: 5) {
Comments(orderBy: { createdAt: desc }, take: 6) {
...PostDisplay
}
Parent {
Expand Down
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -162,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
Expand Down Expand Up @@ -15902,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"
Expand Down

0 comments on commit 9bb5291

Please sign in to comment.