From 084923366fa0e713fb43e2a99f4810747fd4c7fd Mon Sep 17 00:00:00 2001 From: Marc Froehlich Date: Thu, 6 Aug 2026 00:18:36 +0200 Subject: [PATCH] fix(chat): grouped calculation of priority score for equal raw callsigns with different suffixes (also fixes #73) --- .../kst4contest/controller/ScoreService.java | 19 +++++++--- .../view/Kst4ContestApplication.java | 38 ++++++++----------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/main/java/kst4contest/controller/ScoreService.java b/src/main/java/kst4contest/controller/ScoreService.java index f0a191b..6571cfa 100644 --- a/src/main/java/kst4contest/controller/ScoreService.java +++ b/src/main/java/kst4contest/controller/ScoreService.java @@ -227,12 +227,11 @@ public final class ScoreService { ChatMember chosen = null; if (preferredCat != null) { - for (ChatMember v : variants) { - if (v != null && v.getChatCategory() == preferredCat) { - chosen = v; - break; - } - } + chosen = variants.stream() + .filter(Objects::nonNull) + .filter(v -> isSameChatCategory(v.getChatCategory(), preferredCat)) + .max(Comparator.comparingLong(ChatMember::getActivityTimeLastInEpoch)) + .orElse(null); } if (chosen == null) { @@ -247,6 +246,14 @@ public final class ScoreService { return representative; } + + private static boolean isSameChatCategory(ChatCategory left, ChatCategory right) { + return left != null + && right != null + && left.getCategoryNumber() == right.getCategoryNumber(); + } + + /** * Projects the immutable score snapshot back into ChatMember display fields so * the normal station table can sort/filter by score without knowing the score diff --git a/src/main/java/kst4contest/view/Kst4ContestApplication.java b/src/main/java/kst4contest/view/Kst4ContestApplication.java index b64dcd2..2cc73e9 100644 --- a/src/main/java/kst4contest/view/Kst4ContestApplication.java +++ b/src/main/java/kst4contest/view/Kst4ContestApplication.java @@ -4291,32 +4291,24 @@ public class Kst4ContestApplication extends Application implements StatusUpdateL } - private ChatMember resolveChatMemberForTopCandidate(kst4contest.controller.ScoreService.TopCandidate c) { + private ChatMember resolveChatMemberForTopCandidate( + kst4contest.controller.ScoreService.TopCandidate c + ) { - String callRaw = c.getCallSignRaw(); - ChatCategory preferredCategory = c.getPreferredChatCategory(); - - // 1) Prefer exact (callRaw + category) match - synchronized (chatcontroller.getLst_chatMemberList()) { - for (ChatMember m : chatcontroller.getLst_chatMemberList()) { - if (m == null) continue; - if (m.getCallSignRaw() == null) continue; - if (!m.getCallSignRaw().equalsIgnoreCase(callRaw)) continue; - - if (preferredCategory != null && preferredCategory.equals(m.getChatCategory())) { - return m; - } - } - - // 2) Fallback: any variant with the same callsignRaw - for (ChatMember m : chatcontroller.getLst_chatMemberList()) { - if (m == null) continue; - if (m.getCallSignRaw() == null) continue; - if (m.getCallSignRaw().equalsIgnoreCase(callRaw)) return m; - } + if (c == null + || c.getDisplayCallSign() == null + || c.getPreferredChatCategory() == null) { + return null; } - return null; + /* + * Resolve the concrete active login by full callsign and category. Falling + * back to callSignRaw could select a different suffix in the same category. + */ + return chatcontroller.findActiveChatMember( + c.getDisplayCallSign(), + c.getPreferredChatCategory() + ); }