diff --git a/functions/graphql/nexus-typegen.ts b/functions/graphql/nexus-typegen.ts index 8bfff43..9115fc5 100644 --- a/functions/graphql/nexus-typegen.ts +++ b/functions/graphql/nexus-typegen.ts @@ -72,8 +72,10 @@ export interface NexusGenObjects { PostComment: { // root type author: NexusGenRootTypes['User']; // User! body: string; // String! - date: string; // String! + created_at: string; // String! id: number; // Int! + parentId: number; // Int! + votes_count: number; // Int! } Project: { // root type cover_image: string; // String! @@ -195,8 +197,10 @@ export interface NexusGenFieldTypes { PostComment: { // field return type author: NexusGenRootTypes['User']; // User! body: string; // String! - date: string; // String! + created_at: string; // String! id: number; // Int! + parentId: number; // Int! + votes_count: number; // Int! } Project: { // field return type awards: NexusGenRootTypes['Award'][]; // [Award!]! @@ -335,8 +339,10 @@ export interface NexusGenFieldTypeNames { PostComment: { // field return type name author: 'User' body: 'String' - date: 'String' + created_at: 'String' id: 'Int' + parentId: 'Int' + votes_count: 'Int' } Project: { // field return type name awards: 'Award' diff --git a/functions/graphql/schema.graphql b/functions/graphql/schema.graphql index 92330d5..928e48d 100644 --- a/functions/graphql/schema.graphql +++ b/functions/graphql/schema.graphql @@ -78,8 +78,10 @@ interface PostBase { type PostComment { author: User! body: String! - date: String! + created_at: String! id: Int! + parentId: Int! + votes_count: Int! } type Project { diff --git a/functions/graphql/types/post.js b/functions/graphql/types/post.js index cc41d4b..31dab6b 100644 --- a/functions/graphql/types/post.js +++ b/functions/graphql/types/post.js @@ -102,11 +102,13 @@ const PostComment = objectType({ name: 'PostComment', definition(t) { t.nonNull.int('id'); - t.nonNull.string('date'); + t.nonNull.string('created_at'); t.nonNull.string('body'); t.nonNull.field('author', { type: "User" }); + t.nonNull.int('parentId'); + t.nonNull.int('votes_count'); } }) diff --git a/src/features/Posts/Components/CommentCard/CommentCard.stories.tsx b/src/features/Posts/Components/CommentCard/CommentCard.stories.tsx new file mode 100644 index 0000000..e095e04 --- /dev/null +++ b/src/features/Posts/Components/CommentCard/CommentCard.stories.tsx @@ -0,0 +1,22 @@ +import { ComponentStory, ComponentMeta } from '@storybook/react'; +import { MOCK_DATA } from 'src/mocks/data'; + +import CommentCard from './CommentCard'; + +export default { + title: 'Posts/Components/CommentCard', + component: CommentCard, + argTypes: { + backgroundColor: { control: 'color' }, + }, +} as ComponentMeta; + + +const Template: ComponentStory = (args) =>
+ +export const Default = Template.bind({}); +Default.args = { + comment: MOCK_DATA.posts.stories[0].comments[0] +} + + diff --git a/src/features/Posts/Components/CommentCard/CommentCard.tsx b/src/features/Posts/Components/CommentCard/CommentCard.tsx new file mode 100644 index 0000000..b5ab494 --- /dev/null +++ b/src/features/Posts/Components/CommentCard/CommentCard.tsx @@ -0,0 +1,32 @@ +import { BiComment } from "react-icons/bi"; +import Button from "src/Components/Button/Button"; +import VotesCount from "src/Components/VotesCount/VotesCount"; +import { Author } from "../../types" +import Header from "../PostCard/Header/Header"; + +interface Comment { + author: Author + created_at: string + body: string; + votes_count: number +} +interface Props { + comment: Comment +} + +export default function CommentCard({ comment }: Props) { + return ( +
+
+

+ {comment.body} +

+
+ + +
+
+ ) +} diff --git a/src/features/Posts/Components/PostCard/Header/Header.tsx b/src/features/Posts/Components/PostCard/Header/Header.tsx index c5584ad..a73325f 100644 --- a/src/features/Posts/Components/PostCard/Header/Header.tsx +++ b/src/features/Posts/Components/PostCard/Header/Header.tsx @@ -30,16 +30,22 @@ export default function Header({ showTimeAgo = true, ...props }: Props) { + const passedTime = dayjs().diff(props.date, 'hour'); + const dateToShow = passedTime < 24 ? + `${dayjs().diff(props.date, 'hour')}h ago` + : + dayjs(props.date).format('MMMM DD'); + return (

{props.author.name}

-

{dayjs(props.date).format('MMMM DD')}

+

{dateToShow}

- {showTimeAgo &&

+ {/* {showTimeAgo &&

{dayjs().diff(props.date, 'hour') < 24 ? `${dayjs().diff(props.date, 'hour')}h ago` : undefined} -

} +

} */}
) } diff --git a/src/features/Posts/Components/PostCard/QuestionCard/QuestionCard.tsx b/src/features/Posts/Components/PostCard/QuestionCard/QuestionCard.tsx index 62ad74c..5ecd680 100644 --- a/src/features/Posts/Components/PostCard/QuestionCard/QuestionCard.tsx +++ b/src/features/Posts/Components/PostCard/QuestionCard/QuestionCard.tsx @@ -4,6 +4,7 @@ import Header from "../Header/Header" import { FiUsers } from "react-icons/fi" import Badge from "src/Components/Badge/Badge" import { Link } from "react-router-dom" +import { trimText } from "src/utils/helperFunctions" export type QuestionCardType = Pick; +> & { + comments: Array> +}; interface Props { question: QuestionCardType } @@ -52,9 +59,9 @@ export default function QuestionCard({ question }: Props) {
- {question.comments.map(comment =>
-
-

{comment.body}

+ {question.comments.slice(0, 2).map(comment =>
+
+

{trimText(comment.body, 80)}

)}
diff --git a/src/features/Posts/pages/FeedPage/feed.graphql b/src/features/Posts/pages/FeedPage/feed.graphql index 1cad0fc..974bad6 100644 --- a/src/features/Posts/pages/FeedPage/feed.graphql +++ b/src/features/Posts/pages/FeedPage/feed.graphql @@ -59,7 +59,7 @@ query Feed($take: Int, $skip: Int, $sortBy: String, $category: String) { answers_count comments { id - date + created_at body author { id diff --git a/src/features/Posts/pages/PostDetailsPage/postDetails.graphql b/src/features/Posts/pages/PostDetailsPage/postDetails.graphql index 79c5191..096a6b7 100644 --- a/src/features/Posts/pages/PostDetailsPage/postDetails.graphql +++ b/src/features/Posts/pages/PostDetailsPage/postDetails.graphql @@ -20,7 +20,7 @@ query PostDetails($id: Int!, $type: POST_TYPE!) { comments_count comments { id - date + created_at body author { id @@ -79,7 +79,7 @@ query PostDetails($id: Int!, $type: POST_TYPE!) { answers_count comments { id - date + created_at body author { id diff --git a/src/graphql/index.tsx b/src/graphql/index.tsx index ccfeb7c..b2e6e74 100644 --- a/src/graphql/index.tsx +++ b/src/graphql/index.tsx @@ -5,7 +5,7 @@ export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; -const defaultOptions = {} +const defaultOptions = {} as const; /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { ID: string; @@ -110,8 +110,10 @@ export type PostComment = { __typename?: 'PostComment'; author: User; body: Scalars['String']; - date: Scalars['String']; + created_at: Scalars['String']; id: Scalars['Int']; + parentId: Scalars['Int']; + votes_count: Scalars['Int']; }; export type Project = { @@ -265,7 +267,7 @@ export type Vote = { export type NavCategoriesQueryVariables = Exact<{ [key: string]: never; }>; -export type NavCategoriesQuery = { __typename?: 'Query', allCategories: Array<{ __typename?: 'Category', id: number, title: string, icon: string | null | undefined, votes_sum: number }> }; +export type NavCategoriesQuery = { __typename?: 'Query', allCategories: Array<{ __typename?: 'Category', id: number, title: string, icon: string | null, votes_sum: number }> }; export type SearchProjectsQueryVariables = Exact<{ search: Scalars['String']; @@ -287,7 +289,7 @@ export type FeedQueryVariables = Exact<{ }>; -export type FeedQuery = { __typename?: 'Query', getFeed: Array<{ __typename?: 'Bounty', id: number, title: string, date: string, excerpt: string, votes_count: number, type: string, cover_image: string, deadline: string, reward_amount: number, applicants_count: number, author: { __typename?: 'User', id: number, name: string, image: string }, tags: Array<{ __typename?: 'Tag', id: number, title: string }> } | { __typename?: 'Question', id: number, title: string, date: string, excerpt: string, votes_count: number, type: string, answers_count: number, author: { __typename?: 'User', id: number, name: string, image: string }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, comments: Array<{ __typename?: 'PostComment', id: number, date: string, body: string, author: { __typename?: 'User', id: number, name: string, image: string } }> } | { __typename?: 'Story', id: number, title: string, date: string, excerpt: string, votes_count: number, type: string, cover_image: string, comments_count: number, author: { __typename?: 'User', id: number, name: string, image: string }, tags: Array<{ __typename?: 'Tag', id: number, title: string }> }> }; +export type FeedQuery = { __typename?: 'Query', getFeed: Array<{ __typename?: 'Bounty', id: number, title: string, date: string, excerpt: string, votes_count: number, type: string, cover_image: string, deadline: string, reward_amount: number, applicants_count: number, author: { __typename?: 'User', id: number, name: string, image: string }, tags: Array<{ __typename?: 'Tag', id: number, title: string }> } | { __typename?: 'Question', id: number, title: string, date: string, excerpt: string, votes_count: number, type: string, answers_count: number, author: { __typename?: 'User', id: number, name: string, image: string }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, comments: Array<{ __typename?: 'PostComment', id: number, created_at: string, body: string, author: { __typename?: 'User', id: number, name: string, image: string } }> } | { __typename?: 'Story', id: number, title: string, date: string, excerpt: string, votes_count: number, type: string, cover_image: string, comments_count: number, author: { __typename?: 'User', id: number, name: string, image: string }, tags: Array<{ __typename?: 'Tag', id: number, title: string }> }> }; export type PostDetailsQueryVariables = Exact<{ id: Scalars['Int']; @@ -295,14 +297,14 @@ export type PostDetailsQueryVariables = Exact<{ }>; -export type PostDetailsQuery = { __typename?: 'Query', getPostById: { __typename?: 'Bounty', id: number, title: string, date: string, body: string, votes_count: number, type: string, cover_image: string, deadline: string, reward_amount: number, applicants_count: number, author: { __typename?: 'User', id: number, name: string, image: string }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, applications: Array<{ __typename?: 'BountyApplication', id: number, date: string, workplan: string, author: { __typename?: 'User', id: number, name: string, image: string } }> } | { __typename?: 'Question', id: number, title: string, date: string, body: string, votes_count: number, type: string, answers_count: number, author: { __typename?: 'User', id: number, name: string, image: string }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, comments: Array<{ __typename?: 'PostComment', id: number, date: string, body: string, author: { __typename?: 'User', id: number, name: string, image: string } }> } | { __typename?: 'Story', id: number, title: string, date: string, body: string, votes_count: number, type: string, cover_image: string, comments_count: number, author: { __typename?: 'User', id: number, name: string, image: string }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, comments: Array<{ __typename?: 'PostComment', id: number, date: string, body: string, author: { __typename?: 'User', id: number, name: string, image: string } }> } }; +export type PostDetailsQuery = { __typename?: 'Query', getPostById: { __typename?: 'Bounty', id: number, title: string, date: string, body: string, votes_count: number, type: string, cover_image: string, deadline: string, reward_amount: number, applicants_count: number, author: { __typename?: 'User', id: number, name: string, image: string }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, applications: Array<{ __typename?: 'BountyApplication', id: number, date: string, workplan: string, author: { __typename?: 'User', id: number, name: string, image: string } }> } | { __typename?: 'Question', id: number, title: string, date: string, body: string, votes_count: number, type: string, answers_count: number, author: { __typename?: 'User', id: number, name: string, image: string }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, comments: Array<{ __typename?: 'PostComment', id: number, created_at: string, body: string, author: { __typename?: 'User', id: number, name: string, image: string } }> } | { __typename?: 'Story', id: number, title: string, date: string, body: string, votes_count: number, type: string, cover_image: string, comments_count: number, author: { __typename?: 'User', id: number, name: string, image: string }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, comments: Array<{ __typename?: 'PostComment', id: number, created_at: string, body: string, author: { __typename?: 'User', id: number, name: string, image: string } }> } }; export type CategoryPageQueryVariables = Exact<{ categoryId: Scalars['Int']; }>; -export type CategoryPageQuery = { __typename?: 'Query', projectsByCategory: Array<{ __typename?: 'Project', id: number, thumbnail_image: string, title: string, votes_count: number, category: { __typename?: 'Category', title: string, id: number } }>, getCategory: { __typename?: 'Category', id: number, title: string, cover_image: string | null | undefined, apps_count: number } }; +export type CategoryPageQuery = { __typename?: 'Query', projectsByCategory: Array<{ __typename?: 'Project', id: number, thumbnail_image: string, title: string, votes_count: number, category: { __typename?: 'Category', title: string, id: number } }>, getCategory: { __typename?: 'Category', id: number, title: string, cover_image: string | null, apps_count: number } }; export type AllCategoriesQueryVariables = Exact<{ [key: string]: never; }>; @@ -324,7 +326,7 @@ export type ProjectDetailsQueryVariables = Exact<{ }>; -export type ProjectDetailsQuery = { __typename?: 'Query', getProject: { __typename?: 'Project', id: number, title: string, description: string, cover_image: string, thumbnail_image: string, screenshots: Array, website: string, lightning_address: string | null | undefined, lnurl_callback_url: string | null | undefined, votes_count: number, category: { __typename?: 'Category', id: number, title: string }, awards: Array<{ __typename?: 'Award', title: string, image: string, url: string, id: number }>, tags: Array<{ __typename?: 'Tag', id: number, title: string }> } }; +export type ProjectDetailsQuery = { __typename?: 'Query', getProject: { __typename?: 'Project', id: number, title: string, description: string, cover_image: string, thumbnail_image: string, screenshots: Array, website: string, lightning_address: string | null, lnurl_callback_url: string | null, votes_count: number, category: { __typename?: 'Category', id: number, title: string }, awards: Array<{ __typename?: 'Award', title: string, image: string, url: string, id: number }>, tags: Array<{ __typename?: 'Tag', id: number, title: string }> } }; export type VoteMutationVariables = Exact<{ projectId: Scalars['Int']; @@ -540,7 +542,7 @@ export const FeedDocument = gql` answers_count comments { id - date + created_at body author { id @@ -606,7 +608,7 @@ export const PostDetailsDocument = gql` comments_count comments { id - date + created_at body author { id @@ -665,7 +667,7 @@ export const PostDetailsDocument = gql` answers_count comments { id - date + created_at body author { id diff --git a/src/mocks/data/posts.ts b/src/mocks/data/posts.ts index 801ecd1..43727d6 100644 --- a/src/mocks/data/posts.ts +++ b/src/mocks/data/posts.ts @@ -1,32 +1,45 @@ import dayjs from "dayjs"; import { Bounty, Post, Question, Story } from "src/features/Posts/types"; -import { randomItem } from "src/utils/helperFunctions"; +import { random, randomItem } from "src/utils/helperFunctions"; import { getAvatarImage, getCoverImage } from "./utils"; -import chance, { Chance } from 'chance' - -const date = dayjs().subtract(5, 'hour').toString(); - +import { Chance } from 'chance' +const getDate = () => dayjs().subtract(random(5, 48), 'hour').toString(); const getAuthor = () => ({ id: 12, name: "John Doe", image: getAvatarImage(), - join_date: date + join_date: getDate() }) -const getPostComments = (cnt: number = 1): Story['comments'] => Array(cnt).fill(0).map((_, idx) => ({ - id: idx + 1, - body: "This is a comment " + idx + 1, - date, - author: getAuthor() -})) +const getPostComments = (cnt: number = 1): Story['comments'] => { + + let comments = []; + const rootCommentsIds: any[] = [] + for (let i = 0; i < cnt; i++) { + const parentId = Math.random() < .4 ? 0 : rootCommentsIds.length ? randomItem(...rootCommentsIds) : 0; + const comment = { + id: i + 1, + body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nisi, at ut sit id. Vulputate aliquet aliquam penatibus ac, et dictum est etiam. Sagittis odio dui sed viverra donec rutrum iaculis vitae morbi.", + created_at: getDate(), + author: getAuthor(), + votes_count: 123, + parentId + } + comments.push(comment); + if (!parentId) + rootCommentsIds.push(comment.id); + + } + return comments; +} const getApplications = (cnt: number = 1): Bounty['applications'] => Array(cnt).fill(0).map((_, idx) => ({ id: idx + 1, workplan: "I Plan to build this using React, Ts, Redux, and Storybook.", - date, + date: getDate(), author: getAuthor(), })) @@ -72,7 +85,7 @@ export let posts = { body: postBody, cover_image: getCoverImage(), comments_count: 31, - date, + date: getDate(), votes_count: 120, excerpt: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. In odio libero accumsan...', type: "Story", @@ -94,7 +107,7 @@ export let posts = { body: postBody, cover_image: getCoverImage(), applicants_count: 31, - date, + date: getDate(), votes_count: 120, excerpt: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. In odio libero accumsan...', tags: [ @@ -116,7 +129,7 @@ export let posts = { title: 'Digital Editor, Mars Review of Books', body: postBody, answers_count: 31, - date, + date: getDate(), votes_count: 70, excerpt: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. In odio libero accumsan...', tags: [ @@ -124,20 +137,7 @@ export let posts = { { id: 2, title: "webln" }, ], author: getAuthor(), - comments: [ - { - id: 1, - author: getAuthor(), - date, - body: 'Naw, I’m 42 and know people who started in their 50’s, you got this!' - }, - { - id: 2, - author: getAuthor(), - date, - body: 'Naw, I’m 42 and know people who started in their 50’s, you got this!' - }, - ] + comments: getPostComments(3) }, ] as Question[] } @@ -154,8 +154,11 @@ export const feed: Post[] = Array(30).fill(0).map((_, idx) => { const post = feedRandomer.pickone([posts.bounties[0], posts.questions[0], posts.stories[0]]) return { - ...post, id: idx + 1, title: feedRandomer.sentence({ + ...post, + id: idx + 1, + title: feedRandomer.sentence({ words: feedRandomer.integer({ min: 4, max: 7 }) - }) + }), + date: getDate(), } }) diff --git a/src/utils/helperFunctions.tsx b/src/utils/helperFunctions.tsx index b70fcd9..c81ac69 100644 --- a/src/utils/helperFunctions.tsx +++ b/src/utils/helperFunctions.tsx @@ -52,11 +52,9 @@ export function lazyModal> - - - // type ThenArg = T extends PromiseLike ? U : T - // type y = ThenArg> - // type yy = ComponentProps - return { LazyComponent, preload }; +} + +export function trimText(text: string, length: number) { + return text.slice(0, length) + (text.length > length ? "..." : "") } \ No newline at end of file