Skip to content

Commit

Permalink
Integrate TaskQueueView and Enhance SkillTree Functionality (Signific…
Browse files Browse the repository at this point in the history
…ant-Gravitas#5206)

* Add TestQueueView to Main Layout

This commit integrates the TestQueueView into the main layout. The layout now conditionally displays the TestQueueView based on whether a node in the SkillTree is selected.

- TestQueueView appears when a SkillTree node is selected.
- Main layout adjusts to accommodate TestQueueView alongside SkillTreeView and ChatView.
- Implemented responsive layout logic to manage the widths of the different views based on the screen width and the state of the SkillTree.

* Extend SkillTreeViewModel to Track Selected Node Hierarchy

This commit enhances the SkillTreeViewModel to maintain a list of nodes that form a hierarchy from the currently selected node to the root. This allows for more interactive and informative views that can leverage this hierarchical data.

- Added a new property `selectedNodeHierarchy` to keep track of the node hierarchy.
- Modified the `toggleNodeSelection` method to populate or clear `selectedNodeHierarchy` based on node selection.
- Introduced a new method `populateSelectedNodeHierarchy` to build the hierarchy from the selected node to the root.

* Extract skill tree view model reset state to method

* Implement UI enhancements for TaskQueueView

This commit introduces several UI improvements to the TaskQueueView:
- Tiles are padded 20 units from both the leading and trailing edges.
- Tiles now have a white background.
- Added a thin black border to the tiles.
- Incorporated a slight corner radius for the tiles.
- Centered the title and subtitle horizontally within the tiles.
- Added a checkmark button with a tooltip at the bottom-right corner for running a suite of tests.

These changes aim to improve the user experience and visual appeal of the TaskQueueView.

* Make MainLayout a consumer of SkillTreeViewModel
  • Loading branch information
hunteraraujo authored Sep 12, 2023
1 parent a6e195a commit 314cce7
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 26 deletions.
57 changes: 50 additions & 7 deletions frontend/lib/viewmodels/skill_tree_viewmodel.dart
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
import 'package:auto_gpt_flutter_client/models/skill_tree/skill_tree_edge.dart';
import 'package:auto_gpt_flutter_client/models/skill_tree/skill_tree_node.dart';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:graphview/GraphView.dart';

class SkillTreeViewModel extends ChangeNotifier {
List<SkillTreeNode> _skillTreeNodes = [];
List<SkillTreeEdge> _skillTreeEdges = [];
SkillTreeNode? _selectedNode;
List<SkillTreeNode>? _selectedNodeHierarchy;

SkillTreeNode? get selectedNode => _selectedNode;
List<SkillTreeNode>? get selectedNodeHierarchy => _selectedNodeHierarchy;

final Graph graph = Graph()..isTree = true;
BuchheimWalkerConfiguration builder = BuchheimWalkerConfiguration();

void initializeSkillTree() {
_skillTreeNodes = [];
_skillTreeEdges = [];
_selectedNode = null;

graph.nodes.clear();
graph.edges.clear();

// TODO: Load from JSON
resetState();
// Add nodes to _skillTreeNodes
_skillTreeNodes.addAll([
SkillTreeNode(color: 'red', id: 1),
Expand Down Expand Up @@ -61,17 +59,62 @@ class SkillTreeViewModel extends ChangeNotifier {
notifyListeners();
}

void resetState() {
_skillTreeNodes = [];
_skillTreeEdges = [];
_selectedNode = null;
_selectedNodeHierarchy = null;

graph.nodes.clear();
graph.edges.clear();
}

void toggleNodeSelection(int nodeId) {
if (_selectedNode?.id == nodeId) {
// Unselect the node if it's already selected
_selectedNode = null;
_selectedNodeHierarchy = null;
} else {
// Select the new node
_selectedNode = _skillTreeNodes.firstWhere((node) => node.id == nodeId);
populateSelectedNodeHierarchy(nodeId);
}
notifyListeners();
}

void populateSelectedNodeHierarchy(int startNodeId) {
// Initialize an empty list to hold the nodes in the hierarchy.
_selectedNodeHierarchy = [];

// Find the starting node (the selected node) in the skill tree nodes list.
SkillTreeNode? currentNode =
_skillTreeNodes.firstWhere((node) => node.id == startNodeId);

// Loop through the tree to populate the hierarchy list.
// The loop will continue as long as there's a valid current node.
while (currentNode != null) {
// Add the current node to the hierarchy list.
_selectedNodeHierarchy!.add(currentNode);

// Find the parent node by looking through the skill tree edges.
// We find the edge where the 'to' field matches the ID of the current node.
SkillTreeEdge? parentEdge = _skillTreeEdges
.firstWhereOrNull((edge) => edge.to == currentNode?.id.toString());

// If a parent edge is found, find the corresponding parent node.
if (parentEdge != null) {
// The 'from' field of the edge gives us the ID of the parent node.
// We find that node in the skill tree nodes list.
currentNode = _skillTreeNodes
.firstWhereOrNull((node) => node.id.toString() == parentEdge.from);
} else {
// If no parent edge is found, it means we've reached the root node.
// We set currentNode to null to exit the loop.
currentNode = null;
}
}
}

// Getter to expose nodes for the View
List<SkillTreeNode> get skillTreeNodes => _skillTreeNodes;

Expand Down
82 changes: 63 additions & 19 deletions frontend/lib/views/main_layout.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:auto_gpt_flutter_client/views/side_bar/side_bar_view.dart';
import 'package:auto_gpt_flutter_client/views/skill_tree/skill_tree_view.dart';
import 'package:auto_gpt_flutter_client/views/task/task_view.dart';
import 'package:auto_gpt_flutter_client/views/chat/chat_view.dart';
import 'package:auto_gpt_flutter_client/views/task_queue/task_queue_view.dart';
import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';

Expand All @@ -18,37 +19,80 @@ class MainLayout extends StatelessWidget {
// Get the screen width
double width = MediaQuery.of(context).size.width;

// Access the TaskViewModel from the context
// Access the various ViewModels from the context
final taskViewModel = Provider.of<TaskViewModel>(context);

// Access the ChatViewModel from the context
final chatViewModel = Provider.of<ChatViewModel>(context);

// Access the ChatViewModel from the context
final skillTreeViewModel = Provider.of<SkillTreeViewModel>(context);
// Initialize the width for the SideBarView
double sideBarWidth = 60.0;

// Initialize the width for the TaskView
double taskViewWidth = 280.0;

// Calculate remaining width after subtracting the width of the SideBarView
double remainingWidth = width - sideBarWidth;

// Declare variables to hold the widths of SkillTreeView, TestQueueView, and ChatView
double skillTreeViewWidth = 0;
double testQueueViewWidth = 0;
double chatViewWidth = 0;

// Check the screen width and return the appropriate layout
if (width > 800) {
// For larger screens, return a side-by-side layout
return Row(
children: [
SideBarView(selectedViewNotifier: selectedViewNotifier),
SizedBox(
width: sideBarWidth,
child: SideBarView(selectedViewNotifier: selectedViewNotifier)),
ValueListenableBuilder(
valueListenable: selectedViewNotifier,
builder: (context, String value, _) {
if (value == 'TaskView') {
return SizedBox(
width: 280, child: TaskView(viewModel: taskViewModel));
} else {
return Expanded(
child: SkillTreeView(viewModel: skillTreeViewModel));
}
return Consumer<SkillTreeViewModel>(
builder: (context, skillTreeViewModel, _) {
if (value == 'TaskView') {
skillTreeViewModel.resetState();
chatViewWidth = remainingWidth - taskViewWidth;
return Row(
children: [
SizedBox(
width: taskViewWidth,
child: TaskView(viewModel: taskViewModel)),
SizedBox(
width: chatViewWidth,
child: ChatView(viewModel: chatViewModel))
],
);
} else {
if (skillTreeViewModel.selectedNode != null) {
// If TaskQueueView should be displayed
testQueueViewWidth = remainingWidth * 0.25;
skillTreeViewWidth = remainingWidth * 0.25;
chatViewWidth = remainingWidth * 0.5;
} else {
// If only SkillTreeView and ChatView should be displayed
skillTreeViewWidth = remainingWidth * 0.5;
chatViewWidth = remainingWidth * 0.5;
}

return Row(
children: [
SizedBox(
width: skillTreeViewWidth,
child:
SkillTreeView(viewModel: skillTreeViewModel)),
if (skillTreeViewModel.selectedNode != null)
SizedBox(
width: testQueueViewWidth,
child: TaskQueueView()),
SizedBox(
width: chatViewWidth,
child: ChatView(viewModel: chatViewModel)),
],
);
}
},
);
},
),
Expanded(
child: ChatView(
viewModel: chatViewModel,
)),
],
);
} else {
Expand Down
71 changes: 71 additions & 0 deletions frontend/lib/views/task_queue/task_queue_view.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import 'package:flutter/material.dart';
import 'package:auto_gpt_flutter_client/viewmodels/skill_tree_viewmodel.dart';
import 'package:provider/provider.dart';

// TODO: Add view model for task queue instead of skill tree view model
class TaskQueueView extends StatelessWidget {
@override
Widget build(BuildContext context) {
final viewModel = Provider.of<SkillTreeViewModel>(context);

// Reverse the node hierarchy
final reversedHierarchy =
viewModel.selectedNodeHierarchy?.reversed.toList() ?? [];

return Material(
color: Colors.white,
child: Stack(
children: [
// The list of tasks (tiles)
ListView.builder(
itemCount: reversedHierarchy.length,
itemBuilder: (context, index) {
final node = reversedHierarchy[index];
return Container(
margin: EdgeInsets.fromLTRB(20, 5, 20, 5),
decoration: BoxDecoration(
color: Colors.white, // white background
border: Border.all(
color: Colors.black, width: 1), // thin black border
borderRadius: BorderRadius.circular(4), // small corner radius
),
child: ListTile(
title: Center(child: Text('Node ID: ${node.id}')),
subtitle: Center(child: Text('Color: ${node.color}')),
),
);
},
),

// Checkmark button at the bottom right
Positioned(
bottom: 50,
right: 50,
child: Tooltip(
message: 'Run suite of tests',
child: ElevatedButton(
onPressed: () {
// Add your logic here to run the suite of tests
},
child: Icon(Icons.check, color: Colors.green),
style: ButtonStyle(
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
),
backgroundColor: MaterialStateProperty.all(Colors.white),
side: MaterialStateProperty.all(
BorderSide(color: Colors.green, width: 3)),
minimumSize:
MaterialStateProperty.all(Size(50, 50)), // Square size
padding: MaterialStateProperty.all(EdgeInsets.all(0)),
),
),
),
),
],
),
);
}
}

0 comments on commit 314cce7

Please sign in to comment.