refactor: remove electron-related code

This commit is contained in:
codytseng 2024-12-21 23:20:30 +08:00
parent bed8df06e8
commit 2b1e6fe8f5
200 changed files with 2771 additions and 8432 deletions

View file

@ -1,8 +1,8 @@
import 'yet-another-react-lightbox/styles.css'
import './assets/main.css'
import './index.css'
import { Toaster } from '@renderer/components/ui/toaster'
import { ThemeProvider } from '@renderer/providers/ThemeProvider'
import { Toaster } from '@/components/ui/toaster'
import { ThemeProvider } from '@/providers/ThemeProvider'
import { PageManager } from './PageManager'
import NoteListPage from './pages/primary/NoteListPage'
import { FollowListProvider } from './providers/FollowListProvider'

View file

@ -1,11 +1,7 @@
import Sidebar from '@renderer/components/Sidebar'
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup
} from '@renderer/components/ui/resizable'
import { cn } from '@renderer/lib/utils'
import HomePage from '@renderer/pages/secondary/HomePage'
import Sidebar from '@/components/Sidebar'
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable'
import { cn } from '@/lib/utils'
import HomePage from '@/pages/secondary/HomePage'
import { cloneElement, createContext, useContext, useEffect, useState } from 'react'
import { useScreenSize } from './providers/ScreenSizeProvider'
import { routes } from './routes'
@ -187,7 +183,9 @@ export function SecondaryPageLink({
<span
className={cn('cursor-pointer', className)}
onClick={(e) => {
onClick && onClick(e)
if (onClick) {
onClick(e)
}
push(to)
}}
>

View file

@ -1,59 +0,0 @@
import { ElectronAPI } from '@electron-toolkit/preload'
import { Event } from 'nostr-tools'
export type TRelayGroup = {
groupName: string
relayUrls: string[]
isActive: boolean
}
export type TConfig = {
relayGroups: TRelayGroup[]
theme: TThemeSetting
}
export type TThemeSetting = 'light' | 'dark' | 'system'
export type TTheme = 'light' | 'dark'
export type TDraftEvent = Pick<Event, 'content' | 'created_at' | 'kind' | 'tags'>
export interface ISigner {
getPublicKey: () => Promise<string | null>
signEvent: (draftEvent: TDraftEvent) => Promise<Event | null>
}
export type TElectronWindow = {
electron: ElectronAPI
api: {
system: {
isEncryptionAvailable: () => Promise<boolean>
getSelectedStorageBackend: () => Promise<string>
}
theme: {
addChangeListener: (listener: (theme: TTheme) => void) => void
removeChangeListener: () => void
current: () => Promise<TTheme>
}
storage: {
getItem: (key: string) => Promise<string>
setItem: (key: string, value: string) => Promise<void>
removeItem: (key: string) => Promise<void>
}
nostr: {
login: (nsec: string) => Promise<{
pubkey?: string
reason?: string
}>
logout: () => Promise<void>
}
}
nostr: ISigner
}
export type TAccount = {
pubkey: string
signerType: 'nsec' | 'browser-nsec' | 'nip-07' | 'bunker'
nsec?: string
bunker?: string
bunkerClientSecretKey?: string
}

View file

@ -5,7 +5,7 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger
} from '@renderer/components/ui/dialog'
} from '@/components/ui/dialog'
import Username from '../Username'
export default function AboutInfoDialog({ children }: { children: React.ReactNode }) {
@ -38,17 +38,6 @@ export default function AboutInfoDialog({ children }: { children: React.ReactNod
GitHub
</a>
</div>
<div>
Desktop app:{' '}
<a
href="https://github.com/CodyTseng/jumble/releases"
target="_blank"
rel="noreferrer"
className="text-primary hover:underline"
>
Download
</a>
</div>
<div>
If you like this project, you can buy me a coffee <br />
<div className="font-semibold"> codytseng@getalby.com </div>

View file

@ -1,5 +1,5 @@
import { Button } from '@renderer/components/ui/button'
import { useNostr } from '@renderer/providers/NostrProvider'
import { Button } from '@/components/ui/button'
import { useNostr } from '@/providers/NostrProvider'
import { LogIn } from 'lucide-react'
export default function LoginButton({

View file

@ -1,16 +1,16 @@
import { Avatar, AvatarFallback, AvatarImage } from '@renderer/components/ui/avatar'
import { Button } from '@renderer/components/ui/button'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { Button } from '@/components/ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger
} from '@renderer/components/ui/dropdown-menu'
import { useFetchProfile } from '@renderer/hooks'
import { toProfile } from '@renderer/lib/link'
import { formatPubkey, generateImageByPubkey } from '@renderer/lib/pubkey'
import { useSecondaryPage } from '@renderer/PageManager'
import { useNostr } from '@renderer/providers/NostrProvider'
} from '@/components/ui/dropdown-menu'
import { useFetchProfile } from '@/hooks'
import { toProfile } from '@/lib/link'
import { formatPubkey, generateImageByPubkey } from '@/lib/pubkey'
import { useSecondaryPage } from '@/PageManager'
import { useNostr } from '@/providers/NostrProvider'
import { useTranslation } from 'react-i18next'
export default function ProfileButton({
@ -69,9 +69,7 @@ export default function ProfileButton({
return (
<DropdownMenu>
<DropdownMenuTrigger className="non-draggable" asChild>
{triggerComponent}
</DropdownMenuTrigger>
<DropdownMenuTrigger asChild>{triggerComponent}</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem onClick={() => push(toProfile(pubkey))}>{t('Profile')}</DropdownMenuItem>
<DropdownMenuItem className="text-destructive focus:text-destructive" onClick={logout}>

View file

@ -1,4 +1,4 @@
import { useNostr } from '@renderer/providers/NostrProvider'
import { useNostr } from '@/providers/NostrProvider'
import LoginButton from './LoginButton'
import ProfileButton from './ProfileButton'

View file

@ -1,5 +1,5 @@
import { Button } from '@renderer/components/ui/button'
import { useSecondaryPage } from '@renderer/PageManager'
import { Button } from '@/components/ui/button'
import { useSecondaryPage } from '@/PageManager'
import { ChevronLeft } from 'lucide-react'
import { useTranslation } from 'react-i18next'

View file

@ -1,5 +1,5 @@
import { isNsfwEvent } from '@renderer/lib/event'
import { cn } from '@renderer/lib/utils'
import { isNsfwEvent } from '@/lib/event'
import { cn } from '@/lib/utils'
import { Event } from 'nostr-tools'
import { memo } from 'react'
import {

View file

@ -1,5 +1,5 @@
import { toNoteList } from '@renderer/lib/link'
import { SecondaryPageLink } from '@renderer/PageManager'
import { toNoteList } from '@/lib/link'
import { SecondaryPageLink } from '@/PageManager'
import { TEmbeddedRenderer } from './types'
export function EmbeddedHashtag({ hashtag }: { hashtag: string }) {

View file

@ -1,6 +1,6 @@
import { useFetchEvent } from '@renderer/hooks'
import { toNoStrudelArticle, toNoStrudelNote, toNoStrudelStream } from '@renderer/lib/link'
import { cn } from '@renderer/lib/utils'
import { useFetchEvent } from '@/hooks'
import { toNoStrudelArticle, toNoStrudelNote, toNoStrudelStream } from '@/lib/link'
import { cn } from '@/lib/utils'
import { kinds } from 'nostr-tools'
import ShortTextNoteCard from '../NoteCard/ShortTextNoteCard'

View file

@ -1,5 +1,5 @@
import { useSecondaryPage } from '@renderer/PageManager'
import { toNoteList } from '@renderer/lib/link'
import { useSecondaryPage } from '@/PageManager'
import { toNoteList } from '@/lib/link'
import { TEmbeddedRenderer } from './types'
export function EmbeddedWebsocketUrl({ url }: { url: string }) {

View file

@ -1,7 +1,7 @@
import { Button } from '@renderer/components/ui/button'
import { useToast } from '@renderer/hooks'
import { useFollowList } from '@renderer/providers/FollowListProvider'
import { useNostr } from '@renderer/providers/NostrProvider'
import { Button } from '@/components/ui/button'
import { useToast } from '@/hooks'
import { useFollowList } from '@/providers/FollowListProvider'
import { useNostr } from '@/providers/NostrProvider'
import { Loader } from 'lucide-react'
import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'

View file

@ -1,6 +1,6 @@
import { Image } from '@nextui-org/image'
import { ScrollArea, ScrollBar } from '@renderer/components/ui/scroll-area'
import { cn } from '@renderer/lib/utils'
import { ScrollArea, ScrollBar } from '@/components/ui/scroll-area'
import { cn } from '@/lib/utils'
import { useState } from 'react'
import Lightbox from 'yet-another-react-lightbox'
import Zoom from 'yet-another-react-lightbox/plugins/zoom'

View file

@ -1,6 +1,6 @@
import { Button } from '@renderer/components/ui/button'
import { Input } from '@renderer/components/ui/input'
import { useNostr } from '@renderer/providers/NostrProvider'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { useNostr } from '@/providers/NostrProvider'
import { Loader } from 'lucide-react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'

View file

@ -1,8 +1,7 @@
import { Button } from '@renderer/components/ui/button'
import { Input } from '@renderer/components/ui/input'
import { IS_ELECTRON, isElectron } from '@renderer/lib/env'
import { useNostr } from '@renderer/providers/NostrProvider'
import { useEffect, useState } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { useNostr } from '@/providers/NostrProvider'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
export default function PrivateKeyLogin({ onLoginSuccess }: { onLoginSuccess: () => void }) {
@ -10,17 +9,6 @@ export default function PrivateKeyLogin({ onLoginSuccess }: { onLoginSuccess: ()
const { nsecLogin } = useNostr()
const [nsec, setNsec] = useState('')
const [errMsg, setErrMsg] = useState<string | null>(null)
const [storageBackend, setStorageBackend] = useState('unknown')
useEffect(() => {
const init = async () => {
if (!isElectron(window)) return
const backend = await window.api.system.getSelectedStorageBackend()
setStorageBackend(backend)
}
init()
}, [])
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setNsec(e.target.value)
@ -40,15 +28,9 @@ export default function PrivateKeyLogin({ onLoginSuccess }: { onLoginSuccess: ()
return (
<>
<div className="text-orange-400">
{!IS_ELECTRON
? t(
'Using private key login is insecure. It is recommended to use a browser extension for login, such as alby, nostr-keyx or nos2x.'
)
: ['unknown', 'basic_text'].includes(storageBackend)
? t('There are no secret keys stored on this device. Your nsec will be unprotected.')
: t('Your nsec will be encrypted using the {{backend}}.', {
backend: storageBackend
})}
{t(
'Using private key login is insecure. It is recommended to use a browser extension for login, such as alby, nostr-keyx or nos2x.'
)}
</div>
<div className="space-y-1">
<Input

View file

@ -1,13 +1,12 @@
import { Button } from '@renderer/components/ui/button'
import { Button } from '@/components/ui/button'
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle
} from '@renderer/components/ui/dialog'
import { IS_ELECTRON } from '@renderer/lib/env'
import { useNostr } from '@renderer/providers/NostrProvider'
} from '@/components/ui/dialog'
import { useNostr } from '@/providers/NostrProvider'
import { ArrowLeft } from 'lucide-react'
import { Dispatch, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -54,7 +53,7 @@ export default function LoginDialog({
</>
) : (
<>
{!IS_ELECTRON && !!window.nostr && (
{!!window.nostr && (
<Button onClick={() => nip07Login().then(() => setOpen(false))} className="w-full">
{t('Login with Browser Extension')}
</Button>

View file

@ -1,4 +1,4 @@
import { useFetchNip05 } from '@renderer/hooks/useFetchNip05'
import { useFetchNip05 } from '@/hooks/useFetchNip05'
import { BadgeAlert, BadgeCheck } from 'lucide-react'
export default function Nip05({ nip05, pubkey }: { nip05?: string; pubkey: string }) {

View file

@ -1,5 +1,5 @@
import { useSecondaryPage } from '@renderer/PageManager'
import { toNote } from '@renderer/lib/link'
import { useSecondaryPage } from '@/PageManager'
import { toNote } from '@/lib/link'
import { Event } from 'nostr-tools'
import Content from '../Content'
import { FormattedTimestamp } from '../FormattedTimestamp'

View file

@ -1,4 +1,4 @@
import client from '@renderer/services/client.service'
import client from '@/services/client.service'
import { Event, kinds, verifyEvent } from 'nostr-tools'
import { useMemo } from 'react'
import ShortTextNoteCard from './ShortTextNoteCard'

View file

@ -1,8 +1,8 @@
import { useFetchEvent } from '@renderer/hooks'
import { getParentEventId, getRootEventId } from '@renderer/lib/event'
import { toNote } from '@renderer/lib/link'
import { cn } from '@renderer/lib/utils'
import { useSecondaryPage } from '@renderer/PageManager'
import { useFetchEvent } from '@/hooks'
import { getParentEventId, getRootEventId } from '@/lib/event'
import { toNote } from '@/lib/link'
import { cn } from '@/lib/utils'
import { useSecondaryPage } from '@/PageManager'
import { Repeat2 } from 'lucide-react'
import { Event } from 'nostr-tools'
import { useTranslation } from 'react-i18next'

View file

@ -1,5 +1,4 @@
import { Event } from 'nostr-tools'
import { kinds } from 'nostr-tools'
import { Event, kinds } from 'nostr-tools'
import RepostNoteCard from './RepostNoteCard'
import ShortTextNoteCard from './ShortTextNoteCard'

View file

@ -1,11 +1,11 @@
import { Button } from '@renderer/components/ui/button'
import { Switch } from '@renderer/components/ui/switch'
import { useFetchRelayInfos } from '@renderer/hooks'
import { isReplyNoteEvent } from '@renderer/lib/event'
import { cn } from '@renderer/lib/utils'
import { useNostr } from '@renderer/providers/NostrProvider'
import { useScreenSize } from '@renderer/providers/ScreenSizeProvider'
import client from '@renderer/services/client.service'
import { Button } from '@/components/ui/button'
import { Switch } from '@/components/ui/switch'
import { useFetchRelayInfos } from '@/hooks'
import { isReplyNoteEvent } from '@/lib/event'
import { cn } from '@/lib/utils'
import { useNostr } from '@/providers/NostrProvider'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import client from '@/services/client.service'
import dayjs from 'dayjs'
import { Event, Filter, kinds } from 'nostr-tools'
import { useEffect, useMemo, useRef, useState } from 'react'
@ -34,7 +34,6 @@ export default function NoteList({
const [hasMore, setHasMore] = useState<boolean>(true)
const [initialized, setInitialized] = useState(false)
const [displayReplies, setDisplayReplies] = useState(false)
const observer = useRef<IntersectionObserver | null>(null)
const bottomRef = useRef<HTMLDivElement | null>(null)
const noteFilter = useMemo(() => {
return {
@ -109,19 +108,21 @@ export default function NoteList({
threshold: 1
}
observer.current = new IntersectionObserver((entries) => {
const observerInstance = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && hasMore) {
loadMore()
}
}, options)
if (bottomRef.current) {
observer.current.observe(bottomRef.current)
const currentBottomRef = bottomRef.current
if (currentBottomRef) {
observerInstance.observe(currentBottomRef)
}
return () => {
if (observer.current && bottomRef.current) {
observer.current.unobserve(bottomRef.current)
if (observerInstance && currentBottomRef) {
observerInstance.unobserve(currentBottomRef)
}
}
}, [initialized, hasMore, events, timelineKey])

View file

@ -1,8 +1,8 @@
import { createReactionDraftEvent } from '@renderer/lib/draft-event'
import { cn } from '@renderer/lib/utils'
import { useNostr } from '@renderer/providers/NostrProvider'
import { useNoteStats } from '@renderer/providers/NoteStatsProvider'
import client from '@renderer/services/client.service'
import { createReactionDraftEvent } from '@/lib/draft-event'
import { cn } from '@/lib/utils'
import { useNostr } from '@/providers/NostrProvider'
import { useNoteStats } from '@/providers/NoteStatsProvider'
import client from '@/services/client.service'
import { Heart, Loader } from 'lucide-react'
import { Event } from 'nostr-tools'
import { useEffect, useMemo, useState } from 'react'
@ -37,7 +37,7 @@ export default function LikeButton({
if (hasLiked === undefined) {
fetchNoteLikedStatus(event)
}
}, [])
}, [canFetch, event])
const like = async (e: React.MouseEvent) => {
e.stopPropagation()

View file

@ -4,8 +4,8 @@ import {
DialogDescription,
DialogHeader,
DialogTitle
} from '@renderer/components/ui/dialog'
import { ScrollArea, ScrollBar } from '@renderer/components/ui/scroll-area'
} from '@/components/ui/dialog'
import { ScrollArea, ScrollBar } from '@/components/ui/scroll-area'
import { Event } from 'nostr-tools'
export default function RawEventDialog({
@ -25,9 +25,7 @@ export default function RawEventDialog({
<DialogDescription className="hidden" />
</DialogHeader>
<ScrollArea className="h-full">
<pre className="text-sm overflow-x-auto text-muted-foreground">
{JSON.stringify(event, null, 2)}
</pre>
<pre className="text-sm text-muted-foreground">{JSON.stringify(event, null, 2)}</pre>
<ScrollBar orientation="horizontal" />
</ScrollArea>
</DialogContent>

View file

@ -3,8 +3,8 @@ import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger
} from '@renderer/components/ui/dropdown-menu'
import { getSharableEventId } from '@renderer/lib/event'
} from '@/components/ui/dropdown-menu'
import { getSharableEventId } from '@/lib/event'
import { Code, Copy, Ellipsis } from 'lucide-react'
import { Event } from 'nostr-tools'
import { useState } from 'react'

View file

@ -1,5 +1,5 @@
import { useNostr } from '@renderer/providers/NostrProvider'
import { useNoteStats } from '@renderer/providers/NoteStatsProvider'
import { useNostr } from '@/providers/NostrProvider'
import { useNoteStats } from '@/providers/NoteStatsProvider'
import { MessageCircle } from 'lucide-react'
import { Event } from 'nostr-tools'
import { useMemo, useState } from 'react'

View file

@ -3,13 +3,13 @@ import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger
} from '@renderer/components/ui/dropdown-menu'
import { createRepostDraftEvent } from '@renderer/lib/draft-event'
import { getSharableEventId } from '@renderer/lib/event'
import { cn } from '@renderer/lib/utils'
import { useNostr } from '@renderer/providers/NostrProvider'
import { useNoteStats } from '@renderer/providers/NoteStatsProvider'
import client from '@renderer/services/client.service'
} from '@/components/ui/dropdown-menu'
import { createRepostDraftEvent } from '@/lib/draft-event'
import { getSharableEventId } from '@/lib/event'
import { cn } from '@/lib/utils'
import { useNostr } from '@/providers/NostrProvider'
import { useNoteStats } from '@/providers/NoteStatsProvider'
import client from '@/services/client.service'
import { Loader, PencilLine, Repeat } from 'lucide-react'
import { Event } from 'nostr-tools'
import { useEffect, useMemo, useState } from 'react'
@ -45,7 +45,7 @@ export default function RepostButton({
if (hasReposted === undefined) {
fetchNoteRepostedStatus(event)
}
}, [])
}, [canFetch, event])
const repost = async (e: React.MouseEvent) => {
e.stopPropagation()

View file

@ -1,4 +1,4 @@
import { cn } from '@renderer/lib/utils'
import { cn } from '@/lib/utils'
import { Event } from 'nostr-tools'
import LikeButton from './LikeButton'
import NoteOptions from './NoteOptions'

View file

@ -1,6 +1,6 @@
import { Button } from '@renderer/components/ui/button'
import { toNotifications } from '@renderer/lib/link'
import { useSecondaryPage } from '@renderer/PageManager'
import { Button } from '@/components/ui/button'
import { toNotifications } from '@/lib/link'
import { useSecondaryPage } from '@/PageManager'
import { Bell } from 'lucide-react'
import { useTranslation } from 'react-i18next'

View file

@ -1,9 +1,9 @@
import { useFetchEvent } from '@renderer/hooks'
import { toNote } from '@renderer/lib/link'
import { tagNameEquals } from '@renderer/lib/tag'
import { useSecondaryPage } from '@renderer/PageManager'
import { useNostr } from '@renderer/providers/NostrProvider'
import client from '@renderer/services/client.service'
import { useFetchEvent } from '@/hooks'
import { toNote } from '@/lib/link'
import { tagNameEquals } from '@/lib/tag'
import { useSecondaryPage } from '@/PageManager'
import { useNostr } from '@/providers/NostrProvider'
import client from '@/services/client.service'
import dayjs from 'dayjs'
import { Heart, MessageCircle, Repeat } from 'lucide-react'
import { Event, kinds, nip19, validateEvent } from 'nostr-tools'
@ -22,7 +22,6 @@ export default function NotificationList() {
const [notifications, setNotifications] = useState<Event[]>([])
const [until, setUntil] = useState<number | undefined>(dayjs().unix())
const bottomRef = useRef<HTMLDivElement | null>(null)
const observer = useRef<IntersectionObserver | null>(null)
useEffect(() => {
if (!pubkey) {
@ -74,19 +73,21 @@ export default function NotificationList() {
threshold: 1
}
observer.current = new IntersectionObserver((entries) => {
const observerInstance = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
loadMore()
}
}, options)
if (bottomRef.current) {
observer.current.observe(bottomRef.current)
const currentBottomRef = bottomRef.current
if (currentBottomRef) {
observerInstance.observe(currentBottomRef)
}
return () => {
if (observer.current && bottomRef.current) {
observer.current.unobserve(bottomRef.current)
if (observerInstance && currentBottomRef) {
observerInstance.unobserve(currentBottomRef)
}
}
}, [until, initialized, timelineKey])
@ -141,7 +142,7 @@ function ReactionNotification({ notification }: { notification: Event }) {
return eventId
? nip19.neventEncode(author ? { id: eventId, author } : { id: eventId })
: undefined
}, [notification.id])
}, [notification])
const { event } = useFetchEvent(bech32Id)
if (!event || !bech32Id || event.kind !== kinds.ShortTextNote) return null
@ -191,7 +192,7 @@ function RepostNotification({ notification }: { notification: Event }) {
} catch {
return null
}
}, [])
}, [notification.content])
if (!event) return null
return (

View file

@ -1,4 +1,4 @@
import { cn } from '@renderer/lib/utils'
import { cn } from '@/lib/utils'
import { useState } from 'react'
export default function NsfwOverlay({ className }: { className?: string }) {

View file

@ -1,7 +1,7 @@
import { cn } from '@/lib/utils'
import { Event } from 'nostr-tools'
import UserAvatar from '../UserAvatar'
import { cn } from '@renderer/lib/utils'
import { useTranslation } from 'react-i18next'
import UserAvatar from '../UserAvatar'
export default function ParentNotePreview({
event,

View file

@ -1,5 +1,5 @@
import PostDialog from '@renderer/components/PostDialog'
import { Button } from '@renderer/components/ui/button'
import PostDialog from '@/components/PostDialog'
import { Button } from '@/components/ui/button'
import { PencilLine } from 'lucide-react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'

View file

@ -1,7 +1,7 @@
import { Button } from '@renderer/components/ui/button'
import { Popover, PopoverContent, PopoverTrigger } from '@renderer/components/ui/popover'
import { extractMentions } from '@renderer/lib/event'
import { useNostr } from '@renderer/providers/NostrProvider'
import { Button } from '@/components/ui/button'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { extractMentions } from '@/lib/event'
import { useNostr } from '@/providers/NostrProvider'
import { Event } from 'nostr-tools'
import { useEffect, useState } from 'react'
import UserAvatar from '../UserAvatar'
@ -23,7 +23,7 @@ export default function Mentions({
extractMentions(content, parentEvent).then(({ pubkeys }) =>
setPubkeys(pubkeys.filter((p) => p !== pubkey))
)
}, [content])
}, [content, parentEvent, pubkey])
return (
<Popover>

View file

@ -1,4 +1,4 @@
import { Card } from '@renderer/components/ui/card'
import { Card } from '@/components/ui/card'
import dayjs from 'dayjs'
import Content from '../Content'

View file

@ -1,8 +1,9 @@
import { Button } from '@renderer/components/ui/button'
import { useToast } from '@renderer/hooks/use-toast'
import { useNostr } from '@renderer/providers/NostrProvider'
import { Button } from '@/components/ui/button'
import { useToast } from '@/hooks/use-toast'
import { useNostr } from '@/providers/NostrProvider'
import { ImageUp, LoaderCircle } from 'lucide-react'
import { useRef, useState } from 'react'
import { z } from 'zod'
export default function Uploader({
setContent
@ -38,7 +39,8 @@ export default function Uploader({
}
const data = await response.json()
const imageUrl = data.nip94_event?.tags.find(([tagName]) => tagName === 'url')?.[1]
const tags = z.array(z.array(z.string())).parse(data.nip94_event?.tags ?? [])
const imageUrl = tags.find(([tagName]) => tagName === 'url')?.[1]
if (imageUrl) {
setContent((prevContent) => `${prevContent}\n${imageUrl}`)
} else {

View file

@ -1,17 +1,17 @@
import { Button } from '@renderer/components/ui/button'
import { Button } from '@/components/ui/button'
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle
} from '@renderer/components/ui/dialog'
import { ScrollArea } from '@renderer/components/ui/scroll-area'
import { Textarea } from '@renderer/components/ui/textarea'
import { useToast } from '@renderer/hooks/use-toast'
import { createShortTextNoteDraftEvent } from '@renderer/lib/draft-event'
import { useNostr } from '@renderer/providers/NostrProvider'
import client from '@renderer/services/client.service'
} from '@/components/ui/dialog'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Textarea } from '@/components/ui/textarea'
import { useToast } from '@/hooks/use-toast'
import { createShortTextNoteDraftEvent } from '@/lib/draft-event'
import { useNostr } from '@/providers/NostrProvider'
import client from '@/services/client.service'
import { LoaderCircle } from 'lucide-react'
import { Event } from 'nostr-tools'
import { Dispatch, useState } from 'react'

View file

@ -1,6 +1,6 @@
import { Image } from '@nextui-org/image'
import { generateImageByPubkey } from '@renderer/lib/pubkey'
import { cn } from '@renderer/lib/utils'
import { generateImageByPubkey } from '@/lib/pubkey'
import { cn } from '@/lib/utils'
import { useEffect, useMemo, useState } from 'react'
export default function ProfileBanner({

View file

@ -1,6 +1,6 @@
import { Avatar, AvatarFallback, AvatarImage } from '@renderer/components/ui/avatar'
import { useFetchProfile } from '@renderer/hooks'
import { generateImageByPubkey } from '@renderer/lib/pubkey'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { useFetchProfile } from '@/hooks'
import { generateImageByPubkey } from '@/lib/pubkey'
import { useMemo } from 'react'
import FollowButton from '../FollowButton'
import Nip05 from '../Nip05'

View file

@ -1,5 +1,5 @@
import { Button } from '@renderer/components/ui/button'
import { usePrimaryPage } from '@renderer/PageManager'
import { Button } from '@/components/ui/button'
import { usePrimaryPage } from '@/PageManager'
import { RefreshCcw } from 'lucide-react'
import { useTranslation } from 'react-i18next'

View file

@ -1,12 +1,12 @@
import { Button } from '@renderer/components/ui/button'
import { Button } from '@/components/ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger
} from '@renderer/components/ui/dropdown-menu'
import { Input } from '@renderer/components/ui/input'
import { useRelaySettings } from '@renderer/providers/RelaySettingsProvider'
} from '@/components/ui/dropdown-menu'
import { Input } from '@/components/ui/input'
import { useRelaySettings } from '@/providers/RelaySettingsProvider'
import { Check, ChevronDown, Circle, CircleCheck, EllipsisVertical } from 'lucide-react'
import { useState } from 'react'
import RelayUrls from './RelayUrl'

View file

@ -1,19 +1,20 @@
import { Button } from '@renderer/components/ui/button'
import { Input } from '@renderer/components/ui/input'
import { useFetchRelayInfos } from '@renderer/hooks'
import { isWebsocketUrl, normalizeUrl } from '@renderer/lib/url'
import { useRelaySettings } from '@renderer/providers/RelaySettingsProvider'
import client from '@renderer/services/client.service'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { useFetchRelayInfos } from '@/hooks'
import { isWebsocketUrl, normalizeUrl } from '@/lib/url'
import { useRelaySettings } from '@/providers/RelaySettingsProvider'
import client from '@/services/client.service'
import { CircleX, SearchCheck } from 'lucide-react'
import { useEffect, useState } from 'react'
import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
export default function RelayUrls({ groupName }: { groupName: string }) {
const { t } = useTranslation()
const { relayGroups, updateRelayGroupRelayUrls } = useRelaySettings()
const rawRelayUrls = relayGroups.find((group) => group.groupName === groupName)?.relayUrls ?? []
const isActive = relayGroups.find((group) => group.groupName === groupName)?.isActive ?? false
const isActive = useMemo(
() => relayGroups.find((group) => group.groupName === groupName)?.isActive ?? false,
[relayGroups, groupName]
)
const [newRelayUrl, setNewRelayUrl] = useState('')
const [newRelayUrlError, setNewRelayUrlError] = useState<string | null>(null)
const [relays, setRelays] = useState<
@ -21,7 +22,11 @@ export default function RelayUrls({ groupName }: { groupName: string }) {
url: string
isConnected: boolean
}[]
>(rawRelayUrls.map((url) => ({ url, isConnected: false })))
>(
relayGroups
.find((group) => group.groupName === groupName)
?.relayUrls.map((url) => ({ url, isConnected: false })) ?? []
)
useEffect(() => {
const interval = setInterval(() => {

View file

@ -1,7 +1,7 @@
import { Button } from '@renderer/components/ui/button'
import { useFetchRelayInfos } from '@renderer/hooks'
import { useRelaySettings } from '@renderer/providers/RelaySettingsProvider'
import client from '@renderer/services/client.service'
import { Button } from '@/components/ui/button'
import { useFetchRelayInfos } from '@/hooks'
import { useRelaySettings } from '@/providers/RelaySettingsProvider'
import client from '@/services/client.service'
import { Save, SearchCheck } from 'lucide-react'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'

View file

@ -1,7 +1,7 @@
import { Button } from '@renderer/components/ui/button'
import { Input } from '@renderer/components/ui/input'
import { Separator } from '@renderer/components/ui/separator'
import { useRelaySettings } from '@renderer/providers/RelaySettingsProvider'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Separator } from '@/components/ui/separator'
import { useRelaySettings } from '@/providers/RelaySettingsProvider'
import { useEffect, useRef, useState } from 'react'
import { RelaySettingsComponentProvider } from './provider'
import RelayGroup from './RelayGroup'

View file

@ -1,10 +1,10 @@
import RelaySettings from '@renderer/components/RelaySettings'
import { Button } from '@renderer/components/ui/button'
import { Popover, PopoverContent, PopoverTrigger } from '@renderer/components/ui/popover'
import { ScrollArea } from '@renderer/components/ui/scroll-area'
import { toRelaySettings } from '@renderer/lib/link'
import { SecondaryPageLink } from '@renderer/PageManager'
import { useScreenSize } from '@renderer/providers/ScreenSizeProvider'
import RelaySettings from '@/components/RelaySettings'
import { Button } from '@/components/ui/button'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { ScrollArea } from '@/components/ui/scroll-area'
import { toRelaySettings } from '@/lib/link'
import { SecondaryPageLink } from '@/PageManager'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { Server } from 'lucide-react'
import { useTranslation } from 'react-i18next'

View file

@ -1,10 +1,10 @@
import { Separator } from '@renderer/components/ui/separator'
import { isReplyNoteEvent } from '@renderer/lib/event'
import { isReplyETag, isRootETag } from '@renderer/lib/tag'
import { cn } from '@renderer/lib/utils'
import { useNostr } from '@renderer/providers/NostrProvider'
import { useNoteStats } from '@renderer/providers/NoteStatsProvider'
import client from '@renderer/services/client.service'
import { Separator } from '@/components/ui/separator'
import { isReplyNoteEvent } from '@/lib/event'
import { isReplyETag, isRootETag } from '@/lib/tag'
import { cn } from '@/lib/utils'
import { useNostr } from '@/providers/NostrProvider'
import { useNoteStats } from '@/providers/NoteStatsProvider'
import client from '@/services/client.service'
import dayjs from 'dayjs'
import { Event as NEvent, kinds } from 'nostr-tools'
import { useEffect, useRef, useState } from 'react'
@ -43,7 +43,7 @@ export default function ReplyNoteList({ event, className }: { event: NEvent; cla
return () => {
client.removeEventListener('eventPublished', handleEventPublished)
}
}, [])
}, [event])
useEffect(() => {
if (loading) return
@ -87,7 +87,7 @@ export default function ReplyNoteList({ event, className }: { event: NEvent; cla
return () => {
promise.then((closer) => closer?.())
}
}, [])
}, [event])
useEffect(() => {
updateNoteReplyCount(event.id, replies.length)
@ -123,7 +123,7 @@ export default function ReplyNoteList({ event, className }: { event: NEvent; cla
replyMap[reply.id] = { event: reply, level: level + 1, parent }
}
setReplyMap(replyMap)
}, [replies])
}, [replies, event.id, updateNoteReplyCount])
const loadMore = async () => {
if (loading || !until || !timelineKey) return

View file

@ -1,5 +1,5 @@
import { Button } from '@renderer/components/ui/button'
import { cn } from '@renderer/lib/utils'
import { Button } from '@/components/ui/button'
import { cn } from '@/lib/utils'
import { ChevronUp } from 'lucide-react'
export default function ScrollToTopButton({

View file

@ -1,4 +1,4 @@
import { Button } from '@renderer/components/ui/button'
import { Button } from '@/components/ui/button'
import { Search } from 'lucide-react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'

View file

@ -1,17 +1,11 @@
import { SecondaryPageLink } from '@renderer/PageManager'
import { Avatar, AvatarFallback, AvatarImage } from '@renderer/components/ui/avatar'
import {
CommandDialog,
CommandInput,
CommandItem,
CommandList
} from '@renderer/components/ui/command'
import { useSearchProfiles } from '@renderer/hooks'
import { isMacOS } from '@renderer/lib/env'
import { toNote, toNoteList, toProfile, toProfileList } from '@renderer/lib/link'
import { generateImageByPubkey } from '@renderer/lib/pubkey'
import { useRelaySettings } from '@renderer/providers/RelaySettingsProvider'
import { TProfile } from '@renderer/types'
import { SecondaryPageLink } from '@/PageManager'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { CommandDialog, CommandInput, CommandItem, CommandList } from '@/components/ui/command'
import { useSearchProfiles } from '@/hooks'
import { toNote, toNoteList, toProfile, toProfileList } from '@/lib/link'
import { generateImageByPubkey } from '@/lib/pubkey'
import { useRelaySettings } from '@/providers/RelaySettingsProvider'
import { TProfile } from '@/types'
import { Hash, Notebook, UserRound } from 'lucide-react'
import { nip19 } from 'nostr-tools'
import { Dispatch, useEffect, useMemo, useState } from 'react'
@ -68,7 +62,7 @@ export function SearchDialog({ open, setOpen }: { open: boolean; setOpen: Dispat
)}
</>
)
}, [input, profiles])
}, [input, profiles, setOpen])
useEffect(() => {
const handler = setTimeout(() => {
@ -81,11 +75,7 @@ export function SearchDialog({ open, setOpen }: { open: boolean; setOpen: Dispat
}, [input])
return (
<CommandDialog
open={open}
onOpenChange={setOpen}
classNames={{ content: isMacOS() ? 'max-sm:top-9' : 'max-sm:top-0' }}
>
<CommandDialog open={open} onOpenChange={setOpen} classNames={{ content: 'max-sm:top-0' }}>
<CommandInput value={input} onValueChange={setInput} />
<CommandList>{list}</CommandList>
</CommandDialog>

View file

@ -1,6 +1,5 @@
import Logo from '@renderer/assets/Logo'
import { Button } from '@renderer/components/ui/button'
import { IS_ELECTRON } from '@renderer/lib/env'
import Logo from '@/assets/Logo'
import { Button } from '@/components/ui/button'
import { Info } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import AboutInfoDialog from '../AboutInfoDialog'
@ -15,24 +14,22 @@ export default function PrimaryPageSidebar() {
const { t } = useTranslation()
return (
<div className="w-52 h-full shrink-0 hidden xl:flex flex-col pb-8 pt-10 pl-4 justify-between relative">
<div className="draggable absolute top-0 left-0 h-11 w-full" />
<div className="absolute top-0 left-0 h-11 w-full" />
<div className="space-y-2">
<div className="draggable ml-4 mb-8 w-40">
<div className="ml-4 mb-8 w-40">
<Logo />
</div>
<PostButton variant="sidebar" />
<RelaySettingsButton variant="sidebar" />
<NotificationButton variant="sidebar" />
<SearchButton variant="sidebar" />
{IS_ELECTRON && <RefreshButton variant="sidebar" />}
{!IS_ELECTRON && (
<AboutInfoDialog>
<Button variant="sidebar" size="sidebar">
<Info />
{t('About')}
</Button>
</AboutInfoDialog>
)}
<RefreshButton variant="sidebar" />
<AboutInfoDialog>
<Button variant="sidebar" size="sidebar">
<Info />
{t('About')}
</Button>
</AboutInfoDialog>
</div>
<AccountButton variant="sidebar" />
</div>

View file

@ -1,5 +1,5 @@
import { Button } from '@renderer/components/ui/button'
import { useTheme } from '@renderer/providers/ThemeProvider'
import { Button } from '@/components/ui/button'
import { useTheme } from '@/providers/ThemeProvider'
import { Moon, Sun, SunMoon } from 'lucide-react'
import { useTranslation } from 'react-i18next'

View file

@ -0,0 +1,23 @@
import { cn } from '@/lib/utils'
export function Titlebar({
children,
className,
visible = true
}: {
children?: React.ReactNode
className?: string
visible?: boolean
}) {
return (
<div
className={cn(
'absolute top-0 w-full h-9 max-sm:h-11 z-50 bg-background/80 backdrop-blur-md flex items-center font-semibold gap-1 px-2 duration-700 transition-transform',
visible ? '' : '-translate-y-full',
className
)}
>
{children}
</div>
)
}

View file

@ -1,11 +1,11 @@
import { Avatar, AvatarFallback, AvatarImage } from '@renderer/components/ui/avatar'
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@renderer/components/ui/hover-card'
import { Skeleton } from '@renderer/components/ui/skeleton'
import { useFetchProfile } from '@renderer/hooks'
import { generateImageByPubkey } from '@renderer/lib/pubkey'
import { toProfile } from '@renderer/lib/link'
import { cn } from '@renderer/lib/utils'
import { SecondaryPageLink } from '@renderer/PageManager'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card'
import { Skeleton } from '@/components/ui/skeleton'
import { useFetchProfile } from '@/hooks'
import { generateImageByPubkey } from '@/lib/pubkey'
import { toProfile } from '@/lib/link'
import { cn } from '@/lib/utils'
import { SecondaryPageLink } from '@/PageManager'
import ProfileCard from '../ProfileCard'
import { useMemo } from 'react'

View file

@ -1,8 +1,8 @@
import FollowButton from '@renderer/components/FollowButton'
import Nip05 from '@renderer/components/Nip05'
import UserAvatar from '@renderer/components/UserAvatar'
import Username from '@renderer/components/Username'
import { useFetchProfile } from '@renderer/hooks'
import FollowButton from '@/components/FollowButton'
import Nip05 from '@/components/Nip05'
import UserAvatar from '@/components/UserAvatar'
import Username from '@/components/Username'
import { useFetchProfile } from '@/hooks'
export default function UserItem({ pubkey }: { pubkey: string }) {
const { profile } = useFetchProfile(pubkey)

View file

@ -1,9 +1,9 @@
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@renderer/components/ui/hover-card'
import { Skeleton } from '@renderer/components/ui/skeleton'
import { useFetchProfile } from '@renderer/hooks'
import { toProfile } from '@renderer/lib/link'
import { cn } from '@renderer/lib/utils'
import { SecondaryPageLink } from '@renderer/PageManager'
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card'
import { Skeleton } from '@/components/ui/skeleton'
import { useFetchProfile } from '@/hooks'
import { toProfile } from '@/lib/link'
import { cn } from '@/lib/utils'
import { SecondaryPageLink } from '@/PageManager'
import ProfileCard from '../ProfileCard'
export default function Username({

View file

@ -1,4 +1,4 @@
import { cn } from '@renderer/lib/utils'
import { cn } from '@/lib/utils'
import NsfwOverlay from '../NsfwOverlay'
export default function VideoPlayer({

View file

@ -1,6 +1,6 @@
import { Image } from '@nextui-org/image'
import { useFetchWebMetadata } from '@renderer/hooks/useFetchWebMetadata'
import { cn } from '@renderer/lib/utils'
import { useFetchWebMetadata } from '@/hooks/useFetchWebMetadata'
import { cn } from '@/lib/utils'
import { useMemo } from 'react'
export default function WebPreview({

View file

@ -1,7 +1,7 @@
import * as React from "react"
import * as AvatarPrimitive from "@radix-ui/react-avatar"
import * as React from 'react'
import * as AvatarPrimitive from '@radix-ui/react-avatar'
import { cn } from "@renderer/lib/utils"
import { cn } from '@/lib/utils'
const Avatar = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Root>,
@ -9,10 +9,7 @@ const Avatar = React.forwardRef<
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Root
ref={ref}
className={cn(
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
className
)}
className={cn('relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full', className)}
{...props}
/>
))
@ -24,7 +21,7 @@ const AvatarImage = React.forwardRef<
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Image
ref={ref}
className={cn("aspect-square h-full w-full", className)}
className={cn('aspect-square h-full w-full', className)}
{...props}
/>
))
@ -37,7 +34,7 @@ const AvatarFallback = React.forwardRef<
<AvatarPrimitive.Fallback
ref={ref}
className={cn(
"flex h-full w-full items-center justify-center rounded-full bg-muted",
'flex h-full w-full items-center justify-center rounded-full bg-muted',
className
)}
{...props}

View file

@ -2,29 +2,30 @@ import * as React from 'react'
import { Slot } from '@radix-ui/react-slot'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@renderer/lib/utils'
import { cn } from '@/lib/utils'
const buttonVariants = cva(
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground hover:bg-muted/80',
default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
outline:
'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
'secondary-2': 'bg-secondary text-secondary-foreground hover:bg-highlight',
ghost: 'text-muted-foreground hover:bg-accent hover:text-foreground',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
titlebar: 'non-draggable hover:bg-accent hover:text-accent-foreground',
sidebar: 'non-draggable hover:bg-accent hover:text-accent-foreground',
'small-screen-titlebar': 'non-draggable hover:bg-accent hover:text-accent-foreground'
titlebar: 'hover:bg-accent hover:text-accent-foreground',
sidebar: 'hover:bg-accent hover:text-accent-foreground',
'small-screen-titlebar': 'hover:bg-accent hover:text-accent-foreground'
},
size: {
default: 'h-8 rounded-lg px-3',
sm: 'h-8 rounded-lg px-2',
lg: 'h-10 px-4 py-2',
icon: 'h-8 w-8 rounded-full',
default: 'h-9 px-4 py-2',
sm: 'h-8 rounded-md px-3 text-xs',
lg: 'h-10 rounded-md px-8',
icon: 'h-9 w-9',
titlebar: 'h-7 w-7 rounded-full',
sidebar: 'w-full flex py-2 px-4 rounded-full justify-start gap-4 text-lg font-semibold',
'small-screen-titlebar': 'h-8 w-8 rounded-full'

View file

@ -1,6 +1,6 @@
import * as React from 'react'
import { cn } from '@renderer/lib/utils'
import { cn } from '@/lib/utils'
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
@ -20,23 +20,22 @@ const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDiv
)
CardHeader.displayName = 'CardHeader'
const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
const CardTitle = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<h3
<div
ref={ref}
className={cn('text-2xl font-semibold leading-none tracking-tight', className)}
className={cn('font-semibold leading-none tracking-tight', className)}
{...props}
/>
)
)
CardTitle.displayName = 'CardTitle'
const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<p ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} />
))
const CardDescription = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} />
)
)
CardDescription.displayName = 'CardDescription'
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(

View file

@ -9,9 +9,9 @@ import {
DialogDescription,
DialogHeader,
DialogTitle
} from '@renderer/components/ui/dialog'
import { ScrollArea } from '@renderer/components/ui/scroll-area'
import { cn } from '@renderer/lib/utils'
} from '@/components/ui/dialog'
import { ScrollArea } from '@/components/ui/scroll-area'
import { cn } from '@/lib/utils'
const Command = React.forwardRef<
React.ElementRef<typeof CommandPrimitive>,
@ -65,7 +65,7 @@ const CommandInput = React.forwardRef<
<CommandPrimitive.Input
ref={ref}
className={cn(
'flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 pr-6',
'flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 pr-6',
className
)}
{...props}
@ -130,7 +130,7 @@ const CommandItem = React.forwardRef<
<CommandPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-pointer gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected='true']:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
'relative flex cursor-pointer gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
className
)}
{...props}

View file

@ -2,7 +2,7 @@ import * as React from 'react'
import * as DialogPrimitive from '@radix-ui/react-dialog'
import { X } from 'lucide-react'
import { cn } from '@renderer/lib/utils'
import { cn } from '@/lib/utils'
const Dialog = DialogPrimitive.Root
@ -94,8 +94,8 @@ export {
Dialog,
DialogPortal,
DialogOverlay,
DialogClose,
DialogTrigger,
DialogClose,
DialogContent,
DialogHeader,
DialogFooter,

View file

@ -2,7 +2,7 @@ import * as React from 'react'
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
import { Check, ChevronRight, Circle } from 'lucide-react'
import { cn } from '@renderer/lib/utils'
import { cn } from '@/lib/utils'
const DropdownMenu = DropdownMenuPrimitive.Root

View file

@ -1,7 +1,7 @@
import * as React from 'react'
import * as HoverCardPrimitive from '@radix-ui/react-hover-card'
import { cn } from '@renderer/lib/utils'
import { cn } from '@/lib/utils'
const HoverCard = HoverCardPrimitive.Root

View file

@ -0,0 +1,22 @@
import * as React from 'react'
import { cn } from '@/lib/utils'
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
className
)}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = 'Input'
export { Input }

View file

@ -1,12 +1,14 @@
import * as React from 'react'
import * as PopoverPrimitive from '@radix-ui/react-popover'
import { cn } from '@renderer/lib/utils'
import { cn } from '@/lib/utils'
const Popover = PopoverPrimitive.Root
const PopoverTrigger = PopoverPrimitive.Trigger
const PopoverAnchor = PopoverPrimitive.Anchor
const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
@ -27,4 +29,4 @@ const PopoverContent = React.forwardRef<
))
PopoverContent.displayName = PopoverPrimitive.Content.displayName
export { Popover, PopoverTrigger, PopoverContent }
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }

View file

@ -1,17 +1,14 @@
import { GripVertical } from "lucide-react"
import * as ResizablePrimitive from "react-resizable-panels"
import { GripVertical } from 'lucide-react'
import * as ResizablePrimitive from 'react-resizable-panels'
import { cn } from "@renderer/lib/utils"
import { cn } from '@/lib/utils'
const ResizablePanelGroup = ({
className,
...props
}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => (
<ResizablePrimitive.PanelGroup
className={cn(
"flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
className
)}
className={cn('flex h-full w-full data-[panel-group-direction=vertical]:flex-col', className)}
{...props}
/>
)
@ -27,7 +24,7 @@ const ResizableHandle = ({
}) => (
<ResizablePrimitive.PanelResizeHandle
className={cn(
"relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
'relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90',
className
)}
{...props}

View file

@ -1,7 +1,7 @@
import * as React from 'react'
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'
import { cn } from '@renderer/lib/utils'
import { cn } from '@/lib/utils'
const ScrollArea = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.Root>,

View file

@ -0,0 +1,24 @@
import * as React from 'react'
import * as SeparatorPrimitive from '@radix-ui/react-separator'
import { cn } from '@/lib/utils'
const Separator = React.forwardRef<
React.ElementRef<typeof SeparatorPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
>(({ className, orientation = 'horizontal', decorative = true, ...props }, ref) => (
<SeparatorPrimitive.Root
ref={ref}
decorative={decorative}
orientation={orientation}
className={cn(
'shrink-0 bg-border',
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
className
)}
{...props}
/>
))
Separator.displayName = SeparatorPrimitive.Root.displayName
export { Separator }

View file

@ -0,0 +1,7 @@
import { cn } from '@/lib/utils'
function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
return <div className={cn('animate-pulse rounded-md bg-primary/10', className)} {...props} />
}
export { Skeleton }

View file

@ -0,0 +1,27 @@
import * as React from 'react'
import * as SwitchPrimitives from '@radix-ui/react-switch'
import { cn } from '@/lib/utils'
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
'peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
className
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
'pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0'
)}
/>
</SwitchPrimitives.Root>
))
Switch.displayName = SwitchPrimitives.Root.displayName
export { Switch }

View file

@ -0,0 +1,21 @@
import * as React from 'react'
import { cn } from '@/lib/utils'
const Textarea = React.forwardRef<HTMLTextAreaElement, React.ComponentProps<'textarea'>>(
({ className, ...props }, ref) => {
return (
<textarea
className={cn(
'flex min-h-[80px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
className
)}
ref={ref}
{...props}
/>
)
}
)
Textarea.displayName = 'Textarea'
export { Textarea }

View file

@ -1,9 +1,9 @@
import * as React from "react"
import * as ToastPrimitives from "@radix-ui/react-toast"
import { cva, type VariantProps } from "class-variance-authority"
import { X } from "lucide-react"
import * as React from 'react'
import * as ToastPrimitives from '@radix-ui/react-toast'
import { cva, type VariantProps } from 'class-variance-authority'
import { X } from 'lucide-react'
import { cn } from "@renderer/lib/utils"
import { cn } from '@/lib/utils'
const ToastProvider = ToastPrimitives.Provider
@ -14,7 +14,7 @@ const ToastViewport = React.forwardRef<
<ToastPrimitives.Viewport
ref={ref}
className={cn(
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
'fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]',
className
)}
{...props}
@ -23,25 +23,24 @@ const ToastViewport = React.forwardRef<
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
const toastVariants = cva(
"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
'group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full',
{
variants: {
variant: {
default: "border bg-background text-foreground",
default: 'border bg-background text-foreground',
destructive:
"destructive group border-destructive bg-destructive text-destructive-foreground",
},
'destructive group border-destructive bg-destructive text-destructive-foreground'
}
},
defaultVariants: {
variant: "default",
},
variant: 'default'
}
}
)
const Toast = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
VariantProps<typeof toastVariants>
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & VariantProps<typeof toastVariants>
>(({ className, variant, ...props }, ref) => {
return (
<ToastPrimitives.Root
@ -60,7 +59,7 @@ const ToastAction = React.forwardRef<
<ToastPrimitives.Action
ref={ref}
className={cn(
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
'inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive',
className
)}
{...props}
@ -75,7 +74,7 @@ const ToastClose = React.forwardRef<
<ToastPrimitives.Close
ref={ref}
className={cn(
"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
'absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600',
className
)}
toast-close=""
@ -92,7 +91,7 @@ const ToastTitle = React.forwardRef<
>(({ className, ...props }, ref) => (
<ToastPrimitives.Title
ref={ref}
className={cn("text-sm font-semibold", className)}
className={cn('text-sm font-semibold [&+div]:text-xs', className)}
{...props}
/>
))
@ -104,7 +103,7 @@ const ToastDescription = React.forwardRef<
>(({ className, ...props }, ref) => (
<ToastPrimitives.Description
ref={ref}
className={cn("text-sm opacity-90", className)}
className={cn('text-sm opacity-90', className)}
{...props}
/>
))
@ -123,5 +122,5 @@ export {
ToastTitle,
ToastDescription,
ToastClose,
ToastAction,
ToastAction
}

View file

@ -1,4 +1,4 @@
import { useToast } from '@renderer/hooks/use-toast'
import { useToast } from '@/hooks/use-toast'
import {
Toast,
ToastClose,
@ -6,7 +6,7 @@ import {
ToastProvider,
ToastTitle,
ToastViewport
} from '@renderer/components/ui/toast'
} from '@/components/ui/toast'
export function Toaster() {
const { toasts } = useToast()

View file

@ -1,12 +1,9 @@
"use client"
'use client'
// Inspired by react-hot-toast library
import * as React from "react"
import * as React from 'react'
import type {
ToastActionElement,
ToastProps,
} from "@renderer/components/ui/toast"
import type { ToastActionElement, ToastProps } from '@/components/ui/toast'
const TOAST_LIMIT = 1
const TOAST_REMOVE_DELAY = 1000000
@ -19,10 +16,10 @@ type ToasterToast = ToastProps & {
}
const actionTypes = {
ADD_TOAST: "ADD_TOAST",
UPDATE_TOAST: "UPDATE_TOAST",
DISMISS_TOAST: "DISMISS_TOAST",
REMOVE_TOAST: "REMOVE_TOAST",
ADD_TOAST: 'ADD_TOAST',
UPDATE_TOAST: 'UPDATE_TOAST',
DISMISS_TOAST: 'DISMISS_TOAST',
REMOVE_TOAST: 'REMOVE_TOAST'
} as const
let count = 0
@ -36,20 +33,20 @@ type ActionType = typeof actionTypes
type Action =
| {
type: ActionType["ADD_TOAST"]
type: ActionType['ADD_TOAST']
toast: ToasterToast
}
| {
type: ActionType["UPDATE_TOAST"]
type: ActionType['UPDATE_TOAST']
toast: Partial<ToasterToast>
}
| {
type: ActionType["DISMISS_TOAST"]
toastId?: ToasterToast["id"]
type: ActionType['DISMISS_TOAST']
toastId?: ToasterToast['id']
}
| {
type: ActionType["REMOVE_TOAST"]
toastId?: ToasterToast["id"]
type: ActionType['REMOVE_TOAST']
toastId?: ToasterToast['id']
}
interface State {
@ -66,8 +63,8 @@ const addToRemoveQueue = (toastId: string) => {
const timeout = setTimeout(() => {
toastTimeouts.delete(toastId)
dispatch({
type: "REMOVE_TOAST",
toastId: toastId,
type: 'REMOVE_TOAST',
toastId: toastId
})
}, TOAST_REMOVE_DELAY)
@ -76,21 +73,19 @@ const addToRemoveQueue = (toastId: string) => {
export const reducer = (state: State, action: Action): State => {
switch (action.type) {
case "ADD_TOAST":
case 'ADD_TOAST':
return {
...state,
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT)
}
case "UPDATE_TOAST":
case 'UPDATE_TOAST':
return {
...state,
toasts: state.toasts.map((t) =>
t.id === action.toast.id ? { ...t, ...action.toast } : t
),
toasts: state.toasts.map((t) => (t.id === action.toast.id ? { ...t, ...action.toast } : t))
}
case "DISMISS_TOAST": {
case 'DISMISS_TOAST': {
const { toastId } = action
// ! Side effects ! - This could be extracted into a dismissToast() action,
@ -109,22 +104,22 @@ export const reducer = (state: State, action: Action): State => {
t.id === toastId || toastId === undefined
? {
...t,
open: false,
open: false
}
: t
),
)
}
}
case "REMOVE_TOAST":
case 'REMOVE_TOAST':
if (action.toastId === undefined) {
return {
...state,
toasts: [],
toasts: []
}
}
return {
...state,
toasts: state.toasts.filter((t) => t.id !== action.toastId),
toasts: state.toasts.filter((t) => t.id !== action.toastId)
}
}
}
@ -140,34 +135,34 @@ function dispatch(action: Action) {
})
}
type Toast = Omit<ToasterToast, "id">
type Toast = Omit<ToasterToast, 'id'>
function toast({ ...props }: Toast) {
const id = genId()
const update = (props: ToasterToast) =>
dispatch({
type: "UPDATE_TOAST",
toast: { ...props, id },
type: 'UPDATE_TOAST',
toast: { ...props, id }
})
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
const dismiss = () => dispatch({ type: 'DISMISS_TOAST', toastId: id })
dispatch({
type: "ADD_TOAST",
type: 'ADD_TOAST',
toast: {
...props,
id,
open: true,
onOpenChange: (open) => {
if (!open) dismiss()
},
},
}
}
})
return {
id: id,
dismiss,
update,
update
}
}
@ -187,7 +182,7 @@ function useToast() {
return {
...state,
toast,
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
dismiss: (toastId?: string) => dispatch({ type: 'DISMISS_TOAST', toastId })
}
}

View file

@ -1,4 +1,4 @@
import client from '@renderer/services/client.service'
import client from '@/services/client.service'
import { Event } from 'nostr-tools'
import { useEffect, useState } from 'react'

View file

@ -1,5 +1,5 @@
import { tagNameEquals } from '@renderer/lib/tag'
import client from '@renderer/services/client.service'
import { tagNameEquals } from '@/lib/tag'
import client from '@/services/client.service'
import { Event } from 'nostr-tools'
import { useEffect, useState } from 'react'

View file

@ -1,4 +1,4 @@
import { verifyNip05 } from '@renderer/lib/nip05'
import { verifyNip05 } from '@/lib/nip05'
import { useEffect, useState } from 'react'
export function useFetchNip05(nip05?: string, pubkey?: string) {

View file

@ -1,5 +1,5 @@
import client from '@renderer/services/client.service'
import { TProfile } from '@renderer/types'
import client from '@/services/client.service'
import { TProfile } from '@/types'
import { useEffect, useState } from 'react'
export function useFetchProfile(id?: string) {

View file

@ -1,12 +1,13 @@
import { checkAlgoRelay } from '@renderer/lib/relay'
import client from '@renderer/services/client.service'
import { TRelayInfo } from '@renderer/types'
import { checkAlgoRelay } from '@/lib/relay'
import client from '@/services/client.service'
import { TRelayInfo } from '@/types'
import { useEffect, useState } from 'react'
export function useFetchRelayInfos(urls: string[]) {
const [isFetching, setIsFetching] = useState(true)
const [relayInfos, setRelayInfos] = useState<(TRelayInfo | undefined)[]>([])
const [areAlgoRelays, setAreAlgoRelays] = useState(false)
const urlsString = JSON.stringify(urls)
useEffect(() => {
const fetchRelayInfos = async () => {
@ -30,7 +31,7 @@ export function useFetchRelayInfos(urls: string[]) {
}
fetchRelayInfos()
}, [JSON.stringify(urls)])
}, [urlsString])
return { relayInfos, isFetching, areAlgoRelays }
}

View file

@ -1,6 +1,6 @@
import { TRelayList } from '@renderer/types'
import { TRelayList } from '@/types'
import { useEffect, useState } from 'react'
import client from '@renderer/services/client.service'
import client from '@/services/client.service'
export function useFetchRelayList(pubkey?: string | null) {
const [relayList, setRelayList] = useState<TRelayList>({ write: [], read: [] })

View file

@ -1,6 +1,6 @@
import { TWebMetadata } from '@renderer/types'
import { TWebMetadata } from '@/types'
import { useEffect, useState } from 'react'
import webService from '@renderer/services/web.service'
import webService from '@/services/web.service'
export function useFetchWebMetadata(url: string) {
const [metadata, setMetadata] = useState<TWebMetadata>({})

View file

@ -1,6 +1,6 @@
import { useRelaySettings } from '@renderer/providers/RelaySettingsProvider'
import client from '@renderer/services/client.service'
import { TProfile } from '@renderer/types'
import { useRelaySettings } from '@/providers/RelaySettingsProvider'
import client from '@/services/client.service'
import { TProfile } from '@/types'
import { useEffect, useState } from 'react'
export function useSearchProfiles(search: string, limit: number) {

View file

@ -79,10 +79,6 @@ export default {
notifications: 'notifications',
Notifications: 'Notifications',
'no more notifications': 'no more notifications',
'There are no secret keys stored on this device. Your nsec will be unprotected.':
'There are no secret keys stored on this device. Your nsec will be unprotected.',
'Your nsec will be encrypted using the {{backend}}.':
'Your nsec will be encrypted using the {{backend}}.',
'Using private key login is insecure. It is recommended to use a browser extension for login, such as alby, nostr-keyx or nos2x.':
'Using private key login is insecure. It is recommended to use a browser extension for login, such as alby, nostr-keyx or nos2x.',
'Login with Browser Extension': 'Login with Browser Extension',

Some files were not shown because too many files have changed in this diff Show more