connected all apis , create functionality is working

This commit is contained in:
ashrafzarkanisala
2024-06-27 02:35:50 +03:00
parent 9fe25b9bd3
commit 17881694e0
23 changed files with 793 additions and 154 deletions

View File

@ -3,21 +3,24 @@ import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/features/scene/model/create_scene_model.dart';
import 'package:syncrow_app/features/scene/model/scene_static_function.dart';
import 'package:syncrow_app/services/api/scene_api.dart';
part 'create_scene_event.dart';
part 'create_scene_state.dart';
class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState> {
CreateSceneBloc() : super(CreateSceneInitial()) {
on<CreateSceneEvent>((event, emit) => null as FutureOr<void>);
on<CreateSceneWithTasksEvent>(_createSceneWithTasks);
on<AddTaskEvent>(_onAddSceneTask);
on<SelectedValueEvent>(_selectedValue);
on<RemoveTaskEvent>(_removeTaskById);
on<ClearTaskListEvent>(_clearTaskList);
}
List<SceneStaticFunction> tasksList = [];
String selectedValue = '';
dynamic selectedValue;
FutureOr<void> _onAddSceneTask(
AddTaskEvent event, Emitter<CreateSceneState> emit) {
@ -58,4 +61,27 @@ class CreateSceneBloc extends Bloc<CreateSceneEvent, CreateSceneState> {
}
}
}
FutureOr<void> _createSceneWithTasks(
CreateSceneWithTasksEvent event, Emitter<CreateSceneState> emit) async {
emit(CreateSceneLoading());
try {
final response = await SceneApi.createScene(event.createSceneModel);
if (response['success'] == true) {
tasksList.clear();
emit(const CreateSceneWithTasks(success: true));
} else {
emit(const CreateSceneError(message: 'Something went wrong'));
}
} catch (e) {
emit(CreateSceneError(message: e.toString()));
emit(AddSceneTask(tasksList: tasksList));
}
}
FutureOr<void> _clearTaskList(
ClearTaskListEvent event, Emitter<CreateSceneState> emit) {
tasksList.clear();
emit(AddSceneTask(tasksList: tasksList));
}
}

View File

@ -28,12 +28,12 @@ class AddTaskEvent extends CreateSceneEvent {
}
class SelectedValueEvent extends CreateSceneEvent {
final String value;
final dynamic value;
const SelectedValueEvent({required this.value});
const SelectedValueEvent({this.value});
@override
List<Object> get props => [value];
List<Object> get props => [value!];
}
class RemoveTaskEvent extends CreateSceneEvent {
@ -44,3 +44,18 @@ class RemoveTaskEvent extends CreateSceneEvent {
@override
List<Object> get props => [taskId];
}
class CreateSceneWithTasksEvent extends CreateSceneEvent {
final CreateSceneModel createSceneModel;
const CreateSceneWithTasksEvent({required this.createSceneModel});
@override
List<Object> get props => [createSceneModel];
}
class ClearTaskListEvent extends CreateSceneEvent {
const ClearTaskListEvent();
@override
List<Object> get props => [];
}

View File

@ -11,6 +11,14 @@ final class CreateSceneInitial extends CreateSceneState {}
class CreateSceneLoading extends CreateSceneState {}
class CreateSceneError extends CreateSceneState {
final String message;
const CreateSceneError({required this.message});
@override
List<Object> get props => [message];
}
class AddSceneTask extends CreateSceneState {
final List<SceneStaticFunction> tasksList;
const AddSceneTask({required this.tasksList});
@ -20,9 +28,17 @@ class AddSceneTask extends CreateSceneState {
}
class SelectedTaskValueState extends CreateSceneState {
final String value;
final dynamic value;
const SelectedTaskValueState({required this.value});
@override
List<Object> get props => [value];
}
class CreateSceneWithTasks extends CreateSceneState {
final bool success;
const CreateSceneWithTasks({required this.success});
@override
List<Object> get props => [success];
}

View File

@ -1,28 +1,48 @@
import 'dart:async';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_event.dart';
import 'package:syncrow_app/features/scene/model/scene_model.dart';
import 'package:syncrow_app/services/api/scene_api.dart';
part 'scene_state.dart';
class SceneBloc extends Bloc<SceneEvent, SceneState> {
SceneBloc() : super(SceneInitial()) {
on<LoadScenes>(_onLoadScenes);
on<SceneTrigger>(_sceneTrigger);
}
void _onLoadScenes(LoadScenes event, Emitter<SceneState> emit) {
FutureOr<void> _onLoadScenes(
LoadScenes event, Emitter<SceneState> emit) async {
emit(SceneLoading());
try {
final scenes = _loadScenes();
final scenes = await SceneApi.getScenesByUnitId(event.unitId);
if (scenes.isNotEmpty) {
emit(SceneLoaded(scenes));
} catch (_) {
emit(SceneError());
} else {
emit(SceneTriggerError(message: 'Something went wrong'));
}
} catch (e) {
emit(SceneError(message: e.toString()));
}
}
List<SceneModel> _loadScenes() {
//TODO: Load scenes
return [];
FutureOr<void> _sceneTrigger(
SceneTrigger event, Emitter<SceneState> emit) async {
emit(SceneTriggerLoading());
try {
final success = await SceneApi.triggerScene(event.sceneId);
if (success) {
emit(SceneTriggerState(success: true));
} else {
emit(SceneTriggerError(message: 'Something went wrong'));
}
} catch (e) {
emit(SceneTriggerError(message: e.toString()));
}
}
}

View File

@ -1,4 +1,3 @@
import 'package:equatable/equatable.dart';
abstract class SceneEvent extends Equatable {
@ -6,4 +5,20 @@ abstract class SceneEvent extends Equatable {
List<Object> get props => [];
}
class LoadScenes extends SceneEvent {}
class LoadScenes extends SceneEvent {
final String unitId;
LoadScenes(this.unitId);
@override
List<Object> get props => [unitId];
}
class SceneTrigger extends SceneEvent {
final String sceneId;
SceneTrigger(this.sceneId);
@override
List<Object> get props => [sceneId];
}

View File

@ -18,4 +18,31 @@ class SceneLoaded extends SceneState {
List<Object> get props => [scenes];
}
class SceneError extends SceneState {}
class SceneError extends SceneState {
final String message;
SceneError({required this.message});
@override
List<Object> get props => [message];
}
class SceneTriggerState extends SceneState {
final bool success;
SceneTriggerState({required this.success});
@override
List<Object> get props => [success];
}
class SceneTriggerLoading extends SceneState {}
class SceneTriggerError extends SceneState {
final String message;
SceneTriggerError({required this.message});
@override
List<Object> get props => [message];
}

View File

@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
import 'package:syncrow_app/features/scene/model/create_scene_model.dart';
import 'package:syncrow_app/features/scene/model/scene_static_function.dart';
import 'package:syncrow_app/utils/context_extension.dart';
mixin SceneLogicHelper {
bool isOnlyDelayOrDelayLast(List<SceneStaticFunction> tasks) {
final lastTask = tasks.last;
return tasks.every((task) => task.code == 'delay') ||
lastTask.code == 'delay';
}
void handleSaveButtonPress(
BuildContext context,
TextEditingController sceneNameController,
List<SceneStaticFunction> tasks,
) {
if (isOnlyDelayOrDelayLast(tasks)) {
// Show snackbar indicating restriction
context.showCustomSnackbar(
message: 'Only a single delay or delay-last operations are allowed.',
icon: const Icon(
Icons.error,
color: Colors.red,
),
);
} else {
final createSceneModel = CreateSceneModel(
unitUuid: HomeCubit.getInstance().selectedSpace!.id ?? '',
sceneName: sceneNameController.text,
decisionExpr: 'and',
actions: List.generate(
tasks.length,
(index) {
final task = tasks[index];
if (task.code == 'delay') {
return CreateSceneAction(
entityId: tasks[index].deviceId,
actionExecutor: 'delay',
executorProperty: CreateSceneExecutorProperty(
functionCode: task.code,
functionValue: task.operationalValues.first.value,
delaySeconds: 0,
),
);
}
return CreateSceneAction(
entityId: task.deviceId,
actionExecutor: 'device_issue',
executorProperty: CreateSceneExecutorProperty(
functionCode: task.code,
functionValue: task.operationalValues.first.value,
delaySeconds: 0,
),
);
},
),
);
context
.read<CreateSceneBloc>()
.add(CreateSceneWithTasksEvent(createSceneModel: createSceneModel));
Navigator.pop(context);
}
}
}

View File

@ -4,7 +4,7 @@ import 'package:syncrow_app/features/scene/model/scene_static_function.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart';
mixin SceneHelper {
mixin SceneOperationsDataHelper {
List<SceneStaticFunction> getFunctionsWithIcons({
DeviceType? type,
required List<FunctionModel> functions,
@ -79,25 +79,25 @@ mixin SceneHelper {
code: 'sensitivity',
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "1"),
icon: Assets.assetsSensitivityOperationIcon, value: 1),
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "2"),
icon: Assets.assetsSensitivityOperationIcon, value: 2),
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "3"),
icon: Assets.assetsSensitivityOperationIcon, value: 3),
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "4"),
icon: Assets.assetsSensitivityOperationIcon, value: 4),
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "5"),
icon: Assets.assetsSensitivityOperationIcon, value: 5),
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "6"),
icon: Assets.assetsSensitivityOperationIcon, value: 6),
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "7"),
icon: Assets.assetsSensitivityOperationIcon, value: 7),
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "8"),
icon: Assets.assetsSensitivityOperationIcon, value: 8),
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "9"),
icon: Assets.assetsSensitivityOperationIcon, value: 9),
SceneOperationalValue(
icon: Assets.assetsSensitivityOperationIcon, value: "10"),
icon: Assets.assetsSensitivityOperationIcon, value: 10),
],
),
];
@ -151,10 +151,15 @@ List<SceneStaticFunction> threeGangFunctions(
operationName: 'Light 1 Switch',
code: 'switch_1',
operationalValues: [
SceneOperationalValue(icon: Assets.assetsAcPower, value: "ON"),
SceneOperationalValue(icon: Assets.assetsAcPowerOFF, value: "OFF"),
SceneOperationalValue(
icon: Assets.assetsSceneRefresh, value: "Reverse Switch"),
icon: Assets.assetsAcPower, description: "ON", value: true),
SceneOperationalValue(
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
SceneOperationalValue(
icon: Assets.assetsSceneRefresh,
description: "Reverse Switch",
value: false,
),
],
),
SceneStaticFunction(
@ -164,10 +169,15 @@ List<SceneStaticFunction> threeGangFunctions(
operationName: 'Light 2 Switch',
code: 'switch_2',
operationalValues: [
SceneOperationalValue(icon: Assets.assetsAcPower, value: "ON"),
SceneOperationalValue(icon: Assets.assetsAcPowerOFF, value: "OFF"),
SceneOperationalValue(
icon: Assets.assetsSceneRefresh, value: "Reverse Switch"),
icon: Assets.assetsAcPower, description: "ON", value: true),
SceneOperationalValue(
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
SceneOperationalValue(
icon: Assets.assetsSceneRefresh,
description: "Reverse Switch",
value: false,
),
],
),
SceneStaticFunction(
@ -177,10 +187,15 @@ List<SceneStaticFunction> threeGangFunctions(
operationName: 'Light 3 Switch',
code: 'switch_3',
operationalValues: [
SceneOperationalValue(icon: Assets.assetsAcPower, value: "ON"),
SceneOperationalValue(icon: Assets.assetsAcPowerOFF, value: "OFF"),
SceneOperationalValue(
icon: Assets.assetsSceneRefresh, value: "Reverse Switch"),
icon: Assets.assetsAcPower, description: "ON", value: true),
SceneOperationalValue(
icon: Assets.assetsAcPowerOFF, description: "OFF", value: false),
SceneOperationalValue(
icon: Assets.assetsSceneRefresh,
description: "Reverse Switch",
value: false,
),
],
),
SceneStaticFunction(
@ -190,7 +205,7 @@ List<SceneStaticFunction> threeGangFunctions(
operationName: 'Light 1 CountDown',
code: 'countdown_1',
operationalValues: [
SceneOperationalValue(icon: '', value: "0"),
SceneOperationalValue(icon: '', value: 0),
],
),
SceneStaticFunction(
@ -200,7 +215,7 @@ List<SceneStaticFunction> threeGangFunctions(
operationName: 'Light 2 CountDown',
code: 'countdown_1',
operationalValues: [
SceneOperationalValue(icon: '', value: "0"),
SceneOperationalValue(icon: '', value: 0),
],
),
SceneStaticFunction(
@ -210,7 +225,7 @@ List<SceneStaticFunction> threeGangFunctions(
operationName: 'Light 3 CountDown',
code: 'countdown_1',
operationalValues: [
SceneOperationalValue(icon: '', value: "0"),
SceneOperationalValue(icon: '', value: 0),
],
),
];
@ -229,8 +244,16 @@ List<SceneStaticFunction> acFunctions(
operationName: 'Power',
code: 'switch',
operationalValues: [
SceneOperationalValue(icon: Assets.assetsAcPower, value: "ON"),
SceneOperationalValue(icon: Assets.assetsAcPowerOFF, value: "OFF"),
SceneOperationalValue(
icon: Assets.assetsAcPower,
description: "ON",
value: true,
),
SceneOperationalValue(
icon: Assets.assetsAcPowerOFF,
description: "OFF",
value: false,
),
],
),
SceneStaticFunction(
@ -242,15 +265,18 @@ List<SceneStaticFunction> acFunctions(
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsAcCooling,
value: AcValuesEnums.Cooling.name,
description: AcValuesEnums.Cooling.name,
value: TempModes.cold.name,
),
SceneOperationalValue(
icon: Assets.assetsAcHeating,
value: AcValuesEnums.Heating.name,
description: AcValuesEnums.Heating.name,
value: TempModes.hot.name,
),
SceneOperationalValue(
icon: Assets.assetsFanSpeed,
value: AcValuesEnums.Ventilation.name,
description: AcValuesEnums.Ventilation.name,
value: TempModes.wind.name,
),
],
),
@ -262,7 +288,10 @@ List<SceneStaticFunction> acFunctions(
code: 'temp_set',
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsCelsiusDegrees, value: "COOL TO"),
icon: Assets.assetsCelsiusDegrees,
value: 0,
description: 'COOL TO',
),
],
),
SceneStaticFunction(
@ -274,19 +303,23 @@ List<SceneStaticFunction> acFunctions(
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsAcFanLow,
value: ValueACRange.LOW.name,
description: ValueACRange.LOW.name,
value: FanSpeeds.low.name,
),
SceneOperationalValue(
icon: Assets.assetsAcFanMiddle,
value: ValueACRange.MIDDLE.name,
description: ValueACRange.MIDDLE.name,
value: FanSpeeds.middle.name,
),
SceneOperationalValue(
icon: Assets.assetsAcFanHigh,
value: ValueACRange.HIGH.name,
description: ValueACRange.HIGH.name,
value: FanSpeeds.high.name,
),
SceneOperationalValue(
icon: Assets.assetsAcFanAuto,
value: ValueACRange.AUTO.name,
description: ValueACRange.AUTO.name,
value: FanSpeeds.auto.name,
),
],
),
@ -299,11 +332,13 @@ List<SceneStaticFunction> acFunctions(
operationalValues: [
SceneOperationalValue(
icon: Assets.assetsSceneChildLock,
value: 'Lock',
description: 'Lock',
value: true,
),
SceneOperationalValue(
icon: Assets.assetsSceneChildUnlock,
value: 'Unlock',
description: 'Unlock',
value: false,
),
],
),

View File

@ -0,0 +1,222 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
class CreateSceneModel {
/*
{
"unitUuid": "string",
"sceneName": "string",
"decisionExpr": "string",
"actions": [
{
"entityId": "string",
"actionExecutor": "string",
"executorProperty": {
"functionCode": "string",
"functionValue": {},
"delaySeconds": 0
}
}
]
}
*/
String unitUuid;
String sceneName;
String decisionExpr;
List<CreateSceneAction> actions;
CreateSceneModel({
required this.unitUuid,
required this.sceneName,
required this.decisionExpr,
required this.actions,
});
CreateSceneModel copyWith({
String? unitUuid,
String? sceneName,
String? decisionExpr,
List<CreateSceneAction>? actions,
}) {
return CreateSceneModel(
unitUuid: unitUuid ?? this.unitUuid,
sceneName: sceneName ?? this.sceneName,
decisionExpr: decisionExpr ?? this.decisionExpr,
actions: actions ?? this.actions,
);
}
Map<String, dynamic> toMap() {
return {
'unitUuid': unitUuid,
'sceneName': sceneName,
'decisionExpr': decisionExpr,
'actions': actions.map((x) => x.toMap()).toList(),
};
}
factory CreateSceneModel.fromMap(Map<String, dynamic> map) {
return CreateSceneModel(
unitUuid: map['unitUuid'] ?? '',
sceneName: map['sceneName'] ?? '',
decisionExpr: map['decisionExpr'] ?? '',
actions: List<CreateSceneAction>.from(
map['actions']?.map((x) => CreateSceneAction.fromMap(x))),
);
}
String toJson() => json.encode(toMap());
factory CreateSceneModel.fromJson(String source) =>
CreateSceneModel.fromMap(json.decode(source));
@override
String toString() {
return 'CreateSceneModel(unitUuid: $unitUuid, sceneName: $sceneName, decisionExpr: $decisionExpr, actions: $actions)';
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is CreateSceneModel &&
other.unitUuid == unitUuid &&
other.sceneName == sceneName &&
other.decisionExpr == decisionExpr &&
listEquals(other.actions, actions);
}
@override
int get hashCode {
return unitUuid.hashCode ^
sceneName.hashCode ^
decisionExpr.hashCode ^
actions.hashCode;
}
}
class CreateSceneAction {
String entityId;
String actionExecutor;
CreateSceneExecutorProperty executorProperty;
CreateSceneAction({
required this.entityId,
required this.actionExecutor,
required this.executorProperty,
});
CreateSceneAction copyWith({
String? entityId,
String? actionExecutor,
CreateSceneExecutorProperty? executorProperty,
}) {
return CreateSceneAction(
entityId: entityId ?? this.entityId,
actionExecutor: actionExecutor ?? this.actionExecutor,
executorProperty: executorProperty ?? this.executorProperty,
);
}
Map<String, dynamic> toMap() {
return {
'entityId': entityId,
'actionExecutor': actionExecutor,
'executorProperty': executorProperty.toMap(),
};
}
factory CreateSceneAction.fromMap(Map<String, dynamic> map) {
return CreateSceneAction(
entityId: map['entityId'] ?? '',
actionExecutor: map['actionExecutor'] ?? '',
executorProperty:
CreateSceneExecutorProperty.fromMap(map['executorProperty']),
);
}
String toJson() => json.encode(toMap());
factory CreateSceneAction.fromJson(String source) =>
CreateSceneAction.fromMap(json.decode(source));
@override
String toString() =>
'CreateSceneAction(entityId: $entityId, actionExecutor: $actionExecutor, executorProperty: $executorProperty)';
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is CreateSceneAction &&
other.entityId == entityId &&
other.actionExecutor == actionExecutor &&
other.executorProperty == executorProperty;
}
@override
int get hashCode =>
entityId.hashCode ^ actionExecutor.hashCode ^ executorProperty.hashCode;
}
class CreateSceneExecutorProperty {
String functionCode;
dynamic functionValue;
int delaySeconds;
CreateSceneExecutorProperty({
required this.functionCode,
required this.functionValue,
required this.delaySeconds,
});
CreateSceneExecutorProperty copyWith({
String? functionCode,
dynamic functionValue,
int? delaySeconds,
}) {
return CreateSceneExecutorProperty(
functionCode: functionCode ?? this.functionCode,
functionValue: functionValue ?? this.functionValue,
delaySeconds: delaySeconds ?? this.delaySeconds,
);
}
Map<String, dynamic> toMap() {
return {
'functionCode': functionCode,
'functionValue': functionValue,
'delaySeconds': delaySeconds,
};
}
factory CreateSceneExecutorProperty.fromMap(Map<String, dynamic> map) {
return CreateSceneExecutorProperty(
functionCode: map['functionCode'] ?? '',
functionValue: map['functionValue'] ?? '',
delaySeconds: map['delaySeconds']?.toInt() ?? 0,
);
}
String toJson() => json.encode(toMap());
factory CreateSceneExecutorProperty.fromJson(String source) =>
CreateSceneExecutorProperty.fromMap(json.decode(source));
@override
String toString() =>
'CreateSceneExecutorProperty(functionCode: $functionCode, functionValue: $functionValue, delaySeconds: $delaySeconds)';
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is CreateSceneExecutorProperty &&
other.functionCode == functionCode &&
other.functionValue == functionValue &&
other.delaySeconds == delaySeconds;
}
@override
int get hashCode =>
functionCode.hashCode ^ functionValue.hashCode ^ delaySeconds.hashCode;
}

View File

@ -1,46 +1,54 @@
import 'dart:convert';
class SceneModel {
final String id;
final String name;
final String description;
final String imageUrl;
final String location;
final String type;
final String rating;
final String price;
final String duration;
final String date;
final String time;
final String status;
final Status status;
final Type type;
SceneModel({
required this.id,
required this.name,
required this.description,
required this.imageUrl,
required this.location,
required this.type,
required this.rating,
required this.price,
required this.duration,
required this.date,
required this.time,
required this.status,
required this.type,
});
factory SceneModel.fromJson(Map<String, dynamic> json) {
return SceneModel(
id: json['id'],
name: json['name'],
description: json['description'],
imageUrl: json['imageUrl'],
location: json['location'],
type: json['type'],
rating: json['rating'],
price: json['price'],
duration: json['duration'],
date: json['date'],
time: json['time'],
status: json['status'],
factory SceneModel.fromRawJson(String str) =>
SceneModel.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory SceneModel.fromJson(Map<String, dynamic> json) => SceneModel(
id: json["id"],
name: json["name"],
status: statusValues.map[json["status"]]!,
type: typeValues.map[json["type"]]!,
);
Map<String, dynamic> toJson() => {
"id": id,
"name": name,
"status": statusValues.reverse[status],
"type": typeValues.reverse[type],
};
}
enum Status { ENABLE }
final statusValues = EnumValues({"enable": Status.ENABLE});
enum Type { TAP_TO_RUN }
final typeValues = EnumValues({"tap_to_run": Type.TAP_TO_RUN});
class EnumValues<T> {
Map<String, T> map;
late Map<T, String> reverseMap;
EnumValues(this.map);
Map<T, String> get reverse {
reverseMap = map.map((k, v) => MapEntry(v, k));
return reverseMap;
}
}

View File

@ -99,34 +99,36 @@ class SceneStaticFunction {
class SceneOperationalValue {
final String icon;
final String value;
final dynamic value;
final String? description;
SceneOperationalValue({
required this.icon,
required this.value,
this.description,
});
SceneOperationalValue copyWith({
String? icon,
String? value,
dynamic value,
String? description,
}) {
return SceneOperationalValue(
icon: icon ?? this.icon,
value: value ?? this.value,
description: description ?? this.description,
);
}
Map<String, dynamic> toMap() {
return {
'icon': icon,
'value': value,
};
return {'icon': icon, 'value': value, 'description': description};
}
factory SceneOperationalValue.fromMap(Map<String, dynamic> map) {
return SceneOperationalValue(
icon: map['icon'] ?? '',
value: map['value'] ?? '',
value: map['value'],
description: map['description'],
);
}
@ -137,7 +139,7 @@ class SceneOperationalValue {
@override
String toString() =>
'StaticFunctionOperationHelper(icon: $icon, value: $value)';
'StaticFunctionOperationHelper(icon: $icon, value: $value, description: $description)';
@override
bool operator ==(Object other) {
@ -145,9 +147,10 @@ class SceneOperationalValue {
return other is SceneOperationalValue &&
other.icon == icon &&
other.description == description &&
other.value == value;
}
@override
int get hashCode => icon.hashCode ^ value.hashCode;
int get hashCode => icon.hashCode ^ value.hashCode ^ description.hashCode;
}

View File

@ -1,16 +0,0 @@
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/services/api/devices_api.dart';
class SceneRepo {
Future<bool> deviceControl({
required DeviceControlModel controlModel,
required String deviceId,
}) async {
final response = await DevicesAPI.controlDevice(controlModel, deviceId);
if (response['success'] == true) {
return true;
} else {
return false;
}
}
}

View File

@ -35,7 +35,7 @@ class CreateSceneView extends StatelessWidget {
Routes.sceneTasksRoute,
arguments: CreateSceneEnum.tabToRun,
);
context.read<CreateSceneBloc>().tasksList.clear();
context.read<CreateSceneBloc>().add(ClearTaskListEvent());
},
),
DefaultContainer(
@ -53,3 +53,5 @@ class CreateSceneView extends StatelessWidget {
);
}
}

View File

@ -3,11 +3,11 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
import 'package:syncrow_app/features/scene/helper/scene_helper.dart';
import 'package:syncrow_app/features/scene/helper/scene_operations_data_helper.dart';
import 'package:syncrow_app/features/scene/model/scene_static_function.dart';
import 'package:syncrow_app/features/scene/widgets/alert_dialog_countdown.dart';
import 'package:syncrow_app/features/scene/widgets/alert_dialog_functions_body.dart';
import 'package:syncrow_app/features/scene/widgets/alert_dialog_temperature_body.dart';
import 'package:syncrow_app/features/scene/widgets/alert_dialogs/alert_dialog_countdown.dart';
import 'package:syncrow_app/features/scene/widgets/alert_dialogs/alert_dialog_functions_body.dart';
import 'package:syncrow_app/features/scene/widgets/alert_dialogs/alert_dialog_temperature_body.dart';
import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
@ -18,12 +18,13 @@ import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class DeviceFunctionsView extends StatelessWidget with SceneHelper {
class DeviceFunctionsView extends StatelessWidget
with SceneOperationsDataHelper {
const DeviceFunctionsView({super.key});
@override
Widget build(BuildContext context) {
/// this whole widget i need to revamp it later
/// this whole widget needs a refactor later
///
/// static functions based on type
final device = ModalRoute.of(context)?.settings.arguments as DeviceModel;

View File

@ -1,10 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/scene/widgets/create_scene_save_button.dart';
import 'package:syncrow_app/features/scene/widgets/if_then_containers/if_container.dart';
import 'package:syncrow_app/features/scene/widgets/if_then_containers/then_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_button.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
@ -60,18 +59,7 @@ class SceneTasksView extends StatelessWidget {
left: 40,
child: SizedBox(
width: context.width * 0.8,
child: DefaultButton(
onPressed: () {},
customButtonStyle: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(
ColorsManager.primaryColorWithOpacity,
),
),
child: BodyLarge(
text: 'Save',
style: context.bodyLarge.copyWith(color: Colors.white),
),
),
child: const CreateSceneSaveButton(),
),
)
],

View File

@ -48,8 +48,10 @@ class SceneView extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Image.asset(
height: 50,
@ -85,8 +87,10 @@ class SceneView extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Image.asset(
height: 50,

View File

@ -6,7 +6,7 @@ import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.d
class AlertDialogCountdown extends StatefulWidget {
const AlertDialogCountdown({super.key, required this.durationValue});
final String durationValue;
final int durationValue;
@override
State<AlertDialogCountdown> createState() => _AlertDialogCountdownState();
@ -16,7 +16,7 @@ class _AlertDialogCountdownState extends State<AlertDialogCountdown> {
int durationInSeconds = 0;
// Convert seconds to Duration.
Duration get duration =>
Duration(seconds: int.tryParse(widget.durationValue) ?? 0);
Duration(seconds: widget.durationValue) ;
@override
Widget build(BuildContext context) {
return Container(
@ -32,7 +32,7 @@ class _AlertDialogCountdownState extends State<AlertDialogCountdown> {
});
context
.read<CreateSceneBloc>()
.add(SelectedValueEvent(value: newDuration.inSeconds.toString()));
.add(SelectedValueEvent(value: newDuration.inSeconds));
},
),
);

View File

@ -21,7 +21,7 @@ class AlertDialogFunctionsOperationsBody extends StatefulWidget {
class _AlertDialogFunctionsOperationsBodyState
extends State<AlertDialogFunctionsOperationsBody> {
String? groupValue = "";
dynamic groupValue;
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
@ -36,7 +36,7 @@ class _AlertDialogFunctionsOperationsBodyState
minLeadingWidth: 15,
padding: const EdgeInsets.symmetric(horizontal: 16),
assetPath: operation.icon,
titleString: operation.value,
titleString: operation.description.toString(),
textAlign: TextAlign.start,
trailingWidget: Radio(
value: operation.value,
@ -58,7 +58,7 @@ class _AlertDialogFunctionsOperationsBodyState
});
context
.read<CreateSceneBloc>()
.add(SelectedValueEvent(value: groupValue!));
.add(SelectedValueEvent(value: groupValue));
},
);
},

View File

@ -40,7 +40,7 @@ class _AlertDialogTemperatureBodyState
});
context
.read<CreateSceneBloc>()
.add(SelectedValueEvent(value: temperature.toString()));
.add(SelectedValueEvent(value: temperature * 10));
},
icon: const Icon(
Icons.remove,
@ -70,7 +70,8 @@ class _AlertDialogTemperatureBodyState
],
),
subtitle: BodyLarge(
text: widget.functions[widget.index].operationalValues[0].value,
text: widget.functions[widget.index].operationalValues[0].description
.toString(),
textAlign: TextAlign.center,
),
trailing: IconButton(
@ -82,7 +83,7 @@ class _AlertDialogTemperatureBodyState
});
context
.read<CreateSceneBloc>()
.add(SelectedValueEvent(value: temperature.toString()));
.add(SelectedValueEvent(value: temperature * 10));
},
icon: const Icon(
Icons.add,

View File

@ -0,0 +1,106 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
import 'package:syncrow_app/features/scene/helper/scene_logic_helper.dart';
import 'package:syncrow_app/features/shared_widgets/default_button.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class CreateSceneSaveButton extends StatefulWidget {
const CreateSceneSaveButton({
super.key,
});
@override
State<CreateSceneSaveButton> createState() => _CreateSceneSaveButtonState();
}
class _CreateSceneSaveButtonState extends State<CreateSceneSaveButton>
with SceneLogicHelper {
late TextEditingController sceneNameController;
@override
void initState() {
sceneNameController = TextEditingController();
super.initState();
}
@override
void dispose() {
sceneNameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return BlocConsumer<CreateSceneBloc, CreateSceneState>(
listener: (context, state) {
if (state is CreateSceneWithTasks) {
if (state.success == true) {
context.showCustomSnackbar(
message: 'Scene created successfully',
icon: const Icon(
Icons.check,
color: Colors.green,
),
);
sceneNameController.text = '';
}
} else if (state is CreateSceneError) {
context.showCustomSnackbar(
message: state.message,
icon: const Icon(
Icons.error,
color: Colors.red,
),
);
}
},
builder: (context, state) {
return DefaultButton(
onPressed: () {
context.customAlertDialog(
alertBody: Padding(
padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8),
child: SizedBox(
height: 40,
child: SearchBar(
controller: sceneNameController,
elevation: WidgetStateProperty.all(0),
textStyle: WidgetStateProperty.all(context.bodyMedium),
hintStyle: WidgetStateProperty.all(
context.bodyMedium.copyWith(
fontSize: 14,
color: ColorsManager.secondaryTextColor),
),
hintText: 'Enter scene name',
backgroundColor:
WidgetStateProperty.all(ColorsManager.backgroundColor),
),
),
),
title: 'Scene Name',
onConfirm: () {
if (sceneNameController.text.isNotEmpty) {
final tasks = context.read<CreateSceneBloc>().tasksList;
handleSaveButtonPress(context, sceneNameController, tasks);
}
},
);
},
customButtonStyle: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>(
ColorsManager.primaryColorWithOpacity,
),
),
isLoading: state is CreateSceneLoading,
child: BodyLarge(
text: 'Save',
style: context.bodyLarge.copyWith(color: Colors.white),
),
);
},
);
}
}

View File

@ -9,7 +9,8 @@ abstract class ApiEndpoints {
static const String deleteUser = '$baseUrl/authentication/user/delete/{id}';
static const String sendOtp = '$baseUrl/authentication/user/send-otp';
static const String verifyOtp = '$baseUrl/authentication/user/verify-otp';
static const String forgetPassword = '$baseUrl/authentication/user/forget-password';
static const String forgetPassword =
'$baseUrl/authentication/user/forget-password';
////////////////////////////////////// Spaces ///////////////////////////////////////
@ -19,10 +20,12 @@ abstract class ApiEndpoints {
static const String addCommunityToUser = '$baseUrl/community/user';
//GET
static const String communityByUuid = '$baseUrl/community/{communityUuid}';
static const String communityChild = '$baseUrl/community/child/{communityUuid}';
static const String communityChild =
'$baseUrl/community/child/{communityUuid}';
static const String communityUser = '$baseUrl/community/user/{userUuid}';
//PUT
static const String renameCommunity = '$baseUrl/community/rename/{communityUuid}';
static const String renameCommunity =
'$baseUrl/community/rename/{communityUuid}';
///Building Module
//POST
@ -31,10 +34,12 @@ abstract class ApiEndpoints {
//GET
static const String buildingByUuid = '$baseUrl/building/{buildingUuid}';
static const String buildingChild = '$baseUrl/building/child/{buildingUuid}';
static const String buildingParent = '$baseUrl/building/parent/{buildingUuid}';
static const String buildingParent =
'$baseUrl/building/parent/{buildingUuid}';
static const String buildingUser = '$baseUrl/building/user/{userUuid}';
//PUT
static const String renameBuilding = '$baseUrl/building/rename/{buildingUuid}';
static const String renameBuilding =
'$baseUrl/building/rename/{buildingUuid}';
///Floor Module
//POST
@ -57,7 +62,8 @@ abstract class ApiEndpoints {
static const String unitChild = '$baseUrl/unit/child/';
static const String unitParent = '$baseUrl/unit/parent/{unitUuid}';
static const String unitUser = '$baseUrl/unit/user/';
static const String invitationCode = '$baseUrl/unit/{unitUuid}/invitation-code';
static const String invitationCode =
'$baseUrl/unit/{unitUuid}/invitation-code';
static const String verifyInvitationCode = '$baseUrl/unit/user/verify-code';
//PUT
@ -95,8 +101,10 @@ abstract class ApiEndpoints {
//GET
static const String deviceByRoom = '$baseUrl/device/room';
static const String deviceByUuid = '$baseUrl/device/{deviceUuid}';
static const String deviceFunctions = '$baseUrl/device/{deviceUuid}/functions';
static const String deviceFunctionsStatus = '$baseUrl/device/{deviceUuid}/functions/status';
static const String deviceFunctions =
'$baseUrl/device/{deviceUuid}/functions';
static const String deviceFunctionsStatus =
'$baseUrl/device/{deviceUuid}/functions/status';
///Device Permission Module
//POST
@ -104,7 +112,26 @@ abstract class ApiEndpoints {
//GET
static const String devicePermissionList = '$baseUrl/device-permission/list';
//PUT
static const String editDevicePermission = '$baseUrl/device-permission/edit/{userId}';
static const String editDevicePermission =
'$baseUrl/device-permission/edit/{userId}';
static const String assignDeviceToRoom = '$baseUrl/device/room';
/// Scene API ////////////////////
/// POST
static const String createScene = '$baseUrl/scene/tap-to-run';
static const String triggerScene =
'$baseUrl/scene/tap-to-run/trigger/{sceneId}';
/// GET
static const String getUnitScenes = '$baseUrl/scene/tap-to-run/{unitUuid}';
static const String getScene = '$baseUrl/scene/tap-to-run/details/{sceneId}';
/// PUT
static const String updateScene = '$baseUrl/scene/{sceneId}';
/// DELETE
static const String deleteScene =
'$baseUrl/scene/tap-to-run/{sceneUuid}/{sceneId}';
}

View File

@ -0,0 +1,57 @@
import 'package:syncrow_app/features/scene/model/create_scene_model.dart';
import 'package:syncrow_app/features/scene/model/scene_model.dart';
import 'package:syncrow_app/services/api/api_links_endpoints.dart';
import 'package:syncrow_app/services/api/http_service.dart';
class SceneApi {
static final HTTPService _httpService = HTTPService();
static Future<Map<String, dynamic>> createScene(
CreateSceneModel createSceneModel) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.createScene,
body: createSceneModel.toJson(),
showServerMessage: false,
expectedResponseModel: (json) {
return json;
},
);
return response;
} catch (e) {
rethrow;
}
}
static Future<List<SceneModel>> getScenesByUnitId(String unitId) async {
try {
final response = await _httpService.get(
path: ApiEndpoints.getUnitScenes.replaceAll('{unitUuid}', unitId),
showServerMessage: false,
expectedResponseModel: (json) {
List<SceneModel> scenes = [];
for (var scene in json) {
scenes.add(SceneModel.fromJson(scene));
}
return scenes;
},
);
return response;
} catch (e) {
rethrow;
}
}
static Future<bool> triggerScene(String sceneId) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.triggerScene.replaceAll('{sceneUuid}', sceneId),
showServerMessage: false,
expectedResponseModel: (json) => json['success'],
);
return response;
} catch (e) {
rethrow;
}
}
}

View File

@ -47,6 +47,19 @@ extension ContextExtension on BuildContext {
);
}
void showCustomSnackbar({required String message, Widget? icon}) {
ScaffoldMessenger.of(this).showSnackBar(
SnackBar(
content: Row(
children: [
Expanded(child: Text(message)),
icon ?? const SizedBox.shrink(),
],
),
),
);
}
void customAlertDialog(
{required Widget alertBody,
required String title,