Move SSD review selection view updates to typed Kotlin

ober

a94187206541aa3f25898ceab33ad6dd3edf08ed

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index 67f70dc..3fc4ec2 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -4640,6 +4640,7 @@
                 sessionMissing
                 viewportStatePresent viewportStateOrEmpty
                 hasReplacementLabel missingReplacementLabel hasResolvedLabel
+                reviewViewSelectGroupId
                 ssdBrowserResumeReady focusChangeShouldShowDropdown
                 dialogReadyToShowDropdown focusedDialogReadyToShowDropdown
                 permissionResultGranted downloadAccessBeforeRuntimePermissions
@@ -4734,6 +4735,9 @@
           (kotlin-member-call applyLearnedGuesses))
         (extern (reviewViewCaptureViewport (view : SsdReviewView)) : (Nullable ViewportState)
           (kotlin-member-call captureViewport))
+        (extern (reviewViewSelectedGroupIdSet (view : SsdReviewView)
+                                              (id : (Nullable String))) : Unit
+          (kotlin-member-set selectedGroupId))
         (def (normalizeFiringArc (value : String)) : String
           (string-uppercase (string-trim value)))
         (def (normalizeLabel (value : String)) : String
@@ -5210,6 +5214,9 @@
           (if (nullable-null? selectedGroupId)
             #f
             (equal? groupId (nullable-get selectedGroupId))))
+        (def (reviewViewSelectGroupId (view : SsdReviewView)
+                                      (id : (Nullable String))) : Unit
+          (reviewViewSelectedGroupIdSet view id))
         (def (shouldCaptureViewport (includeViewport : Bool)) : Bool
           includeViewport)
         (def (viewportForSave (view : SsdReviewView)
@@ -7028,7 +7035,7 @@
 	       "        ssdAreaMode = false"
 	       "        reviewView.snapSelectionToCells = true"
 	       "        reviewView.selectMode = true"
-	       "        reviewView.selectedGroupId = appendGroup.id"
+	       "        reviewViewSelectGroupId(reviewView, appendGroup.id)"
 	       "        setStatus(appendModeStatus(appendGroup))"
 	       "    }"
 	       ""
@@ -7038,7 +7045,7 @@
 	       "        ssdAreaMode = true"
 	       "        reviewView.snapSelectionToCells = false"
 	       "        reviewView.selectMode = true"
-	       "        reviewView.selectedGroupId = selectedGroupId"
+	       "        reviewViewSelectGroupId(reviewView, selectedGroupId)"
 	       "        reviewView.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK)"
 	       "        setStatus(\"SSD area mode: drag around the ship SSD; tags outside that area will be removed\")"
 	       "    }"
@@ -7085,7 +7092,7 @@
        "        val removed = ssdSessionPruneGroupsOutsideSsdArea(session)"
 	       "        selectedGroupId = ssdSessionSelectedGroupId(session, selectedGroupId)"
 	       "        reviewView.ssdArea = session.ssdArea"
-	       "        reviewView.selectedGroupId = selectedGroupId"
+	       "        reviewViewSelectGroupId(reviewView, selectedGroupId)"
 	       "        truthStore.appendEvent(\"ssd_area_set\", session, ssdAreaSetDetails(removed, beforeGroups, outsideCount))"
 	       "        reviewView.performHapticFeedback(HapticFeedbackConstants.CONFIRM)"
 	       "        reviewView.invalidate()"
@@ -7105,7 +7112,7 @@
        "        if (ssdGroupMissing(group)) return"
        "        val selectedGroup = ssdGroupOrEmpty(group)"
 	       "        selectedGroupId = selectedGroup.id"
-	       "        reviewView.selectedGroupId = selectedGroup.id"
+	       "        reviewViewSelectGroupId(reviewView, selectedGroup.id)"
 	       "        reviewView.centerOnGroup(selectedGroup)"
 	       "        reviewView.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK)"
        "        setStatus(selectedGroupStatus(selectedGroup))"
@@ -7116,7 +7123,7 @@
        "        if (ssdGroupMissing(group)) return null"
        "        val selectedGroup = ssdGroupOrEmpty(group)"
        "        selectedGroupId = selectedGroup.id"
-       "        reviewView.selectedGroupId = selectedGroup.id"
+       "        reviewViewSelectGroupId(reviewView, selectedGroup.id)"
        "        return selectedGroup"
        "    }"
        ""
@@ -7169,7 +7176,7 @@
        "                    selectedGroupId = null"
 	       "                    reviewView.bitmap = bitmap"
 	       "                    reviewView.session = null"
-	       "                    reviewView.selectedGroupId = null"
+	       "                    reviewViewSelectGroupId(reviewView, null)"
 	       "                    reviewView.ssdArea = null"
        "                    restoreViewportAfterLayout(restoredViewport)"
 	       "                    setStatus(renderedLoadingTruthStatus(renderPdfName, renderPageIndex, renderPageCount))"
@@ -7232,7 +7239,7 @@
        "                        currentSession = session"
        "                        selectedGroupId = null"
 	       "                        reviewView.session = session"
-	       "                        reviewView.selectedGroupId = null"
+	       "                        reviewViewSelectGroupId(reviewView, null)"
 	       "                        reviewView.ssdArea = session.ssdArea"
 	       "                        setStatus(sessionCountsStatus(session, \", replayed cached truth\"))"
 	       "                        logDetectResult(\"cached-truth\", session, \"local-truth\")"
@@ -7282,7 +7289,7 @@
        "                    currentSession = session"
        "                    selectedGroupId = null"
 	       "                    reviewView.session = session"
-	       "                    reviewView.selectedGroupId = null"
+	       "                    reviewViewSelectGroupId(reviewView, null)"
 	       "                    reviewView.ssdArea = session.ssdArea"
        "                    val truthNote = detectionTruthNoteFor(existingTruth)"
        "                    val ocrNote = detectionOcrNote(ocrGuesses, session.ocrWords.size)"
@@ -7327,7 +7334,7 @@
        "                currentSession = merged"
        "                selectedGroupId = null"
        "                reviewView.session = merged"
-       "                reviewView.selectedGroupId = null"
+       "                reviewViewSelectGroupId(reviewView, null)"
        "                reviewView.ssdArea = merged.ssdArea"
        "                reviewView.invalidate()"
        "                setStatus(sessionCountsStatus(merged, \", pulled remote truth\"))"
@@ -7494,7 +7501,7 @@
        "        val group = ssdSessionAddApprovedAreaGroup(session, ids, cleanLabel, resolvedBoxTypeIdOr(resolvedType, boxType), firingArc)"
        "        truthStore.appendEvent(\"group_approved\", session)"
        "        selectedGroupId = group.id"
-       "        reviewView.selectedGroupId = group.id"
+       "        reviewViewSelectGroupId(reviewView, group.id)"
        "        finishSelectInteraction()"
        "        reviewView.performHapticFeedback(HapticFeedbackConstants.CONFIRM)"
        "        reviewView.invalidate()"
@@ -7513,7 +7520,7 @@
        "        val added = ssdSessionAppendAreaToGroup(session, group, ids)"
        "        finishSelectInteraction()"
        "        selectedGroupId = group.id"
-       "        reviewView.selectedGroupId = group.id"
+       "        reviewViewSelectGroupId(reviewView, group.id)"
        "        truthStore.appendEvent(\"group_appended\", session)"
        "        reviewView.performHapticFeedback(HapticFeedbackConstants.CONFIRM)"
        "        reviewView.invalidate()"
@@ -7617,7 +7624,7 @@
        "        ssdSessionSuppressGroupAsDeleted(session, deletedGroup)"
        "        truthStore.appendEvent(\"group_deleted\", session)"
 	       "        selectedGroupId = ssdSessionSelectedGroupId(session, null)"
-       "        reviewView.selectedGroupId = selectedGroupId"
+       "        reviewViewSelectGroupId(reviewView, selectedGroupId)"
        "        reviewView.performHapticFeedback(HapticFeedbackConstants.REJECT)"
        "        reviewView.invalidate()"
        "        saveSessionAsync("