Skip to content

Commit 37bc127

Browse files
Merge pull request #13 from stellar-nexus-experience/barran33_dev
feat: Integrate Notifications for Demos/Quests and Fix Tech Tree Tooltip Interference
2 parents 8cd9cec + 379f2c1 commit 37bc127

File tree

12 files changed

+437
-142
lines changed

12 files changed

+437
-142
lines changed

analytics/components/AccountAnalytics.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export const AccountAnalytics: React.FC<AccountAnalyticsProps> = ({ className =
136136
if (loading) {
137137
return (
138138
<div className={`p-6 ${themeClasses.container} ${className}`}>
139-
<div className='animate-pulse space-y-4'>
139+
<div className='space-y-4'>
140140
<div
141141
className={`h-6 ${theme === 'light' ? 'bg-gray-200' : 'bg-white/10'} rounded w-1/3`}
142142
></div>

app/docs/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ export default function DocsPage() {
268268

269269
{/* Powered by Trustless Work */}
270270
<div className='text-center mt-4'>
271-
<p className='text-brand-300/70 text-sm font-medium animate-pulse'>
271+
<p className='text-brand-300/70 text-sm font-medium '>
272272
Powered by{' '}
273273
<span className='text-brand-200 font-semibold'>Trustless Work</span>
274274
</p>

app/home/page.tsx

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import { AccountStatusIndicator } from '@/components/ui/AccountStatusIndicator';
2525
import { LeaderboardSidebar } from '@/components/ui/LeaderboardSidebar';
2626
import { QuestAndReferralSection } from '@/components/ui/quest/QuestAndReferralSection';
2727
import { TOP_BADGES } from '@/utils/constants/demos';
28+
import { useTransactionHistory } from '@/contexts/data/TransactionContext';
29+
import React, { useCallback } from 'react';
2830
import {
2931
DemoSelector,
3032
HeroSection,
@@ -35,7 +37,9 @@ import { DEMO_CARDS } from '@/utils/constants/demos';
3537

3638
export default function HomePageContent() {
3739
const { isConnected } = useGlobalWallet();
40+
const { transactions } = useTransactionHistory();
3841
// Removed unused authentication variables
42+
const activeTx = transactions ? Object.values(transactions).slice(-1)[0] : null;
3943
const {
4044
account,
4145
demoStats,
@@ -86,6 +90,8 @@ export default function HomePageContent() {
8690
} | null>(null);
8791
const [showImmersiveDemo, setShowImmersiveDemo] = useState(false);
8892
const [showTechTree, setShowTechTree] = useState(false);
93+
//Nuevo estado de Contol.
94+
const [isTechTreeProcessing, setIsTechTreeProcessing] = useState(false);
8995

9096
// Authentication modals
9197
const [showAuthModal, setShowAuthModal] = useState(false);
@@ -111,6 +117,7 @@ export default function HomePageContent() {
111117
const handleOpenLeaderboard = () => {
112118
setLeaderboardSidebarOpen(true);
113119
};
120+
114121

115122
window.addEventListener('walletSidebarToggle', handleWalletSidebarToggle as EventListener);
116123
window.addEventListener('openUserProfile', handleOpenUserProfile);
@@ -198,8 +205,34 @@ export default function HomePageContent() {
198205
const handleFeedbackClose = () => {
199206
setShowFeedbackModal(false);
200207
setFeedbackDemoData(null);
208+
201209
};
202210

211+
const onTechTreeClick = useCallback(() => {
212+
if (showTechTree || isTechTreeProcessing) {
213+
// Si ya está abierto o en proceso, ignora el clic (Idempotencia)
214+
return;
215+
}
216+
217+
// 1. Establecer el estado de procesamiento inmediatamente
218+
setIsTechTreeProcessing(true);
219+
220+
// 2. Abrir el modal
221+
setShowTechTree(true);
222+
223+
224+
setTimeout(() => {
225+
setIsTechTreeProcessing(false);
226+
}, 500); // 500ms recomendado para mayor seguridad
227+
228+
}, [showTechTree, isTechTreeProcessing])
229+
230+
const onCloseTechTree = useCallback(() => {
231+
setShowTechTree(false);
232+
setIsTechTreeProcessing(false); // Asegurar que se restablezca el estado de procesamiento
233+
}, []);
234+
235+
203236
return (
204237
<div className='min-h-screen bg-gradient-to-br from-neutral-900 via-brand-900 to-neutral-900 relative overflow-hidden'>
205238
{/* Structured Data for SEO */}
@@ -264,22 +297,29 @@ export default function HomePageContent() {
264297
walletSidebarOpen && walletExpanded ? 'mr-96' : walletSidebarOpen ? 'mr-20' : 'mr-0'
265298
} ${!walletSidebarOpen ? 'pb-32' : 'pb-8'}`}
266299
>
300+
301+
267302
{/* Hero Section */}
268303
<HeroSection
269-
isVideoPlaying={false}
270-
miniGamesUnlocked={miniGamesUnlocked}
271-
onTutorialClick={() => {
272-
const tutorialSection = document.getElementById('interactive-tutorial');
273-
if (tutorialSection) {
274-
tutorialSection.scrollIntoView({
275-
behavior: 'smooth',
276-
block: 'start',
277-
});
278-
}
279-
}}
280-
onTechTreeClick={() => setShowTechTree(true)}
281-
isConnected={isConnected}
282-
isLoadingAccount={firebaseLoading && !isInitialized}
304+
isVideoPlaying={false}
305+
miniGamesUnlocked={miniGamesUnlocked}
306+
onTutorialClick={() => {
307+
const tutorialSection = document.getElementById('interactive-tutorial');
308+
if (tutorialSection) {
309+
tutorialSection.scrollIntoView({
310+
behavior: 'smooth',
311+
block: 'start',
312+
});
313+
}
314+
}}
315+
// ✅ 1. USAR EL HANDLER CON useCallback Y LÓGICA DE CONTROL ✅
316+
onTechTreeClick={onTechTreeClick}
317+
318+
// ✅ 2. AÑADIR LA PROP DE DESHABILITACIÓN PARA CONTROLAR EL BOTÓN Y EL TOOLTIP ✅
319+
isTechTreeDisabled={showTechTree || isTechTreeProcessing}
320+
321+
isConnected={isConnected}
322+
isLoadingAccount={firebaseLoading && !isInitialized}
283323
/>
284324

285325
{/* Demo Cards Section - with fade-in animation */}
@@ -430,24 +470,30 @@ export default function HomePageContent() {
430470
demoTitle={DEMO_CARDS.find(d => d.id === activeDemo)?.title || 'Demo'}
431471
demoDescription={DEMO_CARDS.find(d => d.id === activeDemo)?.subtitle || 'Demo Description'}
432472
estimatedTime={
433-
activeDemo === 'hello-milestone' ? 1 : activeDemo === 'dispute-resolution' ? 3 : 2
473+
activeDemo === 'hello-milestone' ? 1 : activeDemo === 'dispute-resolution' ? 3 : 2
434474
}
435475
demoColor={DEMO_CARDS.find(d => d.id === activeDemo)?.color || 'from-brand-500 to-brand-400'}
436476
onDemoComplete={handleDemoComplete}
437-
>
477+
478+
{...(activeTx ? { transaction: activeTx } : {})}
479+
>
480+
438481
{activeDemo === 'hello-milestone' && (
439-
<HelloMilestoneDemo onDemoComplete={handleDemoComplete} />
482+
<HelloMilestoneDemo onDemoComplete={handleDemoComplete} />
440483
)}
441484
{activeDemo === 'dispute-resolution' && <DisputeResolutionDemo />}
442485
{activeDemo === 'milestone-voting' && <MilestoneVotingDemo />}
443486
{activeDemo === 'micro-marketplace' && (
444-
<MicroTaskMarketplaceDemo onDemoComplete={handleDemoComplete} />
487+
<MicroTaskMarketplaceDemo onDemoComplete={handleDemoComplete} />
445488
)}
446-
</ImmersiveDemoModal>
489+
</ImmersiveDemoModal>
447490
)}
448491

449492
{/* Tech Tree Modal */}
450-
<TechTreeModal isOpen={showTechTree} onClose={() => setShowTechTree(false)} />
493+
<TechTreeModal
494+
isOpen={showTechTree}
495+
onClose={onCloseTechTree}
496+
/>
451497

452498
{/* Authentication Modal */}
453499
<AuthModal

components/demos/HelloMilestoneDemo.tsx

Lines changed: 94 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -102,34 +102,48 @@ export const HelloMilestoneDemo = ({
102102
return hash;
103103
};
104104

105-
// Helper function to create explorer URLs
106105
const createExplorerUrls = (txHash: string, isRealTransaction: boolean = false) => {
107-
// Only create explorer URLs for real blockchain transactions
108-
if (!isRealTransaction) {
109-
return {
110-
explorerUrl: null,
111-
stellarExpertUrl: null,
112-
horizonUrl: null,
113-
accountUrl: walletData?.publicKey
114-
? `${API_ENDPOINTS.STELLAR_EXPERT.BASE_URL}/testnet/account/${walletData.publicKey}`
115-
: null,
116-
};
117-
}
118-
119-
const isTestnet = walletData?.network === 'TESTNET' || !walletData?.isMainnet;
120-
const networkSuffix = isTestnet ? 'testnet' : 'public';
106+
// Lógica de red, definida una sola vez y accesible en toda la función
107+
const isTestnet = walletData?.network === 'TESTNET' || !walletData?.isMainnet;
108+
const networkSuffix = isTestnet ? 'testnet' : 'public';
121109

110+
// 1. Caso de SIMULACIÓN (isRealTransaction es false)
111+
// Devuelve valores nulos/simulados si no es una transacción real
112+
if (!isRealTransaction) {
122113
return {
123-
explorerUrl: `${API_ENDPOINTS.STELLAR_EXPERT.BASE_URL}/${networkSuffix}/tx/${txHash}`,
124-
stellarExpertUrl: `${API_ENDPOINTS.STELLAR_EXPERT.BASE_URL}/${networkSuffix}/tx/${txHash}`,
125-
horizonUrl: isTestnet
126-
? `${API_ENDPOINTS.HORIZON.TESTNET}/transactions/${txHash}`
127-
: `${API_ENDPOINTS.HORIZON.MAINNET}/transactions/${txHash}`,
114+
explorerUrl: null, // URL nula para simulación
115+
stellarExpertUrl: null, // URL nula para simulación
116+
horizonUrl: null, // URL nula para simulación
128117
accountUrl: walletData?.publicKey
118+
// PERO SÍ genera la URL de cuenta para simulación si es necesario
129119
? `${API_ENDPOINTS.STELLAR_EXPERT.BASE_URL}/${networkSuffix}/account/${walletData.publicKey}`
130120
: null,
131121
};
122+
}
123+
124+
// 2. Caso de TRANSACCIÓN REAL (isRealTransaction es true)
125+
// Ahora, construimos las URLs reales usando las constantes globales:
126+
return {
127+
// URL de Horizon (explorador genérico)
128+
explorerUrl: isTestnet
129+
? `${API_ENDPOINTS.HORIZON.TESTNET}/transactions/${txHash}`
130+
: `${API_ENDPOINTS.HORIZON.MAINNET}/transactions/${txHash}`,
131+
132+
// URL de Stellar Expert (La clave que lee el modal)
133+
stellarExpertUrl: `${API_ENDPOINTS.STELLAR_EXPERT.BASE_URL}/${networkSuffix}/tx/${txHash}`,
134+
135+
// Otra referencia a Horizon (si es necesaria)
136+
horizonUrl: isTestnet
137+
? `${API_ENDPOINTS.HORIZON.TESTNET}/transactions/${txHash}`
138+
: `${API_ENDPOINTS.HORIZON.MAINNET}/transactions/${txHash}`,
139+
140+
// URL de Cuenta de Stellar Expert
141+
accountUrl: walletData?.publicKey
142+
? `${API_ENDPOINTS.STELLAR_EXPERT.BASE_URL}/${networkSuffix}/account/${walletData.publicKey}`
143+
: null,
132144
};
145+
};
146+
133147

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

662676
// Create proper explorer URLs for the real transaction
663-
const realUrls = createExplorerUrls(realTxHash);
664-
665-
// Update transaction details with real hash
666-
setTransactionDetails(prev => ({
667-
...prev,
668-
[txHash]: {
669-
...prev[txHash],
670-
hash: realTxHash,
671-
explorerUrl: realUrls.explorerUrl,
672-
stellarExpertUrl: realUrls.stellarExpertUrl,
673-
},
674-
}));
675-
676-
// Update the transaction history with the real hash
677-
updateTransaction(
678-
txHash,
679-
'success',
680-
`Escrow initialized successfully! Real transaction: ${realTxHash}`
681-
);
682-
addTransaction({
683-
hash: realTxHash,
684-
status: 'success',
685-
message: 'Escrow initialized successfully!',
686-
type: 'escrow',
687-
demoId: 'hello-milestone',
688-
amount: '10 USDC',
689-
asset: 'USDC',
690-
});
691-
692-
// Clear timeout and mark as successful
693-
clearTimeout(timeout);
694-
setTransactionTimeouts(prev => {
695-
const newTimeouts = { ...prev };
696-
delete newTimeouts[txHash];
697-
return newTimeouts;
698-
});
699-
700-
updateTransactionStatusAndCheckCompletion(
701-
txHash,
702-
'success',
703-
`Real blockchain transaction completed! Hash: ${realTxHash}`
704-
);
677+
const realUrls = createExplorerUrls(realTxHash, true);
678+
setTransactionDetails(prev => {
679+
const simulatedEntry = prev[txHash];
680+
681+
// 2. Preparamos una copia de trabajo del estado anterior.
682+
const newState = { ...prev };
683+
684+
// 3. Eliminamos explícitamente la clave antigua (hash simulado) de la copia.
685+
if (simulatedEntry) {
686+
delete newState[txHash];
687+
}
688+
689+
// 4. Crea la entrada REAL
690+
const realEntry = {
691+
// Usa los datos simulados como base, con fallback a objeto vacío
692+
...(simulatedEntry || {}),
693+
694+
// ⬇️ ¡SOBRESCRITURA DE DATOS VITALES CON NULLISH COALESCING! ⬇️
695+
hash: realTxHash, // HASH REAL
696+
// Usamos ?. y el fallback a null si realUrls?.explorerUrl es nulo/undefined
697+
explorerUrl: realUrls?.explorerUrl ?? null,
698+
stellarExpertUrl: realUrls?.stellarExpertUrl ?? null, // URL REAL
699+
};
700+
701+
// 5. Añade la entrada REAL con la clave REAL
702+
newState[realTxHash] = realEntry;
703+
704+
// 6. Devuelve el nuevo estado.
705+
return newState;
706+
});
707+
setTimeout(() => {
708+
709+
// 1. Update the transaction history with the real hash
710+
updateTransaction(
711+
realTxHash, // ✅ HASH REAL
712+
'success',
713+
`Escrow initialized successfully! Real transaction: ${realTxHash}`
714+
);
715+
716+
// 2. Add transaction to the history (mantener para asegurar el contexto)
717+
addTransaction({
718+
hash: realTxHash,
719+
status: 'success',
720+
message: 'Escrow initialized successfully!',
721+
type: 'escrow',
722+
demoId: 'hello-milestone',
723+
amount: '10 USDC',
724+
asset: 'USDC',
725+
726+
});
727+
728+
// 3. LLAMAR A LA FUNCIÓN DE COMPLETACIÓN FINAL CON LOS 3 ARGUMENTOS CORREGIDOS
729+
updateTransactionStatusAndCheckCompletion(
730+
realTxHash,
731+
'success',
732+
'Escrow initialized successfully!'
733+
);
734+
735+
}, 3000); // ⬅️ Retardo de 3 segundos
705736

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

0 commit comments

Comments
 (0)