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