import { useState, useMemo } from 'react';
import {
Box,
Container,
Typography,
InputBase,
Avatar,
Chip,
Checkbox,
FormControlLabel,
CircularProgress,
Link,
IconButton,
Button,
} from '@mui/material';
import SearchIcon from '@mui/icons-material/Search';
import CloseIcon from '@mui/icons-material/Close';
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
import AccessTimeIcon from '@mui/icons-material/AccessTime';
import VerifiedIcon from '@mui/icons-material/Verified';
import DownloadIcon from '@mui/icons-material/Download';
import Layout from '../components/Layout';
import ReachiesCarousel from '../components/ReachiesCarousel';
import { useApps } from '../context/AppsContext';
import InstallModal from '../components/InstallModal';
// App Card Component
function AppCard({ app, onInstallClick }) {
const isOfficial = app.isOfficial;
const cardData = app.cardData || {};
const author = app.id?.split('/')?.[0] || app.author || null;
const likes = app.likes || 0;
const lastModified = app.lastModified || app.createdAt || null;
const emoji = cardData.emoji || '📦';
const formattedDate = lastModified
? new Date(lastModified).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
: null;
const handleInstallClick = (e) => {
e.preventDefault();
e.stopPropagation();
onInstallClick?.(app);
};
return (
{/* Top Bar with Author, Official Badge, and Likes */}
{/* Author + Official Badge */}
{author && (
{author.charAt(0).toUpperCase()}
{author}
)}
{/* Official Badge - inline with author */}
{isOfficial && (
}
label="Official"
size="small"
sx={{
bgcolor: 'rgba(255, 149, 0, 0.1)',
color: '#FF9500',
fontWeight: 600,
fontSize: 10,
height: 20,
flexShrink: 0,
'& .MuiChip-icon': {
color: '#FF9500',
ml: 0.5,
},
'& .MuiChip-label': {
px: 0.75,
},
}}
/>
)}
{/* Likes */}
{likes}
{/* Content */}
{/* Title + Emoji Row */}
{app.name || app.id?.split('/').pop()}
{emoji}
{/* Description */}
{cardData.short_description || app.description || 'No description'}
{/* Date + Install Button */}
{/* Date */}
{formattedDate ? (
{formattedDate}
) : (
)}
{/* Install Button */}
}
onClick={handleInstallClick}
sx={{
py: 0.5,
px: 1.5,
borderRadius: '8px',
color: '#FF9500',
fontWeight: 600,
fontSize: 12,
textTransform: 'none',
'&:hover': {
bgcolor: 'rgba(255, 149, 0, 0.08)',
},
}}
>
Install
);
}
// Main Apps Page
export default function Apps() {
// Get apps from context (cached globally)
const { apps, loading, error } = useApps();
const [searchQuery, setSearchQuery] = useState('');
const [officialOnly, setOfficialOnly] = useState(false);
// Install modal state
const [installModalOpen, setInstallModalOpen] = useState(false);
const [selectedApp, setSelectedApp] = useState(null);
const handleInstallClick = (app) => {
setSelectedApp(app);
setInstallModalOpen(true);
};
const handleCloseInstallModal = () => {
setInstallModalOpen(false);
setSelectedApp(null);
};
// Filter apps based on search and official toggle
const filteredApps = useMemo(() => {
let result = apps;
// Filter by official
if (officialOnly) {
result = result.filter(app => app.isOfficial === true);
}
// Filter by search
if (searchQuery.trim()) {
const query = searchQuery.toLowerCase();
result = result.filter(app =>
app.name?.toLowerCase().includes(query) ||
app.id?.toLowerCase().includes(query) ||
app.description?.toLowerCase().includes(query) ||
app.cardData?.short_description?.toLowerCase().includes(query)
);
}
return result;
}, [apps, searchQuery, officialOnly]);
const isFiltered = searchQuery.trim() || officialOnly;
return (
{/* Hero Header */}
{/* Gradient orbs */}
{/* Reachies Carousel */}
{/* Text content */}
Powered by
Applications
Discover apps built by the community and official apps from Pollen Robotics.
Install them directly from the Reachy Mini desktop app.
{/* Search Section */}
setSearchQuery(e.target.value)}
sx={{
flex: 1,
fontSize: 15,
fontWeight: 500,
color: '#333',
'& input::placeholder': {
color: '#999',
opacity: 1,
},
}}
/>
{/* Clear search */}
{searchQuery && (
setSearchQuery('')}
size="small"
sx={{ color: '#999' }}
>
)}
{/* Separator */}
{/* Apps count */}
{isFiltered ? `${filteredApps.length}/${apps.length}` : apps.length}
{/* Separator */}
{/* Official toggle */}
setOfficialOnly(e.target.checked)}
size="small"
sx={{
color: '#999',
'&.Mui-checked': {
color: '#FF9500',
},
}}
/>
}
label={
Official only
}
sx={{ m: 0 }}
/>
{/* Apps Grid */}
{loading ? (
) : error ? (
{error}
) : filteredApps.length === 0 ? (
🔍
No apps found
Try adjusting your search or filters
) : (
{filteredApps.map((app, index) => (
))}
)}
{/* Install Modal */}
);
}