diff --git a/craco.config.js b/craco.config.js index 028e47d1..cd4526b7 100644 --- a/craco.config.js +++ b/craco.config.js @@ -11,6 +11,11 @@ module.exports = { webpackConfig.plugins = webpackConfig.plugins.filter(plugin => !(plugin.constructor && plugin.constructor.name === 'PrettierPlugin') ); + webpackConfig.module.rules.push({ + test: /\.mjs$/, + include: /node_modules/, + type: 'javascript/auto', + }); return webpackConfig; }, plugins: [ diff --git a/package.json b/package.json index 06905474..a887e5d5 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,11 @@ "dependencies": { "@ant-design/icons": "^4.6.2", "@babel/core": "^7.24.7", + "@cashu/cashu-ts": "^2.5.2", "@craco/craco": "^6.1.2", "@lit-labs/react": "^1.0.2", + "@nostr-dev-kit/ndk": "^2.14.32", + "@nostr-dev-kit/ndk-hooks": "^1.2.3", "@react-google-maps/api": "^2.18.1", "@reduxjs/toolkit": "^1.7.1", "@splidejs/react-splide": "^0.7.12", diff --git a/src/App.tsx b/src/App.tsx index c875c495..950202fa 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { ConfigProvider } from 'antd'; import { HelmetProvider } from 'react-helmet-async'; import deDe from 'antd/lib/locale/de_DE'; @@ -13,10 +13,25 @@ import { usePWA } from './hooks/usePWA'; import { useThemeWatcher } from './hooks/useThemeWatcher'; import { useAppSelector } from './hooks/reduxHooks'; import { themeObject } from './styles/themes/themeVariables'; +import NDK from '@nostr-dev-kit/ndk'; +import { useNDKInit } from '@nostr-dev-kit/ndk-hooks'; + +const ndk = new NDK({ + explicitRelayUrls: ['wss://relay.damus.io', 'wss://relay.nostr.band', 'wss://relay.snort.social', 'vault.iris.to'], +}); +ndk + .connect() + .then(() => console.log('NDK connected')) + .catch((error) => console.error('NDK connection error:', error)); const App: React.FC = () => { const { language } = useLanguage(); const theme = useAppSelector((state) => state.theme.theme); + const initializeNDK = useNDKInit(); + + useEffect(() => { + initializeNDK(ndk); + }, [initializeNDK]); usePWA(); diff --git a/src/components/header/Header.styles.ts b/src/components/header/Header.styles.ts index 35bb4d39..12fd4e57 100644 --- a/src/components/header/Header.styles.ts +++ b/src/components/header/Header.styles.ts @@ -7,6 +7,7 @@ import { BaseCol } from '../common/BaseCol/BaseCol'; export const HeaderActionWrapper = styled.div` cursor: pointer; + position: relative; & > .ant-btn > span[role='img'], .ant-badge { @@ -22,6 +23,18 @@ export const HeaderActionWrapper = styled.div` } `; +export const InvalidPubkey = styled.div` + position: absolute; + top: 3rem; + left: 2rem; + width: 100%; + color: var(--error-color); + height: 100%; + display: flex; + align-items: center; + font-weight: bold; +`; + export const DropdownCollapse = styled(BaseCollapse)` & > .ant-collapse-item > .ant-collapse-header { font-weight: 600; diff --git a/src/components/header/components/HeaderSearch/HeaderSearch.tsx b/src/components/header/components/HeaderSearch/HeaderSearch.tsx index bb415642..5541b4be 100644 --- a/src/components/header/components/HeaderSearch/HeaderSearch.tsx +++ b/src/components/header/components/HeaderSearch/HeaderSearch.tsx @@ -37,7 +37,7 @@ export const HeaderSearch: React.FC = () => { useEffect(() => { setModalOpen(false); - setOverlayOpen(false); + //setOverlayOpen(false); }, [pathname]); return ( @@ -59,7 +59,7 @@ export const HeaderSearch: React.FC = () => { query={query} setQuery={setQuery} data={sortedResults} - isOverlayOpen={isOverlayOpen} + isOverlayOpen={false} setOverlayOpen={setOverlayOpen} /> @@ -71,7 +71,7 @@ export const HeaderSearch: React.FC = () => { query={query} setQuery={setQuery} data={sortedResults} - isOverlayOpen={isOverlayOpen} + isOverlayOpen={false} setOverlayOpen={setOverlayOpen} /> )} diff --git a/src/components/header/components/searchDropdown/SearchDropdown.tsx b/src/components/header/components/searchDropdown/SearchDropdown.tsx index 646c08fa..5061ea28 100644 --- a/src/components/header/components/searchDropdown/SearchDropdown.tsx +++ b/src/components/header/components/searchDropdown/SearchDropdown.tsx @@ -1,12 +1,17 @@ import React, { useEffect, useRef, useState } from 'react'; -import { FilterIcon } from 'components/common/icons/FilterIcon'; import { SearchOverlay } from './searchOverlay/SearchOverlay/SearchOverlay'; import { HeaderActionWrapper } from '@app/components/header/Header.styles'; import { CategoryComponents } from '@app/components/header/components/HeaderSearch/HeaderSearch'; import { Btn, InputSearch } from '../HeaderSearch/HeaderSearch.styles'; import { useTranslation } from 'react-i18next'; import { BasePopover } from '@app/components/common/BasePopover/BasePopover'; +import { NDKUserProfile, useNDK } from '@nostr-dev-kit/ndk-hooks'; +import usePaidSubscribers from '@app/hooks/usePaidSubscribers'; +import { convertNDKUserProfileToSubscriberProfile } from '@app/utils/utils'; +import { InvalidPubkey } from '../../Header.styles'; +import { SubscriberProfile } from '@app/hooks/usePaidSubscribers'; +import { SubscriberDetailModal } from '@app/components/relay-dashboard/paid-subscribers/SubscriberDetailModal'; interface SearchOverlayProps { query: string; setQuery: (query: string) => void; @@ -23,7 +28,13 @@ export const SearchDropdown: React.FC = ({ setOverlayOpen, }) => { const [isFilterOpen, setFilterOpen] = useState(false); - + const [isSubscriberDetailModalOpen, setSubscriberDetailModalOpen] = useState(false); + const [fetchingProfile, setFetchingProfile] = useState(false); + const [fetchingFailed, setFetchingFailed] = useState(false); + const [subscriberProfile, setSubscriberProfile] = useState(null); + const [invalidPubkey, setInvalidPubkey] = useState(false); + const { subscribers } = usePaidSubscribers(); + const ndkInstance = useNDK(); const { t } = useTranslation(); useEffect(() => { @@ -33,6 +44,74 @@ export const SearchDropdown: React.FC = ({ // eslint-disable-next-line @typescript-eslint/no-explicit-any const ref = useRef(null); + const fetchProfile = async (pubkey: string): Promise => { + if (!ndkInstance) return null; + + try { + setFetchingProfile(true); + const profile = await ndkInstance.ndk?.getUser({ pubkey: pubkey }).fetchProfile(); + + if (profile) { + setFetchingProfile(false); + setFetchingFailed(false); + return profile; + } else { + console.error('Profile not found for pubkey:', pubkey); + setFetchingProfile(false); + setFetchingFailed(true); + return null; + } + } catch (error) { + console.error('Error fetching profile:', error); + setFetchingProfile(false); + setFetchingFailed(true); + return null; + } + }; + + const handleSearchProfile = async () => { + if (!query) return; + //verify that it's a pubkey + if (/^[a-fA-F0-9]{64}$/.test(query)) { + setSubscriberDetailModalOpen(true); + + //See if the pubkey exists in the subscribers + const pubkey = query; + const subscriber = subscribers.find((sub) => sub.pubkey === query); + // If it exists, open the modal with the subscriber details. If name,picture, or about are not set, fetch profile. + //if It doesnt exist, fetch the profile from NDK + //once fetched, convert it to SubscriberProfile and open the modal + if (subscriber) { + if (!subscriber.name || !subscriber.picture || !subscriber.about) { + const profile = await fetchProfile(pubkey); + if (profile) { + const subscriberProfile: SubscriberProfile = convertNDKUserProfileToSubscriberProfile(pubkey, profile); + // Open the modal with the fetched subscriber profile + setSubscriberProfile(subscriberProfile); + } + } + } else { + const profile = await fetchProfile(pubkey); + if (profile) { + const subscriberProfile: SubscriberProfile = convertNDKUserProfileToSubscriberProfile(pubkey, profile); + // Open the modal with the fetched subscriber profile + setSubscriberProfile(subscriberProfile); + } + } + }else{ + setInvalidPubkey(true); + } + }; + const onCloseSubscriberDetailModal = () => { + setSubscriberDetailModalOpen(false); + setSubscriberProfile(null); + }; + + useEffect(() => { + if(query.length === 0) { + setInvalidPubkey(false); + } + }, [query]); return ( <> = ({ getPopupContainer={() => ref.current} > + {invalidPubkey && ( + + {"Invalid pubkey."} + + )} } onClick={() => setFilterOpen(!isFilterOpen)} /> } onChange={(event) => setQuery(event.target.value)} enterButton={null} addonAfter={null} + onKeyDown={(event) => { + if (event.key === 'Enter') { + handleSearchProfile(); + } + }} />
+ {isSubscriberDetailModalOpen && ( + + )} ); diff --git a/src/components/layouts/main/MainContent/MainContent.tsx b/src/components/layouts/main/MainContent/MainContent.tsx index a9e4426d..a9215627 100644 --- a/src/components/layouts/main/MainContent/MainContent.tsx +++ b/src/components/layouts/main/MainContent/MainContent.tsx @@ -17,7 +17,7 @@ export default styled(BaseLayout.Content)` ${(props) => props?.$isDesktop && css` - z-index: 105; + z-index: 0; `} @media only screen and ${media.md} { diff --git a/src/components/nft-dashboard/trending-creators/SubscriberDetailModal/SubscriberDetailModal.styles.ts b/src/components/nft-dashboard/trending-creators/SubscriberDetailModal/SubscriberDetailModal.styles.ts deleted file mode 100644 index c4508543..00000000 --- a/src/components/nft-dashboard/trending-creators/SubscriberDetailModal/SubscriberDetailModal.styles.ts +++ /dev/null @@ -1,193 +0,0 @@ -import styled from 'styled-components'; -import { Card, Typography, Button, Space } from 'antd'; -import { BORDER_RADIUS, FONT_SIZE, FONT_WEIGHT, FONT_FAMILY, LAYOUT } from '@app/styles/themes/constants'; - -const { Title, Text, Paragraph } = Typography; - -export const StyledModal = styled(Card)` - background: var(--background-color); - border-radius: ${BORDER_RADIUS}; - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); - width: 100%; - overflow: hidden; - max-width: 500px; - margin: 0 auto; - - .ant-modal-content { - background-color: var(--background-color); - border-radius: ${BORDER_RADIUS}; - } - - .ant-modal-header { - background-color: transparent; - border-bottom: none; - padding-bottom: 0; - } - - .ant-modal-body { - padding: 0; - } - - .ant-modal-footer { - border-top: none; - padding-top: 0; - } -`; - -export const HeaderSection = styled.div` - padding: 24px 24px 0; - display: flex; - align-items: center; - justify-content: space-between; -`; - -export const ModalTitle = styled(Title)` - margin: 0 !important; - color: var(--text-main-color); - font-weight: ${FONT_WEIGHT.semibold}; -`; - -export const CloseButton = styled(Button)` - color: var(--text-main-color); - border: none; - background: transparent; - box-shadow: none; - - &:hover { - color: var(--primary-color); - background: transparent; - } -`; - -export const AvatarSection = styled.div` - position: relative; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 2rem 1rem; - background: linear-gradient(180deg, rgba(24,44,89,0.3) 0%, rgba(33,64,125,0.1) 100%); -`; - -export const AvatarContainer = styled.div` - position: relative; - width: 140px; - height: 140px; - margin-bottom: 1.5rem; - border-radius: 50%; - overflow: hidden; - box-shadow: 0 4px 14px rgba(0, 0, 0, 0.2); - border: 4px solid var(--background-color); - - img { - width: 100%; - height: 100%; - object-fit: cover; - } -`; - -export const UserName = styled(Title)` - margin: 0.5rem 0 0 !important; - color: var(--text-main-color); - text-align: center; - font-weight: ${FONT_WEIGHT.bold}; -`; - -export const AboutText = styled(Paragraph)` - color: var(--text-main-color); - text-align: center; - font-size: ${FONT_SIZE.md}; - max-width: 80%; - margin: 0.5rem auto 1rem !important; -`; - -export const InfoSection = styled.div` - padding: 1.5rem; -`; - -export const InfoCard = styled(Card)` - margin-bottom: 1rem; - border-radius: ${BORDER_RADIUS}; - background-color: var(--secondary-background-color); - border: 1px solid var(--border-color); - - .ant-card-body { - padding: 1rem; - } - - &:last-child { - margin-bottom: 0; - } -`; - -export const InfoHeader = styled.div` - display: flex; - align-items: center; - margin-bottom: 0.75rem; - - & > *:first-child { - margin-right: 0.5rem; - color: var(--primary-color); - } -`; - -export const InfoTitle = styled(Text)` - color: var(--text-main-color); - font-weight: ${FONT_WEIGHT.semibold}; - font-size: ${FONT_SIZE.md}; -`; - -export const InfoContent = styled(Text)` - color: var(--text-main-color); - word-break: break-all; - font-family: ${FONT_FAMILY.secondary}; - font-size: ${FONT_SIZE.xs}; - margin: 0; - display: block; -`; - -export const CopyContainer = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - background-color: var(--background-color); - padding: 0.5rem 0.75rem; - border-radius: ${BORDER_RADIUS}; - border: 1px solid var(--border-color); - margin-top: 0.5rem; -`; - -export const CopyButton = styled(Button)` - color: var(--primary-color); - font-size: ${FONT_SIZE.xs}; - padding: 0 8px; - height: auto; - border: none; - background: transparent; - - &:hover { - color: var(--primary-light-color); - background: transparent; - } -`; - -export const StyledKeyText = styled(Text)` - font-family: ${FONT_FAMILY.secondary}; - font-size: ${FONT_SIZE.xs}; - margin: 0; - color: var(--text-light-color); - overflow: hidden; - text-overflow: ellipsis; - width: 85%; - white-space: nowrap; -`; - -export const IconWrapper = styled.div` - display: flex; - align-items: center; - justify-content: center; - width: 28px; - height: 28px; - border-radius: 50%; - background-color: rgba(var(--primary-rgb-color), 0.1); -`; diff --git a/src/components/nft-dashboard/trending-creators/SubscriberDetailModal/SubscriberDetailModal.tsx b/src/components/nft-dashboard/trending-creators/SubscriberDetailModal/SubscriberDetailModal.tsx deleted file mode 100644 index 289c0a6c..00000000 --- a/src/components/nft-dashboard/trending-creators/SubscriberDetailModal/SubscriberDetailModal.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import React, { useState } from 'react'; -import { Modal, message, Button } from 'antd'; -import { - UserOutlined, - KeyOutlined, - CalendarOutlined, - CrownOutlined, - CloseOutlined, - CopyOutlined, - CheckOutlined -} from '@ant-design/icons'; -import { SubscriberProfile } from '@app/hooks/usePaidSubscribers'; -import * as S from './SubscriberDetailModal.styles'; - -interface SubscriberDetailModalProps { - subscriber: SubscriberProfile | null; - isVisible: boolean; - onClose: () => void; -} - -export const SubscriberDetailModal: React.FC = ({ - subscriber, - isVisible, - onClose -}) => { - const [copied, setCopied] = useState(false); - - if (!subscriber) { - return null; - } - - // Function to copy public key - const copyPublicKey = () => { - navigator.clipboard.writeText(subscriber.pubkey) - .then(() => { - setCopied(true); - message.success('Public key copied to clipboard'); - - // Reset copied state after 3 seconds - setTimeout(() => { - setCopied(false); - }, 3000); - }) - .catch(() => { - message.error('Failed to copy public key'); - }); - }; - - // Format public key for display - const formatPublicKey = (key: string) => { - if (key.length <= 16) return key; - return `${key.substring(0, 8)}...${key.substring(key.length - 8)}`; - }; - - return ( - } - width={500} - title={null} - bodyStyle={{ padding: 0 }} - > - {/* Header with title */} - - Subscriber Profile - - - {/* Avatar section with profile picture and name */} - - - {subscriber.name - - - {subscriber.name || 'Anonymous Subscriber'} - - - {subscriber.about && ( - - {subscriber.about} - - )} - - - {/* Information section */} - - {/* Public Key Card */} - - - - - - Public Key - - - - {formatPublicKey(subscriber.pubkey)} - : } - > - {copied ? 'Copied' : 'Copy'} - - - - - {/* Subscription Tier Card (if available) */} - {subscriber.metadata?.subscriptionTier && ( - - - - - - Subscription Tier - - - {subscriber.metadata.subscriptionTier} - - - )} - - {/* Subscription Date Card (if available) */} - {subscriber.metadata?.subscribedSince && ( - - - - - - Subscribed Since - - - {subscriber.metadata.subscribedSince} - - - )} - - - ); -}; diff --git a/src/components/nft-dashboard/trending-creators/SubscriberDetailModal/index.ts b/src/components/nft-dashboard/trending-creators/SubscriberDetailModal/index.ts deleted file mode 100644 index c2b7aa23..00000000 --- a/src/components/nft-dashboard/trending-creators/SubscriberDetailModal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { SubscriberDetailModal } from './SubscriberDetailModal'; diff --git a/src/components/relay-dashboard/paid-subscribers/PaidSubscribers.styles.ts b/src/components/relay-dashboard/paid-subscribers/PaidSubscribers.styles.ts index 592637ea..d5c75b37 100644 --- a/src/components/relay-dashboard/paid-subscribers/PaidSubscribers.styles.ts +++ b/src/components/relay-dashboard/paid-subscribers/PaidSubscribers.styles.ts @@ -29,7 +29,7 @@ export const ArrowBtn = styled(BaseButton)` export const CardWrapper = styled.div` margin: 0 0.40625rem; - + width: min-content; @media only screen and ${media.xl} { margin: 0 0.625rem; } @@ -42,4 +42,17 @@ export const EmptyState = styled.div` padding: 2rem; color: var(--text-light-color); font-size: 1rem; +`; +export const FlexWrapper = styled.div` + display: flex; + flex-wrap: wrap; + width: 90%; + margin: 0 auto; + justify-content: space-between; + align-items: center; + gap: 0.5rem; + + @media only screen and ${media.xl} { + gap: 0.625rem; + } `; \ No newline at end of file diff --git a/src/components/relay-dashboard/paid-subscribers/PaidSubscribers.tsx b/src/components/relay-dashboard/paid-subscribers/PaidSubscribers.tsx index 7837a5d1..09fc7c60 100644 --- a/src/components/relay-dashboard/paid-subscribers/PaidSubscribers.tsx +++ b/src/components/relay-dashboard/paid-subscribers/PaidSubscribers.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState } from 'react'; +import React, { useRef, useState, useEffect, useMemo } from 'react'; import { Splide, SplideSlide, SplideTrack } from '@splidejs/react-splide'; import { LeftOutlined, RightOutlined } from '@ant-design/icons'; import { useTranslation } from 'react-i18next'; @@ -12,48 +12,77 @@ import { BaseCol } from '@app/components/common/BaseCol/BaseCol'; import { SplideCarousel } from '@app/components/common/SplideCarousel/SplideCarousel'; import { useResponsive } from '@app/hooks/useResponsive'; import usePaidSubscribers, { SubscriberProfile } from '@app/hooks/usePaidSubscribers'; -import { Row, Col, Modal, Spin, Typography } from 'antd'; +import { Row, Col, Modal, Typography } from 'antd'; import { nip19 } from 'nostr-tools'; - +import { useNDK } from '@nostr-dev-kit/ndk-hooks'; +import { convertNDKUserProfileToSubscriberProfile } from '@app/utils/utils'; +import { UserOutlined } from '@ant-design/icons'; +import { CreatorButton } from './avatar/SubscriberAvatar.styles'; const { Text } = Typography; export const PaidSubscribers: React.FC = () => { console.log('[PaidSubscribers] Component rendering...'); const hookResult = usePaidSubscribers(12); - const { subscribers, fetchMore, hasMore, loading } = hookResult; - + const { subscribers, fetchMore, hasMore, loading, useDummyData } = hookResult; + const ndkInstance = useNDK(); + // Modal state for subscriber details const [selectedSubscriber, setSelectedSubscriber] = useState(null); const [isModalVisible, setIsModalVisible] = useState(false); - + // Modal state for view all subscribers const [isViewAllModalVisible, setIsViewAllModalVisible] = useState(false); const [allSubscribers, setAllSubscribers] = useState([]); - + const [loadingProfiles, setLoadingProfiles] = useState(true); + + const [subscriberProfiles, setSubscriberProfiles] = useState>( + () => new Map(subscribers.map((s) => [s.pubkey, s])), + ); + const sortedProfiles = useMemo(() => { + return Array.from(subscriberProfiles.entries()).sort(([a], [b]) => a.localeCompare(b)); + }, [subscriberProfiles]); + useEffect(() => { + setSubscriberProfiles((prev) => { + const map = new Map(prev); + for (const s of subscribers) { + if (!map.has(s.pubkey)) { + map.set(s.pubkey, s); + } + } + return map; + }); + }, [subscribers]); + // Handle opening subscriber detail modal const handleOpenSubscriberDetails = (subscriber: SubscriberProfile) => { setSelectedSubscriber(subscriber); setIsModalVisible(true); }; - + // Handle closing subscriber detail modal const handleCloseModal = () => { setIsModalVisible(false); }; - + const updateSubscriberProfile = (pubkey: string, profile: SubscriberProfile) => { + setSubscriberProfiles((prev) => { + const newMap = new Map(prev); + newMap.set(pubkey, profile); + return newMap; + }); + }; // Handle opening view all modal const handleViewAll = async () => { setIsViewAllModalVisible(true); setAllSubscribers([...subscribers]); // Start with current subscribers - + // Fetch more subscribers if available const currentSubscribers = [...subscribers]; let canFetchMore = hasMore; - + while (canFetchMore) { try { await fetchMore(); - // Note: This is a simplified approach. In a real scenario, you'd want to + // Note: This is a simplified approach. In a real scenario, you'd want to // track the updated state properly or use a separate hook for fetching all canFetchMore = false; // For now, just fetch once more } catch (error) { @@ -62,15 +91,62 @@ export const PaidSubscribers: React.FC = () => { } } }; - + + useEffect(() => { + // Fetch profiles for test subscribers + if (useDummyData) { + console.warn('[PaidSubscribers] Using dummy data, skipping profile fetch'); + setLoadingProfiles(false); + return; + } + const fetchProfiles = async () => { + if (!ndkInstance || !ndkInstance.ndk) { + console.error('NDK instance is not initialized'); + return; + } + //1. map through subscribers and fetch profiles. skip profile if already on map + await Promise.all( + subscribers.map(async (subscriber) => { + if ( + subscriberProfiles.has(subscriber.pubkey) && + subscriberProfiles.get(subscriber.pubkey)?.picture && + subscriberProfiles.get(subscriber.pubkey)?.about + ) { + return subscriberProfiles.get(subscriber.pubkey); + } + try { + if (!ndkInstance.ndk) { + console.error('NDK instance is not available'); + return null; + } + const user = await ndkInstance.ndk?.getUser({ pubkey: subscriber.pubkey }).fetchProfile(); + if (user) { + // Convert NDKUserProfile to SubscriberProfile and add to map + const covertedNDKUserProfile = convertNDKUserProfileToSubscriberProfile(subscriber.pubkey, user); + updateSubscriberProfile(subscriber.pubkey, covertedNDKUserProfile); + + return user; + } + } catch (error) { + console.error(`Error fetching profile for ${subscriber.pubkey}:`, error); + } + return null; + }), + ); + setLoadingProfiles(false); + }; + fetchProfiles(); + }, [subscribers, ndkInstance]); + // Handle closing view all modal const handleCloseViewAllModal = () => { + setSelectedSubscriber(null); setIsViewAllModalVisible(false); }; - + console.log('[PaidSubscribers] Received subscribers:', subscribers); console.log('[PaidSubscribers] Complete hook result:', hookResult); - + const sliderRef = useRef(null); const { isTablet: isTabletOrHigher } = useResponsive(); const { t } = useTranslation(); @@ -89,7 +165,7 @@ export const PaidSubscribers: React.FC = () => { // Determine whether to use carousel with looping based on count const shouldUseLoop = subscribers.length >= 7; - + // Simple grid for few subscribers if (subscribers.length > 0 && subscribers.length < 7) { return ( @@ -101,27 +177,27 @@ export const PaidSubscribers: React.FC = () => { - - - {subscribers.map((subscriber: SubscriberProfile) => ( - - + + + {sortedProfiles.map(([pubkey, subscriber], index) => ( + + {subscriber.picture ? ( handleOpenSubscriberDetails(subscriber)} - img={subscriber.picture} + img={subscriber.picture || ''} viewed={false} + onStoryOpen={() => handleOpenSubscriberDetails(subscriber)} /> - - + ) : ( + + + + )} + ))} - - - - + + + + {/* View All Subscribers Modal */} { style={{ top: 20 }} > - {(allSubscribers.length > 0 ? allSubscribers : subscribers).map((subscriber: SubscriberProfile) => ( - -
( + +
{ transition: 'all 0.2s ease', backgroundColor: 'var(--background-color-secondary)', gap: '16px', - boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)' + boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)', }} onClick={() => { setSelectedSubscriber(subscriber); @@ -162,55 +239,62 @@ export const PaidSubscribers: React.FC = () => { e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.1)'; }} - > -
- {subscriber.name -
-
+
+ {subscriber.name +
+
- + - {subscriber.name || 'Anonymous User'} - - + {subscriber.name || 'Anonymous User'} + + - {(() => { - try { - return nip19.npubEncode(subscriber.pubkey); - } catch { - // Fallback to original hex format if encoding fails - return subscriber.pubkey; - } - })()} - -
+ lineHeight: '1.2', + }} + > + {(() => { + try { + return nip19.npubEncode(subscriber.pubkey); + } catch { + // Fallback to original hex format if encoding fails + return subscriber.pubkey; + } + })()} +
- - ))} +
+ + ))} @@ -222,7 +306,7 @@ export const PaidSubscribers: React.FC = () => { <> { - {subscribers.map((subscriber: SubscriberProfile) => ( - - - handleOpenSubscriberDetails(subscriber)} - img={subscriber.picture} - viewed={false} - /> - - - ))} + {!loadingProfiles && + sortedProfiles.map(([pubkey, subscriber], index) => ( + + + {subscriber.picture ? ( + handleOpenSubscriberDetails(subscriber)} + img={subscriber.picture || ''} + viewed={false} + /> + ) : ( + + + + )} + + + ))} - - - + + {isModalVisible && ( + + )} + {/* View All Subscribers Modal */} { style={{ top: 20 }} > - {(allSubscribers.length > 0 ? allSubscribers : subscribers).map((subscriber: SubscriberProfile) => ( - -
( + +
{ transition: 'all 0.2s ease', backgroundColor: 'var(--background-color-secondary)', gap: '16px', - boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)' + boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)', }} onClick={() => { setSelectedSubscriber(subscriber); @@ -330,55 +420,62 @@ export const PaidSubscribers: React.FC = () => { e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.1)'; }} - > -
- {subscriber.name -
-
+
+ {subscriber.name +
+
- + - {subscriber.name || 'Anonymous User'} - - + {subscriber.name || 'Anonymous User'} + + - {(() => { - try { - return nip19.npubEncode(subscriber.pubkey); - } catch { - // Fallback to original hex format if encoding fails - return subscriber.pubkey; - } - })()} - -
+ lineHeight: '1.2', + }} + > + {(() => { + try { + return nip19.npubEncode(subscriber.pubkey); + } catch { + // Fallback to original hex format if encoding fails + return subscriber.pubkey; + } + })()} +
- - ))} +
+ + ))} diff --git a/src/components/relay-dashboard/paid-subscribers/SubscriberDetailModal/SubscriberDetailModal.styles.ts b/src/components/relay-dashboard/paid-subscribers/SubscriberDetailModal/SubscriberDetailModal.styles.ts index c4508543..722a2b30 100644 --- a/src/components/relay-dashboard/paid-subscribers/SubscriberDetailModal/SubscriberDetailModal.styles.ts +++ b/src/components/relay-dashboard/paid-subscribers/SubscriberDetailModal/SubscriberDetailModal.styles.ts @@ -1,7 +1,6 @@ import styled from 'styled-components'; -import { Card, Typography, Button, Space } from 'antd'; -import { BORDER_RADIUS, FONT_SIZE, FONT_WEIGHT, FONT_FAMILY, LAYOUT } from '@app/styles/themes/constants'; - +import { Card, Typography, Button, Badge, Modal } from 'antd'; +import { BORDER_RADIUS, FONT_SIZE, FONT_WEIGHT, FONT_FAMILY } from '@app/styles/themes/constants'; const { Title, Text, Paragraph } = Typography; export const StyledModal = styled(Card)` @@ -12,22 +11,22 @@ export const StyledModal = styled(Card)` overflow: hidden; max-width: 500px; margin: 0 auto; - + .ant-modal-content { background-color: var(--background-color); border-radius: ${BORDER_RADIUS}; } - + .ant-modal-header { background-color: transparent; border-bottom: none; padding-bottom: 0; } - + .ant-modal-body { padding: 0; } - + .ant-modal-footer { border-top: none; padding-top: 0; @@ -52,7 +51,7 @@ export const CloseButton = styled(Button)` border: none; background: transparent; box-shadow: none; - + &:hover { color: var(--primary-color); background: transparent; @@ -66,11 +65,13 @@ export const AvatarSection = styled.div` align-items: center; justify-content: center; padding: 2rem 1rem; - background: linear-gradient(180deg, rgba(24,44,89,0.3) 0%, rgba(33,64,125,0.1) 100%); + background: linear-gradient(180deg, rgba(24, 44, 89, 0.3) 0%, rgba(33, 64, 125, 0.1) 100%); `; export const AvatarContainer = styled.div` position: relative; + margin-top: 0.5rem; + width: 140px; height: 140px; margin-bottom: 1.5rem; @@ -78,7 +79,7 @@ export const AvatarContainer = styled.div` overflow: hidden; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.2); border: 4px solid var(--background-color); - + img { width: 100%; height: 100%; @@ -110,11 +111,11 @@ export const InfoCard = styled(Card)` border-radius: ${BORDER_RADIUS}; background-color: var(--secondary-background-color); border: 1px solid var(--border-color); - + .ant-card-body { padding: 1rem; } - + &:last-child { margin-bottom: 0; } @@ -124,7 +125,7 @@ export const InfoHeader = styled.div` display: flex; align-items: center; margin-bottom: 0.75rem; - + & > *:first-child { margin-right: 0.5rem; color: var(--primary-color); @@ -164,7 +165,7 @@ export const CopyButton = styled(Button)` height: auto; border: none; background: transparent; - + &:hover { color: var(--primary-light-color); background: transparent; @@ -191,3 +192,31 @@ export const IconWrapper = styled.div` border-radius: 50%; background-color: rgba(var(--primary-rgb-color), 0.1); `; +export const UsernameWrapper = styled.div` + display: flex; + align-items: center; + width: max-content; +`; + +interface SubscriptionBadgeProps { + subscribed: boolean; +} + +export const SubscriptionBadge = styled(Badge.Ribbon)` + margin-top: 1rem; + margin-right:.3rem; + background: ${({ subscribed }) => (subscribed ? 'var(--ant-primary-color)' : 'var(--error-color)')}; + color: var(--text-main-color); +`; + +export const StateModal = styled(Modal)` + + .ant-modal-body { + height: 50vh; + flex-direction: column; + display: flex; + align-items: center; + justify-content: center; + } + +` \ No newline at end of file diff --git a/src/components/relay-dashboard/paid-subscribers/SubscriberDetailModal/SubscriberDetailModal.tsx b/src/components/relay-dashboard/paid-subscribers/SubscriberDetailModal/SubscriberDetailModal.tsx index 289c0a6c..f14c70f6 100644 --- a/src/components/relay-dashboard/paid-subscribers/SubscriberDetailModal/SubscriberDetailModal.tsx +++ b/src/components/relay-dashboard/paid-subscribers/SubscriberDetailModal/SubscriberDetailModal.tsx @@ -1,41 +1,64 @@ import React, { useState } from 'react'; -import { Modal, message, Button } from 'antd'; -import { - UserOutlined, - KeyOutlined, - CalendarOutlined, - CrownOutlined, +import { Modal, message, Spin, Typography } from 'antd'; +import { + KeyOutlined, + CalendarOutlined, + CrownOutlined, CloseOutlined, CopyOutlined, - CheckOutlined + CheckOutlined, } from '@ant-design/icons'; import { SubscriberProfile } from '@app/hooks/usePaidSubscribers'; import * as S from './SubscriberDetailModal.styles'; interface SubscriberDetailModalProps { subscriber: SubscriberProfile | null; + loading?: boolean; + fetchFailed?: boolean; isVisible: boolean; onClose: () => void; } -export const SubscriberDetailModal: React.FC = ({ - subscriber, - isVisible, - onClose -}) => { +export const SubscriberDetailModal: React.FC = ({ subscriber, isVisible, onClose, loading = false, fetchFailed = false }) => { const [copied, setCopied] = useState(false); - + // Loading state + if (!subscriber && loading && !fetchFailed) { + return ( + + + + ); + } + + // Error state + if (!subscriber && !loading && fetchFailed) { + return ( + + Failed to fetch subscriber profile. Please try again. + + ); + } + + // Not found state + if (!subscriber && !loading && !fetchFailed) { + return ( + + Couldn't find this subscriber profile. + + ); + } if (!subscriber) { return null; } - + // Function to copy public key const copyPublicKey = () => { - navigator.clipboard.writeText(subscriber.pubkey) + navigator.clipboard + .writeText(subscriber.pubkey) .then(() => { setCopied(true); message.success('Public key copied to clipboard'); - + // Reset copied state after 3 seconds setTimeout(() => { setCopied(false); @@ -45,12 +68,14 @@ export const SubscriberDetailModal: React.FC = ({ message.error('Failed to copy public key'); }); }; - + // Format public key for display const formatPublicKey = (key: string) => { if (key.length <= 16) return key; return `${key.substring(0, 8)}...${key.substring(key.length - 8)}`; }; + const subscribed: boolean = !!subscriber.metadata?.subscriptionTier && !!subscriber.metadata?.subscribedSince; + const subscribedLabel = subscribed ? 'Subscribed' : 'Not Subscribed'; return ( = ({ Subscriber Profile - + {/* Avatar section with profile picture and name */} - - {subscriber.name - - - {subscriber.name || 'Anonymous Subscriber'} - - - {subscriber.about && ( - - {subscriber.about} - - )} + + + {subscriber.name + + + {subscriber.name || 'Anonymous Subscriber'} + + {subscriber.about && {subscriber.about}} - + {/* Information section */} {/* Public Key Card */} @@ -97,19 +115,15 @@ export const SubscriberDetailModal: React.FC = ({ Public Key - + {formatPublicKey(subscriber.pubkey)} - : } - > + : }> {copied ? 'Copied' : 'Copy'} - + {/* Subscription Tier Card (if available) */} {subscriber.metadata?.subscriptionTier && ( @@ -119,12 +133,10 @@ export const SubscriberDetailModal: React.FC = ({ Subscription Tier - - {subscriber.metadata.subscriptionTier} - + {subscriber.metadata.subscriptionTier} )} - + {/* Subscription Date Card (if available) */} {subscriber.metadata?.subscribedSince && ( @@ -134,9 +146,7 @@ export const SubscriberDetailModal: React.FC = ({ Subscribed Since - - {subscriber.metadata.subscribedSince} - + {subscriber.metadata.subscribedSince} )} diff --git a/src/components/relay-dashboard/paid-subscribers/avatar/SubscriberAvatar.styles.ts b/src/components/relay-dashboard/paid-subscribers/avatar/SubscriberAvatar.styles.ts index d6905fbb..c2c11d89 100644 --- a/src/components/relay-dashboard/paid-subscribers/avatar/SubscriberAvatar.styles.ts +++ b/src/components/relay-dashboard/paid-subscribers/avatar/SubscriberAvatar.styles.ts @@ -13,7 +13,10 @@ export const CreatorButton = styled.button` border: 0; cursor: pointer; border-radius: 50%; - padding: 2px; + height: 100%; + min-width: 5rem; + aspect-ratio: 1 / 1; + overflow: hidden; border: 3px solid ${(props) => (!props.$viewed ? 'var(--primary-color)' : 'var(--text-superLight-color)')}; `; @@ -23,5 +26,6 @@ export const Avatar = styled.img` height: auto; max-height: 100%; object-fit: cover; + aspect-ratio: 1 / 1; border-radius: 50%; `; \ No newline at end of file diff --git a/src/components/relay-dashboard/trending-creators/TrendingCreators.styles.ts b/src/components/relay-dashboard/trending-creators/TrendingCreators.styles.ts deleted file mode 100644 index 93f9f12a..00000000 --- a/src/components/relay-dashboard/trending-creators/TrendingCreators.styles.ts +++ /dev/null @@ -1,36 +0,0 @@ -import styled from 'styled-components'; -import { BaseModal } from '@app/components/common/BaseModal/BaseModal'; -import { BaseButton } from '@app/components/common/BaseButton/BaseButton'; -import { BREAKPOINTS, media } from '@app/styles/themes/constants'; - -export const StoriesModal = styled(BaseModal)` - @media only screen and (max-width: ${BREAKPOINTS.md - 0.02}px) { - top: 0; - padding: 0; - margin: 0; - max-width: 100%; - } - - .ant-modal-body { - padding: 0; - } - - .ant-modal-close { - z-index: 999999; - top: 1rem; - - color: var(--text-secondary-color); - } -`; - -export const ArrowBtn = styled(BaseButton)` - color: var(--text-nft-light-color); -`; - -export const CardWrapper = styled.div` - margin: 0 0.40625rem; - - @media only screen and ${media.xl} { - margin: 0 0.625rem; - } -`; diff --git a/src/components/relay-dashboard/trending-creators/TrendingCreators.tsx b/src/components/relay-dashboard/trending-creators/TrendingCreators.tsx deleted file mode 100644 index af40e5d5..00000000 --- a/src/components/relay-dashboard/trending-creators/TrendingCreators.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import React, { useRef, useState } from 'react'; -import { Splide, SplideSlide, SplideTrack } from '@splidejs/react-splide'; -import { LeftOutlined, RightOutlined } from '@ant-design/icons'; -import { useTranslation } from 'react-i18next'; -import { NFTCardHeader } from '@app/components/relay-dashboard/common/NFTCardHeader/NFTCardHeader'; -import { ViewAll } from '@app/components/relay-dashboard/common/ViewAll/ViewAll'; -import { TrendingCreatorsStory } from '@app/components/relay-dashboard/trending-creators/story/TrendingCreatorsStory'; -import { getTrendingCreators, TrendingCreator } from '@app/api/trendingCreators'; -import { SubscriberDetailModal } from './SubscriberDetailModal/SubscriberDetailModal'; -import * as S from './TrendingCreators.styles'; -import { BaseRow } from '@app/components/common/BaseRow/BaseRow'; -import { BaseCol } from '@app/components/common/BaseCol/BaseCol'; -import { SplideCarousel } from '@app/components/common/SplideCarousel/SplideCarousel'; -import { useResponsive } from '@app/hooks/useResponsive'; -import usePaidSubscribers, { SubscriberProfile } from '@app/hooks/usePaidSubscribers'; -import { Row, Col } from 'antd'; - -export const TrendingCreators: React.FC = () => { - console.log('[TrendingCreators] Component rendering...'); - const hookResult = usePaidSubscribers(12); - const { subscribers } = hookResult; - - // Modal state for subscriber details - const [selectedSubscriber, setSelectedSubscriber] = useState(null); - const [isModalVisible, setIsModalVisible] = useState(false); - - // Handle opening subscriber detail modal - const handleOpenSubscriberDetails = (subscriber: SubscriberProfile) => { - setSelectedSubscriber(subscriber); - setIsModalVisible(true); - }; - - // Handle closing subscriber detail modal - const handleCloseModal = () => { - setIsModalVisible(false); - }; - - console.log('[TrendingCreators] Received subscribers:', subscribers); - console.log('[TrendingCreators] Complete hook result:', hookResult); - - const sliderRef = useRef(null); - const { isTablet: isTabletOrHigher } = useResponsive(); - const { t } = useTranslation(); - - const goPrev = () => { - if (sliderRef.current?.splide) { - sliderRef.current.splide.go('-1'); - } - }; - - const goNext = () => { - if (sliderRef.current?.splide) { - sliderRef.current.splide.go('+1'); - } - }; - - // Determine whether to use carousel with looping based on count - const shouldUseLoop = subscribers.length >= 7; - - // Simple grid for few subscribers - if (subscribers.length > 0 && subscribers.length < 7) { - return ( - <> - - - - - - - - - - {subscribers.map((subscriber: SubscriberProfile) => ( - - - handleOpenSubscriberDetails(subscriber)} - img={subscriber.picture} - viewed={false} - /> - - - ))} - - - - - ); - } - - // Carousel view for 7+ subscribers - return ( - <> - - - - - - - - {isTabletOrHigher && subscribers.length > 1 && ( - <> - - - - - - - - - - - - - )} - - - - {subscribers.map((subscriber: SubscriberProfile) => ( - - - handleOpenSubscriberDetails(subscriber)} - img={subscriber.picture} - viewed={false} - /> - - - ))} - - - - - - ); -}; - -export default TrendingCreators; diff --git a/src/hooks/usePaidSubscribers.ts b/src/hooks/usePaidSubscribers.ts index f223893f..70d9751f 100644 --- a/src/hooks/usePaidSubscribers.ts +++ b/src/hooks/usePaidSubscribers.ts @@ -1,5 +1,4 @@ import { useState, useEffect, useCallback, useRef } from 'react'; -import { message } from 'antd'; import config from '@app/config/config'; import { readToken } from '@app/services/localStorage.service'; import { useHandleLogout } from './authUtils'; @@ -21,7 +20,7 @@ import adminDefaultAvatar from '@app/assets/admin-default-avatar.png'; export interface SubscriberProfile { pubkey: string; - picture: string; + picture?: string; name?: string; about?: string; metadata?: { @@ -29,7 +28,16 @@ export interface SubscriberProfile { subscribedSince?: string; }; } +const testSubscribers: SubscriberProfile[] = [ + { pubkey: '91dfb08db37712e74d892adbbf63abab43cb6aa3806950548f3146347d29b6ae' }, + { pubkey: '59cacbd83ad5c54ad91dacf51a49c06e0bef730ac0e7c235a6f6fa29b9230f02' }, + { pubkey: '32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245' }, + { pubkey: '78a317586cbc30d20f8aa94d8450eb0cd58b312bad94fc76139c72eb2e5c81d2' }, + { pubkey: '4657dfe8965be8980a93072bcfb5e59a65124406db0f819215ee78ba47934b3e' }, + { pubkey: '6e75f7972397ca3295e0f4ca0fbc6eb9cc79be85bafdd56bd378220ca8eee74e' }, + { pubkey: '7b991f776d04d87cb5d4259688187a520f6afc16b2b9ad26dac6b8ee76c2840d'} +]; // Define dummy profiles using the imported images const dummyProfiles: SubscriberProfile[] = [ { pubkey: 'dummy-1', picture: profile1 }, @@ -46,6 +54,7 @@ const dummyProfiles: SubscriberProfile[] = [ { pubkey: 'dummy-12', picture: profile11 }, ]; + // URL of the placeholder avatar that comes from the API const PLACEHOLDER_AVATAR_URL = 'http://localhost:3000/placeholder-avatar.png'; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 10bbd559..de99c168 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -6,7 +6,8 @@ import maestro from '@app/assets/images/card-issuers/maestro.png'; import { CurrencyTypeEnum, Severity } from '@app/interfaces/interfaces'; import { BaseBadgeProps } from '@app/components/common/BaseBadge/BaseBadge'; import { currencies } from '@app/constants/config/currencies'; - +import { NDKUserProfile } from '@nostr-dev-kit/ndk'; +import { SubscriberProfile } from '@app/hooks/usePaidSubscribers'; export const camelize = (string: string): string => { return string .split(' ') @@ -21,7 +22,14 @@ export const getSatsCurrency = (price: number | string, currency: CurrencyTypeEn // Handle potential negative sign placement return isIcon ? `${currencySymbol}${formattedPrice}` : `${formattedPrice} ${currency}`; }; - + export const convertNDKUserProfileToSubscriberProfile = (pubkey: string, user: NDKUserProfile): SubscriberProfile => { + return { + pubkey, + name: user.name || '', + picture: user.picture || '', + about: user.about || '', + }; + }; export const getCurrencyPrice = (price: number | string, currency: CurrencyTypeEnum, isIcon = true): string => { const currencySymbol = currencies[currency][isIcon ? 'icon' : 'text']; diff --git a/yarn.lock b/yarn.lock index e44c197e..a1434d19 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1219,6 +1219,16 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@cashu/cashu-ts@^2.5.2": + version "2.5.2" + resolved "https://registry.yarnpkg.com/@cashu/cashu-ts/-/cashu-ts-2.5.2.tgz#808bdc9bdd30cf4a3477e9116cdc77792005f1c9" + integrity sha512-AjfDOZKb3RWWhmpHABC4KJxwJs3wp6eOFg6U3S6d3QOqtSoNkceMTn6lLN4/bYQarLR19rysbrIJ8MHsSwNxeQ== + dependencies: + "@noble/curves" "^1.6.0" + "@noble/hashes" "^1.5.0" + "@scure/bip32" "^1.5.0" + buffer "^6.0.3" + "@cnakazawa/watch@^1.0.3": version "1.0.4" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" @@ -1648,6 +1658,13 @@ dependencies: "@noble/hashes" "1.3.2" +"@noble/curves@^1.6.0", "@noble/curves@~1.9.0": + version "1.9.2" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.9.2.tgz#73388356ce733922396214a933ff7c95afcef911" + integrity sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g== + dependencies: + "@noble/hashes" "1.8.0" + "@noble/curves@~1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.1.0.tgz#f13fc667c89184bc04cccb9b11e8e7bae27d8c3d" @@ -1665,11 +1682,21 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== +"@noble/hashes@1.8.0", "@noble/hashes@^1.5.0", "@noble/hashes@~1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.8.0.tgz#cee43d801fcef9644b11b8194857695acd5f815a" + integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== + "@noble/hashes@~1.3.0", "@noble/hashes@~1.3.1": version "1.3.3" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== +"@noble/secp256k1@^2.1.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-2.3.0.tgz#ddfe6e853472fb88cba4d5e59b7067adc1e64adf" + integrity sha512-0TQed2gcBbIrh7Ccyw+y/uZQvbJwm7Ao4scBUxqpBCcsOlZG0O4KGfjtNAy/li4W8n1xt3dxrwJ0beZ2h2G6Kw== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1691,6 +1718,42 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@nostr-dev-kit/ndk-hooks@^1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@nostr-dev-kit/ndk-hooks/-/ndk-hooks-1.2.3.tgz#d724508e91ef859a8eaa97a997500e73f0db85b2" + integrity sha512-FMlitm4urlBofINfBG8A8L4kH4EaXIPZUOVEYAYJWuHF2WPp37JKPycgSrWdZlRheDH48hkSGGiSBbRcGL/2sQ== + dependencies: + "@nostr-dev-kit/ndk" "^2.14.29" + "@nostr-dev-kit/ndk-wallet" "0.6.2" + "@testing-library/react" "^14.1.2" + zustand "^5" + +"@nostr-dev-kit/ndk-wallet@0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@nostr-dev-kit/ndk-wallet/-/ndk-wallet-0.6.2.tgz#45fd9566eaea507e7fe4180027f3a850da4f522f" + integrity sha512-vJ2rpX3Zprff+BD47O00omWakHwuybNmb7dp9TunQilQTEKKApZ5y0t/B0766qtqkTwHD4ME/LU3wujNnCaIKg== + dependencies: + "@nostr-dev-kit/ndk" "^2.14.11" + debug "^4.3.4" + light-bolt11-decoder "^3.0.0" + tseep "^1.1.1" + typescript "^5.8.2" + webln "^0.3.2" + +"@nostr-dev-kit/ndk@^2.14.11", "@nostr-dev-kit/ndk@^2.14.29", "@nostr-dev-kit/ndk@^2.14.32": + version "2.14.32" + resolved "https://registry.yarnpkg.com/@nostr-dev-kit/ndk/-/ndk-2.14.32.tgz#a7fa56e74c6f07a3ab709c16c4293c0bdbfbc759" + integrity sha512-LUBO35RCB9/emBYsXNDece7m/WO2rGYR8j4SD0Crb3z8GcKTJq6P8OjpZ6+Kr+sLNo8N0uL07XxtAvEBnp2OqQ== + dependencies: + "@noble/curves" "^1.6.0" + "@noble/hashes" "^1.5.0" + "@noble/secp256k1" "^2.1.0" + "@scure/base" "^1.1.9" + debug "^4.3.6" + light-bolt11-decoder "^3.2.0" + tseep "^1.3.1" + typescript-lru-cache "^2" + "@npmcli/fs@^1.0.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257" @@ -1813,6 +1876,11 @@ resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== +"@scure/base@^1.1.9", "@scure/base@~1.2.5": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.6.tgz#ca917184b8231394dd8847509c67a0be522e59f6" + integrity sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg== + "@scure/base@~1.1.0": version "1.1.9" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" @@ -1827,6 +1895,15 @@ "@noble/hashes" "~1.3.1" "@scure/base" "~1.1.0" +"@scure/bip32@^1.5.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.7.0.tgz#b8683bab172369f988f1589640e53c4606984219" + integrity sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw== + dependencies: + "@noble/curves" "~1.9.0" + "@noble/hashes" "~1.8.0" + "@scure/base" "~1.2.5" + "@scure/bip39@1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.1.tgz#5cee8978656b272a917b7871c981e0541ad6ac2a" @@ -1992,6 +2069,29 @@ "@svgr/plugin-svgo" "^5.5.0" loader-utils "^2.0.0" +"@testing-library/dom@^9.0.0": + version "9.3.4" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.4.tgz#50696ec28376926fec0a1bf87d9dbac5e27f60ce" + integrity sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "5.1.3" + chalk "^4.1.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + pretty-format "^27.0.2" + +"@testing-library/react@^14.1.2": + version "14.3.1" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-14.3.1.tgz#29513fc3770d6fb75245c4e1245c470e4ffdd830" + integrity sha512-H99XjUhWQw0lTgyMN05W3xQG1Nh4lq574D8keFf1dDoNTJgp66VbJozRaczoF+wsiaPJNt/TcnfpLGufGxSrZQ== + dependencies: + "@babel/runtime" "^7.12.5" + "@testing-library/dom" "^9.0.0" + "@types/react-dom" "^18.0.0" + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -2017,6 +2117,11 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== +"@types/aria-query@^5.0.1": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" + integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== + "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" @@ -2050,6 +2155,13 @@ dependencies: "@babel/types" "^7.20.7" +"@types/chrome@^0.0.74": + version "0.0.74" + resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.74.tgz#f69827c48fcf7fecc90c96089807661749a5a5e3" + integrity sha512-hzosS5CkQcIKCgxcsV2AzbJ36KNxG/Db2YEN/erEu7Boprg+KpMDLBQqKFmSo+JkQMGqRcicUyqCowJpuT+C6A== + dependencies: + "@types/filesystem" "*" + "@types/country-list@^2.1.0": version "2.1.4" resolved "https://registry.yarnpkg.com/@types/country-list/-/country-list-2.1.4.tgz#703558392ce6fd8a3a1cd30084cebc11ace247aa" @@ -2105,6 +2217,18 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== +"@types/filesystem@*": + version "0.0.36" + resolved "https://registry.yarnpkg.com/@types/filesystem/-/filesystem-0.0.36.tgz#7227c2d76bfed1b21819db310816c7821d303857" + integrity sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA== + dependencies: + "@types/filewriter" "*" + +"@types/filewriter@*": + version "0.0.33" + resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.33.tgz#d9d611db9d9cd99ae4e458de420eeb64ad604ea8" + integrity sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g== + "@types/geojson@*": version "7946.0.15" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.15.tgz#f9d55fd5a0aa2de9dc80b1b04e437538b7298868" @@ -2247,6 +2371,11 @@ dependencies: "@types/react" "*" +"@types/react-dom@^18.0.0": + version "18.3.7" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.7.tgz#b89ddf2cd83b4feafcc4e2ea41afdfb95a0d194f" + integrity sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ== + "@types/react-dom@^18.0.11": version "18.3.2" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.2.tgz#b58a9520f5f317a00bbda0271502889b71c345f0" @@ -2938,6 +3067,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + antd-mask-input@^2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/antd-mask-input/-/antd-mask-input-2.0.7.tgz#202d706eb83571646835bf52e9bfe1d1e4ea5eaf" @@ -3027,6 +3161,13 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +aria-query@5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" + integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== + dependencies: + deep-equal "^2.0.5" + aria-query@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" @@ -3052,6 +3193,14 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== +array-buffer-byte-length@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b" + integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw== + dependencies: + call-bound "^1.0.3" + is-array-buffer "^3.0.5" + array-buffer-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" @@ -3531,7 +3680,7 @@ balanced-match@^2.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9" integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA== -base64-js@^1.0.2: +base64-js@^1.0.2, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -3813,6 +3962,14 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + builtin-modules@^3.1.0: version "3.3.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" @@ -3896,6 +4053,14 @@ call-bind-apply-helpers@^1.0.0: es-errors "^1.3.0" function-bind "^1.1.2" +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7, call-bind@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" @@ -3906,6 +4071,14 @@ call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7, call-bin get-intrinsic "^1.2.4" set-function-length "^1.2.2" +call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + caller-callsite@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" @@ -5023,6 +5196,13 @@ debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.3.6: + version "4.4.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" + integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== + dependencies: + ms "^2.1.3" + decamelize-keys@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" @@ -5063,6 +5243,30 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.5.1" +deep-equal@^2.0.5: + version "2.2.3" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1" + integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.5" + es-get-iterator "^1.1.3" + get-intrinsic "^1.2.2" + is-arguments "^1.1.1" + is-array-buffer "^3.0.2" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.13" + deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -5240,6 +5444,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-accessibility-api@^0.5.9: + version "0.5.16" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" + integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== + dom-align@^1.7.0: version "1.12.4" resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.12.4.tgz#3503992eb2a7cfcb2ed3b2a6d21e0b9c00d54511" @@ -5356,6 +5565,15 @@ dunder-proto@^1.0.0: es-errors "^1.3.0" gopd "^1.2.0" +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + duplexer@^0.1.1, duplexer@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" @@ -5592,6 +5810,21 @@ es-errors@^1.2.1, es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== +es-get-iterator@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + es-iterator-helpers@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz#2f1a3ab998b30cb2d10b195b587c6d9ebdebf152" @@ -5620,6 +5853,13 @@ es-object-atoms@^1.0.0: dependencies: es-errors "^1.3.0" +es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + es-set-tostringtag@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" @@ -6416,6 +6656,13 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +for-each@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" + integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== + dependencies: + is-callable "^1.2.7" + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -6566,6 +6813,22 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-intrinsic@^1.1.3, get-intrinsic@^1.2.2, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: version "1.2.5" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.5.tgz#dfe7dd1b30761b464fe51bf4bb00ac7c37b681e7" @@ -6590,6 +6853,14 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + get-stdin@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" @@ -7166,7 +7437,7 @@ identity-obj-proxy@3.0.0: dependencies: harmony-reflect "^1.4.6" -ieee754@^1.1.4: +ieee754@^1.1.4, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -7324,6 +7595,15 @@ internal-slot@^1.0.7: hasown "^2.0.0" side-channel "^1.0.4" +internal-slot@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961" + integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.2" + side-channel "^1.1.0" + internmap@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/internmap/-/internmap-1.0.1.tgz#0017cc8a3b99605f0302f2b198d272e015e5df95" @@ -7389,6 +7669,15 @@ is-arguments@^1.1.1: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-array-buffer@^3.0.2, is-array-buffer@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" + integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + get-intrinsic "^1.2.6" + is-array-buffer@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" @@ -7601,7 +7890,7 @@ is-hexadecimal@^1.0.0: resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== -is-map@^2.0.3: +is-map@^2.0.2, is-map@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== @@ -7717,7 +8006,7 @@ is-root@2.1.0: resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== -is-set@^2.0.3: +is-set@^2.0.2, is-set@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== @@ -8620,6 +8909,13 @@ libphonenumber-js@^1.11.16: resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.11.16.tgz#3aa64a8a95ffc59253a5df3009940a9604a02102" integrity sha512-Noyazmt0yOvnG0OeRY45Cd1ur8G7Z0HWVkuCuKe+yysGNxPQwBAODBQQ40j0AIagi9ZWurfmmZWNlpg4h4W+XQ== +light-bolt11-decoder@^3.0.0, light-bolt11-decoder@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/light-bolt11-decoder/-/light-bolt11-decoder-3.2.0.tgz#2d48f78386cde526c4131db8f9dfd3250a2d5d4d" + integrity sha512-3QEofgiBOP4Ehs9BI+RkZdXZNtSys0nsJ6fyGeSiAGCBsMwHGUDS/JQlY/sTnWs91A2Nh0S9XXfA8Sy9g6QpuQ== + dependencies: + "@scure/base" "1.1.1" + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -8837,6 +9133,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== + magic-string@^0.25.0, magic-string@^0.25.7: version "0.25.9" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" @@ -8907,6 +9208,11 @@ matchmediaquery@^0.3.0: dependencies: css-mediaquery "^0.1.2" +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + mathml-tag-names@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" @@ -10835,6 +11141,15 @@ pretty-format@^26.6.0, pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" +pretty-format@^27.0.2: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + pretty-time@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e" @@ -12527,6 +12842,35 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + side-channel@^1.0.4, side-channel@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" @@ -12537,6 +12881,17 @@ side-channel@^1.0.4, side-channel@^1.0.6: get-intrinsic "^1.2.4" object-inspect "^1.13.1" +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -12834,6 +13189,14 @@ std-env@^3.0.1: resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5" integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w== +stop-iteration-iterator@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad" + integrity sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ== + dependencies: + es-errors "^1.3.0" + internal-slot "^1.1.0" + stream-browserify@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" @@ -13555,6 +13918,11 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" +tseep@^1.1.1, tseep@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/tseep/-/tseep-1.3.1.tgz#734c5f7ca37cb8af4e4e0a5c205742673562a10e" + integrity sha512-ZPtfk1tQnZVyr7BPtbJ93qaAh2lZuIOpTMjhrYa4XctT8xe7t4SAW9LIxrySDuYMsfNNayE51E/WNGrNVgVicQ== + tslib@2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" @@ -13711,11 +14079,21 @@ typeface-montserrat@^1.1.13: resolved "https://registry.yarnpkg.com/typeface-montserrat/-/typeface-montserrat-1.1.13.tgz#2a16729c174dd1a8c3fec05a851b7725412606cc" integrity sha512-Pklkyj0e+K+6I/t0M6JBDBphpfJkF1k+3qd8qDnp9aVtCC7oGBQWTAcL6+5eArfGe7h73uPwyal73hEkf9YCUA== +typescript-lru-cache@^2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/typescript-lru-cache/-/typescript-lru-cache-2.0.0.tgz#d4ad0f071ab51987b088a57c3c502d7dd62dee07" + integrity sha512-Jp57Qyy8wXeMkdNuZiglE6v2Cypg13eDA1chHwDG6kq51X7gk4K7P7HaDdzZKCxkegXkVHNcPD0n5aW6OZH3aA== + typescript@5.1.6: version "5.1.6" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== +typescript@^5.8.2: + version "5.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" + integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== + unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" @@ -14107,6 +14485,13 @@ webidl-conversions@^6.1.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== +webln@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/webln/-/webln-0.3.2.tgz#bbadf52916666b6059e3661ef5ab73a76b7cd0f4" + integrity sha512-YYT83aOCLup2AmqvJdKtdeBTaZpjC6/JDMe8o6x1kbTYWwiwrtWHyO//PAsPixF3jwFsAkj5DmiceB6w/QSe7Q== + dependencies: + "@types/chrome" "^0.0.74" + webpack-bundle-analyzer@^4.4.2: version "4.10.2" resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz#633af2862c213730be3dbdf40456db171b60d5bd" @@ -14317,7 +14702,7 @@ which-builtin-type@^1.2.0: which-collection "^1.0.2" which-typed-array "^1.1.15" -which-collection@^1.0.2: +which-collection@^1.0.1, which-collection@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== @@ -14332,6 +14717,19 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== +which-typed-array@^1.1.13: + version "1.1.19" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" + integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" + for-each "^0.3.5" + get-proto "^1.0.1" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + which-typed-array@^1.1.14, which-typed-array@^1.1.15: version "1.1.16" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.16.tgz#db4db429c4706feca2f01677a144278e4a8c216b" @@ -14689,6 +15087,11 @@ zrender@5.6.0: dependencies: tslib "2.3.0" +zustand@^5: + version "5.0.6" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.6.tgz#a2da43d8dc3d31e314279e5baec06297bea70a5c" + integrity sha512-ihAqNeUVhe0MAD+X8M5UzqyZ9k3FFZLBTtqo6JLPwV53cbRB/mJwBI0PxcIgqhBBHlEs8G45OTDTMq3gNcLq3A== + zwitch@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"