fixes-12-26 #178
@ -1,11 +1,12 @@
|
||||
import { RouterProvider } from 'react-router-dom'
|
||||
import { useEffect } from 'react'
|
||||
import { routerWithNdkContext } from 'routes'
|
||||
import { useEffect, useMemo } from 'react'
|
||||
import { routerWithNdkContext as routerWithState } from 'routes'
|
||||
import { useNDKContext } from 'hooks'
|
||||
import './styles/styles.css'
|
||||
|
||||
function App() {
|
||||
const ndkContext = useNDKContext()
|
||||
const router = useMemo(() => routerWithState(ndkContext), [ndkContext])
|
||||
|
||||
useEffect(() => {
|
||||
// Find the element with id 'root'
|
||||
@ -24,7 +25,7 @@ function App() {
|
||||
}
|
||||
}, [])
|
||||
|
||||
return <RouterProvider router={routerWithNdkContext(ndkContext)} />
|
||||
return <RouterProvider router={router} />
|
||||
}
|
||||
|
||||
export default App
|
||||
|
@ -252,7 +252,7 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
})
|
||||
.catch((err) => {
|
||||
log(
|
||||
true,
|
||||
false, // Too many failed requests, turned off for clarity
|
||||
LogType.Error,
|
||||
`An error occurred in fetching user's (${hexKey}) ${userRelaysType}`,
|
||||
err
|
||||
|
@ -39,7 +39,7 @@ export const useComments = (
|
||||
})
|
||||
.catch((err) => {
|
||||
log(
|
||||
true,
|
||||
false, // Too many failed requests, turned off for clarity
|
||||
LogType.Error,
|
||||
`An error occurred in fetching user's (${author}) ${UserRelaysType.Read}`,
|
||||
err
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { Link } from 'react-router-dom'
|
||||
import styles from '../styles/footer.module.scss'
|
||||
import { appRoutes, getProfilePageRoute } from 'routes'
|
||||
|
||||
export const Footer = () => {
|
||||
return (
|
||||
@ -7,6 +9,7 @@ export const Footer = () => {
|
||||
<p className={styles.secMainFooterPara}>
|
||||
Built with
|
||||
<a
|
||||
rel='noopener'
|
||||
className={styles.secMainFooterParaLink}
|
||||
href='https://github.com/nostr-protocol/nostr'
|
||||
target='_blank'
|
||||
@ -14,21 +17,26 @@ export const Footer = () => {
|
||||
Nostr
|
||||
</a>{' '}
|
||||
by
|
||||
<a
|
||||
<Link
|
||||
className={styles.secMainFooterParaLink}
|
||||
href='https://degmods.com/profile/nprofile1qqsre6jgq6c7r2vzn5cdtju20qq36sn3cer5avc4x8kfru5pzrlr7sqnancjp'
|
||||
to={getProfilePageRoute(
|
||||
'nprofile1qqsre6jgq6c7r2vzn5cdtju20qq36sn3cer5avc4x8kfru5pzrlr7sqnancjp'
|
||||
)}
|
||||
target='_blank'
|
||||
>
|
||||
Freakoverse
|
||||
</a>
|
||||
</Link>
|
||||
, with the support of{' '}
|
||||
<a className={styles.secMainFooterParaLink} href='backers.html'>
|
||||
<Link
|
||||
className={styles.secMainFooterParaLink}
|
||||
to={appRoutes.supporters}
|
||||
>
|
||||
Supporters
|
||||
</a>
|
||||
</Link>
|
||||
. Check our
|
||||
<a className={styles.secMainFooterParaLink} href='backup.html'>
|
||||
<Link className={styles.secMainFooterParaLink} to={appRoutes.backup}>
|
||||
Backup Plan
|
||||
</a>
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
</div>
|
||||
|
@ -44,7 +44,7 @@ export const Layout = () => {
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [ndk, dispatch])
|
||||
}, [dispatch, ndk])
|
||||
|
||||
// calculate user's wot
|
||||
useEffect(() => {
|
||||
@ -60,7 +60,7 @@ export const Layout = () => {
|
||||
toast.error('An error occurred in calculating user web-of-trust!')
|
||||
})
|
||||
}
|
||||
}, [ndk, userState.user, dispatch])
|
||||
}, [dispatch, ndk, userState.user?.pubkey])
|
||||
|
||||
// get site's wot level
|
||||
useEffect(() => {
|
||||
@ -106,7 +106,7 @@ export const Layout = () => {
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [userState.user, dispatch, fetchEventFromUserRelays])
|
||||
}, [dispatch, fetchEventFromUserRelays, userState.user?.pubkey])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
126
src/pages/backup.tsx
Normal file
126
src/pages/backup.tsx
Normal file
@ -0,0 +1,126 @@
|
||||
import { capitalizeEachWord } from 'utils'
|
||||
import '../styles/backup.css'
|
||||
import backupPlanImg from '../assets/img/DEG Mods Backup Plan.png'
|
||||
// import placeholder from '../assets/img/DEGMods Placeholder Img.png'
|
||||
interface BackupItemProps {
|
||||
name: string
|
||||
image: string
|
||||
link: string
|
||||
type: 'repo' | 'alt' | 'exe'
|
||||
}
|
||||
const BACKUP_LIST: BackupItemProps[] = [
|
||||
// {
|
||||
// name: 'Github',
|
||||
// type: 'repo',
|
||||
// image:
|
||||
// 'https://www.c-sharpcorner.com/article/create-github-repository-and-add-newexisting-project-using-github-desktop/Images/github.png',
|
||||
// link: '#'
|
||||
// },
|
||||
// {
|
||||
// name: 'Github, but nostr',
|
||||
// type: 'repo',
|
||||
// image: 'https://vitorpamplona.com/images/nostr.gif',
|
||||
// link: '#'
|
||||
// },
|
||||
// {
|
||||
// name: 'name',
|
||||
// type: 'alt',
|
||||
// image: placeholder,
|
||||
// link: '#'
|
||||
// },
|
||||
// {
|
||||
// name: '',
|
||||
// type: 'exe',
|
||||
// image: placeholder,
|
||||
// link: '#'
|
||||
// }
|
||||
]
|
||||
|
||||
const BackupItem = ({ name, image, link, type }: BackupItemProps) => {
|
||||
return (
|
||||
<a
|
||||
className='backupListLink'
|
||||
href={link}
|
||||
style={{
|
||||
background: `linear-gradient(15deg, rgba(0,0,0,0.75), rgba(0,0,0,0.25)),
|
||||
url("${image}") center / cover no-repeat,
|
||||
linear-gradient(45deg, rgba(0,0,0,0.1), rgba(255,255,255,0.01) 50%, rgba(0,0,0,0.1))`
|
||||
}}
|
||||
target='_blank'
|
||||
>
|
||||
<div className='backupListLinkInside'>
|
||||
<h3>
|
||||
{type === 'exe' ? type.toUpperCase() : capitalizeEachWord(type)}:{' '}
|
||||
{name}
|
||||
</h3>
|
||||
</div>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
export const BackupPage = () => {
|
||||
return (
|
||||
<div className='InnerBodyMain'>
|
||||
<div className='ContainerMain'>
|
||||
<div className='IBMSecMainGroup'>
|
||||
<div className='IBMSecMain'>
|
||||
<div className='AboutSec'>
|
||||
<div className='LearnText'>
|
||||
<div className='LearnTextInside'>
|
||||
<h1
|
||||
className='LearnTextHeading'
|
||||
style={{ textAlign: 'center' }}
|
||||
>
|
||||
Backup Plan: Repos, Alts, EXE
|
||||
</h1>
|
||||
<img alt='' src={backupPlanImg} />
|
||||
<p className='LearnTextPara'>
|
||||
It's pretty clear that authoritarianism and censorship is on
|
||||
the rise, on all fronts, and from what can be seen, any idea
|
||||
that push for the opposite gets attacked. That's why DEG
|
||||
Mods is running on Nostr, and that's why we're also writing
|
||||
this backup plan.
|
||||
<br />
|
||||
</p>
|
||||
<h3 className='LearnTextHeading'>Repositories</h3>
|
||||
<p className='LearnTextPara'>
|
||||
Wherever we can, we'll put DEG Mods' code on multiple
|
||||
repositories such as Github, and (github but on nostr).
|
||||
Below you can find the links where we've uploaded the site's
|
||||
code to.
|
||||
<br />
|
||||
</p>
|
||||
<h3 className='LearnTextHeading'>Alternatives</h3>
|
||||
<p className='LearnTextPara'>
|
||||
With the repositories for DEG Mods is up on multiple places,
|
||||
we encourage people to take the code and duplicate it
|
||||
elsewhere. Fork it, change the design, remove or add systems
|
||||
and features, and make your own version. Below you can find
|
||||
links of alts that we've found.
|
||||
<br />
|
||||
</p>
|
||||
<h3 className='LearnTextHeading'>EXE</h3>
|
||||
<p className='LearnTextPara'>
|
||||
One last push we'd like to do is to create a .exe that'll
|
||||
open up DEG Mods on your PC, as if you've opened the website
|
||||
normally, with almost all of the functionalities you'd
|
||||
expect (if not all). We want to do this so that in case
|
||||
there are no alternatives, or that they're getting shut
|
||||
down, then you can just rely on this instead. The link to it
|
||||
will be added here the moment it becomes available.
|
||||
<br />
|
||||
</p>
|
||||
<div className='backupList'>
|
||||
{BACKUP_LIST.map((b) => (
|
||||
<BackupItem {...b} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -13,6 +13,7 @@ import {
|
||||
} from 'types'
|
||||
import {
|
||||
DEFAULT_FILTER_OPTIONS,
|
||||
getFallbackPubkey,
|
||||
getLocalStorageItem,
|
||||
log,
|
||||
LogType
|
||||
@ -41,7 +42,8 @@ export const blogRouteLoader =
|
||||
}
|
||||
|
||||
const userState = store.getState().user
|
||||
const loggedInUserPubkey = userState?.user?.pubkey as string | undefined
|
||||
const loggedInUserPubkey =
|
||||
(userState?.user?.pubkey as string | undefined) || getFallbackPubkey()
|
||||
|
||||
// Check if editing and the user is the original author
|
||||
// Redirect if NOT
|
||||
|
@ -16,6 +16,7 @@ import {
|
||||
DEFAULT_FILTER_OPTIONS,
|
||||
extractBlogCardDetails,
|
||||
extractModData,
|
||||
getFallbackPubkey,
|
||||
getLocalStorageItem,
|
||||
getReportingSet,
|
||||
log,
|
||||
@ -46,7 +47,8 @@ export const modRouteLoader =
|
||||
}
|
||||
|
||||
const userState = store.getState().user
|
||||
const loggedInUserPubkey = userState?.user?.pubkey as string | undefined
|
||||
const loggedInUserPubkey =
|
||||
(userState?.user?.pubkey as string | undefined) || getFallbackPubkey()
|
||||
|
||||
try {
|
||||
// Set up the filters
|
||||
|
@ -1,7 +1,13 @@
|
||||
import { NDKContextType } from 'contexts/NDKContext'
|
||||
import { store } from 'store'
|
||||
import { MuteLists } from 'types'
|
||||
import { getReportingSet, CurationSetIdentifiers, log, LogType } from 'utils'
|
||||
import {
|
||||
getReportingSet,
|
||||
CurationSetIdentifiers,
|
||||
log,
|
||||
LogType,
|
||||
getFallbackPubkey
|
||||
} from 'utils'
|
||||
|
||||
export interface ModsPageLoaderResult {
|
||||
muteLists: {
|
||||
@ -31,15 +37,11 @@ export const modsRouteLoader = (ndkContext: NDKContextType) => async () => {
|
||||
|
||||
// Get the current state
|
||||
const userState = store.getState().user
|
||||
|
||||
// Check if current user is logged in
|
||||
let userPubkey: string | undefined
|
||||
if (userState.auth && userState.user?.pubkey) {
|
||||
userPubkey = userState.user.pubkey as string
|
||||
}
|
||||
const loggedInUserPubkey =
|
||||
(userState?.user?.pubkey as string | undefined) || getFallbackPubkey()
|
||||
|
||||
const settled = await Promise.allSettled([
|
||||
ndkContext.getMuteLists(userPubkey),
|
||||
ndkContext.getMuteLists(loggedInUserPubkey),
|
||||
getReportingSet(CurationSetIdentifiers.NSFW, ndkContext),
|
||||
getReportingSet(CurationSetIdentifiers.Repost, ndkContext)
|
||||
])
|
||||
|
@ -46,7 +46,6 @@ export const ProfilePage = () => {
|
||||
profilePubkey,
|
||||
profile,
|
||||
isBlocked: _isBlocked,
|
||||
isOwnProfile,
|
||||
repostList,
|
||||
muteLists,
|
||||
nsfwList
|
||||
@ -60,6 +59,10 @@ export const ProfilePage = () => {
|
||||
const displayName =
|
||||
profile?.displayName || profile?.name || '[name not set up]'
|
||||
const [showReportPopUp, setShowReportPopUp] = useState(false)
|
||||
const isOwnProfile =
|
||||
userState.auth &&
|
||||
userState.user?.pubkey &&
|
||||
userState.user.pubkey === profilePubkey
|
||||
|
||||
const [isBlocked, setIsBlocked] = useState(_isBlocked)
|
||||
const handleBlock = async () => {
|
||||
@ -661,7 +664,7 @@ const ReportUserPopup = ({
|
||||
}
|
||||
|
||||
const ProfileTabBlogs = () => {
|
||||
const { profile, muteLists, nsfwList } =
|
||||
const { profilePubkey, muteLists, nsfwList } =
|
||||
useLoaderData() as ProfilePageLoaderResult
|
||||
const navigation = useNavigation()
|
||||
const { fetchEvents } = useNDKContext()
|
||||
@ -669,7 +672,7 @@ const ProfileTabBlogs = () => {
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const blogfilter: NDKFilter = useMemo(() => {
|
||||
const filter: NDKFilter = {
|
||||
authors: [profile?.pubkey as string],
|
||||
authors: [profilePubkey],
|
||||
kinds: [kinds.LongFormArticle]
|
||||
}
|
||||
|
||||
@ -683,13 +686,13 @@ const ProfileTabBlogs = () => {
|
||||
}
|
||||
|
||||
return filter
|
||||
}, [filterOptions.nsfw, filterOptions.source, profile?.pubkey])
|
||||
}, [filterOptions.nsfw, filterOptions.source, profilePubkey])
|
||||
|
||||
const [page, setPage] = useState(1)
|
||||
const [hasMore, setHasMore] = useState(false)
|
||||
const [blogs, setBlogs] = useState<Partial<BlogCardDetails>[]>([])
|
||||
useEffect(() => {
|
||||
if (profile) {
|
||||
if (profilePubkey) {
|
||||
// Initial blog fetch, go beyond limit to check for next
|
||||
const filter: NDKFilter = {
|
||||
...blogfilter,
|
||||
@ -704,7 +707,7 @@ const ProfileTabBlogs = () => {
|
||||
setIsLoading(false)
|
||||
})
|
||||
}
|
||||
}, [blogfilter, fetchEvents, profile])
|
||||
}, [blogfilter, fetchEvents, profilePubkey])
|
||||
|
||||
const handleNext = useCallback(() => {
|
||||
if (isLoading) return
|
||||
@ -758,7 +761,7 @@ const ProfileTabBlogs = () => {
|
||||
let _blogs = blogs || []
|
||||
const isAdmin = userState.user?.npub === import.meta.env.VITE_REPORTING_NPUB
|
||||
const isOwner =
|
||||
userState.user?.pubkey && userState.user.pubkey === profile?.pubkey
|
||||
userState.user?.pubkey && userState.user.pubkey === profilePubkey
|
||||
const isUnmoderatedFully =
|
||||
filterOptions.moderated === ModeratedFilter.Unmoderated_Fully
|
||||
|
||||
@ -815,7 +818,7 @@ const ProfileTabBlogs = () => {
|
||||
muteLists.user.authors,
|
||||
muteLists.user.replaceableEvents,
|
||||
nsfwList,
|
||||
profile?.pubkey,
|
||||
profilePubkey,
|
||||
userState.user?.npub,
|
||||
userState.user?.pubkey
|
||||
])
|
||||
@ -826,10 +829,7 @@ const ProfileTabBlogs = () => {
|
||||
<LoadingSpinner desc={'Loading...'} />
|
||||
)}
|
||||
|
||||
<BlogsFilter
|
||||
filterKey={'filter-blog'}
|
||||
author={profile?.pubkey as string}
|
||||
/>
|
||||
<BlogsFilter filterKey={'filter-blog'} author={profilePubkey} />
|
||||
|
||||
<div className='IBMSMList IBMSMListAlt'>
|
||||
{moderatedAndSortedBlogs.map((b) => (
|
||||
|
@ -6,6 +6,7 @@ import { store } from 'store'
|
||||
import { MuteLists, UserProfile } from 'types'
|
||||
import {
|
||||
CurationSetIdentifiers,
|
||||
getFallbackPubkey,
|
||||
getReportingSet,
|
||||
log,
|
||||
LogType,
|
||||
@ -16,7 +17,6 @@ export interface ProfilePageLoaderResult {
|
||||
profilePubkey: string
|
||||
profile: UserProfile
|
||||
isBlocked: boolean
|
||||
isOwnProfile: boolean
|
||||
muteLists: {
|
||||
admin: MuteLists
|
||||
user: MuteLists
|
||||
@ -58,21 +58,17 @@ export const profileRouteLoader =
|
||||
|
||||
// Get the current state
|
||||
const userState = store.getState().user
|
||||
|
||||
// Check if current user is logged in
|
||||
let userPubkey: string | undefined
|
||||
if (userState.auth && userState.user?.pubkey) {
|
||||
userPubkey = userState.user.pubkey as string
|
||||
}
|
||||
const loggedInUserPubkey =
|
||||
(userState?.user?.pubkey as string | undefined) || getFallbackPubkey()
|
||||
|
||||
// Redirect if profile naddr is missing
|
||||
// - home if user is not logged
|
||||
let profileRoute = appRoutes.home
|
||||
if (!profilePubkey && userPubkey) {
|
||||
if (!profilePubkey && loggedInUserPubkey) {
|
||||
// - own profile
|
||||
profileRoute = getProfilePageRoute(
|
||||
nip19.nprofileEncode({
|
||||
pubkey: userPubkey
|
||||
pubkey: loggedInUserPubkey
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -83,7 +79,6 @@ export const profileRouteLoader =
|
||||
profilePubkey: profilePubkey,
|
||||
profile: {},
|
||||
isBlocked: false,
|
||||
isOwnProfile: false,
|
||||
muteLists: {
|
||||
admin: {
|
||||
authors: [],
|
||||
@ -98,14 +93,9 @@ export const profileRouteLoader =
|
||||
repostList: []
|
||||
}
|
||||
|
||||
// Check if user the user is logged in
|
||||
if (userState.auth && userState.user?.pubkey) {
|
||||
result.isOwnProfile = userState.user.pubkey === profilePubkey
|
||||
}
|
||||
|
||||
const settled = await Promise.allSettled([
|
||||
ndkContext.findMetadata(profilePubkey),
|
||||
ndkContext.getMuteLists(userPubkey),
|
||||
ndkContext.getMuteLists(loggedInUserPubkey),
|
||||
getReportingSet(CurationSetIdentifiers.NSFW, ndkContext),
|
||||
getReportingSet(CurationSetIdentifiers.Repost, ndkContext)
|
||||
])
|
||||
|
@ -39,6 +39,7 @@ import {
|
||||
scrollIntoView
|
||||
} from 'utils'
|
||||
import { useCuratedSet } from 'hooks/useCuratedSet'
|
||||
import dedup from 'utils/nostr'
|
||||
|
||||
enum SearchKindEnum {
|
||||
Mods = 'Mods',
|
||||
@ -508,6 +509,11 @@ const GamesResult = ({ searchTerm }: GamesResultProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{searchTerm !== '' && filteredGames.length === 0 && (
|
||||
<div className='IBMSecMain IBMSMListWrapper'>
|
||||
Game not found. Send us a message where you can reach us to add it
|
||||
</div>
|
||||
)}
|
||||
<div className='IBMSecMain IBMSMListWrapper'>
|
||||
<div className='IBMSMList IBMSMListFeaturedAlt'>
|
||||
{filteredGames
|
||||
@ -521,20 +527,14 @@ const GamesResult = ({ searchTerm }: GamesResultProps) => {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<Pagination
|
||||
page={page}
|
||||
disabledNext={filteredGames.length <= page * MAX_GAMES_PER_PAGE}
|
||||
handlePrev={handlePrev}
|
||||
handleNext={handleNext}
|
||||
/>
|
||||
{searchTerm !== '' && filteredGames.length > MAX_GAMES_PER_PAGE && (
|
||||
<Pagination
|
||||
page={page}
|
||||
disabledNext={filteredGames.length <= page * MAX_GAMES_PER_PAGE}
|
||||
handlePrev={handlePrev}
|
||||
handleNext={handleNext}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
function dedup(event1: NDKEvent, event2: NDKEvent) {
|
||||
// return the newest of the two
|
||||
if (event1.created_at! > event2.created_at!) {
|
||||
return event1
|
||||
}
|
||||
|
||||
return event2
|
||||
}
|
||||
|
3
src/pages/supporters.tsx
Normal file
3
src/pages/supporters.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
export const SupportersPage = () => {
|
||||
return <h2>WIP</h2>
|
||||
}
|
@ -28,6 +28,8 @@ import { BlogPage } from '../pages/blog'
|
||||
import { blogRouteLoader } from '../pages/blog/loader'
|
||||
import { blogRouteAction } from '../pages/blog/action'
|
||||
import { reportRouteAction } from '../actions/report'
|
||||
import { BackupPage } from 'pages/backup'
|
||||
import { SupportersPage } from 'pages/supporters'
|
||||
|
||||
export const appRoutes = {
|
||||
home: '/',
|
||||
@ -51,7 +53,9 @@ export const appRoutes = {
|
||||
settingsAdmin: '/settings-admin',
|
||||
profile: '/profile/:nprofile?',
|
||||
feed: '/feed',
|
||||
notifications: '/notifications'
|
||||
notifications: '/notifications',
|
||||
backup: '/backup',
|
||||
supporters: '/supporters'
|
||||
}
|
||||
|
||||
export const getGamePageRoute = (name: string) =>
|
||||
@ -185,6 +189,14 @@ export const routerWithNdkContext = (context: NDKContextType) =>
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: appRoutes.backup,
|
||||
element: <BackupPage />
|
||||
},
|
||||
{
|
||||
path: appRoutes.supporters,
|
||||
element: <SupportersPage />
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
element: <NotFoundPage />
|
||||
|
36
src/styles/backup.css
Normal file
36
src/styles/backup.css
Normal file
@ -0,0 +1,36 @@
|
||||
.backupList {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
grid-gap: 15px;
|
||||
}
|
||||
|
||||
.backupListLink {
|
||||
transition: ease 0.4s;
|
||||
overflow: hidden;
|
||||
padding: 15px;
|
||||
box-shadow: 0 0 8px 0 rgb(0,0,0,0.1);
|
||||
border-radius: 10px;
|
||||
/*border: solid 1px rgba(255,255,255,0.1);*/
|
||||
position: relative;
|
||||
color: rgba(255,255,255,0.75);
|
||||
min-height: 150px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.backupListLink:hover {
|
||||
transition: ease 0.4s;
|
||||
text-decoration: unset;
|
||||
color: rgb(255,255,255);
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.backupListLinkInside {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
grid-gap: 0px;
|
||||
flex-grow: 1;
|
||||
justify-content: end;
|
||||
}
|
||||
|
@ -274,3 +274,16 @@ export function orderEventsChronologically(
|
||||
|
||||
return events
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives two events and returns the "correct" event to use.
|
||||
* #nip-33
|
||||
*/
|
||||
export default function dedup(event1: NDKEvent, event2: NDKEvent) {
|
||||
// return the newest of the two
|
||||
if (event1.created_at! > event2.created_at!) {
|
||||
return event1
|
||||
}
|
||||
|
||||
return event2
|
||||
}
|
||||
|
@ -160,3 +160,23 @@ export const parseFormData = <T>(formData: FormData) => {
|
||||
export const capitalizeEachWord = (str: string): string => {
|
||||
return str.replace(/\b\w/g, (char) => char.toUpperCase())
|
||||
}
|
||||
|
||||
/**
|
||||
* nostr-login - helper function
|
||||
* should only be used as the fallback
|
||||
* user state is not updated before `onAuth` triggers but loaders are faster
|
||||
*/
|
||||
export const getFallbackPubkey = () => {
|
||||
try {
|
||||
// read nostr-login conf from localStorage
|
||||
const stored = window.localStorage.getItem('__nostrlogin_nip46')
|
||||
if (!stored) return
|
||||
|
||||
const info = JSON.parse(stored)
|
||||
if (info && !info.pubkey) return
|
||||
|
||||
return info.pubkey as string
|
||||
} catch {
|
||||
// Silently ignore
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user