Skip to content

Commit 42eeef9

Browse files
committed
Clean up project structure
1 parent b30a565 commit 42eeef9

File tree

8 files changed

+197
-118
lines changed

8 files changed

+197
-118
lines changed

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

Lines changed: 8 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ import androidx.compose.runtime.mutableStateOf
2121
import androidx.compose.runtime.remember
2222
import androidx.compose.runtime.setValue
2323
import androidx.compose.ui.Modifier
24+
import androidx.compose.ui.graphics.Color
25+
import androidx.compose.ui.unit.dp
26+
import androidx.compose.ui.unit.sp
27+
import com.squareup.workflow1.traceviewer.model.WorkflowNode
28+
import com.squareup.workflow1.traceviewer.ui.InfoPanel
29+
import com.squareup.workflow1.traceviewer.utils.LoadWorkflowContent
30+
import com.squareup.workflow1.traceviewer.utils.SandboxBackground
31+
import com.squareup.workflow1.traceviewer.utils.UploadFile
2432
import io.github.vinceglb.filekit.PlatformFile
2533
import io.github.vinceglb.filekit.readString
2634

@@ -55,66 +63,3 @@ public fun App(
5563
UploadFile(resetSelectedNode, { selectedFile.value = it })
5664
}
5765
}
58-
59-
60-
61-
@Composable
62-
private fun InfoPanel(
63-
selectedNode: WorkflowNode?
64-
) {
65-
Row {
66-
val panelOpen = remember { mutableStateOf(false) }
67-
68-
// based on open/close, display the node details (Column)
69-
if (panelOpen.value) {
70-
PanelDetails(
71-
selectedNode,
72-
Modifier.fillMaxWidth(.35f)
73-
)
74-
}
75-
76-
IconButton(
77-
onClick = { panelOpen.value = !panelOpen.value },
78-
modifier = Modifier
79-
.padding(8.dp)
80-
.size(30.dp)
81-
.align(Alignment.Top)
82-
) {
83-
Icon(
84-
imageVector = if (panelOpen.value) Filled.KeyboardArrowLeft else Filled.KeyboardArrowRight,
85-
contentDescription = if (panelOpen.value) "Close Panel" else "Open Panel",
86-
modifier = Modifier
87-
)
88-
}
89-
}
90-
}
91-
92-
@Composable
93-
private fun PanelDetails(
94-
node: WorkflowNode?,
95-
modifier: Modifier = Modifier
96-
) {
97-
Column(
98-
modifier
99-
.fillMaxHeight()
100-
.background(Color.LightGray)
101-
) {
102-
if (node == null) {
103-
Text("No node selected")
104-
return@Column
105-
}
106-
107-
Column(
108-
modifier = Modifier
109-
.padding(8.dp),
110-
horizontalAlignment = Alignment.CenterHorizontally
111-
) {
112-
Text("only visible with a node selected")
113-
Text(
114-
text = "This is a node panel for ${node.name}",
115-
fontSize = 20.sp,
116-
modifier = Modifier.padding(8.dp)
117-
)
118-
}
119-
}
120-
}
Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +0,0 @@
1-
package com.squareup.workflow1.traceviewer
2-
3-
import com.squareup.moshi.JsonDataException
4-
import com.squareup.moshi.Moshi
5-
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
6-
import java.io.IOException
7-
8-
/**
9-
* Parses a JSON string into [WorkflowNode] with Moshi adapters.
10-
*
11-
* All the caught exceptions should be handled by the caller, and appropriate UI feedback should be
12-
* provided to user.
13-
*/
14-
public fun parseTrace(
15-
json: String
16-
): WorkflowNode? {
17-
return try {
18-
val moshi = Moshi.Builder()
19-
.add(KotlinJsonAdapterFactory())
20-
.build()
21-
val workflowAdapter = moshi.adapter(WorkflowNode::class.java)
22-
val root = workflowAdapter.fromJson(json)
23-
root
24-
} catch (e: JsonDataException) {
25-
throw JsonDataException("Failed to parse JSON: ${e.message}", e)
26-
} catch (e: IOException) {
27-
throw IOException("Malformed JSON: ${e.message}", e)
28-
}
29-
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.squareup.workflow1.traceviewer.model
2+
3+
/**
4+
* Since the logic of Workflow is hierarchical (where each workflow may have parent workflows and/or
5+
* children workflows, a tree structure is most appropriate for representing the data rather than
6+
* using flat data structures like an array.
7+
*
8+
* TBD what more metadata should be involved with each node, e.g. (props, states, # of render passes)
9+
*/
10+
public data class WorkflowNode(
11+
val id: String,
12+
val name: String,
13+
val children: List<WorkflowNode>
14+
)
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package com.squareup.workflow1.traceviewer.ui
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.Row
6+
import androidx.compose.foundation.layout.fillMaxHeight
7+
import androidx.compose.foundation.layout.fillMaxWidth
8+
import androidx.compose.foundation.layout.padding
9+
import androidx.compose.foundation.layout.size
10+
import androidx.compose.material.Icon
11+
import androidx.compose.material.IconButton
12+
import androidx.compose.material.Text
13+
import androidx.compose.material.icons.Icons.AutoMirrored.Filled
14+
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
15+
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
16+
import androidx.compose.runtime.Composable
17+
import androidx.compose.runtime.mutableStateOf
18+
import androidx.compose.runtime.remember
19+
import androidx.compose.ui.Alignment
20+
import androidx.compose.ui.Modifier
21+
import androidx.compose.ui.graphics.Color
22+
import androidx.compose.ui.unit.dp
23+
import androidx.compose.ui.unit.sp
24+
import com.squareup.workflow1.traceviewer.model.WorkflowNode
25+
26+
/**
27+
* A panel that displays information about the selected workflow node.
28+
* It can be toggled open or closed, and resets when the user selects a new file
29+
*
30+
* @param selectedNode The currently selected workflow node, or null if no node is selected.
31+
*/
32+
@Composable
33+
public fun InfoPanel(
34+
selectedNode: WorkflowNode?,
35+
modifier: Modifier = Modifier
36+
) {
37+
Row {
38+
val panelOpen = remember { mutableStateOf(false) }
39+
40+
// based on open/close, display the node details (Column)
41+
if (panelOpen.value) {
42+
PanelDetails(
43+
selectedNode,
44+
Modifier.fillMaxWidth(.35f)
45+
)
46+
}
47+
48+
IconButton(
49+
onClick = { panelOpen.value = !panelOpen.value },
50+
modifier = Modifier
51+
.padding(8.dp)
52+
.size(30.dp)
53+
.align(Alignment.Top)
54+
) {
55+
Icon(
56+
imageVector = if (panelOpen.value) Filled.KeyboardArrowLeft else Filled.KeyboardArrowRight,
57+
contentDescription = if (panelOpen.value) "Close Panel" else "Open Panel",
58+
modifier = Modifier
59+
)
60+
}
61+
}
62+
}
63+
64+
/**
65+
* The text details of the selected node. This should be closely coupled with the [WorkflowNode]
66+
* data class to see what information should be displayed.
67+
*/
68+
@Composable
69+
private fun PanelDetails(
70+
node: WorkflowNode?,
71+
modifier: Modifier = Modifier
72+
) {
73+
Column(
74+
modifier
75+
.fillMaxHeight()
76+
.background(Color.LightGray)
77+
) {
78+
if (node == null) {
79+
Text("No node selected")
80+
return@Column
81+
}
82+
83+
Column(
84+
modifier = Modifier
85+
.padding(8.dp),
86+
horizontalAlignment = Alignment.CenterHorizontally
87+
) {
88+
Text("only visible with a node selected")
89+
Text(
90+
text = "This is a node panel for ${node.name}",
91+
fontSize = 20.sp,
92+
modifier = Modifier.padding(8.dp)
93+
)
94+
}
95+
}
96+
}

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

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,16 @@ import androidx.compose.ui.Alignment
1818
import androidx.compose.ui.Modifier
1919
import androidx.compose.ui.graphics.Color
2020
import androidx.compose.ui.unit.dp
21+
import com.squareup.workflow1.traceviewer.model.WorkflowNode
2122

2223
/**
23-
* Since the logic of Workflow is hierarchical (where each workflow may have parent workflows and/or
24-
* children workflows, a tree structure is most appropriate for representing the data rather than
25-
* using flat data structures like an array.
26-
*
27-
* TBD what more metadata should be involved with each node, e.g. (props, states, # of render passes)
28-
*/
29-
public data class WorkflowNode(
30-
val id: String,
31-
val name: String,
32-
val children: List<WorkflowNode>
33-
)
34-
35-
/**
36-
* Since the workflow nodes present a tree structure, we utilize a recursive function to draw the tree.
37-
* The Column holds a subtree of nodes, and the Row holds all the children of the current node.
24+
* Since the workflow nodes present a tree structure, we utilize a recursive function to draw the tree
25+
* The Column holds a subtree of nodes, and the Row holds all the children of the current node
3826
*/
3927
@Composable
4028
public fun DrawWorkflowTree(
4129
node: WorkflowNode,
30+
onNodeSelect: (WorkflowNode) -> Unit,
4231
modifier: Modifier = Modifier,
4332
) {
4433
Column(
@@ -48,40 +37,43 @@ public fun DrawWorkflowTree(
4837
.fillMaxSize(),
4938
horizontalAlignment = Alignment.CenterHorizontally,
5039
) {
51-
// draws itself
52-
DrawNode(node)
40+
// draws the node itself
41+
DrawNode(node, onNodeSelect)
5342

54-
// draws children recursively
43+
// draws the node's children recursively
5544
Row(
5645
horizontalArrangement = Arrangement.Center,
5746
verticalAlignment = Alignment.Top
5847
) {
5948
node.children.forEach { childNode ->
60-
DrawWorkflowTree(childNode)
49+
DrawWorkflowTree(childNode, onNodeSelect)
6150
}
6251
}
6352
}
6453
}
6554

6655
/**
67-
* A basic box that represents a workflow node.
56+
* A basic box that represents a workflow node
6857
*/
6958
@Composable
7059
private fun DrawNode(
7160
node: WorkflowNode,
61+
onNodeSelect: (WorkflowNode) -> Unit,
7262
) {
7363
var open by remember { mutableStateOf(false) }
7464
Box(
7565
modifier = Modifier
76-
.clickable { open = !open }
66+
.clickable {
67+
// open.value = !open.value
68+
69+
// selection will bubble back up to the main view to handle the selection
70+
onNodeSelect(node)
71+
}
7772
.padding(10.dp)
7873
) {
7974
Column(horizontalAlignment = Alignment.CenterHorizontally) {
8075
Text(text = node.name)
8176
Text(text = "ID: ${node.id}")
82-
if (open) {
83-
Text("node is opened")
84-
}
8577
}
8678
}
8779
}

workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/SandboxBackground.kt renamed to workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/utils/SandboxBackground.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.squareup.workflow1.traceviewer
1+
package com.squareup.workflow1.traceviewer.utils
22

33
import androidx.compose.foundation.gestures.awaitEachGesture
44
import androidx.compose.foundation.gestures.detectDragGestures

workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/UploadFile.kt renamed to workflow-trace-viewer/src/jvmMain/kotlin/com/squareup/workflow1/traceviewer/utils/UploadFile.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.squareup.workflow1.traceviewer
1+
package com.squareup.workflow1.traceviewer.utils
22

33
import androidx.compose.foundation.layout.Box
44
import androidx.compose.foundation.layout.fillMaxSize
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.squareup.workflow1.traceviewer.utils
2+
3+
import androidx.compose.material.Text
4+
import androidx.compose.runtime.Composable
5+
import androidx.compose.runtime.LaunchedEffect
6+
import androidx.compose.runtime.mutableStateOf
7+
import androidx.compose.runtime.remember
8+
import androidx.compose.ui.Modifier
9+
import com.squareup.moshi.JsonDataException
10+
import com.squareup.moshi.Moshi
11+
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
12+
import com.squareup.workflow1.traceviewer.model.WorkflowNode
13+
import com.squareup.workflow1.traceviewer.ui.DrawWorkflowTree
14+
import io.github.vinceglb.filekit.PlatformFile
15+
import io.github.vinceglb.filekit.readString
16+
import java.io.IOException
17+
18+
/**
19+
* Parses the data from the given file and initiates the workflow tree
20+
*/
21+
@Composable
22+
public fun LoadWorkflowContent(
23+
file: PlatformFile?,
24+
onNodeSelect: (WorkflowNode) -> Unit,
25+
modifier: Modifier = Modifier
26+
) {
27+
val jsonString = remember { mutableStateOf<String?>(null) }
28+
LaunchedEffect(file) {
29+
jsonString.value = file?.readString()
30+
}
31+
val root = jsonString.value?.let { fetchRoot(it) }
32+
33+
if (root != null) {
34+
DrawWorkflowTree(root, onNodeSelect)
35+
} else {
36+
Text("Empty data or failed to parse data") // TODO: proper handling of error
37+
}
38+
}
39+
40+
/**
41+
* Parses a JSON string into [WorkflowNode] with Moshi adapters
42+
*
43+
* All the caught exceptions should be handled by the caller, and appropriate UI feedback should be
44+
* provided to user
45+
*/
46+
public fun fetchRoot(
47+
json: String
48+
): WorkflowNode? {
49+
return try {
50+
val moshi = Moshi.Builder()
51+
.add(KotlinJsonAdapterFactory())
52+
.build()
53+
val workflowAdapter = moshi.adapter(WorkflowNode::class.java)
54+
val root = workflowAdapter.fromJson(json)
55+
root
56+
} catch (e: JsonDataException) {
57+
throw JsonDataException("Failed to parse JSON: ${e.message}", e)
58+
} catch (e: IOException) {
59+
throw IOException("Malformed JSON: ${e.message}", e)
60+
}
61+
}

0 commit comments

Comments
 (0)