diff --git a/screenshot.png b/screenshot.png index aa3f1af6..71e0e0d9 100644 Binary files a/screenshot.png and b/screenshot.png differ diff --git a/web/src/components/AppLayout.tsx b/web/src/components/AppLayout.tsx index 3ba3debd..207a2828 100644 --- a/web/src/components/AppLayout.tsx +++ b/web/src/components/AppLayout.tsx @@ -19,7 +19,7 @@ import { TreasuryOverview } from './treasury/TreasuryOverview'; import { FundingProposal } from './treasury/FundingProposal'; import { SpendingHistory } from './treasury/SpendingHistory'; import { MultiSigApproval } from './treasury/MultiSigApproval'; -import { ExternalLink, Award, FileEdit, Users2, MessageSquare, ShieldCheck, Wifi, WifiOff, Wallet, DollarSign, PiggyBank, History, Key, TrendingUp, ArrowRightLeft, Lock, LogIn, LayoutDashboard, Settings, Users, Droplet, Mail, Coins } from 'lucide-react'; +import { ExternalLink, Award, FileEdit, Users2, MessageSquare, ShieldCheck, Wifi, WifiOff, Wallet, DollarSign, PiggyBank, History, Key, TrendingUp, ArrowRightLeft, Lock, LogIn, LayoutDashboard, Settings, Users, Droplet, Mail, Coins, Menu, X } from 'lucide-react'; import GovernanceInterface from './GovernanceInterface'; import RewardDistribution from './RewardDistribution'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; @@ -27,7 +27,7 @@ import { useWebSocket } from '@/contexts/WebSocketContext'; import { StakingDashboard } from './staking/StakingDashboard'; import { MultiSigWallet } from './wallet/MultiSigWallet'; import { useWallet } from '@/contexts/WalletContext'; -import { PezkuwiWalletButton } from './PezkuwiWalletButton'; +import { WalletConnectModal } from './WalletConnectModal'; import { DEXDashboard } from './dex/DEXDashboard'; import { P2PDashboard } from './p2p/P2PDashboard'; import EducationPlatform from '../pages/EducationPlatform'; @@ -47,6 +47,7 @@ const AppLayout: React.FC = () => { const [showDEX, setShowDEX] = useState(false); const [showEducation, setShowEducation] = useState(false); const [showP2P, setShowP2P] = useState(false); + const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const { t } = useTranslation(); const { isConnected } = useWebSocket(); useWallet(); @@ -69,8 +70,19 @@ const AppLayout: React.FC = () => { PezkuwiChain - - {/* CENTER & RIGHT: Menu + Actions in same row */} + + {/* Mobile: Hamburger + Wallet */} +
+ + +
+ + {/* CENTER & RIGHT: Menu + Actions in same row (Desktop only) */}
{user ? ( <> @@ -270,12 +282,167 @@ const AppLayout: React.FC = () => { - +
+ {/* Mobile Menu Panel */} + {mobileMenuOpen && ( +
+ {/* Backdrop */} +
setMobileMenuOpen(false)} + /> + + {/* Menu Panel */} +
+
+ {user ? ( + <> + + + + + + + + +
+ +
Governance
+ + + + +
+ +
Trading
+ + + + + +
+ + + + + + ) : ( + <> + + + + )} + + +
+
+ )} + {/* Main Content */}
{/* Conditional Rendering for Features */} diff --git a/web/src/components/WalletConnectModal.tsx b/web/src/components/WalletConnectModal.tsx new file mode 100644 index 00000000..221f8095 --- /dev/null +++ b/web/src/components/WalletConnectModal.tsx @@ -0,0 +1,405 @@ +import React, { useState, useEffect } from 'react'; +import { usePezkuwi } from '@/contexts/PezkuwiContext'; +import { Button } from '@/components/ui/button'; +import { Badge } from '@/components/ui/badge'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { Wallet, Check, Copy, LogOut, Smartphone, Monitor, ExternalLink, QrCode } from 'lucide-react'; +import { useToast } from '@/hooks/use-toast'; + +// Detect if user is on mobile +const isMobile = (): boolean => { + if (typeof window === 'undefined') return false; + return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); +}; + +// Pezkuwi Wallet deep link +const PEZKUWI_WALLET_DEEP_LINK = 'pezkuwiwallet://'; +const PLAY_STORE_LINK = 'https://play.google.com/store/apps/details?id=io.novafoundation.nova.market'; +const APP_STORE_LINK = 'https://apps.apple.com/app/nova-polkadot-kusama-wallet/id1597119355'; + +type ConnectionMethod = 'select' | 'extension' | 'walletconnect'; + +export const WalletConnectModal: React.FC = () => { + const { + accounts, + selectedAccount, + setSelectedAccount, + connectWallet, + disconnectWallet, + error + } = usePezkuwi(); + + const [isOpen, setIsOpen] = useState(false); + const [connectionMethod, setConnectionMethod] = useState('select'); + const [showAccountSelect, setShowAccountSelect] = useState(false); + const { toast } = useToast(); + const mobile = isMobile(); + + // Reset to selection when modal opens + useEffect(() => { + if (isOpen && !selectedAccount) { + setConnectionMethod('select'); + } + }, [isOpen, selectedAccount]); + + const handleConnect = () => { + setIsOpen(true); + setConnectionMethod('select'); + }; + + const handleExtensionConnect = async () => { + setConnectionMethod('extension'); + await connectWallet(); + if (accounts.length > 0) { + setShowAccountSelect(true); + } + }; + + const handleWalletConnectConnect = () => { + setConnectionMethod('walletconnect'); + + if (mobile) { + // Try to open Pezkuwi Wallet app + // First try deep link, then fallback to store + const deepLink = PEZKUWI_WALLET_DEEP_LINK; + + // Create a hidden iframe to try the deep link + const iframe = document.createElement('iframe'); + iframe.style.display = 'none'; + iframe.src = deepLink; + document.body.appendChild(iframe); + + // Fallback to app store after timeout + setTimeout(() => { + document.body.removeChild(iframe); + // Check if app was opened by checking if page is still visible + if (!document.hidden) { + // App didn't open, redirect to store + const isIOS = /iPhone|iPad|iPod/i.test(navigator.userAgent); + window.location.href = isIOS ? APP_STORE_LINK : PLAY_STORE_LINK; + } + }, 2500); + + toast({ + title: "Pezkuwi Wallet açılıyor...", + description: "Uygulama yüklü değilse mağazaya yönlendirileceksiniz", + }); + } else { + // Desktop - show QR code or instructions + toast({ + title: "WalletConnect", + description: "QR kod desteği yakında eklenecek. Şimdilik browser extension kullanın.", + }); + } + }; + + const handleSelectAccount = (account: typeof accounts[0]) => { + setSelectedAccount(account); + setIsOpen(false); + setShowAccountSelect(false); + toast({ + title: "Hesap Bağlandı", + description: `${account.meta.name} - ${formatAddress(account.address)}`, + }); + }; + + const handleDisconnect = () => { + disconnectWallet(); + setIsOpen(false); + toast({ + title: "Cüzdan Bağlantısı Kesildi", + description: "Cüzdanınızın bağlantısı kesildi", + }); + }; + + const formatAddress = (address: string) => { + return `${address.slice(0, 6)}...${address.slice(-4)}`; + }; + + const copyAddress = () => { + if (selectedAccount) { + navigator.clipboard.writeText(selectedAccount.address); + toast({ + title: "Adres Kopyalandı", + description: "Adres panoya kopyalandı", + }); + } + }; + + // Connected state - show account info + if (selectedAccount) { + return ( +
+ + + + {/* Account Details Dialog */} + + + + Hesap Detayları + + Bağlı Pezkuwi hesabınız + + + +
+
+
Hesap Adı
+
+ {selectedAccount.meta.name || 'İsimsiz Hesap'} +
+
+ +
+
Adres
+
+ + {selectedAccount.address} + + +
+
+ +
+
Kaynak
+
+ {selectedAccount.meta.source || 'pezkuwi'} +
+
+ + {accounts.length > 1 && ( +
+
Hesap Değiştir
+
+ {accounts.map((account) => ( + + ))} +
+
+ )} +
+
+
+
+ ); + } + + // Not connected - show connect button + return ( + <> + + + {/* Connection Method Selection Dialog */} + + + + + Cüzdan Bağla + + + Bağlantı yönteminizi seçin + + + + {/* Connection Method Selection */} + {connectionMethod === 'select' && ( +
+ {/* Browser Extension Option - Hide on mobile */} + {!mobile && ( + + )} + + {/* Pezkuwi Wallet Option */} + + + {/* Download Wallet Link */} + +
+ )} + + {/* Extension Error State */} + {connectionMethod === 'extension' && error && error.includes('not found') && ( +
+
+
+ +
+

Extension Bulunamadı

+

+ Pezkuwi Wallet veya Polkadot.js extension yüklü değil +

+
+ + + + + + +
+ )} + + {/* WalletConnect State - Desktop QR */} + {connectionMethod === 'walletconnect' && !mobile && ( +
+
+
+
+ +

QR Kod

+

Yakında

+
+
+

+ Pezkuwi Wallet uygulamasıyla QR kodu tarayın +

+
+ + +
+ )} + + {/* Account Selection */} + {showAccountSelect && accounts.length > 0 && ( +
+
Hesap Seçin
+
+ {accounts.map((account) => ( + + ))} +
+
+ )} +
+
+ + ); +};