diff --git a/codegen.yml b/codegen.yml index 54e6960..28cd2dd 100644 --- a/codegen.yml +++ b/codegen.yml @@ -1,5 +1,5 @@ overwrite: true -schema: "https://makers-bolt-fun-preview.netlify.app/.netlify/functions/graphql" +schema: "http://localhost:8888/dev/graphql" documents: "./src/**/*.{ts,graphql}" generates: src/graphql/index.tsx: diff --git a/functions/graphql/nexus-typegen.ts b/functions/graphql/nexus-typegen.ts index f02f63a..64c5bb2 100644 --- a/functions/graphql/nexus-typegen.ts +++ b/functions/graphql/nexus-typegen.ts @@ -34,6 +34,19 @@ export interface NexusGenObjects { title: string; // String! url: string; // String! } + Bounty: { // root type + applicants_count: number; // Int! + author: NexusGenRootTypes['User']; // User! + cover_image: string; // String! + date: string; // String! + deadline: string; // String! + excerpt: string; // String! + id: number; // Int! + reward_amount: number; // Int! + tags: NexusGenRootTypes['Tag'][]; // [Tag!]! + title: string; // String! + votes_count: number; // Int! + } Category: { // root type cover_image?: string | null; // String icon?: string | null; // String @@ -47,6 +60,12 @@ export interface NexusGenObjects { minSendable?: number | null; // Int } Mutation: {}; + PostComment: { // root type + author: NexusGenRootTypes['User']; // User! + body: string; // String! + date: string; // String! + id: number; // Int! + } Project: { // root type cover_image: string; // String! description: string; // String! @@ -60,10 +79,40 @@ export interface NexusGenObjects { website: string; // String! } Query: {}; + Question: { // root type + answers_count: number; // Int! + author: NexusGenRootTypes['User']; // User! + comments: NexusGenRootTypes['PostComment'][]; // [PostComment!]! + cover_image: string; // String! + date: string; // String! + deadline: string; // String! + excerpt: string; // String! + id: number; // Int! + reward_amount: number; // Int! + tags: NexusGenRootTypes['Tag'][]; // [Tag!]! + title: string; // String! + votes_count: number; // Int! + } + Story: { // root type + author: NexusGenRootTypes['User']; // User! + comments_count: number; // Int! + cover_image: string; // String! + date: string; // String! + excerpt: string; // String! + id: number; // Int! + tags: NexusGenRootTypes['Tag'][]; // [Tag!]! + title: string; // String! + votes_count: number; // Int! + } Tag: { // root type id: number; // Int! title: string; // String! } + User: { // root type + id: number; // Int! + image: string; // String! + name: string; // String! + } Vote: { // root type amount_in_sat: number; // Int! id: number; // Int! @@ -74,12 +123,14 @@ export interface NexusGenObjects { } export interface NexusGenInterfaces { + PostBase: NexusGenRootTypes['Bounty'] | NexusGenRootTypes['Question'] | NexusGenRootTypes['Story']; } export interface NexusGenUnions { + Post: NexusGenRootTypes['Bounty'] | NexusGenRootTypes['Question'] | NexusGenRootTypes['Story']; } -export type NexusGenRootTypes = NexusGenObjects +export type NexusGenRootTypes = NexusGenInterfaces & NexusGenObjects & NexusGenUnions export type NexusGenAllTypes = NexusGenRootTypes & NexusGenScalars @@ -91,6 +142,20 @@ export interface NexusGenFieldTypes { title: string; // String! url: string; // String! } + Bounty: { // field return type + applicants_count: number; // Int! + author: NexusGenRootTypes['User']; // User! + cover_image: string; // String! + date: string; // String! + deadline: string; // String! + excerpt: string; // String! + id: number; // Int! + reward_amount: number; // Int! + tags: NexusGenRootTypes['Tag'][]; // [Tag!]! + title: string; // String! + type: string; // String! + votes_count: number; // Int! + } Category: { // field return type apps_count: number; // Int! cover_image: string | null; // String @@ -110,6 +175,12 @@ export interface NexusGenFieldTypes { confirmVote: NexusGenRootTypes['Vote']; // Vote! vote: NexusGenRootTypes['Vote']; // Vote! } + PostComment: { // field return type + author: NexusGenRootTypes['User']; // User! + body: string; // String! + date: string; // String! + id: number; // Int! + } Project: { // field return type awards: NexusGenRootTypes['Award'][]; // [Award!]! category: NexusGenRootTypes['Category']; // Category! @@ -129,18 +200,52 @@ export interface NexusGenFieldTypes { allCategories: NexusGenRootTypes['Category'][]; // [Category!]! allProjects: NexusGenRootTypes['Project'][]; // [Project!]! getCategory: NexusGenRootTypes['Category']; // Category! + getFeed: NexusGenRootTypes['Post'][]; // [Post!]! getLnurlDetailsForProject: NexusGenRootTypes['LnurlDetails']; // LnurlDetails! + getPostById: NexusGenRootTypes['Post']; // Post! getProject: NexusGenRootTypes['Project']; // Project! hottestProjects: NexusGenRootTypes['Project'][]; // [Project!]! newProjects: NexusGenRootTypes['Project'][]; // [Project!]! projectsByCategory: NexusGenRootTypes['Project'][]; // [Project!]! searchProjects: NexusGenRootTypes['Project'][]; // [Project!]! } + Question: { // field return type + answers_count: number; // Int! + author: NexusGenRootTypes['User']; // User! + comments: NexusGenRootTypes['PostComment'][]; // [PostComment!]! + cover_image: string; // String! + date: string; // String! + deadline: string; // String! + excerpt: string; // String! + id: number; // Int! + reward_amount: number; // Int! + tags: NexusGenRootTypes['Tag'][]; // [Tag!]! + title: string; // String! + type: string; // String! + votes_count: number; // Int! + } + Story: { // field return type + author: NexusGenRootTypes['User']; // User! + comments_count: number; // Int! + cover_image: string; // String! + date: string; // String! + excerpt: string; // String! + id: number; // Int! + tags: NexusGenRootTypes['Tag'][]; // [Tag!]! + title: string; // String! + type: string; // String! + votes_count: number; // Int! + } Tag: { // field return type id: number; // Int! project: NexusGenRootTypes['Project'][]; // [Project!]! title: string; // String! } + User: { // field return type + id: number; // Int! + image: string; // String! + name: string; // String! + } Vote: { // field return type amount_in_sat: number; // Int! id: number; // Int! @@ -149,6 +254,15 @@ export interface NexusGenFieldTypes { payment_request: string; // String! project: NexusGenRootTypes['Project']; // Project! } + PostBase: { // field return type + author: NexusGenRootTypes['User']; // User! + date: string; // String! + excerpt: string; // String! + id: number; // Int! + tags: NexusGenRootTypes['Tag'][]; // [Tag!]! + title: string; // String! + votes_count: number; // Int! + } } export interface NexusGenFieldTypeNames { @@ -159,6 +273,20 @@ export interface NexusGenFieldTypeNames { title: 'String' url: 'String' } + Bounty: { // field return type name + applicants_count: 'Int' + author: 'User' + cover_image: 'String' + date: 'String' + deadline: 'String' + excerpt: 'String' + id: 'Int' + reward_amount: 'Int' + tags: 'Tag' + title: 'String' + type: 'String' + votes_count: 'Int' + } Category: { // field return type name apps_count: 'Int' cover_image: 'String' @@ -178,6 +306,12 @@ export interface NexusGenFieldTypeNames { confirmVote: 'Vote' vote: 'Vote' } + PostComment: { // field return type name + author: 'User' + body: 'String' + date: 'String' + id: 'Int' + } Project: { // field return type name awards: 'Award' category: 'Category' @@ -197,18 +331,52 @@ export interface NexusGenFieldTypeNames { allCategories: 'Category' allProjects: 'Project' getCategory: 'Category' + getFeed: 'Post' getLnurlDetailsForProject: 'LnurlDetails' + getPostById: 'Post' getProject: 'Project' hottestProjects: 'Project' newProjects: 'Project' projectsByCategory: 'Project' searchProjects: 'Project' } + Question: { // field return type name + answers_count: 'Int' + author: 'User' + comments: 'PostComment' + cover_image: 'String' + date: 'String' + deadline: 'String' + excerpt: 'String' + id: 'Int' + reward_amount: 'Int' + tags: 'Tag' + title: 'String' + type: 'String' + votes_count: 'Int' + } + Story: { // field return type name + author: 'User' + comments_count: 'Int' + cover_image: 'String' + date: 'String' + excerpt: 'String' + id: 'Int' + tags: 'Tag' + title: 'String' + type: 'String' + votes_count: 'Int' + } Tag: { // field return type name id: 'Int' project: 'Project' title: 'String' } + User: { // field return type name + id: 'Int' + image: 'String' + name: 'String' + } Vote: { // field return type name amount_in_sat: 'Int' id: 'Int' @@ -217,6 +385,15 @@ export interface NexusGenFieldTypeNames { payment_request: 'String' project: 'Project' } + PostBase: { // field return type name + author: 'User' + date: 'String' + excerpt: 'String' + id: 'Int' + tags: 'Tag' + title: 'String' + votes_count: 'Int' + } } export interface NexusGenArgTypes { @@ -238,9 +415,16 @@ export interface NexusGenArgTypes { getCategory: { // args id: number; // Int! } + getFeed: { // args + skip?: number | null; // Int + take: number | null; // Int + } getLnurlDetailsForProject: { // args project_id: number; // Int! } + getPostById: { // args + id: number; // Int! + } getProject: { // args id: number; // Int! } @@ -266,9 +450,14 @@ export interface NexusGenArgTypes { } export interface NexusGenAbstractTypeMembers { + Post: "Bounty" | "Question" | "Story" + PostBase: "Bounty" | "Question" | "Story" } export interface NexusGenTypeInterfaces { + Bounty: "PostBase" + Question: "PostBase" + Story: "PostBase" } export type NexusGenObjectNames = keyof NexusGenObjects; @@ -277,15 +466,15 @@ export type NexusGenInputNames = never; export type NexusGenEnumNames = never; -export type NexusGenInterfaceNames = never; +export type NexusGenInterfaceNames = keyof NexusGenInterfaces; export type NexusGenScalarNames = keyof NexusGenScalars; -export type NexusGenUnionNames = never; +export type NexusGenUnionNames = keyof NexusGenUnions; export type NexusGenObjectsUsingAbstractStrategyIsTypeOf = never; -export type NexusGenAbstractsUsingStrategyResolveType = never; +export type NexusGenAbstractsUsingStrategyResolveType = "Post" | "PostBase"; export type NexusGenFeaturesConfig = { abstractTypeStrategies: { diff --git a/functions/graphql/schema.graphql b/functions/graphql/schema.graphql index 2b39787..71c6dde 100644 --- a/functions/graphql/schema.graphql +++ b/functions/graphql/schema.graphql @@ -10,6 +10,21 @@ type Award { url: String! } +type Bounty implements PostBase { + applicants_count: Int! + author: User! + cover_image: String! + date: String! + deadline: String! + excerpt: String! + id: Int! + reward_amount: Int! + tags: [Tag!]! + title: String! + type: String! + votes_count: Int! +} + type Category { apps_count: Int! cover_image: String @@ -32,6 +47,25 @@ type Mutation { vote(amount_in_sat: Int!, project_id: Int!): Vote! } +union Post = Bounty | Question | Story + +interface PostBase { + author: User! + date: String! + excerpt: String! + id: Int! + tags: [Tag!]! + title: String! + votes_count: Int! +} + +type PostComment { + author: User! + body: String! + date: String! + id: Int! +} + type Project { awards: [Award!]! category: Category! @@ -52,7 +86,9 @@ type Query { allCategories: [Category!]! allProjects(skip: Int = 0, take: Int = 50): [Project!]! getCategory(id: Int!): Category! + getFeed(skip: Int = 0, take: Int = 15): [Post!]! getLnurlDetailsForProject(project_id: Int!): LnurlDetails! + getPostById(id: Int!): Post! getProject(id: Int!): Project! hottestProjects(skip: Int = 0, take: Int = 50): [Project!]! newProjects(skip: Int = 0, take: Int = 50): [Project!]! @@ -60,12 +96,47 @@ type Query { searchProjects(search: String!, skip: Int = 0, take: Int = 50): [Project!]! } +type Question implements PostBase { + answers_count: Int! + author: User! + comments: [PostComment!]! + cover_image: String! + date: String! + deadline: String! + excerpt: String! + id: Int! + reward_amount: Int! + tags: [Tag!]! + title: String! + type: String! + votes_count: Int! +} + +type Story implements PostBase { + author: User! + comments_count: Int! + cover_image: String! + date: String! + excerpt: String! + id: Int! + tags: [Tag!]! + title: String! + type: String! + votes_count: Int! +} + type Tag { id: Int! project: [Project!]! title: String! } +type User { + id: Int! + image: String! + name: String! +} + type Vote { amount_in_sat: Int! id: Int! diff --git a/functions/graphql/types/index.js b/functions/graphql/types/index.js index 021a80a..6e9b03a 100644 --- a/functions/graphql/types/index.js +++ b/functions/graphql/types/index.js @@ -1,9 +1,13 @@ const category = require('./category') const project = require('./project') const vote = require('./vote') +const post = require('./post') +const users = require('./users') module.exports = { ...category, ...project, ...vote, + ...post, + ...users } \ No newline at end of file diff --git a/functions/graphql/types/post.js b/functions/graphql/types/post.js new file mode 100644 index 0000000..b2e513c --- /dev/null +++ b/functions/graphql/types/post.js @@ -0,0 +1,144 @@ +const { + intArg, + objectType, + stringArg, + extendType, + nonNull, + interfaceType, + unionType, +} = require('nexus'); +const { paginationArgs } = require('./helpers'); + + + + +const PostBase = interfaceType({ + name: 'PostBase', + resolveType(post) { + return post.type + }, + definition(t) { + t.nonNull.int('id'); + t.nonNull.string('title'); + t.nonNull.string('date'); + t.nonNull.field('author', { + type: "User" + }); + t.nonNull.string('excerpt'); + t.nonNull.list.nonNull.field('tags', { + type: "Tag" + }); + t.nonNull.int('votes_count'); + }, +}) + +const Story = objectType({ + name: 'Story', + definition(t) { + t.implements('PostBase'); + t.nonNull.string('type', { + resolve: () => 'story' + }); + t.nonNull.string('cover_image'); + t.nonNull.int('comments_count'); + }, +}) + +const Bounty = objectType({ + name: 'Bounty', + definition(t) { + t.implements('PostBase'); + t.nonNull.string('type', { + resolve: () => 'bounty' + }); + t.nonNull.string('cover_image'); + t.nonNull.string('deadline'); + t.nonNull.int('reward_amount'); + t.nonNull.int('applicants_count'); + }, +}) + +const Question = objectType({ + name: 'Question', + definition(t) { + t.implements('PostBase'); + t.nonNull.string('type', { + resolve: () => 'question' + }); + t.nonNull.string('cover_image'); + t.nonNull.string('deadline'); + t.nonNull.int('reward_amount'); + t.nonNull.int('answers_count'); + t.nonNull.list.nonNull.field('comments', { + type: "PostComment" + }) + }, +}) + +const PostComment = objectType({ + name: 'PostComment', + definition(t) { + t.nonNull.int('id'); + t.nonNull.string('date'); + t.nonNull.string('body'); + t.nonNull.field('author', { + type: "User" + }); + } +}) + +const Post = unionType({ + name: 'Post', + definition(t) { + t.members('Story', 'Bounty', 'Question') + }, + resolveType: (item) => item.type, +}) + + + +const getFeed = extendType({ + type: "Query", + definition(t) { + t.nonNull.list.nonNull.field('getFeed', { + type: "Post", + args: { + ...paginationArgs({ take: 15 }) + }, + resolve(_, { take, skip }) { + return [] + } + }) + } +}) + +const getPostById = extendType({ + type: "Query", + definition(t) { + t.nonNull.field('getPostById', { + type: "Post", + args: { + id: nonNull(intArg()) + }, + resolve(_, { id }) { + return {} + } + }) + } +}) + + + + +module.exports = { + // Types + PostBase, + Bounty, + Story, + Question, + PostComment, + Post, + // Queries + getFeed, + getPostById +} \ No newline at end of file diff --git a/functions/graphql/types/users.js b/functions/graphql/types/users.js new file mode 100644 index 0000000..d6bd504 --- /dev/null +++ b/functions/graphql/types/users.js @@ -0,0 +1,17 @@ +const { objectType } = require("nexus"); + +const User = objectType({ + name: 'User', + definition(t) { + t.nonNull.int('id'); + t.nonNull.string('name'); + t.nonNull.string('image'); + + } +}) + + +module.exports = { + // Types + User +} \ No newline at end of file diff --git a/src/features/Posts/Components/PostCard/BountyCard.stories.tsx b/src/features/Posts/Components/PostCard/BountyCard.stories.tsx index 446fb14..dce5f37 100644 --- a/src/features/Posts/Components/PostCard/BountyCard.stories.tsx +++ b/src/features/Posts/Components/PostCard/BountyCard.stories.tsx @@ -12,7 +12,7 @@ export default { } as ComponentMeta; -const Template: ComponentStory = (args) =>
+const Template: ComponentStory = (args) =>
export const Default = Template.bind({}); Default.args = { diff --git a/src/features/Posts/Components/PostCard/BountyCard.tsx b/src/features/Posts/Components/PostCard/BountyCard.tsx index 7ad7162..7db5dad 100644 --- a/src/features/Posts/Components/PostCard/BountyCard.tsx +++ b/src/features/Posts/Components/PostCard/BountyCard.tsx @@ -13,7 +13,7 @@ export default function BountyCard({ bounty }: Props) {
-
+

{bounty.title}

@@ -26,7 +26,7 @@ export default function BountyCard({ bounty }: Props) { Apply
-

{bounty.excerpt}

+

{bounty.excerpt}

{bounty.tags.map(tag => diff --git a/src/features/Posts/Components/PostCard/Header.tsx b/src/features/Posts/Components/PostCard/Header.tsx index 5c6c7e7..d8e124e 100644 --- a/src/features/Posts/Components/PostCard/Header.tsx +++ b/src/features/Posts/Components/PostCard/Header.tsx @@ -1,24 +1,27 @@ -import React from 'react' import Avatar from 'src/features/Profiles/Components/Avatar/Avatar'; import dayjs from 'dayjs' interface Props { - name: string; - avatar: string; - date: string + author: { + id: number, + name: string, + image: string + } + date: string; + size?: 'sm' | 'md' } -export default function Header(props: Props) { +export default function Header({ size = 'md', ...props }: Props) { return (
- +
-

{props.name}

-

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

+

{props.author.name}

+

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

-

- 3h ago +

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

) diff --git a/src/features/Posts/Components/PostCard/PostCard.tsx b/src/features/Posts/Components/PostCard/PostCard.tsx index aa91beb..740a8d6 100644 --- a/src/features/Posts/Components/PostCard/PostCard.tsx +++ b/src/features/Posts/Components/PostCard/PostCard.tsx @@ -1,19 +1,21 @@ +import { Post } from "src/features/Posts/types" +import BountyCard from "./BountyCard" +import QuestionCard from "./QuestionCard" +import StoryCard from "./StoryCard" -type Props = - | { - type: 'story' - } - | { - type: 'question' - } - | { - type: 'bounty' - } - -export default function PostCard(props: Props) { - if ('question' in props) { - } - return ( -
StoryCard
- ) +type Props = { + post: Post +} + +export default function PostCard({ post }: Props) { + if (post.type === 'story') + return + + if (post.type === 'bounty') + return + + if (post.type === 'question') + return + + return null } diff --git a/src/features/Posts/Components/PostCard/QuestionCard.stories.tsx b/src/features/Posts/Components/PostCard/QuestionCard.stories.tsx index 356898d..84e05ba 100644 --- a/src/features/Posts/Components/PostCard/QuestionCard.stories.tsx +++ b/src/features/Posts/Components/PostCard/QuestionCard.stories.tsx @@ -12,7 +12,7 @@ export default { } as ComponentMeta; -const Template: ComponentStory = (args) =>
+const Template: ComponentStory = (args) =>
export const Default = Template.bind({}); Default.args = { diff --git a/src/features/Posts/Components/PostCard/QuestionCard.tsx b/src/features/Posts/Components/PostCard/QuestionCard.tsx index 0359d01..e69f32a 100644 --- a/src/features/Posts/Components/PostCard/QuestionCard.tsx +++ b/src/features/Posts/Components/PostCard/QuestionCard.tsx @@ -15,11 +15,11 @@ export default function QuestionCard({ question }: Props) {
{/* */}
-
+

{question.title}

-

{question.excerpt}

+

{question.excerpt}

@@ -39,16 +39,12 @@ export default function QuestionCard({ question }: Props) {
- {question.comments.map(comment =>
-
- -
-

{comment.author.name}

-

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

-
-
-

{comment.body}

-
)} +
+ {question.comments.map(comment =>
+
+

{comment.body}

+
)} +
diff --git a/src/features/Posts/Components/PostCard/StoryCard.stories.tsx b/src/features/Posts/Components/PostCard/StoryCard.stories.tsx index 545ca97..6649aaa 100644 --- a/src/features/Posts/Components/PostCard/StoryCard.stories.tsx +++ b/src/features/Posts/Components/PostCard/StoryCard.stories.tsx @@ -12,7 +12,7 @@ export default { } as ComponentMeta; -const Template: ComponentStory = (args) =>
+const Template: ComponentStory = (args) =>
export const Default = Template.bind({}); Default.args = { diff --git a/src/features/Posts/Components/PostCard/StoryCard.tsx b/src/features/Posts/Components/PostCard/StoryCard.tsx index bac6b64..fc92399 100644 --- a/src/features/Posts/Components/PostCard/StoryCard.tsx +++ b/src/features/Posts/Components/PostCard/StoryCard.tsx @@ -11,9 +11,9 @@ export default function StoryCard({ story }: Props) {
-
+

{story.title}

-

{story.excerpt}

+

{story.excerpt}


diff --git a/src/features/Posts/Components/PostsList/PostsList.stories.tsx b/src/features/Posts/Components/PostsList/PostsList.stories.tsx new file mode 100644 index 0000000..f34894c --- /dev/null +++ b/src/features/Posts/Components/PostsList/PostsList.stories.tsx @@ -0,0 +1,22 @@ +import { ComponentStory, ComponentMeta } from '@storybook/react'; +import { MOCK_DATA } from 'src/mocks/data'; + +import PostsList from './PostsList'; + +export default { + title: 'Posts/Components/PostsList', + component: PostsList, + argTypes: { + backgroundColor: { control: 'color' }, + }, +} as ComponentMeta; + + +const Template: ComponentStory = (args) =>
+ +export const Default = Template.bind({}); +Default.args = { + posts: MOCK_DATA['feed'] +} + + diff --git a/src/features/Posts/Components/PostsList/PostsList.tsx b/src/features/Posts/Components/PostsList/PostsList.tsx new file mode 100644 index 0000000..0e0bdfb --- /dev/null +++ b/src/features/Posts/Components/PostsList/PostsList.tsx @@ -0,0 +1,16 @@ +import { Post } from "src/features/Posts/types" +import PostCard from "../PostCard/PostCard" + +interface Props { + posts: Post[] +} + +export default function PostsList(props: Props) { + return ( +
+ { + props.posts.map(post => ) + } +
+ ) +} diff --git a/src/features/Posts/pages/FeedPage/FeedPage.tsx b/src/features/Posts/pages/FeedPage/FeedPage.tsx new file mode 100644 index 0000000..3deda80 --- /dev/null +++ b/src/features/Posts/pages/FeedPage/FeedPage.tsx @@ -0,0 +1 @@ +export { } \ No newline at end of file diff --git a/src/features/Posts/pages/FeedPage/feed.graphql b/src/features/Posts/pages/FeedPage/feed.graphql new file mode 100644 index 0000000..6d0629a --- /dev/null +++ b/src/features/Posts/pages/FeedPage/feed.graphql @@ -0,0 +1,75 @@ +query FeedQuery { + getFeed { + ... on Story { + id + title + date + author { + id + name + image + } + excerpt + tags { + id + title + } + votes_count + type + cover_image + comments_count + } + ... on Bounty { + id + title + date + author { + id + name + image + } + excerpt + tags { + id + title + } + votes_count + type + cover_image + deadline + reward_amount + applicants_count + } + ... on Question { + id + title + date + author { + id + name + image + } + excerpt + tags { + id + title + } + votes_count + type + cover_image + deadline + reward_amount + answers_count + comments { + id + date + body + author { + id + name + image + } + } + } + } +} diff --git a/src/features/Posts/pages/PostDetailsPage/PostDetailsPage.tsx b/src/features/Posts/pages/PostDetailsPage/PostDetailsPage.tsx new file mode 100644 index 0000000..3deda80 --- /dev/null +++ b/src/features/Posts/pages/PostDetailsPage/PostDetailsPage.tsx @@ -0,0 +1 @@ +export { } \ No newline at end of file diff --git a/src/features/Posts/pages/PostDetailsPage/postDetails.graphql b/src/features/Posts/pages/PostDetailsPage/postDetails.graphql new file mode 100644 index 0000000..360d1d1 --- /dev/null +++ b/src/features/Posts/pages/PostDetailsPage/postDetails.graphql @@ -0,0 +1,75 @@ +query PostDetailsQuery($postId: Int!) { + getPostById(id: $postId) { + ... on Story { + id + title + date + author { + id + name + image + } + excerpt + tags { + id + title + } + votes_count + type + cover_image + comments_count + } + ... on Bounty { + id + title + date + author { + id + name + image + } + excerpt + tags { + id + title + } + votes_count + type + cover_image + deadline + reward_amount + applicants_count + } + ... on Question { + id + title + date + author { + id + name + image + } + excerpt + tags { + id + title + } + votes_count + type + cover_image + deadline + reward_amount + answers_count + comments { + id + date + body + author { + id + name + image + } + } + } + } +} diff --git a/src/features/Posts/types/index.ts b/src/features/Posts/types/index.ts index 868b6f4..eca6f31 100644 --- a/src/features/Posts/types/index.ts +++ b/src/features/Posts/types/index.ts @@ -1,50 +1 @@ -import { Tag } from "src/utils/interfaces" - -export type User = { - id: number - name: string - image: string -} - -export type Author = User & { - join_date: string -} - -export type PostBase = { - id: number - title: string - date: string - author: Author - excerpt: string - tags: Tag[] - votes_count: number -} - -export type Story = PostBase & { - type: 'story' - cover_image: string; - comments_count: number -} - -export type Bounty = PostBase & { - type: 'bounty' - cover_image: string; - reward_amount: number - deadline: string - applicants_count: number -} - -export type Question = PostBase & { - type: 'question' - answers_count: number - comments: PostComment[] -} - -export type PostComment = { - id: number; - author: Author - date: string - body: string -} - -export type Post = Story | Question | Bounty +export * from './posts.interface' \ No newline at end of file diff --git a/src/features/Posts/types/posts.interface.ts b/src/features/Posts/types/posts.interface.ts new file mode 100644 index 0000000..868b6f4 --- /dev/null +++ b/src/features/Posts/types/posts.interface.ts @@ -0,0 +1,50 @@ +import { Tag } from "src/utils/interfaces" + +export type User = { + id: number + name: string + image: string +} + +export type Author = User & { + join_date: string +} + +export type PostBase = { + id: number + title: string + date: string + author: Author + excerpt: string + tags: Tag[] + votes_count: number +} + +export type Story = PostBase & { + type: 'story' + cover_image: string; + comments_count: number +} + +export type Bounty = PostBase & { + type: 'bounty' + cover_image: string; + reward_amount: number + deadline: string + applicants_count: number +} + +export type Question = PostBase & { + type: 'question' + answers_count: number + comments: PostComment[] +} + +export type PostComment = { + id: number; + author: Author + date: string + body: string +} + +export type Post = Story | Question | Bounty diff --git a/src/graphql/index.tsx b/src/graphql/index.tsx index 65508c4..ab965bc 100644 --- a/src/graphql/index.tsx +++ b/src/graphql/index.tsx @@ -24,6 +24,22 @@ export type Award = { url: Scalars['String']; }; +export type Bounty = PostBase & { + __typename?: 'Bounty'; + applicants_count: Scalars['Int']; + author: User; + cover_image: Scalars['String']; + date: Scalars['String']; + deadline: Scalars['String']; + excerpt: Scalars['String']; + id: Scalars['Int']; + reward_amount: Scalars['Int']; + tags: Array; + title: Scalars['String']; + type: Scalars['String']; + votes_count: Scalars['Int']; +}; + export type Category = { __typename?: 'Category'; apps_count: Scalars['Int']; @@ -61,6 +77,26 @@ export type MutationVoteArgs = { project_id: Scalars['Int']; }; +export type Post = Bounty | Question | Story; + +export type PostBase = { + author: User; + date: Scalars['String']; + excerpt: Scalars['String']; + id: Scalars['Int']; + tags: Array; + title: Scalars['String']; + votes_count: Scalars['Int']; +}; + +export type PostComment = { + __typename?: 'PostComment'; + author: User; + body: Scalars['String']; + date: Scalars['String']; + id: Scalars['Int']; +}; + export type Project = { __typename?: 'Project'; awards: Array; @@ -83,7 +119,9 @@ export type Query = { allCategories: Array; allProjects: Array; getCategory: Category; + getFeed: Array; getLnurlDetailsForProject: LnurlDetails; + getPostById: Post; getProject: Project; hottestProjects: Array; newProjects: Array; @@ -103,11 +141,22 @@ export type QueryGetCategoryArgs = { }; +export type QueryGetFeedArgs = { + skip?: InputMaybe; + take?: InputMaybe; +}; + + export type QueryGetLnurlDetailsForProjectArgs = { project_id: Scalars['Int']; }; +export type QueryGetPostByIdArgs = { + id: Scalars['Int']; +}; + + export type QueryGetProjectArgs = { id: Scalars['Int']; }; @@ -138,6 +187,37 @@ export type QuerySearchProjectsArgs = { take?: InputMaybe; }; +export type Question = PostBase & { + __typename?: 'Question'; + answers_count: Scalars['Int']; + author: User; + comments: Array; + cover_image: Scalars['String']; + date: Scalars['String']; + deadline: Scalars['String']; + excerpt: Scalars['String']; + id: Scalars['Int']; + reward_amount: Scalars['Int']; + tags: Array; + title: Scalars['String']; + type: Scalars['String']; + votes_count: Scalars['Int']; +}; + +export type Story = PostBase & { + __typename?: 'Story'; + author: User; + comments_count: Scalars['Int']; + cover_image: Scalars['String']; + date: Scalars['String']; + excerpt: Scalars['String']; + id: Scalars['Int']; + tags: Array; + title: Scalars['String']; + type: Scalars['String']; + votes_count: Scalars['Int']; +}; + export type Tag = { __typename?: 'Tag'; id: Scalars['Int']; @@ -145,6 +225,13 @@ export type Tag = { title: Scalars['String']; }; +export type User = { + __typename?: 'User'; + id: Scalars['Int']; + image: Scalars['String']; + name: Scalars['String']; +}; + export type Vote = { __typename?: 'Vote'; amount_in_sat: Scalars['Int']; @@ -167,6 +254,18 @@ export type SearchProjectsQueryVariables = Exact<{ export type SearchProjectsQuery = { __typename?: 'Query', searchProjects: Array<{ __typename?: 'Project', id: number, thumbnail_image: string, title: string, category: { __typename?: 'Category', title: string, id: number } }> }; +export type FeedQueryQueryVariables = Exact<{ [key: string]: never; }>; + + +export type FeedQueryQuery = { __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, cover_image: string, deadline: string, reward_amount: number, 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 PostDetailsQueryQueryVariables = Exact<{ + postId: Scalars['Int']; +}>; + + +export type PostDetailsQueryQuery = { __typename?: 'Query', getPostById: { __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, cover_image: string, deadline: string, reward_amount: number, 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 CategoryPageQueryVariables = Exact<{ categoryId: Scalars['Int']; }>; @@ -291,6 +390,215 @@ export function useSearchProjectsLazyQuery(baseOptions?: Apollo.LazyQueryHookOpt export type SearchProjectsQueryHookResult = ReturnType; export type SearchProjectsLazyQueryHookResult = ReturnType; export type SearchProjectsQueryResult = Apollo.QueryResult; +export const FeedQueryDocument = gql` + query FeedQuery { + getFeed { + ... on Story { + id + title + date + author { + id + name + image + } + excerpt + tags { + id + title + } + votes_count + type + cover_image + comments_count + } + ... on Bounty { + id + title + date + author { + id + name + image + } + excerpt + tags { + id + title + } + votes_count + type + cover_image + deadline + reward_amount + applicants_count + } + ... on Question { + id + title + date + author { + id + name + image + } + excerpt + tags { + id + title + } + votes_count + type + cover_image + deadline + reward_amount + answers_count + comments { + id + date + body + author { + id + name + image + } + } + } + } +} + `; + +/** + * __useFeedQueryQuery__ + * + * To run a query within a React component, call `useFeedQueryQuery` and pass it any options that fit your needs. + * When your component renders, `useFeedQueryQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useFeedQueryQuery({ + * variables: { + * }, + * }); + */ +export function useFeedQueryQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(FeedQueryDocument, options); + } +export function useFeedQueryLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(FeedQueryDocument, options); + } +export type FeedQueryQueryHookResult = ReturnType; +export type FeedQueryLazyQueryHookResult = ReturnType; +export type FeedQueryQueryResult = Apollo.QueryResult; +export const PostDetailsQueryDocument = gql` + query PostDetailsQuery($postId: Int!) { + getPostById(id: $postId) { + ... on Story { + id + title + date + author { + id + name + image + } + excerpt + tags { + id + title + } + votes_count + type + cover_image + comments_count + } + ... on Bounty { + id + title + date + author { + id + name + image + } + excerpt + tags { + id + title + } + votes_count + type + cover_image + deadline + reward_amount + applicants_count + } + ... on Question { + id + title + date + author { + id + name + image + } + excerpt + tags { + id + title + } + votes_count + type + cover_image + deadline + reward_amount + answers_count + comments { + id + date + body + author { + id + name + image + } + } + } + } +} + `; + +/** + * __usePostDetailsQueryQuery__ + * + * To run a query within a React component, call `usePostDetailsQueryQuery` and pass it any options that fit your needs. + * When your component renders, `usePostDetailsQueryQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = usePostDetailsQueryQuery({ + * variables: { + * postId: // value for 'postId' + * }, + * }); + */ +export function usePostDetailsQueryQuery(baseOptions: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(PostDetailsQueryDocument, options); + } +export function usePostDetailsQueryLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(PostDetailsQueryDocument, options); + } +export type PostDetailsQueryQueryHookResult = ReturnType; +export type PostDetailsQueryLazyQueryHookResult = ReturnType; +export type PostDetailsQueryQueryResult = Apollo.QueryResult; export const CategoryPageDocument = gql` query CategoryPage($categoryId: Int!) { projectsByCategory(category_id: $categoryId) { diff --git a/src/mocks/data.ts b/src/mocks/data.ts index 98dab99..269a6f6 100644 --- a/src/mocks/data.ts +++ b/src/mocks/data.ts @@ -1,9 +1,9 @@ -import { Project, ProjectCategory } from "src/utils/interfaces"; -import { posts } from "./data/posts"; +import { posts, feed } from "./data/posts"; import { categories, projects } from "./data/projects"; export const MOCK_DATA = { - projects: projects, - categories: categories, - posts: posts + projects, + categories, + posts, + feed } \ No newline at end of file diff --git a/src/mocks/data/posts.ts b/src/mocks/data/posts.ts index 7d22ecf..e6c0882 100644 --- a/src/mocks/data/posts.ts +++ b/src/mocks/data/posts.ts @@ -1,5 +1,6 @@ -import { Bounty, Question, Story } from "src/features/Posts/types"; +import dayjs from "dayjs"; +import { Bounty, Post, Question, Story } from "src/features/Posts/types"; import { getAvatarImage, getCoverImage } from "./utils"; const getAuthor = () => ({ @@ -8,9 +9,9 @@ const getAuthor = () => ({ image: getAvatarImage() }) -const date = 'Mon Mar 14 2022 20:33:17 GMT+0200 (Eastern European Standard Time)' +const date = dayjs().subtract(5, 'hour').toString(); -let posts = { +export let posts = { stories: [ { id: 1, @@ -86,7 +87,9 @@ posts.bounties = posts.bounties.map(b => ({ ...b, __typename: "Bounty" })) posts.questions = posts.questions.map(b => ({ ...b, __typename: "Question" })) posts.stories = posts.stories.map(b => ({ ...b, __typename: "Story" })) +export const feed: Post[] = [ + ...posts.stories, + ...posts.bounties, + ...posts.questions, +] -export { - posts, -} \ No newline at end of file diff --git a/src/mocks/data/projects.ts b/src/mocks/data/projects.ts index 9b928fa..7cd255b 100644 --- a/src/mocks/data/projects.ts +++ b/src/mocks/data/projects.ts @@ -1,7 +1,7 @@ import { Project, ProjectCategory } from "src/utils/interfaces"; -let categories = [ +export let categories = [ { "title": "Shock the Web ⚡️", "id": 11, @@ -79,9 +79,9 @@ let categories = [ "icon": "🔁", "votes_sum": 0 } -] +] as ProjectCategory[] -let projects = [ +export let projects = [ { "id": 16, "title": "Alby", @@ -555,7 +555,7 @@ let projects = [ "title": "Gaming" } } -] +] as Project[] categories = categories.map(c => ({ ...c, __typename: "Category" })) @@ -564,7 +564,3 @@ projects = projects.map(p => ({ ...p, __typename: "Project" })) // 2- Computed Fields categories = categories.map(c => ({ ...c, apps_count: projects.reduce((acc, p) => acc + (p.category.id === c.id ? 1 : 0), 0) })) -export { - projects, - categories -} \ No newline at end of file