diff --git a/src/components/ContentPreview/FollowPackPreview.tsx b/src/components/ContentPreview/FollowPackPreview.tsx
new file mode 100644
index 00000000..f68aebce
--- /dev/null
+++ b/src/components/ContentPreview/FollowPackPreview.tsx
@@ -0,0 +1,22 @@
+import { getFollowPackInfoFromEvent } from '@/lib/event-metadata'
+import { cn } from '@/lib/utils'
+import { Event } from 'nostr-tools'
+import { useMemo } from 'react'
+import { useTranslation } from 'react-i18next'
+
+export default function FollowPackPreview({
+ event,
+ className
+}: {
+ event: Event
+ className?: string
+}) {
+ const { t } = useTranslation()
+ const { title } = useMemo(() => getFollowPackInfoFromEvent(event), [event])
+
+ return (
+
+ [{t('Follow Pack')}] {title}
+
+ )
+}
diff --git a/src/components/ContentPreview/index.tsx b/src/components/ContentPreview/index.tsx
index 18aeaa62..44c89402 100644
--- a/src/components/ContentPreview/index.tsx
+++ b/src/components/ContentPreview/index.tsx
@@ -8,6 +8,7 @@ import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import CommunityDefinitionPreview from './CommunityDefinitionPreview'
import EmojiPackPreview from './EmojiPackPreview'
+import FollowPackPreview from './FollowPackPreview'
import GroupMetadataPreview from './GroupMetadataPreview'
import HighlightPreview from './HighlightPreview'
import LiveEventPreview from './LiveEventPreview'
@@ -105,5 +106,9 @@ export default function ContentPreview({
return
}
+ if (event.kind === ExtendedKind.FOLLOW_PACK) {
+ return
+ }
+
return [{t('Cannot handle event of kind k', { k: event.kind })}]
}
diff --git a/src/components/Note/FollowPack.tsx b/src/components/Note/FollowPack.tsx
new file mode 100644
index 00000000..a94af550
--- /dev/null
+++ b/src/components/Note/FollowPack.tsx
@@ -0,0 +1,55 @@
+import { Button } from '@/components/ui/button'
+import { getFollowPackInfoFromEvent } from '@/lib/event-metadata'
+import { toFollowPack } from '@/lib/link'
+import { useSecondaryPage } from '@/PageManager'
+import { Event } from 'nostr-tools'
+import { useMemo } from 'react'
+import { useTranslation } from 'react-i18next'
+import Image from '../Image'
+
+export default function FollowPack({ event, className }: { event: Event; className?: string }) {
+ const { t } = useTranslation()
+ const { push } = useSecondaryPage()
+ const { title, description, image, pubkeys } = useMemo(
+ () => getFollowPackInfoFromEvent(event),
+ [event]
+ )
+
+ const handleViewDetails = (e: React.MouseEvent) => {
+ e.stopPropagation()
+ push(toFollowPack(event))
+ }
+
+ return (
+
+
+ {image && (
+
+ )}
+
+
+
{title}
+
+ {t('n users', { count: pubkeys.length })}
+
+
+ {description && (
+
{description}
+ )}
+
+
+
+
+
+ )
+}
diff --git a/src/components/Note/index.tsx b/src/components/Note/index.tsx
index 1098bfa3..314b16c6 100644
--- a/src/components/Note/index.tsx
+++ b/src/components/Note/index.tsx
@@ -21,6 +21,7 @@ import UserAvatar from '../UserAvatar'
import Username from '../Username'
import CommunityDefinition from './CommunityDefinition'
import EmojiPack from './EmojiPack'
+import FollowPack from './FollowPack'
import GroupMetadata from './GroupMetadata'
import Highlight from './Highlight'
import LiveEvent from './LiveEvent'
@@ -109,6 +110,8 @@ export default function Note({
content =
} else if (event.kind === kinds.Emojisets) {
content =
+ } else if (event.kind === ExtendedKind.FOLLOW_PACK) {
+ content =
} else {
content =
}
diff --git a/src/constants.ts b/src/constants.ts
index e12111b7..089782d4 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -81,13 +81,14 @@ export const ExtendedKind = {
VOICE_COMMENT: 1244,
FAVORITE_RELAYS: 10012,
BLOSSOM_SERVER_LIST: 10063,
+ FOLLOW_PACK: 39089,
RELAY_REVIEW: 31987,
GROUP_METADATA: 39000,
ADDRESSABLE_NORMAL_VIDEO: 34235,
ADDRESSABLE_SHORT_VIDEO: 34236
}
-export const SUPPORTED_KINDS = [
+export const ALLOWED_FILTER_KINDS = [
kinds.ShortTextNote,
kinds.Repost,
kinds.GenericRepost,
@@ -100,12 +101,17 @@ export const SUPPORTED_KINDS = [
ExtendedKind.VOICE_COMMENT,
kinds.Highlights,
kinds.LongFormArticle,
- ExtendedKind.RELAY_REVIEW,
- kinds.Emojisets,
ExtendedKind.ADDRESSABLE_NORMAL_VIDEO,
ExtendedKind.ADDRESSABLE_SHORT_VIDEO
]
+export const SUPPORTED_KINDS = [
+ ...ALLOWED_FILTER_KINDS,
+ ExtendedKind.RELAY_REVIEW,
+ kinds.Emojisets,
+ ExtendedKind.FOLLOW_PACK
+]
+
export const URL_REGEX =
/https?:\/\/[\w\p{L}\p{N}\p{M}&.\-/?=#@%+_:!~*]+[^\s.,;:'")\]}!?,。;:"'!?】)]/giu
export const WS_URL_REGEX =
diff --git a/src/i18n/locales/ar.ts b/src/i18n/locales/ar.ts
index ff07d9aa..713345dc 100644
--- a/src/i18n/locales/ar.ts
+++ b/src/i18n/locales/ar.ts
@@ -552,6 +552,13 @@ export default {
Highlight: 'تسليط الضوء',
'Optimal relays and {{count}} other relays': 'المرحلات المثلى و {{count}} مرحلات أخرى',
'Likely spam account (Trust score: {{percentile}}%)': 'حساب مشبوه للغاية (درجة الثقة: {{percentile}}%)',
- 'Suspicious account (Trust score: {{percentile}}%)': 'حساب مشبوه (درجة الثقة: {{percentile}}%)'
+ 'Suspicious account (Trust score: {{percentile}}%)': 'حساب مشبوه (درجة الثقة: {{percentile}}%)',
+ 'n users': '{{count}} مستخدمين',
+ 'View Details': 'عرض التفاصيل',
+ 'Follow Pack Not Found': 'لم يتم العثور على حزمة المتابعة',
+ 'Follow pack not found': 'لم يتم العثور على حزمة المتابعة',
+ Users: 'المستخدمون',
+ Feed: 'التغذية',
+ 'Follow Pack': 'حزمة المتابعة'
}
}
diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts
index 3e6bd39c..88cdca64 100644
--- a/src/i18n/locales/de.ts
+++ b/src/i18n/locales/de.ts
@@ -568,6 +568,13 @@ export default {
Highlight: 'Hervorheben',
'Optimal relays and {{count}} other relays': 'Optimale Relays und {{count}} andere Relays',
'Likely spam account (Trust score: {{percentile}}%)': 'Wahrscheinlich Spam-Konto (Vertrauenswert: {{percentile}}%)',
- 'Suspicious account (Trust score: {{percentile}}%)': 'Verdächtiges Konto (Vertrauenswert: {{percentile}}%)'
+ 'Suspicious account (Trust score: {{percentile}}%)': 'Verdächtiges Konto (Vertrauenswert: {{percentile}}%)',
+ 'n users': '{{count}} Benutzer',
+ 'View Details': 'Details anzeigen',
+ 'Follow Pack Not Found': 'Follow-Pack nicht gefunden',
+ 'Follow pack not found': 'Follow-Pack nicht gefunden',
+ Users: 'Benutzer',
+ Feed: 'Feed',
+ 'Follow Pack': 'Follow-Pack'
}
}
diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts
index d1f9688c..54851e2d 100644
--- a/src/i18n/locales/en.ts
+++ b/src/i18n/locales/en.ts
@@ -555,6 +555,15 @@ export default {
'Likely spam account (Trust score: {{percentile}}%)':
'Likely spam account (Trust score: {{percentile}}%)',
'Suspicious account (Trust score: {{percentile}}%)':
- 'Suspicious account (Trust score: {{percentile}}%)'
+ 'Suspicious account (Trust score: {{percentile}}%)',
+ 'n users': '{{count}} users',
+ 'n users_one': '{{count}} user',
+ 'n users_other': '{{count}} users',
+ 'View Details': 'View Details',
+ 'Follow Pack Not Found': 'Follow Pack Not Found',
+ 'Follow pack not found': 'Follow pack not found',
+ Users: 'Users',
+ Feed: 'Feed',
+ 'Follow Pack': 'Follow Pack'
}
}
diff --git a/src/i18n/locales/es.ts b/src/i18n/locales/es.ts
index 47644804..8358dab0 100644
--- a/src/i18n/locales/es.ts
+++ b/src/i18n/locales/es.ts
@@ -563,6 +563,13 @@ export default {
Highlight: 'Destacado',
'Optimal relays and {{count}} other relays': 'Relays óptimos y {{count}} otros relays',
'Likely spam account (Trust score: {{percentile}}%)': 'Probablemente cuenta spam (Puntuación de confianza: {{percentile}}%)',
- 'Suspicious account (Trust score: {{percentile}}%)': 'Cuenta sospechosa (Puntuación de confianza: {{percentile}}%)'
+ 'Suspicious account (Trust score: {{percentile}}%)': 'Cuenta sospechosa (Puntuación de confianza: {{percentile}}%)',
+ 'n users': '{{count}} usuarios',
+ 'View Details': 'Ver detalles',
+ 'Follow Pack Not Found': 'Paquete de seguimiento no encontrado',
+ 'Follow pack not found': 'Paquete de seguimiento no encontrado',
+ Users: 'Usuarios',
+ Feed: 'Feed',
+ 'Follow Pack': 'Paquete de Seguimiento'
}
}
diff --git a/src/i18n/locales/fa.ts b/src/i18n/locales/fa.ts
index ae0bf45d..8e77229f 100644
--- a/src/i18n/locales/fa.ts
+++ b/src/i18n/locales/fa.ts
@@ -557,6 +557,13 @@ export default {
Highlight: 'برجستهسازی',
'Optimal relays and {{count}} other relays': 'رلههای بهینه و {{count}} رله دیگر',
'Likely spam account (Trust score: {{percentile}}%)': 'احتمالاً حساب هرزنامه (امتیاز اعتماد: {{percentile}}%)',
- 'Suspicious account (Trust score: {{percentile}}%)': 'حساب مشکوک (امتیاز اعتماد: {{percentile}}%)'
+ 'Suspicious account (Trust score: {{percentile}}%)': 'حساب مشکوک (امتیاز اعتماد: {{percentile}}%)',
+ 'n users': '{{count}} کاربر',
+ 'View Details': 'مشاهده جزئیات',
+ 'Follow Pack Not Found': 'بسته دنبالکننده یافت نشد',
+ 'Follow pack not found': 'بسته دنبالکننده یافت نشد',
+ Users: 'کاربران',
+ Feed: 'فید',
+ 'Follow Pack': 'بسته دنبالکننده'
}
}
diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts
index 65a273c8..6fc27f6f 100644
--- a/src/i18n/locales/fr.ts
+++ b/src/i18n/locales/fr.ts
@@ -566,6 +566,13 @@ export default {
Highlight: 'Surligner',
'Optimal relays and {{count}} other relays': 'Relais optimaux et {{count}} autres relais',
'Likely spam account (Trust score: {{percentile}}%)': 'Compte probablement spam (Score de confiance: {{percentile}}%)',
- 'Suspicious account (Trust score: {{percentile}}%)': 'Compte suspect (Score de confiance: {{percentile}}%)'
+ 'Suspicious account (Trust score: {{percentile}}%)': 'Compte suspect (Score de confiance: {{percentile}}%)',
+ 'n users': '{{count}} utilisateurs',
+ 'View Details': 'Voir les détails',
+ 'Follow Pack Not Found': 'Pack de suivi introuvable',
+ 'Follow pack not found': 'Pack de suivi introuvable',
+ Users: 'Utilisateurs',
+ Feed: 'Flux',
+ 'Follow Pack': 'Pack de Suivi'
}
}
diff --git a/src/i18n/locales/hi.ts b/src/i18n/locales/hi.ts
index 24199841..c70b4a30 100644
--- a/src/i18n/locales/hi.ts
+++ b/src/i18n/locales/hi.ts
@@ -558,6 +558,13 @@ export default {
Highlight: 'हाइलाइट',
'Optimal relays and {{count}} other relays': 'इष्टतम रिले और {{count}} अन्य रिले',
'Likely spam account (Trust score: {{percentile}}%)': 'संभावित स्पैम खाता (विश्वास स्कोर: {{percentile}}%)',
- 'Suspicious account (Trust score: {{percentile}}%)': 'संदिग्ध खाता (विश्वास स्कोर: {{percentile}}%)'
+ 'Suspicious account (Trust score: {{percentile}}%)': 'संदिग्ध खाता (विश्वास स्कोर: {{percentile}}%)',
+ 'n users': '{{count}} उपयोगकर्ता',
+ 'View Details': 'विवरण देखें',
+ 'Follow Pack Not Found': 'फॉलो पैक नहीं मिला',
+ 'Follow pack not found': 'फॉलो पैक नहीं मिला',
+ Users: 'उपयोगकर्ता',
+ Feed: 'फ़ीड',
+ 'Follow Pack': 'फॉलो पैक'
}
}
diff --git a/src/i18n/locales/hu.ts b/src/i18n/locales/hu.ts
index d587da9b..6a37db40 100644
--- a/src/i18n/locales/hu.ts
+++ b/src/i18n/locales/hu.ts
@@ -553,6 +553,13 @@ export default {
Highlight: 'Kiemelés',
'Optimal relays and {{count}} other relays': 'Optimális relay-ek és {{count}} másik relay',
'Likely spam account (Trust score: {{percentile}}%)': 'Valószínűleg spam fiók (Megbízhatósági pontszám: {{percentile}}%)',
- 'Suspicious account (Trust score: {{percentile}}%)': 'Gyanús fiók (Megbízhatósági pontszám: {{percentile}}%)'
+ 'Suspicious account (Trust score: {{percentile}}%)': 'Gyanús fiók (Megbízhatósági pontszám: {{percentile}}%)',
+ 'n users': '{{count}} felhasználó',
+ 'View Details': 'Részletek megtekintése',
+ 'Follow Pack Not Found': 'Követési csomag nem található',
+ 'Follow pack not found': 'Követési csomag nem található',
+ Users: 'Felhasználók',
+ Feed: 'Hírfolyam',
+ 'Follow Pack': 'Követési Csomag'
}
}
diff --git a/src/i18n/locales/it.ts b/src/i18n/locales/it.ts
index 4a0d5713..7eeb32c2 100644
--- a/src/i18n/locales/it.ts
+++ b/src/i18n/locales/it.ts
@@ -562,6 +562,13 @@ export default {
Highlight: 'Evidenzia',
'Optimal relays and {{count}} other relays': 'Relay ottimali e {{count}} altri relay',
'Likely spam account (Trust score: {{percentile}}%)': 'Probabile account spam (Punteggio di fiducia: {{percentile}}%)',
- 'Suspicious account (Trust score: {{percentile}}%)': 'Account sospetto (Punteggio di fiducia: {{percentile}}%)'
+ 'Suspicious account (Trust score: {{percentile}}%)': 'Account sospetto (Punteggio di fiducia: {{percentile}}%)',
+ 'n users': '{{count}} utenti',
+ 'View Details': 'Visualizza dettagli',
+ 'Follow Pack Not Found': 'Pacchetto di follow non trovato',
+ 'Follow pack not found': 'Pacchetto di follow non trovato',
+ Users: 'Utenti',
+ Feed: 'Feed',
+ 'Follow Pack': 'Pacchetto di Follow'
}
}
diff --git a/src/i18n/locales/ja.ts b/src/i18n/locales/ja.ts
index 9abff234..0d20d9bf 100644
--- a/src/i18n/locales/ja.ts
+++ b/src/i18n/locales/ja.ts
@@ -557,6 +557,13 @@ export default {
Highlight: 'ハイライト',
'Optimal relays and {{count}} other relays': '最適なリレーと他の{{count}}個のリレー',
'Likely spam account (Trust score: {{percentile}}%)': 'スパムの可能性が高いアカウント(信頼スコア:{{percentile}}%)',
- 'Suspicious account (Trust score: {{percentile}}%)': '疑わしいアカウント(信頼スコア:{{percentile}}%)'
+ 'Suspicious account (Trust score: {{percentile}}%)': '疑わしいアカウント(信頼スコア:{{percentile}}%)',
+ 'n users': '{{count}}人のユーザー',
+ 'View Details': '詳細を表示',
+ 'Follow Pack Not Found': 'フォローパックが見つかりません',
+ 'Follow pack not found': 'フォローパックが見つかりません',
+ Users: 'ユーザー',
+ Feed: 'フィード',
+ 'Follow Pack': 'フォローパック'
}
}
diff --git a/src/i18n/locales/ko.ts b/src/i18n/locales/ko.ts
index 721054f8..3258d424 100644
--- a/src/i18n/locales/ko.ts
+++ b/src/i18n/locales/ko.ts
@@ -557,6 +557,13 @@ export default {
Highlight: '하이라이트',
'Optimal relays and {{count}} other relays': '최적 릴레이 및 기타 {{count}}개 릴레이',
'Likely spam account (Trust score: {{percentile}}%)': '스팸 계정 가능성 높음 (신뢰 점수: {{percentile}}%)',
- 'Suspicious account (Trust score: {{percentile}}%)': '의심스러운 계정 (신뢰 점수: {{percentile}}%)'
+ 'Suspicious account (Trust score: {{percentile}}%)': '의심스러운 계정 (신뢰 점수: {{percentile}}%)',
+ 'n users': '{{count}}명의 사용자',
+ 'View Details': '세부 정보 보기',
+ 'Follow Pack Not Found': '팔로우 팩을 찾을 수 없음',
+ 'Follow pack not found': '팔로우 팩을 찾을 수 없습니다',
+ Users: '사용자',
+ Feed: '피드',
+ 'Follow Pack': '팔로우 팩'
}
}
diff --git a/src/i18n/locales/pl.ts b/src/i18n/locales/pl.ts
index a55cfb70..2c329d1b 100644
--- a/src/i18n/locales/pl.ts
+++ b/src/i18n/locales/pl.ts
@@ -563,6 +563,13 @@ export default {
Highlight: 'Podświetl',
'Optimal relays and {{count}} other relays': 'Optymalne przekaźniki i {{count}} innych przekaźników',
'Likely spam account (Trust score: {{percentile}}%)': 'Prawdopodobnie konto spamowe (Wynik zaufania: {{percentile}}%)',
- 'Suspicious account (Trust score: {{percentile}}%)': 'Podejrzane konto (Wynik zaufania: {{percentile}}%)'
+ 'Suspicious account (Trust score: {{percentile}}%)': 'Podejrzane konto (Wynik zaufania: {{percentile}}%)',
+ 'n users': '{{count}} użytkowników',
+ 'View Details': 'Zobacz szczegóły',
+ 'Follow Pack Not Found': 'Nie znaleziono pakietu obserwowanych',
+ 'Follow pack not found': 'Nie znaleziono pakietu obserwowanych',
+ Users: 'Użytkownicy',
+ Feed: 'Kanał',
+ 'Follow Pack': 'Pakiet Obserwowanych'
}
}
diff --git a/src/i18n/locales/pt-BR.ts b/src/i18n/locales/pt-BR.ts
index 57a09126..615bad3a 100644
--- a/src/i18n/locales/pt-BR.ts
+++ b/src/i18n/locales/pt-BR.ts
@@ -558,6 +558,13 @@ export default {
Highlight: 'Marcação',
'Optimal relays and {{count}} other relays': 'Relays ideais e {{count}} outros relays',
'Likely spam account (Trust score: {{percentile}}%)': 'Provável conta de spam (Pontuação de confiança: {{percentile}}%)',
- 'Suspicious account (Trust score: {{percentile}}%)': 'Conta suspeita (Pontuação de confiança: {{percentile}}%)'
+ 'Suspicious account (Trust score: {{percentile}}%)': 'Conta suspeita (Pontuação de confiança: {{percentile}}%)',
+ 'n users': '{{count}} usuários',
+ 'View Details': 'Ver detalhes',
+ 'Follow Pack Not Found': 'Pacote de seguir não encontrado',
+ 'Follow pack not found': 'Pacote de seguir não encontrado',
+ Users: 'Usuários',
+ Feed: 'Feed',
+ 'Follow Pack': 'Pacote de Seguir'
}
}
diff --git a/src/i18n/locales/pt-PT.ts b/src/i18n/locales/pt-PT.ts
index b9b4b6a3..96b3ee9d 100644
--- a/src/i18n/locales/pt-PT.ts
+++ b/src/i18n/locales/pt-PT.ts
@@ -561,6 +561,13 @@ export default {
Highlight: 'Destacar',
'Optimal relays and {{count}} other relays': 'Relays ideais e {{count}} outros relays',
'Likely spam account (Trust score: {{percentile}}%)': 'Provável conta de spam (Pontuação de confiança: {{percentile}}%)',
- 'Suspicious account (Trust score: {{percentile}}%)': 'Conta suspeita (Pontuação de confiança: {{percentile}}%)'
+ 'Suspicious account (Trust score: {{percentile}}%)': 'Conta suspeita (Pontuação de confiança: {{percentile}}%)',
+ 'n users': '{{count}} utilizadores',
+ 'View Details': 'Ver detalhes',
+ 'Follow Pack Not Found': 'Pacote de seguir não encontrado',
+ 'Follow pack not found': 'Pacote de seguir não encontrado',
+ Users: 'Utilizadores',
+ Feed: 'Feed',
+ 'Follow Pack': 'Pacote de Seguir'
}
}
diff --git a/src/i18n/locales/ru.ts b/src/i18n/locales/ru.ts
index c1a3a5b6..2e446874 100644
--- a/src/i18n/locales/ru.ts
+++ b/src/i18n/locales/ru.ts
@@ -563,6 +563,13 @@ export default {
Highlight: 'Выделить',
'Optimal relays and {{count}} other relays': 'Оптимальные релеи и {{count}} других релеев',
'Likely spam account (Trust score: {{percentile}}%)': 'Вероятно спам-аккаунт (Оценка доверия: {{percentile}}%)',
- 'Suspicious account (Trust score: {{percentile}}%)': 'Подозрительный аккаунт (Оценка доверия: {{percentile}}%)'
+ 'Suspicious account (Trust score: {{percentile}}%)': 'Подозрительный аккаунт (Оценка доверия: {{percentile}}%)',
+ 'n users': '{{count}} пользователей',
+ 'View Details': 'Посмотреть детали',
+ 'Follow Pack Not Found': 'Пакет подписок не найден',
+ 'Follow pack not found': 'Пакет подписок не найден',
+ Users: 'Пользователи',
+ Feed: 'Лента',
+ 'Follow Pack': 'Пакет Подписок'
}
}
diff --git a/src/i18n/locales/th.ts b/src/i18n/locales/th.ts
index 8f5ffab9..b0e198c1 100644
--- a/src/i18n/locales/th.ts
+++ b/src/i18n/locales/th.ts
@@ -550,6 +550,13 @@ export default {
Highlight: 'ไฮไลต์',
'Optimal relays and {{count}} other relays': 'รีเลย์ที่เหมาะสมและรีเลย์อื่น {{count}} รายการ',
'Likely spam account (Trust score: {{percentile}}%)': 'น่าจะเป็นบัญชีสแปม (คะแนนความน่าเชื่อถือ: {{percentile}}%)',
- 'Suspicious account (Trust score: {{percentile}}%)': 'บัญชีที่น่าสงสัย (คะแนนความน่าเชื่อถือ: {{percentile}}%)'
+ 'Suspicious account (Trust score: {{percentile}}%)': 'บัญชีที่น่าสงสัย (คะแนนความน่าเชื่อถือ: {{percentile}}%)',
+ 'n users': '{{count}} ผู้ใช้',
+ 'View Details': 'ดูรายละเอียด',
+ 'Follow Pack Not Found': 'ไม่พบแพ็คการติดตาม',
+ 'Follow pack not found': 'ไม่พบแพ็คการติดตาม',
+ Users: 'ผู้ใช้',
+ Feed: 'ฟีด',
+ 'Follow Pack': 'แพ็คการติดตาม'
}
}
diff --git a/src/i18n/locales/zh.ts b/src/i18n/locales/zh.ts
index 83decff8..4c3b844b 100644
--- a/src/i18n/locales/zh.ts
+++ b/src/i18n/locales/zh.ts
@@ -545,6 +545,13 @@ export default {
Highlight: '高亮',
'Optimal relays and {{count}} other relays': '最佳中继器和其他 {{count}} 个中继器',
'Likely spam account (Trust score: {{percentile}}%)': '疑似垃圾账号(信任分数:{{percentile}}%)',
- 'Suspicious account (Trust score: {{percentile}}%)': '可疑账号(信任分数:{{percentile}}%)'
+ 'Suspicious account (Trust score: {{percentile}}%)': '可疑账号(信任分数:{{percentile}}%)',
+ 'n users': '{{count}} 位用户',
+ 'View Details': '查看详情',
+ 'Follow Pack Not Found': '未找到关注包',
+ 'Follow pack not found': '未找到关注包',
+ Users: '用户',
+ Feed: '动态',
+ 'Follow Pack': '关注包'
}
}
diff --git a/src/lib/event-metadata.ts b/src/lib/event-metadata.ts
index e02c8e50..9312f981 100644
--- a/src/lib/event-metadata.ts
+++ b/src/lib/event-metadata.ts
@@ -4,7 +4,7 @@ import { Event, kinds } from 'nostr-tools'
import { buildATag } from './draft-event'
import { getReplaceableEventIdentifier } from './event'
import { getAmountFromInvoice, getLightningAddressFromProfile } from './lightning'
-import { formatPubkey, pubkeyToNpub } from './pubkey'
+import { formatPubkey, isValidPubkey, pubkeyToNpub } from './pubkey'
import { generateBech32IdFromETag, getEmojiInfosFromEmojiTags, tagNameEquals } from './tag'
import { isOnionUrl, isWebsocketUrl, normalizeHttpUrl, normalizeUrl } from './url'
@@ -403,3 +403,28 @@ export function getPinnedEventHexIdSetFromPinListEvent(event?: Event | null): Se
.slice(0, MAX_PINNED_NOTES) ?? []
)
}
+
+export function getFollowPackInfoFromEvent(event: Event) {
+ let title: string | undefined
+ let description: string | undefined
+ let image: string | undefined
+ const pubkeys: string[] = []
+
+ event.tags.forEach(([tagName, tagValue]) => {
+ if (tagName === 'title') {
+ title = tagValue
+ } else if (tagName === 'description') {
+ description = tagValue
+ } else if (tagName === 'image') {
+ image = tagValue
+ } else if (tagName === 'p' && isValidPubkey(tagValue)) {
+ pubkeys.push(tagValue)
+ }
+ })
+
+ if (!title) {
+ title = event.tags.find(tagNameEquals('d'))?.[1] ?? 'Untitled Follow Pack'
+ }
+
+ return { title, description, image, pubkeys }
+}
diff --git a/src/lib/link.ts b/src/lib/link.ts
index b6d2e853..8a08d7e0 100644
--- a/src/lib/link.ts
+++ b/src/lib/link.ts
@@ -77,6 +77,11 @@ export const toRelayReviews = (url: string) => `/relays/${encodeURIComponent(url
export const toMuteList = () => '/mutes'
export const toRizful = () => '/rizful'
export const toBookmarks = () => '/bookmarks'
+export const toFollowPack = (eventOrId: Event | string) => {
+ if (typeof eventOrId === 'string') return `/follow-packs/${eventOrId}`
+ const naddr = getNoteBech32Id(eventOrId)
+ return `/follow-packs/${naddr}`
+}
export const toChachiChat = (relay: string, d: string) => {
return `https://chachi.chat/${relay.replace(/^wss?:\/\//, '').replace(/\/$/, '')}/${d}`
diff --git a/src/pages/secondary/FollowPackPage/index.tsx b/src/pages/secondary/FollowPackPage/index.tsx
new file mode 100644
index 00000000..ba07c3a0
--- /dev/null
+++ b/src/pages/secondary/FollowPackPage/index.tsx
@@ -0,0 +1,116 @@
+import ImageWithLightbox from '@/components/ImageWithLightbox'
+import NormalFeed from '@/components/NormalFeed'
+import ProfileList from '@/components/ProfileList'
+import { Skeleton } from '@/components/ui/skeleton'
+import { useFetchEvent } from '@/hooks/useFetchEvent'
+import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
+import { getFollowPackInfoFromEvent } from '@/lib/event-metadata'
+import { cn } from '@/lib/utils'
+import { useNostr } from '@/providers/NostrProvider'
+import client from '@/services/client.service'
+import { TFeedSubRequest } from '@/types'
+import { forwardRef, useEffect, useMemo, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+
+const FollowPackPage = forwardRef(({ id, index }: { id?: string; index?: number }, ref) => {
+ const { t } = useTranslation()
+ const [tab, setTab] = useState<'users' | 'feed'>('users')
+
+ const { event, isFetching } = useFetchEvent(id)
+
+ const { title, description, image, pubkeys } = useMemo(() => {
+ if (!event) return { title: '', description: '', image: '', pubkeys: [] }
+ return getFollowPackInfoFromEvent(event)
+ }, [event])
+
+ if (isFetching) {
+ return (
+
+
+
+
+
+
+ )
+ }
+
+ if (!event) {
+ return (
+
+ {t('Follow pack not found')}
+
+ )
+ }
+
+ return (
+
+
+ {/* Header */}
+
+ {image && (
+
+ )}
+
+
+
{title}
+
+ {t('n users', { count: pubkeys.length })}
+
+
+
+ {description && (
+
{description}
+ )}
+
+
+
+
+
+
+
+ {/* Content */}
+ {tab === 'users' &&
}
+ {tab === 'feed' && pubkeys.length > 0 &&
}
+
+
+ )
+})
+FollowPackPage.displayName = 'FollowPackPage'
+export default FollowPackPage
+
+function Feed({ pubkeys }: { pubkeys: string[] }) {
+ const { pubkey: myPubkey } = useNostr()
+ const [subRequests, setSubRequests] = useState([])
+
+ useEffect(() => {
+ client.generateSubRequestsForPubkeys(pubkeys, myPubkey).then(setSubRequests)
+ }, [pubkeys, myPubkey])
+
+ return
+}
diff --git a/src/routes/secondary.tsx b/src/routes/secondary.tsx
index d5313d18..fc90bea8 100644
--- a/src/routes/secondary.tsx
+++ b/src/routes/secondary.tsx
@@ -3,6 +3,7 @@ import BookmarkPage from '@/pages/secondary/BookmarkPage'
import EmojiPackSettingsPage from '@/pages/secondary/EmojiPackSettingsPage'
import ExternalContentPage from '@/pages/secondary/ExternalContentPage'
import FollowingListPage from '@/pages/secondary/FollowingListPage'
+import FollowPackPage from '@/pages/secondary/FollowPackPage'
import GeneralSettingsPage from '@/pages/secondary/GeneralSettingsPage'
import MuteListPage from '@/pages/secondary/MuteListPage'
import NoteListPage from '@/pages/secondary/NoteListPage'
@@ -48,7 +49,8 @@ const SECONDARY_ROUTE_CONFIGS = [
{ path: '/profile-editor', element: },
{ path: '/mutes', element: },
{ path: '/rizful', element: },
- { path: '/bookmarks', element: }
+ { path: '/bookmarks', element: },
+ { path: '/follow-packs/:id', element: }
]
export const SECONDARY_ROUTES = SECONDARY_ROUTE_CONFIGS.map(({ path, element }) => ({
diff --git a/src/services/local-storage.service.ts b/src/services/local-storage.service.ts
index 0ba65151..57a1b4a7 100644
--- a/src/services/local-storage.service.ts
+++ b/src/services/local-storage.service.ts
@@ -1,10 +1,10 @@
import {
+ ALLOWED_FILTER_KINDS,
DEFAULT_FAVICON_URL_TEMPLATE,
DEFAULT_NIP_96_SERVICE,
ExtendedKind,
MEDIA_AUTO_LOAD_POLICY,
NOTIFICATION_LIST_STYLE,
- SUPPORTED_KINDS,
StorageKey,
TPrimaryColor
} from '@/constants'
@@ -165,7 +165,7 @@ class LocalStorageService {
const showKindsStr = window.localStorage.getItem(StorageKey.SHOW_KINDS)
if (!showKindsStr) {
- this.showKinds = SUPPORTED_KINDS
+ this.showKinds = ALLOWED_FILTER_KINDS
} else {
const showKindsVersionStr = window.localStorage.getItem(StorageKey.SHOW_KINDS_VERSION)
const showKindsVersion = showKindsVersionStr ? parseInt(showKindsVersionStr) : 0