Merge branch 'dev' into nostr

This commit is contained in:
MTG2000
2022-07-27 18:05:46 +03:00
14 changed files with 260 additions and 9 deletions

View File

@@ -17,7 +17,7 @@ export default function TrendingCard() {
<ul className='flex flex-col'>
{
trendingPosts.loading ?
Array(4).fill(0).map((_, idx) => <li key={idx} className="flex items-start gap-8">
Array(4).fill(0).map((_, idx) => <li key={idx} className="flex items-start gap-8 border-b py-16 last-of-type:border-b-0">
<Skeleton circle width={24} height={24} />
<p className="text-body5 font-medium flex-grow"><Skeleton width={'80%'} />
<Skeleton width={`${random(30, 65)}%`} /></p>

View File

@@ -0,0 +1,29 @@
import dayjs from "dayjs";
import { Link } from "react-router-dom";
import Button from "src/Components/Button/Button";
import { Author } from "src/features/Posts/types";
import Avatar from "src/features/Profiles/Components/Avatar/Avatar";
import { trimText } from "src/utils/helperFunctions";
import { createRoute } from "src/utils/routing";
import Skeleton from 'react-loading-skeleton';
export default function AuthorCardSkeleton() {
return (
<div className="bg-white p-16 border-2 border-gray-200 rounded-12">
<div className='flex gap-8'>
<Skeleton circle width={48} height={48} />
<div className="overflow-hidden">
<p className={`'text-body4' text-black font-medium overflow-hidden text-ellipsis whitespace-nowrap`}><Skeleton width={'15ch'} /></p>
<p className={`text-body6 text-gray-600`}><Skeleton width={'20ch'} /></p>
</div>
</div>
<Button
fullWidth
color="gray"
className="mt-16">
<div className="opacity-0">Hidden</div>
</Button>
</div>
)
}

View File

@@ -0,0 +1,35 @@
import Skeleton from "react-loading-skeleton";
import Badge from "src/Components/Badge/Badge";
import HeaderSkeleton from "src/features/Posts/Components/PostCard/Header/Header.Skeleton";
export default function PageContentSkeleton() {
return <div id="content" className="bg-white md:p-32 md:border-2 border-gray-200 rounded-16 relative">
<div className="flex flex-col gap-24 relative">
<h1 className="text-[42px] leading-[58px] font-bolder">
<Skeleton width={'min(80%,16ch)'} />
</h1>
<HeaderSkeleton />
<div className="flex flex-wrap gap-8">
{Array(3).fill(0).map(i => <Badge key={i} size='sm'>
<div className="opacity-0">hidden</div>
</Badge>)}
</div>
</div>
<div className={`mt-42 text-body4`}>
<Skeleton width={'100%'} />
<Skeleton width={'90%'} />
<Skeleton width={'92%'} />
<Skeleton width={'70%'} />
<div className="mt-32"></div>
<Skeleton width={'100%'} />
<Skeleton width={'92%'} />
<Skeleton width={'95%'} />
<Skeleton width={'40%'} />
</div>
</div>
}

View File

@@ -0,0 +1,38 @@
import { BsBookmark } from "react-icons/bs"
import { FiArrowLeft } from "react-icons/fi"
import { MdIosShare } from "react-icons/md"
import { useNavigate } from "react-router-dom"
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"
export default function PostActionsSkeleton() {
const actions = [
{
icon: MdIosShare,
value: '--'
},
];
const navigate = useNavigate();
return (
<div>
<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={() => navigate(-1)}
>
<FiArrowLeft className={"text-body1"} />
</button>
</div>
)
}

View File

@@ -31,7 +31,7 @@ export default function StoryPageContent({ story }: Props) {
{story.cover_image &&
<img src={story.cover_image}
className='w-full h-[120px] md:h-[240px] object-cover rounded-12 mb-16'
className='w-full object-cover rounded-12 md:rounded-16 mb-16'
alt="" />}
<div className="flex flex-col gap-24 relative">
{curUser?.id === story.author.id && <Menu

View File

@@ -0,0 +1,57 @@
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'
export default function PostDetailsPageSkeleton() {
const { navHeight } = useAppSelector((state) => ({
navHeight: state.ui.navHeight
}));
return (
<div
className={`page-container grid pt-16 w-full gap-32 ${styles.grid}`}
>
<aside id='actions' className='no-scrollbar'>
<div className="sticky"
style={{
top: `${navHeight + 16}px`,
maxHeight: `calc(100vh - ${navHeight}px - 16px)`,
}}>
<PostActionsSkeleton />
</div>
</aside>
<PageContentSkeleton />
<aside id='author' className='no-scrollbar min-w-0'>
<div className="flex flex-col gap-24"
style={{
top: `${navHeight + 16}px`,
maxHeight: `calc(100vh - ${navHeight}px - 16px)`,
overflowY: "scroll",
}}>
<AuthorCardSkeleton />
<TrendingCard />
</div>
</aside>
</div>
)
}

View File

@@ -1,16 +1,17 @@
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 { Post_Type, usePostDetailsQuery } from 'src/graphql'
import { capitalize } from 'src/utils/helperFunctions'
import { useAppSelector, } from 'src/utils/hooks'
import { CommentsSection } from '../../Components/Comments'
import ScrollToTop from 'src/utils/routing/scrollToTop'
import TrendingCard from '../../Components/TrendingCard/TrendingCard'
import AuthorCard from './Components/AuthorCard/AuthorCard'
import PageContent from './Components/PageContent/PageContent'
import PostActions from './Components/PostActions/PostActions'
import PostDetailsPageSkeleton from './PostDetailsPage.skeleton'
import styles from './styles.module.scss'
@@ -32,7 +33,7 @@ export default function PostDetailsPage() {
}));
if (postDetailsQuery.loading)
return <LoadingPage />
return <PostDetailsPageSkeleton />
const post = postDetailsQuery.data?.getPostById;
@@ -45,6 +46,7 @@ export default function PostDetailsPage() {
<title>{post.title}</title>
<meta property="og:title" content={post.title} />
</Helmet>
<ScrollToTop />
<div
className={`page-container grid pt-16 w-full gap-32 ${styles.grid}`}
>

View File

@@ -39,7 +39,7 @@
}
@include gt-lg {
grid-template-columns: auto 1fr calc(min(30%, 326px));
grid-template-columns: 116px 1fr calc(min(30%, 326px));
grid-template-areas:
"actions content author"
". comments ."

View File

@@ -0,0 +1,38 @@
import * as React from 'react';
import LoadingPage from 'src/Components/LoadingPage/LoadingPage';
export type FallbackType = NonNullable<React.ReactNode> | null;
export interface FallbackContextType {
updateFallback: (fallback: FallbackType) => void;
}
export const FallbackContext = React.createContext<FallbackContextType>({
updateFallback: () => { },
});
interface FabllbackProviderProps {
}
export const FallbackProvider: React.FC<React.PropsWithChildren<FabllbackProviderProps>> = ({
children,
}) => {
const [fallback, setFallback] = React.useState<FallbackType>(null);
const updateFallback = React.useCallback((fallback: FallbackType) => {
setFallback(() => <>
<LoadingPage />
{fallback}
</>);
}, []);
const renderChildren = React.useMemo(() => {
return children;
}, [children]);
return (
<FallbackContext.Provider value={{ updateFallback }}>
<React.Suspense fallback={fallback}>{renderChildren}</React.Suspense>
</FallbackContext.Provider>
);
};

View File

@@ -0,0 +1,21 @@
import * as React from 'react';
import { usePage } from './usePage';
interface LoadablePagePageProps { }
const LoadablePage: React.FC<React.PropsWithChildren<LoadablePagePageProps>> = ({ children }) => {
const { onLoad } = usePage();
const render = React.useMemo(() => {
return <>{children}</>;
}, [children]);
React.useEffect(() => {
onLoad(render);
}, [onLoad, render]);
return render;
};
export default LoadablePage;

View File

@@ -1,9 +1,12 @@
import { Outlet, } from 'react-router-dom';
import Navbar from "src/Components/Navbar/Navbar";
import { FallbackProvider } from '../FallbackProvider';
export const NavbarLayout = () => {
return <>
<Navbar />
<Outlet />
<FallbackProvider>
<Outlet />
</FallbackProvider>
</>
};

View File

@@ -1,8 +1,8 @@
import { Suspense } from "react";
import LoadingPage from "src/Components/LoadingPage/LoadingPage";
import LoadablePage from "./Page";
export const Loadable = (Component: any, Loading = LoadingPage) => (props: any) => (
<Suspense fallback={<Loading />}>
<LoadablePage>
<Component {...props} />
</Suspense>
</LoadablePage>
);

View File

@@ -0,0 +1,12 @@
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
export default function ScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
}

View File

@@ -0,0 +1,16 @@
import * as React from 'react';
import { FallbackContext, FallbackType } from './FallbackProvider';
export const usePage = () => {
const { updateFallback } = React.useContext(FallbackContext);
const onLoad = React.useCallback(
(component: FallbackType | undefined) => {
if (component === undefined) component = null;
updateFallback(component);
},
[updateFallback]
);
return { onLoad };
};