mirror of
https://github.com/pezkuwichain/pezkuwi-telegram-miniapp.git
synced 2026-04-21 23:37:55 +00:00
feat: add Start Tracking and Record Trust Score buttons to Scores tab
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pezkuwi-telegram-miniapp",
|
||||
"version": "1.0.211",
|
||||
"version": "1.0.213",
|
||||
"type": "module",
|
||||
"description": "Pezkuwichain Telegram Mini App - Forum, Announcements, Rewards",
|
||||
"author": "Pezkuwichain Team",
|
||||
|
||||
@@ -134,6 +134,16 @@ const ar: Translations = {
|
||||
signingBlockchain: 'جاري التوقيع على البلوكتشين',
|
||||
citizenshipConfirmed: 'مبروك! أنت الآن مواطن!',
|
||||
citizenshipFailed: 'فشل التأكيد',
|
||||
startTracking: 'بدء التتبع',
|
||||
startTrackingDesc: 'تفعيل تتبع نقاط التخزين',
|
||||
startingTracking: 'جاري بدء التتبع...',
|
||||
trackingStarted: 'بدأ التتبع!',
|
||||
trackingFailed: 'فشل بدء التتبع',
|
||||
recordTrustScore: 'تسجيل نقاط الثقة',
|
||||
recordTrustDesc: 'سجل نقاطك لمكافآت هذه الفترة',
|
||||
recordingTrustScore: 'جاري تسجيل نقاط الثقة...',
|
||||
trustScoreRecorded: 'تم تسجيل نقاط الثقة!',
|
||||
trustScoreRecordFailed: 'فشل تسجيل النقاط',
|
||||
},
|
||||
|
||||
wallet: {
|
||||
|
||||
@@ -135,6 +135,16 @@ const ckb: Translations = {
|
||||
signingBlockchain: 'لەسەر بلۆکچەین واژوو دەکرێت',
|
||||
citizenshipConfirmed: 'پیرۆزبێت! تۆ ئێستا هاوڵاتیت!',
|
||||
citizenshipFailed: 'پشتڕاستکردنەوە سەرنەکەوت',
|
||||
startTracking: 'شوێنکەوتن دەست پێ بکە',
|
||||
startTrackingDesc: 'شوێنکەوتنی خاڵی ستەیکینگ چالاک بکە',
|
||||
startingTracking: 'شوێنکەوتن دەست پێ دەکات...',
|
||||
trackingStarted: 'شوێنکەوتن دەست پێ کرد!',
|
||||
trackingFailed: 'دەست پێکردنی شوێنکەوتن سەرنەکەوت',
|
||||
recordTrustScore: 'خاڵی متمانە تۆمار بکە',
|
||||
recordTrustDesc: 'خاڵەکەت بۆ خەڵاتەکانی ئەم سەردەمە تۆمار بکە',
|
||||
recordingTrustScore: 'خاڵ تۆمار دەکرێت...',
|
||||
trustScoreRecorded: 'خاڵی متمانە تۆمار کرا!',
|
||||
trustScoreRecordFailed: 'تۆمارکردنی خاڵ سەرنەکەوت',
|
||||
},
|
||||
|
||||
wallet: {
|
||||
|
||||
@@ -134,6 +134,16 @@ const en: Translations = {
|
||||
signingBlockchain: 'Signing on blockchain',
|
||||
citizenshipConfirmed: 'Congratulations! You are now a citizen!',
|
||||
citizenshipFailed: 'Confirmation failed',
|
||||
startTracking: 'Start Tracking',
|
||||
startTrackingDesc: 'Enable staking score tracking',
|
||||
startingTracking: 'Starting tracking...',
|
||||
trackingStarted: 'Tracking started!',
|
||||
trackingFailed: 'Failed to start tracking',
|
||||
recordTrustScore: 'Record Trust Score',
|
||||
recordTrustDesc: "Record your score for this epoch's rewards",
|
||||
recordingTrustScore: 'Recording trust score...',
|
||||
trustScoreRecorded: 'Trust score recorded!',
|
||||
trustScoreRecordFailed: 'Failed to record score',
|
||||
},
|
||||
|
||||
wallet: {
|
||||
|
||||
@@ -134,6 +134,16 @@ const fa: Translations = {
|
||||
signingBlockchain: 'در حال امضا روی بلاکچین',
|
||||
citizenshipConfirmed: 'تبریک! شما اکنون شهروند هستید!',
|
||||
citizenshipFailed: 'تایید ناموفق بود',
|
||||
startTracking: 'شروع ردیابی',
|
||||
startTrackingDesc: 'ردیابی امتیاز سهامگذاری را فعال کنید',
|
||||
startingTracking: 'در حال شروع ردیابی...',
|
||||
trackingStarted: 'ردیابی شروع شد!',
|
||||
trackingFailed: 'شروع ردیابی ناموفق بود',
|
||||
recordTrustScore: 'ثبت امتیاز اعتماد',
|
||||
recordTrustDesc: 'امتیاز خود را برای پاداشهای این دوره ثبت کنید',
|
||||
recordingTrustScore: 'در حال ثبت امتیاز اعتماد...',
|
||||
trustScoreRecorded: 'امتیاز اعتماد ثبت شد!',
|
||||
trustScoreRecordFailed: 'ثبت امتیاز ناموفق بود',
|
||||
},
|
||||
|
||||
wallet: {
|
||||
|
||||
@@ -137,8 +137,18 @@ const krd: Translations = {
|
||||
confirmCitizenship: 'Welat\u00eeb\u00fbn\u00ea Pi\u015ftrast Bike',
|
||||
confirmingCitizenship: 'Welat\u00eeb\u00fbn t\u00ea pi\u015ftrastkirin...',
|
||||
signingBlockchain: 'Li blockchain t\u00ea \u00eemzekirin',
|
||||
citizenshipConfirmed: 'P\u00eeroz be! Tu b\u00fby\u00ee welat\u00ee!',
|
||||
citizenshipFailed: 'Pi\u015ftrastkirin biserneket',
|
||||
citizenshipConfirmed: 'Pîroz be! Tu bûyî welatî!',
|
||||
citizenshipFailed: 'Piştrastkirin biserneket',
|
||||
startTracking: 'Şopandinê Dest Pê Bike',
|
||||
startTrackingDesc: 'Şopandina pûana stakingê çalak bike',
|
||||
startingTracking: 'Şopandin tê destpêkirin...',
|
||||
trackingStarted: 'Şopandin dest pê kir!',
|
||||
trackingFailed: 'Destpêkirina şopandinê biserneket',
|
||||
recordTrustScore: 'Pûana Pêbaweriyê Tomar Bike',
|
||||
recordTrustDesc: 'Pûana xwe ji bo xelatên vê serdemê tomar bike',
|
||||
recordingTrustScore: 'Pûan tê tomarkirin...',
|
||||
trustScoreRecorded: 'Pûana pêbaweriyê hat tomarkirin!',
|
||||
trustScoreRecordFailed: 'Tomarkirina pûanê biserneket',
|
||||
},
|
||||
|
||||
wallet: {
|
||||
|
||||
@@ -132,8 +132,18 @@ const tr: Translations = {
|
||||
confirmCitizenship: 'Vatanda\u015fl\u0131\u011f\u0131 Onayla',
|
||||
confirmingCitizenship: 'Vatanda\u015fl\u0131k onaylan\u0131yor...',
|
||||
signingBlockchain: "Blockchain'de imzalan\u0131yor",
|
||||
citizenshipConfirmed: 'Tebrikler! Art\u0131k vatanda\u015fs\u0131n\u0131z!',
|
||||
citizenshipFailed: 'Onay ba\u015far\u0131s\u0131z oldu',
|
||||
citizenshipConfirmed: 'Tebrikler! Artık vatandaşsınız!',
|
||||
citizenshipFailed: 'Onay başarısız oldu',
|
||||
startTracking: 'Takibi Başlat',
|
||||
startTrackingDesc: 'Staking puan takibini etkinleştirin',
|
||||
startingTracking: 'Takip başlatılıyor...',
|
||||
trackingStarted: 'Takip başlatıldı!',
|
||||
trackingFailed: 'Takip başlatılamadı',
|
||||
recordTrustScore: 'Güven Puanını Kaydet',
|
||||
recordTrustDesc: 'Bu dönemin ödülleri için puanınızı kaydedin',
|
||||
recordingTrustScore: 'Güven puanı kaydediliyor...',
|
||||
trustScoreRecorded: 'Güven puanı kaydedildi!',
|
||||
trustScoreRecordFailed: 'Puan kaydedilemedi',
|
||||
},
|
||||
|
||||
wallet: {
|
||||
|
||||
@@ -136,6 +136,16 @@ export interface Translations {
|
||||
signingBlockchain: string;
|
||||
citizenshipConfirmed: string;
|
||||
citizenshipFailed: string;
|
||||
startTracking: string;
|
||||
startTrackingDesc: string;
|
||||
startingTracking: string;
|
||||
trackingStarted: string;
|
||||
trackingFailed: string;
|
||||
recordTrustScore: string;
|
||||
recordTrustDesc: string;
|
||||
recordingTrustScore: string;
|
||||
trustScoreRecorded: string;
|
||||
trustScoreRecordFailed: string;
|
||||
};
|
||||
|
||||
// Wallet section
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
import type { ApiPromise } from '@pezkuwi/api';
|
||||
import type { KeyringPair } from '@pezkuwi/keyring/types';
|
||||
|
||||
// ========================================
|
||||
// TYPE DEFINITIONS
|
||||
@@ -425,6 +426,130 @@ export async function getPerwerdeScore(
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// SCORE TRACKING & RECORDING (People Chain Extrinsics)
|
||||
// ========================================
|
||||
|
||||
/**
|
||||
* Start staking score tracking on People Chain.
|
||||
* Calls stakingScore.startScoreTracking() - no arguments.
|
||||
* This enables the noter to cache staking details.
|
||||
*/
|
||||
export async function startScoreTracking(
|
||||
peopleApi: ApiPromise,
|
||||
keypair: KeyringPair
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const tx = peopleApi.tx as any;
|
||||
if (!tx?.stakingScore?.startScoreTracking) {
|
||||
return { success: false, error: 'stakingScore pallet not available' };
|
||||
}
|
||||
|
||||
const result = await new Promise<{ success: boolean; error?: string }>((resolve) => {
|
||||
tx.stakingScore
|
||||
.startScoreTracking()
|
||||
.signAndSend(
|
||||
keypair,
|
||||
{ nonce: -1 },
|
||||
({
|
||||
status,
|
||||
dispatchError,
|
||||
}: {
|
||||
status: {
|
||||
isInBlock: boolean;
|
||||
isFinalized: boolean;
|
||||
};
|
||||
dispatchError?: { isModule: boolean; asModule: unknown; toString: () => string };
|
||||
}) => {
|
||||
if (status.isInBlock || status.isFinalized) {
|
||||
if (dispatchError) {
|
||||
let errorMessage = 'startScoreTracking failed';
|
||||
if (dispatchError.isModule) {
|
||||
const decoded = peopleApi.registry.findMetaError(
|
||||
dispatchError.asModule as Parameters<typeof peopleApi.registry.findMetaError>[0]
|
||||
);
|
||||
errorMessage = `${decoded.section}.${decoded.name}`;
|
||||
}
|
||||
resolve({ success: false, error: errorMessage });
|
||||
return;
|
||||
}
|
||||
resolve({ success: true });
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch((error: Error) => resolve({ success: false, error: error.message }));
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record trust score for current epoch on People Chain.
|
||||
* Calls pezRewards.recordTrustScore() - no arguments.
|
||||
* Required for PEZ epoch rewards.
|
||||
*/
|
||||
export async function recordTrustScore(
|
||||
peopleApi: ApiPromise,
|
||||
keypair: KeyringPair
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const tx = peopleApi.tx as any;
|
||||
if (!tx?.pezRewards?.recordTrustScore) {
|
||||
return { success: false, error: 'pezRewards pallet not available' };
|
||||
}
|
||||
|
||||
const result = await new Promise<{ success: boolean; error?: string }>((resolve) => {
|
||||
tx.pezRewards
|
||||
.recordTrustScore()
|
||||
.signAndSend(
|
||||
keypair,
|
||||
{ nonce: -1 },
|
||||
({
|
||||
status,
|
||||
dispatchError,
|
||||
}: {
|
||||
status: {
|
||||
isInBlock: boolean;
|
||||
isFinalized: boolean;
|
||||
};
|
||||
dispatchError?: { isModule: boolean; asModule: unknown; toString: () => string };
|
||||
}) => {
|
||||
if (status.isInBlock || status.isFinalized) {
|
||||
if (dispatchError) {
|
||||
let errorMessage = 'recordTrustScore failed';
|
||||
if (dispatchError.isModule) {
|
||||
const decoded = peopleApi.registry.findMetaError(
|
||||
dispatchError.asModule as Parameters<typeof peopleApi.registry.findMetaError>[0]
|
||||
);
|
||||
errorMessage = `${decoded.section}.${decoded.name}`;
|
||||
}
|
||||
resolve({ success: false, error: errorMessage });
|
||||
return;
|
||||
}
|
||||
resolve({ success: true });
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch((error: Error) => resolve({ success: false, error: error.message }));
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// COMPREHENSIVE SCORE FETCHING
|
||||
// ========================================
|
||||
|
||||
@@ -21,6 +21,8 @@ import {
|
||||
Target,
|
||||
Sparkles,
|
||||
GraduationCap,
|
||||
Play,
|
||||
PenTool,
|
||||
} from 'lucide-react';
|
||||
import { cn, formatAddress } from '@/lib/utils';
|
||||
import { useTelegram } from '@/hooks/useTelegram';
|
||||
@@ -32,6 +34,8 @@ import { useTranslation } from '@/i18n';
|
||||
import {
|
||||
getAllScores,
|
||||
getStakingScoreStatus,
|
||||
startScoreTracking,
|
||||
recordTrustScore,
|
||||
formatDuration,
|
||||
getScoreColor,
|
||||
getScoreRating,
|
||||
@@ -72,6 +76,8 @@ export function RewardsSection() {
|
||||
const [scoresLoading, setScoresLoading] = useState(false);
|
||||
const [citizenshipStatus, setCitizenshipStatus] = useState<CitizenshipStatus>('NotStarted');
|
||||
const [showConfirmAnimation, setShowConfirmAnimation] = useState(false);
|
||||
const [showTrackingAnimation, setShowTrackingAnimation] = useState(false);
|
||||
const [trackingAnimationText, setTrackingAnimationText] = useState('');
|
||||
|
||||
// Check activity status
|
||||
const checkActivityStatus = useCallback(() => {
|
||||
@@ -168,6 +174,52 @@ export function RewardsSection() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleStartTracking = async () => {
|
||||
if (!peopleApi || !keypair) return;
|
||||
setTrackingAnimationText(t('rewards.startingTracking'));
|
||||
setShowTrackingAnimation(true);
|
||||
hapticImpact('medium');
|
||||
try {
|
||||
const result = await startScoreTracking(peopleApi, keypair);
|
||||
if (result.success) {
|
||||
hapticNotification('success');
|
||||
showAlert(t('rewards.trackingStarted'));
|
||||
fetchUserScores();
|
||||
} else {
|
||||
hapticNotification('error');
|
||||
showAlert(result.error || t('rewards.trackingFailed'));
|
||||
}
|
||||
} catch (err) {
|
||||
hapticNotification('error');
|
||||
showAlert(err instanceof Error ? err.message : t('rewards.trackingFailed'));
|
||||
} finally {
|
||||
setShowTrackingAnimation(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRecordTrustScore = async () => {
|
||||
if (!peopleApi || !keypair) return;
|
||||
setTrackingAnimationText(t('rewards.recordingTrustScore'));
|
||||
setShowTrackingAnimation(true);
|
||||
hapticImpact('medium');
|
||||
try {
|
||||
const result = await recordTrustScore(peopleApi, keypair);
|
||||
if (result.success) {
|
||||
hapticNotification('success');
|
||||
showAlert(t('rewards.trustScoreRecorded'));
|
||||
fetchUserScores();
|
||||
} else {
|
||||
hapticNotification('error');
|
||||
showAlert(result.error || t('rewards.trustScoreRecordFailed'));
|
||||
}
|
||||
} catch (err) {
|
||||
hapticNotification('error');
|
||||
showAlert(err instanceof Error ? err.message : t('rewards.trustScoreRecordFailed'));
|
||||
} finally {
|
||||
setShowTrackingAnimation(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleActivate = () => {
|
||||
hapticNotification('success');
|
||||
localStorage.setItem(ACTIVITY_STORAGE_KEY, Date.now().toString());
|
||||
@@ -758,6 +810,37 @@ export function RewardsSection() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Start Tracking / Record Trust Score Button */}
|
||||
{!stakingStatus?.isTracking ? (
|
||||
<div className="bg-secondary/30 rounded-xl p-4 border border-border/50">
|
||||
<button
|
||||
onClick={handleStartTracking}
|
||||
disabled={!keypair || !peopleApi}
|
||||
className="w-full py-3 rounded-lg font-medium flex items-center justify-center gap-2 bg-gradient-to-r from-blue-500 to-cyan-600 text-white hover:opacity-90 transition-all disabled:opacity-50"
|
||||
>
|
||||
<Play className="w-5 h-5" />
|
||||
{t('rewards.startTracking')}
|
||||
</button>
|
||||
<p className="text-xs text-muted-foreground text-center mt-2">
|
||||
{t('rewards.startTrackingDesc')}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-secondary/30 rounded-xl p-4 border border-border/50">
|
||||
<button
|
||||
onClick={handleRecordTrustScore}
|
||||
disabled={!keypair || !peopleApi}
|
||||
className="w-full py-3 rounded-lg font-medium flex items-center justify-center gap-2 bg-gradient-to-r from-green-500 to-emerald-600 text-white hover:opacity-90 transition-all disabled:opacity-50"
|
||||
>
|
||||
<PenTool className="w-5 h-5" />
|
||||
{t('rewards.recordTrustScore')}
|
||||
</button>
|
||||
<p className="text-xs text-muted-foreground text-center mt-2">
|
||||
{t('rewards.recordTrustDesc')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Staking Rewards from SubQuery */}
|
||||
<div className="bg-secondary/30 rounded-xl p-4 border border-border/50">
|
||||
<h3 className="font-medium text-foreground mb-3 flex items-center gap-2">
|
||||
@@ -856,6 +939,15 @@ export function RewardsSection() {
|
||||
<p className="text-white/60 text-sm mt-2">{t('rewards.signingBlockchain')}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Score Tracking Overlay */}
|
||||
{showTrackingAnimation && (
|
||||
<div className="fixed inset-0 z-50 bg-black/90 flex flex-col items-center justify-center">
|
||||
<KurdistanSun size={120} />
|
||||
<p className="text-white mt-6 text-lg">{trackingAnimationText}</p>
|
||||
<p className="text-white/60 text-sm mt-2">{t('rewards.signingBlockchain')}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "1.0.211",
|
||||
"buildTime": "2026-02-19T22:35:04.294Z",
|
||||
"buildNumber": 1771540504295
|
||||
"version": "1.0.213",
|
||||
"buildTime": "2026-02-19T23:27:53.391Z",
|
||||
"buildNumber": 1771543673392
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user