refactor: change story&feed routes structure, change default route, add banner to feed page, fix Loadable component props typing

This commit is contained in:
MTG2000
2022-09-17 12:14:22 +03:00
parent ac8c2ddca9
commit c5f640e405
12 changed files with 73 additions and 40 deletions

View File

@@ -114,6 +114,7 @@ export interface NexusGenObjects {
applicants_count: number; // Int!
applications: NexusGenRootTypes['BountyApplication'][]; // [BountyApplication!]!
body: string; // String!
cover_image?: string | null; // String
createdAt: NexusGenScalars['Date']; // Date!
deadline: string; // String!
excerpt: string; // String!

View File

@@ -92,15 +92,16 @@ function App() {
</Helmet>
<Suspense fallback={<LoadingPage />}>
<Routes>
<Route path={PAGES_ROUTES.blog.createPost} element={<ProtectedRoute><CreatePostPage /></ProtectedRoute>} />
<Route path={PAGES_ROUTES.blog.writeStory} element={<ProtectedRoute><CreatePostPage initType="story" /></ProtectedRoute>} />
<Route element={<NavbarLayout />}>
<Route path={PAGES_ROUTES.projects.hottest} element={<HottestPage />} />
<Route path={PAGES_ROUTES.projects.byCategoryId} element={<CategoryPage />} />
<Route path={PAGES_ROUTES.projects.default} element={<ExplorePage />} />
<Route path={PAGES_ROUTES.blog.postById} element={<PostDetailsPage />} />
<Route path={PAGES_ROUTES.blog.storyById} element={<PostDetailsPage postType='story' />} />
<Route path={PAGES_ROUTES.blog.feed} element={<FeedPage />} />
<Route path={PAGES_ROUTES.blog.catchStory} element={<Navigate replace to={PAGES_ROUTES.blog.feed} />} />
<Route path={PAGES_ROUTES.hackathons.default} element={<HackathonsPage />} />
@@ -114,7 +115,7 @@ function App() {
<Route path={PAGES_ROUTES.auth.login} element={<LoginPage />} />
<Route path={PAGES_ROUTES.auth.logout} element={<LogoutPage />} />
<Route path="/" element={<Navigate to={PAGES_ROUTES.projects.default} />} />
<Route path="/" element={<Navigate replace to={PAGES_ROUTES.blog.feed} />} />
</Route>
</Routes>

View File

@@ -14,7 +14,7 @@ import {
import '@szhsin/react-menu/dist/index.css';
import { FiChevronDown } from "react-icons/fi";
import Avatar from "src/features/Profiles/Components/Avatar/Avatar";
import { createRoute } from "src/utils/routing";
import { createRoute, PAGES_ROUTES } from "src/utils/routing";
import Button from "../Button/Button";
@@ -64,10 +64,10 @@ export default function NavDesktop() {
menuStyle={{ border: '1px solid' }}
>
<MenuItem
href="/blog"
href={PAGES_ROUTES.blog.feed}
onClick={(e) => {
e.syntheticEvent.preventDefault();
navigate("/blog");
navigate(PAGES_ROUTES.blog.feed);
}}
className='!p-16 font-medium flex gap-16 hover:bg-gray-100 !rounded-12 '
>

View File

@@ -14,7 +14,7 @@ import styles from './styles.module.css'
import '@szhsin/react-menu/dist/index.css';
import { Menu, MenuButton, MenuItem } from "@szhsin/react-menu";
import Avatar from "src/features/Profiles/Components/Avatar/Avatar";
import { createRoute } from "src/utils/routing";
import { createRoute, PAGES_ROUTES } from "src/utils/routing";
const navBtnVariant = {
menuHide: { rotate: 90, opacity: 0 },
@@ -186,7 +186,7 @@ export default function NavMobile() {
>
<div className='flex flex-col gap-24 pt-16' >
<Link
to="/blog"
to={PAGES_ROUTES.blog.feed}
onClick={() => toggleDrawerOpen(false)}
className='font-medium flex gap-16 !rounded-12 '
>

View File

@@ -37,7 +37,7 @@ export default function HackathonsPage() {
<div className={`w-full`}>
<div className="rounded-16 min-h-[280px] relative overflow-hidden p-16 md:p-24 flex flex-col items-start justify-end">
<img
className="w-full h-full object-cover object-top absolute top-0 left-0 z-[-2]"
className="w-full h-full object-cover object-center absolute top-0 left-0 z-[-2]"
src={bannerData.img}
alt=""
/>

View File

@@ -39,7 +39,7 @@ export default function StoryCard({ story }: Props) {
{story.cover_image && <img src={story.cover_image} className='h-[200px] w-full object-cover' alt="" />}
<div className="p-24">
<Header author={story.author} date={story.createdAt} />
<Link to={createRoute({ type: 'story', id: story.id, title: story.title })}>
<Link to={createRoute({ type: 'story', id: story.id, title: story.title, username: story.author.name })}>
<h2 className="text-h5 font-bolder mt-16">{story.title}</h2>
</Link>
<p className="text-body4 text-gray-600 mt-8">{story.excerpt}...</p>

View File

@@ -6,12 +6,13 @@ import BountyForm from "./Components/BountyForm/BountyForm";
import QuestionForm from "./Components/QuestionForm/QuestionForm";
import CreateStoryPage from "./CreateStoryPage/CreateStoryPage";
interface Props {
initType: 'story' | 'bounty' | 'question'
}
export default function CreatePostPage() {
export default function CreatePostPage(props: Props) {
const { type } = useParams()
const [postType] = useState<'story' | 'bounty' | 'question'>((type as any) ?? 'story');
const [postType] = useState<'story' | 'bounty' | 'question'>(props.initType);
const navigate = useNavigate();

View File

@@ -14,6 +14,7 @@ import { FaDiscord } from 'react-icons/fa'
import { FiArrowRight } from 'react-icons/fi'
import { capitalize } from 'src/utils/helperFunctions'
import { bannerData } from 'src/features/Projects/pages/ExplorePage/Header/Header'
import { PAGES_ROUTES } from 'src/utils/routing'
export default function FeedPage() {
@@ -46,6 +47,21 @@ export default function FeedPage() {
<div
className={`page-container`}
>
<div className="rounded-16 min-h-[280px] relative overflow-hidden p-16 md:p-24 flex flex-col items-start justify-end mb-24">
<img
className="w-full h-full object-cover object-center absolute top-0 left-0 z-[-2]"
src={bannerData.img}
alt=""
/>
<div className="w-full h-full object-cover bg-black bg-opacity-60 absolute top-0 left-0 z-[-1]"></div>
<div className="max-w-[90%]">
{bannerData.title}
</div>
<Button href={bannerData.link.url} color="white" className="mt-24">
{bannerData.link.content}
</Button>
</div>
<div className={`w-full ${styles.grid}`}>
<div id="title">
{tagFilter && <p className="text-body6 text-gray-500 font-medium mb-8">
@@ -76,7 +92,7 @@ export default function FeedPage() {
<aside id='categories' className='no-scrollbar'>
<div className="pb-16 md:overflow-y-scroll sticky-side-element">
<Button
href='/blog/create-post'
href={PAGES_ROUTES.blog.writeStory}
color='primary'
fullWidth
>

View File

@@ -6,6 +6,7 @@ import VoteButton from "src/Components/VoteButton/VoteButton"
import { Post } from "src/features/Posts/types"
import { Vote_Item_Type } from "src/graphql"
import { useVote } from "src/utils/hooks"
import { PAGES_ROUTES } from "src/utils/routing"
interface Props {
post: Pick<Post,
@@ -40,7 +41,7 @@ export default function PostActions({ post, isPreview }: Props) {
<button className={`
hidden md:flex w-full aspect-square bg-white rounded-12 border-2 border-gray-200 justify-around items-center text-gray-500 hover:bg-gray-50 active:bg-gray-100
`}
onClick={() => isPreview ? navigate(-1) : navigate('/blog')}
onClick={() => isPreview ? navigate(-1) : navigate(PAGES_ROUTES.blog.feed)}
>
<FiArrowLeft className={"text-body1"} />
</button>

View File

@@ -16,16 +16,22 @@ import { RotatingLines } from 'react-loader-spinner'
const CommentsSection = lazy(() => import( /* webpackChunkName: "comments_section" */ "src/features/Posts/Components/Comments"))
export default function PostDetailsPage() {
const { type: _type, id } = useParams();
const type = capitalize(_type);
interface Props {
postType: 'story' | 'bounty' | 'question'
}
export default function PostDetailsPage(props: Props) {
const { slug } = useParams();
const type = capitalize(props.postType);
const id = Number(slug?.includes('--') ? slug.slice(slug.lastIndexOf('--') + 2) : slug)
const postDetailsQuery = usePostDetailsQuery({
variables: {
id: Number(id!),
id,
type: type as any
},
skip: isNaN(Number(id)),
skip: isNaN(id),
})

View File

@@ -1,8 +1,10 @@
import { Suspense } from "react";
import LoadingPage from "src/Components/LoadingPage/LoadingPage";
export const Loadable = (Component: any, Loading = LoadingPage) => (props: any) => (
<Suspense fallback={<Loading />}>
<Component {...props} />
</Suspense>
);
export function Loadable<P>(Component: React.LazyExoticComponent<(props: P) => JSX.Element>, Loading = LoadingPage) {
return (props: P) => (
<Suspense fallback={<Loading />}>
<Component {...props as any} />
</Suspense>
)
}

View File

@@ -42,21 +42,23 @@ type RouteOptions =
export function createRoute(options: RouteOptions) {
if (options.type === "post")
return `/blog/post/${options.postType.toLowerCase()}/${options.id}`
+ (options.title ? `/${toSlug(options.title)}` : "");
if (options.type === "story")
return `/blog/post/story/${options.id}`
+ (options.title ? `/${toSlug(options.title)}` : "");
if (options.type === "bounty")
if ((options.type === "post" && options.postType.toLowerCase() === 'story')
|| options.type === "story") {
const onlyId = !options.title;
return "/story/"
// + (options.username ? `${toSlug(options.username)}-` : "")
+ (options.title ? `${toSlug(options.title)}-` : "")
+ (!onlyId ? "-" : "")
+ `${options.id}`
}
if ((options.type === "post" && options.postType.toLowerCase() === 'bounty')
|| options.type === "bounty")
return `/blog/post/bounty/${options.id}`
+ (options.title ? `/${toSlug(options.title)}` : "");
if (options.type === "question")
if ((options.type === "post" && options.postType.toLowerCase() === 'question')
|| options.type === "question")
return `/blog/post/question/${options.id}`
+ (options.title ? `/${toSlug(options.title)}` : "");
@@ -82,9 +84,12 @@ export const PAGES_ROUTES = {
byCategoryId: "/projects/category/:id"
},
blog: {
feed: "/blog",
postById: "/blog/post/:type/:id/*",
createPost: "/blog/create-post"
feed: "/feed",
postById: "/feed/post/:type/:id/*",
storyById: "/story/:slug",
writeStory: "/story/write",
createPost: "/story/create-post",
catchStory: '/story'
},
hackathons: {
default: "/hackathons"