feat: persist WebSocket session across reconnections
Android generates a stable client ID (UUID) stored in SharedPreferences and sends it as a query parameter on WS connect. The server uses this ID as the chat session key, so AI conversation context survives app restarts. On reconnection, the server closes the stale connection and replaces it with the new one. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
96f559abbb
commit
b466e3116f
3 changed files with 20 additions and 4 deletions
|
|
@ -23,6 +23,7 @@ import okhttp3.OkHttpClient
|
|||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.core.module.dsl.viewModel
|
||||
import org.koin.dsl.module
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
val appModule = module {
|
||||
|
|
@ -52,7 +53,13 @@ val appModule = module {
|
|||
}
|
||||
|
||||
// WebSocketClient
|
||||
single { WebSocketClient(get(), get()) }
|
||||
single {
|
||||
val prefs = androidContext().getSharedPreferences("picoclaw", android.content.Context.MODE_PRIVATE)
|
||||
val clientId = prefs.getString("client_id", null) ?: UUID.randomUUID().toString().also {
|
||||
prefs.edit().putString("client_id", it).apply()
|
||||
}
|
||||
WebSocketClient(get(), get(), clientId)
|
||||
}
|
||||
|
||||
// ImageFileStorage
|
||||
single { ImageFileStorage(androidContext()) }
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ import kotlinx.serialization.json.Json
|
|||
|
||||
class WebSocketClient(
|
||||
private val client: HttpClient,
|
||||
private val scope: CoroutineScope
|
||||
private val scope: CoroutineScope,
|
||||
private val clientId: String
|
||||
) {
|
||||
|
||||
private val _connectionState = MutableStateFlow(ConnectionState.DISCONNECTED)
|
||||
|
|
@ -47,7 +48,8 @@ class WebSocketClient(
|
|||
while (isActive) {
|
||||
try {
|
||||
_connectionState.value = ConnectionState.CONNECTING
|
||||
client.webSocket(wsUrl) {
|
||||
val url = "$wsUrl?client_id=$clientId"
|
||||
client.webSocket(url) {
|
||||
session = this
|
||||
_connectionState.value = ConnectionState.CONNECTED
|
||||
retryDelay = INITIAL_DELAY
|
||||
|
|
|
|||
|
|
@ -166,7 +166,10 @@ func (c *WebSocketChannel) handleWS(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
clientID := uuid.New().String()
|
||||
clientID := r.URL.Query().Get("client_id")
|
||||
if clientID == "" {
|
||||
clientID = uuid.New().String()
|
||||
}
|
||||
|
||||
logger.InfoCF("websocket", "New WebSocket connection", map[string]interface{}{
|
||||
"client_id": clientID,
|
||||
|
|
@ -176,6 +179,10 @@ func (c *WebSocketChannel) handleWS(w http.ResponseWriter, r *http.Request) {
|
|||
chatID := fmt.Sprintf("ws:%s", clientID)
|
||||
|
||||
c.mu.Lock()
|
||||
if oldConn, ok := c.chatConns[chatID]; ok {
|
||||
delete(c.clients, oldConn)
|
||||
oldConn.Close()
|
||||
}
|
||||
c.clients[conn] = clientID
|
||||
c.chatConns[chatID] = conn
|
||||
c.mu.Unlock()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue