@@ -12,17 +12,24 @@ import { BaseCol } from '@app/components/common/BaseCol/BaseCol';
1212import { SplideCarousel } from '@app/components/common/SplideCarousel/SplideCarousel' ;
1313import { useResponsive } from '@app/hooks/useResponsive' ;
1414import usePaidSubscribers , { SubscriberProfile } from '@app/hooks/usePaidSubscribers' ;
15- import { Row , Col } from 'antd' ;
15+ import { Row , Col , Modal , Spin , Typography } from 'antd' ;
16+ import { nip19 } from 'nostr-tools' ;
17+
18+ const { Text } = Typography ;
1619
1720export const PaidSubscribers : React . FC = ( ) => {
1821 console . log ( '[PaidSubscribers] Component rendering...' ) ;
1922 const hookResult = usePaidSubscribers ( 12 ) ;
20- const { subscribers } = hookResult ;
23+ const { subscribers, fetchMore , hasMore , loading } = hookResult ;
2124
2225 // Modal state for subscriber details
2326 const [ selectedSubscriber , setSelectedSubscriber ] = useState < SubscriberProfile | null > ( null ) ;
2427 const [ isModalVisible , setIsModalVisible ] = useState ( false ) ;
2528
29+ // Modal state for view all subscribers
30+ const [ isViewAllModalVisible , setIsViewAllModalVisible ] = useState ( false ) ;
31+ const [ allSubscribers , setAllSubscribers ] = useState < SubscriberProfile [ ] > ( [ ] ) ;
32+
2633 // Handle opening subscriber detail modal
2734 const handleOpenSubscriberDetails = ( subscriber : SubscriberProfile ) => {
2835 setSelectedSubscriber ( subscriber ) ;
@@ -34,6 +41,33 @@ export const PaidSubscribers: React.FC = () => {
3441 setIsModalVisible ( false ) ;
3542 } ;
3643
44+ // Handle opening view all modal
45+ const handleViewAll = async ( ) => {
46+ setIsViewAllModalVisible ( true ) ;
47+ setAllSubscribers ( [ ...subscribers ] ) ; // Start with current subscribers
48+
49+ // Fetch more subscribers if available
50+ let currentSubscribers = [ ...subscribers ] ;
51+ let canFetchMore = hasMore ;
52+
53+ while ( canFetchMore ) {
54+ try {
55+ await fetchMore ( ) ;
56+ // Note: This is a simplified approach. In a real scenario, you'd want to
57+ // track the updated state properly or use a separate hook for fetching all
58+ canFetchMore = false ; // For now, just fetch once more
59+ } catch ( error ) {
60+ console . error ( 'Error fetching more subscribers:' , error ) ;
61+ break ;
62+ }
63+ }
64+ } ;
65+
66+ // Handle closing view all modal
67+ const handleCloseViewAllModal = ( ) => {
68+ setIsViewAllModalVisible ( false ) ;
69+ } ;
70+
3771 console . log ( '[PaidSubscribers] Received subscribers:' , subscribers ) ;
3872 console . log ( '[PaidSubscribers] Complete hook result:' , hookResult ) ;
3973
@@ -63,7 +97,7 @@ export const PaidSubscribers: React.FC = () => {
6397 < NFTCardHeader title = { t ( 'nft.paidSubs' ) } >
6498 < BaseRow align = "middle" >
6599 < BaseCol >
66- < ViewAll bordered = { false } />
100+ < ViewAll bordered = { false } onClick = { handleViewAll } />
67101 </ BaseCol >
68102 </ BaseRow >
69103 </ NFTCardHeader >
@@ -87,6 +121,98 @@ export const PaidSubscribers: React.FC = () => {
87121 isVisible = { isModalVisible }
88122 onClose = { handleCloseModal }
89123 />
124+
125+ { /* View All Subscribers Modal */ }
126+ < Modal
127+ title = { t ( 'nft.allPaidSubscribers' ) }
128+ open = { isViewAllModalVisible }
129+ onCancel = { handleCloseViewAllModal }
130+ footer = { null }
131+ width = { 800 }
132+ style = { { top : 20 } }
133+ >
134+ < Row gutter = { [ 16 , 16 ] } style = { { padding : '16px 0' } } >
135+ { ( allSubscribers . length > 0 ? allSubscribers : subscribers ) . map ( ( subscriber : SubscriberProfile ) => (
136+ < Col key = { subscriber . pubkey } xs = { 24 } sm = { 24 } md = { 24 } lg = { 24 } xl = { 24 } >
137+ < div style = { {
138+ display : 'flex' ,
139+ alignItems : 'center' ,
140+ padding : '16px' ,
141+ border : '1px solid var(--border-color-base)' ,
142+ borderRadius : '12px' ,
143+ marginBottom : '12px' ,
144+ cursor : 'pointer' ,
145+ transition : 'all 0.2s ease' ,
146+ backgroundColor : 'var(--background-color-secondary)' ,
147+ gap : '16px' ,
148+ boxShadow : '0 2px 4px rgba(0, 0, 0, 0.1)'
149+ } }
150+ onClick = { ( ) => {
151+ setSelectedSubscriber ( subscriber ) ;
152+ setIsModalVisible ( true ) ;
153+ setIsViewAllModalVisible ( false ) ;
154+ } }
155+ onMouseEnter = { ( e ) => {
156+ e . currentTarget . style . backgroundColor = 'var(--background-color-light)' ;
157+ e . currentTarget . style . transform = 'translateY(-2px)' ;
158+ e . currentTarget . style . boxShadow = '0 4px 12px rgba(0, 0, 0, 0.15)' ;
159+ } }
160+ onMouseLeave = { ( e ) => {
161+ e . currentTarget . style . backgroundColor = 'var(--background-color-secondary)' ;
162+ e . currentTarget . style . transform = 'translateY(0)' ;
163+ e . currentTarget . style . boxShadow = '0 2px 4px rgba(0, 0, 0, 0.1)' ;
164+ } }
165+ >
166+ < div style = { { flexShrink : 0 } } >
167+ < img
168+ src = { subscriber . picture }
169+ alt = { subscriber . name || 'Subscriber' }
170+ style = { {
171+ width : '56px' ,
172+ height : '56px' ,
173+ borderRadius : '50%' ,
174+ objectFit : 'cover' ,
175+ border : '3px solid var(--primary-color)' ,
176+ boxShadow : '0 2px 8px rgba(0, 0, 0, 0.1)'
177+ } }
178+ />
179+ </ div >
180+ < div style = { {
181+ flex : 1 ,
182+ minWidth : 0 ,
183+ display : 'flex' ,
184+ flexDirection : 'column' ,
185+ gap : '6px'
186+ } } >
187+ < Text strong style = { {
188+ fontSize : '16px' ,
189+ color : 'var(--text-main-color)' ,
190+ margin : 0
191+ } } >
192+ { subscriber . name || 'Anonymous User' }
193+ </ Text >
194+ < Text style = { {
195+ fontSize : '13px' ,
196+ color : 'var(--text-secondary-color)' ,
197+ fontFamily : 'monospace' ,
198+ margin : 0 ,
199+ lineHeight : '1.2'
200+ } } >
201+ { ( ( ) => {
202+ try {
203+ return nip19 . npubEncode ( subscriber . pubkey ) ;
204+ } catch {
205+ // Fallback to original hex format if encoding fails
206+ return subscriber . pubkey ;
207+ }
208+ } ) ( ) }
209+ </ Text >
210+ </ div >
211+ </ div >
212+ </ Col >
213+ ) ) }
214+ </ Row >
215+ </ Modal >
90216 </ >
91217 ) ;
92218 }
@@ -123,7 +249,7 @@ export const PaidSubscribers: React.FC = () => {
123249 < NFTCardHeader title = { t ( 'nft.paidSubs' ) } >
124250 < BaseRow align = "middle" >
125251 < BaseCol >
126- < ViewAll bordered = { false } />
252+ < ViewAll bordered = { false } onClick = { handleViewAll } />
127253 </ BaseCol >
128254
129255 { isTabletOrHigher && subscribers . length > 1 && (
@@ -163,6 +289,98 @@ export const PaidSubscribers: React.FC = () => {
163289 isVisible = { isModalVisible }
164290 onClose = { handleCloseModal }
165291 />
292+
293+ { /* View All Subscribers Modal */ }
294+ < Modal
295+ title = { t ( 'nft.allPaidSubscribers' ) }
296+ open = { isViewAllModalVisible }
297+ onCancel = { handleCloseViewAllModal }
298+ footer = { null }
299+ width = { 800 }
300+ style = { { top : 20 } }
301+ >
302+ < Row gutter = { [ 16 , 16 ] } style = { { padding : '16px 0' } } >
303+ { ( allSubscribers . length > 0 ? allSubscribers : subscribers ) . map ( ( subscriber : SubscriberProfile ) => (
304+ < Col key = { subscriber . pubkey } xs = { 24 } sm = { 24 } md = { 24 } lg = { 24 } xl = { 24 } >
305+ < div style = { {
306+ display : 'flex' ,
307+ alignItems : 'center' ,
308+ padding : '16px' ,
309+ border : '1px solid var(--border-color-base)' ,
310+ borderRadius : '12px' ,
311+ marginBottom : '12px' ,
312+ cursor : 'pointer' ,
313+ transition : 'all 0.2s ease' ,
314+ backgroundColor : 'var(--background-color-secondary)' ,
315+ gap : '16px' ,
316+ boxShadow : '0 2px 4px rgba(0, 0, 0, 0.1)'
317+ } }
318+ onClick = { ( ) => {
319+ setSelectedSubscriber ( subscriber ) ;
320+ setIsModalVisible ( true ) ;
321+ setIsViewAllModalVisible ( false ) ;
322+ } }
323+ onMouseEnter = { ( e ) => {
324+ e . currentTarget . style . backgroundColor = 'var(--background-color-light)' ;
325+ e . currentTarget . style . transform = 'translateY(-2px)' ;
326+ e . currentTarget . style . boxShadow = '0 4px 12px rgba(0, 0, 0, 0.15)' ;
327+ } }
328+ onMouseLeave = { ( e ) => {
329+ e . currentTarget . style . backgroundColor = 'var(--background-color-secondary)' ;
330+ e . currentTarget . style . transform = 'translateY(0)' ;
331+ e . currentTarget . style . boxShadow = '0 2px 4px rgba(0, 0, 0, 0.1)' ;
332+ } }
333+ >
334+ < div style = { { flexShrink : 0 } } >
335+ < img
336+ src = { subscriber . picture }
337+ alt = { subscriber . name || 'Subscriber' }
338+ style = { {
339+ width : '56px' ,
340+ height : '56px' ,
341+ borderRadius : '50%' ,
342+ objectFit : 'cover' ,
343+ border : '3px solid var(--primary-color)' ,
344+ boxShadow : '0 2px 8px rgba(0, 0, 0, 0.1)'
345+ } }
346+ />
347+ </ div >
348+ < div style = { {
349+ flex : 1 ,
350+ minWidth : 0 ,
351+ display : 'flex' ,
352+ flexDirection : 'column' ,
353+ gap : '6px'
354+ } } >
355+ < Text strong style = { {
356+ fontSize : '16px' ,
357+ color : 'var(--text-main-color)' ,
358+ margin : 0
359+ } } >
360+ { subscriber . name || 'Anonymous User' }
361+ </ Text >
362+ < Text style = { {
363+ fontSize : '13px' ,
364+ color : 'var(--text-secondary-color)' ,
365+ fontFamily : 'monospace' ,
366+ margin : 0 ,
367+ lineHeight : '1.2'
368+ } } >
369+ { ( ( ) => {
370+ try {
371+ return nip19 . npubEncode ( subscriber . pubkey ) ;
372+ } catch {
373+ // Fallback to original hex format if encoding fails
374+ return subscriber . pubkey ;
375+ }
376+ } ) ( ) }
377+ </ Text >
378+ </ div >
379+ </ div >
380+ </ Col >
381+ ) ) }
382+ </ Row >
383+ </ Modal >
166384 </ >
167385 ) ;
168386} ;
0 commit comments