diff --git a/mobile/__mocks__/react-native-gesture-handler.js b/mobile/__mocks__/react-native-gesture-handler.js index 736e56ee..ad0df64b 100644 --- a/mobile/__mocks__/react-native-gesture-handler.js +++ b/mobile/__mocks__/react-native-gesture-handler.js @@ -1,35 +1,33 @@ -const View = require('react-native/Libraries/Components/View/View'); +import { View } from 'react-native'; -module.exports = { - GestureHandlerRootView: View, - PanGestureHandler: View, - TapGestureHandler: View, - PinchGestureHandler: View, - RotationGestureHandler: View, - LongPressGestureHandler: View, - ForceTouchGestureHandler: View, - FlingGestureHandler: View, - NativeViewGestureHandler: View, - createNativeWrapper: (component) => component, - State: {}, - Directions: {}, - gestureHandlerRootHOC: (component) => component, - Swipeable: View, - DrawerLayout: View, - ScrollView: View, - Slider: View, - Switch: View, - TextInput: View, - ToolbarAndroid: View, - ViewPagerAndroid: View, - DrawerLayoutAndroid: View, - WebView: View, - RawButton: View, - BaseButton: View, - RectButton: View, - BorderlessButton: View, - TouchableHighlight: View, - TouchableNativeFeedback: View, - TouchableOpacity: View, - TouchableWithoutFeedback: View, -}; +export const GestureHandlerRootView = View; +export const PanGestureHandler = View; +export const TapGestureHandler = View; +export const PinchGestureHandler = View; +export const RotationGestureHandler = View; +export const LongPressGestureHandler = View; +export const ForceTouchGestureHandler = View; +export const FlingGestureHandler = View; +export const NativeViewGestureHandler = View; +export const createNativeWrapper = (component) => component; +export const State = {}; +export const Directions = {}; +export const gestureHandlerRootHOC = (component) => component; +export const Swipeable = View; +export const DrawerLayout = View; +export const ScrollView = View; +export const Slider = View; +export const Switch = View; +export const TextInput = View; +export const ToolbarAndroid = View; +export const ViewPagerAndroid = View; +export const DrawerLayoutAndroid = View; +export const WebView = View; +export const RawButton = View; +export const BaseButton = View; +export const RectButton = View; +export const BorderlessButton = View; +export const TouchableHighlight = View; +export const TouchableNativeFeedback = View; +export const TouchableOpacity = View; +export const TouchableWithoutFeedback = View; diff --git a/mobile/__mocks__/react-native-reanimated.js b/mobile/__mocks__/react-native-reanimated.js index 870c6dec..ecdb5533 100644 --- a/mobile/__mocks__/react-native-reanimated.js +++ b/mobile/__mocks__/react-native-reanimated.js @@ -1,5 +1,6 @@ -const React = require('react'); -const { View, Text, Image, Animated } = require('react-native'); +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import React from 'react'; +import { View, Text, Image, Animated } from 'react-native'; const Reanimated = { default: { @@ -57,7 +58,7 @@ const Reanimated = { withTiming: jest.fn((value) => value), withSpring: jest.fn((value) => value), withDecay: jest.fn((value) => value), - withDelay: jest.fn((delay, value) => value), + withDelay: jest.fn((_delay, value) => value), withSequence: jest.fn((...args) => args[0]), withRepeat: jest.fn((value) => value), cancelAnimation: jest.fn(), @@ -67,4 +68,5 @@ const Reanimated = { EasingNode: Animated.Easing, }; -module.exports = Reanimated; +export default Reanimated; +export const { useSharedValue, useAnimatedStyle, useAnimatedGestureHandler, useAnimatedScrollHandler, withTiming, withSpring, withDecay, withDelay, withSequence, withRepeat, cancelAnimation, runOnJS, runOnUI, Easing, EasingNode } = Reanimated; diff --git a/mobile/index.ts b/mobile/index.ts index d6071d5f..d1b6581c 100644 --- a/mobile/index.ts +++ b/mobile/index.ts @@ -1,38 +1,39 @@ // CRITICAL: Import crypto polyfill FIRST before anything else -console.log('🚀 [INDEX] Starting app initialization...'); -console.log('📦 [INDEX] Loading react-native-get-random-values...'); +if (__DEV__) console.warn('🚀 [INDEX] Starting app initialization...'); +if (__DEV__) console.warn('📦 [INDEX] Loading react-native-get-random-values...'); import 'react-native-get-random-values'; -console.log('✅ [INDEX] react-native-get-random-values loaded'); +if (__DEV__) console.warn('✅ [INDEX] react-native-get-random-values loaded'); // React Native polyfills for @pezkuwi packages -console.log('📦 [INDEX] Loading URL polyfill...'); +if (__DEV__) console.warn('📦 [INDEX] Loading URL polyfill...'); import 'react-native-url-polyfill/auto'; -console.log('✅ [INDEX] URL polyfill loaded'); +if (__DEV__) console.warn('✅ [INDEX] URL polyfill loaded'); -console.log('📦 [INDEX] Setting up Buffer...'); +if (__DEV__) console.warn('📦 [INDEX] Setting up Buffer...'); import { Buffer } from 'buffer'; // Global polyfills for Polkadot.js -// @ts-ignore +// @ts-expect-error Global Buffer assignment for polyfill global.Buffer = Buffer; -console.log('✅ [INDEX] Buffer configured'); +if (__DEV__) console.warn('✅ [INDEX] Buffer configured'); // TextEncoder/TextDecoder polyfill -console.log('📦 [INDEX] Setting up TextEncoder/TextDecoder...'); +if (__DEV__) console.warn('📦 [INDEX] Setting up TextEncoder/TextDecoder...'); if (typeof global.TextEncoder === 'undefined') { + // eslint-disable-next-line @typescript-eslint/no-require-imports const { TextEncoder, TextDecoder } = require('text-encoding'); - // @ts-ignore + // @ts-expect-error Global TextEncoder assignment for polyfill global.TextEncoder = TextEncoder; - // @ts-ignore + // @ts-expect-error Global TextDecoder assignment for polyfill global.TextDecoder = TextDecoder; - console.log('✅ [INDEX] TextEncoder/TextDecoder configured'); + if (__DEV__) console.warn('✅ [INDEX] TextEncoder/TextDecoder configured'); } else { - console.log('ℹ️ [INDEX] TextEncoder/TextDecoder already available'); + if (__DEV__) console.warn('ℹ️ [INDEX] TextEncoder/TextDecoder already available'); } // Filter out known third-party deprecation warnings const originalWarn = console.warn; -console.warn = (...args: any[]) => { +console.warn = (...args: unknown[]) => { const message = args[0]?.toString() || ''; // Filter react-native-web deprecation warnings @@ -44,15 +45,15 @@ console.warn = (...args: any[]) => { originalWarn.apply(console, args); }; -console.log('📦 [INDEX] Loading Expo...'); +if (__DEV__) console.warn('📦 [INDEX] Loading Expo...'); import { registerRootComponent } from 'expo'; -console.log('✅ [INDEX] Expo loaded'); +if (__DEV__) console.warn('✅ [INDEX] Expo loaded'); -console.log('📦 [INDEX] Loading App component...'); +if (__DEV__) console.warn('📦 [INDEX] Loading App component...'); import App from './App'; -console.log('✅ [INDEX] App component loaded'); +if (__DEV__) console.warn('✅ [INDEX] App component loaded'); -console.log('🎯 [INDEX] All imports successful, registering root component...'); +if (__DEV__) console.warn('🎯 [INDEX] All imports successful, registering root component...'); // registerRootComponent calls AppRegistry.registerComponent('main', () => App); // It also ensures that whether you load the app in Expo Go or in a native build, diff --git a/mobile/package.json b/mobile/package.json index 9e784d82..0ae25f56 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -44,6 +44,7 @@ "@react-navigation/stack": "^7.6.4", "@supabase/supabase-js": "^2.90.1", "buffer": "^6.0.3", + "concat-map": "^0.0.2", "expo": "~54.0.23", "expo-camera": "~17.0.10", "expo-image-picker": "~17.0.10", diff --git a/mobile/src/__mocks__/contexts/AuthContext.tsx b/mobile/src/__mocks__/contexts/AuthContext.tsx index 321e9329..8bedb79e 100644 --- a/mobile/src/__mocks__/contexts/AuthContext.tsx +++ b/mobile/src/__mocks__/contexts/AuthContext.tsx @@ -1,16 +1,16 @@ import React, { createContext, useContext, ReactNode } from 'react'; -import { User } from '@supabase/supabase-js'; +import { User, Session, AuthError } from '@supabase/supabase-js'; // Mock Auth Context for testing interface AuthContextType { user: User | null; - session: any | null; + session: Session | null; loading: boolean; - signIn: (email: string, password: string) => Promise<{ error: any }>; - signUp: (email: string, password: string, fullName: string) => Promise<{ error: any }>; + signIn: (email: string, password: string) => Promise<{ error: AuthError | null }>; + signUp: (email: string, password: string, fullName: string) => Promise<{ error: AuthError | null }>; signOut: () => Promise; - changePassword: (newPassword: string, currentPassword: string) => Promise<{ error: any }>; - resetPassword: (email: string) => Promise<{ error: any }>; + changePassword: (newPassword: string, currentPassword: string) => Promise<{ error: AuthError | null }>; + resetPassword: (email: string) => Promise<{ error: AuthError | null }>; } const mockUser: User = { diff --git a/mobile/src/components/AvatarPickerModal.tsx b/mobile/src/components/AvatarPickerModal.tsx index ce62080e..b4a0790c 100644 --- a/mobile/src/components/AvatarPickerModal.tsx +++ b/mobile/src/components/AvatarPickerModal.tsx @@ -7,7 +7,6 @@ import { StyleSheet, ScrollView, Image, - Alert, ActivityIndicator, Platform, } from 'react-native'; @@ -136,20 +135,20 @@ const AvatarPickerModal: React.FC = ({ const uploadImageToSupabase = async (imageUri: string): Promise => { if (!user) { - console.error('[AvatarPicker] No user found'); + if (__DEV__) console.warn('[AvatarPicker] No user found'); return null; } try { - console.log('[AvatarPicker] Starting upload for URI:', imageUri.substring(0, 50) + '...'); + if (__DEV__) console.warn('[AvatarPicker] Starting upload for URI:', imageUri.substring(0, 50) + '...'); // Convert image URI to blob const response = await fetch(imageUri); const blob = await response.blob(); - console.log('[AvatarPicker] Blob created - size:', blob.size, 'bytes, type:', blob.type); + if (__DEV__) console.warn('[AvatarPicker] Blob created - size:', blob.size, 'bytes, type:', blob.type); if (blob.size === 0) { - console.error('[AvatarPicker] Blob is empty!'); + if (__DEV__) console.warn('[AvatarPicker] Blob is empty!'); return null; } @@ -173,7 +172,7 @@ const AvatarPickerModal: React.FC = ({ const filePath = `avatars/${fileName}`; const contentType = blob.type || `image/${fileExt}`; - console.log('[AvatarPicker] Uploading to path:', filePath, 'contentType:', contentType); + if (__DEV__) console.warn('[AvatarPicker] Uploading to path:', filePath, 'contentType:', contentType); // Upload to Supabase Storage const { data: uploadData, error: uploadError } = await supabase.storage @@ -184,25 +183,26 @@ const AvatarPickerModal: React.FC = ({ }); if (uploadError) { - console.error('[AvatarPicker] Supabase upload error:', uploadError.message, uploadError); + if (__DEV__) console.warn('[AvatarPicker] Supabase upload error:', uploadError.message, uploadError); // Show more specific error to user showAlert('Upload Error', `Storage error: ${uploadError.message}`); return null; } - console.log('[AvatarPicker] Upload successful:', uploadData); + if (__DEV__) console.warn('[AvatarPicker] Upload successful:', uploadData); // Get public URL const { data } = supabase.storage .from('avatars') .getPublicUrl(filePath); - console.log('[AvatarPicker] Public URL:', data.publicUrl); + if (__DEV__) console.warn('[AvatarPicker] Public URL:', data.publicUrl); return data.publicUrl; - } catch (error: any) { - console.error('[AvatarPicker] Error uploading to Supabase:', error?.message || error); - showAlert('Upload Error', `Failed to upload: ${error?.message || 'Unknown error'}`); + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + if (__DEV__) console.warn('[AvatarPicker] Error uploading to Supabase:', errorMessage); + showAlert('Upload Error', `Failed to upload: ${errorMessage}`); return null; } }; diff --git a/mobile/src/components/Card.tsx b/mobile/src/components/Card.tsx index 660c1702..c7e227ea 100644 --- a/mobile/src/components/Card.tsx +++ b/mobile/src/components/Card.tsx @@ -61,7 +61,7 @@ export const Card: React.FC = ({ ); } - return {content}; + return {content}; }; const styles = StyleSheet.create({ diff --git a/mobile/src/components/ChangePasswordModal.tsx b/mobile/src/components/ChangePasswordModal.tsx index 213ea406..71e532d1 100644 --- a/mobile/src/components/ChangePasswordModal.tsx +++ b/mobile/src/components/ChangePasswordModal.tsx @@ -222,7 +222,9 @@ const ChangePasswordModal: React.FC = ({ ); }; -const createStyles = (colors: any) => StyleSheet.create({ +import type { ThemeColors } from '../contexts/ThemeContext'; + +const createStyles = (colors: ThemeColors) => StyleSheet.create({ overlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.5)', diff --git a/mobile/src/components/EmailNotificationsModal.tsx b/mobile/src/components/EmailNotificationsModal.tsx index 8e34af10..bed68bfc 100644 --- a/mobile/src/components/EmailNotificationsModal.tsx +++ b/mobile/src/components/EmailNotificationsModal.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { Modal, View, @@ -11,6 +11,7 @@ import { import AsyncStorage from '@react-native-async-storage/async-storage'; import { KurdistanColors } from '../theme/colors'; import { useTheme } from '../contexts/ThemeContext'; +import type { ThemeColors } from '../contexts/ThemeContext'; const EMAIL_PREFS_KEY = '@pezkuwi/email_notifications'; @@ -39,32 +40,34 @@ const EmailNotificationsModal: React.FC = ({ }); const [saving, setSaving] = useState(false); - useEffect(() => { - loadPreferences(); - }, [visible]); - - const loadPreferences = async () => { + const loadPreferences = useCallback(async () => { try { const saved = await AsyncStorage.getItem(EMAIL_PREFS_KEY); if (saved) { setPreferences(JSON.parse(saved)); } } catch (error) { - console.error('Failed to load email preferences:', error); + if (__DEV__) console.warn('Failed to load email preferences:', error); } - }; + }, []); + + useEffect(() => { + // Load preferences when modal becomes visible - setState is async inside loadPreferences + // eslint-disable-next-line react-hooks/set-state-in-effect + loadPreferences(); + }, [visible, loadPreferences]); const savePreferences = async () => { setSaving(true); try { await AsyncStorage.setItem(EMAIL_PREFS_KEY, JSON.stringify(preferences)); - console.log('[EmailPrefs] Preferences saved:', preferences); + if (__DEV__) console.warn('[EmailPrefs] Preferences saved:', preferences); setTimeout(() => { setSaving(false); onClose(); }, 500); } catch (error) { - console.error('Failed to save email preferences:', error); + if (__DEV__) console.warn('Failed to save email preferences:', error); setSaving(false); } }; @@ -203,7 +206,7 @@ const EmailNotificationsModal: React.FC = ({ ); }; -const createStyles = (colors: any) => StyleSheet.create({ +const createStyles = (colors: ThemeColors) => StyleSheet.create({ overlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.5)', diff --git a/mobile/src/components/FontSizeModal.tsx b/mobile/src/components/FontSizeModal.tsx index e354ee60..358e418b 100644 --- a/mobile/src/components/FontSizeModal.tsx +++ b/mobile/src/components/FontSizeModal.tsx @@ -102,7 +102,9 @@ const FontSizeModal: React.FC = ({ visible, onClose }) => { ); }; -const createStyles = (colors: any) => StyleSheet.create({ +import type { ThemeColors } from '../contexts/ThemeContext'; + +const createStyles = (colors: ThemeColors) => StyleSheet.create({ overlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.5)', diff --git a/mobile/src/components/Input.tsx b/mobile/src/components/Input.tsx index 0b35c441..9acc846a 100644 --- a/mobile/src/components/Input.tsx +++ b/mobile/src/components/Input.tsx @@ -61,7 +61,7 @@ export const Input: React.FC = ({ { setIsFocused(true); props.onFocus?.(e); diff --git a/mobile/src/components/KurdistanSun.tsx b/mobile/src/components/KurdistanSun.tsx index c504f924..c0e6ca8b 100644 --- a/mobile/src/components/KurdistanSun.tsx +++ b/mobile/src/components/KurdistanSun.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useEffect, useMemo } from 'react'; import { View, Animated, Easing, StyleSheet } from 'react-native'; import Svg, { Circle, Line, Defs, RadialGradient, Stop } from 'react-native-svg'; @@ -9,12 +9,12 @@ interface KurdistanSunProps { const AnimatedView = Animated.View; export const KurdistanSun: React.FC = ({ size = 200 }) => { - // Animation values - const greenHaloRotation = useRef(new Animated.Value(0)).current; - const redHaloRotation = useRef(new Animated.Value(0)).current; - const yellowHaloRotation = useRef(new Animated.Value(0)).current; - const raysPulse = useRef(new Animated.Value(1)).current; - const glowPulse = useRef(new Animated.Value(0.6)).current; + // Animation values - use useMemo since these are stable and used during render + const greenHaloRotation = useMemo(() => new Animated.Value(0), []); + const redHaloRotation = useMemo(() => new Animated.Value(0), []); + const yellowHaloRotation = useMemo(() => new Animated.Value(0), []); + const raysPulse = useMemo(() => new Animated.Value(1), []); + const glowPulse = useMemo(() => new Animated.Value(0.6), []); useEffect(() => { // Green halo rotation (3s, clockwise) @@ -82,7 +82,7 @@ export const KurdistanSun: React.FC = ({ size = 200 }) => { }), ]) ).start(); - }, []); + }, [greenHaloRotation, redHaloRotation, yellowHaloRotation, raysPulse, glowPulse]); const greenSpin = greenHaloRotation.interpolate({ inputRange: [0, 1], diff --git a/mobile/src/components/LoadingSkeleton.tsx b/mobile/src/components/LoadingSkeleton.tsx index 46bfe267..9a9b5ad5 100644 --- a/mobile/src/components/LoadingSkeleton.tsx +++ b/mobile/src/components/LoadingSkeleton.tsx @@ -49,7 +49,7 @@ export const Skeleton: React.FC = ({ diff --git a/mobile/src/components/NotificationBell.tsx b/mobile/src/components/NotificationBell.tsx index d4e520c7..5cd8ef36 100644 --- a/mobile/src/components/NotificationBell.tsx +++ b/mobile/src/components/NotificationBell.tsx @@ -4,9 +4,11 @@ import { usePezkuwi } from '../contexts/PezkuwiContext'; import { KurdistanColors } from '../theme/colors'; import { supabaseHelpers } from '../lib/supabase'; +import type { ViewStyle } from 'react-native'; + interface NotificationBellProps { onPress: () => void; - style?: any; + style?: ViewStyle; } export const NotificationBell: React.FC = ({ onPress, style }) => { @@ -15,6 +17,8 @@ export const NotificationBell: React.FC = ({ onPress, sty useEffect(() => { if (!api || !isApiReady || !selectedAccount) { + // Reset count when dependencies are not available - valid conditional setState + // eslint-disable-next-line react-hooks/set-state-in-effect setUnreadCount(0); return; } @@ -24,8 +28,8 @@ export const NotificationBell: React.FC = ({ onPress, sty try { const count = await supabaseHelpers.getUnreadNotificationsCount(selectedAccount.address); setUnreadCount(count); - } catch (error) { - console.error('Failed to fetch unread count:', error); + } catch { + if (__DEV__) console.warn('Failed to fetch unread count'); // If tables don't exist yet, set to 0 setUnreadCount(0); } diff --git a/mobile/src/components/NotificationCenterModal.tsx b/mobile/src/components/NotificationCenterModal.tsx index 7c9d78a1..7a7e4dca 100644 --- a/mobile/src/components/NotificationCenterModal.tsx +++ b/mobile/src/components/NotificationCenterModal.tsx @@ -34,7 +34,7 @@ export const NotificationCenterModal: React.FC = ( }) => { const { selectedAccount } = usePezkuwi(); const [notifications, setNotifications] = useState([]); - const [loading, setLoading] = useState(false); + const [_loading, setLoading] = useState(false); useEffect(() => { if (visible && selectedAccount) { @@ -212,7 +212,7 @@ export const NotificationCenterModal: React.FC = ( 📬 No notifications - You're all caught up! + You're all caught up! ) : ( <> diff --git a/mobile/src/components/PezkuwiWebView.tsx b/mobile/src/components/PezkuwiWebView.tsx index 0b98838d..845f2dcc 100644 --- a/mobile/src/components/PezkuwiWebView.tsx +++ b/mobile/src/components/PezkuwiWebView.tsx @@ -168,7 +168,7 @@ const PezkuwiWebView: React.FC = ({ } // Get the transaction method from API - const txModule = api.tx[section]; + const txModule = api.tx[section] as Record { signAndSend: (...args: unknown[]) => Promise }> | undefined; if (!txModule) { throw new Error(`Unknown section: ${section}`); } diff --git a/mobile/src/components/PrivacyPolicyModal.tsx b/mobile/src/components/PrivacyPolicyModal.tsx index ea457fe9..8191856e 100644 --- a/mobile/src/components/PrivacyPolicyModal.tsx +++ b/mobile/src/components/PrivacyPolicyModal.tsx @@ -35,7 +35,7 @@ const PrivacyPolicyModal: React.FC = ({ visible, onClos Data Minimization Principle Pezkuwi collects the MINIMUM data necessary to provide blockchain wallet functionality. - We operate on a "your keys, your coins, your responsibility" model. + We operate on a "your keys, your coins, your responsibility" model. What Data We Collect @@ -63,10 +63,10 @@ const PrivacyPolicyModal: React.FC = ({ visible, onClos Never Collected: - Browsing History: We don't track which screens you visit + Browsing History: We don't track which screens you visit Device Identifiers: No IMEI, MAC address, or advertising ID collection Location Data: No GPS or location tracking - Contact Lists: We don't access your contacts + Contact Lists: We don't access your contacts Third-party Analytics: No Google Analytics, Facebook Pixel, or similar trackers diff --git a/mobile/src/components/TermsOfServiceModal.tsx b/mobile/src/components/TermsOfServiceModal.tsx index 4066008b..9e00a313 100644 --- a/mobile/src/components/TermsOfServiceModal.tsx +++ b/mobile/src/components/TermsOfServiceModal.tsx @@ -34,8 +34,8 @@ const TermsOfServiceModal: React.FC = ({ visible, onCl 1. Acceptance of Terms - By accessing or using the Pezkuwi mobile application ("App"), you agree to be bound by these - Terms of Service ("Terms"). If you do not agree to these Terms, do not use the App. + By accessing or using the Pezkuwi mobile application ("App"), you agree to be bound by these + Terms of Service ("Terms"). If you do not agree to these Terms, do not use the App. 2. Description of Service @@ -68,7 +68,7 @@ const TermsOfServiceModal: React.FC = ({ visible, onCl • Use the App for any illegal or unauthorized purpose - • Attempt to gain unauthorized access to other users' accounts + • Attempt to gain unauthorized access to other users' accounts • Interfere with or disrupt the App or servers • Upload malicious code or viruses • Engage in fraudulent transactions or money laundering @@ -110,7 +110,7 @@ const TermsOfServiceModal: React.FC = ({ visible, onCl 7. Disclaimer of Warranties - THE APP IS PROVIDED "AS IS" WITHOUT WARRANTIES OF ANY KIND. WE DO NOT GUARANTEE: + THE APP IS PROVIDED "AS IS" WITHOUT WARRANTIES OF ANY KIND. WE DO NOT GUARANTEE: • Uninterrupted or error-free service diff --git a/mobile/src/components/ValidatorSelectionSheet.tsx b/mobile/src/components/ValidatorSelectionSheet.tsx index fbf557f5..d37e5143 100644 --- a/mobile/src/components/ValidatorSelectionSheet.tsx +++ b/mobile/src/components/ValidatorSelectionSheet.tsx @@ -25,10 +25,10 @@ export function ValidatorSelectionSheet({ onClose, onConfirmNominations, }: ValidatorSelectionSheetProps) { - const { api, isApiReady, selectedAccount } = usePezkuwi(); + const { api, isApiReady, _selectedAccount } = usePezkuwi(); const [validators, setValidators] = useState([]); const [loading, setLoading] = useState(true); - const [processing, setProcessing] = useState(false); + const [processing, _setProcessing] = useState(false); const [selectedValidators, setSelectedValidators] = useState([]); // Fetch real validators from chain @@ -44,7 +44,7 @@ export function ValidatorSelectionSheet({ const rawValidators = await api.query.validatorPool.validators(); // Assuming rawValidators is a list of validator addresses or objects // This parsing logic will need adjustment based on the exact structure returned - for (const rawValidator of rawValidators.toHuman() as any[]) { // Adjust 'any' based on actual type + for (const rawValidator of rawValidators.toHuman() as unknown[]) { // Placeholder: Assume rawValidator is just an address for now chainValidators.push({ address: rawValidator.toString(), // or rawValidator.address if it's an object @@ -56,11 +56,11 @@ export function ValidatorSelectionSheet({ } } else { // Fallback to general staking validators if validatorPool pallet is not found/used - const rawStakingValidators = await api.query.staking.validators() as any; + const rawStakingValidators = await api.query.staking.validators() as { keys?: { args: unknown[] }[] }; for (const validatorAddress of (rawStakingValidators.keys || [])) { const address = validatorAddress.args[0].toString(); // Fetch more details about each validator if needed, e.g., commission, total stake - const validatorPrefs = await api.query.staking.validators(address) as any; + const validatorPrefs = await api.query.staking.validators(address) as { commission: { toNumber: () => number } }; const commission = validatorPrefs.commission.toNumber() / 10_000_000; // Assuming 10^7 for percentage // For simplicity, total stake and nominators are placeholders for now diff --git a/mobile/src/components/__tests__/BottomSheet.test.tsx b/mobile/src/components/__tests__/BottomSheet.test.tsx index 1ff538e8..0f852dcc 100644 --- a/mobile/src/components/__tests__/BottomSheet.test.tsx +++ b/mobile/src/components/__tests__/BottomSheet.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render, fireEvent } from '@testing-library/react-native'; +import { render } from '@testing-library/react-native'; import { Text } from 'react-native'; import { BottomSheet } from '../BottomSheet'; diff --git a/mobile/src/components/__tests__/Button.test.tsx b/mobile/src/components/__tests__/Button.test.tsx index 2baf1703..574d008f 100644 --- a/mobile/src/components/__tests__/Button.test.tsx +++ b/mobile/src/components/__tests__/Button.test.tsx @@ -90,7 +90,7 @@ describe('Button', () => { }); it('should render with icon', () => { - const { getByText, getByTestId } = render( + const { _getByText, getByTestId } = render(