From 31e768de45934733f7a8238305984cab584ccb68 Mon Sep 17 00:00:00 2001 From: Kurdistan Tech Ministry Date: Thu, 26 Feb 2026 18:36:35 +0300 Subject: [PATCH] feat: integrate P2P trading into Telegram mini app - Add 8 Supabase edge functions for P2P operations (get-internal-balance, get-payment-methods, get-p2p-offers, accept-p2p-offer, get-p2p-trades, trade-action, p2p-messages, p2p-dispute) - Add frontend P2P API layer (src/lib/p2p-api.ts) - Add 8 P2P components (BalanceCard, OfferList, TradeModal, CreateOfferModal, TradeView, TradeChat, DisputeModal, P2P section) - Embed P2P as internal section in App.tsx instead of external link - Remove old P2PModal component - Add ~70 P2P translation keys across all 6 languages --- src/App.tsx | 60 +- src/components/P2PModal.tsx | 85 --- src/components/p2p/BalanceCard.tsx | 106 ++++ src/components/p2p/CreateOfferModal.tsx | 308 ++++++++++ src/components/p2p/DisputeModal.tsx | 145 +++++ src/components/p2p/OfferList.tsx | 196 ++++++ src/components/p2p/TradeChat.tsx | 160 +++++ src/components/p2p/TradeModal.tsx | 174 ++++++ src/components/p2p/TradeView.tsx | 342 +++++++++++ src/i18n/translations/ar.ts | 94 ++- src/i18n/translations/ckb.ts | 96 ++- src/i18n/translations/en.ts | 82 +++ src/i18n/translations/fa.ts | 96 ++- src/i18n/translations/krd.ts | 86 ++- src/i18n/translations/tr.ts | 94 ++- src/i18n/types.ts | 94 ++- src/lib/p2p-api.ts | 328 ++++++++++ src/sections/P2P.tsx | 259 ++++++++ supabase/functions/accept-p2p-offer/index.ts | 223 +++++++ .../functions/get-internal-balance/index.ts | 182 ++++++ supabase/functions/get-p2p-offers/index.ts | 268 +++++++++ supabase/functions/get-p2p-trades/index.ts | 212 +++++++ .../functions/get-payment-methods/index.ts | 185 ++++++ supabase/functions/p2p-dispute/index.ts | 385 ++++++++++++ supabase/functions/p2p-messages/index.ts | 278 +++++++++ supabase/functions/trade-action/index.ts | 563 ++++++++++++++++++ 26 files changed, 4938 insertions(+), 163 deletions(-) delete mode 100644 src/components/P2PModal.tsx create mode 100644 src/components/p2p/BalanceCard.tsx create mode 100644 src/components/p2p/CreateOfferModal.tsx create mode 100644 src/components/p2p/DisputeModal.tsx create mode 100644 src/components/p2p/OfferList.tsx create mode 100644 src/components/p2p/TradeChat.tsx create mode 100644 src/components/p2p/TradeModal.tsx create mode 100644 src/components/p2p/TradeView.tsx create mode 100644 src/lib/p2p-api.ts create mode 100644 src/sections/P2P.tsx create mode 100644 supabase/functions/accept-p2p-offer/index.ts create mode 100644 supabase/functions/get-internal-balance/index.ts create mode 100644 supabase/functions/get-p2p-offers/index.ts create mode 100644 supabase/functions/get-p2p-trades/index.ts create mode 100644 supabase/functions/get-payment-methods/index.ts create mode 100644 supabase/functions/p2p-dispute/index.ts create mode 100644 supabase/functions/p2p-messages/index.ts create mode 100644 supabase/functions/trade-action/index.ts diff --git a/src/App.tsx b/src/App.tsx index 4f9183d..89a17e6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,10 +1,7 @@ -import { useState, lazy, Suspense, useCallback } from 'react'; +import { useState, lazy, Suspense } from 'react'; import { Megaphone, MessageCircle, Gift, Wallet, Loader2, ArrowLeftRight } from 'lucide-react'; import { cn } from '@/lib/utils'; import { UpdateNotification } from '@/components/UpdateNotification'; -import { P2PModal } from '@/components/P2PModal'; -import { useAuth } from '@/contexts/AuthContext'; -import { useWallet } from '@/contexts/WalletContext'; import { useTranslation } from '@/i18n'; // Lazy load sections for code splitting @@ -20,6 +17,9 @@ const RewardsSection = lazy(() => const WalletSection = lazy(() => import('@/sections/Wallet').then((m) => ({ default: m.WalletSection })) ); +const P2PSection = lazy(() => + import('@/sections/P2P').then((m) => ({ default: m.P2PSection })) +); const CitizenPage = lazy(() => import('@/pages/CitizenPage').then((m) => ({ default: m.CitizenPage })) ); @@ -36,27 +36,22 @@ function SectionLoader() { ); } -type Section = 'announcements' | 'forum' | 'rewards' | 'wallet'; -type NavId = Section | 'p2p'; +type Section = 'announcements' | 'forum' | 'rewards' | 'p2p' | 'wallet'; interface NavItem { - id: NavId; + id: Section; icon: typeof Megaphone; labelKey: string; - isExternal?: boolean; } const NAV_ITEMS: NavItem[] = [ { id: 'announcements', icon: Megaphone, labelKey: 'nav.announcements' }, { id: 'forum', icon: MessageCircle, labelKey: 'nav.forum' }, { id: 'rewards', icon: Gift, labelKey: 'nav.rewards' }, - { id: 'p2p', icon: ArrowLeftRight, labelKey: 'nav.p2p', isExternal: true }, + { id: 'p2p', icon: ArrowLeftRight, labelKey: 'nav.p2p' }, { id: 'wallet', icon: Wallet, labelKey: 'nav.wallet' }, ]; -// P2P Web App URL - Mobile-optimized P2P -const P2P_WEB_URL = 'https://telegram.pezkuwichain.io/p2p'; - // Check for standalone pages via URL query params or path (evaluated once at module level) const PAGE_PARAM = new URLSearchParams(window.location.search).get('page'); const IS_CITIZEN_PAGE = PAGE_PARAM === 'citizen' || window.location.pathname === '/citizens'; @@ -84,42 +79,11 @@ export default function App() { function MainApp() { const [activeSection, setActiveSection] = useState
('announcements'); - const [showP2PModal, setShowP2PModal] = useState(false); - const { sessionToken } = useAuth(); - const { address } = useWallet(); const { t } = useTranslation(); - // Open P2P in popup with auth params - const openP2P = useCallback(() => { - window.Telegram?.WebApp.HapticFeedback.impactOccurred('medium'); - - // Build auth URL with session token - const params = new URLSearchParams(); - if (sessionToken) { - params.set('session_token', sessionToken); - } - if (address) { - params.set('wallet', address); - } - params.set('from', 'miniapp'); - - const url = `${P2P_WEB_URL}?${params.toString()}`; - - // Open in new window/tab - window.open(url, '_blank'); - }, [sessionToken, address]); - const handleNavClick = (item: NavItem) => { window.Telegram?.WebApp.HapticFeedback.selectionChanged(); - - if (item.isExternal) { - // P2P opens modal first - if (item.id === 'p2p') { - setShowP2PModal(true); - } - } else { - setActiveSection(item.id as Section); - } + setActiveSection(item.id); }; return ( @@ -131,6 +95,7 @@ function MainApp() { {activeSection === 'announcements' && } {activeSection === 'forum' && } {activeSection === 'rewards' && } + {activeSection === 'p2p' && } {activeSection === 'wallet' && } @@ -139,15 +104,12 @@ function MainApp() { {/* Update Notification */} - {/* P2P Modal */} - setShowP2PModal(false)} onOpenP2P={openP2P} /> - {/* Bottom Navigation */}