mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-06-14 18:21:01 +00:00
fix(web): live collator/nominator counts after AHM + reliable B2B redirect (#15)
Staking migrated to Asset Hub (AHM), but the landing page still read nominators from the relay (api.query.staking.counterForNominators), which is now empty there — so the count showed '—'. Collators were read from collatorSelection.candidates (empty; collators are invulnerables) and only on Asset Hub, missing the People chain set. - Nominators: query Asset Hub staking.counterForNominators (verified 30). - Collators: count collatorSelection.invulnerables on both Asset Hub and People chain (2 + 2), tracked per-chain and summed. - NetworkStats.tsx already used the correct sources; this aligns the landing page with it. B2B button (/bereketli SSO interstitial): if there is no Supabase session or the token exchange fails, redirect to https://bereketli.pezkiwi.app instead of stranding the user on app.pezkuwichain.io/bereketli. (The backend CORS allowlist was also missing app.pezkuwichain.io; fixed server-side so the SSO exchange itself now succeeds.)
This commit is contained in:
@@ -15,6 +15,8 @@ interface ChainStats {
|
|||||||
validators: number;
|
validators: number;
|
||||||
nominators: number;
|
nominators: number;
|
||||||
collators: number;
|
collators: number;
|
||||||
|
collatorsAH: number;
|
||||||
|
collatorsPeople: number;
|
||||||
activeProposals: number;
|
activeProposals: number;
|
||||||
totalVoters: number;
|
totalVoters: number;
|
||||||
citizenCount: number;
|
citizenCount: number;
|
||||||
@@ -325,6 +327,7 @@ const LandingPageDesktop: React.FC = () => {
|
|||||||
const [stats, setStats] = useState<ChainStats>({
|
const [stats, setStats] = useState<ChainStats>({
|
||||||
latestBlock: 0, finalizedBlock: 0, blockHash: '',
|
latestBlock: 0, finalizedBlock: 0, blockHash: '',
|
||||||
peers: 0, validators: 0, nominators: 0, collators: 0,
|
peers: 0, validators: 0, nominators: 0, collators: 0,
|
||||||
|
collatorsAH: 0, collatorsPeople: 0,
|
||||||
activeProposals: 0, totalVoters: 0, citizenCount: 0,
|
activeProposals: 0, totalVoters: 0, citizenCount: 0,
|
||||||
tokensStakedPct: '—',
|
tokensStakedPct: '—',
|
||||||
});
|
});
|
||||||
@@ -417,12 +420,7 @@ const LandingPageDesktop: React.FC = () => {
|
|||||||
const validators = sessionVals.length;
|
const validators = sessionVals.length;
|
||||||
setStats(prev => ({ ...prev, activeProposals, totalVoters, validators }));
|
setStats(prev => ({ ...prev, activeProposals, totalVoters, validators }));
|
||||||
} catch {}
|
} catch {}
|
||||||
|
// Nominators/staking migrated to Asset Hub — counted in the Asset Hub effect below.
|
||||||
try {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const nomCount = await (api.query.staking as any).counterForNominators?.();
|
|
||||||
if (nomCount != null) setStats(prev => ({ ...prev, nominators: nomCount.toNumber() }));
|
|
||||||
} catch {}
|
|
||||||
})();
|
})();
|
||||||
}, [api, isApiReady]);
|
}, [api, isApiReady]);
|
||||||
|
|
||||||
@@ -448,10 +446,18 @@ const LandingPageDesktop: React.FC = () => {
|
|||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
|
// Nominators live on Asset Hub after the staking migration (AHM).
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const collCount = await (assetHubApi.query.collatorSelection as any)?.candidates?.();
|
const nomCount = await (assetHubApi.query.staking as any)?.counterForNominators?.();
|
||||||
if (collCount != null) setStats(prev => ({ ...prev, collators: collCount.length }));
|
if (nomCount != null) setStats(prev => ({ ...prev, nominators: nomCount.toNumber() }));
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
// Collators are the invulnerable set (not staking candidates, which are empty).
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const inv = await (assetHubApi.query.collatorSelection as any)?.invulnerables?.();
|
||||||
|
if (inv != null) setStats(prev => ({ ...prev, collatorsAH: inv.length, collators: inv.length + prev.collatorsPeople }));
|
||||||
} catch {}
|
} catch {}
|
||||||
})();
|
})();
|
||||||
}, [assetHubApi, isAssetHubReady]);
|
}, [assetHubApi, isAssetHubReady]);
|
||||||
@@ -465,6 +471,13 @@ const LandingPageDesktop: React.FC = () => {
|
|||||||
const entries = await (peopleApi.query as any).tiki?.citizenNft?.entries?.();
|
const entries = await (peopleApi.query as any).tiki?.citizenNft?.entries?.();
|
||||||
if (entries) setStats(prev => ({ ...prev, citizenCount: entries.length }));
|
if (entries) setStats(prev => ({ ...prev, citizenCount: entries.length }));
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
|
// People Chain also runs invulnerable collators — add them to the total.
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const inv = await (peopleApi.query.collatorSelection as any)?.invulnerables?.();
|
||||||
|
if (inv != null) setStats(prev => ({ ...prev, collatorsPeople: inv.length, collators: prev.collatorsAH + inv.length }));
|
||||||
|
} catch {}
|
||||||
})();
|
})();
|
||||||
}, [peopleApi, isPeopleReady]);
|
}, [peopleApi, isPeopleReady]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { supabase } from '@/lib/supabase';
|
import { supabase } from '@/lib/supabase';
|
||||||
import { Loader2 } from 'lucide-react';
|
import { Loader2 } from 'lucide-react';
|
||||||
@@ -12,7 +12,6 @@ const BEREKETLI_API = `${BEREKETLI_URL}/v1`;
|
|||||||
*/
|
*/
|
||||||
export default function Bereketli() {
|
export default function Bereketli() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [error, setError] = useState('');
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
@@ -20,8 +19,10 @@ export default function Bereketli() {
|
|||||||
const {
|
const {
|
||||||
data: { session },
|
data: { session },
|
||||||
} = await supabase.auth.getSession();
|
} = await supabase.auth.getSession();
|
||||||
|
// Not signed in: skip SSO and send the user to the Bereketli site,
|
||||||
|
// which handles its own login. Never dead-end on this interstitial.
|
||||||
if (!session?.access_token) {
|
if (!session?.access_token) {
|
||||||
setError(t('bereketli.noSession', 'Lütfen önce giriş yapın'));
|
window.location.href = BEREKETLI_URL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,27 +47,14 @@ export default function Bereketli() {
|
|||||||
});
|
});
|
||||||
window.location.href = `${BEREKETLI_URL}/app?auth=${btoa(params.toString())}`;
|
window.location.href = `${BEREKETLI_URL}/app?auth=${btoa(params.toString())}`;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : 'Bağlantı hatası');
|
// SSO failed (expired token, network, etc.) — fall back to the public
|
||||||
|
// Bereketli site instead of stranding the user on app.pezkuwichain.io.
|
||||||
|
if (import.meta.env.DEV) console.warn('Bereketli SSO failed, falling back:', err);
|
||||||
|
window.location.href = BEREKETLI_URL;
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}, [t]);
|
}, [t]);
|
||||||
|
|
||||||
if (error) {
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen bg-gray-950 flex items-center justify-center px-6">
|
|
||||||
<div className="text-center space-y-4">
|
|
||||||
<p className="text-red-400 text-sm">{error}</p>
|
|
||||||
<a
|
|
||||||
href="/"
|
|
||||||
className="inline-block px-4 py-2 bg-green-600 text-white rounded-lg text-sm"
|
|
||||||
>
|
|
||||||
{t('common.backToHome', 'Ana Sayfaya Dön')}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-950 flex items-center justify-center">
|
<div className="min-h-screen bg-gray-950 flex items-center justify-center">
|
||||||
<div className="text-center space-y-3">
|
<div className="text-center space-y-3">
|
||||||
|
|||||||
Reference in New Issue
Block a user