Restore the connection line in the main window title (#136)

* Restore the connection line in the main window title

While online, the title showed the connection manager's diagnostic
"ON4KST session is authenticated and synchronized" instead of the line
the JavaFX window carried: categories, call, name, locator, users online,
stations shown and messages total. The title now builds that line from
the login preferences and the live station and message tables, so it
follows every new station, filter change and message. The diagnostic
stays where it belongs, in the status bar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Title: typed categories, no empty category, review fixes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Rsclub2_2andClaude Opus 5.5 authored and GitHub committed 2026-10-16 17:50:12 +02:00
1 parent 92c2576a81
commit bb0e9eb327
5 files changed
+147 -38

No files matched your search

@@ -1,6 +1,7 @@
package kst4contest.view.compose
import kst4contest.controller.On4KstConnectionState
import kst4contest.model.ChatCategory
/** How big the main window opens. */
data class MainWindowSize(val heightDp: Double, val widthDp: Double)
@@ -56,30 +57,75 @@ object MainWindowFrame {
fun title(chatState: String, profileName: String?): String =
if (profileName.isNullOrBlank()) chatState else "$chatState - $profileName"
/**
* What the title's first half says.
*
* The live connection line while the chat is usable -- exactly what the JavaFX title
* carried -- and a wording about the attempt otherwise. The connection manager's own
* detail ("session is authenticated and synchronized") is a diagnostic for the status
* bar, not a title: it says nothing about category, call or traffic.
*
* @param state the connection state, null before the first push
* @param onlineLine the line built by [onlineChatState], asked for only while online
* @return the chat half of the title
*/
fun chatState(state: On4KstConnectionState?, onlineLine: () -> String): String =
if (state == On4KstConnectionState.ONLINE) onlineLine() else offlineChatState(state)
/**
* The connection line while the chat is usable, as the JavaFX title had it:
* "Connected to: 2: 144/432 MHz as DO1JKS (Johannes 70/2) in JN59MU (106 users online,
* 106 shown), 14 messages total."
*
* Counts are passed as text so MessageFormat does not group them by locale. Without a
* main category there is nothing to name, so the line falls back to the offline wording
* for ONLINE instead of reading "Connected to: as ...".
*
* @param mainCategory the main chat category, null when none is configured
* @param secondCategory the second category, null when it is not logged in
* @param callSign the login callsign
* @param name the login name of the main category
* @param locator the login locator
* @param usersOnline every station in the chat
* @param shown the stations the filters let through
* @param messagesTotal every chat message held, the global list behind the public tab
*/
fun onlineChatState(
mainCategory: ChatCategory?,
secondCategory: ChatCategory?,
callSign: String?,
name: String?,
locator: String?,
usersOnline: Int,
shown: Int,
messagesTotal: Int,
): String {
if (mainCategory == null) {
return offlineChatState(On4KstConnectionState.ONLINE)
}
val strings = kst4contest.view.i18n.CurrentStrings.get()
val categories = if (secondCategory == null) {
mainCategory.toString()
} else {
strings.titleBothCategories(mainCategory.toString(), secondCategory.toString())
}
return strings.titleOnline(
categories,
callSign.orEmpty(),
name.orEmpty(),
locator.orEmpty(),
usersOnline.toString(),
shown.toString(),
messagesTotal.toString(),
)
}
/**
* What the title says while the chat is not usable.
*
* The wording separates a scheduled reconnect from a dead session, which is the
* difference between waiting and doing something about it.
*/
/**
* What the title's first half says.
*
* The live connection detail while the chat is usable -- which is the whole status line,
* exactly what the JavaFX title carried -- and a wording about the attempt otherwise. A
* blank detail falls through to the offline wording rather than leaving the title empty.
*
* @param state the connection state, null before the first push
* @param detail the status line the controller pushes
* @return the chat half of the title
*/
fun chatState(state: On4KstConnectionState?, detail: String?): String =
if (state == On4KstConnectionState.ONLINE && !detail.isNullOrBlank()) {
detail
} else {
offlineChatState(state)
}
fun offlineChatState(state: On4KstConnectionState?): String =
when (state ?: On4KstConnectionState.DISCONNECTED) {
On4KstConnectionState.RECONNECT_WAIT -> kst4contest.view.i18n.CurrentStrings.get().titleConnectionLost
@@ -111,14 +111,29 @@ object MainWindowHost {
* Read inside the composition, so the title follows the connection the way
* the JavaFX one did: "Connected to: 2: 144/432 MHz as DN5PW in JO50JP
* (73 users online, 73 shown), 29 messages total." A captured string would
* say "disconnected" for the life of the session.
* say "disconnected" for the life of the session. The counts are Compose
* state, so the title moves with every station and message. visibleRows
* costs nothing extra: it is cached per change, and the station table
* reads the same list anyway.
*/
val surroundings = currentState?.surroundings
val state = currentState
MainWindowFrame.title(
chatState = MainWindowFrame.chatState(
surroundings?.connectionState,
surroundings?.connectionDetail,
),
chatState = if (state == null) {
MainWindowFrame.offlineChatState(null)
} else MainWindowFrame.chatState(state.surroundings.connectionState) {
val prefs = state.prefs
MainWindowFrame.onlineChatState(
mainCategory = prefs.getLoginChatCategoryMain(),
secondCategory = prefs.getLoginChatCategorySecond()
.takeIf { prefs.isLoginToSecondChatEnabled() },
callSign = prefs.getStn_loginCallSign(),
name = prefs.getStn_loginNameMainCat(),
locator = prefs.getStn_loginLocatorMainCat(),
usersOnline = state.stations.rows.size,
shown = state.stations.visibleRows.size,
messagesTotal = state.publicMessages.rows.size,
)
},
profileName = profileName,
)
},