@@ -34,6 +34,8 @@ interface ConversationItem {
3434interface ConversationsData {
3535 conversations : ConversationItem [ ] ;
3636 username : string ;
37+ totalCount : number ;
38+ hasMore : boolean ;
3739}
3840
3941// Enable autoResize to let the SDK handle iframe height automatically
@@ -46,6 +48,8 @@ const conversationsListEl = document.getElementById('conversationsList')!;
4648const loadMoreBtn = document . getElementById ( 'loadMoreBtn' ) as HTMLButtonElement ;
4749
4850let currentData : ConversationsData | null = null ;
51+ let currentOffset = 0 ;
52+ const PAGE_SIZE = 10 ;
4953
5054// Height is now managed automatically by the MCP Apps SDK (autoResize: true)
5155
@@ -70,34 +74,45 @@ function formatRelativeTime(dateStr: string): string {
7074 return date . toLocaleDateString ( ) ;
7175}
7276
73- function updateLoadMoreButton ( count : number ) : void {
74- if ( count === 0 ) {
77+ function updateLoadMoreButton ( data : ConversationsData ) : void {
78+ const displayedCount = document . querySelectorAll ( '.conversation-card' ) . length ;
79+
80+ if ( displayedCount === 0 ) {
7581 // Show button only when empty - lets user check for new mentions
7682 loadMoreBtn . textContent = 'Check for new mentions' ;
7783 loadMoreBtn . classList . add ( 'all-clear' ) ;
7884 loadMoreBtn . classList . remove ( 'hidden' ) ;
85+ } else if ( data . hasMore ) {
86+ // Show load more button when there are more items
87+ const remaining = data . totalCount - displayedCount ;
88+ loadMoreBtn . textContent = `Load more (${ remaining } remaining)` ;
89+ loadMoreBtn . classList . remove ( 'all-clear' , 'hidden' ) ;
7990 } else {
80- // Hide button when conversations are present
91+ // Hide button when all loaded
8192 loadMoreBtn . classList . add ( 'hidden' ) ;
8293 }
8394}
8495
85- function renderConversations ( data : ConversationsData ) : void {
96+ function renderConversations ( data : ConversationsData , append = false ) : void {
8697 currentData = data ;
8798 loadingEl . classList . add ( 'hidden' ) ;
8899 contentEl . classList . remove ( 'hidden' ) ;
89100
90- const count = data . conversations . length ;
91- updateLoadMoreButton ( count ) ;
101+ // Reset offset if not appending
102+ if ( ! append ) {
103+ currentOffset = data . conversations . length ;
104+ }
92105
93- if ( count === 0 ) {
106+ // Check for empty state using totalCount (0 total means nothing to show)
107+ if ( data . totalCount === 0 ) {
94108 conversationsListEl . innerHTML = `
95109 <div class="empty-state">
96110 <div class="empty-icon">✨</div>
97111 <h3>All caught up!</h3>
98112 <p>No conversations need your attention right now.</p>
99113 </div>
100114 ` ;
115+ updateLoadMoreButton ( data ) ;
101116 return ;
102117 }
103118
@@ -111,7 +126,7 @@ function renderConversations(data: ConversationsData): void {
111126 . slice ( 0 , 2 ) ;
112127 }
113128
114- conversationsListEl . innerHTML = data . conversations
129+ const cardsHtml = data . conversations
115130 . map ( ( conv ) => {
116131 const relativeTime = formatRelativeTime ( conv . created_at ) ;
117132 const initials = getInitials ( conv . author_display_name || conv . author_username || '?' ) ;
@@ -155,6 +170,15 @@ function renderConversations(data: ConversationsData): void {
155170 } )
156171 . join ( '' ) ;
157172
173+ if ( append ) {
174+ // Append new cards to existing list
175+ conversationsListEl . insertAdjacentHTML ( 'beforeend' , cardsHtml ) ;
176+ currentOffset += data . conversations . length ;
177+ } else {
178+ // Replace entire list
179+ conversationsListEl . innerHTML = cardsHtml ;
180+ }
181+
158182 // Attach event listeners to action buttons
159183 attachEventListeners ( ) ;
160184
@@ -174,6 +198,9 @@ function renderConversations(data: ConversationsData): void {
174198 }
175199 } ) ;
176200 } , 100 ) ;
201+
202+ // Update load more button state
203+ updateLoadMoreButton ( data ) ;
177204}
178205
179206function attachEventListeners ( ) : void {
@@ -200,9 +227,14 @@ function attachEventListeners(): void {
200227 card . style . opacity = '0.5' ;
201228 setTimeout ( ( ) => card . remove ( ) , 300 ) ;
202229
203- // Update button count
230+ // Update button state based on remaining items
204231 const remaining = document . querySelectorAll ( '.conversation-card' ) . length - 1 ;
205- updateLoadMoreButton ( remaining ) ;
232+ if ( currentData ) {
233+ // Update the data to reflect the removal
234+ currentData . totalCount = Math . max ( 0 , currentData . totalCount - 1 ) ;
235+ currentData . hasMore = currentOffset < currentData . totalCount ;
236+ updateLoadMoreButton ( currentData ) ;
237+ }
206238 } catch ( error ) {
207239 btn . textContent = 'Dismiss' ;
208240 ( btn as HTMLButtonElement ) . disabled = false ;
@@ -324,9 +356,12 @@ function attachEventListeners(): void {
324356 card . style . opacity = '0.5' ;
325357 setTimeout ( ( ) => card . remove ( ) , 500 ) ;
326358
327- // Update button count
328- const remaining = document . querySelectorAll ( '.conversation-card' ) . length - 1 ;
329- updateLoadMoreButton ( remaining ) ;
359+ // Update button state based on remaining items
360+ if ( currentData ) {
361+ currentData . totalCount = Math . max ( 0 , currentData . totalCount - 1 ) ;
362+ currentData . hasMore = currentOffset < currentData . totalCount ;
363+ updateLoadMoreButton ( currentData ) ;
364+ }
330365 } catch {
331366 // Ignore dismiss errors
332367 }
@@ -367,25 +402,32 @@ app.ontoolresult = async () => {
367402 }
368403} ;
369404
370- // Check for new mentions button (only visible when list is empty)
405+ // Load more button - either refreshes when empty or loads next page
371406loadMoreBtn . addEventListener ( 'click' , async ( ) => {
407+ const isLoadingMore = currentOffset > 0 && currentData ?. hasMore ;
372408 loadMoreBtn . disabled = true ;
373- loadMoreBtn . textContent = 'Checking...' ;
409+ loadMoreBtn . textContent = isLoadingMore ? 'Loading...' : 'Checking...' ;
374410
375411 try {
376412 const result = await app . callServerTool ( {
377413 name : 'x_get_conversations' ,
378- arguments : { } ,
414+ arguments : isLoadingMore ? { limit : PAGE_SIZE , offset : currentOffset } : { } ,
379415 } ) ;
380416
381417 const textContent = result . content ?. find ( ( c : { type : string } ) => c . type === 'text' ) ;
382418 if ( textContent && 'text' in textContent ) {
383419 const data = JSON . parse ( textContent . text as string ) as ConversationsData ;
384- renderConversations ( data ) ;
420+ if ( isLoadingMore ) {
421+ // Append new conversations
422+ renderConversations ( data , true ) ;
423+ } else {
424+ // Fresh load
425+ renderConversations ( data , false ) ;
426+ }
385427 }
386428 } catch ( error ) {
387- console . error ( '[ASSA] Failed to check for mentions :' , error ) ;
388- loadMoreBtn . textContent = 'Check for new mentions' ;
429+ console . error ( '[ASSA] Failed to load conversations :' , error ) ;
430+ loadMoreBtn . textContent = currentOffset > 0 ? 'Error - try again' : 'Check for new mentions' ;
389431 } finally {
390432 loadMoreBtn . disabled = false ;
391433 }
0 commit comments