mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-04-22 06:47:55 +00:00
80d8bbbcb1
Replace all web3FromAddress calls with getSigner() that auto-detects walletSource and uses WC signer or extension signer accordingly. This fixes all signing operations when connected via WalletConnect.
287 lines
10 KiB
TypeScript
287 lines
10 KiB
TypeScript
import React, { useState, useMemo } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Label } from '@/components/ui/label';
|
|
import { usePezkuwi } from '@/contexts/PezkuwiContext';
|
|
import {
|
|
Copy,
|
|
Check,
|
|
Share2,
|
|
Mail,
|
|
MessageCircle,
|
|
Twitter,
|
|
Facebook,
|
|
Linkedin
|
|
} from 'lucide-react';
|
|
|
|
interface InviteUserModalProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
}
|
|
|
|
export const InviteUserModal: React.FC<InviteUserModalProps> = ({ isOpen, onClose }) => {
|
|
const { t } = useTranslation();
|
|
const { peopleApi, isPeopleReady, selectedAccount, walletSource } = usePezkuwi();
|
|
const [copied, setCopied] = useState(false);
|
|
const [inviteeAddress, setInviteeAddress] = useState('');
|
|
const [initiating, setInitiating] = useState(false);
|
|
const [initiateSuccess, setInitiateSuccess] = useState(false);
|
|
const [initiateError, setInitiateError] = useState<string | null>(null);
|
|
|
|
// Generate referral link with user's address
|
|
const referralLink = useMemo(() => {
|
|
if (!selectedAccount?.address) return '';
|
|
const baseUrl = window.location.origin;
|
|
return `${baseUrl}/be-citizen?ref=${selectedAccount.address}`;
|
|
}, [selectedAccount?.address]);
|
|
|
|
// Share text for social media
|
|
const shareText = useMemo(() => {
|
|
return `Join me on Digital Kurdistan (PezkuwiChain)! 🏛️\n\nBecome a citizen and get your Welati Tiki NFT.\n\nUse my referral link:\n${referralLink}`;
|
|
}, [referralLink]);
|
|
|
|
const handleCopy = async () => {
|
|
try {
|
|
await navigator.clipboard.writeText(referralLink);
|
|
setCopied(true);
|
|
setTimeout(() => setCopied(false), 2000);
|
|
} catch (error) {
|
|
if (import.meta.env.DEV) console.error('Failed to copy:', error);
|
|
}
|
|
};
|
|
|
|
const handleShare = (platform: string) => {
|
|
const encodedText = encodeURIComponent(shareText);
|
|
const encodedUrl = encodeURIComponent(referralLink);
|
|
|
|
const urls: Record<string, string> = {
|
|
whatsapp: `https://wa.me/?text=${encodedText}`,
|
|
telegram: `https://t.me/share/url?url=${encodedUrl}&text=${encodeURIComponent('Join me on Digital Kurdistan! 🏛️')}`,
|
|
twitter: `https://twitter.com/intent/tweet?text=${encodedText}`,
|
|
facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`,
|
|
linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`,
|
|
email: `mailto:?subject=${encodeURIComponent('Join Digital Kurdistan')}&body=${encodedText}`,
|
|
};
|
|
|
|
if (urls[platform]) {
|
|
window.open(urls[platform], '_blank', 'width=600,height=400');
|
|
}
|
|
};
|
|
|
|
const handleInitiateReferral = async () => {
|
|
if (!peopleApi || !isPeopleReady || !selectedAccount || !inviteeAddress) {
|
|
setInitiateError('Please connect wallet and enter a valid address');
|
|
return;
|
|
}
|
|
|
|
setInitiating(true);
|
|
setInitiateError(null);
|
|
setInitiateSuccess(false);
|
|
|
|
try {
|
|
const { getSigner } = await import('@/lib/get-signer');
|
|
const injector = await getSigner(selectedAccount.address, walletSource, peopleApi);
|
|
|
|
if (import.meta.env.DEV) console.log(`Initiating referral from ${selectedAccount.address} to ${inviteeAddress}...`);
|
|
|
|
// referral pallet is on People Chain
|
|
const tx = peopleApi.tx.referral.initiateReferral(inviteeAddress);
|
|
|
|
await tx.signAndSend(selectedAccount.address, { signer: injector.signer }, ({ status, dispatchError }) => {
|
|
if (dispatchError) {
|
|
let errorMessage = 'Transaction failed';
|
|
if (dispatchError.isModule) {
|
|
const decoded = peopleApi.registry.findMetaError(dispatchError.asModule);
|
|
errorMessage = `${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`;
|
|
} else {
|
|
errorMessage = dispatchError.toString();
|
|
}
|
|
if (import.meta.env.DEV) console.error(errorMessage);
|
|
setInitiateError(errorMessage);
|
|
setInitiating(false);
|
|
return;
|
|
}
|
|
|
|
if (status.isInBlock || status.isFinalized) {
|
|
if (import.meta.env.DEV) console.log('Referral initiated successfully!');
|
|
setInitiateSuccess(true);
|
|
setInitiating(false);
|
|
setInviteeAddress('');
|
|
}
|
|
});
|
|
} catch (err: unknown) {
|
|
if (import.meta.env.DEV) console.error('Failed to initiate referral:', err);
|
|
setInitiateError(err instanceof Error ? err.message : 'Failed to initiate referral');
|
|
setInitiating(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Dialog open={isOpen} onOpenChange={onClose}>
|
|
<DialogContent className="sm:max-w-lg bg-gray-900 border-gray-800">
|
|
<DialogHeader>
|
|
<DialogTitle className="text-white flex items-center gap-2">
|
|
<Share2 className="w-5 h-5 text-green-500" />
|
|
{t('invite.title')}
|
|
</DialogTitle>
|
|
<DialogDescription className="text-gray-400">
|
|
{t('invite.subtitle')}
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<div className="space-y-6 mt-4">
|
|
{/* Referral Link Display */}
|
|
<div className="space-y-2">
|
|
<Label className="text-gray-300">{t('invite.yourLink')}</Label>
|
|
<div className="flex gap-2">
|
|
<Input
|
|
type="text"
|
|
value={referralLink}
|
|
readOnly
|
|
className="bg-gray-800 border-gray-700 text-white font-mono text-sm"
|
|
/>
|
|
<Button
|
|
onClick={handleCopy}
|
|
variant="outline"
|
|
className="border-gray-700 text-gray-300 hover:bg-gray-800 shrink-0"
|
|
>
|
|
{copied ? (
|
|
<>
|
|
<Check className="h-4 w-4 text-green-500" />
|
|
</>
|
|
) : (
|
|
<>
|
|
<Copy className="h-4 w-4" />
|
|
</>
|
|
)}
|
|
</Button>
|
|
</div>
|
|
<p className="text-xs text-gray-500">
|
|
{t('invite.linkDesc')}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Manual Referral Initiation */}
|
|
<div className="space-y-2 bg-blue-900/20 border border-blue-600/30 rounded-lg p-4">
|
|
<Label className="text-blue-300">{t('invite.preRegister')}</Label>
|
|
<p className="text-xs text-gray-400 mb-2">
|
|
{t('invite.preRegisterDesc')}
|
|
</p>
|
|
<div className="flex gap-2">
|
|
<Input
|
|
type="text"
|
|
value={inviteeAddress}
|
|
onChange={(e) => setInviteeAddress(e.target.value)}
|
|
placeholder={t('invite.friendAddress')}
|
|
className="bg-gray-800 border-gray-700 text-white font-mono text-sm placeholder:text-gray-500 placeholder:opacity-50"
|
|
/>
|
|
<Button
|
|
onClick={handleInitiateReferral}
|
|
disabled={initiating || !inviteeAddress}
|
|
className="bg-blue-600 hover:bg-blue-700 shrink-0"
|
|
>
|
|
{initiating ? t('invite.initiating') : t('invite.initiate')}
|
|
</Button>
|
|
</div>
|
|
{initiateSuccess && (
|
|
<p className="text-xs text-green-400">{t('invite.initiated')}</p>
|
|
)}
|
|
{initiateError && (
|
|
<p className="text-xs text-red-400">{initiateError}</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Share Options */}
|
|
<div className="space-y-3">
|
|
<Label className="text-gray-300">{t('invite.shareVia')}</Label>
|
|
<div className="grid grid-cols-2 gap-3">
|
|
{/* WhatsApp */}
|
|
<Button
|
|
onClick={() => handleShare('whatsapp')}
|
|
variant="outline"
|
|
className="border-gray-700 text-gray-300 hover:bg-gray-800 hover:text-green-400"
|
|
>
|
|
<MessageCircle className="mr-2 h-4 w-4" />
|
|
WhatsApp
|
|
</Button>
|
|
|
|
{/* Telegram */}
|
|
<Button
|
|
onClick={() => handleShare('telegram')}
|
|
variant="outline"
|
|
className="border-gray-700 text-gray-300 hover:bg-gray-800 hover:text-blue-400"
|
|
>
|
|
<MessageCircle className="mr-2 h-4 w-4" />
|
|
Telegram
|
|
</Button>
|
|
|
|
{/* Twitter */}
|
|
<Button
|
|
onClick={() => handleShare('twitter')}
|
|
variant="outline"
|
|
className="border-gray-700 text-gray-300 hover:bg-gray-800 hover:text-blue-400"
|
|
>
|
|
<Twitter className="mr-2 h-4 w-4" />
|
|
Twitter
|
|
</Button>
|
|
|
|
{/* Facebook */}
|
|
<Button
|
|
onClick={() => handleShare('facebook')}
|
|
variant="outline"
|
|
className="border-gray-700 text-gray-300 hover:bg-gray-800 hover:text-blue-600"
|
|
>
|
|
<Facebook className="mr-2 h-4 w-4" />
|
|
Facebook
|
|
</Button>
|
|
|
|
{/* LinkedIn */}
|
|
<Button
|
|
onClick={() => handleShare('linkedin')}
|
|
variant="outline"
|
|
className="border-gray-700 text-gray-300 hover:bg-gray-800 hover:text-blue-500"
|
|
>
|
|
<Linkedin className="mr-2 h-4 w-4" />
|
|
LinkedIn
|
|
</Button>
|
|
|
|
{/* Email */}
|
|
<Button
|
|
onClick={() => handleShare('email')}
|
|
variant="outline"
|
|
className="border-gray-700 text-gray-300 hover:bg-gray-800 hover:text-yellow-500"
|
|
>
|
|
<Mail className="mr-2 h-4 w-4" />
|
|
Email
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Rewards Info */}
|
|
<div className="bg-green-900/20 border border-green-600/30 rounded-lg p-4">
|
|
<h4 className="text-green-400 font-semibold mb-2 text-sm">{t('invite.rewards')}</h4>
|
|
<ul className="text-xs text-gray-300 space-y-1">
|
|
<li>• {t('invite.reward1')}</li>
|
|
<li>• {t('invite.reward2')}</li>
|
|
<li>• {t('invite.reward3')}</li>
|
|
<li>• {t('invite.maxReward')}</li>
|
|
</ul>
|
|
</div>
|
|
|
|
{/* Close Button */}
|
|
<div className="flex justify-end">
|
|
<Button
|
|
onClick={onClose}
|
|
className="bg-gray-800 hover:bg-gray-700 text-white"
|
|
>
|
|
{t('invite.done')}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
};
|