mirror of
https://github.com/aljazceru/landscape-template.git
synced 2025-12-26 18:54:21 +01:00
feat: add the post details routes, added skeleton to trending card
This commit is contained in:
24
package-lock.json
generated
24
package-lock.json
generated
@@ -24,6 +24,7 @@
|
||||
"apollo-server": "^3.5.0",
|
||||
"apollo-server-lambda": "^3.5.0",
|
||||
"axios": "^0.24.0",
|
||||
"chance": "^1.1.8",
|
||||
"dayjs": "^1.11.1",
|
||||
"env-cmd": "^10.1.0",
|
||||
"framer-motion": "^5.3.0",
|
||||
@@ -68,6 +69,7 @@
|
||||
"@storybook/node-logger": "^6.3.12",
|
||||
"@storybook/preset-create-react-app": "^3.2.0",
|
||||
"@storybook/react": "^6.3.12",
|
||||
"@types/chance": "^1.1.3",
|
||||
"@types/lodash.debounce": "^4.0.6",
|
||||
"@types/lodash.throttle": "^4.1.6",
|
||||
"@types/marked": "^4.0.3",
|
||||
@@ -9385,6 +9387,12 @@
|
||||
"@types/responselike": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/chance": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/chance/-/chance-1.1.3.tgz",
|
||||
"integrity": "sha512-X6c6ghhe4/sQh4XzcZWSFaTAUOda38GQHmq9BUanYkOE/EO7ZrkazwKmtsj3xzTjkLWmwULE++23g3d3CCWaWw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/chrome": {
|
||||
"version": "0.0.74",
|
||||
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.74.tgz",
|
||||
@@ -13368,6 +13376,11 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/chance": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/chance/-/chance-1.1.8.tgz",
|
||||
"integrity": "sha512-v7fi5Hj2VbR6dJEGRWLmJBA83LJMS47pkAbmROFxHWd9qmE1esHRZW8Clf1Fhzr3rjxnNZVCjOEv/ivFxeIMtg=="
|
||||
},
|
||||
"node_modules/change-case": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz",
|
||||
@@ -67850,6 +67863,12 @@
|
||||
"@types/responselike": "*"
|
||||
}
|
||||
},
|
||||
"@types/chance": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/chance/-/chance-1.1.3.tgz",
|
||||
"integrity": "sha512-X6c6ghhe4/sQh4XzcZWSFaTAUOda38GQHmq9BUanYkOE/EO7ZrkazwKmtsj3xzTjkLWmwULE++23g3d3CCWaWw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/chrome": {
|
||||
"version": "0.0.74",
|
||||
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.74.tgz",
|
||||
@@ -71111,6 +71130,11 @@
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"chance": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/chance/-/chance-1.1.8.tgz",
|
||||
"integrity": "sha512-v7fi5Hj2VbR6dJEGRWLmJBA83LJMS47pkAbmROFxHWd9qmE1esHRZW8Clf1Fhzr3rjxnNZVCjOEv/ivFxeIMtg=="
|
||||
},
|
||||
"change-case": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz",
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"apollo-server": "^3.5.0",
|
||||
"apollo-server-lambda": "^3.5.0",
|
||||
"axios": "^0.24.0",
|
||||
"chance": "^1.1.8",
|
||||
"dayjs": "^1.11.1",
|
||||
"env-cmd": "^10.1.0",
|
||||
"framer-motion": "^5.3.0",
|
||||
@@ -118,6 +119,7 @@
|
||||
"@storybook/node-logger": "^6.3.12",
|
||||
"@storybook/preset-create-react-app": "^3.2.0",
|
||||
"@storybook/react": "^6.3.12",
|
||||
"@types/chance": "^1.1.3",
|
||||
"@types/lodash.debounce": "^4.0.6",
|
||||
"@types/lodash.throttle": "^4.1.6",
|
||||
"@types/marked": "^4.0.3",
|
||||
|
||||
@@ -9,6 +9,7 @@ import CategoryPage from "src/features/Projects/pages/CategoryPage/CategoryPage"
|
||||
import { useWrapperSetup } from "./utils/Wrapper";
|
||||
import HottestPage from "src/features/Projects/pages/HottestPage/HottestPage";
|
||||
import FeedPage from "./features/Posts/pages/FeedPage/FeedPage";
|
||||
import PostDetailsPage from "./features/Posts/pages/PostDetailsPage/PostDetailsPage";
|
||||
|
||||
function App() {
|
||||
const { isWalletConnected } = useAppSelector(state => ({
|
||||
@@ -40,6 +41,7 @@ function App() {
|
||||
<Routes>
|
||||
<Route path="/hottest" element={<HottestPage />} />
|
||||
<Route path="/category/:id" element={<CategoryPage />} />
|
||||
<Route path="/blog/post/:type/:id" element={<PostDetailsPage />} />
|
||||
<Route path="/blog" element={<FeedPage />} />
|
||||
<Route path="/" element={<ExplorePage />} />
|
||||
</Routes>
|
||||
|
||||
@@ -30,8 +30,7 @@ export default function BountyCard({ bounty }: Props) {
|
||||
<Header author={bounty.author} date={bounty.date} />
|
||||
<div className="flex flex-col gap-8 md:gap-0 md:flex-row justify-between">
|
||||
<div>
|
||||
|
||||
<Link to={'#'}>
|
||||
<Link to={`/blog/post/Bounty/${bounty.id}`}>
|
||||
<h2 className="text-h5 font-bolder mt-16 flex items-center gap-8">
|
||||
<span><Badge color="none" size="sm" className="bg-yellow-500 text-black">Bounty</Badge> {bounty.title}</span>
|
||||
</h2>
|
||||
|
||||
@@ -27,7 +27,7 @@ export default function QuestionCard({ question }: Props) {
|
||||
<div className="p-24">
|
||||
<Header author={question.author} date={question.date} />
|
||||
<div className="flex justify-between">
|
||||
<Link to={'#'}>
|
||||
<Link to={`/blog/post/Question/${question.id}`}>
|
||||
<h2 className="text-h5 font-bolder mt-16">{question.title}</h2>
|
||||
</Link>
|
||||
</div>
|
||||
@@ -59,7 +59,7 @@ export default function QuestionCard({ question }: Props) {
|
||||
</div>
|
||||
|
||||
<div className="flex">
|
||||
<Link to='#' className="text-black font-medium p-8 hover:bg-gray-100 rounded">
|
||||
<Link to={`/blog/post/Question/${question.id}`} className="text-black font-medium p-8 hover:bg-gray-100 rounded">
|
||||
See all {question.answers_count} comments
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@ export default function StoryCard({ story }: Props) {
|
||||
<img src={story.cover_image} className='h-[200px] w-full object-cover' alt="" />
|
||||
<div className="p-24">
|
||||
<Header author={story.author} date={story.date} />
|
||||
<Link to={'#'}>
|
||||
<Link to={`/blog/post/Story/${story.id}`}>
|
||||
<h2 className="text-h5 font-bolder mt-16">{story.title}</h2>
|
||||
</Link>
|
||||
<p className="text-body4 text-gray-600 mt-8">{story.excerpt}</p>
|
||||
|
||||
@@ -1,32 +1,37 @@
|
||||
import Skeleton from 'react-loading-skeleton'
|
||||
import { Link } from 'react-router-dom'
|
||||
import Avatar from 'src/features/Profiles/Components/Avatar/Avatar'
|
||||
import { useTrendingPostsQuery } from 'src/graphql'
|
||||
import { random } from 'src/utils/helperFunctions'
|
||||
|
||||
export default function TrendingCard() {
|
||||
|
||||
const trendingPosts = useTrendingPostsQuery()
|
||||
|
||||
if (trendingPosts.loading)
|
||||
return <></>
|
||||
|
||||
console.log(trendingPosts.data?.getTrendingPosts);
|
||||
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-8 border p-16">
|
||||
<h3 className="text-body2 font-bolder mb-16">Trending on BOLT.FUN</h3>
|
||||
<ul className='flex flex-col gap-16'>
|
||||
{trendingPosts.data?.getTrendingPosts.map(post => {
|
||||
console.log(post);
|
||||
|
||||
return <Link key={post.id} to={`/post-details-page/${post.id}`} className="border-b pb-4 last-of-type:border-b-0">
|
||||
<li className="flex items-start gap-8">
|
||||
<Avatar width={24} src={post.author.image} />
|
||||
<p className="text-body5 font-medium">{post.title}</p>
|
||||
{
|
||||
trendingPosts.loading ?
|
||||
Array(4).fill(0).map((_, idx) => <li key={idx} className="flex items-start gap-8">
|
||||
<Skeleton circle width={24} height={24} />
|
||||
<p className="text-body5 font-medium flex-grow"><Skeleton width={'80%'} />
|
||||
<Skeleton width={`${random(30, 65)}%`} /></p>
|
||||
</li>
|
||||
</Link>
|
||||
}
|
||||
)}
|
||||
)
|
||||
:
|
||||
trendingPosts.data?.getTrendingPosts.map(post => {
|
||||
return <Link key={post.id} to={`/post-details-page/${post.id}`} className="border-b pb-4 last-of-type:border-b-0">
|
||||
<li className="flex items-start gap-8">
|
||||
<Avatar width={24} src={post.author.image} />
|
||||
<p className="text-body5 font-medium">{post.title}</p>
|
||||
</li>
|
||||
</Link>
|
||||
}
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -55,20 +55,16 @@ export default function BountyPageContent({ bounty }: Props) {
|
||||
<div className="border bg-white rounded-8 p-24 mt-16">
|
||||
<h4 className="text-body2 font-bolder">Applicants</h4>
|
||||
<ul className="flex flex-col gap-16 mt-16">
|
||||
<li>
|
||||
<Header author={bounty.author} size='sm' date={bounty.date} />
|
||||
<div className="bg-gray-100 mt-10 p-16 rounded-8">
|
||||
<p className="text-body5 font-medium mb-8">Work Plan</p>
|
||||
<p className="text-body5 text-gray-600">I will create the widget using nextjs, react and typescript. and also convert it into a npm package.</p>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<Header author={bounty.author} size='sm' date={bounty.date} />
|
||||
<div className="bg-gray-100 mt-10 p-16 rounded-8">
|
||||
<p className="text-body5 font-medium mb-8">Work Plan</p>
|
||||
<p className="text-body5 text-gray-600">I will create the widget using nextjs, react and typescript. and also convert it into a npm package.</p>
|
||||
</div>
|
||||
</li>
|
||||
{bounty.applications.map(application =>
|
||||
<li key={application.id}>
|
||||
<Header author={application.author} size='sm' date={application.date} />
|
||||
<div className="bg-gray-100 mt-10 p-16 rounded-8">
|
||||
<p className="text-body5 font-medium mb-8">Work Plan</p>
|
||||
<p className="text-body5 text-gray-600"> {application.workplan}</p>
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
import Header from "src/features/Posts/Components/PostCard/Header/Header"
|
||||
import { isBounty, isQuestion, isStory, Post } from "src/features/Posts/types"
|
||||
import { marked } from 'marked';
|
||||
import styles from './styles.module.css'
|
||||
import Badge from "src/Components/Badge/Badge";
|
||||
import { BiComment } from "react-icons/bi";
|
||||
import { RiFlashlightLine } from "react-icons/ri";
|
||||
import StoryPageContent from "./StoryPageContent";
|
||||
import BountyPageContent from "./BountyPageContent";
|
||||
import { PostDetailsQuery } from "src/graphql";
|
||||
|
||||
|
||||
interface Props {
|
||||
post: Post
|
||||
post: PostDetailsQuery['getPostById']
|
||||
}
|
||||
|
||||
export default function PageContent({ post }: Props) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
|
||||
import { ModifyArgs } from 'src/utils/storybook/utils';
|
||||
import PostDetailsPage from './PostDetailsPage';
|
||||
|
||||
export default {
|
||||
@@ -8,6 +8,14 @@ export default {
|
||||
argTypes: {
|
||||
backgroundColor: { control: 'color' },
|
||||
},
|
||||
parameters: {
|
||||
modifyArgs: {
|
||||
router: {
|
||||
routePath: "/:type/:id",
|
||||
currentPath: "/Story/1"
|
||||
}
|
||||
} as ModifyArgs
|
||||
}
|
||||
} as ComponentMeta<typeof PostDetailsPage>;
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
|
||||
import { useFeedQuery } from 'src/graphql'
|
||||
import { MOCK_DATA } from 'src/mocks/data'
|
||||
import { useAppSelector, useInfiniteQuery } from 'src/utils/hooks'
|
||||
import PostsList from '../../Components/PostsList/PostsList'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { usePostDetailsQuery } from 'src/graphql'
|
||||
import { useAppSelector, } from 'src/utils/hooks'
|
||||
import TrendingCard from '../../Components/TrendingCard/TrendingCard'
|
||||
import AuthorCard from './Components/AuthorCard/AuthorCard'
|
||||
import PageContent from './Components/PageContent/PageContent'
|
||||
@@ -12,18 +11,27 @@ import styles from './styles.module.css'
|
||||
|
||||
export default function PostDetailsPage() {
|
||||
|
||||
// const feedQuery = useFeedQuery({
|
||||
// variables: {
|
||||
// take: 10,
|
||||
// skip: 0
|
||||
// },
|
||||
// })
|
||||
// const { fetchMore, isFetchingMore } = useInfiniteQuery(feedQuery, 'getFeed')
|
||||
const post = MOCK_DATA.posts.stories[0]
|
||||
const { type, id } = useParams()
|
||||
const postDetailsQuery = usePostDetailsQuery({
|
||||
variables: {
|
||||
id: Number(id!),
|
||||
type: type as any
|
||||
},
|
||||
skip: isNaN(Number(id))
|
||||
})
|
||||
|
||||
const { navHeight } = useAppSelector((state) => ({
|
||||
navHeight: state.ui.navHeight
|
||||
}));
|
||||
|
||||
if (postDetailsQuery.loading)
|
||||
return <h2>Loading</h2>
|
||||
|
||||
const post = postDetailsQuery.data?.getPostById;
|
||||
|
||||
if (!post)
|
||||
return <h2>404</h2>
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`page-container grid pt-16 w-full gap-32 ${styles.grid}`}
|
||||
|
||||
@@ -3,9 +3,12 @@ import dayjs from "dayjs";
|
||||
import { Bounty, Post, Question, Story } from "src/features/Posts/types";
|
||||
import { randomItem } from "src/utils/helperFunctions";
|
||||
import { getAvatarImage, getCoverImage } from "./utils";
|
||||
import chance, { Chance } from 'chance'
|
||||
|
||||
const date = dayjs().subtract(5, 'hour').toString();
|
||||
|
||||
|
||||
|
||||
const getAuthor = () => ({
|
||||
id: 12,
|
||||
name: "John Doe",
|
||||
@@ -102,7 +105,7 @@ export let posts = {
|
||||
author: getAuthor(),
|
||||
deadline: "25 May",
|
||||
reward_amount: 200_000,
|
||||
applications: getApplications(),
|
||||
applications: getApplications(2),
|
||||
|
||||
}
|
||||
] as Bounty[],
|
||||
@@ -145,8 +148,10 @@ posts.questions = posts.questions.map(b => ({ ...b, __typename: "Question" }))
|
||||
posts.stories = posts.stories.map(b => ({ ...b, __typename: "Story" }))
|
||||
|
||||
|
||||
const feedRandomer = new Chance('feed')
|
||||
|
||||
export const feed: Post[] = Array(30).fill(0).map((_, idx) => {
|
||||
const post = randomItem(posts.bounties[0], posts.questions[0], posts.stories[0]) as Post;
|
||||
const post = feedRandomer.pickone([posts.bounties[0], posts.questions[0], posts.stories[0]])
|
||||
|
||||
return { ...post, id: idx + 1, title: `${post.type} Title ${idx + 1}` }
|
||||
})
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { MOCK_DATA } from "./data";
|
||||
import { Post, Query, QueryGetFeedArgs, QueryGetPostByIdArgs } from 'src/graphql'
|
||||
import { Author } from "src/features/Posts/types";
|
||||
import { Chance } from "chance";
|
||||
|
||||
const chance = new Chance()
|
||||
|
||||
export function getCategory(id: number) {
|
||||
|
||||
@@ -55,46 +57,5 @@ export function getPostById(args: QueryGetPostByIdArgs): Query['getPostById'] {
|
||||
}
|
||||
|
||||
export function getTrendingPosts(): Query['getTrendingPosts'] {
|
||||
return [
|
||||
{
|
||||
id: 1,
|
||||
title: 'How to collect donations within lightning network?',
|
||||
author: {
|
||||
id: 2,
|
||||
name: "John Doe",
|
||||
image: "https://i.pravatar.cc/150?img=2"
|
||||
} as Author,
|
||||
__typename: "Question"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'How to implement the RSMC part of Lightning network?',
|
||||
author: {
|
||||
id: 2,
|
||||
name: "John Doe",
|
||||
image: "https://i.pravatar.cc/150?img=2"
|
||||
} as Author,
|
||||
__typename: "Question"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'c-lightning public node data on explorers',
|
||||
author: {
|
||||
id: 2,
|
||||
name: "John Doe",
|
||||
image: "https://i.pravatar.cc/150?img=2"
|
||||
} as Author,
|
||||
__typename: "Story"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'How to find all nodes and connections in LN?',
|
||||
author: {
|
||||
id: 2,
|
||||
name: "John Doe",
|
||||
image: "https://i.pravatar.cc/150?img=2"
|
||||
} as Author,
|
||||
__typename: "Question"
|
||||
},
|
||||
] as Post[];
|
||||
return chance.pickset(MOCK_DATA.feed, 5);
|
||||
}
|
||||
Reference in New Issue
Block a user