feat: remove default favorite relays
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
import Sidebar from '@/components/Sidebar'
|
import Sidebar from '@/components/Sidebar'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import NoteListPage from '@/pages/primary/NoteListPage'
|
import NoteListPage from '@/pages/primary/NoteListPage'
|
||||||
import HomePage from '@/pages/secondary/HomePage'
|
|
||||||
import { CurrentRelaysProvider } from '@/providers/CurrentRelaysProvider'
|
import { CurrentRelaysProvider } from '@/providers/CurrentRelaysProvider'
|
||||||
import { TPageRef } from '@/types'
|
import { TPageRef } from '@/types'
|
||||||
import {
|
import {
|
||||||
@@ -455,7 +454,8 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) {
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'bg-background overflow-hidden',
|
'bg-background overflow-hidden',
|
||||||
themeSetting === 'pure-black' ? 'border-l' : 'rounded-lg shadow-lg'
|
themeSetting === 'pure-black' ? 'border-l' : 'rounded-lg shadow-lg',
|
||||||
|
secondaryStack.length === 0 ? 'bg-surface' : ''
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{secondaryStack.map((item, index) => (
|
{secondaryStack.map((item, index) => (
|
||||||
@@ -467,13 +467,6 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) {
|
|||||||
{item.component}
|
{item.component}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<div
|
|
||||||
key="home"
|
|
||||||
className="w-full"
|
|
||||||
style={{ display: secondaryStack.length === 0 ? 'block' : 'none' }}
|
|
||||||
>
|
|
||||||
<HomePage />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { toRelaySettings } from '@/lib/link'
|
import { toRelaySettings } from '@/lib/link'
|
||||||
import { simplifyUrl } from '@/lib/url'
|
import { simplifyUrl } from '@/lib/url'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
import { SecondaryPageLink } from '@/PageManager'
|
import { SecondaryPageLink } from '@/PageManager'
|
||||||
import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider'
|
import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider'
|
||||||
import { useFeed } from '@/providers/FeedProvider'
|
import { useFeed } from '@/providers/FeedProvider'
|
||||||
@@ -17,9 +18,9 @@ export default function FeedSwitcher({ close }: { close?: () => void }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{pubkey && (
|
|
||||||
<FeedSwitcherItem
|
<FeedSwitcherItem
|
||||||
isActive={feedInfo.feedType === 'following'}
|
isActive={feedInfo?.feedType === 'following'}
|
||||||
|
disabled={!pubkey}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!pubkey) return
|
if (!pubkey) return
|
||||||
switchFeed('following', { pubkey })
|
switchFeed('following', { pubkey })
|
||||||
@@ -33,7 +34,6 @@ export default function FeedSwitcher({ close }: { close?: () => void }) {
|
|||||||
<div>{t('Following')}</div>
|
<div>{t('Following')}</div>
|
||||||
</div>
|
</div>
|
||||||
</FeedSwitcherItem>
|
</FeedSwitcherItem>
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="flex justify-end items-center text-sm">
|
<div className="flex justify-end items-center text-sm">
|
||||||
<SecondaryPageLink
|
<SecondaryPageLink
|
||||||
@@ -50,7 +50,7 @@ export default function FeedSwitcher({ close }: { close?: () => void }) {
|
|||||||
<RelaySetCard
|
<RelaySetCard
|
||||||
key={set.id}
|
key={set.id}
|
||||||
relaySet={set}
|
relaySet={set}
|
||||||
select={feedInfo.feedType === 'relays' && set.id === feedInfo.id}
|
select={feedInfo?.feedType === 'relays' && set.id === feedInfo.id}
|
||||||
onSelectChange={(select) => {
|
onSelectChange={(select) => {
|
||||||
if (!select) return
|
if (!select) return
|
||||||
switchFeed('relays', { activeRelaySetId: set.id })
|
switchFeed('relays', { activeRelaySetId: set.id })
|
||||||
@@ -61,7 +61,7 @@ export default function FeedSwitcher({ close }: { close?: () => void }) {
|
|||||||
{favoriteRelays.map((relay) => (
|
{favoriteRelays.map((relay) => (
|
||||||
<FeedSwitcherItem
|
<FeedSwitcherItem
|
||||||
key={relay}
|
key={relay}
|
||||||
isActive={feedInfo.feedType === 'relay' && feedInfo.id === relay}
|
isActive={feedInfo?.feedType === 'relay' && feedInfo.id === relay}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
switchFeed('relay', { relay })
|
switchFeed('relay', { relay })
|
||||||
close?.()
|
close?.()
|
||||||
@@ -80,18 +80,27 @@ export default function FeedSwitcher({ close }: { close?: () => void }) {
|
|||||||
function FeedSwitcherItem({
|
function FeedSwitcherItem({
|
||||||
children,
|
children,
|
||||||
isActive,
|
isActive,
|
||||||
|
disabled,
|
||||||
onClick,
|
onClick,
|
||||||
controls
|
controls
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
isActive: boolean
|
isActive: boolean
|
||||||
|
disabled?: boolean
|
||||||
onClick: () => void
|
onClick: () => void
|
||||||
controls?: React.ReactNode
|
controls?: React.ReactNode
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`w-full border rounded-lg p-4 ${isActive ? 'border-primary bg-primary/5' : 'clickable'}`}
|
className={cn(
|
||||||
onClick={onClick}
|
'w-full border rounded-lg p-4',
|
||||||
|
disabled && 'opacity-50 pointer-events-none',
|
||||||
|
isActive ? 'border-primary bg-primary/5' : 'clickable'
|
||||||
|
)}
|
||||||
|
onClick={() => {
|
||||||
|
if (disabled) return
|
||||||
|
onClick()
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div className="font-semibold flex-1">{children}</div>
|
<div className="font-semibold flex-1">{children}</div>
|
||||||
|
|||||||
@@ -2,18 +2,6 @@ import { kinds } from 'nostr-tools'
|
|||||||
|
|
||||||
export const JUMBLE_API_BASE_URL = 'https://api.jumble.social'
|
export const JUMBLE_API_BASE_URL = 'https://api.jumble.social'
|
||||||
|
|
||||||
export const DEFAULT_FAVORITE_RELAYS = [
|
|
||||||
'wss://nostr.wine/',
|
|
||||||
'wss://pyramid.fiatjaf.com/',
|
|
||||||
'wss://relays.land/spatianostra/',
|
|
||||||
'wss://theforest.nostr1.com/',
|
|
||||||
'wss://algo.utxo.one/',
|
|
||||||
'wss://140.f7z.io/',
|
|
||||||
'wss://news.utxo.one/'
|
|
||||||
]
|
|
||||||
|
|
||||||
export const RECOMMENDED_RELAYS = DEFAULT_FAVORITE_RELAYS.concat(['wss://yabu.me/'])
|
|
||||||
|
|
||||||
export const RECOMMENDED_BLOSSOM_SERVERS = [
|
export const RECOMMENDED_BLOSSOM_SERVERS = [
|
||||||
'https://blossom.band/',
|
'https://blossom.band/',
|
||||||
'https://blossom.primal.net/',
|
'https://blossom.primal.net/',
|
||||||
|
|||||||
@@ -484,6 +484,11 @@ export default {
|
|||||||
Extension: 'امتداد',
|
Extension: 'امتداد',
|
||||||
Remote: 'عن بُعد',
|
Remote: 'عن بُعد',
|
||||||
'Encrypted Key': 'مفتاح مشفر',
|
'Encrypted Key': 'مفتاح مشفر',
|
||||||
'Private Key': 'مفتاح خاص'
|
'Private Key': 'مفتاح خاص',
|
||||||
|
'Welcome to Jumble': 'مرحبًا بك في Jumble',
|
||||||
|
'Jumble is a client focused on browsing relays. Get started by exploring interesting relays or login to view your following feed.':
|
||||||
|
'Jumble هو عميل يركز على تصفح المرحلات. ابدأ باستكشاف المرحلات المثيرة للاهتمام أو قم بتسجيل الدخول لعرض خلاصتك.',
|
||||||
|
'Explore Relays': 'استكشف المرحلات',
|
||||||
|
'Choose a feed': 'اختر خلاصة'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -498,6 +498,11 @@ export default {
|
|||||||
Extension: 'Erweiterung',
|
Extension: 'Erweiterung',
|
||||||
Remote: 'Remote',
|
Remote: 'Remote',
|
||||||
'Encrypted Key': 'Verschlüsselter Schlüssel',
|
'Encrypted Key': 'Verschlüsselter Schlüssel',
|
||||||
'Private Key': 'Privater Schlüssel'
|
'Private Key': 'Privater Schlüssel',
|
||||||
|
'Welcome to Jumble': 'Willkommen bei Jumble',
|
||||||
|
'Jumble is a client focused on browsing relays. Get started by exploring interesting relays or login to view your following feed.':
|
||||||
|
'Jumble ist ein Client, der sich auf das Durchsuchen von Relays konzentriert. Beginnen Sie mit der Erkundung interessanter Relays oder melden Sie sich an, um Ihren Following-Feed anzuzeigen.',
|
||||||
|
'Explore Relays': 'Relays erkunden',
|
||||||
|
'Choose a feed': 'Wähle einen Feed'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -483,6 +483,11 @@ export default {
|
|||||||
Extension: 'Extension',
|
Extension: 'Extension',
|
||||||
Remote: 'Remote',
|
Remote: 'Remote',
|
||||||
'Encrypted Key': 'Encrypted Key',
|
'Encrypted Key': 'Encrypted Key',
|
||||||
'Private Key': 'Private Key'
|
'Private Key': 'Private Key',
|
||||||
|
'Welcome to Jumble': 'Welcome to Jumble',
|
||||||
|
'Jumble is a client focused on browsing relays. Get started by exploring interesting relays or login to view your following feed.':
|
||||||
|
'Jumble is a client focused on browsing relays. Get started by exploring interesting relays or login to view your following feed.',
|
||||||
|
'Explore Relays': 'Explore Relays',
|
||||||
|
'Choose a feed': 'Choose a feed'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -492,6 +492,11 @@ export default {
|
|||||||
Extension: 'Extensión',
|
Extension: 'Extensión',
|
||||||
Remote: 'Remoto',
|
Remote: 'Remoto',
|
||||||
'Encrypted Key': 'Clave privada cifrada',
|
'Encrypted Key': 'Clave privada cifrada',
|
||||||
'Private Key': 'Clave privada'
|
'Private Key': 'Clave privada',
|
||||||
|
'Welcome to Jumble': 'Bienvenido a Jumble',
|
||||||
|
'Jumble is a client focused on browsing relays. Get started by exploring interesting relays or login to view your following feed.':
|
||||||
|
'Jumble es un cliente enfocado en explorar relays. Comienza explorando relays interesantes o inicia sesión para ver tu feed de seguidos.',
|
||||||
|
'Explore Relays': 'Explorar Relays',
|
||||||
|
'Choose a feed': 'Elige un feed'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -487,6 +487,11 @@ export default {
|
|||||||
Extension: 'افزونه',
|
Extension: 'افزونه',
|
||||||
Remote: 'از راه دور',
|
Remote: 'از راه دور',
|
||||||
'Encrypted Key': 'رمزگذاری شده کلید',
|
'Encrypted Key': 'رمزگذاری شده کلید',
|
||||||
'Private Key': 'کلید خصوصی'
|
'Private Key': 'کلید خصوصی',
|
||||||
|
'Welcome to Jumble': 'به Jumble خوش آمدید',
|
||||||
|
'Jumble is a client focused on browsing relays. Get started by exploring interesting relays or login to view your following feed.':
|
||||||
|
'Jumble یک کلاینت متمرکز بر مرور رلههاست. با کاوش در رلههای جالب شروع کنید یا وارد شوید تا فید دنبالکنندههای خود را مشاهده کنید.',
|
||||||
|
'Explore Relays': 'کاوش در رلهها',
|
||||||
|
'Choose a feed': 'یک فید انتخاب کنید'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -497,6 +497,11 @@ export default {
|
|||||||
Extension: 'Extension',
|
Extension: 'Extension',
|
||||||
Remote: 'Distant',
|
Remote: 'Distant',
|
||||||
'Encrypted Key': 'Clé chiffrée',
|
'Encrypted Key': 'Clé chiffrée',
|
||||||
'Private Key': 'Clé privée'
|
'Private Key': 'Clé privée',
|
||||||
|
'Welcome to Jumble': 'Bienvenue sur Jumble',
|
||||||
|
'Jumble is a client focused on browsing relays. Get started by exploring interesting relays or login to view your following feed.':
|
||||||
|
"Jumble est un client axé sur la navigation des relais. Commencez par explorer des relais intéressants ou connectez-vous pour voir votre fil d'abonnements.",
|
||||||
|
'Explore Relays': 'Explorer les relais',
|
||||||
|
'Choose a feed': 'Choisir un fil'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -489,6 +489,11 @@ export default {
|
|||||||
Extension: 'एक्सटेंशन',
|
Extension: 'एक्सटेंशन',
|
||||||
Remote: 'रिमोट',
|
Remote: 'रिमोट',
|
||||||
'Encrypted Key': 'एन्क्रिप्टेड की',
|
'Encrypted Key': 'एन्क्रिप्टेड की',
|
||||||
'Private Key': 'प्राइवेट की'
|
'Private Key': 'प्राइवेट की',
|
||||||
|
'Welcome to Jumble': 'Jumble में आपका स्वागत है',
|
||||||
|
'Jumble is a client focused on browsing relays. Get started by exploring interesting relays or login to view your following feed.':
|
||||||
|
'Jumble एक क्लाइंट है जो रिले ब्राउज़ करने पर केंद्रित है। रोचक रिले की खोज करके शुरू करें या अपनी फ़ॉलोइंग फ़ीड देखने के लिए लॉगिन करें।',
|
||||||
|
'Explore Relays': 'रिले एक्सप्लोर करें',
|
||||||
|
'Choose a feed': 'एक फीड चुनें'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -492,6 +492,11 @@ export default {
|
|||||||
Extension: 'Estensione',
|
Extension: 'Estensione',
|
||||||
Remote: 'Remoto',
|
Remote: 'Remoto',
|
||||||
'Encrypted Key': 'Chiave Crittografata',
|
'Encrypted Key': 'Chiave Crittografata',
|
||||||
'Private Key': 'Chiave Privata'
|
'Private Key': 'Chiave Privata',
|
||||||
|
'Welcome to Jumble': 'Benvenuto su Jumble',
|
||||||
|
'Jumble is a client focused on browsing relays. Get started by exploring interesting relays or login to view your following feed.':
|
||||||
|
'Jumble è un client focalizzato sulla navigazione dei relay. Inizia esplorando relay interessanti o effettua il login per visualizzare il tuo feed di following.',
|
||||||
|
'Explore Relays': 'Esplora Relay',
|
||||||
|
'Choose a feed': 'Scegli un feed'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -488,6 +488,11 @@ export default {
|
|||||||
Extension: '拡張機能',
|
Extension: '拡張機能',
|
||||||
Remote: 'リモート',
|
Remote: 'リモート',
|
||||||
'Encrypted Key': '暗号化キー',
|
'Encrypted Key': '暗号化キー',
|
||||||
'Private Key': '暗号化されたキー'
|
'Private Key': '暗号化されたキー',
|
||||||
|
'Welcome to Jumble': 'Jumbleへようこそ',
|
||||||
|
'Jumble is a client focused on browsing relays. Get started by exploring interesting relays or login to view your following feed.':
|
||||||
|
'Jumbleはリレーを閲覧することに焦点を当てたクライアントです。興味深いリレーを探索するか、ログインしてフォロー中のフィードを表示してください。',
|
||||||
|
'Explore Relays': 'リレーを探索',
|
||||||
|
'Choose a feed': 'フィードを選択'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -488,6 +488,11 @@ export default {
|
|||||||
Extension: '확장 프로그램',
|
Extension: '확장 프로그램',
|
||||||
Remote: '원격',
|
Remote: '원격',
|
||||||
'Encrypted Key': '암호화된 키',
|
'Encrypted Key': '암호화된 키',
|
||||||
'Private Key': '개인 키'
|
'Private Key': '개인 키',
|
||||||
|
'Welcome to Jumble': 'Jumble에 오신 것을 환영합니다',
|
||||||
|
'Jumble is a client focused on browsing relays. Get started by exploring interesting relays or login to view your following feed.':
|
||||||
|
'Jumble은 릴레이 탐색에 중점을 둔 클라이언트입니다. 흥미로운 릴레이를 탐색하거나 로그인하여 팔로잉 피드를 확인하세요.',
|
||||||
|
'Explore Relays': '릴레이 탐색',
|
||||||
|
'Choose a feed': '피드 선택'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -492,6 +492,11 @@ export default {
|
|||||||
Extension: 'Rozszerzenie',
|
Extension: 'Rozszerzenie',
|
||||||
Remote: 'Zdalne',
|
Remote: 'Zdalne',
|
||||||
'Encrypted Key': 'Zaszyfrowany Klucz',
|
'Encrypted Key': 'Zaszyfrowany Klucz',
|
||||||
'Private Key': 'Zaszyfrowany Klucz'
|
'Private Key': 'Zaszyfrowany Klucz',
|
||||||
|
'Welcome to Jumble': 'Witamy w Jumble',
|
||||||
|
'Jumble is a client focused on browsing relays. Get started by exploring interesting relays or login to view your following feed.':
|
||||||
|
'Jumble to klient skupiony na przeglądaniu relay. Zacznij od eksploracji ciekawych relay lub zaloguj się, aby zobaczyć swój feed obserwowanych.',
|
||||||
|
'Explore Relays': 'Eksploruj Relay',
|
||||||
|
'Choose a feed': 'Wybierz feed'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -489,6 +489,11 @@ export default {
|
|||||||
Extension: 'Extensão',
|
Extension: 'Extensão',
|
||||||
Remote: 'Remoto',
|
Remote: 'Remoto',
|
||||||
'Encrypted Key': 'Chave Criptografada',
|
'Encrypted Key': 'Chave Criptografada',
|
||||||
'Private Key': 'Chave Privada'
|
'Private Key': 'Chave Privada',
|
||||||
|
'Welcome to Jumble': 'Bem-vindo ao Jumble',
|
||||||
|
'Jumble is a client focused on browsing relays. Get started by exploring interesting relays or login to view your following feed.':
|
||||||
|
'Jumble é um cliente focado em navegar relays. Comece explorando relays interessantes ou faça login para ver seu feed de seguidos.',
|
||||||
|
'Explore Relays': 'Explorar Relays',
|
||||||
|
'Choose a feed': 'Escolha um feed'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -492,6 +492,11 @@ export default {
|
|||||||
Extension: 'Extensão',
|
Extension: 'Extensão',
|
||||||
Remote: 'Remoto',
|
Remote: 'Remoto',
|
||||||
'Encrypted Key': 'Chave Criptografada',
|
'Encrypted Key': 'Chave Criptografada',
|
||||||
'Private Key': 'Chave Privada'
|
'Private Key': 'Chave Privada',
|
||||||
|
'Welcome to Jumble': 'Bem-vindo ao Jumble',
|
||||||
|
'Jumble is a client focused on browsing relays. Get started by exploring interesting relays or login to view your following feed.':
|
||||||
|
'Jumble é um cliente focado em explorar relays. Comece por explorar relays interessantes ou inicie sessão para ver o seu feed de seguidos.',
|
||||||
|
'Explore Relays': 'Explorar Relays',
|
||||||
|
'Choose a feed': 'Escolha um feed'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -494,6 +494,11 @@ export default {
|
|||||||
Extension: 'Расширение',
|
Extension: 'Расширение',
|
||||||
Remote: 'Удалённый',
|
Remote: 'Удалённый',
|
||||||
'Encrypted Key': 'Зашифрованный ключ',
|
'Encrypted Key': 'Зашифрованный ключ',
|
||||||
'Private Key': 'Приватный ключ'
|
'Private Key': 'Приватный ключ',
|
||||||
|
'Welcome to Jumble': 'Добро пожаловать в Jumble',
|
||||||
|
'Jumble is a client focused on browsing relays. Get started by exploring interesting relays or login to view your following feed.':
|
||||||
|
'Jumble — это клиент, ориентированный на просмотр relay. Начните с изучения интересных relay или войдите, чтобы увидеть ленту подписок.',
|
||||||
|
'Explore Relays': 'Исследовать Relay',
|
||||||
|
'Choose a feed': 'Выберите ленту'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -482,6 +482,11 @@ export default {
|
|||||||
Extension: 'ส่วนขยาย',
|
Extension: 'ส่วนขยาย',
|
||||||
Remote: 'ระยะไกล',
|
Remote: 'ระยะไกล',
|
||||||
'Encrypted Key': 'คีย์ที่เข้ารหัส',
|
'Encrypted Key': 'คีย์ที่เข้ารหัส',
|
||||||
'Private Key': 'คีย์ส่วนตัว'
|
'Private Key': 'คีย์ส่วนตัว',
|
||||||
|
'Welcome to Jumble': 'ยินดีต้อนรับสู่ Jumble',
|
||||||
|
'Jumble is a client focused on browsing relays. Get started by exploring interesting relays or login to view your following feed.':
|
||||||
|
'Jumble เป็นไคลเอนต์ที่เน้นการเรียกดูรีเลย์ เริ่มต้นด้วยการสำรวจรีเลย์ที่น่าสนใจ หรือเข้าสู่ระบบเพื่อดูฟีดที่คุณติดตาม',
|
||||||
|
'Explore Relays': 'สำรวจรีเลย์',
|
||||||
|
'Choose a feed': 'เลือกฟีด'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -480,6 +480,11 @@ export default {
|
|||||||
Extension: '扩展',
|
Extension: '扩展',
|
||||||
Remote: '远程',
|
Remote: '远程',
|
||||||
'Encrypted Key': '加密私钥',
|
'Encrypted Key': '加密私钥',
|
||||||
'Private Key': '私钥'
|
'Private Key': '私钥',
|
||||||
|
'Welcome to Jumble': '欢迎来到 Jumble',
|
||||||
|
'Jumble is a client focused on browsing relays. Get started by exploring interesting relays or login to view your following feed.':
|
||||||
|
'Jumble 是一个专注于浏览服务器的客户端。从探索有趣的服务器开始,或者登录查看你的关注动态。',
|
||||||
|
'Explore Relays': '探索服务器',
|
||||||
|
'Choose a feed': '选择一个动态'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,21 +54,21 @@ const FeedSwitcherTrigger = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivEle
|
|||||||
const { feedInfo, relayUrls } = useFeed()
|
const { feedInfo, relayUrls } = useFeed()
|
||||||
const { relaySets } = useFavoriteRelays()
|
const { relaySets } = useFavoriteRelays()
|
||||||
const activeRelaySet = useMemo(() => {
|
const activeRelaySet = useMemo(() => {
|
||||||
return feedInfo.feedType === 'relays' && feedInfo.id
|
return feedInfo?.feedType === 'relays' && feedInfo.id
|
||||||
? relaySets.find((set) => set.id === feedInfo.id)
|
? relaySets.find((set) => set.id === feedInfo.id)
|
||||||
: undefined
|
: undefined
|
||||||
}, [feedInfo, relaySets])
|
}, [feedInfo, relaySets])
|
||||||
const title = useMemo(() => {
|
const title = useMemo(() => {
|
||||||
if (feedInfo.feedType === 'following') {
|
if (feedInfo?.feedType === 'following') {
|
||||||
return t('Following')
|
return t('Following')
|
||||||
}
|
}
|
||||||
if (relayUrls.length === 0) {
|
if (relayUrls.length === 0) {
|
||||||
return t('Choose a relay')
|
return t('Choose a feed')
|
||||||
}
|
}
|
||||||
if (feedInfo.feedType === 'relay') {
|
if (feedInfo?.feedType === 'relay') {
|
||||||
return simplifyUrl(feedInfo.id ?? '')
|
return simplifyUrl(feedInfo?.id ?? '')
|
||||||
}
|
}
|
||||||
if (feedInfo.feedType === 'relays') {
|
if (feedInfo?.feedType === 'relays') {
|
||||||
return activeRelaySet?.name ?? activeRelaySet?.id
|
return activeRelaySet?.name ?? activeRelaySet?.id
|
||||||
}
|
}
|
||||||
}, [feedInfo, activeRelaySet])
|
}, [feedInfo, activeRelaySet])
|
||||||
@@ -79,7 +79,7 @@ const FeedSwitcherTrigger = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivEle
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{feedInfo.feedType === 'following' ? <UsersRound /> : <Server />}
|
{feedInfo?.feedType === 'following' ? <UsersRound /> : <Server />}
|
||||||
<div className="text-lg font-semibold truncate">{title}</div>
|
<div className="text-lg font-semibold truncate">{title}</div>
|
||||||
<ChevronDown />
|
<ChevronDown />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export default function FollowingFeed() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function init() {
|
async function init() {
|
||||||
if (feedInfo.feedType !== 'following' || !pubkey) {
|
if (feedInfo?.feedType !== 'following' || !pubkey) {
|
||||||
setSubRequests([])
|
setSubRequests([])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ export default function FollowingFeed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init()
|
init()
|
||||||
}, [feedInfo.feedType, pubkey])
|
}, [feedInfo?.feedType, pubkey])
|
||||||
|
|
||||||
return <NormalFeed subRequests={subRequests} isMainFeed />
|
return <NormalFeed subRequests={subRequests} isMainFeed />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export default function RelaysFeed() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (feedInfo.feedType !== 'relay' && feedInfo.feedType !== 'relays') {
|
if (!feedInfo || (feedInfo.feedType !== 'relay' && feedInfo.feedType !== 'relays')) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useSecondaryPage } from '@/PageManager'
|
import { usePrimaryPage, useSecondaryPage } from '@/PageManager'
|
||||||
import PostEditor from '@/components/PostEditor'
|
import PostEditor from '@/components/PostEditor'
|
||||||
import RelayInfo from '@/components/RelayInfo'
|
import RelayInfo from '@/components/RelayInfo'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
@@ -9,7 +9,7 @@ import { useFeed } from '@/providers/FeedProvider'
|
|||||||
import { useNostr } from '@/providers/NostrProvider'
|
import { useNostr } from '@/providers/NostrProvider'
|
||||||
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
||||||
import { TPageRef } from '@/types'
|
import { TPageRef } from '@/types'
|
||||||
import { Info, PencilLine, Search } from 'lucide-react'
|
import { Compass, Info, LogIn, PencilLine, Search, Sparkles } from 'lucide-react'
|
||||||
import {
|
import {
|
||||||
Dispatch,
|
Dispatch,
|
||||||
forwardRef,
|
forwardRef,
|
||||||
@@ -28,8 +28,8 @@ const NoteListPage = forwardRef((_, ref) => {
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { addRelayUrls, removeRelayUrls } = useCurrentRelays()
|
const { addRelayUrls, removeRelayUrls } = useCurrentRelays()
|
||||||
const layoutRef = useRef<TPageRef>(null)
|
const layoutRef = useRef<TPageRef>(null)
|
||||||
const { pubkey, checkLogin } = useNostr()
|
const { pubkey } = useNostr()
|
||||||
const { feedInfo, relayUrls, isReady } = useFeed()
|
const { feedInfo, relayUrls, isReady, switchFeed } = useFeed()
|
||||||
const [showRelayDetails, setShowRelayDetails] = useState(false)
|
const [showRelayDetails, setShowRelayDetails] = useState(false)
|
||||||
useImperativeHandle(ref, () => layoutRef.current)
|
useImperativeHandle(ref, () => layoutRef.current)
|
||||||
|
|
||||||
@@ -48,17 +48,25 @@ const NoteListPage = forwardRef((_, ref) => {
|
|||||||
}
|
}
|
||||||
}, [relayUrls])
|
}, [relayUrls])
|
||||||
|
|
||||||
|
if (!feedInfo) {
|
||||||
|
return (
|
||||||
|
<PrimaryPageLayout
|
||||||
|
pageName="home"
|
||||||
|
ref={layoutRef}
|
||||||
|
titlebar={<NoteListPageTitlebar layoutRef={layoutRef} />}
|
||||||
|
displayScrollToTopButton
|
||||||
|
>
|
||||||
|
<WelcomeGuide />
|
||||||
|
</PrimaryPageLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
let content: React.ReactNode = null
|
let content: React.ReactNode = null
|
||||||
if (!isReady) {
|
if (!isReady) {
|
||||||
content = <div className="text-center text-sm text-muted-foreground">{t('loading...')}</div>
|
content = <div className="text-center text-sm text-muted-foreground">{t('loading...')}</div>
|
||||||
} else if (feedInfo.feedType === 'following' && !pubkey) {
|
} else if (feedInfo.feedType === 'following' && !pubkey) {
|
||||||
content = (
|
switchFeed(null)
|
||||||
<div className="flex justify-center w-full">
|
return null
|
||||||
<Button size="lg" onClick={() => checkLogin()}>
|
|
||||||
{t('Please login to view following feed')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
} else if (feedInfo.feedType === 'following') {
|
} else if (feedInfo.feedType === 'following') {
|
||||||
content = <FollowingFeed />
|
content = <FollowingFeed />
|
||||||
} else {
|
} else {
|
||||||
@@ -169,3 +177,38 @@ function SearchButton() {
|
|||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function WelcomeGuide() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { navigate } = usePrimaryPage()
|
||||||
|
const { checkLogin } = useNostr()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center min-h-[60vh] px-4 text-center space-y-6">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center w-full justify-center gap-2">
|
||||||
|
<Sparkles className="text-yellow-400" />
|
||||||
|
<h2 className="text-2xl font-bold">{t('Welcome to Jumble')}</h2>
|
||||||
|
<Sparkles className="text-yellow-400" />
|
||||||
|
</div>
|
||||||
|
<p className="text-muted-foreground max-w-md">
|
||||||
|
{t(
|
||||||
|
'Jumble is a client focused on browsing relays. Get started by exploring interesting relays or login to view your following feed.'
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col sm:flex-row gap-3 w-full max-w-md">
|
||||||
|
<Button size="lg" className="w-full" onClick={() => navigate('explore')}>
|
||||||
|
<Compass className="size-5" />
|
||||||
|
{t('Explore Relays')}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button size="lg" className="w-full" variant="outline" onClick={() => checkLogin()}>
|
||||||
|
<LogIn className="size-5" />
|
||||||
|
{t('Login')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
import { usePrimaryPage, useSecondaryPage } from '@/PageManager'
|
|
||||||
import RelaySimpleInfo from '@/components/RelaySimpleInfo'
|
|
||||||
import { Button } from '@/components/ui/button'
|
|
||||||
import { RECOMMENDED_RELAYS } from '@/constants'
|
|
||||||
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
|
|
||||||
import { toRelay } from '@/lib/link'
|
|
||||||
import relayInfoService from '@/services/relay-info.service'
|
|
||||||
import { TRelayInfo } from '@/types'
|
|
||||||
import { ArrowRight, Server } from 'lucide-react'
|
|
||||||
import { forwardRef, useEffect, useState } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
|
|
||||||
const HomePage = forwardRef(({ index }: { index?: number }, ref) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const { navigate } = usePrimaryPage()
|
|
||||||
const { push } = useSecondaryPage()
|
|
||||||
const [recommendedRelayInfos, setRecommendedRelayInfos] = useState<TRelayInfo[]>([])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const init = async () => {
|
|
||||||
try {
|
|
||||||
const relays = await relayInfoService.getRelayInfos(RECOMMENDED_RELAYS)
|
|
||||||
setRecommendedRelayInfos(relays.filter(Boolean) as TRelayInfo[])
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to fetch recommended relays:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
init()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
if (!recommendedRelayInfos.length) {
|
|
||||||
return (
|
|
||||||
<SecondaryPageLayout ref={ref} index={index} hideBackButton hideTitlebarBottomBorder>
|
|
||||||
<div className="text-muted-foreground w-full h-screen flex items-center justify-center">
|
|
||||||
{t('Welcome! 🥳')}
|
|
||||||
</div>
|
|
||||||
</SecondaryPageLayout>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SecondaryPageLayout
|
|
||||||
ref={ref}
|
|
||||||
index={index}
|
|
||||||
title={
|
|
||||||
<>
|
|
||||||
<Server />
|
|
||||||
<div>{t('Recommended relays')}</div>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
hideBackButton
|
|
||||||
hideTitlebarBottomBorder
|
|
||||||
>
|
|
||||||
<div className="px-4 pt-2">
|
|
||||||
<div className="grid grid-cols-2 gap-3">
|
|
||||||
{recommendedRelayInfos.map((relayInfo) => (
|
|
||||||
<RelaySimpleInfo
|
|
||||||
key={relayInfo.url}
|
|
||||||
className="clickable h-auto px-4 py-3 rounded-lg border"
|
|
||||||
relayInfo={relayInfo}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
push(toRelay(relayInfo.url))
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="flex mt-2 justify-center">
|
|
||||||
<Button variant="ghost" onClick={() => navigate('explore')}>
|
|
||||||
<div>{t('Explore more')}</div>
|
|
||||||
<ArrowRight />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</SecondaryPageLayout>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
HomePage.displayName = 'HomePage'
|
|
||||||
export default HomePage
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BIG_RELAY_URLS, DEFAULT_FAVORITE_RELAYS } from '@/constants'
|
import { BIG_RELAY_URLS } from '@/constants'
|
||||||
import { createFavoriteRelaysDraftEvent, createRelaySetDraftEvent } from '@/lib/draft-event'
|
import { createFavoriteRelaysDraftEvent, createRelaySetDraftEvent } from '@/lib/draft-event'
|
||||||
import { getReplaceableEventIdentifier } from '@/lib/event'
|
import { getReplaceableEventIdentifier } from '@/lib/event'
|
||||||
import { getRelaySetFromEvent } from '@/lib/event-metadata'
|
import { getRelaySetFromEvent } from '@/lib/event-metadata'
|
||||||
@@ -43,7 +43,7 @@ export function FavoriteRelaysProvider({ children }: { children: React.ReactNode
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!favoriteRelaysEvent) {
|
if (!favoriteRelaysEvent) {
|
||||||
const favoriteRelays: string[] = DEFAULT_FAVORITE_RELAYS
|
const favoriteRelays: string[] = []
|
||||||
const storedRelaySets = storage.getRelaySets()
|
const storedRelaySets = storage.getRelaySets()
|
||||||
storedRelaySets.forEach(({ relayUrls }) => {
|
storedRelaySets.forEach(({ relayUrls }) => {
|
||||||
relayUrls.forEach((url) => {
|
relayUrls.forEach((url) => {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { DEFAULT_FAVORITE_RELAYS } from '@/constants'
|
|
||||||
import { getRelaySetFromEvent } from '@/lib/event-metadata'
|
import { getRelaySetFromEvent } from '@/lib/event-metadata'
|
||||||
import { isWebsocketUrl, normalizeUrl } from '@/lib/url'
|
import { isWebsocketUrl, normalizeUrl } from '@/lib/url'
|
||||||
import indexedDb from '@/services/indexed-db.service'
|
import indexedDb from '@/services/indexed-db.service'
|
||||||
@@ -14,7 +13,7 @@ type TFeedContext = {
|
|||||||
relayUrls: string[]
|
relayUrls: string[]
|
||||||
isReady: boolean
|
isReady: boolean
|
||||||
switchFeed: (
|
switchFeed: (
|
||||||
feedType: TFeedType,
|
feedType: TFeedType | null,
|
||||||
options?: { activeRelaySetId?: string; pubkey?: string; relay?: string | null }
|
options?: { activeRelaySetId?: string; pubkey?: string; relay?: string | null }
|
||||||
) => Promise<void>
|
) => Promise<void>
|
||||||
}
|
}
|
||||||
@@ -31,13 +30,10 @@ export const useFeed = () => {
|
|||||||
|
|
||||||
export function FeedProvider({ children }: { children: React.ReactNode }) {
|
export function FeedProvider({ children }: { children: React.ReactNode }) {
|
||||||
const { pubkey, isInitialized } = useNostr()
|
const { pubkey, isInitialized } = useNostr()
|
||||||
const { relaySets, favoriteRelays } = useFavoriteRelays()
|
const { relaySets } = useFavoriteRelays()
|
||||||
const [relayUrls, setRelayUrls] = useState<string[]>([])
|
const [relayUrls, setRelayUrls] = useState<string[]>([])
|
||||||
const [isReady, setIsReady] = useState(false)
|
const [isReady, setIsReady] = useState(false)
|
||||||
const [feedInfo, setFeedInfo] = useState<TFeedInfo>({
|
const [feedInfo, setFeedInfo] = useState<TFeedInfo>(null)
|
||||||
feedType: 'relay',
|
|
||||||
id: DEFAULT_FAVORITE_RELAYS[0]
|
|
||||||
})
|
|
||||||
const feedInfoRef = useRef<TFeedInfo>(feedInfo)
|
const feedInfoRef = useRef<TFeedInfo>(feedInfo)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -46,10 +42,7 @@ export function FeedProvider({ children }: { children: React.ReactNode }) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let feedInfo: TFeedInfo = {
|
let feedInfo: TFeedInfo = null
|
||||||
feedType: 'relay',
|
|
||||||
id: favoriteRelays[0] ?? DEFAULT_FAVORITE_RELAYS[0]
|
|
||||||
}
|
|
||||||
if (pubkey) {
|
if (pubkey) {
|
||||||
const storedFeedInfo = storage.getFeedInfo(pubkey)
|
const storedFeedInfo = storage.getFeedInfo(pubkey)
|
||||||
if (storedFeedInfo) {
|
if (storedFeedInfo) {
|
||||||
@@ -57,6 +50,10 @@ export function FeedProvider({ children }: { children: React.ReactNode }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!feedInfo) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (feedInfo.feedType === 'relays') {
|
if (feedInfo.feedType === 'relays') {
|
||||||
return await switchFeed('relays', { activeRelaySetId: feedInfo.id })
|
return await switchFeed('relays', { activeRelaySetId: feedInfo.id })
|
||||||
}
|
}
|
||||||
@@ -74,14 +71,27 @@ export function FeedProvider({ children }: { children: React.ReactNode }) {
|
|||||||
init()
|
init()
|
||||||
}, [pubkey, isInitialized])
|
}, [pubkey, isInitialized])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (pubkey && !feedInfo) {
|
||||||
|
switchFeed('following', { pubkey })
|
||||||
|
}
|
||||||
|
}, [pubkey, feedInfo])
|
||||||
|
|
||||||
const switchFeed = async (
|
const switchFeed = async (
|
||||||
feedType: TFeedType,
|
feedType: TFeedType | null,
|
||||||
options: {
|
options: {
|
||||||
activeRelaySetId?: string | null
|
activeRelaySetId?: string | null
|
||||||
pubkey?: string | null
|
pubkey?: string | null
|
||||||
relay?: string | null
|
relay?: string | null
|
||||||
} = {}
|
} = {}
|
||||||
) => {
|
) => {
|
||||||
|
if (!feedType) {
|
||||||
|
setFeedInfo(null)
|
||||||
|
feedInfoRef.current = null
|
||||||
|
setRelayUrls([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
setIsReady(false)
|
setIsReady(false)
|
||||||
if (feedType === 'relay') {
|
if (feedType === 'relay') {
|
||||||
const normalizedUrl = normalizeUrl(options.relay ?? '')
|
const normalizedUrl = normalizeUrl(options.relay ?? '')
|
||||||
|
|||||||
2
src/types/index.d.ts
vendored
2
src/types/index.d.ts
vendored
@@ -107,7 +107,7 @@ export type TAccount = {
|
|||||||
export type TAccountPointer = Pick<TAccount, 'pubkey' | 'signerType'>
|
export type TAccountPointer = Pick<TAccount, 'pubkey' | 'signerType'>
|
||||||
|
|
||||||
export type TFeedType = 'following' | 'relays' | 'relay'
|
export type TFeedType = 'following' | 'relays' | 'relay'
|
||||||
export type TFeedInfo = { feedType: TFeedType; id?: string }
|
export type TFeedInfo = { feedType: TFeedType; id?: string } | null
|
||||||
|
|
||||||
export type TLanguage = 'en' | 'zh' | 'pl'
|
export type TLanguage = 'en' | 'zh' | 'pl'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user