Refactoring mods and adding repost functionality #162

Merged
enes merged 7 commits from 107-143-refactoring-mod-and-repost into staging 2024-11-28 16:33:15 +00:00
15 changed files with 510 additions and 323 deletions
Showing only changes of commit b1d578c329 - Show all commits

View File

@ -0,0 +1,165 @@
import { useAppSelector, useLocalStorage } from 'hooks'
import React from 'react'
import {
FilterOptions,
ModeratedFilter,
NSFWFilter,
SortBy,
WOTFilterOptions
} from 'types'
import { DEFAULT_FILTER_OPTIONS } from 'utils'
import { Dropdown } from './Dropdown'
import { Option } from './Option'
import { Filter } from '.'
type Props = {
author?: string | undefined
filterKey?: string | undefined
}
export const BlogsFilter = React.memo(
({ author, filterKey = 'filter-blog' }: Props) => {
const userState = useAppSelector((state) => state.user)
const [filterOptions, setFilterOptions] = useLocalStorage<FilterOptions>(
filterKey,
DEFAULT_FILTER_OPTIONS
)
return (
<Filter>
{/* sort filter options */}
<Dropdown label={filterOptions.sort}>
{Object.values(SortBy).map((item, index) => (
<div
key={`sortByItem-${index}`}
className='dropdown-item dropdownMainMenuItem'
onClick={() =>
setFilterOptions((prev) => ({
...prev,
sort: item
}))
}
>
{item}
</div>
))}
</Dropdown>
{/* moderation filter options */}
<Dropdown label={filterOptions.moderated}>
{Object.values(ModeratedFilter).map((item) => {
if (item === ModeratedFilter.Unmoderated_Fully) {
const isAdmin =
userState.user?.npub === import.meta.env.VITE_REPORTING_NPUB
const isOwnProfile =
author && userState.auth && userState.user?.pubkey === author
if (!(isAdmin || isOwnProfile)) return null
}
return (
<Option
key={`sort-${item}`}
onClick={() =>
setFilterOptions((prev) => ({
...prev,
moderated: item
}))
}
>
{item}
</Option>
)
})}
</Dropdown>
{/* wot filter options */}
<Dropdown label={<>Trust: {filterOptions.wot}</>}>
{Object.values(WOTFilterOptions).map((item, index) => {
// when user is not logged in
if (item === WOTFilterOptions.Site_And_Mine && !userState.auth) {
return null
}
// when logged in user not admin
if (
item === WOTFilterOptions.None ||
item === WOTFilterOptions.Mine_Only ||
item === WOTFilterOptions.Exclude
) {
const isWoTNpub =
userState.user?.npub === import.meta.env.VITE_SITE_WOT_NPUB
const isOwnProfile =
author && userState.auth && userState.user?.pubkey === author
if (!(isWoTNpub || isOwnProfile)) return null
}
return (
<Option
key={`wotFilterOption-${index}`}
onClick={() =>
setFilterOptions((prev) => ({
...prev,
wot: item
}))
}
>
{item}
</Option>
)
})}
</Dropdown>
{/* nsfw filter options */}
<Dropdown label={filterOptions.nsfw}>
{Object.values(NSFWFilter).map((item, index) => (
<Option
key={`nsfwFilterItem-${index}`}
onClick={() =>
setFilterOptions((prev) => ({
...prev,
nsfw: item
}))
}
>
{item}
</Option>
))}
</Dropdown>
{/* source filter options */}
<Dropdown
label={
filterOptions.source === window.location.host
? `Show From: ${filterOptions.source}`
: 'Show All'
}
>
<Option
onClick={() =>
setFilterOptions((prev) => ({
...prev,
source: window.location.host
}))
}
>
Show From: {window.location.host}
</Option>
<Option
onClick={() =>
setFilterOptions((prev) => ({
...prev,
source: 'Show All'
}))
}
>
Show All
</Option>
</Dropdown>
</Filter>
)
}
)

View File

@ -0,0 +1,25 @@
import { PropsWithChildren } from 'react'
interface DropdownProps {
label: React.ReactNode
}
export const Dropdown = ({
label,
children
}: PropsWithChildren<DropdownProps>) => {
return (
<div className='FiltersMainElement'>
<div className='dropdown dropdownMain'>
<button
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
{label}
</button>
<div className='dropdown-menu dropdownMainMenu'>{children}</div>
</div>
</div>
)
}

View File

@ -0,0 +1,182 @@
import { useAppSelector, useLocalStorage } from 'hooks'
import React from 'react'
import {
FilterOptions,
SortBy,
ModeratedFilter,
WOTFilterOptions,
NSFWFilter,
RepostFilter
} from 'types'
import { DEFAULT_FILTER_OPTIONS } from 'utils'
import { Filter } from '.'
import { Dropdown } from './Dropdown'
import { Option } from './Option'
type Props = {
author?: string | undefined
filterKey?: string | undefined
}
export const ModFilter = React.memo(
({ author, filterKey = 'filter' }: Props) => {
const userState = useAppSelector((state) => state.user)
const [filterOptions, setFilterOptions] = useLocalStorage<FilterOptions>(
filterKey,
DEFAULT_FILTER_OPTIONS
)
return (
<Filter>
{/* sort filter options */}
<Dropdown label={filterOptions.sort}>
{Object.values(SortBy).map((item, index) => (
<Option
key={`sortByItem-${index}`}
onClick={() =>
setFilterOptions((prev) => ({
...prev,
sort: item
}))
}
>
{item}
</Option>
))}
</Dropdown>
{/* moderation filter options */}
<Dropdown label={filterOptions.moderated}>
{Object.values(ModeratedFilter).map((item, index) => {
if (item === ModeratedFilter.Unmoderated_Fully) {
const isAdmin =
userState.user?.npub === import.meta.env.VITE_REPORTING_NPUB
const isOwnProfile =
author && userState.auth && userState.user?.pubkey === author
if (!(isAdmin || isOwnProfile)) return null
}
return (
<Option
key={`moderatedFilterItem-${index}`}
onClick={() =>
setFilterOptions((prev) => ({
...prev,
moderated: item
}))
}
>
{item}
</Option>
)
})}
</Dropdown>
{/* wot filter options */}
<Dropdown label={<>Trust: {filterOptions.wot}</>}>
{Object.values(WOTFilterOptions).map((item, index) => {
// when user is not logged in
if (item === WOTFilterOptions.Site_And_Mine && !userState.auth) {
return null
}
// when logged in user not admin
if (
item === WOTFilterOptions.None ||
item === WOTFilterOptions.Mine_Only ||
item === WOTFilterOptions.Exclude
) {
const isWoTNpub =
userState.user?.npub === import.meta.env.VITE_SITE_WOT_NPUB
const isOwnProfile =
author && userState.auth && userState.user?.pubkey === author
if (!(isWoTNpub || isOwnProfile)) return null
}
return (
<Option
key={`wotFilterOption-${index}`}
onClick={() =>
setFilterOptions((prev) => ({
...prev,
wot: item
}))
}
>
{item}
</Option>
)
})}
</Dropdown>
{/* nsfw filter options */}
<Dropdown label={filterOptions.nsfw}>
{Object.values(NSFWFilter).map((item, index) => (
<Option
key={`nsfwFilterItem-${index}`}
onClick={() =>
setFilterOptions((prev) => ({
...prev,
nsfw: item
}))
}
>
{item}
</Option>
))}
</Dropdown>
{/* repost filter options */}
<Dropdown label={filterOptions.repost}>
{Object.values(RepostFilter).map((item, index) => (
<Option
key={`repostFilterItem-${index}`}
onClick={() =>
setFilterOptions((prev) => ({
...prev,
repost: item
}))
}
>
{item}
</Option>
))}
</Dropdown>
{/* source filter options */}
<Dropdown
label={
filterOptions.source === window.location.host
? `Show From: ${filterOptions.source}`
: 'Show All'
}
>
<Option
onClick={() =>
setFilterOptions((prev) => ({
...prev,
source: window.location.host
}))
}
>
Show From: {window.location.host}
</Option>
<Option
onClick={() =>
setFilterOptions((prev) => ({
...prev,
source: 'Show All'
}))
}
>
Show All
</Option>
</Dropdown>
</Filter>
)
}
)

View File

@ -0,0 +1,16 @@
import { PropsWithChildren } from 'react'
interface OptionProps {
onClick: React.MouseEventHandler<HTMLDivElement>
}
export const Option = ({
onClick,
children
}: PropsWithChildren<OptionProps>) => {
return (
<div className='dropdown-item dropdownMainMenuItem' onClick={onClick}>
{children}
</div>
)
}

View File

@ -0,0 +1,9 @@
import { PropsWithChildren } from 'react'
export const Filter = ({ children }: PropsWithChildren) => {
return (
<div className='IBMSecMain'>
<div className='FiltersMain'>{children}</div>
</div>
)
}

View File

@ -1,235 +0,0 @@
import { useAppSelector, useLocalStorage } from 'hooks'
import React from 'react'
import {
FilterOptions,
ModeratedFilter,
NSFWFilter,
SortBy,
WOTFilterOptions
} from 'types'
import { DEFAULT_FILTER_OPTIONS } from 'utils'
type Props = {
author?: string | undefined
filterKey?: string | undefined
}
export const ModFilter = React.memo(
({ author, filterKey = 'filter' }: Props) => {
const userState = useAppSelector((state) => state.user)
const [filterOptions, setFilterOptions] = useLocalStorage<FilterOptions>(
filterKey,
DEFAULT_FILTER_OPTIONS
)
return (
<div className='IBMSecMain'>
<div className='FiltersMain'>
{/* sort filter options */}
<div className='FiltersMainElement'>
<div className='dropdown dropdownMain'>
<button
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
{filterOptions.sort}
</button>
<div className='dropdown-menu dropdownMainMenu'>
{Object.values(SortBy).map((item, index) => (
<div
key={`sortByItem-${index}`}
className='dropdown-item dropdownMainMenuItem'
onClick={() =>
setFilterOptions((prev) => ({
...prev,
sort: item
}))
}
>
{item}
</div>
))}
</div>
</div>
</div>
{/* moderation filter options */}
<div className='FiltersMainElement'>
<div className='dropdown dropdownMain'>
<button
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
{filterOptions.moderated}
</button>
<div className='dropdown-menu dropdownMainMenu'>
{Object.values(ModeratedFilter).map((item, index) => {
if (item === ModeratedFilter.Unmoderated_Fully) {
const isAdmin =
userState.user?.npub ===
import.meta.env.VITE_REPORTING_NPUB
const isOwnProfile =
author &&
userState.auth &&
userState.user?.pubkey === author
if (!(isAdmin || isOwnProfile)) return null
}
return (
<div
key={`moderatedFilterItem-${index}`}
className='dropdown-item dropdownMainMenuItem'
onClick={() =>
setFilterOptions((prev) => ({
...prev,
moderated: item
}))
}
>
{item}
</div>
)
})}
</div>
</div>
</div>
{/* wot filter options */}
<div className='FiltersMainElement'>
<div className='dropdown dropdownMain'>
<button
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
Trust: {filterOptions.wot}
</button>
<div className='dropdown-menu dropdownMainMenu'>
{Object.values(WOTFilterOptions).map((item, index) => {
// when user is not logged in
if (
item === WOTFilterOptions.Site_And_Mine &&
!userState.auth
) {
return null
}
// when logged in user not admin
if (
item === WOTFilterOptions.None ||
item === WOTFilterOptions.Mine_Only ||
item === WOTFilterOptions.Exclude
) {
const isWoTNpub =
userState.user?.npub ===
import.meta.env.VITE_SITE_WOT_NPUB
const isOwnProfile =
author &&
userState.auth &&
userState.user?.pubkey === author
if (!(isWoTNpub || isOwnProfile)) return null
}
return (
<div
key={`wotFilterOption-${index}`}
className='dropdown-item dropdownMainMenuItem'
onClick={() =>
setFilterOptions((prev) => ({
...prev,
wot: item
}))
}
>
{item}
</div>
)
})}
</div>
</div>
</div>
{/* nsfw filter options */}
<div className='FiltersMainElement'>
<div className='dropdown dropdownMain'>
<button
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
{filterOptions.nsfw}
</button>
<div className='dropdown-menu dropdownMainMenu'>
{Object.values(NSFWFilter).map((item, index) => (
<div
key={`nsfwFilterItem-${index}`}
className='dropdown-item dropdownMainMenuItem'
onClick={() =>
setFilterOptions((prev) => ({
...prev,
nsfw: item
}))
}
>
{item}
</div>
))}
</div>
</div>
</div>
{/* source filter options */}
<div className='FiltersMainElement'>
<div className='dropdown dropdownMain'>
<button
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
{filterOptions.source === window.location.host
? `Show From: ${filterOptions.source}`
: 'Show All'}
</button>
<div className='dropdown-menu dropdownMainMenu'>
<div
className='dropdown-item dropdownMainMenuItem'
onClick={() =>
setFilterOptions((prev) => ({
...prev,
source: window.location.host
}))
}
>
Show From: {window.location.host}
</div>
<div
className='dropdown-item dropdownMainMenuItem'
onClick={() =>
setFilterOptions((prev) => ({
...prev,
source: 'Show All'
}))
}
>
Show All
</div>
</div>
</div>
</div>
</div>
</div>
)
}
)

View File

@ -6,6 +6,7 @@ import {
ModeratedFilter, ModeratedFilter,
MuteLists, MuteLists,
NSFWFilter, NSFWFilter,
RepostFilter,
SortBy, SortBy,
WOTFilterOptions WOTFilterOptions
} from 'types' } from 'types'
@ -22,6 +23,7 @@ export const useFilteredMods = (
admin: MuteLists admin: MuteLists
user: MuteLists user: MuteLists
}, },
repostList: string[],
author?: string | undefined author?: string | undefined
) => { ) => {
const { siteWot, siteWotLevel, userWot, userWotLevel } = useAppSelector( const { siteWot, siteWotLevel, userWot, userWotLevel } = useAppSelector(
@ -53,6 +55,30 @@ export const useFilteredMods = (
} }
} }
const repostFilter = (mods: ModDetails[]) => {
if (filterOptions.repost !== RepostFilter.Hide_Repost) {
// Add repost tag to mods included in repostList
mods = mods.map((mod) => {
return !mod.repost && repostList.includes(mod.aTag)
? { ...mod, repost: true }
: mod
})
}
// Determine the filtering logic based on the Repost filter option
switch (filterOptions.repost) {
case RepostFilter.Hide_Repost:
return mods.filter(
(mod) => !mod.repost && !repostList.includes(mod.aTag)
)
case RepostFilter.Show_Repost:
return mods
case RepostFilter.Only_Repost:
return mods.filter(
(mod) => mod.repost || repostList.includes(mod.aTag)
)
}
}
const wotFilter = (mods: ModDetails[]) => { const wotFilter = (mods: ModDetails[]) => {
// Determine the filtering logic based on the WOT filter option and user state // Determine the filtering logic based on the WOT filter option and user state
// when user is not logged in use Site_Only // when user is not logged in use Site_Only
@ -93,7 +119,7 @@ export const useFilteredMods = (
} }
let filtered = nsfwFilter(mods) let filtered = nsfwFilter(mods)
filtered = repostFilter(filtered)
filtered = wotFilter(filtered) filtered = wotFilter(filtered)
const isAdmin = userState.user?.npub === import.meta.env.VITE_REPORTING_NPUB const isAdmin = userState.user?.npub === import.meta.env.VITE_REPORTING_NPUB
@ -135,10 +161,12 @@ export const useFilteredMods = (
filterOptions.moderated, filterOptions.moderated,
filterOptions.wot, filterOptions.wot,
filterOptions.nsfw, filterOptions.nsfw,
filterOptions.repost,
author, author,
mods, mods,
muteLists, muteLists,
nsfwList, nsfwList,
repostList,
siteWot, siteWot,
siteWotLevel, siteWotLevel,
userWot, userWot,

View File

@ -11,6 +11,9 @@ import '../../styles/styles.css'
import { PaginationWithPageNumbers } from 'components/Pagination' import { PaginationWithPageNumbers } from 'components/Pagination'
import { scrollIntoView } from 'utils' import { scrollIntoView } from 'utils'
import { LoadingSpinner } from 'components/LoadingSpinner' import { LoadingSpinner } from 'components/LoadingSpinner'
import { Filter } from 'components/Filters'
import { Dropdown } from 'components/Filters/Dropdown'
import { Option } from 'components/Filters/Option'
export const BlogsPage = () => { export const BlogsPage = () => {
const navigation = useNavigation() const navigation = useNavigation()
@ -126,66 +129,39 @@ export const BlogsPage = () => {
</div> </div>
</div> </div>
<div className='IBMSecMain'> <Filter>
<div className='FiltersMain'> <Dropdown label={filterOptions.sort}>
<div className='FiltersMainElement'> {Object.values(SortBy).map((item, index) => (
<div className='dropdown dropdownMain'> <Option
<button key={`sortByItem-${index}`}
className='btn dropdown-toggle btnMain btnMainDropdown' onClick={() =>
aria-expanded='false' setFilterOptions((prev) => ({
data-bs-toggle='dropdown' ...prev,
type='button' sort: item
> }))
{filterOptions.sort} }
</button> >
<div className='dropdown-menu dropdownMainMenu'> {item}
{Object.values(SortBy).map((item, index) => ( </Option>
<div ))}
key={`sortByItem-${index}`} </Dropdown>
className='dropdown-item dropdownMainMenuItem'
onClick={() => <Dropdown label={filterOptions.nsfw}>
setFilterOptions((prev) => ({ {Object.values(NSFWFilter).map((item, index) => (
...prev, <Option
sort: item key={`nsfwFilterItem-${index}`}
})) onClick={() =>
} setFilterOptions((prev) => ({
> ...prev,
{item} nsfw: item
</div> }))
))} }
</div> >
</div> {item}
</div> </Option>
<div className='FiltersMainElement'> ))}
<div className='dropdown dropdownMain'> </Dropdown>
<button </Filter>
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
{filterOptions.nsfw}
</button>
<div className='dropdown-menu dropdownMainMenu'>
{Object.values(NSFWFilter).map((item, index) => (
<div
key={`nsfwFilterItem-${index}`}
className='dropdown-item dropdownMainMenuItem'
onClick={() =>
setFilterOptions((prev) => ({
...prev,
nsfw: item
}))
}
>
{item}
</div>
))}
</div>
</div>
</div>
</div>
</div>
<div className='IBMSecMain IBMSMListWrapper'> <div className='IBMSecMain IBMSMListWrapper'>
<div className='IBMSMList'> <div className='IBMSMList'>

View File

@ -4,7 +4,7 @@ import {
NDKSubscriptionCacheUsage NDKSubscriptionCacheUsage
} from '@nostr-dev-kit/ndk' } from '@nostr-dev-kit/ndk'
import { ModCard } from 'components/ModCard' import { ModCard } from 'components/ModCard'
import { ModFilter } from 'components/ModsFilter' import { ModFilter } from 'components/Filters/ModsFilter'
import { PaginationWithPageNumbers } from 'components/Pagination' import { PaginationWithPageNumbers } from 'components/Pagination'
import { SearchInput } from 'components/SearchInput' import { SearchInput } from 'components/SearchInput'
import { MAX_MODS_PER_PAGE, T_TAG_VALUE } from 'constants.ts' import { MAX_MODS_PER_PAGE, T_TAG_VALUE } from 'constants.ts'
@ -20,11 +20,13 @@ import { useEffect, useMemo, useRef, useState } from 'react'
import { useParams, useSearchParams } from 'react-router-dom' import { useParams, useSearchParams } from 'react-router-dom'
import { FilterOptions, ModDetails } from 'types' import { FilterOptions, ModDetails } from 'types'
import { import {
CurationSetIdentifiers,
DEFAULT_FILTER_OPTIONS, DEFAULT_FILTER_OPTIONS,
extractModData, extractModData,
isModDataComplete, isModDataComplete,
scrollIntoView scrollIntoView
} from 'utils' } from 'utils'
import { useCuratedSet } from 'hooks/useCuratedSet'
export const GamePage = () => { export const GamePage = () => {
const scrollTargetRef = useRef<HTMLDivElement>(null) const scrollTargetRef = useRef<HTMLDivElement>(null)
@ -33,6 +35,7 @@ export const GamePage = () => {
const { ndk } = useNDKContext() const { ndk } = useNDKContext()
const muteLists = useMuteLists() const muteLists = useMuteLists()
const nsfwList = useNSFWList() const nsfwList = useNSFWList()
const repostList = useCuratedSet(CurationSetIdentifiers.Repost)
const [filterOptions] = useLocalStorage<FilterOptions>( const [filterOptions] = useLocalStorage<FilterOptions>(
'filter', 'filter',
@ -101,7 +104,8 @@ export const GamePage = () => {
userState, userState,
filterOptions, filterOptions,
nsfwList, nsfwList,
muteLists muteLists,
repostList
) )
// Pagination logic // Pagination logic

View File

@ -1,4 +1,4 @@
import { ModFilter } from 'components/ModsFilter' import { ModFilter } from 'components/Filters/ModsFilter'
import { Pagination } from 'components/Pagination' import { Pagination } from 'components/Pagination'
import React, { useCallback, useEffect, useRef, useState } from 'react' import React, { useCallback, useEffect, useRef, useState } from 'react'
import { createSearchParams, useNavigate } from 'react-router-dom' import { createSearchParams, useNavigate } from 'react-router-dom'
@ -39,6 +39,7 @@ export const ModsPage = () => {
) )
const muteLists = useMuteLists() const muteLists = useMuteLists()
const nsfwList = useNSFWList() const nsfwList = useNSFWList()
const repostList = useCuratedSet(CurationSetIdentifiers.Repost)
const [page, setPage] = useState(1) const [page, setPage] = useState(1)
@ -99,17 +100,10 @@ export const ModsPage = () => {
userState, userState,
filterOptions, filterOptions,
nsfwList, nsfwList,
muteLists muteLists,
repostList
) )
// Add repost tag to mods included in repostList
const repostList = useCuratedSet(CurationSetIdentifiers.Repost)
const filteredModListRepost = filteredModList.map((mod) => {
return !mod.repost && repostList.includes(mod.aTag)
? { ...mod, repost: true }
: mod
})
return ( return (
<> <>
{isFetching && <LoadingSpinner desc='Fetching mod details from relays' />} {isFetching && <LoadingSpinner desc='Fetching mod details from relays' />}
@ -124,7 +118,7 @@ export const ModsPage = () => {
<div className='IBMSecMain IBMSMListWrapper'> <div className='IBMSecMain IBMSMListWrapper'>
<div className='IBMSMList'> <div className='IBMSMList'>
{filteredModListRepost.map((mod) => ( {filteredModList.map((mod) => (
<ModCard key={mod.id} {...mod} /> <ModCard key={mod.id} {...mod} />
))} ))}
</div> </div>

View File

@ -1,7 +1,7 @@
import { NDKFilter, NDKKind } from '@nostr-dev-kit/ndk' import { NDKFilter, NDKKind } from '@nostr-dev-kit/ndk'
import { LoadingSpinner } from 'components/LoadingSpinner' import { LoadingSpinner } from 'components/LoadingSpinner'
import { ModCard } from 'components/ModCard' import { ModCard } from 'components/ModCard'
import { ModFilter } from 'components/ModsFilter' import { ModFilter } from 'components/Filters/ModsFilter'
import { Pagination } from 'components/Pagination' import { Pagination } from 'components/Pagination'
import { ProfileSection } from 'components/ProfileSection' import { ProfileSection } from 'components/ProfileSection'
import { Tabs } from 'components/Tabs' import { Tabs } from 'components/Tabs'
@ -39,6 +39,7 @@ import {
import { CheckboxField } from 'components/Inputs' import { CheckboxField } from 'components/Inputs'
import { ProfilePageLoaderResult } from './loader' import { ProfilePageLoaderResult } from './loader'
import { BlogCard } from 'components/BlogCard' import { BlogCard } from 'components/BlogCard'
import { BlogsFilter } from 'components/Filters/BlogsFilter'
export const ProfilePage = () => { export const ProfilePage = () => {
const { const {
@ -269,16 +270,10 @@ export const ProfilePage = () => {
filterOptions, filterOptions,
nsfwList, nsfwList,
muteLists, muteLists,
repostList,
profilePubkey profilePubkey
) )
// Add repost tag to mods included in repostList
const filteredModListRepost = filteredModList.map((mod) => {
return !mod.repost && repostList.includes(mod.aTag)
? { ...mod, repost: true }
: mod
})
return ( return (
<div className='InnerBodyMain'> <div className='InnerBodyMain'>
<div className='ContainerMain'> <div className='ContainerMain'>
@ -429,7 +424,7 @@ export const ProfilePage = () => {
<ModFilter filterKey={filterKey} author={profilePubkey} /> <ModFilter filterKey={filterKey} author={profilePubkey} />
<div className='IBMSMList IBMSMListAlt'> <div className='IBMSMList IBMSMListAlt'>
{filteredModListRepost.map((mod) => ( {filteredModList.map((mod) => (
<ModCard key={mod.id} {...mod} /> <ModCard key={mod.id} {...mod} />
))} ))}
</div> </div>
@ -831,7 +826,10 @@ const ProfileTabBlogs = () => {
<LoadingSpinner desc={'Loading...'} /> <LoadingSpinner desc={'Loading...'} />
)} )}
<ModFilter filterKey={'filter-blog'} author={profile?.pubkey as string} /> <BlogsFilter
filterKey={'filter-blog'}
author={profile?.pubkey as string}
/>
<div className='IBMSMList IBMSMListAlt'> <div className='IBMSMList IBMSMListAlt'>
{moderatedAndSortedBlogs.map((b) => ( {moderatedAndSortedBlogs.map((b) => (

View File

@ -10,7 +10,7 @@ import {
import { ErrorBoundary } from 'components/ErrorBoundary' import { ErrorBoundary } from 'components/ErrorBoundary'
import { GameCard } from 'components/GameCard' import { GameCard } from 'components/GameCard'
import { ModCard } from 'components/ModCard' import { ModCard } from 'components/ModCard'
import { ModFilter } from 'components/ModsFilter' import { ModFilter } from 'components/Filters/ModsFilter'
import { Pagination } from 'components/Pagination' import { Pagination } from 'components/Pagination'
import { Profile } from 'components/ProfileSection' import { Profile } from 'components/ProfileSection'
import { SearchInput } from 'components/SearchInput' import { SearchInput } from 'components/SearchInput'
@ -32,11 +32,13 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'
import { useSearchParams } from 'react-router-dom' import { useSearchParams } from 'react-router-dom'
import { FilterOptions, ModDetails, ModeratedFilter, MuteLists } from 'types' import { FilterOptions, ModDetails, ModeratedFilter, MuteLists } from 'types'
import { import {
CurationSetIdentifiers,
DEFAULT_FILTER_OPTIONS, DEFAULT_FILTER_OPTIONS,
extractModData, extractModData,
isModDataComplete, isModDataComplete,
scrollIntoView scrollIntoView
} from 'utils' } from 'utils'
import { useCuratedSet } from 'hooks/useCuratedSet'
enum SearchKindEnum { enum SearchKindEnum {
Mods = 'Mods', Mods = 'Mods',
@ -50,6 +52,7 @@ export const SearchPage = () => {
const muteLists = useMuteLists() const muteLists = useMuteLists()
const nsfwList = useNSFWList() const nsfwList = useNSFWList()
const repostList = useCuratedSet(CurationSetIdentifiers.Repost)
const searchTermRef = useRef<HTMLInputElement>(null) const searchTermRef = useRef<HTMLInputElement>(null)
const searchKind = const searchKind =
@ -115,6 +118,7 @@ export const SearchPage = () => {
filterOptions={filterOptions} filterOptions={filterOptions}
muteLists={muteLists} muteLists={muteLists}
nsfwList={nsfwList} nsfwList={nsfwList}
repostList={repostList}
el={scrollTargetRef.current} el={scrollTargetRef.current}
/> />
)} )}
@ -233,6 +237,7 @@ type ModsResultProps = {
user: MuteLists user: MuteLists
} }
nsfwList: string[] nsfwList: string[]
repostList: string[]
el: HTMLElement | null el: HTMLElement | null
} }
@ -241,6 +246,7 @@ const ModsResult = ({
searchTerm, searchTerm,
muteLists, muteLists,
nsfwList, nsfwList,
repostList,
el el
}: ModsResultProps) => { }: ModsResultProps) => {
const { ndk } = useNDKContext() const { ndk } = useNDKContext()
@ -313,7 +319,8 @@ const ModsResult = ({
userState, userState,
filterOptions, filterOptions,
nsfwList, nsfwList,
muteLists muteLists,
repostList
) )
const handleNext = () => { const handleNext = () => {

View File

@ -1,3 +1,5 @@
import { SortBy, NSFWFilter, ModeratedFilter } from './modsFilter'
export interface BlogForm { export interface BlogForm {
title: string title: string
content: string content: string
@ -40,3 +42,10 @@ export interface BlogPageLoaderResult {
isAddedToNSFW: boolean isAddedToNSFW: boolean
isBlocked: boolean isBlocked: boolean
} }
export interface BlogsFilterOptions {
sort: SortBy
nsfw: NSFWFilter
source: string
moderated: ModeratedFilter
}

View File

@ -25,10 +25,17 @@ export enum WOTFilterOptions {
Exclude = 'Exclude' Exclude = 'Exclude'
} }
export enum RepostFilter {
Hide_Repost = 'Hide Repost',
Show_Repost = 'Show Repost',
Only_Repost = 'Only Repost'
}
export interface FilterOptions { export interface FilterOptions {
sort: SortBy sort: SortBy
nsfw: NSFWFilter nsfw: NSFWFilter
source: string source: string
moderated: ModeratedFilter moderated: ModeratedFilter
wot: WOTFilterOptions wot: WOTFilterOptions
repost: RepostFilter
} }

View File

@ -3,7 +3,8 @@ import {
SortBy, SortBy,
NSFWFilter, NSFWFilter,
ModeratedFilter, ModeratedFilter,
WOTFilterOptions WOTFilterOptions,
RepostFilter
} from 'types' } from 'types'
export const DEFAULT_FILTER_OPTIONS: FilterOptions = { export const DEFAULT_FILTER_OPTIONS: FilterOptions = {
@ -11,5 +12,6 @@ export const DEFAULT_FILTER_OPTIONS: FilterOptions = {
nsfw: NSFWFilter.Hide_NSFW, nsfw: NSFWFilter.Hide_NSFW,
source: window.location.host, source: window.location.host,
moderated: ModeratedFilter.Moderated, moderated: ModeratedFilter.Moderated,
wot: WOTFilterOptions.Site_Only wot: WOTFilterOptions.Site_Only,
repost: RepostFilter.Show_Repost
} }