fix test ask-AI oauth profile
This commit is contained in:
		| @@ -79,7 +79,7 @@ a.view-markdown:any-link { | ||||
| } | ||||
|  | ||||
| .header-content { | ||||
|     max-width: 1000px; | ||||
|     max-width: 800px; | ||||
|     margin: 0 auto; | ||||
|     display: grid; | ||||
|     grid-template-columns: 1fr auto 1fr; | ||||
| @@ -197,7 +197,6 @@ a.view-markdown:any-link { | ||||
|     margin-bottom: 16px; | ||||
| } | ||||
|  | ||||
| /* Main Content */ | ||||
| .main-content { | ||||
| 	grid-area: main; | ||||
| 	max-width: 800px; | ||||
| @@ -206,13 +205,6 @@ a.view-markdown:any-link { | ||||
| 	width: 100%; | ||||
| } | ||||
|  | ||||
| @media (max-width: 1000px) { | ||||
| 	.main-content { | ||||
| 		padding: 0px; | ||||
| 		max-width: 100%; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Timeline */ | ||||
| .timeline-container { | ||||
|     max-width: 600px; | ||||
| @@ -327,12 +319,9 @@ a.view-markdown:any-link { | ||||
|  | ||||
| /* Article */ | ||||
| .article-container { | ||||
|     display: grid; | ||||
|     grid-template-columns: 1fr 240px; | ||||
|     /* gap: 40px; */ | ||||
|     max-width: 1000px; | ||||
|     max-width: 800px; | ||||
|     margin: 0 auto; | ||||
| 				padding: 100px 0; | ||||
|     padding: 100px 0; | ||||
| } | ||||
|  | ||||
| /* article.article-content { padding: 10px; } */ | ||||
| @@ -396,18 +385,12 @@ a.view-markdown:any-link { | ||||
|     border-color: var(--white); | ||||
| } | ||||
|  | ||||
| /* Sidebar styles */ | ||||
| .article-sidebar { | ||||
|     position: sticky; | ||||
|     top: 100px; | ||||
|     height: fit-content; | ||||
| } | ||||
|  | ||||
| .toc { | ||||
|     background: #f6f8fa; | ||||
|     border: 1px solid #d1d9e0; | ||||
|     border-radius: 8px; | ||||
|     padding: 16px; | ||||
|     margin: 20px 0; | ||||
| } | ||||
|  | ||||
| .toc h3 { | ||||
| @@ -814,10 +797,8 @@ article.article-content { | ||||
| /* Responsive */ | ||||
| @media (max-width: 1000px) { | ||||
|     .article-container { | ||||
|         grid-template-columns: 1fr; | ||||
|         gap: 24px; | ||||
|         max-width: 100%; | ||||
|         padding: 50px 0; | ||||
|         padding: 50px 20px; | ||||
|         margin: 0; | ||||
|     } | ||||
| } | ||||
| @@ -1141,7 +1122,66 @@ article.article-content { | ||||
| 				.article-meta { | ||||
| 					padding: 10px; | ||||
| 				} | ||||
|  | ||||
| 				.article-actions { | ||||
| 					padding: 10px; | ||||
| 				} | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| /* Loading spinner for Ask AI panel */ | ||||
| .loading-content { | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     align-items: center; | ||||
|     justify-content: center; | ||||
|     padding: 20px; | ||||
| } | ||||
|  | ||||
| .loading-spinner { | ||||
|     width: 20px; | ||||
|     height: 20px; | ||||
|     border: 2px solid #f3f3f3; | ||||
|     border-top: 2px solid var(--theme-color); | ||||
|     border-radius: 50%; | ||||
|     animation: spin 1s linear infinite; | ||||
|     margin-bottom: 10px; | ||||
| } | ||||
|  | ||||
| @keyframes spin { | ||||
|     0% { transform: rotate(0deg); } | ||||
|     100% { transform: rotate(360deg); } | ||||
| } | ||||
|  | ||||
| .loading-content p { | ||||
|     margin: 0; | ||||
|     color: #656d76; | ||||
|     font-size: 14px; | ||||
| } | ||||
|  | ||||
| /* Handle links in chat messages */ | ||||
| .message-header .handle a { | ||||
|     color: #656d76; | ||||
|     text-decoration: none; | ||||
|     transition: color 0.2s; | ||||
| } | ||||
|  | ||||
| .message-header .handle a:hover { | ||||
|     color: var(--theme-color); | ||||
|     text-decoration: underline; | ||||
| } | ||||
|  | ||||
| @media (max-width: 1000px) { | ||||
| 	.main-content { | ||||
| 		padding: 0px; | ||||
| 		max-width: 100%; | ||||
| 	} | ||||
| 	article.article-content { | ||||
| 		max-width: 100%; | ||||
| 	} | ||||
| 	.timeline-feed { | ||||
| 		padding: 0px; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -12,24 +12,95 @@ function toggleAskAI() { | ||||
|     panel.style.display = isVisible ? 'none' : 'block'; | ||||
|      | ||||
|     if (!isVisible) { | ||||
|         checkAuthenticationStatus(); | ||||
|         console.log('Ask AI panel opened'); | ||||
|          | ||||
|         // If AI profile data is already available, show introduction immediately | ||||
|         if (aiProfileData) { | ||||
|             console.log('AI profile data available - showing introduction immediately'); | ||||
|             // Quick check for authentication | ||||
|             const userSections = document.querySelectorAll('.user-section'); | ||||
|             const isAuthenticated = userSections.length > 0; | ||||
|             handleAuthenticationStatus(isAuthenticated); | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         // For production fallback - if OAuth app fails to load, show profiles | ||||
|         const isProd = window.location.hostname !== 'localhost' && !window.location.hostname.includes('preview'); | ||||
|         if (isProd) { | ||||
|             console.log('Production environment detected - using fallback profile display'); | ||||
|             // Shorter timeout for production | ||||
|             setTimeout(() => { | ||||
|                 const userSections = document.querySelectorAll('.user-section'); | ||||
|                 console.log('Production check - user sections:', userSections.length); | ||||
|                  | ||||
|                 if (userSections.length === 0) { | ||||
|                     console.log('No user sections found in production - showing profiles directly'); | ||||
|                     handleAuthenticationStatus(false); | ||||
|                 } else { | ||||
|                     console.log('User sections found in production - showing authenticated UI'); | ||||
|                     handleAuthenticationStatus(true); | ||||
|                 } | ||||
|             }, 300); | ||||
|         } else { | ||||
|             checkAuthenticationStatus(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| function checkAuthenticationStatus() { | ||||
|     const userSections = document.querySelectorAll('.user-section'); | ||||
|     const isAuthenticated = userSections.length > 0; | ||||
|     // Check multiple times for OAuth app to load | ||||
|     let checkCount = 0; | ||||
|     const maxChecks = 10; | ||||
|      | ||||
|     const checkForAuth = () => { | ||||
|         console.log(`Auth check attempt ${checkCount + 1}/${maxChecks}`); | ||||
|         const userSections = document.querySelectorAll('.user-section'); | ||||
|         const authButtons = document.querySelectorAll('[data-auth-status]'); | ||||
|         const oauthContainers = document.querySelectorAll('#oauth-container'); | ||||
|          | ||||
|         console.log('User sections found:', userSections.length); | ||||
|         console.log('Auth buttons found:', authButtons.length); | ||||
|         console.log('OAuth containers found:', oauthContainers.length); | ||||
|          | ||||
|         const isAuthenticated = userSections.length > 0; | ||||
|          | ||||
|         if (isAuthenticated || checkCount >= maxChecks - 1) { | ||||
|             console.log('Final auth status:', isAuthenticated); | ||||
|             handleAuthenticationStatus(isAuthenticated); | ||||
|         } else { | ||||
|             checkCount++; | ||||
|             setTimeout(checkForAuth, 200); | ||||
|         } | ||||
|     }; | ||||
|      | ||||
|     checkForAuth(); | ||||
| } | ||||
|  | ||||
| function handleAuthenticationStatus(isAuthenticated) { | ||||
|     console.log('Handling auth status:', isAuthenticated); | ||||
|      | ||||
|     // Always hide loading first | ||||
|     document.getElementById('authCheck').style.display = 'none'; | ||||
|      | ||||
|     if (isAuthenticated) { | ||||
|         // User is authenticated - show Ask AI UI | ||||
|         document.getElementById('authCheck').style.display = 'none'; | ||||
|         console.log('User authenticated - showing AI chat interface'); | ||||
|         document.getElementById('chatForm').style.display = 'block'; | ||||
|         document.getElementById('chatHistory').style.display = 'block'; | ||||
|          | ||||
|         // Show initial greeting if chat history is empty | ||||
|         // Show initial greeting if chat history is empty and AI profile is available | ||||
|         const chatHistory = document.getElementById('chatHistory'); | ||||
|         if (chatHistory.children.length === 0) { | ||||
|             showInitialGreeting(); | ||||
|             if (aiProfileData) { | ||||
|                 showInitialGreeting(); | ||||
|             } else { | ||||
|                 // Wait for AI profile data | ||||
|                 setTimeout(() => { | ||||
|                     if (aiProfileData) { | ||||
|                         showInitialGreeting(); | ||||
|                     } | ||||
|                 }, 500); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         // Focus on input | ||||
| @@ -37,11 +108,18 @@ function checkAuthenticationStatus() { | ||||
|             document.getElementById('aiQuestion').focus(); | ||||
|         }, 50); | ||||
|     } else { | ||||
|         // User not authenticated - show profiles instead of auth message | ||||
|         document.getElementById('authCheck').style.display = 'none'; | ||||
|         // User not authenticated - show AI introduction directly if profile available | ||||
|         console.log('User not authenticated - showing AI introduction'); | ||||
|         document.getElementById('chatForm').style.display = 'none'; | ||||
|         document.getElementById('chatHistory').style.display = 'block'; | ||||
|         loadAndShowProfiles(); | ||||
|          | ||||
|         if (aiProfileData) { | ||||
|             // Show AI introduction directly using available profile data | ||||
|             showAIIntroduction(); | ||||
|         } else { | ||||
|             // Fallback to profile loading | ||||
|             loadAndShowProfiles(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -96,8 +174,7 @@ async function loadAndShowProfiles() { | ||||
|                     <div class="avatar">${avatarElement}</div> | ||||
|                     <div class="user-info"> | ||||
|                         <div class="display-name">${profile.value.author.displayName || profile.value.author.handle} ${adminBadge}</div> | ||||
|                         <div class="handle">@${profile.value.author.handle}</div> | ||||
|                         <div class="timestamp">${new Date(profile.value.createdAt).toLocaleString()}</div> | ||||
|                         <div class="handle"><a href="https://web.syu.is/profile/${profile.value.author.handle}" target="_blank" rel="noopener noreferrer">@${profile.value.author.handle}</a></div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <div class="message-content">${profile.value.text}</div> | ||||
| @@ -178,8 +255,7 @@ function addUserMessage(question) { | ||||
|             <div class="avatar">${userAvatar}</div> | ||||
|             <div class="user-info"> | ||||
|                 <div class="display-name">${userDisplay}</div> | ||||
|                 <div class="handle">@${userHandle}</div> | ||||
|                 <div class="timestamp">${new Date().toLocaleString()}</div> | ||||
|                 <div class="handle"><a href="https://web.syu.is/profile/${userHandle}" target="_blank" rel="noopener noreferrer">@${userHandle}</a></div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="message-content">${question}</div> | ||||
| @@ -242,17 +318,57 @@ function showInitialGreeting() { | ||||
|             <div class="avatar">${avatarElement}</div> | ||||
|             <div class="user-info"> | ||||
|                 <div class="display-name">${aiProfileData.displayName}</div> | ||||
|                 <div class="handle">@${aiProfileData.handle}</div> | ||||
|                 <div class="timestamp">${new Date().toLocaleString()}</div> | ||||
|                 <div class="handle"><a href="https://web.syu.is/profile/${aiProfileData.handle}" target="_blank" rel="noopener noreferrer">@${aiProfileData.handle}</a></div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="message-content"> | ||||
|             Hello! I'm an AI assistant trained on this blog's content. I can answer questions about the articles, provide insights, and help you understand the topics discussed here. What would you like to know? | ||||
|         </div> | ||||
|         <div class="message-content">Hello! I'm an AI assistant trained on this blog's content. I can answer questions about the articles, provide insights, and help you understand the topics discussed here. What would you like to know?</div> | ||||
|     `; | ||||
|     chatHistory.appendChild(greetingDiv); | ||||
| } | ||||
|  | ||||
| function showAIIntroduction() { | ||||
|     if (!aiProfileData) return; | ||||
|  | ||||
|     const chatHistory = document.getElementById('chatHistory'); | ||||
|     chatHistory.innerHTML = ''; // Clear any existing content | ||||
|      | ||||
|     // AI Introduction message | ||||
|     const introDiv = document.createElement('div'); | ||||
|     introDiv.className = 'chat-message ai-message comment-style initial-greeting'; | ||||
|      | ||||
|     const avatarElement = aiProfileData.avatar  | ||||
|         ? `<img src="${aiProfileData.avatar}" alt="${aiProfileData.displayName}" class="profile-avatar">` | ||||
|         : '🤖'; | ||||
|      | ||||
|     introDiv.innerHTML = ` | ||||
|         <div class="message-header"> | ||||
|             <div class="avatar">${avatarElement}</div> | ||||
|             <div class="user-info"> | ||||
|                 <div class="display-name">${aiProfileData.displayName}</div> | ||||
|                 <div class="handle"><a href="https://web.syu.is/profile/${aiProfileData.handle}" target="_blank" rel="noopener noreferrer">@${aiProfileData.handle}</a></div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="message-content">Hello! I'm an AI assistant trained on this blog's content. I can answer questions about the articles, provide insights, and help you understand the topics discussed here. What would you like to know?</div> | ||||
|     `; | ||||
|     chatHistory.appendChild(introDiv); | ||||
|      | ||||
|     // OAuth login message | ||||
|     const loginDiv = document.createElement('div'); | ||||
|     loginDiv.className = 'chat-message user-message comment-style initial-greeting'; | ||||
|      | ||||
|     loginDiv.innerHTML = ` | ||||
|         <div class="message-header"> | ||||
|             <div class="avatar">${avatarElement}</div> | ||||
|             <div class="user-info"> | ||||
|                 <div class="display-name">${aiProfileData.displayName}</div> | ||||
|                 <div class="handle"><a href="https://web.syu.is/profile/${aiProfileData.handle}" target="_blank" rel="noopener noreferrer">@${aiProfileData.handle}</a></div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="message-content">Please atproto oauth login</div> | ||||
|     `; | ||||
|     chatHistory.appendChild(loginDiv); | ||||
| } | ||||
|  | ||||
| function updateAskAIButton() { | ||||
|     const button = document.getElementById('askAiButton'); | ||||
|     if (!button) return; | ||||
| @@ -288,8 +404,7 @@ function handleAIResponse(responseData) { | ||||
|             <div class="avatar">${avatarElement}</div> | ||||
|             <div class="user-info"> | ||||
|                 <div class="display-name">${aiProfile.displayName}</div> | ||||
|                 <div class="handle">@${aiProfile.handle}</div> | ||||
|                 <div class="timestamp">${timestamp.toLocaleString()}</div> | ||||
|                 <div class="handle"><a href="https://web.syu.is/profile/${aiProfile.handle}" target="_blank" rel="noopener noreferrer">@${aiProfile.handle}</a></div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="message-content">${responseData.answer}</div> | ||||
| @@ -377,6 +492,37 @@ function setupAskAIEventListeners() { | ||||
| document.addEventListener('DOMContentLoaded', function() { | ||||
|     setupAskAIEventListeners(); | ||||
|     console.log('Ask AI initialized successfully'); | ||||
|      | ||||
|     // Also listen for OAuth app load completion | ||||
|     const observer = new MutationObserver(function(mutations) { | ||||
|         mutations.forEach(function(mutation) { | ||||
|             if (mutation.type === 'childList') { | ||||
|                 // Check if user-section was added/removed | ||||
|                 const userSectionAdded = Array.from(mutation.addedNodes).some(node =>  | ||||
|                     node.nodeType === Node.ELEMENT_NODE &&  | ||||
|                     (node.classList?.contains('user-section') || node.querySelector?.('.user-section')) | ||||
|                 ); | ||||
|                 const userSectionRemoved = Array.from(mutation.removedNodes).some(node =>  | ||||
|                     node.nodeType === Node.ELEMENT_NODE &&  | ||||
|                     (node.classList?.contains('user-section') || node.querySelector?.('.user-section')) | ||||
|                 ); | ||||
|                  | ||||
|                 if (userSectionAdded || userSectionRemoved) { | ||||
|                     console.log('User section status changed'); | ||||
|                     // Update Ask AI panel if it's visible | ||||
|                     const panel = document.getElementById('askAiPanel'); | ||||
|                     if (panel && panel.style.display !== 'none') { | ||||
|                         checkAuthenticationStatus(); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|     }); | ||||
|      | ||||
|     observer.observe(document.body, { | ||||
|         childList: true, | ||||
|         subtree: true | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| // Global functions for onclick handlers | ||||
|   | ||||
| @@ -61,7 +61,10 @@ | ||||
|         <div class="ask-ai-panel" id="askAiPanel" style="display: none;"> | ||||
|             <div class="ask-ai-content"> | ||||
|                 <div id="authCheck" class="auth-check"> | ||||
|                     <p>profile</p> | ||||
|                     <div class="loading-content"> | ||||
|                         <div class="loading-spinner"></div> | ||||
|                         <p>Loading...</p> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                  | ||||
|                 <div id="chatForm" class="ask-ai-form" style="display: none;"> | ||||
|   | ||||
| @@ -27,21 +27,19 @@ | ||||
|             </div> | ||||
|         </header> | ||||
|          | ||||
|         <div class="article-body"> | ||||
|             {{ post.content | safe }} | ||||
|         </div> | ||||
|          | ||||
|         <div id="comment-atproto"></div> | ||||
|     </article> | ||||
|      | ||||
|     <aside class="article-sidebar"> | ||||
|         <nav class="toc"> | ||||
|             <h3>Contents</h3> | ||||
|             <div id="toc-content"> | ||||
|                 <!-- TOC will be generated by JavaScript --> | ||||
|             </div> | ||||
|         </nav> | ||||
|     </aside> | ||||
|          | ||||
|         <div class="article-body"> | ||||
|             {{ post.content | safe }} | ||||
|         </div> | ||||
|          | ||||
|         <div id="comment-atproto"></div> | ||||
|     </article> | ||||
| </div> | ||||
|  | ||||
| <script> | ||||
|   | ||||
| @@ -139,7 +139,7 @@ body { | ||||
|   /* align-items: center; */ | ||||
|   max-width: 800px; | ||||
|   margin: 0 auto; | ||||
|   padding: 45px 0; | ||||
|   padding: 25px 0; | ||||
|   width: 100%; | ||||
| } | ||||
|  | ||||
| @@ -1070,6 +1070,10 @@ body { | ||||
|     width: 100%; | ||||
|     justify-content: center; | ||||
|   } | ||||
| 		article.article-content { | ||||
| 			max-width: 100%; | ||||
| 		} | ||||
|  | ||||
| } | ||||
|  | ||||
| /* Avatar Styles */ | ||||
|   | ||||
| @@ -69,8 +69,6 @@ export default function ChatRecordList({ chatPairs, apiConfig, user = null, agen | ||||
|                 )} | ||||
|                 <div className="user-info"> | ||||
|                   <div className="display-name">{chatPair.question.value.author?.displayName || chatPair.question.value.author?.handle}</div> | ||||
|                   <div className="handle">@{chatPair.question.value.author?.handle}</div> | ||||
|                   <div className="timestamp">{new Date(chatPair.question.value.createdAt).toLocaleString()}</div> | ||||
|                 </div> | ||||
|                 {canDelete(chatPair) && ( | ||||
|                   <div className="record-actions"> | ||||
| @@ -105,8 +103,6 @@ export default function ChatRecordList({ chatPairs, apiConfig, user = null, agen | ||||
|                 )} | ||||
|                 <div className="user-info"> | ||||
|                   <div className="display-name">{chatPair.answer.value.author?.displayName || chatPair.answer.value.author?.handle}</div> | ||||
|                   <div className="handle">@{chatPair.answer.value.author?.handle}</div> | ||||
|                   <div className="timestamp">{new Date(chatPair.answer.value.createdAt).toLocaleString()}</div> | ||||
|                 </div> | ||||
|               </div> | ||||
|               <div className="message-content">{chatPair.answer.value.text}</div> | ||||
|   | ||||
| @@ -58,8 +58,6 @@ export default function ProfileRecordList({ profileRecords, apiConfig, user = nu | ||||
|                   <span className="admin-badge"> Admin</span> | ||||
|                 )} | ||||
|               </div> | ||||
|               <div className="handle">@{profile.value.author?.handle}</div> | ||||
|               <div className="timestamp">{new Date(profile.value.createdAt).toLocaleString()}</div> | ||||
|             </div> | ||||
|             {canDelete(profile) && ( | ||||
|               <div className="record-actions"> | ||||
|   | ||||
| @@ -64,18 +64,18 @@ export default function RecordTabs({ langRecords, commentRecords, userComments, | ||||
|         > | ||||
|           chat ({userChatRecords?.length || 0}) | ||||
|         </button> | ||||
|         <button  | ||||
|           className={`tab-btn ${activeTab === 'users' ? 'active' : ''}`} | ||||
|           onClick={() => setActiveTab('users')} | ||||
|         > | ||||
|           comment ({filteredUserComments.length}) | ||||
|         </button> | ||||
|         <button  | ||||
|           className={`tab-btn ${activeTab === 'comment' ? 'active' : ''}`} | ||||
|           onClick={() => setActiveTab('comment')} | ||||
|         > | ||||
|           feedback ({filteredCommentRecords.length}) | ||||
|         </button> | ||||
|         <button  | ||||
|           className={`tab-btn ${activeTab === 'users' ? 'active' : ''}`} | ||||
|           onClick={() => setActiveTab('users')} | ||||
|         > | ||||
|           comment ({filteredUserComments.length}) | ||||
|         </button> | ||||
|         <button  | ||||
|           className={`tab-btn ${activeTab === 'lang' ? 'active' : ''}`} | ||||
|           onClick={() => setActiveTab('lang')} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user