diff --git a/public/.well-known/nostr.json b/public/.well-known/nostr.json
new file mode 100644
index 0000000..16c64c4
--- /dev/null
+++ b/public/.well-known/nostr.json
@@ -0,0 +1,7 @@
+{
+ "names": {
+ "_": "8125b911ed0e94dbe3008a0be48cfe5cd0c0b05923cfff917ae7e87da8400883",
+ "cody": "8125b911ed0e94dbe3008a0be48cfe5cd0c0b05923cfff917ae7e87da8400883",
+ "cody2": "24462930821b45f530ec0063eca0a6522e5a577856f982fa944df0ef3caf03ab"
+ }
+}
\ No newline at end of file
diff --git a/src/components/Favicon/index.tsx b/src/components/Favicon/index.tsx
new file mode 100644
index 0000000..43f7d8f
--- /dev/null
+++ b/src/components/Favicon/index.tsx
@@ -0,0 +1,15 @@
+import { useState } from 'react'
+
+export function Favicon({ domain, className }: { domain: string; className?: string }) {
+ const [error, setError] = useState(false)
+ if (error) return null
+
+ return (
+
setError(true)}
+ />
+ )
+}
diff --git a/src/components/FormattedTimestamp/index.tsx b/src/components/FormattedTimestamp/index.tsx
index 6c3b521..ebaef09 100644
--- a/src/components/FormattedTimestamp/index.tsx
+++ b/src/components/FormattedTimestamp/index.tsx
@@ -2,6 +2,22 @@ import dayjs from 'dayjs'
import { useTranslation } from 'react-i18next'
export function FormattedTimestamp({
+ timestamp,
+ short = false,
+ className
+}: {
+ timestamp: number
+ short?: boolean
+ className?: string
+}) {
+ return (
+
+
+
+ )
+}
+
+function FormattedTimestampContent({
timestamp,
short = false
}: {
diff --git a/src/components/Nip05/index.tsx b/src/components/Nip05/index.tsx
index 2f66827..bef22de 100644
--- a/src/components/Nip05/index.tsx
+++ b/src/components/Nip05/index.tsx
@@ -1,9 +1,12 @@
import { Skeleton } from '@/components/ui/skeleton'
import { useFetchProfile } from '@/hooks'
import { useFetchNip05 } from '@/hooks/useFetchNip05'
+import { toNoteList } from '@/lib/link'
+import { SecondaryPageLink } from '@/PageManager'
import { BadgeAlert, BadgeCheck } from 'lucide-react'
+import { Favicon } from '../Favicon'
-export default function Nip05({ pubkey }: { pubkey: string }) {
+export default function Nip05({ pubkey, append }: { pubkey: string; append?: string }) {
const { profile } = useFetchProfile(pubkey)
const { nip05IsVerified, nip05Name, nip05Domain, isFetching } = useFetchNip05(
profile?.nip05,
@@ -13,30 +16,27 @@ export default function Nip05({ pubkey }: { pubkey: string }) {
if (isFetching) {
return (
-
+
)
}
- if (!profile?.nip05) return null
+ if (!profile?.nip05 || !nip05Name || !nip05Domain) return null
return (
- nip05Name &&
- nip05Domain && (
-
- )
+ e.stopPropagation()}>
+ {nip05Name !== '_' ? (
+ @{nip05Name}
+ ) : null}
+
+ {nip05IsVerified ? : }
+ {nip05Domain}
+
+
+ {append && {append}}
+
)
}
diff --git a/src/components/Note/index.tsx b/src/components/Note/index.tsx
index eb2a047..e7a7b79 100644
--- a/src/components/Note/index.tsx
+++ b/src/components/Note/index.tsx
@@ -7,11 +7,13 @@ import {
isSupportedKind
} from '@/lib/event'
import { toNote } from '@/lib/link'
+import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { Event, kinds } from 'nostr-tools'
import { useMemo } from 'react'
import Content from '../Content'
import { FormattedTimestamp } from '../FormattedTimestamp'
import ImageGallery from '../ImageGallery'
+import Nip05 from '../Nip05'
import NoteOptions from '../NoteOptions'
import ParentNotePreview from '../ParentNotePreview'
import TranslateButton from '../TranslateButton'
@@ -33,6 +35,7 @@ export default function Note({
hideParentNotePreview?: boolean
}) {
const { push } = useSecondaryPage()
+ const { isSmallScreen } = useScreenSize()
const parentEventId = useMemo(
() => (hideParentNotePreview ? undefined : getParentEventId(event)),
[event, hideParentNotePreview]
@@ -47,28 +50,33 @@ export default function Note({
-
-
-
+
+
+
{usingClient && size === 'normal' && (
-
using {usingClient}
+
using {usingClient}
)}
-
- {size === 'normal' && }
+ {size === 'normal' && (
+
+ )}
{parentEventId && (
diff --git a/src/components/ProfileList/index.tsx b/src/components/ProfileList/index.tsx
new file mode 100644
index 0000000..f46e7b4
--- /dev/null
+++ b/src/components/ProfileList/index.tsx
@@ -0,0 +1,45 @@
+import { useEffect, useRef, useState } from 'react'
+import UserItem from '../UserItem'
+
+export default function ProfileList({ pubkeys }: { pubkeys: string[] }) {
+ const [visiblePubkeys, setVisiblePubkeys] = useState
([])
+ const bottomRef = useRef(null)
+
+ useEffect(() => {
+ setVisiblePubkeys(pubkeys.slice(0, 10))
+ }, [pubkeys])
+
+ useEffect(() => {
+ const options = {
+ root: null,
+ rootMargin: '10px',
+ threshold: 1
+ }
+
+ const observerInstance = new IntersectionObserver((entries) => {
+ if (entries[0].isIntersecting && pubkeys.length > visiblePubkeys.length) {
+ setVisiblePubkeys((prev) => [...prev, ...pubkeys.slice(prev.length, prev.length + 10)])
+ }
+ }, options)
+
+ const currentBottomRef = bottomRef.current
+ if (currentBottomRef) {
+ observerInstance.observe(currentBottomRef)
+ }
+
+ return () => {
+ if (observerInstance && currentBottomRef) {
+ observerInstance.unobserve(currentBottomRef)
+ }
+ }
+ }, [visiblePubkeys, pubkeys])
+
+ return (
+
+ {visiblePubkeys.map((pubkey, index) => (
+
+ ))}
+ {pubkeys.length > visiblePubkeys.length &&
}
+
+ )
+}
diff --git a/src/components/ReplyNote/index.tsx b/src/components/ReplyNote/index.tsx
index a2a4840..35bf46d 100644
--- a/src/components/ReplyNote/index.tsx
+++ b/src/components/ReplyNote/index.tsx
@@ -1,20 +1,23 @@
import { useSecondaryPage } from '@/PageManager'
import { Button } from '@/components/ui/button'
import { Skeleton } from '@/components/ui/skeleton'
+import { getUsingClient } from '@/lib/event'
import { toNote } from '@/lib/link'
import { useMuteList } from '@/providers/MuteListProvider'
+import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { Event } from 'nostr-tools'
import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Collapsible from '../Collapsible'
import Content from '../Content'
import { FormattedTimestamp } from '../FormattedTimestamp'
+import Nip05 from '../Nip05'
import NoteOptions from '../NoteOptions'
import NoteStats from '../NoteStats'
import ParentNotePreview from '../ParentNotePreview'
+import TranslateButton from '../TranslateButton'
import UserAvatar from '../UserAvatar'
import Username from '../Username'
-import TranslateButton from '../TranslateButton'
export default function ReplyNote({
event,
@@ -28,6 +31,7 @@ export default function ReplyNote({
highlight?: boolean
}) {
const { t } = useTranslation()
+ const { isSmallScreen } = useScreenSize()
const { push } = useSecondaryPage()
const { mutePubkeys } = useMuteList()
const [showMuted, setShowMuted] = useState(false)
@@ -35,6 +39,7 @@ export default function ReplyNote({
() => showMuted || !mutePubkeys.includes(event.pubkey),
[showMuted, mutePubkeys, event]
)
+ const usingClient = useMemo(() => getUsingClient(event), [event])
return (
-
+
-
-
-
-
+
+
+
+ {usingClient && (
+
+ using {usingClient}
+
+ )}
+
+
+
+
-
+
diff --git a/src/components/TranslateButton/index.tsx b/src/components/TranslateButton/index.tsx
index 58d92e6..b116507 100644
--- a/src/components/TranslateButton/index.tsx
+++ b/src/components/TranslateButton/index.tsx
@@ -122,7 +122,7 @@ export default function TranslateButton({
return (