import { useState, useEffect } from 'react' import { useNavigate, useLocation, Routes, Route } from 'react-router-dom' import { Dropdown } from 'react-bootstrap' import axios from 'axios' import { useAuth } from './contexts/AuthContext' import ConversationList from './components/ConversationList' import MessageThread from './components/MessageThread' import Activity from './components/Activity' import Calls from './components/Calls' import DateFilter from './components/DateFilter' import Upload from './components/Upload' import Search from './components/Search' import ChangePasswordModal from './components/ChangePasswordModal' import './App.css' const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8081/api' function App() { const navigate = useNavigate() const location = useLocation() const { user, logout } = useAuth() const [conversations, setConversations] = useState([]) const [conversationsLoading, setConversationsLoading] = useState(false) const [selectedConversation, setSelectedConversation] = useState(null) const [startDate, setStartDate] = useState(null) const [endDate, setEndDate] = useState(null) const [dateRange, setDateRange] = useState({ min: null, max: null }) const [showUpload, setShowUpload] = useState(false) const [showPasswordModal, setShowPasswordModal] = useState(false) const [searchFilter, setSearchFilter] = useState('') // Search state (persisted across tab switches) const [searchQuery, setSearchQuery] = useState('') const [searchResults, setSearchResults] = useState([]) const [searchLoading, setSearchLoading] = useState(false) const [searchExecuted, setSearchExecuted] = useState(false) const [searchScrollPosition, setSearchScrollPosition] = useState(0) // Derive activeView from URL const activeView = location.pathname.startsWith('/activity') ? 'activity' : location.pathname.startsWith('/calls') ? 'calls' : location.pathname.startsWith('/search') ? 'search' : 'conversations' useEffect(() => { fetchDateRange() fetchConversations() }, []) useEffect(() => { fetchConversations() }, [startDate, endDate]) // Sync selected conversation from URL useEffect(() => { const match = location.pathname.match(/^\/conversation\/(.+)$/) if (match) { const address = decodeURIComponent(match[1]) // Find conversation by address const conversation = conversations.find(c => c.address === address) if (conversation) { setSelectedConversation(conversation) } else if (conversations.length > 0) { // If conversation not found in list, create a minimal conversation object setSelectedConversation({ address, contact_name: address, type: 'message' }) } } else if (location.pathname === '/' || location.pathname === '/conversations') { setSelectedConversation(null) } }, [location.pathname, conversations]) const fetchDateRange = async () => { try { const response = await axios.get(`${API_BASE}/daterange`) setDateRange({ min: new Date(response.data.min_date), max: new Date(response.data.max_date) }) } catch (error) { console.error('Error fetching date range:', error) } } const fetchConversations = async () => { setConversationsLoading(true) try { const params = {} if (startDate) params.start = startDate.toISOString() if (endDate) params.end = endDate.toISOString() const response = await axios.get(`${API_BASE}/conversations`, { params }) setConversations(response.data || []) } catch (error) { console.error('Error fetching conversations:', error) } finally { setConversationsLoading(false) } } const handleUploadSuccess = () => { setShowUpload(false) fetchDateRange() fetchConversations() } const handleSelectConversation = (conversation) => { if (conversation) { navigate(`/conversation/${encodeURIComponent(conversation.address)}`) } } const handleViewChange = (view) => { if (view === 'activity') { navigate('/activity') } else if (view === 'calls') { navigate('/calls') } else if (view === 'search') { navigate('/search') } else { navigate('/') } } // Filter conversations based on search text const filteredConversations = conversations.filter(conv => { if (!searchFilter) return true const searchLower = searchFilter.toLowerCase() const nameMatch = conv.contact_name && conv.contact_name.toLowerCase().includes(searchLower) const addressMatch = conv.address && conv.address.toLowerCase().includes(searchLower) return nameMatch || addressMatch }) return (
{/* Header */}

SMS Backup Viewer

View and browse your message history

Logged in as
{user?.username}
Version {__APP_VERSION__} setShowPasswordModal(true)}> Change Password Logout
{/* View Switcher */}
{/* Date Filter */}
{/* Main Content */}
{activeView === 'conversations' ? ( <> {/* Conversation List */}

Conversations

setSearchFilter(e.target.value)} />
{/* Message Thread */}
) : activeView === 'search' ? ( /* Search View */
) : activeView === 'calls' ? ( /* Calls View */
) : ( /* Activity View */
)}
{/* Upload Modal */} {showUpload && ( setShowUpload(false)} onSuccess={handleUploadSuccess} /> )} {/* Change Password Modal */} {showPasswordModal && ( setShowPasswordModal(false)} onSuccess={() => { // Password changed successfully console.log('Password changed successfully') }} /> )}
) } export default App