Compare commits
No commits in common. "4a4ccaaf37366b5d15d650f0ad60561ae74545a6" and "dd5154b115f1849ba08d2691bbb144b8400ffd08" have entirely different histories.
4a4ccaaf37
...
dd5154b115
@ -7,9 +7,6 @@ VITE_ADMIN_NPUBS= <A comma separated list of npubs>
|
|||||||
# A dedicated npub used for reporting mods, blogs, profile and etc.
|
# A dedicated npub used for reporting mods, blogs, profile and etc.
|
||||||
VITE_REPORTING_NPUB= <npub1...>
|
VITE_REPORTING_NPUB= <npub1...>
|
||||||
|
|
||||||
# A dedicated npub used for site WOT.
|
|
||||||
VITE_SITE_WOT_NPUB= <npub1...>
|
|
||||||
|
|
||||||
# if there's no featured image, or if the image breaks somewhere down the line, then it should default to this image
|
# if there's no featured image, or if the image breaks somewhere down the line, then it should default to this image
|
||||||
VITE_FALLBACK_MOD_IMAGE=https://image.nostr.build/1fa7afd3489302c2da8957993ac0fd6c4308eedd4b1b95b24ecfabe3651b2183.png
|
VITE_FALLBACK_MOD_IMAGE=https://image.nostr.build/1fa7afd3489302c2da8957993ac0fd6c4308eedd4b1b95b24ecfabe3651b2183.png
|
||||||
|
|
||||||
|
@ -25,10 +25,10 @@ jobs:
|
|||||||
echo "VITE_APP_RELAY=${{ vars.VITE_APP_RELAY }}" >> .env
|
echo "VITE_APP_RELAY=${{ vars.VITE_APP_RELAY }}" >> .env
|
||||||
echo "VITE_ADMIN_NPUBS=${{ vars.VITE_ADMIN_NPUBS }}" >> .env
|
echo "VITE_ADMIN_NPUBS=${{ vars.VITE_ADMIN_NPUBS }}" >> .env
|
||||||
echo "VITE_REPORTING_NPUB=${{ vars.VITE_REPORTING_NPUB }}" >> .env
|
echo "VITE_REPORTING_NPUB=${{ vars.VITE_REPORTING_NPUB }}" >> .env
|
||||||
echo "VITE_SITE_WOT_NPUB=${{ vars.VITE_SITE_WOT_NPUB }}" >> .env
|
|
||||||
echo "VITE_FALLBACK_MOD_IMAGE=${{ vars.VITE_FALLBACK_MOD_IMAGE }}" >> .env
|
echo "VITE_FALLBACK_MOD_IMAGE=${{ vars.VITE_FALLBACK_MOD_IMAGE }}" >> .env
|
||||||
echo "VITE_FALLBACK_GAME_IMAGE=${{ vars.VITE_FALLBACK_GAME_IMAGE }}" >> .env
|
echo "VITE_FALLBACK_GAME_IMAGE=${{ vars.VITE_FALLBACK_GAME_IMAGE }}" >> .env
|
||||||
echo "VITE_BLOG_NPUBS=${{ vars.VITE_BLOG_NPUBS }}" >> .env
|
echo "VITE_BLOG_NPUBS=${{ vars.VITE_BLOG_NPUBS }}" >> .env
|
||||||
|
echo "VITE_SITE_WOT_NPUB=${{ vars.VITE_SITE_WOT_NPUB }}" >> .env
|
||||||
cat .env
|
cat .env
|
||||||
|
|
||||||
- name: Create Build
|
- name: Create Build
|
||||||
|
@ -25,10 +25,10 @@ jobs:
|
|||||||
echo "VITE_APP_RELAY=${{ vars.VITE_APP_RELAY }}" >> .env
|
echo "VITE_APP_RELAY=${{ vars.VITE_APP_RELAY }}" >> .env
|
||||||
echo "VITE_ADMIN_NPUBS=${{ vars.VITE_ADMIN_NPUBS }}" >> .env
|
echo "VITE_ADMIN_NPUBS=${{ vars.VITE_ADMIN_NPUBS }}" >> .env
|
||||||
echo "VITE_REPORTING_NPUB=${{ vars.VITE_REPORTING_NPUB }}" >> .env
|
echo "VITE_REPORTING_NPUB=${{ vars.VITE_REPORTING_NPUB }}" >> .env
|
||||||
echo "VITE_SITE_WOT_NPUB=${{ vars.VITE_SITE_WOT_NPUB }}" >> .env
|
|
||||||
echo "VITE_FALLBACK_MOD_IMAGE=${{ vars.VITE_FALLBACK_MOD_IMAGE }}" >> .env
|
echo "VITE_FALLBACK_MOD_IMAGE=${{ vars.VITE_FALLBACK_MOD_IMAGE }}" >> .env
|
||||||
echo "VITE_FALLBACK_GAME_IMAGE=${{ vars.VITE_FALLBACK_GAME_IMAGE }}" >> .env
|
echo "VITE_FALLBACK_GAME_IMAGE=${{ vars.VITE_FALLBACK_GAME_IMAGE }}" >> .env
|
||||||
echo "VITE_BLOG_NPUBS=${{ vars.VITE_BLOG_NPUBS }}" >> .env
|
echo "VITE_BLOG_NPUBS=${{ vars.VITE_BLOG_NPUBS }}" >> .env
|
||||||
|
echo "VITE_SITE_WOT_NPUB=${{ vars.VITE_SITE_WOT_NPUB }}" >> .env
|
||||||
cat .env
|
cat .env
|
||||||
|
|
||||||
- name: Create Build
|
- name: Create Build
|
||||||
|
@ -32,11 +32,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "VITE_APP_RELAY=${{ vars.VITE_APP_RELAY }}" >> .env
|
echo "VITE_APP_RELAY=${{ vars.VITE_APP_RELAY }}" >> .env
|
||||||
echo "VITE_ADMIN_NPUBS=${{ vars.VITE_ADMIN_NPUBS }}" >> .env
|
echo "VITE_ADMIN_NPUBS=${{ vars.VITE_ADMIN_NPUBS }}" >> .env
|
||||||
echo "VITE_SITE_WOT_NPUB=${{ vars.VITE_SITE_WOT_NPUB }}" >> .env
|
|
||||||
echo "VITE_FALLBACK_GAME_IMAGE=${{ vars.VITE_FALLBACK_GAME_IMAGE }}" >> .env
|
echo "VITE_FALLBACK_GAME_IMAGE=${{ vars.VITE_FALLBACK_GAME_IMAGE }}" >> .env
|
||||||
echo "VITE_FALLBACK_MOD_IMAGE=${{ vars.VITE_FALLBACK_MOD_IMAGE }}" >> .env
|
echo "VITE_FALLBACK_MOD_IMAGE=${{ vars.VITE_FALLBACK_MOD_IMAGE }}" >> .env
|
||||||
echo "VITE_REPORTING_NPUB=${{ vars.VITE_REPORTING_NPUB }}" >> .env
|
echo "VITE_REPORTING_NPUB=${{ vars.VITE_REPORTING_NPUB }}" >> .env
|
||||||
echo "VITE_BLOG_NPUBS=${{ vars.VITE_BLOG_NPUBS }}" >> .env
|
echo "VITE_BLOG_NPUBS=${{ vars.VITE_BLOG_NPUBS }}" >> .env
|
||||||
|
echo "VITE_SITE_WOT_NPUB=${{ vars.VITE_SITE_WOT_NPUB }}" >> .env
|
||||||
cat .env
|
cat .env
|
||||||
- name: Build
|
- name: Build
|
||||||
run: npm run build
|
run: npm run build
|
||||||
|
@ -3,5 +3,3 @@ Game Name,16 by 9 image,Boxart image
|
|||||||
Minecraft,,https://image.nostr.build/b75b2d3a7855370230f2976567e2d5f913a567c57ac61adfb60c7e1102f05117.jpg
|
Minecraft,,https://image.nostr.build/b75b2d3a7855370230f2976567e2d5f913a567c57ac61adfb60c7e1102f05117.jpg
|
||||||
Vintage Story,,https://image.nostr.build/9efe683d339cc864032a99047ce26b2b5c19fab1ec4dcc6d4db96e2785c44eda.png
|
Vintage Story,,https://image.nostr.build/9efe683d339cc864032a99047ce26b2b5c19fab1ec4dcc6d4db96e2785c44eda.png
|
||||||
Yandere Simulator,,https://image.nostr.build/54ba56b752bb9d411cbdc1d249fa0cb74c6062a305bcd0a70ecacb61b8d50030.png
|
Yandere Simulator,,https://image.nostr.build/54ba56b752bb9d411cbdc1d249fa0cb74c6062a305bcd0a70ecacb61b8d50030.png
|
||||||
Genshin Impact,,https://image.nostr.build/999fccf93cf16a2e0dd8e6f00595b0ab3b5cc6beff9fe4a52f64f427cce9aedd.jpg
|
|
||||||
Zenless Zone Zero,,https://image.nostr.build/4a9b9c2cbef619552d0c123f8794286f35710dc7ca1ca0010380a630883eb2ca.jpg
|
|
|
@ -24966,7 +24966,7 @@ Mystery of Island,,
|
|||||||
SEARCH ALL - POTIONS,,
|
SEARCH ALL - POTIONS,,
|
||||||
Sanctuary Saga Playtest,,
|
Sanctuary Saga Playtest,,
|
||||||
Fantasy Grounds - Beastheart and Monstrous Companions,,
|
Fantasy Grounds - Beastheart and Monstrous Companions,,
|
||||||
Dragon Age: The Veilguard,,https://image.nostr.build/0ec48cc2ef08b3f09647c8403e29c5fd814dc3e4aeb2bb76add93928f3a867b0.jpg
|
Dragon Age™: The Veilguard,,
|
||||||
Transport INC - Map Pack,,
|
Transport INC - Map Pack,,
|
||||||
Fantasy Grounds - Dungeon Crawl Classics #97: The Queen of Elfland's Son,,
|
Fantasy Grounds - Dungeon Crawl Classics #97: The Queen of Elfland's Son,,
|
||||||
cyberpunkdreams: outside edges,,
|
cyberpunkdreams: outside edges,,
|
||||||
|
Can't render this file because it is too large.
|
@ -1,12 +1,6 @@
|
|||||||
import { useAppSelector, useLocalStorage } from 'hooks'
|
import { useAppSelector, useLocalStorage } from 'hooks'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {
|
import { FilterOptions, ModeratedFilter, NSFWFilter, SortBy } from 'types'
|
||||||
FilterOptions,
|
|
||||||
ModeratedFilter,
|
|
||||||
NSFWFilter,
|
|
||||||
SortBy,
|
|
||||||
WOTFilterOptions
|
|
||||||
} from 'types'
|
|
||||||
import { DEFAULT_FILTER_OPTIONS } from 'utils'
|
import { DEFAULT_FILTER_OPTIONS } from 'utils'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -25,7 +19,6 @@ export const ModFilter = React.memo(
|
|||||||
return (
|
return (
|
||||||
<div className='IBMSecMain'>
|
<div className='IBMSecMain'>
|
||||||
<div className='FiltersMain'>
|
<div className='FiltersMain'>
|
||||||
{/* sort filter options */}
|
|
||||||
<div className='FiltersMainElement'>
|
<div className='FiltersMainElement'>
|
||||||
<div className='dropdown dropdownMain'>
|
<div className='dropdown dropdownMain'>
|
||||||
<button
|
<button
|
||||||
@ -55,8 +48,6 @@ export const ModFilter = React.memo(
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* moderation filter options */}
|
|
||||||
<div className='FiltersMainElement'>
|
<div className='FiltersMainElement'>
|
||||||
<div className='dropdown dropdownMain'>
|
<div className='dropdown dropdownMain'>
|
||||||
<button
|
<button
|
||||||
@ -100,66 +91,6 @@ export const ModFilter = React.memo(
|
|||||||
</div>
|
</div>
|
||||||
</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='FiltersMainElement'>
|
||||||
<div className='dropdown dropdownMain'>
|
<div className='dropdown dropdownMain'>
|
||||||
<button
|
<button
|
||||||
@ -188,8 +119,6 @@ export const ModFilter = React.memo(
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* source filter options */}
|
|
||||||
<div className='FiltersMainElement'>
|
<div className='FiltersMainElement'>
|
||||||
<div className='dropdown dropdownMain'>
|
<div className='dropdown dropdownMain'>
|
||||||
<button
|
<button
|
||||||
|
@ -25,7 +25,7 @@ export const LANDING_PAGE_DATA = {
|
|||||||
'naddr1qvzqqqr4gupzpa9lr76m4zlg88mscue3wvlrp8mcpq3txy0k8cqlnhy2hw6z37x4qqjrzcfc8qurjefn943xyen9956rywp595unjc3h94nxvwfexymxxcfnvdjxxlyq37c',
|
'naddr1qvzqqqr4gupzpa9lr76m4zlg88mscue3wvlrp8mcpq3txy0k8cqlnhy2hw6z37x4qqjrzcfc8qurjefn943xyen9956rywp595unjc3h94nxvwfexymxxcfnvdjxxlyq37c',
|
||||||
'naddr1qvzqqqr4gupzpa9lr76m4zlg88mscue3wvlrp8mcpq3txy0k8cqlnhy2hw6z37x4qqjryv3k8qenydpj94nrscmp956xgwtp94snydtz95ekgvphvfnxvvrzvyexzsvsz9y',
|
'naddr1qvzqqqr4gupzpa9lr76m4zlg88mscue3wvlrp8mcpq3txy0k8cqlnhy2hw6z37x4qqjryv3k8qenydpj94nrscmp956xgwtp94snydtz95ekgvphvfnxvvrzvyexzsvsz9y',
|
||||||
'naddr1qvzqqqr4gupzpa9lr76m4zlg88mscue3wvlrp8mcpq3txy0k8cqlnhy2hw6z37x4qq2kwjtwvahns3n0tf8j6kjxggkkz4mff499ge7xzsz',
|
'naddr1qvzqqqr4gupzpa9lr76m4zlg88mscue3wvlrp8mcpq3txy0k8cqlnhy2hw6z37x4qq2kwjtwvahns3n0tf8j6kjxggkkz4mff499ge7xzsz',
|
||||||
'naddr1qvzqqqr4gupzpa9lr76m4zlg88mscue3wvlrp8mcpq3txy0k8cqlnhy2hw6z37x4qqjrycf5vyunyd34943kydn9956rycmp943xydpc95cxge3cvguxgcmyxsmkyzpyj60'
|
'naddr1qvzqqqr4gupzpa9lr76m4zlg88mscue3wvlrp8mcpq3txy0k8cqlnhy2hw6z37x4qq2573jhg9trsu6vgav9gnn4dffkzk2ww3yrjejnc2s'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
// we use this object to check if a user has reacted positively or negatively to a post
|
// we use this object to check if a user has reacted positively or negatively to a post
|
||||||
|
@ -74,6 +74,7 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.onunhandledrejection = async (event: PromiseRejectionEvent) => {
|
window.onunhandledrejection = async (event: PromiseRejectionEvent) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
console.log(event.reason)
|
||||||
if (event.reason?.name === Dexie.errnames.DatabaseClosed) {
|
if (event.reason?.name === Dexie.errnames.DatabaseClosed) {
|
||||||
console.log(
|
console.log(
|
||||||
'Could not open Dexie DB, probably version change. Deleting old DB and reloading...'
|
'Could not open Dexie DB, probably version change. Deleting old DB and reloading...'
|
||||||
@ -244,7 +245,7 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
// Find the user's relays (10s timeout).
|
// Find the user's relays (10s timeout).
|
||||||
const relayUrls = await Promise.race([
|
const relayUrls = await Promise.race([
|
||||||
getRelayListForUser(hexKey, ndk),
|
getRelayListForUser(hexKey, ndk),
|
||||||
timeout(3000)
|
timeout(10000)
|
||||||
])
|
])
|
||||||
.then((ndkRelayList) => {
|
.then((ndkRelayList) => {
|
||||||
if (ndkRelayList) return ndkRelayList[userRelaysType]
|
if (ndkRelayList) return ndkRelayList[userRelaysType]
|
||||||
@ -264,9 +265,7 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
.fetchEvents(
|
.fetchEvents(
|
||||||
filter,
|
filter,
|
||||||
{ closeOnEose: true, cacheUsage: NDKSubscriptionCacheUsage.PARALLEL },
|
{ closeOnEose: true, cacheUsage: NDKSubscriptionCacheUsage.PARALLEL },
|
||||||
relayUrls.length
|
NDKRelaySet.fromRelayUrls(relayUrls, ndk, true)
|
||||||
? NDKRelaySet.fromRelayUrls(relayUrls, ndk, true)
|
|
||||||
: undefined
|
|
||||||
)
|
)
|
||||||
.then((ndkEventSet) => {
|
.then((ndkEventSet) => {
|
||||||
const ndkEvents = Array.from(ndkEventSet)
|
const ndkEvents = Array.from(ndkEventSet)
|
||||||
@ -370,7 +369,7 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
if (!event.sig) throw new Error('Before publishing first sign the event!')
|
if (!event.sig) throw new Error('Before publishing first sign the event!')
|
||||||
|
|
||||||
return event
|
return event
|
||||||
.publish(undefined, 10000)
|
.publish(undefined, 30000)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
const relaysPublishedOn = Array.from(res)
|
const relaysPublishedOn = Array.from(res)
|
||||||
return relaysPublishedOn.map((relay) => relay.url)
|
return relaysPublishedOn.map((relay) => relay.url)
|
||||||
|
@ -66,9 +66,7 @@ export const useComments = (
|
|||||||
closeOnEose: false,
|
closeOnEose: false,
|
||||||
cacheUsage: NDKSubscriptionCacheUsage.CACHE_FIRST
|
cacheUsage: NDKSubscriptionCacheUsage.CACHE_FIRST
|
||||||
},
|
},
|
||||||
relayUrls.size
|
NDKRelaySet.fromRelayUrls(Array.from(relayUrls), ndk)
|
||||||
? NDKRelaySet.fromRelayUrls(Array.from(relayUrls), ndk)
|
|
||||||
: undefined
|
|
||||||
)
|
)
|
||||||
|
|
||||||
subscription.on('event', (ndkEvent) => {
|
subscription.on('event', (ndkEvent) => {
|
||||||
|
@ -6,12 +6,9 @@ import {
|
|||||||
ModeratedFilter,
|
ModeratedFilter,
|
||||||
MuteLists,
|
MuteLists,
|
||||||
NSFWFilter,
|
NSFWFilter,
|
||||||
SortBy,
|
SortBy
|
||||||
WOTFilterOptions
|
|
||||||
} from 'types'
|
} from 'types'
|
||||||
import { npubToHex } from 'utils'
|
import { npubToHex } from 'utils'
|
||||||
import { useAppSelector } from './redux'
|
|
||||||
import { isInWoT } from 'utils/wot'
|
|
||||||
|
|
||||||
export const useFilteredMods = (
|
export const useFilteredMods = (
|
||||||
mods: ModDetails[],
|
mods: ModDetails[],
|
||||||
@ -24,10 +21,6 @@ export const useFilteredMods = (
|
|||||||
},
|
},
|
||||||
author?: string | undefined
|
author?: string | undefined
|
||||||
) => {
|
) => {
|
||||||
const { siteWot, siteWotLevel, userWot, userWotLevel } = useAppSelector(
|
|
||||||
(state) => state.wot
|
|
||||||
)
|
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const nsfwFilter = (mods: ModDetails[]) => {
|
const nsfwFilter = (mods: ModDetails[]) => {
|
||||||
// Add nsfw tag to mods included in nsfwList
|
// Add nsfw tag to mods included in nsfwList
|
||||||
@ -53,49 +46,8 @@ export const useFilteredMods = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const wotFilter = (mods: ModDetails[]) => {
|
|
||||||
// Determine the filtering logic based on the WOT filter option and user state
|
|
||||||
// when user is not logged in use Site_Only
|
|
||||||
if (!userState.auth) {
|
|
||||||
return mods.filter((mod) => isInWoT(siteWot, siteWotLevel, mod.author))
|
|
||||||
}
|
|
||||||
// when user is logged, allow other filter selections
|
|
||||||
const isWoTNpub =
|
|
||||||
userState.user?.npub === import.meta.env.VITE_SITE_WOT_NPUB
|
|
||||||
switch (filterOptions.wot) {
|
|
||||||
case WOTFilterOptions.None:
|
|
||||||
// Only admins can choose None, use siteWoT for others
|
|
||||||
return isWoTNpub
|
|
||||||
? mods
|
|
||||||
: mods.filter((mod) => isInWoT(siteWot, siteWotLevel, mod.author))
|
|
||||||
case WOTFilterOptions.Exclude:
|
|
||||||
// Only admins can choose Exlude, use siteWot for others
|
|
||||||
// Exlude returns the mods not in the site's WoT
|
|
||||||
return isWoTNpub
|
|
||||||
? mods.filter((mod) => !isInWoT(siteWot, siteWotLevel, mod.author))
|
|
||||||
: mods.filter((mod) => isInWoT(siteWot, siteWotLevel, mod.author))
|
|
||||||
case WOTFilterOptions.Site_Only:
|
|
||||||
return mods.filter((mod) =>
|
|
||||||
isInWoT(siteWot, siteWotLevel, mod.author)
|
|
||||||
)
|
|
||||||
case WOTFilterOptions.Mine_Only:
|
|
||||||
// Only admins can choose Mine_Only, use siteWoT for others
|
|
||||||
return isWoTNpub
|
|
||||||
? mods.filter((mod) => isInWoT(userWot, userWotLevel, mod.author))
|
|
||||||
: mods.filter((mod) => isInWoT(siteWot, siteWotLevel, mod.author))
|
|
||||||
case WOTFilterOptions.Site_And_Mine:
|
|
||||||
return mods.filter(
|
|
||||||
(mod) =>
|
|
||||||
isInWoT(siteWot, siteWotLevel, mod.author) ||
|
|
||||||
isInWoT(userWot, userWotLevel, mod.author)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let filtered = nsfwFilter(mods)
|
let filtered = nsfwFilter(mods)
|
||||||
|
|
||||||
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
|
||||||
const isOwner =
|
const isOwner =
|
||||||
userState.user?.npub &&
|
userState.user?.npub &&
|
||||||
@ -129,19 +81,13 @@ export const useFilteredMods = (
|
|||||||
|
|
||||||
return filtered
|
return filtered
|
||||||
}, [
|
}, [
|
||||||
userState.auth,
|
|
||||||
userState.user?.npub,
|
userState.user?.npub,
|
||||||
filterOptions.sort,
|
filterOptions.sort,
|
||||||
filterOptions.moderated,
|
filterOptions.moderated,
|
||||||
filterOptions.wot,
|
|
||||||
filterOptions.nsfw,
|
filterOptions.nsfw,
|
||||||
author,
|
author,
|
||||||
mods,
|
mods,
|
||||||
muteLists,
|
muteLists,
|
||||||
nsfwList,
|
nsfwList
|
||||||
siteWot,
|
|
||||||
siteWotLevel,
|
|
||||||
userWot,
|
|
||||||
userWotLevel
|
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
@ -10,29 +10,11 @@ const useLocalStorageSubscribe = (callback: () => void) => {
|
|||||||
return () => window.removeEventListener('storage', callback)
|
return () => window.removeEventListener('storage', callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergeWithInitialValue<T>(storedValue: T, initialValue: T): T {
|
|
||||||
if (typeof storedValue === 'object' && storedValue !== null) {
|
|
||||||
return { ...initialValue, ...storedValue }
|
|
||||||
}
|
|
||||||
return storedValue
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useLocalStorage<T>(
|
export function useLocalStorage<T>(
|
||||||
key: string,
|
key: string,
|
||||||
initialValue: T
|
initialValue: T
|
||||||
): [T, React.Dispatch<React.SetStateAction<T>>] {
|
): [T, React.Dispatch<React.SetStateAction<T>>] {
|
||||||
const getSnapshot = () => {
|
const getSnapshot = () => getLocalStorageItem(key, initialValue)
|
||||||
// Get the stored value
|
|
||||||
const storedValue = getLocalStorageItem(key, initialValue)
|
|
||||||
|
|
||||||
// Parse the value
|
|
||||||
const parsedStoredValue = JSON.parse(storedValue)
|
|
||||||
|
|
||||||
// Merge the default and the stored in case some of the required fields are missing
|
|
||||||
return JSON.stringify(
|
|
||||||
mergeWithInitialValue(parsedStoredValue, initialValue)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = React.useSyncExternalStore(useLocalStorageSubscribe, getSnapshot)
|
const data = React.useSyncExternalStore(useLocalStorageSubscribe, getSnapshot)
|
||||||
|
|
||||||
@ -53,7 +35,7 @@ export function useLocalStorage<T>(
|
|||||||
console.warn(e)
|
console.warn(e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[data, key]
|
[key, data]
|
||||||
)
|
)
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
@ -16,7 +16,7 @@ export const Footer = () => {
|
|||||||
by
|
by
|
||||||
<a
|
<a
|
||||||
className={styles.secMainFooterParaLink}
|
className={styles.secMainFooterParaLink}
|
||||||
href='https://degmods.com/profile/nprofile1qqsre6jgq6c7r2vzn5cdtju20qq36sn3cer5avc4x8kfru5pzrlr7sqnancjp'
|
href='https://primal.net/p/npub18n4ysp43ux5c98fs6h9c57qpr4p8r3j8f6e32v0vj8egzy878aqqyzzk9r'
|
||||||
target='_blank'
|
target='_blank'
|
||||||
>
|
>
|
||||||
Freakoverse
|
Freakoverse
|
||||||
|
@ -21,7 +21,6 @@ import '../styles/popup.css'
|
|||||||
import { npubToHex } from '../utils'
|
import { npubToHex } from '../utils'
|
||||||
import logo from '../assets/img/DEG Mods Logo With Text.svg'
|
import logo from '../assets/img/DEG Mods Logo With Text.svg'
|
||||||
import placeholder from '../assets/img/DEG Mods Default PP.png'
|
import placeholder from '../assets/img/DEG Mods Default PP.png'
|
||||||
import { resetUserWot } from 'store/reducers/wot'
|
|
||||||
|
|
||||||
export const Header = () => {
|
export const Header = () => {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
@ -49,7 +48,6 @@ export const Header = () => {
|
|||||||
if (opts.type === 'logout') {
|
if (opts.type === 'logout') {
|
||||||
dispatch(setAuth(null))
|
dispatch(setAuth(null))
|
||||||
dispatch(setUser(null))
|
dispatch(setUser(null))
|
||||||
dispatch(resetUserWot())
|
|
||||||
} else {
|
} else {
|
||||||
dispatch(
|
dispatch(
|
||||||
setAuth({
|
setAuth({
|
||||||
@ -225,7 +223,7 @@ export const Header = () => {
|
|||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
className={navStyles.NavMainBottomInsideOtherLink}
|
className={navStyles.NavMainBottomInsideOtherLink}
|
||||||
href='https://degmods.com/profile/nprofile1qqs0f0clkkagh6pe7ux8xvtn8ccf77qgy2e3ra37q8uaez4mks5034gfw4xg6'
|
href='https://primal.net/p/npub17jl3ldd6305rnacvwvchx03snauqsg4nz8mruq0emj9thdpglr2sst825x'
|
||||||
target='_blank'
|
target='_blank'
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
@ -381,29 +379,13 @@ const RegisterButtonWithDialog = () => {
|
|||||||
Browser Extensions (Windows)
|
Browser Extensions (Windows)
|
||||||
</label>
|
</label>
|
||||||
<p className='labelDescriptionMain'>
|
<p className='labelDescriptionMain'>
|
||||||
Once you create your "account" on any of these, come
|
Once you create your "account" on any of these, come back and click login, then sign-in with
|
||||||
back and click login, then sign-in with extension.
|
extension. Here's a quick video guide, and here's a <a
|
||||||
Here's a quick video guide, and here's a{' '}
|
href='https://degmods.com/blog/naddr1qvzqqqr4gupzpa9lr76m4zlg88mscue3wvlrp8mcpq3txy0k8cqlnhy2hw6z37x4qqjrzcfc8qurjefn943xyen9956rywp595unjc3h94nxvwfexymxxcfnvdjxxlyq37c'
|
||||||
<a href='https://degmods.com/blog/naddr1qvzqqqr4gupzpa9lr76m4zlg88mscue3wvlrp8mcpq3txy0k8cqlnhy2hw6z37x4qqjrzcfc8qurjefn943xyen9956rywp595unjc3h94nxvwfexymxxcfnvdjxxlyq37c'>
|
>guide post</a> to help with this process.</p>
|
||||||
guide post
|
<div style={{ width: '100%', height: 'auto', borderRadius: '8px', overflow: 'hidden' }}>
|
||||||
</a>{' '}
|
<video controls style={{ width: '100%' }}><source src="https://video.nostr.build/765aa9bf16dd58bca701efee2572f7e77f29b2787cddd2bee8bbbdea35798153.mp4" type="video/mp4" />
|
||||||
to help with this process.
|
Your browser does not support the video tag.</video>
|
||||||
</p>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
height: 'auto',
|
|
||||||
borderRadius: '8px',
|
|
||||||
overflow: 'hidden'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<video controls style={{ width: '100%' }}>
|
|
||||||
<source
|
|
||||||
src='https://video.nostr.build/765aa9bf16dd58bca701efee2572f7e77f29b2787cddd2bee8bbbdea35798153.mp4'
|
|
||||||
type='video/mp4'
|
|
||||||
/>
|
|
||||||
Your browser does not support the video tag.
|
|
||||||
</video>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
|
@ -3,122 +3,13 @@ import { Footer } from './footer'
|
|||||||
import { Header } from './header'
|
import { Header } from './header'
|
||||||
import { SocialNav } from './socialNav'
|
import { SocialNav } from './socialNav'
|
||||||
import { Head } from './head'
|
import { Head } from './head'
|
||||||
import { useAppDispatch, useAppSelector, useNDKContext } from 'hooks'
|
|
||||||
import { useEffect } from 'react'
|
|
||||||
import { npubToHex } from 'utils'
|
|
||||||
import { calculateWot } from 'utils/wot'
|
|
||||||
import {
|
|
||||||
setSiteWot,
|
|
||||||
setSiteWotLevel,
|
|
||||||
setSiteWotStatus,
|
|
||||||
setUserWot,
|
|
||||||
setUserWotLevel,
|
|
||||||
WOTStatus
|
|
||||||
} from 'store/reducers/wot'
|
|
||||||
import { toast } from 'react-toastify'
|
|
||||||
import { LoadingSpinner } from 'components/LoadingSpinner'
|
|
||||||
import { NDKKind } from '@nostr-dev-kit/ndk'
|
|
||||||
import { UserRelaysType } from 'types'
|
|
||||||
|
|
||||||
export const Layout = () => {
|
export const Layout = () => {
|
||||||
const dispatch = useAppDispatch()
|
|
||||||
const { ndk, fetchEventFromUserRelays } = useNDKContext()
|
|
||||||
const userState = useAppSelector((state) => state.user)
|
|
||||||
const { siteWotStatus } = useAppSelector((state) => state.wot)
|
|
||||||
|
|
||||||
// calculate site's wot
|
|
||||||
useEffect(() => {
|
|
||||||
if (ndk) {
|
|
||||||
const SITE_WOT_NPUB = import.meta.env.VITE_SITE_WOT_NPUB
|
|
||||||
const hexPubkey = npubToHex(SITE_WOT_NPUB)
|
|
||||||
if (hexPubkey) {
|
|
||||||
dispatch(setSiteWotStatus(WOTStatus.LOADING))
|
|
||||||
calculateWot(hexPubkey, ndk)
|
|
||||||
.then((wot) => {
|
|
||||||
dispatch(setSiteWot(wot))
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.trace('An error occurred in calculating site WOT', err)
|
|
||||||
toast.error('An error occurred in calculating site web-of-trust!')
|
|
||||||
dispatch(setSiteWotStatus(WOTStatus.FAILED))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [ndk, dispatch])
|
|
||||||
|
|
||||||
// calculate user's wot
|
|
||||||
useEffect(() => {
|
|
||||||
if (ndk && userState.user?.pubkey) {
|
|
||||||
const hexPubkey = npubToHex(userState.user.pubkey as string)
|
|
||||||
if (hexPubkey)
|
|
||||||
calculateWot(hexPubkey, ndk)
|
|
||||||
.then((wot) => {
|
|
||||||
dispatch(setUserWot(wot))
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.trace('An error occurred in calculating user WOT', err)
|
|
||||||
toast.error('An error occurred in calculating user web-of-trust!')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [ndk, userState.user, dispatch])
|
|
||||||
|
|
||||||
// get site's wot level
|
|
||||||
useEffect(() => {
|
|
||||||
const SITE_WOT_NPUB = import.meta.env.VITE_SITE_WOT_NPUB
|
|
||||||
const hexPubkey = npubToHex(SITE_WOT_NPUB)
|
|
||||||
if (hexPubkey) {
|
|
||||||
fetchEventFromUserRelays(
|
|
||||||
{
|
|
||||||
kinds: [NDKKind.AppSpecificData],
|
|
||||||
'#d': ['degmods'],
|
|
||||||
authors: [hexPubkey]
|
|
||||||
},
|
|
||||||
hexPubkey,
|
|
||||||
UserRelaysType.Both
|
|
||||||
).then((event) => {
|
|
||||||
if (event) {
|
|
||||||
const wot = event.tagValue('wot')
|
|
||||||
if (wot) dispatch(setSiteWotLevel(parseInt(wot)))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [dispatch, fetchEventFromUserRelays])
|
|
||||||
|
|
||||||
// get user's wot level
|
|
||||||
useEffect(() => {
|
|
||||||
if (userState.user?.pubkey) {
|
|
||||||
const hexPubkey = npubToHex(userState.user.pubkey as string)
|
|
||||||
|
|
||||||
if (hexPubkey) {
|
|
||||||
fetchEventFromUserRelays(
|
|
||||||
{
|
|
||||||
kinds: [NDKKind.AppSpecificData],
|
|
||||||
'#d': ['degmods'],
|
|
||||||
authors: [hexPubkey]
|
|
||||||
},
|
|
||||||
hexPubkey,
|
|
||||||
UserRelaysType.Both
|
|
||||||
).then((event) => {
|
|
||||||
if (event) {
|
|
||||||
const wot = event.tagValue('wot')
|
|
||||||
if (wot) dispatch(setUserWotLevel(parseInt(wot)))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [userState.user, dispatch, fetchEventFromUserRelays])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head />
|
<Head />
|
||||||
<Header />
|
<Header />
|
||||||
{siteWotStatus === WOTStatus.LOADED && <Outlet />}
|
<Outlet />
|
||||||
{siteWotStatus === WOTStatus.LOADING && (
|
|
||||||
<LoadingSpinner desc="Loading site's web-of-trust" />
|
|
||||||
)}
|
|
||||||
{siteWotStatus === WOTStatus.FAILED && (
|
|
||||||
<h3>Failed to load site's web-of-trust</h3>
|
|
||||||
)}
|
|
||||||
<Footer />
|
<Footer />
|
||||||
<SocialNav />
|
<SocialNav />
|
||||||
<ScrollRestoration />
|
<ScrollRestoration />
|
||||||
|
@ -22,6 +22,7 @@ export const ReportPopup = ({ handleClose }: ReportPopupProps) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (fetcher.data) {
|
if (fetcher.data) {
|
||||||
const { isSent } = fetcher.data
|
const { isSent } = fetcher.data
|
||||||
|
console.log(fetcher.data)
|
||||||
if (isSent) {
|
if (isSent) {
|
||||||
handleClose()
|
handleClose()
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import { GameCard } from '../components/GameCard'
|
|||||||
import { ModCard } from '../components/ModCard'
|
import { ModCard } from '../components/ModCard'
|
||||||
import { LANDING_PAGE_DATA, PROFILE_BLOG_FILTER_LIMIT } from '../constants'
|
import { LANDING_PAGE_DATA, PROFILE_BLOG_FILTER_LIMIT } from '../constants'
|
||||||
import {
|
import {
|
||||||
useAppSelector,
|
|
||||||
useDidMount,
|
useDidMount,
|
||||||
useGames,
|
useGames,
|
||||||
useLocalStorage,
|
useLocalStorage,
|
||||||
@ -38,7 +37,6 @@ import 'swiper/css/navigation'
|
|||||||
import 'swiper/css/pagination'
|
import 'swiper/css/pagination'
|
||||||
import { LoadingSpinner } from 'components/LoadingSpinner'
|
import { LoadingSpinner } from 'components/LoadingSpinner'
|
||||||
import { Spinner } from 'components/Spinner'
|
import { Spinner } from 'components/Spinner'
|
||||||
import { isInWoT } from 'utils/wot'
|
|
||||||
|
|
||||||
export const HomePage = () => {
|
export const HomePage = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
@ -249,9 +247,6 @@ const DisplayMod = ({ naddr }: DisplayModProps) => {
|
|||||||
const DisplayLatestMods = () => {
|
const DisplayLatestMods = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { fetchMods } = useNDKContext()
|
const { fetchMods } = useNDKContext()
|
||||||
const { siteWot, siteWotLevel, userWot, userWotLevel } = useAppSelector(
|
|
||||||
(state) => state.wot
|
|
||||||
)
|
|
||||||
const [isFetchingLatestMods, setIsFetchingLatestMods] = useState(true)
|
const [isFetchingLatestMods, setIsFetchingLatestMods] = useState(true)
|
||||||
const [latestMods, setLatestMods] = useState<ModDetails[]>([])
|
const [latestMods, setLatestMods] = useState<ModDetails[]>([])
|
||||||
|
|
||||||
@ -263,12 +258,7 @@ const DisplayLatestMods = () => {
|
|||||||
.then((mods) => {
|
.then((mods) => {
|
||||||
// Sort by the latest (published_at descending)
|
// Sort by the latest (published_at descending)
|
||||||
mods.sort((a, b) => b.published_at - a.published_at)
|
mods.sort((a, b) => b.published_at - a.published_at)
|
||||||
const wotFilteredMods = mods.filter(
|
setLatestMods(mods)
|
||||||
(mod) =>
|
|
||||||
isInWoT(siteWot, siteWotLevel, mod.author) ||
|
|
||||||
isInWoT(userWot, userWotLevel, mod.author)
|
|
||||||
)
|
|
||||||
setLatestMods(wotFilteredMods)
|
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setIsFetchingLatestMods(false)
|
setIsFetchingLatestMods(false)
|
||||||
|
@ -1,116 +1,6 @@
|
|||||||
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk'
|
|
||||||
import { LoadingSpinner } from 'components/LoadingSpinner'
|
|
||||||
import { useAppDispatch, useAppSelector, useNDKContext } from 'hooks'
|
|
||||||
import { kinds, UnsignedEvent, Event } from 'nostr-tools'
|
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
import { toast } from 'react-toastify'
|
|
||||||
import { setSiteWotLevel, setUserWotLevel } from 'store/reducers/wot'
|
|
||||||
import { UserRelaysType } from 'types'
|
|
||||||
import { log, LogType, now, npubToHex } from 'utils'
|
|
||||||
|
|
||||||
// todo: use components from Input.tsx
|
// todo: use components from Input.tsx
|
||||||
export const PreferencesSetting = () => {
|
export const PreferencesSetting = () => {
|
||||||
const { ndk, fetchEventFromUserRelays, publish } = useNDKContext()
|
|
||||||
const dispatch = useAppDispatch()
|
|
||||||
|
|
||||||
const user = useAppSelector((state) => state.user.user)
|
|
||||||
const { userWotLevel } = useAppSelector((state) => state.wot)
|
|
||||||
|
|
||||||
const [wotLevel, setWotLevel] = useState(userWotLevel)
|
|
||||||
const [isSaving, setIsSaving] = useState(false)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (user?.pubkey) {
|
|
||||||
const hexPubkey = user.pubkey as string
|
|
||||||
fetchEventFromUserRelays(
|
|
||||||
{
|
|
||||||
kinds: [NDKKind.AppSpecificData],
|
|
||||||
'#d': ['degmods'],
|
|
||||||
authors: [hexPubkey]
|
|
||||||
},
|
|
||||||
hexPubkey,
|
|
||||||
UserRelaysType.Both
|
|
||||||
).then((event) => {
|
|
||||||
if (event) {
|
|
||||||
const wot = event.tagValue('wot')
|
|
||||||
if (wot) setWotLevel(parseInt(wot))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [user, fetchEventFromUserRelays])
|
|
||||||
|
|
||||||
const handleSave = async () => {
|
|
||||||
setIsSaving(true)
|
|
||||||
|
|
||||||
let hexPubkey: string
|
|
||||||
|
|
||||||
if (user?.pubkey) {
|
|
||||||
hexPubkey = user.pubkey as string
|
|
||||||
} else {
|
|
||||||
hexPubkey = (await window.nostr?.getPublicKey()) as string
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hexPubkey) {
|
|
||||||
toast.error('Could not get pubkey')
|
|
||||||
setIsSaving(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsignedEvent: UnsignedEvent = {
|
|
||||||
kind: kinds.Application,
|
|
||||||
created_at: now(),
|
|
||||||
pubkey: hexPubkey,
|
|
||||||
content: '',
|
|
||||||
tags: [
|
|
||||||
['d', 'degmods'],
|
|
||||||
['wot', wotLevel.toString()]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
const signedEvent = await window.nostr
|
|
||||||
?.signEvent(unsignedEvent)
|
|
||||||
.then((event) => event as Event)
|
|
||||||
.catch((err) => {
|
|
||||||
toast.error('Failed to sign the event!')
|
|
||||||
log(true, LogType.Error, 'Failed to sign the event!', err)
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!signedEvent) {
|
|
||||||
setIsSaving(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const ndkEvent = new NDKEvent(ndk, signedEvent)
|
|
||||||
await publish(ndkEvent)
|
|
||||||
.then((publishedOnRelays) => {
|
|
||||||
toast.success(
|
|
||||||
`Preferences published to following relays: \n\n${publishedOnRelays.join(
|
|
||||||
'\n'
|
|
||||||
)}`
|
|
||||||
)
|
|
||||||
|
|
||||||
dispatch(setUserWotLevel(wotLevel))
|
|
||||||
|
|
||||||
// If wot admin, update site wot level too
|
|
||||||
const SITE_WOT_NPUB = import.meta.env.VITE_SITE_WOT_NPUB
|
|
||||||
const siteWotPubkey = npubToHex(SITE_WOT_NPUB)
|
|
||||||
if (siteWotPubkey === hexPubkey) {
|
|
||||||
dispatch(setSiteWotLevel(wotLevel))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error(err)
|
|
||||||
toast.error('Error: Failed to publish preferences!')
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setIsSaving(false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
{isSaving && <LoadingSpinner desc='Saving preferences to relays' />}
|
|
||||||
<div className='IBMSMSplitMainFullSideFWMid'>
|
<div className='IBMSMSplitMainFullSideFWMid'>
|
||||||
<div className='IBMSMSplitMainFullSideSec'>
|
<div className='IBMSMSplitMainFullSideSec'>
|
||||||
<div className='IBMSMSMBS_Write'>
|
<div className='IBMSMSMBS_Write'>
|
||||||
@ -127,7 +17,6 @@ export const PreferencesSetting = () => {
|
|||||||
className='CheckboxMain'
|
className='CheckboxMain'
|
||||||
name='notificationsSettings'
|
name='notificationsSettings'
|
||||||
checked
|
checked
|
||||||
readOnly
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='inputLabelWrapperMain inputLabelWrapperMainAlt inputLabelWrapperMainAltStylized'>
|
<div className='inputLabelWrapperMain inputLabelWrapperMainAlt inputLabelWrapperMainAltStylized'>
|
||||||
@ -139,7 +28,6 @@ export const PreferencesSetting = () => {
|
|||||||
className='CheckboxMain'
|
className='CheckboxMain'
|
||||||
name='notificationsSettings'
|
name='notificationsSettings'
|
||||||
checked
|
checked
|
||||||
readOnly
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='inputLabelWrapperMain inputLabelWrapperMainAlt inputLabelWrapperMainAltStylized'>
|
<div className='inputLabelWrapperMain inputLabelWrapperMainAlt inputLabelWrapperMainAltStylized'>
|
||||||
@ -151,7 +39,6 @@ export const PreferencesSetting = () => {
|
|||||||
className='CheckboxMain'
|
className='CheckboxMain'
|
||||||
name='notificationsSettings'
|
name='notificationsSettings'
|
||||||
checked
|
checked
|
||||||
readOnly
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='inputLabelWrapperMain inputLabelWrapperMainAlt inputLabelWrapperMainAltStylized'>
|
<div className='inputLabelWrapperMain inputLabelWrapperMainAlt inputLabelWrapperMainAltStylized'>
|
||||||
@ -163,7 +50,6 @@ export const PreferencesSetting = () => {
|
|||||||
className='CheckboxMain'
|
className='CheckboxMain'
|
||||||
name='notificationsSettings'
|
name='notificationsSettings'
|
||||||
checked
|
checked
|
||||||
readOnly
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='inputLabelWrapperMain inputLabelWrapperMainAlt inputLabelWrapperMainAltStylized'>
|
<div className='inputLabelWrapperMain inputLabelWrapperMainAlt inputLabelWrapperMainAltStylized'>
|
||||||
@ -175,7 +61,6 @@ export const PreferencesSetting = () => {
|
|||||||
className='CheckboxMain'
|
className='CheckboxMain'
|
||||||
name='notificationsSettings'
|
name='notificationsSettings'
|
||||||
checked
|
checked
|
||||||
readOnly
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -208,13 +93,12 @@ export const PreferencesSetting = () => {
|
|||||||
type='range'
|
type='range'
|
||||||
max='100'
|
max='100'
|
||||||
min='0'
|
min='0'
|
||||||
value={wotLevel}
|
value='10'
|
||||||
onChange={(e) => setWotLevel(parseInt(e.target.value))}
|
|
||||||
step='1'
|
step='1'
|
||||||
required
|
required
|
||||||
name='WoTLevel'
|
name='WoTLevel'
|
||||||
/>
|
/>
|
||||||
<p className='ZapSplitUserBoxRangeText'>{wotLevel}</p>
|
<p className='ZapSplitUserBoxRangeText'>10</p>
|
||||||
</div>
|
</div>
|
||||||
<div className='inputLabelWrapperMain inputLabelWrapperMainAlt inputLabelWrapperMainAltStylized'>
|
<div className='inputLabelWrapperMain inputLabelWrapperMainAlt inputLabelWrapperMainAltStylized'>
|
||||||
<label className='form-label labelMain'>
|
<label className='form-label labelMain'>
|
||||||
@ -225,22 +109,16 @@ export const PreferencesSetting = () => {
|
|||||||
className='CheckboxMain'
|
className='CheckboxMain'
|
||||||
name='WoTZap'
|
name='WoTZap'
|
||||||
checked
|
checked
|
||||||
readOnly
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='IBMSMSMBS_WriteAction'>
|
<div className='IBMSMSMBS_WriteAction'>
|
||||||
<button
|
<button className='btn btnMain' type='button'>
|
||||||
className='btn btnMain'
|
|
||||||
type='button'
|
|
||||||
onClick={handleSave}
|
|
||||||
>
|
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import { Event, kinds, UnsignedEvent } from 'nostr-tools'
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { UserRelaysType } from 'types'
|
import { UserRelaysType } from 'types'
|
||||||
import { log, LogType, normalizeWebSocketURL, now, timeout } from 'utils'
|
import { log, LogType, normalizeWebSocketURL, now } from 'utils'
|
||||||
|
|
||||||
const READ_MARKER = 'read'
|
const READ_MARKER = 'read'
|
||||||
const WRITE_MARKER = 'write'
|
const WRITE_MARKER = 'write'
|
||||||
@ -21,16 +21,12 @@ export const RelaySettings = () => {
|
|||||||
const userState = useAppSelector((state) => state.user)
|
const userState = useAppSelector((state) => state.user)
|
||||||
const [ndkRelayList, setNDKRelayList] = useState<NDKRelayList | null>(null)
|
const [ndkRelayList, setNDKRelayList] = useState<NDKRelayList | null>(null)
|
||||||
const [isPublishing, setIsPublishing] = useState(false)
|
const [isPublishing, setIsPublishing] = useState(false)
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
|
||||||
const [inputValue, setInputValue] = useState('')
|
const [inputValue, setInputValue] = useState('')
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (userState.auth && userState.user?.pubkey) {
|
if (userState.auth && userState.user?.pubkey) {
|
||||||
setIsLoading(true)
|
getRelayListForUser(userState.user.pubkey as string, ndk)
|
||||||
Promise.race([
|
|
||||||
getRelayListForUser(userState.user?.pubkey as string, ndk),
|
|
||||||
timeout(10000)
|
|
||||||
])
|
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setNDKRelayList(res)
|
setNDKRelayList(res)
|
||||||
})
|
})
|
||||||
@ -40,13 +36,9 @@ export const RelaySettings = () => {
|
|||||||
err.message || err
|
err.message || err
|
||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
setNDKRelayList(new NDKRelayList(ndk))
|
setNDKRelayList(null)
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setIsLoading(false)
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
setIsLoading(false)
|
|
||||||
setNDKRelayList(null)
|
setNDKRelayList(null)
|
||||||
}
|
}
|
||||||
}, [userState, ndk])
|
}, [userState, ndk])
|
||||||
@ -232,14 +224,6 @@ export const RelaySettings = () => {
|
|||||||
setIsPublishing(false)
|
setIsPublishing(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLoading)
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div></div>
|
|
||||||
<LoadingSpinner desc='Loading' />
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!ndkRelayList)
|
if (!ndkRelayList)
|
||||||
return <div>Could not fetch user relay list or user is not logged in </div>
|
return <div>Could not fetch user relay list or user is not logged in </div>
|
||||||
|
|
||||||
@ -274,12 +258,6 @@ export const RelaySettings = () => {
|
|||||||
<div className='inputLabelWrapperMain'>
|
<div className='inputLabelWrapperMain'>
|
||||||
<label className='form-label labelMain'>Your relays</label>
|
<label className='form-label labelMain'>Your relays</label>
|
||||||
</div>
|
</div>
|
||||||
{relayEntries.length === 0 && (
|
|
||||||
<>
|
|
||||||
We recommend adding one of our relays if you're planning to
|
|
||||||
frequently use DEG Mods, for a better experience.
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{relayEntries.map(([relayUrl, relayType]) => (
|
{relayEntries.map(([relayUrl, relayType]) => (
|
||||||
<RelayListItem
|
<RelayListItem
|
||||||
key={relayUrl}
|
key={relayUrl}
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import { configureStore } from '@reduxjs/toolkit'
|
import { configureStore } from '@reduxjs/toolkit'
|
||||||
import userReducer from './reducers/user'
|
import userReducer from './reducers/user'
|
||||||
import wotReducer from './reducers/wot'
|
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
user: userReducer,
|
user: userReducer
|
||||||
wot: wotReducer
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
|
||||||
|
|
||||||
export enum WOTStatus {
|
|
||||||
IDLE, // Not started
|
|
||||||
LOADING, // Currently loading
|
|
||||||
LOADED, // Successfully loaded
|
|
||||||
FAILED // Failed to load
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IWOT {
|
|
||||||
siteWot: Record<string, number>
|
|
||||||
siteWotStatus: WOTStatus
|
|
||||||
siteWotLevel: number
|
|
||||||
userWot: Record<string, number>
|
|
||||||
userWotStatus: WOTStatus
|
|
||||||
userWotLevel: number
|
|
||||||
}
|
|
||||||
|
|
||||||
const initialState: IWOT = {
|
|
||||||
siteWot: {},
|
|
||||||
siteWotStatus: WOTStatus.IDLE,
|
|
||||||
siteWotLevel: 0,
|
|
||||||
userWot: {},
|
|
||||||
userWotStatus: WOTStatus.IDLE,
|
|
||||||
userWotLevel: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
export const wotSlice = createSlice({
|
|
||||||
name: 'wot',
|
|
||||||
initialState,
|
|
||||||
reducers: {
|
|
||||||
setSiteWot(state, action: PayloadAction<Record<string, number>>) {
|
|
||||||
state.siteWot = action.payload
|
|
||||||
state.siteWotStatus = WOTStatus.LOADED
|
|
||||||
},
|
|
||||||
setUserWot(state, action: PayloadAction<Record<string, number>>) {
|
|
||||||
state.userWot = action.payload
|
|
||||||
state.userWotStatus = WOTStatus.LOADED
|
|
||||||
},
|
|
||||||
resetUserWot(state) {
|
|
||||||
state.userWot = {}
|
|
||||||
state.userWotStatus = WOTStatus.IDLE
|
|
||||||
state.userWotLevel = 0
|
|
||||||
},
|
|
||||||
setSiteWotStatus(state, action: PayloadAction<WOTStatus>) {
|
|
||||||
state.siteWotStatus = action.payload
|
|
||||||
},
|
|
||||||
setUserWotStatus(state, action: PayloadAction<WOTStatus>) {
|
|
||||||
state.userWotStatus = action.payload
|
|
||||||
},
|
|
||||||
setSiteWotLevel(state, action: PayloadAction<number>) {
|
|
||||||
state.siteWotLevel = action.payload
|
|
||||||
},
|
|
||||||
setUserWotLevel(state, action: PayloadAction<number>) {
|
|
||||||
state.userWotLevel = action.payload
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export const {
|
|
||||||
setSiteWot,
|
|
||||||
setUserWot,
|
|
||||||
setSiteWotStatus,
|
|
||||||
setUserWotStatus,
|
|
||||||
setSiteWotLevel,
|
|
||||||
setUserWotLevel,
|
|
||||||
resetUserWot
|
|
||||||
} = wotSlice.actions
|
|
||||||
|
|
||||||
export default wotSlice.reducer
|
|
@ -19,7 +19,6 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-top: 56.25%;
|
padding-top: 56.25%;
|
||||||
border-bottom: solid 1px rgb(255 255 255 / 5%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.IBMSMSMBSSPostTitle {
|
.IBMSMSMBSSPostTitle {
|
||||||
@ -29,7 +28,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: rgb(255 255 255 / 85%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.IBMSMSMBSSPostBody {
|
.IBMSMSMBSSPostBody {
|
||||||
@ -40,7 +38,6 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
color: rgb(255 255 255 / 85%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.IBMSMSMBSSPostBody > div {
|
.IBMSMSMBSSPostBody > div {
|
||||||
@ -241,35 +238,3 @@
|
|||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.IBMSMSMBSSPostBody > div > div > p {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.IBMSMSMBSSPostBody > div > div > *:is(h1, h2, h3, h4, h5, h6) {
|
|
||||||
margin: 15px 0 15px 0 !important;
|
|
||||||
border-bottom: solid 1px rgb(255 255 255 / 10%);
|
|
||||||
padding: 0px 0 10px 0;
|
|
||||||
line-height: 1.5 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown.dropdownMain.dropdownMainBlogpost {
|
|
||||||
flex-grow: unset;
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
right: 10px;
|
|
||||||
background: rgba(0,0,0,0.1);
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.IBMSMSMBSSWarning {
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 10px 15px;
|
|
||||||
border: solid 2px tomato;
|
|
||||||
background: rgba(255,80,80,0.15);
|
|
||||||
color: rgba(255,255,255,0.95);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -17,18 +17,9 @@ export enum ModeratedFilter {
|
|||||||
Unmoderated_Fully = 'Unmoderated Fully'
|
Unmoderated_Fully = 'Unmoderated Fully'
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum WOTFilterOptions {
|
|
||||||
Site_And_Mine = 'Site & Mine',
|
|
||||||
Site_Only = 'Site Only',
|
|
||||||
Mine_Only = 'Mine Only',
|
|
||||||
None = 'None',
|
|
||||||
Exclude = 'Exclude'
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FilterOptions {
|
export interface FilterOptions {
|
||||||
sort: SortBy
|
sort: SortBy
|
||||||
nsfw: NSFWFilter
|
nsfw: NSFWFilter
|
||||||
source: string
|
source: string
|
||||||
moderated: ModeratedFilter
|
moderated: ModeratedFilter
|
||||||
wot: WOTFilterOptions
|
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,8 @@
|
|||||||
import {
|
import { FilterOptions, SortBy, NSFWFilter, ModeratedFilter } from 'types'
|
||||||
FilterOptions,
|
|
||||||
SortBy,
|
|
||||||
NSFWFilter,
|
|
||||||
ModeratedFilter,
|
|
||||||
WOTFilterOptions
|
|
||||||
} from 'types'
|
|
||||||
|
|
||||||
export const DEFAULT_FILTER_OPTIONS: FilterOptions = {
|
export const DEFAULT_FILTER_OPTIONS: FilterOptions = {
|
||||||
sort: SortBy.Latest,
|
sort: SortBy.Latest,
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
135
src/utils/wot.ts
135
src/utils/wot.ts
@ -1,135 +0,0 @@
|
|||||||
import NDK, {
|
|
||||||
Hexpubkey,
|
|
||||||
NDKFilter,
|
|
||||||
NDKKind,
|
|
||||||
NDKSubscriptionCacheUsage,
|
|
||||||
NDKTag
|
|
||||||
} from '@nostr-dev-kit/ndk'
|
|
||||||
import { nip19 } from 'nostr-tools'
|
|
||||||
|
|
||||||
interface UserRelations {
|
|
||||||
follows: Set<Hexpubkey>
|
|
||||||
muted: Set<Hexpubkey>
|
|
||||||
}
|
|
||||||
|
|
||||||
type Network = Map<Hexpubkey, UserRelations>
|
|
||||||
|
|
||||||
export const calculateWot = async (pubkey: Hexpubkey, ndk: NDK) => {
|
|
||||||
const WoT: Record<string, number> = {}
|
|
||||||
|
|
||||||
const userRelations = await findFollowsAndMuteUsers(pubkey, ndk)
|
|
||||||
const follows = Array.from(userRelations.follows)
|
|
||||||
const muted = Array.from(userRelations.muted)
|
|
||||||
|
|
||||||
// Add all the following users to WoT with score set to Positive_Infinity
|
|
||||||
follows.forEach((f) => {
|
|
||||||
WoT[f] = Number.POSITIVE_INFINITY
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add all the muted users to WoT with score set to Negative_Infinity
|
|
||||||
muted.forEach((m) => {
|
|
||||||
WoT[m] = Number.NEGATIVE_INFINITY
|
|
||||||
})
|
|
||||||
|
|
||||||
const network: Network = new Map()
|
|
||||||
|
|
||||||
// find the userRelations of every user in follow list
|
|
||||||
const promises = follows.map((user) =>
|
|
||||||
findFollowsAndMuteUsers(user, ndk).then((userRelations) => {
|
|
||||||
network.set(user, userRelations)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
await Promise.all(promises)
|
|
||||||
|
|
||||||
// make a list of all the users in the network either mutes or followed
|
|
||||||
const users = new Set<Hexpubkey>()
|
|
||||||
const userRelationsArray = Array.from(network.values())
|
|
||||||
userRelationsArray.forEach(({ follows, muted }) => {
|
|
||||||
follows.forEach((f) => users.add(f))
|
|
||||||
muted.forEach((m) => users.add(m))
|
|
||||||
})
|
|
||||||
|
|
||||||
users.forEach((user) => {
|
|
||||||
// Only calculate if it's not already added to WoT
|
|
||||||
if (!(user in WoT)) {
|
|
||||||
const wotScore = calculateWoTScore(user, network)
|
|
||||||
WoT[user] = wotScore
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return WoT
|
|
||||||
}
|
|
||||||
|
|
||||||
export const calculateWoTScore = (user: Hexpubkey, network: Network) => {
|
|
||||||
let wotScore = 0
|
|
||||||
|
|
||||||
// iterate over all the entries in the network and increment/decrement
|
|
||||||
// wotScore based on the list user in which exists (followed/mutes)
|
|
||||||
network.forEach(({ follows, muted }) => {
|
|
||||||
if (follows.has(user)) wotScore += 1
|
|
||||||
|
|
||||||
if (muted.has(user)) wotScore -= 1
|
|
||||||
})
|
|
||||||
|
|
||||||
return wotScore
|
|
||||||
}
|
|
||||||
|
|
||||||
export const findFollowsAndMuteUsers = async (
|
|
||||||
pubkey: string,
|
|
||||||
ndk: NDK
|
|
||||||
): Promise<UserRelations> => {
|
|
||||||
const follows = new Set<Hexpubkey>()
|
|
||||||
const muted = new Set<Hexpubkey>()
|
|
||||||
|
|
||||||
const filter: NDKFilter = {
|
|
||||||
kinds: [NDKKind.Contacts, NDKKind.MuteList],
|
|
||||||
authors: [pubkey]
|
|
||||||
}
|
|
||||||
|
|
||||||
const events = await ndk.fetchEvents(filter, {
|
|
||||||
groupable: false,
|
|
||||||
closeOnEose: true,
|
|
||||||
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL
|
|
||||||
})
|
|
||||||
|
|
||||||
events.forEach((event) => {
|
|
||||||
if (event.kind === NDKKind.Contacts) {
|
|
||||||
filterValidPTags(event.tags).forEach((f) => {
|
|
||||||
follows.add(f)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.kind === NDKKind.MuteList) {
|
|
||||||
filterValidPTags(event.tags).forEach((f) => {
|
|
||||||
muted.add(f)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
follows,
|
|
||||||
muted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const filterValidPTags = (tags: NDKTag[]) =>
|
|
||||||
tags
|
|
||||||
.filter((t: NDKTag) => t[0] === 'p')
|
|
||||||
.map((t: NDKTag) => t[1])
|
|
||||||
.filter((f: Hexpubkey) => {
|
|
||||||
try {
|
|
||||||
nip19.npubEncode(f)
|
|
||||||
return true
|
|
||||||
} catch {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export const isInWoT = (
|
|
||||||
WoT: Record<string, number>,
|
|
||||||
targetScore: number,
|
|
||||||
targetUser: string
|
|
||||||
): boolean => {
|
|
||||||
const wotScore = WoT[targetUser] ?? 0 // Default to 0 if the user is not in the record
|
|
||||||
return wotScore >= targetScore
|
|
||||||
}
|
|
1
src/vite-env.d.ts
vendored
1
src/vite-env.d.ts
vendored
@ -4,7 +4,6 @@ interface ImportMetaEnv {
|
|||||||
readonly VITE_APP_RELAY: string
|
readonly VITE_APP_RELAY: string
|
||||||
readonly VITE_ADMIN_NPUBS: string
|
readonly VITE_ADMIN_NPUBS: string
|
||||||
readonly VITE_REPORTING_NPUB: string
|
readonly VITE_REPORTING_NPUB: string
|
||||||
readonly VITE_SITE_WOT_NPUB: string
|
|
||||||
readonly VITE_FALLBACK_MOD_IMAGE: string
|
readonly VITE_FALLBACK_MOD_IMAGE: string
|
||||||
readonly VITE_FALLBACK_GAME_IMAGE: string
|
readonly VITE_FALLBACK_GAME_IMAGE: string
|
||||||
readonly VITE_BLOG_NPUBS: string
|
readonly VITE_BLOG_NPUBS: string
|
||||||
|
Loading…
Reference in New Issue
Block a user