Add temperature control stateless widget
This commit is contained in:
@ -404,6 +404,7 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
_buildOnOffControl(entity),
|
_buildOnOffControl(entity),
|
||||||
_buildTemperatureControls(entity),
|
_buildTemperatureControls(entity),
|
||||||
|
_buildTargetTemperatureControls(entity),
|
||||||
_buildHumidityControls(entity),
|
_buildHumidityControls(entity),
|
||||||
_buildOperationControl(entity),
|
_buildOperationControl(entity),
|
||||||
_buildFanControl(entity),
|
_buildFanControl(entity),
|
||||||
@ -578,139 +579,68 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTemperatureControls(ClimateEntity entity) {
|
Widget _buildTemperatureControls(ClimateEntity entity) {
|
||||||
List<Widget> result = [];
|
|
||||||
if ((entity.supportTargetTemperature) && (entity.temperature != null)) {
|
if ((entity.supportTargetTemperature) && (entity.temperature != null)) {
|
||||||
result.addAll(<Widget>[
|
|
||||||
Text(
|
|
||||||
"$_tmpTemperature",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: entity.largeFontSize,
|
|
||||||
color: _showPending ? Colors.red : Colors.black
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
children: <Widget>[
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName('mdi:chevron-up')),
|
|
||||||
iconSize: 30.0,
|
|
||||||
onPressed: () => _temperatureUp(entity, 0.1),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName('mdi:chevron-down')),
|
|
||||||
iconSize: 30.0,
|
|
||||||
onPressed: () => _temperatureDown(entity, 0.1),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
children: <Widget>[
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName('mdi:chevron-double-up')),
|
|
||||||
iconSize: 30.0,
|
|
||||||
onPressed: () => _temperatureUp(entity, 0.5),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName('mdi:chevron-double-down')),
|
|
||||||
iconSize: 30.0,
|
|
||||||
onPressed: () => _temperatureDown(entity, 0.5),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
} else if (entity.supportTargetTemperatureHigh && entity.supportTargetTemperatureLow && (entity.targetHigh != null) && (entity.targetLow != null)) {
|
|
||||||
result.addAll(<Widget>[
|
|
||||||
Text(
|
|
||||||
"$_tmpTargetLow",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: entity.largeFontSize,
|
|
||||||
color: _showPending ? Colors.red : Colors.black
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
children: <Widget>[
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName('mdi:chevron-up')),
|
|
||||||
iconSize: 30.0,
|
|
||||||
onPressed: () => _targetLowUp(entity, 0.1),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName('mdi:chevron-down')),
|
|
||||||
iconSize: 30.0,
|
|
||||||
onPressed: () => _targetLowDown(entity, 0.1),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
children: <Widget>[
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName('mdi:chevron-double-up')),
|
|
||||||
iconSize: 30.0,
|
|
||||||
onPressed: () => _targetLowUp(entity, 0.5),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName('mdi:chevron-double-down')),
|
|
||||||
iconSize: 30.0,
|
|
||||||
onPressed: () => _targetLowDown(entity, 0.5),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Container(height: 10.0),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"$_tmpTargetHigh",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: entity.largeFontSize,
|
|
||||||
color: _showPending ? Colors.red : Colors.black
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
children: <Widget>[
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName('mdi:chevron-up')),
|
|
||||||
iconSize: 30.0,
|
|
||||||
onPressed: () => _targetHighUp(entity, 0.1),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName('mdi:chevron-down')),
|
|
||||||
iconSize: 30.0,
|
|
||||||
onPressed: () => _targetHighDown(entity, 0.1),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
children: <Widget>[
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName('mdi:chevron-double-up')),
|
|
||||||
iconSize: 30.0,
|
|
||||||
onPressed: () => _targetHighUp(entity, 0.5),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName('mdi:chevron-double-down')),
|
|
||||||
iconSize: 30.0,
|
|
||||||
onPressed: () => _targetHighDown(entity, 0.5),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
} else if (entity.supportTargetTemperatureHigh || entity.supportTargetTemperatureLow) {
|
|
||||||
result.add(Text("Unsupported temperature control. Please, report an issue."));
|
|
||||||
}
|
|
||||||
if (result.isNotEmpty) {
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text("Target temperature", style: TextStyle(
|
Text("Target temperature", style: TextStyle(
|
||||||
fontSize: entity.stateFontSize
|
fontSize: entity.stateFontSize
|
||||||
)),
|
)),
|
||||||
Row(
|
TemperatureControlWidget(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
value: _tmpTemperature,
|
||||||
children: result,
|
onLargeDec: () => _temperatureDown(entity, 0.5),
|
||||||
|
onLargeInc: () => _temperatureUp(entity, 0.5),
|
||||||
|
onSmallDec: () => _temperatureDown(entity, 0.1),
|
||||||
|
onSmallInc: () => _temperatureUp(entity, 0.1),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Container(height: 0.0, width: 0.0,);
|
return Container(width: 0.0, height: 0.0,);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTargetTemperatureControls(ClimateEntity entity) {
|
||||||
|
List<Widget> controls = [];
|
||||||
|
if ((entity.supportTargetTemperatureLow) && (entity.targetLow != null)) {
|
||||||
|
controls.addAll(<Widget>[
|
||||||
|
TemperatureControlWidget(
|
||||||
|
value: _tmpTargetLow,
|
||||||
|
onLargeDec: () => _targetLowDown(entity, 0.5),
|
||||||
|
onLargeInc: () => _targetLowUp(entity, 0.5),
|
||||||
|
onSmallDec: () => _targetLowDown(entity, 0.1),
|
||||||
|
onSmallInc: () => _targetLowUp(entity, 0.1),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(height: 10.0),
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ((entity.supportTargetTemperatureHigh) && (entity.targetHigh != null)) {
|
||||||
|
controls.add(
|
||||||
|
TemperatureControlWidget(
|
||||||
|
value: _tmpTargetHigh,
|
||||||
|
onLargeDec: () => _targetHighDown(entity, 0.5),
|
||||||
|
onLargeInc: () => _targetHighUp(entity, 0.5),
|
||||||
|
onSmallDec: () => _targetHighDown(entity, 0.1),
|
||||||
|
onSmallInc: () => _targetHighUp(entity, 0.1),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (controls.isNotEmpty) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text("Target temperature range", style: TextStyle(
|
||||||
|
fontSize: entity.stateFontSize
|
||||||
|
)),
|
||||||
|
Row(
|
||||||
|
children: controls,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(width: 0.0, height: 0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
part of '../main.dart';
|
part of '../main.dart';
|
||||||
|
|
||||||
class EntityWidgetsSizes {
|
class EntityWidgetsSizes {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class EntityModel extends InheritedWidget {
|
class EntityModel extends InheritedWidget {
|
||||||
|
|
||||||
const EntityModel({
|
const EntityModel({
|
||||||
Key key,
|
Key key,
|
||||||
@required this.entity,
|
@required this.entity,
|
||||||
@ -24,11 +21,9 @@ class EntityModel extends InheritedWidget {
|
|||||||
bool updateShouldNotify(InheritedWidget oldWidget) {
|
bool updateShouldNotify(InheritedWidget oldWidget) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class DefaultEntityContainer extends StatelessWidget {
|
class DefaultEntityContainer extends StatelessWidget {
|
||||||
|
|
||||||
DefaultEntityContainer({
|
DefaultEntityContainer({
|
||||||
Key key,
|
Key key,
|
||||||
@required this.state,
|
@required this.state,
|
||||||
@ -52,11 +47,9 @@ class DefaultEntityContainer extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class EntityPageContainer extends StatelessWidget {
|
class EntityPageContainer extends StatelessWidget {
|
||||||
|
|
||||||
EntityPageContainer({Key key, @required this.children}) : super(key: key);
|
EntityPageContainer({Key key, @required this.children}) : super(key: key);
|
||||||
|
|
||||||
final List<Widget> children;
|
final List<Widget> children;
|
||||||
@ -67,17 +60,15 @@ class EntityPageContainer extends StatelessWidget {
|
|||||||
children: children,
|
children: children,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SimpleEntityState extends StatelessWidget {
|
class SimpleEntityState extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final entityModel = EntityModel.of(context);
|
final entityModel = EntityModel.of(context);
|
||||||
return Padding(
|
return Padding(
|
||||||
padding:
|
padding: EdgeInsets.fromLTRB(
|
||||||
EdgeInsets.fromLTRB(0.0, 0.0, entityModel.entity.rightWidgetPadding, 0.0),
|
0.0, 0.0, entityModel.entity.rightWidgetPadding, 0.0),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
child: Text(
|
child: Text(
|
||||||
"${entityModel.entity.state}${entityModel.entity.unitOfMeasurement}",
|
"${entityModel.entity.state}${entityModel.entity.unitOfMeasurement}",
|
||||||
@ -85,15 +76,14 @@ class SimpleEntityState extends StatelessWidget {
|
|||||||
style: new TextStyle(
|
style: new TextStyle(
|
||||||
fontSize: entityModel.entity.stateFontSize,
|
fontSize: entityModel.entity.stateFontSize,
|
||||||
)),
|
)),
|
||||||
onTap: () => entityModel.handleTap ? eventBus.fire(new ShowEntityPageEvent(entityModel.entity)) : null,
|
onTap: () => entityModel.handleTap
|
||||||
)
|
? eventBus.fire(new ShowEntityPageEvent(entityModel.entity))
|
||||||
);
|
: null,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class EntityName extends StatelessWidget {
|
class EntityName extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final entityModel = EntityModel.of(context);
|
final entityModel = EntityModel.of(context);
|
||||||
@ -107,34 +97,36 @@ class EntityName extends StatelessWidget {
|
|||||||
style: TextStyle(fontSize: entityModel.entity.nameFontSize),
|
style: TextStyle(fontSize: entityModel.entity.nameFontSize),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () => entityModel.handleTap ? eventBus.fire(new ShowEntityPageEvent(entityModel.entity)) : null,
|
onTap: () => entityModel.handleTap
|
||||||
|
? eventBus.fire(new ShowEntityPageEvent(entityModel.entity))
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class EntityIcon extends StatelessWidget {
|
class EntityIcon extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final entityModel = EntityModel.of(context);
|
final entityModel = EntityModel.of(context);
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.fromLTRB(entityModel.entity.leftWidgetPadding, 0.0, 12.0, 0.0),
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
entityModel.entity.leftWidgetPadding, 0.0, 12.0, 0.0),
|
||||||
//TODO: move createIconWidgetFromEntityData into this widget
|
//TODO: move createIconWidgetFromEntityData into this widget
|
||||||
child: MaterialDesignIcons.createIconWidgetFromEntityData(
|
child: MaterialDesignIcons.createIconWidgetFromEntityData(
|
||||||
entityModel.entity,
|
entityModel.entity,
|
||||||
entityModel.entity.iconSize,
|
entityModel.entity.iconSize,
|
||||||
Entity.STATE_ICONS_COLORS[entityModel.entity.state] ?? Entity.STATE_ICONS_COLORS["default"]),
|
Entity.STATE_ICONS_COLORS[entityModel.entity.state] ??
|
||||||
|
Entity.STATE_ICONS_COLORS["default"]),
|
||||||
),
|
),
|
||||||
onTap: () => entityModel.handleTap ? eventBus.fire(new ShowEntityPageEvent(entityModel.entity)) : null,
|
onTap: () => entityModel.handleTap
|
||||||
|
? eventBus.fire(new ShowEntityPageEvent(entityModel.entity))
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class LastUpdatedWidget extends StatelessWidget {
|
class LastUpdatedWidget extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final entityModel = EntityModel.of(context);
|
final entityModel = EntityModel.of(context);
|
||||||
@ -144,35 +136,31 @@ class LastUpdatedWidget extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
'${entityModel.entity.lastUpdated}',
|
'${entityModel.entity.lastUpdated}',
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
style:
|
style: TextStyle(
|
||||||
TextStyle(fontSize: entityModel.entity.smallFontSize, color: Colors.black26),
|
fontSize: entityModel.entity.smallFontSize, color: Colors.black26),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class EntityAttributesList extends StatelessWidget {
|
class EntityAttributesList extends StatelessWidget {
|
||||||
|
|
||||||
EntityAttributesList({Key key}) : super(key: key);
|
EntityAttributesList({Key key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final entityModel = EntityModel.of(context);
|
final entityModel = EntityModel.of(context);
|
||||||
List<Widget> attrs = [];
|
List<Widget> attrs = [];
|
||||||
if ((entityModel.entity.attributesToShow == null) || (entityModel.entity.attributesToShow.contains("all"))) {
|
if ((entityModel.entity.attributesToShow == null) ||
|
||||||
entityModel.entity.attributes.forEach((name, value){
|
(entityModel.entity.attributesToShow.contains("all"))) {
|
||||||
attrs.add(
|
entityModel.entity.attributes.forEach((name, value) {
|
||||||
_buildSingleAttribute(entityModel.entity, "$name", "$value")
|
attrs.add(_buildSingleAttribute(entityModel.entity, "$name", "$value"));
|
||||||
);
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
entityModel.entity.attributesToShow.forEach((String attr) {
|
entityModel.entity.attributesToShow.forEach((String attr) {
|
||||||
String attrValue = entityModel.entity.getAttribute("$attr");
|
String attrValue = entityModel.entity.getAttribute("$attr");
|
||||||
if (attrValue != null) {
|
if (attrValue != null) {
|
||||||
attrs.add(
|
attrs.add(
|
||||||
_buildSingleAttribute(entityModel.entity, "$attr", "$attrValue")
|
_buildSingleAttribute(entityModel.entity, "$attr", "$attrValue"));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -188,7 +176,8 @@ class EntityAttributesList extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.fromLTRB(entity.leftWidgetPadding, entity.rowPadding, 0.0, 0.0),
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
entity.leftWidgetPadding, entity.rowPadding, 0.0, 0.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
"$name",
|
"$name",
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
@ -197,7 +186,8 @@ class EntityAttributesList extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.fromLTRB(0.0, entity.rowPadding, entity.rightWidgetPadding, 0.0),
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
0.0, entity.rowPadding, entity.rightWidgetPadding, 0.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
"$value",
|
"$value",
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
@ -210,28 +200,30 @@ class EntityAttributesList extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Badge extends StatelessWidget {
|
class Badge extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final entityModel = EntityModel.of(context);
|
final entityModel = EntityModel.of(context);
|
||||||
double iconSize = 26.0;
|
double iconSize = 26.0;
|
||||||
Widget badgeIcon;
|
Widget badgeIcon;
|
||||||
String onBadgeTextValue;
|
String onBadgeTextValue;
|
||||||
Color iconColor = Entity.badgeColors[entityModel.entity.domain] ?? Entity.badgeColors["default"];
|
Color iconColor = Entity.badgeColors[entityModel.entity.domain] ??
|
||||||
|
Entity.badgeColors["default"];
|
||||||
switch (entityModel.entity.domain) {
|
switch (entityModel.entity.domain) {
|
||||||
case "sun": {
|
case "sun":
|
||||||
badgeIcon = entityModel.entity.state == "below_horizon" ?
|
{
|
||||||
Icon(
|
badgeIcon = entityModel.entity.state == "below_horizon"
|
||||||
|
? Icon(
|
||||||
MaterialDesignIcons.createIconDataFromIconCode(0xf0dc),
|
MaterialDesignIcons.createIconDataFromIconCode(0xf0dc),
|
||||||
size: iconSize,
|
size: iconSize,
|
||||||
) :
|
)
|
||||||
Icon(
|
: Icon(
|
||||||
MaterialDesignIcons.createIconDataFromIconCode(0xf5a8),
|
MaterialDesignIcons.createIconDataFromIconCode(0xf5a8),
|
||||||
size: iconSize,
|
size: iconSize,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "sensor": {
|
case "sensor":
|
||||||
|
{
|
||||||
onBadgeTextValue = entityModel.entity.unitOfMeasurement;
|
onBadgeTextValue = entityModel.entity.unitOfMeasurement;
|
||||||
badgeIcon = Center(
|
badgeIcon = Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -244,13 +236,17 @@ class Badge extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "device_tracker": {
|
case "device_tracker":
|
||||||
badgeIcon = MaterialDesignIcons.createIconWidgetFromEntityData(entityModel.entity, iconSize,Colors.black);
|
{
|
||||||
|
badgeIcon = MaterialDesignIcons.createIconWidgetFromEntityData(
|
||||||
|
entityModel.entity, iconSize, Colors.black);
|
||||||
onBadgeTextValue = entityModel.entity.state;
|
onBadgeTextValue = entityModel.entity.state;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default:
|
||||||
badgeIcon = MaterialDesignIcons.createIconWidgetFromEntityData(entityModel.entity, iconSize,Colors.black);
|
{
|
||||||
|
badgeIcon = MaterialDesignIcons.createIconWidgetFromEntityData(
|
||||||
|
entityModel.entity, iconSize, Colors.black);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Widget onBadgeText;
|
Widget onBadgeText;
|
||||||
@ -261,14 +257,15 @@ class Badge extends StatelessWidget {
|
|||||||
padding: EdgeInsets.fromLTRB(6.0, 2.0, 6.0, 2.0),
|
padding: EdgeInsets.fromLTRB(6.0, 2.0, 6.0, 2.0),
|
||||||
child: Text("$onBadgeTextValue",
|
child: Text("$onBadgeTextValue",
|
||||||
style: TextStyle(fontSize: 12.0, color: Colors.white),
|
style: TextStyle(fontSize: 12.0, color: Colors.white),
|
||||||
textAlign: TextAlign.center, softWrap: false, overflow: TextOverflow.fade),
|
textAlign: TextAlign.center,
|
||||||
|
softWrap: false,
|
||||||
|
overflow: TextOverflow.fade),
|
||||||
decoration: new BoxDecoration(
|
decoration: new BoxDecoration(
|
||||||
// Circle shape
|
// Circle shape
|
||||||
//shape: BoxShape.circle,
|
//shape: BoxShape.circle,
|
||||||
color: iconColor,
|
color: iconColor,
|
||||||
borderRadius: BorderRadius.circular(9.0),
|
borderRadius: BorderRadius.circular(9.0),
|
||||||
)
|
));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -304,8 +301,7 @@ class Badge extends StatelessWidget {
|
|||||||
right: -10.0,
|
right: -10.0,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: onBadgeText,
|
child: onBadgeText,
|
||||||
)
|
))
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -322,10 +318,9 @@ class Badge extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: () => eventBus.fire(new ShowEntityPageEvent(entityModel.entity))
|
onTap: () =>
|
||||||
);
|
eventBus.fire(new ShowEntityPageEvent(entityModel.entity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClimateStateWidget extends StatelessWidget {
|
class ClimateStateWidget extends StatelessWidget {
|
||||||
@ -334,73 +329,140 @@ class ClimateStateWidget extends StatelessWidget {
|
|||||||
final entityModel = EntityModel.of(context);
|
final entityModel = EntityModel.of(context);
|
||||||
final ClimateEntity entity = entityModel.entity;
|
final ClimateEntity entity = entityModel.entity;
|
||||||
String targetTemp = "-";
|
String targetTemp = "-";
|
||||||
if ((entity.supportTargetTemperature) && (entity.temperature!=null)) {
|
if ((entity.supportTargetTemperature) && (entity.temperature != null)) {
|
||||||
targetTemp = "${entity.temperature}";
|
targetTemp = "${entity.temperature}";
|
||||||
} else if ((entity.supportTargetTemperatureLow) && (entity.targetLow != null)) {
|
} else if ((entity.supportTargetTemperatureLow) &&
|
||||||
|
(entity.targetLow != null)) {
|
||||||
targetTemp = "${entity.targetLow}";
|
targetTemp = "${entity.targetLow}";
|
||||||
if ((entity.supportTargetTemperatureHigh) && (entity.targetHigh != null)) {
|
if ((entity.supportTargetTemperatureHigh) &&
|
||||||
|
(entity.targetHigh != null)) {
|
||||||
targetTemp += " - ${entity.targetHigh}";
|
targetTemp += " - ${entity.targetHigh}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Padding(
|
return Padding(
|
||||||
padding:
|
padding: EdgeInsets.fromLTRB(
|
||||||
EdgeInsets.fromLTRB(0.0, 0.0, entityModel.entity.rightWidgetPadding, 0.0),
|
0.0, 0.0, entityModel.entity.rightWidgetPadding, 0.0),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text("${entity.state}",
|
||||||
"${entity.state}",
|
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
style: new TextStyle(
|
style: new TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: entityModel.entity.stateFontSize,
|
fontSize: entityModel.entity.stateFontSize,
|
||||||
)),
|
)),
|
||||||
Text(
|
Text(" $targetTemp",
|
||||||
" $targetTemp",
|
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
style: new TextStyle(
|
style: new TextStyle(
|
||||||
fontSize: entityModel.entity.stateFontSize,
|
fontSize: entityModel.entity.stateFontSize,
|
||||||
))
|
))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Text(
|
entity.attributes["current_temperature"] != null ?
|
||||||
"Currently: ${entity.attributes["current_temperature"]}",
|
Text("Currently: ${entity.attributes["current_temperature"]}",
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
style: new TextStyle(
|
style: new TextStyle(
|
||||||
fontSize: entityModel.entity.stateFontSize,
|
fontSize: entityModel.entity.stateFontSize,
|
||||||
color: Colors.black45
|
color: Colors.black45)
|
||||||
))
|
) :
|
||||||
|
Container(height: 0.0,)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: () => entityModel.handleTap ? eventBus.fire(new ShowEntityPageEvent(entity)) : null,
|
onTap: () => entityModel.handleTap
|
||||||
|
? eventBus.fire(new ShowEntityPageEvent(entity))
|
||||||
|
: null,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TemperatureControlWidget extends StatelessWidget {
|
||||||
|
final double value;
|
||||||
|
final double fontSize;
|
||||||
|
final Color fontColor;
|
||||||
|
final onSmallInc;
|
||||||
|
final onLargeInc;
|
||||||
|
final onSmallDec;
|
||||||
|
final onLargeDec;
|
||||||
|
|
||||||
|
TemperatureControlWidget(
|
||||||
|
{Key key,
|
||||||
|
@required this.value,
|
||||||
|
@required this.onSmallInc,
|
||||||
|
@required this.onSmallDec,
|
||||||
|
@required this.onLargeInc,
|
||||||
|
@required this.onLargeDec,
|
||||||
|
this.fontSize,
|
||||||
|
this.fontColor})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
"$value",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: fontSize ?? 24.0,
|
||||||
|
color: fontColor ?? Colors.black
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
||||||
|
'mdi:chevron-up')),
|
||||||
|
iconSize: 30.0,
|
||||||
|
onPressed: () => onSmallInc(),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
||||||
|
'mdi:chevron-down')),
|
||||||
|
iconSize: 30.0,
|
||||||
|
onPressed: () => onSmallDec(),
|
||||||
)
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
||||||
|
'mdi:chevron-double-up')),
|
||||||
|
iconSize: 30.0,
|
||||||
|
onPressed: () => onLargeInc(),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
||||||
|
'mdi:chevron-double-down')),
|
||||||
|
iconSize: 30.0,
|
||||||
|
onPressed: () => onLargeDec(),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class DateTimeStateWidget extends StatelessWidget {
|
class DateTimeStateWidget extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final entityModel = EntityModel.of(context);
|
final entityModel = EntityModel.of(context);
|
||||||
final DateTimeEntity entity = entityModel.entity;
|
final DateTimeEntity entity = entityModel.entity;
|
||||||
return Padding(
|
return Padding(
|
||||||
padding:
|
padding: EdgeInsets.fromLTRB(0.0, 0.0, entity.rightWidgetPadding, 0.0),
|
||||||
EdgeInsets.fromLTRB(0.0, 0.0, entity.rightWidgetPadding, 0.0),
|
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
child: Text(
|
child: Text("${entity.formattedState}",
|
||||||
"${entity.formattedState}",
|
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
style: new TextStyle(
|
style: new TextStyle(
|
||||||
fontSize: entity.stateFontSize,
|
fontSize: entity.stateFontSize,
|
||||||
)),
|
)),
|
||||||
onTap: () => _handleStateTap(context, entity),
|
onTap: () => _handleStateTap(context, entity),
|
||||||
)
|
));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleStateTap(BuildContext context, DateTimeEntity entity) {
|
void _handleStateTap(BuildContext context, DateTimeEntity entity) {
|
||||||
@ -408,18 +470,35 @@ class DateTimeStateWidget extends StatelessWidget {
|
|||||||
_showDatePicker(context, entity).then((date) {
|
_showDatePicker(context, entity).then((date) {
|
||||||
if (date != null) {
|
if (date != null) {
|
||||||
if (entity.hasTime) {
|
if (entity.hasTime) {
|
||||||
_showTimePicker(context, entity).then((time){
|
_showTimePicker(context, entity).then((time) {
|
||||||
entity.setNewState({"date": "${formatDate(date, [yyyy, '-', mm, '-', dd])}", "time": "${formatDate(DateTime(1970, 1, 1, time.hour, time.minute), [HH, ':', nn])}"});
|
entity.setNewState({
|
||||||
|
"date": "${formatDate(date, [yyyy, '-', mm, '-', dd])}",
|
||||||
|
"time":
|
||||||
|
"${formatDate(DateTime(1970, 1, 1, time.hour, time.minute), [
|
||||||
|
HH,
|
||||||
|
':',
|
||||||
|
nn
|
||||||
|
])}"
|
||||||
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
entity.setNewState({"date": "${formatDate(date, [yyyy, '-', mm, '-', dd])}"});
|
entity.setNewState({
|
||||||
|
"date": "${formatDate(date, [yyyy, '-', mm, '-', dd])}"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (entity.hasTime) {
|
} else if (entity.hasTime) {
|
||||||
_showTimePicker(context, entity).then((time){
|
_showTimePicker(context, entity).then((time) {
|
||||||
if (time != null) {
|
if (time != null) {
|
||||||
entity.setNewState({"time": "${formatDate(DateTime(1970, 1, 1, time.hour, time.minute), [HH, ':', nn])}"});
|
entity.setNewState({
|
||||||
|
"time":
|
||||||
|
"${formatDate(DateTime(1970, 1, 1, time.hour, time.minute), [
|
||||||
|
HH,
|
||||||
|
':',
|
||||||
|
nn
|
||||||
|
])}"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -439,24 +518,24 @@ class DateTimeStateWidget extends StatelessWidget {
|
|||||||
Future _showTimePicker(BuildContext context, DateTimeEntity entity) {
|
Future _showTimePicker(BuildContext context, DateTimeEntity entity) {
|
||||||
return showTimePicker(
|
return showTimePicker(
|
||||||
context: context,
|
context: context,
|
||||||
initialTime: TimeOfDay.fromDateTime(entity.dateTimeState)
|
initialTime: TimeOfDay.fromDateTime(entity.dateTimeState));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class CoverEntityControlState extends StatelessWidget {
|
class CoverEntityControlState extends StatelessWidget {
|
||||||
|
|
||||||
void _open(CoverEntity entity) {
|
void _open(CoverEntity entity) {
|
||||||
eventBus.fire(new ServiceCallEvent(entity.domain, "open_cover", entity.entityId, null));
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, "open_cover", entity.entityId, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _close(CoverEntity entity) {
|
void _close(CoverEntity entity) {
|
||||||
eventBus.fire(new ServiceCallEvent(entity.domain, "close_cover", entity.entityId, null));
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, "close_cover", entity.entityId, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _stop(CoverEntity entity) {
|
void _stop(CoverEntity entity) {
|
||||||
eventBus.fire(new ServiceCallEvent(entity.domain, "stop_cover", entity.entityId, null));
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, "stop_cover", entity.entityId, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -465,64 +544,62 @@ class CoverEntityControlState extends StatelessWidget {
|
|||||||
final CoverEntity entity = entityModel.entity;
|
final CoverEntity entity = entityModel.entity;
|
||||||
List<Widget> buttons = [];
|
List<Widget> buttons = [];
|
||||||
if (entity.supportOpen) {
|
if (entity.supportOpen) {
|
||||||
buttons.add(
|
buttons.add(IconButton(
|
||||||
IconButton(
|
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
MaterialDesignIcons.createIconDataFromIconName("mdi:arrow-up"),
|
MaterialDesignIcons.createIconDataFromIconName("mdi:arrow-up"),
|
||||||
size: entity.iconSize,
|
size: entity.iconSize,
|
||||||
),
|
),
|
||||||
onPressed: entity.canBeOpened ? () =>_open(entity) : null
|
onPressed: entity.canBeOpened ? () => _open(entity) : null));
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
buttons.add(Container(width: entity.iconSize+20.0,));
|
buttons.add(Container(
|
||||||
|
width: entity.iconSize + 20.0,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if (entity.supportStop) {
|
if (entity.supportStop) {
|
||||||
buttons.add(
|
buttons.add(IconButton(
|
||||||
IconButton(
|
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
MaterialDesignIcons.createIconDataFromIconName("mdi:stop"),
|
MaterialDesignIcons.createIconDataFromIconName("mdi:stop"),
|
||||||
size: entity.iconSize,
|
size: entity.iconSize,
|
||||||
),
|
),
|
||||||
onPressed: () => _stop(entity)
|
onPressed: () => _stop(entity)));
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
buttons.add(Container(width: entity.iconSize+20.0,));
|
buttons.add(Container(
|
||||||
|
width: entity.iconSize + 20.0,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if (entity.supportClose) {
|
if (entity.supportClose) {
|
||||||
buttons.add(
|
buttons.add(IconButton(
|
||||||
IconButton(
|
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
MaterialDesignIcons.createIconDataFromIconName("mdi:arrow-down"),
|
MaterialDesignIcons.createIconDataFromIconName("mdi:arrow-down"),
|
||||||
size: entity.iconSize,
|
size: entity.iconSize,
|
||||||
),
|
),
|
||||||
onPressed: entity.canBeClosed ? () => _close(entity) : null
|
onPressed: entity.canBeClosed ? () => _close(entity) : null));
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
buttons.add(Container(width: entity.iconSize+20.0,));
|
buttons.add(Container(
|
||||||
|
width: entity.iconSize + 20.0,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
children: buttons,
|
children: buttons,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class CoverEntityTiltControlButtons extends StatelessWidget {
|
class CoverEntityTiltControlButtons extends StatelessWidget {
|
||||||
|
|
||||||
void _open(CoverEntity entity) {
|
void _open(CoverEntity entity) {
|
||||||
eventBus.fire(new ServiceCallEvent(entity.domain, "open_cover_tilt", entity.entityId, null));
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, "open_cover_tilt", entity.entityId, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _close(CoverEntity entity) {
|
void _close(CoverEntity entity) {
|
||||||
eventBus.fire(new ServiceCallEvent(entity.domain, "close_cover_tilt", entity.entityId, null));
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, "close_cover_tilt", entity.entityId, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _stop(CoverEntity entity) {
|
void _stop(CoverEntity entity) {
|
||||||
eventBus.fire(new ServiceCallEvent(entity.domain, "stop_cover_tilt", entity.entityId, null));
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, "stop_cover_tilt", entity.entityId, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -531,48 +608,46 @@ class CoverEntityTiltControlButtons extends StatelessWidget {
|
|||||||
final CoverEntity entity = entityModel.entity;
|
final CoverEntity entity = entityModel.entity;
|
||||||
List<Widget> buttons = [];
|
List<Widget> buttons = [];
|
||||||
if (entity.supportOpenTilt) {
|
if (entity.supportOpenTilt) {
|
||||||
buttons.add(
|
buttons.add(IconButton(
|
||||||
IconButton(
|
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
MaterialDesignIcons.createIconDataFromIconName("mdi:arrow-top-right"),
|
MaterialDesignIcons.createIconDataFromIconName(
|
||||||
|
"mdi:arrow-top-right"),
|
||||||
size: entity.iconSize,
|
size: entity.iconSize,
|
||||||
),
|
),
|
||||||
onPressed: entity.canTiltBeOpened ? () =>_open(entity) : null
|
onPressed: entity.canTiltBeOpened ? () => _open(entity) : null));
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
buttons.add(Container(width: entity.iconSize+20.0,));
|
buttons.add(Container(
|
||||||
|
width: entity.iconSize + 20.0,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if (entity.supportStopTilt) {
|
if (entity.supportStopTilt) {
|
||||||
buttons.add(
|
buttons.add(IconButton(
|
||||||
IconButton(
|
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
MaterialDesignIcons.createIconDataFromIconName("mdi:stop"),
|
MaterialDesignIcons.createIconDataFromIconName("mdi:stop"),
|
||||||
size: entity.iconSize,
|
size: entity.iconSize,
|
||||||
),
|
),
|
||||||
onPressed: () => _stop(entity)
|
onPressed: () => _stop(entity)));
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
buttons.add(Container(width: entity.iconSize+20.0,));
|
buttons.add(Container(
|
||||||
|
width: entity.iconSize + 20.0,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if (entity.supportCloseTilt) {
|
if (entity.supportCloseTilt) {
|
||||||
buttons.add(
|
buttons.add(IconButton(
|
||||||
IconButton(
|
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
MaterialDesignIcons.createIconDataFromIconName("mdi:arrow-bottom-left"),
|
MaterialDesignIcons.createIconDataFromIconName(
|
||||||
|
"mdi:arrow-bottom-left"),
|
||||||
size: entity.iconSize,
|
size: entity.iconSize,
|
||||||
),
|
),
|
||||||
onPressed: entity.canTiltBeClosed ? () => _close(entity) : null
|
onPressed: entity.canTiltBeClosed ? () => _close(entity) : null));
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
buttons.add(Container(width: entity.iconSize+20.0,));
|
buttons.add(Container(
|
||||||
|
width: entity.iconSize + 20.0,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
children: buttons,
|
children: buttons,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Reference in New Issue
Block a user