Fix all ESLint errors in mobile app (157 errors -> 0)

Major fixes:
- Replace `any` types with proper TypeScript types across all files
- Convert require() imports to ES module imports
- Add __DEV__ guards to console statements
- Escape special characters in JSX (' and ")
- Fix unused variables (prefix with _ or remove)
- Fix React hooks violations (useCallback, useMemo patterns)
- Convert wasm-crypto-shim.js to TypeScript
- Add eslint-disable comments for valid setState patterns

Files affected: 50+ screens, components, contexts, and services

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-18 02:55:03 +03:00
parent 6979f36721
commit 40bc15f1f9
54 changed files with 442 additions and 333 deletions
+2 -2
View File
@@ -71,7 +71,7 @@ const CATEGORIES: { name: CategoryType; icon: string }[] = [
];
const AppsScreen: React.FC = () => {
const navigation = useNavigation<any>();
const navigation = useNavigation<{ navigate: (screen: string) => void }>();
const { selectedAccount, accounts, connectWallet } = usePezkuwi();
const isConnected = !!selectedAccount;
@@ -385,7 +385,7 @@ const AppsScreen: React.FC = () => {
<View style={styles.infoTextContainer}>
<Text style={styles.infoTitle}>Review Process</Text>
<Text style={styles.infoText}>
Your submission will be reviewed by Dijital Kurdistan Tech Inst. We'll contact you within 5-7 business days.
Your submission will be reviewed by Dijital Kurdistan Tech Inst. We&apos;ll contact you within 5-7 business days.
</Text>
</View>
</View>
+2 -2
View File
@@ -15,7 +15,7 @@ import {
} from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
import { useAuth } from '../contexts/AuthContext';
import { KurdistanColors } from '../theme/colors';
import kurdistanMapImage from '../../assets/kurdistan-map.png';
const AuthScreen: React.FC = () => {
const { signIn, signUp } = useAuth();
@@ -135,7 +135,7 @@ const AuthScreen: React.FC = () => {
<View style={styles.header}>
<View style={styles.logoContainer}>
<Image
source={require('../../assets/kurdistan-map.png')}
source={kurdistanMapImage}
style={styles.logoImage}
resizeMode="contain"
/>
+2 -4
View File
@@ -9,10 +9,8 @@ import {
StatusBar,
TextInput,
Alert,
ActivityIndicator,
Modal,
FlatList,
Image,
RefreshControl,
} from 'react-native';
import { useNavigation } from '@react-navigation/native';
@@ -191,7 +189,7 @@ const MOCK_LISTINGS: Listing[] = [
];
const B2BScreen: React.FC = () => {
const navigation = useNavigation();
const _navigation = useNavigation();
const { selectedAccount, api, getKeyPair } = usePezkuwi();
// State
@@ -347,7 +345,7 @@ const B2BScreen: React.FC = () => {
'Escrow hate damezrandin!\nEscrow has been created!\n\nDrav di ewlehiyê de ye.\nFunds are secured.',
[{ text: 'Temam / OK' }]
);
} catch (error) {
} catch {
Alert.alert('Şaşî / Error', 'Escrow nehat damezrandin / Escrow failed');
}
},
+23 -15
View File
@@ -12,6 +12,7 @@ import {
Dimensions,
Platform,
ActivityIndicator,
ImageSourcePropType,
} from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
import { useNavigation } from '@react-navigation/native';
@@ -24,7 +25,7 @@ import { usePezkuwi } from '../contexts/PezkuwiContext';
import { supabase } from '../lib/supabase';
import AvatarPickerModal from '../components/AvatarPickerModal';
import { NotificationCenterModal } from '../components/NotificationCenterModal';
import { fetchUserTikis, getPrimaryRole, getTikiDisplayName, getTikiEmoji, getTikiColor } from '../../shared/lib/tiki';
import { fetchUserTikis, getPrimaryRole, getTikiDisplayName, getTikiEmoji } from '../../shared/lib/tiki';
import { getAllScores, type UserScores } from '../../shared/lib/scores';
import { getKycStatus } from '../../shared/lib/kyc';
@@ -36,12 +37,12 @@ import qaGovernance from '../../../shared/images/quick-actions/qa_governance.jpg
import qaTrading from '../../../shared/images/quick-actions/qa_trading.jpg';
import qaB2B from '../../../shared/images/quick-actions/qa_b2b.png';
import qaBank from '../../../shared/images/quick-actions/qa_bank.png';
import qaGames from '../../../shared/images/quick-actions/qa_games.png';
import _qaGames from '../../../shared/images/quick-actions/qa_games.png';
import qaKurdMedia from '../../../shared/images/quick-actions/qa_kurdmedia.jpg';
import qaUniversity from '../../../shared/images/quick-actions/qa_university.png';
import avatarPlaceholder from '../../../shared/images/app-image.png'; // Fallback avatar
const { width } = Dimensions.get('window');
const { width: _width } = Dimensions.get('window');
// Avatar pool matching AvatarPickerModal
const AVATAR_POOL = [
@@ -79,14 +80,21 @@ const getEmojiFromAvatarId = (avatarId: string): string => {
return avatar ? avatar.emoji : '👤'; // Default to person emoji if not found
};
interface DashboardScreenProps {}
type DashboardScreenProps = Record<string, never>;
interface ProfileData {
id?: string;
full_name?: string | null;
avatar_url?: string | null;
created_at?: string;
}
const DashboardScreen: React.FC<DashboardScreenProps> = () => {
const navigation = useNavigation<NavigationProp<BottomTabParamList & RootStackParamList>>();
const { user } = useAuth();
const { api, isApiReady, selectedAccount, accounts, connectWallet } = usePezkuwi();
const [profileData, setProfileData] = useState<any>(null);
const [loading, setLoading] = useState(true);
const [profileData, setProfileData] = useState<ProfileData | null>(null);
const [_loading, setLoading] = useState(true);
const [avatarModalVisible, setAvatarModalVisible] = useState(false);
const [notificationModalVisible, setNotificationModalVisible] = useState(false);
@@ -166,7 +174,7 @@ const DashboardScreen: React.FC<DashboardScreenProps> = () => {
}, [fetchBlockchainData]);
// Check if user is a visitor (default when no blockchain wallet or no tikis)
const isVisitor = !selectedAccount || tikis.length === 0;
const _isVisitor = !selectedAccount || tikis.length === 0;
// Handle wallet connection
const handleConnectWallet = async () => {
@@ -184,7 +192,7 @@ const DashboardScreen: React.FC<DashboardScreenProps> = () => {
};
const primaryRole = tikis.length > 0 ? getPrimaryRole(tikis) : 'Visitor';
const showComingSoon = (featureName: string) => {
const _showComingSoon = (featureName: string) => {
Alert.alert(
'Coming Soon',
`${featureName} will be available soon!`,
@@ -192,7 +200,7 @@ const DashboardScreen: React.FC<DashboardScreenProps> = () => {
);
};
const showAwaitingGovernment = () => {
const _showAwaitingGovernment = () => {
Alert.alert(
'Li benda damezrandinê / Awaiting Establishment',
'Duaye helbejartina hukumeta Komara Dijitaliya Kurdistanê yên beta damezrandin.\n\nAwaiting the beta elections and establishment of the Digital Kurdistan Republic government.',
@@ -200,7 +208,7 @@ const DashboardScreen: React.FC<DashboardScreenProps> = () => {
);
};
const showUnderMaintenance = () => {
const _showUnderMaintenance = () => {
Alert.alert(
'Di bin çêkirinê de ye / Under Maintenance',
'Ev taybetmendî niha di bin çêkirinê de ye. Ji kerema xwe paşê vegerin.\n\nThis feature is currently under maintenance. Please check back later.',
@@ -208,7 +216,7 @@ const DashboardScreen: React.FC<DashboardScreenProps> = () => {
);
};
const showAwaitingSerokElection = () => {
const _showAwaitingSerokElection = () => {
Alert.alert(
'Li benda hilbijartinên çalak / Awaiting Active Elections',
'Duaye hilbijartinên Serokî yên çalak bibin.\n\nAwaiting active Presidential elections to be initiated.',
@@ -216,7 +224,7 @@ const DashboardScreen: React.FC<DashboardScreenProps> = () => {
);
};
const showAwaitingMinistryOfEducation = () => {
const _showAwaitingMinistryOfEducation = () => {
Alert.alert(
'Li benda Wezareta Perwerdê / Awaiting Ministry of Education',
'Duaye damezrandina Wezareta Perwerdê yên aktîv bibin.\n\nAwaiting the establishment of an active Ministry of Education.',
@@ -230,13 +238,13 @@ const DashboardScreen: React.FC<DashboardScreenProps> = () => {
const handleAvatarSelected = (avatarUrl: string) => {
// Refresh profile data to show new avatar
setProfileData((prev: any) => ({
setProfileData((prev: ProfileData | null) => ({
...prev,
avatar_url: avatarUrl,
}));
};
const renderAppIcon = (title: string, icon: any, onPress: () => void, isEmoji = false, comingSoon = false) => (
const renderAppIcon = (title: string, icon: string | ImageSourcePropType, onPress: () => void, isEmoji = false, comingSoon = false) => (
<TouchableOpacity
style={styles.appIconContainer}
onPress={onPress}
@@ -556,7 +564,7 @@ const DashboardScreen: React.FC<DashboardScreenProps> = () => {
<AvatarPickerModal
visible={avatarModalVisible}
onClose={() => setAvatarModalVisible(false)}
currentAvatar={profileData?.avatar_url}
currentAvatar={profileData?.avatar_url ?? undefined}
onAvatarSelected={handleAvatarSelected}
/>
+4 -3
View File
@@ -11,6 +11,7 @@ import {
Alert,
Platform,
KeyboardAvoidingView,
AlertButton,
} from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { useAuth } from '../contexts/AuthContext';
@@ -20,7 +21,7 @@ import { supabase } from '../lib/supabase';
import AvatarPickerModal from '../components/AvatarPickerModal';
// Cross-platform alert helper
const showAlert = (title: string, message: string, buttons?: Array<{text: string; onPress?: () => void; style?: string}>) => {
const showAlert = (title: string, message: string, buttons?: AlertButton[]) => {
if (Platform.OS === 'web') {
if (buttons && buttons.length > 1) {
const result = window.confirm(`${title}\n\n${message}`);
@@ -34,7 +35,7 @@ const showAlert = (title: string, message: string, buttons?: Array<{text: string
if (buttons?.[0]?.onPress) buttons[0].onPress();
}
} else {
Alert.alert(title, message, buttons as any);
Alert.alert(title, message, buttons);
}
};
@@ -76,7 +77,7 @@ const getEmojiFromAvatarId = (avatarId: string): string => {
const EditProfileScreen: React.FC = () => {
const navigation = useNavigation();
const { user } = useAuth();
const { isDarkMode, colors, fontScale } = useTheme();
const { isDarkMode: _isDarkMode, colors, fontScale } = useTheme();
const [fullName, setFullName] = useState('');
const [avatarUrl, setAvatarUrl] = useState<string | null>(null);
+1 -3
View File
@@ -17,8 +17,6 @@ import { usePezkuwi } from '../contexts/PezkuwiContext';
import {
fetchUserTikiNFTs,
getCitizenNFTDetails,
getTikiDisplayName,
getTikiEmoji,
ROLE_CATEGORIES,
type TikiNFTDetails,
} from '../../shared/lib/tiki';
@@ -223,7 +221,7 @@ const IdentityScreen: React.FC = () => {
<Text style={styles.notCitizenIcon}></Text>
<Text style={styles.notCitizenTitle}>Citizenship Not Found</Text>
<Text style={styles.notCitizenText}>
We couldn't find a Welati (citizen) NFT for this wallet.
We couldn&apos;t find a Welati (citizen) NFT for this wallet.
Please apply for citizenship to get your digital identity.
</Text>
<TouchableOpacity
+3 -5
View File
@@ -9,9 +9,7 @@ import {
StatusBar,
Alert,
Linking,
Image,
} from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
import { useNavigation } from '@react-navigation/native';
import { KurdistanColors } from '../theme/colors';
@@ -140,7 +138,7 @@ const SOCIAL_PLATFORMS: SocialPlatform[] = [
];
const KurdMediaScreen: React.FC = () => {
const navigation = useNavigation();
const _navigation = useNavigation();
const handleMediaPress = (channel: MediaChannel) => {
Alert.alert(
@@ -162,7 +160,7 @@ const KurdMediaScreen: React.FC = () => {
[{ text: 'Temam / OK' }]
);
}
} catch (error) {
} catch {
Alert.alert('Xeletî / Error', 'Tiştek xelet çû.\nSomething went wrong.');
}
};
@@ -284,7 +282,7 @@ const KurdMediaScreen: React.FC = () => {
<Text style={styles.infoBannerIcon}>💡</Text>
<View style={styles.infoBannerContent}>
<Text style={styles.infoBannerText}>
PezkuwiChain - Blockchain'a yekem a netewî ya Kurdan
PezkuwiChain - Blockchain&apos;a yekem a netewî ya Kurdan
</Text>
<Text style={styles.infoBannerTextEn}>
PezkuwiChain - The first national blockchain of the Kurds
+41 -9
View File
@@ -64,11 +64,40 @@ interface ContributionInfo {
refunded: boolean;
}
// Raw presale data from chain toJSON()
interface PresaleChainData {
owner?: string;
paymentAsset?: number;
rewardAsset?: number;
tokensForSale?: string | number;
startBlock?: number;
duration?: number;
status?: PresaleStatus;
accessControl?: 'Public' | 'Whitelist';
limits?: {
minContribution?: string | number;
maxContribution?: string | number;
softCap?: string | number;
hardCap?: string | number;
};
vesting?: VestingSchedule | null;
gracePeriodBlocks?: number;
refundFeePercent?: number;
graceRefundFeePercent?: number;
}
// Raw contribution data from chain toJSON()
interface ContributionChainData {
amount?: string | number;
contributedAt?: number;
refunded?: boolean;
}
const BLOCK_TIME_SECONDS = 6;
const PLATFORM_FEE_PERCENT = 2;
const LaunchpadScreen: React.FC = () => {
const navigation = useNavigation();
const _navigation = useNavigation();
const { api, selectedAccount, isApiReady, getKeyPair } = usePezkuwi();
const [presales, setPresales] = useState<PresaleConfig[]>([]);
@@ -80,7 +109,7 @@ const LaunchpadScreen: React.FC = () => {
const [userContributions, setUserContributions] = useState<Record<number, ContributionInfo>>({});
const [assetBalances, setAssetBalances] = useState<Record<number, string>>({});
const [showDetailModal, setShowDetailModal] = useState(false);
const [currentBlock, setCurrentBlock] = useState(0);
const [_currentBlock, setCurrentBlock] = useState(0);
// Fetch all presales from chain
const fetchPresales = useCallback(async () => {
@@ -113,7 +142,7 @@ const LaunchpadScreen: React.FC = () => {
const presaleData = await api.query.presale?.presales?.(id);
if (!presaleData || presaleData.isNone) continue;
const config = presaleData.toJSON() as any;
const config = presaleData.toJSON() as unknown as PresaleChainData;
if (!config) continue;
// Get total raised and contributors
@@ -124,7 +153,8 @@ const LaunchpadScreen: React.FC = () => {
const duration = config.duration || 0;
const endBlock = startBlock + duration;
const totalRaisedStr = totalRaised?.toString() || '0';
const hardCap = config.limits?.hardCap || '0';
const hardCapValue = config.limits?.hardCap;
const hardCap = hardCapValue !== undefined ? String(hardCapValue) : '0';
// Calculate progress
const progress = hardCap !== '0'
@@ -177,7 +207,7 @@ const LaunchpadScreen: React.FC = () => {
// Get user's contribution for this presale
const contribution = await api.query.presale?.contributions?.(presale.id, selectedAccount.address);
if (contribution && !contribution.isNone) {
const contribData = contribution.toJSON() as any;
const contribData = contribution.toJSON() as unknown as ContributionChainData;
userContribs[presale.id] = {
amount: contribData?.amount?.toString() || '0',
contributedAt: contribData?.contributedAt || 0,
@@ -360,8 +390,9 @@ const LaunchpadScreen: React.FC = () => {
setContributionAmount('');
setShowDetailModal(false);
fetchPresales();
} catch (error: any) {
Alert.alert('Çewtî', error.message || 'Contribution failed.');
} catch (error: unknown) {
const message = error instanceof Error ? error.message : 'Contribution failed.';
Alert.alert('Çewtî', message);
} finally {
setContributing(false);
}
@@ -413,8 +444,9 @@ const LaunchpadScreen: React.FC = () => {
Alert.alert('Serketî!', 'Refund processed successfully!');
fetchPresales();
} catch (error: any) {
Alert.alert('Çewtî', error.message || 'Refund failed.');
} catch (error: unknown) {
const message = error instanceof Error ? error.message : 'Refund failed.';
Alert.alert('Çewtî', message);
}
},
},
+1 -1
View File
@@ -31,7 +31,7 @@ const P2PPlatformScreen: React.FC = () => {
const [selectedTab, setSelectedTab] = useState<'buy' | 'sell'>('buy');
const [selectedFilter, setSelectedFilter] = useState<'all' | 'bank' | 'online'>('all');
const [ads, setAds] = useState<P2PAd[]>([]);
const [loading, setLoading] = useState(false);
const [_loading, setLoading] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const fetchAds = async () => {
+25 -5
View File
@@ -37,10 +37,30 @@ interface Enrollment {
points_earned: number;
}
// Raw course data from chain toJSON()
interface CourseChainData {
id: number;
owner: string;
name: number[] | string;
description: number[] | string;
contentLink: number[] | string;
status: 'Active' | 'Archived';
createdAt: number;
}
// Raw enrollment data from chain toJSON()
interface EnrollmentChainData {
student: string;
courseId: number;
enrolledAt: number;
completedAt: number | null;
pointsEarned: number;
}
type TabType = 'courses' | 'enrolled' | 'completed';
const PerwerdeScreen: React.FC = () => {
const navigation = useNavigation();
const _navigation = useNavigation();
const { selectedAccount, api, isApiReady } = usePezkuwi();
const isConnected = !!selectedAccount;
@@ -63,9 +83,9 @@ const PerwerdeScreen: React.FC = () => {
const entries = await api.query.perwerde.courses.entries();
const courseList: Course[] = [];
for (const [key, value] of entries) {
for (const [_key, value] of entries) {
if (!value.isEmpty) {
const data = value.toJSON() as any;
const data = value.toJSON() as unknown as CourseChainData;
courseList.push({
id: data.id,
owner: data.owner,
@@ -100,7 +120,7 @@ const PerwerdeScreen: React.FC = () => {
for (const courseId of courseIds) {
const enrollment = await api.query.perwerde.enrollments([selectedAccount.address, courseId]);
if (!enrollment.isEmpty) {
const data = enrollment.toJSON() as any;
const data = enrollment.toJSON() as unknown as EnrollmentChainData;
enrollmentList.push({
student: data.student,
course_id: data.courseId,
@@ -236,7 +256,7 @@ const PerwerdeScreen: React.FC = () => {
} else {
Alert.alert('Xeletî / Error', 'Nikarim linkê vebikum.');
}
} catch (error) {
} catch {
Alert.alert('Xeletî / Error', 'Tiştek xelet çû.');
}
};
+29 -7
View File
@@ -14,6 +14,28 @@ import {
import { KurdistanColors } from '../theme/colors';
import { usePezkuwi } from '../contexts/PezkuwiContext';
// Types for Polkadot API responses
interface PoolKeyData {
0?: string[];
[key: number]: string[] | undefined;
}
interface AssetMetadata {
symbol?: string;
decimals?: number;
}
interface AccountInfo {
data: {
free: { toString(): string };
};
}
interface AssetAccount {
isSome: boolean;
unwrap(): { balance: { toString(): string } };
}
interface PoolInfo {
id: string;
asset1: number;
@@ -52,7 +74,7 @@ const PoolBrowserScreen: React.FC = () => {
const poolAccount = value.toString();
// Parse pool assets from key
const keyData = key.toHuman() as any;
const keyData = key.toHuman() as unknown as PoolKeyData;
const assets = keyData[0];
if (!assets || assets.length !== 2) continue;
@@ -69,14 +91,14 @@ const PoolBrowserScreen: React.FC = () => {
try {
if (asset1 !== 0) {
const metadata1 = await api.query.assets.metadata(asset1);
const meta1 = metadata1.toJSON() as any;
const meta1 = metadata1.toJSON() as unknown as AssetMetadata;
asset1Symbol = meta1.symbol || `Asset ${asset1}`;
asset1Decimals = meta1.decimals || 12;
}
if (asset2 !== 0) {
const metadata2 = await api.query.assets.metadata(asset2);
const meta2 = metadata2.toJSON() as any;
const meta2 = metadata2.toJSON() as unknown as AssetMetadata;
asset2Symbol = meta2.symbol || `Asset ${asset2}`;
asset2Decimals = meta2.decimals || 12;
}
@@ -91,18 +113,18 @@ const PoolBrowserScreen: React.FC = () => {
try {
if (asset1 === 0) {
// Native token (wHEZ)
const balance1 = await api.query.system.account(poolAccount) as any;
const balance1 = await api.query.system.account(poolAccount) as unknown as AccountInfo;
reserve1 = balance1.data.free.toString();
} else {
const balance1 = await api.query.assets.account(asset1, poolAccount) as any;
const balance1 = await api.query.assets.account(asset1, poolAccount) as unknown as AssetAccount;
reserve1 = balance1.isSome ? balance1.unwrap().balance.toString() : '0';
}
if (asset2 === 0) {
const balance2 = await api.query.system.account(poolAccount) as any;
const balance2 = await api.query.system.account(poolAccount) as unknown as AccountInfo;
reserve2 = balance2.data.free.toString();
} else {
const balance2 = await api.query.assets.account(asset2, poolAccount) as any;
const balance2 = await api.query.assets.account(asset2, poolAccount) as unknown as AssetAccount;
reserve2 = balance2.isSome ? balance2.unwrap().balance.toString() : '0';
}
} catch (error) {
+4 -4
View File
@@ -9,7 +9,6 @@ import {
StatusBar,
ActivityIndicator,
Alert,
FlatList,
Modal,
RefreshControl,
} from 'react-native';
@@ -256,9 +255,10 @@ const PresidentScreen: React.FC = () => {
}
});
} catch (error: any) {
} catch (error: unknown) {
if (__DEV__) console.error('[President] Vote error:', error);
Alert.alert('Error', error.message || 'Failed to submit vote');
const errorMessage = error instanceof Error ? error.message : 'Failed to submit vote';
Alert.alert('Error', errorMessage);
setSubmittingVote(false);
}
};
@@ -425,7 +425,7 @@ const PresidentScreen: React.FC = () => {
</Text>
</View>
) : (
pastElections.map((result, index) => (
pastElections.map((result, _index) => (
<View key={result.electionId} style={styles.historyCard}>
<View style={styles.historyHeader}>
<Text style={styles.historyTitle}>Election #{result.electionId}</Text>
+1 -1
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback } from 'react';
import React, { useState, useCallback } from 'react';
import {
View,
Text,
+3 -5
View File
@@ -22,8 +22,6 @@ import { KurdistanColors } from '../theme/colors';
import {
getReferralStats,
getMyReferrals,
calculateReferralScore,
type ReferralStats as BlockchainReferralStats,
} from '../../shared/lib/referral';
// Share platform types
@@ -93,7 +91,7 @@ const ReferralScreen: React.FC = () => {
: '';
// Deep link for app-to-app sharing
const deepLink = selectedAccount
const _deepLink = selectedAccount
? `pezkuwichain://join?ref=${selectedAccount.address}`
: '';
@@ -181,7 +179,7 @@ const ReferralScreen: React.FC = () => {
if (!selectedAccount) return;
const encodedMessage = encodeURIComponent(shareMessage);
const encodedLink = encodeURIComponent(referralLink);
const _encodedLink = encodeURIComponent(referralLink);
let url = '';
@@ -624,7 +622,7 @@ const ReferralScreen: React.FC = () => {
<Text style={styles.qrInstructions}>
Bu QR kodu arkadaşlarınla paylaş.{'\n'}
Taratarak Pezkuwi'ye katılabilirler.
Taratarak Pezkuwi&apos;ye katılabilirler.
</Text>
<Text style={styles.qrInstructionsEn}>
Share this QR code with friends.{'\n'}
+25 -13
View File
@@ -19,8 +19,20 @@ import AsyncStorage from '@react-native-async-storage/async-storage';
import * as SecureStore from 'expo-secure-store';
import { Clipboard } from 'react-native';
import { useNavigation, NavigationProp } from '@react-navigation/native';
import type { AlertButton } from 'react-native';
import { RootStackParamList } from '../navigation/AppNavigator';
import { KurdistanColors } from '../theme/colors';
// Profile type for Supabase
interface UserProfile {
id?: string;
full_name: string;
username: string;
bio?: string;
notifications_push: boolean;
notifications_email: boolean;
updated_at?: string;
}
import { useTheme } from '../contexts/ThemeContext';
import { useBiometricAuth } from '../contexts/BiometricAuthContext';
import { useAuth } from '../contexts/AuthContext';
@@ -28,7 +40,7 @@ import { usePezkuwi, NETWORKS } from '../contexts/PezkuwiContext';
import { supabase } from '../lib/supabase';
// Cross-platform alert helper
const showAlert = (title: string, message: string, buttons?: Array<{text: string; onPress?: () => void; style?: string}>) => {
const showAlert = (title: string, message: string, buttons?: AlertButton[]) => {
if (Platform.OS === 'web') {
if (buttons && buttons.length > 1) {
// For confirm dialogs
@@ -40,7 +52,7 @@ const showAlert = (title: string, message: string, buttons?: Array<{text: string
window.alert(`${title}\n\n${message}`);
}
} else {
Alert.alert(title, message, buttons as any);
Alert.alert(title, message, buttons);
}
};
@@ -160,13 +172,13 @@ const SettingsScreen: React.FC = () => {
const { currentNetwork, switchNetwork, selectedAccount } = usePezkuwi();
// Profile State (Supabase)
const [profile, setProfile] = useState<any>({
const [profile, setProfile] = useState<UserProfile>({
full_name: '',
username: '',
notifications_push: false,
notifications_email: true,
});
const [loadingProfile, setLoadingProfile] = useState(false);
const [_loadingProfile, setLoadingProfile] = useState(false);
const [savingSettings, setSavingSettings] = useState(false);
// Modals
@@ -196,8 +208,8 @@ const SettingsScreen: React.FC = () => {
setEditName(data.full_name || '');
setEditBio(data.bio || '');
}
} catch (err) {
console.log('Error fetching profile:', err);
} catch (_err) {
console.log('Error fetching profile:', _err);
} finally {
setLoadingProfile(false);
}
@@ -211,9 +223,9 @@ const SettingsScreen: React.FC = () => {
const updateSetting = async (key: string, value: boolean) => {
if (!user) return;
setSavingSettings(true);
// Optimistic update
setProfile((prev: any) => ({ ...prev, [key]: value }));
setProfile((prev) => ({ ...prev, [key]: value }));
try {
const { error } = await supabase
@@ -225,7 +237,7 @@ const SettingsScreen: React.FC = () => {
} catch (err) {
console.error('Failed to update setting:', err);
// Revert on error
setProfile((prev: any) => ({ ...prev, [key]: !value }));
setProfile((prev) => ({ ...prev, [key]: !value }));
showAlert('Error', 'Failed to save setting. Please check your connection.');
} finally {
setSavingSettings(false);
@@ -246,11 +258,11 @@ const SettingsScreen: React.FC = () => {
.eq('id', user.id);
if (error) throw error;
setProfile((prev: any) => ({ ...prev, full_name: editName, bio: editBio }));
setProfile((prev) => ({ ...prev, full_name: editName, bio: editBio }));
setShowProfileEdit(false);
showAlert('Success', 'Profile updated successfully');
} catch (err) {
} catch {
showAlert('Error', 'Failed to update profile');
}
};
@@ -502,7 +514,7 @@ const SettingsScreen: React.FC = () => {
'@pezkuwi_selected_network'
]);
showAlert('Success', 'Wallet data cleared. Restart the app to see changes.');
} catch (error) {
} catch {
showAlert('Error', 'Failed to clear wallet data');
}
}
+17 -17
View File
@@ -297,7 +297,7 @@ const WalletScreen: React.FC = () => {
}
// Subscribe to balance changes
unsubscribe = await api.query.system.account(accountId, (accountInfo: any) => {
unsubscribe = await api.query.system.account(accountId, (accountInfo: { data: { free: { toString(): string } } }) => {
const hezBalance = (Number(accountInfo.data.free.toString()) / 1e12).toFixed(2);
setBalances(prev => ({ ...prev, HEZ: hezBalance }));
if (__DEV__) console.warn('[Wallet] Balance updated via subscription:', hezBalance, 'HEZ');
@@ -471,7 +471,7 @@ const WalletScreen: React.FC = () => {
decodeAddress(address);
setAddressError('');
return true;
} catch (e) {
} catch {
setAddressError('Invalid address format');
return false;
}
@@ -570,7 +570,7 @@ const WalletScreen: React.FC = () => {
throw new Error('Unknown token type');
}
await tx.signAndSend(keypair, ({ status, events }) => {
await tx.signAndSend(keypair, ({ status, _events }) => {
if (status.isInBlock) {
if (__DEV__) console.warn('[Wallet] Transaction in block:', status.asInBlock.toHex());
}
@@ -584,25 +584,25 @@ const WalletScreen: React.FC = () => {
fetchData();
}
});
} catch (e: any) {
} catch (e: unknown) {
setIsSending(false);
showAlert('Error', e.message);
showAlert('Error', (e as Error).message);
}
};
// Connect/Create Wallet handlers
const handleConnectWallet = async () => {
const _handleConnectWallet = async () => {
if (accounts.length === 0) setCreateWalletModalVisible(true);
else await connectWallet();
};
const handleCreateWallet = async () => {
const _handleCreateWallet = async () => {
try {
const { address, mnemonic } = await createWallet(walletName);
const { _address, mnemonic } = await createWallet(walletName);
setUserMnemonic(mnemonic); // Save for backup
setCreateWalletModalVisible(false);
showAlert('Wallet Created', `Save this mnemonic:\n${mnemonic}`, [{ text: 'OK', onPress: () => connectWallet() }]);
} catch (e) { showAlert('Error', 'Failed'); }
} catch { showAlert('Error', 'Failed'); }
};
// Copy Address Handler
@@ -613,7 +613,7 @@ const WalletScreen: React.FC = () => {
};
// Import Wallet Handler
const handleImportWallet = async () => {
const _handleImportWallet = async () => {
if (!importMnemonic.trim()) {
showAlert('Error', 'Please enter a valid mnemonic');
return;
@@ -630,13 +630,13 @@ const WalletScreen: React.FC = () => {
setImportWalletModalVisible(false);
setImportMnemonic('');
connectWallet();
} catch (e: any) {
showAlert('Error', e.message || 'Invalid mnemonic');
} catch (e: unknown) {
showAlert('Error', (e as Error).message || 'Invalid mnemonic');
}
};
// Backup Mnemonic Handler
const handleBackupMnemonic = async () => {
const _handleBackupMnemonic = async () => {
if (!selectedAccount) {
showAlert('No Wallet', 'Please create or import a wallet first.');
return;
@@ -677,8 +677,8 @@ const WalletScreen: React.FC = () => {
await switchNetwork(network);
setNetworkSelectorVisible(false);
showAlert('Success', `Switched to ${NETWORKS[network].displayName}`);
} catch (e: any) {
showAlert('Error', e.message || 'Failed to switch network');
} catch (e: unknown) {
showAlert('Error', (e as Error).message || 'Failed to switch network');
}
};
@@ -825,7 +825,7 @@ const WalletScreen: React.FC = () => {
{/* Dynamic Token List */}
{allTokens
.filter(t => !hiddenTokens.includes(t.symbol))
.map((token, index) => {
.map((token, _index) => {
const changeColor = token.change24h >= 0 ? '#22C55E' : '#EF4444';
const changePrefix = token.change24h >= 0 ? '+' : '';
@@ -1099,7 +1099,7 @@ const WalletScreen: React.FC = () => {
if (accounts.length <= 1) {
setWalletSelectorVisible(false);
}
} catch (err) {
} catch {
if (Platform.OS === 'web') {
window.alert('Failed to delete wallet');
} else {
@@ -43,7 +43,7 @@ const DelegationScreen: React.FC = () => {
const [delegates, setDelegates] = useState<Delegate[]>([]);
const [userDelegations, setUserDelegations] = useState<UserDelegation[]>([]);
const [loading, setLoading] = useState(false);
const [_loading, setLoading] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const [selectedView, setSelectedView] = useState<'explore' | 'my-delegations'>('explore');
const [selectedDelegate, setSelectedDelegate] = useState<Delegate | null>(null);
@@ -22,7 +22,7 @@ interface ElectionInfo {
totalVotes: number;
}
interface Candidate {
interface _Candidate {
address: string;
name: string;
votes: number;
@@ -32,7 +32,7 @@ interface Candidate {
// Mock data removed - using dynamicCommissionCollective pallet for elections
const ElectionsScreen: React.FC = () => {
const { api, isApiReady } = usePezkuwi();
const { api, isApiReady, error: connectionError } = usePezkuwi();
const [elections, setElections] = useState<ElectionInfo[]>([]);
const [loading, setLoading] = useState(false);
@@ -44,7 +44,7 @@ const ForumScreen: React.FC = () => {
const [categories, setCategories] = useState<Category[]>([]);
const [discussions, setDiscussions] = useState<Discussion[]>([]);
const [loading, setLoading] = useState(false);
const [_loading, setLoading] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
const [searchQuery, setSearchQuery] = useState('');
@@ -28,14 +28,14 @@ interface Proposal {
// Mock data removed - using real blockchain queries from democracy pallet
const ProposalsScreen: React.FC = () => {
const { api, isApiReady, selectedAccount, error: connectionError } = usePezkuwi();
const { api, isApiReady, selectedAccount: _selectedAccount, error: connectionError } = usePezkuwi();
const [proposals, setProposals] = useState<Proposal[]>([]);
const [loading, setLoading] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const [selectedFilter, setSelectedFilter] = useState<'all' | 'active' | 'passed' | 'rejected'>('all');
const formatBalance = (balance: string, decimals: number = 12): string => {
const _formatBalance = (balance: string, decimals: number = 12): string => {
const value = BigInt(balance);
const divisor = BigInt(10 ** decimals);
const wholePart = value / divisor;
@@ -51,7 +51,7 @@ const ProposalsScreen: React.FC = () => {
// Fetch democracy referenda
if (api.query.democracy?.referendumInfoOf) {
const referendaData = await api.query.democracy.referendumInfoOf.entries();
const parsedProposals: Proposal[] = referendaData.map(([key, value]: any) => {
const parsedProposals: Proposal[] = referendaData.map(([key, value]: [{ args: [{ toNumber(): number }] }, { unwrap(): { isOngoing?: boolean; asOngoing?: { tally?: { ayes?: { toString(): string }; nays?: { toString(): string } }; proposalHash?: { toString(): string }; end?: { toNumber(): number } } } }]) => {
const index = key.args[0].toNumber();
const info = value.unwrap();
@@ -57,7 +57,7 @@ const TreasuryScreen: React.FC = () => {
// Fetch treasury proposals
if (api.query.treasury?.proposals) {
const proposalsData = await api.query.treasury.proposals.entries();
const parsedProposals: TreasuryProposal[] = proposalsData.map(([key, value]: any) => {
const parsedProposals: TreasuryProposal[] = proposalsData.map(([key, value]: [{ args: [{ toNumber(): number }] }, { unwrap(): { beneficiary: { toString(): string }; value: { toString(): string }; proposer: { toString(): string }; bond: { toString(): string } } }]) => {
const proposalIndex = key.args[0].toNumber();
const proposal = value.unwrap();