mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-06-13 23:51:01 +00:00
feat: wallet modal with two connection options and Kurdistan flag for language switcher
This commit is contained in:
@@ -0,0 +1,33 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 600">
|
||||||
|
<!-- Red stripe -->
|
||||||
|
<rect width="900" height="200" fill="#ED2024"/>
|
||||||
|
<!-- White stripe -->
|
||||||
|
<rect y="200" width="900" height="200" fill="#FFFFFF"/>
|
||||||
|
<!-- Green stripe -->
|
||||||
|
<rect y="400" width="900" height="200" fill="#21A038"/>
|
||||||
|
<!-- Yellow sun (21 rays) -->
|
||||||
|
<circle cx="450" cy="300" r="100" fill="#FECC02"/>
|
||||||
|
<g fill="#FECC02">
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(0,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(17.14,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(34.29,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(51.43,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(68.57,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(85.71,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(102.86,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(120,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(137.14,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(154.29,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(171.43,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(188.57,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(205.71,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(222.86,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(240,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(257.14,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(274.29,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(291.43,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(308.57,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(325.71,450,300)"/>
|
||||||
|
<polygon points="450,145 440,200 460,200" transform="rotate(342.86,450,300)"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -8,7 +8,7 @@ import PalletsGrid from './PalletsGrid';
|
|||||||
import ChainSpecs from './ChainSpecs';
|
import ChainSpecs from './ChainSpecs';
|
||||||
import TrustScoreCalculator from './TrustScoreCalculator';
|
import TrustScoreCalculator from './TrustScoreCalculator';
|
||||||
import { NetworkStats } from './NetworkStats';
|
import { NetworkStats } from './NetworkStats';
|
||||||
import { WalletModal } from './wallet/WalletModal';
|
|
||||||
import { LanguageSwitcher } from './LanguageSwitcher';
|
import { LanguageSwitcher } from './LanguageSwitcher';
|
||||||
import NotificationBell from './notifications/NotificationBell';
|
import NotificationBell from './notifications/NotificationBell';
|
||||||
import ProposalWizard from './proposals/ProposalWizard';
|
import ProposalWizard from './proposals/ProposalWizard';
|
||||||
@@ -34,7 +34,6 @@ import EducationPlatform from '../pages/EducationPlatform';
|
|||||||
|
|
||||||
const AppLayout: React.FC = () => {
|
const AppLayout: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [walletModalOpen, setWalletModalOpen] = useState(false);
|
|
||||||
const { user, signOut } = useAuth();
|
const { user, signOut } = useAuth();
|
||||||
const [showProposalWizard, setShowProposalWizard] = useState(false);
|
const [showProposalWizard, setShowProposalWizard] = useState(false);
|
||||||
const [showDelegation, setShowDelegation] = useState(false);
|
const [showDelegation, setShowDelegation] = useState(false);
|
||||||
@@ -577,9 +576,6 @@ const AppLayout: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{/* Wallet Modal */}
|
|
||||||
<WalletModal isOpen={walletModalOpen} onClose={() => setWalletModalOpen(false)} />
|
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<footer className="bg-gray-950 border-t border-gray-800 py-12">
|
<footer className="bg-gray-950 border-t border-gray-800 py-12">
|
||||||
<div className="max-w-full mx-auto px-4">
|
<div className="max-w-full mx-auto px-4">
|
||||||
|
|||||||
@@ -39,7 +39,11 @@ export function LanguageSwitcher() {
|
|||||||
<Button variant="ghost" size="sm" className="gap-2">
|
<Button variant="ghost" size="sm" className="gap-2">
|
||||||
<Globe className="h-4 w-4" />
|
<Globe className="h-4 w-4" />
|
||||||
<span className="hidden sm:inline">{currentLanguage.name}</span>
|
<span className="hidden sm:inline">{currentLanguage.name}</span>
|
||||||
<span className="text-lg">{currentLanguage.flag}</span>
|
{currentLanguage.flagImg ? (
|
||||||
|
<img src={currentLanguage.flagImg} alt="" className="h-5 w-5 rounded-sm object-cover" />
|
||||||
|
) : (
|
||||||
|
<span className="text-lg">{currentLanguage.flag}</span>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end" className="w-48">
|
<DropdownMenuContent align="end" className="w-48">
|
||||||
@@ -49,7 +53,11 @@ export function LanguageSwitcher() {
|
|||||||
onClick={() => changeLanguage(code)}
|
onClick={() => changeLanguage(code)}
|
||||||
className={`cursor-pointer ${i18n.language === code ? 'bg-yellow-100 dark:bg-yellow-900' : ''}`}
|
className={`cursor-pointer ${i18n.language === code ? 'bg-yellow-100 dark:bg-yellow-900' : ''}`}
|
||||||
>
|
>
|
||||||
<span className="text-lg mr-2">{lang.flag}</span>
|
{lang.flagImg ? (
|
||||||
|
<img src={lang.flagImg} alt="" className="h-5 w-5 rounded-sm object-cover mr-2" />
|
||||||
|
) : (
|
||||||
|
<span className="text-lg mr-2">{lang.flag}</span>
|
||||||
|
)}
|
||||||
<span>{lang.name}</span>
|
<span>{lang.name}</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -3,48 +3,22 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { usePezkuwi } from '@/contexts/PezkuwiContext';
|
import { usePezkuwi } from '@/contexts/PezkuwiContext';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import {
|
import { Wallet, LogOut } from 'lucide-react';
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
} from '@/components/ui/dialog';
|
|
||||||
import { Wallet, Check, ExternalLink, Copy, LogOut } from 'lucide-react';
|
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
import { useIsMobile } from '@/hooks/use-mobile';
|
import { useIsMobile } from '@/hooks/use-mobile';
|
||||||
|
import { WalletModal } from './wallet/WalletModal';
|
||||||
|
|
||||||
export const PezkuwiWalletButton: React.FC = () => {
|
export const PezkuwiWalletButton: React.FC = () => {
|
||||||
const {
|
const {
|
||||||
accounts,
|
|
||||||
selectedAccount,
|
selectedAccount,
|
||||||
setSelectedAccount,
|
|
||||||
connectWallet,
|
|
||||||
disconnectWallet,
|
disconnectWallet,
|
||||||
error
|
|
||||||
} = usePezkuwi();
|
} = usePezkuwi();
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [walletModalOpen, setWalletModalOpen] = useState(false);
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
const handleConnect = async () => {
|
|
||||||
await connectWallet();
|
|
||||||
if (accounts.length > 0) {
|
|
||||||
setIsOpen(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSelectAccount = (account: typeof accounts[0]) => {
|
|
||||||
setSelectedAccount(account);
|
|
||||||
setIsOpen(false);
|
|
||||||
toast({
|
|
||||||
title: t('pezWallet.connected'),
|
|
||||||
description: `${account.meta.name} - ${formatAddress(account.address)}`,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDisconnect = () => {
|
const handleDisconnect = () => {
|
||||||
disconnectWallet();
|
disconnectWallet();
|
||||||
toast({
|
toast({
|
||||||
@@ -57,23 +31,13 @@ export const PezkuwiWalletButton: React.FC = () => {
|
|||||||
return `${address.slice(0, 6)}...${address.slice(-4)}`;
|
return `${address.slice(0, 6)}...${address.slice(-4)}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyAddress = () => {
|
|
||||||
if (selectedAccount) {
|
|
||||||
navigator.clipboard.writeText(selectedAccount.address);
|
|
||||||
toast({
|
|
||||||
title: t('pezWallet.addressCopied'),
|
|
||||||
description: t('pezWallet.addressCopiedDesc'),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (selectedAccount) {
|
if (selectedAccount) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="bg-green-500/20 border-green-500/50 text-green-400 hover:bg-green-500/30"
|
className="bg-green-500/20 border-green-500/50 text-green-400 hover:bg-green-500/30"
|
||||||
onClick={() => setIsOpen(true)}
|
onClick={() => setWalletModalOpen(true)}
|
||||||
size={isMobile ? "icon" : "default"}
|
size={isMobile ? "icon" : "default"}
|
||||||
>
|
>
|
||||||
<Wallet className={isMobile ? "w-4 h-4" : "w-4 h-4 mr-2"} />
|
<Wallet className={isMobile ? "w-4 h-4" : "w-4 h-4 mr-2"} />
|
||||||
@@ -95,80 +59,7 @@ export const PezkuwiWalletButton: React.FC = () => {
|
|||||||
<LogOut className="w-4 h-4" />
|
<LogOut className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
<WalletModal isOpen={walletModalOpen} onClose={() => setWalletModalOpen(false)} />
|
||||||
<DialogContent className="bg-gray-900 border-gray-800">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle className="text-white">{t('pezWallet.accountDetails')}</DialogTitle>
|
|
||||||
<DialogDescription className="text-gray-400">
|
|
||||||
{t('pezWallet.accountDetailsDesc')}
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="bg-gray-800/50 rounded-lg p-4">
|
|
||||||
<div className="text-sm text-gray-400 mb-1">{t('pezWallet.accountName')}</div>
|
|
||||||
<div className="text-white font-medium">
|
|
||||||
{selectedAccount.meta.name || t('pezWallet.unnamed')}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-gray-800/50 rounded-lg p-4">
|
|
||||||
<div className="text-sm text-gray-400 mb-1">{t('pezWallet.address')}</div>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<code className="text-white text-sm font-mono">
|
|
||||||
{selectedAccount.address}
|
|
||||||
</code>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
onClick={copyAddress}
|
|
||||||
className="text-gray-400 hover:text-white"
|
|
||||||
>
|
|
||||||
<Copy className="w-4 h-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-gray-800/50 rounded-lg p-4">
|
|
||||||
<div className="text-sm text-gray-400 mb-1">{t('pezWallet.source')}</div>
|
|
||||||
<div className="text-white">
|
|
||||||
{selectedAccount.meta.source || 'pezkuwi'}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{accounts.length > 1 && (
|
|
||||||
<div>
|
|
||||||
<div className="text-sm text-gray-400 mb-2">{t('pezWallet.switchAccount')}</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
{accounts.map((account) => (
|
|
||||||
<button
|
|
||||||
key={account.address}
|
|
||||||
onClick={() => handleSelectAccount(account)}
|
|
||||||
className={`w-full p-3 rounded-lg border transition-all flex items-center justify-between ${
|
|
||||||
account.address === selectedAccount.address
|
|
||||||
? 'bg-green-500/20 border-green-500/50'
|
|
||||||
: 'bg-gray-800/50 border-gray-700 hover:border-gray-600'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div className="text-left">
|
|
||||||
<div className="text-white font-medium">
|
|
||||||
{account.meta.name || t('pezWallet.unnamed')}
|
|
||||||
</div>
|
|
||||||
<div className="text-gray-400 text-xs font-mono">
|
|
||||||
{formatAddress(account.address)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{account.address === selectedAccount.address && (
|
|
||||||
<Check className="w-5 h-5 text-green-400" />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -176,7 +67,7 @@ export const PezkuwiWalletButton: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleConnect}
|
onClick={() => setWalletModalOpen(true)}
|
||||||
className="bg-gradient-to-r from-green-600 to-yellow-400 hover:from-green-700 hover:to-yellow-500 text-white"
|
className="bg-gradient-to-r from-green-600 to-yellow-400 hover:from-green-700 hover:to-yellow-500 text-white"
|
||||||
size={isMobile ? "icon" : "default"}
|
size={isMobile ? "icon" : "default"}
|
||||||
>
|
>
|
||||||
@@ -184,70 +75,7 @@ export const PezkuwiWalletButton: React.FC = () => {
|
|||||||
{!isMobile && t('pezWallet.connect')}
|
{!isMobile && t('pezWallet.connect')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{error && error.includes('not found') && (
|
<WalletModal isOpen={walletModalOpen} onClose={() => setWalletModalOpen(false)} />
|
||||||
<Dialog open={!!error} onOpenChange={() => {}}>
|
|
||||||
<DialogContent className="bg-gray-900 border-gray-800">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle className="text-white">{t('pezWallet.installTitle')}</DialogTitle>
|
|
||||||
<DialogDescription className="text-gray-400">
|
|
||||||
{t('pezWallet.installDesc')}
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
|
|
||||||
<div className="space-y-4">
|
|
||||||
<p className="text-gray-300">
|
|
||||||
{t('pezWallet.installText')}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="flex gap-3">
|
|
||||||
<a
|
|
||||||
href="https://chromewebstore.google.com/search/pezkuwi%7B.js%7D%20extension?hl=en-GB&utm_source=ext_sidebar"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="flex-1"
|
|
||||||
>
|
|
||||||
<Button className="w-full bg-green-600 hover:bg-green-700">
|
|
||||||
<ExternalLink className="w-4 h-4 mr-2" />
|
|
||||||
{t('pezWallet.installChrome')}
|
|
||||||
</Button>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="text-xs text-gray-500">
|
|
||||||
{t('pezWallet.installRefresh')}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Dialog open={isOpen && accounts.length > 0} onOpenChange={setIsOpen}>
|
|
||||||
<DialogContent className="bg-gray-900 border-gray-800">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle className="text-white">{t('pezWallet.selectTitle')}</DialogTitle>
|
|
||||||
<DialogDescription className="text-gray-400">
|
|
||||||
{t('pezWallet.selectDesc')}
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
{accounts.map((account) => (
|
|
||||||
<button
|
|
||||||
key={account.address}
|
|
||||||
onClick={() => handleSelectAccount(account)}
|
|
||||||
className="w-full p-4 rounded-lg border border-gray-700 bg-gray-800/50 hover:border-green-500/50 hover:bg-gray-800 transition-all text-left"
|
|
||||||
>
|
|
||||||
<div className="text-white font-medium mb-1">
|
|
||||||
{account.meta.name || t('pezWallet.unnamed')}
|
|
||||||
</div>
|
|
||||||
<div className="text-gray-400 text-sm font-mono">
|
|
||||||
{account.address}
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -11,12 +11,12 @@ import fa from './locales/fa';
|
|||||||
import ckb from './locales/ckb';
|
import ckb from './locales/ckb';
|
||||||
|
|
||||||
export const languages = {
|
export const languages = {
|
||||||
en: { name: 'English', flag: '🇬🇧', dir: 'ltr' },
|
en: { name: 'English', flag: '🇬🇧', flagImg: null, dir: 'ltr' },
|
||||||
tr: { name: 'Türkçe', flag: '🇹🇷', dir: 'ltr' },
|
tr: { name: 'Türkçe', flag: '🇹🇷', flagImg: null, dir: 'ltr' },
|
||||||
'ku-kurmanji': { name: 'Kurdî (Kurmancî)', flag: '☀️', dir: 'ltr' },
|
'ku-kurmanji': { name: 'Kurdî (Kurmancî)', flag: '', flagImg: '/flags/kurdistan.svg', dir: 'ltr' },
|
||||||
'ku-sorani': { name: 'کوردی (سۆرانی)', flag: '☀️', dir: 'rtl' },
|
'ku-sorani': { name: 'کوردی (سۆرانی)', flag: '', flagImg: '/flags/kurdistan.svg', dir: 'rtl' },
|
||||||
ar: { name: 'العربية', flag: '🇸🇦', dir: 'rtl' },
|
ar: { name: 'العربية', flag: '🇸🇦', flagImg: null, dir: 'rtl' },
|
||||||
fa: { name: 'فارسی', flag: '🇮🇷', dir: 'rtl' }
|
fa: { name: 'فارسی', flag: '🇮🇷', flagImg: null, dir: 'rtl' }
|
||||||
};
|
};
|
||||||
|
|
||||||
const resources = {
|
const resources = {
|
||||||
|
|||||||
Reference in New Issue
Block a user