diff --git a/web/src/telegram/TelegramApp.tsx b/web/src/telegram/TelegramApp.tsx deleted file mode 100644 index 45e5ee5c..00000000 --- a/web/src/telegram/TelegramApp.tsx +++ /dev/null @@ -1,214 +0,0 @@ -import { useState, useEffect } from 'react'; -import { useTelegram } from './hooks/useTelegram'; -import { usePezkuwi } from '@/contexts/PezkuwiContext'; -import { useWallet } from '@/contexts/WalletContext'; -import { Loader2, Megaphone, MessageCircle, Gift, Smartphone, Wallet, AlertCircle, RefreshCw } from 'lucide-react'; -import { cn } from '@/lib/utils'; - -// Sections -import { AnnouncementsSection } from './components/Announcements'; -import { ForumSection } from './components/Forum'; -import { RewardsSection } from './components/Rewards'; -import { APKSection } from './components/APK'; -import { WalletSection } from './components/Wallet'; - -export type Section = 'announcements' | 'forum' | 'rewards' | 'apk' | 'wallet'; - -interface NavItem { - id: Section; - icon: React.ReactNode; - label: string; - color: string; -} - -const navItems: NavItem[] = [ - { id: 'announcements', icon: , label: 'Duyurular', color: 'text-yellow-500' }, - { id: 'forum', icon: , label: 'Forum', color: 'text-blue-500' }, - { id: 'rewards', icon: , label: 'Rewards', color: 'text-purple-500' }, - { id: 'apk', icon: , label: 'APK', color: 'text-green-500' }, - { id: 'wallet', icon: , label: 'Wallet', color: 'text-cyan-500' }, -]; - -export function TelegramApp() { - const { - isReady: isTelegramReady, - isTelegram, - startParam, - setHeaderColor, - setBackgroundColor, - hapticSelection, - } = useTelegram(); - - const { api, isApiReady, error: apiError } = usePezkuwi(); - const { isConnected } = useWallet(); - - const [activeSection, setActiveSection] = useState
('announcements'); - const [isRetrying, setIsRetrying] = useState(false); - - // Handle referral from startParam - useEffect(() => { - if (startParam) { - localStorage.setItem('referrerAddress', startParam); - console.log('[TelegramApp] Referral from startParam:', startParam); - } - }, [startParam]); - - // Setup Telegram theme - useEffect(() => { - if (isTelegram) { - setHeaderColor('#030712'); // gray-950 - setBackgroundColor('#030712'); - } - }, [isTelegram, setHeaderColor, setBackgroundColor]); - - const handleNavClick = (section: Section) => { - if (isTelegram) hapticSelection(); - setActiveSection(section); - }; - - const handleRetry = () => { - setIsRetrying(true); - window.location.reload(); - }; - - // Render active section - const renderSection = () => { - switch (activeSection) { - case 'announcements': - return ; - case 'forum': - return ; - case 'rewards': - return ; - case 'apk': - return ; - case 'wallet': - return ; - default: - return ; - } - }; - - // Loading state - if (!isTelegramReady) { - return ( -
- Pezkuwi { - e.currentTarget.style.display = 'none'; - }} - /> - - Pezkuwi Mini App yükleniyor... -
- ); - } - - // API Error state - if (apiError && !isApiReady) { - return ( -
-
- -
-

Bağlantı Hatası

-

- Pezkuwichain ağına bağlanılamadı. Lütfen internet bağlantınızı kontrol edin. -

- -
- ); - } - - return ( -
- {/* Header */} -
-
-
- P -
-
-

Pezkuwichain

-
-
- - {isApiReady ? 'Bağlı' : 'Bağlanıyor...'} - -
-
-
- - {isConnected && ( -
-
- Cüzdan Bağlı -
- )} -
- - {/* API connecting banner */} - {!isApiReady && ( -
- - Blockchain ağına bağlanılıyor... -
- )} - - {/* Main content */} -
- {renderSection()} -
- - {/* Bottom Navigation */} - -
- ); -} - -export default TelegramApp; diff --git a/web/src/telegram/components/APK/index.tsx b/web/src/telegram/components/APK/index.tsx deleted file mode 100644 index f15eb324..00000000 --- a/web/src/telegram/components/APK/index.tsx +++ /dev/null @@ -1,394 +0,0 @@ -import { useState, useEffect } from 'react'; -import { useTelegram } from '../../hooks/useTelegram'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; -import { Badge } from '@/components/ui/badge'; -import { Alert, AlertDescription } from '@/components/ui/alert'; -import { Skeleton } from '@/components/ui/skeleton'; -import { - Smartphone, Download, Clock, CheckCircle2, ExternalLink, - Shield, FileText, Wifi, ChevronDown, ChevronUp, AlertCircle, - Github, Star, Package, Loader2 -} from 'lucide-react'; -import { cn } from '@/lib/utils'; - -interface AppVersion { - version: string; - releaseDate: Date; - downloadUrl: string; - size: string; - changelog: string[]; - isLatest?: boolean; - minAndroidVersion?: string; - downloads?: number; -} - -// Mock versions - will be replaced with GitHub API -const appVersions: AppVersion[] = [ - { - version: '1.2.0', - releaseDate: new Date(Date.now() - 1000 * 60 * 60 * 24 * 2), - downloadUrl: 'https://github.com/pezkuwichain/pezwallet/releases/download/v1.2.0/pezwallet-v1.2.0.apk', - size: '45.2 MB', - isLatest: true, - minAndroidVersion: '7.0', - downloads: 1234, - changelog: [ - 'Yeni: Telegram Mini App entegrasyonu', - 'Yeni: Geliştirilmiş staking arayüzü', - 'Düzeltme: Bakiye yenileme sorunları', - 'Düzeltme: İşlem geçmişi yüklemesi', - 'İyileştirme: Genel performans', - ], - }, - { - version: '1.1.2', - releaseDate: new Date(Date.now() - 1000 * 60 * 60 * 24 * 14), - downloadUrl: 'https://github.com/pezkuwichain/pezwallet/releases/download/v1.1.2/pezwallet-v1.1.2.apk', - size: '44.8 MB', - minAndroidVersion: '7.0', - downloads: 856, - changelog: [ - 'Düzeltme: Kritik güvenlik güncellemesi', - 'Düzeltme: Cüzdan bağlantı kararlılığı', - 'İyileştirme: İşlem imzalama', - ], - }, - { - version: '1.1.0', - releaseDate: new Date(Date.now() - 1000 * 60 * 60 * 24 * 30), - downloadUrl: 'https://github.com/pezkuwichain/pezwallet/releases/download/v1.1.0/pezwallet-v1.1.0.apk', - size: '44.5 MB', - minAndroidVersion: '7.0', - downloads: 2341, - changelog: [ - 'Yeni: Çoklu dil desteği', - 'Yeni: Geliştirilmiş karanlık tema', - 'Yeni: QR kod tarama', - 'Düzeltme: Çeşitli hata düzeltmeleri', - ], - }, -]; - -const features = [ - { - icon: , - title: 'Güvenli Cüzdan', - description: 'Anahtarlarınız, kriptonuz. Tam self-custody.', - color: 'text-green-500', - bgColor: 'bg-green-500/20', - }, - { - icon: , - title: 'Vatandaşlık Yönetimi', - description: 'Vatandaşlık başvurusu ve Tiki yönetimi.', - color: 'text-purple-500', - bgColor: 'bg-purple-500/20', - }, - { - icon: , - title: 'Çevrimdışı Destek', - description: 'Bakiye ve geçmişi çevrimdışı görüntüleyin.', - color: 'text-blue-500', - bgColor: 'bg-blue-500/20', - }, -]; - -function VersionCard({ - version, - isExpanded, - onToggle, - onDownload, - isDownloading -}: { - version: AppVersion; - isExpanded: boolean; - onToggle: () => void; - onDownload: () => void; - isDownloading: boolean; -}) { - const formatDate = (date: Date) => { - return date.toLocaleDateString('tr-TR', { - year: 'numeric', - month: 'short', - day: 'numeric', - }); - }; - - return ( - - - - - {/* Expanded content */} - {isExpanded && ( -
-
-

- - Değişiklikler -

-
    - {version.changelog.map((item, idx) => ( -
  • - - {item} -
  • - ))} -
- - {version.minAndroidVersion && ( -
- - Android {version.minAndroidVersion} veya üstü gerekli -
- )} - - -
-
- )} -
-
- ); -} - -export function APKSection() { - const { hapticImpact, openLink, showConfirm } = useTelegram(); - const [expandedVersion, setExpandedVersion] = useState(appVersions[0]?.version || null); - const [downloading, setDownloading] = useState(null); - - const handleDownload = async (version: AppVersion) => { - hapticImpact('medium'); - - const confirmed = await showConfirm( - `Pezwallet v${version.version} (${version.size}) indirilsin mi?` - ); - - if (confirmed) { - setDownloading(version.version); - openLink(version.downloadUrl); - setTimeout(() => setDownloading(null), 3000); - } - }; - - const handleOpenGitHub = () => { - hapticImpact('light'); - openLink('https://github.com/pezkuwichain/pezwallet/releases'); - }; - - const latestVersion = appVersions.find(v => v.isLatest); - - return ( -
- {/* Header */} -
-
-

- - Pezwallet APK -

- -
-
- - {/* Content */} -
- {/* App Banner */} - - -
- {/* App Icon */} -
- P -
-
-

Pezwallet

-

- Pezkuwichain için resmi cüzdan uygulaması -

- {latestVersion && ( - - )} -
-
-
-
- - {/* Features */} - - - - - Özellikler - - - - {features.map((feature, index) => ( -
-
- {feature.icon} -
-
-

{feature.title}

-

{feature.description}

-
-
- ))} -
-
- - {/* Installation Guide */} - - - - Kurulum Rehberi -
    -
  1. APK dosyasını indirin
  2. -
  3. Cihaz Ayarlarını açın
  4. -
  5. "Bilinmeyen kaynaklardan yükleme"yi etkinleştirin
  6. -
  7. İndirilen APK dosyasını açın
  8. -
  9. Kurulum talimatlarını izleyin
  10. -
-
-
- - {/* Version History */} - - - - - Sürüm Geçmişi - - - - {appVersions.map((version) => ( - setExpandedVersion( - expandedVersion === version.version ? null : version.version - )} - onDownload={() => handleDownload(version)} - isDownloading={downloading === version.version} - /> - ))} - - - - {/* Footer note */} -
-

- Daima resmi kaynaklardan indirdiğinizi doğrulayın -

- -
-
-
- ); -} - -export default APKSection; diff --git a/web/src/telegram/components/Announcements/index.tsx b/web/src/telegram/components/Announcements/index.tsx deleted file mode 100644 index 4b16e81d..00000000 --- a/web/src/telegram/components/Announcements/index.tsx +++ /dev/null @@ -1,308 +0,0 @@ -import { useState, useEffect } from 'react'; -import { useTelegram } from '../../hooks/useTelegram'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; -import { Badge } from '@/components/ui/badge'; -import { Skeleton } from '@/components/ui/skeleton'; -import { - Megaphone, RefreshCw, ThumbsUp, ThumbsDown, Pin, Clock, - User, ChevronDown, ChevronUp, Bell -} from 'lucide-react'; -import { cn } from '@/lib/utils'; - -export interface Announcement { - id: string; - title: string; - content: string; - author: string; - authorAvatar?: string; - createdAt: Date; - likes: number; - dislikes: number; - userReaction?: 'like' | 'dislike' | null; - isPinned?: boolean; - imageUrl?: string; -} - -// Mock data - will be replaced with API calls -const mockAnnouncements: Announcement[] = [ - { - id: '1', - title: 'Pezkuwichain Mainnet Yayında!', - content: 'Pezkuwichain mainnet artık aktif! Bu, Kürt halkı için merkezi olmayan bir dijital devlet inşa etme yolculuğumuzda önemli bir kilometre taşı.\n\nÖne çıkan özellikler:\n- Hızlı işlem kesinliği (6 saniye)\n- Düşük gas ücretleri\n- Yerleşik staking desteği\n- Demokratik yönetişim\n\nKeşfetmeye başlayın: app.pezkuwichain.io', - author: 'Pezkuwi Ekibi', - createdAt: new Date(Date.now() - 1000 * 60 * 60 * 2), - likes: 142, - dislikes: 3, - isPinned: true, - }, - { - id: '2', - title: 'Yeni Referral Programı', - content: 'Arkadaşlarınızı davet edin ve ödüller kazanın! KYC tamamlayan her arkadaşınız için trust score\'unuza katkıda bulunan bonus puanlar alacaksınız.\n\nReferans linkinizi Rewards bölümünden paylaşın.', - author: 'Pezkuwi Ekibi', - createdAt: new Date(Date.now() - 1000 * 60 * 60 * 24), - likes: 89, - dislikes: 5, - }, - { - id: '3', - title: 'Staking Ödülleri Güncellendi', - content: 'Staking ödül mekanizmasını güncelledik. HEZ stake eden vatandaşlar artık her epoch\'ta PEZ token ödülü kazanacak.\n\nMinimum stake: 10 HEZ\nEpoch süresi: 7 gün', - author: 'Pezkuwi Ekibi', - createdAt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 3), - likes: 67, - dislikes: 2, - }, -]; - -function AnnouncementCard({ - announcement, - onReact -}: { - announcement: Announcement; - onReact: (id: string, reaction: 'like' | 'dislike') => void; -}) { - const { hapticImpact } = useTelegram(); - const [isExpanded, setIsExpanded] = useState(false); - - const handleReact = (reaction: 'like' | 'dislike') => { - hapticImpact('light'); - onReact(announcement.id, reaction); - }; - - const formatDate = (date: Date) => { - const now = new Date(); - const diff = now.getTime() - date.getTime(); - const hours = Math.floor(diff / (1000 * 60 * 60)); - const days = Math.floor(hours / 24); - - if (hours < 1) return 'Az önce'; - if (hours < 24) return `${hours} saat önce`; - if (days < 7) return `${days} gün önce`; - return date.toLocaleDateString('tr-TR', { day: 'numeric', month: 'short' }); - }; - - const shouldTruncate = announcement.content.length > 200; - const displayContent = shouldTruncate && !isExpanded - ? announcement.content.slice(0, 200) + '...' - : announcement.content; - - return ( - - - {/* Header */} -
-
-
- {announcement.authorAvatar ? ( - {announcement.author} - ) : ( - - )} -
-
-

{announcement.author}

-
- - {formatDate(announcement.createdAt)} -
-
-
- {announcement.isPinned && ( - - - Sabitlendi - - )} -
- - {/* Title */} -

{announcement.title}

- - {/* Content */} -

- {displayContent} -

- - {/* Show more/less button */} - {shouldTruncate && ( - - )} - - {/* Image */} - {announcement.imageUrl && ( -
- -
- )} - - {/* Reactions */} -
- - - -
-
-
- ); -} - -export function AnnouncementsSection() { - const { hapticNotification } = useTelegram(); - const [announcements, setAnnouncements] = useState(mockAnnouncements); - const [isLoading, setIsLoading] = useState(false); - - const handleReact = (id: string, reaction: 'like' | 'dislike') => { - setAnnouncements(prev => prev.map(ann => { - if (ann.id !== id) return ann; - - const wasLiked = ann.userReaction === 'like'; - const wasDisliked = ann.userReaction === 'dislike'; - const isSameReaction = ann.userReaction === reaction; - - return { - ...ann, - userReaction: isSameReaction ? null : reaction, - likes: reaction === 'like' - ? ann.likes + (isSameReaction ? -1 : 1) - : ann.likes - (wasLiked ? 1 : 0), - dislikes: reaction === 'dislike' - ? ann.dislikes + (isSameReaction ? -1 : 1) - : ann.dislikes - (wasDisliked ? 1 : 0), - }; - })); - }; - - const handleRefresh = async () => { - setIsLoading(true); - hapticNotification('success'); - await new Promise(resolve => setTimeout(resolve, 1000)); - setAnnouncements(mockAnnouncements); - setIsLoading(false); - }; - - // Sort: pinned first, then by date - const sortedAnnouncements = [...announcements].sort((a, b) => { - if (a.isPinned && !b.isPinned) return -1; - if (!a.isPinned && b.isPinned) return 1; - return b.createdAt.getTime() - a.createdAt.getTime(); - }); - - return ( -
- {/* Header */} -
-
-

- - Duyurular -

- -
- - {/* Stats */} -
-
- - {announcements.length} Duyuru -
-
- - - {announcements.filter(a => a.isPinned).length} Sabitlenmiş - -
-
-
- - {/* Announcements List */} -
- {isLoading ? ( - <> - {[...Array(3)].map((_, i) => ( - - ))} - - ) : sortedAnnouncements.length === 0 ? ( -
- -

Henüz duyuru yok

-
- ) : ( - sortedAnnouncements.map(announcement => ( - - )) - )} -
-
- ); -} - -export default AnnouncementsSection; diff --git a/web/src/telegram/components/Forum/index.tsx b/web/src/telegram/components/Forum/index.tsx deleted file mode 100644 index b5e97ddf..00000000 --- a/web/src/telegram/components/Forum/index.tsx +++ /dev/null @@ -1,562 +0,0 @@ -import { useState } from 'react'; -import { useTelegram } from '../../hooks/useTelegram'; -import { usePezkuwi } from '@/contexts/PezkuwiContext'; -import { useWallet } from '@/contexts/WalletContext'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; -import { Badge } from '@/components/ui/badge'; -import { Input } from '@/components/ui/input'; -import { Skeleton } from '@/components/ui/skeleton'; -import { - MessageCircle, Plus, RefreshCw, Search, Pin, Clock, User, - Eye, ChevronRight, ArrowLeft, Send, ThumbsUp, MessageSquare, Hash -} from 'lucide-react'; -import { cn } from '@/lib/utils'; - -export interface ForumThread { - id: string; - title: string; - content: string; - author: string; - authorAddress?: string; - createdAt: Date; - replyCount: number; - viewCount: number; - lastReplyAt?: Date; - lastReplyAuthor?: string; - isPinned?: boolean; - tags?: string[]; -} - -export interface ForumReply { - id: string; - content: string; - author: string; - authorAddress?: string; - createdAt: Date; - likes: number; - userLiked?: boolean; -} - -// Mock data -const mockThreads: ForumThread[] = [ - { - id: '1', - title: 'Pezkuwi Forum\'a Hoş Geldiniz!', - content: 'Bu, Pezkuwi vatandaşları için resmi topluluk forumudur. Dijital devletimiz, yönetişim, geliştirme ve daha fazlası hakkında serbestçe tartışabilirsiniz.\n\nLütfen saygılı olun ve topluluk kurallarımıza uyun.', - author: 'Admin', - createdAt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 7), - replyCount: 45, - viewCount: 1234, - isPinned: true, - tags: ['duyuru', 'kurallar'], - lastReplyAt: new Date(Date.now() - 1000 * 60 * 30), - lastReplyAuthor: 'YeniVatandaş', - }, - { - id: '2', - title: 'HEZ nasıl stake edilir ve ödül kazanılır?', - content: 'Herkese merhaba! İlk HEZ tokenlarımı aldım ve stake etmeye başlamak istiyorum. Biri adım adım süreci açıklayabilir mi? Minimum miktar ne kadar?', - author: 'KriptoYeni', - createdAt: new Date(Date.now() - 1000 * 60 * 60 * 5), - replyCount: 12, - viewCount: 256, - tags: ['staking', 'yardım'], - lastReplyAt: new Date(Date.now() - 1000 * 60 * 60 * 2), - lastReplyAuthor: 'StakingPro', - }, - { - id: '3', - title: 'Öneri: Uygulamaya Kürtçe dil desteği eklenmeli', - content: 'Kürt dijital devleti olarak, tüm uygulamalarımızda tam Kürtçe dil desteği (Kurmancî ve Soranî) olması gerektiğini düşünüyorum.\n\nNe düşünüyorsunuz?', - author: 'KürtGeliştirici', - createdAt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 2), - replyCount: 28, - viewCount: 567, - tags: ['öneri', 'yerelleştirme'], - lastReplyAt: new Date(Date.now() - 1000 * 60 * 60 * 4), - lastReplyAuthor: 'DilUzmanı', - }, - { - id: '4', - title: 'Hata: Cüzdan bakiyesi güncellenmiyor', - content: 'Transfer yaptıktan sonra cüzdan bakiyem hemen güncellenmiyor. Sayfayı birkaç kez yenilemem gerekiyor. Bu sorunu yaşayan başka var mı?', - author: 'TeknikKullanıcı', - createdAt: new Date(Date.now() - 1000 * 60 * 60 * 12), - replyCount: 8, - viewCount: 89, - tags: ['hata', 'cüzdan'], - lastReplyAt: new Date(Date.now() - 1000 * 60 * 60 * 6), - lastReplyAuthor: 'GeliştiriciEkibi', - }, -]; - -const mockReplies: ForumReply[] = [ - { - id: '1', - content: 'Burada olmak harika! Topluluğa katılmak için sabırsızlanıyorum.', - author: 'YeniVatandaş', - createdAt: new Date(Date.now() - 1000 * 60 * 30), - likes: 5, - }, - { - id: '2', - content: 'Hoş geldiniz! Dokümantasyon bölümündeki staking rehberini kontrol etmeyi unutmayın.', - author: 'Yardımcı', - createdAt: new Date(Date.now() - 1000 * 60 * 60), - likes: 12, - }, -]; - -function ThreadCard({ thread, onClick }: { thread: ForumThread; onClick: () => void }) { - const { hapticSelection } = useTelegram(); - - const handleClick = () => { - hapticSelection(); - onClick(); - }; - - const formatDate = (date: Date) => { - const now = new Date(); - const diff = now.getTime() - date.getTime(); - const hours = Math.floor(diff / (1000 * 60 * 60)); - const days = Math.floor(hours / 24); - - if (hours < 1) return 'Az önce'; - if (hours < 24) return `${hours}s önce`; - if (days < 7) return `${days}g önce`; - return date.toLocaleDateString('tr-TR', { day: 'numeric', month: 'short' }); - }; - - return ( - - -
-
- {/* Title with pin badge */} -
- {thread.isPinned && ( - - - Sabit - - )} -

{thread.title}

-
- - {/* Preview */} -

- {thread.content.length > 100 ? thread.content.slice(0, 100) + '...' : thread.content} -

- - {/* Tags */} - {thread.tags && thread.tags.length > 0 && ( -
- {thread.tags.slice(0, 3).map(tag => ( -
- - {tag} -
- ))} -
- )} - - {/* Meta info */} -
-
- - {thread.author} -
-
- - {formatDate(thread.createdAt)} -
-
- - {thread.replyCount} -
-
- - {thread.viewCount} -
-
-
- - -
- - {/* Last reply info */} - {thread.lastReplyAt && thread.lastReplyAuthor && ( -
- Son yanıt: {thread.lastReplyAuthor}{' '} - · {formatDate(thread.lastReplyAt)} -
- )} -
-
- ); -} - -function ThreadView({ - thread, - replies, - onBack, - onReply, - onLikeReply, - isConnected -}: { - thread: ForumThread; - replies: ForumReply[]; - onBack: () => void; - onReply: (content: string) => void; - onLikeReply: (replyId: string) => void; - isConnected: boolean; -}) { - const { hapticImpact } = useTelegram(); - const [replyContent, setReplyContent] = useState(''); - const [isSubmitting, setIsSubmitting] = useState(false); - - const formatDate = (date: Date) => { - return date.toLocaleDateString('tr-TR', { - day: 'numeric', - month: 'short', - year: 'numeric', - hour: '2-digit', - minute: '2-digit' - }); - }; - - const handleSubmitReply = async () => { - if (!replyContent.trim() || isSubmitting) return; - - setIsSubmitting(true); - hapticImpact('medium'); - - try { - await onReply(replyContent); - setReplyContent(''); - } finally { - setIsSubmitting(false); - } - }; - - return ( -
- {/* Header */} -
- -

{thread.title}

-
- - {/* Content */} -
- {/* Original post */} -
- - -
-
- -
-
-

{thread.author}

-
- - {formatDate(thread.createdAt)} -
-
-
- - {/* Tags */} - {thread.tags && thread.tags.length > 0 && ( -
- {thread.tags.map(tag => ( - - - {tag} - - ))} -
- )} - -

- {thread.content} -

-
-
-
- - {/* Replies */} -
-
- - - {replies.length} Yanıt - -
- -
- {replies.map(reply => ( - - -
-
-
- -
-
- {reply.author} - - {formatDate(reply.createdAt)} - -
-
- -
-

{reply.content}

-
-
- ))} - - {replies.length === 0 && ( -
- -

Henüz yanıt yok. İlk yanıtı siz yazın!

-
- )} -
-
-
- - {/* Reply input */} - {isConnected ? ( -
-
- setReplyContent(e.target.value)} - placeholder="Yanıtınızı yazın..." - className="flex-1 bg-gray-800 border-gray-700 text-white placeholder:text-gray-500" - onKeyDown={(e) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - handleSubmitReply(); - } - }} - /> - -
-
- ) : ( -
- Yanıt yazmak için cüzdanınızı bağlayın -
- )} -
- ); -} - -export function ForumSection() { - const { hapticNotification, showAlert } = useTelegram(); - const { selectedAccount } = usePezkuwi(); - const { isConnected } = useWallet(); - - const [threads, setThreads] = useState(mockThreads); - const [selectedThread, setSelectedThread] = useState(null); - const [replies, setReplies] = useState(mockReplies); - const [isLoading, setIsLoading] = useState(false); - const [searchQuery, setSearchQuery] = useState(''); - - const handleRefresh = async () => { - setIsLoading(true); - hapticNotification('success'); - await new Promise(resolve => setTimeout(resolve, 1000)); - setThreads(mockThreads); - setIsLoading(false); - }; - - const handleCreateThread = () => { - if (!isConnected) { - showAlert('Konu oluşturmak için cüzdanınızı bağlayın'); - return; - } - showAlert('Konu oluşturma özelliği yakında!'); - }; - - const handleReply = async (content: string) => { - if (!isConnected || !selectedThread) return; - - const newReply: ForumReply = { - id: String(Date.now()), - content, - author: selectedAccount?.meta?.name || 'Anonim', - createdAt: new Date(), - likes: 0, - }; - - setReplies(prev => [...prev, newReply]); - hapticNotification('success'); - }; - - const handleLikeReply = (replyId: string) => { - setReplies(prev => prev.map(reply => { - if (reply.id !== replyId) return reply; - return { - ...reply, - likes: reply.userLiked ? reply.likes - 1 : reply.likes + 1, - userLiked: !reply.userLiked, - }; - })); - }; - - const filteredThreads = threads.filter(thread => - thread.title.toLowerCase().includes(searchQuery.toLowerCase()) || - thread.content.toLowerCase().includes(searchQuery.toLowerCase()) - ); - - // Sort: pinned first, then by date - const sortedThreads = [...filteredThreads].sort((a, b) => { - if (a.isPinned && !b.isPinned) return -1; - if (!a.isPinned && b.isPinned) return 1; - return b.createdAt.getTime() - a.createdAt.getTime(); - }); - - if (selectedThread) { - return ( - setSelectedThread(null)} - onReply={handleReply} - onLikeReply={handleLikeReply} - isConnected={isConnected} - /> - ); - } - - return ( -
- {/* Header */} -
-
-

- - Forum -

-
- - -
-
- - {/* Search */} -
- - setSearchQuery(e.target.value)} - placeholder="Konularda ara..." - className="pl-9 bg-gray-900 border-gray-800 text-white placeholder:text-gray-500" - /> -
- - {/* Stats */} -
-
- - {threads.length} Konu -
-
- - - {threads.filter(t => t.isPinned).length} Sabitlenmiş - -
-
-
- - {/* Thread List */} -
- {isLoading ? ( - <> - {[...Array(4)].map((_, i) => ( - - ))} - - ) : sortedThreads.length === 0 ? ( -
- -

{searchQuery ? 'Konu bulunamadı' : 'Henüz konu yok'}

- {!searchQuery && ( - - )} -
- ) : ( - sortedThreads.map(thread => ( - setSelectedThread(thread)} - /> - )) - )} -
-
- ); -} - -export default ForumSection; diff --git a/web/src/telegram/components/Rewards/index.tsx b/web/src/telegram/components/Rewards/index.tsx deleted file mode 100644 index 0ac15934..00000000 --- a/web/src/telegram/components/Rewards/index.tsx +++ /dev/null @@ -1,350 +0,0 @@ -import { useState, useEffect } from 'react'; -import { usePezkuwi } from '@/contexts/PezkuwiContext'; -import { useReferral } from '@/contexts/ReferralContext'; -import { useWallet } from '@/contexts/WalletContext'; -import { useTelegram } from '../../hooks/useTelegram'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; -import { Badge } from '@/components/ui/badge'; -import { Skeleton } from '@/components/ui/skeleton'; -import { Alert, AlertDescription } from '@/components/ui/alert'; -import { getReferralStats, ReferralStats, getMyReferrals, calculateReferralScore } from '@shared/lib/referral'; -import { getStakingInfo, StakingInfo } from '@shared/lib/staking'; -import { - Gift, Users, Trophy, Copy, Check, Share2, Loader2, RefreshCw, - UserPlus, Award, Star, Calendar, Zap, ChevronRight, Clock -} from 'lucide-react'; -import { cn } from '@/lib/utils'; - -export function RewardsSection() { - const { api, isApiReady, selectedAccount } = usePezkuwi(); - const { stats, myReferrals, loading: referralLoading, refreshStats } = useReferral(); - const { isConnected } = useWallet(); - const { hapticNotification, hapticImpact, showAlert, openTelegramLink } = useTelegram(); - - const [stakingInfo, setStakingInfo] = useState(null); - const [isLoading, setIsLoading] = useState(false); - const [copied, setCopied] = useState(false); - - const address = selectedAccount?.address; - const referralLink = address ? `https://t.me/pezkuwichain_bot?start=${address}` : ''; - - // Fetch staking data - useEffect(() => { - if (!api || !isApiReady || !address) return; - - const fetchData = async () => { - setIsLoading(true); - try { - const staking = await getStakingInfo(api, address); - setStakingInfo(staking); - } catch (err) { - console.error('Failed to fetch rewards data:', err); - } finally { - setIsLoading(false); - } - }; - - fetchData(); - }, [api, isApiReady, address]); - - const handleCopyLink = async () => { - if (!referralLink) return; - try { - await navigator.clipboard.writeText(referralLink); - setCopied(true); - hapticNotification('success'); - setTimeout(() => setCopied(false), 2000); - } catch { - showAlert('Link kopyalanamadı'); - } - }; - - const handleShare = () => { - if (!referralLink) return; - hapticImpact('medium'); - const text = encodeURIComponent('Pezkuwichain - Kürt Dijital Devleti! Referans linkimle katıl:'); - openTelegramLink(`https://t.me/share/url?url=${encodeURIComponent(referralLink)}&text=${text}`); - }; - - const handleRefresh = async () => { - hapticNotification('success'); - await refreshStats(); - }; - - // Not connected state - if (!isConnected || !selectedAccount) { - return ( -
-
-
- -
-

Ödüller ve Referanslar

-

- Referans programına katılmak ve ödüllerinizi görmek için cüzdanınızı bağlayın. -

-
-
- - Arkadaş Davet -
-
- - Puan Kazan -
-
- - PEZ Ödülü -
-
-
-
- ); - } - - return ( -
- {/* Header Stats */} -
-
-

- - Ödüller -

- -
- - {/* Stats Cards */} -
- - -
- -
- {referralLoading ? ( - - ) : ( -

{stats?.referralCount || 0}

- )} -

Referans

-
-
- - - -
- -
- {referralLoading ? ( - - ) : ( -

{stats?.referralScore || 0}

- )} -

Puan

-
-
- - - -
- -
- {isLoading ? ( - - ) : ( -

{stakingInfo?.stakingScore || 0}

- )} -

Staking

-
-
-
-
- - {/* Referral Invite Section */} -
- - -
-
- -
-
-

Arkadaşını Davet Et

-

Her referans için puan kazan!

-
-
- - {/* Referral Link */} -
-

Referans Linkin

-
- {referralLink} - -
-
- - -
-
-
- - {/* Score System Info */} -
- - - - - Puan Sistemi - - - -
-
- 1-10 referans -
- ×10 puan -
-
-
- 11-50 referans -
- 100 + ×5 puan -
-
-
- 51-100 referans -
- 300 + ×4 puan -
-
-
- 101+ referans -
- 500 (Max) -
-
-
-
- - {/* Epoch Rewards */} - {stakingInfo?.pezRewards?.hasPendingClaim && ( -
- - -
-
- - Epoch Ödülleri -
- - Epoch #{stakingInfo.pezRewards.currentEpoch} - -
- -
-

Bekleyen PEZ

-

- {stakingInfo.pezRewards.totalClaimable} PEZ -

-
- -
- {stakingInfo.pezRewards.claimableRewards.map((reward) => ( -
- Epoch #{reward.epoch} - {reward.amount} PEZ -
- ))} -
- - -
-
-
- )} - - {/* My Referrals List */} - {myReferrals && myReferrals.length > 0 && ( -
- - - - - Referanslarım ({myReferrals.length}) - - - - {myReferrals.slice(0, 5).map((referral, index) => ( -
-
-
- {index + 1} -
- - {referral.slice(0, 6)}...{referral.slice(-4)} - -
- - KYC Onaylı - -
- ))} - {myReferrals.length > 5 && ( -

- +{myReferrals.length - 5} daha fazla -

- )} -
-
-
- )} - - {/* Who invited me */} - {stats?.whoInvitedMe && ( -
- - - - Davet eden: - - {stats.whoInvitedMe.slice(0, 8)}...{stats.whoInvitedMe.slice(-6)} - - - -
- )} -
- ); -} - -export default RewardsSection; diff --git a/web/src/telegram/components/Wallet/index.tsx b/web/src/telegram/components/Wallet/index.tsx deleted file mode 100644 index 983cee4a..00000000 --- a/web/src/telegram/components/Wallet/index.tsx +++ /dev/null @@ -1,394 +0,0 @@ -import { useState, useEffect } from 'react'; -import { usePezkuwi } from '@/contexts/PezkuwiContext'; -import { useWallet } from '@/contexts/WalletContext'; -import { useTelegram } from '../../hooks/useTelegram'; -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; -import { Badge } from '@/components/ui/badge'; -import { Skeleton } from '@/components/ui/skeleton'; -import { getAllScores, UserScores, getScoreRating } from '@shared/lib/scores'; -import { getStakingInfo, StakingInfo } from '@shared/lib/staking'; -import { formatBalance, formatAddress, CHAIN_CONFIG } from '@shared/lib/wallet'; -import { - Wallet, Send, ArrowDownToLine, TrendingUp, Copy, Check, ExternalLink, - Loader2, RefreshCw, Trophy, Users, Star, Award, Coins, Clock, Zap -} from 'lucide-react'; -import { cn } from '@/lib/utils'; - -export function WalletSection() { - const { api, isApiReady, selectedAccount, connectWallet, disconnectWallet, accounts } = usePezkuwi(); - const { balances, refreshBalances, isConnected } = useWallet(); - const { hapticNotification, hapticImpact, showAlert, openLink } = useTelegram(); - - const [scores, setScores] = useState(null); - const [stakingInfo, setStakingInfo] = useState(null); - const [isLoading, setIsLoading] = useState(false); - const [isRefreshing, setIsRefreshing] = useState(false); - const [copied, setCopied] = useState(false); - - const address = selectedAccount?.address; - - // Fetch data when connected - useEffect(() => { - if (!api || !isApiReady || !address) return; - - const fetchData = async () => { - setIsLoading(true); - try { - const [userScores, staking] = await Promise.all([ - getAllScores(api, address), - getStakingInfo(api, address), - ]); - setScores(userScores); - setStakingInfo(staking); - } catch (err) { - console.error('Failed to fetch wallet data:', err); - } finally { - setIsLoading(false); - } - }; - - fetchData(); - }, [api, isApiReady, address]); - - const handleRefresh = async () => { - if (!api || !address || isRefreshing) return; - setIsRefreshing(true); - hapticNotification('success'); - - try { - await refreshBalances(); - const [userScores, staking] = await Promise.all([ - getAllScores(api, address), - getStakingInfo(api, address), - ]); - setScores(userScores); - setStakingInfo(staking); - } catch (err) { - showAlert('Yenileme başarısız'); - } finally { - setIsRefreshing(false); - } - }; - - const handleCopyAddress = async () => { - if (!address) return; - try { - await navigator.clipboard.writeText(address); - setCopied(true); - hapticNotification('success'); - setTimeout(() => setCopied(false), 2000); - } catch { - showAlert('Adres kopyalanamadı'); - } - }; - - const handleConnect = () => { - hapticImpact('medium'); - connectWallet(); - }; - - const handleOpenExplorer = () => { - if (!address) return; - hapticImpact('light'); - openLink(`https://explorer.pezkuwichain.io/account/${address}`); - }; - - // Not connected state - if (!isConnected || !selectedAccount) { - return ( -
-
-
- -
-

Cüzdanınızı Bağlayın

-

- Bakiyelerinizi görüntülemek, stake etmek ve işlem yapmak için Pezkuwi cüzdanınızı bağlayın. -

- - -
-
- - HEZ & PEZ -
-
- - Staking -
-
- - Rewards -
-
-
-
- ); - } - - return ( -
- {/* Account Card */} -
- - -
-
-
- - {selectedAccount?.meta?.name?.charAt(0) || 'P'} - -
-
-

- {selectedAccount?.meta?.name || 'Pezkuwi Hesabı'} -

-
- - {formatAddress(address || '')} - - -
-
-
-
- - -
-
- - {/* Balance Display */} -
-

Toplam Bakiye

- {isLoading ? ( - - ) : ( -
- - {balances?.HEZ || '0.00'} - - {CHAIN_CONFIG.symbol} -
- )} - {balances?.PEZ && parseFloat(balances.PEZ) > 0 && ( -

- + {balances.PEZ} PEZ -

- )} -
- - {/* Quick Actions */} -
- - - -
-
-
-
- - {/* Scores Section */} -
-

- - Puanlarınız -

- - {isLoading ? ( -
- {[...Array(4)].map((_, i) => ( - - ))} -
- ) : scores ? ( - <> - {/* Total Score Banner */} - - -
-

Toplam Skor

-

{scores.totalScore}

-
- - {getScoreRating(scores.totalScore)} - -
-
- - {/* Individual Scores */} -
- - -
-
- -
- Trust -
-

{scores.trustScore}

-
-
- - - -
-
- -
- Referral -
-

{scores.referralScore}

-
-
- - - -
-
- -
- Staking -
-

{scores.stakingScore}

-
-
- - - -
-
- -
- Tiki -
-

{scores.tikiScore}

-
-
-
- - ) : null} -
- - {/* Staking Info */} - {stakingInfo && parseFloat(stakingInfo.bonded) > 0 && ( -
-

- - Staking Durumu -

- - - -
-
-

Stake Edilmiş

-

{stakingInfo.bonded} HEZ

-
-
-

Aktif

-

{stakingInfo.active} HEZ

-
- {stakingInfo.stakingScore !== null && ( -
-

Staking Skoru

-

{stakingInfo.stakingScore}/100

-
- )} -
-

Nominasyonlar

-

{stakingInfo.nominations.length}

-
-
- - {/* PEZ Rewards */} - {stakingInfo.pezRewards?.hasPendingClaim && ( -
-
-
- - Bekleyen PEZ -
- - {stakingInfo.pezRewards.totalClaimable} PEZ - -
- -
- )} -
-
-
- )} - - {/* Disconnect Button */} -
- -
-
- ); -} - -export default WalletSection; diff --git a/web/src/telegram/components/index.ts b/web/src/telegram/components/index.ts deleted file mode 100644 index c520c597..00000000 --- a/web/src/telegram/components/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export { Sidebar } from './Sidebar'; -export type { Section } from './Sidebar'; - -export { Announcements } from './Announcements'; -export { Forum } from './Forum'; -export { Rewards } from './Rewards'; -export { APK } from './APK'; -export { Wallet } from './Wallet'; diff --git a/web/src/telegram/hooks/index.ts b/web/src/telegram/hooks/index.ts deleted file mode 100644 index f34c40bc..00000000 --- a/web/src/telegram/hooks/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { useTelegram } from './useTelegram'; -export type { TelegramUser, TelegramTheme } from './useTelegram'; - -export { usePezkuwiApi, getApiInstance } from './usePezkuwiApi'; diff --git a/web/src/telegram/hooks/usePezkuwiApi.ts b/web/src/telegram/hooks/usePezkuwiApi.ts deleted file mode 100644 index fd772624..00000000 --- a/web/src/telegram/hooks/usePezkuwiApi.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { useState, useEffect } from 'react'; -import { ApiPromise, WsProvider } from '@pezkuwi/api'; - -// RPC endpoint - uses environment variable or falls back to mainnet -const RPC_ENDPOINT = import.meta.env.VITE_WS_ENDPOINT || 'wss://rpc.pezkuwichain.io:9944'; -const FALLBACK_ENDPOINTS = [ - RPC_ENDPOINT, - import.meta.env.VITE_WS_ENDPOINT_FALLBACK_1, - import.meta.env.VITE_WS_ENDPOINT_FALLBACK_2, -].filter(Boolean) as string[]; - -interface UsePezkuwiApiReturn { - api: ApiPromise | null; - isReady: boolean; - isConnecting: boolean; - error: string | null; - reconnect: () => Promise; -} - -// Singleton API instance to avoid multiple connections -let globalApi: ApiPromise | null = null; -let connectionPromise: Promise | null = null; - -async function createApiConnection(): Promise { - // Return existing connection promise if one is in progress - if (connectionPromise) { - return connectionPromise; - } - - // Return existing API if already connected - if (globalApi && globalApi.isConnected) { - return globalApi; - } - - // Create new connection - connectionPromise = (async () => { - for (const endpoint of FALLBACK_ENDPOINTS) { - try { - if (import.meta.env.DEV) { - console.log('[PezkuwiApi] Connecting to:', endpoint); - } - - const provider = new WsProvider(endpoint); - const api = await ApiPromise.create({ provider }); - await api.isReady; - - globalApi = api; - - if (import.meta.env.DEV) { - const [chain, nodeName, nodeVersion] = await Promise.all([ - api.rpc.system.chain(), - api.rpc.system.name(), - api.rpc.system.version(), - ]); - console.log(`[PezkuwiApi] Connected to ${chain} (${nodeName} v${nodeVersion})`); - } - - return api; - } catch (err) { - if (import.meta.env.DEV) { - console.warn(`[PezkuwiApi] Failed to connect to ${endpoint}:`, err); - } - continue; - } - } - - throw new Error('Failed to connect to any endpoint'); - })(); - - try { - return await connectionPromise; - } finally { - connectionPromise = null; - } -} - -export function usePezkuwiApi(): UsePezkuwiApiReturn { - const [api, setApi] = useState(globalApi); - const [isReady, setIsReady] = useState(globalApi?.isConnected || false); - const [isConnecting, setIsConnecting] = useState(false); - const [error, setError] = useState(null); - - const connect = async () => { - if (isConnecting) return; - - setIsConnecting(true); - setError(null); - - try { - const apiInstance = await createApiConnection(); - setApi(apiInstance); - setIsReady(true); - } catch (err) { - const errorMessage = err instanceof Error ? err.message : 'Connection failed'; - setError(errorMessage); - setIsReady(false); - } finally { - setIsConnecting(false); - } - }; - - const reconnect = async () => { - // Disconnect existing connection - if (globalApi) { - await globalApi.disconnect(); - globalApi = null; - } - await connect(); - }; - - useEffect(() => { - // If we already have a global API, use it - if (globalApi && globalApi.isConnected) { - setApi(globalApi); - setIsReady(true); - return; - } - - // Otherwise, establish connection - connect(); - - // Cleanup on unmount - don't disconnect global API, just clean up local state - return () => { - // Note: We don't disconnect globalApi here to maintain connection across components - }; - }, []); - - // Handle disconnection events - useEffect(() => { - if (!api) return; - - const handleDisconnected = () => { - if (import.meta.env.DEV) { - console.log('[PezkuwiApi] Disconnected, attempting to reconnect...'); - } - setIsReady(false); - reconnect(); - }; - - api.on('disconnected', handleDisconnected); - - return () => { - api.off('disconnected', handleDisconnected); - }; - }, [api]); - - return { - api, - isReady, - isConnecting, - error, - reconnect, - }; -} - -// Export helper to get the global API instance (for non-hook usage) -export function getApiInstance(): ApiPromise | null { - return globalApi; -} diff --git a/web/src/telegram/hooks/useTelegram.ts b/web/src/telegram/hooks/useTelegram.ts deleted file mode 100644 index b3060137..00000000 --- a/web/src/telegram/hooks/useTelegram.ts +++ /dev/null @@ -1,314 +0,0 @@ -import { useEffect, useState, useCallback } from 'react'; -import WebApp from '@twa-dev/sdk'; - -export interface TelegramUser { - id: number; - first_name: string; - last_name?: string; - username?: string; - language_code?: string; - is_premium?: boolean; - photo_url?: string; -} - -export interface TelegramTheme { - bg_color: string; - text_color: string; - hint_color: string; - link_color: string; - button_color: string; - button_text_color: string; - secondary_bg_color: string; -} - -interface UseTelegramReturn { - // State - isReady: boolean; - isTelegram: boolean; - user: TelegramUser | null; - startParam: string | null; - theme: TelegramTheme | null; - colorScheme: 'light' | 'dark'; - viewportHeight: number; - viewportStableHeight: number; - isExpanded: boolean; - - // Actions - ready: () => void; - expand: () => void; - close: () => void; - showAlert: (message: string) => void; - showConfirm: (message: string) => Promise; - showPopup: (params: { title?: string; message: string; buttons?: Array<{ id: string; type?: string; text: string }> }) => Promise; - openLink: (url: string, options?: { try_instant_view?: boolean }) => void; - openTelegramLink: (url: string) => void; - sendData: (data: string) => void; - enableClosingConfirmation: () => void; - disableClosingConfirmation: () => void; - setHeaderColor: (color: string) => void; - setBackgroundColor: (color: string) => void; - - // Main Button - showMainButton: (text: string, onClick: () => void) => void; - hideMainButton: () => void; - setMainButtonLoading: (loading: boolean) => void; - - // Back Button - showBackButton: (onClick: () => void) => void; - hideBackButton: () => void; - - // Haptic Feedback - hapticImpact: (style: 'light' | 'medium' | 'heavy' | 'rigid' | 'soft') => void; - hapticNotification: (type: 'error' | 'success' | 'warning') => void; - hapticSelection: () => void; -} - -export function useTelegram(): UseTelegramReturn { - const [isReady, setIsReady] = useState(false); - const [isTelegram, setIsTelegram] = useState(false); - const [user, setUser] = useState(null); - const [startParam, setStartParam] = useState(null); - const [theme, setTheme] = useState(null); - const [colorScheme, setColorScheme] = useState<'light' | 'dark'>('dark'); - const [viewportHeight, setViewportHeight] = useState(window.innerHeight); - const [viewportStableHeight, setViewportStableHeight] = useState(window.innerHeight); - const [isExpanded, setIsExpanded] = useState(false); - - // Initialize Telegram WebApp - useEffect(() => { - try { - // Check if running in Telegram WebApp environment - const tg = WebApp; - - if (tg && tg.initData) { - setIsTelegram(true); - - // Get user info - if (tg.initDataUnsafe?.user) { - setUser(tg.initDataUnsafe.user as TelegramUser); - } - - // Get start parameter (referral code, etc.) - if (tg.initDataUnsafe?.start_param) { - setStartParam(tg.initDataUnsafe.start_param); - } - - // Get theme - if (tg.themeParams) { - setTheme(tg.themeParams as TelegramTheme); - } - - // Get color scheme - setColorScheme(tg.colorScheme as 'light' | 'dark' || 'dark'); - - // Get viewport - setViewportHeight(tg.viewportHeight || window.innerHeight); - setViewportStableHeight(tg.viewportStableHeight || window.innerHeight); - setIsExpanded(tg.isExpanded || false); - - // Listen for viewport changes - tg.onEvent('viewportChanged', (event: { isStateStable: boolean }) => { - setViewportHeight(tg.viewportHeight); - if (event.isStateStable) { - setViewportStableHeight(tg.viewportStableHeight); - } - }); - - // Listen for theme changes - tg.onEvent('themeChanged', () => { - setTheme(tg.themeParams as TelegramTheme); - setColorScheme(tg.colorScheme as 'light' | 'dark' || 'dark'); - }); - - // Signal that app is ready - tg.ready(); - setIsReady(true); - - // Expand by default for better UX - tg.expand(); - - if (import.meta.env.DEV) { - console.log('[Telegram] Mini App initialized'); - console.log('[Telegram] User:', tg.initDataUnsafe?.user); - console.log('[Telegram] Start param:', tg.initDataUnsafe?.start_param); - } - } else { - // Not running in Telegram, but still mark as ready - setIsReady(true); - if (import.meta.env.DEV) { - console.log('[Telegram] Not running in Telegram WebApp environment'); - } - } - } catch (err) { - console.error('[Telegram] Initialization error:', err); - setIsReady(true); // Mark as ready even on error for graceful fallback - } - }, []); - - // Actions - const ready = useCallback(() => { - if (isTelegram) WebApp.ready(); - }, [isTelegram]); - - const expand = useCallback(() => { - if (isTelegram) { - WebApp.expand(); - setIsExpanded(true); - } - }, [isTelegram]); - - const close = useCallback(() => { - if (isTelegram) WebApp.close(); - }, [isTelegram]); - - const showAlert = useCallback((message: string) => { - if (isTelegram) { - WebApp.showAlert(message); - } else { - alert(message); - } - }, [isTelegram]); - - const showConfirm = useCallback((message: string): Promise => { - return new Promise((resolve) => { - if (isTelegram) { - WebApp.showConfirm(message, (confirmed) => { - resolve(confirmed); - }); - } else { - resolve(confirm(message)); - } - }); - }, [isTelegram]); - - const showPopup = useCallback((params: { title?: string; message: string; buttons?: Array<{ id: string; type?: string; text: string }> }): Promise => { - return new Promise((resolve) => { - if (isTelegram) { - WebApp.showPopup(params, (buttonId) => { - resolve(buttonId || ''); - }); - } else { - // Fallback for non-Telegram environment - const result = confirm(params.message); - resolve(result ? 'ok' : 'cancel'); - } - }); - }, [isTelegram]); - - const openLink = useCallback((url: string, options?: { try_instant_view?: boolean }) => { - if (isTelegram) { - WebApp.openLink(url, options); - } else { - window.open(url, '_blank'); - } - }, [isTelegram]); - - const openTelegramLink = useCallback((url: string) => { - if (isTelegram) { - WebApp.openTelegramLink(url); - } else { - window.open(url, '_blank'); - } - }, [isTelegram]); - - const sendData = useCallback((data: string) => { - if (isTelegram) WebApp.sendData(data); - }, [isTelegram]); - - const enableClosingConfirmation = useCallback(() => { - if (isTelegram) WebApp.enableClosingConfirmation(); - }, [isTelegram]); - - const disableClosingConfirmation = useCallback(() => { - if (isTelegram) WebApp.disableClosingConfirmation(); - }, [isTelegram]); - - const setHeaderColor = useCallback((color: string) => { - if (isTelegram) WebApp.setHeaderColor(color as `#${string}`); - }, [isTelegram]); - - const setBackgroundColor = useCallback((color: string) => { - if (isTelegram) WebApp.setBackgroundColor(color as `#${string}`); - }, [isTelegram]); - - // Main Button - const showMainButton = useCallback((text: string, onClick: () => void) => { - if (isTelegram) { - WebApp.MainButton.setText(text); - WebApp.MainButton.onClick(onClick); - WebApp.MainButton.show(); - } - }, [isTelegram]); - - const hideMainButton = useCallback(() => { - if (isTelegram) WebApp.MainButton.hide(); - }, [isTelegram]); - - const setMainButtonLoading = useCallback((loading: boolean) => { - if (isTelegram) { - if (loading) { - WebApp.MainButton.showProgress(); - } else { - WebApp.MainButton.hideProgress(); - } - } - }, [isTelegram]); - - // Back Button - const showBackButton = useCallback((onClick: () => void) => { - if (isTelegram) { - WebApp.BackButton.onClick(onClick); - WebApp.BackButton.show(); - } - }, [isTelegram]); - - const hideBackButton = useCallback(() => { - if (isTelegram) WebApp.BackButton.hide(); - }, [isTelegram]); - - // Haptic Feedback - const hapticImpact = useCallback((style: 'light' | 'medium' | 'heavy' | 'rigid' | 'soft') => { - if (isTelegram) WebApp.HapticFeedback.impactOccurred(style); - }, [isTelegram]); - - const hapticNotification = useCallback((type: 'error' | 'success' | 'warning') => { - if (isTelegram) WebApp.HapticFeedback.notificationOccurred(type); - }, [isTelegram]); - - const hapticSelection = useCallback(() => { - if (isTelegram) WebApp.HapticFeedback.selectionChanged(); - }, [isTelegram]); - - return { - isReady, - isTelegram, - user, - startParam, - theme, - colorScheme, - viewportHeight, - viewportStableHeight, - isExpanded, - ready, - expand, - close, - showAlert, - showConfirm, - showPopup, - openLink, - openTelegramLink, - sendData, - enableClosingConfirmation, - disableClosingConfirmation, - setHeaderColor, - setBackgroundColor, - showMainButton, - hideMainButton, - setMainButtonLoading, - showBackButton, - hideBackButton, - hapticImpact, - hapticNotification, - hapticSelection, - }; -} diff --git a/web/src/telegram/index.ts b/web/src/telegram/index.ts deleted file mode 100644 index 3722096a..00000000 --- a/web/src/telegram/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { TelegramApp } from './TelegramApp'; -export { useTelegram, usePezkuwiApi } from './hooks'; -export type { TelegramUser, TelegramTheme } from './hooks'; diff --git a/web/supabase/functions/process-withdraw/index.ts b/web/supabase/functions/process-withdraw/index.ts index 14a40f59..996e9f3b 100644 --- a/web/supabase/functions/process-withdraw/index.ts +++ b/web/supabase/functions/process-withdraw/index.ts @@ -386,7 +386,7 @@ serve(async (req) => { } // Success! Complete the withdrawal using database function - const { data: completeResult, error: completeError } = await serviceClient + const { error: completeError } = await serviceClient .rpc('complete_withdraw', { p_user_id: user.id, p_token: token,