blob: f2a7a36d139cf0679ede3fe5282a75a85b4c0ec6 [file] [log] [blame]
// Copyright 2019 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:cocoon_service/protos.dart' show Agent;
import '../logic/agent_health_details.dart';
import '../state/agent.dart';
import 'agent_tile.dart';
import 'now.dart';
/// Shows [List<Agent>] that have [Agent.agentId] that contains [agentFilter] in
/// a ListView of [AgentTile].
///
/// Sorts this list to show unhealthy agents first.
class AgentList extends StatefulWidget {
const AgentList({
Key key,
this.agents,
this.agentState,
this.agentFilter,
@visibleForTesting this.insertKeys = false,
}) : super(key: key);
/// All known agents that can be shown.
final List<Agent> agents;
final AgentState agentState;
/// Search term to filter the agents from [agentState] and show only those
/// that contain this term.
///
/// This is set either:
/// 1. Route argument set when being redirected from the build dashboard
/// to view a specific agent that ran a task.
/// 2. In the search bar of this widget.
final String agentFilter;
/// When true, will set a key for the [AgentTile] that is composed of its
/// position in the list and the agent id.
@visibleForTesting
final bool insertKeys;
@override
_AgentListState createState() => _AgentListState();
}
class _AgentListState extends State<AgentList> {
/// Controller for filtering the agents, acting as a search bar.
TextEditingController filterAgentsController = TextEditingController();
@override
void initState() {
super.initState();
/// When redirected from the build dashboard, display a specific agent.
/// Updates the search bar to show [agentFilter].
if (widget.agentFilter != null && widget.agentFilter.isNotEmpty) {
filterAgentsController.text = widget.agentFilter;
}
}
@override
Widget build(BuildContext context) {
if (widget.agents.isEmpty) {
return const Center(
child: SizedBox(
height: 100,
width: 100,
child: CircularProgressIndicator(),
),
);
}
final DateTime now = Now.of(context);
// TODO(chillers): Remove sort once backend handles sorting. https://github.com/flutter/flutter/issues/48249
final List<_SortableAgent> sortableAgents =
widget.agents.map((Agent agent) => _SortableAgent(agent, AgentHealthDetails(agent), now)).toList()..sort();
final List<_SortableAgent> filteredAgents = filterAgents(sortableAgents, filterAgentsController.text);
return Column(
children: <Widget>[
// Padding for the search bar
Container(height: 25),
TextField(
onChanged: (String value) {
setState(() {
// the filterAgentsController.text changed (used above in generating the filteredAgents)
});
},
controller: filterAgentsController,
decoration: const InputDecoration(
labelText: 'Filter',
hintText: 'Filter',
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(25.0),
),
),
),
),
Expanded(
child: ListView(
children: List<AgentTile>.generate(filteredAgents.length, (int i) {
return AgentTile(
key: widget.insertKeys ? Key('$i-${filteredAgents[i].agent.agentId}') : null,
agentHealthDetails: filteredAgents[i].agentHealthDetails,
agentState: widget.agentState,
);
}),
),
),
],
);
}
/// Creates a new [List<FullAgent>] of only the agents that have an [agentId]
/// that contains [filter].
///
/// If filter is empty, the original list of agents.
List<_SortableAgent> filterAgents(List<_SortableAgent> agents, String filter) {
if (filter.isEmpty) {
return agents;
}
return agents.where((_SortableAgent fullAgent) => fullAgent.agent.agentId.contains(filter)).toList();
}
}
/// A wrapper class for sorting [AgentHealthDetails].
///
/// Sorts to show unhealthy agents before healthy agents, with those groups
/// being sorted alphabetically.
class _SortableAgent implements Comparable<_SortableAgent> {
_SortableAgent(
this.agent,
this.agentHealthDetails,
DateTime now,
) : _isHealthy = agentHealthDetails.isHealthy(now);
final Agent agent;
final AgentHealthDetails agentHealthDetails;
final bool _isHealthy;
@override
int compareTo(_SortableAgent other) {
if (_isHealthy && other._isHealthy) {
return agent.agentId.compareTo(other.agent.agentId);
}
return _isHealthy ? 1 : -1;
}
}