diff --git a/web/src/App.tsx b/web/src/App.tsx
index 9d851330..4e62cb72 100644
--- a/web/src/App.tsx
+++ b/web/src/App.tsx
@@ -69,6 +69,7 @@ const BankPage = lazy(() => import('@/pages/finance/BankPage'));
const AssemblyPage = lazy(() => import('@/pages/governance/AssemblyPage'));
const JusticePage = lazy(() => import('@/pages/governance/JusticePage'));
const PollsPage = lazy(() => import('@/pages/governance/PollsPage'));
+const WhatsKURDPage = lazy(() => import('@/pages/social/WhatsKURDPage'));
// Network pages
const Mainnet = lazy(() => import('@/pages/networks/Mainnet'));
@@ -236,6 +237,7 @@ function App() {
} />
} />
} />
+ } />
} />
} />
} />
diff --git a/web/src/components/MobileHomeLayout.tsx b/web/src/components/MobileHomeLayout.tsx
index d999d89f..7439b29a 100644
--- a/web/src/components/MobileHomeLayout.tsx
+++ b/web/src/components/MobileHomeLayout.tsx
@@ -81,7 +81,7 @@ const APP_SECTIONS: AppSection[] = [
emoji: '๐ฌ',
borderColor: 'border-l-blue-500',
apps: [
- { title: 'mobile.app.whatsKurd', icon: '๐ฌ', route: '/message', comingSoon: true },
+ { title: 'mobile.app.whatsKurd', icon: '๐ฌ', route: '/social/whatskurd' },
{ title: 'mobile.app.forum', icon: '๐ฐ', route: '/forum' },
{ title: 'mobile.app.kurdMedia', icon: '๐บ', route: '/forum', comingSoon: true },
{ title: 'mobile.app.events', icon: '๐
', route: '/forum', comingSoon: true },
diff --git a/web/src/pages/social/WhatsKURDPage.tsx b/web/src/pages/social/WhatsKURDPage.tsx
new file mode 100644
index 00000000..5e71f33f
--- /dev/null
+++ b/web/src/pages/social/WhatsKURDPage.tsx
@@ -0,0 +1,187 @@
+import { useState, useEffect } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { useTranslation } from 'react-i18next';
+import { usePezkuwi } from '@/contexts/PezkuwiContext';
+import { useMessaging } from '@/hooks/useMessaging';
+import { KeySetup } from '@/components/messaging/KeySetup';
+import { InboxMessage } from '@/components/messaging/InboxMessage';
+import { ComposeDialog } from '@/components/messaging/ComposeDialog';
+import { Loader2 } from 'lucide-react';
+
+export default function WhatsKURDPage() {
+ const navigate = useNavigate();
+ const { t } = useTranslation();
+ const { peopleApi, isPeopleReady, selectedAccount } = usePezkuwi();
+
+ const {
+ palletReady,
+ isKeyRegistered,
+ isKeyUnlocked,
+ decryptedMessages,
+ era,
+ sendCount,
+ loading,
+ sending,
+ registering,
+ setupKey,
+ unlockKey,
+ sendMessage,
+ refreshInbox,
+ } = useMessaging();
+
+ const [composeOpen, setComposeOpen] = useState(false);
+ const [currentBlock, setCurrentBlock] = useState(0);
+
+ useEffect(() => {
+ if (!peopleApi || !isPeopleReady) return;
+ let unsub: (() => void) | undefined;
+ const subscribe = async () => {
+ try {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ unsub = await (peopleApi.rpc.chain as any).subscribeNewHeads(
+ (header: { number: { toNumber: () => number } }) => {
+ setCurrentBlock(header.number.toNumber());
+ }
+ );
+ } catch {
+ try {
+ const header = await peopleApi.rpc.chain.getHeader();
+ setCurrentBlock(header.number.toNumber());
+ } catch { /* ignore */ }
+ }
+ };
+ subscribe();
+ return () => { unsub?.(); };
+ }, [peopleApi, isPeopleReady]);
+
+ const canCompose = isKeyRegistered && isKeyUnlocked && palletReady;
+
+ return (
+
+ {/* Header */}
+
+
+
+ {t('mobile.section.social', 'Social')}
+
+
+
+
๐ฌ whatsKURD
+
{t('messaging.title', 'Encrypted Messenger')}
+
+
+
+
+ {/* Era / stats */}
+ {selectedAccount && (
+
+ Era {era}
+ ยท
+ {decryptedMessages.length} {t('messaging.messages', 'messages')}
+ ยท
+ {sendCount}/50 {t('messaging.sent', 'sent')}
+
+ )}
+
+
+
+
+ {/* No wallet */}
+ {!selectedAccount && (
+
+
๐
+
{t('messaging.connectWallet', 'Connect Wallet')}
+
{t('messaging.connectWalletDesc', 'Connect your wallet to use encrypted messaging.')}
+
+ )}
+
+ {/* API loading */}
+ {selectedAccount && !isPeopleReady && (
+
+
+
+ )}
+
+ {selectedAccount && isPeopleReady && (
+ <>
+ {/* Pallet not ready */}
+ {!loading && !palletReady && (
+
+
โ ๏ธ
+
+ {t('messaging.palletNotReady', 'Messaging pallet is not yet available on People Chain. A runtime upgrade is required.')}
+
+
+ )}
+
+ {/* Key setup */}
+ {palletReady && (
+
+ )}
+
+ {/* Inbox */}
+ {loading ? (
+
+
+
+ ) : decryptedMessages.length === 0 ? (
+
+
๐ญ
+
{t('messaging.emptyInbox', 'No messages yet.')}
+ {canCompose && (
+
+ )}
+
+ ) : (
+
+ {decryptedMessages.map((msg, i) => (
+
+ ))}
+
+ )}
+ >
+ )}
+
+
+ {/* FAB โ compose button */}
+ {canCompose && (
+
+ )}
+
+
+
+
+
+ );
+}