mirror of
https://github.com/aljazceru/landscape-template.git
synced 2026-01-06 16:04:23 +01:00
refactor: change story&feed routes structure, change default route, add banner to feed page, fix Loadable component props typing
This commit is contained in:
@@ -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!
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 '
|
||||
>
|
||||
|
||||
@@ -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 '
|
||||
>
|
||||
|
||||
@@ -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=""
|
||||
/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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),
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user