Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dev.sharingan

import dev.sharingan.internal.formatClockTime
import dev.sharingan.ui.descriptorOf
import dev.sharingan.ui.protocolCountsLine

/**
* Turns captured events into shareable text.
Expand Down Expand Up @@ -78,12 +79,8 @@ public object SharinganExport {

// ── session assembly ─────────────────────────────────────────

private fun countsLine(events: List<SharinganEvent>): String {
val http = events.count { it is HttpEvent }
val mqtt = events.count { it is MqttEvent }
val ble = events.count { it is BleEvent }
return "${events.size} events · HTTP $http · MQTT $mqtt · BLE $ble"
}
private fun countsLine(events: List<SharinganEvent>): String =
"${events.size} events · ${protocolCountsLine(events)}"

private fun eventJson(event: SharinganEvent, indent: String): String {
val fields = mutableListOf<Pair<String, String>>()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package dev.sharingan.internal

import dev.sharingan.SharinganEvent
import dev.sharingan.ui.Protocol
import dev.sharingan.ui.descriptorOf
import dev.sharingan.ui.protocolOf
import dev.sharingan.ui.protocolCountsLine

/**
* What the capture notification should say — the platform-free half of the
Expand All @@ -29,9 +28,7 @@ internal fun notificationContentOf(
): NotificationContent? {
if (events.isEmpty()) return null
val stateLabel = if (recording) "Capturing" else "Paused"
val countsLine = Protocol.entries.joinToString(" · ") { protocol ->
"${protocol.name} ${events.count { protocolOf(it) == protocol }}"
}
val countsLine = protocolCountsLine(events)
val ticker = events.takeLast(3).reversed().joinToString("\n") { descriptorOf(it).tickerLine(it) }
return NotificationContent(
title = "Sharingan — $stateLabel · ${events.size} events",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.sharingan.ui

import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.ImageVector
import dev.sharingan.BleEvent
import dev.sharingan.BleOperation
import dev.sharingan.formatBytes
Expand All @@ -11,6 +12,8 @@ internal object BleDescriptor : ProtocolDescriptor<BleEvent>() {

override val protocol: Protocol = Protocol.BLE
override val eventNoun: String = "operations"
override val tabIcon: ImageVector = SharinganIcons.Bluetooth
override val searchPlaceholder: String = "Filter characteristic, device…"

override val chips: List<FilterChipSpec> = listOf(
FilterChipSpec("all", "All"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ internal data class FilterChipSpec(val key: String, val label: String)

internal fun protocolOf(event: SharinganEvent): Protocol = descriptorOf(event).protocol

/** `HTTP n · MQTT n · BLE n` — one segment per protocol, in tab order. The
* single home for the cross-protocol counts line shared by the export header
* and the capture notification. */
internal fun protocolCountsLine(events: List<SharinganEvent>): String =
Protocol.entries.joinToString(" · ") { protocol ->
"${protocol.name} ${events.count { protocolOf(it) == protocol }}"
}

/** The design's per-protocol chip rows. */
internal fun chipsFor(protocol: Protocol): List<FilterChipSpec> = descriptorOf(protocol).chips

Expand Down
12 changes: 2 additions & 10 deletions sharingan/src/commonMain/kotlin/dev/sharingan/ui/HomeScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,7 @@ private fun TabBar(
Row(Modifier.fillMaxWidth()) {
Protocol.entries.forEach { protocol ->
val on = protocol == selected
val icon = when (protocol) {
Protocol.HTTP -> SharinganIcons.Globe
Protocol.MQTT -> SharinganIcons.Waves
Protocol.BLE -> SharinganIcons.Bluetooth
}
val icon = descriptorOf(protocol).tabIcon
Column(
Modifier.weight(1f).clickable { onSelect(protocol) },
horizontalAlignment = Alignment.CenterHorizontally,
Expand Down Expand Up @@ -255,11 +251,7 @@ private fun SearchField(
modifier: Modifier = Modifier,
) {
val colors = LocalSharinganColors.current
val placeholder = when (protocol) {
Protocol.HTTP -> "Filter path, status, header…"
Protocol.MQTT -> "Filter topic, payload…"
Protocol.BLE -> "Filter characteristic, device…"
}
val placeholder = descriptorOf(protocol).searchPlaceholder
Row(
modifier
.fillMaxWidth()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
Expand All @@ -32,6 +33,8 @@ internal object HttpDescriptor : ProtocolDescriptor<HttpEvent>() {

override val protocol: Protocol = Protocol.HTTP
override val eventNoun: String = "requests"
override val tabIcon: ImageVector = SharinganIcons.Globe
override val searchPlaceholder: String = "Filter path, status, header…"

override val chips: List<FilterChipSpec> = listOf(
FilterChipSpec("all", "All"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.sharingan.ui

import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.ImageVector
import dev.sharingan.MqttDirection
import dev.sharingan.MqttEvent
import dev.sharingan.formatBytes
Expand All @@ -12,6 +13,8 @@ internal object MqttDescriptor : ProtocolDescriptor<MqttEvent>() {

override val protocol: Protocol = Protocol.MQTT
override val eventNoun: String = "messages"
override val tabIcon: ImageVector = SharinganIcons.Waves
override val searchPlaceholder: String = "Filter topic, payload…"

override val chips: List<FilterChipSpec> = listOf(
FilterChipSpec("all", "All"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.sharingan.ui

import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.ImageVector
import dev.sharingan.BleEvent
import dev.sharingan.HttpEvent
import dev.sharingan.MqttEvent
Expand Down Expand Up @@ -34,6 +35,12 @@ internal abstract class ProtocolDescriptor<E : SharinganEvent> {
/** Quick-filter chips below the search field, in design order. */
abstract val chips: List<FilterChipSpec>

/** Tab-bar icon for this protocol's tab. */
abstract val tabIcon: ImageVector

/** Placeholder hint shown in the search field on this protocol's tab. */
abstract val searchPlaceholder: String

/** Chip matching; `"all"` is handled by the caller before dispatch. */
protected abstract fun chipMatches(event: E, chipKey: String): Boolean

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import dev.sharingan.BleOperation
import dev.sharingan.HttpEvent
import dev.sharingan.MqttDirection
import dev.sharingan.MqttEvent
import dev.sharingan.SharinganEvent
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
Expand Down Expand Up @@ -87,6 +88,24 @@ internal class EventFilterTest {
assertTrue(matchesQuery(http(), " "))
}

@Test
fun `Given a mix of events When the counts line is built Then it shows per-protocol counts`() {
val events: List<SharinganEvent> =
listOf(http(), http(), mqtt(MqttDirection.PUBLISH), ble(BleOperation.NOTIFY))
assertEquals("HTTP 2 · MQTT 1 · BLE 1", protocolCountsLine(events))
}

@Test
fun `When the counts line is built Then it has one segment for every protocol`() {
val events: List<SharinganEvent> =
listOf(http(), mqtt(MqttDirection.PUBLISH), ble(BleOperation.NOTIFY))
val line = protocolCountsLine(events)
assertEquals(Protocol.entries.size, line.split(" · ").size)
for (p in Protocol.entries) {
assertTrue(line.contains(p.name))
}
}

@Test
fun `When chips for a protocol are requested Then they match the design`() {
assertEquals(listOf("all", "err", "2xx", "get", "post"), chipsFor(Protocol.HTTP).map { it.key })
Expand Down
Loading