-
Notifications
You must be signed in to change notification settings - Fork 104
Improve workflow visualizer functionality #1343
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b30a565
42eeef9
7db8039
6dea47e
da2da0c
d85d0ff
bdbc65f
6831535
d2626f2
6bdc3d4
1a6d868
4255a8f
ebe259b
1beba35
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,21 @@ | ||
package com.squareup.workflow1.traceviewer | ||
|
||
import androidx.compose.foundation.layout.Box | ||
import androidx.compose.material.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.LaunchedEffect | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.mutableIntStateOf | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.runtime.setValue | ||
import androidx.compose.ui.Alignment | ||
import androidx.compose.ui.Modifier | ||
import com.squareup.workflow1.traceviewer.model.Node | ||
import com.squareup.workflow1.traceviewer.ui.RenderDiagram | ||
import com.squareup.workflow1.traceviewer.ui.RightInfoPanel | ||
import com.squareup.workflow1.traceviewer.ui.StateSelectTab | ||
import com.squareup.workflow1.traceviewer.util.SandboxBackground | ||
import com.squareup.workflow1.traceviewer.util.UploadFile | ||
import io.github.vinceglb.filekit.PlatformFile | ||
import io.github.vinceglb.filekit.readString | ||
|
||
/** | ||
* Main composable that provides the different layers of UI. | ||
|
@@ -19,28 +24,43 @@ import io.github.vinceglb.filekit.readString | |
public fun App( | ||
modifier: Modifier = Modifier | ||
) { | ||
Box { | ||
var selectedFile by remember { mutableStateOf<PlatformFile?>(null) } | ||
var selectedTraceFile by remember { mutableStateOf<PlatformFile?>(null) } | ||
var selectedNode by remember { mutableStateOf<Node?>(null) } | ||
var workflowFrames by remember { mutableStateOf<List<Node>>(emptyList()) } | ||
var frameIndex by remember { mutableIntStateOf(0) } | ||
|
||
if (selectedFile != null) { | ||
SandboxBackground { WorkflowContent(selectedFile!!) } | ||
Box( | ||
modifier = modifier | ||
) { | ||
// Main content | ||
if (selectedTraceFile != null) { | ||
SandboxBackground { | ||
RenderDiagram( | ||
traceFile = selectedTraceFile!!, | ||
frameInd = frameIndex, | ||
onFileParse = { workflowFrames = it }, | ||
onNodeSelect = { selectedNode = it } | ||
) | ||
} | ||
} | ||
|
||
UploadFile(onFileSelect = { selectedFile = it }) | ||
} | ||
} | ||
StateSelectTab( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Considering how many traces there are likely to be, you might consider using a dropdown instead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, maybe even a combination of the 2, where with a dropdown you can instantly jump to a frame, and a scrolling bar could let you traverse locally. I think, though, this could be something I implement down the line as iterative improvements. |
||
frames = workflowFrames, | ||
currentIndex = frameIndex, | ||
onIndexChange = { frameIndex = it }, | ||
modifier = Modifier.align(Alignment.TopCenter) | ||
) | ||
|
||
@Composable | ||
private fun WorkflowContent(file: PlatformFile) { | ||
var jsonString by remember { mutableStateOf<String?>(null) } | ||
LaunchedEffect(file) { | ||
jsonString = file.readString() | ||
} | ||
val root = jsonString?.let { parseTrace(it) } | ||
RightInfoPanel(selectedNode) | ||
|
||
if (root != null) { | ||
DrawWorkflowTree(root) | ||
} else { | ||
Text("Empty data or failed to parse data") // TODO: proper handling of error | ||
// The states are reset when a new file is selected. | ||
UploadFile( | ||
onFileSelect = { | ||
selectedTraceFile = it | ||
selectedNode = null | ||
frameIndex = 0 | ||
}, | ||
modifier = Modifier.align(Alignment.BottomStart) | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,14 @@ | ||
package com.squareup.workflow1.traceviewer | ||
|
||
import androidx.compose.foundation.layout.fillMaxSize | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.window.singleWindowApplication | ||
|
||
/** | ||
* Main entry point for the desktop application, see [README.md] for more details. | ||
*/ | ||
fun main() { | ||
singleWindowApplication(title = "Workflow Trace Viewer") { | ||
App() | ||
App(Modifier.fillMaxSize()) | ||
} | ||
} |
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.squareup.workflow1.traceviewer.model | ||
|
||
/** | ||
* Since the logic of Workflow is hierarchical (where each workflow may have parent workflows and/or | ||
* children workflows, a tree structure is most appropriate for representing the data rather than | ||
* using flat data structures like an array. | ||
* | ||
* TBD what more metadata should be involved with each node, e.g. (props, states, # of render passes) | ||
*/ | ||
public class Node( | ||
val id: String, | ||
val name: String, | ||
val children: List<Node> | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package com.squareup.workflow1.traceviewer.ui | ||
|
||
import androidx.compose.foundation.clickable | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.lazy.LazyRow | ||
import androidx.compose.foundation.lazy.rememberLazyListState | ||
import androidx.compose.foundation.shape.RoundedCornerShape | ||
import androidx.compose.material.Surface | ||
import androidx.compose.material.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.draw.clip | ||
import androidx.compose.ui.graphics.Color | ||
import androidx.compose.ui.unit.dp | ||
import com.squareup.workflow1.traceviewer.model.Node | ||
|
||
/** | ||
* A trace tab selector that allows devs to switch between different states within the provided trace. | ||
*/ | ||
@Composable | ||
public fun StateSelectTab( | ||
frames: List<Node>, | ||
currentIndex: Int, | ||
onIndexChange: (Int) -> Unit, | ||
modifier: Modifier = Modifier | ||
) { | ||
val state = rememberLazyListState() | ||
|
||
Surface( | ||
modifier = modifier | ||
.padding(4.dp), | ||
color = Color.White, | ||
) { | ||
LazyRow( | ||
modifier = Modifier | ||
.padding(8.dp), | ||
state = state | ||
) { | ||
items(frames.size) { index -> | ||
Text( | ||
text = "State ${index + 1}", | ||
color = if (index == currentIndex) Color.Black else Color.LightGray, | ||
modifier = Modifier | ||
.clip(RoundedCornerShape(16.dp)) | ||
.clickable { onIndexChange(index) } | ||
.padding(10.dp) | ||
) | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Set some standard for naming terminology in this project. Open to changing it though!