Merge commit 'e5d30a9f6d0854e20049309333c2f637cd03025c' as 'frontend'

This commit is contained in:
hunteraraujo
2023-09-06 11:22:37 -07:00
165 changed files with 7133 additions and 0 deletions

View File

@@ -0,0 +1,75 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:auto_gpt_flutter_client/viewmodels/api_settings_viewmodel.dart';
class ApiBaseUrlField extends StatelessWidget {
final TextEditingController controller;
const ApiBaseUrlField({required this.controller});
@override
Widget build(BuildContext context) {
return Consumer<ApiSettingsViewModel>(
builder: (context, apiSettingsViewModel, child) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [
Container(
height: 50,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.black, width: 0.5),
borderRadius: BorderRadius.circular(8),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: TextField(
controller: controller,
decoration: const InputDecoration(
border: InputBorder.none,
hintText: 'API Base URL',
),
),
),
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () {
controller.text = 'http://127.0.0.1:8000';
apiSettingsViewModel.updateBaseURL(controller.text);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: Colors.black,
textStyle: const TextStyle(
color: Colors.black,
),
),
child: const Text("Reset"),
),
ElevatedButton(
onPressed: () {
apiSettingsViewModel.updateBaseURL(controller.text);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: Colors.black,
textStyle: const TextStyle(
color: Colors.black,
),
),
child: const Text("Update"),
),
],
),
],
),
);
},
);
}
}

View File

@@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
class NewTaskButton extends StatelessWidget {
final VoidCallback onPressed;
const NewTaskButton({Key? key, required this.onPressed}) : super(key: key);
@override
Widget build(BuildContext context) {
// Determine the width of the TaskView
double taskViewWidth = MediaQuery.of(context).size.width;
double buttonWidth = taskViewWidth - 20;
if (buttonWidth > 260) {
buttonWidth = 260;
}
return ElevatedButton(
onPressed: onPressed,
style: ButtonStyle(
// Set the button's background color
backgroundColor: MaterialStateProperty.all<Color>(Colors.white),
// Set the button's edge
side: MaterialStateProperty.all<BorderSide>(
const BorderSide(color: Colors.black, width: 0.5)),
// Set the button's shape with rounded corners
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
),
),
child: SizedBox(
width: buttonWidth,
height: 50,
child: const Row(
children: [
// Black plus icon
Icon(Icons.add, color: Colors.black),
SizedBox(width: 8),
// "New Task" label
Text('New Task', style: TextStyle(color: Colors.black)),
],
),
),
);
}
}

View File

@@ -0,0 +1,75 @@
import 'package:flutter/material.dart';
import 'package:auto_gpt_flutter_client/models/task.dart';
class TaskListTile extends StatelessWidget {
final Task task;
final VoidCallback onTap;
final VoidCallback onDelete;
final bool selected;
const TaskListTile({
Key? key,
required this.task,
required this.onTap,
required this.onDelete,
this.selected = false,
}) : super(key: key);
Widget build(BuildContext context) {
// Determine the width of the TaskView
double taskViewWidth = MediaQuery.of(context).size.width;
double tileWidth = taskViewWidth - 20;
if (tileWidth > 260) {
tileWidth = 260;
}
return GestureDetector(
onTap: () {
onTap();
},
child: Material(
// Use a transparent color to avoid any unnecessary color overlay
color: Colors.transparent,
child: Padding(
// Provide a horizontal padding to ensure the tile does not touch the edges
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Container(
// Width and height specifications for the tile
width: tileWidth,
height: 50,
decoration: BoxDecoration(
// Use conditional operator to determine background color based on selection
color: selected ? Colors.grey[300] : Colors.white,
borderRadius: BorderRadius.circular(8.0),
),
child: Row(
children: [
// Space from the left edge of the tile
const SizedBox(width: 8),
// Message bubble icon indicating a task
const Icon(Icons.messenger_outline, color: Colors.black),
const SizedBox(width: 8),
// Task title
Expanded(
child: Text(
task.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(color: Colors.black),
),
),
// If the task is selected, show a delete icon
if (selected)
IconButton(
splashRadius: 0.1,
icon: const Icon(Icons.close, color: Colors.black),
onPressed: onDelete,
),
],
),
),
),
),
);
}
}

View File

@@ -0,0 +1,97 @@
import 'package:auto_gpt_flutter_client/views/task/api_base_url_field.dart';
import 'package:flutter/material.dart';
import 'package:auto_gpt_flutter_client/viewmodels/task_viewmodel.dart';
import 'package:auto_gpt_flutter_client/viewmodels/chat_viewmodel.dart';
import 'package:auto_gpt_flutter_client/viewmodels/api_settings_viewmodel.dart';
import 'package:auto_gpt_flutter_client/views/task/new_task_button.dart';
import 'package:auto_gpt_flutter_client/views/task/task_list_tile.dart';
import 'package:provider/provider.dart';
class TaskView extends StatefulWidget {
final TaskViewModel viewModel;
const TaskView({Key? key, required this.viewModel}) : super(key: key);
@override
_TaskViewState createState() => _TaskViewState();
}
class _TaskViewState extends State<TaskView> {
final TextEditingController _baseUrlController = TextEditingController();
@override
void initState() {
super.initState();
// Schedule the fetchTasks call for after the initial build
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.viewModel.fetchTasks();
_baseUrlController.text =
Provider.of<ApiSettingsViewModel>(context, listen: false).baseURL;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Column(
children: [
// Title and New Task button
Padding(
padding: const EdgeInsets.all(8.0),
child: NewTaskButton(
onPressed: () async {
// Update the current task ID and chats in ChatViewModel
final chatViewModel =
Provider.of<ChatViewModel>(context, listen: false);
chatViewModel.clearCurrentTaskAndChats();
print(
'New Task button pressed, cleared current task ID and chats');
},
)),
// Task List
Expanded(
child: ListView.builder(
itemCount: widget.viewModel.tasks.length,
itemBuilder: (context, index) {
final task = widget.viewModel.tasks[index];
return TaskListTile(
task: task,
onTap: () {
// Select the task in TaskViewModel
widget.viewModel.selectTask(task.id);
// Update the current task ID in ChatViewModel
// TODO: Do we want to have a reference to chat view model in this class?
final chatViewModel =
Provider.of<ChatViewModel>(context, listen: false);
chatViewModel.setCurrentTaskId(task.id);
print('Task ${task.title} tapped');
},
onDelete: () {
// Delete the task in TaskViewModel
widget.viewModel.deleteTask(task.id);
// TODO: Do we want to have a reference to chat view model in this class?
final chatViewModel =
Provider.of<ChatViewModel>(context, listen: false);
if (chatViewModel.currentTaskId == task.id) {
chatViewModel.clearCurrentTaskAndChats();
}
print('Task ${task.title} delete button tapped');
},
selected: task.id == widget.viewModel.selectedTask?.id,
);
},
),
),
const SizedBox(height: 16),
ApiBaseUrlField(controller: _baseUrlController),
const SizedBox(height: 16),
],
),
);
}
}