fix: fixing pre-launch issues

- refactor the tags topics stuff (apis/mocks/components)
- update TagsInput
- Change how feed page looks on tags filtering
This commit is contained in:
MTG2000
2022-06-14 17:40:47 +03:00
parent 3bb0100d36
commit 9a8dc9431b
51 changed files with 529 additions and 617 deletions

View File

@@ -34,7 +34,6 @@ export interface NexusGenInputs {
id?: number | null; // Int
tags: string[]; // [String!]!
title: string; // String!
topicId: number; // Int!
}
UpdateProfileInput: { // input type
avatar?: string | null; // String
@@ -174,12 +173,9 @@ export interface NexusGenObjects {
votes_count: number; // Int!
}
Tag: { // root type
icon?: string | null; // String
id: number; // Int!
title: string; // String!
}
Topic: { // root type
icon: string; // String!
id: number; // Int!
isOfficial?: boolean | null; // Boolean
title: string; // String!
}
User: { // root type
@@ -288,8 +284,8 @@ export interface NexusGenFieldTypes {
id: number; // Int!
location: string; // String!
start_date: NexusGenScalars['Date']; // Date!
tags: NexusGenRootTypes['Tag'][]; // [Tag!]!
title: string; // String!
topics: NexusGenRootTypes['Topic'][]; // [Topic!]!
website: string; // String!
}
LnurlDetails: { // field return type
@@ -333,7 +329,6 @@ export interface NexusGenFieldTypes {
Query: { // field return type
allCategories: NexusGenRootTypes['Category'][]; // [Category!]!
allProjects: NexusGenRootTypes['Project'][]; // [Project!]!
allTopics: NexusGenRootTypes['Topic'][]; // [Topic!]!
getAllHackathons: NexusGenRootTypes['Hackathon'][]; // [Hackathon!]!
getCategory: NexusGenRootTypes['Category']; // Category!
getDonationsStats: NexusGenRootTypes['DonationsStats']; // DonationsStats!
@@ -345,7 +340,8 @@ export interface NexusGenFieldTypes {
hottestProjects: NexusGenRootTypes['Project'][]; // [Project!]!
me: NexusGenRootTypes['User'] | null; // User
newProjects: NexusGenRootTypes['Project'][]; // [Project!]!
popularTopics: NexusGenRootTypes['Topic'][]; // [Topic!]!
officialTags: NexusGenRootTypes['Tag'][]; // [Tag!]!
popularTags: NexusGenRootTypes['Tag'][]; // [Tag!]!
profile: NexusGenRootTypes['User'] | null; // User
projectsByCategory: NexusGenRootTypes['Project'][]; // [Project!]!
searchProjects: NexusGenRootTypes['Project'][]; // [Project!]!
@@ -374,17 +370,13 @@ export interface NexusGenFieldTypes {
id: number; // Int!
tags: NexusGenRootTypes['Tag'][]; // [Tag!]!
title: string; // String!
topic: NexusGenRootTypes['Topic']; // Topic!
type: string; // String!
votes_count: number; // Int!
}
Tag: { // field return type
icon: string | null; // String
id: number; // Int!
title: string; // String!
}
Topic: { // field return type
icon: string; // String!
id: number; // Int!
isOfficial: boolean | null; // Boolean
title: string; // String!
}
User: { // field return type
@@ -489,8 +481,8 @@ export interface NexusGenFieldTypeNames {
id: 'Int'
location: 'String'
start_date: 'Date'
tags: 'Tag'
title: 'String'
topics: 'Topic'
website: 'String'
}
LnurlDetails: { // field return type name
@@ -534,7 +526,6 @@ export interface NexusGenFieldTypeNames {
Query: { // field return type name
allCategories: 'Category'
allProjects: 'Project'
allTopics: 'Topic'
getAllHackathons: 'Hackathon'
getCategory: 'Category'
getDonationsStats: 'DonationsStats'
@@ -546,7 +537,8 @@ export interface NexusGenFieldTypeNames {
hottestProjects: 'Project'
me: 'User'
newProjects: 'Project'
popularTopics: 'Topic'
officialTags: 'Tag'
popularTags: 'Tag'
profile: 'User'
projectsByCategory: 'Project'
searchProjects: 'Project'
@@ -575,17 +567,13 @@ export interface NexusGenFieldTypeNames {
id: 'Int'
tags: 'Tag'
title: 'String'
topic: 'Topic'
type: 'String'
votes_count: 'Int'
}
Tag: { // field return type name
id: 'Int'
title: 'String'
}
Topic: { // field return type name
icon: 'String'
id: 'Int'
isOfficial: 'Boolean'
title: 'String'
}
User: { // field return type name
@@ -658,7 +646,7 @@ export interface NexusGenArgTypes {
}
getAllHackathons: { // args
sortBy?: string | null; // String
topic?: number | null; // Int
tag?: number | null; // Int
}
getCategory: { // args
id: number; // Int!
@@ -666,8 +654,8 @@ export interface NexusGenArgTypes {
getFeed: { // args
skip?: number | null; // Int
sortBy?: string | null; // String
tag?: number | null; // Int
take: number | null; // Int
topic?: number | null; // Int
}
getLnurlDetailsForProject: { // args
project_id: number; // Int!

View File

@@ -78,8 +78,8 @@ type Hackathon {
id: Int!
location: String!
start_date: Date!
tags: [Tag!]!
title: String!
topics: [Topic!]!
website: String!
}
@@ -145,11 +145,10 @@ type Project {
type Query {
allCategories: [Category!]!
allProjects(skip: Int = 0, take: Int = 50): [Project!]!
allTopics: [Topic!]!
getAllHackathons(sortBy: String, topic: Int): [Hackathon!]!
getAllHackathons(sortBy: String, tag: Int): [Hackathon!]!
getCategory(id: Int!): Category!
getDonationsStats: DonationsStats!
getFeed(skip: Int = 0, sortBy: String, take: Int = 10, topic: Int = 0): [Post!]!
getFeed(skip: Int = 0, sortBy: String, tag: Int = 0, take: Int = 10): [Post!]!
getLnurlDetailsForProject(project_id: Int!): LnurlDetails!
getPostById(id: Int!, type: POST_TYPE!): Post!
getProject(id: Int!): Project!
@@ -157,7 +156,8 @@ type Query {
hottestProjects(skip: Int = 0, take: Int = 50): [Project!]!
me: User
newProjects(skip: Int = 0, take: Int = 50): [Project!]!
popularTopics: [Topic!]!
officialTags: [Tag!]!
popularTags: [Tag!]!
profile(id: Int!): User
projectsByCategory(category_id: Int!, skip: Int = 0, take: Int = 10): [Project!]!
searchProjects(search: String!, skip: Int = 0, take: Int = 50): [Project!]!
@@ -188,7 +188,6 @@ type Story implements PostBase {
id: Int!
tags: [Tag!]!
title: String!
topic: Topic!
type: String!
votes_count: Int!
}
@@ -199,17 +198,12 @@ input StoryInputType {
id: Int
tags: [String!]!
title: String!
topicId: Int!
}
type Tag {
icon: String
id: Int!
title: String!
}
type Topic {
icon: String!
id: Int!
isOfficial: Boolean
title: String!
}

View File

@@ -20,10 +20,10 @@ const Hackathon = objectType({
t.nonNull.date('end_date');
t.nonNull.string('location');
t.nonNull.string('website');
t.nonNull.list.nonNull.field('topics', {
type: "Topic",
t.nonNull.list.nonNull.field('tags', {
type: "Tag",
resolve: (parent) => {
return prisma.hackathon.findUnique({ where: { id: parent.id } }).topics();
return prisma.hackathon.findUnique({ where: { id: parent.id } }).tags();
}
});
}
@@ -36,10 +36,10 @@ const getAllHackathons = extendType({
type: "Hackathon",
args: {
sortBy: stringArg(),
topic: intArg(),
tag: intArg(),
},
resolve(_, args) {
const { sortBy, topic } = args;
const { sortBy, tag } = args;
return prisma.hackathon.findMany({
where: {
...(sortBy === 'Upcoming' && {
@@ -57,10 +57,12 @@ const getAllHackathons = extendType({
}
}),
...(topic && {
topics: {
...(tag && {
tags: {
some: {
id: topic
id: tag
}
}
})

View File

@@ -6,8 +6,10 @@ const post = require('./post')
const users = require('./users')
const hackathon = require('./hackathon')
const donation = require('./donation')
const tag = require('./tag')
module.exports = {
...tag,
...scalars,
...category,
...project,

View File

@@ -32,15 +32,6 @@ const asQuestionType = asType('Question')
const asBountyType = asType('Bounty')
const Topic = objectType({
name: 'Topic',
definition(t) {
t.nonNull.int('id');
t.nonNull.string('title');
t.nonNull.string('icon');
}
})
const Author = objectType({
name: 'Author',
definition(t) {
@@ -52,39 +43,7 @@ const Author = objectType({
})
const allTopics = extendType({
type: "Query",
definition(t) {
t.nonNull.list.nonNull.field('allTopics', {
type: "Topic",
resolve: () => {
return prisma.topic.findMany({
});
}
})
}
})
const popularTopics = extendType({
type: "Query",
definition(t) {
t.nonNull.list.nonNull.field('popularTopics', {
type: "Topic",
resolve: () => {
return prisma.topic.findMany({
take: 6,
orderBy: {
stories: {
_count: 'desc'
}
},
});
}
})
}
})
const PostBase = interfaceType({
name: 'PostBase',
@@ -132,14 +91,6 @@ const Story = objectType({
return post._count.comments;
}
});
t.nonNull.field('topic', {
type: "Topic",
resolve: parent => {
return prisma.story.findUnique({
where: { id: parent.id }
}).topic()
}
})
t.nonNull.field('author', {
type: "Author",
resolve: (parent) =>
@@ -158,7 +109,6 @@ const StoryInputType = inputObjectType({
t.nonNull.string('body');
t.nonNull.string('cover_image');
t.nonNull.list.nonNull.string('tags');
t.nonNull.int('topicId');
}
})
const createStory = extendType({
@@ -168,7 +118,7 @@ const createStory = extendType({
type: 'Story',
args: { data: StoryInputType },
async resolve(_root, args, ctx) {
const { id, title, body, cover_image, tags, topicId } = args.data;
const { id, title, body, cover_image, tags } = args.data;
const user = await getUserByPubKey(ctx.userPubKey);
// Do some validation
@@ -215,11 +165,6 @@ const createStory = extendType({
}
})
},
topic: {
connect: {
id: topicId
}
},
}
})
@@ -244,11 +189,6 @@ const createStory = extendType({
}
})
},
topic: {
connect: {
id: topicId
}
},
user: {
connect: {
id: user.id,
@@ -398,11 +338,11 @@ const getFeed = extendType({
args: {
...paginationArgs({ take: 10 }),
sortBy: stringArg(), // all, popular, trending, newest
topic: intArg({
tag: intArg({
default: 0
})
},
resolve(_, { take, skip, topic, sortBy, }) {
resolve(_, { take, skip, tag, sortBy, }) {
let orderBy = { createdAt: "desc" };
@@ -415,7 +355,13 @@ const getFeed = extendType({
return prisma.story.findMany({
orderBy: orderBy,
where: {
topic_id: topic ? topic : undefined,
...(tag && {
tags: {
some: {
id: tag
}
},
})
},
skip,
take,
@@ -485,7 +431,6 @@ module.exports = {
// Types
POST_TYPE,
Author,
Topic,
PostBase,
BountyApplication,
Bounty,
@@ -495,8 +440,6 @@ module.exports = {
PostComment,
Post,
// Queries
allTopics,
popularTopics,
getFeed,
getPostById,
getTrendingPosts,

View File

@@ -64,19 +64,7 @@ const Award = objectType({
}
})
const Tag = objectType({
name: 'Tag',
definition(t) {
t.nonNull.int('id');
t.nonNull.string('title');
// t.nonNull.list.nonNull.field('project', {
// type: "Project",
// resolve: (parent) => {
// return prisma.tag.findUnique({ where: { id: parent.id } }).project();
// }
// })
}
})
const getProject = extendType({
@@ -255,7 +243,6 @@ module.exports = {
// Types
Project,
Award,
Tag,
// Queries
getProject,
allProjects,

View File

@@ -0,0 +1,59 @@
const { objectType, extendType } = require("nexus");
const { prisma } = require('../../../prisma');
const Tag = objectType({
name: 'Tag',
definition(t) {
t.nonNull.int('id');
t.nonNull.string('title');
t.string('icon');
t.boolean('isOfficial');
}
});
const officialTags = extendType({
type: "Query",
definition(t) {
t.nonNull.list.nonNull.field('officialTags', {
type: "Tag",
resolve: () => {
return prisma.tag.findMany({
where: {
isOfficial: true
}
});
}
})
}
})
const popularTags = extendType({
type: "Query",
definition(t) {
t.nonNull.list.nonNull.field('popularTags', {
type: "Tag",
resolve: () => {
return prisma.tag.findMany({
where: {
isOfficial: true
},
take: 8,
orderBy: {
stories: {
_count: 'desc'
}
},
});
}
})
}
})
module.exports = {
// Types
Tag,
// Queries
popularTags,
officialTags,
}

View File

@@ -0,0 +1,46 @@
/*
Warnings:
- You are about to drop the `Topic` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `_HackathonToTopic` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropForeignKey
ALTER TABLE "Question" DROP CONSTRAINT "Question_topic_id_fkey";
-- DropForeignKey
ALTER TABLE "Story" DROP CONSTRAINT "Story_topic_id_fkey";
-- DropForeignKey
ALTER TABLE "_HackathonToTopic" DROP CONSTRAINT "_HackathonToTopic_A_fkey";
-- DropForeignKey
ALTER TABLE "_HackathonToTopic" DROP CONSTRAINT "_HackathonToTopic_B_fkey";
-- AlterTable
ALTER TABLE "Tag" ADD COLUMN "icon" TEXT,
ADD COLUMN "isOfficial" BOOLEAN NOT NULL DEFAULT false;
-- DropTable
DROP TABLE "Topic";
-- DropTable
DROP TABLE "_HackathonToTopic";
-- CreateTable
CREATE TABLE "_HackathonToTag" (
"A" INTEGER NOT NULL,
"B" INTEGER NOT NULL
);
-- CreateIndex
CREATE UNIQUE INDEX "_HackathonToTag_AB_unique" ON "_HackathonToTag"("A", "B");
-- CreateIndex
CREATE INDEX "_HackathonToTag_B_index" ON "_HackathonToTag"("B");
-- AddForeignKey
ALTER TABLE "_HackathonToTag" ADD FOREIGN KEY ("A") REFERENCES "Hackathon"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_HackathonToTag" ADD FOREIGN KEY ("B") REFERENCES "Tag"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,12 @@
/*
Warnings:
- You are about to drop the column `topic_id` on the `Question` table. All the data in the column will be lost.
- You are about to drop the column `topic_id` on the `Story` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE "Question" DROP COLUMN "topic_id";
-- AlterTable
ALTER TABLE "Story" DROP COLUMN "topic_id";

View File

@@ -12,12 +12,15 @@ generator client {
// -----------------
model Tag {
id Int @id @default(autoincrement())
title String @unique
id Int @id @default(autoincrement())
title String @unique
icon String?
isOfficial Boolean @default(false)
project Project[]
stories Story[]
questions Question[]
project Project[]
stories Story[]
questions Question[]
hackathons Hackathon[]
}
model Vote {
@@ -119,8 +122,6 @@ model Story {
cover_image String
votes_count Int @default(0)
topic Topic @relation(fields: [topic_id], references: [id])
topic_id Int
tags Tag[]
@@ -139,8 +140,6 @@ model Question {
excerpt String
votes_count Int @default(0)
topic Topic @relation(fields: [topic_id], references: [id])
topic_id Int
tags Tag[]
@@ -150,16 +149,6 @@ model Question {
comments PostComment[] @relation("QuestionComment")
}
model Topic {
id Int @id @default(autoincrement())
title String @unique
icon String
stories Story[]
questions Question[]
hackathons Hackathon[]
}
model PostComment {
id Int @id @default(autoincrement())
body String
@@ -196,7 +185,7 @@ model Hackathon {
website String
votes_count Int @default(0)
topics Topic[]
tags Tag[]
}
// -----------------

View File

@@ -1,6 +1,4 @@
import React, { useMemo } from 'react'
import { MdClose } from 'react-icons/md';
import IconButton from 'src/Components/IconButton/IconButton';
import { useMemo } from 'react'
import FileThumbnail from './FileThumbnail';
interface Props {

View File

@@ -1,10 +1,20 @@
import { motion } from "framer-motion";
import { useState } from "react";
import { useController } from "react-hook-form";
import Badge from "src/Components/Badge/Badge";
import { Tag as ApiTag } from "src/utils/interfaces";
import CreatableSelect from 'react-select/creatable';
import { OnChangeValue, StylesConfig, components, OptionProps } from "react-select";
import { useOfficialTagsQuery } from "src/graphql";
type Tag = Pick<ApiTag, 'title'>
interface Option {
readonly label: string;
readonly value: string;
readonly icon: string | null
}
type Tag = {
title: string,
icon: string | null
}
interface Props {
classes?: {
@@ -17,6 +27,46 @@ interface Props {
}
const transformer = {
tagToOption: (tag: Tag): Option => ({ label: tag.title, value: tag.title, icon: tag.icon }),
optionToTag: (o: Option): Tag => ({ title: o.value, icon: null })
}
const OptionComponent = (props: OptionProps<Option>) => {
return (
<div>
<components.Option {...props} className='flex items-start'>
<span className={`rounded-8 w-40 h-40 text-center py-8`}>
{props.data.icon}
</span>
<span className="self-center px-16">
{props.data.label}
</span>
</components.Option>
</div>
);
};
const colourStyles: StylesConfig = {
control: (styles, state) => ({
...styles,
padding: '1px 4px',
borderRadius: 8,
}),
indicatorSeparator: (styles, state) => ({
...styles,
display: "none"
}),
input: (styles, state) => ({
...styles,
" input": {
boxShadow: 'none !important'
},
}),
}
export default function TagsInput({
classes,
@@ -24,20 +74,20 @@ export default function TagsInput({
max = 5,
...props }: Props) {
const [inputText, setInputText] = useState("");
const officalTags = useOfficialTagsQuery();
const { field: { value, onChange, onBlur } } = useController({
name: props.name ?? "tags",
control: props.control,
})
const handleSubmit = () => {
onChange([...value, { title: inputText }]);
setInputText('');
const handleChange = (newValue: OnChangeValue<Option, true>,) => {
onChange([...value, ...newValue.map(transformer.optionToTag)]);
onBlur();
}
const handleRemove = (idx: number) => {
onChange((value as Tag[]).filter((_, i) => idx !== i))
onBlur();
@@ -45,35 +95,37 @@ export default function TagsInput({
const isDisabled = value.length >= max;
const tagsOptions = (officalTags.data?.officialTags ?? []).filter(t => !value.some((v: Tag) => v.title === t.title)).map(transformer.tagToOption);
return (
<div className={`${classes?.container}`}>
<div className="input-wrapper relative">
<input
disabled={isDisabled}
type='text'
className={`input-text inline-block
${isDisabled && 'opacity-50'}
${classes?.input}`}
placeholder={placeholder}
value={inputText}
onChange={(e) => setInputText(e.target.value)}
onKeyPress={e => {
if (e.key === 'Enter' && inputText.trim().length > 1) { e.preventDefault(); handleSubmit() }
}}
/>
{inputText.length > 2 && <motion.span
initial={{ scale: 1, y: "-50%" }}
animate={{ scale: 1.05 }}
transition={{
repeat: Infinity,
repeatType: 'mirror',
duration: .9
}}
className="text-gray-500 absolute top-1/2 right-16">
Enter to Insert
</motion.span>}
</div>
<CreatableSelect
isLoading={officalTags.loading}
options={tagsOptions}
isMulti
isDisabled={isDisabled}
placeholder={placeholder}
isClearable
value={[]}
onChange={handleChange as any}
onBlur={onBlur}
components={{
Option: OptionComponent,
MultiValue: () => <></>
}}
styles={colourStyles as any}
theme={(theme) => ({
...theme,
borderRadius: 8,
colors: {
...theme.colors,
primary: 'var(--primary)',
},
})}
/>
<div className="flex mt-16 gap-8 flex-wrap">
{(value as Tag[]).map((tag, idx) => <Badge color="gray" key={tag.title} onRemove={() => handleRemove(idx)} >{tag.title}</Badge>)}
</div>

View File

@@ -0,0 +1,7 @@
query OfficialTags {
officialTags {
id
title
icon
}
}

View File

@@ -1,16 +1,11 @@
import NavMobile from "./NavMobile";
import { MdComment, MdHomeFilled, MdLocalFireDepartment } from "react-icons/md";
import { IoExtensionPuzzle } from "react-icons/io5";
import { useCallback, useEffect, useState } from "react";
import { useAppDispatch, useAppSelector, useMediaQuery } from "src/utils/hooks";
import { openModal } from "src/redux/features/modals.slice";
import { useEffect, } from "react";
import { useAppDispatch, useMediaQuery } from "src/utils/hooks";
import { setNavHeight } from "src/redux/features/ui.slice";
import NavDesktop from "./NavDesktop";
import { MEDIA_QUERIES } from "src/utils/theme/media_queries";
import { IoMdTrophy } from "react-icons/io";
import { useLocation } from "react-router-dom";
import { useMeQuery } from "src/graphql";
import { setUser } from "src/redux/features/user.slice";
export const navLinks = [

View File

@@ -1,6 +1,5 @@
import { openModal } from 'src/redux/features/modals.slice';
import { openProject } from 'src/redux/features/project.slice';
import { useAppDispatch } from 'src/utils/hooks';
import { ProjectSearchItem } from '../Search';
import SearchProjectCard from '../SearchProjectCard/SearchProjectCard';

View File

@@ -1,5 +1,5 @@
import { PropsWithChildren } from "react";
import { Navigate, Outlet } from "react-router-dom";
import { Navigate, } from "react-router-dom";
import { useAppSelector } from "src/utils/hooks";
interface Props {

View File

@@ -4,9 +4,18 @@ import Button from "src/Components/Button/Button"
import dayjs from "dayjs";
import advancedFormat from 'dayjs/plugin/advancedFormat'
import { trimText } from "src/utils/helperFunctions";
import { Override } from "src/utils/interfaces";
import { Tag } from "src/graphql";
dayjs.extend(advancedFormat)
export type HackathonCardType = Hackathon;
export type HackathonCardType = Override<Hackathon,
{
tags: Pick<Tag,
| 'id'
| 'title'
| 'icon'>[]
}
>;
interface Props {
hackathon: HackathonCardType
@@ -32,7 +41,7 @@ export default function HackathonCard({ hackathon }: Props) {
</p>
</div>
<div className="mt-16 flex flex-wrap gap-8">
{hackathon.topics.map(topic => <div key={topic.id} className="p-8 bg-gray-50 rounded-8 text-body5">{topic.icon} {topic.title}</div>)}
{hackathon.tags.map(tag => <div key={tag.id} className="p-8 bg-gray-50 rounded-8 text-body5">{tag.icon} {tag.title}</div>)}
</div>
<div className="mt-auto"></div>
<Button href={hackathon.website} newTab color="gray" fullWidth className="mt-16">

View File

@@ -1,20 +0,0 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import TopicsFilter from './TopicsFilter';
export default {
title: 'Hackathons/Components/Filters/Topics',
component: TopicsFilter,
argTypes: {
backgroundColor: { control: 'color' },
},
} as ComponentMeta<typeof TopicsFilter>;
const Template: ComponentStory<typeof TopicsFilter> = (args) => <div className="max-w-[326px]"><TopicsFilter {...args as any} ></TopicsFilter></div>
export const Default = Template.bind({});
Default.args = {
}

View File

@@ -1,85 +0,0 @@
import { useMediaQuery } from 'src/utils/hooks';
import React, { useState } from 'react'
import Skeleton from 'react-loading-skeleton';
import Slider from 'src/Components/Slider/Slider';
import { useAllTopicsQuery, usePopularTopicsQuery } from 'src/graphql';
import { MEDIA_QUERIES } from 'src/utils/theme';
interface Props {
filterChanged?: (newFilter: number | null) => void
}
export default function PopularTopicsFilter({ filterChanged }: Props) {
const [selected, setSelected] = useState<number | null>(null);
const topicsQuery = useAllTopicsQuery();
const filterClicked = (_newValue: number) => {
const newValue = selected !== _newValue ? _newValue : null;
setSelected(newValue);
filterChanged?.(newValue);
}
const isMdScreen = useMediaQuery(MEDIA_QUERIES.isMedium)
return (
<div className='overflow-hidden'>
{isMdScreen ?
<div className='bg-white border-2 rounded-12 p-16 overflow-hidden'>
<p className="text-body2 font-bolder text-black mb-16">Topics</p>
<ul className=' flex flex-col gap-16'>
{topicsQuery.loading ?
Array(3).fill(0).map((_, idx) => <li
key={idx}
className={`flex items-start rounded-8 font-bold`}
>
<span className='bg-gray-50 rounded-8 w-40 h-40 text-center py-8'> </span>
<span className="self-center px-16"><Skeleton width={'7ch'} />
</span>
</li>
)
:
topicsQuery.data?.allTopics.map((topic, idx) => <li
key={topic.id}
className={`flex items-start rounded-8 cursor-pointer font-bold
active:scale-95 transition-transform
${topic.id === selected ? 'bg-gray-200' : 'hover:bg-gray-100'}`}
onClick={() => filterClicked(topic.id)}
role='button'
>
<span className={`${topic.id !== selected && 'bg-gray-50'} rounded-8 w-40 h-40 text-center py-8`}>{topic.icon}</span>
<span className="self-center px-16">
{topic.title}
</span>
</li>)}
</ul>
</div>
:
<>
{
topicsQuery.loading ?
<ul className="flex gap-8 ">
{Array(4).fill(0).map((_, idx) => <div key={idx} className="py-12 px-16 bg-gray-100 rounded-8 text-body5"><span className="opacity-0">Category</span></div>)}
</ul>
:
<Slider>
{topicsQuery.data?.allTopics.map(topic =>
<div
key={topic.id}
onClick={() => filterClicked(topic.id)}
className={`${topic.id === selected ? 'bg-gray-200' : "bg-gray-100"} py-12 px-16 rounded-8 text-body5`}
>{topic.icon} {topic.title}</div>)}
</Slider>
}
</>
}
</div>
)
}

View File

@@ -1,7 +0,0 @@
query allTopics {
allTopics {
id
title
icon
}
}

View File

@@ -12,12 +12,12 @@ import { Helmet } from 'react-helmet'
export default function HackathonsPage() {
const [sortByFilter, setSortByFilter] = useState<string | null>('Upcoming')
const [topicsFilter, setTopicsFilter] = useState<number | null>(null)
const [tagFilter, setTagFilter] = useState<number | null>(null)
const hackathonsQuery = useGetHackathonsQuery({
variables: {
sortBy: sortByFilter,
topic: Number(topicsFilter)
tag: Number(tagFilter)
},
})
const { navHeight } = useAppSelector((state) => ({

View File

@@ -1,5 +1,5 @@
query getHackathons($sortBy: String, $topic: Int) {
getAllHackathons(sortBy: $sortBy, topic: $topic) {
query getHackathons($sortBy: String, $tag: Int) {
getAllHackathons(sortBy: $sortBy, tag: $tag) {
id
title
description
@@ -8,7 +8,7 @@ query getHackathons($sortBy: String, $topic: Int) {
end_date
location
website
topics {
tags {
id
title
icon

View File

@@ -6,6 +6,7 @@ import Badge from "src/Components/Badge/Badge"
import Button from "src/Components/Button/Button"
import { Link } from "react-router-dom"
import VoteButton from "src/Components/VoteButton/VoteButton"
import { Tag } from "src/graphql"
export type BountyCardType = Pick<Bounty,
| 'id'
@@ -17,9 +18,10 @@ export type BountyCardType = Pick<Bounty,
| 'excerpt'
| 'votes_count'
| "reward_amount"
| "tags"
| "applicants_count"
>;
> & {
tags: Array<Pick<Tag, 'id' | "title">>
};
interface Props {
bounty: BountyCardType
}

View File

@@ -6,6 +6,7 @@ import Badge from "src/Components/Badge/Badge"
import { Link } from "react-router-dom"
import { trimText } from "src/utils/helperFunctions"
import VoteButton from "src/Components/VoteButton/VoteButton"
import { Tag } from "src/graphql"
export type QuestionCardType = Pick<Question,
| 'id'
@@ -15,7 +16,6 @@ export type QuestionCardType = Pick<Question,
| 'author'
| 'excerpt'
| 'votes_count'
| "tags"
| "answers_count"
> & {
comments: Array<Pick<Question['comments'][number],
@@ -24,6 +24,7 @@ export type QuestionCardType = Pick<Question,
| 'body'
| 'createdAt'
>>
tags: Array<Pick<Tag, 'id' | "title">>
};
interface Props {
question: QuestionCardType

View File

@@ -4,7 +4,8 @@ import { BiComment } from 'react-icons/bi'
import { Link } from "react-router-dom"
import VoteButton from "src/Components/VoteButton/VoteButton"
import { useVote } from "src/utils/hooks"
import { Vote_Item_Type } from 'src/graphql';
import { Tag, Vote_Item_Type } from 'src/graphql';
import Badge from "src/Components/Badge/Badge"
export type StoryCardType = Pick<Story,
| 'id'
@@ -16,7 +17,9 @@ export type StoryCardType = Pick<Story,
| 'excerpt'
| 'votes_count'
| 'comments_count'
>;
> & {
tags: Array<Pick<Tag, 'id' | "title">>
};
interface Props {
story: StoryCardType
@@ -37,6 +40,11 @@ export default function StoryCard({ story }: Props) {
<h2 className="text-h5 font-bolder mt-16">{story.title}</h2>
</Link>
<p className="text-body4 text-gray-600 mt-8">{story.excerpt}...</p>
<div className="flex gap-8 mt-8">
{story.tags.map(tag => <Badge key={tag.id} size='sm'>
{tag.title}
</Badge>)}
</div>
<hr className="my-16 bg-gray-200" />
<div className="flex gap-24 items-center">

View File

@@ -6,9 +6,8 @@ import FilesInput from "src/Components/Inputs/FilesInput/FilesInput";
import TagsInput from "src/Components/Inputs/TagsInput/TagsInput";
import * as yup from "yup";
import ContentEditor from "../ContentEditor/ContentEditor";
import { Topic, useCreateStoryMutation } from 'src/graphql'
import { useCreateStoryMutation } from 'src/graphql'
import { useNavigate } from 'react-router-dom'
import TopicsInput from '../TopicsInput/TopicsInput'
import { useAppDispatch, useAppSelector } from 'src/utils/hooks';
import { stageStory } from 'src/redux/features/staging.slice'
import { Override } from 'src/utils/interfaces';
@@ -32,7 +31,6 @@ const FileSchema = yup.lazy((value: string | File[]) => {
const schema = yup.object({
title: yup.string().required().min(10),
topic: yup.object().nullable().required(),
tags: yup.array().required().min(1),
body: yup.string().required().min(50, 'you have to write at least 10 words'),
cover_image: yup.array().of(FileSchema as any)
@@ -43,7 +41,6 @@ const schema = yup.object({
interface IFormInputs {
id: number | null
title: string
topic: NestedValue<Topic> | null
tags: NestedValue<{ title: string }[]>
cover_image: NestedValue<File[]> | NestedValue<string[]>
body: string
@@ -52,7 +49,6 @@ interface IFormInputs {
export type CreateStoryType = Override<IFormInputs, {
topic: Topic | null
tags: { title: string }[]
cover_image: File[] | string[]
}>
@@ -70,7 +66,6 @@ export default function StoryForm() {
defaultValues: {
id: story?.id ?? null,
title: story?.title ?? '',
topic: story?.topic ?? null,
cover_image: story?.cover_image ?? [],
tags: story?.tags ?? [],
body: story?.body ?? '',
@@ -113,7 +108,6 @@ export default function StoryForm() {
body: data.body,
tags: data.tags.map(t => t.title),
cover_image: data.cover_image[0] as string,
topicId: 1,
},
}
})
@@ -160,30 +154,11 @@ export default function StoryForm() {
{errors.title.message}
</p>}
<p className="text-body5 mt-16">
Topic
</p>
<div className="mt-16">
<Controller
control={control}
name="topic"
render={({ field: { onChange, value, onBlur } }) => (
<TopicsInput
value={value}
onChange={onChange}
onBlur={onBlur}
/>
)}
/>
</div>
{errors.topic && <p className="input-error">
{errors.topic.message}
</p>}
<p className="text-body5 mt-16">
Tags
</p>
<TagsInput
placeholder="Enter your tag and click enter. You can add multiple tags to your post"
placeholder="Search from popular tags or add your own by clicking Enter."
classes={{ container: 'mt-8' }}
/>
{errors.tags && <p className="input-error">

View File

@@ -8,11 +8,6 @@ mutation createStory($data: StoryInputType) {
id
title
}
topic {
id
title
icon
}
votes_count
type
cover_image

View File

@@ -1,31 +0,0 @@
import React from 'react'
import AutoComplete from 'src/Components/Inputs/Autocomplete/Autocomplete'
import { Topic, useAllTopicsQuery } from 'src/graphql'
import { ControlledStateHandler } from 'src/utils/interfaces';
type Props<IsMulti extends boolean> = ControlledStateHandler<Topic, IsMulti>
export default function TopicsInput<IsMulti extends boolean = false>(props: Props<IsMulti>) {
const topicsQuery = useAllTopicsQuery();
return (
<AutoComplete
isClearable
placeholder='Choose a topic'
options={topicsQuery.data?.allTopics!}
labelField='title'
valueField='id'
isMulti={props.isMulti}
isLoading={topicsQuery.loading}
value={props.value}
onChange={props.onChange}
onBlur={props.onBlur}
/>
)
}

View File

@@ -5,18 +5,19 @@ import { useFeedQuery } from 'src/graphql'
import { useAppSelector, useInfiniteQuery } from 'src/utils/hooks'
import PostsList from '../../Components/PostsList/PostsList'
import TrendingCard from '../../Components/TrendingCard/TrendingCard'
import PopularTopicsFilter from './PopularTopicsFilter/PopularTopicsFilter'
import PopularTagsFilter, { FilterTag } from './PopularTagsFilter/PopularTagsFilter'
import SortBy from './SortBy/SortBy'
import styles from './styles.module.scss'
import { Helmet } from "react-helmet";
import Button from 'src/Components/Button/Button'
import { FaDiscord } from 'react-icons/fa'
import { FiArrowRight } from 'react-icons/fi'
export default function FeedPage() {
const [sortByFilter, setSortByFilter] = useState<string | null>(null)
const [topicFilter, setTopicFilter] = useState<number | null>(null)
const [tagFilter, setTagFilter] = useState<FilterTag | null>(null)
const feedQuery = useFeedQuery({
@@ -24,11 +25,11 @@ export default function FeedPage() {
take: 10,
skip: 0,
sortBy: sortByFilter,
topic: topicFilter
tag: tagFilter?.id ?? null
},
})
const { fetchMore, isFetchingMore, variablesChanged } = useInfiniteQuery(feedQuery, 'getFeed')
useUpdateEffect(variablesChanged, [sortByFilter, topicFilter]);
useUpdateEffect(variablesChanged, [sortByFilter, tagFilter]);
const { navHeight, isLoggedIn } = useAppSelector((state) => ({
navHeight: state.ui.navHeight,
@@ -45,7 +46,19 @@ export default function FeedPage() {
<div
className={`page-container pt-16 w-full ${styles.grid}`}
>
<h1 id='title' className="text-h2 font-bolder">Stories 🏼</h1>
<div id="title">
{tagFilter && <p className="text-body6 text-gray-500 font-medium mb-8">
<span className='cursor-pointer' onClick={() => setTagFilter(null)}>Stories </span>
<FiArrowRight />
<span> {tagFilter.title}</span>
</p>}
<h1 className="text-h2 font-bolder">{
tagFilter ?
<>{tagFilter.icon} {tagFilter.title}</>
:
"Stories ✍🏼"
}</h1>
</div>
<div id="sort-by">
<SortBy
filterChanged={setSortByFilter}
@@ -71,12 +84,13 @@ export default function FeedPage() {
color='primary'
fullWidth
>
Create a story
Write a story
</Button>}
<div className="my-24"></div>
<div className="my-24"></div>
<PopularTopicsFilter
filterChanged={setTopicFilter}
<PopularTagsFilter
value={tagFilter}
onChange={setTagFilter as any}
/>
</div>

View File

@@ -0,0 +1,20 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import PopularTags from './PopularTagsFilter';
export default {
title: 'Posts/Feed Page/Components/Popular Topics Filter',
component: PopularTags,
argTypes: {
backgroundColor: { control: 'color' },
},
} as ComponentMeta<typeof PopularTags>;
const Template: ComponentStory<typeof PopularTags> = (args) => <div className="max-w-[326px]"><PopularTags {...args as any} ></PopularTags></div>
export const Default = Template.bind({});
Default.args = {
}

View File

@@ -1,38 +1,39 @@
import { useMediaQuery } from 'src/utils/hooks';
import React, { useState } from 'react'
import Skeleton from 'react-loading-skeleton';
import Slider from 'src/Components/Slider/Slider';
import { usePopularTopicsQuery } from 'src/graphql';
import { Tag, usePopularTagsQuery } from 'src/graphql';
import { MEDIA_QUERIES } from 'src/utils/theme';
export type FilterTag = Pick<Tag, 'id' | 'title' | "icon">
interface Props {
filterChanged?: (newFilter: number | null) => void
value: FilterTag | null,
onChange?: (newFilter: FilterTag | null) => void
}
export default function PopularTopicsFilter({ filterChanged }: Props) {
const [selected, setSelected] = useState<number | null>(null);
const topicsQuery = usePopularTopicsQuery();
export default function PopularTagsFilter({ value, onChange }: Props) {
const filterClicked = (_newValue: number) => {
const newValue = selected !== _newValue ? _newValue : null;
setSelected(newValue);
filterChanged?.(newValue);
const tagsQuery = usePopularTagsQuery();
const filterClicked = (_newValue: FilterTag) => {
const newValue = value?.id === _newValue.id ? null : _newValue
onChange?.(newValue);
}
const selectedId = value?.id
const isMdScreen = useMediaQuery(MEDIA_QUERIES.isMedium)
return (
<div className='overflow-hidden'>
{isMdScreen ?
<div className='bg-white border-2 border-gray-200 rounded-12 p-16'>
<p className="text-body2 font-bolder text-black mb-16">Explore Categories</p>
<p className="text-body2 font-bolder text-black mb-16">Popular Tags</p>
<ul className=' flex flex-col gap-16'>
{topicsQuery.loading ?
Array(3).fill(0).map((_, idx) => <li
{tagsQuery.loading ?
Array(5).fill(0).map((_, idx) => <li
key={idx}
className={`flex items-start rounded-8 font-bold`}
@@ -43,18 +44,18 @@ export default function PopularTopicsFilter({ filterChanged }: Props) {
</li>
)
:
topicsQuery.data?.popularTopics.map((topic, idx) => <li
key={topic.id}
tagsQuery.data?.popularTags.map((tag) => <li
key={tag.id}
className={`flex items-start rounded-8 cursor-pointer font-bold
active:scale-95 transition-transform
${topic.id === selected ? 'bg-gray-200' : 'hover:bg-gray-100'}
${tag.id === selectedId ? 'bg-gray-200' : 'hover:bg-gray-100'}
`}
role='button'
onClick={() => filterClicked(topic.id)}
onClick={() => filterClicked(tag)}
>
<span className={`${topic.id !== selected && 'bg-gray-50'} rounded-8 w-40 h-40 text-center py-8`}>{topic.icon}</span>
<span className={`${tag.id !== selectedId && 'bg-gray-50'} rounded-8 w-40 h-40 text-center py-8`}>{tag.icon}</span>
<span className="self-center px-16">
{topic.title}
{tag.title}
</span>
</li>)}
</ul>
@@ -62,18 +63,18 @@ export default function PopularTopicsFilter({ filterChanged }: Props) {
:
<>
{
topicsQuery.loading ?
tagsQuery.loading ?
<ul className="flex gap-8 ">
{Array(4).fill(0).map((_, idx) => <div key={idx} className="py-12 px-16 bg-gray-100 rounded-8 text-body5"><span className="opacity-0">Category</span></div>)}
</ul>
:
<Slider>
{topicsQuery.data?.popularTopics.map(topic =>
{tagsQuery.data?.popularTags.map(tag =>
<div
key={topic.id}
onClick={() => filterClicked(topic.id)}
className={`${topic.id === selected ? 'bg-gray-200' : "bg-gray-100"} py-12 px-16 rounded-8 text-body5`}
>{topic.icon} {topic.title}</div>)}
key={tag.id}
onClick={() => filterClicked(tag)}
className={`${tag.id === selectedId ? 'bg-gray-200' : "bg-gray-100"} py-12 px-16 rounded-8 text-body5`}
>{tag.icon} {tag.title}</div>)}
</Slider>
}
</>

View File

@@ -0,0 +1,7 @@
query PopularTags {
popularTags {
id
title
icon
}
}

View File

@@ -1,20 +0,0 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import PopularTopicsFilter from './PopularTopicsFilter';
export default {
title: 'Posts/Feed Page/Components/Popular Topics Filter',
component: PopularTopicsFilter,
argTypes: {
backgroundColor: { control: 'color' },
},
} as ComponentMeta<typeof PopularTopicsFilter>;
const Template: ComponentStory<typeof PopularTopicsFilter> = (args) => <div className="max-w-[326px]"><PopularTopicsFilter {...args as any} ></PopularTopicsFilter></div>
export const Default = Template.bind({});
Default.args = {
}

View File

@@ -1,7 +0,0 @@
query PopularTopics {
popularTopics {
id
title
icon
}
}

View File

@@ -1,5 +1,5 @@
query Feed($take: Int, $skip: Int, $sortBy: String, $topic: Int) {
getFeed(take: $take, skip: $skip, sortBy: $sortBy, topic: $topic) {
query Feed($take: Int, $skip: Int, $sortBy: String, $tag: Int) {
getFeed(take: $take, skip: $skip, sortBy: $sortBy, tag: $tag) {
... on Story {
id
title

View File

@@ -70,6 +70,7 @@
@media screen and (min-width: 768px) {
grid-template-columns: repeat(4, 1fr);
grid-template-rows: auto auto 1fr;
grid-template-areas:
"categories title title side"

View File

@@ -3,14 +3,11 @@ import { Story } from "src/features/Posts/types"
import { marked } from 'marked';
import styles from '../PageContent/styles.module.css'
import Badge from "src/Components/Badge/Badge";
import { BiComment } from "react-icons/bi";
import { RiFlashlightLine } from "react-icons/ri";
import { numberFormatter } from "src/utils/helperFunctions";
import IconButton from "src/Components/IconButton/IconButton";
import { BsThreeDotsVertical } from "react-icons/bs";
import { Menu, MenuButton, MenuItem } from "@szhsin/react-menu";
import { Menu, MenuItem } from "@szhsin/react-menu";
import { useAppSelector } from "src/utils/hooks";
import { useUpdateStory } from './useUpdateStory'
import { FaPen } from "react-icons/fa";
interface Props {
@@ -29,28 +26,29 @@ export default function StoryPageContent({ story }: Props) {
return (
<>
<div id="content" className="bg-white p-32 border-2 border-gray-200 rounded-16 relative">
{curUser?.id === story.author.id && <Menu
menuClassName='!p-8 !rounded-12'
menuButton={<IconButton className="absolute top-32 right-32"><BsThreeDotsVertical /></IconButton>}>
<MenuItem
onClick={handleEdit}
className='!p-16 font-medium flex gap-16 hover:bg-gray-100 !rounded-12'
>
Edit story
</MenuItem>
<MenuItem
onClick={handleDelete}
className='!p-16 font-medium flex gap-16 hover:bg-gray-100 !rounded-12'
>
Delete
</MenuItem>
</Menu>}
{story.cover_image &&
<img src={story.cover_image}
className='w-full h-[120px] md:h-[240px] object-cover rounded-12 mb-16'
alt="" />}
<div className="flex flex-col gap-24">
<div className="flex flex-col gap-24 relative">
{curUser?.id === story.author.id && <Menu
menuClassName='!p-8 !rounded-12'
menuButton={<IconButton className="absolute top-0 right-0 text-gray-400"><FaPen /></IconButton>}>
<MenuItem
onClick={handleEdit}
className='!p-16 font-medium flex gap-16 hover:bg-gray-100 !rounded-12'
>
Edit story
</MenuItem>
<MenuItem
onClick={handleDelete}
className='!p-16 font-medium flex gap-16 hover:bg-gray-100 !rounded-12'
>
Delete
</MenuItem>
</Menu>}
<h1 className="text-h2 font-bolder">{story.title}</h1>
<Header size="lg" showTimeAgo={false} author={story.author} date={story.createdAt} />
{story.tags.length > 0 && <div className="flex gap-8">

View File

@@ -15,11 +15,6 @@ query PostDetails($id: Int!, $type: POST_TYPE!) {
id
title
}
topic {
id
title
icon
}
votes_count
type
cover_image

View File

@@ -13,13 +13,28 @@ interface Props {
| 'author'
> & {
tags: Array<{ title: string }>
cover_image?: string | File
}
}
export default function PreviewPostContent({ post }: Props) {
let coverImg: string;
if (!post.cover_image)
coverImg = "";
else if (typeof post.cover_image === 'string')
coverImg = post.cover_image;
else
coverImg = URL.createObjectURL(post.cover_image);
return (
<>
<div id="content" className="bg-white p-32 border rounded-16">
{coverImg &&
<img src={coverImg}
className='w-full h-[120px] md:h-[240px] object-cover rounded-12 mb-16'
alt="" />}
<div className="flex flex-col gap-24">
<Header size="lg" showTimeAgo={false} author={post.author} date={post.createdAt} />
<h1 className="text-h2 font-bolder">{post.title}</h1>

View File

@@ -56,7 +56,7 @@ export default function PreviewPostPage() {
/>
</div>
</aside>
<PreviewPostContent post={{ ...post, createdAt: new Date().toISOString(), author: author! }} />
<PreviewPostContent post={{ ...post, createdAt: new Date().toISOString(), author: author!, cover_image: post.cover_image[0] }} />
<aside id='author' className='no-scrollbar min-w-0'>
<div className="flex flex-col gap-24"
style={{

View File

@@ -1,7 +1,6 @@
import { useQuery } from '@apollo/client';
import Badge from 'src/Components/Badge/Badge'
import Slider from 'src/Components/Slider/Slider'
import { Link, useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router-dom';
import { useAllCategoriesQuery } from 'src/graphql';
const colors = [

View File

@@ -1,12 +1,6 @@
import { ComponentProps } from "react";
import { lazyModal } from "src/utils/helperFunctions";
import { Id } from "src/utils/types/utils";
const x = () => import('./Claim_CopySignatureCard');
type ThenArg<T> = T extends PromiseLike<infer U> ? U : T
type y = Id<ThenArg<ReturnType<typeof x>>>
// type yy = ComponentProps<y['default']>
export const { LazyComponent: Claim_CopySignatureCard } = lazyModal(() => import('./Claim_CopySignatureCard'))
export const { LazyComponent: Claim_FundWithdrawCard } = lazyModal(() => import('./Claim_FundWithdrawCard'))
export const { LazyComponent: Claim_GenerateSignatureCard } = lazyModal(() => import('./Claim_GenerateSignatureCard'))

View File

@@ -98,8 +98,8 @@ export type Hackathon = {
id: Scalars['Int'];
location: Scalars['String'];
start_date: Scalars['Date'];
tags: Array<Tag>;
title: Scalars['String'];
topics: Array<Topic>;
website: Scalars['String'];
};
@@ -209,7 +209,6 @@ export type Query = {
__typename?: 'Query';
allCategories: Array<Category>;
allProjects: Array<Project>;
allTopics: Array<Topic>;
getAllHackathons: Array<Hackathon>;
getCategory: Category;
getDonationsStats: DonationsStats;
@@ -221,7 +220,8 @@ export type Query = {
hottestProjects: Array<Project>;
me: Maybe<User>;
newProjects: Array<Project>;
popularTopics: Array<Topic>;
officialTags: Array<Tag>;
popularTags: Array<Tag>;
profile: Maybe<User>;
projectsByCategory: Array<Project>;
searchProjects: Array<Project>;
@@ -236,7 +236,7 @@ export type QueryAllProjectsArgs = {
export type QueryGetAllHackathonsArgs = {
sortBy: InputMaybe<Scalars['String']>;
topic: InputMaybe<Scalars['Int']>;
tag: InputMaybe<Scalars['Int']>;
};
@@ -247,9 +247,9 @@ export type QueryGetCategoryArgs = {
export type QueryGetFeedArgs = {
skip?: InputMaybe<Scalars['Int']>;
sortBy?: InputMaybe<Scalars['String']>;
sortBy: InputMaybe<Scalars['String']>;
tag?: InputMaybe<Scalars['Int']>;
take?: InputMaybe<Scalars['Int']>;
topic?: InputMaybe<Scalars['Int']>;
};
@@ -326,7 +326,6 @@ export type Story = PostBase & {
id: Scalars['Int'];
tags: Array<Tag>;
title: Scalars['String'];
topic: Topic;
type: Scalars['String'];
votes_count: Scalars['Int'];
};
@@ -337,19 +336,13 @@ export type StoryInputType = {
id: InputMaybe<Scalars['Int']>;
tags: Array<Scalars['String']>;
title: Scalars['String'];
topicId: Scalars['Int'];
};
export type Tag = {
__typename?: 'Tag';
icon: Maybe<Scalars['String']>;
id: Scalars['Int'];
title: Scalars['String'];
};
export type Topic = {
__typename?: 'Topic';
icon: Scalars['String'];
id: Scalars['Int'];
isOfficial: Maybe<Scalars['Boolean']>;
title: Scalars['String'];
};
@@ -405,6 +398,11 @@ export type Vote = {
payment_request: Scalars['String'];
};
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 NavCategoriesQueryVariables = Exact<{ [key: string]: never; }>;
@@ -442,18 +440,13 @@ export type ConfirmDonationMutationVariables = Exact<{
export type ConfirmDonationMutation = { __typename?: 'Mutation', confirmDonation: { __typename?: 'Donation', id: number, amount: number, paid: boolean } };
export type AllTopicsQueryVariables = Exact<{ [key: string]: never; }>;
export type AllTopicsQuery = { __typename?: 'Query', allTopics: Array<{ __typename?: 'Topic', id: number, title: string, icon: string }> };
export type GetHackathonsQueryVariables = Exact<{
sortBy: InputMaybe<Scalars['String']>;
topic: InputMaybe<Scalars['Int']>;
tag: InputMaybe<Scalars['Int']>;
}>;
export type GetHackathonsQuery = { __typename?: 'Query', getAllHackathons: Array<{ __typename?: 'Hackathon', id: number, title: string, description: string, cover_image: string, start_date: any, end_date: any, location: string, website: string, topics: Array<{ __typename?: 'Topic', id: number, title: string, icon: string }> }> };
export type GetHackathonsQuery = { __typename?: 'Query', getAllHackathons: Array<{ __typename?: 'Hackathon', id: number, title: string, description: string, cover_image: string, start_date: any, end_date: any, location: string, website: string, tags: Array<{ __typename?: 'Tag', id: number, title: string, icon: string | null }> }> };
export type TrendingPostsQueryVariables = Exact<{ [key: string]: never; }>;
@@ -465,7 +458,7 @@ export type CreateStoryMutationVariables = Exact<{
}>;
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 }>, topic: { __typename?: 'Topic', id: number, title: string, icon: string } } | null };
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 DeleteStoryMutationVariables = Exact<{
deleteStoryId: Scalars['Int'];
@@ -474,16 +467,16 @@ export type DeleteStoryMutationVariables = Exact<{
export type DeleteStoryMutation = { __typename?: 'Mutation', deleteStory: { __typename?: 'Story', id: number } | null };
export type PopularTopicsQueryVariables = Exact<{ [key: string]: never; }>;
export type PopularTagsQueryVariables = Exact<{ [key: string]: never; }>;
export type PopularTopicsQuery = { __typename?: 'Query', popularTopics: Array<{ __typename?: 'Topic', id: number, title: string, icon: string }> };
export type PopularTagsQuery = { __typename?: 'Query', popularTags: Array<{ __typename?: 'Tag', id: number, title: string, icon: string | null }> };
export type FeedQueryVariables = Exact<{
take: InputMaybe<Scalars['Int']>;
skip: InputMaybe<Scalars['Int']>;
sortBy: InputMaybe<Scalars['String']>;
topic: InputMaybe<Scalars['Int']>;
tag: InputMaybe<Scalars['Int']>;
}>;
@@ -495,7 +488,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 }>, topic: { __typename?: 'Topic', id: number, title: string, icon: 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, 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 ProfileQueryVariables = Exact<{
profileId: Scalars['Int'];
@@ -558,6 +551,42 @@ export type ConfirmVoteMutationVariables = Exact<{
export type ConfirmVoteMutation = { __typename?: 'Mutation', confirmVote: { __typename?: 'Vote', id: number, amount_in_sat: number, payment_request: string, payment_hash: string, paid: boolean, item_type: Vote_Item_Type, item_id: number } };
export const OfficialTagsDocument = gql`
query OfficialTags {
officialTags {
id
title
icon
}
}
`;
/**
* __useOfficialTagsQuery__
*
* To run a query within a React component, call `useOfficialTagsQuery` and pass it any options that fit your needs.
* When your component renders, `useOfficialTagsQuery` 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 } = useOfficialTagsQuery({
* variables: {
* },
* });
*/
export function useOfficialTagsQuery(baseOptions?: Apollo.QueryHookOptions<OfficialTagsQuery, OfficialTagsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<OfficialTagsQuery, OfficialTagsQueryVariables>(OfficialTagsDocument, options);
}
export function useOfficialTagsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<OfficialTagsQuery, OfficialTagsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<OfficialTagsQuery, OfficialTagsQueryVariables>(OfficialTagsDocument, options);
}
export type OfficialTagsQueryHookResult = ReturnType<typeof useOfficialTagsQuery>;
export type OfficialTagsLazyQueryHookResult = ReturnType<typeof useOfficialTagsLazyQuery>;
export type OfficialTagsQueryResult = Apollo.QueryResult<OfficialTagsQuery, OfficialTagsQueryVariables>;
export const NavCategoriesDocument = gql`
query NavCategories {
allCategories {
@@ -782,45 +811,9 @@ export function useConfirmDonationMutation(baseOptions?: Apollo.MutationHookOpti
export type ConfirmDonationMutationHookResult = ReturnType<typeof useConfirmDonationMutation>;
export type ConfirmDonationMutationResult = Apollo.MutationResult<ConfirmDonationMutation>;
export type ConfirmDonationMutationOptions = Apollo.BaseMutationOptions<ConfirmDonationMutation, ConfirmDonationMutationVariables>;
export const AllTopicsDocument = gql`
query allTopics {
allTopics {
id
title
icon
}
}
`;
/**
* __useAllTopicsQuery__
*
* To run a query within a React component, call `useAllTopicsQuery` and pass it any options that fit your needs.
* When your component renders, `useAllTopicsQuery` 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 } = useAllTopicsQuery({
* variables: {
* },
* });
*/
export function useAllTopicsQuery(baseOptions?: Apollo.QueryHookOptions<AllTopicsQuery, AllTopicsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<AllTopicsQuery, AllTopicsQueryVariables>(AllTopicsDocument, options);
}
export function useAllTopicsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<AllTopicsQuery, AllTopicsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<AllTopicsQuery, AllTopicsQueryVariables>(AllTopicsDocument, options);
}
export type AllTopicsQueryHookResult = ReturnType<typeof useAllTopicsQuery>;
export type AllTopicsLazyQueryHookResult = ReturnType<typeof useAllTopicsLazyQuery>;
export type AllTopicsQueryResult = Apollo.QueryResult<AllTopicsQuery, AllTopicsQueryVariables>;
export const GetHackathonsDocument = gql`
query getHackathons($sortBy: String, $topic: Int) {
getAllHackathons(sortBy: $sortBy, topic: $topic) {
query getHackathons($sortBy: String, $tag: Int) {
getAllHackathons(sortBy: $sortBy, tag: $tag) {
id
title
description
@@ -829,7 +822,7 @@ export const GetHackathonsDocument = gql`
end_date
location
website
topics {
tags {
id
title
icon
@@ -851,7 +844,7 @@ export const GetHackathonsDocument = gql`
* const { data, loading, error } = useGetHackathonsQuery({
* variables: {
* sortBy: // value for 'sortBy'
* topic: // value for 'topic'
* tag: // value for 'tag'
* },
* });
*/
@@ -934,11 +927,6 @@ export const CreateStoryDocument = gql`
id
title
}
topic {
id
title
icon
}
votes_count
type
cover_image
@@ -1005,9 +993,9 @@ export function useDeleteStoryMutation(baseOptions?: Apollo.MutationHookOptions<
export type DeleteStoryMutationHookResult = ReturnType<typeof useDeleteStoryMutation>;
export type DeleteStoryMutationResult = Apollo.MutationResult<DeleteStoryMutation>;
export type DeleteStoryMutationOptions = Apollo.BaseMutationOptions<DeleteStoryMutation, DeleteStoryMutationVariables>;
export const PopularTopicsDocument = gql`
query PopularTopics {
popularTopics {
export const PopularTagsDocument = gql`
query PopularTags {
popularTags {
id
title
icon
@@ -1016,34 +1004,34 @@ export const PopularTopicsDocument = gql`
`;
/**
* __usePopularTopicsQuery__
* __usePopularTagsQuery__
*
* To run a query within a React component, call `usePopularTopicsQuery` and pass it any options that fit your needs.
* When your component renders, `usePopularTopicsQuery` returns an object from Apollo Client that contains loading, error, and data properties
* To run a query within a React component, call `usePopularTagsQuery` and pass it any options that fit your needs.
* When your component renders, `usePopularTagsQuery` 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 } = usePopularTopicsQuery({
* const { data, loading, error } = usePopularTagsQuery({
* variables: {
* },
* });
*/
export function usePopularTopicsQuery(baseOptions?: Apollo.QueryHookOptions<PopularTopicsQuery, PopularTopicsQueryVariables>) {
export function usePopularTagsQuery(baseOptions?: Apollo.QueryHookOptions<PopularTagsQuery, PopularTagsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<PopularTopicsQuery, PopularTopicsQueryVariables>(PopularTopicsDocument, options);
return Apollo.useQuery<PopularTagsQuery, PopularTagsQueryVariables>(PopularTagsDocument, options);
}
export function usePopularTopicsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<PopularTopicsQuery, PopularTopicsQueryVariables>) {
export function usePopularTagsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<PopularTagsQuery, PopularTagsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<PopularTopicsQuery, PopularTopicsQueryVariables>(PopularTopicsDocument, options);
return Apollo.useLazyQuery<PopularTagsQuery, PopularTagsQueryVariables>(PopularTagsDocument, options);
}
export type PopularTopicsQueryHookResult = ReturnType<typeof usePopularTopicsQuery>;
export type PopularTopicsLazyQueryHookResult = ReturnType<typeof usePopularTopicsLazyQuery>;
export type PopularTopicsQueryResult = Apollo.QueryResult<PopularTopicsQuery, PopularTopicsQueryVariables>;
export type PopularTagsQueryHookResult = ReturnType<typeof usePopularTagsQuery>;
export type PopularTagsLazyQueryHookResult = ReturnType<typeof usePopularTagsLazyQuery>;
export type PopularTagsQueryResult = Apollo.QueryResult<PopularTagsQuery, PopularTagsQueryVariables>;
export const FeedDocument = gql`
query Feed($take: Int, $skip: Int, $sortBy: String, $topic: Int) {
getFeed(take: $take, skip: $skip, sortBy: $sortBy, topic: $topic) {
query Feed($take: Int, $skip: Int, $sortBy: String, $tag: Int) {
getFeed(take: $take, skip: $skip, sortBy: $sortBy, tag: $tag) {
... on Story {
id
title
@@ -1135,7 +1123,7 @@ export const FeedDocument = gql`
* take: // value for 'take'
* skip: // value for 'skip'
* sortBy: // value for 'sortBy'
* topic: // value for 'topic'
* tag: // value for 'tag'
* },
* });
*/
@@ -1168,11 +1156,6 @@ export const PostDetailsDocument = gql`
id
title
}
topic {
id
title
icon
}
votes_count
type
cover_image

View File

@@ -1,9 +1,9 @@
import { Hackathon } from "src/graphql"
import { topics } from "./topics"
import { tags } from "./tags"
import { getCoverImage, getItems } from "./utils"
const generateTopics = () => getItems(topics, {
const generateTags = () => getItems(tags, {
min: 1,
max: 4
})
@@ -18,7 +18,7 @@ export const hackathons = [
location: "Instanbul, Turkey",
cover_image: getCoverImage(),
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam quam felis ut interdum commodo, scelerisque.",
topics: generateTopics(),
tags: generateTags(),
website: "https://bolt.fun/hackathons/shock-the-web"
},
{
@@ -29,7 +29,7 @@ export const hackathons = [
location: "Instanbul, Turkey",
cover_image: getCoverImage(),
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam quam felis ut interdum commodo, scelerisque.",
topics: generateTopics(),
tags: generateTags(),
website: "https://bolt.fun/hackathons/shock-the-web"
},
{
@@ -40,7 +40,7 @@ export const hackathons = [
location: "Instanbul, Turkey",
cover_image: getCoverImage(),
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam quam felis ut interdum commodo, scelerisque.",
topics: generateTopics(),
tags: generateTags(),
website: "https://bolt.fun/hackathons/shock-the-web"
},
{
@@ -51,7 +51,7 @@ export const hackathons = [
location: "Instanbul, Turkey",
cover_image: getCoverImage(),
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam quam felis ut interdum commodo, scelerisque.",
topics: generateTopics(),
tags: generateTags(),
website: "https://bolt.fun/hackathons/shock-the-web"
},
{
@@ -62,7 +62,7 @@ export const hackathons = [
location: "Instanbul, Turkey",
cover_image: getCoverImage(),
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam quam felis ut interdum commodo, scelerisque.",
topics: generateTopics(),
tags: generateTags(),
website: "https://bolt.fun/hackathons/shock-the-web"
},
{
@@ -73,7 +73,7 @@ export const hackathons = [
location: "Instanbul, Turkey",
cover_image: getCoverImage(),
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam quam felis ut interdum commodo, scelerisque.",
topics: generateTopics(),
tags: generateTags(),
website: "https://bolt.fun/hackathons/shock-the-web"
},
].map(i => ({ ...i, __typename: "Hackathon" })) as Hackathon[]

View File

@@ -1,10 +1,10 @@
import dayjs from "dayjs";
import { Bounty, Post, Question, Story } from "src/features/Posts/types";
import { random, randomItem } from "src/utils/helperFunctions";
import { random, randomItem, randomItems } from "src/utils/helperFunctions";
import { getAvatarImage, getCoverImage } from "./utils";
import { Chance } from 'chance'
import { topics } from "./topics";
import { tags } from "./tags";
const getDate = () => dayjs().subtract(random(5, 48), 'hour').toString();
@@ -90,14 +90,9 @@ export let posts = {
votes_count: 120,
excerpt: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. In odio libero accumsan...',
type: "Story",
tags: [
{ id: 1, title: "lnurl" },
{ id: 2, title: "webln" },
{ id: 3, title: "guide" },
],
tags: randomItems(3, ...tags),
author: getAuthor(),
comments: generatePostComments(3),
topic: randomItem(...topics)
},
] as Story[],
@@ -112,11 +107,7 @@ export let posts = {
createdAt: getDate(),
votes_count: 120,
excerpt: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. In odio libero accumsan...',
tags: [
{ id: 1, title: "lnurl" },
{ id: 2, title: "webln" },
{ id: 3, title: "guide" },
],
tags: randomItems(3, ...tags),
author: getAuthor(),
deadline: "25 May",
reward_amount: 200_000,

View File

@@ -1,6 +1,7 @@
import { Topic } from "src/graphql";
import { Tag } from "src/graphql";
export const topics = [
export const tags = [
{
id: 1,
title: 'Bitcoin',
@@ -27,4 +28,4 @@ export const topics = [
title: 'Design',
icon: '🎨'
}
].map(i => ({ __typename: "Topic", ...i })) as Topic[]
].map(i => ({ __typename: "Tag", ...i })) as Tag[]

View File

@@ -1,6 +1,6 @@
import { graphql } from 'msw'
import { allCategories, getAllHackathons, getCategory, getFeed, getPostById, getProject, getTrendingPosts, hottestProjects, me, newProjects, popularTopics, profile, projectsByCategory, searchProjects } from './resolvers'
import { allCategories, getAllHackathons, getCategory, getFeed, getPostById, getProject, getTrendingPosts, hottestProjects, me, newProjects, popularTags, profile, projectsByCategory, searchProjects } from './resolvers'
import {
NavCategoriesQuery,
ExploreProjectsQuery,
@@ -19,12 +19,12 @@ import {
PostDetailsQueryVariables,
FeedQueryVariables,
TrendingPostsQuery,
PopularTopicsQuery,
PopularTopicsQueryVariables,
PopularTagsQuery,
PopularTagsQueryVariables,
GetHackathonsQuery,
GetHackathonsQueryVariables,
AllTopicsQuery,
AllTopicsQueryVariables,
OfficialTagsQuery,
OfficialTagsQueryVariables,
DonationsStatsQuery,
MeQuery,
ProfileQuery,
@@ -111,20 +111,20 @@ export const handlers = [
)
}),
graphql.query<PopularTopicsQuery, PopularTopicsQueryVariables>('PopularTopics', async (req, res, ctx) => {
graphql.query<PopularTagsQuery, PopularTagsQueryVariables>('PopularTags', async (req, res, ctx) => {
await delay()
return res(
ctx.data({
popularTopics: popularTopics()
popularTags: popularTags()
})
)
}),
graphql.query<AllTopicsQuery, AllTopicsQueryVariables>('allTopics', async (req, res, ctx) => {
graphql.query<OfficialTagsQuery, OfficialTagsQueryVariables>('OfficialTags', async (req, res, ctx) => {
await delay()
return res(
ctx.data({
allTopics: popularTopics()
officialTags: popularTags()
})
)
}),
@@ -135,7 +135,7 @@ export const handlers = [
const { take, skip } = req.variables;
return res(
ctx.data({
getFeed: getFeed({ take, skip, })
getFeed: getFeed({ take, skip, sortBy: null, tag: null })
})
)
}),

View File

@@ -1,7 +1,7 @@
import { MOCK_DATA } from "./data";
import { Query, QueryGetFeedArgs, QueryGetPostByIdArgs } from 'src/graphql'
import { Chance } from "chance";
import { topics } from "./data/topics";
import { tags } from "./data/tags";
import { hackathons } from "./data/hackathon";
import { shuffle } from "src/utils/helperFunctions";
@@ -63,8 +63,8 @@ export function getTrendingPosts(): Query['getTrendingPosts'] {
return chance.pickset(MOCK_DATA.feed, 5);
}
export function popularTopics() {
return topics;
export function popularTags() {
return tags;
}
export function getAllHackathons() {

View File

@@ -52,7 +52,7 @@ export const apolloClient = new ApolloClient({
typePolicies: {
Query: {
fields: {
getFeed: offsetLimitPagination(['sortBy', 'topic'])
getFeed: offsetLimitPagination(['sortBy', 'tag'])
},
},
},

View File

@@ -1,4 +1,4 @@
import React, { ComponentProps, ComponentType, ReactNode, Suspense } from "react";
import React, { ComponentProps, ComponentType, Suspense } from "react";
import { RotatingLines } from "react-loader-spinner";
import { isNullOrUndefined } from "remirror";

View File

@@ -1,5 +1,5 @@
import { useRef, useEffect, useCallback } from 'react'
import { Action, AnyAction } from '@reduxjs/toolkit'
import { Action } from '@reduxjs/toolkit'
import { useStore } from 'react-redux'
/**