Files
volana/docs/superpowers/specs/2026-07-02-expo-app-ui-design.md
thesn10 0186a2bf1c Add design spec for translating the HTML prototype into the Expo app
Captures navigation structure, theming, mock data, state, and
responsive (mobile + web) layout decisions agreed on during
brainstorming before implementation planning starts.
2026-07-02 21:48:23 +02:00

136 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Volana Expo App — UI-Umsetzung des Prototyps
## Kontext
`prototype/Volana.dc.html` ist ein interaktiver HTML-Prototyp (DC-Format mit Template-Bindings à la `{{ x }}`, `sc-if`, `sc-for`) der Volana-Konsumenten-Oberfläche: ein dezentraler, zensurresistenter Marktplatz auf Solana (siehe `docs/volana-design-brief.md` für den vollständigen Produkt-Kontext). Der Prototyp enthält Homepage, Browse (Liste/Grid), Listing-Detail, My Orders, Order-Detail, Wallet-Connect-Modal, Checkout-Modal (Review/Pending/Success) und Toasts, inklusive vollständiger Mock-Daten (12 Produkte, 3 Dispute-Resolver, 3 Orders) und Farb-/Theming-Variablen für Light/Dark.
Die Expo-App (`app/`) ist aktuell ein leeres Grundgerüst: Expo Router mit `(tabs)`-Gruppe (Home/Explore), HeroUI Native, Uniwind (Tailwind v4 für React Native), React 19 / RN 0.86 / Expo SDK 57.
**Ziel dieser Iteration:** Das Design des Prototyps 1:1 in Seiten/Komponenten der Expo-App übertragen — mobil UND auf Web (`react-native-web`, in den Dependencies vorhanden) nahe am Prototyp. Keine echte Funktionalität (kein echtes Wallet, kein Backend) — Mock-Daten reichen. Styling bleibt so nah wie möglich an den HeroUI-Native-Defaults; nur Akzentfarbe, Statusfarben und Radius werden angepasst (Theming), komplexere Komponenten/Seiten bekommen zusätzlich Custom-Styles via `className` (Uniwind/Tailwind) bzw. `StyleSheet` wo nötig.
## Nicht-Ziele
- Keine echte Solana-Wallet-Integration (`@solana/connector` o.ä.) — Connect/Disconnect ist gemockt wie im Prototyp.
- Kein Backend/On-Chain-Datenfetching — statische Mock-Daten aus dem Prototyp übernommen.
- Keine Persistenz von Bestellungen (gekaufte Listings erscheinen nicht dynamisch in "My Orders" — der Prototyp macht das ebenfalls nicht).
- Kein manueller Light/Dark-Toggle — die App folgt dem System-Theme (Uniwind unterstützt das nativ).
- Kein Footer (im Prototyp vorhanden, in der App nicht nötig).
- Keine Pixel-genaue 1:1-Übernahme der Inline-Styles des Prototyps — HeroUI-Native-Komponenten und ihre Standard-Abstände/Formen bleiben so weit wie möglich erhalten.
## Theming
Anpassung ausschließlich über CSS-Variablen-Overrides in `src/global.css` (`@layer theme { @variant light {...} @variant dark {...} }`), wie in der HeroUI-Native-Theming-Doku beschrieben. Kein Erstellen neuer Custom-Colors — bestehende semantische Slots werden mit den Prototyp-Werten belegt:
| Variable | Dark (Prototyp `--c-*`) | Light (Prototyp `--c-*`) | Verwendung |
|---|---|---|---|
| `--accent` / `--accent-foreground` | `#b060ff` / weiß | `#7a34d4` / weiß | Primär-CTA, Preis-Hervorhebung, aktive Filter |
| `--success` | `#14f195` | `#059669` | "In Stock", "Verified", "Complete", "Final Payment" |
| `--warning` | `#f5a623` | `#d97706` | "Low Stock", "Awaiting Confirm" |
| `--danger` | `#ff4d4f` | `#dc2626` | "Out of Stock", "Disputed", "Cancel Order" |
| `--background` / `--surface` / `--surface-secondary` | leicht lila-stichige Dunkeltöne (`#0e0e1c`/`#171730`/`#1e1e3c`) | helle lila-stichige Töne (`#f7f6fb`/`#ffffff`/`#f0eef8`) | Basis-/Karten-Hintergründe |
| `--radius` | Basiswert so gewählt, dass abgeleitete `--radius-lg`/`--radius-xl` in etwa den 814px-Radii des Prototyps entsprechen | gleich | Cards, Buttons, Inputs |
Die im Prototyp verwendete "Solana-Mainnet"-Grün-Punkt/"Verified"-Optik nutzt direkt `success`, keine eigene Farbe nötig.
## Datenmodell & Mock-Daten
Neue Datei `src/data/mock.ts` mit TS-Typen und den Werten 1:1 aus dem Prototyp (`_L`, `_R`, `_O` Arrays in `Volana.dc.html`):
```ts
type Currency = 'SOL' | 'USDC';
interface Product {
id: number; name: string; desc: string;
price: string; priceNum: number; cur: Currency; usd: string;
qty: number; seller: string; bg: string; emoji: string;
alt: Currency[]; cat: string;
}
interface Resolver {
id: number; name: string; desc: string;
fee: string; type: 'Human' | 'DAO' | 'Automated';
wins: number; total: number;
}
type OrderStatus = 'AwaitingConfirm' | 'Active' | 'Complete' | 'Cancelled' | 'Disputed';
interface Order {
id: number; lid: number; name: string; amt: string;
status: OrderStatus; date: string; seller: string; emoji: string;
}
```
Emojis dienen weiterhin als Platzhalter-"Produktbild" (wie im Prototyp) statt echter Bilder — kein Asset-Aufwand nötig. Helper-Funktionen analog zu `_si` (Status → Label/Farbe) und `_av` (Verfügbarkeit → Label/Farbe) werden als reine Funktionen in `src/data/mock.ts` oder `src/lib/status.ts` nachgebaut.
## State
- **`WalletProvider`** (`src/state/wallet.tsx`, React Context): `connected: boolean`, `address: string | null`, `connect(wallet: 'phantom' | 'solflare' | 'backpack')` (setzt eine fixe Mock-Adresse je Wallet, identisch zum Prototyp), `disconnect()`.
- **`ModalProvider`** (`src/state/modals.tsx`, React Context): zentraler Zustand für die beiden globalen Overlays:
- Wallet-Connect-Sheet: `open`, `pendingListingId` (falls "Buy Now" ohne verbundenes Wallet ausgelöst wurde → nach Connect automatisch Checkout öffnen, wie im Prototyp)
- Checkout-Sheet: `open`, `listingId`, `step: 'review' | 'pending' | 'success'`, inkl. simuliertem `setTimeout`-Übergang `pending → success` wie im Prototyp (2.2s)
- Bestellungen bleiben statische Mock-Daten (siehe Nicht-Ziele) — kein zusätzlicher Order-State nötig.
- Toasts über HeroUI Natives eingebauten `useToast`/`ToastProvider` (Teil von `HeroUINativeProvider`), verwendet für: Wallet verbunden/getrennt, Adresse kopiert, Order storniert/bestätigt.
Beide Provider werden in `app/_layout.tsx` um den bestehenden `HeroUINativeProvider` ergänzt.
## Navigation & Screens
```
app/_layout.tsx Root Stack + WalletProvider + ModalProvider; rendert zusätzlich
<TopNav /> (nur ab `md:`, s.u.) + globale Sheets (WalletConnectSheet, CheckoutSheet)
app/(tabs)/_layout.tsx Bottom Tabs: Home | Browse | Orders (Tab-Bar ab `md:` ausgeblendet)
app/(tabs)/index.tsx Home
app/(tabs)/browse.tsx Browse (ersetzt bisheriges explore.tsx)
app/(tabs)/orders.tsx My Orders (Liste)
app/listing/[id].tsx Listing-Detail — Stack-Push über den Tabs, eigener Header mit Zurück-Button
app/orders/[id].tsx Order-Detail — Stack-Push über den Tabs, eigener Header mit Zurück-Button
```
Wallet-Connect- und Checkout-Overlay sind bewusst **keine Routen**, sondern globale, über `ModalProvider` gesteuerte `BottomSheet`-Komponenten im Root-Layout — "Buy Now" ist von Home, Browse und Listing-Detail auslösbar und muss überall denselben zentralen State treffen (entspricht dem zentralen Component-State im Prototyp).
### Responsive Shell (Mobile vs. Web/Desktop)
Umschaltung über Uniwind-Responsive-Utilities (Breakpoint `md:`, ~768px) — funktioniert sowohl nativ (z. B. Tablet-Breite) als auch im Web-Build:
- **`TopNav`** (`src/components/TopNav.tsx`): Logo, Suchfeld, "Browse"-/"My Orders"-Links, Wallet-Button/-Pill — analog zur Prototyp-Nav. Klasse `hidden md:flex`, liegt im Root-Layout über allen Screens (auch Listing-/Order-Detail), damit Web-Nutzer durchgehend navigieren können.
- **Bottom-Tab-Bar:** in `(tabs)/_layout.tsx` per `useWindowDimensions` (React-Navigation-`tabBarStyle` ist keine Tailwind-Klasse, daher JS-Bedingung statt CSS) ab `md:`-Breite auf `display: 'none'` gesetzt.
- **Ergebnis:** Mobile = Bottom-Tabs + Stack-Pushes für Details. Web/Desktop = Top-Nav wie Prototyp, keine Tab-Bar, Navigation über `router.push`.
**Grid-Layouts:** React Native kennt kein CSS-Grid (auch nicht im Web-Build, da react-native-web View-Primitives nutzt) — alle "Grid"-Bereiche (Browse-Grid, Featured Listings, Customers-also-bought) werden über `flex-row flex-wrap` + Breiten-Utilities (`w-1/2 md:w-1/3 lg:w-1/4` o. ä.) gebaut, nicht über `display: grid`.
**Listing-Detail:** `flex-col md:flex-row` (mobil gestapelt, ab `md:` zweispaltig wie im Prototyp). Die rechte Kauf-Box nutzt auf Web `position: 'sticky'` (von react-native-web unterstützt); nativ scrollt sie regulär mit.
## Screens im Detail
### Home (`app/(tabs)/index.tsx`)
Kurzer Hero (Headline + Subline + "Browse Listings"-Button, kein Stats-/"Warum Volana"-/"How it works"-Block), darunter Featured-Listings (4 Karten, `ListingCard` Grid-Variante).
### Browse (`app/(tabs)/browse.tsx`)
`SearchField` (Name/Beschreibung), Filter-Reihe (`Chip`-Toggles für Currency All/SOL/USDC und Sort Newest/Price↑/Price↓), `Switch` "In stock only", List/Grid-View-Toggle (Icon-Buttons). Darunter Produktliste aus den gefilterten/sortierten Mock-Daten:
- List-View: horizontale Card (Bild-Platzhalter · Preis · Name+Beschreibung · Buy-Button), analog Prototyp-Zeilen-Layout.
- Grid-View: 2-spaltige (mobil) / mehr-spaltige (`md:`/`lg:`) vertikale Cards.
### Listing-Detail (`app/listing/[id].tsx`)
Bild-Platzhalter (Emoji auf Farbfläche), Titel, Beschreibung, "Verified on Solana"-Zeile mit Copy-Button (Toast "Address copied!"), Info-Grid (Kategorie/Verfügbar/Währung/ID), "Customers also bought" (horizontale/Flex-Wrap-Liste verwandter Produkte). Kauf-Sektion (sticky ab `md:`): Preis, Currency-Umschalter (falls `alt.length > 0`), Resolver-Auswahl (3 selektierbare Cards aus Mock-Resolvern), "Buy Now"-Button (öffnet Wallet-Sheet falls nicht verbunden, sonst Checkout-Sheet), Trust-Badges (Final Payment / Dispute Protection / On-chain Verified).
### Orders (`app/(tabs)/orders.tsx`)
Liste von `OrderRow` (Emoji, Name, Seller-Kürzel, Betrag, Datum, Status-`Chip`), Tap → `app/orders/[id].tsx`.
### Order-Detail (`app/orders/[id].tsx`)
Header (Datum, Name, Seller, Betrag, Status), 3-Schritt-Fortschrittsanzeige (Payment Held → Seller Confirms → You Confirm Receipt) mit Status-abhängiger Einfärbung wie im Prototyp (`_si`-Logik), Escrow-Info-Box, Aktions-Buttons je Status: `AwaitingConfirm` → Cancel Order (mock, zeigt Toast + navigiert zurück), `Active` → Confirm Receipt (mock, zeigt Toast), immer sichtbar → "View Listing".
## Gemeinsame Komponenten (`src/components/`)
- `TopNav` — s. o.
- `AppHeader` — einfacher mobiler Screen-Header für Stack-Screens (Titel + Zurück-Button), auf `md:` ggf. ausgeblendet da `TopNav` übernimmt
- `ListingCard` — Varianten `list` und `grid`
- `StatusChip` — mappt `OrderStatus`/Verfügbarkeit auf Label + Farbe (nutzt `Chip` von HeroUI)
- `ResolverCard` — selektierbare Resolver-Option (Listing-Detail + Checkout-Review)
- `OrderRow` — Zeile in der Orders-Liste
- `WalletConnectSheet` — globales `BottomSheet` mit den drei Mock-Wallets (Phantom/Solflare/Backpack)
- `CheckoutSheet` — globales `BottomSheet`, rendert intern je nach `step` Review-/Pending-(Spinner)-/Success-Inhalt
## Verifikation
Da keine echte Funktionalität besteht, erfolgt Verifikation visuell: App per `expo start` (Web + iOS/Android-Simulator soweit verfügbar) starten, jede Seite und beide Breakpoints (schmal/mobil, breit/`md:`+) durchklicken, Kernflows prüfen (Home → Browse → Listing → Buy Now ohne Wallet → Connect → Checkout Review → Pending → Success → My Orders; Order-Detail Cancel/Confirm-Aktionen; Such-/Filter-/Sortier-/List-Grid-Umschaltung in Browse).