The Ultimate Offline-First Egyptian Summer Party Game Hub & Android TWA Container
Dastet Al3ab (دستة ألعاب) is an offline-first party game hub built for Sahel hangouts, Egyptian summer trips, beach camps, and road trips. It houses a diverse collection of card games, trivia, forbidden words (Taboo), hot potato, and social challenges—all running 100% client-side with zero backend database requirement.
The application is deployed on Vercel as a Progressive Web App (PWA) and packaged as a native Android application using Google's Trusted Web Activity (TWA) framework via Bubblewrap CLI. Once installed on a mobile device or home screen, games can be played completely offline without internet connectivity.
Explore our complete developer & contributor documentation suite:
| Document | Description |
|---|---|
| 🏗️ System Architecture & Workflow | Explains Next.js PWA-to-TWA architecture, instant Vercel over-the-air updates, and Digital Asset Links (assetlinks.json) verification. |
| 💻 Local Development Guide | Instructions for setting up the web app locally, running the dev server, and auditing Service Worker offline features. |
| 📱 Android TWA Build Pipeline | Comprehensive guide for Bubblewrap CLI, JDK 17 setup, keystore signing, .aab production bundles, and Play Store releases. |
| 🔒 Code Contribution Guide | Repository infrastructure guidelines, TypeScript/Tailwind standards, PR conventions, and strict security rules. |
| 🎮 Games Contribution Guide | Step-by-step tutorial on creating new game JSON engines, card datasets, and submitting games via GitHub or Creator Dashboard. |
Get up and running in 3 simple terminal commands:
git clone https://github.com/Imhotep-Tech/dastet-al3ab.git
cd dastet-al3ab
npm install && npm run devOpen http://localhost:3000 in your browser to start playing and testing locally.
dastet-al3ab/
├── docs/ # Complete technical & contribution documentation
│ ├── architecture.md # PWA-to-TWA workflow & Vercel deployment architecture
│ ├── local-development.md # Web environment setup & PWA testing guide
│ ├── android-twa-build.md # Bubblewrap CLI & Play Store release build pipeline
│ ├── CONTRIBUTING-CODE.md # Code standards, security safeguards & PR workflow
│ └── CONTRIBUTING-GAMES.md # Guide for adding new mini-games & cards
├── public/ # Static web assets & service worker outputs
│ ├── .well-known/ # Digital Asset Links verification (assetlinks.json)
│ ├── manifest.json # Web App Manifest
│ ├── sw.js # Compiled Serwist offline Service Worker
│ └── dasta.png # Application logos & launcher icons
├── src/ # Next.js App Router source code
│ ├── app/ # Next.js App Router pages (Home, Game Dispatcher, Creator)
│ ├── components/ # Reusable UI components & BaseEngineLayout wrapper
│ ├── data/ # JSON Game configurations (games/) & Card decks (cards/)
│ ├── engines/ # Game engine components (Classic, MCQ, Taboo, Imposter)
│ ├── hooks/ # Custom state hooks (useGameEngine.ts)
│ └── utils/ # Helper functions (gameUtils.ts)
├── android-app/ # Android TWA Gradle project module (Bubblewrap CLI)
├── twa-manifest.json # Bubblewrap Android TWA package manifest
├── package.json # Project dependencies & build scripts
└── next.config.mjs # Next.js configuration wrapped with @serwist/next
Game state and presentation logic are decoupled cleanly through a unified custom hook and layout wrapper:
graph TD
A[Game Config JSON] -->|Loaded via ID| B(GameDispatcher)
B -->|Resolves Template| C[Custom Game Engine Component]
C -->|Hook Call| D(useGameEngine)
C -->|Renders UI Frame| E(BaseEngineLayout)
D -->|Provides turns, timers, scores| C
E -->|Provides sticky header, scoreboard, modals| C
Engine components wrap their game card UI inside BaseEngineLayout while deriving turn/score state from useGameEngine:
import { useGameEngine } from "@/hooks/useGameEngine";
import BaseEngineLayout from "@/components/BaseEngineLayout";
export default function CustomGameEngine({ config }) {
const engineState = useGameEngine(config);
const currentCard = config.cards[engineState.currentCardIndex];
return (
<BaseEngineLayout config={config} engineState={engineState}>
<div className="bg-slate-900 border border-slate-800 p-8 rounded-3xl text-center">
<h2 className="text-2xl font-black mb-4">{currentCard.question}</h2>
<button
onClick={engineState.handleCorrect}
className="bg-indigo-600 hover:bg-indigo-500 text-white font-bold px-6 py-2 rounded-xl"
>
إجابة صحيحة
</button>
</div>
</BaseEngineLayout>
);
}Maintained with passion by Imhotep Tech 🌴✨.
Contributions are welcome! Check out our Code Contribution Guide and Games Contribution Guide to get started.
