# Android-App-Konzept fuer SingleChat ## Zielbild SingleChat soll als echte Android-App verfuegbar werden, nicht nur als WebView-Wrapper. Die Android-App nutzt die bestehenden Backend-Endpunkte und spricht mit dem vorhandenen Socket.IO-Server dasselbe Ereignisprotokoll wie das Vue-Web-Frontend. Das Ziel fuer den ersten Release ist Funktionsgleichheit mit dem Kern-Chat: - Login mit Benutzername, Geschlecht, Alter und Land - Anzeige aktiver Benutzer - Suche nach Benutzern - Einzelchat mit Textnachrichten - Bildversand ueber bestehenden Upload-Endpunkt - Inbox mit ungelesenen Chats - Verlauf geoeffneter Konversationen - Blockieren und Entblockieren von Benutzern - Session-Wiederherstellung, Logout und 30-Minuten-Inaktivitaetslogik - Feedback- und Partnerseiten optional im MVP, aber technisch ueber vorhandene REST-Endpunkte moeglich ## Bestand Im Repo Das aktuelle System besteht aus: - Backend: Node.js/Express in `server/index.js` - REST-Endpunkte in `server/routes.js` - Socket.IO-Chat-Protokoll in `server/broadcast.js` - Vue/Pinia-Webclient mit Socket.IO-Client in `client/src/stores/chat.js` - Bild-Upload im Webclient ueber `client/src/components/ChatInput.vue` Der Server erlaubt Socket.IO mit `websocket` und `polling`. Das Web-Frontend nutzt aktuell absichtlich nur `polling`, vermutlich wegen Proxy-/WebSocket-Problemen. Fuer Android sollte WebSocket als bevorzugter Transport genutzt werden, mit Polling als Fallback. ## Technische Empfehlung ### App-Technologie Empfohlen: native Android-App mit Kotlin und Jetpack Compose. Gruende: - echte App-Erfahrung statt WebView - robuste Hintergrund-/Reconnect-Logik - gute Kontrolle ueber Cookies, Sessions und Uploads - moderne UI mit Compose schneller wartbar - bessere Basis fuer spaetere Push Notifications Alternative: React Native oder Flutter waeren moeglich, bringen aber fuer diese App keinen klaren Vorteil, weil das Protokoll einfach ist und Android explizit das Ziel ist. ### Zielarchitektur ```text Android App UI: Jetpack Compose State: ViewModel + StateFlow REST: Retrofit/OkHttp Socket: Socket.IO Android Client Session: OkHttp CookieJar + EncryptedSharedPreferences/DataStore Images: Android Photo Picker + Multipart Upload Existing Backend Express REST API Socket.IO Chat Events express-session Cookie connect.sid ``` ## Backend-Anbindung ### Basis-URL Production: ```text https://www.ypchat.net ``` Development: ```text http://10.0.2.2:3300 ``` `10.0.2.2` ist im Android Emulator der Host-Rechner. Auf echtem Geraet braucht es die lokale LAN-IP oder einen Dev-Tunnel. ### REST-Endpunkte Die Android-App kann folgende vorhandene Endpunkte direkt verwenden: | Zweck | Methode | Pfad | Android-Nutzung | |---|---:|---|---| | Health Check | GET | `/api/health` | Diagnose/Verbindungscheck | | Session holen | GET | `/api/session` | Cookie initialisieren, Session wiederherstellen, Express-Session-ID erhalten | | Logout | POST | `/api/logout` | serverseitige Session beenden und Socket trennen | | Laenderliste | GET | `/api/countries` | Login-Land-Auswahl und Flag-Code | | Bild hochladen | POST | `/api/upload-image` | Multipart-Upload mit Session-Cookie | | Bild abrufen | GET | `/api/image/:code` | Anzeige empfangener Bilder | | Partner | GET | `/api/partners` | optionaler App-Screen | | Feedback laden | GET | `/api/feedback` | optionaler Feedback-Screen | | Feedback senden | POST | `/api/feedback` | optionaler Feedback-Screen | | Feedback Admin Login | POST | `/api/feedback/admin-login` | optional, eher nicht MVP | | Feedback Admin Logout | POST | `/api/feedback/admin-logout` | optional, eher nicht MVP | | Feedback loeschen | DELETE | `/api/feedback/:id` | optional, eher nicht MVP | Wichtig: REST und Socket muessen dieselbe Cookie-Session verwenden. Android braucht daher einen gemeinsamen `OkHttpClient` mit persistenter Cookie-Verwaltung. ## Socket.IO-Protokoll ### Verbindungsaufbau Ablauf analog zum Web-Frontend: 1. `GET /api/session` aufrufen, damit das Backend ein `connect.sid`-Cookie setzt und die `sessionId` zurueckgibt. 2. Socket.IO zu derselben Basis-URL verbinden. 3. Nach `connect` das Event `setSessionId` senden: ```json { "expressSessionId": "" } ``` 4. Server sendet `connected`. 5. Wenn `connected.loggedIn === true`, App-State wiederherstellen. 6. Sonst Login-Screen anzeigen. ### Client sendet | Event | Payload | Zweck | |---|---|---| | `setSessionId` | `{ "expressSessionId": string }` | Socket mit Express-Session verknuepfen | | `login` | `{ "userName": string, "gender": string, "age": number, "country": string, "expressSessionId": string }` | Chat-Login | | `message` | `{ "toUserName": string, "message": string, "messageId": string }` | Textnachricht | | `message` | `{ "toUserName": string, "message": imageCode, "messageId": string, "isImage": true, "imageUrl": string }` | Bildnachricht nach REST-Upload | | `requestConversation` | `{ "withUserName": string }` | Konversation laden und als gelesen markieren | | `userSearch` | `{ "nameIncludes"?, "minAge"?, "maxAge"?, "countries"?, "genders"? }` | Benutzer suchen | | `requestHistory` | kein Payload | Chatverlauf-Liste laden | | `requestOpenConversations` | kein Payload | Inbox/ungelesene Chats laden | | `blockUser` | `{ "userName": string }` | Benutzer blockieren | | `unblockUser` | `{ "userName": string }` | Benutzer entblockieren | ### Server sendet | Event | Payload | Android-State | |---|---|---| | `connected` | `{ "sessionId": string, "loggedIn"?: boolean, "user"?: User }` | Session setzen, ggf. Login wiederherstellen | | `loginSuccess` | `{ "sessionId": string, "user": User }` | Login-State setzen | | `userList` | `{ "users": User[] }` | Online-Liste aktualisieren | | `message` | `{ "from": string, "message": string, "messageId": string, "timestamp": string, "isImage"?, "imageUrl"?, "imageCode"? }` | Nachricht empfangen | | `messageSent` | `{ "messageId": string, "to": string }` | lokale Nachricht bestaetigen | | `conversation` | `{ "with": string, "messages": Message[] }` | Konversation anzeigen | | `searchResults` | `{ "results": User[] }` | Suchergebnisse anzeigen | | `historyResults` | `{ "results": HistoryItem[] }` | Verlauf anzeigen | | `inboxResults` | `{ "results": InboxItem[] }` | Inbox anzeigen | | `unreadChats` | `{ "count": number }` | Badge aktualisieren | | `commandResult` | `{ "lines": string[], "kind": string }` | Admin-/Slash-Command-Hinweise anzeigen | | `commandTable` | `{ "title": string, "columns": string[], "rows": unknown[][] }` | Admin-/Statistik-Tabelle anzeigen | | `userBlocked` | `{ "userName": string }` | Blockierstatus setzen | | `userUnblocked` | `{ "userName": string }` | Blockierstatus entfernen | | `error` | `{ "message": string }` | Snackbar/Dialog anzeigen | ## Android-Modulstruktur Vorschlag fuer ein neues Modul oder separates Repo: ```text android/ app/ src/main/ java/net/ypchat/app/ MainActivity.kt YpChatApp.kt core/ Config.kt SessionCookieJar.kt NetworkModule.kt data/ api/ RestApi.kt SocketClient.kt model/ UserDto.kt MessageDto.kt ConversationDto.kt repository/ ChatRepository.kt FeedbackRepository.kt ui/ login/ chat/ users/ search/ inbox/ history/ feedback/ common/ ``` ### Verantwortlichkeiten - `RestApi`: Retrofit-Definitionen fuer `/api/*` - `SocketClient`: kapselt Socket.IO-Verbindung, Event-Handler und Emits - `ChatRepository`: verbindet REST, Socket und lokalen App-State - `ChatViewModel`: bietet `StateFlow` fuer Compose - `SessionCookieJar`: persistiert `connect.sid`, damit REST und Socket dieselbe Session nutzen - `ImageUploader`: Photo Picker, Komprimierung falls noetig, Multipart Upload ## UI-Konzept ### Navigation MVP-Screens: - Login - Online-Benutzer - Suche - Chat - Inbox - Verlauf - Profil/Logout Optional: - Feedback - Partner - Regeln/Sicherheit/FAQ als native Info-Screens oder WebContent aus statischen Texten ### Mobile UX Der Webclient ist desktop-/browsernah. Die App sollte mobiler denken: - Startet in Login oder zuletzt aktiver Chat-Ansicht - Bottom Navigation fuer `Online`, `Suche`, `Inbox`, `Verlauf` - Chat-Screen mit fester Eingabezeile unten - Online-Status und Flagge direkt in User-Zeilen - Unread-Badge auf Inbox-Tab - Bildauswahl ueber Android Photo Picker - Fehlermeldungen als Snackbar, kritische Session-Fehler als Dialog ## Session- und Reconnect-Konzept ### Persistenz Gespeichert werden lokal: - `connect.sid` Cookie - letzter bekannter Login-State fuer UI-Skeleton - Logout-Marker, analog `singlechat_logged_out` im Web - keine Chatnachrichten dauerhaft im MVP, weil Backend diese aktuell nur im Arbeitsspeicher haelt ### Reconnect Empfohlener Ablauf: 1. App startet. 2. Wenn kein Logout-Marker vorhanden: `GET /api/session`. 3. Wenn Session `loggedIn`: Socket verbinden und `setSessionId` senden. 4. Wenn Socket reconnectet: erneut `GET /api/session`, dann `setSessionId`. 5. Bei `connect_error`: exponentielles Retry mit sichtbarem Offline-Banner. 6. Bei Logout: `POST /api/logout`, Socket disconnect, Cookie loeschen, Logout-Marker setzen. ### Inaktivitaet Backend und Webclient verwenden 30 Minuten. Android sollte dieselbe Regel im UI spiegeln: - Timer startet nach `loginSuccess` oder Session-Restore. - Timer wird bei Senden, Empfangen, Suche und Conversation Requests zurueckgesetzt. - Bei Ablauf: lokaler Logout und optional `POST /api/logout`. ## Bildversand Ablauf: 1. Benutzer waehlt Bild per Photo Picker. 2. App prueft MIME-Type und Groesse. 3. Optional: Bild auf sinnvolle Chat-Groesse komprimieren, z.B. max. 1600 px Kantenlaenge. 4. `POST /api/upload-image` als Multipart `image`. 5. Backend antwortet mit: ```json { "success": true, "code": "", "url": "/api/image/" } ``` 6. App sendet Socket-Event `message` mit `isImage: true`, `message: code`, `imageUrl: url`. 7. Anzeige erfolgt ueber volle URL `https://www.ypchat.net/api/image/`. Hinweis: Der Backend-Endpunkt erlaubt 5 MB Upload und haelt Bilder temporaer fuer 6 Stunden. ## Backend-Anpassungen Vor Android-Release Die App kann grundsaetzlich mit dem aktuellen Backend starten. Sinnvolle kleine Anpassungen wuerden die Mobile-Integration aber robuster machen: - CORS ist fuer mobile Apps weniger kritisch, aber Socket.IO-Origin-Handling sollte getestet werden, weil native Clients oft keinen Browser-Origin senden. - Session-Handling sollte fuer Android explizit dokumentiert werden: `GET /api/session` vor Socket-Verbindung. - WebSocket ueber Apache sollte sauber funktionieren. Android kann Polling fallbacken, aber echte WebSocket-Verbindung ist fuer Akku und Latenz besser. - `/api/upload-image` hat aktuell einen Fallback auf den zuletzt aktiven Client. Fuer Mobile waere sauberer: eindeutig ueber `req.sessionID` validieren und keine Aktivitaets-Heuristik verwenden. - Nachrichten und Konversationen liegen aktuell im Arbeitsspeicher. Fuer eine App mit Reconnect/Background-Nutzung sollte mittelfristig Persistenz ergaenzt werden. - Optional: API-Versionierung einfuehren, z.B. `/api/v1/session`, bevor App-Versionen langfristig im Umlauf sind. ## Datenschutz Und Store-Themen Vor Veroeffentlichung im Play Store beachten: - Datenschutzerklaerung direkt in App verlinken - klare Alters-/Community-Regeln im Onboarding - Hinweis, dass Chatnachrichten temporaer serverseitig verarbeitet werden - Bild-Upload transparent erklaeren - Melde-/Blockierfunktion prominent erreichbar machen - Android `INTERNET` Permission erforderlich - Keine Speicherpermission noetig, wenn Android Photo Picker genutzt wird Wenn die App spaeter Push Notifications bekommt, braucht es FCM, Datenschutz-Ergaenzung und serverseitige Device-Token-Verwaltung. ## MVP-Backlog ### Phase 1: Android-Projekt - Gradle/Kotlin/Compose-Projekt anlegen - App-Theme und Basisnavigation erstellen - `Config` fuer Dev/Prod-Basis-URL - OkHttp, Retrofit und Socket.IO-Client einrichten - Persistente CookieJar implementieren ### Phase 2: Session Und Login - `GET /api/session` - Socket-Verbindung mit `setSessionId` - Login-Screen - `login` Event - `connected`, `loginSuccess`, `error` - Logout mit `/api/logout` ### Phase 3: Chat-Kern - Online-Userliste via `userList` - Chat-Screen - `requestConversation` - Textnachrichten senden/empfangen - `messageSent`, `unreadChats` - Reconnect und Offline-Banner ### Phase 4: Suche, Inbox, Verlauf - Suchformular und `userSearch` - Suchergebnisse - Inbox mit `requestOpenConversations` - Verlauf mit `requestHistory` - Blockieren/Entblockieren ### Phase 5: Bilder - Android Photo Picker - Multipart Upload zu `/api/upload-image` - Bildnachricht via Socket senden - Bildanzeige ueber `/api/image/:code` - Fehlerbehandlung fuer abgelaufene Bilder ### Phase 6: Release-Haertung - ProGuard/R8-Regeln fuer Socket.IO/OkHttp testen - Crash-/Error-Logging entscheiden - Play Store Icons, Screenshots, App Name - Datenschutz-/Impressum-/Regeln-Screens - Test auf Emulator, echtem Android-Geraet, schlechtem Netz und App-Background ## Risiken | Risiko | Auswirkung | Gegenmassnahme | |---|---|---| | Socket.IO Android Client Version passt nicht zum Server | Verbindungsfehler | Version gegen Socket.IO Server 4.x testen, `allowEIO3` ist serverseitig aktiv | | Session-Cookie wird nicht zwischen REST und Socket geteilt | Login/Upload funktionieren inkonsistent | gemeinsame OkHttp CookieJar und expliziter `setSessionId` Flow | | Apache WebSocket-Proxy ist instabil | Reconnects/Latenz | WebSocket-Regeln pruefen, Polling als Fallback | | Backend speichert Chat nur im RAM | Nachrichten nach Server-Restart weg | fuer MVP akzeptieren, spaeter DB-Persistenz | | App im Hintergrund verliert Socket | Nachrichten kommen nur bei geoeffneter App | fuer MVP akzeptieren, spaeter Push Notifications | | Bild-Upload-Session-Fallback ist unscharf | falsche Zuordnung theoretisch moeglich | Backend vor Release eindeutiger machen | ## Offene Entscheidungen - Soll Android zuerst als separates Repo oder als `android/` Ordner in diesem Repo entstehen? - Soll der MVP nur Chat enthalten oder auch Feedback/Partner/FAQ? - Soll das bestehende Design exakt nachgebaut oder fuer Mobile bewusst neu interpretiert werden? - Soll die erste Version ohne Push Notifications starten? ## Empfehlung Fuer Den Naechsten Schritt Ich wuerde als naechstes ein Android-Projekt im Ordner `android/` scaffolden und zuerst nur den technischen Durchstich bauen: 1. `GET /api/session` 2. Socket.IO connect 3. `setSessionId` 4. Login 5. Empfang von `userList` 6. Senden und Empfangen einer Textnachricht Wenn dieser Durchstich steht, ist der groesste technische Unsicherheitsblock geloest. Danach ist der Rest vor allem UI- und Zustandsarbeit.