mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-04-22 19:27:56 +00:00
chore: Fix 430+ TS errors, Safe Area issues, WebView SSO, and mock Alice for testing
This commit is contained in:
@@ -9,6 +9,7 @@ import {
|
||||
Image,
|
||||
ActivityIndicator,
|
||||
Platform,
|
||||
Alert,
|
||||
} from 'react-native';
|
||||
import * as ImagePicker from 'expo-image-picker';
|
||||
import { KurdistanColors } from '../theme/colors';
|
||||
@@ -21,7 +22,7 @@ const showAlert = (title: string, message: string, buttons?: Array<{text: string
|
||||
window.alert(`${title}\n\n${message}`);
|
||||
if (buttons?.[0]?.onPress) buttons[0].onPress();
|
||||
} else {
|
||||
showAlert(title, message, buttons);
|
||||
Alert.alert(title, message, buttons);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -109,7 +110,7 @@ const AvatarPickerModal: React.FC<AvatarPickerModalProps> = ({
|
||||
setIsUploading(true);
|
||||
const imageUri = result.assets[0].uri;
|
||||
|
||||
if (__DEV__) console.log('[AvatarPicker] Uploading image:', imageUri);
|
||||
if (__DEV__) console.warn('[AvatarPicker] Uploading image:', imageUri);
|
||||
|
||||
// Upload to Supabase Storage
|
||||
const uploadedUrl = await uploadImageToSupabase(imageUri);
|
||||
@@ -117,7 +118,7 @@ const AvatarPickerModal: React.FC<AvatarPickerModalProps> = ({
|
||||
setIsUploading(false);
|
||||
|
||||
if (uploadedUrl) {
|
||||
if (__DEV__) console.log('[AvatarPicker] Upload successful:', uploadedUrl);
|
||||
if (__DEV__) console.warn('[AvatarPicker] Upload successful:', uploadedUrl);
|
||||
setUploadedImageUri(uploadedUrl);
|
||||
setSelectedAvatar(null); // Clear emoji selection
|
||||
showAlert('Success', 'Photo uploaded successfully!');
|
||||
@@ -215,7 +216,7 @@ const AvatarPickerModal: React.FC<AvatarPickerModalProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
if (__DEV__) console.log('[AvatarPicker] Saving avatar:', avatarToSave);
|
||||
if (__DEV__) console.warn('[AvatarPicker] Saving avatar:', avatarToSave);
|
||||
|
||||
setIsSaving(true);
|
||||
|
||||
@@ -232,7 +233,7 @@ const AvatarPickerModal: React.FC<AvatarPickerModalProps> = ({
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (__DEV__) console.log('[AvatarPicker] Avatar saved successfully:', data);
|
||||
if (__DEV__) console.warn('[AvatarPicker] Avatar saved successfully:', data);
|
||||
|
||||
showAlert('Success', 'Avatar updated successfully!');
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ export const Input: React.FC<InputProps> = ({
|
||||
<TextInput
|
||||
{...props}
|
||||
editable={props.editable !== undefined ? props.editable : !disabled}
|
||||
style={[styles.input, leftIcon && styles.inputWithLeftIcon, style]}
|
||||
style={[styles.input, !!leftIcon && styles.inputWithLeftIcon, style]}
|
||||
onFocus={(e) => {
|
||||
setIsFocused(true);
|
||||
props.onFocus?.(e);
|
||||
|
||||
@@ -49,7 +49,7 @@ export const Skeleton: React.FC<SkeletonProps> = ({
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.skeleton,
|
||||
{ width, height, borderRadius, opacity },
|
||||
{ width: width as any, height, borderRadius, opacity },
|
||||
style,
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
TouchableOpacity,
|
||||
Alert,
|
||||
} from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { KurdistanColors } from '../theme/colors';
|
||||
import { usePezkuwi } from '../contexts/PezkuwiContext';
|
||||
import { supabaseHelpers } from '../lib/supabase';
|
||||
@@ -32,6 +33,7 @@ export const NotificationCenterModal: React.FC<NotificationCenterModalProps> = (
|
||||
visible,
|
||||
onClose,
|
||||
}) => {
|
||||
const insets = useSafeAreaInsets();
|
||||
const { selectedAccount } = usePezkuwi();
|
||||
const [notifications, setNotifications] = useState<Notification[]>([]);
|
||||
const [_loading, setLoading] = useState(false);
|
||||
@@ -178,7 +180,7 @@ export const NotificationCenterModal: React.FC<NotificationCenterModalProps> = (
|
||||
onRequestClose={onClose}
|
||||
>
|
||||
<View style={styles.modalOverlay}>
|
||||
<View style={styles.modalContent}>
|
||||
<View style={[styles.modalContent, { paddingBottom: Math.max(insets.bottom, 20) }]}>
|
||||
{/* Header */}
|
||||
<View style={styles.header}>
|
||||
<View>
|
||||
|
||||
@@ -14,6 +14,8 @@ import { useFocusEffect, useNavigation } from '@react-navigation/native';
|
||||
import type { NavigationProp } from '@react-navigation/native';
|
||||
import { KurdistanColors } from '../theme/colors';
|
||||
import { usePezkuwi } from '../contexts/PezkuwiContext';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { supabase } from '../lib/supabase';
|
||||
|
||||
type RootStackParamList = {
|
||||
Wallet: undefined;
|
||||
@@ -49,6 +51,25 @@ const PezkuwiWebView: React.FC<PezkuwiWebViewProps> = ({
|
||||
|
||||
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
|
||||
const { selectedAccount, getKeyPair, api, isApiReady } = usePezkuwi();
|
||||
const { user } = useAuth();
|
||||
const [sessionToken, setSessionToken] = useState<string | null>(null);
|
||||
const [refreshToken, setRefreshToken] = useState<string | null>(null);
|
||||
|
||||
// Get Supabase session token for WebView authentication
|
||||
React.useEffect(() => {
|
||||
const getSession = async () => {
|
||||
try {
|
||||
const { data: { session } } = await supabase.auth.getSession();
|
||||
if (session?.access_token) {
|
||||
setSessionToken(session.access_token);
|
||||
setRefreshToken(session.refresh_token || null);
|
||||
}
|
||||
} catch (error) {
|
||||
if (__DEV__) console.warn('[WebView] Failed to get session:', error);
|
||||
}
|
||||
};
|
||||
getSession();
|
||||
}, [user]);
|
||||
|
||||
// JavaScript to inject into the WebView
|
||||
// This creates a bridge between the web app and native app
|
||||
@@ -62,6 +83,36 @@ const PezkuwiWebView: React.FC<PezkuwiWebViewProps> = ({
|
||||
${selectedAccount ? `window.PEZKUWI_ADDRESS = '${selectedAccount.address}';` : ''}
|
||||
${selectedAccount ? `window.PEZKUWI_ACCOUNT_NAME = '${selectedAccount.meta?.name || 'Mobile Wallet'}';` : ''}
|
||||
|
||||
// Inject auth session for automatic login
|
||||
${sessionToken ? `window.PEZKUWI_SESSION_TOKEN = '${sessionToken}';` : ''}
|
||||
${refreshToken ? `window.PEZKUWI_REFRESH_TOKEN = '${refreshToken}';` : ''}
|
||||
${user ? `window.PEZKUWI_USER_ID = '${user.id}';` : ''}
|
||||
${user?.email ? `window.PEZKUWI_USER_EMAIL = '${user.email}';` : ''}
|
||||
|
||||
// Auto-authenticate with Supabase if session token exists
|
||||
if (window.PEZKUWI_SESSION_TOKEN) {
|
||||
(function autoAuth(attempts = 0) {
|
||||
if (attempts > 50) {
|
||||
console.warn('[Mobile] Auto-auth timed out: window.supabase not found');
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.supabase && window.supabase.auth) {
|
||||
window.supabase.auth.setSession({
|
||||
access_token: window.PEZKUWI_SESSION_TOKEN,
|
||||
refresh_token: window.PEZKUWI_REFRESH_TOKEN || ''
|
||||
}).then(function(res) {
|
||||
if (res.error) console.warn('[Mobile] Auto-auth error:', res.error);
|
||||
else console.log('[Mobile] Auto-authenticated successfully');
|
||||
}).catch(function(err) {
|
||||
console.warn('[Mobile] Auto-auth promise failed:', err);
|
||||
});
|
||||
} else {
|
||||
setTimeout(function() { autoAuth(attempts + 1); }, 100);
|
||||
}
|
||||
})(0);
|
||||
}
|
||||
|
||||
// Override console.log to send to React Native (for debugging)
|
||||
const originalConsoleLog = console.log;
|
||||
console.log = function(...args) {
|
||||
@@ -164,7 +215,7 @@ const PezkuwiWebView: React.FC<PezkuwiWebViewProps> = ({
|
||||
const { section, method, args } = payload;
|
||||
|
||||
if (__DEV__) {
|
||||
console.log('[WebView] Building transaction:', { section, method, args });
|
||||
console.warn('[WebView] Building transaction:', { section, method, args });
|
||||
}
|
||||
|
||||
// Get the transaction method from API
|
||||
@@ -187,13 +238,13 @@ const PezkuwiWebView: React.FC<PezkuwiWebViewProps> = ({
|
||||
if (result.status.isInBlock) {
|
||||
const hash = result.status.asInBlock?.toString() || '';
|
||||
if (__DEV__) {
|
||||
console.log('[WebView] Transaction included in block:', hash);
|
||||
console.warn('[WebView] Transaction included in block:', hash);
|
||||
}
|
||||
resolve(hash);
|
||||
} else if (result.status.isFinalized) {
|
||||
const hash = result.status.asFinalized?.toString() || '';
|
||||
if (__DEV__) {
|
||||
console.log('[WebView] Transaction finalized:', hash);
|
||||
console.warn('[WebView] Transaction finalized:', hash);
|
||||
}
|
||||
}
|
||||
if (result.dispatchError) {
|
||||
@@ -222,7 +273,7 @@ const PezkuwiWebView: React.FC<PezkuwiWebViewProps> = ({
|
||||
|
||||
case 'CONNECT_WALLET':
|
||||
// Handle wallet connection request from WebView
|
||||
if (__DEV__) console.log('WebView requested wallet connection');
|
||||
if (__DEV__) console.warn('WebView requested wallet connection');
|
||||
|
||||
if (selectedAccount) {
|
||||
// Already connected, notify WebView
|
||||
@@ -267,13 +318,13 @@ const PezkuwiWebView: React.FC<PezkuwiWebViewProps> = ({
|
||||
case 'CONSOLE_LOG':
|
||||
// Forward console logs from WebView (debug only)
|
||||
if (__DEV__) {
|
||||
console.log('[WebView]:', message.payload);
|
||||
console.warn('[WebView]:', message.payload);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (__DEV__) {
|
||||
console.log('Unknown message type:', message.type);
|
||||
console.warn('Unknown message type:', message.type);
|
||||
}
|
||||
}
|
||||
} catch (parseError) {
|
||||
|
||||
@@ -25,7 +25,7 @@ export function ValidatorSelectionSheet({
|
||||
onClose,
|
||||
onConfirmNominations,
|
||||
}: ValidatorSelectionSheetProps) {
|
||||
const { api, isApiReady, _selectedAccount } = usePezkuwi();
|
||||
const { api, isApiReady } = usePezkuwi();
|
||||
const [validators, setValidators] = useState<Validator[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [processing, _setProcessing] = useState(false);
|
||||
@@ -42,29 +42,28 @@ export function ValidatorSelectionSheet({
|
||||
// Attempt to fetch from pallet-validator-pool first
|
||||
if (api.query.validatorPool && api.query.validatorPool.validators) {
|
||||
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 unknown[]) {
|
||||
// Placeholder: Assume rawValidator is just an address for now
|
||||
const validatorList = rawValidators.toHuman() as string[];
|
||||
for (const rawValidator of validatorList) {
|
||||
chainValidators.push({
|
||||
address: rawValidator.toString(), // or rawValidator.address if it's an object
|
||||
commission: 0.05, // Placeholder: Fetch actual commission
|
||||
totalStake: '0 HEZ', // Placeholder: Fetch actual stake
|
||||
selfStake: '0 HEZ', // Placeholder: Fetch actual self stake
|
||||
nominators: 0, // Placeholder: Fetch actual nominators
|
||||
address: String(rawValidator),
|
||||
commission: 0.05,
|
||||
totalStake: '0 HEZ',
|
||||
selfStake: '0 HEZ',
|
||||
nominators: 0,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Fallback to general staking validators if validatorPool pallet is not found/used
|
||||
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 { commission: { toNumber: () => number } };
|
||||
const commission = validatorPrefs.commission.toNumber() / 10_000_000; // Assuming 10^7 for percentage
|
||||
// Fallback to session validators
|
||||
const sessionValidators = await api.query.session.validators();
|
||||
const validatorAddresses = sessionValidators.toJSON() as string[];
|
||||
|
||||
for (const address of validatorAddresses) {
|
||||
const validatorPrefs = await api.query.staking.validators(address);
|
||||
const prefsJson = validatorPrefs.toJSON() as { commission?: number } | null;
|
||||
const commission = prefsJson?.commission
|
||||
? Number(prefsJson.commission) / 1_000_000_000
|
||||
: 0.05;
|
||||
|
||||
// For simplicity, total stake and nominators are placeholders for now
|
||||
// A more complete implementation would query for detailed exposure
|
||||
chainValidators.push({
|
||||
address: address,
|
||||
commission: commission,
|
||||
|
||||
@@ -56,9 +56,9 @@ export const AddTokenModal: React.FC<AddTokenModalProps> = ({
|
||||
} else {
|
||||
const metadata = metadataOption.toJSON() as { symbol?: string; decimals?: number; name?: string } | null;
|
||||
setTokenMetadata({
|
||||
symbol: metadata.symbol || 'UNKNOWN',
|
||||
decimals: metadata.decimals || 12,
|
||||
name: metadata.name || 'Unknown Token',
|
||||
symbol: metadata?.symbol || 'UNKNOWN',
|
||||
decimals: metadata?.decimals || 12,
|
||||
name: metadata?.name || 'Unknown Token',
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
|
||||
Reference in New Issue
Block a user