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/organisms/form/index.tsx b/packages/components/posts/organisms/form/post-form.tsx similarity index 100% rename from packages/components/posts/organisms/form/index.tsx rename to packages/components/posts/organisms/form/post-form.tsx diff --git a/packages/components/posts/organisms/post/post.tsx b/packages/components/posts/organisms/post/post.tsx index 1c3ae2693..a62d3f013 100644 --- a/packages/components/posts/organisms/post/post.tsx +++ b/packages/components/posts/organisms/post/post.tsx @@ -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' @@ -19,27 +19,38 @@ 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) => { - return ( - - ) - })} + {comments.length !== 0 && ( + <> + + {hasMoreComments && <>Load more comments} + {comments.map((comment) => { + return ( + + ) + })} + + )} {isMember && ( diff --git a/packages/lib/get-object-array-memo-string/get-object-array-memo-string.test.ts b/packages/lib/get-object-array-memo-string/get-object-array-memo-string.test.ts index 3a74d7051..f547c74ad 100644 --- a/packages/lib/get-object-array-memo-string/get-object-array-memo-string.test.ts +++ b/packages/lib/get-object-array-memo-string/get-object-array-memo-string.test.ts @@ -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', () => { @@ -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') + }) }) diff --git a/packages/lib/get-object-array-memo-string/get-object-array-memo-string.ts b/packages/lib/get-object-array-memo-string/get-object-array-memo-string.ts index 0eaf64fab..232da643a 100644 --- a/packages/lib/get-object-array-memo-string/get-object-array-memo-string.ts +++ b/packages/lib/get-object-array-memo-string/get-object-array-memo-string.ts @@ -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}` +} 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 e3d48217b..613ce13aa 100644 --- a/packages/lib/post/use-posts.ts +++ b/packages/lib/post/use-posts.ts @@ -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, @@ -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 } 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/paginated-results-reducer.ts b/packages/lib/urql/use-paginated-query/paginated-results-reducer.ts index 7482b84e4..52f00c186 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,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 { @@ -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, }, } 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 7025072c6..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/get-object-array-memo-string' - import { getPaginatedResultsReducer } from './paginated-results-reducer' import { WithId, @@ -18,7 +16,6 @@ export const usePaginatedQuery = ( options: UsePaginationQueryOptions ): UsePaginatedResponse => { const { query, dataKey, variables, pageSize = 10 } = options - const dataRef = useRef('') const [paginatedResultsReducer, { defaultState }] = getPaginatedResultsReducer({ @@ -28,7 +25,6 @@ export const usePaginatedQuery = ( useEffect(() => { dispatch({ type: PaginatedResultsActionKind.NEW_SEARCH }) - dataRef.current = undefined }, [JSON.stringify(variables)]) const [{ data, fetching: dataFetching, ...restQueryResult }, refetch] = @@ -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 64cb39c20..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 }, take: 5) { + Comments(orderBy: { createdAt: desc }, take: 6) { ...PostDisplay } Parent { diff --git a/yarn.lock b/yarn.lock index 37196c4f8..96dcee027 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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 @@ -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"