android version
This commit is contained in:
396
ANDROID-APP-KONZEPT.md
Normal file
396
ANDROID-APP-KONZEPT.md
Normal file
@@ -0,0 +1,396 @@
|
||||
# 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": "<sessionId aus /api/session>"
|
||||
}
|
||||
```
|
||||
|
||||
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<ChatUiState>` 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": "<code>",
|
||||
"url": "/api/image/<code>"
|
||||
}
|
||||
```
|
||||
|
||||
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/<code>`.
|
||||
|
||||
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.
|
||||
Reference in New Issue
Block a user