diff --git a/src/App.js b/src/App.js index fda0ecf..ae65013 100644 --- a/src/App.js +++ b/src/App.js @@ -137,8 +137,8 @@ function App() { : } /> - } /> - } /> + } /> + } /> : } /> diff --git a/src/components/Post/Post.js b/src/components/Post/Post.js index 97908ac..0cf99d1 100644 --- a/src/components/Post/Post.js +++ b/src/components/Post/Post.js @@ -1,6 +1,6 @@ import React from 'react'; import styled from 'styled-components'; -import { FaHeart, FaRetweet, FaReply, FaShare, FaClock, FaGlobeAmericas, FaLock, FaUsers, FaEnvelope } from 'react-icons/fa'; +import { FaHeart, FaRetweet, FaReply, FaShare, FaClock, FaGlobeAmericas, FaLock, FaUsers, FaEnvelope, FaTrash } from 'react-icons/fa'; const PostContainer = styled.article` background: white; @@ -158,7 +158,7 @@ const ReblogInfo = styled.div` } `; -const Post = ({ post, onFavorite, onReblog, onReply, currentUser }) => { +const Post = ({ post, onFavorite, onReblog, onReply, onDelete, currentUser }) => { const isReblog = post.reblog; const actualPost = isReblog ? post.reblog : post; const originalPoster = isReblog ? post.account : null; @@ -281,6 +281,12 @@ const Post = ({ post, onFavorite, onReblog, onReply, currentUser }) => { {actualPost.favourites_count || 0} + {currentUser && String(currentUser.id) === String(actualPost.account.id) && ( + onDelete?.(actualPost)}> + + + )} + navigator.share?.({ url: actualPost.url })}> diff --git a/src/pages/HomePage.js b/src/pages/HomePage.js index 1986b13..510a1e1 100644 --- a/src/pages/HomePage.js +++ b/src/pages/HomePage.js @@ -152,6 +152,20 @@ const HomePage = ({ user }) => { } }; + const handleDelete = async (post) => { + if (!window.confirm('¿Estás seguro de que quieres eliminar este post? Esta acción no se puede deshacer.')) { + return; + } + + try { + await api.deleteStatus(post.id); + setPosts(prev => prev.filter(p => p.id !== post.id)); + } catch (err) { + console.error('Error deleting post:', err); + // Could show a toast or error message here + } + }; + const handleLoadMore = () => { if (posts.length > 0 && hasMore && !loadingMore) { setLoadingMore(true); @@ -215,6 +229,7 @@ const HomePage = ({ user }) => { currentUser={user} onFavorite={handleFavorite} onReblog={handleReblog} + onDelete={handleDelete} onReply={(post) => console.log('Reply to:', post)} /> ))} diff --git a/src/pages/LocalTimelinePage.js b/src/pages/LocalTimelinePage.js index b421e65..b793e22 100644 --- a/src/pages/LocalTimelinePage.js +++ b/src/pages/LocalTimelinePage.js @@ -90,7 +90,7 @@ const EmptyState = styled.div` } `; -const LocalTimelinePage = () => { +const LocalTimelinePage = ({ currentUser }) => { const [posts, setPosts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -171,6 +171,20 @@ const LocalTimelinePage = () => { } }; + const handleDelete = async (post) => { + if (!window.confirm('¿Estás seguro de que quieres eliminar este post? Esta acción no se puede deshacer.')) { + return; + } + + try { + await api.deleteStatus(post.id); + setPosts(prev => prev.filter(p => p.id !== post.id)); + } catch (err) { + console.error('Error deleting post:', err); + // Could show a toast or error message here + } + }; + const handleLoadMore = () => { if (posts.length > 0 && hasMore && !loadingMore) { setLoadingMore(true); @@ -234,8 +248,10 @@ const LocalTimelinePage = () => { console.log('Reply to:', post)} /> ))} diff --git a/src/pages/OAuthCallbackPage.js b/src/pages/OAuthCallbackPage.js index c1e49df..5f51659 100644 --- a/src/pages/OAuthCallbackPage.js +++ b/src/pages/OAuthCallbackPage.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import styled from 'styled-components'; import { FaExclamationTriangle } from 'react-icons/fa'; @@ -80,10 +80,18 @@ const OAuthCallbackPage = ({ onLoginSuccess }) => { } }, [hasCode, hasState]); + const processedRef = useRef(false); + useEffect(() => { + if (processedRef.current) return; + processedRef.current = true; + console.log('OAuth callback: useEffect executed'); + const processCallback = async () => { try { + console.log('OAuth callback: Starting processCallback'); const { code, state, error: oauthError, error_description } = oauthService.getOAuthCallbackParams(); + console.log('OAuth callback: Got params', { code: !!code, state: !!state, oauthError, error_description }); if (oauthError) { throw new Error(error_description || `OAuth error: ${oauthError}`); @@ -94,14 +102,23 @@ const OAuthCallbackPage = ({ onLoginSuccess }) => { } setStatus('processing'); + console.log('OAuth callback: Status set to processing'); + console.log('OAuth callback: Calling exchangeCodeForToken'); const tokenData = await oauthService.exchangeCodeForToken(code, state); - api.setAuth(tokenData.access_token, tokenData.instance_url, tokenData.refresh_token); + console.log('OAuth callback: Token exchange successful', { access_token: !!tokenData.access_token, instance_url: tokenData.instance_url }); + api.setAuth(tokenData.access_token, tokenData.instance_url, tokenData.refresh_token); + console.log('OAuth callback: API auth set'); + + console.log('OAuth callback: Calling verifyCredentials'); const userData = await api.verifyCredentials(); + console.log('OAuth callback: Credentials verified', { username: userData.username }); + localStorage.setItem('current_user', JSON.stringify(userData)); setStatus('success'); + console.log('OAuth callback: Status set to success'); if (onLoginSuccess) { onLoginSuccess(userData); @@ -113,25 +130,15 @@ const OAuthCallbackPage = ({ onLoginSuccess }) => { }, 2000); } catch (err) { + console.error('OAuth callback error:', err); setError(err.message || 'Authentication failed'); setStatus('error'); } }; - const processTimer = setTimeout(processCallback, 100); - - const emergencyTimer = setTimeout(() => { - if (status === 'processing') { - setError('Authentication is taking too long. Please try again or check your connection.'); - setStatus('error'); - } - }, 30000); - - return () => { - clearTimeout(processTimer); - clearTimeout(emergencyTimer); - }; - }, [navigate, onLoginSuccess, status]); + // Call immediately instead of with setTimeout + processCallback(); + }, [navigate, onLoginSuccess]); diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index 7ac19ae..28ad029 100644 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -209,19 +209,37 @@ const ProfilePage = ({ currentUser }) => { setLoading(true); setError(null); + console.log('Loading profile for username:', username); + // First, search for the user by username - const searchResults = await api.search(username, { type: 'accounts', limit: 5 }); + const searchResults = await api.search(username, { type: 'accounts', limit: 20, resolve: true }); let userAccount = null; - // Look for exact username match - if (searchResults.accounts) { + // Look for exact username match (try multiple variations) + if (searchResults.accounts && searchResults.accounts.length > 0) { + // First try exact username match userAccount = searchResults.accounts.find(account => - account.username === username || account.acct === username + account.username === username ); + + // If not found, try with acct field (which includes domain) + if (!userAccount) { + userAccount = searchResults.accounts.find(account => + account.acct === username || account.acct === `${username}@${new URL(api.instanceUrl).host}` + ); + } + + // If still not found, try case-insensitive match + if (!userAccount) { + userAccount = searchResults.accounts.find(account => + account.username.toLowerCase() === username.toLowerCase() + ); + } } if (!userAccount) { + console.log('User not found in search results. Available accounts:', searchResults.accounts?.map(acc => ({ username: acc.username, acct: acc.acct }))); throw new Error('User not found'); } @@ -265,6 +283,20 @@ const ProfilePage = ({ currentUser }) => { loadProfile(); }, [loadProfile]); + const handleDelete = async (post) => { + if (!window.confirm('¿Estás seguro de que quieres eliminar este post? Esta acción no se puede deshacer.')) { + return; + } + + try { + await api.deleteStatus(post.id); + setPosts(prev => prev.filter(p => p.id !== post.id)); + } catch (err) { + console.error('Error deleting post:', err); + // Could show a toast or error message here + } + }; + const handleFollow = async () => { if (!currentUser || !profile) return; @@ -426,6 +458,7 @@ const ProfilePage = ({ currentUser }) => { currentUser={currentUser} onFavorite={() => {}} onReblog={() => {}} + onDelete={handleDelete} onReply={() => {}} /> )) diff --git a/src/pages/PublicTimelinePage.js b/src/pages/PublicTimelinePage.js index 84e82d8..d05a0d7 100644 --- a/src/pages/PublicTimelinePage.js +++ b/src/pages/PublicTimelinePage.js @@ -90,7 +90,7 @@ const EmptyState = styled.div` } `; -const PublicTimelinePage = () => { +const PublicTimelinePage = ({ currentUser }) => { const [posts, setPosts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -171,6 +171,20 @@ const PublicTimelinePage = () => { } }; + const handleDelete = async (post) => { + if (!window.confirm('¿Estás seguro de que quieres eliminar este post? Esta acción no se puede deshacer.')) { + return; + } + + try { + await api.deleteStatus(post.id); + setPosts(prev => prev.filter(p => p.id !== post.id)); + } catch (err) { + console.error('Error deleting post:', err); + // Could show a toast or error message here + } + }; + const handleLoadMore = () => { if (posts.length > 0 && hasMore && !loadingMore) { setLoadingMore(true); @@ -234,8 +248,10 @@ const PublicTimelinePage = () => { console.log('Reply to:', post)} /> ))} diff --git a/src/pages/StatusPage.js b/src/pages/StatusPage.js index d010519..c3a4a2e 100644 --- a/src/pages/StatusPage.js +++ b/src/pages/StatusPage.js @@ -158,6 +158,28 @@ const StatusPage = ({ currentUser }) => { } }; + const handleDelete = async (post) => { + if (!window.confirm('¿Estás seguro de que quieres eliminar este post? Esta acción no se puede deshacer.')) { + return; + } + + try { + await api.deleteStatus(post.id); + if (post.id === status.id) { + // If deleting the main post, redirect to home + window.location.href = '/'; + } else { + // Remove from context + setContext(prev => ({ + ancestors: prev.ancestors.filter(p => p.id !== post.id), + descendants: prev.descendants.filter(p => p.id !== post.id) + })); + } + } catch (err) { + console.error('Error deleting post:', err); + } + }; + // Build reply tree structure const buildReplyTree = (posts, parentId = null, depth = 0) => { return posts @@ -178,6 +200,7 @@ const StatusPage = ({ currentUser }) => { currentUser={currentUser} onFavorite={handleFavorite} onReblog={handleReblog} + onDelete={handleDelete} onReply={() => {}} compact={depth > 0} /> @@ -233,6 +256,7 @@ const StatusPage = ({ currentUser }) => { currentUser={currentUser} onFavorite={handleFavorite} onReblog={handleReblog} + onDelete={handleDelete} onReply={() => {}} /> @@ -246,6 +270,7 @@ const StatusPage = ({ currentUser }) => { currentUser={currentUser} onFavorite={handleFavorite} onReblog={handleReblog} + onDelete={handleDelete} onReply={() => {}} showFullContent={true} />