mirror of
https://github.com/praktimarc/kst4contest.git
synced 2026-10-19 05:42:58 +02:00
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:
1 parent
92c2576a81
commit
bb0e9eb327
5 files changed
+147
-38
No files matched your search
@@ -334,6 +334,8 @@ title.connecting=Verbindung zu ON4KST wird aufgebaut…
|
|||||||
title.authenticating=Verbunden – Anmeldung bei ON4KST läuft…
|
title.authenticating=Verbunden – Anmeldung bei ON4KST läuft…
|
||||||
title.synchronizing=Verbunden – ON4KST-Chatdaten werden abgeglichen…
|
title.synchronizing=Verbunden – ON4KST-Chatdaten werden abgeglichen…
|
||||||
title.disconnected=NICHT VERBUNDEN!
|
title.disconnected=NICHT VERBUNDEN!
|
||||||
|
title.online=Verbunden mit: {0} als {1} ({2}) in {3} ({4} Benutzer online, {5} angezeigt), {6} Nachrichten insgesamt.
|
||||||
|
title.bothCategories={0} und {1}
|
||||||
status.noConnection=Keine ON4KST-Verbindung
|
status.noConnection=Keine ON4KST-Verbindung
|
||||||
status.linkTooltip=ON4KST-Verbindung: {0}\n{1}
|
status.linkTooltip=ON4KST-Verbindung: {0}\n{1}
|
||||||
status.linkConnected=🟢 LINK: Verbunden
|
status.linkConnected=🟢 LINK: Verbunden
|
||||||
|
|||||||
@@ -343,6 +343,8 @@ title.connecting=Connecting to ON4KST…
|
|||||||
title.authenticating=Connected – authenticating with ON4KST…
|
title.authenticating=Connected – authenticating with ON4KST…
|
||||||
title.synchronizing=Connected – synchronizing ON4KST chat data…
|
title.synchronizing=Connected – synchronizing ON4KST chat data…
|
||||||
title.disconnected=DISCONNECTED!
|
title.disconnected=DISCONNECTED!
|
||||||
|
title.online=Connected to: {0} as {1} ({2}) in {3} ({4} users online, {5} shown), {6} messages total.
|
||||||
|
title.bothCategories={0} and {1}
|
||||||
status.noConnection=No ON4KST connection
|
status.noConnection=No ON4KST connection
|
||||||
status.linkTooltip=ON4KST link: {0}\n{1}
|
status.linkTooltip=ON4KST link: {0}\n{1}
|
||||||
status.linkConnected=🟢 LINK: Connected
|
status.linkConnected=🟢 LINK: Connected
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package kst4contest.view.compose
|
package kst4contest.view.compose
|
||||||
|
|
||||||
import kst4contest.controller.On4KstConnectionState
|
import kst4contest.controller.On4KstConnectionState
|
||||||
|
import kst4contest.model.ChatCategory
|
||||||
|
|
||||||
/** How big the main window opens. */
|
/** How big the main window opens. */
|
||||||
data class MainWindowSize(val heightDp: Double, val widthDp: Double)
|
data class MainWindowSize(val heightDp: Double, val widthDp: Double)
|
||||||
@@ -56,30 +57,75 @@ object MainWindowFrame {
|
|||||||
fun title(chatState: String, profileName: String?): String =
|
fun title(chatState: String, profileName: String?): String =
|
||||||
if (profileName.isNullOrBlank()) chatState else "$chatState - $profileName"
|
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.
|
* What the title says while the chat is not usable.
|
||||||
*
|
*
|
||||||
* The wording separates a scheduled reconnect from a dead session, which is the
|
* The wording separates a scheduled reconnect from a dead session, which is the
|
||||||
* difference between waiting and doing something about it.
|
* 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 =
|
fun offlineChatState(state: On4KstConnectionState?): String =
|
||||||
when (state ?: On4KstConnectionState.DISCONNECTED) {
|
when (state ?: On4KstConnectionState.DISCONNECTED) {
|
||||||
On4KstConnectionState.RECONNECT_WAIT -> kst4contest.view.i18n.CurrentStrings.get().titleConnectionLost
|
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
|
* 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
|
* 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
|
* (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(
|
MainWindowFrame.title(
|
||||||
chatState = MainWindowFrame.chatState(
|
chatState = if (state == null) {
|
||||||
surroundings?.connectionState,
|
MainWindowFrame.offlineChatState(null)
|
||||||
surroundings?.connectionDetail,
|
} 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,
|
profileName = profileName,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package kst4contest.view.compose
|
package kst4contest.view.compose
|
||||||
|
|
||||||
import kst4contest.controller.On4KstConnectionState
|
import kst4contest.controller.On4KstConnectionState
|
||||||
|
import kst4contest.model.ChatCategory
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.junit.jupiter.api.Assertions.assertTrue
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
@@ -123,36 +124,79 @@ class MainWindowFrameTest {
|
|||||||
* The title carries the whole connection line while the chat is usable.
|
* The title carries the whole connection line while the chat is usable.
|
||||||
*
|
*
|
||||||
* The JavaFX window did that -- "Connected to: 2: 144/432 MHz as DN5PW (Testing) in
|
* The JavaFX window did that -- "Connected to: 2: 144/432 MHz as DN5PW (Testing) in
|
||||||
* JO50JP (73 users online, 73 shown), 29 messages total." -- and the Compose window
|
* JO50JP (73 users online, 73 shown), 29 messages total." The first Compose title took
|
||||||
* opened with a fixed "KST4Contest (Compose)" instead, which told a tiling window
|
* the connection manager's diagnostic instead and read "ON4KST session is authenticated
|
||||||
* manager nothing and told the operator less.
|
* and synchronized" for the whole contest.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun `the title carries the live connection line once the chat is usable`() {
|
fun `the title carries the live connection line once the chat is usable`() {
|
||||||
val detail = "Connected to: 2: 144/432 MHz as DN5PW (Testing) in JO50JP " +
|
val line = MainWindowFrame.onlineChatState(
|
||||||
"(73 users online, 73 shown), 29 messages total."
|
mainCategory = ChatCategory(ChatCategory.VUHF),
|
||||||
|
secondCategory = null,
|
||||||
|
callSign = "DN5PW",
|
||||||
|
name = "Testing",
|
||||||
|
locator = "JO50JP",
|
||||||
|
usersOnline = 73,
|
||||||
|
shown = 70,
|
||||||
|
messagesTotal = 29,
|
||||||
|
)
|
||||||
|
|
||||||
assertEquals(detail, MainWindowFrame.chatState(On4KstConnectionState.ONLINE, detail))
|
assertEquals(
|
||||||
|
"Connected to: 2: 144/432 MHz as DN5PW (Testing) in JO50JP " +
|
||||||
|
"(73 users online, 70 shown), 29 messages total.",
|
||||||
|
line,
|
||||||
|
)
|
||||||
|
assertEquals(line, MainWindowFrame.chatState(On4KstConnectionState.ONLINE) { line })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `both categories are named when the second chat is logged in`() {
|
||||||
|
val line = MainWindowFrame.onlineChatState(
|
||||||
|
mainCategory = ChatCategory(ChatCategory.VUHF),
|
||||||
|
secondCategory = ChatCategory(ChatCategory.MICROWAVE),
|
||||||
|
callSign = "DN5PW",
|
||||||
|
name = "",
|
||||||
|
locator = "JO50JP",
|
||||||
|
usersOnline = 1,
|
||||||
|
shown = 1,
|
||||||
|
messagesTotal = 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
"Connected to: 2: 144/432 MHz and 3: Microwave as DN5PW () in JO50JP " +
|
||||||
|
"(1 users online, 1 shown), 1 messages total.",
|
||||||
|
line,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `without a main category the title does not name an empty one`() {
|
||||||
|
assertEquals(
|
||||||
|
MainWindowFrame.offlineChatState(On4KstConnectionState.ONLINE),
|
||||||
|
MainWindowFrame.onlineChatState(null, null, "DN5PW", "x", "JO50JP", 1, 1, 1),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `large counts are not grouped by locale`() {
|
||||||
|
val line = MainWindowFrame.onlineChatState(ChatCategory(ChatCategory.VUHF), null, "DN5PW", "x", "JO50JP", 1234, 1200, 23456)
|
||||||
|
|
||||||
|
assertTrue(line.contains("(1234 users online, 1200 shown), 23456 messages total."), line)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `an unusable chat says what it is doing instead`() {
|
fun `an unusable chat says what it is doing instead`() {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
MainWindowFrame.offlineChatState(On4KstConnectionState.CONNECTING),
|
MainWindowFrame.offlineChatState(On4KstConnectionState.CONNECTING),
|
||||||
MainWindowFrame.chatState(On4KstConnectionState.CONNECTING, "ignored while connecting"),
|
MainWindowFrame.chatState(On4KstConnectionState.CONNECTING) { error("not online") },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `a blank detail falls through rather than leaving the title empty`() {
|
fun `no state at all reads as disconnected without building the online line`() {
|
||||||
// The feed substitutes the state name for a blank detail, but it may not have run yet.
|
|
||||||
assertEquals(
|
|
||||||
MainWindowFrame.offlineChatState(On4KstConnectionState.ONLINE),
|
|
||||||
MainWindowFrame.chatState(On4KstConnectionState.ONLINE, " "),
|
|
||||||
)
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
MainWindowFrame.offlineChatState(null),
|
MainWindowFrame.offlineChatState(null),
|
||||||
MainWindowFrame.chatState(null, null),
|
MainWindowFrame.chatState(null) { error("not online") },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user