feat(do-not-disturb): add enabled toggle, redesign UI and duplicate validation
This commit is contained in:
@@ -43,6 +43,7 @@ class DoNotDisturbRemoteDatasourceImpl implements DoNotDisturbRemoteDatasource {
|
||||
'periods': periods
|
||||
.map(
|
||||
(period) => {
|
||||
'enabled': period.enabled,
|
||||
'start': period.start,
|
||||
'end': period.end,
|
||||
'week': period.week,
|
||||
@@ -68,6 +69,7 @@ class DoNotDisturbRemoteDatasourceImpl implements DoNotDisturbRemoteDatasource {
|
||||
periods: item.periods
|
||||
.map(
|
||||
(period) => DoNotDisturbPeriod(
|
||||
enabled: period.enabled,
|
||||
start: period.start,
|
||||
end: period.end,
|
||||
week: period.week,
|
||||
|
||||
@@ -43,6 +43,7 @@ abstract class DoNotDisturbItemDto with _$DoNotDisturbItemDto {
|
||||
@freezed
|
||||
abstract class DoNotDisturbPeriodDto with _$DoNotDisturbPeriodDto {
|
||||
const factory DoNotDisturbPeriodDto({
|
||||
@Default(true) bool enabled,
|
||||
required String start,
|
||||
required String end,
|
||||
required String week,
|
||||
|
||||
@@ -861,7 +861,7 @@ as int?,
|
||||
/// @nodoc
|
||||
mixin _$DoNotDisturbPeriodDto {
|
||||
|
||||
String get start; String get end; String get week;
|
||||
bool get enabled; String get start; String get end; String get week;
|
||||
/// Create a copy of DoNotDisturbPeriodDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -874,16 +874,16 @@ $DoNotDisturbPeriodDtoCopyWith<DoNotDisturbPeriodDto> get copyWith => _$DoNotDis
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is DoNotDisturbPeriodDto&&(identical(other.start, start) || other.start == start)&&(identical(other.end, end) || other.end == end)&&(identical(other.week, week) || other.week == week));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is DoNotDisturbPeriodDto&&(identical(other.enabled, enabled) || other.enabled == enabled)&&(identical(other.start, start) || other.start == start)&&(identical(other.end, end) || other.end == end)&&(identical(other.week, week) || other.week == week));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,start,end,week);
|
||||
int get hashCode => Object.hash(runtimeType,enabled,start,end,week);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DoNotDisturbPeriodDto(start: $start, end: $end, week: $week)';
|
||||
return 'DoNotDisturbPeriodDto(enabled: $enabled, start: $start, end: $end, week: $week)';
|
||||
}
|
||||
|
||||
|
||||
@@ -894,7 +894,7 @@ abstract mixin class $DoNotDisturbPeriodDtoCopyWith<$Res> {
|
||||
factory $DoNotDisturbPeriodDtoCopyWith(DoNotDisturbPeriodDto value, $Res Function(DoNotDisturbPeriodDto) _then) = _$DoNotDisturbPeriodDtoCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String start, String end, String week
|
||||
bool enabled, String start, String end, String week
|
||||
});
|
||||
|
||||
|
||||
@@ -911,9 +911,10 @@ class _$DoNotDisturbPeriodDtoCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of DoNotDisturbPeriodDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? start = null,Object? end = null,Object? week = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? enabled = null,Object? start = null,Object? end = null,Object? week = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
start: null == start ? _self.start : start // ignore: cast_nullable_to_non_nullable
|
||||
enabled: null == enabled ? _self.enabled : enabled // ignore: cast_nullable_to_non_nullable
|
||||
as bool,start: null == start ? _self.start : start // ignore: cast_nullable_to_non_nullable
|
||||
as String,end: null == end ? _self.end : end // ignore: cast_nullable_to_non_nullable
|
||||
as String,week: null == week ? _self.week : week // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
@@ -1001,10 +1002,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String start, String end, String week)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool enabled, String start, String end, String week)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _DoNotDisturbPeriodDto() when $default != null:
|
||||
return $default(_that.start,_that.end,_that.week);case _:
|
||||
return $default(_that.enabled,_that.start,_that.end,_that.week);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -1022,10 +1023,10 @@ return $default(_that.start,_that.end,_that.week);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String start, String end, String week) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool enabled, String start, String end, String week) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _DoNotDisturbPeriodDto():
|
||||
return $default(_that.start,_that.end,_that.week);case _:
|
||||
return $default(_that.enabled,_that.start,_that.end,_that.week);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -1042,10 +1043,10 @@ return $default(_that.start,_that.end,_that.week);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String start, String end, String week)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool enabled, String start, String end, String week)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _DoNotDisturbPeriodDto() when $default != null:
|
||||
return $default(_that.start,_that.end,_that.week);case _:
|
||||
return $default(_that.enabled,_that.start,_that.end,_that.week);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -1057,9 +1058,10 @@ return $default(_that.start,_that.end,_that.week);case _:
|
||||
@JsonSerializable()
|
||||
|
||||
class _DoNotDisturbPeriodDto implements DoNotDisturbPeriodDto {
|
||||
const _DoNotDisturbPeriodDto({required this.start, required this.end, required this.week});
|
||||
const _DoNotDisturbPeriodDto({this.enabled = true, required this.start, required this.end, required this.week});
|
||||
factory _DoNotDisturbPeriodDto.fromJson(Map<String, dynamic> json) => _$DoNotDisturbPeriodDtoFromJson(json);
|
||||
|
||||
@override@JsonKey() final bool enabled;
|
||||
@override final String start;
|
||||
@override final String end;
|
||||
@override final String week;
|
||||
@@ -1077,16 +1079,16 @@ Map<String, dynamic> toJson() {
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _DoNotDisturbPeriodDto&&(identical(other.start, start) || other.start == start)&&(identical(other.end, end) || other.end == end)&&(identical(other.week, week) || other.week == week));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _DoNotDisturbPeriodDto&&(identical(other.enabled, enabled) || other.enabled == enabled)&&(identical(other.start, start) || other.start == start)&&(identical(other.end, end) || other.end == end)&&(identical(other.week, week) || other.week == week));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,start,end,week);
|
||||
int get hashCode => Object.hash(runtimeType,enabled,start,end,week);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DoNotDisturbPeriodDto(start: $start, end: $end, week: $week)';
|
||||
return 'DoNotDisturbPeriodDto(enabled: $enabled, start: $start, end: $end, week: $week)';
|
||||
}
|
||||
|
||||
|
||||
@@ -1097,7 +1099,7 @@ abstract mixin class _$DoNotDisturbPeriodDtoCopyWith<$Res> implements $DoNotDist
|
||||
factory _$DoNotDisturbPeriodDtoCopyWith(_DoNotDisturbPeriodDto value, $Res Function(_DoNotDisturbPeriodDto) _then) = __$DoNotDisturbPeriodDtoCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String start, String end, String week
|
||||
bool enabled, String start, String end, String week
|
||||
});
|
||||
|
||||
|
||||
@@ -1114,9 +1116,10 @@ class __$DoNotDisturbPeriodDtoCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of DoNotDisturbPeriodDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? start = null,Object? end = null,Object? week = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? enabled = null,Object? start = null,Object? end = null,Object? week = null,}) {
|
||||
return _then(_DoNotDisturbPeriodDto(
|
||||
start: null == start ? _self.start : start // ignore: cast_nullable_to_non_nullable
|
||||
enabled: null == enabled ? _self.enabled : enabled // ignore: cast_nullable_to_non_nullable
|
||||
as bool,start: null == start ? _self.start : start // ignore: cast_nullable_to_non_nullable
|
||||
as String,end: null == end ? _self.end : end // ignore: cast_nullable_to_non_nullable
|
||||
as String,week: null == week ? _self.week : week // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
|
||||
@@ -56,6 +56,7 @@ Map<String, dynamic> _$DoNotDisturbItemDtoToJson(
|
||||
_DoNotDisturbPeriodDto _$DoNotDisturbPeriodDtoFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _DoNotDisturbPeriodDto(
|
||||
enabled: json['enabled'] as bool? ?? true,
|
||||
start: json['start'] as String,
|
||||
end: json['end'] as String,
|
||||
week: json['week'] as String,
|
||||
@@ -64,6 +65,7 @@ _DoNotDisturbPeriodDto _$DoNotDisturbPeriodDtoFromJson(
|
||||
Map<String, dynamic> _$DoNotDisturbPeriodDtoToJson(
|
||||
_DoNotDisturbPeriodDto instance,
|
||||
) => <String, dynamic>{
|
||||
'enabled': instance.enabled,
|
||||
'start': instance.start,
|
||||
'end': instance.end,
|
||||
'week': instance.week,
|
||||
|
||||
@@ -5,6 +5,7 @@ part 'do_not_disturb_period.freezed.dart';
|
||||
@freezed
|
||||
abstract class DoNotDisturbPeriod with _$DoNotDisturbPeriod {
|
||||
const factory DoNotDisturbPeriod({
|
||||
@Default(true) bool enabled,
|
||||
required String start,
|
||||
required String end,
|
||||
required String week,
|
||||
|
||||
@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$DoNotDisturbPeriod {
|
||||
|
||||
String get start; String get end; String get week;
|
||||
bool get enabled; String get start; String get end; String get week;
|
||||
/// Create a copy of DoNotDisturbPeriod
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -25,16 +25,16 @@ $DoNotDisturbPeriodCopyWith<DoNotDisturbPeriod> get copyWith => _$DoNotDisturbPe
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is DoNotDisturbPeriod&&(identical(other.start, start) || other.start == start)&&(identical(other.end, end) || other.end == end)&&(identical(other.week, week) || other.week == week));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is DoNotDisturbPeriod&&(identical(other.enabled, enabled) || other.enabled == enabled)&&(identical(other.start, start) || other.start == start)&&(identical(other.end, end) || other.end == end)&&(identical(other.week, week) || other.week == week));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,start,end,week);
|
||||
int get hashCode => Object.hash(runtimeType,enabled,start,end,week);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DoNotDisturbPeriod(start: $start, end: $end, week: $week)';
|
||||
return 'DoNotDisturbPeriod(enabled: $enabled, start: $start, end: $end, week: $week)';
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ abstract mixin class $DoNotDisturbPeriodCopyWith<$Res> {
|
||||
factory $DoNotDisturbPeriodCopyWith(DoNotDisturbPeriod value, $Res Function(DoNotDisturbPeriod) _then) = _$DoNotDisturbPeriodCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String start, String end, String week
|
||||
bool enabled, String start, String end, String week
|
||||
});
|
||||
|
||||
|
||||
@@ -62,9 +62,10 @@ class _$DoNotDisturbPeriodCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of DoNotDisturbPeriod
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? start = null,Object? end = null,Object? week = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? enabled = null,Object? start = null,Object? end = null,Object? week = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
start: null == start ? _self.start : start // ignore: cast_nullable_to_non_nullable
|
||||
enabled: null == enabled ? _self.enabled : enabled // ignore: cast_nullable_to_non_nullable
|
||||
as bool,start: null == start ? _self.start : start // ignore: cast_nullable_to_non_nullable
|
||||
as String,end: null == end ? _self.end : end // ignore: cast_nullable_to_non_nullable
|
||||
as String,week: null == week ? _self.week : week // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
@@ -152,10 +153,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String start, String end, String week)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool enabled, String start, String end, String week)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _DoNotDisturbPeriod() when $default != null:
|
||||
return $default(_that.start,_that.end,_that.week);case _:
|
||||
return $default(_that.enabled,_that.start,_that.end,_that.week);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -173,10 +174,10 @@ return $default(_that.start,_that.end,_that.week);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String start, String end, String week) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool enabled, String start, String end, String week) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _DoNotDisturbPeriod():
|
||||
return $default(_that.start,_that.end,_that.week);case _:
|
||||
return $default(_that.enabled,_that.start,_that.end,_that.week);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -193,10 +194,10 @@ return $default(_that.start,_that.end,_that.week);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String start, String end, String week)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool enabled, String start, String end, String week)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _DoNotDisturbPeriod() when $default != null:
|
||||
return $default(_that.start,_that.end,_that.week);case _:
|
||||
return $default(_that.enabled,_that.start,_that.end,_that.week);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -208,9 +209,10 @@ return $default(_that.start,_that.end,_that.week);case _:
|
||||
|
||||
|
||||
class _DoNotDisturbPeriod implements DoNotDisturbPeriod {
|
||||
const _DoNotDisturbPeriod({required this.start, required this.end, required this.week});
|
||||
const _DoNotDisturbPeriod({this.enabled = true, required this.start, required this.end, required this.week});
|
||||
|
||||
|
||||
@override@JsonKey() final bool enabled;
|
||||
@override final String start;
|
||||
@override final String end;
|
||||
@override final String week;
|
||||
@@ -225,16 +227,16 @@ _$DoNotDisturbPeriodCopyWith<_DoNotDisturbPeriod> get copyWith => __$DoNotDistur
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _DoNotDisturbPeriod&&(identical(other.start, start) || other.start == start)&&(identical(other.end, end) || other.end == end)&&(identical(other.week, week) || other.week == week));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _DoNotDisturbPeriod&&(identical(other.enabled, enabled) || other.enabled == enabled)&&(identical(other.start, start) || other.start == start)&&(identical(other.end, end) || other.end == end)&&(identical(other.week, week) || other.week == week));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,start,end,week);
|
||||
int get hashCode => Object.hash(runtimeType,enabled,start,end,week);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DoNotDisturbPeriod(start: $start, end: $end, week: $week)';
|
||||
return 'DoNotDisturbPeriod(enabled: $enabled, start: $start, end: $end, week: $week)';
|
||||
}
|
||||
|
||||
|
||||
@@ -245,7 +247,7 @@ abstract mixin class _$DoNotDisturbPeriodCopyWith<$Res> implements $DoNotDisturb
|
||||
factory _$DoNotDisturbPeriodCopyWith(_DoNotDisturbPeriod value, $Res Function(_DoNotDisturbPeriod) _then) = __$DoNotDisturbPeriodCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String start, String end, String week
|
||||
bool enabled, String start, String end, String week
|
||||
});
|
||||
|
||||
|
||||
@@ -262,9 +264,10 @@ class __$DoNotDisturbPeriodCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of DoNotDisturbPeriod
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? start = null,Object? end = null,Object? week = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? enabled = null,Object? start = null,Object? end = null,Object? week = null,}) {
|
||||
return _then(_DoNotDisturbPeriod(
|
||||
start: null == start ? _self.start : start // ignore: cast_nullable_to_non_nullable
|
||||
enabled: null == enabled ? _self.enabled : enabled // ignore: cast_nullable_to_non_nullable
|
||||
as bool,start: null == start ? _self.start : start // ignore: cast_nullable_to_non_nullable
|
||||
as String,end: null == end ? _self.end : end // ignore: cast_nullable_to_non_nullable
|
||||
as String,week: null == week ? _self.week : week // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
|
||||
@@ -28,7 +28,6 @@ class DoNotDisturbScreen extends ConsumerWidget {
|
||||
await showErrorDialog(context, I18n.doNotDisturbError);
|
||||
return;
|
||||
}
|
||||
ref.read(doNotDisturbEditorProvider.notifier).clear();
|
||||
await showSuccessDialog(context, I18n.doNotDisturbSaved);
|
||||
});
|
||||
|
||||
@@ -56,6 +55,8 @@ class DoNotDisturbScreen extends ConsumerWidget {
|
||||
),
|
||||
data: (serverPeriods) {
|
||||
final periods = editorState ?? serverPeriods;
|
||||
final remainingSlots = maxPeriods - periods.length;
|
||||
|
||||
return SingleChildScrollView(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SizeUtils.getByScreen(small: 16, big: 14),
|
||||
@@ -75,74 +76,32 @@ class DoNotDisturbScreen extends ConsumerWidget {
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 14)),
|
||||
...periods.asMap().entries.map(
|
||||
(entry) => Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: SizeUtils.getByScreen(small: 8, big: 6),
|
||||
),
|
||||
child: DoNotDisturbPeriodCard(
|
||||
period: entry.value,
|
||||
onEdit: () => _editPeriod(
|
||||
context,
|
||||
ref,
|
||||
periods,
|
||||
entry.key,
|
||||
entry.value,
|
||||
),
|
||||
onDelete: () => _deletePeriod(
|
||||
context,
|
||||
ref,
|
||||
periods,
|
||||
entry.key,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 20, big: 18)),
|
||||
for (int i = 0; i < periods.length; i++) ...[
|
||||
DoNotDisturbPeriodCard(
|
||||
period: periods[i],
|
||||
onTap: () => _editPeriod(
|
||||
context,
|
||||
ref,
|
||||
periods,
|
||||
i,
|
||||
periods[i],
|
||||
),
|
||||
if (periods.isEmpty)
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: SizeUtils.getByScreen(small: 24, big: 20),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
context.translate(I18n.doNotDisturbEmpty),
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
SizeUtils.getByScreen(small: 14, big: 13),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
.withValues(alpha: 0.4),
|
||||
onToggle: (enabled) => ref
|
||||
.read(doNotDisturbEditorProvider.notifier)
|
||||
.toggle(
|
||||
current: periods,
|
||||
index: i,
|
||||
enabled: enabled,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (periods.length < maxPeriods)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: SizeUtils.getByScreen(small: 8, big: 6),
|
||||
),
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () => _addPeriod(context, ref, periods),
|
||||
icon: const Icon(Icons.add),
|
||||
label: Text(
|
||||
context.translate(I18n.doNotDisturbAddPeriod),
|
||||
),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: primaryColor,
|
||||
side: BorderSide(color: primaryColor),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
SizeUtils.getByScreen(small: 12, big: 10),
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical:
|
||||
SizeUtils.getByScreen(small: 12, big: 10),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: SizeUtils.getByScreen(small: 8, big: 6),
|
||||
),
|
||||
],
|
||||
if (remainingSlots > 0)
|
||||
_AddPeriodTile(
|
||||
onTap: () => _addPeriod(context, ref, periods),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -174,6 +133,23 @@ class DoNotDisturbScreen extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
bool _isDuplicate(
|
||||
DoNotDisturbPeriod period,
|
||||
List<DoNotDisturbPeriod> current, {
|
||||
int? excludeIndex,
|
||||
}) {
|
||||
for (var i = 0; i < current.length; i++) {
|
||||
if (i == excludeIndex) continue;
|
||||
final existing = current[i];
|
||||
if (existing.start == period.start &&
|
||||
existing.end == period.end &&
|
||||
existing.week == period.week) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<void> _addPeriod(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
@@ -184,11 +160,15 @@ class DoNotDisturbScreen extends ConsumerWidget {
|
||||
isScrollControlled: true,
|
||||
builder: (_) => const EditPeriodSheet(),
|
||||
);
|
||||
if (period != null) {
|
||||
ref
|
||||
.read(doNotDisturbEditorProvider.notifier)
|
||||
.add(current: current, period: period);
|
||||
if (period == null) return;
|
||||
if (_isDuplicate(period, current)) {
|
||||
if (!context.mounted) return;
|
||||
await showErrorDialog(context, I18n.doNotDisturbDuplicate);
|
||||
return;
|
||||
}
|
||||
ref
|
||||
.read(doNotDisturbEditorProvider.notifier)
|
||||
.add(current: current, period: period);
|
||||
}
|
||||
|
||||
Future<void> _editPeriod(
|
||||
@@ -201,13 +181,20 @@ class DoNotDisturbScreen extends ConsumerWidget {
|
||||
final period = await showModalBottomSheet<DoNotDisturbPeriod>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (_) => EditPeriodSheet(initial: existing),
|
||||
builder: (_) => EditPeriodSheet(
|
||||
initial: existing,
|
||||
onDelete: () => _deletePeriod(context, ref, current, index),
|
||||
),
|
||||
);
|
||||
if (period != null) {
|
||||
ref
|
||||
.read(doNotDisturbEditorProvider.notifier)
|
||||
.replace(current: current, index: index, period: period);
|
||||
if (period == null) return;
|
||||
if (_isDuplicate(period, current, excludeIndex: index)) {
|
||||
if (!context.mounted) return;
|
||||
await showErrorDialog(context, I18n.doNotDisturbDuplicate);
|
||||
return;
|
||||
}
|
||||
ref
|
||||
.read(doNotDisturbEditorProvider.notifier)
|
||||
.replace(current: current, index: index, period: period);
|
||||
}
|
||||
|
||||
Future<void> _deletePeriod(
|
||||
@@ -216,7 +203,7 @@ class DoNotDisturbScreen extends ConsumerWidget {
|
||||
List<DoNotDisturbPeriod> current,
|
||||
int index,
|
||||
) async {
|
||||
final confirmed = await showDialog<bool>(
|
||||
final confirmed = await showLegacyDialog<bool>(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
title: Text(context.translate(I18n.doNotDisturbDeletePeriod)),
|
||||
@@ -243,3 +230,55 @@ class DoNotDisturbScreen extends ConsumerWidget {
|
||||
.remove(current: current, index: index);
|
||||
}
|
||||
}
|
||||
|
||||
class _AddPeriodTile extends StatelessWidget {
|
||||
final VoidCallback onTap;
|
||||
|
||||
const _AddPeriodTile({required this.onTap});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SizeUtils.getByScreen(small: 16, big: 14),
|
||||
vertical: SizeUtils.getByScreen(small: 16, big: 14),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
context.translate(I18n.doNotDisturbAddPeriod),
|
||||
style: TextStyle(
|
||||
fontSize: SizeUtils.getByScreen(small: 15, big: 16),
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: context.sfColors.legacyPrimary,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.add,
|
||||
color: Colors.white,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'dart:async';
|
||||
|
||||
import 'package:device_management/src/core/providers/do_not_disturb_providers.dart';
|
||||
import 'package:device_management/src/features/do_not_disturb/domain/do_not_disturb_period.dart';
|
||||
import 'package:device_management/src/features/do_not_disturb/presentation/providers/do_not_disturb_provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:sf_tracking/sf_tracking.dart';
|
||||
|
||||
@@ -25,7 +24,6 @@ class DoNotDisturbController extends _$DoNotDisturbController {
|
||||
identificator: deviceIdentificator,
|
||||
periods: periods,
|
||||
);
|
||||
ref.invalidate(doNotDisturbPeriodsProvider(deviceIdentificator));
|
||||
unawaited(
|
||||
ref
|
||||
.read(sfTrackingProvider)
|
||||
|
||||
@@ -34,7 +34,7 @@ final class DoNotDisturbControllerProvider
|
||||
}
|
||||
|
||||
String _$doNotDisturbControllerHash() =>
|
||||
r'f84df0b051b53a5fadd8801412d45e870aee6637';
|
||||
r'0943b2ecc8d1d443f0a520a9940009f9aa8fbacc';
|
||||
|
||||
abstract class _$DoNotDisturbController extends $AsyncNotifier<void> {
|
||||
FutureOr<void> build();
|
||||
|
||||
@@ -37,5 +37,17 @@ class DoNotDisturbEditor extends _$DoNotDisturbEditor {
|
||||
state = [...base]..removeAt(index);
|
||||
}
|
||||
|
||||
void toggle({
|
||||
required List<DoNotDisturbPeriod> current,
|
||||
required int index,
|
||||
required bool enabled,
|
||||
}) {
|
||||
final base = state ?? current;
|
||||
if (index < 0 || index >= base.length) return;
|
||||
final updated = [...base];
|
||||
updated[index] = updated[index].copyWith(enabled: enabled);
|
||||
state = updated;
|
||||
}
|
||||
|
||||
void clear() => state = null;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ final class DoNotDisturbEditorProvider
|
||||
}
|
||||
|
||||
String _$doNotDisturbEditorHash() =>
|
||||
r'a82e3bc2ca0eaf5d185d5e7624d2956acf883269';
|
||||
r'406087481a5a0cb2fdcf9df745c40f104d77e2aa';
|
||||
|
||||
abstract class _$DoNotDisturbEditor
|
||||
extends $Notifier<List<DoNotDisturbPeriod>?> {
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import 'package:legacy_theme/legacy_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
import '../../domain/do_not_disturb_period.dart';
|
||||
import 'week_day_row.dart';
|
||||
|
||||
class DoNotDisturbPeriodCard extends ConsumerWidget {
|
||||
final DoNotDisturbPeriod period;
|
||||
final VoidCallback onEdit;
|
||||
final VoidCallback onDelete;
|
||||
final VoidCallback onTap;
|
||||
final ValueChanged<bool> onToggle;
|
||||
|
||||
const DoNotDisturbPeriodCard({
|
||||
super.key,
|
||||
required this.period,
|
||||
required this.onEdit,
|
||||
required this.onDelete,
|
||||
required this.onTap,
|
||||
required this.onToggle,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -23,57 +23,73 @@ class DoNotDisturbPeriodCard extends ConsumerWidget {
|
||||
final primaryColor = context.sfColors.legacyPrimary;
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.all(SizeUtils.getByScreen(small: 14, big: 12)),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(
|
||||
SizeUtils.getByScreen(small: 12, big: 10),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SizeUtils.getByScreen(small: 16, big: 14),
|
||||
vertical: SizeUtils.getByScreen(small: 14, big: 12),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.schedule,
|
||||
color: primaryColor,
|
||||
size: SizeUtils.getByScreen(small: 20, big: 18),
|
||||
),
|
||||
SizedBox(width: SizeUtils.getByScreen(small: 8, big: 6)),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'${period.start} — ${period.end}',
|
||||
style: TextStyle(
|
||||
fontSize: SizeUtils.getByScreen(small: 16, big: 15),
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${period.start}-${period.end}',
|
||||
style: TextStyle(
|
||||
fontSize: SizeUtils.getByScreen(small: 16, big: 15),
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
_weekDaysLabel(context, period.week),
|
||||
style: TextStyle(
|
||||
fontSize: SizeUtils.getByScreen(small: 13, big: 12),
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: onEdit,
|
||||
child: Icon(
|
||||
Icons.edit_outlined,
|
||||
size: SizeUtils.getByScreen(small: 20, big: 18),
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
SizedBox(width: SizeUtils.getByScreen(small: 12, big: 10)),
|
||||
GestureDetector(
|
||||
onTap: onDelete,
|
||||
child: Icon(
|
||||
Icons.delete_outline,
|
||||
size: SizeUtils.getByScreen(small: 20, big: 18),
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
Switch.adaptive(
|
||||
value: period.enabled,
|
||||
onChanged: onToggle,
|
||||
activeTrackColor: primaryColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 6)),
|
||||
WeekDayRow(week: period.week, activeColor: primaryColor),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _weekDaysLabel(BuildContext context, String week) {
|
||||
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 active = <String>[];
|
||||
for (var i = 0; i < 7; i++) {
|
||||
if (chars[i] == '1') active.add(labels[i]);
|
||||
}
|
||||
if (active.length == 7) {
|
||||
return context.translate(I18n.doNotDisturbEveryDay);
|
||||
}
|
||||
return active.join(', ');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,9 @@ import 'week_day_row.dart';
|
||||
|
||||
class EditPeriodSheet extends ConsumerStatefulWidget {
|
||||
final DoNotDisturbPeriod? initial;
|
||||
final VoidCallback? onDelete;
|
||||
|
||||
const EditPeriodSheet({super.key, this.initial});
|
||||
const EditPeriodSheet({super.key, this.initial, this.onDelete});
|
||||
|
||||
@override
|
||||
ConsumerState<EditPeriodSheet> createState() => _EditPeriodSheetState();
|
||||
@@ -169,6 +170,7 @@ class _EditPeriodSheetState extends ConsumerState<EditPeriodSheet> {
|
||||
Navigator.pop(
|
||||
context,
|
||||
DoNotDisturbPeriod(
|
||||
enabled: widget.initial?.enabled ?? true,
|
||||
start: _formatForApi(_start.value),
|
||||
end: _formatForApi(_end.value),
|
||||
week: _weekToString(days),
|
||||
@@ -181,6 +183,21 @@ class _EditPeriodSheetState extends ConsumerState<EditPeriodSheet> {
|
||||
);
|
||||
},
|
||||
),
|
||||
if (isEditing && widget.onDelete != null) ...[
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 10, big: 8)),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
widget.onDelete!();
|
||||
},
|
||||
child: Text(
|
||||
context.translate(I18n.doNotDisturbDeletePeriod),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 10)),
|
||||
],
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user