From bd44334f69347837e8dc48ca1b55946ed6e1ea83 Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Mon, 3 Oct 2022 23:10:06 +0300 Subject: [PATCH] feat: update story card & page to accept tagged project --- api/functions/graphql/nexus-typegen.ts | 2 + api/functions/graphql/schema.graphql | 1 + api/functions/graphql/types/post.js | 7 +++ .../PostCard/Header/Header.Skeleton.tsx | 2 +- .../PostCard/PostCard/PostCard.Skeleton.tsx | 22 +++++--- .../PostCardHeader/PostCardHeader.tsx | 52 ++++++++++++++++++ .../PostCard/StoryCard/StoryCard.tsx | 52 ++++++++++-------- .../Posts/pages/FeedPage/feed.graphql | 6 +++ .../PageContent/PageContent.skeleton.tsx | 5 +- .../Components/PageContent/PageContent.tsx | 2 +- .../PostPageHeader/PostPageHeader.tsx | 54 +++++++++++++++++++ .../StoryPageContent/StoryPageContent.tsx | 9 +++- .../StoryPageContent/useUpdateStory.tsx | 2 - .../PostDetailsPage.skeleton.tsx | 10 ---- .../pages/PostDetailsPage/postDetails.graphql | 6 +++ src/graphql/index.tsx | 17 +++++- 16 files changed, 201 insertions(+), 48 deletions(-) create mode 100644 src/features/Posts/Components/PostCard/PostCardHeader/PostCardHeader.tsx create mode 100644 src/features/Posts/pages/PostDetailsPage/Components/PostPageHeader/PostPageHeader.tsx diff --git a/api/functions/graphql/nexus-typegen.ts b/api/functions/graphql/nexus-typegen.ts index d260935..40fd548 100644 --- a/api/functions/graphql/nexus-typegen.ts +++ b/api/functions/graphql/nexus-typegen.ts @@ -660,6 +660,7 @@ export interface NexusGenFieldTypes { excerpt: string; // String! id: number; // Int! is_published: boolean | null; // Boolean + project: NexusGenRootTypes['Project'] | null; // Project tags: NexusGenRootTypes['Tag'][]; // [Tag!]! title: string; // String! type: string; // String! @@ -1051,6 +1052,7 @@ export interface NexusGenFieldTypeNames { excerpt: 'String' id: 'Int' is_published: 'Boolean' + project: 'Project' tags: 'Tag' title: 'String' type: 'String' diff --git a/api/functions/graphql/schema.graphql b/api/functions/graphql/schema.graphql index a570aee..486e3bc 100644 --- a/api/functions/graphql/schema.graphql +++ b/api/functions/graphql/schema.graphql @@ -399,6 +399,7 @@ type Story implements PostBase { excerpt: String! id: Int! is_published: Boolean + project: Project tags: [Tag!]! title: String! type: String! diff --git a/api/functions/graphql/types/post.js b/api/functions/graphql/types/post.js index c207d65..f899854 100644 --- a/api/functions/graphql/types/post.js +++ b/api/functions/graphql/types/post.js @@ -114,6 +114,13 @@ const Story = objectType({ }); + t.field('project', { + type: "Project", + resolve(parent) { + return null + } + }) + }, }) diff --git a/src/features/Posts/Components/PostCard/Header/Header.Skeleton.tsx b/src/features/Posts/Components/PostCard/Header/Header.Skeleton.tsx index 41b4a0e..31b7404 100644 --- a/src/features/Posts/Components/PostCard/Header/Header.Skeleton.tsx +++ b/src/features/Posts/Components/PostCard/Header/Header.Skeleton.tsx @@ -8,7 +8,7 @@ interface Props { export default function HeaderSkeleton({ size = 'md', }: Props) { return ( -
+

diff --git a/src/features/Posts/Components/PostCard/PostCard/PostCard.Skeleton.tsx b/src/features/Posts/Components/PostCard/PostCard/PostCard.Skeleton.tsx index 9fab751..d91584e 100644 --- a/src/features/Posts/Components/PostCard/PostCard/PostCard.Skeleton.tsx +++ b/src/features/Posts/Components/PostCard/PostCard/PostCard.Skeleton.tsx @@ -1,14 +1,20 @@ import Skeleton from "react-loading-skeleton" -import HeaderSkeleton from "../Header/Header.Skeleton" import Badge from 'src/Components/Badge/Badge' +import Card from "src/Components/Card/Card" export default function PostCardSkeleton() { - return

-
- + return
+
+ + +

+
-
- + +
+ +
+

@@ -24,6 +30,8 @@ export default function PostCardSkeleton() {
-
+ + +
} diff --git a/src/features/Posts/Components/PostCard/PostCardHeader/PostCardHeader.tsx b/src/features/Posts/Components/PostCard/PostCardHeader/PostCardHeader.tsx new file mode 100644 index 0000000..9074902 --- /dev/null +++ b/src/features/Posts/Components/PostCard/PostCardHeader/PostCardHeader.tsx @@ -0,0 +1,52 @@ +import Avatar from 'src/features/Profiles/Components/Avatar/Avatar'; +import dayjs from 'dayjs' +import { UnionToObjectKeys } from 'src/utils/types/utils'; +import { trimText } from 'src/utils/helperFunctions'; +import { Link } from 'react-router-dom'; +import { createRoute } from 'src/utils/routing'; +import { Project, User } from 'src/graphql'; + +interface Props { + author?: Pick + project?: Pick | null + date: string; +} + + +export default function PostCardHeader(props: Props) { + + + const dateToShow = () => { + const passedTime = dayjs().diff(props.date, 'hour'); + if (passedTime === 0) return 'now'; + if (passedTime < 24) return `${dayjs().diff(props.date, 'hour')}h ago` + return dayjs(props.date).format('MMMM DD'); + } + + if (!props.author) return null + + return ( +
+ + + + + {props.project && + + } + + + +

{trimText(props.author.name, 30)}

+ + {props.project && <> + for + +

{trimText(props.project.title, 30)}

+ + } +
+

{dateToShow()}

+
+ ) +} diff --git a/src/features/Posts/Components/PostCard/StoryCard/StoryCard.tsx b/src/features/Posts/Components/PostCard/StoryCard/StoryCard.tsx index d31164c..e85e7d2 100644 --- a/src/features/Posts/Components/PostCard/StoryCard/StoryCard.tsx +++ b/src/features/Posts/Components/PostCard/StoryCard/StoryCard.tsx @@ -1,5 +1,4 @@ import { Story } from "src/features/Posts/types" -import Header from "../Header/Header" import { Link } from "react-router-dom" import VoteButton from "src/Components/VoteButton/VoteButton" import { useVote } from "src/utils/hooks" @@ -8,6 +7,7 @@ import Badge from "src/Components/Badge/Badge" import { createRoute } from "src/utils/routing" import { BiComment } from "react-icons/bi" import Card from "src/Components/Card/Card" +import PostCardHeader from "../PostCardHeader/PostCardHeader" export type StoryCardType = Pick & { tags: Array>, author: Pick @@ -34,30 +35,37 @@ export default function StoryCard({ story }: Props) { itemType: Vote_Item_Type.Story }); - return ( - - {story.cover_image && } -
-
- -

{story.title}

- -

{story.excerpt}...

-
- {story.tags.map(tag => - {tag.title} - )} -
-
-
- -
- {story.comments_count} Comments + return ( +
+ + + {story.cover_image && + + + + } +
+ +

{story.title}

+ +

{story.excerpt}...

+
+ {story.tags.map(tag => + {tag.title} + )} +
+ +
+
+ +
+ {story.comments_count} Comments +
-
- + +
) } diff --git a/src/features/Posts/pages/FeedPage/feed.graphql b/src/features/Posts/pages/FeedPage/feed.graphql index bc76c2d..d25f74c 100644 --- a/src/features/Posts/pages/FeedPage/feed.graphql +++ b/src/features/Posts/pages/FeedPage/feed.graphql @@ -19,6 +19,12 @@ query Feed($take: Int, $skip: Int, $sortBy: String, $tag: Int) { type cover_image comments_count + project { + id + title + thumbnail_image + hashtag + } } ... on Bounty { id diff --git a/src/features/Posts/pages/PostDetailsPage/Components/PageContent/PageContent.skeleton.tsx b/src/features/Posts/pages/PostDetailsPage/Components/PageContent/PageContent.skeleton.tsx index e5ac08c..98ed6b5 100644 --- a/src/features/Posts/pages/PostDetailsPage/Components/PageContent/PageContent.skeleton.tsx +++ b/src/features/Posts/pages/PostDetailsPage/Components/PageContent/PageContent.skeleton.tsx @@ -8,10 +8,13 @@ import HeaderSkeleton from "src/features/Posts/Components/PostCard/Header/Header export default function PageContentSkeleton() { return
+ +
+ +

-
{Array(3).fill(0).map((_, idx) =>
hidden
diff --git a/src/features/Posts/pages/PostDetailsPage/Components/PageContent/PageContent.tsx b/src/features/Posts/pages/PostDetailsPage/Components/PageContent/PageContent.tsx index 0945666..2c2628b 100644 --- a/src/features/Posts/pages/PostDetailsPage/Components/PageContent/PageContent.tsx +++ b/src/features/Posts/pages/PostDetailsPage/Components/PageContent/PageContent.tsx @@ -1,4 +1,4 @@ -import { isBounty, isQuestion, isStory, Post } from "src/features/Posts/types" +import { isBounty, isQuestion, isStory } from "src/features/Posts/types" import StoryPageContent from "../StoryPageContent/StoryPageContent"; import BountyPageContent from "../BountyPageContent/BountyPageContent"; import QuestionPageContent from "../QuestionPageContent/QuestionPageContent"; diff --git a/src/features/Posts/pages/PostDetailsPage/Components/PostPageHeader/PostPageHeader.tsx b/src/features/Posts/pages/PostDetailsPage/Components/PostPageHeader/PostPageHeader.tsx new file mode 100644 index 0000000..5fd5890 --- /dev/null +++ b/src/features/Posts/pages/PostDetailsPage/Components/PostPageHeader/PostPageHeader.tsx @@ -0,0 +1,54 @@ +import Avatar from 'src/features/Profiles/Components/Avatar/Avatar'; +import dayjs from 'dayjs' +import { trimText } from 'src/utils/helperFunctions'; +import { Link } from 'react-router-dom'; +import { createRoute } from 'src/utils/routing'; +import { Project, User } from 'src/graphql'; + +interface Props { + author?: Pick + project?: Pick | null + date: string; + className?: string +} + + +export default function PostPageHeader(props: Props) { + + + const dateToShow = () => { + const passedTime = dayjs().diff(props.date, 'hour'); + if (passedTime === 0) return 'now'; + if (passedTime < 24) return `${dayjs().diff(props.date, 'hour')}h ago` + return dayjs(props.date).format('MMMM DD'); + } + + if (!props.author) return null + + return ( +
+
+ + + + {props.project && + + } +
+
+ + +

{trimText(props.author.name, 30)}

+ + {props.project && <> + for + +

{trimText(props.project.title, 30)}

+ + } +
+

Published {dateToShow()}

+
+
+ ) +} diff --git a/src/features/Posts/pages/PostDetailsPage/Components/StoryPageContent/StoryPageContent.tsx b/src/features/Posts/pages/PostDetailsPage/Components/StoryPageContent/StoryPageContent.tsx index 9587bd7..05314bb 100644 --- a/src/features/Posts/pages/PostDetailsPage/Components/StoryPageContent/StoryPageContent.tsx +++ b/src/features/Posts/pages/PostDetailsPage/Components/StoryPageContent/StoryPageContent.tsx @@ -1,4 +1,4 @@ -import Header from "src/features/Posts/Components/PostCard/Header/Header" + import { Story } from "src/features/Posts/types" import { marked } from 'marked'; import styles from '../PageContent/styles.module.scss' @@ -10,6 +10,7 @@ import { useUpdateStory } from './useUpdateStory' import { FaPen } from "react-icons/fa"; import DOMPurify from 'dompurify'; import Card from "src/Components/Card/Card"; +import PostPageHeader from "../PostPageHeader/PostPageHeader"; interface Props { @@ -29,6 +30,11 @@ export default function StoryPageContent({ story }: Props) { <>
+ {story.cover_image && }

{story.title}

-
{story.tags.length > 0 &&
{story.tags.map(tag => {tag.title} diff --git a/src/features/Posts/pages/PostDetailsPage/Components/StoryPageContent/useUpdateStory.tsx b/src/features/Posts/pages/PostDetailsPage/Components/StoryPageContent/useUpdateStory.tsx index 88629c7..ac64108 100644 --- a/src/features/Posts/pages/PostDetailsPage/Components/StoryPageContent/useUpdateStory.tsx +++ b/src/features/Posts/pages/PostDetailsPage/Components/StoryPageContent/useUpdateStory.tsx @@ -30,8 +30,6 @@ export const useUpdateStory = (story: Story) => { dispatch(stageStory({ ...story, cover_image: story.cover_image ? { id: null, name: null, url: story.cover_image } : null, - project: null, - // TODO: UPDATE WHEN API READY })) navigate(createRoute({ type: "write-story" })) diff --git a/src/features/Posts/pages/PostDetailsPage/PostDetailsPage.skeleton.tsx b/src/features/Posts/pages/PostDetailsPage/PostDetailsPage.skeleton.tsx index 091c0ef..001f01d 100644 --- a/src/features/Posts/pages/PostDetailsPage/PostDetailsPage.skeleton.tsx +++ b/src/features/Posts/pages/PostDetailsPage/PostDetailsPage.skeleton.tsx @@ -1,18 +1,8 @@ -import { Helmet } from 'react-helmet' -import { useParams } from 'react-router-dom' -import LoadingPage from 'src/Components/LoadingPage/LoadingPage' -import NotFoundPage from 'src/features/Shared/pages/NotFoundPage/NotFoundPage' -import { usePostDetailsQuery } from 'src/graphql' -import { capitalize } from 'src/utils/helperFunctions' import { useAppSelector, } from 'src/utils/hooks' -import { PostCardSkeleton } from '../../Components/PostCard' import TrendingCard from '../../Components/TrendingCard/TrendingCard' -import AuthorCard from './Components/AuthorCard/AuthorCard' import AuthorCardSkeleton from './Components/AuthorCard/AuthorCard.skeleton' -import PageContent from './Components/PageContent/PageContent' import PageContentSkeleton from './Components/PageContent/PageContent.skeleton' -import PostActions from './Components/PostActions/PostActions' import PostActionsSkeleton from './Components/PostActions/PostActions.skeleton' import styles from './styles.module.scss' diff --git a/src/features/Posts/pages/PostDetailsPage/postDetails.graphql b/src/features/Posts/pages/PostDetailsPage/postDetails.graphql index e2a1d51..91d8cc5 100644 --- a/src/features/Posts/pages/PostDetailsPage/postDetails.graphql +++ b/src/features/Posts/pages/PostDetailsPage/postDetails.graphql @@ -19,6 +19,12 @@ query PostDetails($id: Int!, $type: POST_TYPE!) { type cover_image is_published + project { + id + title + thumbnail_image + hashtag + } # comments_count # comments { # id diff --git a/src/graphql/index.tsx b/src/graphql/index.tsx index 54e66ad..e2770dd 100644 --- a/src/graphql/index.tsx +++ b/src/graphql/index.tsx @@ -642,6 +642,7 @@ export type Story = PostBase & { excerpt: Scalars['String']; id: Scalars['Int']; is_published: Maybe; + project: Maybe; tags: Array; title: Scalars['String']; type: Scalars['String']; @@ -959,7 +960,7 @@ export type FeedQueryVariables = Exact<{ }>; -export type FeedQuery = { __typename?: 'Query', getFeed: Array<{ __typename?: 'Bounty', id: number, title: string, createdAt: any, excerpt: string, votes_count: number, type: string, cover_image: string | null, deadline: string, reward_amount: number, applicants_count: number, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }> } | { __typename?: 'Question', id: number, title: string, createdAt: any, excerpt: string, votes_count: number, type: string, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }> } | { __typename?: 'Story', id: number, title: string, createdAt: any, excerpt: string, votes_count: number, type: string, cover_image: string | null, comments_count: number, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }> }> }; +export type FeedQuery = { __typename?: 'Query', getFeed: Array<{ __typename?: 'Bounty', id: number, title: string, createdAt: any, excerpt: string, votes_count: number, type: string, cover_image: string | null, deadline: string, reward_amount: number, applicants_count: number, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }> } | { __typename?: 'Question', id: number, title: string, createdAt: any, excerpt: string, votes_count: number, type: string, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }> } | { __typename?: 'Story', id: number, title: string, createdAt: any, excerpt: string, votes_count: number, type: string, cover_image: string | null, comments_count: number, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, project: { __typename?: 'Project', id: number, title: string, thumbnail_image: string, hashtag: string } | null }> }; export type PostDetailsQueryVariables = Exact<{ id: Scalars['Int']; @@ -967,7 +968,7 @@ export type PostDetailsQueryVariables = Exact<{ }>; -export type PostDetailsQuery = { __typename?: 'Query', getPostById: { __typename?: 'Bounty', id: number, title: string, createdAt: any, body: string, votes_count: number, type: string, cover_image: string | null, deadline: string, reward_amount: number, applicants_count: number, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, applications: Array<{ __typename?: 'BountyApplication', id: number, date: string, workplan: string, author: { __typename?: 'Author', id: number, name: string, avatar: string } }> } | { __typename?: 'Question', id: number, title: string, createdAt: any, body: string, votes_count: number, type: string, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }> } | { __typename?: 'Story', id: number, title: string, createdAt: any, body: string, votes_count: number, type: string, cover_image: string | null, is_published: boolean | null, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }> } }; +export type PostDetailsQuery = { __typename?: 'Query', getPostById: { __typename?: 'Bounty', id: number, title: string, createdAt: any, body: string, votes_count: number, type: string, cover_image: string | null, deadline: string, reward_amount: number, applicants_count: number, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, applications: Array<{ __typename?: 'BountyApplication', id: number, date: string, workplan: string, author: { __typename?: 'Author', id: number, name: string, avatar: string } }> } | { __typename?: 'Question', id: number, title: string, createdAt: any, body: string, votes_count: number, type: string, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }> } | { __typename?: 'Story', id: number, title: string, createdAt: any, body: string, votes_count: number, type: string, cover_image: string | null, is_published: boolean | null, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, project: { __typename?: 'Project', id: number, title: string, thumbnail_image: string, hashtag: string } | null } }; type UserBasicInfo_MyProfile_Fragment = { __typename?: 'MyProfile', id: number, name: string, avatar: string, join_date: any, role: string | null, jobTitle: string | null, lightning_address: string | null, website: string | null, twitter: string | null, discord: string | null, github: string | null, linkedin: string | null, bio: string | null, location: string | null }; @@ -1885,6 +1886,12 @@ export const FeedDocument = gql` type cover_image comments_count + project { + id + title + thumbnail_image + hashtag + } } ... on Bounty { id @@ -1982,6 +1989,12 @@ export const PostDetailsDocument = gql` type cover_image is_published + project { + id + title + thumbnail_image + hashtag + } } ... on Bounty { id