@@ -36,14 +36,12 @@ export default function CreatePostPage() {
+
{postType === 'story' && <>
-
+ {/*
Write a Story
-
-
+
*/}
+
>}
{postType === 'bounty' && <>
diff --git a/src/features/Posts/pages/CreatePostPage/CreateStoryPage/CreateStoryPage.tsx b/src/features/Posts/pages/CreatePostPage/CreateStoryPage/CreateStoryPage.tsx
new file mode 100644
index 0000000..65361eb
--- /dev/null
+++ b/src/features/Posts/pages/CreatePostPage/CreateStoryPage/CreateStoryPage.tsx
@@ -0,0 +1,109 @@
+
+import { yupResolver } from "@hookform/resolvers/yup";
+import { useRef, useState } from "react";
+import { FormProvider, NestedValue, Resolver, useForm } from "react-hook-form";
+import { Post_Type } from "src/graphql";
+import { StorageService } from "src/services";
+import { useAppSelector } from "src/utils/hooks";
+import { Override } from "src/utils/interfaces";
+import * as yup from "yup";
+import DraftsContainer from "../Components/DraftsContainer/DraftsContainer";
+import ErrorsContainer from "../Components/ErrorsContainer/ErrorsContainer";
+import StoryForm from "../Components/StoryForm/StoryForm";
+import styles from './styles.module.scss'
+
+const FileSchema = yup.lazy((value: string | File[]) => {
+ switch (typeof value) {
+ case 'object':
+ return yup.mixed()
+ .test("fileSize", "File Size is too large", file => file.size <= 5242880)
+ .test("fileType", "Unsupported File Format, only png/jpg/jpeg images are allowed",
+ (file: File) =>
+ ["image/jpeg", "image/png", "image/jpg"].includes(file.type))
+ case 'string':
+ return yup.string().url();
+ default:
+ return yup.mixed()
+ }
+})
+
+const schema = yup.object({
+ title: yup.string().trim().required().min(10, 'Story title must be 2+ words'),
+ tags: yup.array().required().min(1, 'Add at least one tag'),
+ body: yup.string().required().min(50, 'Post must contain at least 10+ words'),
+ cover_image: yup.array().of(FileSchema as any)
+
+}).required();
+
+
+export interface IStoryFormInputs {
+ id: number | null
+ title: string
+ tags: NestedValue<{ title: string }[]>
+ cover_image: NestedValue | NestedValue
+ body: string
+ is_published: boolean | null
+}
+
+
+
+export type CreateStoryType = Override
+
+const storageService = new StorageService('story-edit');
+
+
+export default function CreateStoryPage() {
+
+
+ const { story } = useAppSelector(state => ({
+ story: state.staging.story || storageService.get()
+ }))
+
+ const formMethods = useForm({
+ resolver: yupResolver(schema) as Resolver,
+ shouldFocusError: false,
+ defaultValues: {
+ id: story?.id ?? null,
+ title: story?.title ?? '',
+ cover_image: story?.cover_image ?? [],
+ tags: story?.tags ?? [],
+ body: story?.body ?? '',
+ is_published: story?.is_published ?? false,
+ },
+ });
+
+
+
+ const errorsContainerRef = useRef(null!);
+ const [formKey, setFormKey] = useState(1)
+
+ const resetForm = () => setFormKey(v => v + 1)
+
+
+ return (
+
+
+
+
+ resetForm()}
+ onValidationError={() => errorsContainerRef.current.scrollIntoView({ behavior: 'smooth', block: "center" })}
+ />
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/features/Posts/pages/CreatePostPage/CreateStoryPage/styles.module.scss b/src/features/Posts/pages/CreatePostPage/CreateStoryPage/styles.module.scss
new file mode 100644
index 0000000..fc18f19
--- /dev/null
+++ b/src/features/Posts/pages/CreatePostPage/CreateStoryPage/styles.module.scss
@@ -0,0 +1,36 @@
+@import "/src/styles/mixins/index.scss";
+
+.grid {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 32px;
+
+ & > * {
+ min-width: 0;
+ }
+
+ grid-template-areas:
+ "errors"
+ "form"
+ "drafts";
+
+ :global {
+ #errors {
+ grid-area: errors;
+ }
+ #form {
+ grid-area: form;
+ }
+ #drafts {
+ grid-area: drafts;
+ }
+ }
+
+ @include gt-xl {
+ grid-template-columns: 1fr calc(min(326px, 25%));
+ grid-template-areas:
+ "form errors"
+ "form drafts"
+ "form .";
+ }
+}
diff --git a/src/features/Posts/pages/FeedPage/FeedPage.tsx b/src/features/Posts/pages/FeedPage/FeedPage.tsx
index 6173ca4..4d62def 100644
--- a/src/features/Posts/pages/FeedPage/FeedPage.tsx
+++ b/src/features/Posts/pages/FeedPage/FeedPage.tsx
@@ -2,7 +2,7 @@
import { useUpdateEffect } from '@react-hookz/web'
import { useState } from 'react'
import { useFeedQuery } from 'src/graphql'
-import { useAppSelector, useInfiniteQuery } from 'src/utils/hooks'
+import { useAppSelector, useInfiniteQuery, usePreload } from 'src/utils/hooks'
import PostsList from '../../Components/PostsList/PostsList'
import TrendingCard from '../../Components/TrendingCard/TrendingCard'
import PopularTagsFilter, { FilterTag } from './PopularTagsFilter/PopularTagsFilter'
@@ -32,6 +32,8 @@ export default function FeedPage() {
const { fetchMore, isFetchingMore, variablesChanged } = useInfiniteQuery(feedQuery, 'getFeed')
useUpdateEffect(variablesChanged, [sortByFilter, tagFilter]);
+ usePreload('PostPage');
+
const { navHeight, isLoggedIn } = useAppSelector((state) => ({
navHeight: state.ui.navHeight,
isLoggedIn: Boolean(state.user.me),
@@ -79,14 +81,13 @@ export default function FeedPage() {
top: `${navHeight + 16}px`,
maxHeight: `calc(100vh - ${navHeight}px - 16px)`,
}}>
- {isLoggedIn &&
- }
+
)
diff --git a/src/features/Posts/pages/PostDetailsPage/Components/StoryPageContent/StoryPageContent.tsx b/src/features/Posts/pages/PostDetailsPage/Components/StoryPageContent/StoryPageContent.tsx
index 5f8c125..4750a72 100644
--- a/src/features/Posts/pages/PostDetailsPage/Components/StoryPageContent/StoryPageContent.tsx
+++ b/src/features/Posts/pages/PostDetailsPage/Components/StoryPageContent/StoryPageContent.tsx
@@ -49,7 +49,7 @@ export default function StoryPageContent({ story }: Props) {
Delete
}
-
{story.title}
+
{story.title}
{story.tags.length > 0 &&
{story.tags.map(tag =>
diff --git a/src/features/Posts/pages/PostDetailsPage/Components/StoryPageContent/useUpdateStory.tsx b/src/features/Posts/pages/PostDetailsPage/Components/StoryPageContent/useUpdateStory.tsx
index 4ee3d82..d920c1b 100644
--- a/src/features/Posts/pages/PostDetailsPage/Components/StoryPageContent/useUpdateStory.tsx
+++ b/src/features/Posts/pages/PostDetailsPage/Components/StoryPageContent/useUpdateStory.tsx
@@ -28,18 +28,18 @@ export const useUpdateStory = (story: Story) => {
const handleEdit = () => {
dispatch(stageStory({
...story,
- cover_image: [story.cover_image]
+ cover_image: story.cover_image ? [story.cover_image] : []
}))
navigate("/blog/create-post?type=story")
};
- const onInsertImage = useCallback(({ payload: { confirmed } }: typeof CONFIRM_DELETE_STORY) => {
+ const onConfirmDelete = useCallback(({ payload: { confirmed } }: typeof CONFIRM_DELETE_STORY) => {
if (confirmed)
deleteMutation()
}, [deleteMutation])
- useReduxEffect(onInsertImage, CONFIRM_DELETE_STORY.type);
+ useReduxEffect(onConfirmDelete, CONFIRM_DELETE_STORY.type);
const handleDelete = () => {
dispatch(openModal({
diff --git a/src/features/Posts/pages/PostDetailsPage/postDetails.graphql b/src/features/Posts/pages/PostDetailsPage/postDetails.graphql
index bf1157e..1b4ccfc 100644
--- a/src/features/Posts/pages/PostDetailsPage/postDetails.graphql
+++ b/src/features/Posts/pages/PostDetailsPage/postDetails.graphql
@@ -18,6 +18,7 @@ query PostDetails($id: Int!, $type: POST_TYPE!) {
votes_count
type
cover_image
+ is_published
comments_count
comments {
id
diff --git a/src/features/Posts/pages/PreviewPostPage/PreviewPostContent/PreviewPostContent.stories.tsx b/src/features/Posts/pages/PreviewPostPage/PreviewPostContent/PreviewPostContent.stories.tsx
deleted file mode 100644
index c1c1f29..0000000
--- a/src/features/Posts/pages/PreviewPostPage/PreviewPostContent/PreviewPostContent.stories.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { ComponentStory, ComponentMeta } from '@storybook/react';
-import { MOCK_DATA } from 'src/mocks/data';
-
-import PreviewPostContent from './PreviewPostContent';
-
-export default {
- title: 'Posts/Post Details Page/Components/Story Page Content',
- component: PreviewPostContent,
- argTypes: {
- backgroundColor: { control: 'color' },
- },
-} as ComponentMeta;
-
-
-const Template: ComponentStory = (args) =>
-
-export const Default = Template.bind({});
-Default.args = {
- post: MOCK_DATA.posts.stories[0]
-}
-
-
-
-
diff --git a/src/features/Posts/pages/PreviewPostPage/PreviewPostPage.tsx b/src/features/Posts/pages/PreviewPostPage/PreviewPostPage.tsx
deleted file mode 100644
index 9a9897b..0000000
--- a/src/features/Posts/pages/PreviewPostPage/PreviewPostPage.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-
-import { Helmet } from 'react-helmet'
-import { useParams } from 'react-router-dom'
-import NotFoundPage from 'src/features/Shared/pages/NotFoundPage/NotFoundPage'
-import { useAppSelector, } from 'src/utils/hooks'
-import TrendingCard from '../../Components/TrendingCard/TrendingCard'
-import AuthorCard from '../PostDetailsPage/Components/AuthorCard/AuthorCard'
-import PostActions from '../PostDetailsPage/Components/PostActions/PostActions'
-import styles from '../PostDetailsPage/styles.module.scss';
-import PreviewPostContent from './PreviewPostContent/PreviewPostContent'
-
-function isPost(type?: string): type is 'story' {
- return type === 'story'
- // || type === 'question' || type === 'bounty'
-}
-
-
-export default function PreviewPostPage() {
-
- const { type: _type } = useParams()
-
- const type = _type?.toLowerCase();
-
- const { post, author, navHeight } = useAppSelector(state => ({
- post: isPost(type) ? state.staging[type] : null,
- author: state.user.me,
- navHeight: state.ui.navHeight
- }))
-
-
-
- if (!post)
- return
-
- return (
- <>
-
- {post.title}
-
-
-
- >
- )
-}
diff --git a/src/features/Profiles/pages/ProfilePage/AboutCard/UpdateAboutForm.tsx b/src/features/Profiles/pages/ProfilePage/AboutCard/UpdateAboutForm.tsx
index 4f06cb1..17a2b8d 100644
--- a/src/features/Profiles/pages/ProfilePage/AboutCard/UpdateAboutForm.tsx
+++ b/src/features/Profiles/pages/ProfilePage/AboutCard/UpdateAboutForm.tsx
@@ -30,9 +30,10 @@ const schema: yup.SchemaOf = yup.object({
if (value) {
const [name, domain] = value.split("@");
const lnurl = `https://${domain}/.well-known/lnurlp/${name}`;
- await fetch(lnurl);
+ const res = await fetch(lnurl);
+ if (res.status === 200) return true;
}
- return true;
+ return false;
} catch (error) {
return false;
}
@@ -141,7 +142,7 @@ export default function UpdateAboutForm({ data, onClose }: Props) {
type='text'
className="input-text"
- placeholder="UK, London"
+ placeholder="Back-end Developer"
{...register("jobTitle")}
/>
diff --git a/src/graphql/index.tsx b/src/graphql/index.tsx
index 3259494..574cfde 100644
--- a/src/graphql/index.tsx
+++ b/src/graphql/index.tsx
@@ -40,15 +40,17 @@ export type Bounty = PostBase & {
applications: Array
;
author: Author;
body: Scalars['String'];
- cover_image: Scalars['String'];
+ cover_image: Maybe;
createdAt: Scalars['Date'];
deadline: Scalars['String'];
excerpt: Scalars['String'];
id: Scalars['Int'];
+ is_published: Maybe;
reward_amount: Scalars['Int'];
tags: Array;
title: Scalars['String'];
type: Scalars['String'];
+ updatedAt: Scalars['Date'];
votes_count: Scalars['Int'];
};
@@ -174,7 +176,9 @@ export type PostBase = {
createdAt: Scalars['Date'];
excerpt: Scalars['String'];
id: Scalars['Int'];
+ is_published: Maybe;
title: Scalars['String'];
+ updatedAt: Scalars['Date'];
votes_count: Scalars['Int'];
};
@@ -214,6 +218,7 @@ export type Query = {
getDonationsStats: DonationsStats;
getFeed: Array;
getLnurlDetailsForProject: LnurlDetails;
+ getMyDrafts: Array;
getPostById: Post;
getProject: Project;
getTrendingPosts: Array;
@@ -258,6 +263,11 @@ export type QueryGetLnurlDetailsForProjectArgs = {
};
+export type QueryGetMyDraftsArgs = {
+ type: Post_Type;
+};
+
+
export type QueryGetPostByIdArgs = {
id: Scalars['Int'];
type: Post_Type;
@@ -308,9 +318,11 @@ export type Question = PostBase & {
createdAt: Scalars['Date'];
excerpt: Scalars['String'];
id: Scalars['Int'];
+ is_published: Maybe;
tags: Array;
title: Scalars['String'];
type: Scalars['String'];
+ updatedAt: Scalars['Date'];
votes_count: Scalars['Int'];
};
@@ -320,26 +332,30 @@ export type Story = PostBase & {
body: Scalars['String'];
comments: Array;
comments_count: Scalars['Int'];
- cover_image: Scalars['String'];
+ cover_image: Maybe;
createdAt: Scalars['Date'];
excerpt: Scalars['String'];
id: Scalars['Int'];
+ is_published: Maybe;
tags: Array;
title: Scalars['String'];
type: Scalars['String'];
+ updatedAt: Scalars['Date'];
votes_count: Scalars['Int'];
};
export type StoryInputType = {
body: Scalars['String'];
- cover_image: Scalars['String'];
+ cover_image: InputMaybe;
id: InputMaybe;
+ is_published: InputMaybe;
tags: Array;
title: Scalars['String'];
};
export type Tag = {
__typename?: 'Tag';
+ description: Maybe;
icon: Maybe;
id: Scalars['Int'];
isOfficial: Maybe;
@@ -401,7 +417,7 @@ export type Vote = {
export type OfficialTagsQueryVariables = Exact<{ [key: string]: never; }>;
-export type OfficialTagsQuery = { __typename?: 'Query', officialTags: Array<{ __typename?: 'Tag', id: number, title: string, icon: string | null }> };
+export type OfficialTagsQuery = { __typename?: 'Query', officialTags: Array<{ __typename?: 'Tag', id: number, title: string, icon: string | null, description: string | null }> };
export type NavCategoriesQueryVariables = Exact<{ [key: string]: never; }>;
@@ -453,12 +469,19 @@ export type TrendingPostsQueryVariables = Exact<{ [key: string]: never; }>;
export type TrendingPostsQuery = { __typename?: 'Query', getTrendingPosts: Array<{ __typename?: 'Bounty', id: number, title: string, author: { __typename?: 'Author', id: number, avatar: string } } | { __typename?: 'Question', id: number, title: string, author: { __typename?: 'Author', id: number, avatar: string } } | { __typename?: 'Story', id: number, title: string, author: { __typename?: 'Author', id: number, avatar: string } }> };
+export type GetMyDraftsQueryVariables = Exact<{
+ type: Post_Type;
+}>;
+
+
+export type GetMyDraftsQuery = { __typename?: 'Query', getMyDrafts: Array<{ __typename?: 'Bounty', id: number, title: string, updatedAt: any } | { __typename?: 'Question', id: number, title: string, updatedAt: any } | { __typename?: 'Story', id: number, title: string, updatedAt: any }> };
+
export type CreateStoryMutationVariables = Exact<{
data: InputMaybe;
}>;
-export type CreateStoryMutation = { __typename?: 'Mutation', createStory: { __typename?: 'Story', id: number, title: string, createdAt: any, body: string, votes_count: number, type: string, cover_image: string, comments_count: number, tags: Array<{ __typename?: 'Tag', id: number, title: string }> } | null };
+export type CreateStoryMutation = { __typename?: 'Mutation', createStory: { __typename?: 'Story', id: number, title: string, createdAt: any, body: string, votes_count: number, is_published: boolean | null, type: string, cover_image: string | null, comments_count: number, tags: Array<{ __typename?: 'Tag', id: number, title: string }> } | null };
export type DeleteStoryMutationVariables = Exact<{
deleteStoryId: Scalars['Int'];
@@ -480,7 +503,7 @@ export type FeedQueryVariables = Exact<{
}>;
-export type FeedQuery = { __typename?: 'Query', getFeed: Array<{ __typename?: 'Bounty', id: number, title: string, createdAt: any, excerpt: string, votes_count: number, type: string, cover_image: string, deadline: string, reward_amount: number, applicants_count: number, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }> } | { __typename?: 'Question', id: number, title: string, createdAt: any, excerpt: string, votes_count: number, type: string, answers_count: number, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, comments: Array<{ __typename?: 'PostComment', id: number, createdAt: any, body: string, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any } }> } | { __typename?: 'Story', id: number, title: string, createdAt: any, excerpt: string, votes_count: number, type: string, cover_image: string, comments_count: number, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }> }> };
+export type FeedQuery = { __typename?: 'Query', getFeed: Array<{ __typename?: 'Bounty', id: number, title: string, createdAt: any, excerpt: string, votes_count: number, type: string, cover_image: string | null, deadline: string, reward_amount: number, applicants_count: number, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }> } | { __typename?: 'Question', id: number, title: string, createdAt: any, excerpt: string, votes_count: number, type: string, answers_count: number, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, comments: Array<{ __typename?: 'PostComment', id: number, createdAt: any, body: string, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any } }> } | { __typename?: 'Story', id: number, title: string, createdAt: any, excerpt: string, votes_count: number, type: string, cover_image: string | null, comments_count: number, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }> }> };
export type PostDetailsQueryVariables = Exact<{
id: Scalars['Int'];
@@ -488,7 +511,7 @@ export type PostDetailsQueryVariables = Exact<{
}>;
-export type PostDetailsQuery = { __typename?: 'Query', getPostById: { __typename?: 'Bounty', id: number, title: string, createdAt: any, body: string, votes_count: number, type: string, cover_image: string, deadline: string, reward_amount: number, applicants_count: number, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, applications: Array<{ __typename?: 'BountyApplication', id: number, date: string, workplan: string, author: { __typename?: 'Author', id: number, name: string, avatar: string } }> } | { __typename?: 'Question', id: number, title: string, createdAt: any, body: string, votes_count: number, type: string, answers_count: number, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, comments: Array<{ __typename?: 'PostComment', id: number, createdAt: any, body: string, votes_count: number, parentId: number | null, author: { __typename?: 'Author', id: number, name: string, avatar: string } }> } | { __typename?: 'Story', id: number, title: string, createdAt: any, body: string, votes_count: number, type: string, cover_image: string, comments_count: number, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, comments: Array<{ __typename?: 'PostComment', id: number, createdAt: any, body: string, votes_count: number, parentId: number | null, author: { __typename?: 'Author', id: number, name: string, avatar: string } }> } };
+export type PostDetailsQuery = { __typename?: 'Query', getPostById: { __typename?: 'Bounty', id: number, title: string, createdAt: any, body: string, votes_count: number, type: string, cover_image: string | null, deadline: string, reward_amount: number, applicants_count: number, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, applications: Array<{ __typename?: 'BountyApplication', id: number, date: string, workplan: string, author: { __typename?: 'Author', id: number, name: string, avatar: string } }> } | { __typename?: 'Question', id: number, title: string, createdAt: any, body: string, votes_count: number, type: string, answers_count: number, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, comments: Array<{ __typename?: 'PostComment', id: number, createdAt: any, body: string, votes_count: number, parentId: number | null, author: { __typename?: 'Author', id: number, name: string, avatar: string } }> } | { __typename?: 'Story', id: number, title: string, createdAt: any, body: string, votes_count: number, type: string, cover_image: string | null, is_published: boolean | null, comments_count: number, author: { __typename?: 'Author', id: number, name: string, avatar: string, join_date: any }, tags: Array<{ __typename?: 'Tag', id: number, title: string }>, comments: Array<{ __typename?: 'PostComment', id: number, createdAt: any, body: string, votes_count: number, parentId: number | null, author: { __typename?: 'Author', id: number, name: string, avatar: string } }> } };
export type ProfileQueryVariables = Exact<{
profileId: Scalars['Int'];
@@ -557,6 +580,7 @@ export const OfficialTagsDocument = gql`
id
title
icon
+ description
}
}
`;
@@ -916,6 +940,55 @@ export function useTrendingPostsLazyQuery(baseOptions?: Apollo.LazyQueryHookOpti
export type TrendingPostsQueryHookResult = ReturnType;
export type TrendingPostsLazyQueryHookResult = ReturnType;
export type TrendingPostsQueryResult = Apollo.QueryResult;
+export const GetMyDraftsDocument = gql`
+ query GetMyDrafts($type: POST_TYPE!) {
+ getMyDrafts(type: $type) {
+ ... on Story {
+ id
+ title
+ updatedAt
+ }
+ ... on Bounty {
+ id
+ title
+ updatedAt
+ }
+ ... on Question {
+ id
+ title
+ updatedAt
+ }
+ }
+}
+ `;
+
+/**
+ * __useGetMyDraftsQuery__
+ *
+ * To run a query within a React component, call `useGetMyDraftsQuery` and pass it any options that fit your needs.
+ * When your component renders, `useGetMyDraftsQuery` 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 } = useGetMyDraftsQuery({
+ * variables: {
+ * type: // value for 'type'
+ * },
+ * });
+ */
+export function useGetMyDraftsQuery(baseOptions: Apollo.QueryHookOptions) {
+ const options = {...defaultOptions, ...baseOptions}
+ return Apollo.useQuery(GetMyDraftsDocument, options);
+ }
+export function useGetMyDraftsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) {
+ const options = {...defaultOptions, ...baseOptions}
+ return Apollo.useLazyQuery(GetMyDraftsDocument, options);
+ }
+export type GetMyDraftsQueryHookResult = ReturnType;
+export type GetMyDraftsLazyQueryHookResult = ReturnType;
+export type GetMyDraftsQueryResult = Apollo.QueryResult;
export const CreateStoryDocument = gql`
mutation createStory($data: StoryInputType) {
createStory(data: $data) {
@@ -928,6 +1001,7 @@ export const CreateStoryDocument = gql`
title
}
votes_count
+ is_published
type
cover_image
comments_count
@@ -1159,6 +1233,7 @@ export const PostDetailsDocument = gql`
votes_count
type
cover_image
+ is_published
comments_count
comments {
id
diff --git a/src/mocks/data/tags.ts b/src/mocks/data/tags.ts
index c40aeb4..a75bb15 100644
--- a/src/mocks/data/tags.ts
+++ b/src/mocks/data/tags.ts
@@ -5,27 +5,37 @@ export const tags = [
{
id: 1,
title: 'Bitcoin',
+ description: 'Lorem ipsum dolor sit amort consectetur, adipisicing elit. Possimus officia sit numquam nobis iure atque ab sunt nihil voluptatibus',
icon: "🅱",
+ isOfficial: true,
},
{
id: 2,
title: 'Lightning',
+ description: 'Lorem ipsum dolor sit amort consectetur, adipisicing elit. Possimus officia sit numquam nobis iure atque ab sunt nihil voluptatibus',
icon: "âš¡",
+ isOfficial: true,
},
{
id: 3,
title: 'Webln',
+ description: 'Lorem ipsum dolor sit amort consectetur, adipisicing elit. Possimus officia sit numquam nobis iure atque ab sunt nihil voluptatibus',
icon: "🔗",
+ isOfficial: true,
},
{
id: 4,
title: 'Gaming',
+ description: 'Lorem ipsum dolor sit amort consectetur, adipisicing elit. Possimus officia sit numquam nobis iure atque ab sunt nihil voluptatibus',
icon: "🎮",
+ isOfficial: true,
},
{
id: 5,
title: 'Design',
- icon: '🎨'
+ description: 'Lorem ipsum dolor sit amort consectetur, adipisicing elit. Possimus officia sit numquam nobis iure atque ab sunt nihil voluptatibus',
+ icon: '🎨',
+ isOfficial: true,
}
].map(i => ({ __typename: "Tag", ...i })) as Tag[]
\ No newline at end of file
diff --git a/src/redux/features/staging.slice.ts b/src/redux/features/staging.slice.ts
index 15e8a1e..c2008e0 100644
--- a/src/redux/features/staging.slice.ts
+++ b/src/redux/features/staging.slice.ts
@@ -1,5 +1,5 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
-import { CreateStoryType } from "src/features/Posts/pages/CreatePostPage/Components/StoryForm/StoryForm";
+import { CreateStoryType } from "src/features/Posts/pages/CreatePostPage/CreateStoryPage/CreateStoryPage";
interface StoreState {
story: CreateStoryType | null
diff --git a/src/services/index.ts b/src/services/index.ts
index 8a6ce53..5bd6a97 100644
--- a/src/services/index.ts
+++ b/src/services/index.ts
@@ -1,5 +1,3 @@
-import Wallet_Service from './wallet.service'
-
-export {
- Wallet_Service
-}
\ No newline at end of file
+export { default as Wallet_Service } from './wallet.service'
+export * from './storage.service'
+export * from './notifications.service'
diff --git a/src/services/storage.service.ts b/src/services/storage.service.ts
new file mode 100644
index 0000000..5a70c54
--- /dev/null
+++ b/src/services/storage.service.ts
@@ -0,0 +1,24 @@
+
+export class StorageService {
+ key: string;
+
+ constructor(key: string) {
+ this.key = key;
+ }
+
+ set(newValue: T) {
+ localStorage.setItem(this.key, JSON.stringify(newValue));
+ }
+
+ get() {
+ const str = localStorage.getItem(this.key);
+ if (!str)
+ return null;
+
+ return JSON.parse(str) as T;
+ }
+
+ clear() {
+ localStorage.removeItem(this.key)
+ }
+}
diff --git a/src/styles/mixins/_media_queries.scss b/src/styles/mixins/_media_queries.scss
index e449ea6..10363b9 100644
--- a/src/styles/mixins/_media_queries.scss
+++ b/src/styles/mixins/_media_queries.scss
@@ -91,3 +91,9 @@ $screen-xl-max: 50000px;
@content;
}
}
+
+@mixin gt-xl {
+ @media screen and (min-width: #{$screen-xl-min}) {
+ @content;
+ }
+}
diff --git a/src/styles/mixins/_post_body.scss b/src/styles/mixins/_post_body.scss
index 93eeb7b..9ebac9f 100644
--- a/src/styles/mixins/_post_body.scss
+++ b/src/styles/mixins/_post_body.scss
@@ -8,6 +8,10 @@
}
}
+ :where(h1, h2, h3, h4, h5, h6, a, ul, ol, pre, a, p, iframe, ul, ol, blockquote) {
+ margin-bottom: 16px;
+ }
+
h1 {
font-size: 36px;
line-height: 50px;
@@ -66,4 +70,35 @@
aspect-ratio: 16/9;
margin: 36px auto;
}
+
+ ul,
+ ol {
+ margin-inline-start: 32px;
+
+ p {
+ margin-bottom: 0;
+ }
+ li {
+ margin-bottom: 8px;
+ }
+ }
+
+ ul li {
+ list-style: disc;
+ }
+ ol li {
+ list-style: decimal;
+ }
+
+ blockquote {
+ border-left: 3px solid #dee2e6;
+ margin-left: 0;
+ margin-right: 0;
+ padding-left: 10px;
+ font-style: italic;
+
+ p {
+ color: #777;
+ }
+ }
}
diff --git a/src/utils/helperFunctions.tsx b/src/utils/helperFunctions.tsx
index 8729817..aaa8d18 100644
--- a/src/utils/helperFunctions.tsx
+++ b/src/utils/helperFunctions.tsx
@@ -1,3 +1,4 @@
+import dayjs from "dayjs";
import React, { ComponentProps, ComponentType, Suspense } from "react";
import { RotatingLines } from "react-loader-spinner";
import { isNullOrUndefined } from "remirror";
@@ -102,4 +103,22 @@ export function capitalize(s?: string) {
return s && s[0].toUpperCase() + s.slice(1);
}
-export const withHttp = (url: string) => !/^https?:\/\//i.test(url) ? `http://${url}` : url;
\ No newline at end of file
+export const withHttp = (url: string) => !/^https?:\/\//i.test(url) ? `http://${url}` : url;
+
+export function getPropertyFromUnknown(obj: unknown, prop: string | number | symbol): Value | null {
+ if (typeof obj === 'object' && obj !== null && prop in obj)
+ return (obj as any)[prop as any] as Value;
+ return null
+}
+
+export function getDateDifference(date: string) {
+ const now = dayjs();
+ const mins = now.diff(date, 'minute');
+ if (mins < 60) return mins + 'm';
+ const hrs = now.diff(date, 'hour');
+ if (hrs < 24) return hrs + 'h';
+ const days = now.diff(date, 'day');
+ if (days < 30) return days + 'd';
+ const months = now.diff(date, 'month');
+ return months + 'mo'
+}
\ No newline at end of file
diff --git a/src/utils/hooks/index.ts b/src/utils/hooks/index.ts
index f3f318b..af04c8d 100644
--- a/src/utils/hooks/index.ts
+++ b/src/utils/hooks/index.ts
@@ -9,3 +9,4 @@ export * from "./useVote";
export * from './useWindowSize'
export * from './useMediaQuery'
export * from './useCurrentSection'
+export * from './usePreload'
diff --git a/src/utils/hooks/usePreload.ts b/src/utils/hooks/usePreload.ts
new file mode 100644
index 0000000..681a764
--- /dev/null
+++ b/src/utils/hooks/usePreload.ts
@@ -0,0 +1,13 @@
+import { useEffect } from 'react';
+
+const Components = {
+ PostPage: () => import('../../features/Posts/pages/PostDetailsPage/PostDetailsPage'),
+}
+
+type ComponentToLoad = keyof typeof Components;
+
+export const usePreload = (componentToLoad: ComponentToLoad) => {
+ useEffect(() => {
+ Components[componentToLoad]()
+ }, [componentToLoad])
+}
\ No newline at end of file
diff --git a/src/utils/interfaces/misc.interfaces.ts b/src/utils/interfaces/misc.interfaces.ts
index 5239bca..f3fc015 100644
--- a/src/utils/interfaces/misc.interfaces.ts
+++ b/src/utils/interfaces/misc.interfaces.ts
@@ -1,8 +1,6 @@
+import { Tag as ApiTag } from "src/graphql";
-export type Tag = {
- id: number
- title: string
-}
+export type Tag = ApiTag;
export type ListComponentProps = {
diff --git a/src/utils/routing/index.ts b/src/utils/routing/index.ts
index 7256eea..de328f8 100644
--- a/src/utils/routing/index.ts
+++ b/src/utils/routing/index.ts
@@ -1 +1,2 @@
-export * from './routes'
\ No newline at end of file
+export * from './routes'
+export * from './loadable'
\ No newline at end of file
diff --git a/src/utils/routing/layouts/NavbarLayout.tsx b/src/utils/routing/layouts/NavbarLayout.tsx
new file mode 100644
index 0000000..cc63068
--- /dev/null
+++ b/src/utils/routing/layouts/NavbarLayout.tsx
@@ -0,0 +1,9 @@
+import { Outlet, } from 'react-router-dom';
+import Navbar from "src/Components/Navbar/Navbar";
+
+export const NavbarLayout = () => {
+ return <>
+
+
+ >
+};
diff --git a/src/utils/routing/layouts/index.ts b/src/utils/routing/layouts/index.ts
new file mode 100644
index 0000000..6a608f4
--- /dev/null
+++ b/src/utils/routing/layouts/index.ts
@@ -0,0 +1,2 @@
+
+export * from './NavbarLayout'
\ No newline at end of file
diff --git a/src/utils/routing/loadable.tsx b/src/utils/routing/loadable.tsx
new file mode 100644
index 0000000..d8eabce
--- /dev/null
+++ b/src/utils/routing/loadable.tsx
@@ -0,0 +1,8 @@
+import { Suspense } from "react";
+import LoadingPage from "src/Components/LoadingPage/LoadingPage";
+
+export const Loadable = (Component: any, Loading = LoadingPage) => (props: any) => (
+ }>
+
+
+);