Skip to content

Commit 02bfd21

Browse files
authored
Merge pull request #1363 from square/wenli/visualizer-render-data
Workflow visualizer prototype
2 parents 69f29c3 + 2f4f69a commit 02bfd21

File tree

6 files changed

+111
-20
lines changed

6 files changed

+111
-20
lines changed

workflow-trace-viewer/api/workflow-trace-viewer.api

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,19 @@ public final class com/squareup/workflow1/traceviewer/MainKt {
1616

1717
public final class com/squareup/workflow1/traceviewer/model/Node {
1818
public static final field $stable I
19-
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V
19+
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/util/List;)V
20+
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
21+
public fun equals (Ljava/lang/Object;)Z
2022
public final fun getChildren ()Ljava/util/List;
2123
public final fun getId ()Ljava/lang/String;
2224
public final fun getName ()Ljava/lang/String;
25+
public final fun getParent ()Ljava/lang/String;
26+
public final fun getParentId ()Ljava/lang/String;
27+
public final fun getProps ()Ljava/lang/Object;
28+
public final fun getRenderings ()Ljava/lang/Object;
29+
public final fun getState ()Ljava/lang/Object;
30+
public fun hashCode ()I
31+
public fun toString ()Ljava/lang/String;
2332
}
2433

2534
public final class com/squareup/workflow1/traceviewer/ui/FrameSelectTabKt {
@@ -42,6 +51,7 @@ public final class com/squareup/workflow1/traceviewer/util/ComposableSingletons$
4251
}
4352

4453
public final class com/squareup/workflow1/traceviewer/util/JsonParserKt {
54+
public static final field ROOT_ID Ljava/lang/String;
4555
public static final fun parseTrace (Lio/github/vinceglb/filekit/PlatformFile;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
4656
}
4757

workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/model/Node.kt

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,25 @@ package com.squareup.workflow1.traceviewer.model
88
* TBD what more metadata should be involved with each node, e.g. (props, states, # of render passes)
99
*/
1010
public class Node(
11-
val id: String,
1211
val name: String,
13-
val children: List<Node>
14-
)
12+
val id: String,
13+
val parent: String,
14+
val parentId: String,
15+
val props: Any? = null,
16+
val state: Any? = null,
17+
val renderings: Any? = null,
18+
val children: List<Node>,
19+
) {
20+
override fun toString(): String {
21+
return "Node(name='$name', parent='$parent', children=${children.size})"
22+
}
23+
24+
override fun equals(other: Any?): Boolean {
25+
if (this === other) return true
26+
if (other !is Node) return false
27+
return this.id == other.id
28+
}
29+
override fun hashCode(): Int {
30+
return id.hashCode()
31+
}
32+
}

workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/ui/FrameSelectTab.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public fun FrameSelectTab(
3838
) {
3939
items(frames.size) { index ->
4040
Text(
41-
text = "State ${index + 1}",
41+
text = "Frame ${index + 1}",
4242
color = if (index == currentIndex) Color.Black else Color.LightGray,
4343
modifier = Modifier
4444
.clip(RoundedCornerShape(16.dp))

workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/ui/WorkflowInfoPanel.kt

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import androidx.compose.runtime.setValue
2222
import androidx.compose.ui.Alignment
2323
import androidx.compose.ui.Modifier
2424
import androidx.compose.ui.graphics.Color
25+
import androidx.compose.ui.text.TextStyle
26+
import androidx.compose.ui.text.style.TextAlign
2527
import androidx.compose.ui.unit.dp
2628
import androidx.compose.ui.unit.sp
2729
import com.squareup.workflow1.traceviewer.model.Node
@@ -83,11 +85,22 @@ private fun NodePanelDetails(
8385
return@Column
8486
}
8587

86-
Text("only visible with a node selected")
87-
Text(
88-
text = "This is a node panel for ${node.name}",
89-
fontSize = 20.sp,
90-
modifier = Modifier.padding(8.dp)
88+
val textModifier = Modifier.padding(8.dp)
89+
val textStyle = TextStyle(fontSize = 16.sp, textAlign = TextAlign.Center)
90+
val fields = mapOf(
91+
"Name" to node.name,
92+
"ID" to node.id,
93+
"Props" to node.props.toString(),
94+
"State" to node.state.toString(),
95+
"Renderings" to node.renderings.toString()
9196
)
97+
98+
fields.forEach { (label, value) ->
99+
Text(
100+
text = "$label: $value",
101+
modifier = textModifier,
102+
style = textStyle
103+
)
104+
}
92105
}
93106
}

workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/ui/WorkflowTree.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,6 @@ public fun RenderDiagram(
6464
if (!isLoading) {
6565
DrawTree(frames[frameInd], onNodeSelect)
6666
}
67-
68-
// TODO: catch errors and display UI here
6967
}
7068

7169
/**

workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/util/JsonParser.kt

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,15 @@ import com.squareup.workflow1.traceviewer.model.Node
88
import io.github.vinceglb.filekit.PlatformFile
99
import io.github.vinceglb.filekit.readString
1010

11+
/*
12+
The root workflow Node uses an ID of 0, and since we are filtering childrenByParent by the
13+
parentId, the root node has a parent of -1 ID. This is reflected seen inside android-register
14+
*/
15+
const val ROOT_ID: String = "-1"
16+
1117
/**
12-
* Parses a given file's JSON String into [Node] with Moshi adapters.
18+
* Parses a given file's JSON String into a list of [Node]s with Moshi adapters. Each of these nodes
19+
* count as the root of a tree which forms a Frame.
1320
*
1421
* @return A [ParseResult] representing result of parsing, either an error related to the
1522
* format of the JSON, or a success and a parsed trace.
@@ -19,18 +26,63 @@ public suspend fun parseTrace(
1926
): ParseResult {
2027
return try {
2128
val jsonString = file.readString()
22-
val moshi = Moshi.Builder()
23-
.add(KotlinJsonAdapterFactory())
24-
.build()
25-
val workflowList = Types.newParameterizedType(List::class.java, Node::class.java)
26-
val workflowAdapter: JsonAdapter<List<Node>> = moshi.adapter(workflowList)
27-
val trace = workflowAdapter.fromJson(jsonString)
28-
ParseResult.Success(trace)
29+
val workflowAdapter = createMoshiAdapter()
30+
val parsedRenderPasses = workflowAdapter.fromJson(jsonString)
31+
32+
val parsedFrames = mutableListOf<Node>()
33+
parsedRenderPasses?.forEach { renderPass ->
34+
val parsed = getFrameFromRenderPass(renderPass)
35+
parsedFrames.add(parsed)
36+
}
37+
38+
ParseResult.Success(parsedFrames)
2939
} catch (e: Exception) {
3040
ParseResult.Failure(e)
3141
}
3242
}
3343

44+
/**
45+
* Creates a Moshi adapter for parsing the JSON trace file.
46+
*/
47+
private fun createMoshiAdapter(): JsonAdapter<List<List<Node>>> {
48+
val moshi = Moshi.Builder()
49+
.add(KotlinJsonAdapterFactory())
50+
.build()
51+
val workflowList = Types.newParameterizedType(
52+
List::class.java,
53+
Types.newParameterizedType(List::class.java, Node::class.java)
54+
)
55+
val adapter: JsonAdapter<List<List<Node>>> = moshi.adapter(workflowList)
56+
return adapter
57+
}
58+
59+
/**
60+
* We take an unparsed render pass and build up a tree structure from it to form a Frame.
61+
*
62+
* @return Node the root node of the tree for that specific frame.
63+
*/
64+
private fun getFrameFromRenderPass(renderPass: List<Node>): Node {
65+
val childrenByParent: Map<String, List<Node>> = renderPass.groupBy { it.parentId }
66+
val root = childrenByParent[ROOT_ID]?.single()
67+
return buildTree(root!!, childrenByParent)
68+
}
69+
70+
/**
71+
* Recursively builds a tree using each node's children.
72+
*/
73+
private fun buildTree(node: Node, childrenByParent: Map<String, List<Node>>): Node {
74+
val children = (childrenByParent[node.id] ?: emptyList())
75+
return Node(
76+
name = node.name,
77+
id = node.id,
78+
parent = node.parent,
79+
parentId = node.parentId,
80+
props = node.props,
81+
state = node.state,
82+
children = children.map { buildTree(it, childrenByParent) },
83+
)
84+
}
85+
3486
sealed interface ParseResult {
3587
class Success(val trace: List<Node>?) : ParseResult
3688
class Failure(val error: Throwable) : ParseResult

0 commit comments

Comments
 (0)