fix: tab reloads actually reload — kanban switch spam, plugins/skills refresh, ws mint (#897)
- ws: ticket-mint response body was read on the calling (main) thread —
the socket read threw NetworkOnMainThreadException, the mint failed,
and the kanban events stream never connected (board stuck Offline).
execute() + body read now run entirely on Dispatchers.IO; same fix in
DashboardSessionTokenRefresher.fetch (loopback mint path).
- kanban: re-selecting the already-active board reloads data and
reconnects events WITHOUT the redundant POST /boards/{id}/switch that
refresh fired on every tap.
- plugins: top-bar Refresh + pull-to-refresh wired through HermesScaffold
(previously no reload control at all).
- skills: Refresh icon now does a plain GET reload; the heavy hub-update
POST moved to the cloud icon so refresh is no longer disguised as update.
- test: mock the 3-arg Log.w overload (catch now logs the full stack).
Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
1 parent
9c7bb8635f
commit
90c1e8dc5f
6 files changed
+131
-106
No files matched your search
+23
-16
@@ -1,6 +1,7 @@
|
||||
package com.m57.hermescontrol.data.remote
|
||||
|
||||
import com.m57.hermescontrol.data.local.AuthManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okio.Buffer
|
||||
@@ -31,22 +32,28 @@ internal object DashboardSessionTokenRefresher {
|
||||
.url(baseUrl)
|
||||
.get()
|
||||
.build()
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) return null
|
||||
// Cap the read — the SPA HTML can be large, and we only need the
|
||||
// injected token near the top of the document. Reading the whole
|
||||
// body into a String risks OOM on low-end devices.
|
||||
val html =
|
||||
response.body.source().use { source ->
|
||||
val buffer = Buffer()
|
||||
source.read(buffer, MAX_BODY_BYTES)
|
||||
buffer.readUtf8()
|
||||
}
|
||||
tokenPattern
|
||||
.find(html)
|
||||
?.groupValues
|
||||
?.getOrNull(1)
|
||||
?.takeIf { it.isNotBlank() }
|
||||
// execute() + body read on Dispatchers.IO: the body read touches
|
||||
// the socket on the CALLING thread otherwise — when that is main
|
||||
// (loopback ticket mint from the kanban events connect), it throws
|
||||
// NetworkOnMainThreadException (same bug class as requestWsTicket).
|
||||
kotlinx.coroutines.runBlocking(Dispatchers.IO) {
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) return@runBlocking null
|
||||
// Cap the read — the SPA HTML can be large, and we only need the
|
||||
// injected token near the top of the document. Reading the whole
|
||||
// body into a String risks OOM on low-end devices.
|
||||
val html =
|
||||
response.body.source().use { source ->
|
||||
val buffer = Buffer()
|
||||
source.read(buffer, MAX_BODY_BYTES)
|
||||
buffer.readUtf8()
|
||||
}
|
||||
tokenPattern
|
||||
.find(html)
|
||||
?.groupValues
|
||||
?.getOrNull(1)
|
||||
?.takeIf { it.isNotBlank() }
|
||||
}
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
|
||||
Reference in new issue
Block a user