fix: TypeScript errors, shadow deprecations, and build configuration

- Fix shadow style deprecation warnings across components (boxShadow)
- Add type declaration files (codec.d.ts, modules.d.ts)
- Update metro.config.cjs for proper asset extensions
- Update tsconfig.json with module resolution settings
- Fix TypeScript errors in shared/lib files
- Update app icons (optimized PNG files)
This commit is contained in:
2026-01-15 09:37:37 +03:00
parent 0cac4023ff
commit ba74fe4298
28 changed files with 225 additions and 75 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

After

Width:  |  Height:  |  Size: 263 KiB

+9 -8
View File
@@ -23,22 +23,23 @@ const projectRoot = __dirname;
// FILE EXTENSIONS
// ============================================
// Extend default extensions instead of replacing them
config.resolver.sourceExts = [
...config.resolver.sourceExts,
'expo.ts',
'expo.tsx',
'expo.js',
'expo.jsx',
'ts',
'tsx',
'js',
'jsx',
'json',
'wasm',
'svg',
'cjs',
'mjs',
];
// SVG should be handled as source file for react-native-svg transformer
// Remove svg from assetExts if present, add to sourceExts
config.resolver.assetExts = config.resolver.assetExts.filter(ext => ext !== 'svg');
if (!config.resolver.sourceExts.includes('svg')) {
config.resolver.sourceExts.push('svg');
}
// ============================================
// NODE POLYFILLS
// ============================================
+8 -3
View File
@@ -38,7 +38,7 @@
"@pezkuwi/util": "14.0.11",
"@pezkuwi/util-crypto": "14.0.11",
"@react-native-async-storage/async-storage": "^2.2.0",
"@react-native-picker/picker": "^2.9.2",
"@react-native-picker/picker": "2.11.1",
"@react-navigation/bottom-tabs": "^7.8.5",
"@react-navigation/native": "^7.1.20",
"@react-navigation/stack": "^7.6.4",
@@ -88,9 +88,9 @@
"@pezkuwi/util-crypto": "14.0.11"
},
"devDependencies": {
"@expo/ngrok": "^4.1.0",
"@pezkuwi/extension-dapp": "0.62.14",
"@pezkuwi/extension-inject": "0.62.14",
"@expo/ngrok": "^4.1.0",
"@testing-library/jest-native": "^5.4.3",
"@testing-library/react-native": "^13.3.3",
"@types/invariant": "^2",
@@ -110,5 +110,10 @@
"typescript": "~5.9.2",
"typescript-eslint": "^8.47.0"
},
"private": true
"private": true,
"expo": {
"install": {
"exclude": ["@types/react"]
}
}
}
+7 -2
View File
@@ -47,7 +47,12 @@ export const Card: React.FC<CardProps> = ({
testID={testID}
onPress={onPress}
style={({ pressed }) => [
...cardStyle,
styles.card,
variant === 'elevated' && styles.elevated,
variant === 'outlined' && styles.outlined,
variant === 'filled' && styles.filled,
elevation ? { elevation } : null,
style,
pressed && styles.pressed,
]}
>
@@ -56,7 +61,7 @@ export const Card: React.FC<CardProps> = ({
);
}
return <View testID={testID} style={cardStyle}>{content}</View>;
return <View testID={testID} style={cardStyle as any}>{content}</View>;
};
const styles = StyleSheet.create({
@@ -90,7 +90,7 @@ const ChangePasswordModal: React.FC<ChangePasswordModalProps> = ({
{
text: 'Send Reset Link',
onPress: async () => {
const { error } = await resetPassword(user.email);
const { error } = await resetPassword(user.email!);
if (error) {
Alert.alert('Error', error.message || 'Failed to send reset email');
} else {
+4 -4
View File
@@ -16,6 +16,7 @@ interface InputProps extends TextInputProps {
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
onRightIconPress?: () => void;
disabled?: boolean;
}
/**
@@ -30,6 +31,7 @@ export const Input: React.FC<InputProps> = ({
rightIcon,
onRightIconPress,
style,
disabled,
...props
}) => {
const [isFocused, setIsFocused] = useState(false);
@@ -58,8 +60,8 @@ export const Input: React.FC<InputProps> = ({
{leftIcon && <View style={styles.leftIcon}>{leftIcon}</View>}
<TextInput
{...props}
editable={props.editable !== undefined ? props.editable : !props.disabled}
style={[styles.input, leftIcon && styles.inputWithLeftIcon, style]}
editable={props.editable !== undefined ? props.editable : !disabled}
style={[styles.input, leftIcon && styles.inputWithLeftIcon, style] as any}
onFocus={(e) => {
setIsFocused(true);
props.onFocus?.(e);
@@ -94,7 +96,6 @@ const styles = StyleSheet.create({
fontWeight: '500',
color: AppColors.textSecondary,
marginBottom: 8,
transition: 'all 0.2s',
},
labelFocused: {
color: KurdistanColors.kesk,
@@ -115,7 +116,6 @@ const styles = StyleSheet.create({
inputContainerFocused: {
borderColor: KurdistanColors.kesk,
borderWidth: 2,
boxShadow: '0px 2px 4px rgba(0, 128, 0, 0.1)',
elevation: 2,
},
inputContainerError: {
+1 -1
View File
@@ -49,7 +49,7 @@ export const Skeleton: React.FC<SkeletonProps> = ({
<Animated.View
style={[
styles.skeleton,
{ width, height, borderRadius, opacity },
{ width, height, borderRadius, opacity } as any,
style,
]}
/>
@@ -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();
for (const validatorAddress of rawStakingValidators.keys) {
const rawStakingValidators = await api.query.staking.validators() as any;
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);
const validatorPrefs = await api.query.staking.validators(address) as any;
const commission = validatorPrefs.commission.toNumber() / 10_000_000; // Assuming 10^7 for percentage
// For simplicity, total stake and nominators are placeholders for now
@@ -3,11 +3,16 @@ import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
import { KurdistanColors } from '../../theme/colors';
import type { StackHeaderProps } from '@react-navigation/stack';
import type { BottomTabHeaderProps } from '@react-navigation/bottom-tabs';
interface GradientHeaderProps extends StackHeaderProps {
type HeaderPropsBase = StackHeaderProps | BottomTabHeaderProps;
interface GradientHeaderProps extends Omit<HeaderPropsBase, 'progress' | 'styleInterpolator'> {
subtitle?: string;
rightButtons?: React.ReactNode;
gradientColors?: [string, string];
progress?: unknown;
styleInterpolator?: unknown;
}
export const GradientHeader: React.FC<GradientHeaderProps> = ({
@@ -18,11 +23,12 @@ export const GradientHeader: React.FC<GradientHeaderProps> = ({
rightButtons,
gradientColors = [KurdistanColors.kesk, '#008f43'],
}) => {
const title = options.headerTitle !== undefined
? options.headerTitle
: options.title !== undefined
? options.title
: route.name;
const getTitle = () => {
if (typeof options.headerTitle === 'string') return options.headerTitle;
if (typeof options.title === 'string') return options.title;
return route.name;
};
const title = getTitle();
const canGoBack = navigation.canGoBack();
@@ -51,8 +57,10 @@ export const GradientHeader: React.FC<GradientHeaderProps> = ({
);
};
interface SimpleHeaderProps extends StackHeaderProps {
interface SimpleHeaderProps extends Omit<HeaderPropsBase, 'progress' | 'styleInterpolator'> {
rightButtons?: React.ReactNode;
progress?: unknown;
styleInterpolator?: unknown;
}
export const SimpleHeader: React.FC<SimpleHeaderProps> = ({
@@ -61,11 +69,12 @@ export const SimpleHeader: React.FC<SimpleHeaderProps> = ({
route,
rightButtons,
}) => {
const title = options.headerTitle !== undefined
? options.headerTitle
: options.title !== undefined
? options.title
: route.name;
const getTitle = () => {
if (typeof options.headerTitle === 'string') return options.headerTitle;
if (typeof options.title === 'string') return options.title;
return route.name;
};
const title = getTitle();
const canGoBack = navigation.canGoBack();
@@ -104,8 +113,10 @@ export const BackButton: React.FC<{ onPress?: () => void; color?: string }> = ({
);
};
interface AppBarHeaderProps extends StackHeaderProps {
interface AppBarHeaderProps extends Omit<HeaderPropsBase, 'progress' | 'styleInterpolator'> {
rightButtons?: React.ReactNode;
progress?: unknown;
styleInterpolator?: unknown;
}
export const AppBarHeader: React.FC<AppBarHeaderProps> = ({
@@ -114,11 +125,12 @@ export const AppBarHeader: React.FC<AppBarHeaderProps> = ({
route,
rightButtons,
}) => {
const title = options.headerTitle !== undefined
? options.headerTitle
: options.title !== undefined
? options.title
: route.name;
const getTitle = () => {
if (typeof options.headerTitle === 'string') return options.headerTitle;
if (typeof options.title === 'string') return options.title;
return route.name;
};
const title = getTitle();
const canGoBack = navigation.canGoBack();
+1 -1
View File
@@ -173,7 +173,7 @@ export const PezkuwiProvider: React.FC<PezkuwiProviderProps> = ({ children }) =>
newApi.registry.setChainProperties(
newApi.registry.createType('ChainProperties', {
ss58Format: networkConfig.ss58Format,
})
}) as any
);
console.log(`✅ [Pezkuwi] API created with SS58 format: ${networkConfig.ss58Format}`);
+1 -1
View File
@@ -52,7 +52,7 @@ const AuthScreen: React.FC = () => {
setLoading(true);
try {
const { error: signInError } = await signIn(loginEmail, loginPassword, rememberMe);
const { error: signInError } = await signIn(loginEmail, loginPassword);
if (signInError) {
if (signInError.message?.includes('Invalid login credentials')) {
+2 -2
View File
@@ -411,7 +411,7 @@ const DashboardScreen: React.FC<DashboardScreenProps> = () => {
{kycStatus === 'NotStarted' && (
<TouchableOpacity
style={styles.kycButton}
onPress={() => navigation.navigate('BeCitizen')}
onPress={() => navigation.navigate('BeCitizenChoice')}
>
<Text style={styles.kycButtonText}>Apply</Text>
</TouchableOpacity>
@@ -454,7 +454,7 @@ const DashboardScreen: React.FC<DashboardScreenProps> = () => {
{renderAppIcon('Justice', '⚖️', () => showComingSoon('Dad / Justice'), true, true)}
{renderAppIcon('Proposals', '📜', () => showComingSoon('Proposals'), true, true)}
{renderAppIcon('Polls', '📊', () => showComingSoon('Public Polls'), true, true)}
{renderAppIcon('Identity', '🆔', () => navigation.navigate('BeCitizen'), true)}
{renderAppIcon('Identity', '🆔', () => navigation.navigate('BeCitizenChoice'), true)}
</View>
</View>
+1 -1
View File
@@ -68,7 +68,7 @@ export default function NFTGalleryScreen() {
description: 'Official citizenship NFT of Digital Kurdistan. This NFT represents your verified status as a citizen of the Pezkuwi nation.',
image: '🪪', // Will use emoji/icon for now
rarity: 'legendary',
mintDate: new Date(nftData?.mintedAt || Date.now()).toISOString(),
mintDate: new Date((nftData?.mintedAt as number | string | undefined) || Date.now()).toISOString(),
attributes: [
{ trait: 'Type', value: 'Citizenship' },
{ trait: 'Nation', value: 'Kurdistan' },
+4 -4
View File
@@ -91,18 +91,18 @@ const PoolBrowserScreen: React.FC = () => {
try {
if (asset1 === 0) {
// Native token (wHEZ)
const balance1 = await api.query.system.account(poolAccount);
const balance1 = await api.query.system.account(poolAccount) as any;
reserve1 = balance1.data.free.toString();
} else {
const balance1 = await api.query.assets.account(asset1, poolAccount);
const balance1 = await api.query.assets.account(asset1, poolAccount) as any;
reserve1 = balance1.isSome ? balance1.unwrap().balance.toString() : '0';
}
if (asset2 === 0) {
const balance2 = await api.query.system.account(poolAccount);
const balance2 = await api.query.system.account(poolAccount) as any;
reserve2 = balance2.data.free.toString();
} else {
const balance2 = await api.query.assets.account(asset2, poolAccount);
const balance2 = await api.query.assets.account(asset2, poolAccount) as any;
reserve2 = balance2.isSome ? balance2.unwrap().balance.toString() : '0';
}
} catch (error) {
+3 -2
View File
@@ -18,7 +18,8 @@ import {
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as SecureStore from 'expo-secure-store';
import { Clipboard } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { useNavigation, NavigationProp } from '@react-navigation/native';
import { RootStackParamList } from '../navigation/AppNavigator';
import { KurdistanColors } from '../theme/colors';
import { useTheme } from '../contexts/ThemeContext';
import { useBiometricAuth } from '../contexts/BiometricAuthContext';
@@ -152,7 +153,7 @@ const SettingToggle = ({
// --- MAIN SCREEN ---
const SettingsScreen: React.FC = () => {
const navigation = useNavigation();
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
const { isDarkMode, toggleDarkMode, colors, fontSize, setFontSize } = useTheme();
const { isBiometricEnabled, enableBiometric, disableBiometric, biometricType, autoLockTimer, setAutoLockTimer } = useBiometricAuth();
const { signOut, user } = useAuth();
+1 -1
View File
@@ -70,7 +70,7 @@ export default function StakingScreen() {
const scores = await getAllScores(api, selectedAccount.address);
// 3. Get Current Era
const currentEraOpt = await api.query.staking.currentEra();
const currentEraOpt = await api.query.staking.currentEra() as any;
const currentEra = currentEraOpt.unwrapOrDefault().toNumber();
// Calculations
+4 -4
View File
@@ -78,10 +78,10 @@ const SwapScreen: React.FC = () => {
// Fetch From Token Balance
try {
if (fromToken.symbol === 'HEZ') {
const accountInfo = await api.query.system.account(selectedAccount.address);
const accountInfo = await api.query.system.account(selectedAccount.address) as any;
setFromBalance(accountInfo.data.free.toString());
} else {
const balanceData = await api.query.assets.account(fromToken.assetId, selectedAccount.address);
const balanceData = await api.query.assets.account(fromToken.assetId, selectedAccount.address) as any;
setFromBalance(balanceData.isSome ? balanceData.unwrap().balance.toString() : '0');
}
} catch (error) {
@@ -92,10 +92,10 @@ const SwapScreen: React.FC = () => {
// Fetch To Token Balance
try {
if (toToken.symbol === 'HEZ') {
const accountInfo = await api.query.system.account(selectedAccount.address);
const accountInfo = await api.query.system.account(selectedAccount.address) as any;
setToBalance(accountInfo.data.free.toString());
} else {
const balanceData = await api.query.assets.account(toToken.assetId, selectedAccount.address);
const balanceData = await api.query.assets.account(toToken.assetId, selectedAccount.address) as any;
setToBalance(balanceData.isSome ? balanceData.unwrap().balance.toString() : '0');
}
} catch (error) {
+4 -4
View File
@@ -205,13 +205,13 @@ const WalletScreen: React.FC = () => {
accountId = selectedAccount.address as any;
}
const accountInfo = await api.query.system.account(accountId);
const accountInfo = await api.query.system.account(accountId) as any;
const hezBalance = (Number(accountInfo.data.free.toString()) / 1e12).toFixed(2);
let pezBalance = '0.00';
try {
if (api.query.assets?.account) {
const pezAsset = await api.query.assets.account(1, accountId);
const pezAsset = await api.query.assets.account(1, accountId) as any;
if (pezAsset.isSome) pezBalance = (Number(pezAsset.unwrap().balance.toString()) / 1e12).toFixed(2);
}
} catch (e) {
@@ -222,9 +222,9 @@ const WalletScreen: React.FC = () => {
try {
if (api.query.assets?.account) {
// Check ID 1000 first (as per constants), fallback to 2 just in case
let usdtAsset = await api.query.assets.account(1000, accountId);
let usdtAsset = await api.query.assets.account(1000, accountId) as any;
if (usdtAsset.isNone) {
usdtAsset = await api.query.assets.account(2, accountId);
usdtAsset = await api.query.assets.account(2, accountId) as any;
}
if (usdtAsset.isSome) {
@@ -114,7 +114,7 @@ const DelegationScreen: React.FC = () => {
// Fetch user's delegations
if (selectedAccount) {
const userVoting = await api.query.democracy.voting(selectedAccount.address);
const userVoting = await api.query.democracy.voting(selectedAccount.address) as any;
if (userVoting.isDelegating) {
const delegating = userVoting.asDelegating;
setUserDelegations([{
@@ -47,12 +47,12 @@ const ElectionsScreen: React.FC = () => {
// Fetch commission proposals (acting as elections)
if (api.query.dynamicCommissionCollective?.proposals) {
const proposalHashes = await api.query.dynamicCommissionCollective.proposals();
const proposalHashes = await api.query.dynamicCommissionCollective.proposals() as any;
const electionsData: ElectionInfo[] = [];
for (const hash of proposalHashes) {
const voting = await api.query.dynamicCommissionCollective.voting(hash);
for (const hash of (proposalHashes || [])) {
const voting = await api.query.dynamicCommissionCollective.voting(hash) as any;
if (voting.isSome) {
const voteData = voting.unwrap();
electionsData.push({
@@ -111,7 +111,7 @@ const ForumScreen: React.FC = () => {
};
const getCategoryById = (categoryId: string): Category | undefined => {
return CATEGORIES.find(c => c.id === categoryId);
return categories.find((c: Category) => c.id === categoryId);
};
const getTimeAgo = (dateString: string): string => {
@@ -209,7 +209,7 @@ const ForumScreen: React.FC = () => {
📋 All Topics
</Text>
</TouchableOpacity>
{CATEGORIES.map((category) => (
{categories.map((category) => (
<TouchableOpacity
key={category.id}
style={[
+65
View File
@@ -0,0 +1,65 @@
/**
* Type augmentations for Polkadot.js Codec type
* These extend the base Codec type with Option-like methods
*/
import '@pezkuwi/types';
declare module '@pezkuwi/types/types/codec' {
interface Codec {
// Option methods
isNone: boolean;
isSome: boolean;
isEmpty: boolean;
unwrap(): any;
unwrapOr<T>(defaultValue: T): any;
unwrapOrDefault(): any;
// Primitive conversions
toNumber(): number;
toBigInt(): bigint;
toJSON(): any;
toString(): string;
toHex(): string;
// Common properties
data?: any;
free?: any;
balance?: any;
commission?: any;
keys?: any;
// Delegation checks
isDelegating?: boolean;
asDelegating?: any;
// Iterator support
[Symbol.iterator]?(): Iterator<any>;
map?<T>(fn: (value: any) => T): T[];
}
}
declare module '@pezkuwi/types-codec' {
interface Codec {
isNone: boolean;
isSome: boolean;
isEmpty: boolean;
unwrap(): any;
unwrapOr<T>(defaultValue: T): any;
unwrapOrDefault(): any;
toNumber(): number;
toBigInt(): bigint;
toJSON(): any;
toString(): string;
toHex(): string;
data?: any;
free?: any;
balance?: any;
commission?: any;
keys?: any;
isDelegating?: boolean;
asDelegating?: any;
[Symbol.iterator]?(): Iterator<any>;
map?<T>(fn: (value: any) => T): T[];
}
}
+58
View File
@@ -0,0 +1,58 @@
/**
* Type declarations for external modules
*/
// Pezkuwi extension types
declare module '@pezkuwi/extension-inject/types' {
import type { Signer } from '@pezkuwi/api/types';
export interface InjectedAccountWithMeta {
address: string;
meta: {
name?: string;
source: string;
genesisHash?: string;
};
}
export interface InjectedExtension {
name: string;
version: string;
accounts: {
get: () => Promise<InjectedAccountWithMeta[]>;
subscribe: (cb: (accounts: InjectedAccountWithMeta[]) => void) => () => void;
};
signer: Signer;
}
}
declare module '@pezkuwi/extension-dapp' {
import type { InjectedAccountWithMeta } from '@pezkuwi/extension-inject/types';
interface InjectedWeb3 {
signer: any;
name: string;
version: string;
}
export function web3Enable(appName: string): Promise<InjectedWeb3[]>;
export function web3Accounts(): Promise<InjectedAccountWithMeta[]>;
export function web3FromAddress(address: string): Promise<InjectedWeb3>;
export function web3FromSource(source: string): Promise<InjectedWeb3>;
}
// Path alias for shared lib - used in web context
declare module '@/lib/supabase' {
export const supabase: any;
}
// Import.meta.env for Vite-like environments
interface ImportMetaEnv {
readonly VITE_PINATA_API_KEY?: string;
readonly VITE_PINATA_SECRET_KEY?: string;
[key: string]: string | undefined;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
+6 -3
View File
@@ -8,7 +8,10 @@
"@pezkuwi/utils/*": ["../shared/utils/*"],
"@pezkuwi/theme/*": ["../shared/theme/*"],
"@pezkuwi/types/*": ["../shared/types/*"],
"@pezkuwi/i18n": ["../shared/i18n"]
}
}
"@pezkuwi/i18n": ["../shared/i18n"],
"@/*": ["src/*"]
},
"typeRoots": ["./src/types", "./node_modules/@types"]
},
"include": ["src/**/*.ts", "src/**/*.tsx", "shared/**/*.ts"]
}
+3 -3
View File
@@ -261,7 +261,7 @@ export async function isStakingScoreTracking(
return false;
}
const startBlock = await api.query.stakingScore.stakingStartBlock(address);
const startBlock = await api.query.stakingScore.stakingStartBlock(address) as any;
return !startBlock.isNone;
} catch (error) {
console.error('Error checking staking score tracking:', error);
@@ -281,7 +281,7 @@ export async function isStaking(
return false;
}
const ledger = await api.query.staking.ledger(address);
const ledger = await api.query.staking.ledger(address) as any;
return !ledger.isNone;
} catch (error) {
console.error('Error checking staking status:', error);
@@ -370,7 +370,7 @@ export async function getCitizenDataCid(
// Try to get from identity storage
// This assumes the pallet stores IPFS CID somewhere
// Adjust based on actual pallet storage structure
const identity = await api.query.identityKyc.identities(address);
const identity = await api.query.identityKyc.identities(address) as any;
if (identity.isNone) {
return null;
+1 -1
View File
@@ -289,7 +289,7 @@ export async function checkStakingScoreTracking(
return false;
}
const startBlock = await api.query.stakingScore.stakingStartBlock(address);
const startBlock = await api.query.stakingScore.stakingStartBlock(address) as any;
return !startBlock.isNone;
} catch (error) {
console.error('Error checking staking score tracking:', error);
+2 -2
View File
@@ -50,7 +50,7 @@ export async function getMultisigMembers(
if (memberConfig.isUnique) {
// Query from chain for unique roles
try {
const holder = await api.query.tiki.tikiHolder(memberConfig.tiki);
const holder = await api.query.tiki.tikiHolder(memberConfig.tiki) as any;
if (holder.isSome) {
const address = holder.unwrap().toString();
members.push(address);
@@ -137,7 +137,7 @@ export async function getMultisigMemberInfo(
if (memberConfig.isUnique) {
try {
const holder = await api.query.tiki.tikiHolder(memberConfig.tiki);
const holder = await api.query.tiki.tikiHolder(memberConfig.tiki) as any;
if (holder.isSome) {
address = holder.unwrap().toString();
}