feat(alarm): changed alarm endpoint and modified models, entities and presentation
This commit is contained in:
@@ -1,13 +1,9 @@
|
||||
import 'package:settings/src/core/data/models/create_alarm_request_model.dart';
|
||||
import 'package:settings/src/core/data/models/get_alarms_response_model.dart';
|
||||
import 'package:settings/src/core/data/models/update_alarm_request_model.dart';
|
||||
import 'package:settings/src/features/alarm/domain/entities/alarm_entity.dart';
|
||||
|
||||
abstract class AlarmRemoteDatasource {
|
||||
Future<GetAlarmsResponseModel> getAlarms({required String deviceId});
|
||||
Future<void> createAlarm({required CreateAlarmRequestModel request});
|
||||
Future<void> updateAlarm({
|
||||
required String alarmId,
|
||||
required UpdateAlarmRequestModel request,
|
||||
Future<List<AlarmEntity>> getAlarms({required String deviceId});
|
||||
Future<List<AlarmEntity>> upsertAlarms({
|
||||
required String deviceId,
|
||||
required List<AlarmEntity> alarms,
|
||||
});
|
||||
Future<void> deleteAlarm({required String alarmId});
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:settings/src/core/data/datasources/alarm_remote_datasource.dart';
|
||||
import 'package:settings/src/core/data/models/create_alarm_request_model.dart';
|
||||
import 'package:settings/src/core/data/models/get_alarms_response_model.dart';
|
||||
import 'package:settings/src/core/data/models/update_alarm_request_model.dart';
|
||||
import 'package:settings/src/features/alarm/domain/entities/alarm_entity.dart';
|
||||
import 'package:sf_infrastructure/sf_infrastructure.dart';
|
||||
|
||||
class AlarmRemoteDatasourceImpl implements AlarmRemoteDatasource {
|
||||
@@ -12,55 +11,48 @@ class AlarmRemoteDatasourceImpl implements AlarmRemoteDatasource {
|
||||
final SaveFamilyRepository _repository;
|
||||
|
||||
@override
|
||||
Future<GetAlarmsResponseModel> getAlarms({required String deviceId}) async {
|
||||
Future<List<AlarmEntity>> getAlarms({required String deviceId}) async {
|
||||
try {
|
||||
final response = await _repository.get<Map<String, dynamic>>(
|
||||
'/devices/$deviceId/takepills-reminders',
|
||||
'/devices/$deviceId/alarms',
|
||||
);
|
||||
|
||||
final data = response.data;
|
||||
if (data == null || data.isEmpty) {
|
||||
throw Exception(
|
||||
'Empty response from /devices/$deviceId/takepills-reminders',
|
||||
);
|
||||
}
|
||||
|
||||
return GetAlarmsResponseModel.fromJson(data);
|
||||
if (data == null || data.isEmpty) return [];
|
||||
final model = GetAlarmsResponseModel.fromJson(data);
|
||||
return model.item.toEntities();
|
||||
} on DioException catch (error) {
|
||||
if (error.response?.statusCode == 404) return [];
|
||||
throw mapDioError(error, defaultMessage: 'Error getting alarms');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> createAlarm({required CreateAlarmRequestModel request}) async {
|
||||
await safeCall(
|
||||
() => _repository.post<dynamic>(
|
||||
'/takepills-reminders',
|
||||
body: request.toJson(),
|
||||
),
|
||||
'Error creating alarm',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateAlarm({
|
||||
required String alarmId,
|
||||
required UpdateAlarmRequestModel request,
|
||||
Future<List<AlarmEntity>> upsertAlarms({
|
||||
required String deviceId,
|
||||
required List<AlarmEntity> alarms,
|
||||
}) async {
|
||||
await safeCall(
|
||||
() => _repository.put<dynamic>(
|
||||
'/takepills-reminders/$alarmId',
|
||||
body: request.toJson(),
|
||||
),
|
||||
'Error updating alarm',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteAlarm({required String alarmId}) async {
|
||||
await safeCall(
|
||||
() => _repository.delete<dynamic>('/takepills-reminders/$alarmId'),
|
||||
'Error deleting alarm',
|
||||
);
|
||||
try {
|
||||
final body = {
|
||||
'alarmList': alarms
|
||||
.map((alarm) => {
|
||||
'time': alarm.time,
|
||||
'frequency': alarm.frequency.name,
|
||||
if (alarm.frequency == AlarmFrequency.custom &&
|
||||
alarm.week != null)
|
||||
'week': alarm.week,
|
||||
})
|
||||
.toList(),
|
||||
};
|
||||
final response = await _repository.put<Map<String, dynamic>>(
|
||||
'/devices/$deviceId/alarms',
|
||||
body: body,
|
||||
);
|
||||
final data = response.data;
|
||||
if (data == null || data.isEmpty) return alarms;
|
||||
final model = UpsertAlarmsResponseModel.fromJson(data);
|
||||
return model.item.toEntities();
|
||||
} on DioException catch (error) {
|
||||
throw mapDioError(error, defaultMessage: 'Error saving alarms');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'create_alarm_request_model.freezed.dart';
|
||||
part 'create_alarm_request_model.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class CreateAlarmRequestModel with _$CreateAlarmRequestModel {
|
||||
const factory CreateAlarmRequestModel({
|
||||
required String id,
|
||||
required String deviceId,
|
||||
required String time,
|
||||
required String message,
|
||||
required int order,
|
||||
}) = _CreateAlarmRequestModel;
|
||||
|
||||
factory CreateAlarmRequestModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$CreateAlarmRequestModelFromJson(json);
|
||||
}
|
||||
@@ -1,289 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'create_alarm_request_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$CreateAlarmRequestModel {
|
||||
|
||||
String get id; String get deviceId; String get time; String get message; int get order;
|
||||
/// Create a copy of CreateAlarmRequestModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$CreateAlarmRequestModelCopyWith<CreateAlarmRequestModel> get copyWith => _$CreateAlarmRequestModelCopyWithImpl<CreateAlarmRequestModel>(this as CreateAlarmRequestModel, _$identity);
|
||||
|
||||
/// Serializes this CreateAlarmRequestModel to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is CreateAlarmRequestModel&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.time, time) || other.time == time)&&(identical(other.message, message) || other.message == message)&&(identical(other.order, order) || other.order == order));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,deviceId,time,message,order);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CreateAlarmRequestModel(id: $id, deviceId: $deviceId, time: $time, message: $message, order: $order)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $CreateAlarmRequestModelCopyWith<$Res> {
|
||||
factory $CreateAlarmRequestModelCopyWith(CreateAlarmRequestModel value, $Res Function(CreateAlarmRequestModel) _then) = _$CreateAlarmRequestModelCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String id, String deviceId, String time, String message, int order
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$CreateAlarmRequestModelCopyWithImpl<$Res>
|
||||
implements $CreateAlarmRequestModelCopyWith<$Res> {
|
||||
_$CreateAlarmRequestModelCopyWithImpl(this._self, this._then);
|
||||
|
||||
final CreateAlarmRequestModel _self;
|
||||
final $Res Function(CreateAlarmRequestModel) _then;
|
||||
|
||||
/// Create a copy of CreateAlarmRequestModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? deviceId = null,Object? time = null,Object? message = null,Object? order = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||
as String,time: null == time ? _self.time : time // ignore: cast_nullable_to_non_nullable
|
||||
as String,message: null == message ? _self.message : message // ignore: cast_nullable_to_non_nullable
|
||||
as String,order: null == order ? _self.order : order // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [CreateAlarmRequestModel].
|
||||
extension CreateAlarmRequestModelPatterns on CreateAlarmRequestModel {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _CreateAlarmRequestModel value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _CreateAlarmRequestModel() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _CreateAlarmRequestModel value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _CreateAlarmRequestModel():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _CreateAlarmRequestModel value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _CreateAlarmRequestModel() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String deviceId, String time, String message, int order)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CreateAlarmRequestModel() when $default != null:
|
||||
return $default(_that.id,_that.deviceId,_that.time,_that.message,_that.order);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String deviceId, String time, String message, int order) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CreateAlarmRequestModel():
|
||||
return $default(_that.id,_that.deviceId,_that.time,_that.message,_that.order);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String deviceId, String time, String message, int order)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CreateAlarmRequestModel() when $default != null:
|
||||
return $default(_that.id,_that.deviceId,_that.time,_that.message,_that.order);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _CreateAlarmRequestModel implements CreateAlarmRequestModel {
|
||||
const _CreateAlarmRequestModel({required this.id, required this.deviceId, required this.time, required this.message, required this.order});
|
||||
factory _CreateAlarmRequestModel.fromJson(Map<String, dynamic> json) => _$CreateAlarmRequestModelFromJson(json);
|
||||
|
||||
@override final String id;
|
||||
@override final String deviceId;
|
||||
@override final String time;
|
||||
@override final String message;
|
||||
@override final int order;
|
||||
|
||||
/// Create a copy of CreateAlarmRequestModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$CreateAlarmRequestModelCopyWith<_CreateAlarmRequestModel> get copyWith => __$CreateAlarmRequestModelCopyWithImpl<_CreateAlarmRequestModel>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$CreateAlarmRequestModelToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CreateAlarmRequestModel&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.time, time) || other.time == time)&&(identical(other.message, message) || other.message == message)&&(identical(other.order, order) || other.order == order));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,deviceId,time,message,order);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CreateAlarmRequestModel(id: $id, deviceId: $deviceId, time: $time, message: $message, order: $order)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$CreateAlarmRequestModelCopyWith<$Res> implements $CreateAlarmRequestModelCopyWith<$Res> {
|
||||
factory _$CreateAlarmRequestModelCopyWith(_CreateAlarmRequestModel value, $Res Function(_CreateAlarmRequestModel) _then) = __$CreateAlarmRequestModelCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String id, String deviceId, String time, String message, int order
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$CreateAlarmRequestModelCopyWithImpl<$Res>
|
||||
implements _$CreateAlarmRequestModelCopyWith<$Res> {
|
||||
__$CreateAlarmRequestModelCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _CreateAlarmRequestModel _self;
|
||||
final $Res Function(_CreateAlarmRequestModel) _then;
|
||||
|
||||
/// Create a copy of CreateAlarmRequestModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? deviceId = null,Object? time = null,Object? message = null,Object? order = null,}) {
|
||||
return _then(_CreateAlarmRequestModel(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||
as String,time: null == time ? _self.time : time // ignore: cast_nullable_to_non_nullable
|
||||
as String,message: null == message ? _self.message : message // ignore: cast_nullable_to_non_nullable
|
||||
as String,order: null == order ? _self.order : order // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -1,27 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'create_alarm_request_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_CreateAlarmRequestModel _$CreateAlarmRequestModelFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _CreateAlarmRequestModel(
|
||||
id: json['id'] as String,
|
||||
deviceId: json['deviceId'] as String,
|
||||
time: json['time'] as String,
|
||||
message: json['message'] as String,
|
||||
order: (json['order'] as num).toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$CreateAlarmRequestModelToJson(
|
||||
_CreateAlarmRequestModel instance,
|
||||
) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'deviceId': instance.deviceId,
|
||||
'time': instance.time,
|
||||
'message': instance.message,
|
||||
'order': instance.order,
|
||||
};
|
||||
@@ -6,48 +6,66 @@ part 'get_alarms_response_model.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class GetAlarmsResponseModel with _$GetAlarmsResponseModel {
|
||||
const factory GetAlarmsResponseModel({
|
||||
required int total,
|
||||
required List<AlarmItemResponseModel> items,
|
||||
required int page,
|
||||
required int pages,
|
||||
}) = _GetAlarmsResponseModel;
|
||||
const factory GetAlarmsResponseModel({required AlarmItemModel item}) =
|
||||
_GetAlarmsResponseModel;
|
||||
|
||||
factory GetAlarmsResponseModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$GetAlarmsResponseModelFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class AlarmItemResponseModel with _$AlarmItemResponseModel {
|
||||
const factory AlarmItemResponseModel({
|
||||
required String id,
|
||||
required String deviceId,
|
||||
required String time,
|
||||
required String message,
|
||||
required int order,
|
||||
required int createdAt,
|
||||
int? updatedAt,
|
||||
}) = _AlarmItemResponseModel;
|
||||
abstract class UpsertAlarmsResponseModel with _$UpsertAlarmsResponseModel {
|
||||
const factory UpsertAlarmsResponseModel({
|
||||
@Default(false) bool isUpdated,
|
||||
required AlarmItemModel item,
|
||||
}) = _UpsertAlarmsResponseModel;
|
||||
|
||||
factory AlarmItemResponseModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$AlarmItemResponseModelFromJson(json);
|
||||
factory UpsertAlarmsResponseModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$UpsertAlarmsResponseModelFromJson(json);
|
||||
}
|
||||
|
||||
extension AlarmsResponseMapper on GetAlarmsResponseModel {
|
||||
List<AlarmEntity> toEntity() {
|
||||
return items.map((item) {
|
||||
final decoded = AlarmEntity.decodeTime(item.time);
|
||||
@freezed
|
||||
abstract class AlarmItemModel with _$AlarmItemModel {
|
||||
const factory AlarmItemModel({
|
||||
required String id,
|
||||
required String deviceId,
|
||||
@Default([]) List<AlarmModel> alarmList,
|
||||
int? createdAt,
|
||||
int? updatedAt,
|
||||
}) = _AlarmItemModel;
|
||||
|
||||
factory AlarmItemModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$AlarmItemModelFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class AlarmModel with _$AlarmModel {
|
||||
const factory AlarmModel({
|
||||
required String time,
|
||||
required String frequency,
|
||||
String? week,
|
||||
}) = _AlarmModel;
|
||||
|
||||
factory AlarmModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$AlarmModelFromJson(json);
|
||||
}
|
||||
|
||||
extension AlarmItemModelMapper on AlarmItemModel {
|
||||
List<AlarmEntity> toEntities() {
|
||||
return alarmList.map((alarm) {
|
||||
return AlarmEntity(
|
||||
id: item.id,
|
||||
deviceId: item.deviceId,
|
||||
time: decoded.time,
|
||||
message: item.message,
|
||||
order: item.order,
|
||||
dateOption: decoded.dateOption,
|
||||
days: decoded.days,
|
||||
createdAt: item.createdAt,
|
||||
updatedAt: item.updatedAt,
|
||||
time: alarm.time,
|
||||
frequency: _parseFrequency(alarm.frequency),
|
||||
week: alarm.week,
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
static AlarmFrequency _parseFrequency(String value) {
|
||||
return switch (value) {
|
||||
'every' => AlarmFrequency.every,
|
||||
'custom' => AlarmFrequency.custom,
|
||||
_ => AlarmFrequency.once,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,43 +9,55 @@ part of 'get_alarms_response_model.dart';
|
||||
_GetAlarmsResponseModel _$GetAlarmsResponseModelFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _GetAlarmsResponseModel(
|
||||
total: (json['total'] as num).toInt(),
|
||||
items: (json['items'] as List<dynamic>)
|
||||
.map((e) => AlarmItemResponseModel.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
page: (json['page'] as num).toInt(),
|
||||
pages: (json['pages'] as num).toInt(),
|
||||
item: AlarmItemModel.fromJson(json['item'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GetAlarmsResponseModelToJson(
|
||||
_GetAlarmsResponseModel instance,
|
||||
) => <String, dynamic>{
|
||||
'total': instance.total,
|
||||
'items': instance.items,
|
||||
'page': instance.page,
|
||||
'pages': instance.pages,
|
||||
};
|
||||
) => <String, dynamic>{'item': instance.item};
|
||||
|
||||
_AlarmItemResponseModel _$AlarmItemResponseModelFromJson(
|
||||
_UpsertAlarmsResponseModel _$UpsertAlarmsResponseModelFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _AlarmItemResponseModel(
|
||||
id: json['id'] as String,
|
||||
deviceId: json['deviceId'] as String,
|
||||
time: json['time'] as String,
|
||||
message: json['message'] as String,
|
||||
order: (json['order'] as num).toInt(),
|
||||
createdAt: (json['createdAt'] as num).toInt(),
|
||||
updatedAt: (json['updatedAt'] as num?)?.toInt(),
|
||||
) => _UpsertAlarmsResponseModel(
|
||||
isUpdated: json['isUpdated'] as bool? ?? false,
|
||||
item: AlarmItemModel.fromJson(json['item'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$AlarmItemResponseModelToJson(
|
||||
_AlarmItemResponseModel instance,
|
||||
) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'deviceId': instance.deviceId,
|
||||
'time': instance.time,
|
||||
'message': instance.message,
|
||||
'order': instance.order,
|
||||
'createdAt': instance.createdAt,
|
||||
'updatedAt': instance.updatedAt,
|
||||
};
|
||||
Map<String, dynamic> _$UpsertAlarmsResponseModelToJson(
|
||||
_UpsertAlarmsResponseModel instance,
|
||||
) => <String, dynamic>{'isUpdated': instance.isUpdated, 'item': instance.item};
|
||||
|
||||
_AlarmItemModel _$AlarmItemModelFromJson(Map<String, dynamic> json) =>
|
||||
_AlarmItemModel(
|
||||
id: json['id'] as String,
|
||||
deviceId: json['deviceId'] as String,
|
||||
alarmList:
|
||||
(json['alarmList'] as List<dynamic>?)
|
||||
?.map((e) => AlarmModel.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
createdAt: (json['createdAt'] as num?)?.toInt(),
|
||||
updatedAt: (json['updatedAt'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$AlarmItemModelToJson(_AlarmItemModel instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'deviceId': instance.deviceId,
|
||||
'alarmList': instance.alarmList,
|
||||
'createdAt': instance.createdAt,
|
||||
'updatedAt': instance.updatedAt,
|
||||
};
|
||||
|
||||
_AlarmModel _$AlarmModelFromJson(Map<String, dynamic> json) => _AlarmModel(
|
||||
time: json['time'] as String,
|
||||
frequency: json['frequency'] as String,
|
||||
week: json['week'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$AlarmModelToJson(_AlarmModel instance) =>
|
||||
<String, dynamic>{
|
||||
'time': instance.time,
|
||||
'frequency': instance.frequency,
|
||||
'week': instance.week,
|
||||
};
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'update_alarm_request_model.freezed.dart';
|
||||
part 'update_alarm_request_model.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class UpdateAlarmRequestModel with _$UpdateAlarmRequestModel {
|
||||
const factory UpdateAlarmRequestModel({
|
||||
required String deviceId,
|
||||
required String time,
|
||||
required String message,
|
||||
required int order,
|
||||
}) = _UpdateAlarmRequestModel;
|
||||
|
||||
factory UpdateAlarmRequestModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$UpdateAlarmRequestModelFromJson(json);
|
||||
}
|
||||
@@ -1,286 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'update_alarm_request_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$UpdateAlarmRequestModel {
|
||||
|
||||
String get deviceId; String get time; String get message; int get order;
|
||||
/// Create a copy of UpdateAlarmRequestModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$UpdateAlarmRequestModelCopyWith<UpdateAlarmRequestModel> get copyWith => _$UpdateAlarmRequestModelCopyWithImpl<UpdateAlarmRequestModel>(this as UpdateAlarmRequestModel, _$identity);
|
||||
|
||||
/// Serializes this UpdateAlarmRequestModel to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is UpdateAlarmRequestModel&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.time, time) || other.time == time)&&(identical(other.message, message) || other.message == message)&&(identical(other.order, order) || other.order == order));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,deviceId,time,message,order);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'UpdateAlarmRequestModel(deviceId: $deviceId, time: $time, message: $message, order: $order)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $UpdateAlarmRequestModelCopyWith<$Res> {
|
||||
factory $UpdateAlarmRequestModelCopyWith(UpdateAlarmRequestModel value, $Res Function(UpdateAlarmRequestModel) _then) = _$UpdateAlarmRequestModelCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String deviceId, String time, String message, int order
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$UpdateAlarmRequestModelCopyWithImpl<$Res>
|
||||
implements $UpdateAlarmRequestModelCopyWith<$Res> {
|
||||
_$UpdateAlarmRequestModelCopyWithImpl(this._self, this._then);
|
||||
|
||||
final UpdateAlarmRequestModel _self;
|
||||
final $Res Function(UpdateAlarmRequestModel) _then;
|
||||
|
||||
/// Create a copy of UpdateAlarmRequestModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? deviceId = null,Object? time = null,Object? message = null,Object? order = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||
as String,time: null == time ? _self.time : time // ignore: cast_nullable_to_non_nullable
|
||||
as String,message: null == message ? _self.message : message // ignore: cast_nullable_to_non_nullable
|
||||
as String,order: null == order ? _self.order : order // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [UpdateAlarmRequestModel].
|
||||
extension UpdateAlarmRequestModelPatterns on UpdateAlarmRequestModel {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _UpdateAlarmRequestModel value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _UpdateAlarmRequestModel() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _UpdateAlarmRequestModel value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _UpdateAlarmRequestModel():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _UpdateAlarmRequestModel value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _UpdateAlarmRequestModel() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String deviceId, String time, String message, int order)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _UpdateAlarmRequestModel() when $default != null:
|
||||
return $default(_that.deviceId,_that.time,_that.message,_that.order);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String deviceId, String time, String message, int order) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _UpdateAlarmRequestModel():
|
||||
return $default(_that.deviceId,_that.time,_that.message,_that.order);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String deviceId, String time, String message, int order)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _UpdateAlarmRequestModel() when $default != null:
|
||||
return $default(_that.deviceId,_that.time,_that.message,_that.order);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _UpdateAlarmRequestModel implements UpdateAlarmRequestModel {
|
||||
const _UpdateAlarmRequestModel({required this.deviceId, required this.time, required this.message, required this.order});
|
||||
factory _UpdateAlarmRequestModel.fromJson(Map<String, dynamic> json) => _$UpdateAlarmRequestModelFromJson(json);
|
||||
|
||||
@override final String deviceId;
|
||||
@override final String time;
|
||||
@override final String message;
|
||||
@override final int order;
|
||||
|
||||
/// Create a copy of UpdateAlarmRequestModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$UpdateAlarmRequestModelCopyWith<_UpdateAlarmRequestModel> get copyWith => __$UpdateAlarmRequestModelCopyWithImpl<_UpdateAlarmRequestModel>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$UpdateAlarmRequestModelToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _UpdateAlarmRequestModel&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.time, time) || other.time == time)&&(identical(other.message, message) || other.message == message)&&(identical(other.order, order) || other.order == order));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,deviceId,time,message,order);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'UpdateAlarmRequestModel(deviceId: $deviceId, time: $time, message: $message, order: $order)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$UpdateAlarmRequestModelCopyWith<$Res> implements $UpdateAlarmRequestModelCopyWith<$Res> {
|
||||
factory _$UpdateAlarmRequestModelCopyWith(_UpdateAlarmRequestModel value, $Res Function(_UpdateAlarmRequestModel) _then) = __$UpdateAlarmRequestModelCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String deviceId, String time, String message, int order
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$UpdateAlarmRequestModelCopyWithImpl<$Res>
|
||||
implements _$UpdateAlarmRequestModelCopyWith<$Res> {
|
||||
__$UpdateAlarmRequestModelCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _UpdateAlarmRequestModel _self;
|
||||
final $Res Function(_UpdateAlarmRequestModel) _then;
|
||||
|
||||
/// Create a copy of UpdateAlarmRequestModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? deviceId = null,Object? time = null,Object? message = null,Object? order = null,}) {
|
||||
return _then(_UpdateAlarmRequestModel(
|
||||
deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||
as String,time: null == time ? _self.time : time // ignore: cast_nullable_to_non_nullable
|
||||
as String,message: null == message ? _self.message : message // ignore: cast_nullable_to_non_nullable
|
||||
as String,order: null == order ? _self.order : order // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -1,25 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'update_alarm_request_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_UpdateAlarmRequestModel _$UpdateAlarmRequestModelFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _UpdateAlarmRequestModel(
|
||||
deviceId: json['deviceId'] as String,
|
||||
time: json['time'] as String,
|
||||
message: json['message'] as String,
|
||||
order: (json['order'] as num).toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$UpdateAlarmRequestModelToJson(
|
||||
_UpdateAlarmRequestModel instance,
|
||||
) => <String, dynamic>{
|
||||
'deviceId': instance.deviceId,
|
||||
'time': instance.time,
|
||||
'message': instance.message,
|
||||
'order': instance.order,
|
||||
};
|
||||
@@ -1,7 +1,4 @@
|
||||
import 'package:settings/src/core/data/datasources/alarm_remote_datasource.dart';
|
||||
import 'package:settings/src/core/data/models/create_alarm_request_model.dart';
|
||||
import 'package:settings/src/core/data/models/get_alarms_response_model.dart';
|
||||
import 'package:settings/src/core/data/models/update_alarm_request_model.dart';
|
||||
import 'package:settings/src/core/domain/repositories/alarm_repository.dart';
|
||||
import 'package:settings/src/features/alarm/domain/entities/alarm_entity.dart';
|
||||
|
||||
@@ -11,26 +8,12 @@ class AlarmRepositoryImpl implements AlarmRepository {
|
||||
final AlarmRemoteDatasource _remote;
|
||||
|
||||
@override
|
||||
Future<List<AlarmEntity>> getAlarms({required String deviceId}) async {
|
||||
final response = await _remote.getAlarms(deviceId: deviceId);
|
||||
return response.toEntity();
|
||||
}
|
||||
Future<List<AlarmEntity>> getAlarms({required String deviceId}) =>
|
||||
_remote.getAlarms(deviceId: deviceId);
|
||||
|
||||
@override
|
||||
Future<void> createAlarm({required CreateAlarmRequestModel request}) {
|
||||
return _remote.createAlarm(request: request);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateAlarm({
|
||||
required String alarmId,
|
||||
required UpdateAlarmRequestModel request,
|
||||
}) {
|
||||
return _remote.updateAlarm(alarmId: alarmId, request: request);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteAlarm({required String alarmId}) {
|
||||
return _remote.deleteAlarm(alarmId: alarmId);
|
||||
}
|
||||
Future<List<AlarmEntity>> upsertAlarms({
|
||||
required String deviceId,
|
||||
required List<AlarmEntity> alarms,
|
||||
}) => _remote.upsertAlarms(deviceId: deviceId, alarms: alarms);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import 'package:settings/src/core/data/models/create_alarm_request_model.dart';
|
||||
import 'package:settings/src/core/data/models/update_alarm_request_model.dart';
|
||||
import 'package:settings/src/features/alarm/domain/entities/alarm_entity.dart';
|
||||
|
||||
abstract class AlarmRepository {
|
||||
Future<List<AlarmEntity>> getAlarms({required String deviceId});
|
||||
Future<void> createAlarm({required CreateAlarmRequestModel request});
|
||||
Future<void> updateAlarm({
|
||||
required String alarmId,
|
||||
required UpdateAlarmRequestModel request,
|
||||
Future<List<AlarmEntity>> upsertAlarms({
|
||||
required String deviceId,
|
||||
required List<AlarmEntity> alarms,
|
||||
});
|
||||
Future<void> deleteAlarm({required String alarmId});
|
||||
}
|
||||
|
||||
@@ -1,77 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'alarm_entity.freezed.dart';
|
||||
|
||||
enum AlarmDateOption { once, daily, custom }
|
||||
enum AlarmFrequency { once, every, custom }
|
||||
|
||||
@freezed
|
||||
abstract class AlarmEntity with _$AlarmEntity {
|
||||
const AlarmEntity._();
|
||||
|
||||
const factory AlarmEntity({
|
||||
required String id,
|
||||
required String deviceId,
|
||||
required TimeOfDay time,
|
||||
required String message,
|
||||
required int order,
|
||||
required AlarmDateOption dateOption,
|
||||
@Default([false, false, false, false, false, false, false]) List<bool> days,
|
||||
int? createdAt,
|
||||
int? updatedAt,
|
||||
required String time,
|
||||
required AlarmFrequency frequency,
|
||||
String? week,
|
||||
}) = _AlarmEntity;
|
||||
|
||||
/// Encodes time + dateOption + days into API format: HH:MM-1-{type}[-XXXXXXX]
|
||||
String get encodedTime {
|
||||
final hh = time.hour.toString().padLeft(2, '0');
|
||||
final mm = time.minute.toString().padLeft(2, '0');
|
||||
|
||||
switch (dateOption) {
|
||||
case AlarmDateOption.once:
|
||||
return '$hh:$mm-1-1';
|
||||
case AlarmDateOption.daily:
|
||||
return '$hh:$mm-1-2';
|
||||
case AlarmDateOption.custom:
|
||||
final daysBits = days.map((d) => d ? '1' : '0').join();
|
||||
return '$hh:$mm-1-3-$daysBits';
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses API time format: HH:MM-1-{type}[-XXXXXXX]
|
||||
static ({TimeOfDay time, AlarmDateOption dateOption, List<bool> days})
|
||||
decodeTime(String raw) {
|
||||
final parts = raw.split('-');
|
||||
final timeParts = parts[0].split(':');
|
||||
final time = TimeOfDay(
|
||||
hour: int.parse(timeParts[0]),
|
||||
minute: int.parse(timeParts[1]),
|
||||
);
|
||||
|
||||
final type = int.parse(parts[2]);
|
||||
|
||||
switch (type) {
|
||||
case 1:
|
||||
return (
|
||||
time: time,
|
||||
dateOption: AlarmDateOption.once,
|
||||
days: List.filled(7, false),
|
||||
);
|
||||
case 2:
|
||||
return (
|
||||
time: time,
|
||||
dateOption: AlarmDateOption.daily,
|
||||
days: List.filled(7, false),
|
||||
);
|
||||
case 3:
|
||||
final daysStr = parts[3];
|
||||
final days = List.generate(7, (i) => daysStr[i] == '1');
|
||||
return (time: time, dateOption: AlarmDateOption.custom, days: days);
|
||||
default:
|
||||
return (
|
||||
time: time,
|
||||
dateOption: AlarmDateOption.once,
|
||||
days: List.filled(7, false),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$AlarmEntity {
|
||||
|
||||
String get id; String get deviceId; TimeOfDay get time; String get message; int get order; AlarmDateOption get dateOption; List<bool> get days; int? get createdAt; int? get updatedAt;
|
||||
String get time; AlarmFrequency get frequency; String? get week;
|
||||
/// Create a copy of AlarmEntity
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -25,16 +25,16 @@ $AlarmEntityCopyWith<AlarmEntity> get copyWith => _$AlarmEntityCopyWithImpl<Alar
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is AlarmEntity&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.time, time) || other.time == time)&&(identical(other.message, message) || other.message == message)&&(identical(other.order, order) || other.order == order)&&(identical(other.dateOption, dateOption) || other.dateOption == dateOption)&&const DeepCollectionEquality().equals(other.days, days)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is AlarmEntity&&(identical(other.time, time) || other.time == time)&&(identical(other.frequency, frequency) || other.frequency == frequency)&&(identical(other.week, week) || other.week == week));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,deviceId,time,message,order,dateOption,const DeepCollectionEquality().hash(days),createdAt,updatedAt);
|
||||
int get hashCode => Object.hash(runtimeType,time,frequency,week);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AlarmEntity(id: $id, deviceId: $deviceId, time: $time, message: $message, order: $order, dateOption: $dateOption, days: $days, createdAt: $createdAt, updatedAt: $updatedAt)';
|
||||
return 'AlarmEntity(time: $time, frequency: $frequency, week: $week)';
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ abstract mixin class $AlarmEntityCopyWith<$Res> {
|
||||
factory $AlarmEntityCopyWith(AlarmEntity value, $Res Function(AlarmEntity) _then) = _$AlarmEntityCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String id, String deviceId, TimeOfDay time, String message, int order, AlarmDateOption dateOption, List<bool> days, int? createdAt, int? updatedAt
|
||||
String time, AlarmFrequency frequency, String? week
|
||||
});
|
||||
|
||||
|
||||
@@ -62,18 +62,12 @@ class _$AlarmEntityCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of AlarmEntity
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? deviceId = null,Object? time = null,Object? message = null,Object? order = null,Object? dateOption = null,Object? days = null,Object? createdAt = freezed,Object? updatedAt = freezed,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? time = null,Object? frequency = null,Object? week = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||
as String,time: null == time ? _self.time : time // ignore: cast_nullable_to_non_nullable
|
||||
as TimeOfDay,message: null == message ? _self.message : message // ignore: cast_nullable_to_non_nullable
|
||||
as String,order: null == order ? _self.order : order // ignore: cast_nullable_to_non_nullable
|
||||
as int,dateOption: null == dateOption ? _self.dateOption : dateOption // ignore: cast_nullable_to_non_nullable
|
||||
as AlarmDateOption,days: null == days ? _self.days : days // ignore: cast_nullable_to_non_nullable
|
||||
as List<bool>,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as int?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
time: null == time ? _self.time : time // ignore: cast_nullable_to_non_nullable
|
||||
as String,frequency: null == frequency ? _self.frequency : frequency // ignore: cast_nullable_to_non_nullable
|
||||
as AlarmFrequency,week: freezed == week ? _self.week : week // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -158,10 +152,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String deviceId, TimeOfDay time, String message, int order, AlarmDateOption dateOption, List<bool> days, int? createdAt, int? updatedAt)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String time, AlarmFrequency frequency, String? week)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AlarmEntity() when $default != null:
|
||||
return $default(_that.id,_that.deviceId,_that.time,_that.message,_that.order,_that.dateOption,_that.days,_that.createdAt,_that.updatedAt);case _:
|
||||
return $default(_that.time,_that.frequency,_that.week);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -179,10 +173,10 @@ return $default(_that.id,_that.deviceId,_that.time,_that.message,_that.order,_th
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String deviceId, TimeOfDay time, String message, int order, AlarmDateOption dateOption, List<bool> days, int? createdAt, int? updatedAt) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String time, AlarmFrequency frequency, String? week) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AlarmEntity():
|
||||
return $default(_that.id,_that.deviceId,_that.time,_that.message,_that.order,_that.dateOption,_that.days,_that.createdAt,_that.updatedAt);case _:
|
||||
return $default(_that.time,_that.frequency,_that.week);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -199,10 +193,10 @@ return $default(_that.id,_that.deviceId,_that.time,_that.message,_that.order,_th
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String deviceId, TimeOfDay time, String message, int order, AlarmDateOption dateOption, List<bool> days, int? createdAt, int? updatedAt)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String time, AlarmFrequency frequency, String? week)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AlarmEntity() when $default != null:
|
||||
return $default(_that.id,_that.deviceId,_that.time,_that.message,_that.order,_that.dateOption,_that.days,_that.createdAt,_that.updatedAt);case _:
|
||||
return $default(_that.time,_that.frequency,_that.week);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -213,25 +207,13 @@ return $default(_that.id,_that.deviceId,_that.time,_that.message,_that.order,_th
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _AlarmEntity extends AlarmEntity {
|
||||
const _AlarmEntity({required this.id, required this.deviceId, required this.time, required this.message, required this.order, required this.dateOption, final List<bool> days = const [false, false, false, false, false, false, false], this.createdAt, this.updatedAt}): _days = days,super._();
|
||||
class _AlarmEntity implements AlarmEntity {
|
||||
const _AlarmEntity({required this.time, required this.frequency, this.week});
|
||||
|
||||
|
||||
@override final String id;
|
||||
@override final String deviceId;
|
||||
@override final TimeOfDay time;
|
||||
@override final String message;
|
||||
@override final int order;
|
||||
@override final AlarmDateOption dateOption;
|
||||
final List<bool> _days;
|
||||
@override@JsonKey() List<bool> get days {
|
||||
if (_days is EqualUnmodifiableListView) return _days;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_days);
|
||||
}
|
||||
|
||||
@override final int? createdAt;
|
||||
@override final int? updatedAt;
|
||||
@override final String time;
|
||||
@override final AlarmFrequency frequency;
|
||||
@override final String? week;
|
||||
|
||||
/// Create a copy of AlarmEntity
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -243,16 +225,16 @@ _$AlarmEntityCopyWith<_AlarmEntity> get copyWith => __$AlarmEntityCopyWithImpl<_
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AlarmEntity&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.time, time) || other.time == time)&&(identical(other.message, message) || other.message == message)&&(identical(other.order, order) || other.order == order)&&(identical(other.dateOption, dateOption) || other.dateOption == dateOption)&&const DeepCollectionEquality().equals(other._days, _days)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AlarmEntity&&(identical(other.time, time) || other.time == time)&&(identical(other.frequency, frequency) || other.frequency == frequency)&&(identical(other.week, week) || other.week == week));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,deviceId,time,message,order,dateOption,const DeepCollectionEquality().hash(_days),createdAt,updatedAt);
|
||||
int get hashCode => Object.hash(runtimeType,time,frequency,week);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AlarmEntity(id: $id, deviceId: $deviceId, time: $time, message: $message, order: $order, dateOption: $dateOption, days: $days, createdAt: $createdAt, updatedAt: $updatedAt)';
|
||||
return 'AlarmEntity(time: $time, frequency: $frequency, week: $week)';
|
||||
}
|
||||
|
||||
|
||||
@@ -263,7 +245,7 @@ abstract mixin class _$AlarmEntityCopyWith<$Res> implements $AlarmEntityCopyWith
|
||||
factory _$AlarmEntityCopyWith(_AlarmEntity value, $Res Function(_AlarmEntity) _then) = __$AlarmEntityCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String id, String deviceId, TimeOfDay time, String message, int order, AlarmDateOption dateOption, List<bool> days, int? createdAt, int? updatedAt
|
||||
String time, AlarmFrequency frequency, String? week
|
||||
});
|
||||
|
||||
|
||||
@@ -280,18 +262,12 @@ class __$AlarmEntityCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of AlarmEntity
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? deviceId = null,Object? time = null,Object? message = null,Object? order = null,Object? dateOption = null,Object? days = null,Object? createdAt = freezed,Object? updatedAt = freezed,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? time = null,Object? frequency = null,Object? week = freezed,}) {
|
||||
return _then(_AlarmEntity(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||
as String,time: null == time ? _self.time : time // ignore: cast_nullable_to_non_nullable
|
||||
as TimeOfDay,message: null == message ? _self.message : message // ignore: cast_nullable_to_non_nullable
|
||||
as String,order: null == order ? _self.order : order // ignore: cast_nullable_to_non_nullable
|
||||
as int,dateOption: null == dateOption ? _self.dateOption : dateOption // ignore: cast_nullable_to_non_nullable
|
||||
as AlarmDateOption,days: null == days ? _self._days : days // ignore: cast_nullable_to_non_nullable
|
||||
as List<bool>,createdAt: freezed == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as int?,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
time: null == time ? _self.time : time // ignore: cast_nullable_to_non_nullable
|
||||
as String,frequency: null == frequency ? _self.frequency : frequency // ignore: cast_nullable_to_non_nullable
|
||||
as AlarmFrequency,week: freezed == week ? _self.week : week // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
import 'package:settings/src/features/alarm/domain/entities/alarm_entity.dart';
|
||||
import 'package:settings/src/features/alarm/presentation/state/alarm_view_model.dart';
|
||||
import 'package:settings/src/features/alarm/presentation/state/alarm_view_state.dart';
|
||||
import 'package:settings/src/features/alarm/presentation/widgets/alarm_form_screen.dart'
|
||||
show showAlarmForm;
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
@@ -18,33 +18,39 @@ class AlarmScreen extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.watch(themePortProvider);
|
||||
final state = ref.watch(alarmViewModelProvider);
|
||||
final vm = ref.read(alarmViewModelProvider.notifier);
|
||||
final (alarms, isLoading, isSaving, maxAlarms) = ref.watch(
|
||||
alarmViewModelProvider.select(
|
||||
(s) => (s.alarms, s.isLoading, s.isSaving, s.maxAlarms),
|
||||
),
|
||||
);
|
||||
final primaryColor = theme.getColorFor(ThemeCode.legacyPrimary);
|
||||
|
||||
ref.listen(alarmViewModelProvider.select((s) => s.errorMessage), (
|
||||
_,
|
||||
errorMessage,
|
||||
) {
|
||||
if (errorMessage.isNotEmpty) {
|
||||
showTopSnackbar(
|
||||
context,
|
||||
message: errorMessage,
|
||||
type: MessageType.error,
|
||||
);
|
||||
}
|
||||
ref.listen(alarmViewModelProvider.select((s) => s.errorEvent), (_, next) {
|
||||
if (next == null) return;
|
||||
final message = switch (next) {
|
||||
AlarmErrorEvent.load || AlarmErrorEvent.save => context.translate(
|
||||
I18n.alarmError,
|
||||
),
|
||||
AlarmErrorEvent.maxAlarms => context
|
||||
.translate(I18n.alarmMaxReached)
|
||||
.replaceAll('{max}', maxAlarms.toString()),
|
||||
};
|
||||
showTopSnackbar(context, message: message, type: MessageType.error);
|
||||
vm.clearError();
|
||||
});
|
||||
|
||||
ref.listen(alarmViewModelProvider.select((s) => s.successMessage), (
|
||||
ref.listen(alarmViewModelProvider.select((s) => s.saveSuccess), (
|
||||
_,
|
||||
successMessage,
|
||||
success,
|
||||
) {
|
||||
if (successMessage.isNotEmpty) {
|
||||
if (success) {
|
||||
showTopSnackbar(
|
||||
context,
|
||||
message: context.translate(successMessage),
|
||||
message: context.translate(I18n.alarmSaved),
|
||||
type: MessageType.success,
|
||||
);
|
||||
ref.read(alarmViewModelProvider.notifier).clearSuccess();
|
||||
vm.clearSaveSuccess();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -74,7 +80,7 @@ class AlarmScreen extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
if (state.alarms.length < 3)
|
||||
if (alarms.length < maxAlarms)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
right: SizeUtils.getByScreen(small: 16, big: 14),
|
||||
@@ -85,7 +91,7 @@ class AlarmScreen extends ConsumerWidget {
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: IconButton(
|
||||
onPressed: () => _openForm(context),
|
||||
onPressed: () => _openForm(context, vm),
|
||||
icon: Icon(
|
||||
Icons.add,
|
||||
color: Colors.white,
|
||||
@@ -98,17 +104,50 @@ class AlarmScreen extends ConsumerWidget {
|
||||
),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
child: state.isLoading
|
||||
child: isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: state.alarms.isEmpty
|
||||
? _EmptyState(primaryColor: primaryColor)
|
||||
: _AlarmList(onEdit: (alarm) => _openForm(context, alarm: alarm)),
|
||||
: alarms.isEmpty
|
||||
? _EmptyState(primaryColor: primaryColor)
|
||||
: _AlarmList(
|
||||
alarms: alarms,
|
||||
onEdit: (index, alarm) =>
|
||||
_openForm(context, vm, index: index, alarm: alarm),
|
||||
onDelete: vm.removeAlarm,
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: (!isLoading && alarms.isNotEmpty)
|
||||
? Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 24, vertical: 10),
|
||||
child: isSaving
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: PrimaryButton(
|
||||
onPressed: vm.save,
|
||||
text: context.translate(I18n.alarmSave),
|
||||
color: primaryColor,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
void _openForm(BuildContext context, {AlarmEntity? alarm}) {
|
||||
showAlarmForm(context, alarm: alarm);
|
||||
void _openForm(
|
||||
BuildContext context,
|
||||
AlarmViewModel vm, {
|
||||
int? index,
|
||||
AlarmEntity? alarm,
|
||||
}) {
|
||||
showAlarmForm(
|
||||
context,
|
||||
alarm: alarm,
|
||||
onSave: (result) {
|
||||
if (index != null) {
|
||||
vm.updateAlarm(index, result);
|
||||
} else {
|
||||
vm.addAlarm(result);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,14 +182,18 @@ class _EmptyState extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _AlarmList extends ConsumerWidget {
|
||||
final void Function(AlarmEntity alarm) onEdit;
|
||||
final List<AlarmEntity> alarms;
|
||||
final void Function(int index, AlarmEntity alarm) onEdit;
|
||||
final void Function(int index) onDelete;
|
||||
|
||||
const _AlarmList({required this.onEdit});
|
||||
const _AlarmList({
|
||||
required this.alarms,
|
||||
required this.onEdit,
|
||||
required this.onDelete,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final alarms = ref.watch(alarmViewModelProvider.select((s) => s.alarms));
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: SizeUtils.getByScreen(
|
||||
@@ -158,8 +201,12 @@ class _AlarmList extends ConsumerWidget {
|
||||
big: EdgeInsets.symmetric(horizontal: 21, vertical: 8),
|
||||
),
|
||||
child: Column(
|
||||
children: alarms.map((alarm) {
|
||||
return _AlarmCard(alarm: alarm, onEdit: () => onEdit(alarm));
|
||||
children: alarms.asMap().entries.map((entry) {
|
||||
return _AlarmCard(
|
||||
alarm: entry.value,
|
||||
onEdit: () => onEdit(entry.key, entry.value),
|
||||
onDelete: () => onDelete(entry.key),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
@@ -170,35 +217,24 @@ class _AlarmList extends ConsumerWidget {
|
||||
class _AlarmCard extends ConsumerWidget {
|
||||
final AlarmEntity alarm;
|
||||
final VoidCallback onEdit;
|
||||
final VoidCallback onDelete;
|
||||
|
||||
const _AlarmCard({required this.alarm, required this.onEdit});
|
||||
const _AlarmCard({
|
||||
required this.alarm,
|
||||
required this.onEdit,
|
||||
required this.onDelete,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.watch(themePortProvider);
|
||||
final primaryColor = theme.getColorFor(ThemeCode.legacyPrimary);
|
||||
final hh = alarm.time.hour.toString().padLeft(2, '0');
|
||||
final mm = alarm.time.minute.toString().padLeft(2, '0');
|
||||
|
||||
String typeLabel;
|
||||
String? daysLabel;
|
||||
switch (alarm.dateOption) {
|
||||
case AlarmDateOption.once:
|
||||
typeLabel = context.translate(I18n.once);
|
||||
case AlarmDateOption.daily:
|
||||
typeLabel = context.translate(I18n.daily);
|
||||
daysLabel = context.translate(I18n.daily);
|
||||
case AlarmDateOption.custom:
|
||||
typeLabel = context.translate(I18n.selectDay);
|
||||
final active = <String>[];
|
||||
for (final entry in weekDayI18nKeys.entries) {
|
||||
final dayIndex = entry.key - 1;
|
||||
if (alarm.days[dayIndex]) {
|
||||
active.add(weekDayShortLabel(context, entry.value));
|
||||
}
|
||||
}
|
||||
daysLabel = active.join(' ');
|
||||
}
|
||||
final frequencyLabel = switch (alarm.frequency) {
|
||||
AlarmFrequency.once => context.translate(I18n.once),
|
||||
AlarmFrequency.every => context.translate(I18n.daily),
|
||||
AlarmFrequency.custom => context.translate(I18n.selectDay),
|
||||
};
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
@@ -220,7 +256,7 @@ class _AlarmCard extends ConsumerWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'$hh:$mm',
|
||||
alarm.time,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: theme.getColorFor(ThemeCode.textPrimary),
|
||||
@@ -228,7 +264,7 @@ class _AlarmCard extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
typeLabel,
|
||||
frequencyLabel,
|
||||
style: TextStyle(
|
||||
color: primaryColor,
|
||||
fontSize: SizeUtils.getByScreen(small: 14, big: 15),
|
||||
@@ -237,27 +273,10 @@ class _AlarmCard extends ConsumerWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Text(
|
||||
alarm.message,
|
||||
style: TextStyle(
|
||||
color: theme.getColorFor(ThemeCode.textPrimary).withAlpha(178),
|
||||
fontSize: SizeUtils.getByScreen(small: 14, big: 15),
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
if (daysLabel != null) ...[
|
||||
SizedBox(height: 2),
|
||||
Text(
|
||||
daysLabel,
|
||||
style: TextStyle(
|
||||
color: theme
|
||||
.getColorFor(ThemeCode.textPrimary)
|
||||
.withAlpha(128),
|
||||
fontSize: SizeUtils.getByScreen(small: 12, big: 13),
|
||||
),
|
||||
),
|
||||
if (alarm.frequency == AlarmFrequency.custom &&
|
||||
alarm.week != null) ...[
|
||||
SizedBox(height: 6),
|
||||
_WeekDaysDisplay(week: alarm.week!, primaryColor: primaryColor),
|
||||
],
|
||||
SizedBox(height: 8),
|
||||
Row(
|
||||
@@ -272,7 +291,7 @@ class _AlarmCard extends ConsumerWidget {
|
||||
_ActionButton(
|
||||
icon: Icons.delete_outline,
|
||||
color: Colors.red,
|
||||
onPressed: () => _confirmDelete(context, ref),
|
||||
onPressed: onDelete,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -281,29 +300,35 @@ class _AlarmCard extends ConsumerWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _confirmDelete(BuildContext context, WidgetRef ref) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(context.translate(I18n.deleteAlarm)),
|
||||
content: Text(context.translate(I18n.deleteAlarmConfirm)),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(context.translate(I18n.cancel)),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
ref.read(alarmViewModelProvider.notifier).deleteAlarm(alarm.id);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(
|
||||
context.translate(I18n.delete),
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
),
|
||||
],
|
||||
class _WeekDaysDisplay extends StatelessWidget {
|
||||
final String week;
|
||||
final Color primaryColor;
|
||||
|
||||
const _WeekDaysDisplay({required this.week, required this.primaryColor});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final labels = [
|
||||
context.translate(I18n.weekdayMonShort),
|
||||
context.translate(I18n.weekdayTueShort),
|
||||
context.translate(I18n.weekdayWedShort),
|
||||
context.translate(I18n.weekdayThuShort),
|
||||
context.translate(I18n.weekdayFriShort),
|
||||
context.translate(I18n.weekdaySatShort),
|
||||
context.translate(I18n.weekdaySunShort),
|
||||
];
|
||||
final chars = week.padRight(7, '0').split('');
|
||||
final activeLabels = <String>[];
|
||||
for (int i = 0; i < 7; i++) {
|
||||
if (chars[i] == '1') activeLabels.add(labels[i]);
|
||||
}
|
||||
return Text(
|
||||
activeLabels.join(' '),
|
||||
style: TextStyle(
|
||||
color: primaryColor.withValues(alpha: 0.7),
|
||||
fontSize: SizeUtils.getByScreen(small: 12, big: 13),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:settings/src/core/data/models/create_alarm_request_model.dart';
|
||||
import 'package:settings/src/core/data/models/update_alarm_request_model.dart';
|
||||
import 'package:settings/src/core/domain/repositories/alarm_repository.dart';
|
||||
import 'package:settings/src/core/providers/alarm_repository_provider.dart';
|
||||
import 'package:settings/src/features/alarm/domain/entities/alarm_entity.dart';
|
||||
import 'package:settings/src/features/alarm/presentation/state/alarm_view_state.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:sf_tracking/sf_tracking.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
final alarmViewModelProvider =
|
||||
NotifierProvider.autoDispose<AlarmViewModel, AlarmViewState>(
|
||||
@@ -26,130 +21,100 @@ class AlarmViewModel extends Notifier<AlarmViewState> {
|
||||
AlarmViewState build() {
|
||||
_repository = ref.read(alarmRepositoryProvider);
|
||||
_tracking = ref.read(sfTrackingProvider);
|
||||
|
||||
final capabilities = ref.read(selectedDeviceProvider).value?.capabilities;
|
||||
final maxAlarms = capabilities?.alarms?.maxAlarms ?? 3;
|
||||
|
||||
Future.microtask(_load);
|
||||
return const AlarmViewState();
|
||||
return AlarmViewState(maxAlarms: maxAlarms);
|
||||
}
|
||||
|
||||
String _formatTime(TimeOfDay time) {
|
||||
final hh = time.hour.toString().padLeft(2, '0');
|
||||
final mm = time.minute.toString().padLeft(2, '0');
|
||||
return '$hh:$mm';
|
||||
}
|
||||
String? get _deviceId => ref.read(selectedDeviceProvider).value?.id;
|
||||
|
||||
Future<void> _load() async {
|
||||
try {
|
||||
final device = ref.read(selectedDeviceProvider).value;
|
||||
if (device == null) return;
|
||||
final deviceId = _deviceId;
|
||||
if (deviceId == null) {
|
||||
state = state.copyWith(isLoading: false);
|
||||
return;
|
||||
}
|
||||
|
||||
final alarms = await _repository.getAlarms(deviceId: device.id);
|
||||
try {
|
||||
final alarms = await _repository.getAlarms(deviceId: deviceId);
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(alarms: alarms, isLoading: false);
|
||||
} catch (e) {
|
||||
} catch (_) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: formatErrorMessage(e),
|
||||
errorEvent: AlarmErrorEvent.load,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> createAlarm(AlarmEntity alarm) async {
|
||||
state = state.copyWith(isSaving: true, errorMessage: '');
|
||||
void addAlarm(AlarmEntity alarm) {
|
||||
if (state.alarms.length >= state.maxAlarms) {
|
||||
state = state.copyWith(errorEvent: AlarmErrorEvent.maxAlarms);
|
||||
return;
|
||||
}
|
||||
state = state.copyWith(
|
||||
alarms: [...state.alarms, alarm],
|
||||
errorEvent: null,
|
||||
);
|
||||
}
|
||||
|
||||
void updateAlarm(int index, AlarmEntity alarm) {
|
||||
if (index < 0 || index >= state.alarms.length) return;
|
||||
final updated = [...state.alarms];
|
||||
updated[index] = alarm;
|
||||
state = state.copyWith(alarms: updated, errorEvent: null);
|
||||
}
|
||||
|
||||
void removeAlarm(int index) {
|
||||
if (index < 0 || index >= state.alarms.length) return;
|
||||
final updated = [...state.alarms]..removeAt(index);
|
||||
state = state.copyWith(alarms: updated, errorEvent: null);
|
||||
}
|
||||
|
||||
Future<void> save() async {
|
||||
final deviceId = _deviceId;
|
||||
if (deviceId == null) return;
|
||||
|
||||
state = state.copyWith(
|
||||
isSaving: true,
|
||||
saveSuccess: false,
|
||||
errorEvent: null,
|
||||
);
|
||||
|
||||
try {
|
||||
final device = ref.read(selectedDeviceProvider).value;
|
||||
if (device == null) return;
|
||||
|
||||
final id = const Uuid().v4();
|
||||
final request = CreateAlarmRequestModel(
|
||||
id: id,
|
||||
deviceId: device.id,
|
||||
time: alarm.encodedTime,
|
||||
message: alarm.message,
|
||||
order: state.alarms.length + 1,
|
||||
final alarms = await _repository.upsertAlarms(
|
||||
deviceId: deviceId,
|
||||
alarms: state.alarms,
|
||||
);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
await _repository.createAlarm(request: request);
|
||||
|
||||
final updatedAlarm = alarm.copyWith(
|
||||
id: id,
|
||||
deviceId: device.id,
|
||||
order: state.alarms.length + 1,
|
||||
state = state.copyWith(
|
||||
isSaving: false,
|
||||
alarms: alarms,
|
||||
saveSuccess: true,
|
||||
);
|
||||
|
||||
unawaited(
|
||||
_tracking.legacySettingsAlarmAdded(time: _formatTime(alarm.time)),
|
||||
_tracking.legacySettingsAlarmsSaved(alarmsCount: alarms.length),
|
||||
);
|
||||
|
||||
state = state.copyWith(
|
||||
alarms: [...state.alarms, updatedAlarm],
|
||||
isSaving: false,
|
||||
successMessage: I18n.alarmCreated,
|
||||
);
|
||||
} catch (e) {
|
||||
} catch (_) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isSaving: false,
|
||||
errorMessage: formatErrorMessage(e),
|
||||
errorEvent: AlarmErrorEvent.save,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateAlarm(AlarmEntity alarm) async {
|
||||
state = state.copyWith(isSaving: true, errorMessage: '');
|
||||
|
||||
try {
|
||||
final request = UpdateAlarmRequestModel(
|
||||
deviceId: alarm.deviceId,
|
||||
time: alarm.encodedTime,
|
||||
message: alarm.message,
|
||||
order: alarm.order,
|
||||
);
|
||||
|
||||
await _repository.updateAlarm(alarmId: alarm.id, request: request);
|
||||
|
||||
final updatedAlarms = state.alarms
|
||||
.map((a) => a.id == alarm.id ? alarm : a)
|
||||
.toList();
|
||||
|
||||
unawaited(
|
||||
_tracking.legacySettingsAlarmUpdated(time: _formatTime(alarm.time)),
|
||||
);
|
||||
|
||||
state = state.copyWith(
|
||||
alarms: updatedAlarms,
|
||||
isSaving: false,
|
||||
successMessage: I18n.alarmUpdated,
|
||||
);
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
isSaving: false,
|
||||
errorMessage: formatErrorMessage(e),
|
||||
);
|
||||
}
|
||||
void clearError() {
|
||||
if (state.errorEvent != null) state = state.copyWith(errorEvent: null);
|
||||
}
|
||||
|
||||
Future<void> deleteAlarm(String alarmId) async {
|
||||
state = state.copyWith(isSaving: true, errorMessage: '');
|
||||
|
||||
try {
|
||||
await _repository.deleteAlarm(alarmId: alarmId);
|
||||
|
||||
final updatedAlarms = state.alarms.where((a) => a.id != alarmId).toList();
|
||||
|
||||
unawaited(_tracking.legacySettingsAlarmRemoved());
|
||||
|
||||
state = state.copyWith(
|
||||
alarms: updatedAlarms,
|
||||
isSaving: false,
|
||||
successMessage: I18n.alarmDeleted,
|
||||
);
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
isSaving: false,
|
||||
errorMessage: formatErrorMessage(e),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void clearSuccess() {
|
||||
state = state.copyWith(successMessage: '');
|
||||
void clearSaveSuccess() {
|
||||
if (state.saveSuccess) state = state.copyWith(saveSuccess: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,16 @@ import 'package:settings/src/features/alarm/domain/entities/alarm_entity.dart';
|
||||
|
||||
part 'alarm_view_state.freezed.dart';
|
||||
|
||||
enum AlarmErrorEvent { load, save, maxAlarms }
|
||||
|
||||
@freezed
|
||||
abstract class AlarmViewState with _$AlarmViewState {
|
||||
const factory AlarmViewState({
|
||||
@Default([]) List<AlarmEntity> alarms,
|
||||
@Default(true) bool isLoading,
|
||||
@Default(false) bool isSaving,
|
||||
@Default('') String successMessage,
|
||||
@Default('') String errorMessage,
|
||||
@Default(3) int maxAlarms,
|
||||
AlarmErrorEvent? errorEvent,
|
||||
@Default(false) bool saveSuccess,
|
||||
}) = _AlarmViewState;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$AlarmViewState {
|
||||
|
||||
List<AlarmEntity> get alarms; bool get isLoading; bool get isSaving; String get successMessage; String get errorMessage;
|
||||
List<AlarmEntity> get alarms; bool get isLoading; bool get isSaving; int get maxAlarms; AlarmErrorEvent? get errorEvent; bool get saveSuccess;
|
||||
/// Create a copy of AlarmViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -25,16 +25,16 @@ $AlarmViewStateCopyWith<AlarmViewState> get copyWith => _$AlarmViewStateCopyWith
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is AlarmViewState&&const DeepCollectionEquality().equals(other.alarms, alarms)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.successMessage, successMessage) || other.successMessage == successMessage)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is AlarmViewState&&const DeepCollectionEquality().equals(other.alarms, alarms)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.maxAlarms, maxAlarms) || other.maxAlarms == maxAlarms)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.saveSuccess, saveSuccess) || other.saveSuccess == saveSuccess));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(alarms),isLoading,isSaving,successMessage,errorMessage);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(alarms),isLoading,isSaving,maxAlarms,errorEvent,saveSuccess);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AlarmViewState(alarms: $alarms, isLoading: $isLoading, isSaving: $isSaving, successMessage: $successMessage, errorMessage: $errorMessage)';
|
||||
return 'AlarmViewState(alarms: $alarms, isLoading: $isLoading, isSaving: $isSaving, maxAlarms: $maxAlarms, errorEvent: $errorEvent, saveSuccess: $saveSuccess)';
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ abstract mixin class $AlarmViewStateCopyWith<$Res> {
|
||||
factory $AlarmViewStateCopyWith(AlarmViewState value, $Res Function(AlarmViewState) _then) = _$AlarmViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
List<AlarmEntity> alarms, bool isLoading, bool isSaving, String successMessage, String errorMessage
|
||||
List<AlarmEntity> alarms, bool isLoading, bool isSaving, int maxAlarms, AlarmErrorEvent? errorEvent, bool saveSuccess
|
||||
});
|
||||
|
||||
|
||||
@@ -62,14 +62,15 @@ class _$AlarmViewStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of AlarmViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? alarms = null,Object? isLoading = null,Object? isSaving = null,Object? successMessage = null,Object? errorMessage = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? alarms = null,Object? isLoading = null,Object? isSaving = null,Object? maxAlarms = null,Object? errorEvent = freezed,Object? saveSuccess = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
alarms: null == alarms ? _self.alarms : alarms // ignore: cast_nullable_to_non_nullable
|
||||
as List<AlarmEntity>,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isSaving: null == isSaving ? _self.isSaving : isSaving // ignore: cast_nullable_to_non_nullable
|
||||
as bool,successMessage: null == successMessage ? _self.successMessage : successMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as bool,maxAlarms: null == maxAlarms ? _self.maxAlarms : maxAlarms // ignore: cast_nullable_to_non_nullable
|
||||
as int,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as AlarmErrorEvent?,saveSuccess: null == saveSuccess ? _self.saveSuccess : saveSuccess // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -154,10 +155,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<AlarmEntity> alarms, bool isLoading, bool isSaving, String successMessage, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<AlarmEntity> alarms, bool isLoading, bool isSaving, int maxAlarms, AlarmErrorEvent? errorEvent, bool saveSuccess)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AlarmViewState() when $default != null:
|
||||
return $default(_that.alarms,_that.isLoading,_that.isSaving,_that.successMessage,_that.errorMessage);case _:
|
||||
return $default(_that.alarms,_that.isLoading,_that.isSaving,_that.maxAlarms,_that.errorEvent,_that.saveSuccess);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -175,10 +176,10 @@ return $default(_that.alarms,_that.isLoading,_that.isSaving,_that.successMessage
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<AlarmEntity> alarms, bool isLoading, bool isSaving, String successMessage, String errorMessage) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<AlarmEntity> alarms, bool isLoading, bool isSaving, int maxAlarms, AlarmErrorEvent? errorEvent, bool saveSuccess) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AlarmViewState():
|
||||
return $default(_that.alarms,_that.isLoading,_that.isSaving,_that.successMessage,_that.errorMessage);case _:
|
||||
return $default(_that.alarms,_that.isLoading,_that.isSaving,_that.maxAlarms,_that.errorEvent,_that.saveSuccess);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -195,10 +196,10 @@ return $default(_that.alarms,_that.isLoading,_that.isSaving,_that.successMessage
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<AlarmEntity> alarms, bool isLoading, bool isSaving, String successMessage, String errorMessage)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<AlarmEntity> alarms, bool isLoading, bool isSaving, int maxAlarms, AlarmErrorEvent? errorEvent, bool saveSuccess)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AlarmViewState() when $default != null:
|
||||
return $default(_that.alarms,_that.isLoading,_that.isSaving,_that.successMessage,_that.errorMessage);case _:
|
||||
return $default(_that.alarms,_that.isLoading,_that.isSaving,_that.maxAlarms,_that.errorEvent,_that.saveSuccess);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -210,7 +211,7 @@ return $default(_that.alarms,_that.isLoading,_that.isSaving,_that.successMessage
|
||||
|
||||
|
||||
class _AlarmViewState implements AlarmViewState {
|
||||
const _AlarmViewState({final List<AlarmEntity> alarms = const [], this.isLoading = true, this.isSaving = false, this.successMessage = '', this.errorMessage = ''}): _alarms = alarms;
|
||||
const _AlarmViewState({final List<AlarmEntity> alarms = const [], this.isLoading = true, this.isSaving = false, this.maxAlarms = 3, this.errorEvent, this.saveSuccess = false}): _alarms = alarms;
|
||||
|
||||
|
||||
final List<AlarmEntity> _alarms;
|
||||
@@ -222,8 +223,9 @@ class _AlarmViewState implements AlarmViewState {
|
||||
|
||||
@override@JsonKey() final bool isLoading;
|
||||
@override@JsonKey() final bool isSaving;
|
||||
@override@JsonKey() final String successMessage;
|
||||
@override@JsonKey() final String errorMessage;
|
||||
@override@JsonKey() final int maxAlarms;
|
||||
@override final AlarmErrorEvent? errorEvent;
|
||||
@override@JsonKey() final bool saveSuccess;
|
||||
|
||||
/// Create a copy of AlarmViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -235,16 +237,16 @@ _$AlarmViewStateCopyWith<_AlarmViewState> get copyWith => __$AlarmViewStateCopyW
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AlarmViewState&&const DeepCollectionEquality().equals(other._alarms, _alarms)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.successMessage, successMessage) || other.successMessage == successMessage)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AlarmViewState&&const DeepCollectionEquality().equals(other._alarms, _alarms)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.maxAlarms, maxAlarms) || other.maxAlarms == maxAlarms)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.saveSuccess, saveSuccess) || other.saveSuccess == saveSuccess));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_alarms),isLoading,isSaving,successMessage,errorMessage);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_alarms),isLoading,isSaving,maxAlarms,errorEvent,saveSuccess);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AlarmViewState(alarms: $alarms, isLoading: $isLoading, isSaving: $isSaving, successMessage: $successMessage, errorMessage: $errorMessage)';
|
||||
return 'AlarmViewState(alarms: $alarms, isLoading: $isLoading, isSaving: $isSaving, maxAlarms: $maxAlarms, errorEvent: $errorEvent, saveSuccess: $saveSuccess)';
|
||||
}
|
||||
|
||||
|
||||
@@ -255,7 +257,7 @@ abstract mixin class _$AlarmViewStateCopyWith<$Res> implements $AlarmViewStateCo
|
||||
factory _$AlarmViewStateCopyWith(_AlarmViewState value, $Res Function(_AlarmViewState) _then) = __$AlarmViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
List<AlarmEntity> alarms, bool isLoading, bool isSaving, String successMessage, String errorMessage
|
||||
List<AlarmEntity> alarms, bool isLoading, bool isSaving, int maxAlarms, AlarmErrorEvent? errorEvent, bool saveSuccess
|
||||
});
|
||||
|
||||
|
||||
@@ -272,14 +274,15 @@ class __$AlarmViewStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of AlarmViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? alarms = null,Object? isLoading = null,Object? isSaving = null,Object? successMessage = null,Object? errorMessage = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? alarms = null,Object? isLoading = null,Object? isSaving = null,Object? maxAlarms = null,Object? errorEvent = freezed,Object? saveSuccess = null,}) {
|
||||
return _then(_AlarmViewState(
|
||||
alarms: null == alarms ? _self._alarms : alarms // ignore: cast_nullable_to_non_nullable
|
||||
as List<AlarmEntity>,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isSaving: null == isSaving ? _self.isSaving : isSaving // ignore: cast_nullable_to_non_nullable
|
||||
as bool,successMessage: null == successMessage ? _self.successMessage : successMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as bool,maxAlarms: null == maxAlarms ? _self.maxAlarms : maxAlarms // ignore: cast_nullable_to_non_nullable
|
||||
as int,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as AlarmErrorEvent?,saveSuccess: null == saveSuccess ? _self.saveSuccess : saveSuccess // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -4,23 +4,27 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:settings/src/features/alarm/domain/entities/alarm_entity.dart';
|
||||
import 'package:settings/src/features/alarm/presentation/state/alarm_view_model.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
void showAlarmForm(BuildContext context, {AlarmEntity? alarm}) {
|
||||
void showAlarmForm(
|
||||
BuildContext context, {
|
||||
AlarmEntity? alarm,
|
||||
required ValueChanged<AlarmEntity> onSave,
|
||||
}) {
|
||||
showModalBottomSheet<void>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (_) => _AlarmFormSheet(alarm: alarm),
|
||||
builder: (_) => _AlarmFormSheet(alarm: alarm, onSave: onSave),
|
||||
);
|
||||
}
|
||||
|
||||
class _AlarmFormSheet extends ConsumerStatefulWidget {
|
||||
final AlarmEntity? alarm;
|
||||
final ValueChanged<AlarmEntity> onSave;
|
||||
|
||||
const _AlarmFormSheet({this.alarm});
|
||||
const _AlarmFormSheet({this.alarm, required this.onSave});
|
||||
|
||||
@override
|
||||
ConsumerState<_AlarmFormSheet> createState() => _AlarmFormSheetState();
|
||||
@@ -28,33 +32,30 @@ class _AlarmFormSheet extends ConsumerStatefulWidget {
|
||||
|
||||
class _AlarmFormSheetState extends ConsumerState<_AlarmFormSheet> {
|
||||
late Duration _duration;
|
||||
late AlarmDateOption _dateOption;
|
||||
late AlarmFrequency _frequency;
|
||||
late List<bool> _days;
|
||||
late TextEditingController _messageController;
|
||||
|
||||
bool get _isEditing => widget.alarm != null;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final time = widget.alarm?.time ?? const TimeOfDay(hour: 8, minute: 0);
|
||||
_duration = Duration(hours: time.hour, minutes: time.minute);
|
||||
_dateOption = widget.alarm?.dateOption ?? AlarmDateOption.once;
|
||||
_days = widget.alarm?.days.toList() ?? List.filled(7, false);
|
||||
_messageController = TextEditingController(
|
||||
text: widget.alarm?.message ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_messageController.dispose();
|
||||
super.dispose();
|
||||
if (widget.alarm != null) {
|
||||
final parts = widget.alarm!.time.split(':');
|
||||
final hour = int.tryParse(parts[0]) ?? 8;
|
||||
final minute = int.tryParse(parts.length > 1 ? parts[1] : '0') ?? 0;
|
||||
_duration = Duration(hours: hour, minutes: minute);
|
||||
_frequency = widget.alarm!.frequency;
|
||||
_days = widget.alarm!.week != null
|
||||
? widget.alarm!.week!.padRight(7, '0').split('').map((c) => c == '1').toList()
|
||||
: List.filled(7, false);
|
||||
} else {
|
||||
_duration = const Duration(hours: 8);
|
||||
_frequency = AlarmFrequency.once;
|
||||
_days = List.filled(7, false);
|
||||
}
|
||||
}
|
||||
|
||||
bool get _canSave {
|
||||
if (_messageController.text.trim().isEmpty) return false;
|
||||
if (_dateOption == AlarmDateOption.custom && !_days.contains(true)) {
|
||||
if (_frequency == AlarmFrequency.custom && !_days.contains(true)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -63,28 +64,16 @@ class _AlarmFormSheetState extends ConsumerState<_AlarmFormSheet> {
|
||||
void _save() {
|
||||
if (!_canSave) return;
|
||||
|
||||
final vm = ref.read(alarmViewModelProvider.notifier);
|
||||
final time = TimeOfDay(
|
||||
hour: _duration.inHours % 24,
|
||||
minute: _duration.inMinutes % 60,
|
||||
);
|
||||
final hour = _duration.inHours % 24;
|
||||
final minute = _duration.inMinutes % 60;
|
||||
final time =
|
||||
'${hour.toString().padLeft(2, '0')}:${minute.toString().padLeft(2, '0')}';
|
||||
|
||||
final entity = AlarmEntity(
|
||||
id: widget.alarm?.id ?? '',
|
||||
deviceId: widget.alarm?.deviceId ?? '',
|
||||
time: time,
|
||||
message: _messageController.text.trim(),
|
||||
order: widget.alarm?.order ?? 0,
|
||||
dateOption: _dateOption,
|
||||
days: _days,
|
||||
);
|
||||
|
||||
if (_isEditing) {
|
||||
vm.updateAlarm(entity);
|
||||
} else {
|
||||
vm.createAlarm(entity);
|
||||
}
|
||||
final week = _frequency == AlarmFrequency.custom
|
||||
? _days.map((day) => day ? '1' : '0').join()
|
||||
: null;
|
||||
|
||||
widget.onSave(AlarmEntity(time: time, frequency: _frequency, week: week));
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
@@ -113,7 +102,6 @@ class _AlarmFormSheetState extends ConsumerState<_AlarmFormSheet> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Handle bar
|
||||
Center(
|
||||
child: Container(
|
||||
width: 40,
|
||||
@@ -125,15 +113,15 @@ class _AlarmFormSheetState extends ConsumerState<_AlarmFormSheet> {
|
||||
),
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 14)),
|
||||
|
||||
// Header
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
_isEditing
|
||||
? context.translate(I18n.editAlarm)
|
||||
: context.translate(I18n.addAlarm),
|
||||
context.translate(
|
||||
widget.alarm != null
|
||||
? I18n.editAlarm
|
||||
: I18n.addAlarm,
|
||||
),
|
||||
style: TextStyle(
|
||||
fontSize: SizeUtils.getByScreen(small: 20, big: 21),
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -154,8 +142,6 @@ class _AlarmFormSheetState extends ConsumerState<_AlarmFormSheet> {
|
||||
],
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 18)),
|
||||
|
||||
// Time picker
|
||||
Container(
|
||||
height: SizeUtils.getByScreen(small: 160, big: 180),
|
||||
decoration: BoxDecoration(
|
||||
@@ -165,49 +151,12 @@ class _AlarmFormSheetState extends ConsumerState<_AlarmFormSheet> {
|
||||
child: CupertinoTimerPicker(
|
||||
mode: CupertinoTimerPickerMode.hm,
|
||||
initialTimerDuration: _duration,
|
||||
onTimerDurationChanged: (d) {
|
||||
setState(() => _duration = d);
|
||||
onTimerDurationChanged: (duration) {
|
||||
setState(() => _duration = duration);
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 20, big: 22)),
|
||||
|
||||
// Message
|
||||
Text(
|
||||
context.translate(I18n.alarmMessage),
|
||||
style: TextStyle(
|
||||
fontSize: SizeUtils.getByScreen(small: 15, big: 16),
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
TextField(
|
||||
controller: _messageController,
|
||||
onChanged: (_) => setState(() {}),
|
||||
decoration: InputDecoration(
|
||||
hintText: context.translate(I18n.alarmMessageHint),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
borderSide: BorderSide(color: primaryColor, width: 2),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 14,
|
||||
vertical: 14,
|
||||
),
|
||||
),
|
||||
maxLines: 1,
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 20, big: 22)),
|
||||
|
||||
// Repetition
|
||||
Text(
|
||||
context.translate(I18n.selectDay),
|
||||
style: TextStyle(
|
||||
@@ -218,28 +167,26 @@ class _AlarmFormSheetState extends ConsumerState<_AlarmFormSheet> {
|
||||
SizedBox(height: 8),
|
||||
_RadioOption(
|
||||
label: context.translate(I18n.once),
|
||||
isSelected: _dateOption == AlarmDateOption.once,
|
||||
isSelected: _frequency == AlarmFrequency.once,
|
||||
primaryColor: primaryColor,
|
||||
onTap: () =>
|
||||
setState(() => _dateOption = AlarmDateOption.once),
|
||||
setState(() => _frequency = AlarmFrequency.once),
|
||||
),
|
||||
_RadioOption(
|
||||
label: context.translate(I18n.daily),
|
||||
isSelected: _dateOption == AlarmDateOption.daily,
|
||||
isSelected: _frequency == AlarmFrequency.every,
|
||||
primaryColor: primaryColor,
|
||||
onTap: () =>
|
||||
setState(() => _dateOption = AlarmDateOption.daily),
|
||||
setState(() => _frequency = AlarmFrequency.every),
|
||||
),
|
||||
_RadioOption(
|
||||
label: context.translate(I18n.selectDay),
|
||||
isSelected: _dateOption == AlarmDateOption.custom,
|
||||
isSelected: _frequency == AlarmFrequency.custom,
|
||||
primaryColor: primaryColor,
|
||||
onTap: () =>
|
||||
setState(() => _dateOption = AlarmDateOption.custom),
|
||||
setState(() => _frequency = AlarmFrequency.custom),
|
||||
),
|
||||
|
||||
// Day chips
|
||||
if (_dateOption == AlarmDateOption.custom) ...[
|
||||
if (_frequency == AlarmFrequency.custom) ...[
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 14)),
|
||||
WeekDayChips.multi(
|
||||
selectedDays: _days,
|
||||
@@ -252,7 +199,6 @@ class _AlarmFormSheetState extends ConsumerState<_AlarmFormSheet> {
|
||||
theme: theme,
|
||||
),
|
||||
],
|
||||
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 18)),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -535,6 +535,10 @@
|
||||
"editAlarm": "Alarm bearbeiten",
|
||||
"noAlarms": "Keine Alarme konfiguriert",
|
||||
"alarmCreated": "Alarm erfolgreich erstellt",
|
||||
"alarmError": "Fehler bei der Verwaltung der Alarme",
|
||||
"alarmMaxReached": "Maximal {max} Alarme erlaubt",
|
||||
"alarmSave": "Änderungen speichern",
|
||||
"alarmSaved": "Alarme gespeichert",
|
||||
"alarmUpdated": "Alarm erfolgreich aktualisiert",
|
||||
"alarmDeleted": "Alarm erfolgreich gelöscht",
|
||||
"noActivityData": "Keine Aktivitätsmessungen vorhanden",
|
||||
|
||||
@@ -648,6 +648,10 @@
|
||||
"editAlarm": "Edit alarm",
|
||||
"noAlarms": "No alarms configured",
|
||||
"alarmCreated": "Alarm created successfully",
|
||||
"alarmError": "Error managing alarms",
|
||||
"alarmMaxReached": "Maximum {max} alarms allowed",
|
||||
"alarmSave": "Save changes",
|
||||
"alarmSaved": "Alarms saved successfully",
|
||||
"alarmUpdated": "Alarm updated successfully",
|
||||
"alarmDeleted": "Alarm deleted successfully",
|
||||
"remoteTurnOff": "Apagado Remoto",
|
||||
|
||||
@@ -649,6 +649,10 @@
|
||||
"editAlarm": "Editar alarma",
|
||||
"noAlarms": "No hay alarmas configuradas",
|
||||
"alarmCreated": "Alarma creada correctamente",
|
||||
"alarmError": "Error al gestionar alarmas",
|
||||
"alarmMaxReached": "Máximo {max} alarmas permitidas",
|
||||
"alarmSave": "Guardar cambios",
|
||||
"alarmSaved": "Alarmas guardadas correctamente",
|
||||
"alarmUpdated": "Alarma actualizada correctamente",
|
||||
"alarmDeleted": "Alarma eliminada correctamente",
|
||||
"remoteTurnOff": "Apagado Remoto",
|
||||
|
||||
@@ -535,6 +535,10 @@
|
||||
"editAlarm": "Modifier l'alarme",
|
||||
"noAlarms": "Aucune alarme configurée",
|
||||
"alarmCreated": "Alarme créée avec succès",
|
||||
"alarmError": "Erreur lors de la gestion des alarmes",
|
||||
"alarmMaxReached": "Maximum {max} alarmes autorisées",
|
||||
"alarmSave": "Enregistrer",
|
||||
"alarmSaved": "Alarmes enregistrées",
|
||||
"alarmUpdated": "Alarme mise à jour avec succès",
|
||||
"alarmDeleted": "Alarme supprimée avec succès",
|
||||
"noActivityData": "Aucune mesure d'activité à afficher",
|
||||
|
||||
@@ -535,6 +535,10 @@
|
||||
"editAlarm": "Modifica allarme",
|
||||
"noAlarms": "Nessun allarme configurato",
|
||||
"alarmCreated": "Allarme creato con successo",
|
||||
"alarmError": "Errore nella gestione degli allarmi",
|
||||
"alarmMaxReached": "Massimo {max} allarmi consentiti",
|
||||
"alarmSave": "Salva modifiche",
|
||||
"alarmSaved": "Allarmi salvati",
|
||||
"alarmUpdated": "Allarme aggiornato con successo",
|
||||
"alarmDeleted": "Allarme eliminato con successo",
|
||||
"noActivityData": "Nessuna misurazione di attività da mostrare",
|
||||
|
||||
@@ -535,6 +535,10 @@
|
||||
"editAlarm": "Editar alarme",
|
||||
"noAlarms": "Nenhum alarme configurado",
|
||||
"alarmCreated": "Alarme criado com sucesso",
|
||||
"alarmError": "Erro ao gerir alarmes",
|
||||
"alarmMaxReached": "Máximo {max} alarmes permitidos",
|
||||
"alarmSave": "Guardar alterações",
|
||||
"alarmSaved": "Alarmes guardados",
|
||||
"alarmUpdated": "Alarme atualizado com sucesso",
|
||||
"alarmDeleted": "Alarme eliminado com sucesso",
|
||||
"noActivityData": "Sem medições de atividade para exibir",
|
||||
|
||||
@@ -38,8 +38,12 @@ class I18n {
|
||||
static const String alarm = 'alarm';
|
||||
static const String alarmCreated = 'alarmCreated';
|
||||
static const String alarmDeleted = 'alarmDeleted';
|
||||
static const String alarmError = 'alarmError';
|
||||
static const String alarmMaxReached = 'alarmMaxReached';
|
||||
static const String alarmMessage = 'alarmMessage';
|
||||
static const String alarmMessageHint = 'alarmMessageHint';
|
||||
static const String alarmSave = 'alarmSave';
|
||||
static const String alarmSaved = 'alarmSaved';
|
||||
static const String alarmSettings = 'alarmSettings';
|
||||
static const String alarmsMessage = 'alarmsMessage';
|
||||
static const String alarmUpdated = 'alarmUpdated';
|
||||
|
||||
@@ -3,14 +3,8 @@ import 'package:sf_tracking/src/tracking.dart';
|
||||
const _prefix = 'legacy_settings';
|
||||
|
||||
mixin SettingsTracking on Tracking {
|
||||
Future<void> legacySettingsAlarmAdded({required String time}) =>
|
||||
trackEvent('${_prefix}_alarm_added', {'time': time});
|
||||
|
||||
Future<void> legacySettingsAlarmUpdated({required String time}) =>
|
||||
trackEvent('${_prefix}_alarm_updated', {'time': time});
|
||||
|
||||
Future<void> legacySettingsAlarmRemoved() =>
|
||||
trackEvent('${_prefix}_alarm_removed');
|
||||
Future<void> legacySettingsAlarmsSaved({required int alarmsCount}) =>
|
||||
trackEvent('${_prefix}_alarms_saved', {'alarms_count': alarmsCount});
|
||||
|
||||
Future<void> legacySettingsSosContactAdded({required int totalCount}) =>
|
||||
trackEvent('${_prefix}_sos_contact_added', {'total_count': totalCount});
|
||||
|
||||
Reference in New Issue
Block a user