Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion analytics/components/AccountAnalytics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export const AccountAnalytics: React.FC<AccountAnalyticsProps> = ({ className =
if (loading) {
return (
<div className={`p-6 ${themeClasses.container} ${className}`}>
<div className='animate-pulse space-y-4'>
<div className='space-y-4'>
<div
className={`h-6 ${theme === 'light' ? 'bg-gray-200' : 'bg-white/10'} rounded w-1/3`}
></div>
Expand Down
2 changes: 1 addition & 1 deletion app/docs/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ export default function DocsPage() {

{/* Powered by Trustless Work */}
<div className='text-center mt-4'>
<p className='text-brand-300/70 text-sm font-medium animate-pulse'>
<p className='text-brand-300/70 text-sm font-medium '>
Powered by{' '}
<span className='text-brand-200 font-semibold'>Trustless Work</span>
</p>
Expand Down
86 changes: 66 additions & 20 deletions app/home/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import { AccountStatusIndicator } from '@/components/ui/AccountStatusIndicator';
import { LeaderboardSidebar } from '@/components/ui/LeaderboardSidebar';
import { QuestAndReferralSection } from '@/components/ui/quest/QuestAndReferralSection';
import { TOP_BADGES } from '@/utils/constants/demos';
import { useTransactionHistory } from '@/contexts/data/TransactionContext';
import React, { useCallback } from 'react';
import {
DemoSelector,
HeroSection,
Expand All @@ -35,7 +37,9 @@ import { DEMO_CARDS } from '@/utils/constants/demos';

export default function HomePageContent() {
const { isConnected } = useGlobalWallet();
const { transactions } = useTransactionHistory();
// Removed unused authentication variables
const activeTx = transactions ? Object.values(transactions).slice(-1)[0] : null;
const {
account,
demoStats,
Expand Down Expand Up @@ -86,6 +90,8 @@ export default function HomePageContent() {
} | null>(null);
const [showImmersiveDemo, setShowImmersiveDemo] = useState(false);
const [showTechTree, setShowTechTree] = useState(false);
//Nuevo estado de Contol.
const [isTechTreeProcessing, setIsTechTreeProcessing] = useState(false);

// Authentication modals
const [showAuthModal, setShowAuthModal] = useState(false);
Expand All @@ -111,6 +117,7 @@ export default function HomePageContent() {
const handleOpenLeaderboard = () => {
setLeaderboardSidebarOpen(true);
};


window.addEventListener('walletSidebarToggle', handleWalletSidebarToggle as EventListener);
window.addEventListener('openUserProfile', handleOpenUserProfile);
Expand Down Expand Up @@ -198,8 +205,34 @@ export default function HomePageContent() {
const handleFeedbackClose = () => {
setShowFeedbackModal(false);
setFeedbackDemoData(null);

};

const onTechTreeClick = useCallback(() => {
if (showTechTree || isTechTreeProcessing) {
// Si ya está abierto o en proceso, ignora el clic (Idempotencia)
return;
}

// 1. Establecer el estado de procesamiento inmediatamente
setIsTechTreeProcessing(true);

// 2. Abrir el modal
setShowTechTree(true);


setTimeout(() => {
setIsTechTreeProcessing(false);
}, 500); // 500ms recomendado para mayor seguridad

}, [showTechTree, isTechTreeProcessing])

const onCloseTechTree = useCallback(() => {
setShowTechTree(false);
setIsTechTreeProcessing(false); // Asegurar que se restablezca el estado de procesamiento
}, []);


return (
<div className='min-h-screen bg-gradient-to-br from-neutral-900 via-brand-900 to-neutral-900 relative overflow-hidden'>
{/* Structured Data for SEO */}
Expand Down Expand Up @@ -264,22 +297,29 @@ export default function HomePageContent() {
walletSidebarOpen && walletExpanded ? 'mr-96' : walletSidebarOpen ? 'mr-20' : 'mr-0'
} ${!walletSidebarOpen ? 'pb-32' : 'pb-8'}`}
>


{/* Hero Section */}
<HeroSection
isVideoPlaying={false}
miniGamesUnlocked={miniGamesUnlocked}
onTutorialClick={() => {
const tutorialSection = document.getElementById('interactive-tutorial');
if (tutorialSection) {
tutorialSection.scrollIntoView({
behavior: 'smooth',
block: 'start',
});
}
}}
onTechTreeClick={() => setShowTechTree(true)}
isConnected={isConnected}
isLoadingAccount={firebaseLoading && !isInitialized}
isVideoPlaying={false}
miniGamesUnlocked={miniGamesUnlocked}
onTutorialClick={() => {
const tutorialSection = document.getElementById('interactive-tutorial');
if (tutorialSection) {
tutorialSection.scrollIntoView({
behavior: 'smooth',
block: 'start',
});
}
}}
// ✅ 1. USAR EL HANDLER CON useCallback Y LÓGICA DE CONTROL ✅
onTechTreeClick={onTechTreeClick}

// ✅ 2. AÑADIR LA PROP DE DESHABILITACIÓN PARA CONTROLAR EL BOTÓN Y EL TOOLTIP ✅
isTechTreeDisabled={showTechTree || isTechTreeProcessing}

isConnected={isConnected}
isLoadingAccount={firebaseLoading && !isInitialized}
/>

{/* Demo Cards Section - with fade-in animation */}
Expand Down Expand Up @@ -430,24 +470,30 @@ export default function HomePageContent() {
demoTitle={DEMO_CARDS.find(d => d.id === activeDemo)?.title || 'Demo'}
demoDescription={DEMO_CARDS.find(d => d.id === activeDemo)?.subtitle || 'Demo Description'}
estimatedTime={
activeDemo === 'hello-milestone' ? 1 : activeDemo === 'dispute-resolution' ? 3 : 2
activeDemo === 'hello-milestone' ? 1 : activeDemo === 'dispute-resolution' ? 3 : 2
}
demoColor={DEMO_CARDS.find(d => d.id === activeDemo)?.color || 'from-brand-500 to-brand-400'}
onDemoComplete={handleDemoComplete}
>

{...(activeTx ? { transaction: activeTx } : {})}
>

{activeDemo === 'hello-milestone' && (
<HelloMilestoneDemo onDemoComplete={handleDemoComplete} />
<HelloMilestoneDemo onDemoComplete={handleDemoComplete} />
)}
{activeDemo === 'dispute-resolution' && <DisputeResolutionDemo />}
{activeDemo === 'milestone-voting' && <MilestoneVotingDemo />}
{activeDemo === 'micro-marketplace' && (
<MicroTaskMarketplaceDemo onDemoComplete={handleDemoComplete} />
<MicroTaskMarketplaceDemo onDemoComplete={handleDemoComplete} />
)}
</ImmersiveDemoModal>
</ImmersiveDemoModal>
)}

{/* Tech Tree Modal */}
<TechTreeModal isOpen={showTechTree} onClose={() => setShowTechTree(false)} />
<TechTreeModal
isOpen={showTechTree}
onClose={onCloseTechTree}
/>

{/* Authentication Modal */}
<AuthModal
Expand Down
157 changes: 94 additions & 63 deletions components/demos/HelloMilestoneDemo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,34 +102,48 @@ export const HelloMilestoneDemo = ({
return hash;
};

// Helper function to create explorer URLs
const createExplorerUrls = (txHash: string, isRealTransaction: boolean = false) => {
// Only create explorer URLs for real blockchain transactions
if (!isRealTransaction) {
return {
explorerUrl: null,
stellarExpertUrl: null,
horizonUrl: null,
accountUrl: walletData?.publicKey
? `${API_ENDPOINTS.STELLAR_EXPERT.BASE_URL}/testnet/account/${walletData.publicKey}`
: null,
};
}

const isTestnet = walletData?.network === 'TESTNET' || !walletData?.isMainnet;
const networkSuffix = isTestnet ? 'testnet' : 'public';
// Lógica de red, definida una sola vez y accesible en toda la función
const isTestnet = walletData?.network === 'TESTNET' || !walletData?.isMainnet;
const networkSuffix = isTestnet ? 'testnet' : 'public';

// 1. Caso de SIMULACIÓN (isRealTransaction es false)
// Devuelve valores nulos/simulados si no es una transacción real
if (!isRealTransaction) {
return {
explorerUrl: `${API_ENDPOINTS.STELLAR_EXPERT.BASE_URL}/${networkSuffix}/tx/${txHash}`,
stellarExpertUrl: `${API_ENDPOINTS.STELLAR_EXPERT.BASE_URL}/${networkSuffix}/tx/${txHash}`,
horizonUrl: isTestnet
? `${API_ENDPOINTS.HORIZON.TESTNET}/transactions/${txHash}`
: `${API_ENDPOINTS.HORIZON.MAINNET}/transactions/${txHash}`,
explorerUrl: null, // URL nula para simulación
stellarExpertUrl: null, // URL nula para simulación
horizonUrl: null, // URL nula para simulación
accountUrl: walletData?.publicKey
// PERO SÍ genera la URL de cuenta para simulación si es necesario
? `${API_ENDPOINTS.STELLAR_EXPERT.BASE_URL}/${networkSuffix}/account/${walletData.publicKey}`
: null,
};
}

// 2. Caso de TRANSACCIÓN REAL (isRealTransaction es true)
// Ahora, construimos las URLs reales usando las constantes globales:
return {
// URL de Horizon (explorador genérico)
explorerUrl: isTestnet
? `${API_ENDPOINTS.HORIZON.TESTNET}/transactions/${txHash}`
: `${API_ENDPOINTS.HORIZON.MAINNET}/transactions/${txHash}`,

// URL de Stellar Expert (La clave que lee el modal)
stellarExpertUrl: `${API_ENDPOINTS.STELLAR_EXPERT.BASE_URL}/${networkSuffix}/tx/${txHash}`,

// Otra referencia a Horizon (si es necesaria)
horizonUrl: isTestnet
? `${API_ENDPOINTS.HORIZON.TESTNET}/transactions/${txHash}`
: `${API_ENDPOINTS.HORIZON.MAINNET}/transactions/${txHash}`,

// URL de Cuenta de Stellar Expert
accountUrl: walletData?.publicKey
? `${API_ENDPOINTS.STELLAR_EXPERT.BASE_URL}/${networkSuffix}/account/${walletData.publicKey}`
: null,
};
};


// Check if demo was already completed
const isCompleted = (() => {
Expand Down Expand Up @@ -660,48 +674,65 @@ export const HelloMilestoneDemo = ({
const realTxHash = transactionResult.hash;

// Create proper explorer URLs for the real transaction
const realUrls = createExplorerUrls(realTxHash);

// Update transaction details with real hash
setTransactionDetails(prev => ({
...prev,
[txHash]: {
...prev[txHash],
hash: realTxHash,
explorerUrl: realUrls.explorerUrl,
stellarExpertUrl: realUrls.stellarExpertUrl,
},
}));

// Update the transaction history with the real hash
updateTransaction(
txHash,
'success',
`Escrow initialized successfully! Real transaction: ${realTxHash}`
);
addTransaction({
hash: realTxHash,
status: 'success',
message: 'Escrow initialized successfully!',
type: 'escrow',
demoId: 'hello-milestone',
amount: '10 USDC',
asset: 'USDC',
});

// Clear timeout and mark as successful
clearTimeout(timeout);
setTransactionTimeouts(prev => {
const newTimeouts = { ...prev };
delete newTimeouts[txHash];
return newTimeouts;
});

updateTransactionStatusAndCheckCompletion(
txHash,
'success',
`Real blockchain transaction completed! Hash: ${realTxHash}`
);
const realUrls = createExplorerUrls(realTxHash, true);
setTransactionDetails(prev => {
const simulatedEntry = prev[txHash];

// 2. Preparamos una copia de trabajo del estado anterior.
const newState = { ...prev };

// 3. Eliminamos explícitamente la clave antigua (hash simulado) de la copia.
if (simulatedEntry) {
delete newState[txHash];
}

// 4. Crea la entrada REAL
const realEntry = {
// Usa los datos simulados como base, con fallback a objeto vacío
...(simulatedEntry || {}),

// ⬇️ ¡SOBRESCRITURA DE DATOS VITALES CON NULLISH COALESCING! ⬇️
hash: realTxHash, // HASH REAL
// Usamos ?. y el fallback a null si realUrls?.explorerUrl es nulo/undefined
explorerUrl: realUrls?.explorerUrl ?? null,
stellarExpertUrl: realUrls?.stellarExpertUrl ?? null, // URL REAL
};

// 5. Añade la entrada REAL con la clave REAL
newState[realTxHash] = realEntry;

// 6. Devuelve el nuevo estado.
return newState;
});
setTimeout(() => {

// 1. Update the transaction history with the real hash
updateTransaction(
realTxHash, // ✅ HASH REAL
'success',
`Escrow initialized successfully! Real transaction: ${realTxHash}`
);

// 2. Add transaction to the history (mantener para asegurar el contexto)
addTransaction({
hash: realTxHash,
status: 'success',
message: 'Escrow initialized successfully!',
type: 'escrow',
demoId: 'hello-milestone',
amount: '10 USDC',
asset: 'USDC',

});

// 3. LLAMAR A LA FUNCIÓN DE COMPLETACIÓN FINAL CON LOS 3 ARGUMENTOS CORREGIDOS
updateTransactionStatusAndCheckCompletion(
realTxHash,
'success',
'Escrow initialized successfully!'
);

}, 3000); // ⬅️ Retardo de 3 segundos

// Clear from pending transactions
setPendingTransactions(prev => {
Expand Down Expand Up @@ -1420,7 +1451,7 @@ export const HelloMilestoneDemo = ({
<div className='flex items-center space-x-2'>
{/* Auto-completion countdown */}
<div className='text-xs text-blue-300 bg-blue-500/20 px-2 py-1 rounded flex items-center space-x-1'>
<div className='w-2 h-2 bg-blue-400 rounded-full animate-pulse'></div>
<div className='w-2 h-2 bg-blue-400 rounded-full '></div>
<span>
Auto-completing in {autoCompleteCountdown['initialize'] || 5}s...
</span>
Expand Down
Loading
Loading